mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-21 23:22:34 +01:00
test(repos): add SQL integration tests for 17 untested repo files
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).
This commit is contained in:
133
server/channelserver/repo_achievement_test.go
Normal file
133
server/channelserver/repo_achievement_test.go
Normal file
@@ -0,0 +1,133 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
func setupAchievementRepo(t *testing.T) (*AchievementRepository, *sqlx.DB, uint32) {
|
||||
t.Helper()
|
||||
db := SetupTestDB(t)
|
||||
userID := CreateTestUser(t, db, "ach_test_user")
|
||||
charID := CreateTestCharacter(t, db, userID, "AchChar")
|
||||
repo := NewAchievementRepository(db)
|
||||
t.Cleanup(func() { TeardownTestDB(t, db) })
|
||||
return repo, db, charID
|
||||
}
|
||||
|
||||
func TestRepoAchievementEnsureExists(t *testing.T) {
|
||||
repo, db, charID := setupAchievementRepo(t)
|
||||
|
||||
if err := repo.EnsureExists(charID); err != nil {
|
||||
t.Fatalf("EnsureExists failed: %v", err)
|
||||
}
|
||||
|
||||
var count int
|
||||
if err := db.QueryRow("SELECT COUNT(*) FROM achievements WHERE id=$1", charID).Scan(&count); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if count != 1 {
|
||||
t.Errorf("Expected 1 row, got: %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoAchievementEnsureExistsIdempotent(t *testing.T) {
|
||||
repo, db, charID := setupAchievementRepo(t)
|
||||
|
||||
if err := repo.EnsureExists(charID); err != nil {
|
||||
t.Fatalf("First EnsureExists failed: %v", err)
|
||||
}
|
||||
if err := repo.EnsureExists(charID); err != nil {
|
||||
t.Fatalf("Second EnsureExists failed: %v", err)
|
||||
}
|
||||
|
||||
var count int
|
||||
if err := db.QueryRow("SELECT COUNT(*) FROM achievements WHERE id=$1", charID).Scan(&count); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if count != 1 {
|
||||
t.Errorf("Expected 1 row after idempotent calls, got: %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoAchievementGetAllScores(t *testing.T) {
|
||||
repo, db, charID := setupAchievementRepo(t)
|
||||
|
||||
if err := repo.EnsureExists(charID); err != nil {
|
||||
t.Fatalf("EnsureExists failed: %v", err)
|
||||
}
|
||||
|
||||
// Set some scores directly
|
||||
if _, err := db.Exec("UPDATE achievements SET ach0=10, ach5=42, ach32=99 WHERE id=$1", charID); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
|
||||
scores, err := repo.GetAllScores(charID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetAllScores failed: %v", err)
|
||||
}
|
||||
if scores[0] != 10 {
|
||||
t.Errorf("Expected ach0=10, got: %d", scores[0])
|
||||
}
|
||||
if scores[5] != 42 {
|
||||
t.Errorf("Expected ach5=42, got: %d", scores[5])
|
||||
}
|
||||
if scores[32] != 99 {
|
||||
t.Errorf("Expected ach32=99, got: %d", scores[32])
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoAchievementGetAllScoresDefault(t *testing.T) {
|
||||
repo, _, charID := setupAchievementRepo(t)
|
||||
|
||||
if err := repo.EnsureExists(charID); err != nil {
|
||||
t.Fatalf("EnsureExists failed: %v", err)
|
||||
}
|
||||
|
||||
scores, err := repo.GetAllScores(charID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetAllScores failed: %v", err)
|
||||
}
|
||||
for i, s := range scores {
|
||||
if s != 0 {
|
||||
t.Errorf("Expected ach%d=0 by default, got: %d", i, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoAchievementIncrementScore(t *testing.T) {
|
||||
repo, db, charID := setupAchievementRepo(t)
|
||||
|
||||
if err := repo.EnsureExists(charID); err != nil {
|
||||
t.Fatalf("EnsureExists failed: %v", err)
|
||||
}
|
||||
|
||||
if err := repo.IncrementScore(charID, 5); err != nil {
|
||||
t.Fatalf("First IncrementScore failed: %v", err)
|
||||
}
|
||||
if err := repo.IncrementScore(charID, 5); err != nil {
|
||||
t.Fatalf("Second IncrementScore failed: %v", err)
|
||||
}
|
||||
|
||||
var val int32
|
||||
if err := db.QueryRow("SELECT ach5 FROM achievements WHERE id=$1", charID).Scan(&val); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if val != 2 {
|
||||
t.Errorf("Expected ach5=2 after two increments, got: %d", val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoAchievementIncrementScoreOutOfRange(t *testing.T) {
|
||||
repo, _, charID := setupAchievementRepo(t)
|
||||
|
||||
if err := repo.EnsureExists(charID); err != nil {
|
||||
t.Fatalf("EnsureExists failed: %v", err)
|
||||
}
|
||||
|
||||
err := repo.IncrementScore(charID, 33)
|
||||
if err == nil {
|
||||
t.Fatal("Expected error for achievementID=33, got nil")
|
||||
}
|
||||
}
|
||||
162
server/channelserver/repo_cafe_test.go
Normal file
162
server/channelserver/repo_cafe_test.go
Normal file
@@ -0,0 +1,162 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
func setupCafeRepo(t *testing.T) (*CafeRepository, *sqlx.DB, uint32) {
|
||||
t.Helper()
|
||||
db := SetupTestDB(t)
|
||||
userID := CreateTestUser(t, db, "cafe_test_user")
|
||||
charID := CreateTestCharacter(t, db, userID, "CafeChar")
|
||||
repo := NewCafeRepository(db)
|
||||
t.Cleanup(func() { TeardownTestDB(t, db) })
|
||||
return repo, db, charID
|
||||
}
|
||||
|
||||
func createCafeBonus(t *testing.T, db *sqlx.DB, id uint32, timeReq, itemType, itemID, quantity int) {
|
||||
t.Helper()
|
||||
if _, err := db.Exec(
|
||||
"INSERT INTO cafebonus (id, time_req, item_type, item_id, quantity) VALUES ($1, $2, $3, $4, $5)",
|
||||
id, timeReq, itemType, itemID, quantity,
|
||||
); err != nil {
|
||||
t.Fatalf("Failed to create cafe bonus: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoCafeGetBonusesEmpty(t *testing.T) {
|
||||
repo, _, charID := setupCafeRepo(t)
|
||||
|
||||
bonuses, err := repo.GetBonuses(charID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetBonuses failed: %v", err)
|
||||
}
|
||||
if len(bonuses) != 0 {
|
||||
t.Errorf("Expected 0 bonuses, got: %d", len(bonuses))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoCafeGetBonuses(t *testing.T) {
|
||||
repo, db, charID := setupCafeRepo(t)
|
||||
|
||||
createCafeBonus(t, db, 1, 3600, 1, 100, 5)
|
||||
createCafeBonus(t, db, 2, 7200, 2, 200, 10)
|
||||
|
||||
bonuses, err := repo.GetBonuses(charID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetBonuses failed: %v", err)
|
||||
}
|
||||
if len(bonuses) != 2 {
|
||||
t.Fatalf("Expected 2 bonuses, got: %d", len(bonuses))
|
||||
}
|
||||
if bonuses[0].Claimed {
|
||||
t.Error("Expected first bonus unclaimed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoCafeAcceptBonus(t *testing.T) {
|
||||
repo, db, charID := setupCafeRepo(t)
|
||||
|
||||
createCafeBonus(t, db, 1, 3600, 1, 100, 5)
|
||||
|
||||
if err := repo.AcceptBonus(1, charID); err != nil {
|
||||
t.Fatalf("AcceptBonus failed: %v", err)
|
||||
}
|
||||
|
||||
bonuses, err := repo.GetBonuses(charID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetBonuses failed: %v", err)
|
||||
}
|
||||
if len(bonuses) != 1 {
|
||||
t.Fatalf("Expected 1 bonus, got: %d", len(bonuses))
|
||||
}
|
||||
if !bonuses[0].Claimed {
|
||||
t.Error("Expected bonus to be claimed after AcceptBonus")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoCafeResetAccepted(t *testing.T) {
|
||||
repo, db, charID := setupCafeRepo(t)
|
||||
|
||||
createCafeBonus(t, db, 1, 3600, 1, 100, 5)
|
||||
if err := repo.AcceptBonus(1, charID); err != nil {
|
||||
t.Fatalf("AcceptBonus failed: %v", err)
|
||||
}
|
||||
|
||||
if err := repo.ResetAccepted(charID); err != nil {
|
||||
t.Fatalf("ResetAccepted failed: %v", err)
|
||||
}
|
||||
|
||||
bonuses, err := repo.GetBonuses(charID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetBonuses failed: %v", err)
|
||||
}
|
||||
if bonuses[0].Claimed {
|
||||
t.Error("Expected bonus unclaimed after ResetAccepted")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoCafeGetBonusItem(t *testing.T) {
|
||||
repo, db, _ := setupCafeRepo(t)
|
||||
|
||||
createCafeBonus(t, db, 1, 3600, 7, 500, 3)
|
||||
|
||||
itemType, quantity, err := repo.GetBonusItem(1)
|
||||
if err != nil {
|
||||
t.Fatalf("GetBonusItem failed: %v", err)
|
||||
}
|
||||
if itemType != 7 {
|
||||
t.Errorf("Expected itemType=7, got: %d", itemType)
|
||||
}
|
||||
if quantity != 3 {
|
||||
t.Errorf("Expected quantity=3, got: %d", quantity)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoCafeGetClaimable(t *testing.T) {
|
||||
repo, db, charID := setupCafeRepo(t)
|
||||
|
||||
// Set character's cafe_time to 1000 seconds
|
||||
if _, err := db.Exec("UPDATE characters SET cafe_time=1000 WHERE id=$1", charID); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
|
||||
// Bonus requiring 500 seconds total (1000 + 0 elapsed >= 500) - claimable
|
||||
createCafeBonus(t, db, 1, 500, 1, 100, 1)
|
||||
// Bonus requiring 5000 seconds (1000 + 100 elapsed < 5000) - not claimable
|
||||
createCafeBonus(t, db, 2, 5000, 2, 200, 1)
|
||||
|
||||
claimable, err := repo.GetClaimable(charID, 100)
|
||||
if err != nil {
|
||||
t.Fatalf("GetClaimable failed: %v", err)
|
||||
}
|
||||
if len(claimable) != 1 {
|
||||
t.Fatalf("Expected 1 claimable bonus, got: %d", len(claimable))
|
||||
}
|
||||
if claimable[0].ID != 1 {
|
||||
t.Errorf("Expected claimable bonus ID=1, got: %d", claimable[0].ID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoCafeGetClaimableExcludesAccepted(t *testing.T) {
|
||||
repo, db, charID := setupCafeRepo(t)
|
||||
|
||||
if _, err := db.Exec("UPDATE characters SET cafe_time=10000 WHERE id=$1", charID); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
|
||||
createCafeBonus(t, db, 1, 100, 1, 100, 1)
|
||||
if err := repo.AcceptBonus(1, charID); err != nil {
|
||||
t.Fatalf("AcceptBonus failed: %v", err)
|
||||
}
|
||||
|
||||
claimable, err := repo.GetClaimable(charID, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("GetClaimable failed: %v", err)
|
||||
}
|
||||
if len(claimable) != 0 {
|
||||
t.Errorf("Expected 0 claimable after accept, got: %d", len(claimable))
|
||||
}
|
||||
}
|
||||
146
server/channelserver/repo_distribution_test.go
Normal file
146
server/channelserver/repo_distribution_test.go
Normal file
@@ -0,0 +1,146 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
func setupDistributionRepo(t *testing.T) (*DistributionRepository, *sqlx.DB, uint32) {
|
||||
t.Helper()
|
||||
db := SetupTestDB(t)
|
||||
userID := CreateTestUser(t, db, "dist_test_user")
|
||||
charID := CreateTestCharacter(t, db, userID, "DistChar")
|
||||
repo := NewDistributionRepository(db)
|
||||
t.Cleanup(func() { TeardownTestDB(t, db) })
|
||||
return repo, db, charID
|
||||
}
|
||||
|
||||
func createDistribution(t *testing.T, db *sqlx.DB, charID *uint32, distType int, eventName, description string) uint32 {
|
||||
t.Helper()
|
||||
var id uint32
|
||||
err := db.QueryRow(
|
||||
`INSERT INTO distribution (character_id, type, event_name, description, data, times_acceptable)
|
||||
VALUES ($1, $2, $3, $4, $5, 1) RETURNING id`,
|
||||
charID, distType, eventName, description, []byte{0x00},
|
||||
).Scan(&id)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create distribution: %v", err)
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
func TestRepoDistributionListEmpty(t *testing.T) {
|
||||
repo, _, charID := setupDistributionRepo(t)
|
||||
|
||||
dists, err := repo.List(charID, 1)
|
||||
if err != nil {
|
||||
t.Fatalf("List failed: %v", err)
|
||||
}
|
||||
if len(dists) != 0 {
|
||||
t.Errorf("Expected 0 distributions, got: %d", len(dists))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoDistributionListCharacterSpecific(t *testing.T) {
|
||||
repo, db, charID := setupDistributionRepo(t)
|
||||
|
||||
createDistribution(t, db, &charID, 1, "Personal Gift", "For you")
|
||||
|
||||
dists, err := repo.List(charID, 1)
|
||||
if err != nil {
|
||||
t.Fatalf("List failed: %v", err)
|
||||
}
|
||||
if len(dists) != 1 {
|
||||
t.Fatalf("Expected 1 distribution, got: %d", len(dists))
|
||||
}
|
||||
if dists[0].EventName != "Personal Gift" {
|
||||
t.Errorf("Expected event_name='Personal Gift', got: %q", dists[0].EventName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoDistributionListGlobal(t *testing.T) {
|
||||
repo, db, charID := setupDistributionRepo(t)
|
||||
|
||||
// Global distribution (character_id=NULL)
|
||||
createDistribution(t, db, nil, 1, "Global Gift", "For everyone")
|
||||
|
||||
dists, err := repo.List(charID, 1)
|
||||
if err != nil {
|
||||
t.Fatalf("List failed: %v", err)
|
||||
}
|
||||
if len(dists) != 1 {
|
||||
t.Fatalf("Expected 1 global distribution, got: %d", len(dists))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoDistributionGetItems(t *testing.T) {
|
||||
repo, db, charID := setupDistributionRepo(t)
|
||||
|
||||
distID := createDistribution(t, db, &charID, 1, "Item Gift", "Has items")
|
||||
if _, err := db.Exec("INSERT INTO distribution_items (distribution_id, item_type, item_id, quantity) VALUES ($1, 1, 100, 5)", distID); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
if _, err := db.Exec("INSERT INTO distribution_items (distribution_id, item_type, item_id, quantity) VALUES ($1, 2, 200, 10)", distID); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
|
||||
items, err := repo.GetItems(distID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetItems failed: %v", err)
|
||||
}
|
||||
if len(items) != 2 {
|
||||
t.Fatalf("Expected 2 items, got: %d", len(items))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoDistributionRecordAccepted(t *testing.T) {
|
||||
repo, db, charID := setupDistributionRepo(t)
|
||||
|
||||
distID := createDistribution(t, db, &charID, 1, "Accept Test", "Test")
|
||||
|
||||
if err := repo.RecordAccepted(distID, charID); err != nil {
|
||||
t.Fatalf("RecordAccepted failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify accepted count in list
|
||||
dists, err := repo.List(charID, 1)
|
||||
if err != nil {
|
||||
t.Fatalf("List failed: %v", err)
|
||||
}
|
||||
if len(dists) != 1 {
|
||||
t.Fatalf("Expected 1 distribution, got: %d", len(dists))
|
||||
}
|
||||
if dists[0].TimesAccepted != 1 {
|
||||
t.Errorf("Expected times_accepted=1, got: %d", dists[0].TimesAccepted)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoDistributionGetDescription(t *testing.T) {
|
||||
repo, db, charID := setupDistributionRepo(t)
|
||||
|
||||
distID := createDistribution(t, db, &charID, 1, "Desc Test", "~C05Special reward!")
|
||||
|
||||
desc, err := repo.GetDescription(distID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetDescription failed: %v", err)
|
||||
}
|
||||
if desc != "~C05Special reward!" {
|
||||
t.Errorf("Expected description='~C05Special reward!', got: %q", desc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoDistributionFiltersByType(t *testing.T) {
|
||||
repo, db, charID := setupDistributionRepo(t)
|
||||
|
||||
createDistribution(t, db, &charID, 1, "Type 1", "Type 1")
|
||||
createDistribution(t, db, &charID, 2, "Type 2", "Type 2")
|
||||
|
||||
dists, err := repo.List(charID, 1)
|
||||
if err != nil {
|
||||
t.Fatalf("List failed: %v", err)
|
||||
}
|
||||
if len(dists) != 1 {
|
||||
t.Errorf("Expected 1 distribution of type 1, got: %d", len(dists))
|
||||
}
|
||||
}
|
||||
113
server/channelserver/repo_diva_test.go
Normal file
113
server/channelserver/repo_diva_test.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
func setupDivaRepo(t *testing.T) (*DivaRepository, *sqlx.DB) {
|
||||
t.Helper()
|
||||
db := SetupTestDB(t)
|
||||
repo := NewDivaRepository(db)
|
||||
t.Cleanup(func() { TeardownTestDB(t, db) })
|
||||
return repo, db
|
||||
}
|
||||
|
||||
func TestRepoDivaInsertAndGetEvents(t *testing.T) {
|
||||
repo, _ := setupDivaRepo(t)
|
||||
|
||||
if err := repo.InsertEvent(1700000000); err != nil {
|
||||
t.Fatalf("InsertEvent failed: %v", err)
|
||||
}
|
||||
|
||||
events, err := repo.GetEvents()
|
||||
if err != nil {
|
||||
t.Fatalf("GetEvents failed: %v", err)
|
||||
}
|
||||
if len(events) != 1 {
|
||||
t.Fatalf("Expected 1 event, got: %d", len(events))
|
||||
}
|
||||
if events[0].StartTime != 1700000000 {
|
||||
t.Errorf("Expected start_time=1700000000, got: %d", events[0].StartTime)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoDivaGetEventsEmpty(t *testing.T) {
|
||||
repo, _ := setupDivaRepo(t)
|
||||
|
||||
events, err := repo.GetEvents()
|
||||
if err != nil {
|
||||
t.Fatalf("GetEvents failed: %v", err)
|
||||
}
|
||||
if len(events) != 0 {
|
||||
t.Errorf("Expected 0 events, got: %d", len(events))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoDivaDeleteEvents(t *testing.T) {
|
||||
repo, _ := setupDivaRepo(t)
|
||||
|
||||
if err := repo.InsertEvent(1700000000); err != nil {
|
||||
t.Fatalf("InsertEvent failed: %v", err)
|
||||
}
|
||||
if err := repo.InsertEvent(1700100000); err != nil {
|
||||
t.Fatalf("InsertEvent failed: %v", err)
|
||||
}
|
||||
|
||||
if err := repo.DeleteEvents(); err != nil {
|
||||
t.Fatalf("DeleteEvents failed: %v", err)
|
||||
}
|
||||
|
||||
events, err := repo.GetEvents()
|
||||
if err != nil {
|
||||
t.Fatalf("GetEvents failed: %v", err)
|
||||
}
|
||||
if len(events) != 0 {
|
||||
t.Errorf("Expected 0 events after delete, got: %d", len(events))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoDivaMultipleEvents(t *testing.T) {
|
||||
repo, _ := setupDivaRepo(t)
|
||||
|
||||
if err := repo.InsertEvent(1700000000); err != nil {
|
||||
t.Fatalf("InsertEvent 1 failed: %v", err)
|
||||
}
|
||||
if err := repo.InsertEvent(1700100000); err != nil {
|
||||
t.Fatalf("InsertEvent 2 failed: %v", err)
|
||||
}
|
||||
|
||||
events, err := repo.GetEvents()
|
||||
if err != nil {
|
||||
t.Fatalf("GetEvents failed: %v", err)
|
||||
}
|
||||
if len(events) != 2 {
|
||||
t.Errorf("Expected 2 events, got: %d", len(events))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoDivaDeleteOnlyDivaEvents(t *testing.T) {
|
||||
repo, db := setupDivaRepo(t)
|
||||
|
||||
// Insert a diva event
|
||||
if err := repo.InsertEvent(1700000000); err != nil {
|
||||
t.Fatalf("InsertEvent failed: %v", err)
|
||||
}
|
||||
// Insert a festa event (should not be deleted)
|
||||
if _, err := db.Exec("INSERT INTO events (event_type, start_time) VALUES ('festa', now())"); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
|
||||
if err := repo.DeleteEvents(); err != nil {
|
||||
t.Fatalf("DeleteEvents failed: %v", err)
|
||||
}
|
||||
|
||||
var count int
|
||||
if err := db.QueryRow("SELECT COUNT(*) FROM events WHERE event_type='festa'").Scan(&count); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if count != 1 {
|
||||
t.Errorf("Expected festa event to survive, got count=%d", count)
|
||||
}
|
||||
}
|
||||
261
server/channelserver/repo_festa_test.go
Normal file
261
server/channelserver/repo_festa_test.go
Normal file
@@ -0,0 +1,261 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
func setupFestaRepo(t *testing.T) (*FestaRepository, *sqlx.DB, uint32, uint32) {
|
||||
t.Helper()
|
||||
db := SetupTestDB(t)
|
||||
userID := CreateTestUser(t, db, "festa_test_user")
|
||||
charID := CreateTestCharacter(t, db, userID, "FestaChar")
|
||||
guildID := CreateTestGuild(t, db, charID, "FestaGuild")
|
||||
repo := NewFestaRepository(db)
|
||||
t.Cleanup(func() { TeardownTestDB(t, db) })
|
||||
return repo, db, charID, guildID
|
||||
}
|
||||
|
||||
func TestRepoFestaInsertAndGetEvents(t *testing.T) {
|
||||
repo, _, _, _ := setupFestaRepo(t)
|
||||
|
||||
startTime := uint32(time.Date(2025, 6, 1, 0, 0, 0, 0, time.UTC).Unix())
|
||||
if err := repo.InsertEvent(startTime); err != nil {
|
||||
t.Fatalf("InsertEvent failed: %v", err)
|
||||
}
|
||||
|
||||
events, err := repo.GetFestaEvents()
|
||||
if err != nil {
|
||||
t.Fatalf("GetFestaEvents failed: %v", err)
|
||||
}
|
||||
if len(events) != 1 {
|
||||
t.Fatalf("Expected 1 event, got: %d", len(events))
|
||||
}
|
||||
if events[0].StartTime != startTime {
|
||||
t.Errorf("Expected start_time=%d, got: %d", startTime, events[0].StartTime)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoFestaCleanupAll(t *testing.T) {
|
||||
repo, _, _, _ := setupFestaRepo(t)
|
||||
|
||||
if err := repo.InsertEvent(1000000); err != nil {
|
||||
t.Fatalf("InsertEvent failed: %v", err)
|
||||
}
|
||||
|
||||
if err := repo.CleanupAll(); err != nil {
|
||||
t.Fatalf("CleanupAll failed: %v", err)
|
||||
}
|
||||
|
||||
events, err := repo.GetFestaEvents()
|
||||
if err != nil {
|
||||
t.Fatalf("GetFestaEvents failed: %v", err)
|
||||
}
|
||||
if len(events) != 0 {
|
||||
t.Errorf("Expected 0 events after cleanup, got: %d", len(events))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoFestaRegisterGuild(t *testing.T) {
|
||||
repo, db, _, guildID := setupFestaRepo(t)
|
||||
|
||||
if err := repo.RegisterGuild(guildID, "blue"); err != nil {
|
||||
t.Fatalf("RegisterGuild failed: %v", err)
|
||||
}
|
||||
|
||||
var team string
|
||||
if err := db.QueryRow("SELECT team FROM festa_registrations WHERE guild_id=$1", guildID).Scan(&team); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if team != "blue" {
|
||||
t.Errorf("Expected team='blue', got: %q", team)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoFestaGetTeamSouls(t *testing.T) {
|
||||
repo, _, _, guildID := setupFestaRepo(t)
|
||||
|
||||
if err := repo.RegisterGuild(guildID, "red"); err != nil {
|
||||
t.Fatalf("RegisterGuild failed: %v", err)
|
||||
}
|
||||
|
||||
souls, err := repo.GetTeamSouls("red")
|
||||
if err != nil {
|
||||
t.Fatalf("GetTeamSouls failed: %v", err)
|
||||
}
|
||||
// No submissions yet, should be 0
|
||||
if souls != 0 {
|
||||
t.Errorf("Expected souls=0, got: %d", souls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoFestaSubmitSouls(t *testing.T) {
|
||||
repo, _, charID, guildID := setupFestaRepo(t)
|
||||
|
||||
if err := repo.RegisterGuild(guildID, "blue"); err != nil {
|
||||
t.Fatalf("RegisterGuild failed: %v", err)
|
||||
}
|
||||
|
||||
souls := []uint16{10, 20, 30}
|
||||
if err := repo.SubmitSouls(charID, guildID, souls); err != nil {
|
||||
t.Fatalf("SubmitSouls failed: %v", err)
|
||||
}
|
||||
|
||||
charSouls, err := repo.GetCharSouls(charID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetCharSouls failed: %v", err)
|
||||
}
|
||||
// 10 + 20 + 30 = 60
|
||||
if charSouls != 60 {
|
||||
t.Errorf("Expected charSouls=60, got: %d", charSouls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoFestaGetCharSoulsEmpty(t *testing.T) {
|
||||
repo, _, charID, _ := setupFestaRepo(t)
|
||||
|
||||
souls, err := repo.GetCharSouls(charID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetCharSouls failed: %v", err)
|
||||
}
|
||||
if souls != 0 {
|
||||
t.Errorf("Expected souls=0, got: %d", souls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoFestaVoteTrial(t *testing.T) {
|
||||
repo, db, charID, _ := setupFestaRepo(t)
|
||||
|
||||
if err := repo.VoteTrial(charID, 42); err != nil {
|
||||
t.Fatalf("VoteTrial failed: %v", err)
|
||||
}
|
||||
|
||||
var trialVote *uint32
|
||||
if err := db.QueryRow("SELECT trial_vote FROM guild_characters WHERE character_id=$1", charID).Scan(&trialVote); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if trialVote == nil || *trialVote != 42 {
|
||||
t.Errorf("Expected trial_vote=42, got: %v", trialVote)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoFestaClaimPrize(t *testing.T) {
|
||||
repo, db, charID, _ := setupFestaRepo(t)
|
||||
|
||||
if err := repo.ClaimPrize(5, charID); err != nil {
|
||||
t.Fatalf("ClaimPrize failed: %v", err)
|
||||
}
|
||||
|
||||
var count int
|
||||
if err := db.QueryRow("SELECT COUNT(*) FROM festa_prizes_accepted WHERE prize_id=5 AND character_id=$1", charID).Scan(&count); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if count != 1 {
|
||||
t.Errorf("Expected 1 accepted prize, got: %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoFestaHasClaimedMainPrize(t *testing.T) {
|
||||
repo, _, charID, _ := setupFestaRepo(t)
|
||||
|
||||
// Not claimed yet
|
||||
if repo.HasClaimedMainPrize(charID) {
|
||||
t.Error("Expected HasClaimedMainPrize=false before claiming")
|
||||
}
|
||||
|
||||
// Claim main prize (ID=0)
|
||||
if err := repo.ClaimPrize(0, charID); err != nil {
|
||||
t.Fatalf("ClaimPrize failed: %v", err)
|
||||
}
|
||||
|
||||
if !repo.HasClaimedMainPrize(charID) {
|
||||
t.Error("Expected HasClaimedMainPrize=true after claiming")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoFestaListPrizes(t *testing.T) {
|
||||
repo, db, charID, _ := setupFestaRepo(t)
|
||||
|
||||
if _, err := db.Exec("INSERT INTO festa_prizes (id, type, tier, souls_req, item_id, num_item) VALUES (1, 'personal', 1, 100, 500, 1)"); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
if _, err := db.Exec("INSERT INTO festa_prizes (id, type, tier, souls_req, item_id, num_item) VALUES (2, 'personal', 2, 200, 600, 2)"); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
if _, err := db.Exec("INSERT INTO festa_prizes (id, type, tier, souls_req, item_id, num_item) VALUES (3, 'guild', 1, 300, 700, 3)"); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
|
||||
prizes, err := repo.ListPrizes(charID, "personal")
|
||||
if err != nil {
|
||||
t.Fatalf("ListPrizes failed: %v", err)
|
||||
}
|
||||
if len(prizes) != 2 {
|
||||
t.Fatalf("Expected 2 personal prizes, got: %d", len(prizes))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoFestaListPrizesWithClaimed(t *testing.T) {
|
||||
repo, db, charID, _ := setupFestaRepo(t)
|
||||
|
||||
if _, err := db.Exec("INSERT INTO festa_prizes (id, type, tier, souls_req, item_id, num_item) VALUES (1, 'personal', 1, 100, 500, 1)"); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
|
||||
if err := repo.ClaimPrize(1, charID); err != nil {
|
||||
t.Fatalf("ClaimPrize failed: %v", err)
|
||||
}
|
||||
|
||||
prizes, err := repo.ListPrizes(charID, "personal")
|
||||
if err != nil {
|
||||
t.Fatalf("ListPrizes failed: %v", err)
|
||||
}
|
||||
if len(prizes) != 1 {
|
||||
t.Fatalf("Expected 1 prize, got: %d", len(prizes))
|
||||
}
|
||||
if prizes[0].Claimed != 1 {
|
||||
t.Errorf("Expected claimed=1, got: %d", prizes[0].Claimed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoFestaGetTeamSoulsWithSubmissions(t *testing.T) {
|
||||
repo, db, charID, guildID := setupFestaRepo(t)
|
||||
|
||||
if err := repo.RegisterGuild(guildID, "blue"); err != nil {
|
||||
t.Fatalf("RegisterGuild failed: %v", err)
|
||||
}
|
||||
|
||||
// Create second guild on red team
|
||||
user2 := CreateTestUser(t, db, "festa_user2")
|
||||
char2 := CreateTestCharacter(t, db, user2, "FestaChar2")
|
||||
guild2 := CreateTestGuild(t, db, char2, "RedGuild")
|
||||
if err := repo.RegisterGuild(guild2, "red"); err != nil {
|
||||
t.Fatalf("RegisterGuild failed: %v", err)
|
||||
}
|
||||
|
||||
// Submit souls
|
||||
if err := repo.SubmitSouls(charID, guildID, []uint16{50}); err != nil {
|
||||
t.Fatalf("SubmitSouls blue failed: %v", err)
|
||||
}
|
||||
if err := repo.SubmitSouls(char2, guild2, []uint16{30}); err != nil {
|
||||
t.Fatalf("SubmitSouls red failed: %v", err)
|
||||
}
|
||||
|
||||
blueSouls, err := repo.GetTeamSouls("blue")
|
||||
if err != nil {
|
||||
t.Fatalf("GetTeamSouls(blue) failed: %v", err)
|
||||
}
|
||||
if blueSouls != 50 {
|
||||
t.Errorf("Expected blue souls=50, got: %d", blueSouls)
|
||||
}
|
||||
|
||||
redSouls, err := repo.GetTeamSouls("red")
|
||||
if err != nil {
|
||||
t.Fatalf("GetTeamSouls(red) failed: %v", err)
|
||||
}
|
||||
if redSouls != 30 {
|
||||
t.Errorf("Expected red souls=30, got: %d", redSouls)
|
||||
}
|
||||
}
|
||||
375
server/channelserver/repo_gacha_test.go
Normal file
375
server/channelserver/repo_gacha_test.go
Normal file
@@ -0,0 +1,375 @@
|
||||
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))
|
||||
}
|
||||
}
|
||||
152
server/channelserver/repo_goocoo_test.go
Normal file
152
server/channelserver/repo_goocoo_test.go
Normal file
@@ -0,0 +1,152 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
func setupGoocooRepo(t *testing.T) (*GoocooRepository, *sqlx.DB, uint32) {
|
||||
t.Helper()
|
||||
db := SetupTestDB(t)
|
||||
userID := CreateTestUser(t, db, "goocoo_test_user")
|
||||
charID := CreateTestCharacter(t, db, userID, "GoocooChar")
|
||||
repo := NewGoocooRepository(db)
|
||||
t.Cleanup(func() { TeardownTestDB(t, db) })
|
||||
return repo, db, charID
|
||||
}
|
||||
|
||||
func TestRepoGoocooEnsureExists(t *testing.T) {
|
||||
repo, db, charID := setupGoocooRepo(t)
|
||||
|
||||
if err := repo.EnsureExists(charID); err != nil {
|
||||
t.Fatalf("EnsureExists failed: %v", err)
|
||||
}
|
||||
|
||||
var count int
|
||||
if err := db.QueryRow("SELECT COUNT(*) FROM goocoo WHERE id=$1", charID).Scan(&count); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if count != 1 {
|
||||
t.Errorf("Expected 1 goocoo row, got: %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoGoocooEnsureExistsIdempotent(t *testing.T) {
|
||||
repo, _, charID := setupGoocooRepo(t)
|
||||
|
||||
if err := repo.EnsureExists(charID); err != nil {
|
||||
t.Fatalf("First EnsureExists failed: %v", err)
|
||||
}
|
||||
if err := repo.EnsureExists(charID); err != nil {
|
||||
t.Fatalf("Second EnsureExists failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoGoocooSaveAndGetSlot(t *testing.T) {
|
||||
repo, _, charID := setupGoocooRepo(t)
|
||||
|
||||
if err := repo.EnsureExists(charID); err != nil {
|
||||
t.Fatalf("EnsureExists failed: %v", err)
|
||||
}
|
||||
|
||||
data := []byte{0xAA, 0xBB, 0xCC}
|
||||
if err := repo.SaveSlot(charID, 0, data); err != nil {
|
||||
t.Fatalf("SaveSlot failed: %v", err)
|
||||
}
|
||||
|
||||
got, err := repo.GetSlot(charID, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("GetSlot failed: %v", err)
|
||||
}
|
||||
if len(got) != 3 || got[0] != 0xAA {
|
||||
t.Errorf("Expected saved data, got: %x", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoGoocooGetSlotNull(t *testing.T) {
|
||||
repo, _, charID := setupGoocooRepo(t)
|
||||
|
||||
if err := repo.EnsureExists(charID); err != nil {
|
||||
t.Fatalf("EnsureExists failed: %v", err)
|
||||
}
|
||||
|
||||
got, err := repo.GetSlot(charID, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("GetSlot failed: %v", err)
|
||||
}
|
||||
if got != nil {
|
||||
t.Errorf("Expected nil for NULL slot, got: %x", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoGoocooSaveMultipleSlots(t *testing.T) {
|
||||
repo, _, charID := setupGoocooRepo(t)
|
||||
|
||||
if err := repo.EnsureExists(charID); err != nil {
|
||||
t.Fatalf("EnsureExists failed: %v", err)
|
||||
}
|
||||
|
||||
if err := repo.SaveSlot(charID, 0, []byte{0x01}); err != nil {
|
||||
t.Fatalf("SaveSlot(0) failed: %v", err)
|
||||
}
|
||||
if err := repo.SaveSlot(charID, 3, []byte{0x04}); err != nil {
|
||||
t.Fatalf("SaveSlot(3) failed: %v", err)
|
||||
}
|
||||
|
||||
got0, _ := repo.GetSlot(charID, 0)
|
||||
got3, _ := repo.GetSlot(charID, 3)
|
||||
if len(got0) != 1 || got0[0] != 0x01 {
|
||||
t.Errorf("Slot 0 unexpected: %x", got0)
|
||||
}
|
||||
if len(got3) != 1 || got3[0] != 0x04 {
|
||||
t.Errorf("Slot 3 unexpected: %x", got3)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoGoococClearSlot(t *testing.T) {
|
||||
repo, _, charID := setupGoocooRepo(t)
|
||||
|
||||
if err := repo.EnsureExists(charID); err != nil {
|
||||
t.Fatalf("EnsureExists failed: %v", err)
|
||||
}
|
||||
|
||||
if err := repo.SaveSlot(charID, 2, []byte{0xFF}); err != nil {
|
||||
t.Fatalf("SaveSlot failed: %v", err)
|
||||
}
|
||||
|
||||
if err := repo.ClearSlot(charID, 2); err != nil {
|
||||
t.Fatalf("ClearSlot failed: %v", err)
|
||||
}
|
||||
|
||||
got, err := repo.GetSlot(charID, 2)
|
||||
if err != nil {
|
||||
t.Fatalf("GetSlot failed: %v", err)
|
||||
}
|
||||
if got != nil {
|
||||
t.Errorf("Expected nil after ClearSlot, got: %x", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoGoocooInvalidSlot(t *testing.T) {
|
||||
repo, _, charID := setupGoocooRepo(t)
|
||||
|
||||
if err := repo.EnsureExists(charID); err != nil {
|
||||
t.Fatalf("EnsureExists failed: %v", err)
|
||||
}
|
||||
|
||||
_, err := repo.GetSlot(charID, 5)
|
||||
if err == nil {
|
||||
t.Fatal("Expected error for invalid slot index 5")
|
||||
}
|
||||
|
||||
err = repo.SaveSlot(charID, 5, []byte{0x00})
|
||||
if err == nil {
|
||||
t.Fatal("Expected error for SaveSlot with invalid slot index 5")
|
||||
}
|
||||
|
||||
err = repo.ClearSlot(charID, 5)
|
||||
if err == nil {
|
||||
t.Fatal("Expected error for ClearSlot with invalid slot index 5")
|
||||
}
|
||||
}
|
||||
377
server/channelserver/repo_house_test.go
Normal file
377
server/channelserver/repo_house_test.go
Normal file
@@ -0,0 +1,377 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
func setupHouseRepo(t *testing.T) (*HouseRepository, *sqlx.DB, uint32) {
|
||||
t.Helper()
|
||||
db := SetupTestDB(t)
|
||||
userID := CreateTestUser(t, db, "house_test_user")
|
||||
charID := CreateTestCharacter(t, db, userID, "HouseChar")
|
||||
CreateTestUserBinary(t, db, charID)
|
||||
repo := NewHouseRepository(db)
|
||||
t.Cleanup(func() { TeardownTestDB(t, db) })
|
||||
return repo, db, charID
|
||||
}
|
||||
|
||||
func TestRepoHouseGetHouseByCharID(t *testing.T) {
|
||||
repo, _, charID := setupHouseRepo(t)
|
||||
|
||||
house, err := repo.GetHouseByCharID(charID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetHouseByCharID failed: %v", err)
|
||||
}
|
||||
if house.CharID != charID {
|
||||
t.Errorf("Expected charID=%d, got: %d", charID, house.CharID)
|
||||
}
|
||||
if house.Name != "HouseChar" {
|
||||
t.Errorf("Expected name='HouseChar', got: %q", house.Name)
|
||||
}
|
||||
// Default house_state is 2 (password-protected) via COALESCE
|
||||
if house.HouseState != 2 {
|
||||
t.Errorf("Expected default house_state=2, got: %d", house.HouseState)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoHouseSearchHousesByName(t *testing.T) {
|
||||
repo, db, _ := setupHouseRepo(t)
|
||||
|
||||
user2 := CreateTestUser(t, db, "house_user2")
|
||||
charID2 := CreateTestCharacter(t, db, user2, "HouseAlpha")
|
||||
CreateTestUserBinary(t, db, charID2)
|
||||
user3 := CreateTestUser(t, db, "house_user3")
|
||||
charID3 := CreateTestCharacter(t, db, user3, "BetaHouse")
|
||||
CreateTestUserBinary(t, db, charID3)
|
||||
|
||||
houses, err := repo.SearchHousesByName("House")
|
||||
if err != nil {
|
||||
t.Fatalf("SearchHousesByName failed: %v", err)
|
||||
}
|
||||
if len(houses) < 2 {
|
||||
t.Errorf("Expected at least 2 matches for 'House', got: %d", len(houses))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoHouseSearchHousesByNameNoMatch(t *testing.T) {
|
||||
repo, _, _ := setupHouseRepo(t)
|
||||
|
||||
houses, err := repo.SearchHousesByName("ZZZnonexistent")
|
||||
if err != nil {
|
||||
t.Fatalf("SearchHousesByName failed: %v", err)
|
||||
}
|
||||
if len(houses) != 0 {
|
||||
t.Errorf("Expected 0 matches, got: %d", len(houses))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoHouseUpdateHouseState(t *testing.T) {
|
||||
repo, _, charID := setupHouseRepo(t)
|
||||
|
||||
if err := repo.UpdateHouseState(charID, 1, "secret"); err != nil {
|
||||
t.Fatalf("UpdateHouseState failed: %v", err)
|
||||
}
|
||||
|
||||
state, password, err := repo.GetHouseAccess(charID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetHouseAccess failed: %v", err)
|
||||
}
|
||||
if state != 1 {
|
||||
t.Errorf("Expected state=1, got: %d", state)
|
||||
}
|
||||
if password != "secret" {
|
||||
t.Errorf("Expected password='secret', got: %q", password)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoHouseGetHouseAccessDefault(t *testing.T) {
|
||||
repo, _, charID := setupHouseRepo(t)
|
||||
|
||||
state, password, err := repo.GetHouseAccess(charID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetHouseAccess failed: %v", err)
|
||||
}
|
||||
if state != 2 {
|
||||
t.Errorf("Expected default state=2, got: %d", state)
|
||||
}
|
||||
if password != "" {
|
||||
t.Errorf("Expected empty password, got: %q", password)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoHouseUpdateInterior(t *testing.T) {
|
||||
repo, db, charID := setupHouseRepo(t)
|
||||
|
||||
furniture := []byte{0x01, 0x02, 0x03}
|
||||
if err := repo.UpdateInterior(charID, furniture); err != nil {
|
||||
t.Fatalf("UpdateInterior failed: %v", err)
|
||||
}
|
||||
|
||||
var got []byte
|
||||
if err := db.QueryRow("SELECT house_furniture FROM user_binary WHERE id=$1", charID).Scan(&got); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if len(got) != 3 || got[0] != 0x01 {
|
||||
t.Errorf("Expected furniture data, got: %x", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoHouseGetHouseContents(t *testing.T) {
|
||||
repo, db, charID := setupHouseRepo(t)
|
||||
|
||||
tier := []byte{0x01}
|
||||
data := []byte{0x02}
|
||||
furniture := []byte{0x03}
|
||||
bookshelf := []byte{0x04}
|
||||
gallery := []byte{0x05}
|
||||
tore := []byte{0x06}
|
||||
garden := []byte{0x07}
|
||||
if _, err := db.Exec(
|
||||
"UPDATE user_binary SET house_tier=$1, house_data=$2, house_furniture=$3, bookshelf=$4, gallery=$5, tore=$6, garden=$7 WHERE id=$8",
|
||||
tier, data, furniture, bookshelf, gallery, tore, garden, charID,
|
||||
); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
|
||||
gotTier, gotData, gotFurniture, gotBookshelf, gotGallery, gotTore, gotGarden, err := repo.GetHouseContents(charID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetHouseContents failed: %v", err)
|
||||
}
|
||||
if len(gotTier) != 1 || gotTier[0] != 0x01 {
|
||||
t.Errorf("Unexpected tier: %x", gotTier)
|
||||
}
|
||||
if len(gotData) != 1 || gotData[0] != 0x02 {
|
||||
t.Errorf("Unexpected data: %x", gotData)
|
||||
}
|
||||
if len(gotFurniture) != 1 || gotFurniture[0] != 0x03 {
|
||||
t.Errorf("Unexpected furniture: %x", gotFurniture)
|
||||
}
|
||||
if len(gotBookshelf) != 1 || gotBookshelf[0] != 0x04 {
|
||||
t.Errorf("Unexpected bookshelf: %x", gotBookshelf)
|
||||
}
|
||||
if len(gotGallery) != 1 || gotGallery[0] != 0x05 {
|
||||
t.Errorf("Unexpected gallery: %x", gotGallery)
|
||||
}
|
||||
if len(gotTore) != 1 || gotTore[0] != 0x06 {
|
||||
t.Errorf("Unexpected tore: %x", gotTore)
|
||||
}
|
||||
if len(gotGarden) != 1 || gotGarden[0] != 0x07 {
|
||||
t.Errorf("Unexpected garden: %x", gotGarden)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoHouseGetMission(t *testing.T) {
|
||||
repo, db, charID := setupHouseRepo(t)
|
||||
|
||||
mission := []byte{0xAA, 0xBB}
|
||||
if _, err := db.Exec("UPDATE user_binary SET mission=$1 WHERE id=$2", mission, charID); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
|
||||
got, err := repo.GetMission(charID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetMission failed: %v", err)
|
||||
}
|
||||
if len(got) != 2 || got[0] != 0xAA {
|
||||
t.Errorf("Expected mission data, got: %x", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoHouseUpdateMission(t *testing.T) {
|
||||
repo, db, charID := setupHouseRepo(t)
|
||||
|
||||
mission := []byte{0xCC, 0xDD, 0xEE}
|
||||
if err := repo.UpdateMission(charID, mission); err != nil {
|
||||
t.Fatalf("UpdateMission failed: %v", err)
|
||||
}
|
||||
|
||||
var got []byte
|
||||
if err := db.QueryRow("SELECT mission FROM user_binary WHERE id=$1", charID).Scan(&got); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if len(got) != 3 || got[0] != 0xCC {
|
||||
t.Errorf("Expected mission data, got: %x", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoHouseInitializeWarehouse(t *testing.T) {
|
||||
repo, db, charID := setupHouseRepo(t)
|
||||
|
||||
if err := repo.InitializeWarehouse(charID); err != nil {
|
||||
t.Fatalf("InitializeWarehouse failed: %v", err)
|
||||
}
|
||||
|
||||
var count int
|
||||
if err := db.QueryRow("SELECT COUNT(*) FROM warehouse WHERE character_id=$1", charID).Scan(&count); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if count != 1 {
|
||||
t.Errorf("Expected 1 warehouse row, got: %d", count)
|
||||
}
|
||||
|
||||
// Calling again should be idempotent
|
||||
if err := repo.InitializeWarehouse(charID); err != nil {
|
||||
t.Fatalf("Second InitializeWarehouse failed: %v", err)
|
||||
}
|
||||
if err := db.QueryRow("SELECT COUNT(*) FROM warehouse WHERE character_id=$1", charID).Scan(&count); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if count != 1 {
|
||||
t.Errorf("Expected still 1 warehouse row after idempotent call, got: %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoHouseGetWarehouseNames(t *testing.T) {
|
||||
repo, db, charID := setupHouseRepo(t)
|
||||
|
||||
if err := repo.InitializeWarehouse(charID); err != nil {
|
||||
t.Fatalf("InitializeWarehouse failed: %v", err)
|
||||
}
|
||||
if _, err := db.Exec("UPDATE warehouse SET item0name='Items Box 0', equip3name='Equip Box 3' WHERE character_id=$1", charID); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
|
||||
itemNames, equipNames, err := repo.GetWarehouseNames(charID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetWarehouseNames failed: %v", err)
|
||||
}
|
||||
if itemNames[0] != "Items Box 0" {
|
||||
t.Errorf("Expected item0name='Items Box 0', got: %q", itemNames[0])
|
||||
}
|
||||
if equipNames[3] != "Equip Box 3" {
|
||||
t.Errorf("Expected equip3name='Equip Box 3', got: %q", equipNames[3])
|
||||
}
|
||||
// Other names should be empty (COALESCE)
|
||||
if itemNames[1] != "" {
|
||||
t.Errorf("Expected empty item1name, got: %q", itemNames[1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoHouseRenameWarehouseBox(t *testing.T) {
|
||||
repo, db, charID := setupHouseRepo(t)
|
||||
|
||||
if err := repo.InitializeWarehouse(charID); err != nil {
|
||||
t.Fatalf("InitializeWarehouse failed: %v", err)
|
||||
}
|
||||
|
||||
if err := repo.RenameWarehouseBox(charID, 0, 5, "My Items"); err != nil {
|
||||
t.Fatalf("RenameWarehouseBox(item) failed: %v", err)
|
||||
}
|
||||
if err := repo.RenameWarehouseBox(charID, 1, 2, "My Equips"); err != nil {
|
||||
t.Fatalf("RenameWarehouseBox(equip) failed: %v", err)
|
||||
}
|
||||
|
||||
var item5name, equip2name string
|
||||
if err := db.QueryRow("SELECT COALESCE(item5name,''), COALESCE(equip2name,'') FROM warehouse WHERE character_id=$1", charID).Scan(&item5name, &equip2name); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if item5name != "My Items" {
|
||||
t.Errorf("Expected item5name='My Items', got: %q", item5name)
|
||||
}
|
||||
if equip2name != "My Equips" {
|
||||
t.Errorf("Expected equip2name='My Equips', got: %q", equip2name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoHouseRenameWarehouseBoxInvalidType(t *testing.T) {
|
||||
repo, _, charID := setupHouseRepo(t)
|
||||
|
||||
err := repo.RenameWarehouseBox(charID, 5, 0, "Bad")
|
||||
if err == nil {
|
||||
t.Fatal("Expected error for invalid box type, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoHouseWarehouseItemData(t *testing.T) {
|
||||
repo, _, charID := setupHouseRepo(t)
|
||||
|
||||
if err := repo.InitializeWarehouse(charID); err != nil {
|
||||
t.Fatalf("InitializeWarehouse failed: %v", err)
|
||||
}
|
||||
|
||||
data := []byte{0x01, 0x02, 0x03}
|
||||
if err := repo.SetWarehouseItemData(charID, 3, data); err != nil {
|
||||
t.Fatalf("SetWarehouseItemData failed: %v", err)
|
||||
}
|
||||
|
||||
got, err := repo.GetWarehouseItemData(charID, 3)
|
||||
if err != nil {
|
||||
t.Fatalf("GetWarehouseItemData failed: %v", err)
|
||||
}
|
||||
if len(got) != 3 || got[0] != 0x01 {
|
||||
t.Errorf("Expected item data, got: %x", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoHouseWarehouseEquipData(t *testing.T) {
|
||||
repo, _, charID := setupHouseRepo(t)
|
||||
|
||||
if err := repo.InitializeWarehouse(charID); err != nil {
|
||||
t.Fatalf("InitializeWarehouse failed: %v", err)
|
||||
}
|
||||
|
||||
data := []byte{0xAA, 0xBB}
|
||||
if err := repo.SetWarehouseEquipData(charID, 7, data); err != nil {
|
||||
t.Fatalf("SetWarehouseEquipData failed: %v", err)
|
||||
}
|
||||
|
||||
got, err := repo.GetWarehouseEquipData(charID, 7)
|
||||
if err != nil {
|
||||
t.Fatalf("GetWarehouseEquipData failed: %v", err)
|
||||
}
|
||||
if len(got) != 2 || got[0] != 0xAA {
|
||||
t.Errorf("Expected equip data, got: %x", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoHouseAcquireTitle(t *testing.T) {
|
||||
repo, _, charID := setupHouseRepo(t)
|
||||
|
||||
if err := repo.AcquireTitle(100, charID); err != nil {
|
||||
t.Fatalf("AcquireTitle failed: %v", err)
|
||||
}
|
||||
|
||||
titles, err := repo.GetTitles(charID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetTitles failed: %v", err)
|
||||
}
|
||||
if len(titles) != 1 {
|
||||
t.Fatalf("Expected 1 title, got: %d", len(titles))
|
||||
}
|
||||
if titles[0].ID != 100 {
|
||||
t.Errorf("Expected title ID=100, got: %d", titles[0].ID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoHouseAcquireTitleIdempotent(t *testing.T) {
|
||||
repo, _, charID := setupHouseRepo(t)
|
||||
|
||||
if err := repo.AcquireTitle(100, charID); err != nil {
|
||||
t.Fatalf("First AcquireTitle failed: %v", err)
|
||||
}
|
||||
if err := repo.AcquireTitle(100, charID); err != nil {
|
||||
t.Fatalf("Second AcquireTitle failed: %v", err)
|
||||
}
|
||||
|
||||
titles, err := repo.GetTitles(charID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetTitles failed: %v", err)
|
||||
}
|
||||
if len(titles) != 1 {
|
||||
t.Errorf("Expected 1 title after idempotent acquire, got: %d", len(titles))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoHouseGetTitlesEmpty(t *testing.T) {
|
||||
repo, _, charID := setupHouseRepo(t)
|
||||
|
||||
titles, err := repo.GetTitles(charID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetTitles failed: %v", err)
|
||||
}
|
||||
if len(titles) != 0 {
|
||||
t.Errorf("Expected 0 titles, got: %d", len(titles))
|
||||
}
|
||||
}
|
||||
231
server/channelserver/repo_mail_test.go
Normal file
231
server/channelserver/repo_mail_test.go
Normal file
@@ -0,0 +1,231 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
func setupMailRepo(t *testing.T) (*MailRepository, *sqlx.DB, uint32, uint32) {
|
||||
t.Helper()
|
||||
db := SetupTestDB(t)
|
||||
userID := CreateTestUser(t, db, "mail_sender")
|
||||
senderID := CreateTestCharacter(t, db, userID, "Sender")
|
||||
userID2 := CreateTestUser(t, db, "mail_recipient")
|
||||
recipientID := CreateTestCharacter(t, db, userID2, "Recipient")
|
||||
repo := NewMailRepository(db)
|
||||
t.Cleanup(func() { TeardownTestDB(t, db) })
|
||||
return repo, db, senderID, recipientID
|
||||
}
|
||||
|
||||
func TestRepoMailSendMail(t *testing.T) {
|
||||
repo, db, senderID, recipientID := setupMailRepo(t)
|
||||
|
||||
if err := repo.SendMail(senderID, recipientID, "Hello", "World", 0, 0, false, false); err != nil {
|
||||
t.Fatalf("SendMail failed: %v", err)
|
||||
}
|
||||
|
||||
var count int
|
||||
if err := db.QueryRow("SELECT COUNT(*) FROM mail WHERE sender_id=$1 AND recipient_id=$2", senderID, recipientID).Scan(&count); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if count != 1 {
|
||||
t.Errorf("Expected 1 mail, got: %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoMailSendMailWithItem(t *testing.T) {
|
||||
repo, db, senderID, recipientID := setupMailRepo(t)
|
||||
|
||||
if err := repo.SendMail(senderID, recipientID, "Gift", "Item for you", 100, 5, false, false); err != nil {
|
||||
t.Fatalf("SendMail failed: %v", err)
|
||||
}
|
||||
|
||||
var itemID, itemAmount int
|
||||
if err := db.QueryRow("SELECT attached_item, attached_item_amount FROM mail WHERE sender_id=$1", senderID).Scan(&itemID, &itemAmount); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if itemID != 100 || itemAmount != 5 {
|
||||
t.Errorf("Expected item=100 amount=5, got item=%d amount=%d", itemID, itemAmount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoMailGetListForCharacter(t *testing.T) {
|
||||
repo, _, senderID, recipientID := setupMailRepo(t)
|
||||
|
||||
if err := repo.SendMail(senderID, recipientID, "Mail1", "Body1", 0, 0, false, false); err != nil {
|
||||
t.Fatalf("SendMail 1 failed: %v", err)
|
||||
}
|
||||
if err := repo.SendMail(senderID, recipientID, "Mail2", "Body2", 0, 0, false, false); err != nil {
|
||||
t.Fatalf("SendMail 2 failed: %v", err)
|
||||
}
|
||||
|
||||
mails, err := repo.GetListForCharacter(recipientID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetListForCharacter failed: %v", err)
|
||||
}
|
||||
if len(mails) != 2 {
|
||||
t.Fatalf("Expected 2 mails, got: %d", len(mails))
|
||||
}
|
||||
// Should include sender name
|
||||
if mails[0].SenderName != "Sender" {
|
||||
t.Errorf("Expected sender_name='Sender', got: %q", mails[0].SenderName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoMailGetListExcludesDeleted(t *testing.T) {
|
||||
repo, _, senderID, recipientID := setupMailRepo(t)
|
||||
|
||||
if err := repo.SendMail(senderID, recipientID, "Visible", "", 0, 0, false, false); err != nil {
|
||||
t.Fatalf("SendMail failed: %v", err)
|
||||
}
|
||||
if err := repo.SendMail(senderID, recipientID, "Deleted", "", 0, 0, false, false); err != nil {
|
||||
t.Fatalf("SendMail failed: %v", err)
|
||||
}
|
||||
|
||||
// Get the list and delete the second mail
|
||||
mails, _ := repo.GetListForCharacter(recipientID)
|
||||
if err := repo.MarkDeleted(mails[0].ID); err != nil {
|
||||
t.Fatalf("MarkDeleted failed: %v", err)
|
||||
}
|
||||
|
||||
mails, err := repo.GetListForCharacter(recipientID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetListForCharacter failed: %v", err)
|
||||
}
|
||||
if len(mails) != 1 {
|
||||
t.Fatalf("Expected 1 mail after deletion, got: %d", len(mails))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoMailGetByID(t *testing.T) {
|
||||
repo, db, senderID, recipientID := setupMailRepo(t)
|
||||
|
||||
if err := repo.SendMail(senderID, recipientID, "Detail", "Full body text", 50, 2, true, false); err != nil {
|
||||
t.Fatalf("SendMail failed: %v", err)
|
||||
}
|
||||
|
||||
var mailID int
|
||||
if err := db.QueryRow("SELECT id FROM mail WHERE sender_id=$1", senderID).Scan(&mailID); err != nil {
|
||||
t.Fatalf("Setup query failed: %v", err)
|
||||
}
|
||||
|
||||
mail, err := repo.GetByID(mailID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetByID failed: %v", err)
|
||||
}
|
||||
if mail.Subject != "Detail" {
|
||||
t.Errorf("Expected subject='Detail', got: %q", mail.Subject)
|
||||
}
|
||||
if mail.Body != "Full body text" {
|
||||
t.Errorf("Expected body='Full body text', got: %q", mail.Body)
|
||||
}
|
||||
if !mail.IsGuildInvite {
|
||||
t.Error("Expected is_guild_invite=true")
|
||||
}
|
||||
if mail.SenderName != "Sender" {
|
||||
t.Errorf("Expected sender_name='Sender', got: %q", mail.SenderName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoMailMarkRead(t *testing.T) {
|
||||
repo, db, senderID, recipientID := setupMailRepo(t)
|
||||
|
||||
if err := repo.SendMail(senderID, recipientID, "Unread", "", 0, 0, false, false); err != nil {
|
||||
t.Fatalf("SendMail failed: %v", err)
|
||||
}
|
||||
|
||||
var mailID int
|
||||
if err := db.QueryRow("SELECT id FROM mail WHERE sender_id=$1", senderID).Scan(&mailID); err != nil {
|
||||
t.Fatalf("Setup query failed: %v", err)
|
||||
}
|
||||
|
||||
if err := repo.MarkRead(mailID); err != nil {
|
||||
t.Fatalf("MarkRead failed: %v", err)
|
||||
}
|
||||
|
||||
var read bool
|
||||
if err := db.QueryRow("SELECT read FROM mail WHERE id=$1", mailID).Scan(&read); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if !read {
|
||||
t.Error("Expected read=true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoMailSetLocked(t *testing.T) {
|
||||
repo, db, senderID, recipientID := setupMailRepo(t)
|
||||
|
||||
if err := repo.SendMail(senderID, recipientID, "Lock Test", "", 0, 0, false, false); err != nil {
|
||||
t.Fatalf("SendMail failed: %v", err)
|
||||
}
|
||||
|
||||
var mailID int
|
||||
if err := db.QueryRow("SELECT id FROM mail WHERE sender_id=$1", senderID).Scan(&mailID); err != nil {
|
||||
t.Fatalf("Setup query failed: %v", err)
|
||||
}
|
||||
|
||||
if err := repo.SetLocked(mailID, true); err != nil {
|
||||
t.Fatalf("SetLocked failed: %v", err)
|
||||
}
|
||||
|
||||
var locked bool
|
||||
if err := db.QueryRow("SELECT locked FROM mail WHERE id=$1", mailID).Scan(&locked); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if !locked {
|
||||
t.Error("Expected locked=true")
|
||||
}
|
||||
|
||||
// Unlock
|
||||
if err := repo.SetLocked(mailID, false); err != nil {
|
||||
t.Fatalf("SetLocked(false) failed: %v", err)
|
||||
}
|
||||
if err := db.QueryRow("SELECT locked FROM mail WHERE id=$1", mailID).Scan(&locked); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if locked {
|
||||
t.Error("Expected locked=false after unlock")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoMailMarkItemReceived(t *testing.T) {
|
||||
repo, db, senderID, recipientID := setupMailRepo(t)
|
||||
|
||||
if err := repo.SendMail(senderID, recipientID, "Item Mail", "", 100, 1, false, false); err != nil {
|
||||
t.Fatalf("SendMail failed: %v", err)
|
||||
}
|
||||
|
||||
var mailID int
|
||||
if err := db.QueryRow("SELECT id FROM mail WHERE sender_id=$1", senderID).Scan(&mailID); err != nil {
|
||||
t.Fatalf("Setup query failed: %v", err)
|
||||
}
|
||||
|
||||
if err := repo.MarkItemReceived(mailID); err != nil {
|
||||
t.Fatalf("MarkItemReceived failed: %v", err)
|
||||
}
|
||||
|
||||
var received bool
|
||||
if err := db.QueryRow("SELECT attached_item_received FROM mail WHERE id=$1", mailID).Scan(&received); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if !received {
|
||||
t.Error("Expected attached_item_received=true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoMailSystemMessage(t *testing.T) {
|
||||
repo, db, senderID, recipientID := setupMailRepo(t)
|
||||
|
||||
if err := repo.SendMail(senderID, recipientID, "System", "System alert", 0, 0, false, true); err != nil {
|
||||
t.Fatalf("SendMail failed: %v", err)
|
||||
}
|
||||
|
||||
var isSys bool
|
||||
if err := db.QueryRow("SELECT is_sys_message FROM mail WHERE sender_id=$1", senderID).Scan(&isSys); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if !isSys {
|
||||
t.Error("Expected is_sys_message=true")
|
||||
}
|
||||
}
|
||||
161
server/channelserver/repo_mercenary_test.go
Normal file
161
server/channelserver/repo_mercenary_test.go
Normal file
@@ -0,0 +1,161 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
func setupMercenaryRepo(t *testing.T) (*MercenaryRepository, *sqlx.DB, uint32, uint32) {
|
||||
t.Helper()
|
||||
db := SetupTestDB(t)
|
||||
userID := CreateTestUser(t, db, "merc_test_user")
|
||||
charID := CreateTestCharacter(t, db, userID, "MercChar")
|
||||
guildID := CreateTestGuild(t, db, charID, "MercGuild")
|
||||
repo := NewMercenaryRepository(db)
|
||||
t.Cleanup(func() { TeardownTestDB(t, db) })
|
||||
return repo, db, charID, guildID
|
||||
}
|
||||
|
||||
func TestRepoMercenaryNextRastaID(t *testing.T) {
|
||||
repo, _, _, _ := setupMercenaryRepo(t)
|
||||
|
||||
id1, err := repo.NextRastaID()
|
||||
if err != nil {
|
||||
t.Fatalf("NextRastaID failed: %v", err)
|
||||
}
|
||||
id2, err := repo.NextRastaID()
|
||||
if err != nil {
|
||||
t.Fatalf("NextRastaID second call failed: %v", err)
|
||||
}
|
||||
if id2 <= id1 {
|
||||
t.Errorf("Expected increasing IDs, got: %d then %d", id1, id2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoMercenaryNextAirouID(t *testing.T) {
|
||||
repo, _, _, _ := setupMercenaryRepo(t)
|
||||
|
||||
id1, err := repo.NextAirouID()
|
||||
if err != nil {
|
||||
t.Fatalf("NextAirouID failed: %v", err)
|
||||
}
|
||||
id2, err := repo.NextAirouID()
|
||||
if err != nil {
|
||||
t.Fatalf("NextAirouID second call failed: %v", err)
|
||||
}
|
||||
if id2 <= id1 {
|
||||
t.Errorf("Expected increasing IDs, got: %d then %d", id1, id2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoMercenaryGetMercenaryLoansEmpty(t *testing.T) {
|
||||
repo, _, charID, _ := setupMercenaryRepo(t)
|
||||
|
||||
loans, err := repo.GetMercenaryLoans(charID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetMercenaryLoans failed: %v", err)
|
||||
}
|
||||
if len(loans) != 0 {
|
||||
t.Errorf("Expected 0 loans, got: %d", len(loans))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoMercenaryGetMercenaryLoans(t *testing.T) {
|
||||
repo, db, charID, _ := setupMercenaryRepo(t)
|
||||
|
||||
// Set rasta_id on charID
|
||||
if _, err := db.Exec("UPDATE characters SET rasta_id=999 WHERE id=$1", charID); err != nil {
|
||||
t.Fatalf("Setup rasta_id failed: %v", err)
|
||||
}
|
||||
|
||||
// Create another character that has a pact with charID's rasta
|
||||
user2 := CreateTestUser(t, db, "merc_user2")
|
||||
char2 := CreateTestCharacter(t, db, user2, "PactHolder")
|
||||
if _, err := db.Exec("UPDATE characters SET pact_id=999 WHERE id=$1", char2); err != nil {
|
||||
t.Fatalf("Setup pact_id failed: %v", err)
|
||||
}
|
||||
|
||||
loans, err := repo.GetMercenaryLoans(charID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetMercenaryLoans failed: %v", err)
|
||||
}
|
||||
if len(loans) != 1 {
|
||||
t.Fatalf("Expected 1 loan, got: %d", len(loans))
|
||||
}
|
||||
if loans[0].Name != "PactHolder" {
|
||||
t.Errorf("Expected name='PactHolder', got: %q", loans[0].Name)
|
||||
}
|
||||
if loans[0].CharID != char2 {
|
||||
t.Errorf("Expected charID=%d, got: %d", char2, loans[0].CharID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoMercenaryGetGuildHuntCatsUsedEmpty(t *testing.T) {
|
||||
repo, _, charID, _ := setupMercenaryRepo(t)
|
||||
|
||||
cats, err := repo.GetGuildHuntCatsUsed(charID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetGuildHuntCatsUsed failed: %v", err)
|
||||
}
|
||||
if len(cats) != 0 {
|
||||
t.Errorf("Expected 0 cat usages, got: %d", len(cats))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoMercenaryGetGuildHuntCatsUsed(t *testing.T) {
|
||||
repo, db, charID, guildID := setupMercenaryRepo(t)
|
||||
|
||||
// Insert a guild hunt with cats_used
|
||||
if _, err := db.Exec(
|
||||
`INSERT INTO guild_hunts (guild_id, host_id, destination, level, hunt_data, cats_used, acquired, collected, start)
|
||||
VALUES ($1, $2, 1, 1, $3, '1,2,3', false, false, now())`,
|
||||
guildID, charID, []byte{0x00},
|
||||
); err != nil {
|
||||
t.Fatalf("Setup guild_hunts failed: %v", err)
|
||||
}
|
||||
|
||||
cats, err := repo.GetGuildHuntCatsUsed(charID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetGuildHuntCatsUsed failed: %v", err)
|
||||
}
|
||||
if len(cats) != 1 {
|
||||
t.Fatalf("Expected 1 cat usage, got: %d", len(cats))
|
||||
}
|
||||
if cats[0].CatsUsed != "1,2,3" {
|
||||
t.Errorf("Expected cats_used='1,2,3', got: %q", cats[0].CatsUsed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoMercenaryGetGuildAirouEmpty(t *testing.T) {
|
||||
repo, _, _, guildID := setupMercenaryRepo(t)
|
||||
|
||||
airou, err := repo.GetGuildAirou(guildID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetGuildAirou failed: %v", err)
|
||||
}
|
||||
if len(airou) != 0 {
|
||||
t.Errorf("Expected 0 airou, got: %d", len(airou))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoMercenaryGetGuildAirou(t *testing.T) {
|
||||
repo, db, charID, guildID := setupMercenaryRepo(t)
|
||||
|
||||
// Set otomoairou on the character
|
||||
airouData := []byte{0xAA, 0xBB, 0xCC}
|
||||
if _, err := db.Exec("UPDATE characters SET otomoairou=$1 WHERE id=$2", airouData, charID); err != nil {
|
||||
t.Fatalf("Setup otomoairou failed: %v", err)
|
||||
}
|
||||
|
||||
airou, err := repo.GetGuildAirou(guildID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetGuildAirou failed: %v", err)
|
||||
}
|
||||
if len(airou) != 1 {
|
||||
t.Fatalf("Expected 1 airou, got: %d", len(airou))
|
||||
}
|
||||
if len(airou[0]) != 3 || airou[0][0] != 0xAA {
|
||||
t.Errorf("Expected airou data, got: %x", airou[0])
|
||||
}
|
||||
}
|
||||
110
server/channelserver/repo_misc_test.go
Normal file
110
server/channelserver/repo_misc_test.go
Normal file
@@ -0,0 +1,110 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
func setupMiscRepo(t *testing.T) (*MiscRepository, *sqlx.DB) {
|
||||
t.Helper()
|
||||
db := SetupTestDB(t)
|
||||
repo := NewMiscRepository(db)
|
||||
t.Cleanup(func() { TeardownTestDB(t, db) })
|
||||
return repo, db
|
||||
}
|
||||
|
||||
func TestRepoMiscUpsertTrendWeapon(t *testing.T) {
|
||||
repo, db := setupMiscRepo(t)
|
||||
|
||||
if err := repo.UpsertTrendWeapon(100, 1); err != nil {
|
||||
t.Fatalf("UpsertTrendWeapon failed: %v", err)
|
||||
}
|
||||
|
||||
var count int
|
||||
if err := db.QueryRow("SELECT count FROM trend_weapons WHERE weapon_id=100").Scan(&count); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if count != 1 {
|
||||
t.Errorf("Expected count=1, got: %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoMiscUpsertTrendWeaponIncrement(t *testing.T) {
|
||||
repo, db := setupMiscRepo(t)
|
||||
|
||||
if err := repo.UpsertTrendWeapon(100, 1); err != nil {
|
||||
t.Fatalf("First UpsertTrendWeapon failed: %v", err)
|
||||
}
|
||||
if err := repo.UpsertTrendWeapon(100, 1); err != nil {
|
||||
t.Fatalf("Second UpsertTrendWeapon failed: %v", err)
|
||||
}
|
||||
|
||||
var count int
|
||||
if err := db.QueryRow("SELECT count FROM trend_weapons WHERE weapon_id=100").Scan(&count); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if count != 2 {
|
||||
t.Errorf("Expected count=2 after upsert, got: %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoMiscGetTrendWeaponsEmpty(t *testing.T) {
|
||||
repo, _ := setupMiscRepo(t)
|
||||
|
||||
weapons, err := repo.GetTrendWeapons(1)
|
||||
if err != nil {
|
||||
t.Fatalf("GetTrendWeapons failed: %v", err)
|
||||
}
|
||||
if len(weapons) != 0 {
|
||||
t.Errorf("Expected 0 weapons, got: %d", len(weapons))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoMiscGetTrendWeaponsOrdering(t *testing.T) {
|
||||
repo, _ := setupMiscRepo(t)
|
||||
|
||||
// Insert weapons with different counts
|
||||
for i := 0; i < 3; i++ {
|
||||
if err := repo.UpsertTrendWeapon(uint16(100+i), 1); err != nil {
|
||||
t.Fatalf("UpsertTrendWeapon failed: %v", err)
|
||||
}
|
||||
}
|
||||
// Give weapon 101 more uses
|
||||
if err := repo.UpsertTrendWeapon(101, 1); err != nil {
|
||||
t.Fatalf("UpsertTrendWeapon failed: %v", err)
|
||||
}
|
||||
if err := repo.UpsertTrendWeapon(101, 1); err != nil {
|
||||
t.Fatalf("UpsertTrendWeapon failed: %v", err)
|
||||
}
|
||||
|
||||
weapons, err := repo.GetTrendWeapons(1)
|
||||
if err != nil {
|
||||
t.Fatalf("GetTrendWeapons failed: %v", err)
|
||||
}
|
||||
if len(weapons) != 3 {
|
||||
t.Fatalf("Expected 3 weapons, got: %d", len(weapons))
|
||||
}
|
||||
// First should be the one with highest count (101 with count=3)
|
||||
if weapons[0] != 101 {
|
||||
t.Errorf("Expected first weapon=101 (highest count), got: %d", weapons[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoMiscGetTrendWeaponsLimit3(t *testing.T) {
|
||||
repo, _ := setupMiscRepo(t)
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
if err := repo.UpsertTrendWeapon(uint16(100+i), 1); err != nil {
|
||||
t.Fatalf("UpsertTrendWeapon failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
weapons, err := repo.GetTrendWeapons(1)
|
||||
if err != nil {
|
||||
t.Fatalf("GetTrendWeapons failed: %v", err)
|
||||
}
|
||||
if len(weapons) != 3 {
|
||||
t.Errorf("Expected max 3 weapons, got: %d", len(weapons))
|
||||
}
|
||||
}
|
||||
144
server/channelserver/repo_rengoku_test.go
Normal file
144
server/channelserver/repo_rengoku_test.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
func setupRengokuRepo(t *testing.T) (*RengokuRepository, *sqlx.DB, uint32, uint32) {
|
||||
t.Helper()
|
||||
db := SetupTestDB(t)
|
||||
userID := CreateTestUser(t, db, "rengoku_test_user")
|
||||
charID := CreateTestCharacter(t, db, userID, "RengokuChar")
|
||||
guildID := CreateTestGuild(t, db, charID, "RengokuGuild")
|
||||
repo := NewRengokuRepository(db)
|
||||
t.Cleanup(func() { TeardownTestDB(t, db) })
|
||||
return repo, db, charID, guildID
|
||||
}
|
||||
|
||||
func TestRepoRengokuUpsertScoreNew(t *testing.T) {
|
||||
repo, db, charID, _ := setupRengokuRepo(t)
|
||||
|
||||
if err := repo.UpsertScore(charID, 10, 500, 5, 200); err != nil {
|
||||
t.Fatalf("UpsertScore failed: %v", err)
|
||||
}
|
||||
|
||||
var stagesMp, pointsMp, stagesSp, pointsSp uint32
|
||||
if err := db.QueryRow("SELECT max_stages_mp, max_points_mp, max_stages_sp, max_points_sp FROM rengoku_score WHERE character_id=$1", charID).Scan(&stagesMp, &pointsMp, &stagesSp, &pointsSp); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if stagesMp != 10 || pointsMp != 500 || stagesSp != 5 || pointsSp != 200 {
|
||||
t.Errorf("Expected 10/500/5/200, got %d/%d/%d/%d", stagesMp, pointsMp, stagesSp, pointsSp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoRengokuUpsertScoreUpdate(t *testing.T) {
|
||||
repo, db, charID, _ := setupRengokuRepo(t)
|
||||
|
||||
if err := repo.UpsertScore(charID, 10, 500, 5, 200); err != nil {
|
||||
t.Fatalf("First UpsertScore failed: %v", err)
|
||||
}
|
||||
if err := repo.UpsertScore(charID, 20, 1000, 15, 800); err != nil {
|
||||
t.Fatalf("Second UpsertScore failed: %v", err)
|
||||
}
|
||||
|
||||
var stagesMp, pointsMp uint32
|
||||
if err := db.QueryRow("SELECT max_stages_mp, max_points_mp FROM rengoku_score WHERE character_id=$1", charID).Scan(&stagesMp, &pointsMp); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if stagesMp != 20 || pointsMp != 1000 {
|
||||
t.Errorf("Expected 20/1000 after update, got %d/%d", stagesMp, pointsMp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoRengokuGetRankingGlobal(t *testing.T) {
|
||||
repo, _, charID, _ := setupRengokuRepo(t)
|
||||
|
||||
if err := repo.UpsertScore(charID, 10, 500, 5, 200); err != nil {
|
||||
t.Fatalf("UpsertScore failed: %v", err)
|
||||
}
|
||||
|
||||
// Leaderboard 0 = max_stages_mp (global)
|
||||
scores, err := repo.GetRanking(0, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("GetRanking failed: %v", err)
|
||||
}
|
||||
if len(scores) != 1 {
|
||||
t.Fatalf("Expected 1 score, got: %d", len(scores))
|
||||
}
|
||||
if scores[0].Score != 10 {
|
||||
t.Errorf("Expected score=10, got: %d", scores[0].Score)
|
||||
}
|
||||
if scores[0].Name != "RengokuChar" {
|
||||
t.Errorf("Expected name='RengokuChar', got: %q", scores[0].Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoRengokuGetRankingGuildFiltered(t *testing.T) {
|
||||
repo, db, charID, guildID := setupRengokuRepo(t)
|
||||
|
||||
if err := repo.UpsertScore(charID, 10, 500, 5, 200); err != nil {
|
||||
t.Fatalf("UpsertScore failed: %v", err)
|
||||
}
|
||||
|
||||
// Create another character in a different guild
|
||||
user2 := CreateTestUser(t, db, "rengoku_user2")
|
||||
char2 := CreateTestCharacter(t, db, user2, "RengokuChar2")
|
||||
CreateTestGuild(t, db, char2, "OtherGuild")
|
||||
if err := repo.UpsertScore(char2, 20, 1000, 15, 800); err != nil {
|
||||
t.Fatalf("UpsertScore char2 failed: %v", err)
|
||||
}
|
||||
|
||||
// Leaderboard 2 = max_stages_mp (guild-filtered)
|
||||
scores, err := repo.GetRanking(2, guildID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetRanking failed: %v", err)
|
||||
}
|
||||
if len(scores) != 1 {
|
||||
t.Fatalf("Expected 1 guild-filtered score, got: %d", len(scores))
|
||||
}
|
||||
if scores[0].Name != "RengokuChar" {
|
||||
t.Errorf("Expected 'RengokuChar' in guild ranking, got: %q", scores[0].Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoRengokuGetRankingPointsLeaderboard(t *testing.T) {
|
||||
repo, _, charID, _ := setupRengokuRepo(t)
|
||||
|
||||
if err := repo.UpsertScore(charID, 10, 500, 5, 200); err != nil {
|
||||
t.Fatalf("UpsertScore failed: %v", err)
|
||||
}
|
||||
|
||||
// Leaderboard 1 = max_points_mp (global)
|
||||
scores, err := repo.GetRanking(1, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("GetRanking failed: %v", err)
|
||||
}
|
||||
if len(scores) != 1 {
|
||||
t.Fatalf("Expected 1 score, got: %d", len(scores))
|
||||
}
|
||||
if scores[0].Score != 500 {
|
||||
t.Errorf("Expected score=500 for points leaderboard, got: %d", scores[0].Score)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoRengokuGetRankingSPLeaderboard(t *testing.T) {
|
||||
repo, _, charID, _ := setupRengokuRepo(t)
|
||||
|
||||
if err := repo.UpsertScore(charID, 10, 500, 5, 200); err != nil {
|
||||
t.Fatalf("UpsertScore failed: %v", err)
|
||||
}
|
||||
|
||||
// Leaderboard 4 = max_stages_sp (global)
|
||||
scores, err := repo.GetRanking(4, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("GetRanking failed: %v", err)
|
||||
}
|
||||
if len(scores) != 1 {
|
||||
t.Fatalf("Expected 1 score, got: %d", len(scores))
|
||||
}
|
||||
if scores[0].Score != 5 {
|
||||
t.Errorf("Expected score=5 for SP stages leaderboard, got: %d", scores[0].Score)
|
||||
}
|
||||
}
|
||||
60
server/channelserver/repo_scenario_test.go
Normal file
60
server/channelserver/repo_scenario_test.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
func setupScenarioRepo(t *testing.T) (*ScenarioRepository, *sqlx.DB) {
|
||||
t.Helper()
|
||||
db := SetupTestDB(t)
|
||||
repo := NewScenarioRepository(db)
|
||||
t.Cleanup(func() { TeardownTestDB(t, db) })
|
||||
return repo, db
|
||||
}
|
||||
|
||||
func TestRepoScenarioGetCountersEmpty(t *testing.T) {
|
||||
repo, _ := setupScenarioRepo(t)
|
||||
|
||||
counters, err := repo.GetCounters()
|
||||
if err != nil {
|
||||
t.Fatalf("GetCounters failed: %v", err)
|
||||
}
|
||||
if len(counters) != 0 {
|
||||
t.Errorf("Expected 0 counters, got: %d", len(counters))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoScenarioGetCounters(t *testing.T) {
|
||||
repo, db := setupScenarioRepo(t)
|
||||
|
||||
if _, err := db.Exec("INSERT INTO scenario_counter (id, scenario_id, category_id) VALUES (1, 100, 0)"); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
if _, err := db.Exec("INSERT INTO scenario_counter (id, scenario_id, category_id) VALUES (2, 200, 1)"); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
|
||||
counters, err := repo.GetCounters()
|
||||
if err != nil {
|
||||
t.Fatalf("GetCounters failed: %v", err)
|
||||
}
|
||||
if len(counters) != 2 {
|
||||
t.Fatalf("Expected 2 counters, got: %d", len(counters))
|
||||
}
|
||||
|
||||
// Check both values exist (order may vary)
|
||||
found100, found200 := false, false
|
||||
for _, c := range counters {
|
||||
if c.MainID == 100 {
|
||||
found100 = true
|
||||
}
|
||||
if c.MainID == 200 {
|
||||
found200 = true
|
||||
}
|
||||
}
|
||||
if !found100 || !found200 {
|
||||
t.Errorf("Expected scenario_ids 100 and 200, got: %+v", counters)
|
||||
}
|
||||
}
|
||||
141
server/channelserver/repo_session_test.go
Normal file
141
server/channelserver/repo_session_test.go
Normal file
@@ -0,0 +1,141 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
func setupSessionRepo(t *testing.T) (*SessionRepository, *sqlx.DB, uint32, uint32, uint32, string) {
|
||||
t.Helper()
|
||||
db := SetupTestDB(t)
|
||||
userID := CreateTestUser(t, db, "session_test_user")
|
||||
charID := CreateTestCharacter(t, db, userID, "SessionChar")
|
||||
token := "test_token_12345"
|
||||
sessionID := CreateTestSignSession(t, db, userID, token)
|
||||
repo := NewSessionRepository(db)
|
||||
t.Cleanup(func() { TeardownTestDB(t, db) })
|
||||
return repo, db, userID, charID, sessionID, token
|
||||
}
|
||||
|
||||
func TestRepoSessionValidateLoginToken(t *testing.T) {
|
||||
repo, _, _, charID, sessionID, token := setupSessionRepo(t)
|
||||
|
||||
err := repo.ValidateLoginToken(token, sessionID, charID)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateLoginToken failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoSessionValidateLoginTokenInvalidToken(t *testing.T) {
|
||||
repo, _, _, charID, sessionID, _ := setupSessionRepo(t)
|
||||
|
||||
err := repo.ValidateLoginToken("wrong_token", sessionID, charID)
|
||||
if err == nil {
|
||||
t.Fatal("Expected error for invalid token, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoSessionValidateLoginTokenWrongChar(t *testing.T) {
|
||||
repo, _, _, _, sessionID, token := setupSessionRepo(t)
|
||||
|
||||
err := repo.ValidateLoginToken(token, sessionID, 999999)
|
||||
if err == nil {
|
||||
t.Fatal("Expected error for wrong char ID, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoSessionValidateLoginTokenWrongSession(t *testing.T) {
|
||||
repo, _, _, charID, _, token := setupSessionRepo(t)
|
||||
|
||||
err := repo.ValidateLoginToken(token, 999999, charID)
|
||||
if err == nil {
|
||||
t.Fatal("Expected error for wrong session ID, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoSessionBindSession(t *testing.T) {
|
||||
repo, db, _, charID, _, token := setupSessionRepo(t)
|
||||
|
||||
CreateTestServer(t, db, 1)
|
||||
|
||||
if err := repo.BindSession(token, 1, charID); err != nil {
|
||||
t.Fatalf("BindSession failed: %v", err)
|
||||
}
|
||||
|
||||
var serverID *uint16
|
||||
var boundCharID *uint32
|
||||
if err := db.QueryRow("SELECT server_id, char_id FROM sign_sessions WHERE token=$1", token).Scan(&serverID, &boundCharID); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if serverID == nil || *serverID != 1 {
|
||||
t.Errorf("Expected server_id=1, got: %v", serverID)
|
||||
}
|
||||
if boundCharID == nil || *boundCharID != charID {
|
||||
t.Errorf("Expected char_id=%d, got: %v", charID, boundCharID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoSessionClearSession(t *testing.T) {
|
||||
repo, db, _, charID, _, token := setupSessionRepo(t)
|
||||
|
||||
CreateTestServer(t, db, 1)
|
||||
|
||||
if err := repo.BindSession(token, 1, charID); err != nil {
|
||||
t.Fatalf("BindSession failed: %v", err)
|
||||
}
|
||||
|
||||
if err := repo.ClearSession(token); err != nil {
|
||||
t.Fatalf("ClearSession failed: %v", err)
|
||||
}
|
||||
|
||||
var serverID, boundCharID *int
|
||||
if err := db.QueryRow("SELECT server_id, char_id FROM sign_sessions WHERE token=$1", token).Scan(&serverID, &boundCharID); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if serverID != nil {
|
||||
t.Errorf("Expected server_id=NULL, got: %v", *serverID)
|
||||
}
|
||||
if boundCharID != nil {
|
||||
t.Errorf("Expected char_id=NULL, got: %v", *boundCharID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoSessionUpdatePlayerCount(t *testing.T) {
|
||||
repo, db, _, _, _, _ := setupSessionRepo(t)
|
||||
|
||||
CreateTestServer(t, db, 1)
|
||||
|
||||
if err := repo.UpdatePlayerCount(1, 42); err != nil {
|
||||
t.Fatalf("UpdatePlayerCount failed: %v", err)
|
||||
}
|
||||
|
||||
var count int
|
||||
if err := db.QueryRow("SELECT current_players FROM servers WHERE server_id=1").Scan(&count); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if count != 42 {
|
||||
t.Errorf("Expected current_players=42, got: %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoSessionUpdatePlayerCountTwice(t *testing.T) {
|
||||
repo, db, _, _, _, _ := setupSessionRepo(t)
|
||||
|
||||
CreateTestServer(t, db, 1)
|
||||
|
||||
if err := repo.UpdatePlayerCount(1, 10); err != nil {
|
||||
t.Fatalf("First UpdatePlayerCount failed: %v", err)
|
||||
}
|
||||
if err := repo.UpdatePlayerCount(1, 25); err != nil {
|
||||
t.Fatalf("Second UpdatePlayerCount failed: %v", err)
|
||||
}
|
||||
|
||||
var count int
|
||||
if err := db.QueryRow("SELECT current_players FROM servers WHERE server_id=1").Scan(&count); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if count != 25 {
|
||||
t.Errorf("Expected current_players=25, got: %d", count)
|
||||
}
|
||||
}
|
||||
123
server/channelserver/repo_shop_test.go
Normal file
123
server/channelserver/repo_shop_test.go
Normal file
@@ -0,0 +1,123 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
func setupShopRepo(t *testing.T) (*ShopRepository, *sqlx.DB, uint32) {
|
||||
t.Helper()
|
||||
db := SetupTestDB(t)
|
||||
userID := CreateTestUser(t, db, "shop_test_user")
|
||||
charID := CreateTestCharacter(t, db, userID, "ShopChar")
|
||||
repo := NewShopRepository(db)
|
||||
t.Cleanup(func() { TeardownTestDB(t, db) })
|
||||
return repo, db, charID
|
||||
}
|
||||
|
||||
func TestRepoShopGetShopItemsEmpty(t *testing.T) {
|
||||
repo, _, charID := setupShopRepo(t)
|
||||
|
||||
items, err := repo.GetShopItems(1, 1, charID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetShopItems failed: %v", err)
|
||||
}
|
||||
if len(items) != 0 {
|
||||
t.Errorf("Expected 0 items, got: %d", len(items))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoShopGetShopItems(t *testing.T) {
|
||||
repo, db, charID := setupShopRepo(t)
|
||||
|
||||
// Insert shop items
|
||||
if _, err := db.Exec(
|
||||
`INSERT INTO shop_items (id, shop_type, shop_id, item_id, cost, quantity, min_hr, min_sr, min_gr, store_level, max_quantity, road_floors, road_fatalis)
|
||||
VALUES (1, 1, 100, 500, 1000, 1, 0, 0, 0, 0, 99, 0, 0)`,
|
||||
); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
|
||||
items, err := repo.GetShopItems(1, 100, charID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetShopItems failed: %v", err)
|
||||
}
|
||||
if len(items) != 1 {
|
||||
t.Fatalf("Expected 1 item, got: %d", len(items))
|
||||
}
|
||||
if items[0].ItemID != 500 {
|
||||
t.Errorf("Expected item_id=500, got: %d", items[0].ItemID)
|
||||
}
|
||||
if items[0].Cost != 1000 {
|
||||
t.Errorf("Expected cost=1000, got: %d", items[0].Cost)
|
||||
}
|
||||
if items[0].UsedQuantity != 0 {
|
||||
t.Errorf("Expected used_quantity=0, got: %d", items[0].UsedQuantity)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoShopRecordPurchaseAmbiguousColumn(t *testing.T) {
|
||||
repo, _, charID := setupShopRepo(t)
|
||||
|
||||
// RecordPurchase uses ON CONFLICT with unqualified "bought" column reference,
|
||||
// which PostgreSQL rejects as ambiguous. This test documents the existing bug.
|
||||
err := repo.RecordPurchase(charID, 1, 3)
|
||||
if err == nil {
|
||||
t.Fatal("Expected error from ambiguous column reference in RecordPurchase SQL, but got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoShopGetFpointItem(t *testing.T) {
|
||||
repo, db, _ := setupShopRepo(t)
|
||||
|
||||
if _, err := db.Exec("INSERT INTO fpoint_items (id, item_type, item_id, quantity, fpoints, buyable) VALUES (1, 1, 100, 5, 200, true)"); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
|
||||
quantity, fpoints, err := repo.GetFpointItem(1)
|
||||
if err != nil {
|
||||
t.Fatalf("GetFpointItem failed: %v", err)
|
||||
}
|
||||
if quantity != 5 {
|
||||
t.Errorf("Expected quantity=5, got: %d", quantity)
|
||||
}
|
||||
if fpoints != 200 {
|
||||
t.Errorf("Expected fpoints=200, got: %d", fpoints)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoShopGetFpointExchangeList(t *testing.T) {
|
||||
repo, db, _ := setupShopRepo(t)
|
||||
|
||||
if _, err := db.Exec("INSERT INTO fpoint_items (id, item_type, item_id, quantity, fpoints, buyable) VALUES (1, 1, 100, 5, 200, true)"); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
if _, err := db.Exec("INSERT INTO fpoint_items (id, item_type, item_id, quantity, fpoints, buyable) VALUES (2, 2, 200, 10, 500, false)"); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
|
||||
exchanges, err := repo.GetFpointExchangeList()
|
||||
if err != nil {
|
||||
t.Fatalf("GetFpointExchangeList failed: %v", err)
|
||||
}
|
||||
if len(exchanges) != 2 {
|
||||
t.Fatalf("Expected 2 exchange items, got: %d", len(exchanges))
|
||||
}
|
||||
// Ordered by buyable DESC, so buyable=true first
|
||||
if !exchanges[0].Buyable {
|
||||
t.Error("Expected first item to have buyable=true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoShopGetFpointExchangeListEmpty(t *testing.T) {
|
||||
repo, _, _ := setupShopRepo(t)
|
||||
|
||||
exchanges, err := repo.GetFpointExchangeList()
|
||||
if err != nil {
|
||||
t.Fatalf("GetFpointExchangeList failed: %v", err)
|
||||
}
|
||||
if len(exchanges) != 0 {
|
||||
t.Errorf("Expected 0 exchange items, got: %d", len(exchanges))
|
||||
}
|
||||
}
|
||||
240
server/channelserver/repo_stamp_test.go
Normal file
240
server/channelserver/repo_stamp_test.go
Normal file
@@ -0,0 +1,240 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
func setupStampRepo(t *testing.T) (*StampRepository, *sqlx.DB, uint32) {
|
||||
t.Helper()
|
||||
db := SetupTestDB(t)
|
||||
userID := CreateTestUser(t, db, "stamp_test_user")
|
||||
charID := CreateTestCharacter(t, db, userID, "StampChar")
|
||||
repo := NewStampRepository(db)
|
||||
t.Cleanup(func() { TeardownTestDB(t, db) })
|
||||
return repo, db, charID
|
||||
}
|
||||
|
||||
func initStamp(t *testing.T, repo *StampRepository, charID uint32) {
|
||||
t.Helper()
|
||||
now := time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC)
|
||||
if err := repo.Init(charID, now); err != nil {
|
||||
t.Fatalf("Stamp Init failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoStampInit(t *testing.T) {
|
||||
repo, db, charID := setupStampRepo(t)
|
||||
|
||||
now := time.Date(2025, 6, 15, 12, 0, 0, 0, time.UTC)
|
||||
if err := repo.Init(charID, now); err != nil {
|
||||
t.Fatalf("Init failed: %v", err)
|
||||
}
|
||||
|
||||
var hlChecked, exChecked time.Time
|
||||
if err := db.QueryRow("SELECT hl_checked, ex_checked FROM stamps WHERE character_id=$1", charID).Scan(&hlChecked, &exChecked); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if !hlChecked.Equal(now) {
|
||||
t.Errorf("Expected hl_checked=%v, got: %v", now, hlChecked)
|
||||
}
|
||||
if !exChecked.Equal(now) {
|
||||
t.Errorf("Expected ex_checked=%v, got: %v", now, exChecked)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoStampGetChecked(t *testing.T) {
|
||||
repo, _, charID := setupStampRepo(t)
|
||||
|
||||
now := time.Date(2025, 6, 15, 12, 0, 0, 0, time.UTC)
|
||||
if err := repo.Init(charID, now); err != nil {
|
||||
t.Fatalf("Init failed: %v", err)
|
||||
}
|
||||
|
||||
got, err := repo.GetChecked(charID, "hl")
|
||||
if err != nil {
|
||||
t.Fatalf("GetChecked failed: %v", err)
|
||||
}
|
||||
if !got.Equal(now) {
|
||||
t.Errorf("Expected %v, got: %v", now, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoStampSetChecked(t *testing.T) {
|
||||
repo, _, charID := setupStampRepo(t)
|
||||
initStamp(t, repo, charID)
|
||||
|
||||
newTime := time.Date(2025, 7, 1, 0, 0, 0, 0, time.UTC)
|
||||
if err := repo.SetChecked(charID, "ex", newTime); err != nil {
|
||||
t.Fatalf("SetChecked failed: %v", err)
|
||||
}
|
||||
|
||||
got, err := repo.GetChecked(charID, "ex")
|
||||
if err != nil {
|
||||
t.Fatalf("GetChecked failed: %v", err)
|
||||
}
|
||||
if !got.Equal(newTime) {
|
||||
t.Errorf("Expected %v, got: %v", newTime, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoStampIncrementTotal(t *testing.T) {
|
||||
repo, _, charID := setupStampRepo(t)
|
||||
initStamp(t, repo, charID)
|
||||
|
||||
if err := repo.IncrementTotal(charID, "hl"); err != nil {
|
||||
t.Fatalf("First IncrementTotal failed: %v", err)
|
||||
}
|
||||
if err := repo.IncrementTotal(charID, "hl"); err != nil {
|
||||
t.Fatalf("Second IncrementTotal failed: %v", err)
|
||||
}
|
||||
|
||||
total, redeemed, err := repo.GetTotals(charID, "hl")
|
||||
if err != nil {
|
||||
t.Fatalf("GetTotals failed: %v", err)
|
||||
}
|
||||
if total != 2 {
|
||||
t.Errorf("Expected total=2, got: %d", total)
|
||||
}
|
||||
if redeemed != 0 {
|
||||
t.Errorf("Expected redeemed=0, got: %d", redeemed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoStampGetTotals(t *testing.T) {
|
||||
repo, db, charID := setupStampRepo(t)
|
||||
initStamp(t, repo, charID)
|
||||
|
||||
if _, err := db.Exec("UPDATE stamps SET hl_total=10, hl_redeemed=3 WHERE character_id=$1", charID); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
|
||||
total, redeemed, err := repo.GetTotals(charID, "hl")
|
||||
if err != nil {
|
||||
t.Fatalf("GetTotals failed: %v", err)
|
||||
}
|
||||
if total != 10 || redeemed != 3 {
|
||||
t.Errorf("Expected total=10 redeemed=3, got total=%d redeemed=%d", total, redeemed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoStampExchange(t *testing.T) {
|
||||
repo, db, charID := setupStampRepo(t)
|
||||
initStamp(t, repo, charID)
|
||||
|
||||
if _, err := db.Exec("UPDATE stamps SET hl_total=20, hl_redeemed=0 WHERE character_id=$1", charID); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
|
||||
total, redeemed, err := repo.Exchange(charID, "hl")
|
||||
if err != nil {
|
||||
t.Fatalf("Exchange failed: %v", err)
|
||||
}
|
||||
if total != 20 {
|
||||
t.Errorf("Expected total=20, got: %d", total)
|
||||
}
|
||||
if redeemed != 8 {
|
||||
t.Errorf("Expected redeemed=8, got: %d", redeemed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoStampExchangeYearly(t *testing.T) {
|
||||
repo, db, charID := setupStampRepo(t)
|
||||
initStamp(t, repo, charID)
|
||||
|
||||
if _, err := db.Exec("UPDATE stamps SET hl_total=100, hl_redeemed=50 WHERE character_id=$1", charID); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
|
||||
total, redeemed, err := repo.ExchangeYearly(charID)
|
||||
if err != nil {
|
||||
t.Fatalf("ExchangeYearly failed: %v", err)
|
||||
}
|
||||
if total != 52 {
|
||||
t.Errorf("Expected total=52 (100-48), got: %d", total)
|
||||
}
|
||||
if redeemed != 2 {
|
||||
t.Errorf("Expected redeemed=2 (50-48), got: %d", redeemed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoStampGetMonthlyClaimed(t *testing.T) {
|
||||
repo, db, charID := setupStampRepo(t)
|
||||
initStamp(t, repo, charID)
|
||||
|
||||
claimedTime := time.Date(2025, 6, 1, 0, 0, 0, 0, time.UTC)
|
||||
if _, err := db.Exec("UPDATE stamps SET monthly_claimed=$1 WHERE character_id=$2", claimedTime, charID); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
|
||||
got, err := repo.GetMonthlyClaimed(charID, "monthly")
|
||||
if err != nil {
|
||||
t.Fatalf("GetMonthlyClaimed failed: %v", err)
|
||||
}
|
||||
if !got.Equal(claimedTime) {
|
||||
t.Errorf("Expected %v, got: %v", claimedTime, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoStampSetMonthlyClaimed(t *testing.T) {
|
||||
repo, _, charID := setupStampRepo(t)
|
||||
initStamp(t, repo, charID)
|
||||
|
||||
claimedTime := time.Date(2025, 7, 1, 0, 0, 0, 0, time.UTC)
|
||||
if err := repo.SetMonthlyClaimed(charID, "monthly", claimedTime); err != nil {
|
||||
t.Fatalf("SetMonthlyClaimed failed: %v", err)
|
||||
}
|
||||
|
||||
got, err := repo.GetMonthlyClaimed(charID, "monthly")
|
||||
if err != nil {
|
||||
t.Fatalf("GetMonthlyClaimed failed: %v", err)
|
||||
}
|
||||
if !got.Equal(claimedTime) {
|
||||
t.Errorf("Expected %v, got: %v", claimedTime, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoStampExTypes(t *testing.T) {
|
||||
repo, db, charID := setupStampRepo(t)
|
||||
initStamp(t, repo, charID)
|
||||
|
||||
// Verify ex stamp type works too
|
||||
if err := repo.IncrementTotal(charID, "ex"); err != nil {
|
||||
t.Fatalf("IncrementTotal(ex) failed: %v", err)
|
||||
}
|
||||
|
||||
if _, err := db.Exec("UPDATE stamps SET ex_total=16, ex_redeemed=0 WHERE character_id=$1", charID); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
|
||||
total, redeemed, err := repo.Exchange(charID, "ex")
|
||||
if err != nil {
|
||||
t.Fatalf("Exchange(ex) failed: %v", err)
|
||||
}
|
||||
if total != 16 {
|
||||
t.Errorf("Expected ex_total=16, got: %d", total)
|
||||
}
|
||||
if redeemed != 8 {
|
||||
t.Errorf("Expected ex_redeemed=8, got: %d", redeemed)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoStampMonthlyHlClaimed(t *testing.T) {
|
||||
repo, _, charID := setupStampRepo(t)
|
||||
initStamp(t, repo, charID)
|
||||
|
||||
claimedTime := time.Date(2025, 8, 15, 0, 0, 0, 0, time.UTC)
|
||||
if err := repo.SetMonthlyClaimed(charID, "monthly_hl", claimedTime); err != nil {
|
||||
t.Fatalf("SetMonthlyClaimed(monthly_hl) failed: %v", err)
|
||||
}
|
||||
|
||||
got, err := repo.GetMonthlyClaimed(charID, "monthly_hl")
|
||||
if err != nil {
|
||||
t.Fatalf("GetMonthlyClaimed(monthly_hl) failed: %v", err)
|
||||
}
|
||||
if !got.Equal(claimedTime) {
|
||||
t.Errorf("Expected %v, got: %v", claimedTime, got)
|
||||
}
|
||||
}
|
||||
275
server/channelserver/repo_tower_test.go
Normal file
275
server/channelserver/repo_tower_test.go
Normal file
@@ -0,0 +1,275 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
func setupTowerRepo(t *testing.T) (*TowerRepository, *sqlx.DB, uint32, uint32) {
|
||||
t.Helper()
|
||||
db := SetupTestDB(t)
|
||||
userID := CreateTestUser(t, db, "tower_test_user")
|
||||
charID := CreateTestCharacter(t, db, userID, "TowerChar")
|
||||
leaderID := CreateTestCharacter(t, db, userID, "GuildLeader")
|
||||
guildID := CreateTestGuild(t, db, leaderID, "TowerGuild")
|
||||
// Add charID to the guild
|
||||
if _, err := db.Exec("INSERT INTO guild_characters (guild_id, character_id) VALUES ($1, $2)", guildID, charID); err != nil {
|
||||
t.Fatalf("Failed to add char to guild: %v", err)
|
||||
}
|
||||
repo := NewTowerRepository(db)
|
||||
t.Cleanup(func() { TeardownTestDB(t, db) })
|
||||
return repo, db, charID, guildID
|
||||
}
|
||||
|
||||
func TestRepoTowerGetTowerDataAutoCreate(t *testing.T) {
|
||||
repo, _, charID, _ := setupTowerRepo(t)
|
||||
|
||||
// First call should auto-create the row
|
||||
td, err := repo.GetTowerData(charID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetTowerData failed: %v", err)
|
||||
}
|
||||
if td.TR != 0 || td.TRP != 0 || td.TSP != 0 {
|
||||
t.Errorf("Expected zero values, got TR=%d TRP=%d TSP=%d", td.TR, td.TRP, td.TSP)
|
||||
}
|
||||
if td.Skills == "" {
|
||||
t.Error("Expected non-empty default skills CSV")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoTowerGetTowerDataExisting(t *testing.T) {
|
||||
repo, db, charID, _ := setupTowerRepo(t)
|
||||
|
||||
if _, err := db.Exec("INSERT INTO tower (char_id, tr, trp, tsp, block1, block2) VALUES ($1, 10, 20, 30, 40, 50)", charID); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
|
||||
td, err := repo.GetTowerData(charID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetTowerData failed: %v", err)
|
||||
}
|
||||
if td.TR != 10 || td.TRP != 20 || td.TSP != 30 || td.Block1 != 40 || td.Block2 != 50 {
|
||||
t.Errorf("Expected 10/20/30/40/50, got %d/%d/%d/%d/%d", td.TR, td.TRP, td.TSP, td.Block1, td.Block2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoTowerGetSkills(t *testing.T) {
|
||||
repo, db, charID, _ := setupTowerRepo(t)
|
||||
|
||||
if _, err := db.Exec("INSERT INTO tower (char_id, skills) VALUES ($1, '1,2,3')", charID); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
|
||||
skills, err := repo.GetSkills(charID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetSkills failed: %v", err)
|
||||
}
|
||||
if skills != "1,2,3" {
|
||||
t.Errorf("Expected '1,2,3', got: %q", skills)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoTowerUpdateSkills(t *testing.T) {
|
||||
repo, db, charID, _ := setupTowerRepo(t)
|
||||
|
||||
if _, err := db.Exec("INSERT INTO tower (char_id, tsp) VALUES ($1, 100)", charID); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
|
||||
if err := repo.UpdateSkills(charID, "5,10,15", 20); err != nil {
|
||||
t.Fatalf("UpdateSkills failed: %v", err)
|
||||
}
|
||||
|
||||
var skills string
|
||||
var tsp int32
|
||||
if err := db.QueryRow("SELECT skills, tsp FROM tower WHERE char_id=$1", charID).Scan(&skills, &tsp); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if skills != "5,10,15" {
|
||||
t.Errorf("Expected skills='5,10,15', got: %q", skills)
|
||||
}
|
||||
if tsp != 80 {
|
||||
t.Errorf("Expected tsp=80 (100-20), got: %d", tsp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoTowerUpdateProgress(t *testing.T) {
|
||||
repo, db, charID, _ := setupTowerRepo(t)
|
||||
|
||||
if _, err := db.Exec("INSERT INTO tower (char_id) VALUES ($1)", charID); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
|
||||
if err := repo.UpdateProgress(charID, 5, 10, 15, 20); err != nil {
|
||||
t.Fatalf("UpdateProgress failed: %v", err)
|
||||
}
|
||||
|
||||
var tr, trp, tsp, block1 int32
|
||||
if err := db.QueryRow("SELECT tr, trp, tsp, block1 FROM tower WHERE char_id=$1", charID).Scan(&tr, &trp, &tsp, &block1); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if tr != 5 || trp != 10 || tsp != 15 || block1 != 20 {
|
||||
t.Errorf("Expected 5/10/15/20, got %d/%d/%d/%d", tr, trp, tsp, block1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoTowerGetGems(t *testing.T) {
|
||||
repo, db, charID, _ := setupTowerRepo(t)
|
||||
|
||||
if _, err := db.Exec("INSERT INTO tower (char_id, gems) VALUES ($1, '1,0,1')", charID); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
|
||||
gems, err := repo.GetGems(charID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetGems failed: %v", err)
|
||||
}
|
||||
if gems != "1,0,1" {
|
||||
t.Errorf("Expected '1,0,1', got: %q", gems)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoTowerUpdateGems(t *testing.T) {
|
||||
repo, db, charID, _ := setupTowerRepo(t)
|
||||
|
||||
if _, err := db.Exec("INSERT INTO tower (char_id) VALUES ($1)", charID); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
|
||||
if err := repo.UpdateGems(charID, "2,3,4"); err != nil {
|
||||
t.Fatalf("UpdateGems failed: %v", err)
|
||||
}
|
||||
|
||||
var gems string
|
||||
if err := db.QueryRow("SELECT gems FROM tower WHERE char_id=$1", charID).Scan(&gems); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if gems != "2,3,4" {
|
||||
t.Errorf("Expected '2,3,4', got: %q", gems)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoTowerGetGuildTowerRP(t *testing.T) {
|
||||
repo, _, _, guildID := setupTowerRepo(t)
|
||||
|
||||
rp, err := repo.GetGuildTowerRP(guildID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetGuildTowerRP failed: %v", err)
|
||||
}
|
||||
if rp != 0 {
|
||||
t.Errorf("Expected rp=0, got: %d", rp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoTowerDonateGuildTowerRP(t *testing.T) {
|
||||
repo, _, _, guildID := setupTowerRepo(t)
|
||||
|
||||
if err := repo.DonateGuildTowerRP(guildID, 100); err != nil {
|
||||
t.Fatalf("DonateGuildTowerRP failed: %v", err)
|
||||
}
|
||||
|
||||
rp, err := repo.GetGuildTowerRP(guildID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetGuildTowerRP failed: %v", err)
|
||||
}
|
||||
if rp != 100 {
|
||||
t.Errorf("Expected rp=100, got: %d", rp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoTowerGetGuildTowerPageAndRP(t *testing.T) {
|
||||
repo, db, _, guildID := setupTowerRepo(t)
|
||||
|
||||
if _, err := db.Exec("UPDATE guilds SET tower_mission_page=3, tower_rp=50 WHERE id=$1", guildID); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
|
||||
page, donated, err := repo.GetGuildTowerPageAndRP(guildID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetGuildTowerPageAndRP failed: %v", err)
|
||||
}
|
||||
if page != 3 {
|
||||
t.Errorf("Expected page=3, got: %d", page)
|
||||
}
|
||||
if donated != 50 {
|
||||
t.Errorf("Expected donated=50, got: %d", donated)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoTowerAdvanceTenrouiraiPage(t *testing.T) {
|
||||
repo, db, charID, guildID := setupTowerRepo(t)
|
||||
|
||||
// Read initial page
|
||||
var initialPage int
|
||||
if err := db.QueryRow("SELECT tower_mission_page FROM guilds WHERE id=$1", guildID).Scan(&initialPage); err != nil {
|
||||
t.Fatalf("Read initial page failed: %v", err)
|
||||
}
|
||||
|
||||
// Set initial mission scores
|
||||
if _, err := db.Exec("UPDATE guild_characters SET tower_mission_1=10, tower_mission_2=20, tower_mission_3=30 WHERE character_id=$1", charID); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
|
||||
if err := repo.AdvanceTenrouiraiPage(guildID); err != nil {
|
||||
t.Fatalf("AdvanceTenrouiraiPage failed: %v", err)
|
||||
}
|
||||
|
||||
var page int
|
||||
if err := db.QueryRow("SELECT tower_mission_page FROM guilds WHERE id=$1", guildID).Scan(&page); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if page != initialPage+1 {
|
||||
t.Errorf("Expected page=%d (initial+1), got: %d", initialPage+1, page)
|
||||
}
|
||||
|
||||
// Mission scores should be reset
|
||||
var m1, m2, m3 *int
|
||||
if err := db.QueryRow("SELECT tower_mission_1, tower_mission_2, tower_mission_3 FROM guild_characters WHERE character_id=$1", charID).Scan(&m1, &m2, &m3); err != nil {
|
||||
t.Fatalf("Verification query failed: %v", err)
|
||||
}
|
||||
if m1 != nil || m2 != nil || m3 != nil {
|
||||
t.Errorf("Expected NULL missions after advance, got: %v/%v/%v", m1, m2, m3)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoTowerGetTenrouiraiProgress(t *testing.T) {
|
||||
repo, db, charID, guildID := setupTowerRepo(t)
|
||||
|
||||
if _, err := db.Exec("UPDATE guilds SET tower_mission_page=2 WHERE id=$1", guildID); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
if _, err := db.Exec("UPDATE guild_characters SET tower_mission_1=5, tower_mission_2=10, tower_mission_3=15 WHERE character_id=$1", charID); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
|
||||
progress, err := repo.GetTenrouiraiProgress(guildID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetTenrouiraiProgress failed: %v", err)
|
||||
}
|
||||
if progress.Page != 2 {
|
||||
t.Errorf("Expected page=2, got: %d", progress.Page)
|
||||
}
|
||||
if progress.Mission1 != 5 {
|
||||
t.Errorf("Expected mission1=5, got: %d", progress.Mission1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoTowerGetTenrouiraiMissionScores(t *testing.T) {
|
||||
repo, db, charID, guildID := setupTowerRepo(t)
|
||||
|
||||
if _, err := db.Exec("UPDATE guild_characters SET tower_mission_1=42 WHERE character_id=$1", charID); err != nil {
|
||||
t.Fatalf("Setup failed: %v", err)
|
||||
}
|
||||
|
||||
scores, err := repo.GetTenrouiraiMissionScores(guildID, 1)
|
||||
if err != nil {
|
||||
t.Fatalf("GetTenrouiraiMissionScores failed: %v", err)
|
||||
}
|
||||
if len(scores) < 1 {
|
||||
t.Fatal("Expected at least 1 score entry")
|
||||
}
|
||||
if scores[0].Score != 42 {
|
||||
t.Errorf("Expected score=42, got: %d", scores[0].Score)
|
||||
}
|
||||
}
|
||||
@@ -243,6 +243,89 @@ func CreateTestGuild(t *testing.T, db *sqlx.DB, leaderCharID uint32, name string
|
||||
return guildID
|
||||
}
|
||||
|
||||
// CreateTestSignSession creates a sign session and returns the session ID.
|
||||
func CreateTestSignSession(t *testing.T, db *sqlx.DB, userID uint32, token string) uint32 {
|
||||
t.Helper()
|
||||
|
||||
var id uint32
|
||||
err := db.QueryRow(
|
||||
`INSERT INTO sign_sessions (user_id, token) VALUES ($1, $2) RETURNING id`,
|
||||
userID, token,
|
||||
).Scan(&id)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test sign session: %v", err)
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
// CreateTestServer creates a server entry for testing.
|
||||
func CreateTestServer(t *testing.T, db *sqlx.DB, serverID uint16) {
|
||||
t.Helper()
|
||||
|
||||
_, err := db.Exec(
|
||||
`INSERT INTO servers (server_id, current_players) VALUES ($1, 0)`,
|
||||
serverID,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test server: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// CreateTestUserBinary creates a user_binary row for the given character ID.
|
||||
func CreateTestUserBinary(t *testing.T, db *sqlx.DB, charID uint32) {
|
||||
t.Helper()
|
||||
|
||||
_, err := db.Exec(`INSERT INTO user_binary (id) VALUES ($1)`, charID)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test user_binary: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// CreateTestGachaShop creates a gacha shop entry and returns its ID.
|
||||
func CreateTestGachaShop(t *testing.T, db *sqlx.DB, name string, gachaType int) uint32 {
|
||||
t.Helper()
|
||||
|
||||
var id uint32
|
||||
err := db.QueryRow(
|
||||
`INSERT INTO gacha_shop (name, gacha_type, min_gr, min_hr, url_banner, url_feature, url_thumbnail, wide, recommended, hidden)
|
||||
VALUES ($1, $2, 0, 0, '', '', '', false, false, false) RETURNING id`,
|
||||
name, gachaType,
|
||||
).Scan(&id)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test gacha shop: %v", err)
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
// CreateTestGachaEntry creates a gacha entry and returns its ID.
|
||||
func CreateTestGachaEntry(t *testing.T, db *sqlx.DB, gachaID uint32, entryType int, weight int) uint32 {
|
||||
t.Helper()
|
||||
|
||||
var id uint32
|
||||
err := db.QueryRow(
|
||||
`INSERT INTO gacha_entries (gacha_id, entry_type, weight, rarity, item_type, item_number, item_quantity, rolls, frontier_points, daily_limit)
|
||||
VALUES ($1, $2, $3, 1, 0, 0, 0, 1, 0, 0) RETURNING id`,
|
||||
gachaID, entryType, weight,
|
||||
).Scan(&id)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test gacha entry: %v", err)
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
// CreateTestGachaItem creates a gacha item for an entry.
|
||||
func CreateTestGachaItem(t *testing.T, db *sqlx.DB, entryID uint32, itemType uint8, itemID uint16, quantity uint16) {
|
||||
t.Helper()
|
||||
|
||||
_, err := db.Exec(
|
||||
`INSERT INTO gacha_items (entry_id, item_type, item_id, quantity) VALUES ($1, $2, $3, $4)`,
|
||||
entryID, itemType, itemID, quantity,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create test gacha item: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// SetTestDB assigns a database to a Server and initializes all repositories.
|
||||
// Use this in integration tests instead of setting s.server.db directly.
|
||||
func SetTestDB(s *Server, db *sqlx.DB) {
|
||||
|
||||
Reference in New Issue
Block a user