130 lines
6.1 KiB
Go
130 lines
6.1 KiB
Go
package domain
|
|
|
|
import "testing"
|
|
|
|
func TestProjectConfigValidate(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
cfg ProjectConfig
|
|
wantErr bool
|
|
}{
|
|
{"empty ok", ProjectConfig{}, false},
|
|
{"good agent config", ProjectConfig{AgentConfig: AgentConfig{Model: "m", Permissions: PermissionModeAuto}}, false},
|
|
{"bad permission", ProjectConfig{AgentConfig: AgentConfig{Permissions: "yolo"}}, true},
|
|
{"good session prefix", ProjectConfig{SessionPrefix: "ao"}, false},
|
|
{"session prefix with slash", ProjectConfig{SessionPrefix: "ao/project"}, true},
|
|
{"session prefix with backslash", ProjectConfig{SessionPrefix: `ao\project`}, true},
|
|
{"session prefix traversal component", ProjectConfig{SessionPrefix: ".."}, true},
|
|
{"good role override", ProjectConfig{Worker: RoleOverride{Harness: HarnessCodex}}, false},
|
|
{"unknown role harness", ProjectConfig{Orchestrator: RoleOverride{Harness: "nope"}}, true},
|
|
{"bad role agent config", ProjectConfig{Worker: RoleOverride{AgentConfig: AgentConfig{Permissions: "nope"}}}, true},
|
|
{"good symlinks", ProjectConfig{Symlinks: []string{".env", "configs/dev.toml"}}, false},
|
|
{"symlink absolute path", ProjectConfig{Symlinks: []string{"/etc/passwd"}}, true},
|
|
{"symlink parent escape", ProjectConfig{Symlinks: []string{"../escape"}}, true},
|
|
{"symlink embedded parent", ProjectConfig{Symlinks: []string{"a/../../b"}}, true},
|
|
{"symlink bare ..", ProjectConfig{Symlinks: []string{".."}}, true},
|
|
{"good reviewers", ProjectConfig{Reviewers: []ReviewerConfig{{Harness: ReviewerClaudeCode}}}, false},
|
|
{"good codex reviewer", ProjectConfig{Reviewers: []ReviewerConfig{{Harness: ReviewerCodex}}}, false},
|
|
{"good opencode reviewer", ProjectConfig{Reviewers: []ReviewerConfig{{Harness: ReviewerOpenCode}}}, false},
|
|
{"unknown reviewer harness", ProjectConfig{Reviewers: []ReviewerConfig{{Harness: "nope"}}}, true},
|
|
{"worker-only harness is not auto a reviewer", ProjectConfig{Reviewers: []ReviewerConfig{{Harness: ReviewerHarness(HarnessAider)}}}, true},
|
|
{"empty reviewer harness", ProjectConfig{Reviewers: []ReviewerConfig{{Harness: ""}}}, true},
|
|
{"tracker intake assignee rule", ProjectConfig{TrackerIntake: TrackerIntakeConfig{Enabled: true, Assignee: "alice"}}, false},
|
|
{"tracker intake explicit github", ProjectConfig{TrackerIntake: TrackerIntakeConfig{Enabled: true, Provider: TrackerProviderGitHub, Assignee: "alice"}}, false},
|
|
{"tracker intake no rule", ProjectConfig{TrackerIntake: TrackerIntakeConfig{Enabled: true}}, true},
|
|
{"tracker intake unknown provider", ProjectConfig{TrackerIntake: TrackerIntakeConfig{Enabled: true, Provider: "linear", Assignee: "alice"}}, true},
|
|
{"tracker intake repo with whitespace", ProjectConfig{TrackerIntake: TrackerIntakeConfig{Enabled: true, Repo: " acme/demo", Assignee: "alice"}}, true},
|
|
{"tracker intake assignee with whitespace", ProjectConfig{TrackerIntake: TrackerIntakeConfig{Enabled: true, Assignee: " alice"}}, true},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if err := tt.cfg.Validate(); (err != nil) != tt.wantErr {
|
|
t.Fatalf("Validate() err = %v, wantErr = %v", err, tt.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDefaultProjectConfig(t *testing.T) {
|
|
def := DefaultProjectConfig()
|
|
|
|
// The one documented non-empty default.
|
|
if def.DefaultBranch != "main" {
|
|
t.Fatalf("default DefaultBranch = %q, want main", def.DefaultBranch)
|
|
}
|
|
|
|
// Every other field defaults to its zero value: clearing the documented
|
|
// default must leave the config completely empty.
|
|
def.DefaultBranch = ""
|
|
if !def.IsZero() {
|
|
t.Fatalf("default config has unexpected non-zero fields: %#v", def)
|
|
}
|
|
}
|
|
|
|
func TestProjectConfigWithDefaults(t *testing.T) {
|
|
// An unset config gets the documented defaults.
|
|
got := (ProjectConfig{}).WithDefaults()
|
|
if got.DefaultBranch != DefaultBranchName {
|
|
t.Fatalf("WithDefaults = %#v, want branch=main", got)
|
|
}
|
|
|
|
// Set fields are preserved, not overwritten.
|
|
got = (ProjectConfig{
|
|
DefaultBranch: "develop",
|
|
AgentConfig: AgentConfig{Model: "m"},
|
|
}).WithDefaults()
|
|
if got.DefaultBranch != "develop" {
|
|
t.Fatalf("WithDefaults overwrote set fields: %#v", got)
|
|
}
|
|
if got.AgentConfig.Model != "m" {
|
|
t.Fatalf("WithDefaults dropped a set field: %#v", got.AgentConfig)
|
|
}
|
|
|
|
got = (ProjectConfig{TrackerIntake: TrackerIntakeConfig{Enabled: true, Assignee: "alice"}}).WithDefaults()
|
|
if got.TrackerIntake.Provider != TrackerProviderGitHub {
|
|
t.Fatalf("TrackerIntake.Provider = %q, want %q", got.TrackerIntake.Provider, TrackerProviderGitHub)
|
|
}
|
|
|
|
got = (ProjectConfig{}).WithDefaults()
|
|
if got.TrackerIntake.Provider != "" {
|
|
t.Fatalf("disabled TrackerIntake.Provider = %q, want empty", got.TrackerIntake.Provider)
|
|
}
|
|
}
|
|
|
|
func TestResolveReviewerHarness(t *testing.T) {
|
|
// A configured reviewer always wins, regardless of the worker harness.
|
|
cfg := ProjectConfig{Reviewers: []ReviewerConfig{{Harness: ReviewerClaudeCode}}}
|
|
if got := cfg.ResolveReviewerHarness(HarnessAider); got != ReviewerClaudeCode {
|
|
t.Fatalf("configured reviewer = %q, want claude-code", got)
|
|
}
|
|
|
|
// No reviewer configured: always use claude-code, regardless of the worker
|
|
// harness (see #2241).
|
|
if got := (ProjectConfig{}).ResolveReviewerHarness(HarnessClaudeCode); got != ReviewerClaudeCode {
|
|
t.Fatalf("default = %q, want reviewer claude-code", got)
|
|
}
|
|
if got := (ProjectConfig{}).ResolveReviewerHarness(HarnessCodex); got != ReviewerClaudeCode {
|
|
t.Fatalf("default = %q, want reviewer claude-code", got)
|
|
}
|
|
if got := (ProjectConfig{}).ResolveReviewerHarness(HarnessOpenCode); got != ReviewerClaudeCode {
|
|
t.Fatalf("default = %q, want reviewer claude-code", got)
|
|
}
|
|
|
|
// A worker harness that is not claude-code also falls back to claude-code.
|
|
if got := (ProjectConfig{}).ResolveReviewerHarness(HarnessAider); got != FallbackReviewerHarness {
|
|
t.Fatalf("fallback = %q, want %q", got, FallbackReviewerHarness)
|
|
}
|
|
}
|
|
|
|
func TestProjectConfigIsZero(t *testing.T) {
|
|
if !(ProjectConfig{}).IsZero() {
|
|
t.Fatal("empty config should be zero")
|
|
}
|
|
if (ProjectConfig{DefaultBranch: "main"}).IsZero() {
|
|
t.Fatal("populated config should not be zero")
|
|
}
|
|
if (ProjectConfig{Env: map[string]string{"A": "b"}}).IsZero() {
|
|
t.Fatal("config with env should not be zero")
|
|
}
|
|
}
|