feat: implement storage provider with local and S3 adapters
This commit is contained in:
146
tests/lib/storage/storage.local.test.ts
Normal file
146
tests/lib/storage/storage.local.test.ts
Normal file
@@ -0,0 +1,146 @@
|
||||
import { LocalStorageAdapter } from '@/lib/storage/storage.adapter';
|
||||
import { promises as fs } from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
describe('LocalStorageAdapter', () => {
|
||||
const testUploadDir = 'tests/tmp/uploads';
|
||||
let adapter: LocalStorageAdapter;
|
||||
|
||||
beforeEach(async () => {
|
||||
adapter = new LocalStorageAdapter(testUploadDir);
|
||||
// Clean up test directory before each test
|
||||
try {
|
||||
await fs.rm(testUploadDir, { recursive: true, force: true });
|
||||
} catch {
|
||||
// Directory doesn't exist yet
|
||||
}
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
// Clean up test directory after each test
|
||||
try {
|
||||
await fs.rm(testUploadDir, { recursive: true, force: true });
|
||||
} catch {
|
||||
// Directory might not exist
|
||||
}
|
||||
});
|
||||
|
||||
describe('put', () => {
|
||||
it('should create uploads directory if it does not exist', async () => {
|
||||
const key = 'test-image.jpg';
|
||||
const file = Buffer.from('test file content');
|
||||
|
||||
await adapter.put(key, file, 'image/jpeg');
|
||||
|
||||
const dirExists = await fs
|
||||
.stat(testUploadDir)
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
expect(dirExists).toBe(true);
|
||||
});
|
||||
|
||||
it('should save file to disk with provided key', async () => {
|
||||
const key = 'test-image.jpg';
|
||||
const fileContent = 'test file content';
|
||||
const file = Buffer.from(fileContent);
|
||||
|
||||
await adapter.put(key, file, 'image/jpeg');
|
||||
|
||||
const savedContent = await fs.readFile(
|
||||
path.join(testUploadDir, key),
|
||||
'utf-8'
|
||||
);
|
||||
expect(savedContent).toBe(fileContent);
|
||||
});
|
||||
|
||||
it('should return StorageResult with local type', async () => {
|
||||
const key = 'test-image.jpg';
|
||||
const file = Buffer.from('test file content');
|
||||
|
||||
const result = await adapter.put(key, file, 'image/jpeg');
|
||||
|
||||
expect(result).toEqual({
|
||||
type: 'local',
|
||||
provider: null,
|
||||
key,
|
||||
publicUrl: `/tests/tmp/uploads/test-image.jpg`,
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle Blob input', async () => {
|
||||
const key = 'test-blob.jpg';
|
||||
const fileContent = 'blob file content';
|
||||
const blob = new Blob([fileContent], { type: 'image/jpeg' });
|
||||
|
||||
const result = await adapter.put(key, blob, 'image/jpeg');
|
||||
|
||||
const savedContent = await fs.readFile(
|
||||
path.join(testUploadDir, key),
|
||||
'utf-8'
|
||||
);
|
||||
expect(savedContent).toBe(fileContent);
|
||||
expect(result.type).toBe('local');
|
||||
});
|
||||
|
||||
it('should create nested directories for keys with paths', async () => {
|
||||
const key = 'articles/2026/04/image.jpg';
|
||||
const file = Buffer.from('nested file content');
|
||||
|
||||
const result = await adapter.put(key, file, 'image/jpeg');
|
||||
|
||||
const savedContent = await fs.readFile(
|
||||
path.join(testUploadDir, key),
|
||||
'utf-8'
|
||||
);
|
||||
expect(savedContent).toBe('nested file content');
|
||||
expect(result.publicUrl).toBe(
|
||||
`/tests/tmp/uploads/articles/2026/04/image.jpg`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('delete', () => {
|
||||
it('should delete file from disk', async () => {
|
||||
const key = 'test-image.jpg';
|
||||
const file = Buffer.from('test file content');
|
||||
|
||||
await adapter.put(key, file, 'image/jpeg');
|
||||
const filePathBeforeDelete = path.join(testUploadDir, key);
|
||||
expect(
|
||||
await fs
|
||||
.stat(filePathBeforeDelete)
|
||||
.then(() => true)
|
||||
.catch(() => false)
|
||||
).toBe(true);
|
||||
|
||||
await adapter.delete(key);
|
||||
|
||||
const fileExists = await fs
|
||||
.stat(filePathBeforeDelete)
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
expect(fileExists).toBe(false);
|
||||
});
|
||||
|
||||
it('should not throw error if file does not exist', async () => {
|
||||
const key = 'non-existent-file.jpg';
|
||||
|
||||
// Should not throw
|
||||
await expect(adapter.delete(key)).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('should delete nested files', async () => {
|
||||
const key = 'articles/2026/04/image.jpg';
|
||||
const file = Buffer.from('nested file content');
|
||||
|
||||
await adapter.put(key, file, 'image/jpeg');
|
||||
await adapter.delete(key);
|
||||
|
||||
const fileExists = await fs
|
||||
.stat(path.join(testUploadDir, key))
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
expect(fileExists).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user