# Testability Changes Summary (01-testability-refactoring)

Applied 7 change(s):

## Config extraction (build-time env vars)

- **C01 (AZ-448)** — changed `getWeatherData` in `src/features/flights/flightPlanUtils.ts`: the OpenWeatherMap API key is now read from `import.meta.env.VITE_OWM_API_KEY` instead of being a hardcoded literal in source. Production builds inject the key through the build environment; tests can run with the key unset and `getWeatherData` returns `null` gracefully. Unblocks NFT-SEC-09 source-string check (results_report.md row 63). Risk: low.
- **C02 (AZ-449)** — changed `getWeatherData` in `src/features/flights/flightPlanUtils.ts`: the OWM REST base URL is now read from `import.meta.env.VITE_OWM_BASE_URL` (default `https://api.openweathermap.org/data/2.5`, trailing slash stripped). The e2e profile sets it to `http://owm-stub:8081/data/2.5` so the stub can serve deterministic responses without source edits. Risk: low.
- **C03 (AZ-450)** — changed `TILE_URLS` in `src/features/flights/types.ts`: the classic OSM and Esri satellite Leaflet tile templates are now read from `VITE_OSM_TILE_URL` and `VITE_ESRI_TILE_URL` (defaults preserve today's public endpoints, trailing slash stripped). Lets the e2e `tile-stub` service intercept tile fetches for deterministic map rendering. Risk: low.
- **C05 (AZ-452)** — added `getApiBase()` accessor in `src/api/client.ts`; `request()`, `refreshToken()`, and `createSSE()` (`src/api/sse.ts`) now prepend the returned prefix to every API URL. Default is `''` (production: SPA + suite share nginx, paths are relative), `VITE_API_BASE_URL` overrides. Lets the fast-component-test harness point at a stub server without touching call sites. Risk: low.

## Bundling change (drop external CDN dependency)

- **C04 (AZ-451)** — changed `defaultIcon` in `src/features/flights/mapIcons.ts`: the Leaflet marker PNG is now imported from the `leaflet` package (`leaflet/dist/images/marker-icon.png`) and bundled into `dist/assets/` by Vite, replacing the hardcoded `https://unpkg.com/leaflet@1.7.1/...` CDN URL. Fixes the air-gap restriction (E1, NFT-RES-03), kills a version-mismatch bug (source pinned 1.7.1, `package.json` pins ^1.9.4), and removes one external dependency for e2e determinism. Risk: low.

## Singleton wrapping (overridable indirection)

- **C06 (AZ-453)** — added `navigateToLoginImpl` + `setNavigateToLogin` in `src/api/client.ts`: the failed-refresh redirect now flows through a module-level function whose default writes `'/login'` to `window.location.href`. Tests can install a spy via `setNavigateToLogin(fn)` to assert "redirect invoked" without globally stubbing `window.location`. Production behavior unchanged. Risk: low.

## Documentation (intent preservation)

- **C07 (AZ-454)** — added JSDoc on `setToken` and `getToken` in `src/api/client.ts`: documents the AC-02 / O2 design intent (in-memory bearer; survives reload only via the HttpOnly refresh cookie) and the test-override role of these accessors. Prevents a future maintainer from deleting them as "dead code" because their imports look dead at static-grep time. Comment-only — zero behavioral change. Risk: low.

---

## Verification snapshot

- `bun run tsc --noEmit` → 0 errors
- `grep -rn '335799082893fad97fa36118b131f919' src/` → 0 hits (C01 AC-2)
- `grep -rn 'unpkg.com' src/` → 0 hits (C04 acceptance)
- `grep -rn 'api.openweathermap.org' src/` → 1 hit, inside the `DEFAULT_OWM_BASE_URL` constant only (C02 default-fallback contract)
- `grep -rn 'window.location.href' src/` → 2 hits, both inside the default `navigateToLoginImpl` (JSDoc reference + the default impl body); no caller writes `window.location` directly (C06 AC-3)

## Out of scope (carried forward)

All 13 items rejected from this run are recorded in `deferred_to_refactor.md` (D01–D13). They are candidates for Step 8 (Refactor) or Phase B feature cycles, not for this testability pass.

## Tracker links

| Change | Task | Epic |
|--------|------|------|
| C01 | [AZ-448](https://denyspopov.atlassian.net/browse/AZ-448) | [AZ-447](https://denyspopov.atlassian.net/browse/AZ-447) |
| C02 | [AZ-449](https://denyspopov.atlassian.net/browse/AZ-449) | AZ-447 |
| C03 | [AZ-450](https://denyspopov.atlassian.net/browse/AZ-450) | AZ-447 |
| C04 | [AZ-451](https://denyspopov.atlassian.net/browse/AZ-451) | AZ-447 |
| C05 | [AZ-452](https://denyspopov.atlassian.net/browse/AZ-452) | AZ-447 |
| C06 | [AZ-453](https://denyspopov.atlassian.net/browse/AZ-453) | AZ-447 |
| C07 | [AZ-454](https://denyspopov.atlassian.net/browse/AZ-454) | AZ-447 |
