Files
descrybe/docs/full-app-qa-report.md
T

311 lines
14 KiB
Markdown
Raw Normal View History

# Full app QA report (Descrybe v2)
**Date:** 2026-08-04
**Repo:** `f:/laragon/www/_MY/descrybe-v2`
**Wave:** 20 parallel agents (credits, analytics, Stripe, tutorial, SEO copy, Woo, E2E, Free tier, settings, products, catalog, admin/API, security, dashboard, processing, exports, nav) + final reconcile
**Build gates (reconcile):** `go build ./...`**PASS** · `npm run check` (apps/web) → **0 errors / 0 warnings**
---
## Executive summary
The 20-agent QA wave is complete. Descrybe v2 is green to demo locally: credits and analytics now read the **live wallet** (no more stale cycle inflation), Stripe Checkout works in **mock** (`STRIPE_MOCK=true`) and is ready for real keys, the in-app tutorial is forced-on-actions but skippable, Woo has a seed path without a live shop, and both catalog and marketing E2E suites passed.
**Local Demo Co is back on Enterprise** after mock Stripe checkout temporarily assigned a paid self-serve plan — restored to ~1M AI credits / Unlimited packaging for continued demo use.
| You want… | Status |
|-----------|--------|
| Demo the product end-to-end | **Ready** — login below |
| Buy a plan in prod | Set Stripe env (see § Stripe) |
| Connect a real Woo store | UI `/woocommerce` or `WOO_*` env (see § Woo) |
| Try Woo audiences without WP | `make seed-woo` / `seed-woo-demo` |
**Still not production-cutover:** FTP feed sync, daily credit ledger, live Stripe/Woo untested in this wave, and cutover gates in [cutover.md](cutover.md).
---
## Demo login (use this)
| | |
|---|---|
| Email | `demo@descrybe.local` (alias `demo@descrybe.test` also works) |
| Password | `DemoPass123!` |
| Company | **Local Demo Co** (`ee246275-dec0-4446-9e83-58d0c16c258a`) |
| Plan | **Enterprise** · ~1,000,000 AI credits · `can_use_ai=true` (restored after mock Stripe) |
| Platform admin | yes |
| Web | http://127.0.0.1:5174/login |
| API | http://127.0.0.1:8080 |
| Docs | http://127.0.0.1:5174/docs |
| Demo API key | `dk_demo_local_descrybe_test_key_v1` |
Do **not** use this account or key in production. Details: [demo-user.md](demo-user.md).
---
## Executive matrix
| Area | Verdict | Evidence doc |
|------|---------|--------------|
| Credits / billing | **PASS** (bugs fixed) | [billing-credits-audit.md](billing-credits-audit.md) |
| Analytics / usage | **PASS** (bugs fixed) | [analytics-audit.md](analytics-audit.md) |
| Stripe Checkout / portal / webhooks | **PASS** (mock + real path; demo Enterprise restored after mock) | [stripe-setup.md](stripe-setup.md) |
| Free tier gates | **PASS** | [free-tier-verify.md](free-tier-verify.md) · [free-tier.md](free-tier.md) |
| Tutorial (forced + skippable) | **PASS** | [tutorial.md](tutorial.md) |
| SEO / marketing copy | **PASS** | sibling SEO pass + `/pricing` polish |
| WooCommerce | **PASS** (seed path; live optional) | [woocommerce-demo.md](woocommerce-demo.md) |
| E2E feeds → process → export | **PASS** | [e2e-feeds-process-export.md](e2e-feeds-process-export.md) |
| E2E marketing (brand/SEO/campaign/email) | **PASS** | [e2e-marketing.md](e2e-marketing.md) |
| Processing UI | **PASS** | [e2e-processing.md](e2e-processing.md) |
| Admin / health / public API | **PASS** 20/20 | [api-surface-smoke.md](api-surface-smoke.md) |
| Security (Stripe webhook / SSRF) | **PASS** (hardened) | [security-notes.md](security-notes.md) |
| Settings / API keys / invites | **PASS** | sibling settings QA |
| Products / categories / attributes | **PASS** (UX fixes) | siblings |
| Export public URLs | **PASS** | sibling export QA |
| AI (Green Chat LAN) | **PASS** when `:8767` open | [ai-full-smoke.md](ai-full-smoke.md) · [green-chat-smoke.md](green-chat-smoke.md) |
---
## 1. Credits & billing
**Verdict:** Demo Enterprise wallet and Billing UI agree: **1,000,000 remaining / 0 used**. Free grants **0** AI credits.
### What was wrong
1. `/api/billing/usage` preferred stale `billing_cycles` rows → showed e.g. **22 credits used** while wallet was 1M/0.
2. Campaign AI swallowed `ConsumeCredits` errors (silent free AI).
3. SEO AI only checked `CanUseAI` (true on paid with empty wallet).
4. Billing page fake date-range + false “Out of credits” on Free.
### Fixes
- `UsageSummary` always uses live `credit_balances` for credits; range filters products/tokens only.
- `AssignPlan` closes open cycles and opens a fresh cycle; `ConsumeCredits` updates only open cycles.
- Campaign/SEO require `RemainingCredits ≥ 1`; debit after AI; map insufficient → **402**.
- Billing UI: real `?range=`, Free info banner, used/total + Enterprise “Unlimited” label.
### Smoke
```text
CREDITS plan=Enterprise total=1000000 used=0 rem=1000000
USAGE matches /auth/me
AFTER_FREE → rem=0 can_use_ai=false
AFTER_ENT → rem=1000000
```
Full write-up: [billing-credits-audit.md](billing-credits-audit.md).
---
## 2. Analytics & usage
**Verdict:** Tenant + admin meters match live Postgres for Local Demo Co.
| Meter | API | DB |
|-------|-----|-----|
| Wallet used / total | 0 / 1_000_000 | match |
| Products (all) | ~4326 | match |
| Input / export feeds | 12 / 5 | match |
| Plan cycle | Aug → Sep 2026 | `company_plans` |
Admin analytics now includes feed counts and real `ai_provider_mode` rollups (after API rebuild). Details: [analytics-audit.md](analytics-audit.md).
---
## 3. Stripe (purchase path)
**Verdict:** Checkout + Customer Portal + signed webhooks implemented. Local works in **mock** without a Stripe account.
### Configure for real
1. Set in **root** `.env` (never commit secrets; placeholders in `.env.example`):
| Variable | Purpose |
|----------|---------|
| `STRIPE_SECRET_KEY` | `sk_test_…` / `sk_live_…` |
| `STRIPE_WEBHOOK_SECRET` | `whsec_…` |
| `STRIPE_MOCK` | `false` for live |
| `STRIPE_PRICE_STARTER_MONTHLY` / `_YEARLY` | Price IDs |
| `STRIPE_PRICE_GROWTH_MONTHLY` / `_YEARLY` | Price IDs |
| `STRIPE_PRICE_BUSINESS_MONTHLY` / `_YEARLY` | Price IDs |
| `WEB_ORIGIN` | e.g. `https://app.example.com` (Checkout return URLs) |
2. Stripe Dashboard: Products + Prices for Starter / Growth / Business; enable Customer Portal.
3. Webhook → `https://<api-host>/api/webhooks/stripe`
Events: `checkout.session.completed`, `customer.subscription.created|updated|deleted`.
4. Local: `stripe listen --forward-to localhost:8080/api/webhooks/stripe` → paste `whsec_…`.
5. Apply migration `016_stripe_billing.sql` (`pwsh -File scripts/migrate.ps1`), restart API.
### Mock local QA
`STRIPE_MOCK=true``POST /api/billing/checkout` assigns plan + credits and returns `mock:true`. Empty secret alone no longer free-upgrades (security harden).
**Note:** Mock checkout on Local Demo Co temporarily moved the tenant off Enterprise (e.g. to Starter). Enterprise was **restored** afterward so demo stays on ~1M credits / Unlimited. Re-run `go run ./cmd/seed-demo` (or admin AssignPlan Enterprise) if mock Checkout is used again on the demo company.
Enterprise stays sales-led (no self-serve price). UI: `/plans`, `/billing`. Full guide: [stripe-setup.md](stripe-setup.md).
---
## 4. WooCommerce
**Verdict:** Product push + orders/reviews pull + campaign audiences work. Live shop optional; **seed path** covers demo without WordPress.
### Configure for real
1. WooCommerce → Settings → Advanced → REST API → Read/Write key.
2. Descrybe `/woocommerce` → enable sync → Store URL + `ck_…` / `cs_…` → Save → Test Connection.
3. Queue product sync, then Orders / Reviews; keep **worker** running.
4. Optional env (or UI only): `WOO_STORE_URL`, `WOO_CONSUMER_KEY`, `WOO_CONSUMER_SECRET`
(aliases `WOOCOMMERCE_*` also work). Encrypt at rest needs `CREDENTIALS_ENCRYPTION_KEY`.
5. SSRF: `http` allowed only for localhost/loopback; remote shops need https (or tunnel).
### No live shop?
```powershell
cd apps/api
$env:DATABASE_URL = "postgres://descrybe:descrybe@localhost:5433/descrybe?sslmode=disable"
go run ./cmd/seed-woo-demo -postgres $env:DATABASE_URL -company "Local Demo Co"
# or: make seed-woo
```
Seeds Demo Electronics products, 4 orders, 3 reviews, draft “purchased” campaign. Details: [woocommerce-demo.md](woocommerce-demo.md).
---
## 5. Tutorial
**Verdict:** Forced action tour with Skip / Pause / Resume / Restart. No free Continue on action steps.
- Progress: `localStorage` key `descrybe.tutorial.v2`
- 15 steps: welcome → standard fields → feeds map/auto-map/save/sync-sample → products → export → campaigns → done
- Advance only on real clicks (`data-tour`) or successful `tutorial.reportAction(…)`
- Header + dashboard: Start / Resume / Restart
Manual checklist: [tutorial.md](tutorial.md).
---
## 6. E2E results
### Feeds → map → process → export — **PASS**
| Check | Result |
|-------|--------|
| 12 input feeds mapped | PASS |
| Process job 5/5 steps | PASS (worker required) |
| Export XML/CSV generate | PASS (~4322 products) |
| Public token downloads | PASS |
| Mapping PUT nested `{key,mapping}` | Fixed this wave |
FTP/FTPS supplier sync still **400** (not supported). Migrated data already present so process/export work without re-sync. Details: [e2e-feeds-process-export.md](e2e-feeds-process-export.md).
### Marketing (brand / SEO / campaign / email / calendar) — **PASS** (14/14)
Against Green Chat `overloaded-local` @ `192.168.50.181:8767`:
| Step | Result |
|------|--------|
| Brand kit PUT/GET + tips | PASS |
| SEO `mode=ai` | PASS (~1s, brand terms, credits charged) |
| Campaign `mode=ai` + template | PASS |
| Email / campaign `dry_run` | PASS |
| Calendar prepare Black Friday | PASS (path fix: `/api/marketing/calendar/prepare`) |
Details: [e2e-marketing.md](e2e-marketing.md).
### Processing UI — **PASS**
Create → cancel (steps marked cancelled) → enhance_only via Green Chat (~1s). `/tasks` → 307 `/processing`. Details: [e2e-processing.md](e2e-processing.md).
### Free tier (while demo is Enterprise) — **PASS**
New register → Free / 0 credits / `can_use_ai=false`; AI-only jobs → **402**; normalize/full still runs with AI/EPREL skipped. Demo remains Enterprise. Details: [free-tier-verify.md](free-tier-verify.md).
### Admin / health / public API — **PASS** 20/20
Platform admin routes, `/healthz` `/readyz`, RapiDoc docs, OpenAPI, v1 CRUD smoke + 401 without key. Details: [api-surface-smoke.md](api-surface-smoke.md).
---
## 7. Other sibling fixes (this wave)
| Area | Outcome |
|------|---------|
| **Security** | Stripe webhook always verified when `STRIPE_WEBHOOK_SECRET` set; mock purchases need explicit `STRIPE_MOCK`; generic webhook errors; SSRF notes in [security-notes.md](security-notes.md) |
| **Billing / Plans UI** | Enterprise shows Unlimited; Checkout CTAs; no false Free out-of-credits banner |
| **Dashboard** | Clear CTAs (tutorial / process / feeds / BF); clickable credit widgets; dead upload section removed |
| **SEO / copy** | Plain ecommerce voice; SeoHead on privacy/terms/features; clearer empty states |
| **Products** | Search includes processed/raw names; CSV ingest via Feeds only; server-side sort |
| **Categories / attributes** | Attributes default `roots=1`; formula 404 UX fixed |
| **Settings** | API keys CRUD, invites, email link; Copy Key no longer copies useless prefix |
| **Export feeds** | Public routes mounted before session auth (401→404/405); absolute public URLs in UI |
| **Nav / a11y / tutorial targets** | `data-tour` preserved for tour |
---
## 8. Known gaps
| Gap | Notes |
|-----|-------|
| No daily **credit** ledger | Usage range filters products/tokens only; credit burn is wallet-level |
| FTP/FTPS feed sync | Not supported; sync returns 400 for those supplier URLs |
| Stripe live untested in this wave | Mock + unit/integration coverage; real Checkout needs keys above |
| Live Woo optional | Seed covers audiences; live sync needs a real store + worker |
| Email delivery | Prefer `force_dry_run` / `EMAIL_DRY_RUN`; stub SMTP test fails without a real listener |
| Cutover / production | Staging data OK; production cutover still blocked — see [cutover.md](cutover.md) · [go-live-checklist.md](go-live-checklist.md) |
| Migrated tenants | May hold legacy wallets; entitlements treat empty plan as Free-with-leftover-credits |
| Campaign/SEO charge after LLM | Wallet gated first; mid-flight race can still burn tokens then fail debit |
| Structured-descriptions / vector-categories | UI shells exist; backend still partial ([status-and-gaps.md](status-and-gaps.md)) |
| CI | Makefile + unit tests; no GitHub Actions yet |
---
## 9. How to re-verify locally
```powershell
# Postgres
cd f:\laragon\www\_MY\descrybe-v2
docker compose up -d # :5433
# Build gates
cd apps\api; go build ./...
cd ..\web; npm run check
# Runtime (separate terminals)
# scripts\run-api.ps1 → API :8080
# make worker / go run ./cmd/worker
# make web → :5174
# Login
# demo@descrybe.local / DemoPass123!
```
Spot-check:
1. `/billing` → 1M remaining, usage matches wallet
2. `/plans` → Upgrade (mock Checkout if `STRIPE_MOCK=true`)
3. Start tutorial → action steps have no Continue
4. `/feeds` → map → `/processing``/export-feeds` generate
5. `/campaigns` + `/seo` AI (needs Green Chat + `OPENAI_*`)
6. `/woocommerce` or `make seed-woo` → orders/reviews + audience
---
## 10. Related docs index
| Doc | Topic |
|-----|-------|
| [billing-credits-audit.md](billing-credits-audit.md) | Credits correctness |
| [analytics-audit.md](analytics-audit.md) | Usage / admin analytics |
| [stripe-setup.md](stripe-setup.md) | Stripe real + mock |
| [woocommerce-demo.md](woocommerce-demo.md) | Woo live + seed |
| [tutorial.md](tutorial.md) | Guided tour |
| [e2e-feeds-process-export.md](e2e-feeds-process-export.md) | Catalog pipeline E2E |
| [e2e-marketing.md](e2e-marketing.md) | Marketing AI E2E |
| [e2e-processing.md](e2e-processing.md) | Background tasks |
| [free-tier-verify.md](free-tier-verify.md) | Free vs Enterprise proof |
| [api-surface-smoke.md](api-surface-smoke.md) | Admin / docs / v1 |
| [demo-user.md](demo-user.md) | Credentials |
| [security-notes.md](security-notes.md) | AuthZ / webhooks / SSRF |
| [status-and-gaps.md](status-and-gaps.md) | Broader product gaps |
| [ai-full-smoke.md](ai-full-smoke.md) | Green Chat AI matrix |