chore: initial commit

This commit is contained in:
2026-10-22 17:07:08 +08:00
commit 0acef64f48
200 changed files with 35546 additions and 0 deletions
+347
View File
@@ -0,0 +1,347 @@
import assert from 'node:assert/strict';
import {EventEmitter} from 'node:events';
import {mkdtemp, mkdir, writeFile, symlink, rm} from 'node:fs/promises';
import {tmpdir} from 'node:os';
import path from 'node:path';
import {performance} from 'node:perf_hooks';
import {test} from 'node:test';
import {register} from 'tsx/esm/api';
// One module graph is essential: separate tsImport namespaces would isolate the shared WeakMap meter.
register();
const {instrument_bot, measure_sync, ResourceInfo} = await import('../bot/info/resource_metrics.ts');
const {init: CpuInfo} = await import('../bot/info/instance/used_cpu.ts');
const {NetworkCounter, init: NetworkInfo} = await import('../bot/info/instance/used_network.ts');
const {estimate_memory, init: MemoryInfo} = await import('../bot/info/instance/used_memory.ts');
const {scan_storage, init: StorageInfo} = await import('../bot/info/instance/used_storage.ts');
const {create_info_manager} = await import('../bot/info/init.ts');
const {create_function_manager} = await import('../bot/function/init.ts');
function bot() {
const bot = new EventEmitter();
bot._client = new EventEmitter();
return bot;
}
test('sync meter deduplicates nested bot/client events and preserves invocation semantics', t => {
const clock = t.mock.method(performance, 'now');
let now = 0;
clock.mock.mockImplementation(() => now);
const b = bot();
const original = b.emit;
const clientOriginal = b._client.emit;
const meter = instrument_bot(b);
b._client.on('packet', function(a, c) {
assert.equal(this, b._client);
assert.deepEqual([a, c], [1, 2]);
now += 3;
b.emit('physicsTick');
now += 2;
});
b.on('physicsTick', () => { now += 5; });
assert.equal(b._client.emit('packet', 1, 2), true);
assert.equal(meter.total(), 10);
assert.equal(b.emit('absent'), false);
const error = new Error('same error');
b.on('failure', () => { now += 7; throw error; });
assert.throws(() => b.emit('failure'), e => e === error);
assert.equal(meter.total(), 17);
const other = bot();
other.on('x', () => { now += 100; });
other.emit('x');
assert.equal(meter.total(), 17);
meter.dispose(); meter.dispose();
assert.equal(b.emit, original);
assert.equal(b._client.emit, clientOriginal);
assert.equal(Object.hasOwn(b, 'emit'), false);
});
test('sync invocation never awaits promises and disposal respects replacement hooks', async t => {
let now = 0;
t.mock.method(performance, 'now', () => now);
const b = bot();
const meter = instrument_bot(b);
let release;
const pending = new Promise(resolve => { release = resolve; });
assert.equal(measure_sync(b, () => { now += 4; return pending; }), pending);
now += 500;
release(true);
await pending;
assert.equal(meter.total(), 4);
const replacement = () => 'replacement';
b.emit = replacement;
meter.dispose();
assert.equal(b.emit, replacement);
});
test('CPU samples actual elapsed deltas and cancels timer on disposal', async t => {
t.mock.timers.enable({apis: ['setInterval']});
let now = 0;
t.mock.method(performance, 'now', () => now);
const b = bot();
const info = new CpuInfo(b);
assert.equal(info.get_info().usage_percent, null);
b.on('physicsTick', () => { now += 20; });
b.emit('physicsTick');
now = 2000;
t.mock.timers.tick(1000);
assert.equal(info.get_info().usage_percent, 1);
assert.equal(info.get_info().sampled_time_ms, 20);
assert.equal(info.get_info().interval_ms, 2000);
assert.match(info.get_info().coverage, /协议解析/);
let updates = 0;
info.on_update(() => updates++);
info.dispose();
await Promise.resolve();
t.mock.timers.tick(5000);
assert.equal(updates, 0);
assert.equal(b.listenerCount('end'), 0);
assert.throws(() => info.on_update(() => {}), /disposed/);
});
test('function manager meters synchronous run invocation, with no promise wait', async t => {
const b = bot();
b.entity = {isValid: true};
b._client.state = 'play';
const manager = await create_function_manager({bot_instance: b, bot_status: 'online'});
const meter = instrument_bot(b);
t.after(() => { manager.dispose(); meter.dispose(); });
let now = 0;
t.mock.method(performance, 'now', () => now);
b.chat = () => { now += 12; };
assert.equal(await manager.run('bot', 'chat', ['hello']), true);
assert.equal(meter.total(), 12);
});
test('socket counters use elapsed intervals and retain observed totals across resets and gaps', () => {
const counter = new NetworkCounter();
const first = {bytesRead: 100, bytesWritten: 50};
assert.equal(counter.sample(undefined, 0).available, false);
assert.equal(counter.sample(first, 100).received_bytes_per_second, null);
first.bytesRead += 300; first.bytesWritten += 100;
const sample = counter.sample(first, 1600);
assert.equal(sample.received_bytes_per_second, 200);
assert.equal(sample.sent_bytes_per_second, 100 / 1.5);
assert.equal(sample.received_bytes, 400);
const second = {bytesRead: 20, bytesWritten: 10};
assert.equal(counter.sample(second, 2000).received_bytes_per_second, null);
assert.equal(counter.sample(undefined, 2500).received_bytes, 420);
second.bytesRead = 30;
assert.equal(counter.sample(second, 3000).received_bytes, 430);
second.bytesRead = 2;
assert.equal(counter.sample(second, 4000).received_bytes_per_second, null);
assert.equal(counter.sample(second, 5000).received_bytes, 432);
});
test('memory scans own descriptors, real columns, cycles and shared backing buffers without getters', async () => {
const backing = new ArrayBuffer(1024);
const column = {a: new Uint8Array(backing), b: new DataView(backing)};
Object.defineProperty(column.a, 'buffer', {get() { throw new Error('no getter'); }});
const b = bot();
b.registry = {large: new ArrayBuffer(100000)};
b.world = {getColumns: () => [{chunkX: 0, chunkZ: 0, column}], external: b.registry};
b.inventory = {slots: [column.a], registry: b.registry};
b.currentWindow = b.inventory;
b.entities = {one: {name: 'one'}};
b.players = {one: b.entities.one};
b.entities.one.self = b.entities.one;
const original = await estimate_memory(b);
assert.equal(original.partial, false);
assert.ok(original.total_bytes >= 1024 && original.total_bytes < 3000);
const duplicated = await estimate_memory({...b, currentWindow: {...b.inventory}});
assert.ok(duplicated.total_bytes > original.total_bytes);
Object.defineProperty(b.entities.one, 'danger', {get() { throw new Error('getter executed'); }});
assert.equal((await estimate_memory(b)).partial, true);
assert.equal((await estimate_memory(b, () => true)).partial, true);
assert.equal((await estimate_memory(b, () => false, {nodes: 1, properties: 10, milliseconds: 1000})).partial, true);
b.world.getColumns = () => { throw new Error('unavailable'); };
assert.equal((await estimate_memory(b)).partial, true);
});
for (const hugeCategory of ['chunks', 'entities']) {
for (const cap of ['nodes', 'properties']) {
test(`memory reserves category budgets with huge ${hugeCategory} and a ${cap} cap`, async () => {
const b = bot();
// Wide objects exercise ownKeys; nested objects exercise independent node limits.
const huge = Object.fromEntries(Array.from({length: 10000}, (_, i) => [`key${i}`, {value: i}]));
b.world = {getColumns: () => [{column: hugeCategory === 'chunks' ? huge : {data: new Uint8Array(64)}}]};
b.entities = hugeCategory === 'entities' ? huge : {one: {name: 'entity'}};
b.inventory = {slots: [{name: 'stone'}]};
b.time = {age: 100n};
const limits = {nodes: 100000, properties: 100000, milliseconds: 10000, [cap]: 80};
const result = await estimate_memory(b, () => false, limits);
for (const category of ['chunks', 'entities', 'inventory', 'other']) {
assert.ok(result[`${category}_bytes`] > 0, category);
assert.equal(result.categories_complete[category], category !== hugeCategory, category);
}
assert.equal(result.partial, true);
assert.equal(result.total_bytes, result.chunks_bytes + result.entities_bytes
+ result.inventory_bytes + result.other_bytes);
});
}
}
test('memory deduplicates shared objects and backing stores across categories deterministically', async () => {
const sample = async size => {
const b = bot();
const backing = new ArrayBuffer(size);
const shared = {view: new Uint8Array(backing)};
shared.self = shared;
b.world = {getColumns: () => [{column: {shared, view: new DataView(backing)}}]};
b.entities = {shared, view: new Uint8Array(backing)};
b.inventory = {shared, view: new DataView(backing)};
b.game = {shared, backing};
return estimate_memory(b, () => false, {nodes: 1000, properties: 1000, milliseconds: 10000});
};
const small = await sample(100);
const large = await sample(1100);
assert.equal(large.total_bytes - small.total_bytes, 1000);
assert.deepEqual(large, await sample(1100));
assert.deepEqual(large.categories_complete, {chunks: true, entities: true, inventory: true, other: true});
assert.equal(large.partial, false);
});
test('memory scans only allowlisted other roots and reports omissions without calling getters', async () => {
const b = bot();
const roots = ['game', 'time', 'settings', 'scoreboards', 'teams', 'tablist'];
for (const key of roots) {
const result = await estimate_memory(Object.assign(bot(), {[key]: {data: 'owned'}}));
assert.ok(result.other_bytes > 0, key);
assert.equal(result.categories_complete.other, true);
}
b.unlisted = {data: new ArrayBuffer(1000000)};
assert.equal((await estimate_memory(b)).other_bytes, 0);
let getters = 0;
Object.defineProperty(b, 'game', {get() { getters++; return {}; }});
b.world = {};
Object.defineProperty(b.world, 'getColumns', {get() { getters++; return () => []; }});
b.entities = {one: {name: 'one'}};
Object.defineProperty(b.entities.one, 'danger', {get() { getters++; return 123; }});
const result = await estimate_memory(b);
assert.equal(getters, 0);
assert.deepEqual(result.categories_complete, {chunks: false, entities: false, inventory: true, other: false});
assert.equal(result.partial, true);
for (const limits of [{nodes: 0, properties: 100, milliseconds: 1000},
{nodes: 100, properties: 0, milliseconds: 1000},
{nodes: 100, properties: 100, milliseconds: 0}]) {
const capped = await estimate_memory(b, () => false, limits);
assert.equal(capped.categories_complete.entities, false);
assert.equal(capped.entities_bytes, 0);
assert.equal(capped.partial, true);
}
const cancelled = await estimate_memory(b, () => true);
assert.deepEqual(cancelled.categories_complete, {chunks: false, entities: false, inventory: false, other: false});
});
test('memory marks column and unsupported collection truncation per category', async () => {
const b = bot();
b.world = {getColumns: () => Array.from({length: 4097}, () => ({column: {}}))};
b.game = new Map([['data', 'omitted']]);
const result = await estimate_memory(b, () => false, {nodes: 50000, properties: 50000, milliseconds: 10000});
assert.deepEqual(result.categories_complete, {chunks: false, entities: true, inventory: true, other: false});
assert.equal(result.partial, true);
});
test('storage scans nested files without following symlinks and reports missing, error and bounds', async t => {
const directory = await mkdtemp(path.join(tmpdir(), 'mf-resource-'));
t.after(() => rm(directory, {recursive: true, force: true}));
await mkdir(path.join(directory, 'nested'));
await writeFile(path.join(directory, 'a'), 'abc');
await writeFile(path.join(directory, 'nested', 'b'), '12345');
await symlink(directory, path.join(directory, 'nested', 'loop'));
await symlink(path.join(directory, 'a'), path.join(directory, 'alias'));
assert.deepEqual(await scan_storage(directory), {available: true, total_bytes: 8, file_count: 2, partial: false});
assert.deepEqual(await scan_storage(path.join(directory, 'missing')),
{available: true, total_bytes: 0, file_count: 0, partial: false});
assert.equal((await scan_storage(path.join(directory, 'a'))).available, false);
assert.equal((await scan_storage(directory, () => true)).partial, true);
assert.equal((await scan_storage(directory, () => false, {entries: 2, depth: 32, milliseconds: 1000})).partial, true);
assert.equal((await scan_storage(directory, () => false, {entries: 100, depth: 1, milliseconds: 1000})).partial, true);
});
test('resource modules have complete field metadata, no end listeners and safe async disposal', async t => {
t.mock.timers.enable({apis: ['setInterval']});
const b = bot();
const infos = [new CpuInfo(b), new NetworkInfo(b), new MemoryInfo(b), new StorageInfo(b)];
assert.deepEqual(infos[2].get_info().categories_complete,
{chunks: false, entities: false, inventory: false, other: false});
assert.equal(infos[2].get_info().partial, true);
let updates = 0;
for (const info of infos) {
assert.equal(info.i18n.lang, 'zh-CN');
assert.match(info.i18n.name, /[\u4e00-\u9fff]/u);
assert.match(info.i18n.description, /[\u4e00-\u9fff]/u);
for (const key of Object.keys(info.get_info())) {
const field = info.i18n.fields[key];
assert.ok(field, key);
assert.equal(field.lang, 'zh-CN');
assert.notEqual(field.name, key);
assert.match(field.name, /[\u4e00-\u9fff]/u);
assert.match(field.description, /[\u4e00-\u9fff]/u);
}
const checkFields = (value, metadata) => {
for (const [key, entry] of Object.entries(value)) {
const field = metadata.fields[key];
assert.ok(field, key);
assert.equal(field.lang, 'zh-CN');
assert.match(field.name, /[\u4e00-\u9fff]/u);
assert.match(field.description, /[\u4e00-\u9fff]/u);
if (entry && typeof entry === 'object') checkFields(entry, field);
}
};
checkFields(info.get_info(), info.i18n);
info.on_update(() => updates++);
}
assert.equal(b.listenerCount('end'), 0);
for (const info of infos) { info.dispose(); info.dispose(); }
await Promise.resolve();
t.mock.timers.tick(60000);
assert.equal(updates, 0);
assert.equal(infos[3].get_info().available, false);
});
test('async resource sampling does not overlap or publish after disposal', async t => {
t.mock.timers.enable({apis: ['setInterval']});
let release;
let calls = 0;
class Sample extends ResourceInfo {
name = 'sample';
i18n = {lang: 'en', name: 'Sample', description: ''};
constructor() {
super(0);
this.start(1000, async () => {
calls++;
await new Promise(resolve => { release = resolve; });
this.publish(1);
}, true);
}
}
const sample = new Sample();
t.mock.timers.tick(30000);
assert.equal(calls, 1);
sample.dispose();
release();
await Promise.resolve();
await Promise.resolve();
assert.equal(sample.get_info(), 0);
t.mock.timers.tick(30000);
assert.equal(calls, 1);
});
test('InfoManager discovers all resource modules and owns wrappers through end/recreation', async () => {
const b = bot();
const original = b.emit;
const manager = await create_info_manager(b);
for (const name of ['used_cpu', 'used_memory', 'used_network', 'used_storage']) {
assert.ok(manager.get_info(name), name);
}
assert.equal(b.listenerCount('end'), 1);
assert.notEqual(b.emit, original);
b.emit('end');
assert.equal(b.emit, original);
assert.equal(b.listenerCount('end'), 0);
assert.equal(manager.get_all_info().length, 0);
const next = await create_info_manager(b);
next.dispose();
assert.equal(b.emit, original);
});