Files
member-console/internal/integrations/stripe/workflows/webhook_subscription_test.go
T
cgalo5758 88db730fcc Add dual licensing and SPDX headers
Introduce a commercial license option alongside AGPL-3.0-only, require a
CLA for contributors, and document the terms in COMMERCIAL.md and
NOTICE. Add a script to stamp SPDX headers on Go files and apply it
across the tree.
2026-09-06 02:29:42 -05:00

99 lines
2.9 KiB
Go

// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Commercial
// SPDX-FileCopyrightText: 2025-2026 Christian Galo
package workflows
import (
"database/sql"
"encoding/json"
"log/slog"
"os"
"testing"
_ "github.com/jackc/pgx/v5/stdlib"
)
// testDB returns a database connection for integration tests, assuming an
// already-migrated database. Skips the test if TEST_DATABASE_URL is not set.
// Matches the pgx + TEST_DATABASE_URL convention used by the rest of this
// directory (customer_mapping_test.go) and the wider repo.
func testDB(t *testing.T) *sql.DB {
t.Helper()
dsn := os.Getenv("TEST_DATABASE_URL")
if dsn == "" {
t.Skip("TEST_DATABASE_URL not set, skipping integration test")
}
db, err := sql.Open("pgx", dsn)
if err != nil {
t.Fatalf("open db: %v", err)
}
t.Cleanup(func() { db.Close() })
return db
}
// TestCheckoutSessionPayloadParsing verifies the thin checkout handler reads
// only the ids it needs (mode + subscription) and ignores everything else —
// including line_items, which Stripe omits and which we no longer trust.
func TestCheckoutSessionPayloadParsing(t *testing.T) {
raw := `{
"id": "cs_test_123",
"mode": "subscription",
"customer": "cus_abc",
"subscription": "sub_xyz",
"metadata": {"billing_account_id": "ba-uuid-here"},
"line_items": []
}`
var payload webhookCheckoutSessionPayload
if err := json.Unmarshal([]byte(raw), &payload); err != nil {
t.Fatalf("failed to unmarshal: %v", err)
}
if payload.Mode != "subscription" {
t.Errorf("expected mode subscription, got %s", payload.Mode)
}
if payload.Subscription != "sub_xyz" {
t.Errorf("expected subscription sub_xyz, got %s", payload.Subscription)
}
}
// TestSubscriptionPayloadParsing verifies the subscription handler extracts only
// the subscription id; status/items/periods come from the API at reconcile time.
func TestSubscriptionPayloadParsing(t *testing.T) {
raw := `{
"id": "sub_test",
"customer": "cus_test",
"status": "canceled",
"items": [{"id": "si_1", "price_id": "price_1", "quantity": 2}]
}`
var parsed webhookSubscriptionIDPayload
if err := json.Unmarshal([]byte(raw), &parsed); err != nil {
t.Fatalf("unmarshal failed: %v", err)
}
if parsed.ID != "sub_test" {
t.Errorf("expected id sub_test, got %s", parsed.ID)
}
}
// TestDispatchEvent_RoutesSubscriptionEvents documents the event types the
// dispatcher routes to type-specific handlers.
func TestDispatchEvent_RoutesSubscriptionEvents(t *testing.T) {
acts := &WebhookActivities{Logger: slog.Default()}
handledPrefixes := []string{
"checkout.session.completed",
"customer.subscription.created",
"customer.subscription.updated",
"customer.subscription.deleted",
"customer.created",
"product.created",
"price.created",
}
for _, et := range handledPrefixes {
t.Run(et, func(t *testing.T) {
// Routing logic must not panic when constructing the handler set.
_ = acts
})
}
}