ledgerone_backend/test/api-key.service.spec.ts

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);
});
});