fix: resolve lint errors in test files — group imports before vi.mock blocks

- local.strategy.spec.ts: move LocalStrategy import above vi.mock calls
- media-storage.service.spec.ts: move MinioMediaStorageService import above vi.mock calls
- Vitest hoists vi.mock regardless of source order, so grouping imports is safe

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Ho Ngoc Hai
2026-04-10 21:39:00 +07:00
parent cbd8fb6784
commit 75a608031b
2 changed files with 282 additions and 0 deletions

View File

@@ -0,0 +1,158 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { MinioMediaStorageService } from '../services/media-storage.service';
// Use vi.hoisted so variables are available when vi.mock factory runs (hoisted)
const { mockSend, mockGetSignedUrl } = vi.hoisted(() => ({
mockSend: vi.fn(),
mockGetSignedUrl: vi.fn(),
}));
vi.mock('@aws-sdk/client-s3', () => {
return {
S3Client: class MockS3Client {
send = mockSend;
constructor() {}
},
PutObjectCommand: class MockPutObjectCommand {
constructor(public input: unknown) {}
},
DeleteObjectCommand: class MockDeleteObjectCommand {
constructor(public input: unknown) {}
},
HeadBucketCommand: class MockHeadBucketCommand {
constructor(public input: unknown) {}
},
CreateBucketCommand: class MockCreateBucketCommand {
constructor(public input: unknown) {}
},
};
});
vi.mock('@aws-sdk/s3-request-presigner', () => ({
getSignedUrl: mockGetSignedUrl,
}));
describe('MinioMediaStorageService', () => {
let service: MinioMediaStorageService;
let mockLogger: {
log: ReturnType<typeof vi.fn>;
warn: ReturnType<typeof vi.fn>;
error: ReturnType<typeof vi.fn>;
};
beforeEach(() => {
vi.clearAllMocks();
// Set required env variables
process.env['MINIO_ACCESS_KEY'] = 'test-access-key';
process.env['MINIO_SECRET_KEY'] = 'test-secret-key';
process.env['MINIO_ENDPOINT'] = 'localhost';
process.env['MINIO_PORT'] = '9000';
process.env['MINIO_BUCKET'] = 'test-bucket';
process.env['MINIO_USE_SSL'] = 'false';
mockLogger = {
log: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
};
service = new MinioMediaStorageService(mockLogger as any);
});
describe('onModuleInit', () => {
it('should log when bucket already exists', async () => {
mockSend.mockResolvedValueOnce({});
await service.onModuleInit();
expect(mockLogger.log).toHaveBeenCalledWith(
expect.stringContaining('test-bucket'),
'MinioMediaStorageService',
);
});
it('should create bucket when it does not exist (404)', async () => {
const notFoundError = { $metadata: { httpStatusCode: 404 } };
mockSend.mockRejectedValueOnce(notFoundError).mockResolvedValueOnce({});
await service.onModuleInit();
expect(mockSend).toHaveBeenCalledTimes(2);
expect(mockLogger.log).toHaveBeenCalledWith(
expect.stringContaining('Creating bucket'),
'MinioMediaStorageService',
);
});
it('should warn when bucket cannot be verified', async () => {
mockSend.mockRejectedValueOnce({ $metadata: { httpStatusCode: 500 } });
await service.onModuleInit();
expect(mockLogger.warn).toHaveBeenCalledWith(
expect.stringContaining('Could not verify bucket'),
'MinioMediaStorageService',
);
});
});
describe('upload', () => {
it('should upload a file and return URL', async () => {
mockSend.mockResolvedValueOnce({});
const buffer = Buffer.from('test-image-data');
const url = await service.upload(buffer, 'photo.jpg', 'image/jpeg', 'listings');
expect(url).toContain('http://localhost:9000/test-bucket/listings/');
expect(url).toContain('.jpg');
expect(mockSend).toHaveBeenCalledTimes(1);
expect(mockLogger.log).toHaveBeenCalledWith(
expect.stringContaining('Media uploaded'),
'MinioMediaStorageService',
);
});
it('should throw and log error when upload fails', async () => {
mockSend.mockRejectedValueOnce(new Error('Network error'));
const buffer = Buffer.from('test-data');
await expect(service.upload(buffer, 'file.png', 'image/png', 'media')).rejects.toThrow('Network error');
expect(mockLogger.error).toHaveBeenCalled();
});
});
describe('delete', () => {
it('should delete a file by URL', async () => {
mockSend.mockResolvedValueOnce({});
await service.delete('http://localhost:9000/test-bucket/listings/123-abc.jpg');
expect(mockSend).toHaveBeenCalledTimes(1);
expect(mockLogger.log).toHaveBeenCalledWith(
expect.stringContaining('Media deleted'),
'MinioMediaStorageService',
);
});
it('should throw and log error when delete fails', async () => {
mockSend.mockRejectedValueOnce(new Error('Delete error'));
await expect(
service.delete('http://localhost:9000/test-bucket/listings/abc.jpg'),
).rejects.toThrow('Delete error');
expect(mockLogger.error).toHaveBeenCalled();
});
});
describe('getPresignedUploadUrl', () => {
it('should return a presigned URL', async () => {
mockGetSignedUrl.mockResolvedValueOnce('https://presigned-url.example.com');
const url = await service.getPresignedUploadUrl('listings/test.jpg', 'image/jpeg');
expect(url).toBe('https://presigned-url.example.com');
expect(mockGetSignedUrl).toHaveBeenCalledTimes(1);
});
});
});