From 4e5cb4d40059bdcf6d09c42333cb6ce6f8ff4450 Mon Sep 17 00:00:00 2001 From: can1357 Date: Fri, 24 Jul 2026 03:38:15 +0200 Subject: [PATCH] test(coding-agent): fixed environment-sensitive and stale-trigger test failures - Shimmed writable select.value in export-html harness; linkedom's getter-only HTMLSelectElement.value made template.js theme assignment throw under strict mode. - Bound the oauth-flow port blocker to 127.0.0.1 explicitly; macOS lets a specific-address bind coexist with a wildcard one, so the flow bound the blocked port and never fell back. - Re-anchored atomic-rewrite race tests on rewriteEntries() after the compaction-supersede rewrite trigger was removed in cb63ebd. --- .../coding-agent/test/export-html-markdown.test.ts | 13 +++++++++++++ packages/coding-agent/test/oauth-flow.test.ts | 5 ++++- .../session-manager-atomic-rewrite-race.test.ts | 11 +++++++---- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/packages/coding-agent/test/export-html-markdown.test.ts b/packages/coding-agent/test/export-html-markdown.test.ts index a6836ab1d..7205d6117 100644 --- a/packages/coding-agent/test/export-html-markdown.test.ts +++ b/packages/coding-agent/test/export-html-markdown.test.ts @@ -45,6 +45,19 @@ function renderSession(session: MinimalSession) { value: () => ({ matches: false, addEventListener() {}, removeEventListener() {} }), configurable: true, }); + // linkedom's HTMLSelectElement.value is getter-only; template.js assigns it + // under 'use strict', which would throw. Shim a writable value like a browser. + const themeSelect = document.getElementById("theme-select"); + if (themeSelect) { + let themeValue = "auto"; + Object.defineProperty(themeSelect, "value", { + get: () => themeValue, + set: next => { + themeValue = String(next); + }, + configurable: true, + }); + } const context = vm.createContext({ window, diff --git a/packages/coding-agent/test/oauth-flow.test.ts b/packages/coding-agent/test/oauth-flow.test.ts index 79c791dc1..07fe97f0f 100644 --- a/packages/coding-agent/test/oauth-flow.test.ts +++ b/packages/coding-agent/test/oauth-flow.test.ts @@ -540,7 +540,10 @@ describe("mcp oauth flow", () => { // Here `clientId` is unset, so `MCPOAuthFlow.#tryRegisterClient` will // register the actual fallback URI with the provider and the // authorization request will use that fresh client_id. - const blocker = Bun.serve({ port: 0, fetch: () => new Response("blocker") }); + // Occupy 127.0.0.1 explicitly — the interface callback flows bind for + // `localhost` — because macOS lets a specific-address bind coexist with a + // wildcard one, which would let the flow bind the "blocked" port. + const blocker = Bun.serve({ hostname: "127.0.0.1", port: 0, fetch: () => new Response("blocker") }); const blockerPort = blocker.port; if (typeof blockerPort !== "number") { blocker.stop(true); diff --git a/packages/coding-agent/test/session-manager-atomic-rewrite-race.test.ts b/packages/coding-agent/test/session-manager-atomic-rewrite-race.test.ts index 8048e2a73..8c8bf6732 100644 --- a/packages/coding-agent/test/session-manager-atomic-rewrite-race.test.ts +++ b/packages/coding-agent/test/session-manager-atomic-rewrite-race.test.ts @@ -177,6 +177,8 @@ describe("SessionManager atomic rewrite race", () => { sessionManager.appendCompaction("older summary", "older", firstKeptEntryId, 100); await sessionManager.flush(); sessionManager.appendCompaction("newer summary", "newer", firstKeptEntryId, 80); + // Kick off a full-file rewrite that parks inside the fake storage until released. + const rewritePublished = sessionManager.rewriteEntries(); await storage.rewriteStarted.promise; sessionManager.appendMessage({ role: "user", content: "during rewrite prompt", timestamp: Date.now() }); @@ -185,6 +187,7 @@ describe("SessionManager atomic rewrite race", () => { const titlePersisted = sessionManager.setSessionName("Post rewrite title", "user", "test"); storage.allowRewrite.resolve(); + await rewritePublished; await titlePersisted; await sessionManager.flush(); sessionManager.appendMessage({ @@ -288,9 +291,10 @@ describe("SessionManager atomic rewrite race", () => { if (!firstKeptEntryId) throw new Error("Expected seeded branch entry"); sessionManager.appendCompaction("older summary", "older", firstKeptEntryId, 100); await sessionManager.flush(); - // Second compaction elides the first, scheduling a full-file rewrite that - // parks inside the fake storage until we release it. sessionManager.appendCompaction("newer summary", "newer", firstKeptEntryId, 80); + // Kick off a full-file rewrite that parks inside the fake storage until we + // release it. + const rewritePublished = sessionManager.rewriteEntries(); await storage.rewriteStarted.promise; // Simulate a Ctrl+C teardown: append a session_exit custom entry (fenced @@ -308,8 +312,7 @@ describe("SessionManager atomic rewrite race", () => { // stale body serialized before flushSync bumped the disk epoch; otherwise // the async publish would overwrite the durable exit record. storage.allowRewrite.resolve(); - await Promise.resolve(); - await Promise.resolve(); + await rewritePublished; const afterRelease = await storage.readText(sessionFile); expect(afterRelease).toContain('"customType":"session_exit"');