import assert from 'node:assert/strict'; import {readFile} from 'node:fs/promises'; import vm from 'node:vm'; import test from 'node:test'; import {transformSync} from 'esbuild'; const source = await readFile(new URL('../service/server/update/function_list.ts', import.meta.url), 'utf8'); const code = transformSync(source.replace(/^import .*;\n/gm, '') .replace('export async function', 'async function'), {loader: 'ts'}).code; function fixture(response = {status: 'success', data: 0}) { const reports = []; const logs = []; const list = [{path: 'inventory', name: 'inventory', i18n: [], function_list: []}]; const context = { get_function_list: async () => list, console_print: {log: value => logs.push(value), success: value => value}, socket: {connected: true, timeout(ms) {assert.equal(ms, 5000); return this;}, async emitWithAck(...args) {reports.push(args); return response;}}, }; return {context, reports, logs, list, run: () => vm.runInNewContext(`${code}\nupdate_function_list(socket, 1, is_current);`, {...context, is_current: () => context.socket.connected && context.current !== false})}; } test('function list reports grouped metadata and validates success, error and malformed ACKs', async () => { const f = fixture(); await f.run(); assert.equal(f.reports[0][0], 'update_function_list'); assert.equal(f.reports[0][1].id, 1); assert.equal(f.reports[0][1].function_list, f.list); assert.equal(f.logs.length, 1); for (const response of [undefined, null, {}, {status: 'unknown'}, {status: 'error', data: 403}]) { const failed = fixture(response); if (response === undefined) failed.context.socket.emitWithAck = async () => undefined; await assert.rejects(failed.run(), /函数列表上报失败/); assert.equal(failed.logs.length, 0); } }); test('function list never reports a late scan or ACK through a replaced session', async () => { const f = fixture(); let finish; f.context.get_function_list = () => new Promise(resolve => {finish = resolve;}); const task = f.run(); f.context.current = false; finish(f.list); await task; assert.equal(f.reports.length, 0); const late = fixture(); let ack; let started; const emitted = new Promise(resolve => {started = resolve;}); late.context.socket.emitWithAck = () => new Promise(resolve => {ack = resolve; started();}); const reporting = late.run(); await emitted; late.context.current = false; ack({status: 'success'}); await reporting; assert.equal(late.logs.length, 0); });