Go · 4704 bytes Raw Blame History
1 // SPDX-License-Identifier: AGPL-3.0-or-later
2
3 package stripebilling
4
5 import (
6 "errors"
7 "fmt"
8 "testing"
9
10 stripeapi "github.com/stripe/stripe-go/v85"
11 "github.com/stripe/stripe-go/v85/webhook"
12 )
13
14 func TestNewValidatesRequiredConfig(t *testing.T) {
15 t.Parallel()
16 if _, err := New(Config{}); !errors.Is(err, ErrSecretKeyRequired) {
17 t.Fatalf("New without secret key: got %v", err)
18 }
19 if _, err := New(Config{SecretKey: "sk_test_123"}); !errors.Is(err, ErrWebhookSecretRequired) {
20 t.Fatalf("New without webhook secret: got %v", err)
21 }
22 if _, err := New(Config{SecretKey: "sk_test_123", WebhookSecret: "whsec_123"}); !errors.Is(err, ErrTeamPriceRequired) {
23 t.Fatalf("New without price id: got %v", err)
24 }
25 }
26
27 func TestSupportsProReflectsConfig(t *testing.T) {
28 t.Parallel()
29 teamOnly, err := New(Config{
30 SecretKey: "sk_test_123",
31 WebhookSecret: "whsec_123",
32 TeamPriceID: "price_team",
33 })
34 if err != nil {
35 t.Fatalf("New team-only: %v", err)
36 }
37 if teamOnly.SupportsPro() {
38 t.Errorf("SupportsPro should be false when ProPriceID empty")
39 }
40
41 withPro, err := New(Config{
42 SecretKey: "sk_test_123",
43 WebhookSecret: "whsec_123",
44 TeamPriceID: "price_team",
45 ProPriceID: "price_pro",
46 })
47 if err != nil {
48 t.Fatalf("New with pro: %v", err)
49 }
50 if !withPro.SupportsPro() {
51 t.Errorf("SupportsPro should be true when ProPriceID set")
52 }
53 }
54
55 func TestNormalizeSubjectLegacyOrgOnly(t *testing.T) {
56 t.Parallel()
57 kind, id, label, err := normalizeSubject("", 0, "", 42, "acme")
58 if err != nil {
59 t.Fatalf("legacy org-only: %v", err)
60 }
61 if kind != SubjectKindOrg || id != 42 || label != "acme" {
62 t.Errorf("legacy normalize: kind=%s id=%d label=%q", kind, id, label)
63 }
64 }
65
66 func TestNormalizeSubjectExplicitUser(t *testing.T) {
67 t.Parallel()
68 kind, id, label, err := normalizeSubject(SubjectKindUser, 7, "alice", 0, "")
69 if err != nil {
70 t.Fatalf("explicit user: %v", err)
71 }
72 if kind != SubjectKindUser || id != 7 || label != "alice" {
73 t.Errorf("user normalize: kind=%s id=%d label=%q", kind, id, label)
74 }
75 }
76
77 func TestNormalizeSubjectRejectsBogusKind(t *testing.T) {
78 t.Parallel()
79 if _, _, _, err := normalizeSubject("alien", 1, "x", 0, ""); !errors.Is(err, ErrInvalidSubjectKind) {
80 t.Fatalf("expected ErrInvalidSubjectKind, got %v", err)
81 }
82 }
83
84 func TestNormalizeSubjectRequiresIDOrOrgFallback(t *testing.T) {
85 t.Parallel()
86 // User kind without an ID is invalid.
87 if _, _, _, err := normalizeSubject(SubjectKindUser, 0, "", 0, ""); !errors.Is(err, ErrInvalidSubjectKind) {
88 t.Fatalf("user without id: expected ErrInvalidSubjectKind, got %v", err)
89 }
90 // Org kind with zero SubjectID but OrgID set falls back.
91 kind, id, _, err := normalizeSubject(SubjectKindOrg, 0, "acme", 99, "acme")
92 if err != nil {
93 t.Fatalf("org fallback: %v", err)
94 }
95 if kind != SubjectKindOrg || id != 99 {
96 t.Errorf("org fallback: kind=%s id=%d", kind, id)
97 }
98 }
99
100 func TestSubjectMetadataOrgKindIncludesLegacyKeys(t *testing.T) {
101 t.Parallel()
102 m := subjectMetadata(SubjectKindOrg, 42, "acme", 42, "acme")
103 if m[MetadataSubjectKind] != "org" || m[MetadataSubjectID] != "42" {
104 t.Errorf("PRO04 keys missing for org: %+v", m)
105 }
106 if m[MetadataOrgID] != "42" || m[MetadataOrgSlug] != "acme" {
107 t.Errorf("legacy keys missing for org: %+v", m)
108 }
109 }
110
111 func TestSubjectMetadataUserKindOmitsLegacyOrgKeys(t *testing.T) {
112 t.Parallel()
113 m := subjectMetadata(SubjectKindUser, 7, "alice", 0, "")
114 if m[MetadataSubjectKind] != "user" || m[MetadataSubjectID] != "7" {
115 t.Errorf("PRO04 keys missing for user: %+v", m)
116 }
117 if _, ok := m[MetadataOrgID]; ok {
118 t.Errorf("user metadata should omit MetadataOrgID; got %+v", m)
119 }
120 if _, ok := m[MetadataOrgSlug]; ok {
121 t.Errorf("user metadata should omit MetadataOrgSlug; got %+v", m)
122 }
123 }
124
125 func TestVerifyWebhookUsesSigningSecret(t *testing.T) {
126 t.Parallel()
127 client, err := New(Config{
128 SecretKey: "sk_test_123",
129 WebhookSecret: "whsec_test",
130 TeamPriceID: "price_123",
131 })
132 if err != nil {
133 t.Fatalf("New: %v", err)
134 }
135 payload := []byte(fmt.Sprintf(`{"id":"evt_test","object":"event","api_version":%q,"type":"customer.subscription.updated","data":{"object":{"id":"sub_test","object":"subscription"}}}`, stripeapi.APIVersion))
136 signed := webhook.GenerateTestSignedPayload(&webhook.UnsignedPayload{
137 Payload: payload,
138 Secret: "whsec_test",
139 })
140 event, err := client.VerifyWebhook(payload, signed.Header)
141 if err != nil {
142 t.Fatalf("VerifyWebhook: %v", err)
143 }
144 if event.ID != "evt_test" || event.Type != "customer.subscription.updated" {
145 t.Fatalf("unexpected event: id=%s type=%s", event.ID, event.Type)
146 }
147 if _, err := client.VerifyWebhook(payload, "t=1,v1=bad"); err == nil {
148 t.Fatalf("VerifyWebhook accepted bad signature")
149 }
150 }
151