221 lines
5.8 KiB
Go
221 lines
5.8 KiB
Go
package card
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"gpt-plus/internal/db"
|
|
|
|
"github.com/glebarez/sqlite"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/logger"
|
|
)
|
|
|
|
func setupCardTestDB(t *testing.T) *gorm.DB {
|
|
t.Helper()
|
|
d, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
|
|
Logger: logger.Default.LogMode(logger.Silent),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("open db: %v", err)
|
|
}
|
|
d.AutoMigrate(&db.SystemConfig{}, &db.Card{}, &db.CardCode{})
|
|
db.DB = d
|
|
return d
|
|
}
|
|
|
|
func insertCard(t *testing.T, d *gorm.DB, number, status string, maxBinds int) *db.Card {
|
|
t.Helper()
|
|
card := &db.Card{
|
|
NumberHash: db.HashSHA256(number),
|
|
NumberEnc: number, // no encryption in test (key not set)
|
|
CVCEnc: "123",
|
|
ExpMonth: "12", ExpYear: "2030",
|
|
Name: "Test", Country: "US",
|
|
Status: status, MaxBinds: maxBinds,
|
|
}
|
|
d.Create(card)
|
|
return card
|
|
}
|
|
|
|
func TestGetCardReturnsActiveCard(t *testing.T) {
|
|
d := setupCardTestDB(t)
|
|
insertCard(t, d, "4111111111111111", "active", 3)
|
|
|
|
prov := NewDBCardProvider(DBCardProviderConfig{DB: d})
|
|
card, err := prov.GetCard(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("GetCard: %v", err)
|
|
}
|
|
if card.Number != "4111111111111111" {
|
|
t.Fatalf("number = %q", card.Number)
|
|
}
|
|
}
|
|
|
|
func TestGetCardActivatesAvailable(t *testing.T) {
|
|
d := setupCardTestDB(t)
|
|
insertCard(t, d, "4242424242424242", "available", 1)
|
|
|
|
prov := NewDBCardProvider(DBCardProviderConfig{DB: d})
|
|
card, err := prov.GetCard(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("GetCard: %v", err)
|
|
}
|
|
if card.Number != "4242424242424242" {
|
|
t.Fatalf("number = %q", card.Number)
|
|
}
|
|
|
|
var dbCard db.Card
|
|
d.First(&dbCard, "number_hash = ?", db.HashSHA256("4242424242424242"))
|
|
if dbCard.Status != "active" {
|
|
t.Fatalf("card should now be active, got %q", dbCard.Status)
|
|
}
|
|
}
|
|
|
|
func TestGetCardNoCardsReturnsError(t *testing.T) {
|
|
d := setupCardTestDB(t)
|
|
|
|
prov := NewDBCardProvider(DBCardProviderConfig{DB: d})
|
|
_, err := prov.GetCard(context.Background())
|
|
if err == nil {
|
|
t.Fatal("expected error when no cards")
|
|
}
|
|
}
|
|
|
|
func TestReportResultSuccessIncrementsBind(t *testing.T) {
|
|
d := setupCardTestDB(t)
|
|
insertCard(t, d, "5555555555554444", "active", 3)
|
|
|
|
prov := NewDBCardProvider(DBCardProviderConfig{DB: d})
|
|
card, _ := prov.GetCard(context.Background())
|
|
|
|
err := prov.ReportResult(context.Background(), card, true)
|
|
if err != nil {
|
|
t.Fatalf("ReportResult: %v", err)
|
|
}
|
|
|
|
var dbCard db.Card
|
|
d.First(&dbCard, "number_hash = ?", db.HashSHA256("5555555555554444"))
|
|
if dbCard.BindCount != 1 {
|
|
t.Fatalf("bind_count = %d, want 1", dbCard.BindCount)
|
|
}
|
|
if dbCard.Status != "active" {
|
|
t.Fatalf("still active when under max_binds, got %q", dbCard.Status)
|
|
}
|
|
}
|
|
|
|
func TestReportResultExhaustsCard(t *testing.T) {
|
|
d := setupCardTestDB(t)
|
|
c := insertCard(t, d, "6011111111111117", "active", 1)
|
|
// Pre-set to 0, so one success = 1 = maxBinds → exhausted
|
|
_ = c
|
|
|
|
prov := NewDBCardProvider(DBCardProviderConfig{DB: d})
|
|
card, _ := prov.GetCard(context.Background())
|
|
|
|
prov.ReportResult(context.Background(), card, true)
|
|
|
|
var dbCard db.Card
|
|
d.First(&dbCard, "number_hash = ?", db.HashSHA256("6011111111111117"))
|
|
if dbCard.Status != "exhausted" {
|
|
t.Fatalf("status = %q, want exhausted", dbCard.Status)
|
|
}
|
|
}
|
|
|
|
func TestReportResultExhaustedActivatesNext(t *testing.T) {
|
|
d := setupCardTestDB(t)
|
|
insertCard(t, d, "1111000011110000", "active", 1)
|
|
insertCard(t, d, "2222000022220000", "available", 5)
|
|
|
|
prov := NewDBCardProvider(DBCardProviderConfig{DB: d})
|
|
card, _ := prov.GetCard(context.Background())
|
|
prov.ReportResult(context.Background(), card, true) // exhaust first
|
|
|
|
// Now GetCard should return the second card
|
|
next, err := prov.GetCard(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("GetCard after exhaust: %v", err)
|
|
}
|
|
if next.Number != "2222000022220000" {
|
|
t.Fatalf("next card = %q, want 2222000022220000", next.Number)
|
|
}
|
|
}
|
|
|
|
func TestReportResultRejectedCard(t *testing.T) {
|
|
d := setupCardTestDB(t)
|
|
insertCard(t, d, "3333000033330000", "active", 5)
|
|
|
|
prov := NewDBCardProvider(DBCardProviderConfig{DB: d})
|
|
card, _ := prov.GetCard(context.Background())
|
|
|
|
prov.ReportResult(context.Background(), card, false)
|
|
|
|
var dbCard db.Card
|
|
d.First(&dbCard, "number_hash = ?", db.HashSHA256("3333000033330000"))
|
|
if dbCard.Status != "rejected" {
|
|
t.Fatalf("status = %q, want rejected", dbCard.Status)
|
|
}
|
|
if dbCard.LastError == "" {
|
|
t.Fatal("last_error should be set")
|
|
}
|
|
}
|
|
|
|
func TestDefaultCountryAndCurrency(t *testing.T) {
|
|
prov := NewDBCardProvider(DBCardProviderConfig{})
|
|
if prov.defaultCountry != "US" {
|
|
t.Fatalf("defaultCountry = %q, want US", prov.defaultCountry)
|
|
}
|
|
if prov.defaultCurrency != "USD" {
|
|
t.Fatalf("defaultCurrency = %q, want USD", prov.defaultCurrency)
|
|
}
|
|
}
|
|
|
|
func TestToCardInfoAppliesDefaults(t *testing.T) {
|
|
d := setupCardTestDB(t)
|
|
|
|
prov := NewDBCardProvider(DBCardProviderConfig{
|
|
DB: d,
|
|
DefaultAddress: "123 Main St",
|
|
DefaultCity: "NYC",
|
|
DefaultState: "NY",
|
|
DefaultPostalCode: "10001",
|
|
})
|
|
|
|
card := &db.Card{
|
|
NumberEnc: "4111111111111111",
|
|
CVCEnc: "123",
|
|
ExpMonth: "12", ExpYear: "2030",
|
|
Name: "Test", Country: "US",
|
|
}
|
|
|
|
info, err := prov.toCardInfo(card)
|
|
if err != nil {
|
|
t.Fatalf("toCardInfo: %v", err)
|
|
}
|
|
if info.Address != "123 Main St" {
|
|
t.Fatalf("address = %q, want default", info.Address)
|
|
}
|
|
if info.City != "NYC" || info.State != "NY" || info.PostalCode != "10001" {
|
|
t.Fatalf("defaults not applied: %+v", info)
|
|
}
|
|
}
|
|
|
|
func TestSingleActiveCardPolicy(t *testing.T) {
|
|
d := setupCardTestDB(t)
|
|
insertCard(t, d, "AAAA000000001111", "active", 5)
|
|
insertCard(t, d, "BBBB000000002222", "available", 5)
|
|
|
|
// Verify only one active
|
|
var count int64
|
|
d.Model(&db.Card{}).Where("status = ?", "active").Count(&count)
|
|
if count != 1 {
|
|
t.Fatalf("active count = %d, want 1", count)
|
|
}
|
|
|
|
prov := NewDBCardProvider(DBCardProviderConfig{DB: d})
|
|
card, _ := prov.GetCard(context.Background())
|
|
if card.Number != "AAAA000000001111" {
|
|
t.Fatalf("should return the active card, got %q", card.Number)
|
|
}
|
|
}
|