The keeper can bring back a seat a browser lost: a new token on the ledger, the old one refused and any phrase it minted voided, and a link that carries it in the fragment, minted by waving-hands-reseat.sh through a /keeper door Caddy does not proxy; the join page keeps a linked seat only once the server takes it and never over a seat that still opens, a watcher at a duel in play is told both ways back, the hall asks before forgetting a duel in play, and the server's rooms have tests of their own
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MbtUPJ24P6vc6tE1x6RS3m
This commit is contained in:
1 parent
fbe110a7bb
commit
b51934ecd6
13 files changed
+198
-20
No files matched your search
@@ -0,0 +1,49 @@
|
||||
import { mkdtempSync, rmSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
||||
import { Rooms } from './rooms';
|
||||
import { Store } from './store';
|
||||
|
||||
let dir: string;
|
||||
beforeEach(() => {
|
||||
dir = mkdtempSync(join(tmpdir(), 'rooms-'));
|
||||
});
|
||||
afterEach(() => {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
/** A table of three with Ann, who opened it, and Bo seated, the duel begun. */
|
||||
function table() {
|
||||
const rooms = new Rooms(new Store(dir));
|
||||
const host = rooms.create('Ann', 3);
|
||||
const guest = rooms.join(host.room, 'Bo');
|
||||
rooms.begin(host.room, host.token);
|
||||
return { rooms, room: host.room, ann: host.token, bo: guest.token };
|
||||
}
|
||||
|
||||
describe('rooms', () => {
|
||||
it("gives a seat a new token at the keeper's word: the old one opens nothing, and a restart keeps the change", () => {
|
||||
const { rooms, room, bo } = table();
|
||||
const phrase = rooms.transfer(room, bo).phrase;
|
||||
const { seat, token } = rooms.reseat(room, 'B');
|
||||
expect(seat.name).toBe('Bo');
|
||||
expect(rooms.seatOf(room, token).id).toBe('B');
|
||||
expect(() => rooms.seatOf(room, bo)).toThrow(/opens no seat/);
|
||||
// A phrase minted with the old token carries nothing now.
|
||||
expect(() => rooms.claim(phrase)).toThrow(/unknown or has expired/);
|
||||
const reopened = new Rooms(new Store(dir)).get(room.id);
|
||||
expect(rooms.seatOf(reopened, token).id).toBe('B');
|
||||
expect(() => rooms.seatOf(reopened, bo)).toThrow(/opens no seat/);
|
||||
});
|
||||
|
||||
it('re-seats only a wizard, and says when each last acted', () => {
|
||||
const { rooms, room, ann } = table();
|
||||
expect(() => rooms.reseat(room, 'Z')).toThrow(/No wizard sits/);
|
||||
rooms.submit(room, ann, { left: 'P', right: '-', casts: [] }, 0);
|
||||
const seats = rooms.seatsForKeeper(room);
|
||||
expect(seats.map((s) => [s.id, s.name])).toEqual([['A', 'Ann'], ['B', 'Bo']]);
|
||||
expect(seats[0].lastActed).toBeGreaterThan(0);
|
||||
expect(seats[1].lastActed).toBeNull();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user