first commit
This commit is contained in:
180
internal/payments/worker_test.go
Normal file
180
internal/payments/worker_test.go
Normal file
@@ -0,0 +1,180 @@
|
||||
package payments
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/noderunners/nip05api/internal/db"
|
||||
"github.com/noderunners/nip05api/internal/invoice"
|
||||
"github.com/noderunners/nip05api/internal/user"
|
||||
)
|
||||
|
||||
const testHex = "0e8c41ebcd55a8d8db2e0a8c3a4b9c5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c"
|
||||
|
||||
func newTestDB(t *testing.T) *db.DB {
|
||||
t.Helper()
|
||||
d, err := db.Open(filepath.Join(t.TempDir(), "test.db"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := d.Migrate(context.Background()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { d.Close() })
|
||||
return d
|
||||
}
|
||||
|
||||
func TestComputeTarget_NewYearly(t *testing.T) {
|
||||
now := time.Date(2026, 6, 1, 0, 0, 0, 0, time.UTC)
|
||||
p := &invoice.PendingInvoice{SubscriptionType: user.SubYearly, Years: 1}
|
||||
got := computeTarget(p, nil, now)
|
||||
if got == nil || !got.Equal(now.AddDate(1, 0, 0)) {
|
||||
t.Errorf("got %v want %v", got, now.AddDate(1, 0, 0))
|
||||
}
|
||||
}
|
||||
|
||||
func TestComputeTarget_RenewActive(t *testing.T) {
|
||||
now := time.Date(2026, 6, 1, 0, 0, 0, 0, time.UTC)
|
||||
current := now.AddDate(0, 6, 0)
|
||||
p := &invoice.PendingInvoice{SubscriptionType: user.SubYearly, Years: 1}
|
||||
existing := &user.User{ExpiresAt: ¤t}
|
||||
got := computeTarget(p, existing, now)
|
||||
want := current.AddDate(1, 0, 0)
|
||||
if !got.Equal(want) {
|
||||
t.Errorf("got %v want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestComputeTarget_RenewExpired(t *testing.T) {
|
||||
now := time.Date(2026, 6, 1, 0, 0, 0, 0, time.UTC)
|
||||
past := now.AddDate(0, -1, 0)
|
||||
p := &invoice.PendingInvoice{SubscriptionType: user.SubYearly, Years: 1}
|
||||
existing := &user.User{ExpiresAt: &past}
|
||||
got := computeTarget(p, existing, now)
|
||||
want := now.AddDate(1, 0, 0)
|
||||
if !got.Equal(want) {
|
||||
t.Errorf("got %v want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestComputeTarget_Lifetime(t *testing.T) {
|
||||
now := time.Now()
|
||||
p := &invoice.PendingInvoice{SubscriptionType: user.SubLifetime}
|
||||
if got := computeTarget(p, nil, now); got != nil {
|
||||
t.Errorf("expected nil, got %v", got)
|
||||
}
|
||||
}
|
||||
|
||||
// Idempotent confirm: applying the same target twice produces identical state.
|
||||
func TestSetActiveExpiry_Idempotent(t *testing.T) {
|
||||
d := newTestDB(t)
|
||||
repo := user.NewRepo(d)
|
||||
|
||||
expires := time.Date(2027, 6, 1, 0, 0, 0, 0, time.UTC)
|
||||
u := &user.User{
|
||||
Pubkey: testHex,
|
||||
Username: "alice",
|
||||
SubscriptionType: user.SubYearly,
|
||||
ExpiresAt: &expires,
|
||||
IsActive: true,
|
||||
}
|
||||
if err := repo.Insert(context.Background(), u); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
target := time.Date(2028, 6, 1, 0, 0, 0, 0, time.UTC)
|
||||
if err := repo.SetActiveExpiry(context.Background(), testHex, user.SubYearly, &target); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, err := repo.GetByPubkey(context.Background(), testHex)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !got.ExpiresAt.Equal(target) {
|
||||
t.Errorf("first apply: got %v want %v", got.ExpiresAt, target)
|
||||
}
|
||||
|
||||
// Re-apply same target — must not advance further.
|
||||
if err := repo.SetActiveExpiry(context.Background(), testHex, user.SubYearly, &target); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, _ = repo.GetByPubkey(context.Background(), testHex)
|
||||
if !got.ExpiresAt.Equal(target) {
|
||||
t.Errorf("re-apply changed value: got %v want %v", got.ExpiresAt, target)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetTargetIfUnset_OnlyOnce(t *testing.T) {
|
||||
d := newTestDB(t)
|
||||
repo := invoice.NewRepo(d)
|
||||
target1 := time.Date(2027, 6, 1, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
p := &invoice.PendingInvoice{
|
||||
PaymentHash: "hash1",
|
||||
PaymentRequest: "lnbc1...",
|
||||
Username: "alice",
|
||||
Pubkey: testHex,
|
||||
SubscriptionType: user.SubYearly,
|
||||
Years: 1,
|
||||
AmountSats: 1000,
|
||||
ExpiresAt: time.Now().Add(30 * time.Minute),
|
||||
}
|
||||
if err := repo.Insert(context.Background(), p); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
won, err := repo.SetTargetIfUnset(context.Background(), "hash1", &target1)
|
||||
if err != nil || !won {
|
||||
t.Fatalf("first set: won=%v err=%v", won, err)
|
||||
}
|
||||
|
||||
target2 := time.Date(2030, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
won, err = repo.SetTargetIfUnset(context.Background(), "hash1", &target2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if won {
|
||||
t.Error("second set should be a no-op")
|
||||
}
|
||||
|
||||
fresh, err := repo.Get(context.Background(), "hash1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !fresh.TargetSet || fresh.TargetExpiresAt == nil || !fresh.TargetExpiresAt.Equal(target1) {
|
||||
t.Errorf("target should be the first value, got set=%v at=%v",
|
||||
fresh.TargetSet, fresh.TargetExpiresAt)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClaimPaid_Atomic(t *testing.T) {
|
||||
d := newTestDB(t)
|
||||
repo := invoice.NewRepo(d)
|
||||
|
||||
p := &invoice.PendingInvoice{
|
||||
PaymentHash: "hash2",
|
||||
PaymentRequest: "lnbc...",
|
||||
Username: "bob",
|
||||
Pubkey: testHex,
|
||||
SubscriptionType: user.SubYearly,
|
||||
Years: 1,
|
||||
AmountSats: 1000,
|
||||
ExpiresAt: time.Now().Add(30 * time.Minute),
|
||||
}
|
||||
if err := repo.Insert(context.Background(), p); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
won1, err := repo.ClaimPaid(context.Background(), "hash2")
|
||||
if err != nil || !won1 {
|
||||
t.Fatalf("first claim: won=%v err=%v", won1, err)
|
||||
}
|
||||
won2, err := repo.ClaimPaid(context.Background(), "hash2")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if won2 {
|
||||
t.Error("second claim should lose race")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user