package reward import ( "context" "database/sql" "testing" "time" "github.com/wangjia/pangolin/server/internal/config" "github.com/wangjia/pangolin/server/internal/store" ) func openDB(t *testing.T) *sql.DB { db, err := store.Open(&config.Config{Driver: "sqlite", DSN: ":memory:"}) if err != nil { t.Fatal(err) } t.Cleanup(func() { _ = db.Close() }) if err := store.MigrateUp(db, "sqlite"); err != nil { t.Fatal(err) } if err := store.ApplyCodesLibMigrations(context.Background(), db, "sqlite"); err != nil { t.Fatal(err) } return db } func seedU(t *testing.T, db *sql.DB, id int64, uuid string) { _, err := db.Exec(`INSERT INTO users (id,uuid,email,pw_hash,dp_uuid,status,created_at) VALUES (?,?,?, 'x','dp-'||?, 'active', ?)`, id, uuid, uuid+"@x", uuid, time.Now().UTC()) if err != nil { t.Fatal(err) } } func TestEnsureAndResolveInviteCode(t *testing.T) { db := openDB(t) seedU(t, db, 1, "u1") st := NewStore(db) code, err := st.EnsureInviteCode(context.Background(), 1, func() string { return "ABCD2345" }) if err != nil || code != "ABCD2345" { t.Fatalf("ensure: %q %v", code, err) } again, _ := st.EnsureInviteCode(context.Background(), 1, func() string { return "ZZZZ9999" }) if again != "ABCD2345" { t.Fatalf("second ensure changed code: %q", again) } inviter, ok, _ := st.ResolveInviteCode(context.Background(), "ABCD2345") if !ok || inviter != 1 { t.Fatalf("resolve: %d %v", inviter, ok) } } func TestInsertClaimUniqueGuards(t *testing.T) { db := openDB(t) seedU(t, db, 1, "u1") seedU(t, db, 2, "u2") st := NewStore(db) tx, _ := db.Begin() if err := st.InsertClaimTx(context.Background(), tx, 1, "telegram_join", "tg-100", 3, time.Now().UTC()); err != nil { t.Fatal(err) } tx.Commit() // 同 user 再领 → ErrClaimExists tx2, _ := db.Begin() if err := st.InsertClaimTx(context.Background(), tx2, 1, "telegram_join", "tg-999", 3, time.Now().UTC()); err != ErrClaimExists { t.Fatalf("same user reclaim err = %v, want ErrClaimExists", err) } tx2.Rollback() // 同 telegram_id 换 user → ErrClaimExists tx3, _ := db.Begin() if err := st.InsertClaimTx(context.Background(), tx3, 2, "telegram_join", "tg-100", 3, time.Now().UTC()); err != ErrClaimExists { t.Fatalf("same tgid reclaim err = %v, want ErrClaimExists", err) } tx3.Rollback() } func TestInsertReferralUniqueInviteeGuard(t *testing.T) { db := openDB(t) seedU(t, db, 1, "u1") seedU(t, db, 2, "u2") seedU(t, db, 3, "u3") st := NewStore(db) now := time.Now().UTC() tx, _ := db.Begin() if err := st.InsertReferralTx(context.Background(), tx, 1, 2, "dev", "reg_rewarded", &now, now); err != nil { t.Fatal(err) } tx.Commit() // 同一 invitee(2)被另一 inviter(3)再次绑定 → invitee_id UNIQUE 命中,返回 ErrClaimExists tx2, _ := db.Begin() if err := st.InsertReferralTx(context.Background(), tx2, 3, 2, "dev", "reg_rewarded", &now, now); err != ErrClaimExists { t.Fatalf("dup invitee insert err = %v, want ErrClaimExists", err) } tx2.Rollback() }