- Fix unused function parameters (prefix with _) - Fix unused imports in test files - Fix import ordering in test files - Auto-fix formatting issues across 22 files Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
161 lines
4.8 KiB
TypeScript
161 lines
4.8 KiB
TypeScript
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
|
|
// Mock the S3 client send method
|
|
const mockSend = mock(() => Promise.resolve({}));
|
|
const mockGetSignedUrl = mock(() =>
|
|
Promise.resolve("https://minio:9000/gearbox-images/test.jpg?signed=1"),
|
|
);
|
|
|
|
// Mock modules before importing the service
|
|
mock.module("@aws-sdk/client-s3", () => ({
|
|
S3Client: class MockS3Client {
|
|
send = mockSend;
|
|
},
|
|
PutObjectCommand: class MockPutObjectCommand {
|
|
input: Record<string, unknown>;
|
|
constructor(input: Record<string, unknown>) {
|
|
this.input = input;
|
|
}
|
|
},
|
|
DeleteObjectCommand: class MockDeleteObjectCommand {
|
|
input: Record<string, unknown>;
|
|
constructor(input: Record<string, unknown>) {
|
|
this.input = input;
|
|
}
|
|
},
|
|
GetObjectCommand: class MockGetObjectCommand {
|
|
input: Record<string, unknown>;
|
|
constructor(input: Record<string, unknown>) {
|
|
this.input = input;
|
|
}
|
|
},
|
|
}));
|
|
|
|
mock.module("@aws-sdk/s3-request-presigner", () => ({
|
|
getSignedUrl: mockGetSignedUrl,
|
|
}));
|
|
|
|
// Set env vars before importing the service
|
|
process.env.S3_ENDPOINT = "http://localhost:9000";
|
|
process.env.S3_ACCESS_KEY = "minioadmin";
|
|
process.env.S3_SECRET_KEY = "minioadmin";
|
|
process.env.S3_BUCKET = "gearbox-images";
|
|
process.env.S3_REGION = "us-east-1";
|
|
|
|
// Import after mocking
|
|
const { uploadImage, deleteImage, getImageUrl, withImageUrl, withImageUrls } =
|
|
await import("@/server/services/storage.service");
|
|
|
|
describe("storage.service", () => {
|
|
beforeEach(() => {
|
|
mockSend.mockClear();
|
|
mockGetSignedUrl.mockClear();
|
|
mockGetSignedUrl.mockResolvedValue(
|
|
"https://minio:9000/gearbox-images/test.jpg?signed=1",
|
|
);
|
|
});
|
|
|
|
describe("uploadImage", () => {
|
|
test("calls PutObjectCommand with correct params", async () => {
|
|
const buffer = Buffer.from("fake-image-data");
|
|
await uploadImage(buffer, "test-image.jpg", "image/jpeg");
|
|
|
|
expect(mockSend).toHaveBeenCalledTimes(1);
|
|
const command = mockSend.mock.calls[0][0] as {
|
|
input: Record<string, unknown>;
|
|
};
|
|
expect(command.input.Bucket).toBe("gearbox-images");
|
|
expect(command.input.Key).toBe("test-image.jpg");
|
|
expect(command.input.ContentType).toBe("image/jpeg");
|
|
expect(Buffer.isBuffer(command.input.Body)).toBe(true);
|
|
});
|
|
|
|
test("handles ArrayBuffer input", async () => {
|
|
const arrayBuffer = new ArrayBuffer(10);
|
|
await uploadImage(arrayBuffer, "test.png", "image/png");
|
|
|
|
expect(mockSend).toHaveBeenCalledTimes(1);
|
|
const command = mockSend.mock.calls[0][0] as {
|
|
input: Record<string, unknown>;
|
|
};
|
|
expect(Buffer.isBuffer(command.input.Body)).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("deleteImage", () => {
|
|
test("calls DeleteObjectCommand with correct params", async () => {
|
|
await deleteImage("test-image.jpg");
|
|
|
|
expect(mockSend).toHaveBeenCalledTimes(1);
|
|
const command = mockSend.mock.calls[0][0] as {
|
|
input: Record<string, unknown>;
|
|
};
|
|
expect(command.input.Bucket).toBe("gearbox-images");
|
|
expect(command.input.Key).toBe("test-image.jpg");
|
|
});
|
|
});
|
|
|
|
describe("getImageUrl", () => {
|
|
test("calls getSignedUrl and returns result", async () => {
|
|
const url = await getImageUrl("test-image.jpg");
|
|
|
|
expect(mockGetSignedUrl).toHaveBeenCalledTimes(1);
|
|
expect(url).toBe("https://minio:9000/gearbox-images/test.jpg?signed=1");
|
|
});
|
|
});
|
|
|
|
describe("withImageUrl", () => {
|
|
test("returns null imageUrl when imageFilename is null", async () => {
|
|
const record = { id: 1, name: "Test Item", imageFilename: null };
|
|
const result = await withImageUrl(record);
|
|
|
|
expect(result.imageUrl).toBeNull();
|
|
expect(result.id).toBe(1);
|
|
expect(result.name).toBe("Test Item");
|
|
expect(mockGetSignedUrl).not.toHaveBeenCalled();
|
|
});
|
|
|
|
test("returns presigned URL when imageFilename is present", async () => {
|
|
const record = {
|
|
id: 1,
|
|
name: "Test Item",
|
|
imageFilename: "photo.jpg",
|
|
};
|
|
const result = await withImageUrl(record);
|
|
|
|
expect(result.imageUrl).toBe(
|
|
"https://minio:9000/gearbox-images/test.jpg?signed=1",
|
|
);
|
|
expect(result.id).toBe(1);
|
|
expect(mockGetSignedUrl).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|
|
|
|
describe("withImageUrls", () => {
|
|
test("processes array of records correctly", async () => {
|
|
const records = [
|
|
{ id: 1, imageFilename: "a.jpg" },
|
|
{ id: 2, imageFilename: null },
|
|
{ id: 3, imageFilename: "b.png" },
|
|
];
|
|
const results = await withImageUrls(records);
|
|
|
|
expect(results).toHaveLength(3);
|
|
expect(results[0].imageUrl).toBe(
|
|
"https://minio:9000/gearbox-images/test.jpg?signed=1",
|
|
);
|
|
expect(results[1].imageUrl).toBeNull();
|
|
expect(results[2].imageUrl).toBe(
|
|
"https://minio:9000/gearbox-images/test.jpg?signed=1",
|
|
);
|
|
// Called twice: for records[0] and records[2]
|
|
expect(mockGetSignedUrl).toHaveBeenCalledTimes(2);
|
|
});
|
|
|
|
test("handles empty array", async () => {
|
|
const results = await withImageUrls([]);
|
|
expect(results).toHaveLength(0);
|
|
});
|
|
});
|
|
});
|