mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-21 23:22:34 +01:00
Add 148 integration tests exercising actual SQL against PostgreSQL for all previously untested repository files. Includes 6 new fixture helpers in testhelpers_db.go and CI PostgreSQL service configuration. Discovered and documented existing RecordPurchase SQL bug (ambiguous column reference in ON CONFLICT clause).
376 lines
9.8 KiB
Go
376 lines
9.8 KiB
Go
package channelserver
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"testing"
|
|
|
|
"github.com/jmoiron/sqlx"
|
|
)
|
|
|
|
func setupGachaRepo(t *testing.T) (*GachaRepository, *sqlx.DB, uint32) {
|
|
t.Helper()
|
|
db := SetupTestDB(t)
|
|
userID := CreateTestUser(t, db, "gacha_test_user")
|
|
charID := CreateTestCharacter(t, db, userID, "GachaChar")
|
|
repo := NewGachaRepository(db)
|
|
t.Cleanup(func() { TeardownTestDB(t, db) })
|
|
return repo, db, charID
|
|
}
|
|
|
|
func TestRepoGachaListShopEmpty(t *testing.T) {
|
|
repo, _, _ := setupGachaRepo(t)
|
|
|
|
shops, err := repo.ListShop()
|
|
if err != nil {
|
|
t.Fatalf("ListShop failed: %v", err)
|
|
}
|
|
if len(shops) != 0 {
|
|
t.Errorf("Expected empty shop list, got: %d", len(shops))
|
|
}
|
|
}
|
|
|
|
func TestRepoGachaListShop(t *testing.T) {
|
|
repo, db, _ := setupGachaRepo(t)
|
|
|
|
CreateTestGachaShop(t, db, "Test Gacha", 1)
|
|
CreateTestGachaShop(t, db, "Premium Gacha", 2)
|
|
|
|
shops, err := repo.ListShop()
|
|
if err != nil {
|
|
t.Fatalf("ListShop failed: %v", err)
|
|
}
|
|
if len(shops) != 2 {
|
|
t.Fatalf("Expected 2 shops, got: %d", len(shops))
|
|
}
|
|
}
|
|
|
|
func TestRepoGachaGetShopType(t *testing.T) {
|
|
repo, db, _ := setupGachaRepo(t)
|
|
|
|
shopID := CreateTestGachaShop(t, db, "Type Test", 3)
|
|
|
|
gachaType, err := repo.GetShopType(shopID)
|
|
if err != nil {
|
|
t.Fatalf("GetShopType failed: %v", err)
|
|
}
|
|
if gachaType != 3 {
|
|
t.Errorf("Expected gacha_type=3, got: %d", gachaType)
|
|
}
|
|
}
|
|
|
|
func TestRepoGachaGetEntryForTransaction(t *testing.T) {
|
|
repo, db, _ := setupGachaRepo(t)
|
|
|
|
shopID := CreateTestGachaShop(t, db, "Entry Test", 1)
|
|
_, err := db.Exec(
|
|
`INSERT INTO gacha_entries (gacha_id, entry_type, weight, rarity, item_type, item_number, item_quantity, rolls, frontier_points, daily_limit)
|
|
VALUES ($1, 5, 100, 1, 7, 500, 10, 3, 0, 0)`, shopID,
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("Setup failed: %v", err)
|
|
}
|
|
|
|
itemType, itemNumber, rolls, err := repo.GetEntryForTransaction(shopID, 5)
|
|
if err != nil {
|
|
t.Fatalf("GetEntryForTransaction failed: %v", err)
|
|
}
|
|
if itemType != 7 {
|
|
t.Errorf("Expected itemType=7, got: %d", itemType)
|
|
}
|
|
if itemNumber != 500 {
|
|
t.Errorf("Expected itemNumber=500, got: %d", itemNumber)
|
|
}
|
|
if rolls != 3 {
|
|
t.Errorf("Expected rolls=3, got: %d", rolls)
|
|
}
|
|
}
|
|
|
|
func TestRepoGachaGetRewardPoolEmpty(t *testing.T) {
|
|
repo, db, _ := setupGachaRepo(t)
|
|
|
|
shopID := CreateTestGachaShop(t, db, "Empty Pool", 1)
|
|
|
|
entries, err := repo.GetRewardPool(shopID)
|
|
if err != nil {
|
|
t.Fatalf("GetRewardPool failed: %v", err)
|
|
}
|
|
if len(entries) != 0 {
|
|
t.Errorf("Expected empty reward pool, got: %d", len(entries))
|
|
}
|
|
}
|
|
|
|
func TestRepoGachaGetRewardPoolOrdering(t *testing.T) {
|
|
repo, db, _ := setupGachaRepo(t)
|
|
|
|
shopID := CreateTestGachaShop(t, db, "Pool Test", 1)
|
|
// entry_type=100 is the reward pool
|
|
CreateTestGachaEntry(t, db, shopID, 100, 50)
|
|
CreateTestGachaEntry(t, db, shopID, 100, 200)
|
|
CreateTestGachaEntry(t, db, shopID, 100, 100)
|
|
// entry_type=5 should NOT appear in reward pool
|
|
CreateTestGachaEntry(t, db, shopID, 5, 999)
|
|
|
|
entries, err := repo.GetRewardPool(shopID)
|
|
if err != nil {
|
|
t.Fatalf("GetRewardPool failed: %v", err)
|
|
}
|
|
if len(entries) != 3 {
|
|
t.Fatalf("Expected 3 reward entries, got: %d", len(entries))
|
|
}
|
|
// Should be ordered by weight DESC
|
|
if entries[0].Weight < entries[1].Weight || entries[1].Weight < entries[2].Weight {
|
|
t.Errorf("Expected descending weight order, got: %v, %v, %v", entries[0].Weight, entries[1].Weight, entries[2].Weight)
|
|
}
|
|
}
|
|
|
|
func TestRepoGachaGetItemsForEntry(t *testing.T) {
|
|
repo, db, _ := setupGachaRepo(t)
|
|
|
|
shopID := CreateTestGachaShop(t, db, "Items Test", 1)
|
|
entryID := CreateTestGachaEntry(t, db, shopID, 100, 100)
|
|
CreateTestGachaItem(t, db, entryID, 1, 100, 5)
|
|
CreateTestGachaItem(t, db, entryID, 2, 200, 10)
|
|
|
|
items, err := repo.GetItemsForEntry(entryID)
|
|
if err != nil {
|
|
t.Fatalf("GetItemsForEntry failed: %v", err)
|
|
}
|
|
if len(items) != 2 {
|
|
t.Fatalf("Expected 2 items, got: %d", len(items))
|
|
}
|
|
}
|
|
|
|
func TestRepoGachaGetGuaranteedItems(t *testing.T) {
|
|
repo, db, _ := setupGachaRepo(t)
|
|
|
|
shopID := CreateTestGachaShop(t, db, "Guaranteed Test", 1)
|
|
entryID := CreateTestGachaEntry(t, db, shopID, 10, 0)
|
|
CreateTestGachaItem(t, db, entryID, 3, 300, 1)
|
|
|
|
items, err := repo.GetGuaranteedItems(10, shopID)
|
|
if err != nil {
|
|
t.Fatalf("GetGuaranteedItems failed: %v", err)
|
|
}
|
|
if len(items) != 1 {
|
|
t.Fatalf("Expected 1 guaranteed item, got: %d", len(items))
|
|
}
|
|
if items[0].ItemID != 300 {
|
|
t.Errorf("Expected item_id=300, got: %d", items[0].ItemID)
|
|
}
|
|
}
|
|
|
|
func TestRepoGachaGetAllEntries(t *testing.T) {
|
|
repo, db, _ := setupGachaRepo(t)
|
|
|
|
shopID := CreateTestGachaShop(t, db, "All Entries", 1)
|
|
CreateTestGachaEntry(t, db, shopID, 100, 50)
|
|
CreateTestGachaEntry(t, db, shopID, 5, 200)
|
|
|
|
entries, err := repo.GetAllEntries(shopID)
|
|
if err != nil {
|
|
t.Fatalf("GetAllEntries failed: %v", err)
|
|
}
|
|
if len(entries) != 2 {
|
|
t.Fatalf("Expected 2 entries, got: %d", len(entries))
|
|
}
|
|
}
|
|
|
|
func TestRepoGachaGetWeightDivisorZero(t *testing.T) {
|
|
repo, db, _ := setupGachaRepo(t)
|
|
|
|
shopID := CreateTestGachaShop(t, db, "Zero Weight", 1)
|
|
|
|
divisor, err := repo.GetWeightDivisor(shopID)
|
|
if err != nil {
|
|
t.Fatalf("GetWeightDivisor failed: %v", err)
|
|
}
|
|
if divisor != 0 {
|
|
t.Errorf("Expected divisor=0 for empty, got: %f", divisor)
|
|
}
|
|
}
|
|
|
|
func TestRepoGachaGetWeightDivisor(t *testing.T) {
|
|
repo, db, _ := setupGachaRepo(t)
|
|
|
|
shopID := CreateTestGachaShop(t, db, "Weight Test", 1)
|
|
CreateTestGachaEntry(t, db, shopID, 100, 50000)
|
|
CreateTestGachaEntry(t, db, shopID, 100, 50000)
|
|
|
|
divisor, err := repo.GetWeightDivisor(shopID)
|
|
if err != nil {
|
|
t.Fatalf("GetWeightDivisor failed: %v", err)
|
|
}
|
|
// (50000 + 50000) / 100000 = 1.0
|
|
if divisor != 1.0 {
|
|
t.Errorf("Expected divisor=1.0, got: %f", divisor)
|
|
}
|
|
}
|
|
|
|
func TestRepoGachaHasEntryTypeTrue(t *testing.T) {
|
|
repo, db, _ := setupGachaRepo(t)
|
|
|
|
shopID := CreateTestGachaShop(t, db, "HasType Test", 1)
|
|
CreateTestGachaEntry(t, db, shopID, 100, 50)
|
|
|
|
has, err := repo.HasEntryType(shopID, 100)
|
|
if err != nil {
|
|
t.Fatalf("HasEntryType failed: %v", err)
|
|
}
|
|
if !has {
|
|
t.Error("Expected HasEntryType=true for entry_type=100")
|
|
}
|
|
}
|
|
|
|
func TestRepoGachaHasEntryTypeFalse(t *testing.T) {
|
|
repo, db, _ := setupGachaRepo(t)
|
|
|
|
shopID := CreateTestGachaShop(t, db, "HasType False", 1)
|
|
|
|
has, err := repo.HasEntryType(shopID, 100)
|
|
if err != nil {
|
|
t.Fatalf("HasEntryType failed: %v", err)
|
|
}
|
|
if has {
|
|
t.Error("Expected HasEntryType=false for empty gacha")
|
|
}
|
|
}
|
|
|
|
// Stepup tests
|
|
|
|
func TestRepoGachaStepupLifecycle(t *testing.T) {
|
|
repo, db, charID := setupGachaRepo(t)
|
|
|
|
shopID := CreateTestGachaShop(t, db, "Stepup Test", 1)
|
|
|
|
// Insert stepup
|
|
if err := repo.InsertStepup(shopID, 1, charID); err != nil {
|
|
t.Fatalf("InsertStepup failed: %v", err)
|
|
}
|
|
|
|
// Get step
|
|
step, err := repo.GetStepupStep(shopID, charID)
|
|
if err != nil {
|
|
t.Fatalf("GetStepupStep failed: %v", err)
|
|
}
|
|
if step != 1 {
|
|
t.Errorf("Expected step=1, got: %d", step)
|
|
}
|
|
|
|
// Delete stepup
|
|
if err := repo.DeleteStepup(shopID, charID); err != nil {
|
|
t.Fatalf("DeleteStepup failed: %v", err)
|
|
}
|
|
|
|
// Get step should fail
|
|
_, err = repo.GetStepupStep(shopID, charID)
|
|
if err == nil {
|
|
t.Fatal("Expected error after DeleteStepup, got nil")
|
|
}
|
|
}
|
|
|
|
func TestRepoGachaGetStepupWithTime(t *testing.T) {
|
|
repo, db, charID := setupGachaRepo(t)
|
|
|
|
shopID := CreateTestGachaShop(t, db, "Stepup Time", 1)
|
|
|
|
if err := repo.InsertStepup(shopID, 2, charID); err != nil {
|
|
t.Fatalf("InsertStepup failed: %v", err)
|
|
}
|
|
|
|
step, createdAt, err := repo.GetStepupWithTime(shopID, charID)
|
|
if err != nil {
|
|
t.Fatalf("GetStepupWithTime failed: %v", err)
|
|
}
|
|
if step != 2 {
|
|
t.Errorf("Expected step=2, got: %d", step)
|
|
}
|
|
if createdAt.IsZero() {
|
|
t.Error("Expected non-zero created_at")
|
|
}
|
|
}
|
|
|
|
func TestRepoGachaGetStepupWithTimeNotFound(t *testing.T) {
|
|
repo, db, charID := setupGachaRepo(t)
|
|
|
|
shopID := CreateTestGachaShop(t, db, "Stepup NF", 1)
|
|
|
|
_, _, err := repo.GetStepupWithTime(shopID, charID)
|
|
if !errors.Is(err, sql.ErrNoRows) {
|
|
t.Fatalf("Expected sql.ErrNoRows, got: %v", err)
|
|
}
|
|
}
|
|
|
|
// Box gacha tests
|
|
|
|
func TestRepoGachaBoxLifecycle(t *testing.T) {
|
|
repo, db, charID := setupGachaRepo(t)
|
|
|
|
shopID := CreateTestGachaShop(t, db, "Box Test", 1)
|
|
entryID1 := CreateTestGachaEntry(t, db, shopID, 100, 50)
|
|
entryID2 := CreateTestGachaEntry(t, db, shopID, 100, 100)
|
|
|
|
// Initially empty
|
|
ids, err := repo.GetBoxEntryIDs(shopID, charID)
|
|
if err != nil {
|
|
t.Fatalf("GetBoxEntryIDs failed: %v", err)
|
|
}
|
|
if len(ids) != 0 {
|
|
t.Errorf("Expected empty box, got: %d entries", len(ids))
|
|
}
|
|
|
|
// Insert drawn entries
|
|
if err := repo.InsertBoxEntry(shopID, entryID1, charID); err != nil {
|
|
t.Fatalf("InsertBoxEntry failed: %v", err)
|
|
}
|
|
if err := repo.InsertBoxEntry(shopID, entryID2, charID); err != nil {
|
|
t.Fatalf("InsertBoxEntry failed: %v", err)
|
|
}
|
|
|
|
ids, err = repo.GetBoxEntryIDs(shopID, charID)
|
|
if err != nil {
|
|
t.Fatalf("GetBoxEntryIDs failed: %v", err)
|
|
}
|
|
if len(ids) != 2 {
|
|
t.Errorf("Expected 2 box entries, got: %d", len(ids))
|
|
}
|
|
|
|
// Delete all box entries (reset)
|
|
if err := repo.DeleteBoxEntries(shopID, charID); err != nil {
|
|
t.Fatalf("DeleteBoxEntries failed: %v", err)
|
|
}
|
|
|
|
ids, err = repo.GetBoxEntryIDs(shopID, charID)
|
|
if err != nil {
|
|
t.Fatalf("GetBoxEntryIDs after delete failed: %v", err)
|
|
}
|
|
if len(ids) != 0 {
|
|
t.Errorf("Expected empty box after delete, got: %d", len(ids))
|
|
}
|
|
}
|
|
|
|
func TestRepoGachaBoxIsolation(t *testing.T) {
|
|
repo, db, charID := setupGachaRepo(t)
|
|
|
|
shopID := CreateTestGachaShop(t, db, "Box Iso", 1)
|
|
entryID := CreateTestGachaEntry(t, db, shopID, 100, 50)
|
|
|
|
// Create another character
|
|
userID2 := CreateTestUser(t, db, "gacha_other_user")
|
|
charID2 := CreateTestCharacter(t, db, userID2, "GachaChar2")
|
|
|
|
// Char1 draws
|
|
if err := repo.InsertBoxEntry(shopID, entryID, charID); err != nil {
|
|
t.Fatalf("InsertBoxEntry failed: %v", err)
|
|
}
|
|
|
|
// Char2 should have empty box
|
|
ids, err := repo.GetBoxEntryIDs(shopID, charID2)
|
|
if err != nil {
|
|
t.Fatalf("GetBoxEntryIDs for char2 failed: %v", err)
|
|
}
|
|
if len(ids) != 0 {
|
|
t.Errorf("Expected empty box for char2, got: %d entries", len(ids))
|
|
}
|
|
}
|