add unit tests for FileService methods and refactor file model to remove userId index
This commit is contained in:
@@ -15,7 +15,6 @@ const fileSchema = new mongoose.Schema<File>(
|
|||||||
userId: {
|
userId: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
index: true,
|
|
||||||
},
|
},
|
||||||
objectKey: {
|
objectKey: {
|
||||||
type: String,
|
type: String,
|
||||||
|
|||||||
@@ -77,7 +77,6 @@ export class RestStorage {
|
|||||||
const userId = req.payload.uuid;
|
const userId = req.payload.uuid;
|
||||||
const objectKey = req.params[0];
|
const objectKey = req.params[0];
|
||||||
|
|
||||||
console.log(objectKey);
|
|
||||||
if (!objectKey.startsWith(`user-${userId}/`)) {
|
if (!objectKey.startsWith(`user-${userId}/`)) {
|
||||||
return forbidden(res);
|
return forbidden(res);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,210 @@
|
|||||||
|
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||||
|
import { FileModel } from "../../../src/db/models/file";
|
||||||
|
import { FileService } from "../../../src/services/db/fileService";
|
||||||
|
|
||||||
|
vi.mock("../../../src/db/models/file");
|
||||||
|
|
||||||
|
const mockedFileModel = vi.mocked(FileModel);
|
||||||
|
|
||||||
|
describe("FileService", () => {
|
||||||
|
let fileService: FileService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
(FileService as any).instance = undefined;
|
||||||
|
fileService = FileService.getInstance();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("getInstance (singleton)", () => {
|
||||||
|
it("should create a singleton instance", () => {
|
||||||
|
const instance1 = fileService;
|
||||||
|
const instance2 = FileService.getInstance();
|
||||||
|
|
||||||
|
expect(instance1).toBe(instance2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("createFileRecord", () => {
|
||||||
|
it("should create a file record and return it", async () => {
|
||||||
|
const mockDate = new Date();
|
||||||
|
vi.useFakeTimers();
|
||||||
|
vi.setSystemTime(mockDate);
|
||||||
|
|
||||||
|
const mockFile = {
|
||||||
|
userId: "user123",
|
||||||
|
objectKey: "object-key-123",
|
||||||
|
originalName: "test-file.txt",
|
||||||
|
mimeType: "text/plain",
|
||||||
|
size: 1024,
|
||||||
|
uploadedAt: mockDate,
|
||||||
|
save: vi.fn().mockResolvedValue({
|
||||||
|
userId: "user123",
|
||||||
|
objectKey: "object-key-123",
|
||||||
|
originalName: "test-file.txt",
|
||||||
|
mimeType: "text/plain",
|
||||||
|
size: 1024,
|
||||||
|
uploadedAt: mockDate,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
mockedFileModel.mockImplementation(() => mockFile as any);
|
||||||
|
|
||||||
|
const result = await fileService.createFileRecord(
|
||||||
|
"user123",
|
||||||
|
"object-key-123",
|
||||||
|
"test-file.txt",
|
||||||
|
"text/plain",
|
||||||
|
1024
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(mockedFileModel).toHaveBeenCalledWith({
|
||||||
|
userId: "user123",
|
||||||
|
objectKey: "object-key-123",
|
||||||
|
originalName: "test-file.txt",
|
||||||
|
mimeType: "text/plain",
|
||||||
|
size: 1024,
|
||||||
|
uploadedAt: mockDate,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockFile.save).toHaveBeenCalled();
|
||||||
|
expect(result).toEqual({
|
||||||
|
userId: "user123",
|
||||||
|
objectKey: "object-key-123",
|
||||||
|
originalName: "test-file.txt",
|
||||||
|
mimeType: "text/plain",
|
||||||
|
size: 1024,
|
||||||
|
uploadedAt: mockDate,
|
||||||
|
});
|
||||||
|
|
||||||
|
vi.useRealTimers();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("getFilesByUserId", () => {
|
||||||
|
it("should return files for a given userId", async () => {
|
||||||
|
const mockFiles = [
|
||||||
|
{ objectKey: "object1", originalName: "file1.txt" },
|
||||||
|
{ objectKey: "object2", originalName: "file2.txt" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const mockExec = vi.fn().mockResolvedValue(mockFiles);
|
||||||
|
const mockSort = vi.fn().mockReturnValue({ exec: mockExec });
|
||||||
|
mockedFileModel.find.mockReturnValue({ sort: mockSort } as any);
|
||||||
|
|
||||||
|
const result = await fileService.getFilesByUserId("user123");
|
||||||
|
|
||||||
|
expect(mockedFileModel.find).toHaveBeenCalledWith({ userId: "user123" });
|
||||||
|
expect(mockSort).toHaveBeenCalledWith({ uploadedAt: -1 });
|
||||||
|
expect(result).toEqual(mockFiles);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("getFileByObjectKey", () => {
|
||||||
|
it("should return a file for a given objectKey", async () => {
|
||||||
|
const mockFile = { objectKey: "object-key-123", originalName: "file1.txt" };
|
||||||
|
|
||||||
|
const mockExec = vi.fn().mockResolvedValue(mockFile);
|
||||||
|
mockedFileModel.findOne.mockReturnValue({ exec: mockExec } as any);
|
||||||
|
|
||||||
|
const result = await fileService.getFileByObjectKey("object-key-123");
|
||||||
|
|
||||||
|
expect(mockedFileModel.findOne).toHaveBeenCalledWith({ objectKey: "object-key-123" });
|
||||||
|
expect(result).toEqual(mockFile);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return null if file not found", async () => {
|
||||||
|
const mockExec = vi.fn().mockResolvedValue(null);
|
||||||
|
mockedFileModel.findOne.mockReturnValue({ exec: mockExec } as any);
|
||||||
|
|
||||||
|
const result = await fileService.getFileByObjectKey("non-existent-key");
|
||||||
|
|
||||||
|
expect(result).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("deleteFileRecord", () => {
|
||||||
|
it("should delete a file record and return true on success", async () => {
|
||||||
|
mockedFileModel.deleteOne.mockResolvedValue({ deletedCount: 1 } as any);
|
||||||
|
|
||||||
|
const result = await fileService.deleteFileRecord("object-key-123");
|
||||||
|
|
||||||
|
expect(mockedFileModel.deleteOne).toHaveBeenCalledWith({ objectKey: "object-key-123" });
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false if no file was deleted", async () => {
|
||||||
|
mockedFileModel.deleteOne.mockResolvedValue({ deletedCount: 0 } as any);
|
||||||
|
|
||||||
|
const result = await fileService.deleteFileRecord("non-existent-key");
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("isFileDuplicate", () => {
|
||||||
|
it("should return true if a file with the same name exists for the user", async () => {
|
||||||
|
mockedFileModel.countDocuments.mockResolvedValue(1);
|
||||||
|
|
||||||
|
const result = await fileService.isFileDuplicate("duplicate-file.txt", "user123");
|
||||||
|
|
||||||
|
expect(mockedFileModel.countDocuments).toHaveBeenCalledWith({
|
||||||
|
userId: "user123",
|
||||||
|
originalName: "duplicate-file.txt",
|
||||||
|
});
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false if no duplicate file exists", async () => {
|
||||||
|
mockedFileModel.countDocuments.mockResolvedValue(0);
|
||||||
|
|
||||||
|
const result = await fileService.isFileDuplicate("unique-file.txt", "user123");
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false if an error occurs", async () => {
|
||||||
|
mockedFileModel.countDocuments.mockRejectedValue(new Error("Database error"));
|
||||||
|
|
||||||
|
// Mock console.error to prevent test output cluttering
|
||||||
|
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
||||||
|
|
||||||
|
const result = await fileService.isFileDuplicate("error-file.txt", "user123");
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("Error in isFileDuplicate"));
|
||||||
|
|
||||||
|
consoleSpy.mockRestore();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("updateObjectKey", () => {
|
||||||
|
it("should update object key and return the updated file", async () => {
|
||||||
|
const mockFile = {
|
||||||
|
_id: "file123",
|
||||||
|
objectKey: "new-object-key",
|
||||||
|
originalName: "test.txt",
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockExec = vi.fn().mockResolvedValue(mockFile);
|
||||||
|
mockedFileModel.findByIdAndUpdate.mockReturnValue({ exec: mockExec } as any);
|
||||||
|
|
||||||
|
const result = await fileService.updateObjectKey("file123", "new-object-key");
|
||||||
|
|
||||||
|
expect(mockedFileModel.findByIdAndUpdate).toHaveBeenCalledWith(
|
||||||
|
"file123",
|
||||||
|
{ objectKey: "new-object-key" },
|
||||||
|
{ new: true }
|
||||||
|
);
|
||||||
|
expect(result).toEqual(mockFile);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return null if file not found", async () => {
|
||||||
|
const mockExec = vi.fn().mockResolvedValue(null);
|
||||||
|
mockedFileModel.findByIdAndUpdate.mockReturnValue({ exec: mockExec } as any);
|
||||||
|
|
||||||
|
const result = await fileService.updateObjectKey("non-existent-id", "new-object-key");
|
||||||
|
|
||||||
|
expect(result).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -10,15 +10,17 @@ vi.mock("@aws-sdk/client-s3", async (importOriginal) => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
vi.mock("../../src/services/db/fileService", () => ({
|
||||||
|
FileService: {
|
||||||
|
getInstance: vi.fn(),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
import { S3Service, S3ClientConfig } from "../../src/services/s3Service";
|
import { S3Service, S3ClientConfig } from "../../src/services/s3Service";
|
||||||
import {
|
import { S3Client, CreateBucketCommand, PutObjectCommand, DeleteObjectCommand } from "@aws-sdk/client-s3";
|
||||||
S3Client,
|
|
||||||
CreateBucketCommand,
|
|
||||||
PutObjectCommand,
|
|
||||||
ListObjectsV2Command,
|
|
||||||
DeleteObjectCommand,
|
|
||||||
} from "@aws-sdk/client-s3";
|
|
||||||
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
||||||
|
import { FileService } from "../../src/services/db/fileService";
|
||||||
|
import { File } from "../../src/db/models/file";
|
||||||
|
|
||||||
const testConfig: S3ClientConfig = {
|
const testConfig: S3ClientConfig = {
|
||||||
endpoint: "http://test-minio",
|
endpoint: "http://test-minio",
|
||||||
@@ -32,6 +34,13 @@ const testConfig: S3ClientConfig = {
|
|||||||
const MockS3Client = vi.mocked(S3Client);
|
const MockS3Client = vi.mocked(S3Client);
|
||||||
const mockSend = vi.fn();
|
const mockSend = vi.fn();
|
||||||
const mockGetSignedUrl = vi.mocked(getSignedUrl);
|
const mockGetSignedUrl = vi.mocked(getSignedUrl);
|
||||||
|
const mockFileService = {
|
||||||
|
createFileRecord: vi.fn(),
|
||||||
|
getFilesByUserId: vi.fn(),
|
||||||
|
isFileDuplicate: vi.fn(),
|
||||||
|
deleteFileRecord: vi.fn(),
|
||||||
|
updateObjectKey: vi.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
describe("S3Service", () => {
|
describe("S3Service", () => {
|
||||||
let s3Service: S3Service;
|
let s3Service: S3Service;
|
||||||
@@ -53,10 +62,12 @@ describe("S3Service", () => {
|
|||||||
}) as never
|
}) as never
|
||||||
);
|
);
|
||||||
|
|
||||||
|
vi.mocked(FileService.getInstance).mockReturnValue(mockFileService as any);
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
S3Service.instance = undefined;
|
S3Service.instance = undefined;
|
||||||
|
|
||||||
s3Service = S3Service.getInstance(testConfig);
|
s3Service = S3Service.getInstance(testConfig, mockFileService as any);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Initialization and Bucket Creation", () => {
|
describe("Initialization and Bucket Creation", () => {
|
||||||
@@ -90,9 +101,12 @@ describe("S3Service", () => {
|
|||||||
originalname: "test-image.jpg",
|
originalname: "test-image.jpg",
|
||||||
buffer: Buffer.from("test-data"),
|
buffer: Buffer.from("test-data"),
|
||||||
mimetype: "image/jpeg",
|
mimetype: "image/jpeg",
|
||||||
|
size: 1024,
|
||||||
};
|
};
|
||||||
|
|
||||||
const userId = "user-123";
|
const userId = "user-123";
|
||||||
|
mockSend.mockResolvedValue({});
|
||||||
|
mockFileService.createFileRecord.mockResolvedValue({});
|
||||||
|
|
||||||
const objectKey = await s3Service.uploadFile(mockFile as never, userId);
|
const objectKey = await s3Service.uploadFile(mockFile as never, userId);
|
||||||
|
|
||||||
@@ -106,6 +120,14 @@ describe("S3Service", () => {
|
|||||||
expect(sentCommand.Bucket).toBe("test-bucket");
|
expect(sentCommand.Bucket).toBe("test-bucket");
|
||||||
expect(sentCommand.Key).toBe(objectKey);
|
expect(sentCommand.Key).toBe(objectKey);
|
||||||
expect(sentCommand.Body).toBe(mockFile.buffer);
|
expect(sentCommand.Body).toBe(mockFile.buffer);
|
||||||
|
|
||||||
|
expect(mockFileService.createFileRecord).toHaveBeenCalledWith(
|
||||||
|
userId,
|
||||||
|
objectKey,
|
||||||
|
mockFile.originalname,
|
||||||
|
mockFile.mimetype,
|
||||||
|
mockFile.size
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -113,69 +135,60 @@ describe("S3Service", () => {
|
|||||||
const userId = "user-123";
|
const userId = "user-123";
|
||||||
|
|
||||||
it("should return a correctly formatted list of files for a user", async () => {
|
it("should return a correctly formatted list of files for a user", async () => {
|
||||||
const mockS3Response = {
|
const mockDbFiles: Partial<File>[] = [
|
||||||
Contents: [
|
{
|
||||||
{
|
objectKey: `user-${userId}/uuid1_file1.txt`,
|
||||||
Key: `user-${userId}/uuid1_file1.txt`,
|
originalName: "file1.txt",
|
||||||
LastModified: new Date("2023-01-01"),
|
mimeType: "text/plain",
|
||||||
},
|
size: 100,
|
||||||
{
|
uploadedAt: new Date("2023-01-01"),
|
||||||
Key: `user-${userId}/uuid2_image.jpg`,
|
},
|
||||||
LastModified: new Date("2023-01-02"),
|
{
|
||||||
},
|
objectKey: `user-${userId}/uuid2_image.jpg`,
|
||||||
],
|
originalName: "image.jpg",
|
||||||
};
|
mimeType: "image/jpeg",
|
||||||
mockSend.mockResolvedValue(mockS3Response);
|
size: 1024,
|
||||||
|
uploadedAt: new Date("2023-01-02"),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
mockFileService.getFilesByUserId.mockResolvedValue(mockDbFiles);
|
||||||
|
|
||||||
const files = await s3Service.listFilesForUser(userId);
|
const files = await s3Service.listFilesForUser(userId);
|
||||||
|
|
||||||
expect(mockSend).toHaveBeenCalledOnce();
|
expect(mockFileService.getFilesByUserId).toHaveBeenCalledWith(userId);
|
||||||
expect(mockSend).toHaveBeenCalledWith(expect.any(ListObjectsV2Command));
|
|
||||||
|
|
||||||
const sentCommand = (mockSend.mock.calls[0][0] as ListObjectsV2Command).input;
|
|
||||||
expect(sentCommand.Bucket).toBe("test-bucket");
|
|
||||||
expect(sentCommand.Prefix).toBe(`user-${userId}/`);
|
|
||||||
|
|
||||||
expect(files).toHaveLength(2);
|
expect(files).toHaveLength(2);
|
||||||
expect(files).toContainEqual({
|
expect(files).toContainEqual({
|
||||||
key: `user-${userId}/uuid1_file1.txt`,
|
key: `user-${userId}/uuid1_file1.txt`,
|
||||||
lastModified: new Date("2023-01-01"),
|
lastModified: new Date("2023-01-01"),
|
||||||
originalName: "file1.txt",
|
originalName: "file1.txt",
|
||||||
|
mimeType: "text/plain",
|
||||||
|
size: 100,
|
||||||
});
|
});
|
||||||
expect(files).toContainEqual({
|
expect(files).toContainEqual({
|
||||||
key: `user-${userId}/uuid2_image.jpg`,
|
key: `user-${userId}/uuid2_image.jpg`,
|
||||||
lastModified: new Date("2023-01-02"),
|
lastModified: new Date("2023-01-02"),
|
||||||
originalName: "image.jpg",
|
originalName: "image.jpg",
|
||||||
|
mimeType: "image/jpeg",
|
||||||
|
size: 1024,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return an empty array if the user has no files", async () => {
|
it("should return an empty array if the user has no files", async () => {
|
||||||
const mockS3Response = {
|
mockFileService.getFilesByUserId.mockResolvedValue([]);
|
||||||
Contents: [],
|
|
||||||
};
|
|
||||||
mockSend.mockResolvedValue(mockS3Response);
|
|
||||||
|
|
||||||
const files = await s3Service.listFilesForUser(userId);
|
const files = await s3Service.listFilesForUser(userId);
|
||||||
|
|
||||||
expect(mockSend).toHaveBeenCalledOnce();
|
expect(mockFileService.getFilesByUserId).toHaveBeenCalledWith(userId);
|
||||||
expect(files).toEqual([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return an empty array if the S3 response has no 'Contents' property", async () => {
|
|
||||||
const mockS3Response = {};
|
|
||||||
mockSend.mockResolvedValue(mockS3Response);
|
|
||||||
|
|
||||||
const files = await s3Service.listFilesForUser(userId);
|
|
||||||
|
|
||||||
expect(mockSend).toHaveBeenCalledOnce();
|
|
||||||
expect(files).toEqual([]);
|
expect(files).toEqual([]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("deleteFile", () => {
|
describe("deleteFile", () => {
|
||||||
it("should call the S3 client with the correct DeleteObjectCommand", async () => {
|
it("should call the S3 client with the correct DeleteObjectCommand and delete the file record", async () => {
|
||||||
const objectKey = "user-123/some-file-to-delete.txt";
|
const objectKey = "user-123/some-file-to-delete.txt";
|
||||||
mockSend.mockResolvedValue({});
|
mockSend.mockResolvedValue({});
|
||||||
|
mockFileService.deleteFileRecord.mockResolvedValue(true);
|
||||||
|
|
||||||
await expect(s3Service.deleteFile(objectKey)).resolves.toBeUndefined();
|
await expect(s3Service.deleteFile(objectKey)).resolves.toBeUndefined();
|
||||||
|
|
||||||
@@ -185,6 +198,8 @@ describe("S3Service", () => {
|
|||||||
const sentCommand = (mockSend.mock.calls[0][0] as DeleteObjectCommand).input;
|
const sentCommand = (mockSend.mock.calls[0][0] as DeleteObjectCommand).input;
|
||||||
expect(sentCommand.Bucket).toBe("test-bucket");
|
expect(sentCommand.Bucket).toBe("test-bucket");
|
||||||
expect(sentCommand.Key).toBe(objectKey);
|
expect(sentCommand.Key).toBe(objectKey);
|
||||||
|
|
||||||
|
expect(mockFileService.deleteFileRecord).toHaveBeenCalledWith(objectKey);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should throw an error if the S3 client fails to delete the object", async () => {
|
it("should throw an error if the S3 client fails to delete the object", async () => {
|
||||||
@@ -193,11 +208,13 @@ describe("S3Service", () => {
|
|||||||
mockSend.mockRejectedValue(s3Error);
|
mockSend.mockRejectedValue(s3Error);
|
||||||
|
|
||||||
await expect(s3Service.deleteFile(objectKey)).rejects.toThrow("Access Denied");
|
await expect(s3Service.deleteFile(objectKey)).rejects.toThrow("Access Denied");
|
||||||
|
|
||||||
|
expect(mockFileService.deleteFileRecord).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("isFileDuplicate", () => {
|
describe("isFileDuplicate", () => {
|
||||||
it("should correctly identify duplicate files", async () => {
|
it("should use FileService to check for duplicate files", async () => {
|
||||||
const userId = "user-123";
|
const userId = "user-123";
|
||||||
const mockFile = {
|
const mockFile = {
|
||||||
originalname: "duplicate-image.jpg",
|
originalname: "duplicate-image.jpg",
|
||||||
@@ -205,30 +222,12 @@ describe("S3Service", () => {
|
|||||||
mimetype: "image/jpeg",
|
mimetype: "image/jpeg",
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockFiles = [
|
mockFileService.isFileDuplicate.mockResolvedValue(true);
|
||||||
{
|
|
||||||
key: `user-${userId}/file1_original-file.txt`,
|
|
||||||
lastModified: new Date("2023-01-01"),
|
|
||||||
originalName: "original-file.txt",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: `user-${userId}/file2_duplicate-image.jpg`,
|
|
||||||
lastModified: new Date("2023-01-02"),
|
|
||||||
originalName: "duplicate-image.jpg",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
mockSend.mockResolvedValueOnce({
|
|
||||||
Contents: mockFiles.map((file) => ({
|
|
||||||
Key: file.key,
|
|
||||||
LastModified: file.lastModified,
|
|
||||||
})),
|
|
||||||
});
|
|
||||||
|
|
||||||
const isDuplicate = await s3Service.isFileDuplicate(mockFile as never, userId);
|
const isDuplicate = await s3Service.isFileDuplicate(mockFile as never, userId);
|
||||||
|
|
||||||
expect(isDuplicate).toBe(true);
|
expect(isDuplicate).toBe(true);
|
||||||
expect(mockSend).toHaveBeenCalledWith(expect.any(ListObjectsV2Command));
|
expect(mockFileService.isFileDuplicate).toHaveBeenCalledWith(mockFile.originalname, userId);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should correctly identify non-duplicate files", async () => {
|
it("should correctly identify non-duplicate files", async () => {
|
||||||
@@ -239,60 +238,12 @@ describe("S3Service", () => {
|
|||||||
mimetype: "image/jpeg",
|
mimetype: "image/jpeg",
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockFiles = [
|
mockFileService.isFileDuplicate.mockResolvedValue(false);
|
||||||
{
|
|
||||||
key: `user-${userId}/file1_existing-file.txt`,
|
|
||||||
lastModified: new Date("2023-01-01"),
|
|
||||||
originalName: "existing-file.txt",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: `user-${userId}/file2_another-image.jpg`,
|
|
||||||
lastModified: new Date("2023-01-02"),
|
|
||||||
originalName: "another-image.jpg",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
mockSend.mockResolvedValueOnce({
|
|
||||||
Contents: mockFiles.map((file) => ({
|
|
||||||
Key: file.key,
|
|
||||||
LastModified: file.lastModified,
|
|
||||||
})),
|
|
||||||
});
|
|
||||||
|
|
||||||
const isDuplicate = await s3Service.isFileDuplicate(mockFile as never, userId);
|
const isDuplicate = await s3Service.isFileDuplicate(mockFile as never, userId);
|
||||||
|
|
||||||
expect(isDuplicate).toBe(false);
|
expect(isDuplicate).toBe(false);
|
||||||
expect(mockSend).toHaveBeenCalledWith(expect.any(ListObjectsV2Command));
|
expect(mockFileService.isFileDuplicate).toHaveBeenCalledWith(mockFile.originalname, userId);
|
||||||
});
|
|
||||||
|
|
||||||
it("should handle empty file lists correctly", async () => {
|
|
||||||
const userId = "user-123";
|
|
||||||
const mockFile = {
|
|
||||||
originalname: "test-image.jpg",
|
|
||||||
buffer: Buffer.from("test-data"),
|
|
||||||
mimetype: "image/jpeg",
|
|
||||||
};
|
|
||||||
|
|
||||||
mockSend.mockResolvedValueOnce({
|
|
||||||
Contents: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
const isDuplicate = await s3Service.isFileDuplicate(mockFile as never, userId);
|
|
||||||
|
|
||||||
expect(isDuplicate).toBe(false);
|
|
||||||
expect(mockSend).toHaveBeenCalledWith(expect.any(ListObjectsV2Command));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("extractOriginalNameFromKey", () => {
|
|
||||||
it("should correctly extract the original filename from an object key", () => {
|
|
||||||
const originalName = (s3Service as any).extractOriginalNameFromKey("user-123/abc123_original-file.jpg");
|
|
||||||
expect(originalName).toBe("original-file.jpg");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return undefined for invalid object keys", () => {
|
|
||||||
const originalName = (s3Service as any).extractOriginalNameFromKey("invalid-key");
|
|
||||||
expect(originalName).toBeUndefined();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -307,7 +258,6 @@ describe("S3Service", () => {
|
|||||||
|
|
||||||
expect(signedUrl).toBe(fakeSignedUrl);
|
expect(signedUrl).toBe(fakeSignedUrl);
|
||||||
|
|
||||||
// Prüfung, dass getSignedUrl korrekt aufgerufen wurde
|
|
||||||
expect(mockGetSignedUrl).toHaveBeenCalledOnce();
|
expect(mockGetSignedUrl).toHaveBeenCalledOnce();
|
||||||
expect(mockGetSignedUrl).toHaveBeenCalledWith(
|
expect(mockGetSignedUrl).toHaveBeenCalledWith(
|
||||||
expect.anything(),
|
expect.anything(),
|
||||||
|
|||||||
Reference in New Issue
Block a user