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.
129 lines
3.6 KiB
TypeScript
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();
|
|
});
|
|
});
|
|
});
|