feat(F-138): completed feature
This commit is contained in:
31
work/artifacts/F-138/implementer.md
Normal file
31
work/artifacts/F-138/implementer.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# F-138 — Implementer evidence
|
||||
|
||||
## What
|
||||
Closed the `GET /pricing/variants/:id` 404 race: `PricingService.seedVariantPrice` (pricing owns its table) is injected into `CreateProductVariant` and called best-effort right after variant creation. The single constructor covers all 3 call sites (product autovariant, lazy variant GET, explicit POST variant).
|
||||
|
||||
## Design recap (architect-approved)
|
||||
- `PricingRepository` + `PricingService` gain `seedVariantPrice(variantId): Promise<void>`.
|
||||
- `PgPricingRepository.seedVariantPrice`: `INSERT pricing_variant_prices(variant_id, net_unit_amount_cents, offer_cents, cost_cents, vat_rate) VALUES ($1, 0, NULL, NULL, 'general') ON CONFLICT (variant_id) DO NOTHING` — same column set as `setVariantPrice` (`currency`/`created_at`/`updated_at` via DDL defaults). Idempotent + parameterized.
|
||||
- `CreateProductVariant` ctor += `pricing: PricingServicePort` (type-only import of pricing **public index** → R1-clean); after `variants.create`, best-effort `await this.pricing.seedVariantPrice(variant.id)` (try/catch: variant already persisted; seed failure must not fail/roll back variant creation).
|
||||
- `catalog.routes.ts`: `CatalogRoutesDeps += pricing`; `new CreateProductVariant(repository, variants, deps.pricing)`.
|
||||
- `build-app.ts`: hoist `const pricing = createPricingService(deps.pool)` above `registerCatalogRoutes` so it can be passed in; pricing routes + cart keep using it (unchanged).
|
||||
|
||||
## Files changed (source)
|
||||
- `pricing/domain/ports.ts` — +`seedVariantPrice` on `PricingService` + `PricingRepository`.
|
||||
- `pricing/application/pricing-service.ts` — +`seedVariantPrice` delegate.
|
||||
- `pricing/infrastructure/pg-pricing-repository.ts` — +`seedVariantPrice` impl.
|
||||
- `catalog/application/variant-use-cases.ts` — import `PricingServicePort`; ctor + execute seed (best-effort).
|
||||
- `catalog/api/catalog.routes.ts` — `CatalogRoutesDeps += pricing: PricingServicePort`; ctor passes `deps.pricing`.
|
||||
- `app/build-app.ts` — hoist `pricing` const; pass to `registerCatalogRoutes` deps.
|
||||
|
||||
## Tests
|
||||
- **NEW** `catalog/tests/variant-use-cases.test.ts` (3 tests, runnable no DB): FakeProductRepository + FakeProductVariantRepository + FakePricingService — asserts seed-called-with-variant.id, no-seed-when-product-missing, seed-failure-doesn't-break-creation.
|
||||
- `catalog.itest.ts` AC itest (skipIf !hasDb): POST `/products/:id/variants` → `pricing_variant_prices` row exists with `net=0/vat=general/currency=EUR`.
|
||||
- Collateral (fake fakes updated to satisfy new port method): `pricing/tests/pricing-service.test.ts` repo() factory, `cart/tests/cart-service.test.ts` + `checkout/tests/checkout-service.test.ts` PricingServicePort fakes — added `seedVariantPrice: async () => undefined` no-op.
|
||||
|
||||
## Verification (fresh run)
|
||||
- `npx tsc --noEmit` → **0 errors** (strict, noUncheckedIndexedAccess).
|
||||
- `npx vitest run` (targeted: variant/pricing/cart/checkout/product/build-app) → **16/16 pass**.
|
||||
- `npm test` → **209 passed | 57 skipped** (was 206 → +3 new; no regression in cart/checkout/pricing/build-app).
|
||||
- `scripts/check-module-boundaries.mjs src` → **0 NEW R1 violations**. (Pre-existing R1 on `security.routes.ts:14`→`log-broadcaster` is out of F-138 scope; git diff proves untouched.)
|
||||
- `./scripts/verify.sh` → **VERIFY_OK** (pre-close).
|
||||
Reference in New Issue
Block a user