Initial Commit
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
import { describe, expect, it } from "@suaveplan/testing/runner";
|
||||
import {
|
||||
AUTH_ROLLED_CODES,
|
||||
AuthRolledError,
|
||||
} from "../error-codes/error-codes.js";
|
||||
import {
|
||||
DEFAULT_COOKIE_NAME,
|
||||
DEFAULT_MIN_PASSWORD_STRENGTH,
|
||||
DEFAULT_REFRESH_TTL_SEC,
|
||||
DEFAULT_ROTATION_PERIOD_MS,
|
||||
DEFAULT_ROUTE_PREFIX,
|
||||
DEFAULT_SESSION_TTL_SEC,
|
||||
parseRolledAuthOptions,
|
||||
} from "./options.js";
|
||||
|
||||
const baseRaw = (): Record<string, unknown> => ({
|
||||
store: { __mark: "store" },
|
||||
keys: [
|
||||
{
|
||||
kid: "k1",
|
||||
privateJwk: { kty: "RSA" },
|
||||
publicJwk: { kty: "RSA" },
|
||||
active: true,
|
||||
},
|
||||
],
|
||||
issuer: "https://api.example.com",
|
||||
audience: "front",
|
||||
});
|
||||
|
||||
describe("parseRolledAuthOptions", () => {
|
||||
it("accepts a minimal valid options bag", { timeout: 5000 }, () => {
|
||||
const opts = parseRolledAuthOptions(baseRaw());
|
||||
expect(opts.issuer).toBe("https://api.example.com");
|
||||
expect(opts.audience).toBe("front");
|
||||
expect(opts.keys).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("throws DATABASE_REQUIRED when raw is not an object", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
for (const bad of [null, undefined, 42, "hi", [1]]) {
|
||||
try {
|
||||
parseRolledAuthOptions(bad);
|
||||
throw new Error("did not throw");
|
||||
} catch (err) {
|
||||
expect(err).toBeInstanceOf(AuthRolledError);
|
||||
expect((err as AuthRolledError).code).toBe(
|
||||
AUTH_ROLLED_CODES.DATABASE_REQUIRED
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("throws DATABASE_REQUIRED when store is missing", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
const raw = baseRaw();
|
||||
delete raw["store"];
|
||||
try {
|
||||
parseRolledAuthOptions(raw);
|
||||
throw new Error("did not throw");
|
||||
} catch (err) {
|
||||
expect(err).toBeInstanceOf(AuthRolledError);
|
||||
expect((err as AuthRolledError).code).toBe(
|
||||
AUTH_ROLLED_CODES.DATABASE_REQUIRED
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("throws DATABASE_REQUIRED when store is null", { timeout: 5000 }, () => {
|
||||
const raw = baseRaw();
|
||||
raw["store"] = null;
|
||||
expect(() => parseRolledAuthOptions(raw)).toThrow(AuthRolledError);
|
||||
});
|
||||
|
||||
it("throws SIGNING_KEY_NOT_CONFIGURED on schema failure (zero keys)", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
const raw = baseRaw();
|
||||
raw["keys"] = [];
|
||||
try {
|
||||
parseRolledAuthOptions(raw);
|
||||
throw new Error("did not throw");
|
||||
} catch (err) {
|
||||
expect(err).toBeInstanceOf(AuthRolledError);
|
||||
expect((err as AuthRolledError).code).toBe(
|
||||
AUTH_ROLLED_CODES.SIGNING_KEY_NOT_CONFIGURED
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("throws SIGNING_KEY_NOT_CONFIGURED when more than one key is active", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
const raw = baseRaw();
|
||||
raw["keys"] = [
|
||||
{
|
||||
kid: "k1",
|
||||
privateJwk: { kty: "RSA" },
|
||||
publicJwk: { kty: "RSA" },
|
||||
active: true,
|
||||
},
|
||||
{
|
||||
kid: "k2",
|
||||
privateJwk: { kty: "RSA" },
|
||||
publicJwk: { kty: "RSA" },
|
||||
active: true,
|
||||
},
|
||||
];
|
||||
expect(() => parseRolledAuthOptions(raw)).toThrow(AuthRolledError);
|
||||
});
|
||||
|
||||
it("throws SIGNING_KEY_NOT_CONFIGURED when active key is revoked", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
const raw = baseRaw();
|
||||
raw["keys"] = [
|
||||
{
|
||||
kid: "k1",
|
||||
privateJwk: { kty: "RSA" },
|
||||
publicJwk: { kty: "RSA" },
|
||||
active: true,
|
||||
revokedAt: 1,
|
||||
},
|
||||
];
|
||||
expect(() => parseRolledAuthOptions(raw)).toThrow(AuthRolledError);
|
||||
});
|
||||
|
||||
it("rejects non-slash routePrefix", { timeout: 5000 }, () => {
|
||||
const raw = baseRaw();
|
||||
raw["routePrefix"] = "auth";
|
||||
expect(() => parseRolledAuthOptions(raw)).toThrow(AuthRolledError);
|
||||
});
|
||||
|
||||
it("rejects unknown keys (strictObject)", { timeout: 5000 }, () => {
|
||||
const raw = baseRaw();
|
||||
(raw as Record<string, unknown>)["unknownField"] = 1;
|
||||
expect(() => parseRolledAuthOptions(raw)).toThrow(AuthRolledError);
|
||||
});
|
||||
|
||||
it("exposes named defaults", { timeout: 5000 }, () => {
|
||||
expect(DEFAULT_COOKIE_NAME).toBe("suaveplan.session");
|
||||
expect(DEFAULT_ROUTE_PREFIX).toBe("/auth");
|
||||
expect(DEFAULT_SESSION_TTL_SEC).toBe(3600);
|
||||
expect(DEFAULT_REFRESH_TTL_SEC).toBe(2592000);
|
||||
expect(DEFAULT_ROTATION_PERIOD_MS).toBe(86_400_000);
|
||||
expect(DEFAULT_MIN_PASSWORD_STRENGTH).toBe(3);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user