import { spawn } from 'node:child_process'; import { describe, expect, it } from 'vitest'; import { parseProcedureOutput, parseSqlRows, sanitizeDiagnostic, stopStandalone, } from './spacetime-editor-idempotency-smoke.mjs'; describe('SpacetimeDB editor idempotency smoke parsers', () => { it('parses successful and failed procedure SATS results', () => { expect( parseProcedureOutput( '[true,[0,["project-a","owner-a"]],[1,[]]]', 'create_editor_project_and_return', ), ).toEqual({ ok: true, record: ['project-a', 'owner-a'], errorMessage: null, }); expect( parseProcedureOutput( '[false,[1,[]],[0,"幂等创建冲突"]]', 'create_editor_project_and_return', ), ).toEqual({ ok: false, record: null, errorMessage: '幂等创建冲突', }); expect( parseProcedureOutput( '[true,[1,[]],[0,["receipt-owner",[],[]]],[1,[]]]', 'delete_editor_asset_folder_and_return', ), ).toEqual({ ok: true, record: ['receipt-owner', [], []], errorMessage: null, }); }); it('parses only the rows from one JSON SQL statement', () => { const stdout = JSON.stringify([ { schema: { elements: [{ name: 'project_id' }] }, rows: [['project-a'], ['project-b']], total_duration_micros: 12, }, ]); expect(parseSqlRows(stdout, 'projects')).toEqual([ ['project-a'], ['project-b'], ]); expect(() => parseSqlRows('[]', 'projects')).toThrow( 'invalid result shape', ); }); it('redacts an isolated CLI token from diagnostics', () => { expect(sanitizeDiagnostic('failed token-private', ['token-private'])).toBe( 'failed [redacted]', ); }); }); describe('SpacetimeDB editor idempotency smoke cleanup', () => { // 本用例真会拉起真实子进程:spawn 一个常驻 node,再由 stopStandalone 在 Windows 上 // spawn taskkill /PID /T /F 杀进程树并等退出。全并行负载下这两个真实进程的启动与回收 // 会明显超过 vitest 默认的 5000ms,因此只给这一条设局部有界超时,不动全局 testTimeout // 与 maxThreads。这里不加注入桩:本用例的意义就是验真实杀进程路径,加桩等于换掉被测对象。 // stopStandalone 自身对 SIGTERM → SIGKILL 只有 5s 有界等待,20s 外层上限仍能抓住真实挂死。 it('terminates a live standalone-shaped child process', async () => { const child = spawn( process.execPath, ['-e', 'setInterval(() => {}, 1000)'], { stdio: 'ignore' }, ); await stopStandalone(child); expect(child.exitCode !== null || child.signalCode !== null).toBe(true); }, 20_000); });