fix(multiplayer): wire a durable RankedProfileProvider

Same discovery pattern as ResultSubmitter/SessionIssuer, one level
deeper: api.Service.rankedProfile and .profile both only ever read
from an in-memory RankedProfiles map with no durable-store equivalent
at all -- not "adapter exists but unwired" this time, there was no
adapter. Every real request to GET /v1/profile/ranked or /api/v1/profile
always 404'd regardless of a player's actual rating.

Add RankedProfileProvider (an interface, not a struct-literal adapter
this time) and store.PostgresRankedProfiles reading the ratings table;
Service.rankedProfileFor prefers it when set and falls back to the
map otherwise, so every existing test/direct Service literal keeps
compiling and passing unchanged. A missing ratings row maps to the
exact same (zero value, false, nil) the map lookup already produced,
preserving existing not-found semantics rather than reinterpreting
them. LastSeasonID/SeasonHistory are deliberately left unset -- the
ratings table has no season pointer, and reconstructing history needs
its own query and display semantics, not bundled in here speculatively.

Wired into both cmd/control-plane and cmd/testkit-api. Verified
against real PostgreSQL via curl: a fresh identity's ranked profile
correctly 404s through the real adapter (same behavior as before,
now for a real reason instead of an empty map).
This commit is contained in:
Josh Creek
2026-09-01 14:20:04 +01:00
parent 8a9972b6fc
commit a1ae36a54c
4 changed files with 117 additions and 47 deletions
+12 -11
View File
@@ -54,17 +54,18 @@ func main() {
fatalf("apply migrations: %v", err)
}
handler := (&api.Service{
SessionBackend: store.PostgresSessions{DB: db},
SessionIssuer: store.PostgresSessions{DB: db},
SteamLogin: fakeSteamLogin{db: db},
QueueBackend: store.PostgresQueue{DB: db},
ProposalBackend: api.ProposalProviderFromStore(db),
ProposalPromoter: api.ProposalPromoterFromStore(db),
ServerRegistrar: api.ServerRegistrarFromStore(db),
ResultSubmitter: store.PostgresResults{DB: db},
Assignment: api.AssignmentProviderFromStore(db),
ProbeRecorder: store.PostgresQueue{DB: db},
Now: func() time.Time { return time.Now().UTC() },
SessionBackend: store.PostgresSessions{DB: db},
SessionIssuer: store.PostgresSessions{DB: db},
SteamLogin: fakeSteamLogin{db: db},
QueueBackend: store.PostgresQueue{DB: db},
ProposalBackend: api.ProposalProviderFromStore(db),
ProposalPromoter: api.ProposalPromoterFromStore(db),
ServerRegistrar: api.ServerRegistrarFromStore(db),
ResultSubmitter: store.PostgresResults{DB: db},
RankedProfileProvider: store.PostgresRankedProfiles{DB: db},
Assignment: api.AssignmentProviderFromStore(db),
ProbeRecorder: store.PostgresQueue{DB: db},
Now: func() time.Time { return time.Now().UTC() },
}).Handler()
listener, err := net.Listen("tcp", *listen)
if err != nil {