package store_test import ( "context" "testing" "github.com/stretchr/testify/require" "git.d-ma.be/mathias/tapir/internal/adapters/store" ) // seedUserRow inserts a bare users row (FK target for a connection) as the // superuser pool, which bypasses RLS. func seedUserRow(t *testing.T, userID string) { t.Helper() _, err := rawPool(t).Exec(context.Background(), `INSERT INTO users (id) VALUES ($1) ON CONFLICT (id) DO NOTHING`, userID) require.NoError(t, err) } func TestUpsertConnectionInsertsRow(t *testing.T) { ctx := context.Background() s := newStore(t) p := rawPool(t) resetDB(t, p) seedUserRow(t, userA) require.NoError(t, s.UpsertConnection(ctx, userA, store.Connection{ Provider: "youtube", ProviderAccount: "chan@example.com", TokenRef: "youtube/" + userA + "/refresh_token", Status: "active", })) conns, err := s.ConnectionsForUser(ctx, userA) require.NoError(t, err) require.Len(t, conns, 1) require.Equal(t, "youtube", conns[0].Provider) require.Equal(t, "chan@example.com", conns[0].ProviderAccount) require.Equal(t, "youtube/"+userA+"/refresh_token", conns[0].TokenRef) require.Equal(t, "active", conns[0].Status) require.False(t, conns[0].ConnectedAt.IsZero(), "connected_at set by the DB default") } func TestUpsertConnectionIsIdempotentOnUserProvider(t *testing.T) { ctx := context.Background() s := newStore(t) p := rawPool(t) resetDB(t, p) seedUserRow(t, userA) require.NoError(t, s.UpsertConnection(ctx, userA, store.Connection{ Provider: "youtube", TokenRef: "ref-1", Status: "active", })) // Re-connect the same provider: must update in place, not duplicate. require.NoError(t, s.UpsertConnection(ctx, userA, store.Connection{ Provider: "youtube", TokenRef: "ref-2", Status: "revoked", })) var count int require.NoError(t, p.QueryRow(ctx, `SELECT count(*) FROM video_connections WHERE user_id = $1 AND provider = 'youtube'`, userA).Scan(&count)) require.Equal(t, 1, count, "second connect must update, not duplicate") conns, err := s.ConnectionsForUser(ctx, userA) require.NoError(t, err) require.Len(t, conns, 1) require.Equal(t, "ref-2", conns[0].TokenRef, "token_ref overwritten") require.Equal(t, "revoked", conns[0].Status, "status overwritten") } func TestDeleteConnection(t *testing.T) { ctx := context.Background() s := newStore(t) resetDB(t, rawPool(t)) seedUserRow(t, userA) require.NoError(t, s.UpsertConnection(ctx, userA, store.Connection{ Provider: "youtube", TokenRef: "ref", Status: "active", })) require.NoError(t, s.DeleteConnection(ctx, userA, "youtube")) conns, err := s.ConnectionsForUser(ctx, userA) require.NoError(t, err) require.Empty(t, conns) // Deleting an absent connection is a no-op, not an error. require.NoError(t, s.DeleteConnection(ctx, userA, "youtube")) } func TestConnectionsForUserIsScoped(t *testing.T) { ctx := context.Background() s := newStore(t) resetDB(t, rawPool(t)) seedUserRow(t, userA) seedUserRow(t, userB) require.NoError(t, s.UpsertConnection(ctx, userA, store.Connection{ Provider: "youtube", TokenRef: "a-ref", Status: "active", })) // User B must not see user A's connection. connsB, err := s.ConnectionsForUser(ctx, userB) require.NoError(t, err) require.Empty(t, connsB, "user B must not see user A's connections") connsA, err := s.ConnectionsForUser(ctx, userA) require.NoError(t, err) require.Len(t, connsA, 1) }