This repository has no description
1package workflow
2
3import (
4 "encoding/json"
5 "strings"
6 "testing"
7
8 "github.com/stretchr/testify/assert"
9 "tangled.org/core/api/tangled"
10)
11
12var trigger = tangled.Pipeline_TriggerMetadata{
13 Kind: string(TriggerKindPush),
14 Push: &tangled.Pipeline_PushTriggerData{
15 Ref: "refs/heads/main",
16 OldSha: strings.Repeat("0", 40),
17 NewSha: strings.Repeat("f", 40),
18 },
19}
20
21var when = []Constraint{
22 {
23 Event: []string{"push"},
24 Branch: []string{"main"},
25 },
26}
27
28func TestCompileWorkflow_MatchingWorkflowWithSteps(t *testing.T) {
29 wf := Workflow{
30 Name: ".tangled/workflows/test.yml",
31 Engine: "nixery",
32 When: when,
33 CloneOpts: CloneOpts{}, // default true
34 }
35
36 c := Compiler{Trigger: trigger}
37 cp := c.Compile([]Workflow{wf})
38
39 assert.Len(t, cp.Workflows, 1)
40 assert.Equal(t, wf.Name, cp.Workflows[0].Name)
41 assert.False(t, cp.Workflows[0].Clone.Skip)
42 assert.False(t, c.Diagnostics.IsErr())
43}
44
45func TestCompileWorkflow_RunsOnLabelsPersistWithoutFanout(t *testing.T) {
46 wf := Workflow{
47 Name: ".tangled/workflows/arm64.yml",
48 Engine: "microvm",
49 When: when,
50 RunsOn: []string{"linux/arm64", "kvm"},
51 }
52
53 c := Compiler{Trigger: trigger}
54 cp := c.Compile([]Workflow{wf})
55
56 assert.Len(t, cp.Workflows, 1)
57 assert.Equal(t, []string{"linux/arm64", "kvm"}, cp.Workflows[0].RunsOn)
58
59 raw, err := json.Marshal(cp.Workflows[0])
60 assert.NoError(t, err)
61
62 var persisted map[string]any
63 assert.NoError(t, json.Unmarshal(raw, &persisted))
64 assert.Equal(t, []any{"linux/arm64", "kvm"}, persisted["runsOn"])
65}
66
67func TestCompileWorkflow_LegacyWorkflowOmitsRunsOn(t *testing.T) {
68 wf := Workflow{
69 Name: ".tangled/workflows/legacy.yml",
70 Engine: "microvm",
71 When: when,
72 }
73
74 c := Compiler{Trigger: trigger}
75 cp := c.Compile([]Workflow{wf})
76
77 assert.Len(t, cp.Workflows, 1)
78 assert.Empty(t, cp.Workflows[0].RunsOn)
79
80 raw, err := json.Marshal(cp.Workflows[0])
81 assert.NoError(t, err)
82
83 var persisted map[string]any
84 assert.NoError(t, json.Unmarshal(raw, &persisted))
85 _, ok := persisted["runsOn"]
86 assert.False(t, ok, "legacy workflow JSON should not gain runsOn")
87}
88
89func TestCompileWorkflow_TriggerMismatch(t *testing.T) {
90 wf := Workflow{
91 Name: ".tangled/workflows/mismatch.yml",
92 Engine: "nixery",
93 When: []Constraint{
94 {
95 Event: []string{"push"},
96 Branch: []string{"master"}, // different branch
97 },
98 },
99 }
100
101 c := Compiler{Trigger: trigger}
102 cp := c.Compile([]Workflow{wf})
103
104 assert.Len(t, cp.Workflows, 0)
105 assert.Len(t, c.Diagnostics.Warnings, 1)
106 assert.Equal(t, WorkflowSkipped, c.Diagnostics.Warnings[0].Type)
107}
108
109func TestCompileWorkflow_CloneFalseWithShallowTrue(t *testing.T) {
110 wf := Workflow{
111 Name: ".tangled/workflows/clone_skip.yml",
112 Engine: "nixery",
113 When: when,
114 CloneOpts: CloneOpts{
115 Skip: true,
116 Depth: 1,
117 }, // false
118 }
119
120 c := Compiler{Trigger: trigger}
121 cp := c.Compile([]Workflow{wf})
122
123 assert.Len(t, cp.Workflows, 1)
124 assert.True(t, cp.Workflows[0].Clone.Skip)
125 assert.Len(t, c.Diagnostics.Warnings, 1)
126 assert.Equal(t, InvalidConfiguration, c.Diagnostics.Warnings[0].Type)
127}
128
129func TestCompileWorkflow_MissingEngine(t *testing.T) {
130 wf := Workflow{
131 Name: ".tangled/workflows/missing_engine.yml",
132 When: when,
133 Engine: "",
134 }
135
136 c := Compiler{Trigger: trigger}
137 cp := c.Compile([]Workflow{wf})
138
139 assert.Len(t, cp.Workflows, 0)
140 assert.Len(t, c.Diagnostics.Errors, 1)
141 assert.Equal(t, MissingEngine, c.Diagnostics.Errors[0].Error)
142}
143
144func TestCompileWorkflow_ChangedFilesMatchesPaths(t *testing.T) {
145 wf := Workflow{
146 Name: ".tangled/workflows/test.yml",
147 Engine: "nixery",
148 When: []Constraint{
149 {
150 Event: []string{"push"},
151 Branch: []string{"main"},
152 Paths: []string{"src/**"},
153 },
154 },
155 }
156
157 c := Compiler{
158 Trigger: trigger,
159 ChangedFiles: []string{"src/main.go", "src/util.go"},
160 }
161 cp := c.Compile([]Workflow{wf})
162
163 assert.Len(t, cp.Workflows, 1)
164 assert.Equal(t, wf.Name, cp.Workflows[0].Name)
165 assert.False(t, c.Diagnostics.IsErr())
166}
167
168func TestCompileWorkflow_ChangedFilesNoMatch(t *testing.T) {
169 wf := Workflow{
170 Name: ".tangled/workflows/test.yml",
171 Engine: "nixery",
172 When: []Constraint{
173 {
174 Event: []string{"push"},
175 Branch: []string{"main"},
176 Paths: []string{"src/**"},
177 },
178 },
179 }
180
181 c := Compiler{
182 Trigger: trigger,
183 ChangedFiles: []string{"docs/guide.md", "README.md"},
184 }
185 cp := c.Compile([]Workflow{wf})
186
187 assert.Len(t, cp.Workflows, 0)
188 assert.Len(t, c.Diagnostics.Warnings, 1)
189 assert.Equal(t, WorkflowSkipped, c.Diagnostics.Warnings[0].Type)
190}
191
192func TestCompileWorkflow_NoPaths_ChangedFilesIgnored(t *testing.T) {
193 wf := Workflow{
194 Name: ".tangled/workflows/test.yml",
195 Engine: "nixery",
196 When: when, // no Paths constraint
197 }
198
199 c := Compiler{
200 Trigger: trigger,
201 ChangedFiles: []string{"docs/guide.md"},
202 }
203 cp := c.Compile([]Workflow{wf})
204
205 assert.Len(t, cp.Workflows, 1)
206 assert.False(t, c.Diagnostics.IsErr())
207}
208
209func TestCompileWorkflow_MultipleBranchAndTag(t *testing.T) {
210 wf := Workflow{
211 Name: ".tangled/workflows/branch_and_tag.yml",
212 When: []Constraint{
213 {
214 Event: []string{"push"},
215 Branch: []string{"main", "develop"},
216 Tag: []string{"v*"},
217 },
218 },
219 Engine: "nixery",
220 }
221
222 tests := []struct {
223 name string
224 trigger tangled.Pipeline_TriggerMetadata
225 shouldMatch bool
226 expectedCount int
227 }{
228 {
229 name: "matches main branch",
230 trigger: tangled.Pipeline_TriggerMetadata{
231 Kind: string(TriggerKindPush),
232 Push: &tangled.Pipeline_PushTriggerData{
233 Ref: "refs/heads/main",
234 OldSha: strings.Repeat("0", 40),
235 NewSha: strings.Repeat("f", 40),
236 },
237 },
238 shouldMatch: true,
239 expectedCount: 1,
240 },
241 {
242 name: "matches develop branch",
243 trigger: tangled.Pipeline_TriggerMetadata{
244 Kind: string(TriggerKindPush),
245 Push: &tangled.Pipeline_PushTriggerData{
246 Ref: "refs/heads/develop",
247 OldSha: strings.Repeat("0", 40),
248 NewSha: strings.Repeat("f", 40),
249 },
250 },
251 shouldMatch: true,
252 expectedCount: 1,
253 },
254 {
255 name: "matches v* tag pattern",
256 trigger: tangled.Pipeline_TriggerMetadata{
257 Kind: string(TriggerKindPush),
258 Push: &tangled.Pipeline_PushTriggerData{
259 Ref: "refs/tags/v1.0.0",
260 OldSha: strings.Repeat("0", 40),
261 NewSha: strings.Repeat("f", 40),
262 },
263 },
264 shouldMatch: true,
265 expectedCount: 1,
266 },
267 {
268 name: "matches v* tag pattern with different version",
269 trigger: tangled.Pipeline_TriggerMetadata{
270 Kind: string(TriggerKindPush),
271 Push: &tangled.Pipeline_PushTriggerData{
272 Ref: "refs/tags/v2.5.3",
273 OldSha: strings.Repeat("0", 40),
274 NewSha: strings.Repeat("f", 40),
275 },
276 },
277 shouldMatch: true,
278 expectedCount: 1,
279 },
280 {
281 name: "does not match master branch",
282 trigger: tangled.Pipeline_TriggerMetadata{
283 Kind: string(TriggerKindPush),
284 Push: &tangled.Pipeline_PushTriggerData{
285 Ref: "refs/heads/master",
286 OldSha: strings.Repeat("0", 40),
287 NewSha: strings.Repeat("f", 40),
288 },
289 },
290 shouldMatch: false,
291 expectedCount: 0,
292 },
293 {
294 name: "does not match non-v tag",
295 trigger: tangled.Pipeline_TriggerMetadata{
296 Kind: string(TriggerKindPush),
297 Push: &tangled.Pipeline_PushTriggerData{
298 Ref: "refs/tags/release-1.0",
299 OldSha: strings.Repeat("0", 40),
300 NewSha: strings.Repeat("f", 40),
301 },
302 },
303 shouldMatch: false,
304 expectedCount: 0,
305 },
306 {
307 name: "does not match feature branch",
308 trigger: tangled.Pipeline_TriggerMetadata{
309 Kind: string(TriggerKindPush),
310 Push: &tangled.Pipeline_PushTriggerData{
311 Ref: "refs/heads/feature/new-feature",
312 OldSha: strings.Repeat("0", 40),
313 NewSha: strings.Repeat("f", 40),
314 },
315 },
316 shouldMatch: false,
317 expectedCount: 0,
318 },
319 }
320
321 for _, tt := range tests {
322 t.Run(tt.name, func(t *testing.T) {
323 c := Compiler{Trigger: tt.trigger}
324 cp := c.Compile([]Workflow{wf})
325
326 assert.Len(t, cp.Workflows, tt.expectedCount)
327 if tt.shouldMatch {
328 assert.Equal(t, wf.Name, cp.Workflows[0].Name)
329 }
330 })
331 }
332}