writekit/internal/billing/billing_test.go
Josh b2b2a42ca9
Some checks failed
ci/woodpecker/push/build Pipeline failed
fix: enable CI tests with build tags and add billing tests
- Add build tag to studio/embed.go to exclude from test builds
- Create stub embed_testing.go for test builds
- Update CI pipeline to use -tags testing
- Add tests for webhook signature verification and status normalization

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 02:44:49 +02:00

185 lines
4.3 KiB
Go

package billing
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"testing"
)
func TestVerifyWebhook(t *testing.T) {
secret := "test-webhook-secret"
client := &LemonClient{webhookSecret: secret}
t.Run("valid signature", func(t *testing.T) {
payload := []byte(`{"meta":{"event_name":"subscription_created"}}`)
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(payload)
signature := hex.EncodeToString(mac.Sum(nil))
if !client.VerifyWebhook(payload, signature) {
t.Error("expected valid signature to pass verification")
}
})
t.Run("invalid signature", func(t *testing.T) {
payload := []byte(`{"meta":{"event_name":"subscription_created"}}`)
signature := "invalid-signature"
if client.VerifyWebhook(payload, signature) {
t.Error("expected invalid signature to fail verification")
}
})
t.Run("wrong secret", func(t *testing.T) {
payload := []byte(`{"meta":{"event_name":"subscription_created"}}`)
mac := hmac.New(sha256.New, []byte("different-secret"))
mac.Write(payload)
signature := hex.EncodeToString(mac.Sum(nil))
if client.VerifyWebhook(payload, signature) {
t.Error("expected signature with wrong secret to fail")
}
})
t.Run("empty secret", func(t *testing.T) {
clientNoSecret := &LemonClient{webhookSecret: ""}
payload := []byte(`{"test": true}`)
if clientNoSecret.VerifyWebhook(payload, "any-signature") {
t.Error("expected empty secret to fail verification")
}
})
t.Run("empty payload", func(t *testing.T) {
payload := []byte{}
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(payload)
signature := hex.EncodeToString(mac.Sum(nil))
if !client.VerifyWebhook(payload, signature) {
t.Error("expected empty payload with valid signature to pass")
}
})
}
func TestNormalizeStatus(t *testing.T) {
tests := []struct {
input string
expected string
}{
{"on_trial", "active"},
{"active", "active"},
{"paused", "past_due"},
{"past_due", "past_due"},
{"unpaid", "past_due"},
{"cancelled", "cancelled"},
{"expired", "cancelled"},
{"unknown_status", "unknown_status"},
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
got := normalizeStatus(tt.input)
if got != tt.expected {
t.Errorf("normalizeStatus(%q) = %q, want %q", tt.input, got, tt.expected)
}
})
}
}
func TestParseWebhookEvent(t *testing.T) {
client := &LemonClient{}
t.Run("valid event", func(t *testing.T) {
payload := []byte(`{
"meta": {
"event_name": "subscription_created",
"custom_data": {"tenant_id": "tenant-123", "user_id": "user-456"}
},
"data": {"id": "sub-789"}
}`)
event, err := client.ParseWebhookEvent(payload)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if event.Meta.EventName != "subscription_created" {
t.Errorf("EventName = %q, want %q", event.Meta.EventName, "subscription_created")
}
if event.Meta.CustomData["tenant_id"] != "tenant-123" {
t.Errorf("tenant_id = %q, want %q", event.Meta.CustomData["tenant_id"], "tenant-123")
}
})
t.Run("invalid JSON", func(t *testing.T) {
payload := []byte(`{invalid json}`)
_, err := client.ParseWebhookEvent(payload)
if err == nil {
t.Error("expected error for invalid JSON")
}
})
}
func TestGetSubscriptionData(t *testing.T) {
event := &WebhookEvent{
Data: []byte(`{
"id": "sub-123",
"attributes": {
"customer_id": 456,
"variant_name": "Pro Monthly",
"user_email": "test@example.com",
"status": "active"
}
}`),
}
data, err := event.GetSubscriptionData()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if data.ID != "sub-123" {
t.Errorf("ID = %q, want %q", data.ID, "sub-123")
}
if data.Attributes.CustomerID != 456 {
t.Errorf("CustomerID = %d, want %d", data.Attributes.CustomerID, 456)
}
if data.Attributes.Status != "active" {
t.Errorf("Status = %q, want %q", data.Attributes.Status, "active")
}
}
func TestGetOrderData(t *testing.T) {
event := &WebhookEvent{
Data: []byte(`{
"id": "order-123",
"attributes": {
"user_name": "John Doe",
"user_email": "john@example.com",
"total_usd": 500
}
}`),
}
data, err := event.GetOrderData()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if data.ID != "order-123" {
t.Errorf("ID = %q, want %q", data.ID, "order-123")
}
if data.Attributes.TotalUsd != 500 {
t.Errorf("TotalUsd = %d, want %d", data.Attributes.TotalUsd, 500)
}
}