package domain import ( "errors" "testing" ) func TestBuildSpec_Validate(t *testing.T) { tests := []struct { name string spec BuildSpec wantErr error }{ { name: "valid spec with prompt", spec: BuildSpec{Prompt: "Build a landing page"}, wantErr: nil, }, { name: "valid spec with all fields", spec: BuildSpec{Prompt: "Build it", Template: "nextjs", AutoCommit: true, AutoPush: true}, wantErr: nil, }, { name: "empty prompt", spec: BuildSpec{}, wantErr: ErrPromptRequired, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := tt.spec.Validate() if tt.wantErr != nil { if !errors.Is(err, tt.wantErr) { t.Errorf("Validate() error = %v, want %v", err, tt.wantErr) } } else if err != nil { t.Errorf("Validate() unexpected error = %v", err) } }) } } func TestBuildStatus_IsValid(t *testing.T) { tests := []struct { status BuildStatus want bool }{ {BuildStatusPending, true}, {BuildStatusRunning, true}, {BuildStatusCompleted, true}, {BuildStatusFailed, true}, {BuildStatusCancelled, true}, {"unknown", false}, {"", false}, } for _, tt := range tests { t.Run(string(tt.status), func(t *testing.T) { if got := tt.status.IsValid(); got != tt.want { t.Errorf("IsValid() = %v, want %v", got, tt.want) } }) } } func TestBuildResult_ToWorkResult(t *testing.T) { t.Run("success with all fields", func(t *testing.T) { result := &BuildResult{ Success: true, Output: "Build completed", CommitSHA: "abc123", FilesChanged: []string{"main.go", "go.mod"}, DurationMs: 1500, Artifacts: map[string]string{"deploy_url": "https://example.com"}, } wr := result.ToWorkResult() if wr.Output != "Build completed" { t.Errorf("Output = %q, want %q", wr.Output, "Build completed") } if wr.Artifacts["commit_sha"] != "abc123" { t.Errorf("commit_sha = %q, want %q", wr.Artifacts["commit_sha"], "abc123") } if wr.Artifacts["duration_ms"] != "1500" { t.Errorf("duration_ms = %q, want %q", wr.Artifacts["duration_ms"], "1500") } if wr.Artifacts["files_changed_count"] != "2" { t.Errorf("files_changed_count = %q, want %q", wr.Artifacts["files_changed_count"], "2") } if wr.Artifacts["deploy_url"] != "https://example.com" { t.Errorf("deploy_url = %q, want %q", wr.Artifacts["deploy_url"], "https://example.com") } }) t.Run("failure uses error as output", func(t *testing.T) { result := &BuildResult{ Success: false, Output: "partial output", Error: "build failed: missing deps", } wr := result.ToWorkResult() if wr.Output != "build failed: missing deps" { t.Errorf("Output = %q, want error message", wr.Output) } }) t.Run("nil artifacts map is safe", func(t *testing.T) { result := &BuildResult{ Success: true, Output: "done", } wr := result.ToWorkResult() if wr.Output != "done" { t.Errorf("Output = %q, want %q", wr.Output, "done") } if len(wr.Artifacts) != 0 { t.Errorf("Artifacts = %v, want empty", wr.Artifacts) } }) t.Run("zero duration not included", func(t *testing.T) { result := &BuildResult{ Success: true, DurationMs: 0, } wr := result.ToWorkResult() if _, ok := wr.Artifacts["duration_ms"]; ok { t.Error("duration_ms should not be in artifacts when zero") } }) t.Run("nil receiver returns empty result", func(t *testing.T) { var result *BuildResult wr := result.ToWorkResult() if wr.Output != "" { t.Errorf("Output = %q, want empty", wr.Output) } if wr.Artifacts != nil { t.Errorf("Artifacts = %v, want nil", wr.Artifacts) } }) t.Run("promoted fields overwrite existing artifacts", func(t *testing.T) { result := &BuildResult{ Success: true, CommitSHA: "new-sha", Artifacts: map[string]string{ "commit_sha": "old-sha", "custom_key": "kept", }, } wr := result.ToWorkResult() if wr.Artifacts["commit_sha"] != "new-sha" { t.Errorf("commit_sha = %q, want %q (promoted field should overwrite)", wr.Artifacts["commit_sha"], "new-sha") } if wr.Artifacts["custom_key"] != "kept" { t.Errorf("custom_key = %q, want %q", wr.Artifacts["custom_key"], "kept") } }) t.Run("failed with empty error keeps output", func(t *testing.T) { result := &BuildResult{ Success: false, Output: "partial output before crash", Error: "", } wr := result.ToWorkResult() if wr.Output != "partial output before crash" { t.Errorf("Output = %q, want original output when error is empty", wr.Output) } }) } func TestBuildStatus_IsTerminal(t *testing.T) { tests := []struct { status BuildStatus want bool }{ {BuildStatusPending, false}, {BuildStatusRunning, false}, {BuildStatusCompleted, true}, {BuildStatusFailed, true}, {BuildStatusCancelled, true}, } for _, tt := range tests { t.Run(string(tt.status), func(t *testing.T) { if got := tt.status.IsTerminal(); got != tt.want { t.Errorf("IsTerminal() = %v, want %v", got, tt.want) } }) } }