Forked monorepo for Tangled
0

Configure Feed

Select the types of activity you want to include in your feed.

core / spindle / models / clone_test.go
11 kB 422 lines
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}