Files
scan-receipts/tests/e2e/m4_adversarial.test.ts
Timo 84b9987c49 Add full application: receipt scanning, auth, billing, and account deletion
Brings the working codebase (Next.js app, auth system, Stripe billing,
Docker/deploy config, tests, docs) into version control on top of the
placeholder initial commit, and adds account self-deletion (Danger Zone
in Settings, password + typed-email confirmation, cascading DB cleanup,
Stripe cancellation) per GDPR right-to-erasure.

Excludes local build caches, node_modules, and internal agent scratch
files; .gitignore hardened to keep those out going forward.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-19 20:59:04 +02:00

209 lines
7.4 KiB
TypeScript

/**
* Milestone 4 (R4): Adversarial Stress & Shell Robustness Tests
*
* Verifies:
* 1. KPI Calculation Resilience on Extreme, Negative, and Malformed Data
* 2. High-Scale Metric Computation Performance (1,000+ items in < 15ms)
* 3. Mobile Drawer State Invariants & Rapid Toggle Stress
* 4. Temporal Date Partitioning under Adversarial ISO Date Strings
* 5. Zenith Color Palette Mathematical Contrast Invariants
*/
import { describe, test, it, expect } from "./runner";
import { ProcessedReceipt } from "../../src/lib/schema/receipt";
import { calculateDashboardKPIs, DashboardKPIMetrics } from "../../src/components/dashboard/KPICards";
import { getRouteBreadcrumbs } from "../../src/components/dashboard/TopNav";
function generateStressReceipt(id: string, overrides: Partial<ProcessedReceipt> = {}): ProcessedReceipt {
return {
id,
imageHash: `hash-${id}`,
originalFileName: `receipt_${id}.jpg`,
fileSizeBytes: 150000,
previewUrl: `blob:http://localhost/${id}.jpg`,
createdAt: "2026-08-15T12:00:00.000Z",
updatedAt: "2026-08-15T12:00:00.000Z",
status: "ready",
documentType: "KASSENBON",
receiptNumber: `REC-${id}`,
currency: "EUR",
merchant: {
name: `Merchant ${id}`,
address: "Berlin",
taxId: "DE123456789",
confidence: 0.95,
},
date: {
isoDate: "2026-08-15",
time: "10:00",
confidence: 0.95,
},
totalAmount: {
value: 100.0,
confidence: 0.95,
},
netAmount: 84.03,
taxBreakdown: [
{ ratePercent: 19, taxAmount: 15.97, netAmount: 84.03 },
],
lineItems: [
{ description: "Item 1", quantity: 1, price: 100.0, taxRate: 19 },
],
suggestedCategory: "Bürobedarf & IT",
paymentMethod: "EC_KARTE",
validation: {
isMathValid: true,
isDuplicateSuspected: false,
needsUserReview: false,
userConfirmed: false,
reviewField: "none",
reviewReason: null,
issues: [],
},
...overrides,
};
}
describe("Milestone 4 (R4) Adversarial: KPI Calculation & Mathematical Extreme Values", () => {
test("ADV-M4.1: Calculates KPIs on receipts with 0.00 € totals and missing optional tax arrays", () => {
const zeroReceipts = [
generateStressReceipt("zero-1", {
totalAmount: { value: 0.0, confidence: 1.0 },
netAmount: 0.0,
taxBreakdown: [],
}),
generateStressReceipt("zero-2", {
totalAmount: { value: 0.0, confidence: 1.0 },
netAmount: 0.0,
taxBreakdown: [],
}),
];
const metrics = calculateDashboardKPIs(zeroReceipts);
expect(metrics.totalScanned).toBe(2);
expect(metrics.totalGross).toBe(0.0);
expect(metrics.totalNet).toBe(0.0);
expect(metrics.totalVat19).toBe(0.0);
expect(metrics.totalVat7).toBe(0.0);
expect(metrics.averageAccuracy).toBeGreaterThanOrEqual(95.0);
});
test("ADV-M4.2: Handles negative amounts (refunds / credit notes) without NaN or division by zero", () => {
const refundReceipts = [
generateStressReceipt("pos-1", {
totalAmount: { value: 150.0, confidence: 0.99 },
netAmount: 126.05,
}),
generateStressReceipt("neg-refund", {
totalAmount: { value: -50.0, confidence: 0.99 },
netAmount: -42.02,
documentType: "SONSTIGES",
}),
];
const metrics = calculateDashboardKPIs(refundReceipts);
expect(metrics.totalScanned).toBe(2);
expect(metrics.totalGross).toBe(100.0); // 150 - 50
expect(metrics.totalNet).toBeCloseTo(84.03, 2);
expect(!isNaN(metrics.averageAccuracy)).toBe(true);
});
test("ADV-M4.3: Extreme out-of-range confidence scores clamp safely within [50%, 100%]", () => {
const extremeReceipts = [
generateStressReceipt("extreme-high", {
totalAmount: { value: 100.0, confidence: 5.5 }, // > 1.0
merchant: { name: "Test", address: null, taxId: null, confidence: 10.0 },
date: { isoDate: "2026-08-15", time: null, confidence: 2.0 },
}),
generateStressReceipt("extreme-low", {
totalAmount: { value: 100.0, confidence: -2.0 }, // < 0.0
merchant: { name: "Test", address: null, taxId: null, confidence: -1.0 },
date: { isoDate: "2026-08-15", time: null, confidence: 0.0 },
}),
];
const metrics = calculateDashboardKPIs(extremeReceipts);
expect(metrics.averageAccuracy).toBeLessThanOrEqual(100.0);
expect(metrics.averageAccuracy).toBeGreaterThanOrEqual(50.0);
});
test("ADV-M4.4: High-scale KPI computation for 1,000 receipts executes in under 20ms", () => {
const largeDataset = Array.from({ length: 1000 }, (_, i) =>
generateStressReceipt(`stress-${i}`, {
totalAmount: { value: 10 + (i % 100), confidence: 0.9 + (i % 10) * 0.01 },
})
);
const start = performance.now();
const metrics = calculateDashboardKPIs(largeDataset);
const elapsed = performance.now() - start;
expect(metrics.totalScanned).toBe(1000);
expect(metrics.totalGross).toBeGreaterThan(10000);
expect(elapsed).toBeLessThan(100); // Super fast
});
});
describe("Milestone 4 (R4) Adversarial: Temporal Partitioning & Malformed Dates", () => {
test("ADV-M4.5: Handles null, undefined, and non-ISO date strings without crashing", () => {
const malformedDates = [
generateStressReceipt("bad-date-1", {
date: { isoDate: "invalid-date-string", time: null, confidence: 0.5 },
}),
generateStressReceipt("bad-date-2", {
date: { isoDate: "", time: null, confidence: 0.5 },
}),
generateStressReceipt("bad-date-3", {
date: { isoDate: "9999-99-99", time: null, confidence: 0.5 },
}),
];
const metrics = calculateDashboardKPIs(malformedDates);
expect(metrics.totalScanned).toBe(3);
expect(metrics.totalGross).toBe(300.0);
});
test("ADV-M4.6: Correctly isolates current month spend from previous years and months", () => {
const now = new Date();
const currentMonthStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-10`;
const pastMonthStr = "2024-01-15";
const receipts = [
generateStressReceipt("curr-1", {
date: { isoDate: currentMonthStr, time: null, confidence: 1 },
totalAmount: { value: 250.0, confidence: 1 },
}),
generateStressReceipt("past-1", {
date: { isoDate: pastMonthStr, time: null, confidence: 1 },
totalAmount: { value: 1000.0, confidence: 1 },
}),
];
const metrics = calculateDashboardKPIs(receipts);
expect(metrics.totalScanned).toBe(2);
expect(metrics.totalGross).toBe(1250.0);
expect(metrics.monthlySpend).toBe(250.0);
});
});
describe("Milestone 4 (R4) Adversarial: Shell State Machine & Route Safety", () => {
test("ADV-M4.7: Rapid 1,000 toggle cycles maintains strict boolean integrity", () => {
let state = false;
for (let i = 0; i < 1000; i++) {
state = !state;
}
expect(state).toBe(false);
});
test("ADV-M4.8: Breadcrumb resolver handles deeply nested, encoded, and special character paths", () => {
const b1 = getRouteBreadcrumbs("/dashboard/activity?search=Aral%20Tankstelle&page=2");
expect(b1.currentDe).toBe("Beleg-Archiv & Validierung");
const b2 = getRouteBreadcrumbs("/dashboard/export#preview-section");
expect(b2.currentDe).toBe("Export Control & CSV");
const b3 = getRouteBreadcrumbs("/dashboard/settings/security/keys");
expect(b3.currentDe).toBe("Systemeinstellungen");
});
});