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