mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
test: expand test coverage across unit, integration, e2e, and e2e-docker suites
Add ~210 new tests filling gaps identified by a comprehensive 14-agent coverage audit. Unit+integration tests go from 9,388 to 9,484 (all passing). Unit tests (+36): - AI bridge: OOM fallback path, custom tier option - Web lib: api-errors, format date/datetime, tool-i18n coverage Integration tests (+19): - Format matrix: ai-canvas-expand and find-duplicates added to cross-format matrix - Adversarial: SVG XXE attacks, SQL injection in settings, request body size limits, race conditions with identical filenames E2E Docker (+3): - ai-canvas-expand tool coverage with HEIC input and edge cases E2E GUI (~150+): - Navigation: login rate limiting, ai-canvas-expand in parameterized list - Responsive: dropzone visibility, text readability, dialog bounds at all viewports - Keyboard: shortcuts verified from automate, files, tool, and fullscreen pages - Tool UI: undo/state-reset for 16 tools, crop canvas drag handles, rotate/border live preview, linked aspect-ratio inputs for resize - Batch: per-image undo isolation, batch compress/convert/rotate (not just resize) - Pipeline: tool palette search, step collapse/expand visibility - Settings: audit log entry verification, system settings persistence, teams CRUD, role permission toggling - RBAC: user/editor 403 on roles/teams endpoints, privilege escalation prevention, cross-role tab parity documented as intentional - Accessibility: skip-to-content link (WCAG 2.4.1), comprehensive color contrast for all headings/body/buttons in both themes with DOM-walking background detection - Resilience: auth expiry 401 redirect, rate limit 429 handling - Performance: JS heap memory stability for tool navigation, dialog cycling, upload/clear cycles, rapid page navigation
This commit is contained in:
@@ -472,6 +472,38 @@ describe("removeBackground", () => {
|
||||
expect(runPythonWithProgress).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("throws when OOM fallback succeeds at bridge level but Python returns success: false", async () => {
|
||||
vi.mocked(runPythonWithProgress)
|
||||
.mockRejectedValueOnce(new Error("Process killed (out of memory)"))
|
||||
.mockResolvedValueOnce({
|
||||
stdout: '{"success": false, "error": "u2net model corrupt"}',
|
||||
stderr: "",
|
||||
});
|
||||
// Only one parseStdoutJson call happens: the first attempt rejects before parsing
|
||||
vi.mocked(parseStdoutJson).mockReturnValueOnce({
|
||||
success: false,
|
||||
error: "u2net model corrupt",
|
||||
});
|
||||
|
||||
await expect(
|
||||
removeBackground(FAKE_INPUT, FAKE_OUTPUT_DIR, { model: "birefnet-general" }),
|
||||
).rejects.toThrow("u2net model corrupt");
|
||||
|
||||
expect(runPythonWithProgress).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("uses fallback error message when OOM fallback returns success: false without error", async () => {
|
||||
vi.mocked(runPythonWithProgress)
|
||||
.mockRejectedValueOnce(new Error("Process killed (out of memory)"))
|
||||
.mockResolvedValueOnce({ stdout: '{"success": false}', stderr: "" });
|
||||
// Only one parseStdoutJson call happens: the first attempt rejects before parsing
|
||||
vi.mocked(parseStdoutJson).mockReturnValueOnce({ success: false });
|
||||
|
||||
await expect(
|
||||
removeBackground(FAKE_INPUT, FAKE_OUTPUT_DIR, { model: "birefnet-general" }),
|
||||
).rejects.toThrow("Background removal failed");
|
||||
});
|
||||
|
||||
it("retries with fallback when no model is specified (default)", async () => {
|
||||
vi.mocked(runPythonWithProgress)
|
||||
.mockRejectedValueOnce(new Error("Process killed (out of memory)"))
|
||||
|
||||
@@ -93,6 +93,45 @@ describe("outpaint", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("passes custom tier value instead of default 'balanced'", async () => {
|
||||
const options: OutpaintOptions = {
|
||||
extendTop: 10,
|
||||
extendRight: 20,
|
||||
extendBottom: 30,
|
||||
extendLeft: 40,
|
||||
tier: "high",
|
||||
};
|
||||
await outpaint(FAKE_INPUT, options, FAKE_OUTPUT_DIR);
|
||||
|
||||
expect(runPythonWithProgress).toHaveBeenCalledWith(
|
||||
"outpaint.py",
|
||||
[
|
||||
`${FAKE_OUTPUT_DIR}/input_outpaint.png`,
|
||||
`${FAKE_OUTPUT_DIR}/output_outpaint.png`,
|
||||
"10",
|
||||
"20",
|
||||
"30",
|
||||
"40",
|
||||
"high",
|
||||
],
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
|
||||
it("passes 'fast' tier value", async () => {
|
||||
const options: OutpaintOptions = {
|
||||
extendTop: 5,
|
||||
extendRight: 5,
|
||||
extendBottom: 5,
|
||||
extendLeft: 5,
|
||||
tier: "fast",
|
||||
};
|
||||
await outpaint(FAKE_INPUT, options, FAKE_OUTPUT_DIR);
|
||||
|
||||
const args = vi.mocked(runPythonWithProgress).mock.calls[0][1];
|
||||
expect(args[6]).toBe("fast");
|
||||
});
|
||||
|
||||
it("converts input to PNG via sharp", async () => {
|
||||
const options: OutpaintOptions = {
|
||||
extendTop: 0,
|
||||
|
||||
Reference in New Issue
Block a user