The in-process scheduler (ADR-018) needs to enumerate every user to run a discovery pass each. user_identities is the un-RLS'd map; add ListAllUsers as a plain pool query (no withUser) — the same enumerate-then-act pattern UserBySubject and the login_events gate query established. Scoping it to a single user would defeat the point; user_identities carries no RLS by design. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
137 lines
3.5 KiB
Go
137 lines
3.5 KiB
Go
package store_test
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
|
|
)
|
|
|
|
const (
|
|
subjectA = "dex|alice-123"
|
|
subjectB = "dex|bob-456"
|
|
)
|
|
|
|
func TestUserBySubjectUnknownReturnsNotFound(t *testing.T) {
|
|
ctx := context.Background()
|
|
s := newStore(t)
|
|
resetDB(t, rawPool(t))
|
|
|
|
id, found, err := s.UserBySubject(ctx, subjectA)
|
|
require.NoError(t, err)
|
|
require.False(t, found)
|
|
require.Empty(t, id)
|
|
}
|
|
|
|
func TestRegisterUserCreatesUserAndIdentity(t *testing.T) {
|
|
ctx := context.Background()
|
|
s := newStore(t)
|
|
p := rawPool(t)
|
|
resetDB(t, p)
|
|
|
|
id, err := s.RegisterUser(ctx, subjectA, "Alice")
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, id)
|
|
|
|
// Exactly one users row with the returned id and the given display name.
|
|
var users int
|
|
var name string
|
|
require.NoError(t, p.QueryRow(ctx,
|
|
`SELECT count(*), coalesce(max(display_name), '') FROM users WHERE id = $1`, id).
|
|
Scan(&users, &name))
|
|
require.Equal(t, 1, users)
|
|
require.Equal(t, "Alice", name)
|
|
|
|
// Exactly one identity row mapping the subject to that id.
|
|
var idents int
|
|
require.NoError(t, p.QueryRow(ctx,
|
|
`SELECT count(*) FROM user_identities WHERE dex_subject = $1 AND user_id = $2`,
|
|
subjectA, id).Scan(&idents))
|
|
require.Equal(t, 1, idents)
|
|
|
|
// And it now resolves straight through.
|
|
got, found, err := s.UserBySubject(ctx, subjectA)
|
|
require.NoError(t, err)
|
|
require.True(t, found)
|
|
require.Equal(t, id, got)
|
|
}
|
|
|
|
func TestRegisterUserRejectsDuplicateSubject(t *testing.T) {
|
|
ctx := context.Background()
|
|
s := newStore(t)
|
|
p := rawPool(t)
|
|
resetDB(t, p)
|
|
|
|
first, err := s.RegisterUser(ctx, subjectA, "Alice")
|
|
require.NoError(t, err)
|
|
|
|
_, err = s.RegisterUser(ctx, subjectA, "Alice Again")
|
|
require.Error(t, err)
|
|
require.True(t, errors.Is(err, store.ErrSubjectRegistered))
|
|
|
|
// No second user was created; the original mapping is intact.
|
|
var users, idents int
|
|
require.NoError(t, p.QueryRow(ctx, `SELECT count(*) FROM users`).Scan(&users))
|
|
require.NoError(t, p.QueryRow(ctx, `SELECT count(*) FROM user_identities`).Scan(&idents))
|
|
require.Equal(t, 1, users)
|
|
require.Equal(t, 1, idents)
|
|
|
|
got, found, err := s.UserBySubject(ctx, subjectA)
|
|
require.NoError(t, err)
|
|
require.True(t, found)
|
|
require.Equal(t, first, got)
|
|
}
|
|
|
|
func TestListAllUsersReturnsEveryIdentity(t *testing.T) {
|
|
ctx := context.Background()
|
|
s := newStore(t)
|
|
resetDB(t, rawPool(t))
|
|
|
|
empty, err := s.ListAllUsers(ctx)
|
|
require.NoError(t, err)
|
|
require.Empty(t, empty, "no registrations yet → empty slice")
|
|
|
|
const subjectC = "dex|carol-789"
|
|
idA, err := s.RegisterUser(ctx, subjectA, "Alice")
|
|
require.NoError(t, err)
|
|
idB, err := s.RegisterUser(ctx, subjectB, "Bob")
|
|
require.NoError(t, err)
|
|
idC, err := s.RegisterUser(ctx, subjectC, "Carol")
|
|
require.NoError(t, err)
|
|
|
|
users, err := s.ListAllUsers(ctx)
|
|
require.NoError(t, err)
|
|
require.Len(t, users, 3)
|
|
|
|
// Order is unspecified; compare as a set of (userID, subject) pairs.
|
|
got := make(map[string]string, len(users))
|
|
for _, u := range users {
|
|
got[u.DexSubject] = u.UserID
|
|
}
|
|
require.Equal(t, map[string]string{
|
|
subjectA: idA,
|
|
subjectB: idB,
|
|
subjectC: idC,
|
|
}, got)
|
|
}
|
|
|
|
func TestRegisterUserDistinctSubjectsGetDistinctUsers(t *testing.T) {
|
|
ctx := context.Background()
|
|
s := newStore(t)
|
|
p := rawPool(t)
|
|
resetDB(t, p)
|
|
|
|
idA, err := s.RegisterUser(ctx, subjectA, "Alice")
|
|
require.NoError(t, err)
|
|
idB, err := s.RegisterUser(ctx, subjectB, "Bob")
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, idA, idB)
|
|
|
|
var users int
|
|
require.NoError(t, p.QueryRow(ctx, `SELECT count(*) FROM users`).Scan(&users))
|
|
require.Equal(t, 2, users)
|
|
}
|