first cycle lab release

This commit is contained in:
2026-10-04 00:30:09 +07:00
parent 85f6897365
commit d134daafd8
92 changed files with 5623 additions and 9 deletions
+132
View File
@@ -0,0 +1,132 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import * as cryptoModule from 'node:crypto';
import path from 'node:path';
import { verifyRelease, readSettings, runtime } from '../platform/runtime.mjs';
import { fixture, seal } from './helpers/runtime-fixture.mjs';
test('release validation rejects tampering, incomplete coverage, and extra files', t => {
const f = fixture(t);
assert(verifyRelease(f.release).identity);
fs.appendFileSync(path.join(f.release, 'fixture.mrpack'), 'tampered');
assert.throws(() => verifyRelease(f.release), /checksum mismatch/);
seal(f.release);
fs.writeFileSync(path.join(f.release, 'extra.txt'), 'unexpected');
assert.throws(() => verifyRelease(f.release), /coverage/);
});
test('EULA/identity must be explicit and startup refusal leaves data/binding absent', t => {
const f = fixture(t), settings = path.join(f.host, '.env');
fs.writeFileSync(settings, fs.readFileSync(settings, 'utf8').replace('MC_EULA=TRUE', 'MC_EULA=FALSE'));
assert.throws(() => f.run(['up', '-d']), /EULA acceptance/);
assert(!fs.existsSync(path.join(f.host, 'runtime')));
assert(!fs.existsSync(path.join(f.host, '.mcpack-runtime.json')));
fs.appendFileSync(settings, 'MC_UID=123\n');
assert.throws(() => readSettings(settings), /Invalid local settings/);
});
test('fresh up binds identity, preserves local UID over ambient env, and same-release recreate works', t => {
const f = fixture(t);
f.env.MC_UID = '999';
assert.equal(f.run(['up', '-d']), 0);
const state = JSON.parse(fs.readFileSync(path.join(f.host, '.mcpack-runtime.json')));
assert.equal(state.project, 'fixture-server');
assert.equal(state.release_manifest_sha256, verifyRelease(f.release).identity);
const invocation = JSON.parse(fs.readFileSync(path.join(f.root, '.tools/invocation.json')));
assert.equal(invocation.uid, '1000');
assert.equal(invocation.gid, '1000');
assert.deepEqual(invocation.args.slice(0, 2), ['--project-name', 'fixture-server']);
fs.writeFileSync(path.join(f.host, 'runtime/world-probe'), 'preserve');
assert.equal(f.run(['create', '--force-recreate']), 0);
assert.equal(fs.readFileSync(path.join(f.host, 'runtime/world-probe'), 'utf8'), 'preserve');
});
test('another release/project cannot be applied to a bound runtime', t => {
const f = fixture(t);
f.run(['up', '-d']);
f.env.MC_PROJECT_NAME = 'another-server';
assert.throws(() => f.run(['up', '-d']), /bound to another/);
f.env.MC_PROJECT_NAME = 'fixture-server';
fs.appendFileSync(path.join(f.release, 'fixture.mrpack'), 'another release');
seal(f.release);
assert.throws(() => f.run(['up', '-d']), /bound to another/);
});
test('existing unidentified world cannot be silently adopted', t => {
const f = fixture(t);
fs.mkdirSync(path.join(f.host, 'runtime'));
fs.writeFileSync(path.join(f.host, 'runtime/level.dat'), 'valuable data');
assert.throws(() => f.run(['up', '-d']), /Nonempty runtime/);
assert.equal(fs.readFileSync(path.join(f.host, 'runtime/level.dat'), 'utf8'), 'valuable data');
});
test('failed Compose startup preserves binding, propagates exit code, and releases lock', t => {
const f = fixture(t);
fs.writeFileSync(path.join(f.root, '.tools/fail-start'), '');
assert.equal(f.run(['up', '-d']), 19);
assert(fs.existsSync(path.join(f.host, '.mcpack-runtime.json')));
assert(!fs.existsSync(path.join(f.host, '.mcpack-runtime.lock')));
});
test('operation lock and compiled offline contract are enforced before runtime mutation', t => {
const f = fixture(t);
fs.mkdirSync(path.join(f.host, '.mcpack-runtime.lock'));
assert.throws(() => f.run(['up', '-d']), /Another runtime operation/);
assert(!fs.existsSync(path.join(f.host, 'runtime')));
fs.rmdirSync(path.join(f.host, '.mcpack-runtime.lock'));
fs.writeFileSync(path.join(f.root, '.tools/wrong-contract'), '');
assert.throws(() => f.run(['up', '-d']), /offline release contract/);
assert(!fs.existsSync(path.join(f.host, '.mcpack-runtime.json')));
});
test('unsafe paths and Compose file/project overrides are rejected', t => {
const f = fixture(t);
assert.throws(() => f.run(['up', '-f', 'another.yaml']), /controlled by the wrapper/);
assert.throws(() => f.run(['up', '-fanother.yaml']), /controlled by the wrapper/);
f.env.MC_ENV_FILE = path.join(f.release, 'server/.env.example');
assert.throws(() => f.run(['up']), /outside the immutable release/);
delete f.env.MC_ENV_FILE;
f.env.MC_ROOT = 'relative';
assert.throws(() => f.run(['up']), /absolute path/);
});
test('existing Compose project with another data root cannot be replaced or stopped', t => {
const f = fixture(t);
fs.writeFileSync(path.join(f.root, '.tools/existing-project'), '');
assert.throws(() => f.run(['up', '-d']), /belongs to another runtime root/);
assert.throws(() => f.run(['down']), /belongs to another runtime root/);
assert(!fs.existsSync(path.join(f.host, 'runtime')));
assert(!fs.existsSync(path.join(f.host, '.mcpack-runtime.json')));
assert(!fs.existsSync(path.join(f.host, '.mcpack-runtime.lock')));
});
test('bind address is validated and host settings override ambient bind address', t => {
const f=fixture(t),settings=path.join(f.host,'.env');
assert.equal(readSettings(settings).MC_BIND_ADDRESS,'127.0.0.1');
fs.writeFileSync(settings,fs.readFileSync(settings,'utf8').replace('MC_BIND_ADDRESS=127.0.0.1','MC_BIND_ADDRESS=0.0.0.0'));
assert.equal(readSettings(settings).MC_BIND_ADDRESS,'0.0.0.0');
f.env.MC_BIND_ADDRESS='127.0.0.1';
assert.equal(f.run(['up','-d']),0);
assert.equal(JSON.parse(fs.readFileSync(path.join(f.root,'.tools/invocation.json'))).bind,'0.0.0.0');
fs.writeFileSync(settings,fs.readFileSync(settings,'utf8').replace('MC_BIND_ADDRESS=0.0.0.0','MC_BIND_ADDRESS=invalid.host'));
assert.throws(()=>readSettings(settings),/IPv4 address/);
});
test('legacy immutable Compose honors explicit host bind using a temporary port-only overlay', t => {
const f=fixture(t),repository=new URL('../',import.meta.url).pathname;
fs.copyFileSync(path.join(repository,'.tools/docker-compose'),path.join(f.root,'.tools/docker-compose'));
const image=JSON.parse(fs.readFileSync(path.join(f.release,'manifest.json'))).server.image;
const compose=`services:\n minecraft:\n image: ${image}\n environment:\n TYPE: MODRINTH\n ONLINE_MODE: "false"\n ENFORCE_SECURE_PROFILE: "false"\n MODRINTH_MODPACK: /modpacks/fixture.mrpack\n ports:\n - "127.0.0.1:25565:25565"\n volumes:\n - "\${MC_ROOT}/runtime:/data"\n - "\${MC_RELEASE_DIR}:/modpacks:ro"\n - "\${MC_RELEASE_DIR}/server/config:/config:ro"\n`;
fs.writeFileSync(path.join(f.release,'server/compose.yaml'),compose);
const manifest=JSON.parse(fs.readFileSync(path.join(f.release,'manifest.json')));
const {createHash}=cryptoModule;
manifest.tools.compose.sha256=createHash('sha256').update(fs.readFileSync(path.join(f.root,'.tools/docker-compose'))).digest('hex');
fs.writeFileSync(path.join(f.release,'manifest.json'),JSON.stringify(manifest));seal(f.release);
fs.writeFileSync(path.join(f.host,'.env'),fs.readFileSync(path.join(f.host,'.env'),'utf8').replace('MC_BIND_ADDRESS=127.0.0.1','MC_BIND_ADDRESS=0.0.0.0'));
const before=fs.readFileSync(path.join(f.release,'server/compose.yaml'),'utf8');
const result=JSON.parse(runtime({repository:f.root,env:f.env,args:['config','--format','json'],capture:true}));
assert.equal(result.services.minecraft.ports.length,1);assert.equal(result.services.minecraft.ports[0].host_ip,'0.0.0.0');
assert.equal(fs.readFileSync(path.join(f.release,'server/compose.yaml'),'utf8'),before);
assert(!fs.existsSync(path.join(f.host,'runtime')));
});