package store import ( "context" "testing" "time" ) func openMem(t *testing.T) *Store { t.Helper() s, err := Open(":memory:") if err != nil { t.Fatalf("open: %v", err) } t.Cleanup(func() { _ = s.Close() }) return s } func TestNextAddrIndexMonotonic(t *testing.T) { s := openMem(t) ctx := context.Background() for want := uint32(0); want < 5; want++ { got, err := s.NextAddrIndex(ctx) if err != nil { t.Fatalf("next: %v", err) } if got != want { t.Fatalf("index got %d want %d", got, want) } } } func TestOrderRoundtripAndMarkPaidIdempotent(t *testing.T) { s := openMem(t) ctx := context.Background() now := time.Unix(1_700_000_000, 0) o := &Order{ OrderNo: "PAY1", SKU: "pro-year", ExpectAmount: 5_000000, AddrIndex: 0, Address: "TUEZSdKsoDHQMeZwihtdoBiN46zxhGWYdH", Status: StatusPending, CreatedAt: now, ExpiresAt: now.Add(15 * time.Minute), } if err := s.CreateOrder(ctx, o); err != nil { t.Fatalf("create: %v", err) } got, err := s.GetOrder(ctx, "PAY1") if err != nil { t.Fatalf("get: %v", err) } if got.SKU != "pro-year" || got.ExpectAmount != 5_000000 || got.Status != StatusPending { t.Fatalf("roundtrip mismatch: %+v", got) } ok, err := s.MarkPaid(ctx, "PAY1", "tx-abc") if err != nil || !ok { t.Fatalf("first MarkPaid ok=%v err=%v (want true,nil)", ok, err) } ok2, err := s.MarkPaid(ctx, "PAY1", "tx-dup") if err != nil || ok2 { t.Fatalf("second MarkPaid ok=%v err=%v (want false,nil — idempotent)", ok2, err) } got, _ = s.GetOrder(ctx, "PAY1") if got.Status != StatusPaid || got.TxID != "tx-abc" { t.Fatalf("after paid: status=%s tx=%s (want paid,tx-abc)", got.Status, got.TxID) } } func TestMarkExpired(t *testing.T) { s := openMem(t) ctx := context.Background() base := time.Unix(1_700_000_000, 0) past := &Order{OrderNo: "old", SKU: "x", ExpectAmount: 1, Address: "T1", Status: StatusPending, CreatedAt: base, ExpiresAt: base.Add(1 * time.Minute)} future := &Order{OrderNo: "new", SKU: "x", ExpectAmount: 1, Address: "T2", Status: StatusPending, CreatedAt: base, ExpiresAt: base.Add(1 * time.Hour)} _ = s.CreateOrder(ctx, past) _ = s.CreateOrder(ctx, future) n, err := s.MarkExpired(ctx, base.Add(10*time.Minute)) if err != nil || n != 1 { t.Fatalf("MarkExpired n=%d err=%v (want 1)", n, err) } oldO, _ := s.GetOrder(ctx, "old") newO, _ := s.GetOrder(ctx, "new") if oldO.Status != StatusExpired { t.Fatalf("old should be expired, got %s", oldO.Status) } if newO.Status != StatusPending { t.Fatalf("new should still be pending, got %s", newO.Status) } }