# testing.md

## Test Categories

### 1. Unit logic (PHP)
- Pure-function helpers: `FundNormalizer::managerFromName`, `HmrcTaxCalculator::calculateTax`, `HmrcTaxCalculator::grossUp`, `SvgChart::lineChart`.
- Domain calculations: `Scenario::totalPortfolioValue`, `Asset::applyReturn`, `SippAsset::maxWithdrawal`, `StatePensionAsset::annualIncome`, `IsaAsset::withdraw`.
- Currently **manual** — written for testability but no PHPUnit suite yet (Composer-free constraint). Phase 5 will add a single-file PHPUnit alternative or equivalent.

### 2. Data integrity (MySQL)
- Unique-key enforcement on `funds.isin`, `funds.sedol`, `tax_brackets(tenant_id, tax_year, threshold_start)`, `fund_performance(fund_id, year)`.
- Foreign-key cascades: scenario delete → assets delete; client delete → scenarios delete.
- Tenant isolation: random spot-check `SELECT ... FROM scenarios WHERE id = ? AND tenant_id = ?` returns NULL when tenant mismatches.

### 3. Integration (engine end-to-end)
- Run a known scenario through `/simulate/:id` and verify:
  - `success_rate` is within expected band for that input
  - `confidence_bands` has one row per simulation year
  - `withdrawal_amount` × ~1.25 ≈ `gross_drawn` (because HMRC gross-up on basic-rate taxpayer)
  - `net_withdrawal` equals the user's input minus `state_pension_income`
- Run the same scenario with each risk profile and verify aggressive > moderate > conservative on equity-heavy portfolios.
- Verify SIPP asset only draws from 75% taxable fraction.
- Verify ISA asset withdrawals are tax-free (no gross-up).

### 4. Business flow validation
- Create client → create scenario → run simulation → download PDF: must complete in <10s end-to-end.
- Strategy comparator (Phase 2): baseline + revised both render, swap recommendations sane.
- Billing: Stripe test-mode flow → webhook received → subscription activated → quota increased.
- UK GDPR consent revoked → `ConsentWithdrawn` listener runs → client PII fields nulled.

---

## Validation Rules

- **All critical outputs verified twice** — once in the engine (path generation), once at aggregation (percentile computation). Mismatch logged.
- **Data consistency checks** required before any persist:
  - Asset value ≥ 0
  - Equity + bond + cash weights sum to 1.0 (±0.001)
  - Drawdown rate within valid range for asset type
  - SIPP minimum access age: 57 from 2028 (engine enforces)
  - Simulation years between 5 and 50
  - Inflation rate between 0 and 0.20
- **Verified data sticky** — `FundIngestor` refuses to overwrite `is_verified = 1` with an unverified row.
- **Tax brackets** — total must sum monotonically; lowest start must be 0; highest end must be NULL.

---

## Smoke-test checklist (run after every deploy)

From the latest `DEPLOY_NOW.md`:

1. ✅ Footer version banner matches deployed `APP_JS_VERSION`.
2. ✅ Login works, redirected to dashboard.
3. ✅ Existing scenario opens cleanly; assets render correctly (SIPP / ISA / State Pension types shown).
4. ✅ "Run Monte Carlo" returns within 3 seconds with JSON.
5. ✅ Dashboard shows 2 charts + confidence-bands table.
6. ✅ Amounts display in GBP (£) with `en-GB` formatting throughout.
7. ✅ "Download PDF Report" produces a 4-page report:
   - p1: header + headline + tax + assets (SIPP/ISA/State Pension breakdown)
   - p2: both charts (paths + bands)
   - p3: bands year-by-year table
   - p4: recommendations + FCA disclosure disclaimer + footer
8. ✅ Legend on left side of each chart, not overlapping data lines.
9. ✅ Numbers in tables centered; text labels left-aligned.
10. ✅ "Print / Save" opens browser print dialog with clean A4 layout.
11. ✅ Admin → Funds list paginates and searches (SEDOL column displayed).
12. ✅ Admin → Import Performance dry-run shows correct column mapping (ISIN/SEDOL accepted).
13. ✅ UK GDPR consent checkbox required before client save.
14. ✅ No PHP errors in cPanel error log during the test.

---

## Manual scenarios to validate engine correctness

### Scenario A — Conservative SIPP-only
- 1 SIPP asset, £300,000, drawdown 5% (£15,000/yr gross)
- UK State Pension: £11,502/yr (35 qualifying years)
- Annual net target: £25,000
- Expected: ~55–70% success (State Pension covers ~46% of target; SIPP drawdown taxable)

### Scenario B — ISA + SIPP combination
- ISA £200,000 (priority 1, tax-free) + SIPP £400,000 60/30/10 (priority 2)
- Annual net target: £30,000
- Expected: ~85–92% success (ISA draws reduce SIPP taxable income)

### Scenario C — Conservative balanced (three wrappers)
- Cash £20,000 + ISA £150,000 + SIPP £400,000 + State Pension £11,502/yr
- Annual net target: £35,000
- Risk profile: conservative
- Expected: 70–80% success; ConservativeProfile rebalancing kicks in on simulated drawdowns >15%

### Scenario D — Edge case: simulation timeout
- 1000 runs × 50 years × 12 months
- Confirm completes within PHP 30s execution limit
- If close to limit: drop default to 500 runs

---

## What's deliberately not tested yet

- **Load testing** — no concurrent-user simulation has been done. Shared host can probably handle 5–10 advisers simultaneously; we'll learn from production.
- **Security pen-test** — planned for end of Phase 4.
- **Browser matrix** — only tested on Chrome and Edge so far. Safari + Firefox sanity check pending.
- **Mobile** — dashboard responsive but not optimised for phones.
- **Stripe webhook end-to-end** — use Stripe CLI (`stripe listen`) to replay test events against staging before going live.
