Monorepo for Tangled
tangled.org
1package models
2
3import (
4 "strings"
5 "testing"
6
7 "tangled.org/core/api/tangled"
8 "tangled.org/core/workflow"
9)
10
11func sp(s string) *string { return &s }
12
13func TestBuildCloneStep_PushTrigger(t *testing.T) {
14 twf := tangled.Pipeline_Workflow{
15 Clone: &tangled.Pipeline_CloneOpts{
16 Depth: 1,
17 Submodules: false,
18 Skip: false,
19 },
20 }
21 tr := tangled.Pipeline_TriggerMetadata{
22 Kind: string(workflow.TriggerKindPush),
23 Push: &tangled.Pipeline_PushTriggerData{
24 NewSha: "abc123",
25 OldSha: "def456",
26 Ref: "refs/heads/main",
27 },
28 Repo: &tangled.Pipeline_TriggerRepo{
29 Knot: "example.com",
30 Did: "did:plc:user123",
31 Repo: sp("my-repo"),
32 RepoDid: sp("did:plc:boltless"),
33 },
34 }
35
36 step := BuildCloneStep(twf, tr, false)
37
38 if step.Kind() != StepKindSystem {
39 t.Errorf("Expected StepKindSystem, got %v", step.Kind())
40 }
41
42 if step.Name() != "Clone repository into workspace" {
43 t.Errorf("Expected 'Clone repository into workspace', got '%s'", step.Name())
44 }
45
46 commands := step.Commands()
47 if len(commands) != 4 {
48 t.Errorf("Expected 4 commands, got %d", len(commands))
49 }
50
51 // Verify commands contain expected git operations
52 allCmds := strings.Join(commands, " ")
53 if !strings.Contains(allCmds, "git init") {
54 t.Error("Commands should contain 'git init'")
55 }
56 if !strings.Contains(allCmds, "git remote add origin") {
57 t.Error("Commands should contain 'git remote add origin'")
58 }
59 if !strings.Contains(allCmds, "git fetch") {
60 t.Error("Commands should contain 'git fetch'")
61 }
62 if !strings.Contains(allCmds, "abc123") {
63 t.Error("Commands should contain commit SHA")
64 }
65 if !strings.Contains(allCmds, "git checkout FETCH_HEAD") {
66 t.Error("Commands should contain 'git checkout FETCH_HEAD'")
67 }
68 if !strings.Contains(allCmds, "https://example.com/did:plc:boltless") {
69 t.Error("Commands should contain expected repo URL")
70 }
71}
72
73func TestBuildCloneStep_PullRequestTrigger(t *testing.T) {
74 twf := tangled.Pipeline_Workflow{
75 Clone: &tangled.Pipeline_CloneOpts{
76 Depth: 1,
77 Skip: false,
78 },
79 }
80 tr := tangled.Pipeline_TriggerMetadata{
81 Kind: string(workflow.TriggerKindPullRequest),
82 PullRequest: &tangled.Pipeline_PullRequestTriggerData{
83 SourceSha: "pr-sha-789",
84 SourceBranch: "feature-branch",
85 TargetBranch: "main",
86 Action: "opened",
87 },
88 Repo: &tangled.Pipeline_TriggerRepo{
89 Knot: "example.com",
90 Did: "did:plc:user123",
91 Repo: sp("my-repo"),
92 RepoDid: sp("did:plc:boltless"),
93 },
94 }
95
96 step := BuildCloneStep(twf, tr, false)
97
98 allCmds := strings.Join(step.Commands(), " ")
99 if !strings.Contains(allCmds, "pr-sha-789") {
100 t.Error("Commands should contain PR commit SHA")
101 }
102}
103
104func TestBuildCloneStep_ManualTrigger(t *testing.T) {
105 twf := tangled.Pipeline_Workflow{
106 Clone: &tangled.Pipeline_CloneOpts{
107 Depth: 1,
108 Skip: false,
109 },
110 }
111 tr := tangled.Pipeline_TriggerMetadata{
112 Kind: string(workflow.TriggerKindManual),
113 Manual: &tangled.Pipeline_ManualTriggerData{
114 Inputs: nil,
115 },
116 Repo: &tangled.Pipeline_TriggerRepo{
117 Knot: "example.com",
118 Did: "did:plc:user123",
119 Repo: sp("my-repo"),
120 RepoDid: sp("did:plc:boltless"),
121 },
122 }
123
124 step := BuildCloneStep(twf, tr, false)
125
126 // Manual triggers don't have a SHA yet (TODO), so git fetch won't include a SHA
127 allCmds := strings.Join(step.Commands(), " ")
128 // Should still have basic git commands
129 if !strings.Contains(allCmds, "git init") {
130 t.Error("Commands should contain 'git init'")
131 }
132 if !strings.Contains(allCmds, "git fetch") {
133 t.Error("Commands should contain 'git fetch'")
134 }
135}
136
137func TestBuildCloneStep_SkipFlag(t *testing.T) {
138 twf := tangled.Pipeline_Workflow{
139 Clone: &tangled.Pipeline_CloneOpts{
140 Skip: true,
141 },
142 }
143 tr := tangled.Pipeline_TriggerMetadata{
144 Kind: string(workflow.TriggerKindPush),
145 Push: &tangled.Pipeline_PushTriggerData{
146 NewSha: "abc123",
147 },
148 Repo: &tangled.Pipeline_TriggerRepo{
149 Knot: "example.com",
150 Did: "did:plc:user123",
151 Repo: sp("my-repo"),
152 RepoDid: sp("did:plc:boltless"),
153 },
154 }
155
156 step := BuildCloneStep(twf, tr, false)
157
158 // Empty step when skip is true
159 if step.Name() != "" {
160 t.Error("Expected empty step name when Skip is true")
161 }
162 if len(step.Commands()) != 0 {
163 t.Errorf("Expected no commands when Skip is true, got %d commands", len(step.Commands()))
164 }
165}
166
167func TestBuildCloneStep_DevMode(t *testing.T) {
168 twf := tangled.Pipeline_Workflow{
169 Engine: "nixery",
170 Clone: &tangled.Pipeline_CloneOpts{
171 Depth: 1,
172 Skip: false,
173 },
174 }
175 tr := tangled.Pipeline_TriggerMetadata{
176 Kind: string(workflow.TriggerKindPush),
177 Push: &tangled.Pipeline_PushTriggerData{
178 NewSha: "abc123",
179 },
180 Repo: &tangled.Pipeline_TriggerRepo{
181 Knot: "knot.tngl.boltless.dev",
182 Did: "did:plc:user123",
183 Repo: sp("my-repo"),
184 RepoDid: sp("did:plc:boltless"),
185 },
186 }
187
188 step := BuildCloneStep(twf, tr, true)
189
190 // In dev mode, sslVerify should be disabled
191 allCmds := strings.Join(step.Commands(), " ")
192 if !strings.Contains(allCmds, "git -c http.sslVerify=false fetch") {
193 t.Error("Expected sslVerify to be disabled in dev mode clone commands")
194 }
195}
196
197func TestBuildCloneStep_DepthAndSubmodules(t *testing.T) {
198 twf := tangled.Pipeline_Workflow{
199 Clone: &tangled.Pipeline_CloneOpts{
200 Depth: 10,
201 Submodules: true,
202 Skip: false,
203 },
204 }
205 tr := tangled.Pipeline_TriggerMetadata{
206 Kind: string(workflow.TriggerKindPush),
207 Push: &tangled.Pipeline_PushTriggerData{
208 NewSha: "abc123",
209 },
210 Repo: &tangled.Pipeline_TriggerRepo{
211 Knot: "example.com",
212 Did: "did:plc:user123",
213 Repo: sp("my-repo"),
214 RepoDid: sp("did:plc:boltless"),
215 },
216 }
217
218 step := BuildCloneStep(twf, tr, false)
219
220 allCmds := strings.Join(step.Commands(), " ")
221 if !strings.Contains(allCmds, "--depth=10") {
222 t.Error("Commands should contain '--depth=10'")
223 }
224
225 if !strings.Contains(allCmds, "--recurse-submodules=yes") {
226 t.Error("Commands should contain '--recurse-submodules=yes'")
227 }
228}
229
230func TestBuildCloneStep_DefaultDepth(t *testing.T) {
231 twf := tangled.Pipeline_Workflow{
232 Clone: &tangled.Pipeline_CloneOpts{
233 Depth: 0, // Default should be 1
234 Skip: false,
235 },
236 }
237 tr := tangled.Pipeline_TriggerMetadata{
238 Kind: string(workflow.TriggerKindPush),
239 Push: &tangled.Pipeline_PushTriggerData{
240 NewSha: "abc123",
241 },
242 Repo: &tangled.Pipeline_TriggerRepo{
243 Knot: "example.com",
244 Did: "did:plc:user123",
245 Repo: sp("my-repo"),
246 RepoDid: sp("did:plc:boltless"),
247 },
248 }
249
250 step := BuildCloneStep(twf, tr, false)
251
252 allCmds := strings.Join(step.Commands(), " ")
253 if !strings.Contains(allCmds, "--depth=1") {
254 t.Error("Commands should default to '--depth=1'")
255 }
256}
257
258func TestBuildCloneStep_NilPushData(t *testing.T) {
259 twf := tangled.Pipeline_Workflow{
260 Clone: &tangled.Pipeline_CloneOpts{
261 Depth: 1,
262 Skip: false,
263 },
264 }
265 tr := tangled.Pipeline_TriggerMetadata{
266 Kind: string(workflow.TriggerKindPush),
267 Push: nil, // Nil push data should create error step
268 Repo: &tangled.Pipeline_TriggerRepo{
269 Knot: "example.com",
270 Did: "did:plc:user123",
271 Repo: sp("my-repo"),
272 RepoDid: sp("did:plc:boltless"),
273 },
274 }
275
276 step := BuildCloneStep(twf, tr, false)
277
278 // Should return an error step
279 if !strings.Contains(step.Name(), "error") {
280 t.Error("Expected error in step name when push data is nil")
281 }
282
283 allCmds := strings.Join(step.Commands(), " ")
284 if !strings.Contains(allCmds, "Failed to get clone info") {
285 t.Error("Commands should contain error message")
286 }
287 if !strings.Contains(allCmds, "exit 1") {
288 t.Error("Commands should exit with error")
289 }
290}
291
292func TestBuildCloneStep_NilPRData(t *testing.T) {
293 twf := tangled.Pipeline_Workflow{
294 Clone: &tangled.Pipeline_CloneOpts{
295 Depth: 1,
296 Skip: false,
297 },
298 }
299 tr := tangled.Pipeline_TriggerMetadata{
300 Kind: string(workflow.TriggerKindPullRequest),
301 PullRequest: nil, // Nil PR data should create error step
302 Repo: &tangled.Pipeline_TriggerRepo{
303 Knot: "example.com",
304 Did: "did:plc:user123",
305 Repo: sp("my-repo"),
306 RepoDid: sp("did:plc:boltless"),
307 },
308 }
309
310 step := BuildCloneStep(twf, tr, false)
311
312 // Should return an error step
313 if !strings.Contains(step.Name(), "error") {
314 t.Error("Expected error in step name when pull request data is nil")
315 }
316
317 allCmds := strings.Join(step.Commands(), " ")
318 if !strings.Contains(allCmds, "Failed to get clone info") {
319 t.Error("Commands should contain error message")
320 }
321}
322
323func TestBuildCloneStep_UnknownTriggerKind(t *testing.T) {
324 twf := tangled.Pipeline_Workflow{
325 Clone: &tangled.Pipeline_CloneOpts{
326 Depth: 1,
327 Skip: false,
328 },
329 }
330 tr := tangled.Pipeline_TriggerMetadata{
331 Kind: "unknown_trigger",
332 Repo: &tangled.Pipeline_TriggerRepo{
333 Knot: "example.com",
334 Did: "did:plc:user123",
335 Repo: sp("my-repo"),
336 RepoDid: sp("did:plc:boltless"),
337 },
338 }
339
340 step := BuildCloneStep(twf, tr, false)
341
342 // Should return an error step
343 if !strings.Contains(step.Name(), "error") {
344 t.Error("Expected error in step name for unknown trigger kind")
345 }
346
347 allCmds := strings.Join(step.Commands(), " ")
348 if !strings.Contains(allCmds, "unknown trigger kind") {
349 t.Error("Commands should contain error message about unknown trigger kind")
350 }
351}
352
353func TestBuildCloneStep_NilCloneOpts(t *testing.T) {
354 twf := tangled.Pipeline_Workflow{
355 Clone: nil, // Nil clone options should use defaults
356 }
357 tr := tangled.Pipeline_TriggerMetadata{
358 Kind: string(workflow.TriggerKindPush),
359 Push: &tangled.Pipeline_PushTriggerData{
360 NewSha: "abc123",
361 },
362 Repo: &tangled.Pipeline_TriggerRepo{
363 Knot: "example.com",
364 Did: "did:plc:user123",
365 Repo: sp("my-repo"),
366 RepoDid: sp("did:plc:boltless"),
367 },
368 }
369
370 step := BuildCloneStep(twf, tr, false)
371
372 // Should still work with default options
373 if step.Kind() != StepKindSystem {
374 t.Errorf("Expected StepKindSystem, got %v", step.Kind())
375 }
376
377 allCmds := strings.Join(step.Commands(), " ")
378 if !strings.Contains(allCmds, "--depth=1") {
379 t.Error("Commands should default to '--depth=1' when Clone is nil")
380 }
381 if !strings.Contains(allCmds, "git init") {
382 t.Error("Commands should contain 'git init'")
383 }
384}