package oidc import ( "context" "os" "testing" "time" "git.netcell-it.de/projekte/edgeguard-native/internal/database" "git.netcell-it.de/projekte/edgeguard-native/internal/services/secrets" ) // migrateRetry umgeht die goose-Erst-Apply-Race, wenn mehrere guarded // Test-Pakete dieselbe frische DB parallel migrieren. func migrateRetry(ctx context.Context, dsn string) error { var err error for i := 0; i < 3; i++ { if err = database.Migrate(ctx, dsn); err == nil { return nil } time.Sleep(700 * time.Millisecond) } return err } // Guarded integration test: set EG_FWTEST_DSN (sonst skip). func testRepo(t *testing.T) *Repo { t.Helper() dsn := os.Getenv("EG_FWTEST_DSN") if dsn == "" { t.Skip("set EG_FWTEST_DSN to run the oidc settings test") } ctx := context.Background() if err := migrateRetry(ctx, dsn); err != nil { t.Fatalf("migrate: %v", err) } pool, err := database.Open(ctx, dsn) if err != nil { t.Fatalf("open: %v", err) } t.Cleanup(pool.Close) box := secrets.New(t.TempDir() + "/master_key") // Settings auf einen sauberen Default zurücksetzen. if _, err := pool.Exec(ctx, `UPDATE oidc_settings SET enabled=false, issuer_url='', client_id='', client_secret_enc=NULL WHERE id=1`); err != nil { t.Fatalf("reset: %v", err) } return New(pool, box) } func TestSettings_SecretWriteOnly(t *testing.T) { r := testRepo(t) ctx := context.Background() str := func(s string) *string { return &s } // 1) Neues Secret setzen. if err := r.Update(ctx, UpdateInput{Enabled: true, IssuerURL: "https://idp.example/realms/x", ClientID: "eg", ClientSecret: str("s3cr3t")}); err != nil { t.Fatal(err) } if has, _ := r.HasSecret(ctx); !has { t.Fatal("HasSecret should be true after setting a secret") } got, err := r.ClientSecret(ctx) if err != nil || got != "s3cr3t" { t.Fatalf("ClientSecret = %q, %v; want s3cr3t", got, err) } // 2) Update mit nil → Secret bleibt unverändert. if err := r.Update(ctx, UpdateInput{Enabled: true, IssuerURL: "https://idp.example/realms/x", ClientID: "eg2", ClientSecret: nil}); err != nil { t.Fatal(err) } got, _ = r.ClientSecret(ctx) if got != "s3cr3t" { t.Fatalf("secret should be preserved on nil update, got %q", got) } if s, _ := r.Get(ctx); s.ClientID != "eg2" { t.Fatalf("client_id should update to eg2, got %q", s.ClientID) } // 3) Update mit "" → Secret gelöscht. if err := r.Update(ctx, UpdateInput{Enabled: false, IssuerURL: "", ClientID: "", ClientSecret: str("")}); err != nil { t.Fatal(err) } if has, _ := r.HasSecret(ctx); has { t.Fatal("HasSecret should be false after clearing the secret") } got, _ = r.ClientSecret(ctx) if got != "" { t.Fatalf("secret should be empty after clear, got %q", got) } }