mirror of
https://github.com/tiennm99/noitu.git
synced 2026-10-16 00:15:15 +00:00
refactor(wsapi): dedupe routing and seating, fix ordering races
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
This commit is contained in:
1 parent
5848751c6b
commit
55abdb36c1
26 files changed
+643
-584
No files matched your search
@@ -1,7 +1,7 @@
|
||||
// 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 string literals in the Go transport, not an enum — so the guard reads
|
||||
// them out of the source that emits them.
|
||||
// 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
|
||||
@@ -14,35 +14,46 @@ 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));
|
||||
}
|
||||
|
||||
/**
|
||||
* Every error-code literal in the transport, excluding its own tests. Both the
|
||||
* one-recipient and the broadcast call sites count: a code that only ever goes
|
||||
* to both players is no less a code the client has to know.
|
||||
* 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 serverErrorCodes() {
|
||||
const codes = new Set();
|
||||
const files = readdirSync(wsapiDir).filter((f) => f.endsWith('.go') && !f.endsWith('_test.go'));
|
||||
|
||||
for (const file of files) {
|
||||
const source = readFileSync(join(wsapiDir, file), 'utf8');
|
||||
for (const [, code] of source.matchAll(/(?:errorMsg|broadcastError)\("([a-z_]+)"\)/g)) {
|
||||
codes.add(code);
|
||||
}
|
||||
}
|
||||
return codes;
|
||||
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 call sites at all, so an empty match cannot pass as agreement', () => {
|
||||
// A rename of errorMsg would otherwise turn this whole file green by
|
||||
// finding nothing to check.
|
||||
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([]);
|
||||
|
||||
Reference in new issue
Block a user