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:
SnapOtter
2026-05-09 09:02:29 +08:00
parent 4f0fbade6d
commit 649ad5db9e
138 changed files with 22105 additions and 327 deletions
+107 -3
View File
@@ -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);
});
});