diff --git a/test/e2e/fixtures/compatible-anthropic-switch.ts b/test/e2e/fixtures/compatible-anthropic-switch.ts index 9cc38afb5dc..d80c96d29d8 100644 --- a/test/e2e/fixtures/compatible-anthropic-switch.ts +++ b/test/e2e/fixtures/compatible-anthropic-switch.ts @@ -123,8 +123,8 @@ function readOwnedGatewayStateFile(filePath: string, currentUid: number): string } } -function managedOpenShellGatewayPid(): number | null { - const stateDirectory = resolveDockerDriverGatewayStateDir(process.env, os.homedir()); +function managedOpenShellGatewayPid(homeDir: string): number | null { + const stateDirectory = resolveDockerDriverGatewayStateDir(process.env, homeDir); const pidPath = path.join(stateDirectory, "openshell-gateway.pid"); const markerPath = path.join(stateDirectory, "runtime.json"); const pidPathExists = pathExists(pidPath); @@ -186,8 +186,8 @@ function managedOpenShellGatewayPid(): number | null { return pid; } -async function activeOpenShellGatewayPid(host: HostCliClient): Promise { - const managedPid = managedOpenShellGatewayPid(); +async function activeOpenShellGatewayPid(host: HostCliClient, homeDir: string): Promise { + const managedPid = managedOpenShellGatewayPid(homeDir); if (managedPid !== null) return managedPid; for (const serviceName of GATEWAY_SERVICE_NAMES) { const result = await host.command( @@ -219,11 +219,12 @@ async function activeOpenShellGatewayPid(host: HostCliClient): Promise { export async function installGatewayHostVerificationAlias( host: HostCliClient, cleanup: { add(name: string, run: () => Promise | void): void }, + homeDir: string = os.homedir(), ): Promise { - const gatewayPid = await activeOpenShellGatewayPid(host); + const gatewayPid = await activeOpenShellGatewayPid(host, homeDir); const ownerToken = randomBytes(16).toString("hex"); const fixtureDirectory = fs.mkdtempSync( - path.join(os.homedir(), ".nemoclaw-gateway-resolver-"), + path.join(homeDir, ".nemoclaw-gateway-resolver-"), ); const resolverSource = path.join(fixtureDirectory, "hosts"); const ownedLine = `127.0.0.1 ${OPENSHELL_HOST_ALIAS} # nemoclaw-gateway-host-verifier:${ownerToken}`; diff --git a/test/e2e/live/openclaw-inference-switch.test.ts b/test/e2e/live/openclaw-inference-switch.test.ts index c5c269360de..298d3c93569 100644 --- a/test/e2e/live/openclaw-inference-switch.test.ts +++ b/test/e2e/live/openclaw-inference-switch.test.ts @@ -1093,7 +1093,7 @@ test("openclaw-inference-switch: switches route and preserves live OpenClaw beha if (SWITCH_PROVIDER === "compatible-anthropic-endpoint" && SWITCH_MOCK_ANTHROPIC === "1") { mockProvider = await startMockAnthropicProvider(); - await installGatewayHostVerificationAlias(host, cleanup); + await installGatewayHostVerificationAlias(host, cleanup, home); await artifacts.writeJson("mock-anthropic-provider.json", { endpointUrl: mockProvider.endpointUrl, }); diff --git a/test/e2e/support/compatible-anthropic-switch.test.ts b/test/e2e/support/compatible-anthropic-switch.test.ts index d3110c2813d..0c561559854 100644 --- a/test/e2e/support/compatible-anthropic-switch.test.ts +++ b/test/e2e/support/compatible-anthropic-switch.test.ts @@ -54,6 +54,20 @@ const INVALID_MANAGED_GATEWAY_STATE_CASES = [ }, ] as const; +function mockGatewayProcess(pid: number, gatewayBin: string): void { + const statSync = fs.statSync; + vi.spyOn(fs, "statSync").mockImplementation(((target) => + String(target) === `/proc/${pid}` + ? ({ uid: process.getuid?.() ?? 0 } as fs.Stats) + : statSync(target)) as typeof fs.statSync); + const realpathSync = fs.realpathSync; + const gatewayExecutablePaths = new Set([`/proc/${pid}/exe`, gatewayBin]); + vi.spyOn(fs, "realpathSync").mockImplementation(((target) => + gatewayExecutablePaths.has(String(target)) + ? gatewayBin + : realpathSync(target)) as typeof fs.realpathSync); +} + describe("compatible Anthropic inference switch setup", () => { afterEach(() => { vi.unstubAllEnvs(); @@ -99,11 +113,19 @@ describe("compatible Anthropic inference switch setup", () => { expect(rewrite).not.toHaveBeenCalled(); }); - it("uses the managed Docker-driver gateway before the user service (#9166)", async () => { - const stateDirectory = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-managed-gateway-test-")); + it("uses managed Docker-driver gateway state from the target home before the user service (#9166)", async () => { + const home = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-target-home-gateway-test-")); + const stateDirectory = path.join( + home, + ".local", + "state", + "nemoclaw", + "openshell-docker-gateway", + ); const pid = process.pid; const gatewayBin = "/usr/bin/openshell-gateway"; - vi.stubEnv("NEMOCLAW_OPENSHELL_GATEWAY_STATE_DIR", stateDirectory); + fs.mkdirSync(stateDirectory, { recursive: true }); + vi.stubEnv("NEMOCLAW_OPENSHELL_GATEWAY_STATE_DIR", ""); writeDockerDriverGatewayPidFile(path.join(stateDirectory, "openshell-gateway.pid"), pid); writeDockerDriverGatewayRuntimeMarkerForStateDir(stateDirectory, { desiredEnv: {}, @@ -111,17 +133,16 @@ describe("compatible Anthropic inference switch setup", () => { gatewayBin, pid, }); - const realpathSync = fs.realpathSync; - const gatewayExecutablePaths = new Set([`/proc/${pid}/exe`, gatewayBin]); - vi.spyOn(fs, "realpathSync").mockImplementation( - ((target) => - gatewayExecutablePaths.has(String(target)) ? gatewayBin : realpathSync(target)) as typeof fs.realpathSync, - ); + mockGatewayProcess(pid, gatewayBin); const command = vi.fn().mockResolvedValue({ exitCode: 0, stderr: "", stdout: "" }); const add = vi.fn(); try { - await installGatewayHostVerificationAlias({ command } as unknown as HostCliClient, { add }); + await installGatewayHostVerificationAlias( + { command } as unknown as HostCliClient, + { add }, + home, + ); const cleanupMount = add.mock.calls[0]?.[1] as () => Promise; await cleanupMount(); @@ -133,7 +154,7 @@ describe("compatible Anthropic inference switch setup", () => { ); } } finally { - fs.rmSync(stateDirectory, { force: true, recursive: true }); + fs.rmSync(home, { force: true, recursive: true }); } }); @@ -153,7 +174,11 @@ describe("compatible Anthropic inference switch setup", () => { const add = vi.fn(); try { - await installGatewayHostVerificationAlias({ command } as unknown as HostCliClient, { add }); + await installGatewayHostVerificationAlias( + { command } as unknown as HostCliClient, + { add }, + stateDirectory, + ); const cleanupMount = add.mock.calls[0]?.[1] as () => Promise; await cleanupMount();