Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
5.6 KiB
E2E: Feeds → Map → Process → Export (Local Demo Co)
When: 2026-08-04
Tenant: Local Demo Co (ee246275-dec0-4446-9e83-58d0c16c258a)
Web: http://127.0.0.1:5174 (not 5173 — that port may be another Vite app)
API: http://127.0.0.1:8080
DB: postgres://descrybe:***@localhost:5433/descrybe
Credentials
demo@descrybe.local |
|
| Password | DemoPass123! |
| API key | dk_demo_local_descrybe_test_key_v1 |
Session login needs CSRF: GET any /api/* route to set descrybe_csrf, then POST /api/auth/login with header X-CSRF-Token.
Authorization: Bearer dk_demo_local_descrybe_test_key_v1
Pipeline (what “done” looks like)
input feeds → PUT mappings → (sync*) → POST process → export generate → public XML/CSV
* Live FTP/FTPS supplier URLs return 400 ftp/ftps feed sync is not supported yet. Local Demo Co already has migrated raw/processed rows, so process + export work without re-sync.
Verified counts (after this E2E)
| Surface | Result |
|---|---|
| Input feeds | 12 (active / mapped) |
| Feed mappings | Present (e.g. Vama Trade 13 fields) |
| Processed products | ~4330 (GET /api/v1/products) |
| Raw / unprocessed | 23744 (kind=raw or kind=unprocessed) |
| Export generate XML | 4322 products, status=completed |
| Export generate CSV | 4322 products, status=completed |
| Process job | 5/5 completed (normalize → specs → fill → eprel skipped → ai) |
API path (copy/paste)
1. List feeds + mappings
$H = @{ Authorization = "Bearer dk_demo_local_descrybe_test_key_v1"; "Content-Type" = "application/json" }
Invoke-RestMethod http://127.0.0.1:8080/api/v1/feeds -Headers $H
Invoke-RestMethod http://127.0.0.1:8080/api/v1/feeds/b5fc7c4c-830b-4d6e-a90c-9b72800a89a1/mappings -Headers $H
2. Save mappings (flips unmapped → mapped, sets options.item_path)
Body uses the dashboard shape { key, mapping: { fieldName, xpath } }:
$body = @{
mappings = @(
@{ key = "Export/Item/name"; mapping = @{ fieldName = "name"; xpath = "Export/Item/name" } }
@{ key = "Export/Item/EAN"; mapping = @{ fieldName = "gtin"; xpath = "Export/Item/EAN" } }
)
} | ConvertTo-Json -Depth 6
Invoke-RestMethod -Method PUT -Uri http://127.0.0.1:8080/api/v1/feeds/<feedId>/mappings -Headers $H -Body $body
3. Process sample raw products
$raw = Invoke-RestMethod "http://127.0.0.1:8080/api/v1/products?kind=raw&limit=5" -Headers $H
$ids = @($raw.products | ForEach-Object { $_.id })
$jobBody = @{ raw_product_ids = $ids; processing_type = "normalize" } | ConvertTo-Json
$job = Invoke-RestMethod -Method POST -Uri http://127.0.0.1:8080/api/v1/process -Headers $H -Body $jobBody
# poll GET /api/v1/process/{id} until status=completed (worker must be running)
Dashboard alias: POST /api/processing/jobs (same body).
4. Export XML + CSV
# Example feed IDs on Local Demo Co
$xmlId = "d52bc02c-77ee-4cd5-bcd9-fde652133f38" # XML - Example
$csvId = "d6ccb83e-7c76-4328-8b60-820503bd24d0" # CSV - Example
Invoke-RestMethod -Method POST -Uri "http://127.0.0.1:8080/api/v1/export-feeds/$xmlId/generate" -Headers $H -Body "{}"
Invoke-RestMethod -Method POST -Uri "http://127.0.0.1:8080/api/v1/export-feeds/$csvId/generate" -Headers $H -Body "{}"
# Public downloads (token from export feed)
# GET /api/public/export-feeds/{public_token}.xml
# GET /api/public/export-feeds/{public_token}.csv
Example tokens after seed/migrate: XML f2321bf2d18609b7abf5ea0f0f9fbb86, CSV 7fdea27d7bc9e21aec6403e24ccb3c8a.
UI path
| Step | URL |
|---|---|
| Login | http://127.0.0.1:5174/login |
| Feeds | http://127.0.0.1:5174/feeds |
| Mapping | http://127.0.0.1:5174/feeds/{feedId}/mapping |
| Products | http://127.0.0.1:5174/products (Processed / Unprocessed tabs) |
| Processing | http://127.0.0.1:5174/processing |
| Export feeds | http://127.0.0.1:5174/export-feeds |
Session-cookie calls go through the Vite proxy (/api/... → :8080). Same CSRF rules as direct API.
SSR smoke (logged in): /dashboard, /feeds, /products, /export-feeds, /processing, /feeds/.../mapping → 200.
Blockers fixed in this pass
| Issue | Fix |
|---|---|
parseMappings ignored UI {key,mapping} rows → sync would apply zero fields |
Unwrap nested mapping + key as xpath/source in internal/feeds/mapping.go |
PUT mappings left feed status=unmapped |
Derive item_path from xpath parents; always set unmapped→mapped when fields saved (service.go / extract_schema.go) |
kind=unprocessed ignored (returned processed) |
Alias to raw list in catalog_handlers.go |
| Empty-looking processed tab | Data was present (~4.3k); use Unprocessed/kind=raw for inventory; API totals verified |
Known limits
- FTP/FTPS sync not supported:
POST .../syncand.../sync-process-samplereturn 400 for supplier FTP URLs (Vama, ComTrade, …). - Worker required for process jobs (
scripts/run-api.ps1 workerorgo run ./cmd/worker). - Public export HEAD may 405; use GET.
- Web port is 5174 in local docs/smoke; confirm with the Descrybe title before testing.
Verification commands
cd apps/api
go test ./internal/feeds ./internal/httpapi -count=1
go build -o bin/api.exe ./cmd/api
cd ../web
npm run check
Related
- demo-user.md — credentials / API key
- qa-local-demo.md — broader Local Demo Co checklist
- local-smoke-results.md — prior smoke matrix