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.
This commit is contained in:
Mathis HERRIOT
2026-01-14 12:11:39 +01:00
parent 9c45bf11e4
commit 514bd354bf
64 changed files with 1801 additions and 1295 deletions

View File

@@ -1,38 +1,31 @@
import { Logger } from "@nestjs/common";
import { Test, TestingModule } from "@nestjs/testing";
import { DatabaseService } from "../../database/database.service";
import { ContentsRepository } from "../../contents/repositories/contents.repository";
import { ReportsRepository } from "../../reports/repositories/reports.repository";
import { SessionsRepository } from "../../sessions/repositories/sessions.repository";
import { UsersRepository } from "../../users/repositories/users.repository";
import { PurgeService } from "./purge.service";
describe("PurgeService", () => {
let service: PurgeService;
const mockDb = {
delete: jest.fn(),
where: jest.fn(),
returning: jest.fn(),
};
const mockSessionsRepository = { purgeExpired: jest.fn().mockResolvedValue([]) };
const mockReportsRepository = { purgeObsolete: jest.fn().mockResolvedValue([]) };
const mockUsersRepository = { purgeDeleted: jest.fn().mockResolvedValue([]) };
const mockContentsRepository = { purgeSoftDeleted: jest.fn().mockResolvedValue([]) };
beforeEach(async () => {
jest.clearAllMocks();
jest.spyOn(Logger.prototype, "error").mockImplementation(() => {});
jest.spyOn(Logger.prototype, "log").mockImplementation(() => {});
const chain = {
delete: jest.fn().mockReturnThis(),
where: jest.fn().mockReturnThis(),
returning: jest.fn().mockResolvedValue([]),
};
const mockImplementation = () => Object.assign(Promise.resolve([]), chain);
for (const mock of Object.values(chain)) {
mock.mockImplementation(mockImplementation);
}
Object.assign(mockDb, chain);
const module: TestingModule = await Test.createTestingModule({
providers: [
PurgeService,
{ provide: DatabaseService, useValue: { db: mockDb } },
{ provide: SessionsRepository, useValue: mockSessionsRepository },
{ provide: ReportsRepository, useValue: mockReportsRepository },
{ provide: UsersRepository, useValue: mockUsersRepository },
{ provide: ContentsRepository, useValue: mockContentsRepository },
],
}).compile();
@@ -44,23 +37,22 @@ describe("PurgeService", () => {
});
describe("purgeExpiredData", () => {
it("should purge data", async () => {
mockDb.returning
.mockResolvedValueOnce([{ id: "s1" }]) // sessions
.mockResolvedValueOnce([{ id: "r1" }]) // reports
.mockResolvedValueOnce([{ id: "u1" }]) // users
.mockResolvedValueOnce([{ id: "c1" }]); // contents
it("should purge data using repositories", async () => {
mockSessionsRepository.purgeExpired.mockResolvedValue([{ id: "s1" }]);
mockReportsRepository.purgeObsolete.mockResolvedValue([{ id: "r1" }]);
mockUsersRepository.purgeDeleted.mockResolvedValue([{ id: "u1" }]);
mockContentsRepository.purgeSoftDeleted.mockResolvedValue([{ id: "c1" }]);
await service.purgeExpiredData();
expect(mockDb.delete).toHaveBeenCalledTimes(4);
expect(mockDb.returning).toHaveBeenCalledTimes(4);
expect(mockSessionsRepository.purgeExpired).toHaveBeenCalled();
expect(mockReportsRepository.purgeObsolete).toHaveBeenCalled();
expect(mockUsersRepository.purgeDeleted).toHaveBeenCalled();
expect(mockContentsRepository.purgeSoftDeleted).toHaveBeenCalled();
});
it("should handle errors", async () => {
mockDb.delete.mockImplementation(() => {
throw new Error("Db error");
});
mockSessionsRepository.purgeExpired.mockRejectedValue(new Error("Db error"));
await expect(service.purgeExpiredData()).resolves.not.toThrow();
});
});