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 Sha: "manualsha456",
115 Inputs: nil,
116 },
117 Repo: &tangled.Pipeline_TriggerRepo{
118 Knot: "example.com",
119 Did: "did:plc:user123",
120 Repo: sp("my-repo"),
121 RepoDid: sp("did:plc:boltless"),
122 },
123 }
124
125 step := BuildCloneStep(twf, tr, false)
126
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 // Manual triggers now carry an explicit SHA, which the fetch targets
136 if !strings.Contains(allCmds, "manualsha456") {
137 t.Error("Commands should contain the manual trigger SHA")
138 }
139}
140
141func TestBuildCloneStep_SkipFlag(t *testing.T) {
142 twf := tangled.Pipeline_Workflow{
143 Clone: &tangled.Pipeline_CloneOpts{
144 Skip: true,
145 },
146 }
147 tr := tangled.Pipeline_TriggerMetadata{
148 Kind: string(workflow.TriggerKindPush),
149 Push: &tangled.Pipeline_PushTriggerData{
150 NewSha: "abc123",
151 },
152 Repo: &tangled.Pipeline_TriggerRepo{
153 Knot: "example.com",
154 Did: "did:plc:user123",
155 Repo: sp("my-repo"),
156 RepoDid: sp("did:plc:boltless"),
157 },
158 }
159
160 step := BuildCloneStep(twf, tr, false)
161
162 // Empty step when skip is true
163 if step.Name() != "" {
164 t.Error("Expected empty step name when Skip is true")
165 }
166 if len(step.Commands()) != 0 {
167 t.Errorf("Expected no commands when Skip is true, got %d commands", len(step.Commands()))
168 }
169}
170
171func TestBuildCloneStep_DevMode(t *testing.T) {
172 twf := tangled.Pipeline_Workflow{
173 Engine: "nixery",
174 Clone: &tangled.Pipeline_CloneOpts{
175 Depth: 1,
176 Skip: false,
177 },
178 }
179 tr := tangled.Pipeline_TriggerMetadata{
180 Kind: string(workflow.TriggerKindPush),
181 Push: &tangled.Pipeline_PushTriggerData{
182 NewSha: "abc123",
183 },
184 Repo: &tangled.Pipeline_TriggerRepo{
185 Knot: "knot.tngl.boltless.dev",
186 Did: "did:plc:user123",
187 Repo: sp("my-repo"),
188 RepoDid: sp("did:plc:boltless"),
189 },
190 }
191
192 step := BuildCloneStep(twf, tr, true)
193
194 // In dev mode, sslVerify should be disabled
195 allCmds := strings.Join(step.Commands(), " ")
196 if !strings.Contains(allCmds, "git -c http.sslVerify=false fetch") {
197 t.Error("Expected sslVerify to be disabled in dev mode clone commands")
198 }
199}
200
201func TestBuildCloneStep_DepthAndSubmodules(t *testing.T) {
202 twf := tangled.Pipeline_Workflow{
203 Clone: &tangled.Pipeline_CloneOpts{
204 Depth: 10,
205 Submodules: true,
206 Skip: false,
207 },
208 }
209 tr := tangled.Pipeline_TriggerMetadata{
210 Kind: string(workflow.TriggerKindPush),
211 Push: &tangled.Pipeline_PushTriggerData{
212 NewSha: "abc123",
213 },
214 Repo: &tangled.Pipeline_TriggerRepo{
215 Knot: "example.com",
216 Did: "did:plc:user123",
217 Repo: sp("my-repo"),
218 RepoDid: sp("did:plc:boltless"),
219 },
220 }
221
222 step := BuildCloneStep(twf, tr, false)
223
224 allCmds := strings.Join(step.Commands(), " ")
225 if !strings.Contains(allCmds, "--depth=10") {
226 t.Error("Commands should contain '--depth=10'")
227 }
228
229 if !strings.Contains(allCmds, "--recurse-submodules=yes") {
230 t.Error("Commands should contain '--recurse-submodules=yes'")
231 }
232}
233
234func TestBuildCloneStep_DefaultDepth(t *testing.T) {
235 twf := tangled.Pipeline_Workflow{
236 Clone: &tangled.Pipeline_CloneOpts{
237 Depth: 0, // Default should be 1
238 Skip: false,
239 },
240 }
241 tr := tangled.Pipeline_TriggerMetadata{
242 Kind: string(workflow.TriggerKindPush),
243 Push: &tangled.Pipeline_PushTriggerData{
244 NewSha: "abc123",
245 },
246 Repo: &tangled.Pipeline_TriggerRepo{
247 Knot: "example.com",
248 Did: "did:plc:user123",
249 Repo: sp("my-repo"),
250 RepoDid: sp("did:plc:boltless"),
251 },
252 }
253
254 step := BuildCloneStep(twf, tr, false)
255
256 allCmds := strings.Join(step.Commands(), " ")
257 if !strings.Contains(allCmds, "--depth=1") {
258 t.Error("Commands should default to '--depth=1'")
259 }
260}
261
262func TestBuildCloneStep_NilPushData(t *testing.T) {
263 twf := tangled.Pipeline_Workflow{
264 Clone: &tangled.Pipeline_CloneOpts{
265 Depth: 1,
266 Skip: false,
267 },
268 }
269 tr := tangled.Pipeline_TriggerMetadata{
270 Kind: string(workflow.TriggerKindPush),
271 Push: nil, // Nil push data should create error step
272 Repo: &tangled.Pipeline_TriggerRepo{
273 Knot: "example.com",
274 Did: "did:plc:user123",
275 Repo: sp("my-repo"),
276 RepoDid: sp("did:plc:boltless"),
277 },
278 }
279
280 step := BuildCloneStep(twf, tr, false)
281
282 // Should return an error step
283 if !strings.Contains(step.Name(), "error") {
284 t.Error("Expected error in step name when push data is nil")
285 }
286
287 allCmds := strings.Join(step.Commands(), " ")
288 if !strings.Contains(allCmds, "Failed to get clone info") {
289 t.Error("Commands should contain error message")
290 }
291 if !strings.Contains(allCmds, "exit 1") {
292 t.Error("Commands should exit with error")
293 }
294}
295
296func TestBuildCloneStep_NilPRData(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.TriggerKindPullRequest),
305 PullRequest: nil, // Nil PR 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 pull request 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}
326
327func TestBuildCloneStep_UnknownTriggerKind(t *testing.T) {
328 twf := tangled.Pipeline_Workflow{
329 Clone: &tangled.Pipeline_CloneOpts{
330 Depth: 1,
331 Skip: false,
332 },
333 }
334 tr := tangled.Pipeline_TriggerMetadata{
335 Kind: "unknown_trigger",
336 Repo: &tangled.Pipeline_TriggerRepo{
337 Knot: "example.com",
338 Did: "did:plc:user123",
339 Repo: sp("my-repo"),
340 RepoDid: sp("did:plc:boltless"),
341 },
342 }
343
344 step := BuildCloneStep(twf, tr, false)
345
346 // Should return an error step
347 if !strings.Contains(step.Name(), "error") {
348 t.Error("Expected error in step name for unknown trigger kind")
349 }
350
351 allCmds := strings.Join(step.Commands(), " ")
352 if !strings.Contains(allCmds, "unknown trigger kind") {
353 t.Error("Commands should contain error message about unknown trigger kind")
354 }
355}
356
357func TestBuildCloneStep_NilCloneOpts(t *testing.T) {
358 twf := tangled.Pipeline_Workflow{
359 Clone: nil, // Nil clone options should use defaults
360 }
361 tr := tangled.Pipeline_TriggerMetadata{
362 Kind: string(workflow.TriggerKindPush),
363 Push: &tangled.Pipeline_PushTriggerData{
364 NewSha: "abc123",
365 },
366 Repo: &tangled.Pipeline_TriggerRepo{
367 Knot: "example.com",
368 Did: "did:plc:user123",
369 Repo: sp("my-repo"),
370 RepoDid: sp("did:plc:boltless"),
371 },
372 }
373
374 step := BuildCloneStep(twf, tr, false)
375
376 // Should still work with default options
377 if step.Kind() != StepKindSystem {
378 t.Errorf("Expected StepKindSystem, got %v", step.Kind())
379 }
380
381 allCmds := strings.Join(step.Commands(), " ")
382 if !strings.Contains(allCmds, "--depth=1") {
383 t.Error("Commands should default to '--depth=1' when Clone is nil")
384 }
385 if !strings.Contains(allCmds, "git init") {
386 t.Error("Commands should contain 'git init'")
387 }
388}