- 7 new enums: ReportType, ExportFormat, DestructionMethod, TransportStatus, ComplianceArea, ComplianceStatus, RetentionCategory - Extended: StaffPermission (+3), AuditEventType (+5), NotificationType (+2) - Flyway V23-V29: destruction_records, transport_records, propagation_sources, prevention_activities, generated_reports, compliance_deadlines, distribution THC/CBD - 6 new JPA entities extending AbstractTenantEntity - 6 new Spring Data repositories with tenant-scoped queries - ReportGenerator<T> interface + ReportGeneratorService (auto-discovery, format dispatch) - ComplianceRecordsController (CRUD for destruction/transport/propagation/prevention) - ComplianceDeadlineController (create, list, complete, overdue) - DateRangeReportParameters record for report generation
29 KiB
Sprint 9 Test Plan — Berichtszentrale (Report Center)
Date: 2026-06-15 Author: Patrick Plate / Lumen (Architect) Status: Draft v2 (updated with panel review advisory items) Basis: cannamanage-sprint9-plan.md v2 Total Test Cases: 80
Test Overview
| ID | Description | Type | Class/Location | Status |
|---|---|---|---|---|
| T-01 | Destruction record CRUD | Unit | DestructionRecordServiceTest |
⬜ |
| T-02 | Transport record CRUD | Unit | TransportRecordServiceTest |
⬜ |
| T-03 | Propagation source CRUD | Unit | PropagationSourceServiceTest |
⬜ |
| T-04 | Prevention activity CRUD | Unit | PreventionActivityServiceTest |
⬜ |
| T-05 | Generated report record keeping | Unit | GeneratedReportServiceTest |
⬜ |
| T-06 | Compliance deadline CRUD + status | Unit | ComplianceDeadlineServiceTest |
⬜ |
| T-07 | EÜR calculation correctness | Unit | EurReportGeneratorTest |
⬜ |
| T-08 | EÜR zero-income edge case | Unit | EurReportGeneratorTest |
⬜ |
| T-09 | EÜR multi-category expense grouping | Unit | EurReportGeneratorTest |
⬜ |
| T-10 | EÜR CSV encoding ISO-8859-1 | Unit | EurReportGeneratorTest |
⬜ |
| T-11 | EÜR decimal comma format | Unit | EurReportGeneratorTest |
⬜ |
| T-12 | Kassenbuch export with running balance | Unit | KassenbuchExportGeneratorTest |
⬜ |
| T-13 | Kassenbuch period filtering | Unit | KassenbuchExportGeneratorTest |
⬜ |
| T-14 | Fee confirmation per member | Unit | FeeConfirmationGeneratorTest |
⬜ |
| T-15 | Fee confirmation batch generation | Unit | FeeConfirmationGeneratorTest |
⬜ |
| T-16 | Annual authority report - all fields populated | Unit | AnnualAuthorityReportGeneratorTest |
⬜ |
| T-17 | Annual authority report - by-strain breakdown | Unit | AnnualAuthorityReportGeneratorTest |
⬜ |
| T-18 | Annual authority report - stock reconciliation check | Unit | AnnualAuthorityReportGeneratorTest |
⬜ |
| T-19 | Annual authority report - empty year (no activity) | Unit | AnnualAuthorityReportGeneratorTest |
⬜ |
| T-20 | Annual authority report - JSON schema validation | Unit | AnnualAuthorityReportGeneratorTest |
⬜ |
| T-21 | Distribution log - date range filter | Unit | DistributionLogGeneratorTest |
⬜ |
| T-22 | Distribution log - quota violation flagging | Unit | DistributionLogGeneratorTest |
⬜ |
| T-23 | Distribution log - Heranwachsende THC limit flag | Unit | DistributionLogGeneratorTest |
⬜ |
| T-24 | Distribution log - CSV semicolons + ISO-8859-1 | Unit | DistributionLogGeneratorTest |
⬜ |
| T-25 | Stock inventory - point-in-time snapshot | Unit | StockInventoryReportGeneratorTest |
⬜ |
| T-26 | Stock inventory - includes propagation material count | Unit | StockInventoryReportGeneratorTest |
⬜ |
| T-27 | Destruction protocol - sequential numbering | Unit | DestructionProtocolGeneratorTest |
⬜ |
| T-28 | Destruction protocol - witness fields | Unit | DestructionProtocolGeneratorTest |
⬜ |
| T-29 | Destruction protocol - annual totals | Unit | DestructionProtocolGeneratorTest |
⬜ |
| T-30 | Cultivation report - harvest aggregation by strain | Unit | CultivationReportGeneratorTest |
⬜ |
| T-31 | Transport certificate - all §22(4) fields present | Unit | TransportCertificateGeneratorTest |
⬜ |
| T-32 | Transport certificate - PDF single page | Unit | TransportCertificateGeneratorTest |
⬜ |
| T-33 | Full authority export - ZIP structure valid | Unit | FullAuthorityExportGeneratorTest |
⬜ |
| T-34 | Full authority export - JSON files parseable | Unit | FullAuthorityExportGeneratorTest |
⬜ |
| T-35 | Full authority export - DSGVO minimization (birth year only) | Unit | FullAuthorityExportGeneratorTest |
⬜ |
| T-36 | Full authority export - includes README.txt | Unit | FullAuthorityExportGeneratorTest |
⬜ |
| T-37 | Distribution info sheet - all §21(2) fields | Unit | DistributionInfoSheetGeneratorTest |
⬜ |
| T-38 | Distribution info sheet - health warnings present | Unit | DistributionInfoSheetGeneratorTest |
⬜ |
| T-39 | VVT generator - pre-filled template complete | Unit | VvtGeneratorTest |
⬜ |
| T-40 | VVT generator - club-specific data inserted | Unit | VvtGeneratorTest |
⬜ |
| T-41 | TOM generator - all 7 control areas present | Unit | TomGeneratorTest |
⬜ |
| T-42 | DSFA generator - structure correct | Unit | DsfaGeneratorTest |
⬜ |
| T-43 | Deletion concept - all retention categories listed | Unit | DeletionConceptGeneratorTest |
⬜ |
| T-44 | Member list registry - §67 BGB format | Unit | MemberListRegistryGeneratorTest |
⬜ |
| T-45 | Member list registry - minimal data only | Unit | MemberListRegistryGeneratorTest |
⬜ |
| T-46 | Board change notice - old vs new composition | Unit | BoardChangeNoticeGeneratorTest |
⬜ |
| T-47 | Annual board report - combines all data sources | Unit | AnnualBoardReportGeneratorTest |
⬜ |
| T-48 | Compliance status - GREEN when all obligations met | Unit | ComplianceDashboardServiceTest |
⬜ |
| T-49 | Compliance status - YELLOW when deadline within 30 days | Unit | ComplianceDashboardServiceTest |
⬜ |
| T-50 | Compliance status - RED when deadline passed | Unit | ComplianceDashboardServiceTest |
⬜ |
| T-51 | Compliance status - KCanG area calculation | Unit | ComplianceDashboardServiceTest |
⬜ |
| T-52 | Retention service - identifies expired records | Unit | RetentionServiceTest |
⬜ |
| T-53 | Retention service - respects different retention periods | Unit | RetentionServiceTest |
⬜ |
| T-54 | Retention service - never auto-deletes | Unit | RetentionServiceTest |
⬜ |
| T-55 | Deadline scheduler - rolls annual deadlines | Unit | ComplianceDeadlineSchedulerTest |
⬜ |
| T-56 | Deadline scheduler - creates notifications | Unit | ComplianceDeadlineSchedulerTest |
⬜ |
| T-57 | PDF generation - German umlauts render correctly | Integration | PdfRenderingTest |
⬜ |
| T-58 | PDF generation - legal reference in footer | Integration | PdfRenderingTest |
⬜ |
| T-59 | PDF generation - club letterhead | Integration | PdfRenderingTest |
⬜ |
| T-60 | Report controller - generate EÜR endpoint | Integration | ReportControllerTest |
⬜ |
| T-61 | Report controller - authority annual report endpoint | Integration | ReportControllerTest |
⬜ |
| T-62 | Report controller - full export ZIP endpoint | Integration | ReportControllerTest |
⬜ |
| T-63 | Report controller - permission check (ADMIN only) | Integration | ReportControllerTest |
⬜ |
| T-64 | Destruction controller - CRUD + PDF | Integration | DestructionRecordControllerTest |
⬜ |
| T-65 | Transport controller - CRUD + certificate | Integration | TransportRecordControllerTest |
⬜ |
| T-66 | Sidebar renders grouped navigation | E2E | navigation.spec.ts |
⬜ |
| T-67 | Berichtszentrale page loads with compliance cards | E2E | reports.spec.ts |
⬜ |
| T-68 | Report download flow (generate → download PDF) | E2E | reports.spec.ts |
⬜ |
| T-69 | Rate limiter - 6th report in 1 min returns 429 | Integration | ReportControllerTest |
⬜ |
| T-70 | Rate limiter - different tenant not affected | Integration | ReportControllerTest |
⬜ |
| T-71 | CSV injection prevention - dangerous cell prefixes escaped | Unit | CsvExportUtilTest |
⬜ |
| T-72 | CSV injection - formula in member name does not execute | Unit | CsvExportUtilTest |
⬜ |
| T-73 | Authority export re-authentication required | Integration | ReportControllerTest |
⬜ |
| T-74 | Authority export - expired reconfirm token rejected | Integration | ReportControllerTest |
⬜ |
| T-75 | Authority export - reason field minimum length enforced | Integration | ReportControllerTest |
⬜ |
| T-76 | Streaming ZIP - large export does not OOM | Integration | FullAuthorityExportGeneratorTest |
⬜ |
| T-77 | Breach notification - Art. 33 section present | Unit | BreachNotificationGeneratorTest |
⬜ |
| T-78 | Breach notification - Art. 34 section separate | Unit | BreachNotificationGeneratorTest |
⬜ |
| T-79 | Breach notification - 72h deadline reminder included | Unit | BreachNotificationGeneratorTest |
⬜ |
| T-80 | Empty-state Berichtszentrale shows onboarding for new club | E2E | reports.spec.ts |
⬜ |
Status: ⬜ Pending | ✅ Passed | ❌ Failed | ⏭️ Skipped
Test Cases — Detailed
T-01: Destruction Record CRUD
Type: Unit
Class: DestructionRecordServiceTest
Method: testCreateDestructionRecord(), testListByDateRange(), testDeleteNotAllowed()
Preconditions:
- Tenant with active batch in stock
Scenarios:
| # | Input | Expected Result |
|---|---|---|
| a | Valid destruction (batch, 100g, INCINERATION, reason, witness) | Record created, stock updated (decrease by 100g) |
| b | Destruction with 0 grams | Validation error: grams must be > 0 |
| c | Destruction exceeding batch remaining stock | Validation error: cannot destroy more than available |
| d | List destructions for date range | Only records within range returned |
| e | Delete destruction record | Rejected: destruction records are immutable (audit trail) |
T-07: EÜR Calculation Correctness
Type: Unit
Class: EurReportGeneratorTest
Method: testEurCalculation_normalYear()
Preconditions:
- 12 months of financial transactions (income + expenses)
- Multiple expense categories
Scenarios:
| # | Input | Expected Result |
|---|---|---|
| a | Year 2025 with €54000 income, €46000 expenses | EÜR shows Überschuss €8000 |
| b | Opening balance €5000, closing balance €13000 | Balance change matches Überschuss |
| c | 6 expense categories | All categories listed with correct subtotals |
| d | Amounts in cents (internal) | Display in Euro with comma decimals (1.234,56) |
T-10: EÜR CSV Encoding ISO-8859-1
Type: Unit
Class: EurReportGeneratorTest
Method: testCsvEncoding()
Scenarios:
| # | Input | Expected Result |
|---|---|---|
| a | Category "Büroausstattung" with umlaut | CSV file readable in ISO-8859-1, ü correctly encoded |
| b | Amount 1234.56 | CSV shows "1234,56" (decimal comma) |
| c | Field separator | Semicolons (;) not commas |
| d | Line ending | CRLF (Windows-compatible for DATEV) |
T-16: Annual Authority Report — All Fields Populated
Type: Unit
Class: AnnualAuthorityReportGeneratorTest
Method: testAnnualReport_complete()
Preconditions:
- Grow module has harvest records for 3 strains
- Distribution records for 2025
- 2 destruction records
- Stock snapshot available for Dec 31
Scenarios:
| # | Input | Expected Result |
|---|---|---|
| a | Year 2025 | Report contains all 4 sections: cultivated, distributed, destroyed, end-stock |
| b | 3 strains with different THC/CBD | Each strain listed separately with correct averages |
| c | Cross-check: cultivated - distributed - destroyed = stock change | Discrepancy < 1g (rounding) or flagged |
| d | JSON output | Valid JSON matching defined schema |
| e | PDF output | Contains legal reference "§26 Abs. 3 KCanG" in footer |
T-22: Distribution Log — Quota Violation Flagging
Type: Unit
Class: DistributionLogGeneratorTest
Method: testQuotaViolationFlags()
Preconditions:
- Member A (age 25): received 26g on one day (exceeds 25g/day limit)
- Member B (age 19): received cannabis with 12% THC (exceeds 10% for Heranwachsende)
- Member C (age 30): received 52g in one month (exceeds 50g/month)
Scenarios:
| # | Input | Expected Result |
|---|---|---|
| a | Member A daily violation | Report flags: "TAGESLIMIT ÜBERSCHRITTEN: 26g > 25g" |
| b | Member B THC violation | Report flags: "THC-LIMIT HERANWACHSENDE: 12% > 10%" |
| c | Member C monthly violation | Report flags: "MONATSLIMIT ÜBERSCHRITTEN: 52g > 50g" |
| d | Member D (age 25, 20g/day, 45g/month) | No flags — within all limits |
T-33: Full Authority Export — ZIP Structure Valid
Type: Unit
Class: FullAuthorityExportGeneratorTest
Method: testZipStructure()
Scenarios:
| # | Input | Expected Result |
|---|---|---|
| a | Generate full export | ZIP contains: README.txt, distributions.json, distributions.csv, stock.json, destructions.json, cultivation.json, transports.json, members.json, summary.pdf |
| b | Each JSON file | Valid JSON, parseable without errors |
| c | members.json | Contains ONLY: name, firstName, birthYear — NO address, NO full DOB, NO phone |
| d | README.txt | Contains: generation date, legal basis reference, file descriptions |
T-35: Full Authority Export — DSGVO Minimization
Type: Unit
Class: FullAuthorityExportGeneratorTest
Method: testDsgvoMinimization()
Critical test — ensures we don't leak unnecessary personal data to authorities.
Scenarios:
| # | Input | Expected Result |
|---|---|---|
| a | Member with full profile (name, address, phone, email, DOB 1990-05-15) | Export contains only: "Max", "Müller", 1990 |
| b | Distribution record | Contains member name + birth year, NOT member ID or address |
| c | No bank details in any export file | Grep for IBAN patterns returns zero matches |
| d | No email addresses in export | Grep for @ returns zero matches |
T-48: Compliance Status — GREEN When All Obligations Met
Type: Unit
Class: ComplianceDashboardServiceTest
Method: testGreenStatus()
Preconditions:
- Annual authority report generated for previous year
- EÜR generated for previous year
- VVT exists and updated within last 12 months
- Next MV scheduled
- All board terms valid
Scenarios:
| # | Input | Expected Result |
|---|---|---|
| a | All conditions met | Overall status: GREEN |
| b | KCanG area | GREEN: annual report submitted, records complete |
| c | Finance area | GREEN: EÜR generated, no overdue deadlines |
| d | DSGVO area | GREEN: VVT exists and recent |
| e | Verein area | GREEN: MV scheduled, board terms valid |
T-50: Compliance Status — RED When Deadline Passed
Type: Unit
Class: ComplianceDashboardServiceTest
Method: testRedStatus()
Preconditions:
- Current date: February 15
- Annual authority report for previous year NOT generated (deadline was Jan 31)
Scenarios:
| # | Input | Expected Result |
|---|---|---|
| a | Authority report overdue | KCanG status: RED |
| b | Overall status | RED (worst of all areas) |
| c | Deadline record | Status marked OVERDUE |
T-52: Retention Service — Identifies Expired Records
Type: Unit
Class: RetentionServiceTest
Method: testIdentifiesExpiredRecords()
Preconditions:
- Distribution record created 6 years ago (past 5-year KCanG retention)
- Financial transaction created 11 years ago (past 10-year AO retention)
- MV protocol from 20 years ago (indefinite retention — should NOT be flagged)
Scenarios:
| # | Input | Expected Result |
|---|---|---|
| a | 6-year-old distribution record | Flagged for deletion review |
| b | 11-year-old financial transaction | Flagged for deletion review |
| c | 20-year-old MV protocol | NOT flagged (indefinite retention) |
| d | 4-year-old distribution record | NOT flagged (within 5-year period) |
T-54: Retention Service — Never Auto-Deletes
Type: Unit
Class: RetentionServiceTest
Method: testNeverAutoDeletes()
Critical safety test.
Scenarios:
| # | Input | Expected Result |
|---|---|---|
| a | Run retention check with expired records | No records deleted from database |
| b | Expired record | Status changed to "RETENTION_EXPIRED", admin notification created |
| c | Admin confirms deletion | THEN record is soft-deleted (retention log entry created) |
| d | Without admin confirmation | Record persists indefinitely |
T-57: PDF Generation — German Umlauts Render Correctly
Type: Integration
Class: PdfRenderingTest
Method: testGermanCharacters()
Scenarios:
| # | Input | Expected Result |
|---|---|---|
| a | Text with äöüÄÖÜß | All characters render correctly in PDF |
| b | Club name "Grüner Daumen e.V." | Renders correctly in letterhead |
| c | Category "Büroausstattung" | Renders correctly in EÜR table |
| d | Legal text "§26 Abs. 3 KCanG" | § symbol renders correctly |
| e | Euro amounts "1.234,56 €" | Euro sign renders correctly |
T-63: Report Controller — Permission Check
Type: Integration
Class: ReportControllerTest
Method: testPermissions()
Scenarios:
| # | Input | Expected Result |
|---|---|---|
| a | ADMIN requests EÜR | 200 OK + PDF |
| b | STAFF (Kassenwart) requests EÜR | 200 OK + PDF (finance permission) |
| c | MEMBER requests EÜR | 403 Forbidden |
| d | ADMIN requests authority export | 200 OK + ZIP |
| e | STAFF without finance permission requests EÜR | 403 Forbidden |
| f | Unauthenticated request | 401 Unauthorized |
T-66: Sidebar Renders Grouped Navigation
Type: E2E (Playwright)
File: cannamanage-frontend/e2e/authenticated/navigation.spec.ts
Scenarios:
| # | Action | Expected Result |
|---|---|---|
| a | Load dashboard page | Sidebar shows 4 groups: Betrieb, Kommunikation, Verwaltung, Compliance |
| b | Click group header "Kommunikation" | Group collapses/expands |
| c | Navigate to /reports | "Berichtszentrale" item highlighted in Compliance group |
| d | All existing URLs still work | /members, /distributions, /stock, /grow, /finance, /assemblies all accessible |
T-67: Berichtszentrale Page Loads
Type: E2E (Playwright)
File: cannamanage-frontend/e2e/authenticated/reports.spec.ts
Scenarios:
| # | Action | Expected Result |
|---|---|---|
| a | Navigate to /reports | Page loads with compliance status cards |
| b | Compliance cards visible | 4 cards: KCanG, Finanzen, DSGVO, Verein — each with status color |
| c | Report categories visible | 4 category sections with report listings |
| d | Click "EÜR generieren" | Year picker appears, generate button enabled |
| e | Click "Behörden-Export" | Confirmation dialog shown (due to data sensitivity) |
T-68: Report Download Flow
Type: E2E (Playwright)
File: cannamanage-frontend/e2e/authenticated/reports.spec.ts
Scenarios:
| # | Action | Expected Result |
|---|---|---|
| a | Select year 2025, click generate EÜR PDF | Download triggered, file non-empty |
| b | Generate distribution log for last month | Download triggered, PDF contains data |
| c | Report appears in history table | History shows: type, date, generated by |
| d | Re-download from history | Same file downloadable again |
T-69: Rate Limiter - 6th Report in 1 Minute Returns 429
Type: Integration
Class: ReportControllerTest
Method: testRateLimit_sixthRequestReturns429()
Preconditions:
- Authenticated admin user
- Resilience4j rate limiter configured: 5 permits/minute/tenant
Scenarios:
| # | Action | Expected Result |
|---|---|---|
| a | Generate 5 reports in rapid succession | All return 200 OK |
| b | Generate 6th report within same minute | Returns 429 Too Many Requests |
| c | Response body contains "Rate limit exceeded" message | Helpful German error message |
| d | Response includes Retry-After header |
Header present with seconds value |
T-70: Rate Limiter - Different Tenant Not Affected
Type: Integration
Class: ReportControllerTest
Method: testRateLimit_differentTenantNotBlocked()
Preconditions:
- Two tenants configured in test
Scenarios:
| # | Action | Expected Result |
|---|---|---|
| a | Tenant A generates 5 reports (hitting limit) | All return 200 |
| b | Tenant B generates 1 report immediately after | Returns 200 OK (not affected by A's limit) |
T-71: CSV Injection Prevention - Dangerous Cell Prefixes Escaped
Type: Unit
Class: CsvExportUtilTest
Method: testCsvInjection_dangerousPrefixesEscaped()
Preconditions:
- CSV export utility available
Scenarios:
| # | Input Cell Value | Expected Output |
|---|---|---|
| a | =SUM(A1:A10) |
'=SUM(A1:A10) |
| b | +cmd|'/C calc'|''!A0 |
'+cmd|'/C calc'|''!A0 |
| c | -2+3+cmd|'/C calc'|'!A0 |
'-2+3+cmd|'/C calc'|'!A0 |
| d | @SUM(A1:A10) |
'@SUM(A1:A10) |
| e | Normal text value |
Normal text value (unchanged) |
| f | 123.45 |
123.45 (numbers unchanged) |
T-72: CSV Injection - Formula in Member Name Does Not Execute
Type: Unit
Class: CsvExportUtilTest
Method: testCsvInjection_memberNameWithFormula()
Preconditions:
- Member with name
=HYPERLINK("http://evil.com","Click")in database
Scenarios:
| # | Action | Expected Result |
|---|---|---|
| a | Export distribution log as CSV with this member | Cell contains '=HYPERLINK(...) with leading quote |
| b | Open CSV in LibreOffice Calc | No formula execution, displays as text |
T-73: Authority Export Re-Authentication Required
Type: Integration
Class: ReportControllerTest
Method: testAuthorityExport_requiresReauthentication()
Preconditions:
- Authenticated admin user with valid session
Scenarios:
| # | Action | Expected Result |
|---|---|---|
| a | Call GET /api/reports/authority-export/zip without reconfirm token |
Returns 403 Forbidden |
| b | Call POST /api/auth/reconfirm with correct password |
Returns fresh token (valid 30s) |
| c | Call authority export with valid reconfirm token within 30s | Returns 200 with ZIP stream |
| d | Audit log entry created | Records: who, when, reason, IP address |
T-74: Authority Export - Expired Reconfirm Token Rejected
Type: Integration
Class: ReportControllerTest
Method: testAuthorityExport_expiredTokenRejected()
Preconditions:
- Reconfirm token obtained but 31+ seconds elapsed
Scenarios:
| # | Action | Expected Result |
|---|---|---|
| a | Call authority export with 31-second-old reconfirm token | Returns 403 Forbidden |
| b | Error message indicates re-authentication required | "Bestätigung abgelaufen. Bitte erneut authentifizieren." |
T-75: Authority Export - Reason Field Minimum Length Enforced
Type: Integration
Class: ReportControllerTest
Method: testAuthorityExport_reasonFieldValidation()
Preconditions:
- Valid reconfirm token
Scenarios:
| # | Reason Value | Expected Result |
|---|---|---|
| a | "" (empty) |
400 Bad Request — reason required |
| b | "test" (4 chars) |
400 Bad Request — minimum 10 characters |
| c | "." (1 char) |
400 Bad Request — minimum 10 characters |
| d | "Behördenanfrage vom 15.06.2026" (30 chars) |
200 OK — accepted |
| e | "Jährliche Prüfung der Dokumentation" |
200 OK — accepted |
T-76: Streaming ZIP - Large Export Does Not OOM
Type: Integration
Class: FullAuthorityExportGeneratorTest
Method: testStreamingZip_largeDataDoesNotOOM()
Preconditions:
- Test data with 500 members, 5000 distributions, 200 destruction records
- JVM heap limited to 256MB in test configuration
Scenarios:
| # | Action | Expected Result |
|---|---|---|
| a | Generate full authority export with large dataset | Completes without OutOfMemoryError |
| b | Response is streamed (chunked transfer encoding) | Transfer-Encoding: chunked header present |
| c | ZIP file is valid and contains all expected entries | All 8 files present in ZIP |
| d | Peak memory usage stays below heap limit | No GC pressure spikes above 200MB |
T-77: Breach Notification - Art. 33 Section Present
Type: Unit
Class: BreachNotificationGeneratorTest
Method: testBreachNotification_article33SectionComplete()
Preconditions:
- Club with configured DPO and Landesdatenschutzbehörde contact
Scenarios:
| # | Check | Expected Content |
|---|---|---|
| a | Nature of breach field | Present and editable placeholder |
| b | Categories of data subjects | Pre-filled: "Mitglieder der Anbauvereinigung" |
| c | Approximate number affected | Editable field with club member count pre-filled |
| d | DPO contact details | Auto-filled from club settings |
| e | Likely consequences | Pre-filled with Art. 9 health data risk template |
| f | Measures taken/proposed | Editable placeholder |
| g | 72-hour deadline reminder | Bold text: "Meldung innerhalb 72 Stunden nach Kenntnisnahme" |
T-78: Breach Notification - Art. 34 Section Separate
Type: Unit
Class: BreachNotificationGeneratorTest
Method: testBreachNotification_article34SectionSeparate()
Preconditions:
- Same as T-77
Scenarios:
| # | Check | Expected Result |
|---|---|---|
| a | Art. 34 section has separate heading | "Benachrichtigung der Betroffenen (Art. 34 DSGVO)" |
| b | Plain language requirement noted | "In klarer und einfacher Sprache" instruction present |
| c | Template is distinct from Art. 33 section | Not merged — separate page/section in PDF |
| d | Includes contact details for data subjects | Phone, email, postal address fields |
T-79: Breach Notification - 72h Deadline Reminder Included
Type: Unit
Class: BreachNotificationGeneratorTest
Method: testBreachNotification_72hDeadlineReminder()
Preconditions:
- Generated breach notification PDF
Scenarios:
| # | Check | Expected Content |
|---|---|---|
| a | Deadline prominently displayed | "72 Stunden" appears in bold or highlighted |
| b | Authority contact for notification | Landesdatenschutzbehörde name + URL/email |
| c | "Zeitpunkt der Kenntnisnahme" field | Date/time field for when breach was discovered |
| d | Countdown note | "Frist beginnt ab Kenntnisnahme, nicht ab Entdeckung" |
T-80: Empty-State Berichtszentrale Shows Onboarding for New Club
Type: E2E (Playwright)
File: cannamanage-frontend/e2e/authenticated/reports.spec.ts
Preconditions:
- New tenant with zero generated reports
Scenarios:
| # | Action | Expected Result |
|---|---|---|
| a | Navigate to /reports as new club admin | "Erste Schritte" banner visible |
| b | Compliance cards show neutral state | Gray "Einrichtung erforderlich" — NOT red indicators |
| c | 4-step guide links present | VVT, Jahresbericht, Kassenbuch, Fristen links all working |
| d | Click "Verstanden, Dashboard anzeigen" | Banner dismissed, normal view shown |
| e | Generate one report in any category | After refresh, traffic-light indicators appear |
| f | Banner does not reappear after dismissal | LocalStorage persists dismissal |
Test Data Requirements
Seed Data Enhancements
For test scenarios to work, seed data must include:
| Data | Quantity | Details |
|---|---|---|
| Financial transactions | 50+ | Spanning 12 months, multiple categories |
| Distributions with THC% | 30+ | Various strains, some near-limit quantities |
| Destruction records | 3 | Different methods, dates, witnesses |
| Transport records | 2 | With authority notification timestamps |
| Propagation sources | 2 | One person, one Anbauvereinigung |
| Prevention activities | 5 | Different activity types |
| Grow harvests with THC/CBD | 5 | Different strains, quantities |
| Stock entries | 10+ | Current stock by strain |
| Compliance deadlines | 4 | One per area, various due dates |
| Members aged 18-21 | 2 | For Heranwachsende limit testing |
Test Coverage Summary
| Component | Unit Tests | Integration Tests | E2E Tests | Total |
|---|---|---|---|---|
| Phase 1 (CRUD) | 6 | 2 | 0 | 8 |
| Phase 2 (Financial) | 9 | 1 | 0 | 10 |
| Phase 3 (KCanG) | 23 | 3 | 0 | 26 |
| Phase 4 (DSGVO/Verein) | 12 | 0 | 0 | 12 |
| Phase 5 (Frontend) | 0 | 0 | 4 | 4 |
| Phase 6 (Compliance + Security) | 9 | 8 | 0 | 17 |
| Cross-cutting (CSV util) | 2 | 0 | 0 | 2 |
| Breach notification (v2) | 3 | 0 | 0 | 3 |
| Total | 64 | 14 | 4 | 80 (was 68) |
Critical Test Priorities
Tests marked as CRITICAL — must pass before sprint completion:
| ID | Test | Why Critical |
|---|---|---|
| T-16 | Annual authority report complete | Legal obligation — §26(3) KCanG |
| T-22 | Quota violation flagging | Safety — prevents illegal distribution |
| T-33 | Authority export ZIP structure | Authority inspection readiness |
| T-35 | DSGVO minimization in exports | Privacy violation risk |
| T-54 | Retention service never auto-deletes | Data loss prevention |
| T-57 | German umlauts in PDFs | Usability — broken characters = unprofessional |
| T-63 | Permission checks | Security — unauthorized report access |
| T-69 | Rate limiter enforced | Security — prevents DoS via report generation |
| T-71 | CSV injection prevention | Security — prevents formula injection in exports |
| T-73 | Authority export re-authentication | Security — protects Art. 9 DSGVO health data |
| T-76 | Streaming ZIP no OOM | Reliability — prevents server crash on large exports |
| T-77 | Breach notification Art. 33 complete | Compliance — 72h notification obligation |
Test Naming Convention
- Test class:
<OriginalClass>Test.java - Test method:
test<What>_<Scenario>()or descriptive name - Location: mirrors source structure under
src/test/java/ - Assertions: use AssertJ for fluent assertions
- Mocking: Mockito for service dependencies
- PDF verification: extract text from generated PDF bytes using Apache PDFBox in test scope