Files
marketplaces/e2e/currency-switch.spec.ts
sdarbinyan 14467cc6fb
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
feat: FX-quote-backed currency conversion, delete admin rate editor
F10-F12 of the frontend backlog. Contract: PHASE-1-MONEY-FX-PAYMENTS-CONTRACT.md §3.

Removed the failure mode §5 of that contract exists to close: rates were
typed once by an admin into Settings, persisted to localStorage, seeded from
a hardcoded DEFAULT_RATES table (USD: 0.011, AMD: 4.3) that never updated and
drifted from market. Nothing recorded which rate produced a displayed price
or when.

- currency-rates.service.ts   now fetches through FX_QUOTE_GATEWAY instead of
                              reading admin-typed/localStorage numbers. Stays
                              synchronous at the call site (getRate/convert) -
                              rewriting every consuming template to `| async`
                              is a separate, larger change (F13, not this
                              commit). Before a quote has loaded for a pair,
                              getRate returns 1 rather than a fabricated
                              market rate; isRateReady() lets a caller that
                              cares distinguish the two. ensureFreshQuote()
                              added for checkout to await before charging,
                              per contract §3.2's stale-quote policy.
- language.service.ts        setCurrency() now triggers a quote fetch instead
                              of just flipping the display signal.
- cart.component.ts           openPaymentPopup() awaits ensureFreshQuote()
                              before computing the charged amount.
- admin-settings-page.*        currency-rate editor deleted (F11) - card,
                              component state, and the three orphaned i18n
                              keys it was the only consumer of.

Two real bugs surfaced fixing this, neither cosmetic:

1. fx-quote-local.gateway.ts had CurrencyRatesService.convert() as its rate
   source. That is now circular - CurrencyRatesService depends on
   FX_QUOTE_GATEWAY, and under useMockData:true this gateway IS
   FX_QUOTE_GATEWAY. Would have recursed the moment mock FX data was
   exercised. Fixed by giving the local gateway its own static mock table -
   the correct home for those numbers now: explicitly labelled dev/mock data,
   only wired in behind useMockData, never presented as a live rate.

2. currency-convert.pipe.ts memoized its result on (amount, from, to) alone.
   That was already latently wrong - rates could change via the old
   setRate() without the pipe re-evaluating for an already-rendered price -
   but never surfaced because rates never changed mid-session in practice.
   Async quote loading made it concrete and reproducible: a price rendered
   before its quote arrived stayed wrong forever, because none of the three
   cached inputs ever changed again on their own. Fixed with a ratesVersion
   counter on the service, bumped on every quote arrival, included in the
   pipe's cache key.

Both found and fixed via the E2E suite (docs from the prior commit) actually
exercising the real code path: GET /api/v2/pricing/fx-quote intercepted with
a contract-shaped response rather than flipping the whole app into mock mode,
so the test runs the real FxQuoteApiGateway, not a stand-in for it.

Verified: 3/3 E2E green, 115/115 unit tests green, arch:check clean,
production build succeeds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 14:03:02 +04:00

5.2 KiB