Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
4.0 KiB
Free tier verification (while demo is Enterprise)
Proof that cmd/seed-demo assigning Enterprise to Local Demo Co does not break forever-Free packaging or gates for new signups.
Date: 2026-08-04
Related: free-tier.md, demo-user.md
Expected matrix
| Check | Free signup | Demo (Enterprise) |
|---|---|---|
| Plan name | Free |
Enterprise |
monthly_credits |
0 | 1000000 |
| Wallet remaining | 0 | ≈1M |
can_use_ai |
false |
true |
can_use_eprel |
false |
true |
AI-only job (title / description / enhance) |
402 ai_requires_upgrade |
Allowed (credits) |
| EPREL-only job | 402 eprel_requires_upgrade |
Allowed |
| Full / normalize process | 202 → completes; ai_enhance + eprel skipped |
Full AI path |
Live smoke (2026-08-04)
API: http://127.0.0.1:8080 · Postgres: localhost:5433
1. Public plan rows after Enterprise seed
SELECT name, monthly_credits, max_products, is_custom
FROM plans WHERE lower(name) IN ('free','enterprise') ORDER BY name;
| name | monthly_credits | max_products | is_custom |
|---|---|---|---|
| Free | 0 | 100 | f |
| Enterprise | 1000000 | null | t |
2. New Free signup path
POST /api/auth/register → 201 → ProvisionFreePlan.
Observed (/api/auth/me → credits):
plan.name=Free,monthly_credits=0remaining_credits=0,can_use_ai=false,can_use_eprel=false,is_free_plan=true
3. 402 on AI (Free company with a raw product)
| Request | Status | Body code |
|---|---|---|
POST /api/processing/jobs processing_type=title |
402 | ai_requires_upgrade |
processing_type=description |
402 | ai_requires_upgrade |
processing_type=enhance |
402 | ai_requires_upgrade |
processing_type=eprel_only |
402 | eprel_requires_upgrade |
Example body:
{
"code": "ai_requires_upgrade",
"error": "ai features require a paid plan or AI credits — upgrade your plan or add AI credits",
"upgrade_url": "/pricing"
}
4. Non-AI process works
POST /api/processing/jobs processing_type=full → 202, job completed:
normalize/parse_specs/fill_fields→doneeprel→skipped(entitlement_can_use_eprel)ai_enhance→skipped(entitlement_can_use_ai)
5. Demo still Enterprise
demo@descrybe.local → /api/billing/credits: plan.name=Enterprise, can_use_ai=true, remaining ≈ 1M.
Regression fixed
ProvisionFreePlan previously fell back to SELECT id FROM plans ORDER BY id LIMIT 1 when Free was missing. After migration + seed-demo, Enterprise can have a lower id than Free, so that fallback could assign Enterprise to a new signup. Fallback removed — only the Free row is assigned (best-effort no-op if Free is absent after EnsureDefaultPlans).
seed-demo now fails closed if Free monthly_credits ≠ 0 or max_products ≠ 100 after EnsureDefaultPlans.
Automated checks
cd apps/api
go test ./internal/billing/ ./internal/processing/ -count=1
go build ./...
# Optional DB smokes (requires migrated + seed-demo DB):
$env:DATABASE_URL = "postgres://descrybe:descrybe@localhost:5433/descrybe?sslmode=disable"
go test ./cmd/seed-demo/ -count=1 -v
Unit coverage:
TestComputeEntitlements— Free 0 credits → no AI/EPRELTestDefaultPublicPlansFreeZeroCredits— Free packaging locked at 0 / 100 SKUsTestDefaultPublicPlansEnterpriseUnlimited— Enterprise 1M / unlimited SKUsTestFreePlanUnaffectedByEnterpriseSeed— live Free row after demo seedTestLocalDemoCoEnterpriseCredits— demo company still Enterprise
Re-run checklist
go run ./cmd/seed-demo(demo → Enterprise; Free row still 0 credits)- Register a fresh user → Free / 0 credits /
can_use_ai=false - Start
titlejob → 402ai_requires_upgrade - Start
fulljob → 202; AI/EPREL steps skipped - Login as demo → Enterprise credits unchanged