package config import ( "os" "strings" "testing" "go.yaml.in/yaml/v4" ) func TestForgeDefaultsToAuto(t *testing.T) { var f Forge if err := f.Validate(); err == nil { t.Fatal("expected error for missing url") } f = Forge{URL: "git.example.org"} if err := f.Validate(); err != nil { t.Fatalf("Validate: %v", err) } if f.Type != "auto" { t.Errorf("expected default type auto, got %q", f.Type) } } func TestForgeRejectsUnsupportedType(t *testing.T) { f := Forge{URL: "example.test", Type: "github"} err := f.Validate() if err == nil { t.Fatal("expected error for unsupported type") } if !strings.Contains(err.Error(), "unsupported") { t.Errorf("expected unsupported-type error, got %v", err) } } func TestForgeSupportsKnownTypes(t *testing.T) { for _, typ := range []string{ForgeTypeAuto, ForgeTypeForgejo, ForgeTypeGitea} { f := Forge{URL: "example.test", Type: typ} if err := f.Validate(); err != nil { t.Errorf("type %q should be accepted: %v", typ, err) } } } func TestParseForgesFromYAML(t *testing.T) { cfg := ` db_path: "/tmp/db" forges: - url: git.example.org - url: git.example.net type: forgejo ` var c Config if err := yaml.Unmarshal([]byte(cfg), &c); err != nil { t.Fatalf("unmarshal: %v", err) } if len(c.Forges) != 2 { t.Fatalf("want 2 forges, got %d", len(c.Forges)) } if c.Forges[0].URL != "git.example.org" || c.Forges[0].Type != "" { t.Errorf("forge 0: %+v", c.Forges[0]) } if c.Forges[1].URL != "git.example.net" || c.Forges[1].Type != "forgejo" { t.Errorf("forge 1: %+v", c.Forges[1]) } if err := c.Forges[0].Validate(); err != nil { t.Fatalf("validate forge 0: %v", err) } if c.Forges[0].Type != "auto" { t.Errorf("forge 0 type should normalize to auto, got %q", c.Forges[0].Type) } } func TestResolveAPIKeyFromInline(t *testing.T) { tc := TypesenseConfig{ApiKey: "inline-key"} got, err := tc.ResolveAPIKey() if err != nil { t.Fatalf("ResolveAPIKey: %v", err) } if got != "inline-key" { t.Errorf("got %q, want inline-key", got) } } func TestResolveAPIKeyFromFileTakesPrecedence(t *testing.T) { p := t.TempDir() + "/key" if err := os.WriteFile(p, []byte("file-key-with-newline\n"), 0o600); err != nil { t.Fatal(err) } // Both set: the file wins, and surrounding whitespace is trimmed. tc := TypesenseConfig{ApiKey: "inline-key", ApiKeyFile: p} got, err := tc.ResolveAPIKey() if err != nil { t.Fatalf("ResolveAPIKey: %v", err) } if got != "file-key-with-newline" { t.Errorf("got %q, want file key with newline trimmed", got) } } func TestResolveAPIKeyNone(t *testing.T) { tc := TypesenseConfig{} got, err := tc.ResolveAPIKey() if err != nil { t.Fatalf("ResolveAPIKey: %v", err) } if got != "" { t.Errorf("got %q, want empty", got) } } func TestResolveAPIKeyMissingFile(t *testing.T) { tc := TypesenseConfig{ApiKeyFile: "/nonexistent/does-not-exist"} if _, err := tc.ResolveAPIKey(); err == nil { t.Error("expected error for missing api_key_file") } } func TestParseAPIKeyFileFromYAML(t *testing.T) { cfg := ` db_path: "/tmp/db" typesense: host: "http://localhost:8108" api_key: "plain" api_key_file: "/run/secrets/typesense" ` var c Config if err := yaml.Unmarshal([]byte(cfg), &c); err != nil { t.Fatalf("unmarshal: %v", err) } if c.Typesense.ApiKey != "plain" || c.Typesense.ApiKeyFile != "/run/secrets/typesense" { t.Errorf("typesense: %+v", c.Typesense) } }