Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
## Template Version Alignment
- Go: 1.23 → 1.25 across all templates (go.work, go.mod, Dockerfiles, CI)
- Alpine: latest → 3.19 (explicit version pinning)
- Woodpecker: failure:retry → failure:ignore (invalid syntax fix)
## SDLC Tree Fixes (slackpath-5-full-lifecycle)
Fixed merge failures by correcting lifecycle flow:
1. **Branch Creation**: Added missing create-branch step (planned → ready)
- Bug: Merge command requires feature.Branch field to be set
- Fix: POST /projects/{id}/sdlc/features/{slug}/branch
2. **Artifact Status**: Changed approval to pass for execution artifacts
- Bug: Review/audit/QA need status="passed" not "approved"
- Fix: /artifacts/{type}/approve → /artifacts/{type}/pass
- Added: pass-qa step after wait-qa
3. **Phase Transition Order**: Reordered merge phase transition
- Bug: Merge command checks if phase == "merge" first
- Fix: transition-to-merge BEFORE merge-feature (not after)
## GCS Provisioner Fix
- Replaced deprecated option.WithCredentialsFile with env var approach
- Now uses GOOGLE_APPLICATION_CREDENTIALS for ADC (Application Default Credentials)
- Avoids security risk from deprecated credential options
- Fixed test: Added ComponentTypeGCS to ValidComponentTypes test
## Critical Rules Added
- Version alignment: All template versions must stay in sync
- When updating versions, grep entire templates/ tree
## Files Changed
- 27 template files: Go version + Woodpecker syntax
- 1 tree file: SDLC lifecycle flow corrections
- 1 CLAUDE.md: Version alignment rule
- 1 GCS provisioner: Deprecated API fix
- 1 test file: Added missing component type
Root cause: Skeleton templates lagged behind Go 1.25 release and had
invalid Woodpecker syntax. SDLC tree skipped required branch creation
and used wrong artifact approval endpoints.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
238 lines
6.3 KiB
Go
238 lines
6.3 KiB
Go
package domain
|
|
|
|
import "testing"
|
|
|
|
func TestIsValidComponentType(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
expected bool
|
|
}{
|
|
{"service", "service", true},
|
|
{"worker", "worker", true},
|
|
{"app-astro", "app-astro", true},
|
|
{"app-react", "app-react", true},
|
|
{"app-nextjs", "app-nextjs", true},
|
|
{"cli", "cli", true},
|
|
{"postgres", "postgres", true},
|
|
{"redis", "redis", true},
|
|
{"invalid", "invalid", false},
|
|
{"empty", "", false},
|
|
{"uppercase", "SERVICE", false},
|
|
{"partial", "serv", false},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
result := IsValidComponentType(tc.input)
|
|
if result != tc.expected {
|
|
t.Errorf("IsValidComponentType(%q) = %v, want %v", tc.input, result, tc.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestComponentType_DestDir(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
componentType ComponentType
|
|
expected string
|
|
}{
|
|
{"service", ComponentTypeService, "services"},
|
|
{"worker", ComponentTypeWorker, "workers"},
|
|
{"app-astro", ComponentTypeAppAstro, "apps"},
|
|
{"app-react", ComponentTypeAppReact, "apps"},
|
|
{"app-nextjs", ComponentTypeAppNextJS, "apps"},
|
|
{"cli", ComponentTypeCLI, "cli"},
|
|
{"unknown", ComponentType("unknown"), ""},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
result := tc.componentType.DestDir()
|
|
if result != tc.expected {
|
|
t.Errorf("%s.DestDir() = %q, want %q", tc.componentType, result, tc.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestComponentType_StartingPort(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
componentType ComponentType
|
|
expected int
|
|
}{
|
|
{"service", ComponentTypeService, 8001},
|
|
{"worker", ComponentTypeWorker, 0},
|
|
{"app-astro", ComponentTypeAppAstro, 3001},
|
|
{"app-react", ComponentTypeAppReact, 3001},
|
|
{"app-nextjs", ComponentTypeAppNextJS, 3001},
|
|
{"cli", ComponentTypeCLI, 0},
|
|
{"unknown", ComponentType("unknown"), 0},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
result := tc.componentType.StartingPort()
|
|
if result != tc.expected {
|
|
t.Errorf("%s.StartingPort() = %d, want %d", tc.componentType, result, tc.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestComponentType_NeedsPort(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
componentType ComponentType
|
|
expected bool
|
|
}{
|
|
{"service", ComponentTypeService, true},
|
|
{"worker", ComponentTypeWorker, false},
|
|
{"app-astro", ComponentTypeAppAstro, true},
|
|
{"app-react", ComponentTypeAppReact, true},
|
|
{"app-nextjs", ComponentTypeAppNextJS, true},
|
|
{"cli", ComponentTypeCLI, false},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
result := tc.componentType.NeedsPort()
|
|
if result != tc.expected {
|
|
t.Errorf("%s.NeedsPort() = %v, want %v", tc.componentType, result, tc.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestComponentType_IsGoComponent(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
componentType ComponentType
|
|
expected bool
|
|
}{
|
|
{"service", ComponentTypeService, true},
|
|
{"worker", ComponentTypeWorker, true},
|
|
{"app-astro", ComponentTypeAppAstro, false},
|
|
{"app-react", ComponentTypeAppReact, false},
|
|
{"app-nextjs", ComponentTypeAppNextJS, false},
|
|
{"cli", ComponentTypeCLI, true},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
result := tc.componentType.IsGoComponent()
|
|
if result != tc.expected {
|
|
t.Errorf("%s.IsGoComponent() = %v, want %v", tc.componentType, result, tc.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidateComponentName(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
wantErr bool
|
|
}{
|
|
{"valid simple", "auth", false},
|
|
{"valid with dash", "auth-api", false},
|
|
{"valid with numbers", "api2", false},
|
|
{"valid complex", "user-service-v2", false},
|
|
{"empty", "", true},
|
|
{"starts with number", "2api", true},
|
|
{"starts with dash", "-api", true},
|
|
{"uppercase", "Auth", true},
|
|
{"mixed case", "authApi", true},
|
|
{"underscore", "auth_api", true},
|
|
{"space", "auth api", true},
|
|
{"special char", "auth@api", true},
|
|
{"too long", "a" + string(make([]byte, 63)), true},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
err := ValidateComponentName(tc.input)
|
|
if (err != nil) != tc.wantErr {
|
|
t.Errorf("ValidateComponentName(%q) error = %v, wantErr %v", tc.input, err, tc.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidComponentTypes(t *testing.T) {
|
|
// Ensure all valid types are in the slice
|
|
expected := []ComponentType{
|
|
ComponentTypeService,
|
|
ComponentTypeWorker,
|
|
ComponentTypeAppAstro,
|
|
ComponentTypeAppReact,
|
|
ComponentTypeAppNextJS,
|
|
ComponentTypeCLI,
|
|
ComponentTypePostgres,
|
|
ComponentTypeRedis,
|
|
ComponentTypeGCS,
|
|
}
|
|
|
|
if len(ValidComponentTypes) != len(expected) {
|
|
t.Errorf("ValidComponentTypes has %d types, want %d", len(ValidComponentTypes), len(expected))
|
|
}
|
|
|
|
for i, ct := range ValidComponentTypes {
|
|
if ct != expected[i] {
|
|
t.Errorf("ValidComponentTypes[%d] = %s, want %s", i, ct, expected[i])
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestComponentType_IsAppComponent(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
componentType ComponentType
|
|
expected bool
|
|
}{
|
|
{"service", ComponentTypeService, false},
|
|
{"worker", ComponentTypeWorker, false},
|
|
{"app-astro", ComponentTypeAppAstro, true},
|
|
{"app-react", ComponentTypeAppReact, true},
|
|
{"app-nextjs", ComponentTypeAppNextJS, true},
|
|
{"cli", ComponentTypeCLI, false},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
result := tc.componentType.IsAppComponent()
|
|
if result != tc.expected {
|
|
t.Errorf("%s.IsAppComponent() = %v, want %v", tc.componentType, result, tc.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestComponentType_IsInfraComponent(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
componentType ComponentType
|
|
expected bool
|
|
}{
|
|
{"postgres", ComponentTypePostgres, true},
|
|
{"redis", ComponentTypeRedis, true},
|
|
{"service", ComponentTypeService, false},
|
|
{"worker", ComponentTypeWorker, false},
|
|
{"app-astro", ComponentTypeAppAstro, false},
|
|
{"app-react", ComponentTypeAppReact, false},
|
|
{"app-nextjs", ComponentTypeAppNextJS, false},
|
|
{"cli", ComponentTypeCLI, false},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
result := tc.componentType.IsInfraComponent()
|
|
if result != tc.expected {
|
|
t.Errorf("%s.IsInfraComponent() = %v, want %v", tc.componentType, result, tc.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|