112 lines
3.5 KiB
JavaScript
112 lines
3.5 KiB
JavaScript
import { describe, it } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { readFileSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
|
|
import {
|
|
parsePropertiesFile,
|
|
serializePropertiesFile,
|
|
} from '../dist/servers/properties/server-properties.util.js';
|
|
import { assertSafeServerPath } from '../dist/servers/shared/server-path.util.js';
|
|
|
|
describe('phase 3 server utilities', () => {
|
|
it('parses server.properties with types', () => {
|
|
const raw = [
|
|
'# comment',
|
|
'max-players=20',
|
|
'online-mode=false',
|
|
'motd=Hello World',
|
|
'',
|
|
].join('\n');
|
|
|
|
const parsed = parsePropertiesFile(raw);
|
|
assert.equal(parsed['max-players'], 20);
|
|
assert.equal(parsed['online-mode'], false);
|
|
assert.equal(parsed['motd'], 'Hello World');
|
|
});
|
|
|
|
it('serializes server.properties while preserving comments', () => {
|
|
const raw = '# header\nmax-players=10\nonline-mode=true\n';
|
|
const updated = parsePropertiesFile(raw);
|
|
updated['max-players'] = 25;
|
|
updated['online-mode'] = false;
|
|
|
|
const serialized = serializePropertiesFile(updated, raw);
|
|
assert.match(serialized, /^# header/);
|
|
assert.match(serialized, /max-players=25/);
|
|
assert.match(serialized, /online-mode=false/);
|
|
});
|
|
|
|
it('rejects path traversal', () => {
|
|
assert.throws(
|
|
() => assertSafeServerPath('../etc/passwd'),
|
|
(error) => error.message.includes('traversal'),
|
|
);
|
|
assert.throws(() => assertSafeServerPath('foo//bar'));
|
|
});
|
|
|
|
it('normalizes safe paths', () => {
|
|
assert.equal(assertSafeServerPath(''), '.');
|
|
assert.equal(assertSafeServerPath('logs/latest.log'), 'logs/latest.log');
|
|
assert.equal(assertSafeServerPath('\\world\\level.dat'), 'world/level.dat');
|
|
});
|
|
});
|
|
|
|
describe('phase 3 api contract', () => {
|
|
it('documents console, files, properties and players routes', () => {
|
|
const files = [
|
|
'servers/console/console.controller.ts',
|
|
'servers/files/files.controller.ts',
|
|
'servers/properties/properties.controller.ts',
|
|
'servers/players/players.controller.ts',
|
|
'servers/console/console.gateway.ts',
|
|
];
|
|
|
|
for (const file of files) {
|
|
const source = readFileSync(join(process.cwd(), 'src', file), 'utf8');
|
|
assert.ok(source.length > 0, `${file} should exist`);
|
|
}
|
|
|
|
const consoleSource = readFileSync(
|
|
join(process.cwd(), 'src', 'servers/console/console.controller.ts'),
|
|
'utf8',
|
|
);
|
|
assert.ok(consoleSource.includes("@Get('token')"));
|
|
|
|
const gatewaySource = readFileSync(
|
|
join(process.cwd(), 'src', 'servers/console/console.gateway.ts'),
|
|
'utf8',
|
|
);
|
|
assert.ok(gatewaySource.includes('console/ws'));
|
|
|
|
const filesSource = readFileSync(
|
|
join(process.cwd(), 'src', 'servers/files/files.controller.ts'),
|
|
'utf8',
|
|
);
|
|
assert.ok(filesSource.includes("@Get('content')"));
|
|
|
|
const propertiesSource = readFileSync(
|
|
join(process.cwd(), 'src', 'servers/properties/properties.controller.ts'),
|
|
'utf8',
|
|
);
|
|
assert.ok(propertiesSource.includes("@Patch()"));
|
|
|
|
const playersSource = readFileSync(
|
|
join(process.cwd(), 'src', 'servers/players/players.controller.ts'),
|
|
'utf8',
|
|
);
|
|
assert.ok(playersSource.includes('whitelist'));
|
|
|
|
const combined = [
|
|
consoleSource,
|
|
gatewaySource,
|
|
filesSource,
|
|
propertiesSource,
|
|
playersSource,
|
|
].join('\n');
|
|
for (const snippet of ['files', 'properties', 'players']) {
|
|
assert.ok(combined.includes(snippet), `missing route hint ${snippet}`);
|
|
}
|
|
});
|
|
});
|