test: expand coverage to 3,382 tests across all layers

- Unit: 1,353 tests (42 files) — +256 new tests covering AI bridge
  modules, image-engine sharpen/optimize-for-web, Zustand stores, and
  icon-map validation
- Integration: 1,640 tests (57 files) — +826 new tests across all
  tool routes, pipeline/progress/batch infrastructure, user-files,
  edit-metadata, and a 321-test cross-format matrix
- E2E-Docker: 389 passing (20 spec files) — 6 new spec files for
  batch processing, format conversion, layout, optimization,
  watermark/overlay, and pipeline chains. Tests verified against fresh
  Docker container with all 6 AI bundles installed.

Bug fixes discovered during testing:
- fix(compress): SVG/BMP/exotic formats crashed Sharp encoder — added
  format-safety fallback to PNG
- fix(rate-limit): increase default login attempt limit from 10 to 500
  per minute — previous value caused false test failures and is too
  restrictive for a self-hosted app
- fix(auth.setup): wait for consent button visibility before clicking
  to prevent flaky E2E-Docker auth setup
This commit is contained in:
SnapOtter
2026-04-24 22:43:14 +08:00
parent bc82781282
commit 7f62bc32db
48 changed files with 13121 additions and 154 deletions
+263
View File
@@ -235,4 +235,267 @@ describe("Find Duplicates", () => {
expect(res.statusCode).toBe(401);
});
// ── Extended coverage: thresholds, batch sizes, settings ───────────
it("detects duplicates across different formats (PNG vs WebP of same content)", async () => {
// Same image in PNG and WebP should be perceptual duplicates
const { body, contentType } = createMultipartPayload([
{ name: "file", filename: "a.png", contentType: "image/png", content: PNG },
{ name: "file", filename: "b.webp", contentType: "image/webp", content: WEBP },
{ name: "file", filename: "c.jpg", contentType: "image/jpeg", content: JPG },
]);
const res = await app.inject({
method: "POST",
url: "/api/v1/tools/find-duplicates",
headers: {
authorization: `Bearer ${adminToken}`,
"content-type": contentType,
},
body,
});
expect(res.statusCode).toBe(200);
const result = JSON.parse(res.body);
expect(result.totalImages).toBe(3);
// All three are different images, so no duplicates expected
// (they're different content: 200x150 vs 50x50 vs 100x100)
});
it("uses high threshold to group even dissimilar images", async () => {
const { body, contentType } = createMultipartPayload([
{ name: "file", filename: "a.png", contentType: "image/png", content: PNG },
{ name: "file", filename: "b.jpg", contentType: "image/jpeg", content: JPG },
{
name: "settings",
content: JSON.stringify({ threshold: 20 }),
},
]);
const res = await app.inject({
method: "POST",
url: "/api/v1/tools/find-duplicates",
headers: {
authorization: `Bearer ${adminToken}`,
"content-type": contentType,
},
body,
});
expect(res.statusCode).toBe(200);
const result = JSON.parse(res.body);
expect(result.totalImages).toBe(2);
// With threshold 20, more images may be grouped as duplicates
});
it("uses threshold via settings JSON field", async () => {
const { body, contentType } = createMultipartPayload([
{ name: "file", filename: "a.png", contentType: "image/png", content: PNG },
{ name: "file", filename: "b.png", contentType: "image/png", content: PNG },
{
name: "settings",
content: JSON.stringify({ threshold: 5 }),
},
]);
const res = await app.inject({
method: "POST",
url: "/api/v1/tools/find-duplicates",
headers: {
authorization: `Bearer ${adminToken}`,
"content-type": contentType,
},
body,
});
expect(res.statusCode).toBe(200);
const result = JSON.parse(res.body);
// Identical images should still be grouped with threshold 5
expect(result.duplicateGroups).toHaveLength(1);
});
it("handles 5+ images in a single batch", async () => {
const { body, contentType } = createMultipartPayload([
{ name: "file", filename: "a.png", contentType: "image/png", content: PNG },
{ name: "file", filename: "b.png", contentType: "image/png", content: PNG },
{ name: "file", filename: "c.jpg", contentType: "image/jpeg", content: JPG },
{ name: "file", filename: "d.jpg", contentType: "image/jpeg", content: JPG },
{ name: "file", filename: "e.jpg", contentType: "image/jpeg", content: PORTRAIT },
]);
const res = await app.inject({
method: "POST",
url: "/api/v1/tools/find-duplicates",
headers: {
authorization: `Bearer ${adminToken}`,
"content-type": contentType,
},
body,
});
expect(res.statusCode).toBe(200);
const result = JSON.parse(res.body);
expect(result.totalImages).toBe(5);
// At least one duplicate group should be found (PNG pair or JPG pair)
expect(result.duplicateGroups.length).toBeGreaterThanOrEqual(1);
// Total duplicated files across all groups should be at least 4 (2 PNG + 2 JPG pairs)
const totalGroupedFiles = result.duplicateGroups.reduce(
(sum: number, g: { files: unknown[] }) => sum + g.files.length,
0,
);
expect(totalGroupedFiles).toBeGreaterThanOrEqual(2);
});
it("detects 3 identical images in one group", async () => {
const { body, contentType } = createMultipartPayload([
{ name: "file", filename: "a.png", contentType: "image/png", content: PNG },
{ name: "file", filename: "b.png", contentType: "image/png", content: PNG },
{ name: "file", filename: "c.png", contentType: "image/png", content: PNG },
]);
const res = await app.inject({
method: "POST",
url: "/api/v1/tools/find-duplicates",
headers: {
authorization: `Bearer ${adminToken}`,
"content-type": contentType,
},
body,
});
expect(res.statusCode).toBe(200);
const result = JSON.parse(res.body);
expect(result.duplicateGroups).toHaveLength(1);
expect(result.duplicateGroups[0].files).toHaveLength(3);
expect(result.uniqueImages).toBe(0);
// Only one should be marked as best
const bestFiles = result.duplicateGroups[0].files.filter((f: { isBest: boolean }) => f.isBest);
expect(bestFiles).toHaveLength(1);
});
it("sorts duplicate groups by highest similarity descending", async () => {
const { body, contentType } = createMultipartPayload([
{ name: "file", filename: "a.png", contentType: "image/png", content: PNG },
{ name: "file", filename: "b.png", contentType: "image/png", content: PNG },
{ name: "file", filename: "c.jpg", contentType: "image/jpeg", content: JPG },
{ name: "file", filename: "d.jpg", contentType: "image/jpeg", content: JPG },
{
name: "settings",
content: JSON.stringify({ threshold: 20 }),
},
]);
const res = await app.inject({
method: "POST",
url: "/api/v1/tools/find-duplicates",
headers: {
authorization: `Bearer ${adminToken}`,
"content-type": contentType,
},
body,
});
expect(res.statusCode).toBe(200);
const result = JSON.parse(res.body);
// If there are multiple groups, they should be sorted by max similarity desc
if (result.duplicateGroups.length >= 2) {
const maxSim0 = Math.max(
...result.duplicateGroups[0].files.map((f: { similarity: number }) => f.similarity),
);
const maxSim1 = Math.max(
...result.duplicateGroups[1].files.map((f: { similarity: number }) => f.similarity),
);
expect(maxSim0).toBeGreaterThanOrEqual(maxSim1);
}
});
it("handles HEIC input images", async () => {
const HEIC = readFileSync(join(FIXTURES, "test-200x150.heic"));
const { body, contentType } = createMultipartPayload([
{ name: "file", filename: "a.heic", contentType: "image/heic", content: HEIC },
{ name: "file", filename: "b.heic", contentType: "image/heic", content: HEIC },
]);
const res = await app.inject({
method: "POST",
url: "/api/v1/tools/find-duplicates",
headers: {
authorization: `Bearer ${adminToken}`,
"content-type": contentType,
},
body,
});
expect(res.statusCode).toBe(200);
const result = JSON.parse(res.body);
expect(result.totalImages).toBe(2);
expect(result.duplicateGroups).toHaveLength(1);
});
it("includes groupId in duplicate groups", async () => {
const { body, contentType } = createMultipartPayload([
{ name: "file", filename: "a.png", contentType: "image/png", content: PNG },
{ name: "file", filename: "b.png", contentType: "image/png", content: PNG },
]);
const res = await app.inject({
method: "POST",
url: "/api/v1/tools/find-duplicates",
headers: {
authorization: `Bearer ${adminToken}`,
"content-type": contentType,
},
body,
});
expect(res.statusCode).toBe(200);
const result = JSON.parse(res.body);
expect(result.duplicateGroups[0].groupId).toBe(1);
});
it("rejects threshold exceeding max (20)", async () => {
const { body, contentType } = createMultipartPayload([
{ name: "file", filename: "a.png", contentType: "image/png", content: PNG },
{ name: "file", filename: "b.png", contentType: "image/png", content: PNG },
{
name: "settings",
content: JSON.stringify({ threshold: 25 }),
},
]);
const res = await app.inject({
method: "POST",
url: "/api/v1/tools/find-duplicates",
headers: {
authorization: `Bearer ${adminToken}`,
"content-type": contentType,
},
body,
});
expect(res.statusCode).toBe(400);
});
it("rejects requests with no files at all", async () => {
const { body, contentType } = createMultipartPayload([
{ name: "settings", content: JSON.stringify({}) },
]);
const res = await app.inject({
method: "POST",
url: "/api/v1/tools/find-duplicates",
headers: {
authorization: `Bearer ${adminToken}`,
"content-type": contentType,
},
body,
});
expect(res.statusCode).toBe(400);
const result = JSON.parse(res.body);
expect(result.error).toMatch(/at least 2/i);
});
});