mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
test: add comprehensive target size integration tests for image-to-pdf
This commit is contained in:
@@ -405,4 +405,188 @@ describe("image-to-pdf", () => {
|
|||||||
expect(json.compression.jpegQuality).toBeLessThanOrEqual(95);
|
expect(json.compression.jpegQuality).toBeLessThanOrEqual(95);
|
||||||
expect(json.processedSize).toBeLessThanOrEqual(5 * 1024 * 1024);
|
expect(json.processedSize).toBeLessThanOrEqual(5 * 1024 * 1024);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("returns targetMet=false when target is impossibly small", async () => {
|
||||||
|
// Generate an 800x800 high-frequency pattern image that cannot compress
|
||||||
|
// below 50KB even at JPEG quality 10, making the target impossible to meet.
|
||||||
|
const w = 800;
|
||||||
|
const h = 800;
|
||||||
|
const raw = Buffer.alloc(w * h * 3);
|
||||||
|
for (let y = 0; y < h; y++) {
|
||||||
|
for (let x = 0; x < w; x++) {
|
||||||
|
const idx = (y * w + x) * 3;
|
||||||
|
raw[idx] = (x * 7 + y * 13) % 256;
|
||||||
|
raw[idx + 1] = (x * 11 + y * 3) % 256;
|
||||||
|
raw[idx + 2] = (x * 5 + y * 17) % 256;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const sharp = (await import("sharp")).default;
|
||||||
|
const largePng = await sharp(raw, { raw: { width: w, height: h, channels: 3 } })
|
||||||
|
.png()
|
||||||
|
.toBuffer();
|
||||||
|
|
||||||
|
const { body, contentType } = createMultipartPayload([
|
||||||
|
{ name: "file", filename: "big.png", contentType: "image/png", content: largePng },
|
||||||
|
{
|
||||||
|
name: "settings",
|
||||||
|
content: JSON.stringify({ targetSize: { value: 50, unit: "KB" } }),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const res = await app.inject({
|
||||||
|
method: "POST",
|
||||||
|
url: "/api/v1/tools/image-to-pdf",
|
||||||
|
headers: { authorization: `Bearer ${adminToken}`, "content-type": contentType },
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
const json = JSON.parse(res.body);
|
||||||
|
expect(json.compression).toBeDefined();
|
||||||
|
expect(json.compression.targetMet).toBe(false);
|
||||||
|
expect(json.compression.jpegQuality).toBe(10);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rejects target size below 50KB", async () => {
|
||||||
|
const { body, contentType } = createMultipartPayload([
|
||||||
|
{ name: "file", filename: "test.png", contentType: "image/png", content: PNG },
|
||||||
|
{
|
||||||
|
name: "settings",
|
||||||
|
content: JSON.stringify({ targetSize: { value: 10, unit: "KB" } }),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const res = await app.inject({
|
||||||
|
method: "POST",
|
||||||
|
url: "/api/v1/tools/image-to-pdf",
|
||||||
|
headers: { authorization: `Bearer ${adminToken}`, "content-type": contentType },
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(400);
|
||||||
|
const json = JSON.parse(res.body);
|
||||||
|
expect(json.error).toContain("at least 50KB");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rejects negative target size value", async () => {
|
||||||
|
const { body, contentType } = createMultipartPayload([
|
||||||
|
{ name: "file", filename: "test.png", contentType: "image/png", content: PNG },
|
||||||
|
{
|
||||||
|
name: "settings",
|
||||||
|
content: JSON.stringify({ targetSize: { value: -1, unit: "MB" } }),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const res = await app.inject({
|
||||||
|
method: "POST",
|
||||||
|
url: "/api/v1/tools/image-to-pdf",
|
||||||
|
headers: { authorization: `Bearer ${adminToken}`, "content-type": contentType },
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(400);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rejects invalid target size unit", async () => {
|
||||||
|
const { body, contentType } = createMultipartPayload([
|
||||||
|
{ name: "file", filename: "test.png", contentType: "image/png", content: PNG },
|
||||||
|
{
|
||||||
|
name: "settings",
|
||||||
|
content: JSON.stringify({ targetSize: { value: 1, unit: "GB" } }),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const res = await app.inject({
|
||||||
|
method: "POST",
|
||||||
|
url: "/api/v1/tools/image-to-pdf",
|
||||||
|
headers: { authorization: `Bearer ${adminToken}`, "content-type": contentType },
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(400);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("compresses multi-image PDF to meet target size", async () => {
|
||||||
|
const { body, contentType } = createMultipartPayload([
|
||||||
|
{ name: "file", filename: "p1.png", contentType: "image/png", content: PNG },
|
||||||
|
{ name: "file", filename: "p2.jpg", contentType: "image/jpeg", content: JPG },
|
||||||
|
{
|
||||||
|
name: "settings",
|
||||||
|
content: JSON.stringify({ targetSize: { value: 5, unit: "MB" } }),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const res = await app.inject({
|
||||||
|
method: "POST",
|
||||||
|
url: "/api/v1/tools/image-to-pdf",
|
||||||
|
headers: { authorization: `Bearer ${adminToken}`, "content-type": contentType },
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
const json = JSON.parse(res.body);
|
||||||
|
expect(json.pages).toBe(2);
|
||||||
|
expect(json.compression).toBeDefined();
|
||||||
|
expect(json.compression.targetMet).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("omits compression field when no targetSize is provided", async () => {
|
||||||
|
const { body, contentType } = createMultipartPayload([
|
||||||
|
{ name: "file", filename: "test.png", contentType: "image/png", content: PNG },
|
||||||
|
{ name: "settings", content: JSON.stringify({}) },
|
||||||
|
]);
|
||||||
|
|
||||||
|
const res = await app.inject({
|
||||||
|
method: "POST",
|
||||||
|
url: "/api/v1/tools/image-to-pdf",
|
||||||
|
headers: { authorization: `Bearer ${adminToken}`, "content-type": contentType },
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
const json = JSON.parse(res.body);
|
||||||
|
expect(json.compression).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("accepts decimal MB values for target size", async () => {
|
||||||
|
const { body, contentType } = createMultipartPayload([
|
||||||
|
{ name: "file", filename: "test.png", contentType: "image/png", content: PNG },
|
||||||
|
{
|
||||||
|
name: "settings",
|
||||||
|
content: JSON.stringify({ targetSize: { value: 1.5, unit: "MB" } }),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const res = await app.inject({
|
||||||
|
method: "POST",
|
||||||
|
url: "/api/v1/tools/image-to-pdf",
|
||||||
|
headers: { authorization: `Bearer ${adminToken}`, "content-type": contentType },
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
const json = JSON.parse(res.body);
|
||||||
|
expect(json.compression.targetRequested).toBe(Math.round(1.5 * 1024 * 1024));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("accepts KB unit for target size", async () => {
|
||||||
|
const { body, contentType } = createMultipartPayload([
|
||||||
|
{ name: "file", filename: "test.png", contentType: "image/png", content: PNG },
|
||||||
|
{
|
||||||
|
name: "settings",
|
||||||
|
content: JSON.stringify({ targetSize: { value: 500, unit: "KB" } }),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const res = await app.inject({
|
||||||
|
method: "POST",
|
||||||
|
url: "/api/v1/tools/image-to-pdf",
|
||||||
|
headers: { authorization: `Bearer ${adminToken}`, "content-type": contentType },
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res.statusCode).toBe(200);
|
||||||
|
const json = JSON.parse(res.body);
|
||||||
|
expect(json.compression.targetRequested).toBe(500 * 1024);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user