Online co-op browser remake of Arknights' Stronghold Protocol: Alliance (client-simulated combat, official waves/bonds/items/strategies, 3D board, facing wheel, operator loadout, official emotes, deployment scripts).
72 lines
2.8 KiB
JavaScript
72 lines
2.8 KiB
JavaScript
// The optional local-client art manifest (data/local-assets.json, DESIGN §13): an install without it must get an
|
|
// empty manifest (200) instead of a 404, and an install with it must get the real file.
|
|
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
import http from 'node:http';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { createStaticHandler } from '../server/index.js';
|
|
|
|
const ROOT = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
|
|
function serve(dataDir) {
|
|
const handler = createStaticHandler({ publicDir: path.join(ROOT, 'public'), dataDir, sharedDir: path.join(ROOT, 'shared') });
|
|
const srv = http.createServer((req, res) => {
|
|
const [p, q] = req.url.split('?');
|
|
handler(req, res, p, q || '');
|
|
});
|
|
return new Promise((resolve) => srv.listen(0, '127.0.0.1', () => resolve(srv)));
|
|
}
|
|
|
|
function get(srv, url, method = 'GET') {
|
|
return new Promise((resolve, reject) => {
|
|
const req = http.request({ host: '127.0.0.1', port: srv.address().port, path: url, method }, (res) => {
|
|
let body = '';
|
|
res.setEncoding('utf8');
|
|
res.on('data', (d) => { body += d; });
|
|
res.on('end', () => resolve({ status: res.statusCode, type: res.headers['content-type'], body }));
|
|
});
|
|
req.on('error', reject);
|
|
req.end();
|
|
});
|
|
}
|
|
|
|
test('missing data/local-assets.json → 200 empty manifest (no 404 on installs without local art)', async () => {
|
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'sp-noart-'));
|
|
fs.writeFileSync(path.join(dir, 'config.json'), '{}');
|
|
const srv = await serve(dir);
|
|
try {
|
|
const r = await get(srv, '/data/local-assets.json');
|
|
assert.equal(r.status, 200);
|
|
assert.match(r.type, /application\/json/);
|
|
const doc = JSON.parse(r.body);
|
|
assert.deepEqual(doc.groups, {});
|
|
const h = await get(srv, '/data/local-assets.json', 'HEAD');
|
|
assert.equal(h.status, 200);
|
|
assert.equal(h.body, '');
|
|
// other missing data files still 404
|
|
assert.equal((await get(srv, '/data/nope.json')).status, 404);
|
|
assert.equal((await get(srv, '/data/sub/local-assets.json')).status, 404);
|
|
} finally {
|
|
srv.close();
|
|
fs.rmSync(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test('present data/local-assets.json is served as-is', async () => {
|
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'sp-art-'));
|
|
const doc = { version: 1, source: 'local-client', count: 1, groups: { module: { 'mar-x': { path: '/assets/local/module/mar-x.png' } } } };
|
|
fs.writeFileSync(path.join(dir, 'local-assets.json'), JSON.stringify(doc));
|
|
const srv = await serve(dir);
|
|
try {
|
|
const r = await get(srv, '/data/local-assets.json');
|
|
assert.equal(r.status, 200);
|
|
assert.deepEqual(JSON.parse(r.body), doc);
|
|
} finally {
|
|
srv.close();
|
|
fs.rmSync(dir, { recursive: true, force: true });
|
|
}
|
|
});
|