feat: everything
This commit is contained in:
95
tests/auth.test.ts
Normal file
95
tests/auth.test.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
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("");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user