mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-08-02 01:10:40 +00:00
update
This commit is contained in:
@@ -14,6 +14,7 @@ vi.mock("../../src/core/Schemas", async () => {
|
||||
});
|
||||
|
||||
import { GameType } from "../../src/core/game/Game";
|
||||
import { Client } from "../../src/server/Client";
|
||||
import { GameServer } from "../../src/server/GameServer";
|
||||
|
||||
describe("GameLifecycle", () => {
|
||||
@@ -86,3 +87,114 @@ describe("GameLifecycle", () => {
|
||||
expect((game as any)._hasEnded).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("GameServer.rejoinClient — clanTag identityUpdate", () => {
|
||||
let mockLogger: any;
|
||||
const mkWs = (): any => ({
|
||||
readyState: 1, // OPEN
|
||||
on: vi.fn(),
|
||||
send: vi.fn(),
|
||||
close: vi.fn(),
|
||||
removeAllListeners: vi.fn(),
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
mockLogger = {
|
||||
child: vi.fn().mockReturnThis(),
|
||||
info: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
error: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
const seedClient = (game: GameServer, clanTag: string | null) => {
|
||||
const ws = mkWs();
|
||||
const client = new Client(
|
||||
"cid-1",
|
||||
"pid-1",
|
||||
null,
|
||||
null,
|
||||
undefined,
|
||||
"127.0.0.1",
|
||||
"tester",
|
||||
clanTag,
|
||||
ws,
|
||||
undefined,
|
||||
undefined,
|
||||
[],
|
||||
);
|
||||
// Seed internals as if the client had joined normally.
|
||||
(game as any).activeClients.push(client);
|
||||
(game as any).allClients.set(client.clientID, client);
|
||||
(game as any).persistentIdToClientId.set(
|
||||
client.persistentID,
|
||||
client.clientID,
|
||||
);
|
||||
(game as any).websockets.add(ws);
|
||||
return client;
|
||||
};
|
||||
|
||||
it("preserves clanTag on reconnect when identityUpdate omits it", () => {
|
||||
const game = new GameServer("g-1", mockLogger, Date.now(), {
|
||||
gameType: GameType.Private,
|
||||
} as any);
|
||||
const client = seedClient(game, "ABC");
|
||||
|
||||
const newWs = mkWs();
|
||||
const ok = game.rejoinClient(newWs as any, "pid-1", 0, {
|
||||
username: "renamed",
|
||||
});
|
||||
|
||||
expect(ok).toBe(true);
|
||||
expect(client.clanTag).toBe("ABC");
|
||||
expect(client.username).toBe("renamed");
|
||||
});
|
||||
|
||||
it("clears clanTag on reconnect when identityUpdate passes null", () => {
|
||||
const game = new GameServer("g-2", mockLogger, Date.now(), {
|
||||
gameType: GameType.Private,
|
||||
} as any);
|
||||
const client = seedClient(game, "ABC");
|
||||
|
||||
game.rejoinClient(mkWs() as any, "pid-1", 0, {
|
||||
username: "tester",
|
||||
clanTag: null,
|
||||
});
|
||||
|
||||
expect(client.clanTag).toBeNull();
|
||||
});
|
||||
|
||||
it("updates clanTag on reconnect when identityUpdate passes a new tag", () => {
|
||||
const game = new GameServer("g-3", mockLogger, Date.now(), {
|
||||
gameType: GameType.Private,
|
||||
} as any);
|
||||
const client = seedClient(game, "ABC");
|
||||
|
||||
game.rejoinClient(mkWs() as any, "pid-1", 0, {
|
||||
username: "tester",
|
||||
clanTag: "XYZ",
|
||||
});
|
||||
|
||||
expect(client.clanTag).toBe("XYZ");
|
||||
});
|
||||
|
||||
it("does not change identity if the game has already started", () => {
|
||||
const game = new GameServer("g-4", mockLogger, Date.now(), {
|
||||
gameType: GameType.Private,
|
||||
} as any);
|
||||
const client = seedClient(game, "ABC");
|
||||
(game as any)._hasStarted = true;
|
||||
|
||||
game.rejoinClient(mkWs() as any, "pid-1", 0, {
|
||||
username: "renamed",
|
||||
clanTag: "XYZ",
|
||||
});
|
||||
|
||||
expect(client.clanTag).toBe("ABC");
|
||||
expect(client.username).toBe("tester");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
vi.mock("../../src/server/ServerEnv", () => ({
|
||||
ServerEnv: {
|
||||
jwtIssuer: () => "http://auth.test",
|
||||
apiKey: () => "test-key",
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("../../src/server/Logger", () => ({
|
||||
logger: {
|
||||
child: () => ({
|
||||
info: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
error: vi.fn(),
|
||||
}),
|
||||
},
|
||||
}));
|
||||
|
||||
import {
|
||||
_clearClanExistsCacheForTest,
|
||||
clanExistsByTag,
|
||||
} from "../../src/server/jwt";
|
||||
|
||||
const jsonResponse = (status: number, body: unknown = "") => ({
|
||||
status,
|
||||
text: async () => (typeof body === "string" ? body : JSON.stringify(body)),
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
vi.unstubAllGlobals();
|
||||
vi.clearAllMocks();
|
||||
_clearClanExistsCacheForTest();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
describe("clanExistsByTag", () => {
|
||||
it("returns true on HTTP 200", async () => {
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn(() => Promise.resolve(jsonResponse(200))),
|
||||
);
|
||||
await expect(clanExistsByTag("ABC")).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it("returns false on HTTP 404", async () => {
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn(() => Promise.resolve(jsonResponse(404))),
|
||||
);
|
||||
await expect(clanExistsByTag("XYZ")).resolves.toBe(false);
|
||||
});
|
||||
|
||||
it("returns null and fails open on unexpected status (5xx)", async () => {
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn(() => Promise.resolve(jsonResponse(503))),
|
||||
);
|
||||
await expect(clanExistsByTag("ABC")).resolves.toBeNull();
|
||||
});
|
||||
|
||||
it("returns null and fails open on rate-limit (429)", async () => {
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn(() => Promise.resolve(jsonResponse(429))),
|
||||
);
|
||||
await expect(clanExistsByTag("ABC")).resolves.toBeNull();
|
||||
});
|
||||
|
||||
it("returns null on transport error", async () => {
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn(() => Promise.reject(new Error("offline"))),
|
||||
);
|
||||
await expect(clanExistsByTag("ABC")).resolves.toBeNull();
|
||||
});
|
||||
|
||||
it("caches results across calls within TTL", async () => {
|
||||
const fetchSpy = vi.fn(() => Promise.resolve(jsonResponse(200)));
|
||||
vi.stubGlobal("fetch", fetchSpy);
|
||||
await clanExistsByTag("ABC");
|
||||
await clanExistsByTag("ABC");
|
||||
await clanExistsByTag("ABC");
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("does not cache fail-open (null) results so transient outages recover", async () => {
|
||||
const fetchSpy = vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce(jsonResponse(503))
|
||||
.mockResolvedValueOnce(jsonResponse(200));
|
||||
vi.stubGlobal("fetch", fetchSpy);
|
||||
await expect(clanExistsByTag("ABC")).resolves.toBeNull();
|
||||
await expect(clanExistsByTag("ABC")).resolves.toBe(true);
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("uppercases the tag in the URL", async () => {
|
||||
const fetchSpy = vi.fn(
|
||||
(_input: string | URL | Request, _init?: RequestInit) =>
|
||||
Promise.resolve(jsonResponse(200)),
|
||||
);
|
||||
vi.stubGlobal("fetch", fetchSpy);
|
||||
await clanExistsByTag("abc");
|
||||
const calledUrl = fetchSpy.mock.calls[0]![0] as string;
|
||||
expect(calledUrl).toContain("/public/clan/ABC/exists");
|
||||
});
|
||||
|
||||
it("treats a body {exists:false} as false on 200 (forward-compat)", async () => {
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn(() => Promise.resolve(jsonResponse(200, { exists: false }))),
|
||||
);
|
||||
await expect(clanExistsByTag("ABC")).resolves.toBe(false);
|
||||
});
|
||||
|
||||
it("caches by uppercased tag (different cases hit the same entry)", async () => {
|
||||
const fetchSpy = vi.fn(() => Promise.resolve(jsonResponse(200)));
|
||||
vi.stubGlobal("fetch", fetchSpy);
|
||||
await clanExistsByTag("abc");
|
||||
await clanExistsByTag("ABC");
|
||||
await clanExistsByTag("Abc");
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("sends Accept: application/json header", async () => {
|
||||
const fetchSpy = vi.fn(
|
||||
(_input: string | URL | Request, _init?: RequestInit) =>
|
||||
Promise.resolve(jsonResponse(200)),
|
||||
);
|
||||
vi.stubGlobal("fetch", fetchSpy);
|
||||
await clanExistsByTag("ABC");
|
||||
const init = fetchSpy.mock.calls[0]![1] as RequestInit;
|
||||
expect((init.headers as Record<string, string>).Accept).toBe(
|
||||
"application/json",
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user