ducky/subscriptions

Paddy 2015-06-16 Parent:f1a22fc2321d Child:b240b6123548

2:61c4ce5850da Go to Latest

ducky/subscriptions/subscription_store_test.go

Use stripe's built-in subscriptions. We're going to use Stripe's built-in subscriptions to manage our subscriptions, which required us to change a lot of stuff. We're now tracking stripe_subscription instead of stripe_customer, and we need to track the plan, status, and if the user is canceling after this month. We also don't need to know when to begin charging them (Stripe will do it), but we should track when their trial starts and ends, when the current pay period they're in starts and ends, when they canceled (if they've canceled), the number of failed charge attempts they've had, and the last time we notified them about billing (To avoid spamming users). We get to delete all the stuff about periods, which is nice. We updated our SubscriptionChange type to match. Notably, there are a lot of non-user modifiable things now, but our Stripe webhook will need to use them to update our database records and keep them in sync. We no longer need to deal with sorting stuff, which is also nice. Our SubscriptionStats have been updated to be... useful? Now we can track how many users we have, and how many of them have failing credit cards, how many are canceling at the end of their current payment period, and how many users are on each plan. We also switched around how the TestUpdateSubscription loops were written, to avoid resetting more than we needed to. Before, we had to call store.reset() after every single change iteration. Now we get to call it only when switching stores. This makes a significant difference in the amount of time it takes to run tests. Finally, we added a test case for retrieving subscription stats. It's minimal, but it works.

History
1 package subscriptions
3 import (
4 "os"
5 "strconv"
6 "testing"
7 "time"
9 "code.secondbit.org/uuid.hg"
10 )
12 const (
13 subscriptionChangeStripeSubscription = 1 << iota
14 subscriptionChangePlan
15 subscriptionChangeStatus
16 subscriptionChangeCanceling
17 subscriptionChangeTrialStart
18 subscriptionChangeTrialEnd
19 subscriptionChangePeriodStart
20 subscriptionChangePeriodEnd
21 subscriptionChangeCanceledAt
22 subscriptionChangeFailedChargeAttempts
23 subscriptionChangeLastFailedCharge
24 subscriptionChangeLastNotified
25 )
27 func init() {
28 if os.Getenv("PG_TEST_DB") != "" {
29 p, err := NewPostgres(os.Getenv("PG_TEST_DB"))
30 if err != nil {
31 panic(err)
32 }
33 testSubscriptionStores = append(testSubscriptionStores, p)
34 }
35 }
37 var testSubscriptionStores = []subscriptionStore{
38 NewMemstore(),
39 }
41 func compareSubscriptions(sub1, sub2 Subscription) (bool, string, interface{}, interface{}) {
42 if !sub1.UserID.Equal(sub2.UserID) {
43 return false, "UserID", sub1.UserID, sub2.UserID
44 }
45 if sub1.StripeSubscription != sub2.StripeSubscription {
46 return false, "StripeSubscription", sub1.StripeSubscription, sub2.StripeSubscription
47 }
48 if sub1.Plan != sub2.Plan {
49 return false, "Plan", sub1.Plan, sub2.Plan
50 }
51 if sub1.Status != sub2.Status {
52 return false, "Status", sub1.Status, sub2.Status
53 }
54 if sub1.Canceling != sub2.Canceling {
55 return false, "Canceling", sub1.Canceling, sub2.Canceling
56 }
57 if !sub1.Created.Equal(sub2.Created) {
58 return false, "Created", sub1.Created, sub2.Created
59 }
60 if !sub1.TrialStart.Equal(sub2.TrialStart) {
61 return false, "TrialStart", sub1.TrialStart, sub2.TrialStart
62 }
63 if !sub1.TrialEnd.Equal(sub2.TrialEnd) {
64 return false, "TrialEnd", sub1.TrialEnd, sub2.TrialEnd
65 }
66 if !sub1.PeriodStart.Equal(sub2.PeriodStart) {
67 return false, "PeriodStart", sub1.PeriodStart, sub2.PeriodStart
68 }
69 if !sub1.PeriodEnd.Equal(sub2.PeriodEnd) {
70 return false, "PeriodEnd", sub1.PeriodEnd, sub2.PeriodEnd
71 }
72 if !sub1.CanceledAt.Equal(sub2.CanceledAt) {
73 return false, "CanceledAt", sub1.CanceledAt, sub2.CanceledAt
74 }
75 if sub1.FailedChargeAttempts != sub2.FailedChargeAttempts {
76 return false, "FailedChargeAttempts", sub1.FailedChargeAttempts, sub2.FailedChargeAttempts
77 }
78 if !sub1.LastFailedCharge.Equal(sub2.LastFailedCharge) {
79 return false, "LastFailedCharge", sub1.LastFailedCharge, sub2.LastFailedCharge
80 }
81 if !sub1.LastNotified.Equal(sub2.LastNotified) {
82 return false, "LastNotified", sub1.LastNotified, sub2.LastNotified
83 }
84 return true, "", nil, nil
85 }
87 func subscriptionMapContains(subscriptionMap map[string]Subscription, subscriptions ...Subscription) (bool, []Subscription) {
88 var missing []Subscription
89 for _, sub := range subscriptions {
90 if _, ok := subscriptionMap[sub.UserID.String()]; !ok {
91 missing = append(missing, sub)
92 }
93 }
94 if len(missing) > 0 {
95 return false, missing
96 }
97 return true, missing
98 }
100 func compareSubscriptionStats(stat1, stat2 SubscriptionStats) (bool, string, interface{}, interface{}) {
101 if stat1.Number != stat2.Number {
102 return false, "Number", stat1.Number, stat2.Number
103 }
104 if stat1.Canceling != stat2.Canceling {
105 return false, "Canceling", stat1.Canceling, stat2.Canceling
106 }
107 if stat1.Failing != stat2.Failing {
108 return false, "Failing", stat1.Failing, stat2.Failing
109 }
110 if len(stat1.Plans) != len(stat2.Plans) {
111 return false, "Plans", stat1.Plans, stat2.Plans
112 }
113 for key, count := range stat1.Plans {
114 count2, ok := stat2.Plans[key]
115 if !ok {
116 return false, "Plans", stat1.Plans, stat2.Plans
117 }
118 if count != count2 {
119 return false, "Plans", stat1.Plans, stat2.Plans
120 }
121 }
122 return true, "", nil, nil
123 }
125 func TestCreateSubscription(t *testing.T) {
126 for _, store := range testSubscriptionStores {
127 err := store.reset()
128 if err != nil {
129 t.Fatalf("Error resetting %T: %+v\n", store, err)
130 }
131 customerID := uuid.NewID()
132 sub := Subscription{
133 UserID: customerID,
134 StripeSubscription: "stripeSubscription1",
135 Created: time.Now().Round(time.Millisecond),
136 TrialStart: time.Now().Round(time.Millisecond),
137 TrialEnd: time.Now().Round(time.Millisecond).Add(time.Hour * 24 * 31),
138 }
139 err = store.createSubscription(sub)
140 if err != nil {
141 t.Errorf("Error creating subscription in %T: %+v\n", store, err)
142 }
143 retrieved, err := store.getSubscriptions([]uuid.ID{sub.UserID})
144 if err != nil {
145 t.Errorf("Error retrieving subscription from %T: %+v\n", store, err)
146 }
147 if _, returned := retrieved[sub.UserID.String()]; !returned {
148 t.Errorf("Error retrieving subscription from %T: %s wasn't in the results.", store, sub.UserID)
149 }
150 ok, field, expected, result := compareSubscriptions(sub, retrieved[sub.UserID.String()])
151 if !ok {
152 t.Errorf("Expected %s to be %v, got %v from %T\n", field, expected, result, store)
153 }
154 err = store.createSubscription(sub)
155 if err != ErrSubscriptionAlreadyExists {
156 t.Errorf("Unexpected error creating subscription in %T (wanted %+v): %+v\n", store, ErrSubscriptionAlreadyExists, err)
157 }
158 sub.UserID = uuid.NewID()
159 err = store.createSubscription(sub)
160 if err != ErrStripeSubscriptionAlreadyExists {
161 t.Errorf("Unexpected error creating subscription in %T (wanted %+v): %#+v\n", store, ErrStripeSubscriptionAlreadyExists, err)
162 }
163 sub.StripeSubscription = "stripeSubscription2"
164 err = store.createSubscription(sub)
165 if err != nil {
166 t.Errorf("Error creating subscription in %T: %+v\n", store, err)
167 }
168 }
169 }
171 func TestUpdateSubscription(t *testing.T) {
172 variations := 1 << 12
173 sub := Subscription{
174 UserID: uuid.NewID(),
175 StripeSubscription: "default",
176 Created: time.Now().Round(time.Millisecond).Add(time.Hour * -24 * -32),
177 TrialStart: time.Now().Round(time.Millisecond).Add(time.Hour * -24 * -32),
178 TrialEnd: time.Now().Round(time.Millisecond).Add(time.Hour * -24),
179 LastNotified: time.Now().Round(time.Millisecond).Add(time.Hour * -24),
180 }
181 sub2 := Subscription{
182 UserID: uuid.NewID(),
183 StripeSubscription: "stripeSubscription2",
184 Created: time.Now().Round(time.Millisecond),
185 TrialStart: time.Now().Round(time.Millisecond),
186 TrialEnd: time.Now().Round(time.Millisecond),
187 LastNotified: time.Now().Round(time.Millisecond),
188 }
190 for _, store := range testSubscriptionStores {
191 err := store.reset()
192 if err != nil {
193 t.Fatalf("Error resetting %T: %+v\n", store, err)
194 }
195 err = store.createSubscription(sub)
196 if err != nil {
197 t.Fatalf("Error saving subscription in %T: %s\n", store, err)
198 }
199 for i := 1; i < variations; i++ {
200 var stripeSubscription, plan, status string
201 var canceling bool
202 var failedChargeAttempts int
203 var trialStart, trialEnd, periodStart, periodEnd, canceledAt, lastFailedCharge, lastNotified time.Time
205 change := SubscriptionChange{}
206 empty := change.IsEmpty()
207 if !empty {
208 t.Errorf("Expected empty to be %t, was %t\n", true, empty)
209 }
210 result := sub
211 strI := strconv.Itoa(i)
213 if i&subscriptionChangeStripeSubscription != 0 {
214 stripeSubscription = "stripeSubscription-" + strI
215 change.StripeSubscription = &stripeSubscription
216 sub.StripeSubscription = stripeSubscription
217 }
219 if i&subscriptionChangePlan != 0 {
220 plan = "plan-" + strI
221 change.Plan = &plan
222 sub.Plan = plan
223 }
225 if i&subscriptionChangeStatus != 0 {
226 status = "status-" + strI
227 change.Status = &status
228 sub.Status = status
229 }
231 if i&subscriptionChangeCanceling != 0 {
232 canceling = i%2 == 0
233 change.Canceling = &canceling
234 sub.Canceling = canceling
235 }
237 if i&subscriptionChangeTrialStart != 0 {
238 trialStart = time.Now().Round(time.Millisecond).Add(time.Hour * time.Duration(i))
239 change.TrialStart = &trialStart
240 sub.TrialStart = trialStart
241 }
243 if i&subscriptionChangeTrialEnd != 0 {
244 trialEnd = time.Now().Round(time.Millisecond).Add(time.Hour * time.Duration(i))
245 change.TrialEnd = &trialEnd
246 sub.TrialEnd = trialEnd
247 }
249 if i&subscriptionChangePeriodStart != 0 {
250 periodStart = time.Now().Round(time.Millisecond).Add(time.Hour * time.Duration(i))
251 change.PeriodStart = &periodStart
252 sub.PeriodStart = periodStart
253 }
255 if i&subscriptionChangePeriodEnd != 0 {
256 periodEnd = time.Now().Round(time.Millisecond).Add(time.Hour * time.Duration(i))
257 change.PeriodEnd = &periodEnd
258 sub.PeriodEnd = periodEnd
259 }
261 if i&subscriptionChangeCanceledAt != 0 {
262 canceledAt = time.Now().Round(time.Millisecond).Add(time.Hour * time.Duration(i))
263 change.CanceledAt = &canceledAt
264 sub.CanceledAt = canceledAt
265 }
267 if i&subscriptionChangeFailedChargeAttempts != 0 {
268 failedChargeAttempts = i
269 change.FailedChargeAttempts = &failedChargeAttempts
270 sub.FailedChargeAttempts = failedChargeAttempts
271 }
273 if i&subscriptionChangeLastFailedCharge != 0 {
274 lastFailedCharge = time.Now().Round(time.Millisecond).Add(time.Hour * time.Duration(i))
275 change.LastFailedCharge = &lastFailedCharge
276 sub.LastFailedCharge = lastFailedCharge
277 }
279 if i&subscriptionChangeLastNotified != 0 {
280 lastNotified = time.Now().Round(time.Millisecond).Add(time.Hour * time.Duration(i))
281 change.LastNotified = &lastNotified
282 sub.LastNotified = lastNotified
283 }
285 empty = change.IsEmpty()
286 if empty {
287 t.Errorf("Expected empty to be %t, was %t\n", false, empty)
288 }
290 result.ApplyChange(change)
291 match, field, expected, got := compareSubscriptions(sub, result)
292 if !match {
293 t.Errorf("Expected field `%s` to be `%v`, got `%v`\n", field, expected, got)
294 }
295 err = store.updateSubscription(sub.UserID, change)
296 if err != nil {
297 t.Errorf("Error updating subscription in %T: %s\n", store, err)
298 }
299 retrieved, err := store.getSubscriptions([]uuid.ID{sub.UserID})
300 if err != nil {
301 t.Errorf("Error getting subscription from %T: %s\n", store, err)
302 }
303 ok, missing := subscriptionMapContains(retrieved, sub)
304 if !ok {
305 t.Errorf("Expected to retrieve %s from %T, but missing was %+v\n", sub.UserID.String(), store, missing)
306 }
307 match, field, expected, got = compareSubscriptions(sub, retrieved[sub.UserID.String()])
308 if !match {
309 t.Errorf("Expected field `%s` to be `%v`, got `%v` from %T\n", field, expected, got, store)
310 }
311 sub = result
312 }
314 err = store.createSubscription(sub2)
315 if err != nil {
316 t.Fatalf("Error saving subscription in %T: %+v\n", store, err)
317 }
318 change := SubscriptionChange{}
319 err = store.updateSubscription(sub.UserID, change)
320 if err != ErrSubscriptionChangeEmpty {
321 t.Errorf("Expected err to be %+v, but got %+v from %T\n", ErrSubscriptionChangeEmpty, err, store)
322 }
323 stripeSubscription := sub2.StripeSubscription
324 change.StripeSubscription = &stripeSubscription
325 err = store.updateSubscription(uuid.NewID(), change)
326 if err != ErrSubscriptionNotFound {
327 t.Errorf("Expected err to be %+v, but got %+v from %T\n", ErrSubscriptionNotFound, err, store)
328 }
329 err = store.updateSubscription(sub.UserID, change)
330 if err != ErrStripeSubscriptionAlreadyExists {
331 t.Errorf("Expected err to be %+v, but got %+v from %T\n", ErrStripeSubscriptionAlreadyExists, err, store)
332 }
333 }
334 }
336 func TestDeleteSubscription(t *testing.T) {
337 for _, store := range testSubscriptionStores {
338 err := store.reset()
339 if err != nil {
340 t.Fatalf("Error resetting %T: %+v\n", store, err)
341 }
342 sub1 := Subscription{
343 UserID: uuid.NewID(),
344 StripeSubscription: "stripeSubscription1",
345 }
346 sub2 := Subscription{
347 UserID: uuid.NewID(),
348 StripeSubscription: "stripeSubscription2",
349 }
350 err = store.createSubscription(sub1)
351 if err != nil {
352 t.Fatalf("Error creating %+v in %T: %+v\n", sub1, store, err)
353 }
354 err = store.createSubscription(sub2)
355 if err != nil {
356 t.Fatalf("Error creating %+v in %T: %+v\n", sub2, store, err)
357 }
358 err = store.deleteSubscription(sub1.UserID)
359 if err != nil {
360 t.Fatalf("Error deleting %+v in %T: %+v\n", sub1, store, err)
361 }
362 retrieved, err := store.getSubscriptions([]uuid.ID{sub1.UserID, sub2.UserID})
363 if err != nil {
364 t.Errorf("Error retrieving subscriptions from %T: %+v\n", store, err)
365 }
366 ok, missing := subscriptionMapContains(retrieved, sub1)
367 if ok {
368 t.Errorf("Expected not to retrieve %s from %T, but missing was %+v\n", sub1.UserID.String(), store, missing)
369 }
370 ok, missing = subscriptionMapContains(retrieved, sub2)
371 if !ok {
372 t.Errorf("Expected to retrieve %s from %T, but missing was %+v\n", sub2.UserID.String(), store, missing)
373 }
374 err = store.deleteSubscription(sub1.UserID)
375 if err != ErrSubscriptionNotFound {
376 t.Errorf("Expected err to be %+v, but got %+v from %T\n", ErrSubscriptionNotFound, err, store)
377 }
378 }
379 }
381 func TestGetSubscriptions(t *testing.T) {
382 for _, store := range testSubscriptionStores {
383 err := store.reset()
384 if err != nil {
385 t.Fatalf("Error resetting %T: %+v\n", store, err)
386 }
387 sub1 := Subscription{
388 UserID: uuid.NewID(),
389 StripeSubscription: "stripeSubscription1",
390 Plan: "plan1",
391 Created: time.Now().Round(time.Millisecond),
392 TrialStart: time.Now().Round(time.Millisecond),
393 TrialEnd: time.Now().Round(time.Millisecond).Add(time.Hour * 24 * 32),
394 }
395 sub2 := Subscription{
396 UserID: uuid.NewID(),
397 StripeSubscription: "stripeSubscription2",
398 Plan: "plan2",
399 Created: time.Now().Round(time.Millisecond).Add(time.Hour * -720),
400 TrialStart: time.Now().Round(time.Millisecond).Add(time.Hour * -720),
401 TrialEnd: time.Now().Round(time.Millisecond),
402 }
403 sub3 := Subscription{
404 UserID: uuid.NewID(),
405 StripeSubscription: "stripeSubscription3",
406 Plan: "plan3",
407 Created: time.Now().Round(time.Millisecond).Add(time.Hour * -1440),
408 TrialStart: time.Now().Round(time.Millisecond).Add(time.Hour * -1440),
409 TrialEnd: time.Now().Round(time.Millisecond).Add(time.Hour * -720),
410 PeriodStart: time.Now().Round(time.Millisecond).Add(time.Hour * -720),
411 PeriodEnd: time.Now().Round(time.Millisecond),
412 Status: "unpaid",
413 }
414 err = store.createSubscription(sub1)
415 if err != nil {
416 t.Fatalf("Error creating %+v in %T: %+v\n", sub1, store, err)
417 }
418 err = store.createSubscription(sub2)
419 if err != nil {
420 t.Fatalf("Error creating %+v in %T: %+v\n", sub1, store, err)
421 }
422 err = store.createSubscription(sub3)
423 if err != nil {
424 t.Fatalf("Error creating %+v in %T: %+v\n", sub1, store, err)
425 }
426 retrieved, err := store.getSubscriptions([]uuid.ID{})
427 if err != ErrNoSubscriptionID {
428 t.Errorf("Error retrieving no subscriptions from %T. Expected %+v, got %+v\n", store, ErrNoSubscriptionID, err)
429 }
430 retrieved, err = store.getSubscriptions([]uuid.ID{sub1.UserID})
431 if err != nil {
432 t.Errorf("Error retrieving %s from %T: %+v\n", sub1.UserID, store, err)
433 }
434 ok, missing := subscriptionMapContains(retrieved, sub1)
435 if !ok {
436 t.Logf("Results: %+v\n", retrieved)
437 t.Errorf("Expected %+v to be in the results, was not for %T.\n", missing, store)
438 }
439 retrieved, err = store.getSubscriptions([]uuid.ID{sub1.UserID, sub2.UserID})
440 if err != nil {
441 t.Errorf("Error retrieving %s and %s from %T: %+v\n", sub1.UserID, sub2.UserID, store, err)
442 }
443 ok, missing = subscriptionMapContains(retrieved, sub1, sub2)
444 if !ok {
445 t.Logf("Results: %+v\n", retrieved)
446 t.Errorf("Expected %+v to be in the results, was not for %T.\n", missing, store)
447 }
448 retrieved, err = store.getSubscriptions([]uuid.ID{sub1.UserID, sub3.UserID})
449 if err != nil {
450 t.Errorf("Error retrieving %s and %s from %T: %+v\n", sub1.UserID, sub3.UserID, store, err)
451 }
452 ok, missing = subscriptionMapContains(retrieved, sub1, sub3)
453 if !ok {
454 t.Logf("Results: %+v\n", retrieved)
455 t.Errorf("Expected %+v to be in the results, was not for %T.\n", missing, store)
456 }
457 retrieved, err = store.getSubscriptions([]uuid.ID{sub1.UserID, sub2.UserID, sub3.UserID})
458 if err != nil {
459 t.Errorf("Error retrieving %s, %s, and %s from %T: %+v\n", sub1.UserID, sub2.UserID, sub3.UserID, store, err)
460 }
461 ok, missing = subscriptionMapContains(retrieved, sub1, sub2, sub3)
462 if !ok {
463 t.Logf("Results: %+v\n", retrieved)
464 t.Errorf("Expected %+v to be in the results, was not for %T.\n", missing, store)
465 }
466 retrieved, err = store.getSubscriptions([]uuid.ID{sub2.UserID})
467 if err != nil {
468 t.Errorf("Error retrieving %s from %T: %+v\n", sub2.UserID, store, err)
469 }
470 ok, missing = subscriptionMapContains(retrieved, sub2)
471 if !ok {
472 t.Logf("Results: %+v\n", retrieved)
473 t.Errorf("Expected %+v to be in the results, was not for %T.\n", missing, store)
474 }
475 retrieved, err = store.getSubscriptions([]uuid.ID{sub2.UserID, sub3.UserID})
476 if err != nil {
477 t.Errorf("Error retrieving %s and %s from %T: %+v\n", sub2.UserID, sub3.UserID, store, err)
478 }
479 ok, missing = subscriptionMapContains(retrieved, sub2, sub3)
480 if !ok {
481 t.Logf("Results: %+v\n", retrieved)
482 t.Errorf("Expected %+v to be in the results, was not for %T.\n", missing, store)
483 }
484 retrieved, err = store.getSubscriptions([]uuid.ID{sub3.UserID})
485 if err != nil {
486 t.Errorf("Error retrieving %s from %T: %+v\n", sub3.UserID, store, err)
487 }
488 ok, missing = subscriptionMapContains(retrieved, sub3)
489 if !ok {
490 t.Logf("Results: %+v\n", retrieved)
491 t.Errorf("Expected %+v to be in the results, was not for %T.\n", missing, store)
492 }
493 retrieved, err = store.getSubscriptions([]uuid.ID{uuid.NewID()})
494 if err != nil {
495 t.Errorf("Error retrieving non-existent ID from %T: %+v\n", store, err)
496 }
497 if len(retrieved) != 0 {
498 t.Errorf("Expected no results, %T returned %+v\n", store, retrieved)
499 }
500 retrieved, err = store.getSubscriptions([]uuid.ID{sub1.UserID, sub2.UserID, uuid.NewID(), sub3.UserID})
501 if err != nil {
502 t.Errorf("Error retrieving non-existent ID from %T: %+v\n", store, err)
503 }
504 if len(retrieved) != 3 {
505 t.Errorf("Expected 3 results, %T returned %+v\n", store, retrieved)
506 }
507 ok, missing = subscriptionMapContains(retrieved, sub1, sub2, sub3)
508 if !ok {
509 t.Logf("Results: %+v\n", retrieved)
510 t.Errorf("Expected %+v to be in the results, was not for %T.\n", missing, store)
511 }
512 }
513 }
515 func TestGetSubscriptionStats(t *testing.T) {
516 for _, store := range testSubscriptionStores {
517 err := store.reset()
518 if err != nil {
519 t.Fatalf("Error resetting %T: %+v\n", store, err)
520 }
521 sub1 := Subscription{
522 UserID: uuid.NewID(),
523 StripeSubscription: "stripeSubscription1",
524 Plan: "plan1",
525 Canceling: true,
526 }
527 sub2 := Subscription{
528 UserID: uuid.NewID(),
529 StripeSubscription: "stripeSubscription2",
530 Plan: "plan2",
531 Status: "past_due",
532 }
533 err = store.createSubscription(sub1)
534 if err != nil {
535 t.Fatalf("Error creating %+v in %T: %+v\n", sub1, store, err)
536 }
537 stats, err := store.getSubscriptionStats()
538 if err != nil {
539 t.Errorf("Error getting stats from %T: %+v\n", store, err)
540 }
541 ok, field, expected, results := compareSubscriptionStats(SubscriptionStats{
542 Number: 1,
543 Canceling: 1,
544 Failing: 0,
545 Plans: map[string]int64{
546 "plan1": 1,
547 },
548 }, stats)
549 if !ok {
550 t.Errorf("Expected %s to be %+v, got %+v from %T\n", field, expected, results, store)
551 }
552 err = store.createSubscription(sub2)
553 if err != nil {
554 t.Fatalf("Error creating %+v in %T: %+v\n", sub2, store, err)
555 }
556 stats, err = store.getSubscriptionStats()
557 if err != nil {
558 t.Errorf("Error getting status from %T: %+v\n", store, err)
559 }
560 ok, field, expected, results = compareSubscriptionStats(SubscriptionStats{
561 Number: 2,
562 Canceling: 1,
563 Failing: 1,
564 Plans: map[string]int64{
565 "plan1": 1,
566 "plan2": 1,
567 },
568 }, stats)
569 if !ok {
570 t.Errorf("Expected %s to be %+v, got %+v from %T\n", field, expected, results, store)
571 }
572 err = store.deleteSubscription(sub1.UserID)
573 if err != nil {
574 t.Errorf("Error deleting subscription from %T: %+v\n", store, err)
575 }
576 stats, err = store.getSubscriptionStats()
577 if err != nil {
578 t.Errorf("Error getting status from %T: %+v\n", store, err)
579 }
580 ok, field, expected, results = compareSubscriptionStats(SubscriptionStats{
581 Number: 1,
582 Canceling: 0,
583 Failing: 1,
584 Plans: map[string]int64{
585 "plan2": 1,
586 },
587 }, stats)
588 if !ok {
589 t.Errorf("Expected %s to be %+v, got %+v from %T\n", field, expected, results, store)
590 }
591 }
592 }