Files
next-tjwater-agent/tests/context/productContext.test.ts
T
jiang 6a49d2209e feat(agent): unify MCP tools and adopt Responses delivery
Move project and host queries into native MCP with host-owned OAuth/context,
while retaining memory, skills, actions and publication plugins. Add nearby-model
queries and explicit chat/task/learning model defaults.

Adopt Responses HTTP/SSE and native steer/queue inbox delivery, preserving scoped
session ownership, pending input edits and investigation communication.

Disable local-provider discovery: Server /health allowed native vLLM to probe
/v1/models on port 8000. A native before/after regression prevents recurrence.

Validation: 170 focused Bun tests; service/plugin typechecks; native discovery,
provider-discovery and input-inbox smoke checks; no paid model inference.
2026-10-19 18:46:58 +08:00

134 lines
6.1 KiB
TypeScript

import { afterEach, describe, expect, it } from "bun:test";
import { mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import type { Request } from "express";
import {
attachTrustedAgentContext,
getLocalAgentContext,
ProductContextError,
getTrustedProductCredential,
isTrustedTaskDispatchRequest,
} from "../../src/context/localContext.js";
import { SessionMetadataStore } from "../../src/sessions/metadataStore.js";
const originalFetch = globalThis.fetch;
afterEach(() => {
globalThis.fetch = originalFetch;
});
const requestWithHeaders = (headers: Record<string, string>) => ({
header: (name: string) => headers[name.toLowerCase()],
}) as Request;
const authResponse = (userId: string, projectId: string) => ({
is_superuser: false,
keycloak_sub: `sub-${userId}`,
network: "supply",
permissions: ["analysis:read"],
project_id: projectId,
project_role: "member",
role: "user",
token_expires_at: "2026-09-17T00:00:00Z",
user_id: userId,
username: userId,
});
describe("trusted product chat context", () => {
it("authorizes message dispatch only for verified service accounts with execution permission", async () => {
const request = requestWithHeaders({ authorization: "Bearer verified", "x-project-id": "project-a" });
for (const [role, permissions, allowed] of [
["user", ["operational_task.execute"], false],
["service", ["operational_task.view"], false],
["service", ["operational_task.execute"], true],
["user", [], false],
] as const) {
globalThis.fetch = (async () => Response.json({ ...authResponse("actor", "project-a"), role, permissions })) as unknown as typeof fetch;
await attachTrustedAgentContext(request);
expect(isTrustedTaskDispatchRequest(request)).toBe(allowed);
}
});
it("verifies personal chats by Bearer without assigning a shared project or business credential", async () => {
const requests: Array<{ url: string; headers: Headers }> = [];
globalThis.fetch = (async (input: RequestInfo | URL, init?: RequestInit) => {
requests.push({ url: String(input), headers: new Headers(init?.headers) });
return Response.json({ user_id: requests.length === 1 ? "user-a" : "user-b" });
}) as unknown as typeof fetch;
const first = requestWithHeaders({ authorization: "Bearer personal-a", "x-agent-local-user": "spoofed" });
const second = requestWithHeaders({ authorization: "Bearer personal-b" });
await attachTrustedAgentContext(first);
await attachTrustedAgentContext(second);
expect(requests[0]!.url).toEndWith("/api/v1/access-context");
expect(requests[0]!.headers.has("X-Project-Id")).toBeFalse();
expect(getLocalAgentContext(first).userId).toBe("user-a");
expect(getLocalAgentContext(first).projectId).toBeUndefined();
expect(getLocalAgentContext(first).projectKey).not.toBe(getLocalAgentContext(second).projectKey);
expect(getTrustedProductCredential(first)).toBeNull();
});
it("does not turn a project header or expired personal token into anonymous access", async () => {
globalThis.fetch = (async () => Response.json({ detail: "expired" }, { status: 401 })) as unknown as typeof fetch;
await expect(attachTrustedAgentContext(requestWithHeaders({ "x-project-id": "project-a" }))).rejects.toBeInstanceOf(ProductContextError);
await expect(attachTrustedAgentContext(requestWithHeaders({ authorization: "Bearer expired-personal" }))).rejects.toBeInstanceOf(ProductContextError);
});
it("uses verified product users and projects instead of spoofable local headers", async () => {
const identities = [authResponse("product-user-1", "project-1"), authResponse("product-user-2", "project-2")];
globalThis.fetch = (async () => Response.json(identities.shift())) as unknown as typeof fetch;
const first = requestWithHeaders({
authorization: "Bearer product-token-1",
"x-project-id": "project-1",
"x-agent-local-user": "spoofed-user",
"x-agent-local-project": "spoofed-project",
});
const second = requestWithHeaders({
authorization: "Bearer product-token-2",
"x-project-id": "project-2",
"x-agent-local-user": "spoofed-user",
"x-agent-local-project": "spoofed-project",
});
await attachTrustedAgentContext(first);
await attachTrustedAgentContext(second);
const firstContext = getLocalAgentContext(first);
const secondContext = getLocalAgentContext(second);
expect(firstContext.userId).toBe("product-user-1");
expect(firstContext.projectId).toBe("project-1");
expect(secondContext.userId).toBe("product-user-2");
expect(secondContext.projectId).toBe("project-2");
expect(firstContext.actorKey).not.toBe(secondContext.actorKey);
expect(firstContext.projectKey).not.toBe(secondContext.projectKey);
const localSpoof = requestWithHeaders({
"x-agent-local-user": "product-user-1",
"x-agent-local-project": "project-1",
});
await attachTrustedAgentContext(localSpoof);
const localContext = getLocalAgentContext(localSpoof);
expect(localContext.actorKey).not.toBe(firstContext.actorKey);
expect(localContext.projectKey).not.toBe(firstContext.projectKey);
const tempDir = await mkdtemp(join(tmpdir(), "tjwater-product-session-"));
try {
const store = new SessionMetadataStore(tempDir);
await store.initialize();
await store.ensure({ ...firstContext, sessionId: "private-session" });
expect(await store.get(secondContext, "private-session")).toBeNull();
expect((await store.get(firstContext, "private-session"))?.sessionId).toBe("private-session");
} finally {
await rm(tempDir, { force: true, recursive: true });
}
});
it("fails closed when any supplied product credential cannot be verified", async () => {
globalThis.fetch = (async () => Response.json({ detail: "expired" }, { status: 401 })) as unknown as typeof fetch;
const request = requestWithHeaders({
authorization: "Bearer expired-token",
"x-project-id": "project-1",
});
expect(attachTrustedAgentContext(request)).rejects.toBeInstanceOf(ProductContextError);
});
});