refactor(api): drop expired session cityCode fallbacks

Remove deprecated cityCode fallback logic from guess and new-game routes, simplify session handling, update tests.
This commit is contained in:
tiennm99 committed 2026-09-01 17:17:11 +07:00
1 parent bf67ae655d
commit eebe1b7d6f
3 files changed
+3 -19

No files matched your search

+2 -6
View File
@@ -65,7 +65,7 @@ export async function POST(request) {
// guessing over 331,000 km2, and the district-scale ladder would zero
// almost every honest guess. The picked region is not a secret, so this
// reveals nothing.
const pickedCode = session.pickedRegion ?? session.cityCode ?? null;
const pickedCode = session.pickedRegion ?? null;
const scoreBands = pickedCode && isRegion(pickedCode)
? bandsForBbox(getRegion(pickedCode).bbox)
: SCORE_BANDS;
@@ -76,11 +76,7 @@ export async function POST(request) {
// The region the panorama was actually in, resolved server-side when the
// round was created. Never read from the request: a client that could name
// its own region could farm any district's board.
//
// The `?? cityCode` fallback carries sessions created before this deploy.
// They have no district, so they credit province and country only. Sessions
// live 30 minutes, so this can be dropped a release from now.
const scoringRegion = session.regionCode ?? session.cityCode;
const scoringRegion = session.regionCode;
// Claim the session before writing, and score only if this request is the
// one that removed it. DEL is atomic, so exactly one of N concurrent
+1 -1
View File
@@ -122,7 +122,7 @@ export async function POST(request) {
// Only what the player picked. Neither exactLocation nor regionCode is
// exposed: the caller owns this session id, so either one would hand
// them the answer to their own round.
pickedRegion: session.pickedRegion ?? session.cityCode ?? null,
pickedRegion: session.pickedRegion ?? null,
createdAt: session.createdAt
}
});
-12
View File
@@ -103,18 +103,6 @@ describe('POST /api/guess', () => {
expect((await getLeaderboard('TPHCM-Q7'))[0].score).toBe(5);
});
it('scores a session created before the tree at province level', async () => {
// Sessions live 30 minutes, so a deploy strands some in the old shape.
// They have no district and must still score rather than throw.
await seedSession('s5', { regionCode: undefined, cityCode: 'TPHCM' });
const body = await (
await guess({ username: 'mai', sessionId: 's5', guessLat: HCMC.lat, guessLng: HCMC.lng })
).json();
expect(body.success).toBe(true);
expect(body.gameResult.levels.map((l) => l.code)).toEqual(['TPHCM', 'VN']);
});
it('lets exactly one of many concurrent submits score', async () => {
// Read-then-delete is not a guard: ten requests all read a live session,
// all delete it, and all write. DEL is atomic, so gating on its count is