1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package predicate
16
17 import (
18 "context"
19 "regexp"
20 "testing"
21
22 "github.com/stretchr/testify/assert"
23
24 "edge-infra.dev/pkg/f8n/devinfra/repo/owners/policybot/pull"
25 "edge-infra.dev/pkg/f8n/devinfra/repo/owners/policybot/pull/pulltest"
26
27 "edge-infra.dev/pkg/f8n/devinfra/repo/owners/policybot/policy/common"
28 )
29
30 func TestChangedFiles(t *testing.T) {
31 p := &ChangedFiles{
32 Paths: []common.Regexp{
33 common.NewCompiledRegexp(regexp.MustCompile("app/.*\\.go")),
34 common.NewCompiledRegexp(regexp.MustCompile("server/.*\\.go")),
35 },
36 IgnorePaths: []common.Regexp{
37 common.NewCompiledRegexp(regexp.MustCompile(".*/special\\.go")),
38 },
39 }
40
41 runFileTests(t, p, []FileTestCase{
42 {
43 "empty",
44 []*pull.File{},
45 &common.PredicateResult{
46 Satisfied: false,
47 Values: []string{},
48 ConditionsMap: map[string][]string{
49 "path patterns": {"app/.*\\.go", "server/.*\\.go"},
50 "while ignoring": {".*/special\\.go"},
51 },
52 },
53 },
54 {
55 "onlyMatches",
56 []*pull.File{
57 {
58 Filename: "app/client.go",
59 Status: pull.FileAdded,
60 },
61 {
62 Filename: "server/server.go",
63 Status: pull.FileModified,
64 },
65 },
66 &common.PredicateResult{
67 Satisfied: true,
68 Values: []string{"app/client.go"},
69 ConditionsMap: map[string][]string{
70 "path patterns": {"app/.*\\.go", "server/.*\\.go"},
71 "while ignoring": {".*/special\\.go"},
72 },
73 },
74 },
75 {
76 "someMatches",
77 []*pull.File{
78 {
79 Filename: "app/client.go",
80 Status: pull.FileAdded,
81 },
82 {
83 Filename: "model/user.go",
84 Status: pull.FileModified,
85 },
86 },
87 &common.PredicateResult{
88 Satisfied: true,
89 Values: []string{"app/client.go"},
90 ConditionsMap: map[string][]string{
91 "path patterns": {"app/.*\\.go", "server/.*\\.go"},
92 "while ignoring": {".*/special\\.go"},
93 },
94 },
95 },
96 {
97 "noMatches",
98 []*pull.File{
99 {
100 Filename: "model/order.go",
101 Status: pull.FileDeleted,
102 },
103 {
104 Filename: "model/user.go",
105 Status: pull.FileModified,
106 },
107 },
108 &common.PredicateResult{
109 Satisfied: false,
110 Values: []string{"model/order.go", "model/user.go"},
111 ConditionsMap: map[string][]string{
112 "path patterns": {"app/.*\\.go", "server/.*\\.go"},
113 "while ignoring": {".*/special\\.go"},
114 },
115 },
116 },
117 {
118 "ignoreAll",
119 []*pull.File{
120 {
121 Filename: "app/special.go",
122 Status: pull.FileDeleted,
123 },
124 {
125 Filename: "server/special.go",
126 Status: pull.FileModified,
127 },
128 },
129 &common.PredicateResult{
130 Satisfied: false,
131 Values: []string{"app/special.go", "server/special.go"},
132 ConditionsMap: map[string][]string{
133 "path patterns": {"app/.*\\.go", "server/.*\\.go"},
134 "while ignoring": {".*/special\\.go"},
135 },
136 },
137 },
138 {
139 "ignoreSome",
140 []*pull.File{
141 {
142 Filename: "app/normal.go",
143 Status: pull.FileDeleted,
144 },
145 {
146 Filename: "server/special.go",
147 Status: pull.FileModified,
148 },
149 },
150 &common.PredicateResult{
151 Satisfied: true,
152 Values: []string{"app/normal.go"},
153 ConditionsMap: map[string][]string{
154 "path patterns": {"app/.*\\.go", "server/.*\\.go"},
155 "while ignoring": {".*/special\\.go"},
156 },
157 },
158 },
159 })
160 }
161
162 func TestOnlyChangedFiles(t *testing.T) {
163 p := &OnlyChangedFiles{
164 Paths: []common.Regexp{
165 common.NewCompiledRegexp(regexp.MustCompile("app/.*\\.go")),
166 common.NewCompiledRegexp(regexp.MustCompile("server/.*\\.go")),
167 },
168 }
169
170 runFileTests(t, p, []FileTestCase{
171 {
172 "empty",
173 []*pull.File{},
174 &common.PredicateResult{
175 Satisfied: false,
176 Values: []string{},
177 ConditionValues: []string{"app/.*\\.go", "server/.*\\.go"},
178 },
179 },
180 {
181 "onlyMatches",
182 []*pull.File{
183 {
184 Filename: "app/client.go",
185 Status: pull.FileAdded,
186 },
187 {
188 Filename: "server/server.go",
189 Status: pull.FileModified,
190 },
191 },
192 &common.PredicateResult{
193 Satisfied: true,
194 Values: []string{"app/client.go", "server/server.go"},
195 ConditionValues: []string{"app/.*\\.go", "server/.*\\.go"},
196 },
197 },
198 {
199 "someMatches",
200 []*pull.File{
201 {
202 Filename: "app/client.go",
203 Status: pull.FileAdded,
204 },
205 {
206 Filename: "model/user.go",
207 Status: pull.FileModified,
208 },
209 },
210 &common.PredicateResult{
211 Satisfied: false,
212 Values: []string{"model/user.go"},
213 ConditionValues: []string{"app/.*\\.go", "server/.*\\.go"},
214 },
215 },
216 {
217 "noMatches",
218 []*pull.File{
219 {
220 Filename: "model/order.go",
221 Status: pull.FileDeleted,
222 },
223 {
224 Filename: "model/user.go",
225 Status: pull.FileModified,
226 },
227 },
228 &common.PredicateResult{
229 Satisfied: false,
230 Values: []string{"model/order.go"},
231 ConditionValues: []string{"app/.*\\.go", "server/.*\\.go"},
232 },
233 },
234 })
235 }
236
237 func TestModifiedLines(t *testing.T) {
238 p := &ModifiedLines{
239 Additions: ComparisonExpr{Op: OpGreaterThan, Value: 100},
240 Deletions: ComparisonExpr{Op: OpGreaterThan, Value: 10},
241 }
242
243 runFileTests(t, p, []FileTestCase{
244 {
245 "empty",
246 []*pull.File{},
247 &common.PredicateResult{
248 Satisfied: false,
249 Values: []string{"+0", "-0"},
250 ConditionValues: []string{"added lines > 100", "deleted lines > 10"},
251 },
252 },
253 {
254 "additions",
255 []*pull.File{
256 {Additions: 55},
257 {Additions: 10},
258 {Additions: 45},
259 },
260 &common.PredicateResult{
261 Satisfied: true,
262 Values: []string{"+110"},
263 ConditionValues: []string{"added lines > 100"},
264 },
265 },
266 {
267 "deletions",
268 []*pull.File{
269 {Additions: 5},
270 {Additions: 10, Deletions: 10},
271 {Additions: 5},
272 {Deletions: 10},
273 },
274 &common.PredicateResult{
275 Satisfied: true,
276 Values: []string{"-20"},
277 ConditionValues: []string{"deleted lines > 10"},
278 },
279 },
280 })
281
282 p = &ModifiedLines{
283 Total: ComparisonExpr{Op: OpGreaterThan, Value: 100},
284 }
285
286 runFileTests(t, p, []FileTestCase{
287 {
288 "total",
289 []*pull.File{
290 {Additions: 20, Deletions: 20},
291 {Additions: 20},
292 {Deletions: 20},
293 {Additions: 20, Deletions: 20},
294 },
295 &common.PredicateResult{
296 Satisfied: true,
297 Values: []string{"total 120"},
298 ConditionValues: []string{"total modifications > 100"},
299 },
300 },
301 })
302 }
303
304 func TestComparisonExpr(t *testing.T) {
305 tests := map[string]struct {
306 Expr ComparisonExpr
307 Value int64
308 Output bool
309 }{
310 "greaterThanTrue": {
311 Expr: ComparisonExpr{Op: OpGreaterThan, Value: 100},
312 Value: 200,
313 Output: true,
314 },
315 "greaterThanFalse": {
316 Expr: ComparisonExpr{Op: OpGreaterThan, Value: 100},
317 Value: 50,
318 Output: false,
319 },
320 "lessThanTrue": {
321 Expr: ComparisonExpr{Op: OpLessThan, Value: 100},
322 Value: 50,
323 Output: true,
324 },
325 "lessThanFalse": {
326 Expr: ComparisonExpr{Op: OpLessThan, Value: 100},
327 Value: 200,
328 Output: false,
329 },
330 }
331
332 for name, test := range tests {
333 t.Run(name, func(t *testing.T) {
334 ok := test.Expr.Evaluate(test.Value)
335 assert.Equal(t, test.Output, ok, "evaluation was not correct")
336 })
337 }
338
339 t.Run("isEmpty", func(t *testing.T) {
340 assert.True(t, ComparisonExpr{}.IsEmpty(), "expression was not empty")
341 assert.False(t, ComparisonExpr{Op: OpGreaterThan, Value: 100}.IsEmpty(), "expression was empty")
342 })
343
344 parseTests := map[string]struct {
345 Input string
346 Output ComparisonExpr
347 Err bool
348 }{
349 "lessThan": {
350 Input: "<100",
351 Output: ComparisonExpr{Op: OpLessThan, Value: 100},
352 },
353 "greaterThan": {
354 Input: ">100",
355 Output: ComparisonExpr{Op: OpGreaterThan, Value: 100},
356 },
357 "innerSpaces": {
358 Input: "< 35",
359 Output: ComparisonExpr{Op: OpLessThan, Value: 35},
360 },
361 "leadingSpaces": {
362 Input: " < 35",
363 Output: ComparisonExpr{Op: OpLessThan, Value: 35},
364 },
365 "trailngSpaces": {
366 Input: "< 35 ",
367 Output: ComparisonExpr{Op: OpLessThan, Value: 35},
368 },
369 "invalidOp": {
370 Input: "=10",
371 Err: true,
372 },
373 "invalidValue": {
374 Input: "< 10ab",
375 Err: true,
376 },
377 }
378
379 for name, test := range parseTests {
380 t.Run(name, func(t *testing.T) {
381 var expr ComparisonExpr
382 err := expr.UnmarshalText([]byte(test.Input))
383 if test.Err {
384 assert.Error(t, err, "expected error parsing expression, but got nil")
385 return
386 }
387 if assert.NoError(t, err, "unexpected error parsing expression") {
388 assert.Equal(t, test.Output, expr, "parsed expression was not correct")
389 }
390 })
391 }
392 }
393
394 type FileTestCase struct {
395 Name string
396 Files []*pull.File
397 ExpectedPredicateResult *common.PredicateResult
398 }
399
400 func runFileTests(t *testing.T, p Predicate, cases []FileTestCase) {
401 ctx := context.Background()
402
403 for _, tc := range cases {
404 t.Run(tc.Name, func(t *testing.T) {
405 prctx := &pulltest.Context{
406 ChangedFilesValue: tc.Files,
407 }
408
409 predicateResult, err := p.Evaluate(ctx, prctx)
410 if assert.NoError(t, err, "evaluation failed") {
411 assertPredicateResult(t, tc.ExpectedPredicateResult, predicateResult)
412 }
413 })
414 }
415 }
416
View as plain text