96 lines
3.1 KiB
TypeScript
96 lines
3.1 KiB
TypeScript
import { describe, it, expect } from "bun:test";
|
|
import { generateJWT, getAuthHeaders } from "../src/auth/index.js";
|
|
import type { X3Config } from "../src/config.js";
|
|
|
|
function makeConfig(overrides: Partial<X3Config> = {}): X3Config {
|
|
return {
|
|
url: "https://example.com/api",
|
|
endpoint: "SEED",
|
|
clientId: "test-client-id",
|
|
secret: "test-secret-key-for-hs256",
|
|
user: "TEST_USER",
|
|
tokenLifetime: 600,
|
|
mode: "authenticated",
|
|
tlsRejectUnauthorized: true,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function decodeJwtPayload(token: string): Record<string, unknown> {
|
|
const parts = token.split(".");
|
|
const payload = Buffer.from(parts[1], "base64url").toString();
|
|
return JSON.parse(payload);
|
|
}
|
|
|
|
describe("generateJWT", () => {
|
|
it("produces a token with 3 dot-separated parts", async () => {
|
|
const token = await generateJWT(makeConfig());
|
|
const parts = token.split(".");
|
|
expect(parts).toHaveLength(3);
|
|
expect(parts.every((p) => p.length > 0)).toBe(true);
|
|
});
|
|
|
|
it("encodes correct claims in the payload", async () => {
|
|
const config = makeConfig({ tokenLifetime: 900 });
|
|
const beforeTime = Math.floor(Date.now() / 1000) - 30;
|
|
const token = await generateJWT(config);
|
|
const afterTime = Math.floor(Date.now() / 1000) - 30;
|
|
|
|
const claims = decodeJwtPayload(token);
|
|
|
|
expect(claims.iss).toBe("test-client-id");
|
|
expect(claims.sub).toBe("TEST_USER");
|
|
expect(claims.aud).toBe("");
|
|
|
|
const iat = claims.iat as number;
|
|
expect(iat).toBeGreaterThanOrEqual(beforeTime);
|
|
expect(iat).toBeLessThanOrEqual(afterTime);
|
|
|
|
expect(claims.exp).toBe(iat + 900);
|
|
});
|
|
|
|
it("throws when clientId is missing", async () => {
|
|
expect(generateJWT(makeConfig({ clientId: undefined }))).rejects.toThrow(
|
|
"clientId, secret, and user are required",
|
|
);
|
|
});
|
|
|
|
it("throws when secret is missing", async () => {
|
|
expect(generateJWT(makeConfig({ secret: undefined }))).rejects.toThrow(
|
|
"clientId, secret, and user are required",
|
|
);
|
|
});
|
|
|
|
it("throws when user is missing", async () => {
|
|
expect(generateJWT(makeConfig({ user: undefined }))).rejects.toThrow(
|
|
"clientId, secret, and user are required",
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("getAuthHeaders", () => {
|
|
it("returns only Content-Type in sandbox mode", async () => {
|
|
const headers = await getAuthHeaders(makeConfig({ mode: "sandbox" }));
|
|
|
|
expect(headers["Content-Type"]).toBe("application/json");
|
|
expect(headers["Authorization"]).toBeUndefined();
|
|
expect(headers["x-xtrem-endpoint"]).toBeUndefined();
|
|
});
|
|
|
|
it("returns Authorization and x-xtrem-endpoint in authenticated mode", async () => {
|
|
const headers = await getAuthHeaders(makeConfig());
|
|
|
|
expect(headers["Content-Type"]).toBe("application/json");
|
|
expect(headers["Authorization"]).toStartWith("Bearer ");
|
|
expect(headers["x-xtrem-endpoint"]).toBe("SEED");
|
|
});
|
|
|
|
it("sets x-xtrem-endpoint to empty string when endpoint is undefined", async () => {
|
|
const headers = await getAuthHeaders(
|
|
makeConfig({ endpoint: undefined }),
|
|
);
|
|
|
|
expect(headers["x-xtrem-endpoint"]).toBe("");
|
|
});
|
|
});
|