package store_test import ( "context" "testing" "github.com/jackc/pgx/v5/pgxpool" "github.com/stretchr/testify/require" ) // countLoginEvents counts a user's login_events via the superuser pool, which // bypasses RLS — so the assertion sees the true row count regardless of scope. func countLoginEvents(t *testing.T, p *pgxpool.Pool, userID string) int { t.Helper() var n int require.NoError(t, p.QueryRow(context.Background(), `SELECT count(*) FROM login_events WHERE user_id = $1`, userID).Scan(&n)) return n } // TestStampLoginThrottlesToOnePerDay: repeated stamps within the same day insert // exactly one row — the throttle that keeps login_events one-row-per-active-day. func TestStampLoginThrottlesToOnePerDay(t *testing.T) { ctx := context.Background() s := newStore(t) super := rawPool(t) resetDB(t, super) _, err := super.Exec(ctx, `INSERT INTO users (id) VALUES ($1)`, userA) require.NoError(t, err) for i := 0; i < 3; i++ { require.NoError(t, s.StampLogin(ctx, userA)) } require.Equal(t, 1, countLoginEvents(t, super, userA), "three same-day stamps must collapse to one row") } // TestStampLoginRecordsOncePerNewDay: with yesterday's row already present, a // stamp today is NOT throttled — it appends the day's row, so distinct active days // accumulate (the substrate the gate's distinct-week count reads). func TestStampLoginRecordsOncePerNewDay(t *testing.T) { ctx := context.Background() s := newStore(t) super := rawPool(t) resetDB(t, super) _, err := super.Exec(ctx, `INSERT INTO users (id) VALUES ($1)`, userA) require.NoError(t, err) // Seed an event dated yesterday (before today's start), so the throttle's // "row exists with seen_at >= start-of-today" probe finds nothing for today. _, err = super.Exec(ctx, `INSERT INTO login_events (user_id, seen_at) VALUES ($1, NOW() - INTERVAL '1 day')`, userA) require.NoError(t, err) require.NoError(t, s.StampLogin(ctx, userA)) require.Equal(t, 2, countLoginEvents(t, super, userA), "a stamp on a new day must append a second row") // A second stamp the same day is throttled again. require.NoError(t, s.StampLogin(ctx, userA)) require.Equal(t, 2, countLoginEvents(t, super, userA), "the same-day repeat must not add a third row") } // TestStampLoginIsUserScoped: one user's stamp lands only on that user's rows — // the throttle probe is RLS-scoped, so user B's existing same-day row neither // suppresses nor is touched by user A's stamp. func TestStampLoginIsUserScoped(t *testing.T) { ctx := context.Background() s := newStore(t) super := rawPool(t) resetDB(t, super) _, err := super.Exec(ctx, `INSERT INTO users (id) VALUES ($1), ($2)`, userA, userB) require.NoError(t, err) // B already has a same-day row; it must not throttle A's first stamp. _, err = super.Exec(ctx, `INSERT INTO login_events (user_id) VALUES ($1)`, userB) require.NoError(t, err) require.NoError(t, s.StampLogin(ctx, userA)) require.Equal(t, 1, countLoginEvents(t, super, userA), "A's stamp must record despite B's same-day row") require.Equal(t, 1, countLoginEvents(t, super, userB), "A's stamp must not touch B's rows") }