08 — Test Plan
Project: CannaManage — B2B SaaS for German Cannabis Social Clubs Version: 14.0 (Sprint 14) Date: 2026-06-19 Status: Active — 500+ automated tests passing, JaCoCo 80% coverage gate
1. Test Strategy Overview
1.1 Testing Pyramid
┌───────────────────┐
│ E2E / System │ 5% — Playwright (browser automation)
│ Tests │
├───────────────────┤
│ Integration │ 25% — Spring Boot Test + Testcontainers
│ Tests │
├───────────────────┤
│ Frontend Unit │ 20% — Vitest + MSW (React components)
│ Tests │
├───────────────────┤
│ Backend Unit │ 50% — JUnit 5 + Mockito
│ Tests │
└───────────────────┘
The compliance-critical path (ComplianceService) requires 100% line coverage — no exceptions. Every quota rule is a legal obligation under CanG §§19–22.
1.2 Tools and Frameworks
| Layer | Tool | Purpose |
|---|---|---|
| Backend Unit | JUnit 5 (junit-jupiter) |
Test runner |
| Backend Unit | Mockito 5 | Mock dependencies |
| Backend Unit | AssertJ | Fluent assertions |
| Backend Integration | Spring Boot Test (@SpringBootTest) |
Full application context |
| Backend Integration | Testcontainers (PostgreSQL 16) | Real DB in Docker |
| Backend Integration | MockMvc / RestAssured | HTTP layer testing |
| Backend Coverage | JaCoCo | Line/branch coverage — 80% gate |
| Frontend Unit | Vitest | Fast ESM-native test runner |
| Frontend Unit | MSW (Mock Service Worker) | API mocking at network level |
| Frontend Unit | Testing Library | Component rendering + queries |
| Frontend E2E | Playwright | Browser automation, multi-browser |
| Frontend E2E | Playwright Test | Test runner with fixtures + assertions |
| System Tests | Playwright + SQL seed | Full-stack with seeded database |
1.3 CI Pipeline Test Flow
graph LR
Push["git push"] --> CI["Gitea Actions"]
CI --> Backend["Maven Build\n+ Unit Tests\n+ Integration Tests"]
CI --> Frontend["pnpm install\n+ Vitest"]
Backend --> JaCoCo["JaCoCo Report\n80% gate"]
JaCoCo -->|"pass"| Deploy["Deploy"]
JaCoCo -->|"fail"| Block["❌ Block Merge"]
Frontend --> E2E["Playwright E2E\n(on main only)"]
E2E --> Deploy
| Branch pattern | Tests run |
|---|---|
| Feature PR | Backend unit + integration, Frontend Vitest |
main |
All above + JaCoCo gate + Playwright E2E |
2. Backend Testing
2.1 Unit Tests (JUnit 5 + Mockito)
Target: All service classes, especially compliance-critical code.
ComplianceService — Critical Path (100% coverage required)
| TC | Method Under Test | Scenario | Expected |
|---|---|---|---|
| TC-001 | checkDistributionAllowed |
Adult at 50g monthly limit + 1g request | QuotaExceededException(MONTHLY) |
| TC-002 | checkDistributionAllowed |
Under-21 at 30g monthly limit + 1g request | QuotaExceededException(MONTHLY) |
| TC-003 | checkDistributionAllowed |
Adult at 25g daily limit + 0.5g request | QuotaExceededException(DAILY) |
| TC-004 | checkDistributionAllowed |
THC >10% for under-21 | THCLimitExceededException |
| TC-005 | checkDistributionAllowed |
Suspended member | MemberSuspendedException |
| TC-006 | checkDistributionAllowed |
Valid request within limits | No exception |
| TC-007 | checkDistributionAllowed |
New month resets quota | No exception |
| TC-008 | calculateRemainingQuota |
Mid-month with partial usage | Correct remaining |
Other Service Tests
| Service | Key Test Cases |
|---|---|
DistributionService |
Create, validate batch availability, stock deduction |
MemberService |
CRUD, status transitions, age calculation |
StockService |
Batch management, movement tracking, low-stock alerts |
ReportService |
Monthly report generation, CSV/PDF export |
FinanceService |
Transaction recording, balance calculation |
BankImportService |
MT940/CAMT053 parsing, auto-matching |
AssemblyService |
Vote creation, ballot counting, quorum check |
AuditService |
Event recording, immutability verification |
NotificationService |
Dispatch, preference filtering, rate limiting |
2.2 Integration Tests (Testcontainers)
Real PostgreSQL 16 in Docker — catches issues that H2/mocks hide:
@SpringBootTest
@Testcontainers
class DistributionIntegrationTest {
@Container
static PostgreSQLContainer<?> pg = new PostgreSQLContainer<>("postgres:16-alpine");
@Test
void createDistribution_validRequest_persistsAndUpdatesQuota() {
// Given: member with remaining quota, batch with stock
// When: POST /api/v1/distributions
// Then: distribution persisted, quota updated, stock decremented
}
}
Key integration test scenarios:
| Area | Test Cases |
|---|---|
| Auth flow | Login → JWT → protected endpoint → 200 |
| Token revocation | Revoke → subsequent request → 401 |
| Tenant isolation | Tenant A data invisible to Tenant B |
| Flyway migrations | All V1–V36 apply cleanly to fresh DB |
| Stripe webhooks | Payment success → subscription activated |
| Bank import | Upload MT940 → parse → auto-match → transactions created |
| Report generation | Generate monthly report → PDF valid |
2.3 Coverage — JaCoCo Configuration
<!-- pom.xml -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<configuration>
<rules>
<rule>
<element>BUNDLE</element>
<limits>
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>0.80</minimum>
</limit>
</limits>
</rule>
<rule>
<element>CLASS</element>
<includes>
<include>de.cannamanage.service.ComplianceService</include>
</includes>
<limits>
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>1.00</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</plugin>
| Rule | Target | Threshold |
|---|---|---|
| Bundle (overall) | All classes | 80% line coverage |
| ComplianceService | Single class | 100% line coverage |
| Controllers | All controllers | 70% (integration tests cover the rest) |
3. Frontend Testing
3.1 Unit Tests — Vitest + MSW
Config: vitest.config.ts with jsdom environment.
// Example: MemberList component test
import { render, screen } from '@testing-library/react';
import { server } from '@/mocks/server';
import { http, HttpResponse } from 'msw';
test('renders member list from API', async () => {
server.use(
http.get('/api/v1/members', () =>
HttpResponse.json([{ id: 1, firstName: 'Max', lastName: 'Mustermann' }])
)
);
render(<MemberList />);
expect(await screen.findByText('Max Mustermann')).toBeInTheDocument();
});
MSW handler pattern: Mirrors the real API contract from src/types/api.ts — tests validate against the actual interface.
| Area | Key Test Cases |
|---|---|
| Components | Render, user interaction, conditional display |
| Hooks | React Query hooks with MSW responses |
| Forms | Validation, submission, error display |
| Auth | Protected route redirection, token refresh |
| i18n | German + English text rendering |
3.2 E2E Tests — Playwright
Config: playwright.config.ts — Chromium, Firefox, WebKit.
// Example: Distribution recording flow
test('admin records a distribution', async ({ page }) => {
await page.goto('/dashboard/distributions');
await page.click('[data-testid="new-distribution"]');
// Step 1: Select member
await page.fill('[data-testid="member-search"]', 'Max');
await page.click('[data-testid="member-option-1"]');
// Step 2: Quota check shown
await expect(page.locator('[data-testid="remaining-quota"]')).toBeVisible();
// Step 3: Select batch + amount
await page.selectOption('[data-testid="batch-select"]', 'batch-001');
await page.fill('[data-testid="amount"]', '5.0');
// Step 4: Confirm
await page.click('[data-testid="confirm-distribution"]');
await expect(page.locator('[data-testid="success-toast"]')).toBeVisible();
});
E2E test coverage:
| Flow | What's tested |
|---|---|
| Login (admin) | JWT auth, redirect to dashboard |
| Login (member) | Session auth, redirect to portal |
| Distribution recording | 4-step form, quota enforcement |
| Member management | CRUD, search, filter |
| Stock management | Add batch, view movements |
| Report generation | Select type, generate, download |
| Staff invite | Create invite, permission editor |
| Payment import | Upload file, review matches, confirm |
3.3 System Tests (SQL Seed + Playwright)
Full-stack tests that seed the database via SQL, then drive the application through the browser:
test.describe('System: Distribution Compliance', () => {
test.beforeAll(async () => {
// Seed database with member at 49g/month
await seedDatabase('test-data/member-near-limit.sql');
});
test('blocks distribution exceeding monthly quota', async ({ page }) => {
await loginAsAdmin(page);
await page.goto('/dashboard/distributions');
// Try to distribute 2g (would exceed 50g limit)
// ...
await expect(page.locator('[data-testid="quota-error"]')).toContainText('50g');
});
});
4. Test Data Strategy
| Environment | Data Source | Lifecycle |
|---|---|---|
| Unit tests | Mocked (Mockito / MSW) | Per-test |
| Integration tests | Testcontainers (fresh DB each run) | Per-class |
| E2E tests | SQL seed files | Per-suite |
| System tests | SQL seed + API-driven setup | Per-suite |
Test Data Files
cannamanage-api/src/test/resources/
├── test-data/
│ ├── member-near-limit.sql
│ ├── full-club-setup.sql
│ ├── bank-import-mt940.txt
│ └── bank-import-camt053.xml
5. Quality Gates
| Gate | Threshold | Blocks |
|---|---|---|
| JaCoCo overall | ≥ 80% line | PR merge |
| JaCoCo ComplianceService | 100% line | PR merge |
| Backend tests | All pass | PR merge |
| Frontend Vitest | All pass | PR merge |
| Playwright E2E | All pass | Deploy to prod |
| Build success | mvn clean verify | Everything |
6. Running Tests Locally
Backend
# All tests
mvn clean verify
# Unit only (fast)
mvn test
# Integration only
mvn verify -DskipUnitTests
# Single test class
mvn test -Dtest=ComplianceServiceTest
# With coverage report
mvn verify -Pjacoco
open target/site/jacoco/index.html
Frontend
cd cannamanage-frontend
# Unit tests (Vitest)
pnpm test
# Watch mode
pnpm test:watch
# Coverage
pnpm test:coverage
# E2E (Playwright)
pnpm exec playwright test
# E2E with UI
pnpm exec playwright test --ui
# Specific test file
pnpm exec playwright test tests/distribution.spec.ts
7. Test Metrics (Current)
| Metric | Value |
|---|---|
| Total automated tests | 500+ |
| Backend unit tests | ~250 |
| Backend integration tests | ~100 |
| Frontend Vitest tests | ~100 |
| Playwright E2E tests | ~50 |
| JaCoCo overall coverage | ~82% |
| ComplianceService coverage | 100% |
| CI pipeline duration | ~4 minutes |
| Flaky test rate | < 1% |
🌿 CannaManage
📋 Planning
🏗️ Architecture
🎨 Design
💻 Development
🌟 Product
📊 Sprint Status
| Sprint | Theme | Status |
|---|---|---|
| 1 | Domain Foundation | ✅ |
| 2 | REST API | ✅ |
| 3 | Staff & Portal | ✅ |
| 4 | Frontend MVP | ✅ |
| 5 | API Integration | ✅ |
| 6 | Production Readiness | ✅ |
| 7 | Communication | ✅ |
| 8 | Vereinsverwaltung | ✅ |
| 9 | Berichtszentrale | ✅ |
| 10 | Payment Import | ✅ |
| 11 | Test Coverage | ✅ |
| 12 | Golden Tests | ✅ |
| 13 | Prod Hardening | ✅ |
| 14 | Marketing | ✅ |
📈 Metrics
| Metric | Value |
|---|---|
| Entities | 57 |
| Controllers | 33 |
| Migrations | V1–V36 |
| Tests | 500+ |
| Coverage | 80% |