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.
99 lines
2.9 KiB
Go
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
|
|
})
|
|
}
|
|
}
|