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:
22
.github/workflows/go.yml
vendored
22
.github/workflows/go.yml
vendored
@@ -29,6 +29,22 @@ jobs:
|
|||||||
name: Test
|
name: Test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:15-alpine
|
||||||
|
env:
|
||||||
|
POSTGRES_USER: test
|
||||||
|
POSTGRES_PASSWORD: test
|
||||||
|
POSTGRES_DB: erupe_test
|
||||||
|
ports:
|
||||||
|
- 5433:5432
|
||||||
|
options: >-
|
||||||
|
--health-cmd pg_isready
|
||||||
|
--health-interval 2s
|
||||||
|
--health-timeout 2s
|
||||||
|
--health-retries 10
|
||||||
|
--mount type=tmpfs,destination=/var/lib/postgresql/data
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
@@ -42,6 +58,12 @@ jobs:
|
|||||||
|
|
||||||
- name: Run Tests with Race Detector and Coverage
|
- name: Run Tests with Race Detector and Coverage
|
||||||
run: go test -race -coverprofile=coverage.out ./... -timeout=10m
|
run: go test -race -coverprofile=coverage.out ./... -timeout=10m
|
||||||
|
env:
|
||||||
|
TEST_DB_HOST: localhost
|
||||||
|
TEST_DB_PORT: 5433
|
||||||
|
TEST_DB_USER: test
|
||||||
|
TEST_DB_PASSWORD: test
|
||||||
|
TEST_DB_NAME: erupe_test
|
||||||
|
|
||||||
- name: Check coverage threshold
|
- name: Check coverage threshold
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
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
|
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.
|
// SetTestDB assigns a database to a Server and initializes all repositories.
|
||||||
// Use this in integration tests instead of setting s.server.db directly.
|
// Use this in integration tests instead of setting s.server.db directly.
|
||||||
func SetTestDB(s *Server, db *sqlx.DB) {
|
func SetTestDB(s *Server, db *sqlx.DB) {
|
||||||
|
|||||||
Reference in New Issue
Block a user