1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package approval
16
17 import (
18 "context"
19 "errors"
20 "reflect"
21 "regexp"
22 "testing"
23
24 "github.com/stretchr/testify/assert"
25 "github.com/stretchr/testify/require"
26 "gopkg.in/yaml.v2"
27
28 "edge-infra.dev/pkg/f8n/devinfra/repo/owners/policybot/pull"
29 "edge-infra.dev/pkg/f8n/devinfra/repo/owners/policybot/pull/pulltest"
30
31 "edge-infra.dev/pkg/f8n/devinfra/repo/owners/policybot/policy/common"
32 "edge-infra.dev/pkg/f8n/devinfra/repo/owners/policybot/policy/predicate"
33 )
34
35 func TestRules(t *testing.T) {
36 ruleText := `
37 - name: rule1
38 if:
39 changed_files:
40 paths: ["path1"]
41 only_changed_files:
42 paths: ["path2"]
43 has_author_in:
44 teams: ["team1"]
45 users: ["user1", "user2"]
46 organizations: ["org1"]
47 has_contributor_in:
48 teams: ["team2"]
49 users: ["user3"]
50 organizations: ["org2"]
51 options:
52 allow_author: true
53 allow_contributor: true
54 # invalidate_on_push: true
55 methods:
56 comments: ["+1"]
57 github_review: true
58 requires:
59 users: ["user4"]
60 teams: ["team3", "team4"]
61 organizations: ["org3"]
62 count: 5
63 `
64
65 var rules []*Rule
66 require.NoError(t, yaml.UnmarshalStrict([]byte(ruleText), &rules))
67
68 defaultGithubReview := true
69 defaultComments := []string{
70 ":+1:",
71 "👍",
72 }
73 comments := []string{"+1"}
74 expected := []*Rule{
75 {
76 Name: "rule1",
77 Predicates: predicate.Predicates{
78 ChangedFiles: &predicate.ChangedFiles{
79 Paths: []common.Regexp{
80 common.NewCompiledRegexp(regexp.MustCompile("path1")),
81 },
82 },
83 OnlyChangedFiles: &predicate.OnlyChangedFiles{
84 Paths: []common.Regexp{
85 common.NewCompiledRegexp(regexp.MustCompile("path2")),
86 },
87 },
88 HasAuthorIn: &predicate.HasAuthorIn{
89 Actors: common.Actors{
90 Teams: []string{"team1"},
91 Users: []string{"user1", "user2"},
92 Organizations: []string{"org1"},
93 },
94 },
95 HasContributorIn: &predicate.HasContributorIn{
96 Actors: common.Actors{
97 Teams: []string{"team2"},
98 Users: []string{"user3"},
99 Organizations: []string{"org2"},
100 },
101 },
102 },
103 Options: Options{
104 AllowAuthor: true,
105 AllowContributor: true,
106
107 Methods: &common.Methods{
108 Comments: comments,
109 GithubReview: &defaultGithubReview,
110 },
111 },
112 Requires: Requires{
113 Count: 5,
114 Actors: common.Actors{
115 Users: []string{"user4"},
116 Teams: []string{"team3", "team4"},
117 Organizations: []string{"org3"},
118 },
119 },
120 },
121 }
122
123 require.True(t, reflect.DeepEqual(expected, rules))
124
125 optionsText := `
126 allow_author: true
127 allow_contributor: true
128 invalidate_on_push: true
129 methods:
130 comments: ["+1"]
131 `
132 expectedMethods := &common.Methods{
133 Comments: comments,
134 GithubReview: &defaultGithubReview,
135 }
136 var options *Options
137 require.NoError(t, yaml.UnmarshalStrict([]byte(optionsText), &options))
138
139 methods := options.GetMethods()
140
141 require.True(t, reflect.DeepEqual(expectedMethods.Comments, methods.Comments))
142 require.True(t, reflect.DeepEqual(*expectedMethods.GithubReview, *methods.GithubReview))
143
144 optionsText = `
145 allow_author: true
146 allow_contributor: true
147 invalidate_on_push: true
148 methods:
149 github_review: false
150 `
151 falseGithubReview := false
152 expectedMethods = &common.Methods{
153 Comments: defaultComments,
154 GithubReview: &falseGithubReview,
155 }
156
157 var optionsTwo *Options
158 require.NoError(t, yaml.UnmarshalStrict([]byte(optionsText), &optionsTwo))
159
160 methods = optionsTwo.GetMethods()
161
162 require.True(t, reflect.DeepEqual(expectedMethods.Comments, methods.Comments))
163 require.True(t, reflect.DeepEqual(*expectedMethods.GithubReview, *methods.GithubReview))
164
165 optionsText = `
166 allow_author: true
167 allow_contributor: true
168 invalidate_on_push: true
169 `
170 expectedMethods = &common.Methods{
171 Comments: defaultComments,
172 GithubReview: &defaultGithubReview,
173 }
174 var optionsThree *Options
175 require.NoError(t, yaml.UnmarshalStrict([]byte(optionsText), &optionsThree))
176
177 methods = optionsThree.GetMethods()
178
179 require.True(t, reflect.DeepEqual(expectedMethods.Comments, methods.Comments))
180 require.True(t, reflect.DeepEqual(*expectedMethods.GithubReview, *methods.GithubReview))
181
182 }
183
184 type mockRequirement struct {
185 result *common.Result
186 }
187
188 func (m *mockRequirement) Trigger() common.Trigger {
189 return common.TriggerStatic
190 }
191
192 func (m *mockRequirement) Evaluate(ctx context.Context, prctx pull.Context) common.Result {
193 return *m.result
194 }
195
196 func makeRulesResultingIn(es ...common.EvaluationStatus) []common.Evaluator {
197 var requirements []common.Evaluator
198 for _, e := range es {
199 requirements = append(requirements, &mockRequirement{
200 result: &common.Result{
201 Status: e,
202 },
203 })
204 }
205 return requirements
206 }
207
208 func TestAndRequirement(t *testing.T) {
209 ctx := context.Background()
210 prctx := &pulltest.Context{}
211
212
213 and := &AndRequirement{
214 requirements: makeRulesResultingIn(common.StatusApproved, common.StatusPending),
215 }
216 result := and.Evaluate(ctx, prctx)
217 assert.NoError(t, result.Error)
218 assert.Equal(t, common.StatusPending, result.Status)
219
220
221 and = &AndRequirement{
222 requirements: makeRulesResultingIn(common.StatusApproved),
223 }
224 result = and.Evaluate(ctx, prctx)
225 assert.NoError(t, result.Error)
226 assert.Equal(t, common.StatusApproved, result.Status)
227
228
229 and = &AndRequirement{
230 requirements: makeRulesResultingIn(common.StatusApproved, common.StatusSkipped),
231 }
232 result = and.Evaluate(ctx, prctx)
233 assert.NoError(t, result.Error)
234 assert.Equal(t, common.StatusApproved, result.Status)
235
236
237 and = &AndRequirement{
238 requirements: makeRulesResultingIn(common.StatusSkipped),
239 }
240 result = and.Evaluate(ctx, prctx)
241 assert.NoError(t, result.Error)
242 assert.Equal(t, common.StatusSkipped, result.Status)
243
244
245 and = &AndRequirement{
246 requirements: []common.Evaluator{
247 &mockRequirement{
248 result: &common.Result{
249 Status: common.StatusApproved,
250 },
251 },
252 &mockRequirement{
253 result: &common.Result{
254 Error: errors.New("error"),
255 },
256 },
257 },
258 }
259 result = and.Evaluate(ctx, prctx)
260 assert.Error(t, result.Error)
261 }
262
263 func TestOrRequirement(t *testing.T) {
264 ctx := context.Background()
265 prctx := &pulltest.Context{}
266
267
268 or := &OrRequirement{
269 requirements: makeRulesResultingIn(common.StatusPending, common.StatusSkipped, common.StatusApproved),
270 }
271 result := or.Evaluate(ctx, prctx)
272 assert.NoError(t, result.Error)
273 assert.Equal(t, common.StatusApproved, result.Status)
274
275
276 or = &OrRequirement{
277 requirements: makeRulesResultingIn(common.StatusApproved),
278 }
279 result = or.Evaluate(ctx, prctx)
280 assert.NoError(t, result.Error)
281 assert.Equal(t, common.StatusApproved, result.Status)
282
283
284 or = &OrRequirement{
285 requirements: makeRulesResultingIn(common.StatusApproved, common.StatusSkipped),
286 }
287 result = or.Evaluate(ctx, prctx)
288 assert.NoError(t, result.Error)
289 assert.Equal(t, common.StatusApproved, result.Status)
290
291
292 or = &OrRequirement{
293 requirements: makeRulesResultingIn(common.StatusSkipped),
294 }
295 result = or.Evaluate(ctx, prctx)
296 assert.NoError(t, result.Error)
297 assert.Equal(t, common.StatusSkipped, result.Status)
298
299
300 or = &OrRequirement{
301 requirements: []common.Evaluator{
302 &mockRequirement{
303 result: &common.Result{
304 Status: common.StatusApproved,
305 },
306 },
307 &mockRequirement{
308 result: &common.Result{
309 Error: errors.New("error"),
310 },
311 },
312 },
313 }
314 result = or.Evaluate(ctx, prctx)
315 assert.NoError(t, result.Error)
316 assert.Equal(t, common.StatusApproved, result.Status)
317 }
318
View as plain text