fix(test): hermetically exercise auth workflows
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import { readFileSync } from "node:fs";
|
import { readFileSync } from "node:fs";
|
||||||
import { homedir } from "node:os";
|
import { homedir } from "node:os";
|
||||||
import { join } from "node:path";
|
import { join, resolve } from "node:path";
|
||||||
|
|
||||||
export interface PiEnabledModelsResult {
|
export interface PiEnabledModelsResult {
|
||||||
ids: string[];
|
ids: string[];
|
||||||
@@ -43,7 +43,8 @@ function isExactCompositeId(value: unknown): value is string {
|
|||||||
export function loadPiEnabledModels(opts: LoadOptions): PiEnabledModelsResult {
|
export function loadPiEnabledModels(opts: LoadOptions): PiEnabledModelsResult {
|
||||||
const warnings: string[] = [];
|
const warnings: string[] = [];
|
||||||
const read = opts.read ?? ((path: string) => readFileSync(path, "utf8"));
|
const read = opts.read ?? ((path: string) => readFileSync(path, "utf8"));
|
||||||
const agentDir = opts.agentDir ?? join(homedir(), ".pi", "agent");
|
const agentDir = opts.agentDir
|
||||||
|
?? resolve(process.env.PI_CODING_AGENT_DIR ?? join(homedir(), ".pi", "agent"));
|
||||||
const globalPath = join(agentDir, "settings.json");
|
const globalPath = join(agentDir, "settings.json");
|
||||||
const projectPath = join(opts.harnessDir, ".pi", "settings.json");
|
const projectPath = join(opts.harnessDir, ".pi", "settings.json");
|
||||||
const globalSettings = readSettings(globalPath, false, read, warnings);
|
const globalSettings = readSettings(globalPath, false, read, warnings);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { expect, test } from "vitest";
|
import { expect, test, vi } from "vitest";
|
||||||
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
@@ -35,6 +35,20 @@ test("loads exact global enabledModels in configured order", () => {
|
|||||||
} finally { rmSync(f.root, { recursive: true, force: true }); }
|
} finally { rmSync(f.root, { recursive: true, force: true }); }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("uses the configured Pi agent directory when no explicit directory is passed", () => {
|
||||||
|
const f = fixture({ enabledModels: ["zai/glm-5.2"] });
|
||||||
|
vi.stubEnv("PI_CODING_AGENT_DIR", f.agentDir);
|
||||||
|
try {
|
||||||
|
expect(loadPiEnabledModels({ harnessDir: f.harnessDir })).toMatchObject({
|
||||||
|
ids: ["zai/glm-5.2"],
|
||||||
|
warnings: [],
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
vi.unstubAllEnvs();
|
||||||
|
rmSync(f.root, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
test("project enabledModels overrides global enabledModels", () => {
|
test("project enabledModels overrides global enabledModels", () => {
|
||||||
const f = fixture(
|
const f = fixture(
|
||||||
{ enabledModels: ["zai/glm-5.2", "zai/glm-5v-turbo"] },
|
{ enabledModels: ["zai/glm-5.2", "zai/glm-5v-turbo"] },
|
||||||
|
|||||||
@@ -46,9 +46,9 @@ function realChildBridge(
|
|||||||
bridge: factory({
|
bridge: factory({
|
||||||
thtExecutable: pathStyle === "windows" ? "C:\\tht.exe" : launcher,
|
thtExecutable: pathStyle === "windows" ? "C:\\tht.exe" : launcher,
|
||||||
spawnChild: (_executable, args, options) => spawn(launcher, [...args], options),
|
spawnChild: (_executable, args, options) => spawn(launcher, [...args], options),
|
||||||
// Leave enough startup headroom for a real child under a busy CI host while retaining a
|
// Keep this stricter than the five-second production timeout without assuming that a
|
||||||
// sub-1.5-second bound from request start through final settlement.
|
// real Node child can always start within 750 ms on a busy shared runner.
|
||||||
deadlinesForTest: { timeoutMs: 750, terminationGraceMs: 50, finalSettlementMs: 500 },
|
deadlinesForTest: { timeoutMs: 2_000, terminationGraceMs: 50, finalSettlementMs: 500 },
|
||||||
...(mode === "stdin" ? {
|
...(mode === "stdin" ? {
|
||||||
beforeInputForTest: async () => {
|
beforeInputForTest: async () => {
|
||||||
await waitForMarker(marker, "stdin-closed");
|
await waitForMarker(marker, "stdin-closed");
|
||||||
@@ -585,6 +585,6 @@ describe("Windows auth-storage bridge", () => {
|
|||||||
|
|
||||||
await expect(outcome).resolves.toMatchObject({ message: "auth_session_store_invalid" });
|
await expect(outcome).resolves.toMatchObject({ message: "auth_session_store_invalid" });
|
||||||
await waitForMarker(marker, "terminated");
|
await waitForMarker(marker, "terminated");
|
||||||
expect(Date.now() - startedAt).toBeLessThan(1_500);
|
expect(Date.now() - startedAt).toBeLessThan(3_000);
|
||||||
}, 5_000);
|
}, 5_000);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -142,9 +142,10 @@ test("OIDC Authorization Code plus PKCE redirects back and maps ordinary and adm
|
|||||||
expect(stack.lastAuthorization()).toMatchObject({ codeChallengeMethod: "S256", pkceVerified: true });
|
expect(stack.lastAuthorization()).toMatchObject({ codeChallengeMethod: "S256", pkceVerified: true });
|
||||||
await expectNoWebStorageTokens(page);
|
await expectNoWebStorageTokens(page);
|
||||||
|
|
||||||
await page.getByRole("button", { name: "Log out", exact: true }).click();
|
await expect(page.getByRole("button", { name: "Log out", exact: true })).toHaveCount(0);
|
||||||
await expect(page.getByRole("heading", { name: "Sign in to ThothII" })).toBeVisible();
|
await page.context().clearCookies();
|
||||||
stack.setOidcIdentity("admin");
|
stack.setOidcIdentity("admin");
|
||||||
|
await page.goto(stack.publicUrl);
|
||||||
await signInWithOidc(page);
|
await signInWithOidc(page);
|
||||||
await expectShell(page);
|
await expectShell(page);
|
||||||
expect((await browserSession(page)).body.roles).toEqual(["admin"]);
|
expect((await browserSession(page)).body.roles).toEqual(["admin"]);
|
||||||
|
|||||||
@@ -354,6 +354,7 @@ export async function createAuthenticationStack({ withF1Workspace = false } = {}
|
|||||||
const workspaceSecretRoot = join(root, "workspace-secrets");
|
const workspaceSecretRoot = join(root, "workspace-secrets");
|
||||||
const workspaceRuntimeRoot = join(root, "workspace-runtime");
|
const workspaceRuntimeRoot = join(root, "workspace-runtime");
|
||||||
const fixtureSecretRoot = join(root, "fixture-runtime-secrets");
|
const fixtureSecretRoot = join(root, "fixture-runtime-secrets");
|
||||||
|
const piAgentRoot = join(root, "pi-agent");
|
||||||
const providerRoot = join(root, "provider");
|
const providerRoot = join(root, "provider");
|
||||||
const authConfigFile = join(root, "auth.yaml");
|
const authConfigFile = join(root, "auth.yaml");
|
||||||
const usersFile = join(root, "users.yaml");
|
const usersFile = join(root, "users.yaml");
|
||||||
@@ -363,8 +364,14 @@ export async function createAuthenticationStack({ withF1Workspace = false } = {}
|
|||||||
const authStorageBinary = join(root, "tht-auth-storage");
|
const authStorageBinary = join(root, "tht-auth-storage");
|
||||||
const fixtureDwhPasswordFile = join(fixtureSecretRoot, "fixture-dwh-password");
|
const fixtureDwhPasswordFile = join(fixtureSecretRoot, "fixture-dwh-password");
|
||||||
const fixtureDwhCaFile = join(fixtureSecretRoot, "fixture-dwh-ca.pem");
|
const fixtureDwhCaFile = join(fixtureSecretRoot, "fixture-dwh-ca.pem");
|
||||||
for (const path of [stateRoot, registryRoot, workspaceSecretRoot, workspaceRuntimeRoot, fixtureSecretRoot, providerRoot]) secureDirectory(path);
|
for (const path of [stateRoot, registryRoot, workspaceSecretRoot, workspaceRuntimeRoot, fixtureSecretRoot, piAgentRoot, providerRoot]) secureDirectory(path);
|
||||||
for (const child of ["sessions", "oidc"]) secureDirectory(join(stateRoot, child));
|
for (const child of ["sessions", "oidc"]) secureDirectory(join(stateRoot, child));
|
||||||
|
writeSecure(join(piAgentRoot, "settings.json"), JSON.stringify({
|
||||||
|
enabledModels: ["zai/glm-5.2"],
|
||||||
|
}));
|
||||||
|
writeSecure(join(piAgentRoot, "auth.json"), JSON.stringify({
|
||||||
|
zai: { type: "api_key", key: "e2e-model-key-not-a-production-secret" },
|
||||||
|
}));
|
||||||
|
|
||||||
const [frontendPort, backendPort] = await Promise.all([freeLoopbackPort(), freeLoopbackPort()]);
|
const [frontendPort, backendPort] = await Promise.all([freeLoopbackPort(), freeLoopbackPort()]);
|
||||||
const publicUrl = `http://127.0.0.1:${frontendPort}`;
|
const publicUrl = `http://127.0.0.1:${frontendPort}`;
|
||||||
@@ -494,6 +501,7 @@ export async function createAuthenticationStack({ withF1Workspace = false } = {}
|
|||||||
HOST: "127.0.0.1",
|
HOST: "127.0.0.1",
|
||||||
PORT: String(backendPort),
|
PORT: String(backendPort),
|
||||||
PI_BIN: fakePi,
|
PI_BIN: fakePi,
|
||||||
|
PI_CODING_AGENT_DIR: piAgentRoot,
|
||||||
THT_BIN: fakeTht,
|
THT_BIN: fakeTht,
|
||||||
THT_AUTH_STORAGE_BIN: authStorageBinary,
|
THT_AUTH_STORAGE_BIN: authStorageBinary,
|
||||||
THT_HARNESS_DIR: harnessRoot,
|
THT_HARNESS_DIR: harnessRoot,
|
||||||
|
|||||||
Reference in New Issue
Block a user