mirror of
https://github.com/tiennm99/noitu.git
synced 2026-10-05 08:14:44 +00:00
Route room inputs through one toRoom helper, iterate live seats with connected(), and share seating and reconnect-window code. Declare every error code once in errcodes.go, which the web vocabulary test now reads. Fixes: - a kicked player is released before being told, so their next action is answered not_in_a_room - a rejected word is counted before move_rejected is sent - room code draw and registration share one lock hold - two concurrent resumes with one token can no longer both take a seat - multiplayer tests ready guests one at a time instead of racing Behaviour changes: - opening or joining another room while a game is running is refused with already_in_a_game - resigning in the lobby answers game_not_started
71 lines
2.7 KiB
JavaScript
71 lines
2.7 KiB
JavaScript
// ServerError.code is a UI key, so the server decides the vocabulary and this
|
|
// file has to speak all of it. The proto schema cannot help here — the codes
|
|
// are strings in the Go transport, not an enum — so the guard reads them out
|
|
// of the one file that declares them, server/internal/wsapi/errcodes.go.
|
|
//
|
|
// Without this, adding a code on the server degrades silently to the generic
|
|
// fallback: the player is told "something went wrong" for a situation the
|
|
// server described precisely.
|
|
|
|
import { readFileSync, readdirSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { describe, expect, it } from 'vitest';
|
|
import { errorMessages } from '../src/lib/i18n/vi.js';
|
|
|
|
const wsapiDir = fileURLToPath(new URL('../../server/internal/wsapi', import.meta.url));
|
|
const registry = 'errcodes.go';
|
|
|
|
/** Every code declared in the registry, as its wire string. */
|
|
function serverErrorCodes() {
|
|
const source = readFileSync(join(wsapiDir, registry), 'utf8');
|
|
return new Set([...source.matchAll(/errCode = "([a-z_]+)"/g)].map(([, code]) => code));
|
|
}
|
|
|
|
/**
|
|
* Call sites that hand an emitter a string literal instead of a registry
|
|
* constant. Go converts an untyped literal to errCode silently, so this is
|
|
* what keeps a new code from bypassing the list the rest of this file checks.
|
|
*/
|
|
function literalCallSites() {
|
|
const files = readdirSync(wsapiDir).filter(
|
|
(f) => f.endsWith('.go') && !f.endsWith('_test.go') && f !== registry
|
|
);
|
|
return files.flatMap((file) =>
|
|
[
|
|
...readFileSync(join(wsapiDir, file), 'utf8').matchAll(
|
|
/\b(?:errorMsg|broadcastError|toRoom)\([^)]*"/g
|
|
)
|
|
].map(([call]) => `${file}: ${call}`)
|
|
);
|
|
}
|
|
|
|
describe('server error codes', () => {
|
|
const codes = serverErrorCodes();
|
|
|
|
it('finds the codes at all, so an empty match cannot pass as agreement', () => {
|
|
// A renamed type or moved registry would otherwise turn this whole file
|
|
// green by finding nothing to check.
|
|
expect(codes.size).toBeGreaterThan(10);
|
|
expect(codes.has('room_not_found')).toBe(true);
|
|
});
|
|
|
|
it('declares every code in the registry, never inline at a call site', () => {
|
|
expect(literalCallSites()).toEqual([]);
|
|
});
|
|
|
|
it('has a Vietnamese message for every code the server can send', () => {
|
|
const missing = [...codes].filter((code) => !(code in errorMessages)).sort();
|
|
expect(missing).toEqual([]);
|
|
});
|
|
|
|
it('carries no message for a code the server cannot send', () => {
|
|
// Dead copy is a smaller problem than a missing message, but it is still
|
|
// a claim about the server that has stopped being true.
|
|
const stale = Object.keys(errorMessages)
|
|
.filter((code) => !codes.has(code))
|
|
.sort();
|
|
expect(stale).toEqual([]);
|
|
});
|
|
});
|