Initial Commit
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Self-conformance for the `TenantResolver` contract: the jwt resolver MUST
|
||||
* (a) expose a non-empty `id`, (b) return `Promise<TenantContext | null>`
|
||||
* shape on every call, (c) never mutate the inbound `Request`, (d) be safe
|
||||
* to call concurrently across many requests sharing the same factory.
|
||||
*
|
||||
* `@suaveplan/api-types/tenant-resolver` does not (yet) ship a conformance
|
||||
* harness; this file stands in as one.
|
||||
*/
|
||||
import type {
|
||||
Principal,
|
||||
PrincipalId,
|
||||
Scope,
|
||||
} from "@suaveplan/api-types/principal";
|
||||
import type { TenantId } from "@suaveplan/api-types/tenant";
|
||||
import type { TenantResolver } from "@suaveplan/api-types/tenant-resolver";
|
||||
import { describe, expect, it } from "@suaveplan/testing/runner";
|
||||
import { createJwtTenantResolver } from "../src/resolver/resolver.js";
|
||||
|
||||
function principal(tenantId: string | undefined): Principal {
|
||||
return {
|
||||
id: "u" as PrincipalId,
|
||||
scopes: [] as readonly Scope[],
|
||||
tenantId: tenantId as TenantId | undefined,
|
||||
claims: {},
|
||||
};
|
||||
}
|
||||
|
||||
function makeRequest(p: Principal | null): Request {
|
||||
const req = new Request(`https://example.com/${Math.random()}`);
|
||||
Object.defineProperty(req, "principal", { value: p, enumerable: true });
|
||||
return req;
|
||||
}
|
||||
|
||||
describe("TenantResolver conformance — jwt", () => {
|
||||
const factory = (): TenantResolver => createJwtTenantResolver();
|
||||
|
||||
it("exposes a non-empty id", { timeout: 5000 }, () => {
|
||||
expect(factory().id.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("returns a TenantContext when claim is present", {
|
||||
timeout: 5000,
|
||||
}, async () => {
|
||||
const ctx = await factory().resolve(makeRequest(principal("t1")));
|
||||
expect(ctx).not.toBeNull();
|
||||
expect(ctx?.id).toBe("t1");
|
||||
expect(typeof ctx?.slug).toBe("string");
|
||||
});
|
||||
|
||||
it("returns null (chain advance) when claim is absent", {
|
||||
timeout: 5000,
|
||||
}, async () => {
|
||||
expect(
|
||||
await factory().resolve(makeRequest(principal(undefined)))
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it("does not mutate the inbound Request", { timeout: 5000 }, async () => {
|
||||
const req = makeRequest(principal("t2"));
|
||||
const before = Object.keys(req).sort();
|
||||
await factory().resolve(req);
|
||||
expect(Object.keys(req).sort()).toEqual(before);
|
||||
});
|
||||
|
||||
it("is safe to call concurrently", { timeout: 5000 }, async () => {
|
||||
const resolver = factory();
|
||||
const results = await Promise.all(
|
||||
Array.from({ length: 32 }, (_, i) =>
|
||||
resolver.resolve(makeRequest(principal(`t${i}`)))
|
||||
)
|
||||
);
|
||||
expect(results).toHaveLength(32);
|
||||
for (const [i, ctx] of results.entries()) {
|
||||
expect(ctx?.id).toBe(`t${i}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user