Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
- Add --connect-timeout 10 and --max-time 15 to all verify step curl calls to prevent hanging on registry health checks - Fix cli template: depends_on [deps] -> [preflight] for consistency - Add cross-reference comment to service template about verify logic being replicated across all 5 component templates - Document component CI step rules in composable-monorepo.md - Compile regexes at package level instead of per-call in component_updates.go - Add component_updates_test.go Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
278 lines
6.9 KiB
Go
278 lines
6.9 KiB
Go
package service
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestExtractDeployStepName(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
stepYaml string
|
|
want string
|
|
}{
|
|
{
|
|
name: "service template with deploy step",
|
|
stepYaml: `build-studio-api:
|
|
depends_on: [preflight]
|
|
image: woodpeckerci/plugin-kaniko
|
|
|
|
deploy-studio-api:
|
|
depends_on: [verify-studio-api]
|
|
image: bitnami/kubectl:latest`,
|
|
want: "deploy-studio-api",
|
|
},
|
|
{
|
|
name: "worker template with deploy step",
|
|
stepYaml: `build-processor:
|
|
depends_on: [preflight]
|
|
|
|
deploy-processor:
|
|
image: bitnami/kubectl:latest`,
|
|
want: "deploy-processor",
|
|
},
|
|
{
|
|
name: "CLI template without deploy step",
|
|
stepYaml: `build-mytool:
|
|
depends_on: [deps]
|
|
image: golang:1.25-alpine
|
|
commands:
|
|
- go build ./cmd`,
|
|
want: "",
|
|
},
|
|
{
|
|
name: "empty input",
|
|
stepYaml: "",
|
|
want: "",
|
|
},
|
|
{
|
|
name: "hyphenated component name",
|
|
stepYaml: `deploy-my-cool-service:
|
|
image: bitnami/kubectl:latest`,
|
|
want: "deploy-my-cool-service",
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
got := extractDeployStepName(tc.stepYaml)
|
|
if got != tc.want {
|
|
t.Errorf("extractDeployStepName() = %q, want %q", got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAddBuildCompleteDep(t *testing.T) {
|
|
baseYml := `steps:
|
|
preflight:
|
|
depends_on: [deps]
|
|
image: alpine/curl
|
|
|
|
# COMPONENT_STEPS_BELOW
|
|
|
|
build-complete:
|
|
depends_on: [preflight] # BUILD_COMPLETE_DEPS
|
|
image: alpine:3.19
|
|
commands:
|
|
- echo "All component builds complete"`
|
|
|
|
tests := []struct {
|
|
name string
|
|
yml string
|
|
stepName string
|
|
wantDeps string
|
|
}{
|
|
{
|
|
name: "first component added",
|
|
yml: baseYml,
|
|
stepName: "deploy-studio-api",
|
|
wantDeps: "depends_on: [preflight, deploy-studio-api] # BUILD_COMPLETE_DEPS",
|
|
},
|
|
{
|
|
name: "second component added",
|
|
yml: strings.Replace(baseYml,
|
|
"depends_on: [preflight] # BUILD_COMPLETE_DEPS",
|
|
"depends_on: [preflight, deploy-studio-api] # BUILD_COMPLETE_DEPS", 1),
|
|
stepName: "deploy-studio-ui",
|
|
wantDeps: "depends_on: [preflight, deploy-studio-api, deploy-studio-ui] # BUILD_COMPLETE_DEPS",
|
|
},
|
|
{
|
|
name: "duplicate detection",
|
|
yml: strings.Replace(baseYml, "depends_on: [preflight] # BUILD_COMPLETE_DEPS", "depends_on: [preflight, deploy-studio-api] # BUILD_COMPLETE_DEPS", 1),
|
|
stepName: "deploy-studio-api",
|
|
wantDeps: "depends_on: [preflight, deploy-studio-api] # BUILD_COMPLETE_DEPS",
|
|
},
|
|
{
|
|
name: "missing marker returns unchanged",
|
|
yml: "steps:\n build-complete:\n image: alpine:3.19",
|
|
stepName: "deploy-foo",
|
|
wantDeps: "",
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
got := addBuildCompleteDep(tc.yml, tc.stepName)
|
|
|
|
if tc.wantDeps == "" {
|
|
// Expect no change
|
|
if got != tc.yml {
|
|
t.Errorf("expected unchanged yml, but got modification")
|
|
}
|
|
return
|
|
}
|
|
|
|
if !strings.Contains(got, tc.wantDeps) {
|
|
t.Errorf("result does not contain expected depends_on line\nwant: %s\ngot:\n%s", tc.wantDeps, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAddBuildCompleteDep_PreservesIndentation(t *testing.T) {
|
|
yml := `steps:
|
|
build-complete:
|
|
depends_on: [preflight] # BUILD_COMPLETE_DEPS
|
|
image: alpine:3.19`
|
|
|
|
got := addBuildCompleteDep(yml, "deploy-foo")
|
|
|
|
// The indentation (8 spaces) should be preserved
|
|
want := " depends_on: [preflight, deploy-foo] # BUILD_COMPLETE_DEPS"
|
|
if !strings.Contains(got, want) {
|
|
t.Errorf("indentation not preserved\nwant line: %q\ngot:\n%s", want, got)
|
|
}
|
|
}
|
|
|
|
func TestUpdateWoodpeckerYml_WiresBuildCompleteDeps(t *testing.T) {
|
|
skeleton := `steps:
|
|
preflight:
|
|
depends_on: [deps]
|
|
image: alpine/curl
|
|
|
|
# COMPONENT_STEPS_BELOW
|
|
# Do not remove the marker above
|
|
|
|
build-complete:
|
|
depends_on: [preflight] # BUILD_COMPLETE_DEPS
|
|
image: alpine:3.19
|
|
commands:
|
|
- echo "All component builds complete"`
|
|
|
|
stepYaml := `build-studio-api:
|
|
depends_on: [preflight]
|
|
image: woodpeckerci/plugin-kaniko
|
|
|
|
verify-studio-api:
|
|
depends_on: [build-studio-api]
|
|
image: alpine/curl
|
|
|
|
deploy-studio-api:
|
|
depends_on: [verify-studio-api]
|
|
image: bitnami/kubectl:latest`
|
|
|
|
svc := &ComponentService{}
|
|
result := svc.updateWoodpeckerYml(skeleton, stepYaml)
|
|
|
|
// Verify step YAML was inserted after marker
|
|
if !strings.Contains(result, " build-studio-api:") {
|
|
t.Error("component build step not inserted")
|
|
}
|
|
if !strings.Contains(result, " verify-studio-api:") {
|
|
t.Error("component verify step not inserted")
|
|
}
|
|
if !strings.Contains(result, " deploy-studio-api:") {
|
|
t.Error("component deploy step not inserted")
|
|
}
|
|
|
|
// Verify build-complete depends_on was updated
|
|
if !strings.Contains(result, "depends_on: [preflight, deploy-studio-api] # BUILD_COMPLETE_DEPS") {
|
|
t.Errorf("build-complete depends_on not updated\ngot:\n%s", result)
|
|
}
|
|
|
|
// Verify marker is preserved for future components
|
|
if !strings.Contains(result, "# COMPONENT_STEPS_BELOW") {
|
|
t.Error("COMPONENT_STEPS_BELOW marker was removed")
|
|
}
|
|
}
|
|
|
|
func TestUpdateWoodpeckerYml_TwoComponents(t *testing.T) {
|
|
skeleton := `steps:
|
|
preflight:
|
|
depends_on: [deps]
|
|
|
|
# COMPONENT_STEPS_BELOW
|
|
|
|
build-complete:
|
|
depends_on: [preflight] # BUILD_COMPLETE_DEPS
|
|
image: alpine:3.19`
|
|
|
|
step1 := `build-studio-api:
|
|
depends_on: [preflight]
|
|
|
|
verify-studio-api:
|
|
depends_on: [build-studio-api]
|
|
|
|
deploy-studio-api:
|
|
depends_on: [verify-studio-api]`
|
|
|
|
step2 := `build-studio-ui:
|
|
depends_on: [preflight]
|
|
|
|
verify-studio-ui:
|
|
depends_on: [build-studio-ui]
|
|
|
|
deploy-studio-ui:
|
|
depends_on: [verify-studio-ui]`
|
|
|
|
svc := &ComponentService{}
|
|
|
|
// Add first component
|
|
result := svc.updateWoodpeckerYml(skeleton, step1)
|
|
|
|
// Add second component
|
|
result = svc.updateWoodpeckerYml(result, step2)
|
|
|
|
// Both deploy steps should be in build-complete depends_on
|
|
if !strings.Contains(result, "depends_on: [preflight, deploy-studio-api, deploy-studio-ui] # BUILD_COMPLETE_DEPS") {
|
|
t.Errorf("build-complete doesn't depend on both deploy steps\ngot:\n%s", result)
|
|
}
|
|
}
|
|
|
|
func TestUpdateWoodpeckerYml_CLISkipsBuildCompleteDep(t *testing.T) {
|
|
skeleton := `steps:
|
|
# COMPONENT_STEPS_BELOW
|
|
|
|
build-complete:
|
|
depends_on: [preflight] # BUILD_COMPLETE_DEPS
|
|
image: alpine:3.19`
|
|
|
|
cliStep := `build-mytool:
|
|
depends_on: [deps]
|
|
image: golang:1.25-alpine
|
|
commands:
|
|
- go build ./cmd`
|
|
|
|
svc := &ComponentService{}
|
|
result := svc.updateWoodpeckerYml(skeleton, cliStep)
|
|
|
|
// CLI has no deploy step, so build-complete should stay unchanged
|
|
if !strings.Contains(result, "depends_on: [preflight] # BUILD_COMPLETE_DEPS") {
|
|
t.Errorf("build-complete was modified for CLI component (no deploy step)\ngot:\n%s", result)
|
|
}
|
|
}
|
|
|
|
func TestUpdateWoodpeckerYml_MissingMarker(t *testing.T) {
|
|
noMarker := `steps:
|
|
build:
|
|
image: golang:1.25`
|
|
|
|
svc := &ComponentService{}
|
|
result := svc.updateWoodpeckerYml(noMarker, "deploy-foo:\n image: test")
|
|
|
|
if result != noMarker {
|
|
t.Error("expected unchanged yml when marker is missing")
|
|
}
|
|
}
|