Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,22 @@ import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { setTimeout as delay } from "node:timers/promises";
import { describe, expect, it, vi } from "vitest";
import { ContainedStdioClientTransport } from "../src/upstream/contained-stdio-transport.js";
import { MultiUpstreamProcessManager } from "../src/upstream/multi-upstream-process-manager.js";
import { UpstreamProcessManager } from "../src/upstream/upstream-process-manager.js";
import { SecretRedactor } from "../src/secrets/redact.js";
import { MiftahError } from "../src/utils/errors.js";
import { ContainedStdioClientTransport } from "../../src/upstream/contained-stdio-transport.js";
import { MultiUpstreamProcessManager } from "../../src/upstream/multi-upstream-process-manager.js";
import { UpstreamProcessManager } from "../../src/upstream/upstream-process-manager.js";
import { SecretRedactor } from "../../src/secrets/redact.js";
import { MiftahError } from "../../src/utils/errors.js";

const fixture = join(dirname(fileURLToPath(import.meta.url)), "fixtures", "fake-upstream.mjs");
const fixture = join(dirname(fileURLToPath(import.meta.url)), "..", "fixtures", "fake-upstream.mjs");
const retainedStdioDescendantFixture = join(
dirname(fileURLToPath(import.meta.url)),
"..",
"fixtures",
"retained-stdio-descendant.mjs"
);
const backToBackProgressFixture = join(
dirname(fileURLToPath(import.meta.url)),
"..",
"fixtures",
"back-to-back-progress-upstream.mjs"
);
Expand Down Expand Up @@ -83,7 +85,16 @@ function observeTransportClose(transport: ContainedStdioClientTransport, onClose
});
}

describe("upstream process manager", () => {
export type UpstreamManagerContractGroup = "basics" | "recovery" | "teardown";

export function registerUpstreamManagerContracts(group: UpstreamManagerContractGroup): void {
if (group === "basics") return registerBasics();
if (group === "recovery") return registerRecovery();
if (group === "teardown") return registerTeardown();
}

function registerBasics(): void {
describe("upstream process manager", () => {
it.runIf(process.platform === "win32")("rejects a command shim before it can create a child process", async () => {
const directory = await mkdtemp(join(tmpdir(), "miftah-windows-command-shim-"));
const markerPath = join(directory, "command-shim-ran");
Expand Down Expand Up @@ -474,6 +485,11 @@ describe("upstream process manager", () => {
}
});

});
}

function registerRecovery(): void {
describe("upstream process manager", () => {
it("does not restart a crashed process unless automatic recovery is configured", async () => {
const directory = await mkdtemp(join(tmpdir(), "miftah-no-restart-"));
const crashPath = join(directory, "crash");
Expand Down Expand Up @@ -850,6 +866,11 @@ describe("upstream process manager", () => {
}
});

});
}

function registerTeardown(): void {
describe("upstream process manager", () => {
it("forces a delayed shutdown to respect the configured timeout", async () => {
const directory = await mkdtemp(join(tmpdir(), "miftah-shutdown-"));
const shutdownEndPath = join(directory, "stdin-ended");
Expand Down Expand Up @@ -1575,4 +1596,5 @@ describe("upstream process manager", () => {
await rm(directory, { recursive: true, force: true });
}
});
});
});
}
3 changes: 3 additions & 0 deletions tests/upstream-manager-basics.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { registerUpstreamManagerContracts } from "./helpers/upstream-manager-contracts.js";

registerUpstreamManagerContracts("basics");
3 changes: 3 additions & 0 deletions tests/upstream-manager-recovery.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { registerUpstreamManagerContracts } from "./helpers/upstream-manager-contracts.js";

registerUpstreamManagerContracts("recovery");
32 changes: 32 additions & 0 deletions tests/upstream-manager-suite-layout.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { existsSync, readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";

const testDirectory = new URL("./", import.meta.url);

function readTestFile(name: string): string {
const target = new URL(name, testDirectory);
return existsSync(target) ? readFileSync(target, "utf8") : "";
}

describe("upstream manager suite layout", () => {
it("runs process-heavy lifecycle groups through separate fresh-fork entry files", () => {
expect(existsSync(new URL("upstream-manager.test.ts", testDirectory))).toBe(false);

for (const [file, group] of [
["upstream-manager-basics.test.ts", "basics"],
["upstream-manager-recovery.test.ts", "recovery"],
["upstream-manager-teardown.test.ts", "teardown"]
] as const) {
expect(readTestFile(file)).toContain(`registerUpstreamManagerContracts("${group}")`);
}

const contracts = readTestFile("helpers/upstream-manager-contracts.ts");
expect(contracts).toContain('export type UpstreamManagerContractGroup = "basics" | "recovery" | "teardown"');
expect(contracts).toContain("export function registerUpstreamManagerContracts(");
expect(contracts).toContain('if (group === "basics")');
expect(contracts).toContain('if (group === "recovery")');
expect(contracts).toContain('if (group === "teardown")');
expect(contracts).toContain("does not idle-shutdown a profile while an upstream request is in flight");
expect(contracts).toContain("forces a delayed shutdown to respect the configured timeout");
});
});
3 changes: 3 additions & 0 deletions tests/upstream-manager-teardown.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { registerUpstreamManagerContracts } from "./helpers/upstream-manager-contracts.js";

registerUpstreamManagerContracts("teardown");