Files
ark-stronghold-protocol/test/package.test.js
T
sganggs b6c9918c46 feedback5 R6: add a player package tool with a lite dry-run
tools/package.mjs packs git-tracked runtime files and production dependencies. --lite leaves out local art, fonts and the local asset manifest. --dry-run lists the files and the uncompressed sizes and does not write a zip.
2026-10-05 16:23:47 +08:00

156 lines
6.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Player-package file selection. Dry-run only: no zip, no npm ci.
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { spawnSync } from 'node:child_process';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import {
extraPaths,
isExcluded,
packageOutIsUnsafe,
selectTracked,
textHasHomePath,
trackedFiles,
} from '../tools/package.mjs';
const ROOT = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
const SAMPLE = [
'server/index.js',
'server/sim/rng.js',
'shared/constants.js',
'public/index.html',
'public/js/main.js',
'public/vendor/preact.mjs',
'public/dev/game-mock.html',
'public/assets/board.png',
'public/fonts/fonts.css',
'data/chess.json',
'data/local-assets.json',
'tools/setup.mjs',
'tools/vendor.mjs',
'tools/fetch-assets.mjs',
'tools/assets/plan.mjs',
'tools/local-extract/extract.py',
'tools/golden.mjs',
'tools/check-imports.mjs',
'scripts/start.sh',
'docs/PLAYING.md',
'docs/DEPLOY.md',
'docs/WINDOWS.md',
'docs/ASSETS.md',
'docs/img/combat.jpg',
'docs/DESIGN.md',
'docs/SIM.md',
'docs/research/03-operators.json',
'docs/research/10-networking-hosting.md',
'test/version.test.js',
'package.json',
'package-lock.json',
'LICENSE',
'NOTICE.md',
'THIRD-PARTY-NOTICES.md',
'README.md',
'CHANGELOG.md',
'Dockerfile',
'.dockerignore',
'AGENTS.md',
'.env',
'.env.local',
'server/.env.production',
'pv/clip.mp4',
'3,9,11回合情况/note.txt',
'handoff/HANDOFF.md',
'node_modules/ws/index.js',
'.cache/x',
'eslint.config.js',
'jsconfig.json',
'types/core.js',
];
test('selectTracked keeps runtime files and drops the rest', () => {
const got = selectTracked(SAMPLE);
assert.deepEqual(got, selectTracked(SAMPLE, { lite: true }));
for (const rel of [
'server/index.js', 'tools/setup.mjs', 'tools/vendor.mjs', 'docs/PLAYING.md',
'docs/DEPLOY.md', 'public/index.html', 'data/chess.json', 'package.json',
'Dockerfile', 'scripts/start.sh', 'tools/assets/plan.mjs', 'tools/local-extract/extract.py',
]) assert.ok(got.includes(rel), rel);
for (const rel of [
'tools/golden.mjs', 'tools/check-imports.mjs', 'test/version.test.js', 'public/dev/game-mock.html',
'public/assets/board.png', 'public/fonts/fonts.css', 'docs/research/10-networking-hosting.md',
'docs/research/03-operators.json', 'docs/DESIGN.md', 'docs/SIM.md', 'AGENTS.md', '.env',
'.env.local', 'server/.env.production', 'pv/clip.mp4', '3,9,11回合情况/note.txt',
'handoff/HANDOFF.md', 'node_modules/ws/index.js', 'data/local-assets.json', 'eslint.config.js',
'types/core.js',
]) assert.ok(!got.includes(rel), rel);
assert.equal(isExcluded('docs/research/10-networking-hosting.md'), true);
});
test('lite extras skip art, fonts and the local manifest', () => {
assert.deepEqual(extraPaths({ lite: true, hasLocalManifest: true }), ['public/vendor']);
assert.deepEqual(extraPaths({ lite: false, hasLocalManifest: true }), [
'public/vendor', 'public/assets', 'public/fonts', 'data/local-assets.json',
]);
assert.deepEqual(extraPaths({ lite: false, hasLocalManifest: false }), [
'public/vendor', 'public/assets', 'public/fonts',
]);
});
test('home-directory text and unsafe output directories', () => {
assert.equal(textHasHomePath('see /Users/example/game'), 'mac-users');
assert.equal(textHasHomePath('C:\\Users\\example\\game'), 'win-users');
assert.equal(textHasHomePath('prefix /home/example/game'), 'posix-home');
assert.equal(textHasHomePath('https://github.com/example/game'), null);
assert.equal(packageOutIsUnsafe(ROOT, ROOT), true);
assert.equal(packageOutIsUnsafe(path.dirname(ROOT), ROOT), true);
assert.equal(packageOutIsUnsafe(path.join(ROOT, 'dist-pack'), ROOT), false);
});
test('real git ls-files: runtime in, dev and private out', () => {
const got = new Set(selectTracked(trackedFiles(ROOT)));
assert.ok(got.has('server/index.js'));
assert.ok(got.has('tools/setup.mjs'));
assert.ok(got.has('docs/PLAYING.md'));
assert.ok(got.has('package-lock.json'));
assert.equal(got.has('tools/golden.mjs'), false);
assert.equal(got.has('tools/package.mjs'), false);
assert.equal(got.has('test/golden.test.js'), false);
assert.equal(got.has('docs/DESIGN.md'), false);
assert.equal(got.has('eslint.config.js'), false);
for (const rel of got) {
assert.equal(rel.startsWith('docs/research/'), false, rel);
assert.equal(rel.startsWith('test/'), false, rel);
assert.equal(rel.startsWith('public/dev/'), false, rel);
assert.equal(rel.startsWith('handoff/'), false, rel);
}
});
test('dry-run lists the lite set and prints both size estimates', () => {
const lite = spawnSync(process.execPath, ['tools/package.mjs', '--dry-run', '--lite'], {
cwd: ROOT, encoding: 'utf8', maxBuffer: 32 * 1024 * 1024,
});
assert.equal(lite.status, 0, lite.stderr);
assert.match(lite.stdout, /^mode: lite/m);
assert.match(lite.stdout, /^dry-run: yes/m);
assert.match(lite.stdout, /^file server\/index\.js$/m);
assert.match(lite.stdout, /^file tools\/setup\.mjs$/m);
assert.match(lite.stdout, /^file docs\/PLAYING\.md$/m);
assert.match(lite.stdout, /^extra public\/vendor$/m);
assert.doesNotMatch(lite.stdout, /^file tools\/golden\.mjs$/m);
assert.doesNotMatch(lite.stdout, /^file test\//m);
assert.doesNotMatch(lite.stdout, /^extra public\/assets$/m);
assert.match(lite.stdout, /^uncompressed lite: \d+/m);
assert.match(lite.stdout, /^uncompressed full estimate: \d+/m);
assert.match(lite.stdout, /puppeteer-core/);
const full = spawnSync(process.execPath, ['tools/package.mjs', '--dry-run'], {
cwd: ROOT, encoding: 'utf8', maxBuffer: 32 * 1024 * 1024,
});
assert.equal(full.status, 0, full.stderr);
assert.match(full.stdout, /^mode: full/m);
assert.match(full.stdout, /^extra public\/assets$/m);
assert.match(full.stdout, /^extra public\/fonts$/m);
const assets = Number((full.stdout.match(/^assets bytes: (\d+)/m) || [])[1]);
assert.ok(assets > 300 * 1048576, `assets bytes ${assets}`);
});