77 lines
2.6 KiB
TypeScript
77 lines
2.6 KiB
TypeScript
import { BadRequestException, UnauthorizedException } from "@nestjs/common";
|
|
import { ApiKeyService } from "../src/public-api/api-key.service";
|
|
import { createPrismaMock } from "./utils/mock-prisma";
|
|
|
|
describe("ApiKeyService", () => {
|
|
it("creates a hashed public API key and returns the raw key once", async () => {
|
|
const prisma = createPrismaMock();
|
|
prisma.apiKey.create.mockImplementation(async ({ data }) => ({
|
|
id: "key_1",
|
|
...data,
|
|
createdAt: new Date("2026-01-01T00:00:00.000Z"),
|
|
}));
|
|
const service = new ApiKeyService(prisma as any);
|
|
|
|
const result = await service.createKey("user_1", { name: "Power tools" });
|
|
|
|
expect(result.key).toMatch(/^l1_[A-Za-z0-9_-]+$/);
|
|
expect(result.prefix).toBe(result.key.slice(0, 10));
|
|
expect(result.scopes).toEqual(["transactions:read"]);
|
|
expect(prisma.apiKey.create).toHaveBeenCalledWith({
|
|
data: expect.objectContaining({
|
|
userId: "user_1",
|
|
name: "Power tools",
|
|
prefix: result.key.slice(0, 10),
|
|
keyHash: expect.stringMatching(/^[a-f0-9]{64}$/),
|
|
scopes: ["transactions:read"],
|
|
}),
|
|
});
|
|
});
|
|
|
|
it("rejects unsupported scopes", async () => {
|
|
const service = new ApiKeyService(createPrismaMock() as any);
|
|
|
|
await expect(service.createKey("user_1", { scopes: ["transactions:write"] })).rejects.toBeInstanceOf(BadRequestException);
|
|
});
|
|
|
|
it("authenticates a valid API key and updates last used time", async () => {
|
|
const prisma = createPrismaMock();
|
|
prisma.apiKey.findUnique.mockResolvedValue({
|
|
id: "key_1",
|
|
userId: "user_1",
|
|
scopes: ["transactions:read"],
|
|
revokedAt: null,
|
|
expiresAt: null,
|
|
});
|
|
prisma.apiKey.update.mockResolvedValue({});
|
|
const service = new ApiKeyService(prisma as any);
|
|
|
|
await expect(service.authenticate("l1_secret")).resolves.toEqual({
|
|
userId: "user_1",
|
|
keyId: "key_1",
|
|
scopes: ["transactions:read"],
|
|
});
|
|
expect(prisma.apiKey.findUnique).toHaveBeenCalledWith({
|
|
where: { keyHash: expect.stringMatching(/^[a-f0-9]{64}$/) },
|
|
});
|
|
expect(prisma.apiKey.update).toHaveBeenCalledWith({
|
|
where: { id: "key_1" },
|
|
data: { lastUsedAt: expect.any(Date) },
|
|
});
|
|
});
|
|
|
|
it("rejects revoked API keys", async () => {
|
|
const prisma = createPrismaMock();
|
|
prisma.apiKey.findUnique.mockResolvedValue({
|
|
id: "key_1",
|
|
userId: "user_1",
|
|
scopes: ["transactions:read"],
|
|
revokedAt: new Date(),
|
|
expiresAt: null,
|
|
});
|
|
const service = new ApiKeyService(prisma as any);
|
|
|
|
await expect(service.authenticate("l1_secret")).rejects.toBeInstanceOf(UnauthorizedException);
|
|
});
|
|
});
|