package cmd import ( "errors" "strings" "testing" "github.com/Pingu-Studio/MaidnCLI/internal/config" ) func TestCreateForgejoRegistryTokenRequiresConfig(t *testing.T) { originalConfigPath, originalCreate := bootstrapConfigPath, bootstrapCreateForgejoRegistryToken defer func() { bootstrapConfigPath = originalConfigPath bootstrapCreateForgejoRegistryToken = originalCreate }() bootstrapConfigPath = "" bootstrapCreateForgejoRegistryToken = true if err := runBootstrap(nil, nil); err == nil { t.Fatal("--create-forgejo-registry-token accepted a missing --config") } } func TestSeedForgejoOperationalCredentialsUsesEncryptedUpsertBoundary(t *testing.T) { original := upsertOperationalSecret t.Cleanup(func() { upsertOperationalSecret = original }) cfg := config.Config{ Git: config.GitConfig{Username: "webhook-bot", Token: "test-pat"}, SOPS: config.SOPSConfig{OperationalSecretsPath: "secrets.sops.yaml", AgeKeyPath: "age-key.txt"}, } type call struct{ path, key, value string } var calls []call upsertOperationalSecret = func(secretsPath, ageKeyPath, path, key, value string) error { if secretsPath != cfg.SOPS.OperationalSecretsPath || ageKeyPath != cfg.SOPS.AgeKeyPath { t.Fatal("credential upsert used unexpected secret paths") } calls = append(calls, call{path, key, value}) return nil } if err := seedForgejoOperationalCredentials(cfg); err != nil { t.Fatal(err) } if len(calls) != 2 || calls[0] != (call{"cicd/forgejo", "username", cfg.Git.Username}) || calls[1] != (call{"cicd/forgejo", "token", cfg.Git.Token}) { t.Fatal("configured Forgejo credentials were not passed only to the encrypted upsert boundary") } } func TestSeedForgejoOperationalCredentialsHidesTokenOnUpsertFailure(t *testing.T) { original := upsertOperationalSecret t.Cleanup(func() { upsertOperationalSecret = original }) cfg := config.Config{Git: config.GitConfig{Username: "webhook-bot", Token: "test-pat"}} upsertOperationalSecret = func(_, _, _, key, _ string) error { if key == "token" { return errors.New("unavailable") } return nil } err := seedForgejoOperationalCredentials(cfg) if err == nil || !strings.Contains(err.Error(), "save Forgejo token for webhook registration") || strings.Contains(err.Error(), cfg.Git.Token) { t.Fatal("credential upsert failure was not clear and token-safe") } } func TestCreateOrReuseForgejoDeliveryStatusTokenCreatesSeedsAndRedacts(t *testing.T) { originalRead, originalPrompt, originalCreate, originalUpsert, originalInitialize := readOperationalSecrets, promptForgejoDeliveryStatusToken, createForgejoDeliveryStatusToken, upsertOperationalSecret, initializeOpenBao t.Cleanup(func() { readOperationalSecrets, promptForgejoDeliveryStatusToken, createForgejoDeliveryStatusToken, upsertOperationalSecret, initializeOpenBao = originalRead, originalPrompt, originalCreate, originalUpsert, originalInitialize }) cfg := config.Config{Git: config.GitConfig{BaseURL: "https://git.example.test", Username: "delivery-bot"}, SOPS: config.SOPSConfig{OperationalSecretsPath: "secrets.sops.yaml", AgeKeyPath: "age-key.txt"}} readOperationalSecrets = func(_, _ string) (map[string]map[string]string, error) { return map[string]map[string]string{}, nil } promptForgejoDeliveryStatusToken = func() (string, string, error) { return "password", "otp", nil } createForgejoDeliveryStatusToken = func(baseURL, username, password, otp string) (string, error) { if baseURL != cfg.Git.BaseURL || username != cfg.Git.Username || password != "password" || otp != "otp" { t.Fatal("delivery token creation crossed an unexpected credential boundary") } return "delivery-token", nil } seeded := false upsertOperationalSecret = func(path, agePath, secretPath, key, value string) error { seeded = path == cfg.SOPS.OperationalSecretsPath && agePath == cfg.SOPS.AgeKeyPath && secretPath == "cicd/forgejo-delivery-status" && key == "token" && value == "delivery-token" return nil } initializeOpenBao = func(config.Config) error { return errors.New("OpenBao rejected delivery-token") } err := createOrReuseForgejoDeliveryStatusToken(cfg) if !seeded || err == nil || strings.Contains(err.Error(), "delivery-token") { t.Fatalf("delivery token create/seed failure leaked or skipped a credential: seeded=%t err=%v", seeded, err) } } func TestCreateOrReuseForgejoDeliveryStatusTokenReusesAndReseeds(t *testing.T) { originalRead, originalPrompt, originalCreate, originalUpsert, originalInitialize := readOperationalSecrets, promptForgejoDeliveryStatusToken, createForgejoDeliveryStatusToken, upsertOperationalSecret, initializeOpenBao t.Cleanup(func() { readOperationalSecrets, promptForgejoDeliveryStatusToken, createForgejoDeliveryStatusToken, upsertOperationalSecret, initializeOpenBao = originalRead, originalPrompt, originalCreate, originalUpsert, originalInitialize }) cfg := config.Config{SOPS: config.SOPSConfig{OperationalSecretsPath: "secrets.sops.yaml", AgeKeyPath: "age-key.txt"}} readOperationalSecrets = func(_, _ string) (map[string]map[string]string, error) { return map[string]map[string]string{"cicd/forgejo-delivery-status": {"token": "existing-token"}}, nil } promptForgejoDeliveryStatusToken = func() (string, string, error) { t.Fatal("existing delivery token must not prompt"); return "", "", nil } createForgejoDeliveryStatusToken = func(string, string, string, string) (string, error) { t.Fatal("existing delivery token must not be recreated") return "", nil } upsertOperationalSecret = func(string, string, string, string, string) error { t.Fatal("existing delivery token must not be rewritten") return nil } seeded := false initializeOpenBao = func(received config.Config) error { seeded = received.SOPS.OperationalSecretsPath == cfg.SOPS.OperationalSecretsPath return nil } if err := createOrReuseForgejoDeliveryStatusToken(cfg); err != nil || !seeded { t.Fatalf("delivery token reuse did not reseed OpenBao: seeded=%t err=%v", seeded, err) } } func TestCreateOrReuseForgejoDeliveryStatusTokenRejectsAmbiguousState(t *testing.T) { originalRead, originalPrompt := readOperationalSecrets, promptForgejoDeliveryStatusToken t.Cleanup(func() { readOperationalSecrets, promptForgejoDeliveryStatusToken = originalRead, originalPrompt }) readOperationalSecrets = func(_, _ string) (map[string]map[string]string, error) { return map[string]map[string]string{"cicd/forgejo-delivery-status": {"token": "", "legacy": "value"}}, nil } promptForgejoDeliveryStatusToken = func() (string, string, error) { t.Fatal("ambiguous state must not create a replacement token") return "", "", nil } if err := createOrReuseForgejoDeliveryStatusToken(config.Config{}); err == nil || !strings.Contains(err.Error(), "ambiguous") { t.Fatalf("ambiguous delivery token state = %v", err) } }