package config import ( "os" "path/filepath" "strings" "testing" ) func validConfig(t *testing.T) Config { t.Helper() return Config{ ClusterID: "test-cluster", WorkspaceDir: t.TempDir(), Git: GitConfig{Provider: "forgejo", BaseURL: "https://git.example.test", Username: "bot", Token: "token", Owner: "test-org"}, Flux: FluxConfig{RepoName: "cluster", Branch: "main", ClusterPath: "./clusters/test", ClusterDomain: "example.test", ManifestsRepo: "manifests"}, Templates: TemplateConfig{TalosRepoURL: "https://git.example.test/talos.git", TalosRepoRef: "main", CICDRepoURL: "https://git.example.test/template.git", CICDRepoRef: "main", ManifestsRepoURL: "https://git.example.test/manifests.git", ManifestsRepoRef: "main"}, Cilium: CiliumConfig{TrafficInterface: "eth1", LoadBalancerStart: "192.168.45.19", LoadBalancerEnd: "192.168.45.30"}, DemocraticCSI: DemocraticCSIConfig{TrueNASAPIKey: "api-key", TrueNASHost: "truenas.example.test", TargetPortal: "truenas.example.test:3260", ShareHost: "truenas.example.test", DatasetParentNFS: "pool/kubernetes/nfs/v", DatasetSnapshotsNFS: "pool/kubernetes/nfs/s", AllowedNetworks: "192.168.45.0/24", NameSuffix: "-test", PortalGroup: "1", InitiatorGroup: "1"}, Delivery: DeliveryConfig{AppName: "web-ui", AppRepoURL: "https://git.example.test/test-org/web-ui.git", AppRepoRef: "main", ProductionBranch: "production", ImageRepository: "registry.example.test/test-org/web-ui", WebhookHostname: "tekton.example.test", WebhookPath: "/"}, Talos: TalosConfig{ RepoDirName: "talos", TerraformDir: "terraform", GeneratedDir: "generated", ConfigFileName: "terraform.tfvars", Proxmox: TalosProxmoxConfig{APIURL: "https://proxmox.example.test:8006", APITokenID: "id", APITokenSecret: "secret"}, Cluster: TalosClusterConfig{Name: "test-cluster", Domain: "example.test"}, Image: TalosImageConfig{TalosVersion: "v1.13.6", KubernetesVersion: "v1.33.4", SchematicID: "abcdefghijkl"}, Nodes: []TalosNode{{Name: "cp-01", VMID: 100, Role: "controlplane", Networks: []TalosNetwork{{IP: "192.168.45.3", CIDR: "192.168.45.0/28", Gateway: "192.168.45.1", VLANID: 45}, {IP: "192.168.45.18", CIDR: "192.168.45.16/28", VLANID: 451}}}}, }, } } func TestResolveDeliveryStates(t *testing.T) { platformOnly := validConfig(t) platformOnly.Delivery = DeliveryConfig{} partialLegacy := platformOnly partialLegacy.Delivery.AppName = "legacy-app" for _, test := range []struct { name string cfg Config configured bool }{ {name: "absent", cfg: platformOnly}, {name: "partial legacy", cfg: partialLegacy}, {name: "complete", cfg: validConfig(t), configured: true}, } { t.Run(test.name, func(t *testing.T) { resolved, err := Resolve(test.cfg) if err != nil { t.Fatalf("Resolve() error = %v", err) } if resolved.Delivery.Configured() != test.configured { t.Fatalf("Configured() = %t, want %t", resolved.Delivery.Configured(), test.configured) } if !test.configured && resolved.Delivery.AppRepoRef != "" { t.Fatal("Resolve() defaulted delivery configuration treated as absent") } }) } } func TestValidateDeliveryRequiresCompleteConfig(t *testing.T) { cfg := validConfig(t) cfg.Delivery.ImageRepository = "" if err := ValidateDelivery(cfg); err == nil || !strings.Contains(err.Error(), "imageRepository") { t.Fatalf("ValidateDelivery() error = %v, want incomplete delivery error", err) } } func TestValidateRejectsCredentialBearingDeliveryURLs(t *testing.T) { cfg := validConfig(t) cfg.Delivery.AppRepoURL = "https://reader:token@git.example.test/test-org/web-ui.git" if _, err := Resolve(cfg); err == nil || !strings.Contains(err.Error(), "credentials") { t.Fatalf("Resolve() error = %v, want credential-free app repository error", err) } } func TestValidateRequiresDeliveryRepositoryOnForgejoOrigin(t *testing.T) { cfg := validConfig(t) cfg.Delivery.AppRepoURL = "https://attacker.example.test/test-org/web-ui.git" if _, err := Resolve(cfg); err == nil || !strings.Contains(err.Error(), "configured Forgejo origin") { t.Fatalf("Resolve() error = %v, want Forgejo origin error", err) } cfg.Delivery.AppRepoURL = "https://git.example.test/test-org/web-ui.git" if _, err := Resolve(cfg); err != nil { t.Fatalf("Resolve() rejected delivery repository on configured Forgejo origin: %v", err) } } func TestValidateRequiresDistinctValidProductionBranch(t *testing.T) { cfg := validConfig(t) cfg.Delivery.ProductionBranch = cfg.Delivery.AppRepoRef if _, err := Resolve(cfg); err == nil || !strings.Contains(err.Error(), "productionBranch") { t.Fatalf("Resolve() error = %v, want distinct production branch error", err) } cfg.Delivery.ProductionBranch = "production/../unsafe" if _, err := Resolve(cfg); err == nil || !strings.Contains(err.Error(), "productionBranch") { t.Fatalf("Resolve() error = %v, want valid production branch error", err) } } func TestResolveDefaultsWebhookEndpoint(t *testing.T) { cfg, err := Resolve(validConfig(t)) if err != nil { t.Fatal(err) } if cfg.Delivery.WebhookURL() != "https://tekton.example.test/" { t.Fatalf("WebhookURL() = %q", cfg.Delivery.WebhookURL()) } if cfg.Templates.TektonCatalogRepoURL != "https://git.pingu.pw/Maidn/tekton-pipelines.git" || cfg.Templates.TektonCatalogRepoRef != "main" || cfg.Flux.TektonCatalogRepo != "tekton-pipelines" { t.Fatalf("Tekton catalog defaults = %q@%q", cfg.Templates.TektonCatalogRepoURL, cfg.Templates.TektonCatalogRepoRef) } } func TestLoadRejectsUnknownFields(t *testing.T) { path := filepath.Join(t.TempDir(), "config.yaml") if err := os.WriteFile(path, []byte("workspaceDir: test\nunknown: value\n"), 0600); err != nil { t.Fatal(err) } if _, err := Load(path); err == nil || !strings.Contains(err.Error(), "field unknown") { t.Fatalf("Load() error = %v, want unknown field error", err) } } func TestLoadRawAllowsCompletionBeforeValidation(t *testing.T) { path := filepath.Join(t.TempDir(), "config.yaml") if err := os.WriteFile(path, []byte("workspaceDir: test\n"), 0600); err != nil { t.Fatal(err) } if _, err := LoadRaw(path); err != nil { t.Fatalf("LoadRaw() error = %v", err) } } func TestWriteRedactedOmitsCredentials(t *testing.T) { cfg := validConfig(t) cfg.Git.BaseURL = "https://reader:git-token@git.example.test" cfg.Templates.CICDRepoURL = "https://reader:template-token@git.example.test/template.git?access_token=query-token" cfg.Delivery.ImageRepository = "reader:registry-token@registry.example.test/team/app" path := filepath.Join(t.TempDir(), "resolved.yaml") if err := WriteRedacted(path, cfg); err != nil { t.Fatal(err) } data, err := os.ReadFile(path) if err != nil { t.Fatal(err) } if strings.Contains(string(data), "token: token") || strings.Contains(string(data), "apiTokenSecret: secret") || strings.Contains(string(data), "truenasApiKey: api-key") || strings.Contains(string(data), "git-token") || strings.Contains(string(data), "template-token") || strings.Contains(string(data), "query-token") || strings.Contains(string(data), "registry-token") { t.Fatal("redacted config contains credentials") } } func TestValidateRejectsLoadBalancerAddressOnNode(t *testing.T) { cfg := validConfig(t) cfg.Talos.Nodes[0].Networks[1].IP = cfg.Cilium.LoadBalancerStart if _, err := Resolve(cfg); err == nil { t.Fatal("Resolve() succeeded with traffic node address in LoadBalancer range") } }