Seed Catalog
Every database seeder in the platform — the framework (Seeder interface, runner, seed all/fresh/list/info, the load-bearing cache flush, and the absence of any prod guard), all 41 registered seeders in dependency order, the 11 unregistered test-invoked orchestration seeders that wire vendors→tests, the no-op Grasshopper D1 seed, and the committed dev credentials worth rotating.
Every seeder, what it creates, the run order, and the gotchas. This is the fixture layer the Test Catalog runs against. Reminder from the standing rules: you write seeders/migrations — the user applies them. Never run seed/migrate yourself.
The framework
The Seeder interface (database/seeder/seeder.go:14) has three methods — no priority, no declared dependencies:
type Seeder interface {
Name() string
Run(ctx, repository) error
ShouldRun(ctx, repository) (bool, error) // idempotency guard
}Order = registration order. InitializeSeeder (runner.go:13-78) is the canonical list; RegisterSeeder just appends — there is no sort. RunAll (seeder.go:69) flushes the cache, then per seeder calls ShouldRun → Run, fail-fast (any error aborts the whole run). There is no wrapping transaction across seeders, and most seeders don't wrap their own work either (the eSIM orchestration seeders are the exceptions).
CLI (main.go:334 handleSeed)
| Command | Effect |
|---|---|
seed all | InitializeSeeder(...).RunAll — everything registered |
seed fresh | InitializeFreshSeeder — reference data + wallets only, preserves clients & vendors (runner.go:84) |
seed list | list registered seeders |
seed info | per-seeder "needs to run / already run" |
seed <name> | run one specific seeder; unknown → seeder <name> not found |
Idempotency
Two ShouldRun patterns: row-count guard (SELECT COUNT(*), run only when 0/mismatch — countries, currencies, blacklists, order-item-responses, test-configs) and lookup-by-key (admin user, credentials, vendors/products by code, orchestration by product name). Writes are guarded inserts / upserts — no seeder truncates — so re-running is generally a safe no-op.
Two load-bearing gotchas
- The cache flush is load-bearing.
RunAll/RunSeederwipe the whole cache (DeleteByPattern(ctx,"*"),seeder.go:41) before seeding, because repo reads hit cache first — aftermigrate fresh, stale cache entries make existence checks return phantom "already exists" and seeders silently skip INSERTs. A flush failure is logged but non-fatal, so a "successful" run can still leave rows missing. - No prod guard anywhere. A grep for
prod/APP_ENV/IsProdacross all seeders finds nothing — no seeder refuses to run on prod. Safety is pure convention: operators useseed fresh(which excludes clients/vendors/orders/payouts) on shared envs, neverseed all.
Registered seeders (run by seed all), in dependency order
41 registered in InitializeSeeder. Grouped by purpose; order is the actual run order.
Reference data (idempotent, prod-safe)
timezones (~431 from data/timezones.json) → countries (~245) → currencies (~156) → categories (10) → subcategories (16) → forex_values (~22 pairs). All load from data/*.json.
Config (clients, users, vendors)
admin_user (creates admin + the system_admin user that order/payout seeders use for transactions) → clients (29 from data/clients.json) → client_users (bcrypt password123) → tenant_connections → client_credentials → webhook_urls → users → wallets (USD/INR/AED per client) → products (57 from data/products.json; test products ids 13/14/15).
Vendor seeders (each writes vendors + vendor_attributes): vendors (Wupex WPX, iRewardify IRW, EpinForce, Grasshopper) → seagm_vendor → dtone_vendor (service_id 1) → dtone_esim_vendor (service_id 13 + webhook token) → runa_vendor → neo_vendor → octopus_vendor → octopus_topup_vendor (OCTO_TOPUP) → octopus_esim_vendor (OCTO_ESIM).
Then (order matters — after vendors and clients): client_vendor_blacklists, client_product_blacklists, vendor_products, vendor_wallets, client_product_details.
Fixtures (destructive on prod)
inventory (1000 voucher items) → orders (sample orders + items + responses inline) → payout providers merit_provider (MERIT) + ledig_provider (LEDIG) → beneficiaries → payouts → files.
Migration/backfill & test config
shopify_product_mapping_backfill (idempotent, UNIQUE-guarded — safe to leave registered) → test_configs (12 from data/test_configs.json, gated on client_id=10, runs last).
Unregistered seeders (test-invoked or disabled)
11 seeder files exist but are not in runner.go — invoked directly by orchestration tests, or disabled:
- Orchestration fixtures (test-invoked):
dtone_topup_orchestration,dtone_esim_orchestration,seagm_topup_orchestration,octopus_topup_orchestration,octopus_esim_orchestration, and the voucher-sideruna_vendor_products,neo_vendor_products,wupex_vendor_products,irewardify_vendor_products. - Inline (done by the
ordersseeder):order_items,order_item_responses. - Disabled stress seeders:
bulk_orders(1000/wallet,runner.go:60),large_inventory(10,000 items, commented outrunner.go:58).
Orchestration seeders — where seeders meet tests
Orchestration fixtures are no longer auto-seeded (runner.go:54); after a fresh DB, topup/eSIM/voucher products come only from vendor catalog sync, and each orchestration test calls its seeder in setup. Each wires: a pre-seeded parent vendor → one deterministic product+variant → credentials pointing at the mock server.
| Seeder | Vendor | Product / denom | Mock target | Credentials |
|---|---|---|---|---|
dtone_topup_orchestration | DTONE | Airtel India topup, $0.15 | mocky dtone_products id 8141 | dtone_test/dtone_secret_123 |
dtone_esim_orchestration | DTONE_ESIM | 1GB/7d Albania, $0.85 | mocky dtone_products id 70000 | self-heals to same |
seagm_topup_orchestration | SEAGM | PUBG UC gaming, $0.99 | mocky seagm_recharge_types id 1 | SEAGM vendor sk_test_... |
octopus_topup_orchestration | OCTO_TOPUP | Mobile topup 1:1, $0.15 | in-process octopusfake | env=sandbox |
octopus_esim_orchestration | OCTO_ESIM | eSIM 1:1, $0.85 | in-process octopusfake | env=sandbox |
runa/neo/wupex/irewardify_vendor_products | RUNA/NEO/WPX/IRW | voucher denoms (open 5–500 / fixed 10) | mocky localhost:8788 hosts | seeded by vendor seeders |
This coupling is fragile in one place: each voucher orchestration suite self-seeds its product because the shared vendor_product seeder randomly assigns variants to only the first 10 vendors, and Runa/others were added later — if seeder ordering changes, suites break.
Grasshopper D1 seed — a no-op
frontend/grasshopper/drizzle/seed.sql is verified to contain only SELECT 1+1 AS result;. Grasshopper's voucher_types catalog is repopulated by the Octopus → Grasshopper sync, not by a seed file (provisioning).
db:seed→ runs the no-op SQL.db:reset→ local wipe + migrate + no-op seed.db:reset:remote→reset-remote.shdeletes+recreates the prod D1 with no confirmation — the most destructive item here — then migrates, no seed.db:reset:sandbox→reset-sandbox.sh, guarded (refuses if the DB name lacks "sandbox"), then instructs re-running the sync.
The mock rows the orchestration seeders point at live in mocky-balboa's own seed.sql/seed.ts (frontend/mocky-balboa/), the source of truth for those dtone_products/seagm_recharge_types ids.
Committed dev credentials (rotation review)
All appear to be dev/mock values but are in git:
| Secret | Where |
|---|---|
Grasshopper vendor api_key Pnw0ohHAJoyqi254QRdBmDTFzAPUYk (real-shaped — verify first) | vendor_seeder.go:235 |
Runa webhook secret whsec_MfKQ9r8tZlF3q+bJvR2GkXcPdS5TmYwA6HnV1xJ4eUo= + api key xx_test_runa_playground_123 | runa_vendor_seeder.go:92,96 |
DTOne dtone_test/dtone_secret_123, SEAGM sk_test_seagm_secret_key_12345, Neo neo_local_*, Wupex test-api-key, EpinForce/iRewardify tokens | vendor seeders |
Admin default password WmQIcJ5XgrJ3OsZp9VIGvVkKPqFrkw (#nosec) | admin_user_seeder.go:73 |
Portal/API password123 / demo123; JWT fallback your-super-secret-key | client_credentials_seeder.go:20,145 |
These join the broader committed-secrets finding. Because no seeder has a prod guard, running a vendor/credential seeder against a shared env would overwrite live credentials with localhost mock values — the practical danger, beyond the fixtures simply inserting junk rows.
Key files
- Framework:
database/seeder/{seeder,runner}.go; CLImain.go:96,265,334 - Data:
database/seeder/data/*.json(timezones/countries/currencies/clients/products/test_configs) - Orchestration:
database/seeder/*_orchestration_seeder.go,*_vendor_product_seeder.go - Grasshopper:
frontend/grasshopper/drizzle/seed.sql,scripts/reset-{remote,sandbox}.sh; mock datafrontend/mocky-balboa/
Mocky-Balboa — Vendor Mock
The Cloudflare Worker (Hono + D1) that impersonates all seven third-party vendor APIs so the orchestration and contract tests run without touching live vendors — its per-vendor emulation, async-delivery modes (Svix/signed/unsigned webhooks vs poll), the chaos engine and its MustHaveFired self-deletion contract, the D1 schema and seeded credentials, deploy, and the shared-state fragility.
Memory Appendix
Verbatim dump of every persistent-memory file Claude accumulated on the Octopus project.