package profile import ( "context" "errors" "os" "path/filepath" "strings" "testing" ) func TestFilesystemRepository_GetProfile(t *testing.T) { tmpDir, err := os.MkdirTemp("", "execution_profile_test") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpDir) files, err := os.ReadDir("testdata") if err != nil { t.Fatalf("failed to read testdata: %v", err) } for _, f := range files { src := filepath.Join("testdata", f.Name()) dst := filepath.Join(tmpDir, f.Name()) data, err := os.ReadFile(src) if err != nil { t.Fatal(err) } if err := os.WriteFile(dst, data, 0644); err != nil { t.Fatal(err) } } repo := NewFilesystemRepository(tmpDir) ctx := context.Background() t.Run("valid local profile", func(t *testing.T) { p, err := repo.GetProfile(ctx, "local-default") if err != nil { t.Fatalf("expected no error, got %v", err) } if p.ID != "local-default" { t.Fatalf("unexpected id: %q", p.ID) } if p.Endpoint == "" || p.Model == "" { t.Fatalf("expected endpoint/model to be set: %+v", p) } }) t.Run("valid profile with api_key_env", func(t *testing.T) { p, err := repo.GetProfile(ctx, "local-secure") if err != nil { t.Fatalf("expected no error, got %v", err) } if p.APIKeyEnv != "SCRIPTORIUM_API_KEY" { t.Fatalf("unexpected api_key_env: %q", p.APIKeyEnv) } if p.ReasoningEffort != "medium" { t.Fatalf("unexpected reasoning_effort: %q", p.ReasoningEffort) } if p.ServiceTier != "priority" { t.Fatalf("unexpected service_tier: %q", p.ServiceTier) } }) t.Run("valid nested profile", func(t *testing.T) { nestedDir := filepath.Join(tmpDir, "local") if err := os.MkdirAll(nestedDir, 0o755); err != nil { t.Fatal(err) } writeProfileTestFile(t, filepath.Join(nestedDir, "nested-local.yaml"), ` id: nested-local endpoint: http://localhost:8000/v1 model: nested-model temperature: 0.1 `) p, err := repo.GetProfile(ctx, "nested-local") if err != nil { t.Fatalf("expected no error, got %v", err) } if p.Model != "nested-model" { t.Fatalf("unexpected model: %q", p.Model) } }) t.Run("duplicate profile IDs fail as ambiguous", func(t *testing.T) { writeProfileTestFile(t, filepath.Join(tmpDir, "duplicate-profile-a.yaml"), ` id: duplicate-profile endpoint: http://localhost:8000/v1 model: first-model `) nestedDir := filepath.Join(tmpDir, "duplicates") if err := os.MkdirAll(nestedDir, 0o755); err != nil { t.Fatal(err) } writeProfileTestFile(t, filepath.Join(nestedDir, "duplicate-profile-b.yaml"), ` id: duplicate-profile endpoint: http://localhost:8000/v1 model: second-model `) _, err := repo.GetProfile(ctx, "duplicate-profile") if !errors.Is(err, ErrInvalidProfile) { t.Fatalf("expected duplicate profile to return ErrInvalidProfile, got %v", err) } for _, want := range []string{"duplicate execution profile id", "duplicate-profile-a.yaml", filepath.Join("duplicates", "duplicate-profile-b.yaml")} { if !strings.Contains(err.Error(), want) { t.Fatalf("expected error to contain %q, got %v", want, err) } } }) t.Run("nested raw api_key rejected for likely target file", func(t *testing.T) { nestedDir := filepath.Join(tmpDir, "secure") if err := os.MkdirAll(nestedDir, 0o755); err != nil { t.Fatal(err) } writeProfileTestFile(t, filepath.Join(nestedDir, "not_named_like_id.yaml"), ` id: nested_raw_api_key endpoint: http://localhost:8000/v1 model: m api_key: secret `) _, err := repo.GetProfile(ctx, "nested_raw_api_key") if !errors.Is(err, ErrRawAPIKeyNotAllowed) { t.Fatalf("expected ErrRawAPIKeyNotAllowed, got %v", err) } if !strings.Contains(err.Error(), filepath.Join("secure", "not_named_like_id.yaml")) { t.Fatalf("expected nested path in error, got %v", err) } }) t.Run("invalid yaml", func(t *testing.T) { _, err := repo.GetProfile(ctx, "invalid_yaml") if !errors.Is(err, ErrInvalidYAML) { t.Fatalf("expected ErrInvalidYAML, got %v", err) } }) t.Run("missing id", func(t *testing.T) { _, err := repo.GetProfile(ctx, "missing_id") if !errors.Is(err, ErrProfileNotFound) { t.Fatalf("expected ErrProfileNotFound, got %v", err) } }) t.Run("missing endpoint", func(t *testing.T) { _, err := repo.GetProfile(ctx, "missing-endpoint") if !errors.Is(err, ErrInvalidProfile) { t.Fatalf("expected ErrInvalidProfile, got %v", err) } }) t.Run("missing model", func(t *testing.T) { _, err := repo.GetProfile(ctx, "missing-model") if !errors.Is(err, ErrInvalidProfile) { t.Fatalf("expected ErrInvalidProfile, got %v", err) } }) t.Run("unknown field", func(t *testing.T) { _, err := repo.GetProfile(ctx, "unknown_field") if !errors.Is(err, ErrInvalidYAML) { t.Fatalf("expected ErrInvalidYAML for strict decode unknown field, got %v", err) } }) t.Run("raw api_key rejected", func(t *testing.T) { _, err := repo.GetProfile(ctx, "raw_api_key") if !errors.Is(err, ErrRawAPIKeyNotAllowed) { t.Fatalf("expected ErrRawAPIKeyNotAllowed, got %v", err) } }) t.Run("profile not found", func(t *testing.T) { _, err := repo.GetProfile(ctx, "does-not-exist") if !errors.Is(err, ErrProfileNotFound) { t.Fatalf("expected ErrProfileNotFound, got %v", err) } }) } func writeProfileTestFile(t *testing.T, path string, content string) { t.Helper() if err := os.WriteFile(path, []byte(strings.TrimLeft(content, "\n")), 0o644); err != nil { t.Fatalf("failed to write profile test file %q: %v", path, err) } }