Nothing but tests imported them: kits_t3 / kits_alt_t3 / kits_t4 / kits_t5 read kits/index.js TIER_KITS, playtest5_elements / playtest6-combat / facing the real modules, simServe checks /sim/content/kits/index.js; test/backups.test.js composes the prototypes through shared/diy.js diyRecordOf at their locked selection. check:imports passes. Docs: kits/README.md, DATA.md. Golden: no scenario moved.
56 lines
3.3 KiB
JavaScript
56 lines
3.3 KiB
JavaScript
// The HTTP server serves the battle simulation to browsers (DESIGN §14): /sim/ = server/sim read-only (`.js` only, no
|
|
// listings, the Node-only loader excluded), /data.js = the generated browser stand-in of server/data.js, never any other
|
|
// server file. The browser-side data injection (simdata.js setSimData / getSimData) is exercised in Node.
|
|
import { test, after } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { startServer, DATA_SHIM_JS } from '../../server/index.js';
|
|
import * as simdata from '../../server/sim/simdata.js';
|
|
|
|
let srv = null;
|
|
after(async () => { if (srv) await srv.close(); });
|
|
|
|
test('/sim/ serves the simulation modules read-only; nothing else of server/ is reachable', async () => {
|
|
srv = await startServer({ port: 0, host: '127.0.0.1', quiet: true });
|
|
const get = (p, headers = {}) => fetch(srv.url + p, { redirect: 'manual', headers });
|
|
for (const p of ['/sim/spec.js', '/sim/Battle.js', '/sim/simdata.js', '/sim/constants.js', '/sim/content/index.js', '/sim/content/kits/index.js', '/sim/content/support/index.js']) {
|
|
const r = await get(p);
|
|
assert.equal(r.status, 200, p);
|
|
assert.match(r.headers.get('content-type'), /^text\/javascript/, p);
|
|
assert.equal(r.headers.get('cache-control'), 'no-cache', `${p} revalidates (ETag)`);
|
|
assert.ok(r.headers.get('etag'));
|
|
const body = await r.text();
|
|
assert.ok(!/from ['"]node:/.test(body), `${p}: no static node: import`);
|
|
}
|
|
const gz = await get('/sim/Battle.js', { 'accept-encoding': 'gzip' });
|
|
assert.equal(gz.status, 200);
|
|
// the Node-only loader stays private under any spelling (case-insensitive host file systems: macOS, Windows)
|
|
for (const p of ['/sim/nodeData.js', '/sim/NODEDATA.JS', '/sim/NodeData.js', '/sim/nodedata.js', '/sim/', '/sim', '/sim/content/', '/sim/content', '/sim/../index.js', '/sim/%2e%2e/index.js', '/sim/..%2fdata.js',
|
|
'/server/index.js', '/server/data.js', '/server/match/Match.js', '/sim/.hidden.js', '/sim/constants.json', '/sim/spec.js/']) {
|
|
const r = await get(p);
|
|
assert.ok(r.status === 404 || r.status === 403 || r.status === 400, `${p} → ${r.status}`);
|
|
}
|
|
const shim = await get('/data.js');
|
|
assert.equal(shim.status, 200);
|
|
assert.match(shim.headers.get('content-type'), /^text\/javascript/);
|
|
assert.equal(await shim.text(), DATA_SHIM_JS);
|
|
assert.match(DATA_SHIM_JS, /from '\.\/sim\/simdata\.js'/, 'the stand-in reads the data injected into the sim');
|
|
const again = await get('/data.js', { 'if-none-match': shim.headers.get('etag') });
|
|
assert.equal(again.status, 304);
|
|
});
|
|
|
|
test('simdata: setSimData injects the default source (browser path) and getSimData serves it', async () => {
|
|
const before = simdata.getSimData();
|
|
try {
|
|
const raw = { chess: { chess_x_a: { chessId: 'chess_x_a', name: 'X', tier: 1, stats: { maxHp: 1234, atk: 5 } } }, enemies: {}, tokens: {}, stages: {}, waves: {} };
|
|
const ds = simdata.setSimData(raw);
|
|
assert.equal(simdata.getSimData(), raw);
|
|
assert.equal(simdata.getDefaultSource(), ds);
|
|
assert.equal(ds.getChess('chess_x_a').stats.maxHp, 1234);
|
|
assert.equal(ds.getChess('chess_char_1_01_a'), null, 'no research fallback once data is injected (both sides resolve ids alike)');
|
|
assert.equal(simdata.hasGeneratedData(), true);
|
|
} finally {
|
|
simdata.setSimData(before);
|
|
await simdata.reloadDataSources();
|
|
}
|
|
});
|