55110c95af
Implements the Sprint 10 Phase 1 foundation for the Smart Payment Import feature:
Domain layer:
- 3 new enums: BankFormat (MT940, CAMT053, CSV), ImportSessionStatus, MatchStatus
- StaffPermission.FINANCE_IMPORT
- AuditEventType: BANK_IMPORT_STARTED/COMPLETED/FAILED + BANK_PAYMENT_CONFIRMED
- NotificationType.BANK_IMPORT_COMPLETED
- ConsentType.BANK_DATA (DSGVO consent for IBAN storage)
- 3 new entities: BankImportSession, BankTransaction, CsvColumnMapping
- Member: + iban (VARCHAR 34) + ibanConsentDate
- MemberStatus.LEFT (semantic alias for RESIGNED, referenced by Sprint 9 RetentionService)
Persistence:
- V30__bank_import_sessions.sql
- V31__bank_transactions.sql
- V32__csv_column_mappings.sql (also adds iban + iban_consent_date to members)
- 3 Spring Data repositories
Parser infrastructure (cannamanage-service/src/main/java/de/cannamanage/service/bankimport):
- BankStatementParser interface (Strategy pattern, Spring-injected list)
- ParsedTransaction + ParseResult records
- BankStatementParseException (parse errors)
- Mt940Parser: custom state machine, CENTURY_BOUNDARY=70 for YY→YYYY, proprietary
header tolerance (skips lines before first :20: for StarMoney/WISO/Hibiscus wrappers)
- Camt053Parser: StAX streaming with XXE hardening (IS_SUPPORTING_EXTERNAL_ENTITIES,
SUPPORT_DTD, IS_REPLACING_ENTITY_REFERENCES all false); supports camt.053.001.02
and camt.053.001.08 namespaces
- CsvBankParser: Apache Commons CSV with configurable columns per club; German number
format ("1.234,56"); ISO-8859-1 default encoding
- BankStatementParserService: filename-extension hint + content probe; throws
UnrecognizedFormatException when no parser claims the file
Build verified via Docker (cannamanage-api:sprint10-phase1).
Sprint 9 fix (incidental, required to compile):
- Added MemberStatus.LEFT (Sprint 9 RetentionService referenced it but the enum
value was missing)
- MemberListRegistryGenerator: added LEFT to formatStatus() switch (mapped to
"Ausgetreten", same as RESIGNED)
Sprint 10 docs: analysis, plan, plan-review, testplan.
Co-Authored-By: Lumen <lumen@cannamanage.de>
560 lines
24 KiB
Markdown
560 lines
24 KiB
Markdown
# Sprint 10 Test Plan — Smart Payment Import
|
|
|
|
**Date:** 2026-06-15
|
|
**Author:** Patrick Plate / Lumen (Architect)
|
|
**Status:** Draft v1
|
|
**Basis:** cannamanage-sprint10-plan.md
|
|
**Sprint Goal:** Bank statement import with auto-matching for member payment reconciliation
|
|
|
|
---
|
|
|
|
## Test Overview
|
|
|
|
| ID | Description | Type | Class/Component | Status |
|
|
|----|------------|------|-----------------|--------|
|
|
| T-01 | MT940 parsing — standard Sparkasse file | Unit | Mt940ParserTest | ⬜ |
|
|
| T-02 | MT940 parsing — multi-line Verwendungszweck | Unit | Mt940ParserTest | ⬜ |
|
|
| T-03 | MT940 parsing — debit/credit identification | Unit | Mt940ParserTest | ⬜ |
|
|
| T-04 | MT940 parsing — German amount format (comma decimal) | Unit | Mt940ParserTest | ⬜ |
|
|
| T-05 | MT940 parsing — multi-statement file (multiple accounts) | Unit | Mt940ParserTest | ⬜ |
|
|
| T-06 | MT940 parsing — malformed input (graceful error) | Unit | Mt940ParserTest | ⬜ |
|
|
| T-07 | CAMT.053 parsing — standard XML file | Unit | Camt053ParserTest | ⬜ |
|
|
| T-08 | CAMT.053 parsing — multiple entries extraction | Unit | Camt053ParserTest | ⬜ |
|
|
| T-09 | CAMT.053 parsing — namespace v2 and v8 support | Unit | Camt053ParserTest | ⬜ |
|
|
| T-10 | CAMT.053 parsing — counterparty IBAN + name extraction | Unit | Camt053ParserTest | ⬜ |
|
|
| T-11 | CAMT.053 parsing — invalid XML (error handling) | Unit | Camt053ParserTest | ⬜ |
|
|
| T-12 | CSV parsing — semicolon delimiter, ISO-8859-1 | Unit | CsvBankParserTest | ⬜ |
|
|
| T-13 | CSV parsing — German number format (1.234,56) | Unit | CsvBankParserTest | ⬜ |
|
|
| T-14 | CSV parsing — configurable column mapping | Unit | CsvBankParserTest | ⬜ |
|
|
| T-15 | CSV parsing — skip header rows | Unit | CsvBankParserTest | ⬜ |
|
|
| T-16 | CSV parsing — UTF-8 encoding variant | Unit | CsvBankParserTest | ⬜ |
|
|
| T-17 | CSV parsing — empty/malformed rows skipped | Unit | CsvBankParserTest | ⬜ |
|
|
| T-18 | Format detection — MT940 by content signature | Unit | BankStatementParserServiceTest | ⬜ |
|
|
| T-19 | Format detection — CAMT.053 by XML namespace | Unit | BankStatementParserServiceTest | ⬜ |
|
|
| T-20 | Format detection — CSV by file extension | Unit | BankStatementParserServiceTest | ⬜ |
|
|
| T-21 | Format detection — unrecognized format throws exception | Unit | BankStatementParserServiceTest | ⬜ |
|
|
| T-22 | Matching — exact amount match scores 100 | Unit | PaymentMatchingServiceTest | ⬜ |
|
|
| T-23 | Matching — amount within 5% scores 80 | Unit | PaymentMatchingServiceTest | ⬜ |
|
|
| T-24 | Matching — member number in Verwendungszweck (M-0042) | Unit | PaymentMatchingServiceTest | ⬜ |
|
|
| T-25 | Matching — full member name in reference text | Unit | PaymentMatchingServiceTest | ⬜ |
|
|
| T-26 | Matching — last name only (partial name match) | Unit | PaymentMatchingServiceTest | ⬜ |
|
|
| T-27 | Matching — IBAN exact match | Unit | PaymentMatchingServiceTest | ⬜ |
|
|
| T-28 | Matching — confidence ≥90% → MATCHED status | Unit | PaymentMatchingServiceTest | ⬜ |
|
|
| T-29 | Matching — confidence 60-89% → SUGGESTED status | Unit | PaymentMatchingServiceTest | ⬜ |
|
|
| T-30 | Matching — confidence <60% → UNMATCHED status | Unit | PaymentMatchingServiceTest | ⬜ |
|
|
| T-31 | Matching — conflict: multiple members match → SUGGESTED | Unit | PaymentMatchingServiceTest | ⬜ |
|
|
| T-32 | Matching — negative amount (debit) skipped from matching | Unit | PaymentMatchingServiceTest | ⬜ |
|
|
| T-33 | Matching — no active fee assignment → no match | Unit | PaymentMatchingServiceTest | ⬜ |
|
|
| T-34 | Import service — upload and parse creates session | Integration | BankImportServiceTest | ⬜ |
|
|
| T-35 | Import service — run matching updates transactions | Integration | BankImportServiceTest | ⬜ |
|
|
| T-36 | Import service — confirm match creates Payment + LedgerEntry | Integration | BankImportServiceTest | ⬜ |
|
|
| T-37 | Import service — bulk confirm all matched | Integration | BankImportServiceTest | ⬜ |
|
|
| T-38 | Import service — skip transaction sets status | Integration | BankImportServiceTest | ⬜ |
|
|
| T-39 | Import service — manual assign with explicit member | Integration | BankImportServiceTest | ⬜ |
|
|
| T-40 | Import service — categorize as expense creates LedgerEntry | Integration | BankImportServiceTest | ⬜ |
|
|
| T-41 | Import service — complete session updates status | Integration | BankImportServiceTest | ⬜ |
|
|
| T-42 | Tier enforcement — Starter: CSV only | Unit | TierLimitServiceTest | ⬜ |
|
|
| T-43 | Tier enforcement — Starter: max 1 import/month | Unit | TierLimitServiceTest | ⬜ |
|
|
| T-44 | Tier enforcement — Pro: all formats allowed | Unit | TierLimitServiceTest | ⬜ |
|
|
| T-45 | Tier enforcement — Pro: max 3 CSV templates | Unit | TierLimitServiceTest | ⬜ |
|
|
| T-46 | Tier enforcement — Enterprise: auto-confirm enabled | Unit | TierLimitServiceTest | ⬜ |
|
|
| T-47 | IBAN validation — valid German IBAN (DE) | Unit | IbanValidatorTest | ⬜ |
|
|
| T-48 | IBAN validation — invalid checksum rejected | Unit | IbanValidatorTest | ⬜ |
|
|
| T-49 | IBAN validation — wrong length rejected | Unit | IbanValidatorTest | ⬜ |
|
|
| T-50 | IBAN validation — international IBAN formats | Unit | IbanValidatorTest | ⬜ |
|
|
| T-51 | REST API — upload file returns session | Integration | BankImportControllerTest | ⬜ |
|
|
| T-52 | REST API — upload exceeds 10MB → 413 error | Integration | BankImportControllerTest | ⬜ |
|
|
| T-53 | REST API — unauthorized user → 403 | Integration | BankImportControllerTest | ⬜ |
|
|
| T-54 | REST API — confirm match endpoint | Integration | BankImportControllerTest | ⬜ |
|
|
| T-55 | REST API — list sessions paginated | Integration | BankImportControllerTest | ⬜ |
|
|
| T-56 | REST API — CSV mapping CRUD | Integration | BankImportControllerTest | ⬜ |
|
|
| T-57 | REST API — tenant isolation (club A cannot see club B sessions) | Integration | BankImportControllerTest | ⬜ |
|
|
| T-58 | Member IBAN — store with consent verification | Integration | MemberControllerTest | ⬜ |
|
|
| T-59 | Member IBAN — reject without consent | Integration | MemberControllerTest | ⬜ |
|
|
| T-60 | Flyway migration — V30 applies cleanly | Integration | FlywayMigrationTest | ⬜ |
|
|
| T-61 | Flyway migration — V31 applies cleanly | Integration | FlywayMigrationTest | ⬜ |
|
|
| T-62 | Flyway migration — V32 applies cleanly | Integration | FlywayMigrationTest | ⬜ |
|
|
| T-63 | End-to-end — MT940 upload → match → confirm → payment created | E2E | BankImportE2ETest | ⬜ |
|
|
| T-64 | End-to-end — CSV with custom mapping → successful import | E2E | BankImportE2ETest | ⬜ |
|
|
| T-65 | End-to-end — mixed matches (auto + manual + skip) | E2E | BankImportE2ETest | ⬜ |
|
|
| T-66 | Frontend — upload wizard renders all 4 steps | E2E | Playwright | ⬜ |
|
|
| T-67 | Frontend — CSV column mapping interaction | E2E | Playwright | ⬜ |
|
|
| T-68 | Frontend — match review table filtering | E2E | Playwright | ⬜ |
|
|
| T-69 | Frontend — bulk confirm action | E2E | Playwright | ⬜ |
|
|
| T-70 | Frontend — import history table | E2E | Playwright | ⬜ |
|
|
|
|
Status: ⬜ Pending | ✅ Passed | ❌ Failed | ⏭️ Skipped
|
|
|
|
---
|
|
|
|
## Test Cases — Detailed
|
|
|
|
### T-01: MT940 parsing — standard Sparkasse file
|
|
|
|
**Type:** Unit
|
|
**Class:** `cannamanage-service/src/test/java/de/cannamanage/service/bankimport/Mt940ParserTest.java`
|
|
**Method:** `testParseSparkasseFile()`
|
|
|
|
**Preconditions:**
|
|
- MT940 test fixture file in `src/test/resources/fixtures/mt940/sparkasse-standard.sta`
|
|
|
|
**Scenarios:**
|
|
|
|
| # | Input | Expected Result |
|
|
|---|-------|----------------|
|
|
| a | Standard Sparkasse MT940 with 5 transactions | ParseResult with 5 transactions, correct amounts/dates |
|
|
| b | Opening balance `:60F:C260601EUR1234,56` | openingBalanceCents = 123456 |
|
|
| c | Credit transaction `:61:2506150615CR50,00` | amountCents = 5000, bookingDate = 2025-06-15 |
|
|
| d | Account IBAN from `:25:` field | accountIban extracted correctly |
|
|
|
|
**Postconditions:**
|
|
- All transactions have non-null bookingDate and amountCents
|
|
|
|
---
|
|
|
|
### T-02: MT940 parsing — multi-line Verwendungszweck
|
|
|
|
**Type:** Unit
|
|
**Class:** `Mt940ParserTest.java`
|
|
**Method:** `testMultiLineReference()`
|
|
|
|
**Scenarios:**
|
|
|
|
| # | Input | Expected Result |
|
|
|---|-------|----------------|
|
|
| a | `:86:` spanning 3 lines with `SVWZ+` | referenceText contains full concatenated text |
|
|
| b | `:86:` with `EREF+`, `KREF+`, `MREF+` sub-fields | Sub-fields parsed, SVWZ extracted as reference |
|
|
| c | `:86:` with `DEBT+DE89370400440532013000` | counterpartyIban = "DE89370400440532013000" |
|
|
|
|
---
|
|
|
|
### T-03: MT940 parsing — debit/credit identification
|
|
|
|
**Type:** Unit
|
|
**Class:** `Mt940ParserTest.java`
|
|
**Method:** `testDebitCreditIdentification()`
|
|
|
|
**Scenarios:**
|
|
|
|
| # | Input | Expected Result |
|
|
|---|-------|----------------|
|
|
| a | `:61:` with `CR` indicator | amountCents > 0 (positive/incoming) |
|
|
| b | `:61:` with `D` indicator | amountCents < 0 (negative/outgoing) |
|
|
| c | `:61:` with `RC` (reversal credit) | amountCents > 0 |
|
|
| d | `:61:` with `RD` (reversal debit) | amountCents < 0 |
|
|
|
|
---
|
|
|
|
### T-04: MT940 parsing — German amount format
|
|
|
|
**Type:** Unit
|
|
**Class:** `Mt940ParserTest.java`
|
|
**Method:** `testGermanAmountParsing()`
|
|
|
|
**Scenarios:**
|
|
|
|
| # | Input | Expected Result |
|
|
|---|-------|----------------|
|
|
| a | `50,00` | 5000 cents |
|
|
| b | `1234,56` | 123456 cents |
|
|
| c | `0,01` | 1 cent |
|
|
| d | `99999,99` | 9999999 cents |
|
|
|
|
---
|
|
|
|
### T-05: MT940 parsing — multi-statement file
|
|
|
|
**Type:** Unit
|
|
**Class:** `Mt940ParserTest.java`
|
|
**Method:** `testMultiStatementFile()`
|
|
|
|
**Scenarios:**
|
|
|
|
| # | Input | Expected Result |
|
|
|---|-------|----------------|
|
|
| a | File with 2 statements (2 different accounts) | Parse both, return transactions from all |
|
|
| b | Filter by specific account IBAN | Only matching account's transactions returned |
|
|
|
|
---
|
|
|
|
### T-06: MT940 parsing — malformed input
|
|
|
|
**Type:** Unit
|
|
**Class:** `Mt940ParserTest.java`
|
|
**Method:** `testMalformedInput()`
|
|
|
|
**Scenarios:**
|
|
|
|
| # | Input | Expected Result |
|
|
|---|-------|----------------|
|
|
| a | Empty file | ParseResult with empty list + warning |
|
|
| b | Missing `:60F:` (no opening balance) | Parse continues, openingBalanceCents = null |
|
|
| c | Corrupted amount field | Transaction skipped, warning added |
|
|
| d | Binary file (not text) | Exception or empty result with error warning |
|
|
|
|
---
|
|
|
|
### T-07: CAMT.053 parsing — standard XML file
|
|
|
|
**Type:** Unit
|
|
**Class:** `cannamanage-service/src/test/java/de/cannamanage/service/bankimport/Camt053ParserTest.java`
|
|
**Method:** `testParseStandardCamt053()`
|
|
|
|
**Preconditions:**
|
|
- CAMT.053 test fixture in `src/test/resources/fixtures/camt053/standard.xml`
|
|
|
|
**Scenarios:**
|
|
|
|
| # | Input | Expected Result |
|
|
|---|-------|----------------|
|
|
| a | Standard CAMT.053 with 3 entries | ParseResult with 3 transactions |
|
|
| b | `<Amt Ccy="EUR">50.00</Amt>` | amountCents = 5000 |
|
|
| c | `<CdtDbtInd>CRDT</CdtDbtInd>` | amountCents positive |
|
|
| d | `<BookgDt><Dt>2025-06-15</Dt></BookgDt>` | bookingDate = 2025-06-15 |
|
|
|
|
---
|
|
|
|
### T-08: CAMT.053 parsing — multiple entries
|
|
|
|
**Type:** Unit
|
|
**Class:** `Camt053ParserTest.java`
|
|
**Method:** `testMultipleEntries()`
|
|
|
|
**Scenarios:**
|
|
|
|
| # | Input | Expected Result |
|
|
|---|-------|----------------|
|
|
| a | 10 `<Ntry>` elements | 10 ParsedTransaction objects |
|
|
| b | Mix of CRDT and DBIT | Correct positive/negative amounts |
|
|
|
|
---
|
|
|
|
### T-09: CAMT.053 parsing — namespace versions
|
|
|
|
**Type:** Unit
|
|
**Class:** `Camt053ParserTest.java`
|
|
**Method:** `testNamespaceVersions()`
|
|
|
|
**Scenarios:**
|
|
|
|
| # | Input | Expected Result |
|
|
|---|-------|----------------|
|
|
| a | Namespace `camt.053.001.02` | Parsed successfully |
|
|
| b | Namespace `camt.053.001.08` | Parsed successfully |
|
|
| c | Unknown namespace `camt.054.001.02` | canParse returns false or warning |
|
|
|
|
---
|
|
|
|
### T-10: CAMT.053 parsing — counterparty extraction
|
|
|
|
**Type:** Unit
|
|
**Class:** `Camt053ParserTest.java`
|
|
**Method:** `testCounterpartyExtraction()`
|
|
|
|
**Scenarios:**
|
|
|
|
| # | Input | Expected Result |
|
|
|---|-------|----------------|
|
|
| a | `<Dbtr><Nm>Max Mustermann</Nm></Dbtr>` | counterpartyName = "Max Mustermann" |
|
|
| b | `<DbtrAcct><Id><IBAN>DE12...</IBAN></Id></DbtrAcct>` | counterpartyIban = "DE12..." |
|
|
| c | `<Ustrd>M-0042 Beitrag</Ustrd>` | referenceText = "M-0042 Beitrag" |
|
|
|
|
---
|
|
|
|
### T-11: CAMT.053 parsing — invalid XML
|
|
|
|
**Type:** Unit
|
|
**Class:** `Camt053ParserTest.java`
|
|
**Method:** `testInvalidXml()`
|
|
|
|
**Scenarios:**
|
|
|
|
| # | Input | Expected Result |
|
|
|---|-------|----------------|
|
|
| a | Malformed XML (unclosed tag) | Exception with descriptive message |
|
|
| b | Valid XML but wrong schema (not CAMT) | Empty result or format detection rejects |
|
|
|
|
---
|
|
|
|
### T-12 through T-17: CSV parsing tests
|
|
|
|
**Type:** Unit
|
|
**Class:** `cannamanage-service/src/test/java/de/cannamanage/service/bankimport/CsvBankParserTest.java`
|
|
|
|
Test fixtures:
|
|
- `src/test/resources/fixtures/csv/sparkasse.csv` (semicolon, ISO-8859-1, dd.MM.yyyy)
|
|
- `src/test/resources/fixtures/csv/ing-diba.csv` (semicolon, ISO-8859-1, different columns)
|
|
- `src/test/resources/fixtures/csv/commerzbank.csv` (semicolon, UTF-8)
|
|
- `src/test/resources/fixtures/csv/malformed.csv` (bad rows)
|
|
|
|
Key scenarios:
|
|
- Semicolon delimiter parsing
|
|
- German number format: `"1.234,56"` → 123456 cents
|
|
- Configurable column positions via CsvColumnMapping
|
|
- Header row skipping (1 or 2 rows)
|
|
- UTF-8 encoding support
|
|
- Empty/malformed rows produce warnings, don't abort
|
|
|
|
---
|
|
|
|
### T-22 through T-33: Matching algorithm tests
|
|
|
|
**Type:** Unit
|
|
**Class:** `cannamanage-service/src/test/java/de/cannamanage/service/bankimport/PaymentMatchingServiceTest.java`
|
|
|
|
**Test data setup:**
|
|
```java
|
|
// Members:
|
|
// - Member "M-0042" (Max Mustermann, IBAN DE89370400440532013000, fee 50€/month)
|
|
// - Member "M-0043" (Anna Schmidt, no IBAN, fee 75€/quarter)
|
|
// - Member "M-0044" (Peter Müller, IBAN DE12345678901234567890, fee 50€/month)
|
|
```
|
|
|
|
**Key test scenarios:**
|
|
|
|
| Test | Transaction | Expected Match | Confidence | Rationale |
|
|
|------|-------------|---------------|------------|-----------|
|
|
| T-22 | 50.00€, ref "M-0042 Beitrag" | M-0042 | ≥95% | Exact amount + member number |
|
|
| T-23 | 51.00€, ref "M-0042 Beitrag" | M-0042 | ~85% | Amount within 5% + member number |
|
|
| T-24 | 50.00€, ref "M-0042 Juni" | M-0042 | ≥90% | Exact amount + member number |
|
|
| T-25 | 50.00€, ref "Max Mustermann Beitrag" | M-0042 | ≥80% | Amount + full name |
|
|
| T-26 | 50.00€, ref "Mustermann Mitgliedsbeitrag" | M-0042 | ~75% | Amount + last name |
|
|
| T-27 | 50.00€, ref "Überweisung", IBAN DE89... | M-0042 | ≥85% | Amount + IBAN |
|
|
| T-28 | 50.00€, ref "M-0042", IBAN DE89... | M-0042 | ≥95% (MATCHED) | All criteria hit |
|
|
| T-29 | 50.00€, ref "Beitrag", no IBAN | M-0042 or M-0044 | 60-89% (SUGGESTED) | Amount matches two members |
|
|
| T-30 | 123.45€, ref "random text" | None | <60% (UNMATCHED) | No criteria match |
|
|
| T-31 | 50.00€, ref "Beitrag", no IBAN match | Conflict | SUGGESTED | Two 50€ members, can't distinguish |
|
|
| T-32 | -50.00€ (debit) | Skipped | N/A | Negative = outgoing, not a payment |
|
|
| T-33 | 50.00€ from member with no fee assignment | None | <60% | No expected payment to match against |
|
|
|
|
---
|
|
|
|
### T-34 through T-41: Import service integration tests
|
|
|
|
**Type:** Integration (Spring Boot Test with H2)
|
|
**Class:** `cannamanage-service/src/test/java/de/cannamanage/service/bankimport/BankImportServiceTest.java`
|
|
|
|
**Setup:** `@SpringBootTest` with `@Transactional`, test fixtures loaded via Flyway
|
|
|
|
| Test | Scenario | Verification |
|
|
|------|----------|-------------|
|
|
| T-34 | Upload MT940 file | Session created, status=PENDING, transactions persisted |
|
|
| T-35 | Run matching on session | Transactions updated with matchStatus + confidence |
|
|
| T-36 | Confirm a MATCHED transaction | Payment entity created, LedgerEntry(INCOME) created, tx status=CONFIRMED |
|
|
| T-37 | Bulk confirm 5 MATCHED transactions | 5 Payments created, session.confirmedCount = 5 |
|
|
| T-38 | Skip a transaction | tx status=SKIPPED, skipReason set |
|
|
| T-39 | Manual assign to different member | Payment created with specified memberId |
|
|
| T-40 | Categorize debit as RENT expense | LedgerEntry(EXPENSE, RENT) created |
|
|
| T-41 | Complete session | status=COMPLETED, completedAt set |
|
|
|
|
**Postconditions for T-36:**
|
|
- `Payment` exists with correct memberId, amountCents, paymentMethod=BANK_TRANSFER
|
|
- `LedgerEntry` exists with transactionType=INCOME, relatedPaymentId set
|
|
- `BankTransaction.matchedPaymentId` set to new Payment ID
|
|
- Audit event BANK_PAYMENT_CONFIRMED logged
|
|
|
|
---
|
|
|
|
### T-42 through T-46: Tier enforcement tests
|
|
|
|
**Type:** Unit
|
|
**Class:** `cannamanage-service/src/test/java/de/cannamanage/service/TierLimitServiceTest.java`
|
|
|
|
| Test | Tier | Action | Expected |
|
|
|------|------|--------|----------|
|
|
| T-42 | STARTER | Import MT940 | TierLimitExceededException |
|
|
| T-43 | STARTER | 2nd import in same month | TierLimitExceededException |
|
|
| T-44 | PRO | Import MT940 | Allowed |
|
|
| T-45 | PRO | Create 4th CSV mapping | TierLimitExceededException |
|
|
| T-46 | ENTERPRISE | Check autoConfirmAllowed | returns true |
|
|
|
|
---
|
|
|
|
### T-47 through T-50: IBAN validation tests
|
|
|
|
**Type:** Unit
|
|
**Class:** `cannamanage-service/src/test/java/de/cannamanage/service/bankimport/IbanValidatorTest.java`
|
|
|
|
| Test | Input | Expected |
|
|
|------|-------|----------|
|
|
| T-47 | `DE89370400440532013000` | valid = true |
|
|
| T-48 | `DE00370400440532013000` (bad checksum) | valid = false |
|
|
| T-49 | `DE893704004` (too short) | valid = false |
|
|
| T-50 | `GB29NWBK60161331926819` (UK IBAN) | valid = true |
|
|
|
|
---
|
|
|
|
### T-51 through T-57: REST API integration tests
|
|
|
|
**Type:** Integration (MockMvc)
|
|
**Class:** `cannamanage-api/src/test/java/de/cannamanage/api/controller/BankImportControllerTest.java`
|
|
|
|
| Test | Request | Expected Response |
|
|
|------|---------|------------------|
|
|
| T-51 | POST /api/finance/import/upload (multipart) | 201, session JSON with transaction count |
|
|
| T-52 | POST /api/finance/import/upload (15MB file) | 413 Payload Too Large |
|
|
| T-53 | POST /api/finance/import/upload (no FINANCE_IMPORT permission) | 403 Forbidden |
|
|
| T-54 | POST /api/finance/import/transactions/{id}/confirm | 200, updated transaction JSON |
|
|
| T-55 | GET /api/finance/import/sessions?page=0&size=10 | 200, paginated session list |
|
|
| T-56 | POST + GET + PUT + DELETE /api/finance/import/csv-mappings | Full CRUD cycle |
|
|
| T-57 | GET /api/finance/import/sessions (as different club) | 200, empty list (tenant isolation) |
|
|
|
|
---
|
|
|
|
### T-58 through T-59: Member IBAN consent tests
|
|
|
|
**Type:** Integration
|
|
**Class:** `cannamanage-api/src/test/java/de/cannamanage/api/controller/MemberControllerTest.java`
|
|
|
|
| Test | Precondition | Request | Expected |
|
|
|------|-------------|---------|----------|
|
|
| T-58 | BANK_DATA consent granted | PATCH /api/members/{id}/iban | 200, IBAN stored |
|
|
| T-59 | No BANK_DATA consent | PATCH /api/members/{id}/iban | 400, "Consent required" |
|
|
|
|
---
|
|
|
|
### T-60 through T-62: Flyway migration tests
|
|
|
|
**Type:** Integration
|
|
**Class:** `cannamanage-api/src/test/java/de/cannamanage/api/FlywayMigrationTest.java`
|
|
|
|
Verify all migrations V30-V32 apply cleanly on a fresh H2 database without errors.
|
|
|
|
---
|
|
|
|
### T-63 through T-65: End-to-end backend tests
|
|
|
|
**Type:** E2E (Spring Boot integration with full context)
|
|
**Class:** `cannamanage-api/src/test/java/de/cannamanage/api/BankImportE2ETest.java`
|
|
|
|
| Test | Flow | Verification |
|
|
|------|------|-------------|
|
|
| T-63 | Upload MT940 → detect → parse → match → confirm all → complete | Payments + LedgerEntries created, session COMPLETED |
|
|
| T-64 | Upload CSV → create mapping → parse with mapping → match → confirm | Custom mapping applied, correct column extraction |
|
|
| T-65 | Upload → match (3 auto, 2 suggested, 1 unmatched) → confirm 3 → assign 2 → skip 1 → complete | All statuses correct, counts accurate |
|
|
|
|
---
|
|
|
|
### T-66 through T-70: Frontend E2E tests (Playwright)
|
|
|
|
**Type:** E2E (Playwright)
|
|
**File:** `cannamanage-frontend/e2e/bank-import.spec.ts`
|
|
|
|
| Test | Scenario | Verification |
|
|
|------|----------|-------------|
|
|
| T-66 | Navigate to /finance/import, verify 4-step wizard structure | Steps visible, upload zone rendered |
|
|
| T-67 | Upload CSV → column mapping dropdowns → assign columns → proceed | Mapping applied, preview table shown |
|
|
| T-68 | Match review table → filter by status → verify correct rows | Filter tabs work, row counts match |
|
|
| T-69 | Click "Alle bestätigen" → success toast → counts update | Confirmed count increases, green rows disappear |
|
|
| T-70 | Navigate to import history → past sessions listed | Table with filename, date, status columns |
|
|
|
|
---
|
|
|
|
## Test Data (Fixtures)
|
|
|
|
### MT940 Test File (`sparkasse-standard.sta`)
|
|
|
|
```
|
|
:20:STARTUMSE
|
|
:25:20050550/7654321
|
|
:28C:00000/001
|
|
:60F:C260601EUR1234,56
|
|
:61:2506150615CR50,00NMSCNONREF
|
|
:86:SVWZ+M-0042 Mitgliedsbeitrag Juni 2025
|
|
EREF+NOTPROVIDED
|
|
DEBT+DE89370400440532013000
|
|
:61:2506150615CR75,00NMSCNONREF
|
|
:86:SVWZ+Anna Schmidt Quartalsbeitrag
|
|
DEBT+DE55500105175898765432
|
|
:61:2506150615DR120,00NMSCNONREF
|
|
:86:SVWZ+Miete Vereinsraum Juni
|
|
CRED+DE44100800000123456789
|
|
:61:2506150615CR50,00NMSCNONREF
|
|
:86:SVWZ+Beitrag
|
|
DEBT+DE12345678901234567890
|
|
:61:2506150615CR25,00NMSCNONREF
|
|
:86:SVWZ+Spende Vereinsfest
|
|
DEBT+DE99876543210987654321
|
|
:62F:C260615EUR1239,56
|
|
```
|
|
|
|
### CAMT.053 Test File (`standard.xml`)
|
|
|
|
```xml
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02">
|
|
<BkToCstmrStmt>
|
|
<Stmt>
|
|
<Id>2025-06-15-001</Id>
|
|
<Acct><Id><IBAN>DE20050550007654321</IBAN></Id></Acct>
|
|
<Bal><!-- balances --></Bal>
|
|
<Ntry>
|
|
<Amt Ccy="EUR">50.00</Amt>
|
|
<CdtDbtInd>CRDT</CdtDbtInd>
|
|
<BookgDt><Dt>2025-06-15</Dt></BookgDt>
|
|
<ValDt><Dt>2025-06-15</Dt></ValDt>
|
|
<NtryDtls><TxDtls>
|
|
<RmtInf><Ustrd>M-0042 Mitgliedsbeitrag Juni 2025</Ustrd></RmtInf>
|
|
<RltdPties>
|
|
<Dbtr><Nm>Max Mustermann</Nm></Dbtr>
|
|
<DbtrAcct><Id><IBAN>DE89370400440532013000</IBAN></Id></DbtrAcct>
|
|
</RltdPties>
|
|
</TxDtls></NtryDtls>
|
|
</Ntry>
|
|
<!-- more entries... -->
|
|
</Stmt>
|
|
</BkToCstmrStmt>
|
|
</Document>
|
|
```
|
|
|
|
### CSV Test File (`sparkasse.csv`)
|
|
|
|
```csv
|
|
"Auftragskonto";"Buchungstag";"Valutadatum";"Buchungstext";"Verwendungszweck";"Beguenstigter/Zahlungspflichtiger";"Kontonummer";"BLZ";"Betrag";"Waehrung"
|
|
"DE20050550007654321";"15.06.2025";"15.06.2025";"GUTSCHR";"M-0042 Mitgliedsbeitrag Juni 2025";"Max Mustermann";"DE89370400440532013000";"37040044";"50,00";"EUR"
|
|
"DE20050550007654321";"15.06.2025";"15.06.2025";"GUTSCHR";"Anna Schmidt Quartalsbeitrag";"Anna Schmidt";"DE55500105175898765432";"50010517";"75,00";"EUR"
|
|
```
|
|
|
|
---
|
|
|
|
## Test Coverage Matrix
|
|
|
|
| Component | Unit | Integration | E2E | Total |
|
|
|-----------|------|-------------|-----|-------|
|
|
| Mt940Parser | 6 | 0 | 1 | 7 |
|
|
| Camt053Parser | 5 | 0 | 0 | 5 |
|
|
| CsvBankParser | 6 | 0 | 1 | 7 |
|
|
| BankStatementParserService | 4 | 0 | 0 | 4 |
|
|
| PaymentMatchingService | 12 | 0 | 0 | 12 |
|
|
| BankImportService | 0 | 8 | 3 | 11 |
|
|
| TierLimitService | 5 | 0 | 0 | 5 |
|
|
| IbanValidator | 4 | 0 | 0 | 4 |
|
|
| BankImportController | 0 | 7 | 0 | 7 |
|
|
| MemberController (IBAN) | 0 | 2 | 0 | 2 |
|
|
| Flyway Migrations | 0 | 3 | 0 | 3 |
|
|
| Frontend (Playwright) | 0 | 0 | 5 | 5 |
|
|
| **Total** | **42** | **20** | **10** | **70** |
|
|
|
|
---
|
|
|
|
## Traceability Matrix
|
|
|
|
| Requirement | Plan Step | Test Case(s) |
|
|
|-------------|-----------|-------------|
|
|
| MT940 format parsing | Phase 2, Step 2.2 | T-01 through T-06 |
|
|
| CAMT.053 format parsing | Phase 2, Step 2.3 | T-07 through T-11 |
|
|
| CSV format parsing with configurable columns | Phase 2, Step 2.4 | T-12 through T-17 |
|
|
| Auto-format detection | Phase 2, Step 2.5 | T-18 through T-21 |
|
|
| Weighted matching algorithm | Phase 3, Step 3.1 | T-22 through T-33 |
|
|
| Import session lifecycle | Phase 3, Step 3.2 | T-34 through T-41 |
|
|
| Tier-based restrictions | Phase 3, Step 3.3 | T-42 through T-46 |
|
|
| IBAN validation + consent | Phase 4, Step 4.4 | T-47 through T-50, T-58, T-59 |
|
|
| REST API endpoints | Phase 4, Step 4.1 | T-51 through T-57 |
|
|
| Database migrations | Phase 1, Step 1.1 | T-60 through T-62 |
|
|
| Full import flow | All phases | T-63 through T-65 |
|
|
| Frontend wizard UX | Phase 5 | T-66 through T-70 |
|
|
| Tenant isolation | Phase 4, Step 4.3 | T-57 |
|
|
| GDPR consent for IBAN | Phase 1, Step 1.2 (ConsentType) | T-58, T-59 |
|
|
| File size limit (10MB) | Phase 4, Step 4.3 | T-52 |
|
|
| Permission enforcement | Phase 4, Step 4.3 | T-53 |
|