Files
minecraft-lab/tests/commands.test.mjs
2026-10-04 00:30:09 +07:00

77 lines
5.0 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import path from 'node:path';
import os from 'node:os';
import crypto from 'node:crypto';
import { spawnSync } from 'node:child_process';
import { fileURLToPath } from 'node:url';
import { parseArgs, packwizArguments } from '../platform/commands.mjs';
const repository = fileURLToPath(new URL('../', import.meta.url));
test('mod commands map to packwiz without automatic yes', () => {
assert.deepEqual(packwizArguments(parseArgs(['add', 'mouse-tweaks'])), ['modrinth', 'add', 'mouse-tweaks']);
assert.deepEqual(packwizArguments(parseArgs(['remove', 'mouse-tweaks'])), ['remove', 'mouse-tweaks']);
assert.deepEqual(packwizArguments(parseArgs(['update', 'mouse-tweaks'])), ['update', 'mouse-tweaks']);
assert.deepEqual(packwizArguments(parseArgs(['update', '--all'])), ['update', '--all']);
});
test('add forwards literal search and yes while config selects project only', () => {
const options = parseArgs(['add', 'some search $(literal)', '-y', '--config', 'platform/other.yaml']);
assert.equal(options.configFile, 'platform/other.yaml');
assert.deepEqual(packwizArguments(options), ['--yes', 'modrinth', 'add', 'some search $(literal)']);
const versionUrl = 'https://modrinth.com/mod/sophisticated-backpacks/version/pJxNzk4X';
assert.deepEqual(packwizArguments(parseArgs(['add', versionUrl])), ['modrinth', 'add', versionUrl]);
});
test('invalid argument combinations fail before any tool invocation', () => {
for (const args of [[], ['add'], ['remove'], ['update'], ['update', '--all', 'mod'], ['add', 'mod', '--all'], ['remove', 'mod', '--version-id', 'v'], ['build', '--yes'], ['add', 'mod', '--release'], ['add', 'mod', '--config'], ['update', '--all', '--all'], ['add', 'a', 'b'], ['build', '--config', '--release']]) {
assert.throws(() => parseArgs(args));
}
});
test('CLI preserves packwiz stdout/stderr/exit and configured cwd with separate argv', t => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mcpack-command-'));
t.after(() => fs.rmSync(root, { recursive: true, force: true }));
for (const file of ['platform', 'scripts', 'package.json', 'toolchain.lock.env']) fs.cpSync(path.join(repository, file), path.join(root, file), { recursive: true });
fs.symlinkSync(path.join(repository, 'node_modules'), path.join(root, 'node_modules'), 'dir');
fs.mkdirSync(path.join(root, 'custom-pack'));
fs.writeFileSync(path.join(root, 'custom-pack/pack.toml'), 'name = "fixture"\n');
const configuration = path.join(root, 'platform/config.yaml');
fs.writeFileSync(configuration, fs.readFileSync(configuration, 'utf8').replace('./pack', './custom-pack'));
fs.mkdirSync(path.join(root, '.tools/packwiz'), { recursive: true });
const executable = path.join(root, '.tools/packwiz/packwiz');
const record = path.join(root, '.tools/record.json');
fs.writeFileSync(executable, `#!/usr/bin/env node
import fs from 'node:fs';
fs.writeFileSync(${JSON.stringify(record)},JSON.stringify({cwd:process.cwd(),args:process.argv.slice(2)}));
console.log('packwiz original stdout');
console.error('packwiz original stderr');
process.exit(17);
`);
fs.chmodSync(executable, 0o755);
const sha256 = crypto.createHash('sha256').update(fs.readFileSync(executable)).digest('hex');
const lock = path.join(root, 'toolchain.lock.env');
fs.writeFileSync(lock, fs.readFileSync(lock, 'utf8').replace(/^PACKWIZ_BINARY_SHA256=.*$/m, `PACKWIZ_BINARY_SHA256=${sha256}`));
const result = spawnSync(process.execPath, [path.join(root, 'scripts/mcpack.mjs'), 'add', 'some search $(literal)', '--yes'], { cwd: os.tmpdir(), encoding: 'utf8' });
assert.equal(result.status, 17);
assert.match(result.stdout, /packwiz original stdout/);
assert.match(result.stderr, /packwiz original stderr/);
assert(!result.stderr.includes('mcpack:'));
const invocation = JSON.parse(fs.readFileSync(record));
assert.equal(invocation.cwd, path.join(root, 'custom-pack'));
assert.deepEqual(invocation.args.slice(4), ['--yes', 'modrinth', 'add', 'some search $(literal)']);
fs.appendFileSync(executable, '\n// tampered\n');
const rejected = spawnSync(process.execPath, [path.join(root, 'scripts/mcpack.mjs'), 'remove', 'fixture'], { encoding: 'utf8' });
assert.equal(rejected.status, 1);
assert.match(rejected.stderr, /checksum mismatch/);
});
test('backup and restore parse only their own complete arguments', () => {
assert.equal(parseArgs(['backup']).waitTimeout, 120);
assert.equal(parseArgs(['backup', '--target-release', '/release', '--wait-timeout', '30']).waitTimeout, 30);
assert.equal(parseArgs(['restore', '--backup', '/snapshot', '--destination', '/new']).destination, '/new');
for (const args of [['backup', '--wait-timeout', '0'], ['backup', '--wait-timeout', 'NaN'], ['backup', '--release'], ['backup', '--destination', '/new'], ['restore'], ['restore', '--backup', '/snap'], ['restore', '--backup', '/snap', '--destination', '/new', '--wait-timeout', '3'], ['build', '--target-release', '/release']]) assert.throws(() => parseArgs(args));
});