Make the platform list shared across users; keep games private
Platforms no longer have a user_id: there's one global list, unique by name, that every authenticated user reads from and writes to. Games are unchanged - still private per user, still referencing platform_id. - Migration: dropped platform.user_id and its FK, UNIQUE(user_id, name) -> UNIQUE(name), index on change_seq alone instead of (user_id, change_seq). Amended the one init migration in place rather than stacking a second one, since nothing is deployed yet. - db/query.sql: platform queries no longer take a user_id parameter. DeletePlatform's NOT EXISTS guard already checked games from any user (it never filtered the subquery by user_id), so deleting a shared platform is correctly blocked by any user's games on it, not just the deleter's. - routes.go: platform handlers still require auth (the list isn't public to anonymous requests) but no longer scope by user. - Tests: updated the mocks for the new signatures, added TestListPlatformsIsSharedAcrossUsers. Verified against a real Postgres: user A creates "PC", user B sees it immediately, both can add games against it, each only sees their own games, deleting the platform is blocked while either user has a game on it, and the name stays globally unique. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5072919b61
commit
7fbd403d30
8 files changed
+93
-86
No files matched your search
@@ -119,10 +119,10 @@ func TestDeletePlatformConflictWhenReferencedByGames(t *testing.T) {
|
||||
// the 0-rows result itself and turn it into 409, not just pass through
|
||||
// whatever error (if any) the query returns.
|
||||
mockDB := authedMock()
|
||||
mockDB.GetPlatformFunc = func(ctx context.Context, arg db.GetPlatformParams) (db.GetPlatformRow, error) {
|
||||
return db.GetPlatformRow{ID: arg.ID, Name: "PC"}, nil
|
||||
mockDB.GetPlatformFunc = func(ctx context.Context, id uuid.UUID) (db.GetPlatformRow, error) {
|
||||
return db.GetPlatformRow{ID: id, Name: "PC"}, nil
|
||||
}
|
||||
mockDB.DeletePlatformFunc = func(ctx context.Context, arg db.DeletePlatformParams) (int64, error) {
|
||||
mockDB.DeletePlatformFunc = func(ctx context.Context, id uuid.UUID) (int64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ func TestDeletePlatformConflictWhenReferencedByGames(t *testing.T) {
|
||||
|
||||
func TestDeletePlatformNotFound(t *testing.T) {
|
||||
mockDB := authedMock()
|
||||
mockDB.GetPlatformFunc = func(ctx context.Context, arg db.GetPlatformParams) (db.GetPlatformRow, error) {
|
||||
mockDB.GetPlatformFunc = func(ctx context.Context, id uuid.UUID) (db.GetPlatformRow, error) {
|
||||
return db.GetPlatformRow{}, pgx.ErrNoRows
|
||||
}
|
||||
|
||||
@@ -161,9 +161,8 @@ func TestCreateGameRejectsScoreOutOfRange(t *testing.T) {
|
||||
|
||||
func TestCreatePlatform(t *testing.T) {
|
||||
mockDB := authedMock()
|
||||
mockDB.CreatePlatformFunc = func(ctx context.Context, arg db.CreatePlatformParams) (db.CreatePlatformRow, error) {
|
||||
assert.Equal(t, testUserID, arg.UserID)
|
||||
return db.CreatePlatformRow{ID: uuid.New(), Name: arg.Name}, nil
|
||||
mockDB.CreatePlatformFunc = func(ctx context.Context, name string) (db.CreatePlatformRow, error) {
|
||||
return db.CreatePlatformRow{ID: uuid.New(), Name: name}, nil
|
||||
}
|
||||
|
||||
e, _ := newTestAPI(mockDB)
|
||||
@@ -174,6 +173,27 @@ func TestCreatePlatform(t *testing.T) {
|
||||
assert.Contains(t, rec.Body.String(), "Sega Genesis")
|
||||
}
|
||||
|
||||
func TestListPlatformsIsSharedAcrossUsers(t *testing.T) {
|
||||
// Unlike games, the platform list has no user_id to scope by: every
|
||||
// authenticated user sees the same rows. ListPlatformsFunc taking no
|
||||
// user id (beyond ctx) is itself part of what's being checked here -
|
||||
// this test would fail to compile if a user_id parameter crept back in.
|
||||
calls := 0
|
||||
mockDB := authedMock()
|
||||
mockDB.ListPlatformsFunc = func(ctx context.Context) ([]db.ListPlatformsRow, error) {
|
||||
calls++
|
||||
return []db.ListPlatformsRow{{ID: uuid.New(), Name: "PC"}}, nil
|
||||
}
|
||||
|
||||
e, _ := newTestAPI(mockDB)
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
e.ServeHTTP(rec, authedRequest(http.MethodGet, "/api/platforms", ""))
|
||||
assert.Equal(t, http.StatusOK, rec.Code)
|
||||
assert.Contains(t, rec.Body.String(), "PC")
|
||||
assert.Equal(t, 1, calls)
|
||||
}
|
||||
|
||||
func TestRegisterDuplicateEmailConflicts(t *testing.T) {
|
||||
mockDB := &MockQuerier{
|
||||
CreateUserFunc: func(ctx context.Context, arg db.CreateUserParams) (db.CreateUserRow, error) {
|
||||
|
||||
Reference in new issue
Block a user