- Replace gorilla/csrf with net/http CrossOriginProtection - Require valkey-password and add TLS options for session store - End session at /logout and revoke refresh tokens - Re-derive identity and roles from provider every five minutes - Process each Stripe webhook event in its own Temporal workflow - Give each outbox entry its own workflow with Temporal retries - Guard against stale Stripe events with provider timestamps - Derive transport security from base-url scheme
120 lines
3.8 KiB
Go
120 lines
3.8 KiB
Go
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Commercial
|
|
// SPDX-FileCopyrightText: 2025-2026 Christian Galo
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
_ "github.com/jackc/pgx/v5/stdlib"
|
|
"github.com/spf13/viper"
|
|
|
|
"git.coopcloud.tech/wiki-cafe/member-console/internal/config"
|
|
"git.coopcloud.tech/wiki-cafe/member-console/internal/db"
|
|
"git.coopcloud.tech/wiki-cafe/member-console/internal/integration"
|
|
)
|
|
|
|
// vcSpec is a brand-neutral fixture spec for validate-config's DB-backed
|
|
// tests: one duration key, the shape the maintainer's bogus
|
|
// fedwiki-sync-interval save exposed (proposal.md, typed-config-keys).
|
|
var vcSpec = []config.ConfigKey{
|
|
{Name: "widget-sync-interval", Default: time.Hour, Usage: "widget sync interval"},
|
|
}
|
|
|
|
// validVCConfig resets the global Viper and sets a complete, well-formed
|
|
// core configuration pointed at dsn, mirroring internal/config's own
|
|
// validConfig() test helper (this package cannot import that unexported
|
|
// helper, so it is restated here against the same key set ValidateStart
|
|
// checks unconditionally).
|
|
func validVCConfig(dsn string) {
|
|
viper.Reset()
|
|
viper.Set("db-dsn", dsn)
|
|
viper.Set("valkey-addr", "localhost:6379")
|
|
viper.Set("valkey-password", "test-store-password")
|
|
viper.Set("oidc-idp-issuer-url", "https://idp.example.com/realms/main")
|
|
viper.Set("oidc-sp-client-id", "member-console")
|
|
viper.Set("base-url", "https://console.example.com")
|
|
viper.Set("deployment-name", config.DefaultDeploymentName)
|
|
}
|
|
|
|
// TestRunValidateConfig_DB covers validate-config's two failure paths (D10,
|
|
// task 2.5) and its clean-exit path against a real (scratch) database: a
|
|
// bad stored override row, a bad environment value for the same key, and a
|
|
// clean configuration.
|
|
func TestRunValidateConfig_DB(t *testing.T) {
|
|
dsn := os.Getenv("TEST_DATABASE_URL")
|
|
if dsn == "" {
|
|
t.Skip("TEST_DATABASE_URL not set, skipping integration test")
|
|
}
|
|
|
|
database, err := sql.Open("pgx", dsn)
|
|
if err != nil {
|
|
t.Fatalf("open database: %v", err)
|
|
}
|
|
t.Cleanup(func() { database.Close() })
|
|
if err := db.RunMigrations(database, migrationSources()); err != nil {
|
|
t.Fatalf("run migrations: %v", err)
|
|
}
|
|
q := integration.New(database)
|
|
clean := func() {
|
|
if _, err := q.DeleteConfigOverride(context.Background(), "widget-sync-interval"); err != nil {
|
|
t.Fatalf("clear override: %v", err)
|
|
}
|
|
}
|
|
clean()
|
|
t.Cleanup(clean)
|
|
|
|
t.Run("a bad stored row fails naming the key, the parser's sentence, and the remediation", func(t *testing.T) {
|
|
validVCConfig(dsn)
|
|
if err := q.UpsertConfigOverride(context.Background(), integration.UpsertConfigOverrideParams{
|
|
Key: "widget-sync-interval", Value: "hello",
|
|
}); err != nil {
|
|
t.Fatalf("seed bad override: %v", err)
|
|
}
|
|
t.Cleanup(clean)
|
|
|
|
ok, message := runValidateConfig(context.Background(), vcSpec)
|
|
if ok {
|
|
t.Fatalf("want failure, got ok: %s", message)
|
|
}
|
|
for _, want := range []string{
|
|
"widget-sync-interval",
|
|
`is not a duration; for example 30m or 1h30m`,
|
|
"clear it: member-console config clear widget-sync-interval",
|
|
} {
|
|
if !strings.Contains(message, want) {
|
|
t.Errorf("message %q missing %q", message, want)
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("a bad environment value fails with the parser's sentence", func(t *testing.T) {
|
|
validVCConfig(dsn)
|
|
viper.Set("widget-sync-interval", "hello")
|
|
|
|
ok, message := runValidateConfig(context.Background(), vcSpec)
|
|
if ok {
|
|
t.Fatalf("want failure, got ok: %s", message)
|
|
}
|
|
if !strings.Contains(message, `is not a duration; for example 30m or 1h30m`) {
|
|
t.Errorf("message %q missing the parser's sentence", message)
|
|
}
|
|
})
|
|
|
|
t.Run("a clean configuration exits ok", func(t *testing.T) {
|
|
validVCConfig(dsn)
|
|
ok, message := runValidateConfig(context.Background(), vcSpec)
|
|
if !ok {
|
|
t.Fatalf("want ok, got failure: %s", message)
|
|
}
|
|
if message != "config validate: ok" {
|
|
t.Errorf("message = %q", message)
|
|
}
|
|
})
|
|
}
|