mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
test: massive test coverage expansion (+1,437 tests, 22 new files)
Expand test coverage across all layers via 14 parallel agents: Unit tests (3,378 total, +534): - First-ever AI sidecar tests (157 tests covering bridge lifecycle, all 12 tool modules) - API route infrastructure (auth, pipeline, batch, settings, teams, roles, audit, api-keys, files, docs) - Lib coverage improvements (audit 7%->95%, worker-pool 33%->100%) - Web store/lib gap fills (features-store, tool-registry) Integration tests (4,403 total, +903): - Expanded 19 tool test files with parameter variations, format edge cases, boundary values - Cross-format matrix: 290 tests covering 14 tools x 17 formats - Adversarial/edge cases: 63 tests for extreme inputs, concurrent requests, corrupted files E2E-Docker (125 new tests): - Expanded 8 spec files + 1 new file covering all 49 tools - Added HEIC/format handling, auth failures, download verification GUI E2E (expanded 28 spec files): - Navigation, responsive layout, keyboard shortcuts - All 51 tool UIs with settings, processing, display modes - Batch/pipeline workflows, settings/RBAC, visual regression - Resilience, accessibility (ARIA, contrast, focus), performance budgets
This commit is contained in:
@@ -101,7 +101,7 @@ describe("Split", () => {
|
||||
// 150/3 = floor 50, last row: 150 - 2*50 = 50
|
||||
const bottomRight = entries.find((e) => e.entryName === "img_r3_c3.png");
|
||||
expect(bottomRight).toBeDefined();
|
||||
const meta = await sharp(bottomRight!.getData()).metadata();
|
||||
const meta = await sharp(bottomRight?.getData()).metadata();
|
||||
expect(meta.width).toBe(200 - 2 * 66); // 68
|
||||
expect(meta.height).toBe(150 - 2 * 50); // 50
|
||||
});
|
||||
@@ -494,14 +494,14 @@ describe("Split", () => {
|
||||
// Last column tile should have remainder width
|
||||
const lastColTile = entries.find((e) => e.entryName.includes("_r1_c3"));
|
||||
expect(lastColTile).toBeDefined();
|
||||
const lastColMeta = await sharp(lastColTile!.getData()).metadata();
|
||||
const lastColMeta = await sharp(lastColTile?.getData()).metadata();
|
||||
expect(lastColMeta.width).toBe(200 - 2 * 80); // 40
|
||||
expect(lastColMeta.height).toBe(80);
|
||||
|
||||
// Last row tile should have remainder height
|
||||
const lastRowTile = entries.find((e) => e.entryName.includes("_r2_c1"));
|
||||
expect(lastRowTile).toBeDefined();
|
||||
const lastRowMeta = await sharp(lastRowTile!.getData()).metadata();
|
||||
const lastRowMeta = await sharp(lastRowTile?.getData()).metadata();
|
||||
expect(lastRowMeta.width).toBe(80);
|
||||
expect(lastRowMeta.height).toBe(150 - 80); // 70
|
||||
});
|
||||
@@ -1485,4 +1485,108 @@ describe("Split", () => {
|
||||
expect(meta.height).toBeGreaterThan(0);
|
||||
}
|
||||
});
|
||||
|
||||
// ── JXL output format ─────────────────────────────────────────────
|
||||
|
||||
it("accepts jxl as a valid output format in settings schema", async () => {
|
||||
// JXL support depends on the Sharp build. The split route uses reply.hijack()
|
||||
// which means errors after headers are sent can hang the connection. Instead
|
||||
// of testing actual JXL output, we verify the settings schema accepts "jxl"
|
||||
// by confirming it doesn't get rejected with 400, using the batch endpoint
|
||||
// which returns JSON rather than streaming.
|
||||
const { body, contentType } = createMultipartPayload([
|
||||
{ name: "file", filename: "test.png", contentType: "image/png", content: PNG },
|
||||
{
|
||||
name: "settings",
|
||||
content: JSON.stringify({ columns: 2, rows: 1, outputFormat: "jxl", quality: 75 }),
|
||||
},
|
||||
]);
|
||||
|
||||
const res = await app.inject({
|
||||
method: "POST",
|
||||
url: "/api/v1/tools/split/batch",
|
||||
headers: {
|
||||
authorization: `Bearer ${adminToken}`,
|
||||
"content-type": contentType,
|
||||
},
|
||||
body,
|
||||
});
|
||||
|
||||
// Should not be 400 (invalid settings) -- jxl is a valid enum value
|
||||
expect(res.statusCode).not.toBe(400);
|
||||
});
|
||||
|
||||
// ── Corrupt image file ────────────────────────────────────────────
|
||||
|
||||
it("rejects a corrupt image file", async () => {
|
||||
const corruptBuffer = Buffer.from("this is not an image file at all");
|
||||
const { body, contentType } = createMultipartPayload([
|
||||
{ name: "file", filename: "corrupt.png", contentType: "image/png", content: corruptBuffer },
|
||||
{
|
||||
name: "settings",
|
||||
content: JSON.stringify({ columns: 2, rows: 2 }),
|
||||
},
|
||||
]);
|
||||
|
||||
const res = await app.inject({
|
||||
method: "POST",
|
||||
url: "/api/v1/tools/split",
|
||||
headers: {
|
||||
authorization: `Bearer ${adminToken}`,
|
||||
"content-type": contentType,
|
||||
},
|
||||
body,
|
||||
});
|
||||
|
||||
// Split hijacks the reply, so error may come as 422 or fail to stream
|
||||
expect([400, 422]).toContain(res.statusCode);
|
||||
});
|
||||
|
||||
// ── Quality at boundary values ────────────────────────────────────
|
||||
|
||||
it("rejects quality below minimum (1)", async () => {
|
||||
const { body, contentType } = createMultipartPayload([
|
||||
{ name: "file", filename: "test.png", contentType: "image/png", content: PNG },
|
||||
{
|
||||
name: "settings",
|
||||
content: JSON.stringify({ columns: 2, rows: 2, outputFormat: "jpg", quality: 0 }),
|
||||
},
|
||||
]);
|
||||
|
||||
const res = await app.inject({
|
||||
method: "POST",
|
||||
url: "/api/v1/tools/split",
|
||||
headers: {
|
||||
authorization: `Bearer ${adminToken}`,
|
||||
"content-type": contentType,
|
||||
},
|
||||
body,
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(400);
|
||||
});
|
||||
|
||||
// ── tileWidth below minimum ───────────────────────────────────────
|
||||
|
||||
it("rejects tileWidth below minimum (10)", async () => {
|
||||
const { body, contentType } = createMultipartPayload([
|
||||
{ name: "file", filename: "test.png", contentType: "image/png", content: PNG },
|
||||
{
|
||||
name: "settings",
|
||||
content: JSON.stringify({ tileWidth: 5, tileHeight: 50 }),
|
||||
},
|
||||
]);
|
||||
|
||||
const res = await app.inject({
|
||||
method: "POST",
|
||||
url: "/api/v1/tools/split",
|
||||
headers: {
|
||||
authorization: `Bearer ${adminToken}`,
|
||||
"content-type": contentType,
|
||||
},
|
||||
body,
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(400);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user