From 567945d1e21a7b92ff147b3645ab08f1fe00af25 Mon Sep 17 00:00:00 2001 From: antoine-berger <64917674+antoine-berger@users.noreply.github.com> Date: Wed, 9 Sep 2026 16:35:03 -0500 Subject: [PATCH 1/3] test(harness): complete the auth mock in the definition list wiring test [SAP-3214] The server now imports credentialsFilePath from @sapiom/mcp/auth for the credential store observer (4fd155b9), which landed on main after #880 was rebased. The wiring test's mock did not export it, so the test failed on main while it passed on the PR's own CI. The mock now spreads the real module and only replaces the credential store and the browser flow, with credentialsFilePath pointing at a store that does not exist. Co-Authored-By: Claude Fable 5.1 --- .../src/server/definition-list-enrichment.test.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/harness/src/server/definition-list-enrichment.test.ts b/packages/harness/src/server/definition-list-enrichment.test.ts index ad05e99c0..67dc5759c 100644 --- a/packages/harness/src/server/definition-list-enrichment.test.ts +++ b/packages/harness/src/server/definition-list-enrichment.test.ts @@ -14,7 +14,17 @@ import * as path from "node:path"; import { fileURLToPath } from "node:url"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -vi.mock("@sapiom/mcp/auth", () => ({ +const authFixture = vi.hoisted(() => ({ + // A store that does not exist: the credential observer sees ENOENT and + // stays quiet, and nothing under ~/.sapiom is ever watched or written. + credentialsPath: `${process.env.TMPDIR ?? "/tmp"}/sap3214-enrichment-missing/credentials.json`, +})); + +// Every other export stays real so a new import in the server cannot turn +// this fake into a missing-export failure; only the credential store and the +// browser flow are replaced. +vi.mock("@sapiom/mcp/auth", async (importOriginal) => ({ + ...(await importOriginal()), resolveEnvironment: vi.fn(async (environment?: string) => ({ name: environment === "dev" ? "staging" : "production", appURL: "https://app.example.test", @@ -29,6 +39,7 @@ vi.mock("@sapiom/mcp/auth", () => ({ }), writeCredentials: vi.fn(async () => {}), clearCredentials: vi.fn(async () => {}), + credentialsFilePath: vi.fn(() => authFixture.credentialsPath), })); import type { From b640c60cd6ac30dba7835f07bc7eabd0c05a6904 Mon Sep 17 00:00:00 2001 From: antoine-berger <64917674+antoine-berger@users.noreply.github.com> Date: Wed, 9 Sep 2026 17:29:08 -0500 Subject: [PATCH 2/3] test(harness): partial auth mock in both server wiring tests [SAP-3214] Apply the importOriginal spread to auth-mcp-wiring.test.ts as well, the only other test that boots startServer() behind a full @sapiom/mcp/auth mock. State in the enrichment test's header that its mock is partial and which exports are replaced, and use a plain /tmp literal for the missing credential store, matching the sibling test. Co-Authored-By: Claude Fable 5.1 --- packages/harness/src/server/auth-mcp-wiring.test.ts | 6 +++++- .../src/server/definition-list-enrichment.test.ts | 12 +++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/harness/src/server/auth-mcp-wiring.test.ts b/packages/harness/src/server/auth-mcp-wiring.test.ts index d4be43e26..57c73c51e 100644 --- a/packages/harness/src/server/auth-mcp-wiring.test.ts +++ b/packages/harness/src/server/auth-mcp-wiring.test.ts @@ -30,7 +30,10 @@ const authFixture = vi.hoisted(() => ({ }, })); -vi.mock("@sapiom/mcp/auth", () => { +// Partial mock: every export not listed below stays real, so a new auth +// import in the server cannot fail here as a missing mock export. +vi.mock("@sapiom/mcp/auth", async (importOriginal) => { + const actual = await importOriginal(); const resolveEnvironment = vi.fn(async (environment?: string) => { const requested = environment ?? "production"; const name = @@ -57,6 +60,7 @@ vi.mock("@sapiom/mcp/auth", () => { }); return { + ...actual, resolveEnvironment, readCredentials: vi.fn(async () => authFixture.credential), readCredentialsOrThrow: vi.fn(async () => { diff --git a/packages/harness/src/server/definition-list-enrichment.test.ts b/packages/harness/src/server/definition-list-enrichment.test.ts index 67dc5759c..e229494c3 100644 --- a/packages/harness/src/server/definition-list-enrichment.test.ts +++ b/packages/harness/src/server/definition-list-enrichment.test.ts @@ -1,8 +1,10 @@ /** * Wiring regression for SAP-3214: one tenant-scoped list request per pass, * never a by-id request for a definition the account can't see. Fake Agents - * API counting list and detail requests; `@sapiom/mcp/auth` mocked so the - * disconnect route never touches ~/.sapiom. + * API counting list and detail requests. `@sapiom/mcp/auth` is a PARTIAL + * mock: the credential store, the browser flow and `credentialsFilePath` are + * replaced so the disconnect route and the credential observer never touch + * ~/.sapiom; every other export runs for real. */ import { createServer as createHttpServer, @@ -17,12 +19,12 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; const authFixture = vi.hoisted(() => ({ // A store that does not exist: the credential observer sees ENOENT and // stays quiet, and nothing under ~/.sapiom is ever watched or written. - credentialsPath: `${process.env.TMPDIR ?? "/tmp"}/sap3214-enrichment-missing/credentials.json`, + credentialsPath: "/tmp/sap3214-enrichment-missing/credentials.json", })); // Every other export stays real so a new import in the server cannot turn -// this fake into a missing-export failure; only the credential store and the -// browser flow are replaced. +// this fake into a missing-export failure. A new real export would resolve +// paths through the mocked `credentialsFilePath`, so it stays contained. vi.mock("@sapiom/mcp/auth", async (importOriginal) => ({ ...(await importOriginal()), resolveEnvironment: vi.fn(async (environment?: string) => ({ From 2d2ca90590b4476c5a1d5e49e12f826f5d4ce2d5 Mon Sep 17 00:00:00 2001 From: antoine-berger <64917674+antoine-berger@users.noreply.github.com> Date: Wed, 9 Sep 2026 17:34:18 -0500 Subject: [PATCH 3/3] test(harness): contain the auth mock by redirecting the home directory [SAP-3214] A mocked credentialsFilePath export does not contain the real module: its internal calls resolve through os.homedir() directly. Redirect homedir to a temp dir instead (the mcp-config test idiom), drop the now-unneeded export override, and correct the comment that claimed otherwise. Co-Authored-By: Claude Fable 5.1 --- .../server/definition-list-enrichment.test.ts | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/packages/harness/src/server/definition-list-enrichment.test.ts b/packages/harness/src/server/definition-list-enrichment.test.ts index e229494c3..c0cc6853f 100644 --- a/packages/harness/src/server/definition-list-enrichment.test.ts +++ b/packages/harness/src/server/definition-list-enrichment.test.ts @@ -2,9 +2,9 @@ * Wiring regression for SAP-3214: one tenant-scoped list request per pass, * never a by-id request for a definition the account can't see. Fake Agents * API counting list and detail requests. `@sapiom/mcp/auth` is a PARTIAL - * mock: the credential store, the browser flow and `credentialsFilePath` are - * replaced so the disconnect route and the credential observer never touch - * ~/.sapiom; every other export runs for real. + * mock: the credential store and the browser flow are replaced, every other + * export runs for real, and the home directory is redirected to a temp dir + * so nothing real can reach ~/.sapiom. */ import { createServer as createHttpServer, @@ -16,15 +16,19 @@ import * as path from "node:path"; import { fileURLToPath } from "node:url"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -const authFixture = vi.hoisted(() => ({ - // A store that does not exist: the credential observer sees ENOENT and - // stays quiet, and nothing under ~/.sapiom is ever watched or written. - credentialsPath: "/tmp/sap3214-enrichment-missing/credentials.json", +// The real module resolves every path through `os.homedir()` internally, so +// a mocked `credentialsFilePath` export alone would not contain a new real +// export. Redirect the home directory instead: the credential store then +// lives under a temp dir that does not exist, the observer sees ENOENT and +// stays quiet, and nothing under the real ~/.sapiom is watched or written. +vi.mock("node:os", async (importOriginal) => ({ + ...(await importOriginal()), + homedir: () => "/tmp/sap3214-enrichment-home", })); // Every other export stays real so a new import in the server cannot turn -// this fake into a missing-export failure. A new real export would resolve -// paths through the mocked `credentialsFilePath`, so it stays contained. +// this fake into a missing-export failure; with the home redirected above, +// whatever runs for real stays inside the temp home. vi.mock("@sapiom/mcp/auth", async (importOriginal) => ({ ...(await importOriginal()), resolveEnvironment: vi.fn(async (environment?: string) => ({ @@ -41,7 +45,6 @@ vi.mock("@sapiom/mcp/auth", async (importOriginal) => ({ }), writeCredentials: vi.fn(async () => {}), clearCredentials: vi.fn(async () => {}), - credentialsFilePath: vi.fn(() => authFixture.credentialsPath), })); import type {