Files
memegoat/backend/src/api-keys/api-keys.service.spec.ts
Mathis HERRIOT 514bd354bf feat: add modular services and repositories for improved code organization
Introduce repository pattern across multiple services, including `favorites`, `tags`, `sessions`, `reports`, `auth`, and more. Decouple crypto functionalities into modular services like `HashingService`, `JwtService`, and `EncryptionService`. Improve testability and maintainability by simplifying dependencies and consolidating utility logic.
2026-01-14 12:11:39 +01:00

129 lines
3.6 KiB
TypeScript

import { Test, TestingModule } from "@nestjs/testing";
import { HashingService } from "../crypto/services/hashing.service";
import { ApiKeysService } from "./api-keys.service";
import { ApiKeysRepository } from "./repositories/api-keys.repository";
describe("ApiKeysService", () => {
let service: ApiKeysService;
let repository: ApiKeysRepository;
const mockApiKeysRepository = {
create: jest.fn(),
findAll: jest.fn(),
revoke: jest.fn(),
findActiveByKeyHash: jest.fn(),
updateLastUsed: jest.fn(),
};
const mockHashingService = {
hashSha256: jest.fn().mockResolvedValue("hashed-key"),
};
beforeEach(async () => {
jest.clearAllMocks();
const module: TestingModule = await Test.createTestingModule({
providers: [
ApiKeysService,
{
provide: ApiKeysRepository,
useValue: mockApiKeysRepository,
},
{
provide: HashingService,
useValue: mockHashingService,
},
],
}).compile();
service = module.get<ApiKeysService>(ApiKeysService);
repository = module.get<ApiKeysRepository>(ApiKeysRepository);
});
it("should be defined", () => {
expect(service).toBeDefined();
});
describe("create", () => {
it("should create an API key", async () => {
const userId = "user-id";
const name = "Test Key";
const expiresAt = new Date();
const result = await service.create(userId, name, expiresAt);
expect(repository.create).toHaveBeenCalledWith(
expect.objectContaining({
userId,
name,
prefix: "mg_live_",
expiresAt,
}),
);
expect(result).toHaveProperty("key");
expect(result.name).toBe(name);
expect(result.expiresAt).toBe(expiresAt);
expect(result.key).toMatch(/^mg_live_/);
});
});
describe("findAll", () => {
it("should find all API keys for a user", async () => {
const userId = "user-id";
const expectedKeys = [{ id: "1", name: "Key 1" }];
mockApiKeysRepository.findAll.mockResolvedValue(expectedKeys);
const result = await service.findAll(userId);
expect(repository.findAll).toHaveBeenCalledWith(userId);
expect(result).toEqual(expectedKeys);
});
});
describe("revoke", () => {
it("should revoke an API key", async () => {
const userId = "user-id";
const keyId = "key-id";
const expectedResult = [{ id: keyId, isActive: false }];
mockApiKeysRepository.revoke.mockResolvedValue(expectedResult);
const result = await service.revoke(userId, keyId);
expect(repository.revoke).toHaveBeenCalledWith(userId, keyId);
expect(result).toEqual(expectedResult);
});
});
describe("validateKey", () => {
it("should validate a valid API key", async () => {
const key = "mg_live_testkey";
const apiKey = { id: "1", isActive: true, expiresAt: null };
mockApiKeysRepository.findActiveByKeyHash.mockResolvedValue(apiKey);
const result = await service.validateKey(key);
expect(result).toEqual(apiKey);
expect(repository.findActiveByKeyHash).toHaveBeenCalled();
expect(repository.updateLastUsed).toHaveBeenCalledWith(apiKey.id);
});
it("should return null for invalid API key", async () => {
mockApiKeysRepository.findActiveByKeyHash.mockResolvedValue(null);
const result = await service.validateKey("invalid-key");
expect(result).toBeNull();
});
it("should return null for expired API key", async () => {
const key = "mg_live_testkey";
const expiredDate = new Date();
expiredDate.setFullYear(expiredDate.getFullYear() - 1);
const apiKey = { id: "1", isActive: true, expiresAt: expiredDate };
mockApiKeysRepository.findActiveByKeyHash.mockResolvedValue(apiKey);
const result = await service.validateKey(key);
expect(result).toBeNull();
});
});
});