Files
memegoat/backend/src/s3/s3.service.spec.ts
Mathis HERRIOT 951b38db67 chore: update lint scripts and improve formatting consistency
- Added `lint:fix` scripts for backend, frontend, and documentation.
- Enabled `biome check --write` for unsafe fixes in backend scripts.
- Fixed imports, formatting, and logging for improved code clarity.
- Adjusted service unit tests for better readability and maintainability.
2026-01-21 11:38:25 +01:00

223 lines
6.7 KiB
TypeScript

import { ConfigService } from "@nestjs/config";
import { Test, TestingModule } from "@nestjs/testing";
import * as Minio from "minio";
import { S3Service } from "./s3.service";
jest.mock("minio");
describe("S3Service", () => {
let service: S3Service;
let configService: ConfigService;
let minioClient: any;
beforeEach(async () => {
minioClient = {
bucketExists: jest.fn().mockResolvedValue(true),
makeBucket: jest.fn().mockResolvedValue(undefined),
putObject: jest.fn().mockResolvedValue(undefined),
getObject: jest.fn().mockResolvedValue({}),
presignedUrl: jest.fn().mockResolvedValue("http://localhost/url"),
removeObject: jest.fn().mockResolvedValue(undefined),
statObject: jest.fn().mockResolvedValue({ size: 100, etag: "123" }),
copyObject: jest.fn().mockResolvedValue(undefined),
};
(Minio.Client as jest.Mock).mockImplementation(() => minioClient);
const module: TestingModule = await Test.createTestingModule({
providers: [
S3Service,
{
provide: ConfigService,
useValue: {
get: jest.fn((key: string, defaultValue?: string) => {
if (key === "S3_PORT") return "9000";
if (key === "S3_USE_SSL") return "false";
return defaultValue || key;
}),
},
},
],
}).compile();
service = module.get<S3Service>(S3Service);
configService = module.get<ConfigService>(ConfigService);
});
it("should be defined", () => {
expect(service).toBeDefined();
});
describe("onModuleInit", () => {
it("should create bucket if it does not exist", async () => {
minioClient.bucketExists.mockResolvedValue(false);
await service.onModuleInit();
expect(minioClient.makeBucket).toHaveBeenCalledWith("memegoat");
});
it("should not create bucket if it exists", async () => {
minioClient.bucketExists.mockResolvedValue(true);
await service.onModuleInit();
expect(minioClient.makeBucket).not.toHaveBeenCalled();
});
});
describe("uploadFile", () => {
it("should upload a file to default bucket", async () => {
const fileName = "test.txt";
const file = Buffer.from("test content");
const mimeType = "text/plain";
const result = await service.uploadFile(fileName, file, mimeType);
expect(minioClient.putObject).toHaveBeenCalledWith(
"memegoat",
fileName,
file,
file.length,
{ "Content-Type": mimeType },
);
expect(result).toBe(fileName);
});
it("should upload a file to specific bucket", async () => {
const fileName = "test.txt";
const file = Buffer.from("test content");
const mimeType = "text/plain";
const bucketName = "other-bucket";
await service.uploadFile(fileName, file, mimeType, {}, bucketName);
expect(minioClient.putObject).toHaveBeenCalledWith(
bucketName,
fileName,
file,
file.length,
{ "Content-Type": mimeType },
);
});
});
describe("getFile", () => {
it("should get a file from default bucket", async () => {
const fileName = "test.txt";
await service.getFile(fileName);
expect(minioClient.getObject).toHaveBeenCalledWith("memegoat", fileName);
});
it("should get a file from specific bucket", async () => {
const fileName = "test.txt";
const bucketName = "other-bucket";
await service.getFile(fileName, bucketName);
expect(minioClient.getObject).toHaveBeenCalledWith(bucketName, fileName);
});
});
describe("getFileUrl", () => {
it("should get a presigned URL from default bucket", async () => {
const fileName = "test.txt";
const url = await service.getFileUrl(fileName);
expect(minioClient.presignedUrl).toHaveBeenCalledWith(
"GET",
"memegoat",
fileName,
3600,
);
expect(url).toBe("http://localhost/url");
});
it("should get a presigned URL from specific bucket", async () => {
const fileName = "test.txt";
const bucketName = "other-bucket";
await service.getFileUrl(fileName, 3600, bucketName);
expect(minioClient.presignedUrl).toHaveBeenCalledWith(
"GET",
bucketName,
fileName,
3600,
);
});
});
describe("deleteFile", () => {
it("should delete a file from default bucket", async () => {
const fileName = "test.txt";
await service.deleteFile(fileName);
expect(minioClient.removeObject).toHaveBeenCalledWith("memegoat", fileName);
});
it("should delete a file from specific bucket", async () => {
const fileName = "test.txt";
const bucketName = "other-bucket";
await service.deleteFile(fileName, bucketName);
expect(minioClient.removeObject).toHaveBeenCalledWith(bucketName, fileName);
});
});
describe("ensureBucketExists", () => {
it("should create bucket if it does not exist", async () => {
minioClient.bucketExists.mockResolvedValue(false);
await service.ensureBucketExists("new-bucket");
expect(minioClient.makeBucket).toHaveBeenCalledWith("new-bucket");
});
it("should not create bucket if it exists", async () => {
minioClient.bucketExists.mockResolvedValue(true);
await service.ensureBucketExists("existing-bucket");
expect(minioClient.makeBucket).not.toHaveBeenCalled();
});
});
describe("getFileInfo", () => {
it("should return file info from default bucket", async () => {
const fileName = "test.txt";
const info = await service.getFileInfo(fileName);
expect(minioClient.statObject).toHaveBeenCalledWith("memegoat", fileName);
expect(info).toEqual({ size: 100, etag: "123" });
});
it("should return file info from specific bucket", async () => {
const fileName = "test.txt";
const bucketName = "other-bucket";
await service.getFileInfo(fileName, bucketName);
expect(minioClient.statObject).toHaveBeenCalledWith(bucketName, fileName);
});
});
describe("getPublicUrl", () => {
it("should use API_URL if provided", () => {
(configService.get as jest.Mock).mockImplementation((key: string) => {
if (key === "API_URL") return "https://api.test.com";
return null;
});
const url = service.getPublicUrl("test.webp");
expect(url).toBe("https://api.test.com/media?path=test.webp");
});
it("should use DOMAIN_NAME and PORT for localhost", () => {
(configService.get as jest.Mock).mockImplementation(
(key: string, def: unknown) => {
if (key === "API_URL") return null;
if (key === "DOMAIN_NAME") return "localhost";
if (key === "PORT") return 3000;
return def;
},
);
const url = service.getPublicUrl("test.webp");
expect(url).toBe("http://localhost:3000/media?path=test.webp");
});
it("should use api.DOMAIN_NAME for production", () => {
(configService.get as jest.Mock).mockImplementation(
(key: string, def: unknown) => {
if (key === "API_URL") return null;
if (key === "DOMAIN_NAME") return "memegoat.fr";
return def;
},
);
const url = service.getPublicUrl("test.webp");
expect(url).toBe("https://api.memegoat.fr/media?path=test.webp");
});
});
});