Files
pay/internal/store/subscription_test.go

66 lines
2.1 KiB
Go

package store_test
import (
"testing"
"time"
"github.com/wangjia/pay/internal/model"
"github.com/wangjia/pay/internal/store"
)
func newSubStore(t *testing.T) *store.SubscriptionStore {
return store.NewSubscriptionStore(model.OpenTestDB(t))
}
func TestSubscriptionCreateIdempotent(t *testing.T) {
s := newSubStore(t)
sub := &model.Subscription{SubID: "SUB-1", Channel: "stripe", ProviderSubRef: "sub_x",
OutTradeNo: "PAY-1", BizSystem: "pangolin", BizRef: "u-1", BizCode: "pro_month",
AmountMinor: 2999, Currency: "USD", Status: model.SubActive}
created, err := s.Create(sub)
if err != nil || !created {
t.Fatalf("first create: created=%v err=%v", created, err)
}
// 同 provider_sub_ref 再建 → 幂等 no-op(Stripe 重投 checkout.completed)
again, err := s.Create(&model.Subscription{SubID: "SUB-2", Channel: "stripe", ProviderSubRef: "sub_x",
OutTradeNo: "PAY-1", AmountMinor: 2999, Currency: "USD", Status: model.SubActive})
if err != nil || again {
t.Fatalf("dup create: again=%v err=%v", again, err)
}
}
func TestSubscriptionStateMachine(t *testing.T) {
s := newSubStore(t)
end := time.Now().Add(30 * 24 * time.Hour)
if _, err := s.Create(&model.Subscription{SubID: "SUB-9", Channel: "stripe", ProviderSubRef: "sub_9",
OutTradeNo: "PAY-9", AmountMinor: 2999, Currency: "USD", Status: model.SubActive}); err != nil {
t.Fatal(err)
}
// active → past_due
ok, _ := s.MarkPastDue("stripe", "sub_9")
if !ok {
t.Fatal("MarkPastDue should flip active→past_due")
}
// past_due → active(续费恢复)
ok, _ = s.Activate("SUB-9", &end)
if !ok {
t.Fatal("Activate should recover past_due→active")
}
// → canceled(终态)
ok, _ = s.MarkCanceled("SUB-9")
if !ok {
t.Fatal("MarkCanceled should flip →canceled")
}
// canceled 后不可复活
if ok, _ := s.Activate("SUB-9", &end); ok {
t.Fatal("canceled sub must not be re-activated")
}
if ok, _ := s.MarkPastDue("stripe", "sub_9"); ok {
t.Fatal("canceled sub must not go past_due")
}
got, err := s.GetBySubID("SUB-9")
if err != nil || got.Status != model.SubCanceled {
t.Fatalf("final status = %v err=%v", got.Status, err)
}
}