1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3package domain
4
5import (
6 "errors"
7 "testing"
8)
9
10func budgetID(t *testing.T, s string) ObjectID {
11 t.Helper()
12 id, err := ParseObjectID(s)
13 if err != nil {
14 t.Fatal(err)
15 }
16 return id
17}
18
19func TestValidateCommitObjects(t *testing.T) {
20 id := budgetID(t, "1111111111111111111111111111111111111111")
21 for _, tt := range []struct {
22 name string
23 ids []ObjectID
24 max int
25 want error
26 }{
27 {"empty", nil, 1, nil}, {"boundary", []ObjectID{id}, 1, nil},
28 {"too many", []ObjectID{id, id}, 1, ErrInvalid}, {"zero", []ObjectID{{}}, 1, ErrInvalidObjectID},
29 } {
30 t.Run(tt.name, func(t *testing.T) {
31 if err := ValidateCommitObjects(tt.ids, tt.max); !errors.Is(err, tt.want) {
32 t.Fatalf("got %v, want %v", err, tt.want)
33 }
34 })
35 }
36}
37
38func TestCommitObjectBudget(t *testing.T) {
39 id := budgetID(t, "1111111111111111111111111111111111111111")
40 for _, tt := range []struct {
41 name string
42 commit bool
43 size, total int64
44 seen, take, stop bool
45 }{
46 {"non commit", false, 1, 0, false, false, false},
47 {"oversize", true, MaxCommitObjectBytes + 1, 0, false, false, false},
48 {"exact object", true, MaxCommitObjectBytes, 0, false, true, false},
49 {"exact total", true, 1, MaxCommitObjectsBytes - 1, false, true, false},
50 {"over total", true, 2, MaxCommitObjectsBytes - 1, false, false, true},
51 {"duplicate", true, 1, MaxCommitObjectsBytes, true, false, false},
52 {"negative", true, -1, 0, false, false, false},
53 } {
54 t.Run(tt.name, func(t *testing.T) {
55 b := CommitObjectBudget{total: tt.total}
56 if tt.seen {
57 b.seen = map[ObjectID]bool{id: true}
58 }
59 take, stop := b.Admit(id, tt.commit, tt.size)
60 if take != tt.take || stop != tt.stop {
61 t.Fatalf("got %v,%v", take, stop)
62 }
63 if take {
64 b.Charge(id, tt.size)
65 if !b.Seen(id) || b.total != tt.total+tt.size {
66 t.Fatal("charge not recorded")
67 }
68 }
69 })
70 }
71}
72
73func TestDiffBudgetWindow(t *testing.T) {
74 for _, tt := range []struct {
75 name string
76 limits DiffLimits
77 kept, from, to int
78 truncated bool
79 }{
80 {"unlimited", DiffLimits{}, 5, 0, 5, false},
81 {"cap", DiffLimits{MaxFiles: 3}, 3, 0, 3, true},
82 {"window", DiffLimits{From: 1, To: 4}, 5, 1, 4, false},
83 {"clamped", DiffLimits{From: -1, To: 9, MaxFiles: 3}, 3, 0, 3, true},
84 {"past end", DiffLimits{From: 8}, 5, 8, 5, false},
85 {"reversed", DiffLimits{From: 4, To: 2}, 5, 4, 2, false},
86 } {
87 t.Run(tt.name, func(t *testing.T) {
88 b := NewDiffBudget(tt.limits)
89 k, f, to, tr := b.Window(5)
90 if k != tt.kept || f != tt.from || to != tt.to || tr != tt.truncated {
91 t.Fatalf("got %d,%d,%d,%v", k, f, to, tr)
92 }
93 })
94 }
95}
96
97func TestDiffBudgetHunks(t *testing.T) {
98 for _, tt := range []struct {
99 name string
100 binary bool
101 n, lines, bytes int
102 want bool
103 }{
104 {"fits", false, 3, 3, 1, true}, {"binary", true, 3, 3, 1, false},
105 {"no changes", false, 0, 3, 1, false}, {"file limit", false, 4, 5, 1, false},
106 {"total lines", false, 3, 2, 1, false}, {"no bytes", false, 1, 3, 0, false},
107 } {
108 t.Run(tt.name, func(t *testing.T) {
109 b := NewDiffBudget(DiffLimits{MaxFileLines: 3, MaxTotalLines: tt.lines, MaxTotalBytes: tt.bytes})
110 if got := b.WantsHunks(tt.binary, tt.n); got != tt.want {
111 t.Fatalf("got %v", got)
112 }
113 })
114 }
115}
116
117func TestDiffBudgetCharge(t *testing.T) {
118 for _, tt := range []struct {
119 name string
120 bytes int
121 fits bool
122 }{{"exact", 3, true}, {"over", 4, false}} {
123 t.Run(tt.name, func(t *testing.T) {
124 b := NewDiffBudget(DiffLimits{MaxFileLines: 2, MaxTotalLines: 1, MaxTotalBytes: 3})
125 before := b
126 if b.FileLines() != 1 {
127 t.Fatal("wrong file limit")
128 }
129 if b.ChargeLine(tt.bytes) != tt.fits {
130 t.Fatal("wrong charge result")
131 }
132 if b.ChargeLine(0) {
133 t.Fatal("line budget exceeded")
134 }
135 b = before
136 if !b.WantsHunks(false, 1) {
137 t.Fatal("discarded file consumed budget")
138 }
139 })
140 }
141}