Initial commit of Descrybe v2 without local scratch artifacts.

Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
This commit is contained in:
2026-08-09 22:47:43 +02:00
commit 8580c996c3
1285 changed files with 325780 additions and 0 deletions
@@ -0,0 +1,259 @@
package billing
import (
"context"
"encoding/json"
"fmt"
"os"
"testing"
"time"
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgxpool"
)
func openStripeMockPool(t *testing.T) (*pgxpool.Pool, context.Context, context.CancelFunc) {
t.Helper()
dsn := os.Getenv("DATABASE_URL")
if dsn == "" {
t.Skip("DATABASE_URL not set")
}
ctx, cancel := context.WithTimeout(context.Background(), 45*time.Second)
pg, err := pgxpool.New(ctx, dsn)
if err != nil {
cancel()
t.Fatal(err)
}
t.Cleanup(func() { pg.Close() })
return pg, ctx, cancel
}
func seedStripeMockCompany(t *testing.T, pg *pgxpool.Pool, ctx context.Context) uuid.UUID {
t.Helper()
companyID := uuid.New()
_, err := pg.Exec(ctx, `INSERT INTO companies (id, name) VALUES ($1, $2)`, companyID, "stripe-mock-"+companyID.String()[:8])
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
cleanupCtx, cleanupCancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cleanupCancel()
_, _ = pg.Exec(cleanupCtx, `DELETE FROM stripe_webhook_events WHERE company_id = $1`, companyID)
_, _ = pg.Exec(cleanupCtx, `DELETE FROM companies WHERE id = $1`, companyID)
})
return companyID
}
func creditTotal(t *testing.T, pg *pgxpool.Pool, ctx context.Context, companyID uuid.UUID) int {
t.Helper()
var total int
err := pg.QueryRow(ctx, `SELECT COALESCE(total_credits, 0) FROM credit_balances WHERE company_id = $1`, companyID).Scan(&total)
if err != nil {
return 0
}
return total
}
func activePlanName(t *testing.T, pg *pgxpool.Pool, ctx context.Context, companyID uuid.UUID) string {
t.Helper()
var name string
err := pg.QueryRow(ctx, `
SELECT lower(p.name) FROM company_plans cp
JOIN plans p ON p.id = cp.plan_id
WHERE cp.company_id = $1 AND cp.is_active = true
ORDER BY cp.created_at DESC LIMIT 1`, companyID).Scan(&name)
if err != nil {
return ""
}
return name
}
func TestMockCheckoutPlanAssignsAndGrants(t *testing.T) {
pg, ctx, cancel := openStripeMockPool(t)
defer cancel()
companyID := seedStripeMockCompany(t, pg, ctx)
billing := &Service{Pool: pg}
if err := billing.EnsureDefaultPlans(ctx); err != nil {
t.Fatal(err)
}
s := &StripeService{
Pool: pg,
Billing: billing,
Cfg: StripeConfig{ForceMock: true, WebOrigin: "http://localhost:5174"},
}
res, err := s.CreateCheckoutSession(ctx, companyID, "mock@example.com", "Mock Co", CheckoutRequest{Plan: "starter", Term: "monthly"})
if err != nil {
t.Fatal(err)
}
if !res.Mock || !res.Applied {
t.Fatalf("expected mock applied checkout, got %#v", res)
}
if activePlanName(t, pg, ctx, companyID) != "starter" {
t.Fatalf("plan=%q want starter", activePlanName(t, pg, ctx, companyID))
}
want := MonthlyCreditsForPlan("Starter", 0)
if got := creditTotal(t, pg, ctx, companyID); got != want {
t.Fatalf("credits=%d want %d", got, want)
}
var subID *string
_ = pg.QueryRow(ctx, `
SELECT stripe_subscription_id FROM company_plans
WHERE company_id = $1 AND is_active = true`, companyID).Scan(&subID)
if subID == nil || *subID == "" {
t.Fatal("mock checkout must set stripe_subscription_id")
}
}
func TestMockCreditPackCheckoutGrants(t *testing.T) {
pg, ctx, cancel := openStripeMockPool(t)
defer cancel()
companyID := seedStripeMockCompany(t, pg, ctx)
billing := &Service{Pool: pg}
s := &StripeService{
Pool: pg,
Billing: billing,
Cfg: StripeConfig{ForceMock: true, WebOrigin: "http://localhost:5174"},
}
before := creditTotal(t, pg, ctx, companyID)
res, err := s.CreateCreditPackCheckout(ctx, companyID, "mock@example.com", "Mock Co", "small")
if err != nil {
t.Fatal(err)
}
if !res.Mock || !res.Applied {
t.Fatalf("expected mock applied pack, got %#v", res)
}
pack, _ := CreditPackByID("small")
if got := creditTotal(t, pg, ctx, companyID); got != before+pack.Credits {
t.Fatalf("credits=%d want %d", got, before+pack.Credits)
}
}
func TestWebhookClaimIdempotentAndCreditGrant(t *testing.T) {
pg, ctx, cancel := openStripeMockPool(t)
defer cancel()
companyID := seedStripeMockCompany(t, pg, ctx)
billing := &Service{Pool: pg}
s := &StripeService{
Pool: pg,
Billing: billing,
Cfg: StripeConfig{ForceMock: true}, // unsigned allowed locally; no webhook secret
}
eventID := "evt_mock_credit_" + companyID.String()[:8]
payload, err := json.Marshal(map[string]any{
"id": eventID,
"type": "checkout.session.completed",
"data": map[string]any{
"object": map[string]any{
"id": "cs_mock_1",
"client_reference_id": companyID.String(),
"metadata": map[string]string{
"kind": "credit_pack",
"pack": "tiny",
"credits": "9999", // must be ignored for catalog pack
},
},
},
})
if err != nil {
t.Fatal(err)
}
before := creditTotal(t, pg, ctx, companyID)
if err := s.HandleWebhook(ctx, payload, ""); err != nil {
t.Fatal(err)
}
pack, _ := CreditPackByID("tiny")
if got := creditTotal(t, pg, ctx, companyID); got != before+pack.Credits {
t.Fatalf("after grant credits=%d want %d", got, before+pack.Credits)
}
mid := creditTotal(t, pg, ctx, companyID)
if err := s.HandleWebhook(ctx, payload, ""); err != nil {
t.Fatal(err)
}
if got := creditTotal(t, pg, ctx, companyID); got != mid {
t.Fatalf("idempotent claim must not double-grant: got %d mid %d", got, mid)
}
}
func TestWebhookSubscriptionDeletedDowngrades(t *testing.T) {
pg, ctx, cancel := openStripeMockPool(t)
defer cancel()
companyID := seedStripeMockCompany(t, pg, ctx)
billing := &Service{Pool: pg}
if err := billing.EnsureDefaultPlans(ctx); err != nil {
t.Fatal(err)
}
s := &StripeService{
Pool: pg,
Billing: billing,
Cfg: StripeConfig{ForceMock: true},
}
if _, err := s.CreateCheckoutSession(ctx, companyID, "mock@example.com", "Mock Co", CheckoutRequest{Plan: "plus", Term: "monthly"}); err != nil {
t.Fatal(err)
}
if activePlanName(t, pg, ctx, companyID) != "plus" {
t.Fatalf("precondition plan=%q", activePlanName(t, pg, ctx, companyID))
}
eventID := "evt_mock_del_" + companyID.String()[:8]
payload, err := json.Marshal(map[string]any{
"id": eventID,
"type": "customer.subscription.deleted",
"data": map[string]any{
"object": map[string]any{
"id": "sub_mock_del",
"customer": "cus_mock",
"status": "canceled",
"metadata": map[string]string{"company_id": companyID.String()},
},
},
})
if err != nil {
t.Fatal(err)
}
if err := s.HandleWebhook(ctx, payload, ""); err != nil {
t.Fatal(err)
}
if got := activePlanName(t, pg, ctx, companyID); got != "free" {
t.Fatalf("after delete plan=%q want free", got)
}
}
func TestWebhookVerifyStillRequiredWithSecretUnderForceMock(t *testing.T) {
pg, ctx, cancel := openStripeMockPool(t)
defer cancel()
companyID := seedStripeMockCompany(t, pg, ctx)
secret := "whsec_mock_local"
s := &StripeService{
Pool: pg,
Billing: &Service{Pool: pg},
Cfg: StripeConfig{ForceMock: true, WebhookSecret: secret},
}
payload, err := json.Marshal(map[string]any{
"id": "evt_signed_" + companyID.String()[:8],
"type": "ping",
"data": map[string]any{"object": map[string]any{}},
})
if err != nil {
t.Fatal(err)
}
if err := s.HandleWebhook(ctx, payload, ""); err == nil {
t.Fatal("unsigned must fail when webhook secret set")
}
sig := signStripePayload(t, secret, payload)
if err := s.HandleWebhook(ctx, payload, sig); err != nil {
t.Fatalf("valid signature under ForceMock: %v", err)
}
// Second delivery is an idempotent no-op.
if err := s.HandleWebhook(ctx, payload, sig); err != nil {
t.Fatal(err)
}
var n int
if err := pg.QueryRow(ctx, `SELECT COUNT(*) FROM stripe_webhook_events WHERE event_id = $1`,
fmt.Sprintf("evt_signed_%s", companyID.String()[:8])).Scan(&n); err != nil {
t.Fatal(err)
}
if n != 1 {
t.Fatalf("claim rows=%d want 1", n)
}
}