package oidc import ( "context" "errors" "os" "testing" "github.com/jackc/pgx/v5/pgxpool" ) func setupStateTest(t *testing.T) (*StateStore, func()) { t.Helper() adminDSN := os.Getenv("TEST_ADMIN_DSN") if adminDSN == "" { t.Skip("TEST_ADMIN_DSN nicht gesetzt, Integrationstest uebersprungen") } ctx := context.Background() pool, err := pgxpool.New(ctx, adminDSN) if err != nil { t.Fatalf("pool: %v", err) } if _, err := pool.Exec(ctx, ` CREATE TABLE IF NOT EXISTS oidc_states ( state TEXT PRIMARY KEY, nonce TEXT NOT NULL, expires_at TIMESTAMPTZ NOT NULL, used_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT now() )`); err != nil { t.Fatalf("schema: %v", err) } cleanup := func() { pool.Close() } return NewStateStore(pool), cleanup } // Akzeptanzkriterium/Pruefung 3: State/Nonce-Handling gegen CSRF und Replay. func TestStateStore_ConsumeIsSingleUse(t *testing.T) { store, cleanup := setupStateTest(t) defer cleanup() ctx := context.Background() state, nonce, err := store.Generate(ctx) if err != nil { t.Fatalf("generate: %v", err) } gotNonce, err := store.Consume(ctx, state) if err != nil { t.Fatalf("consume (erste einloesung): %v", err) } if gotNonce != nonce { t.Fatalf("nonce = %q, want %q", gotNonce, nonce) } if _, err := store.Consume(ctx, state); !errors.Is(err, ErrStateInvalid) { t.Fatalf("erwartet ErrStateInvalid bei erneutem einloesen (replay), habe %v", err) } } func TestStateStore_RejectsUnknownState(t *testing.T) { store, cleanup := setupStateTest(t) defer cleanup() ctx := context.Background() if _, err := store.Consume(ctx, "niemals-ausgestellter-state"); !errors.Is(err, ErrStateInvalid) { t.Fatalf("erwartet ErrStateInvalid, habe %v", err) } }