From af1bc0e666346f64fe93c170fb4242b204493bfe Mon Sep 17 00:00:00 2001 From: Guillaume ARM Date: Sun, 14 Jun 2026 17:30:31 +0200 Subject: [PATCH] test(sandbox): fix duplicate registration assertion --- .../test-integration/duplicate.test.ts | 17 ++++++++--------- tools/trap-sandbox/test-integration/harness.ts | 6 +++++- .../test-integration/lua/gateway-client.lua | 11 ++++++++--- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/tools/trap-sandbox/test-integration/duplicate.test.ts b/tools/trap-sandbox/test-integration/duplicate.test.ts index c3b1dab..fcc17c6 100644 --- a/tools/trap-sandbox/test-integration/duplicate.test.ts +++ b/tools/trap-sandbox/test-integration/duplicate.test.ts @@ -3,9 +3,9 @@ import test from "node:test"; import { formatFailure, startCraftos, startSandbox, waitFor } from "./harness.js"; // Two real computers with the same traposId (id:label): newest-wins. After both -// complete hello, exactly one registration exists; aborting the newest clears it -// (the displaced first computer is in its 5s reconnect backoff), proving the newest -// connection is the one that's registered. +// complete hello, exactly one registration exists; aborting the displaced first +// computer does not affect the live registration, and a gateway->computer ping is +// still answered by the newest computer. test("duplicate traposId is resolved newest-wins", async () => { const sandbox = await startSandbox(); const TRAPOS_ID = "9:dup"; @@ -13,7 +13,7 @@ test("duplicate traposId is resolved newest-wins", async () => { const first = startCraftos("gateway-client.lua", { computerId: 9, computerLabel: "dup", - shellArgs: [sandbox.baseUrl, "-", "idle"], + shellArgs: [sandbox.baseUrl, "-", "idle", "60"], }); const second = startCraftos("gateway-client.lua", { computerId: 9, @@ -30,11 +30,10 @@ test("duplicate traposId is resolved newest-wins", async () => { assert.equal(sandbox.registry.count(), 1, "exactly one registration after the duplicate"); assert.ok(sandbox.registry.has("local", TRAPOS_ID)); - // Aborting the newest clears the entry; the displaced first is in reconnect - // backoff (~5s) and has not re-registered yet. - second.abort(); - await second.done.catch(() => undefined); - await waitFor(() => sandbox.registry.count() === 0, 3_000, "newest was the live registration"); + first.abort(); + await first.done.catch(() => undefined); + const payload = await sandbox.registry.request("local", TRAPOS_ID, "ping", {}, 2_000); + assert.deepEqual(payload, { traposId: TRAPOS_ID }, "newest stayed registered after first exited"); } catch (error) { throw new Error( formatFailure( diff --git a/tools/trap-sandbox/test-integration/harness.ts b/tools/trap-sandbox/test-integration/harness.ts index c8d995f..5ade9c1 100644 --- a/tools/trap-sandbox/test-integration/harness.ts +++ b/tools/trap-sandbox/test-integration/harness.ts @@ -139,7 +139,11 @@ export function startCraftos( const result = await new Promise<{ status: number | null; signal: NodeJS.Signals | null }>((resolve) => { child.once("close", (code, signal) => resolve({ status: code, signal })); - child.once("error", () => resolve({ status: null, signal: null })); + child.once("error", (err: NodeJS.ErrnoException) => { + if (err.code !== "ABORT_ERR") { + resolve({ status: null, signal: null }); + } + }); }); return { status: result.status, signal: result.signal, output: Buffer.concat(chunks).toString("utf8") }; diff --git a/tools/trap-sandbox/test-integration/lua/gateway-client.lua b/tools/trap-sandbox/test-integration/lua/gateway-client.lua index fa87e4c..5041c56 100644 --- a/tools/trap-sandbox/test-integration/lua/gateway-client.lua +++ b/tools/trap-sandbox/test-integration/lua/gateway-client.lua @@ -2,7 +2,7 @@ -- against a real gateway. Reuses startSession/request/onMessage so the test covers -- production client code + CraftOS JSON serialization quirks. -- --- Args (via shell.run): url, secret, scenario. +-- Args (via shell.run): url, secret, scenario, reconnectDelay. -- url gateway origin, e.g. ws://127.0.0.1:PORT (daemon appends /gateway) -- secret shared secret ('' => none) -- scenario health | auth | idle (default: health) @@ -15,18 +15,23 @@ local url = args[1]; local secret = args[2]; if secret == '' or secret == '-' then secret = nil; end local scenario = args[3] or 'health'; +local reconnectDelay = tonumber(args[4] or ''); local createSandbox = require('/apis/libsandbox'); local createEventLoop = require('/apis/eventloop'); _G.bootEventLoop = createEventLoop(); local sandbox = createSandbox(); -local session = sandbox.startSession({ +local sessionOpts = { eventloop = _G.bootEventLoop, url = url, secret = secret, os = os, -}); +}; +if reconnectDelay then + sessionOpts.reconnectDelay = reconnectDelay; +end +local session = sandbox.startSession(sessionOpts); -- Connect, probe the gateway, report opencodeOk. local function runHealth()