forked from
tangled.org/core
Forked monorepo for Tangled
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 },
87 Repo: &tangled.Pipeline_TriggerRepo{
88 Knot: "example.com",
89 Did: "did:plc:user123",
90 Repo: sp("my-repo"),
91 RepoDid: sp("did:plc:boltless"),
92 },
93 }
94
95 step := BuildCloneStep(twf, tr, false)
96
97 allCmds := strings.Join(step.Commands(), " ")
98 if !strings.Contains(allCmds, "pr-sha-789") {
99 t.Error("Commands should contain PR commit SHA")
100 }
101}
102
103func TestBuildCloneStep_SourceRepo(t *testing.T) {
104 twf := tangled.Pipeline_Workflow{
105 Clone: &tangled.Pipeline_CloneOpts{
106 Depth: 1,
107 Skip: false,
108 },
109 }
110 sourceRepoDid := "did:plc:fork"
111 tr := tangled.Pipeline_TriggerMetadata{
112 Kind: string(workflow.TriggerKindPullRequest),
113 PullRequest: &tangled.Pipeline_PullRequestTriggerData{
114 SourceSha: "pr-sha-789",
115 SourceBranch: "feature-branch",
116 TargetBranch: "main",
117 },
118 Repo: &tangled.Pipeline_TriggerRepo{
119 Knot: "fork.example.com",
120 Did: "did:plc:user456",
121 Repo: sp("fork-repo"),
122 RepoDid: &sourceRepoDid,
123 },
124 SourceRepo: &sourceRepoDid,
125 }
126
127 step := BuildCloneStep(twf, tr, false)
128
129 allCmds := strings.Join(step.Commands(), " ")
130 if !strings.Contains(allCmds, "https://fork.example.com/did:plc:fork") {
131 t.Error("Commands should clone from source repo URL")
132 }
133 if strings.Contains(allCmds, "https://target.example.com/did:plc:target") {
134 t.Error("Commands should not clone from target repo URL when sourceRepo is set")
135 }
136}
137
138func TestBuildCloneStep_ManualTrigger(t *testing.T) {
139 twf := tangled.Pipeline_Workflow{
140 Clone: &tangled.Pipeline_CloneOpts{
141 Depth: 1,
142 Skip: false,
143 },
144 }
145 tr := tangled.Pipeline_TriggerMetadata{
146 Kind: string(workflow.TriggerKindManual),
147 Manual: &tangled.Pipeline_ManualTriggerData{
148 Sha: "manualsha456",
149 Inputs: nil,
150 },
151 Repo: &tangled.Pipeline_TriggerRepo{
152 Knot: "example.com",
153 Did: "did:plc:user123",
154 Repo: sp("my-repo"),
155 RepoDid: sp("did:plc:boltless"),
156 },
157 }
158
159 step := BuildCloneStep(twf, tr, false)
160
161 allCmds := strings.Join(step.Commands(), " ")
162 // Should still have basic git commands
163 if !strings.Contains(allCmds, "git init") {
164 t.Error("Commands should contain 'git init'")
165 }
166 if !strings.Contains(allCmds, "git fetch") {
167 t.Error("Commands should contain 'git fetch'")
168 }
169 // Manual triggers now carry an explicit SHA, which the fetch targets
170 if !strings.Contains(allCmds, "manualsha456") {
171 t.Error("Commands should contain the manual trigger SHA")
172 }
173}
174
175func TestBuildCloneStep_SkipFlag(t *testing.T) {
176 twf := tangled.Pipeline_Workflow{
177 Clone: &tangled.Pipeline_CloneOpts{
178 Skip: true,
179 },
180 }
181 tr := tangled.Pipeline_TriggerMetadata{
182 Kind: string(workflow.TriggerKindPush),
183 Push: &tangled.Pipeline_PushTriggerData{
184 NewSha: "abc123",
185 },
186 Repo: &tangled.Pipeline_TriggerRepo{
187 Knot: "example.com",
188 Did: "did:plc:user123",
189 Repo: sp("my-repo"),
190 RepoDid: sp("did:plc:boltless"),
191 },
192 }
193
194 step := BuildCloneStep(twf, tr, false)
195
196 // Empty step when skip is true
197 if step.Name() != "" {
198 t.Error("Expected empty step name when Skip is true")
199 }
200 if len(step.Commands()) != 0 {
201 t.Errorf("Expected no commands when Skip is true, got %d commands", len(step.Commands()))
202 }
203}
204
205func TestBuildCloneStep_DevMode(t *testing.T) {
206 twf := tangled.Pipeline_Workflow{
207 Engine: "nixery",
208 Clone: &tangled.Pipeline_CloneOpts{
209 Depth: 1,
210 Skip: false,
211 },
212 }
213 tr := tangled.Pipeline_TriggerMetadata{
214 Kind: string(workflow.TriggerKindPush),
215 Push: &tangled.Pipeline_PushTriggerData{
216 NewSha: "abc123",
217 },
218 Repo: &tangled.Pipeline_TriggerRepo{
219 Knot: "knot.tngl.boltless.dev",
220 Did: "did:plc:user123",
221 Repo: sp("my-repo"),
222 RepoDid: sp("did:plc:boltless"),
223 },
224 }
225
226 step := BuildCloneStep(twf, tr, true)
227
228 // In dev mode, sslVerify should be disabled
229 allCmds := strings.Join(step.Commands(), " ")
230 if !strings.Contains(allCmds, "git -c http.sslVerify=false fetch") {
231 t.Error("Expected sslVerify to be disabled in dev mode clone commands")
232 }
233}
234
235func TestBuildCloneStep_DepthAndSubmodules(t *testing.T) {
236 twf := tangled.Pipeline_Workflow{
237 Clone: &tangled.Pipeline_CloneOpts{
238 Depth: 10,
239 Submodules: true,
240 Skip: false,
241 },
242 }
243 tr := tangled.Pipeline_TriggerMetadata{
244 Kind: string(workflow.TriggerKindPush),
245 Push: &tangled.Pipeline_PushTriggerData{
246 NewSha: "abc123",
247 },
248 Repo: &tangled.Pipeline_TriggerRepo{
249 Knot: "example.com",
250 Did: "did:plc:user123",
251 Repo: sp("my-repo"),
252 RepoDid: sp("did:plc:boltless"),
253 },
254 }
255
256 step := BuildCloneStep(twf, tr, false)
257
258 allCmds := strings.Join(step.Commands(), " ")
259 if !strings.Contains(allCmds, "--depth=10") {
260 t.Error("Commands should contain '--depth=10'")
261 }
262
263 if !strings.Contains(allCmds, "--recurse-submodules=yes") {
264 t.Error("Commands should contain '--recurse-submodules=yes'")
265 }
266}
267
268func TestBuildCloneStep_DefaultDepth(t *testing.T) {
269 twf := tangled.Pipeline_Workflow{
270 Clone: &tangled.Pipeline_CloneOpts{
271 Depth: 0, // Default should be 1
272 Skip: false,
273 },
274 }
275 tr := tangled.Pipeline_TriggerMetadata{
276 Kind: string(workflow.TriggerKindPush),
277 Push: &tangled.Pipeline_PushTriggerData{
278 NewSha: "abc123",
279 },
280 Repo: &tangled.Pipeline_TriggerRepo{
281 Knot: "example.com",
282 Did: "did:plc:user123",
283 Repo: sp("my-repo"),
284 RepoDid: sp("did:plc:boltless"),
285 },
286 }
287
288 step := BuildCloneStep(twf, tr, false)
289
290 allCmds := strings.Join(step.Commands(), " ")
291 if !strings.Contains(allCmds, "--depth=1") {
292 t.Error("Commands should default to '--depth=1'")
293 }
294}
295
296func TestBuildCloneStep_NilPushData(t *testing.T) {
297 twf := tangled.Pipeline_Workflow{
298 Clone: &tangled.Pipeline_CloneOpts{
299 Depth: 1,
300 Skip: false,
301 },
302 }
303 tr := tangled.Pipeline_TriggerMetadata{
304 Kind: string(workflow.TriggerKindPush),
305 Push: nil, // Nil push data should create error step
306 Repo: &tangled.Pipeline_TriggerRepo{
307 Knot: "example.com",
308 Did: "did:plc:user123",
309 Repo: sp("my-repo"),
310 RepoDid: sp("did:plc:boltless"),
311 },
312 }
313
314 step := BuildCloneStep(twf, tr, false)
315
316 // Should return an error step
317 if !strings.Contains(step.Name(), "error") {
318 t.Error("Expected error in step name when push data is nil")
319 }
320
321 allCmds := strings.Join(step.Commands(), " ")
322 if !strings.Contains(allCmds, "Failed to get clone info") {
323 t.Error("Commands should contain error message")
324 }
325 if !strings.Contains(allCmds, "exit 1") {
326 t.Error("Commands should exit with error")
327 }
328}
329
330func TestBuildCloneStep_NilPRData(t *testing.T) {
331 twf := tangled.Pipeline_Workflow{
332 Clone: &tangled.Pipeline_CloneOpts{
333 Depth: 1,
334 Skip: false,
335 },
336 }
337 tr := tangled.Pipeline_TriggerMetadata{
338 Kind: string(workflow.TriggerKindPullRequest),
339 PullRequest: nil, // Nil PR data should create error step
340 Repo: &tangled.Pipeline_TriggerRepo{
341 Knot: "example.com",
342 Did: "did:plc:user123",
343 Repo: sp("my-repo"),
344 RepoDid: sp("did:plc:boltless"),
345 },
346 }
347
348 step := BuildCloneStep(twf, tr, false)
349
350 // Should return an error step
351 if !strings.Contains(step.Name(), "error") {
352 t.Error("Expected error in step name when pull request data is nil")
353 }
354
355 allCmds := strings.Join(step.Commands(), " ")
356 if !strings.Contains(allCmds, "Failed to get clone info") {
357 t.Error("Commands should contain error message")
358 }
359}
360
361func TestBuildCloneStep_UnknownTriggerKind(t *testing.T) {
362 twf := tangled.Pipeline_Workflow{
363 Clone: &tangled.Pipeline_CloneOpts{
364 Depth: 1,
365 Skip: false,
366 },
367 }
368 tr := tangled.Pipeline_TriggerMetadata{
369 Kind: "unknown_trigger",
370 Repo: &tangled.Pipeline_TriggerRepo{
371 Knot: "example.com",
372 Did: "did:plc:user123",
373 Repo: sp("my-repo"),
374 RepoDid: sp("did:plc:boltless"),
375 },
376 }
377
378 step := BuildCloneStep(twf, tr, false)
379
380 // Should return an error step
381 if !strings.Contains(step.Name(), "error") {
382 t.Error("Expected error in step name for unknown trigger kind")
383 }
384
385 allCmds := strings.Join(step.Commands(), " ")
386 if !strings.Contains(allCmds, "unknown trigger kind") {
387 t.Error("Commands should contain error message about unknown trigger kind")
388 }
389}
390
391func TestBuildCloneStep_NilCloneOpts(t *testing.T) {
392 twf := tangled.Pipeline_Workflow{
393 Clone: nil, // Nil clone options should use defaults
394 }
395 tr := tangled.Pipeline_TriggerMetadata{
396 Kind: string(workflow.TriggerKindPush),
397 Push: &tangled.Pipeline_PushTriggerData{
398 NewSha: "abc123",
399 },
400 Repo: &tangled.Pipeline_TriggerRepo{
401 Knot: "example.com",
402 Did: "did:plc:user123",
403 Repo: sp("my-repo"),
404 RepoDid: sp("did:plc:boltless"),
405 },
406 }
407
408 step := BuildCloneStep(twf, tr, false)
409
410 // Should still work with default options
411 if step.Kind() != StepKindSystem {
412 t.Errorf("Expected StepKindSystem, got %v", step.Kind())
413 }
414
415 allCmds := strings.Join(step.Commands(), " ")
416 if !strings.Contains(allCmds, "--depth=1") {
417 t.Error("Commands should default to '--depth=1' when Clone is nil")
418 }
419 if !strings.Contains(allCmds, "git init") {
420 t.Error("Commands should contain 'git init'")
421 }
422}