// Package racenamedirtest exposes the shared behavioural test suite that // every ports.RaceNameDirectory implementation must pass. The PostgreSQL // adapter and the in-process stub run the same cases so both back ends // stay behaviourally equivalent. // // Subtests run sequentially: the PostgreSQL adapter shares one // testcontainers instance across the suite and relies on TruncateAll // between factory invocations, which would race under t.Parallel. package racenamedirtest import ( "context" "errors" "sort" "testing" "time" "galaxy/lobby/internal/ports" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // Factory constructs a fresh ports.RaceNameDirectory for one test case. // Implementations honour the supplied clock so tests can frame // reserved_at_ms and registered_at_ms deterministically. type Factory func(now func() time.Time) ports.RaceNameDirectory // Run executes the shared behavioural suite against factory. Call it // from each adapter's _test.go file alongside any adapter-specific // assertions. func Run(t *testing.T, factory Factory) { t.Helper() t.Run("Canonicalize rejects invalid input", func(t *testing.T) { testCanonicalizeRejectsInvalid(t, factory) }) t.Run("Canonicalize is deterministic", func(t *testing.T) { testCanonicalizeDeterministic(t, factory) }) t.Run("Check empty directory", func(t *testing.T) { testCheckEmpty(t, factory) }) t.Run("Check treats actor as own holder", func(t *testing.T) { testCheckActorNotTaken(t, factory) }) t.Run("Check exposes holder and kind to other users", func(t *testing.T) { testCheckHolderAndKind(t, factory) }) t.Run("Reserve records new holding", func(t *testing.T) { testReserveRecords(t, factory) }) t.Run("Reserve idempotent for same holder same game", func(t *testing.T) { testReserveIdempotent(t, factory) }) t.Run("Reserve allows same user across games", func(t *testing.T) { testReserveCrossGame(t, factory) }) t.Run("Reserve rejects cross-user same game", func(t *testing.T) { testReserveCrossUserSameGame(t, factory) }) t.Run("Reserve rejects cross-user different games", func(t *testing.T) { testReserveCrossUserDifferentGames(t, factory) }) t.Run("Reserve rejects invalid name", func(t *testing.T) { testReserveInvalidName(t, factory) }) t.Run("ReleaseReservation missing", func(t *testing.T) { testReleaseReservationMissing(t, factory) }) t.Run("ReleaseReservation wrong holder", func(t *testing.T) { testReleaseReservationWrongHolder(t, factory) }) t.Run("ReleaseReservation clears sole binding", func(t *testing.T) { testReleaseReservationClears(t, factory) }) t.Run("ReleaseReservation swallows invalid name", func(t *testing.T) { testReleaseReservationInvalidName(t, factory) }) t.Run("ReleaseReservation keeps cross-game holding visible", func(t *testing.T) { testReleaseReservationKeepsCrossGame(t, factory) }) t.Run("MarkPendingRegistration promotes reservation", func(t *testing.T) { testMarkPendingPromotes(t, factory) }) t.Run("MarkPendingRegistration idempotent same eligible", func(t *testing.T) { testMarkPendingIdempotent(t, factory) }) t.Run("MarkPendingRegistration rejects different eligible", func(t *testing.T) { testMarkPendingDifferentEligible(t, factory) }) t.Run("MarkPendingRegistration rejects missing reservation", func(t *testing.T) { testMarkPendingMissing(t, factory) }) t.Run("ExpirePendingRegistrations empty", func(t *testing.T) { testExpirePendingEmpty(t, factory) }) t.Run("ExpirePendingRegistrations releases expired entries", func(t *testing.T) { testExpirePendingReleasesExpired(t, factory) }) t.Run("ExpirePendingRegistrations skips future entries", func(t *testing.T) { testExpirePendingSkipsFuture(t, factory) }) t.Run("ExpirePendingRegistrations idempotent replay", func(t *testing.T) { testExpirePendingIdempotent(t, factory) }) t.Run("Register converts pending to registered", func(t *testing.T) { testRegisterConverts(t, factory) }) t.Run("Register idempotent on repeat", func(t *testing.T) { testRegisterIdempotent(t, factory) }) t.Run("Register rejects missing pending", func(t *testing.T) { testRegisterMissingPending(t, factory) }) t.Run("Register rejects expired pending", func(t *testing.T) { testRegisterExpiredPending(t, factory) }) t.Run("List methods partition correctly", func(t *testing.T) { testListsPartition(t, factory) }) t.Run("ReleaseAllByUser clears every kind", func(t *testing.T) { testReleaseAllByUserClears(t, factory) }) t.Run("ReleaseAllByUser leaves other users intact", func(t *testing.T) { testReleaseAllByUserIsolated(t, factory) }) t.Run("ReleaseAllByUser idempotent", func(t *testing.T) { testReleaseAllByUserIdempotent(t, factory) }) t.Run("Honors canceled context", func(t *testing.T) { testContextCancellation(t, factory) }) } const ( raceNameA = "PilotNova" raceNameB = "Vanguard" gameA = "game-A" gameB = "game-B" userA = "user-A" userB = "user-B" ) // fixedClock returns a deterministic clock starting at instant and // advanceable via the returned callbacks. func fixedClock(instant time.Time) (now func() time.Time, advance func(delta time.Duration)) { current := instant now = func() time.Time { return current } advance = func(delta time.Duration) { current = current.Add(delta) } return now, advance } // baseTime pins a stable reference instant for deterministic timestamps // across subtests. func baseTime() time.Time { return time.Date(2026, 5, 1, 12, 0, 0, 0, time.UTC) } func testCanonicalizeRejectsInvalid(t *testing.T, factory Factory) { t.Helper() directory := factory(nil) for _, input := range []string{"", " ", "Pilot Nova", "-Pilot", "Pilot-"} { _, err := directory.Canonicalize(input) require.Error(t, err, "input %q must be rejected", input) require.ErrorIs(t, err, ports.ErrInvalidName, "input %q should expose ErrInvalidName", input) } } func testCanonicalizeDeterministic(t *testing.T, factory Factory) { t.Helper() directory := factory(nil) first, err := directory.Canonicalize("PilotNova") require.NoError(t, err) second, err := directory.Canonicalize("pilotnova") require.NoError(t, err) require.Equal(t, first, second, "case variants must produce identical canonical keys") confusable, err := directory.Canonicalize("P1l0tN0va") require.NoError(t, err) require.Equal(t, first, confusable, "anti-fraud confusables must collapse to the same canonical key") } func testCheckEmpty(t *testing.T, factory Factory) { t.Helper() directory := factory(nil) availability, err := directory.Check(context.Background(), raceNameA, userA) require.NoError(t, err) assert.False(t, availability.Taken) assert.Empty(t, availability.HolderUserID) assert.Empty(t, availability.Kind) } func testCheckActorNotTaken(t *testing.T, factory Factory) { t.Helper() directory := factory(nil) ctx := context.Background() require.NoError(t, directory.Reserve(ctx, gameA, userA, raceNameA)) availability, err := directory.Check(ctx, raceNameA, userA) require.NoError(t, err) assert.False(t, availability.Taken) assert.Equal(t, userA, availability.HolderUserID) assert.Equal(t, ports.KindReservation, availability.Kind) } func testCheckHolderAndKind(t *testing.T, factory Factory) { t.Helper() now, _ := fixedClock(baseTime()) directory := factory(now) ctx := context.Background() require.NoError(t, directory.Reserve(ctx, gameA, userA, raceNameA)) availability, err := directory.Check(ctx, raceNameA, userB) require.NoError(t, err) assert.True(t, availability.Taken) assert.Equal(t, userA, availability.HolderUserID) assert.Equal(t, ports.KindReservation, availability.Kind) eligibleUntil := now().Add(30 * 24 * time.Hour) require.NoError(t, directory.MarkPendingRegistration(ctx, gameA, userA, raceNameA, eligibleUntil)) availability, err = directory.Check(ctx, raceNameA, userB) require.NoError(t, err) assert.Equal(t, ports.KindPendingRegistration, availability.Kind) require.NoError(t, directory.Register(ctx, gameA, userA, raceNameA)) availability, err = directory.Check(ctx, raceNameA, userB) require.NoError(t, err) assert.Equal(t, ports.KindRegistered, availability.Kind) } func testReserveRecords(t *testing.T, factory Factory) { t.Helper() now, _ := fixedClock(baseTime()) directory := factory(now) ctx := context.Background() require.NoError(t, directory.Reserve(ctx, gameA, userA, raceNameA)) reservations, err := directory.ListReservations(ctx, userA) require.NoError(t, err) require.Len(t, reservations, 1) assert.Equal(t, gameA, reservations[0].GameID) assert.Equal(t, raceNameA, reservations[0].RaceName) assert.Equal(t, now().UTC().UnixMilli(), reservations[0].ReservedAtMs) } func testReserveIdempotent(t *testing.T, factory Factory) { t.Helper() directory := factory(nil) ctx := context.Background() require.NoError(t, directory.Reserve(ctx, gameA, userA, raceNameA)) require.NoError(t, directory.Reserve(ctx, gameA, userA, raceNameA)) reservations, err := directory.ListReservations(ctx, userA) require.NoError(t, err) require.Len(t, reservations, 1) } func testReserveCrossGame(t *testing.T, factory Factory) { t.Helper() directory := factory(nil) ctx := context.Background() require.NoError(t, directory.Reserve(ctx, gameA, userA, raceNameA)) require.NoError(t, directory.Reserve(ctx, gameB, userA, raceNameA)) reservations, err := directory.ListReservations(ctx, userA) require.NoError(t, err) require.Len(t, reservations, 2) sort.Slice(reservations, func(i, j int) bool { return reservations[i].GameID < reservations[j].GameID }) assert.Equal(t, gameA, reservations[0].GameID) assert.Equal(t, gameB, reservations[1].GameID) } func testReserveCrossUserSameGame(t *testing.T, factory Factory) { t.Helper() directory := factory(nil) ctx := context.Background() require.NoError(t, directory.Reserve(ctx, gameA, userA, raceNameA)) err := directory.Reserve(ctx, gameA, userB, raceNameA) require.Error(t, err) assert.ErrorIs(t, err, ports.ErrNameTaken) } func testReserveCrossUserDifferentGames(t *testing.T, factory Factory) { t.Helper() directory := factory(nil) ctx := context.Background() require.NoError(t, directory.Reserve(ctx, gameA, userA, raceNameA)) err := directory.Reserve(ctx, gameB, userB, raceNameA) require.Error(t, err) assert.ErrorIs(t, err, ports.ErrNameTaken) } func testReserveInvalidName(t *testing.T, factory Factory) { t.Helper() directory := factory(nil) ctx := context.Background() err := directory.Reserve(ctx, gameA, userA, " ") require.Error(t, err) assert.ErrorIs(t, err, ports.ErrInvalidName) } func testReleaseReservationMissing(t *testing.T, factory Factory) { t.Helper() directory := factory(nil) ctx := context.Background() require.NoError(t, directory.ReleaseReservation(ctx, gameA, userA, raceNameA)) } func testReleaseReservationWrongHolder(t *testing.T, factory Factory) { t.Helper() directory := factory(nil) ctx := context.Background() require.NoError(t, directory.Reserve(ctx, gameA, userA, raceNameA)) require.NoError(t, directory.ReleaseReservation(ctx, gameA, userB, raceNameA)) availability, err := directory.Check(ctx, raceNameA, userB) require.NoError(t, err) assert.True(t, availability.Taken) assert.Equal(t, userA, availability.HolderUserID) } func testReleaseReservationClears(t *testing.T, factory Factory) { t.Helper() directory := factory(nil) ctx := context.Background() require.NoError(t, directory.Reserve(ctx, gameA, userA, raceNameA)) require.NoError(t, directory.ReleaseReservation(ctx, gameA, userA, raceNameA)) availability, err := directory.Check(ctx, raceNameA, userB) require.NoError(t, err) assert.False(t, availability.Taken) assert.Empty(t, availability.HolderUserID) assert.Empty(t, availability.Kind) reservations, err := directory.ListReservations(ctx, userA) require.NoError(t, err) assert.Empty(t, reservations) } func testReleaseReservationInvalidName(t *testing.T, factory Factory) { t.Helper() directory := factory(nil) ctx := context.Background() require.NoError(t, directory.ReleaseReservation(ctx, gameA, userA, "")) require.NoError(t, directory.ReleaseReservation(ctx, gameA, userA, "Pilot Nova")) } func testReleaseReservationKeepsCrossGame(t *testing.T, factory Factory) { t.Helper() directory := factory(nil) ctx := context.Background() require.NoError(t, directory.Reserve(ctx, gameA, userA, raceNameA)) require.NoError(t, directory.Reserve(ctx, gameB, userA, raceNameA)) require.NoError(t, directory.ReleaseReservation(ctx, gameA, userA, raceNameA)) availability, err := directory.Check(ctx, raceNameA, userB) require.NoError(t, err) assert.True(t, availability.Taken, "other users still blocked by the remaining game B reservation") assert.Equal(t, userA, availability.HolderUserID) assert.Equal(t, ports.KindReservation, availability.Kind) reservations, err := directory.ListReservations(ctx, userA) require.NoError(t, err) require.Len(t, reservations, 1) assert.Equal(t, gameB, reservations[0].GameID) } func testMarkPendingPromotes(t *testing.T, factory Factory) { t.Helper() now, _ := fixedClock(baseTime()) directory := factory(now) ctx := context.Background() require.NoError(t, directory.Reserve(ctx, gameA, userA, raceNameA)) eligibleUntil := now().Add(30 * 24 * time.Hour) require.NoError(t, directory.MarkPendingRegistration(ctx, gameA, userA, raceNameA, eligibleUntil)) pending, err := directory.ListPendingRegistrations(ctx, userA) require.NoError(t, err) require.Len(t, pending, 1) assert.Equal(t, gameA, pending[0].GameID) assert.Equal(t, eligibleUntil.UTC().UnixMilli(), pending[0].EligibleUntilMs) reservations, err := directory.ListReservations(ctx, userA) require.NoError(t, err) assert.Empty(t, reservations, "pending entries must not appear in ListReservations") } func testMarkPendingIdempotent(t *testing.T, factory Factory) { t.Helper() now, _ := fixedClock(baseTime()) directory := factory(now) ctx := context.Background() require.NoError(t, directory.Reserve(ctx, gameA, userA, raceNameA)) eligibleUntil := now().Add(24 * time.Hour) require.NoError(t, directory.MarkPendingRegistration(ctx, gameA, userA, raceNameA, eligibleUntil)) require.NoError(t, directory.MarkPendingRegistration(ctx, gameA, userA, raceNameA, eligibleUntil)) pending, err := directory.ListPendingRegistrations(ctx, userA) require.NoError(t, err) require.Len(t, pending, 1) } func testMarkPendingDifferentEligible(t *testing.T, factory Factory) { t.Helper() now, _ := fixedClock(baseTime()) directory := factory(now) ctx := context.Background() require.NoError(t, directory.Reserve(ctx, gameA, userA, raceNameA)) eligibleUntil := now().Add(24 * time.Hour) require.NoError(t, directory.MarkPendingRegistration(ctx, gameA, userA, raceNameA, eligibleUntil)) err := directory.MarkPendingRegistration(ctx, gameA, userA, raceNameA, eligibleUntil.Add(time.Hour)) require.Error(t, err) assert.ErrorIs(t, err, ports.ErrInvalidName) } func testMarkPendingMissing(t *testing.T, factory Factory) { t.Helper() now, _ := fixedClock(baseTime()) directory := factory(now) ctx := context.Background() err := directory.MarkPendingRegistration(ctx, gameA, userA, raceNameA, now().Add(time.Hour)) require.Error(t, err) } func testExpirePendingEmpty(t *testing.T, factory Factory) { t.Helper() directory := factory(nil) expired, err := directory.ExpirePendingRegistrations(context.Background(), time.Now()) require.NoError(t, err) assert.Empty(t, expired) } func testExpirePendingReleasesExpired(t *testing.T, factory Factory) { t.Helper() now, advance := fixedClock(baseTime()) directory := factory(now) ctx := context.Background() require.NoError(t, directory.Reserve(ctx, gameA, userA, raceNameA)) eligibleUntil := now().Add(time.Hour) require.NoError(t, directory.MarkPendingRegistration(ctx, gameA, userA, raceNameA, eligibleUntil)) advance(2 * time.Hour) expired, err := directory.ExpirePendingRegistrations(ctx, now()) require.NoError(t, err) require.Len(t, expired, 1) assert.Equal(t, userA, expired[0].UserID) assert.Equal(t, gameA, expired[0].GameID) assert.Equal(t, raceNameA, expired[0].RaceName) assert.Equal(t, eligibleUntil.UTC().UnixMilli(), expired[0].EligibleUntilMs) pending, err := directory.ListPendingRegistrations(ctx, userA) require.NoError(t, err) assert.Empty(t, pending) availability, err := directory.Check(ctx, raceNameA, userB) require.NoError(t, err) assert.False(t, availability.Taken) } func testExpirePendingSkipsFuture(t *testing.T, factory Factory) { t.Helper() now, _ := fixedClock(baseTime()) directory := factory(now) ctx := context.Background() require.NoError(t, directory.Reserve(ctx, gameA, userA, raceNameA)) eligibleUntil := now().Add(time.Hour) require.NoError(t, directory.MarkPendingRegistration(ctx, gameA, userA, raceNameA, eligibleUntil)) expired, err := directory.ExpirePendingRegistrations(ctx, now()) require.NoError(t, err) assert.Empty(t, expired) } func testExpirePendingIdempotent(t *testing.T, factory Factory) { t.Helper() now, advance := fixedClock(baseTime()) directory := factory(now) ctx := context.Background() require.NoError(t, directory.Reserve(ctx, gameA, userA, raceNameA)) require.NoError(t, directory.MarkPendingRegistration(ctx, gameA, userA, raceNameA, now().Add(time.Hour))) advance(2 * time.Hour) expiredFirst, err := directory.ExpirePendingRegistrations(ctx, now()) require.NoError(t, err) require.Len(t, expiredFirst, 1) expiredSecond, err := directory.ExpirePendingRegistrations(ctx, now()) require.NoError(t, err) assert.Empty(t, expiredSecond) } func testRegisterConverts(t *testing.T, factory Factory) { t.Helper() now, _ := fixedClock(baseTime()) directory := factory(now) ctx := context.Background() require.NoError(t, directory.Reserve(ctx, gameA, userA, raceNameA)) require.NoError(t, directory.MarkPendingRegistration(ctx, gameA, userA, raceNameA, now().Add(time.Hour))) require.NoError(t, directory.Register(ctx, gameA, userA, raceNameA)) registered, err := directory.ListRegistered(ctx, userA) require.NoError(t, err) require.Len(t, registered, 1) assert.Equal(t, gameA, registered[0].SourceGameID) assert.Equal(t, raceNameA, registered[0].RaceName) assert.Equal(t, now().UTC().UnixMilli(), registered[0].RegisteredAtMs) pending, err := directory.ListPendingRegistrations(ctx, userA) require.NoError(t, err) assert.Empty(t, pending) } func testRegisterIdempotent(t *testing.T, factory Factory) { t.Helper() now, _ := fixedClock(baseTime()) directory := factory(now) ctx := context.Background() require.NoError(t, directory.Reserve(ctx, gameA, userA, raceNameA)) require.NoError(t, directory.MarkPendingRegistration(ctx, gameA, userA, raceNameA, now().Add(time.Hour))) require.NoError(t, directory.Register(ctx, gameA, userA, raceNameA)) require.NoError(t, directory.Register(ctx, gameA, userA, raceNameA)) registered, err := directory.ListRegistered(ctx, userA) require.NoError(t, err) require.Len(t, registered, 1) } func testRegisterMissingPending(t *testing.T, factory Factory) { t.Helper() directory := factory(nil) ctx := context.Background() err := directory.Register(ctx, gameA, userA, raceNameA) require.Error(t, err) assert.ErrorIs(t, err, ports.ErrPendingMissing) // Reservation without pending is also missing pending. require.NoError(t, directory.Reserve(ctx, gameA, userA, raceNameA)) err = directory.Register(ctx, gameA, userA, raceNameA) require.Error(t, err) assert.ErrorIs(t, err, ports.ErrPendingMissing) } func testRegisterExpiredPending(t *testing.T, factory Factory) { t.Helper() now, advance := fixedClock(baseTime()) directory := factory(now) ctx := context.Background() require.NoError(t, directory.Reserve(ctx, gameA, userA, raceNameA)) require.NoError(t, directory.MarkPendingRegistration(ctx, gameA, userA, raceNameA, now().Add(time.Hour))) advance(2 * time.Hour) err := directory.Register(ctx, gameA, userA, raceNameA) require.Error(t, err) assert.ErrorIs(t, err, ports.ErrPendingExpired) } func testListsPartition(t *testing.T, factory Factory) { t.Helper() now, _ := fixedClock(baseTime()) directory := factory(now) ctx := context.Background() // userA: reserved in gameA (raceNameA), pending in gameB (raceNameB), registered raceNameA after convert. require.NoError(t, directory.Reserve(ctx, gameA, userA, raceNameA)) require.NoError(t, directory.MarkPendingRegistration(ctx, gameA, userA, raceNameA, now().Add(time.Hour))) require.NoError(t, directory.Register(ctx, gameA, userA, raceNameA)) require.NoError(t, directory.Reserve(ctx, gameB, userA, raceNameB)) registered, err := directory.ListRegistered(ctx, userA) require.NoError(t, err) require.Len(t, registered, 1) assert.Equal(t, raceNameA, registered[0].RaceName) reservations, err := directory.ListReservations(ctx, userA) require.NoError(t, err) require.Len(t, reservations, 1) assert.Equal(t, raceNameB, reservations[0].RaceName) assert.Equal(t, gameB, reservations[0].GameID) pending, err := directory.ListPendingRegistrations(ctx, userA) require.NoError(t, err) assert.Empty(t, pending, "pending entry was converted to registered") } func testReleaseAllByUserClears(t *testing.T, factory Factory) { t.Helper() now, _ := fixedClock(baseTime()) directory := factory(now) ctx := context.Background() require.NoError(t, directory.Reserve(ctx, gameA, userA, raceNameA)) require.NoError(t, directory.MarkPendingRegistration(ctx, gameA, userA, raceNameA, now().Add(time.Hour))) require.NoError(t, directory.Register(ctx, gameA, userA, raceNameA)) require.NoError(t, directory.Reserve(ctx, gameB, userA, raceNameB)) require.NoError(t, directory.MarkPendingRegistration(ctx, gameB, userA, raceNameB, now().Add(2*time.Hour))) require.NoError(t, directory.ReleaseAllByUser(ctx, userA)) registered, err := directory.ListRegistered(ctx, userA) require.NoError(t, err) assert.Empty(t, registered) reservations, err := directory.ListReservations(ctx, userA) require.NoError(t, err) assert.Empty(t, reservations) pending, err := directory.ListPendingRegistrations(ctx, userA) require.NoError(t, err) assert.Empty(t, pending) availabilityA, err := directory.Check(ctx, raceNameA, userB) require.NoError(t, err) assert.False(t, availabilityA.Taken) availabilityB, err := directory.Check(ctx, raceNameB, userB) require.NoError(t, err) assert.False(t, availabilityB.Taken) } func testReleaseAllByUserIsolated(t *testing.T, factory Factory) { t.Helper() now, _ := fixedClock(baseTime()) directory := factory(now) ctx := context.Background() require.NoError(t, directory.Reserve(ctx, gameA, userA, raceNameA)) require.NoError(t, directory.Reserve(ctx, gameB, userB, raceNameB)) require.NoError(t, directory.ReleaseAllByUser(ctx, userA)) availability, err := directory.Check(ctx, raceNameB, userA) require.NoError(t, err) assert.True(t, availability.Taken) assert.Equal(t, userB, availability.HolderUserID) reservationsB, err := directory.ListReservations(ctx, userB) require.NoError(t, err) require.Len(t, reservationsB, 1) } func testReleaseAllByUserIdempotent(t *testing.T, factory Factory) { t.Helper() directory := factory(nil) ctx := context.Background() require.NoError(t, directory.ReleaseAllByUser(ctx, userA)) require.NoError(t, directory.Reserve(ctx, gameA, userA, raceNameA)) require.NoError(t, directory.ReleaseAllByUser(ctx, userA)) require.NoError(t, directory.ReleaseAllByUser(ctx, userA)) } func testContextCancellation(t *testing.T, factory Factory) { t.Helper() directory := factory(nil) ctx, cancel := context.WithCancel(context.Background()) cancel() err := directory.Reserve(ctx, gameA, userA, raceNameA) require.Error(t, err) assert.True(t, errors.Is(err, context.Canceled), "Reserve must surface context.Canceled, got %v", err) _, err = directory.Check(ctx, raceNameA, userA) require.Error(t, err) assert.True(t, errors.Is(err, context.Canceled)) err = directory.ReleaseReservation(ctx, gameA, userA, raceNameA) require.Error(t, err) assert.True(t, errors.Is(err, context.Canceled)) err = directory.MarkPendingRegistration(ctx, gameA, userA, raceNameA, time.Now()) require.Error(t, err) assert.True(t, errors.Is(err, context.Canceled)) _, err = directory.ExpirePendingRegistrations(ctx, time.Now()) require.Error(t, err) assert.True(t, errors.Is(err, context.Canceled)) err = directory.Register(ctx, gameA, userA, raceNameA) require.Error(t, err) assert.True(t, errors.Is(err, context.Canceled)) _, err = directory.ListRegistered(ctx, userA) require.Error(t, err) assert.True(t, errors.Is(err, context.Canceled)) _, err = directory.ListReservations(ctx, userA) require.Error(t, err) assert.True(t, errors.Is(err, context.Canceled)) _, err = directory.ListPendingRegistrations(ctx, userA) require.Error(t, err) assert.True(t, errors.Is(err, context.Canceled)) err = directory.ReleaseAllByUser(ctx, userA) require.Error(t, err) assert.True(t, errors.Is(err, context.Canceled)) }