pi-mono/packages/chord/test/node/http/authenticate.test.ts
Mario Zechner e9c0ff4d84 feat(chord): Chord v2 is the package's root
src/next is src (the main export), src/next-node is src/node with short subpaths (/http, /workers, /plugins,
/plugins/runtime, /app, /surfaces, /web, /react, /tui, /tui/launcher); the foundations get /json and /watch;
Chord v1 moves to src/old (/old, /old/node) until p2 runs on v2; tests and docs move likewise (test/, docs/,
test/old, docs/old). Durable and env import the foundations from their subpaths.
2026-11-02 22:36:55 +00:00

58 lines
2.2 KiB
TypeScript

// Tests of the authenticators: asked in contributor order, the first principal wins, a throwing one reported and
// skipped, none: anonymous.
import { describe, expect, it } from "vitest";
import { BACKGROUND_CONTEXT as context, withAbortSignal } from "../../../src/context/index.ts";
import { type Authenticator, authenticate, composeAuthenticators } from "../../../src/node/http/authenticate.ts";
import type { HttpRequest } from "../../../src/node/http/index.ts";
import type { RegistryTable } from "../../../src/registries/index.ts";
const request = { headers: new Headers() } as HttpRequest;
describe("authenticators", () => {
it("are asked in contributor order; the first principal wins; a throwing one is reported and skipped", async () => {
const asked: string[] = [];
const answer = (name: string, principal: { kind: string }) => {
asked.push(name);
return principal;
};
const table: RegistryTable<Authenticator> = [
{ plugin: "@app/a", version: 1, value: () => void asked.push("a") },
{
plugin: "@app/b",
version: 1,
value: () => {
asked.push("b");
throw new Error("broken");
},
},
{ plugin: "@app/c", version: 1, value: async () => answer("c", { kind: "owner" }) },
{ plugin: "@app/d", version: 1, value: () => answer("d", { kind: "guest" }) },
];
const failed: [string, unknown][] = [];
const principal = await authenticate(
composeAuthenticators(table),
request,
(plugin, error) => failed.push([plugin, error]),
context,
);
expect([principal, asked, failed]).toEqual([
{ kind: "owner" },
["a", "b", "c"],
[["@app/b", new Error("broken")]],
]);
});
// Sol's second recheck C: a fresh authenticator started after the request was cancelled.
it("start no authenticator once the context is aborted", async () => {
let calls = 0;
const table: RegistryTable<Authenticator> = [{ plugin: "@app/a", version: 1, value: () => void calls++ }];
const aborted = withAbortSignal(AbortSignal.abort(), context);
expect(await authenticate(composeAuthenticators(table), request, () => {}, aborted)).toBeUndefined();
expect(calls).toBe(0);
});
it("give no principal when none knows the request", async () => {
expect(await authenticate([], request, () => {}, context)).toBeUndefined();
});
});