mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-22 07:32:32 +01:00
refactor(tower): extract tower logic into TowerService
The tower repo had business logic beyond simple CRUD: AddGem used a fetch-transform-save pattern, progress capping was inline in the handler, and RP donation orchestrated multiple repo calls with conditional page advancement. Move these into a new TowerService following the established service layer pattern.
This commit is contained in:
@@ -307,7 +307,7 @@ func handleMsgMhfGetTenrouirai(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
data = append(data, bf)
|
data = append(data, bf)
|
||||||
}
|
}
|
||||||
case 4:
|
case 4:
|
||||||
progress, err := s.server.towerRepo.GetTenrouiraiProgress(pkt.GuildID)
|
progress, err := s.server.towerService.GetTenrouiraiProgressCapped(pkt.GuildID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to read tower mission page", zap.Error(err))
|
s.logger.Error("Failed to read tower mission page", zap.Error(err))
|
||||||
} else {
|
} else {
|
||||||
@@ -317,19 +317,6 @@ func handleMsgMhfGetTenrouirai(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
tenrouirai.Progress[0].Mission3 = progress.Mission3
|
tenrouirai.Progress[0].Mission3 = progress.Mission3
|
||||||
}
|
}
|
||||||
|
|
||||||
if tenrouirai.Progress[0].Page < 1 {
|
|
||||||
tenrouirai.Progress[0].Page = 1
|
|
||||||
}
|
|
||||||
if tenrouirai.Progress[0].Mission1 > tenrouiraiData[(tenrouirai.Progress[0].Page*3)-3].Goal {
|
|
||||||
tenrouirai.Progress[0].Mission1 = tenrouiraiData[(tenrouirai.Progress[0].Page*3)-3].Goal
|
|
||||||
}
|
|
||||||
if tenrouirai.Progress[0].Mission2 > tenrouiraiData[(tenrouirai.Progress[0].Page*3)-2].Goal {
|
|
||||||
tenrouirai.Progress[0].Mission2 = tenrouiraiData[(tenrouirai.Progress[0].Page*3)-2].Goal
|
|
||||||
}
|
|
||||||
if tenrouirai.Progress[0].Mission3 > tenrouiraiData[(tenrouirai.Progress[0].Page*3)-1].Goal {
|
|
||||||
tenrouirai.Progress[0].Mission3 = tenrouiraiData[(tenrouirai.Progress[0].Page*3)-1].Goal
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, progress := range tenrouirai.Progress {
|
for _, progress := range tenrouirai.Progress {
|
||||||
bf := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
bf.WriteUint8(progress.Page)
|
bf.WriteUint8(progress.Page)
|
||||||
@@ -384,33 +371,18 @@ func handleMsgMhfPostTenrouirai(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if pkt.Op == 2 {
|
if pkt.Op == 2 {
|
||||||
page, donated, err := s.server.towerRepo.GetGuildTowerPageAndRP(pkt.GuildID)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to read guild tower state for donation", zap.Error(err))
|
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var requirement int
|
|
||||||
for i := 0; i < (page*3)+1; i++ {
|
|
||||||
requirement += int(tenrouiraiData[i].Cost)
|
|
||||||
}
|
|
||||||
|
|
||||||
bf := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
|
|
||||||
sd, err := GetCharacterSaveData(s, s.charID)
|
sd, err := GetCharacterSaveData(s, s.charID)
|
||||||
if err == nil && sd != nil {
|
if err == nil && sd != nil {
|
||||||
sd.RP -= pkt.DonatedRP
|
sd.RP -= pkt.DonatedRP
|
||||||
sd.Save(s)
|
sd.Save(s)
|
||||||
if donated+int(pkt.DonatedRP) >= requirement {
|
result, err := s.server.towerService.DonateGuildTowerRP(pkt.GuildID, pkt.DonatedRP)
|
||||||
if err := s.server.towerRepo.AdvanceTenrouiraiPage(pkt.GuildID); err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to advance tower mission page", zap.Error(err))
|
s.logger.Error("Failed to process tower RP donation", zap.Error(err))
|
||||||
}
|
bf.WriteUint32(0)
|
||||||
pkt.DonatedRP = uint16(requirement - donated)
|
} else {
|
||||||
}
|
bf.WriteUint32(uint32(result.ActualDonated))
|
||||||
bf.WriteUint32(uint32(pkt.DonatedRP))
|
|
||||||
if err := s.server.towerRepo.DonateGuildTowerRP(pkt.GuildID, pkt.DonatedRP); err != nil {
|
|
||||||
s.logger.Error("Failed to update guild tower RP", zap.Error(err))
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bf.WriteUint32(0)
|
bf.WriteUint32(0)
|
||||||
@@ -509,7 +481,7 @@ func handleMsgMhfPostGemInfo(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
switch pkt.Op {
|
switch pkt.Op {
|
||||||
case 1: // Add gem
|
case 1: // Add gem
|
||||||
i := int((pkt.Gem >> 8 * 5) + (pkt.Gem - pkt.Gem&0xFF00 - 1%5))
|
i := int((pkt.Gem >> 8 * 5) + (pkt.Gem - pkt.Gem&0xFF00 - 1%5))
|
||||||
if err := s.server.towerRepo.AddGem(s.charID, i, int(pkt.Quantity)); err != nil {
|
if err := s.server.towerService.AddGem(s.charID, i, int(pkt.Quantity)); err != nil {
|
||||||
s.logger.Error("Failed to update tower gems", zap.Error(err))
|
s.logger.Error("Failed to update tower gems", zap.Error(err))
|
||||||
}
|
}
|
||||||
case 2: // Transfer gem
|
case 2: // Transfer gem
|
||||||
|
|||||||
@@ -210,7 +210,6 @@ type TowerRepo interface {
|
|||||||
UpdateProgress(charID uint32, tr, trp, cost, block1 int32) error
|
UpdateProgress(charID uint32, tr, trp, cost, block1 int32) error
|
||||||
GetGems(charID uint32) (string, error)
|
GetGems(charID uint32) (string, error)
|
||||||
UpdateGems(charID uint32, gems string) error
|
UpdateGems(charID uint32, gems string) error
|
||||||
AddGem(charID uint32, gemIndex int, quantity int) error
|
|
||||||
GetTenrouiraiProgress(guildID uint32) (TenrouiraiProgressData, error)
|
GetTenrouiraiProgress(guildID uint32) (TenrouiraiProgressData, error)
|
||||||
GetTenrouiraiMissionScores(guildID uint32, missionIndex uint8) ([]TenrouiraiCharScore, error)
|
GetTenrouiraiMissionScores(guildID uint32, missionIndex uint8) ([]TenrouiraiCharScore, error)
|
||||||
GetGuildTowerRP(guildID uint32) (uint32, error)
|
GetGuildTowerRP(guildID uint32) (uint32, error)
|
||||||
|
|||||||
@@ -944,6 +944,7 @@ type mockTowerRepo struct {
|
|||||||
skillsErr error
|
skillsErr error
|
||||||
gems string
|
gems string
|
||||||
gemsErr error
|
gemsErr error
|
||||||
|
updatedGems string
|
||||||
|
|
||||||
progress TenrouiraiProgressData
|
progress TenrouiraiProgressData
|
||||||
progressErr error
|
progressErr error
|
||||||
@@ -954,6 +955,10 @@ type mockTowerRepo struct {
|
|||||||
page int
|
page int
|
||||||
donated int
|
donated int
|
||||||
pageRPErr error
|
pageRPErr error
|
||||||
|
advanceErr error
|
||||||
|
advanceCalled bool
|
||||||
|
donateErr error
|
||||||
|
donatedRP uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockTowerRepo) GetTowerData(_ uint32) (TowerData, error) { return m.towerData, m.towerDataErr }
|
func (m *mockTowerRepo) GetTowerData(_ uint32) (TowerData, error) { return m.towerData, m.towerDataErr }
|
||||||
@@ -961,8 +966,10 @@ func (m *mockTowerRepo) GetSkills(_ uint32) (string, error) { return m.s
|
|||||||
func (m *mockTowerRepo) UpdateSkills(_ uint32, _ string, _ int32) error { return nil }
|
func (m *mockTowerRepo) UpdateSkills(_ uint32, _ string, _ int32) error { return nil }
|
||||||
func (m *mockTowerRepo) UpdateProgress(_ uint32, _, _, _, _ int32) error { return nil }
|
func (m *mockTowerRepo) UpdateProgress(_ uint32, _, _, _, _ int32) error { return nil }
|
||||||
func (m *mockTowerRepo) GetGems(_ uint32) (string, error) { return m.gems, m.gemsErr }
|
func (m *mockTowerRepo) GetGems(_ uint32) (string, error) { return m.gems, m.gemsErr }
|
||||||
func (m *mockTowerRepo) UpdateGems(_ uint32, _ string) error { return nil }
|
func (m *mockTowerRepo) UpdateGems(_ uint32, gems string) error {
|
||||||
func (m *mockTowerRepo) AddGem(_ uint32, _ int, _ int) error { return nil }
|
m.updatedGems = gems
|
||||||
|
return nil
|
||||||
|
}
|
||||||
func (m *mockTowerRepo) GetTenrouiraiProgress(_ uint32) (TenrouiraiProgressData, error) {
|
func (m *mockTowerRepo) GetTenrouiraiProgress(_ uint32) (TenrouiraiProgressData, error) {
|
||||||
return m.progress, m.progressErr
|
return m.progress, m.progressErr
|
||||||
}
|
}
|
||||||
@@ -973,8 +980,14 @@ func (m *mockTowerRepo) GetGuildTowerRP(_ uint32) (uint32, error) { return m.gui
|
|||||||
func (m *mockTowerRepo) GetGuildTowerPageAndRP(_ uint32) (int, int, error) {
|
func (m *mockTowerRepo) GetGuildTowerPageAndRP(_ uint32) (int, int, error) {
|
||||||
return m.page, m.donated, m.pageRPErr
|
return m.page, m.donated, m.pageRPErr
|
||||||
}
|
}
|
||||||
func (m *mockTowerRepo) AdvanceTenrouiraiPage(_ uint32) error { return nil }
|
func (m *mockTowerRepo) AdvanceTenrouiraiPage(_ uint32) error {
|
||||||
func (m *mockTowerRepo) DonateGuildTowerRP(_ uint32, _ uint16) error { return nil }
|
m.advanceCalled = true
|
||||||
|
return m.advanceErr
|
||||||
|
}
|
||||||
|
func (m *mockTowerRepo) DonateGuildTowerRP(_ uint32, rp uint16) error {
|
||||||
|
m.donatedRP = rp
|
||||||
|
return m.donateErr
|
||||||
|
}
|
||||||
|
|
||||||
// --- mockFestaRepo ---
|
// --- mockFestaRepo ---
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ package channelserver
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"erupe-ce/common/stringsupport"
|
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -78,16 +76,6 @@ func (r *TowerRepository) UpdateGems(charID uint32, gems string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddGem adds quantity to a specific gem index.
|
|
||||||
func (r *TowerRepository) AddGem(charID uint32, gemIndex int, quantity int) error {
|
|
||||||
gems, err := r.GetGems(charID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
newGems := stringsupport.CSVSetIndex(gems, gemIndex, stringsupport.CSVGetIndex(gems, gemIndex)+quantity)
|
|
||||||
return r.UpdateGems(charID, newGems)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TenrouiraiProgressData holds the guild's tenrouirai (sky corridor) progress.
|
// TenrouiraiProgressData holds the guild's tenrouirai (sky corridor) progress.
|
||||||
type TenrouiraiProgressData struct {
|
type TenrouiraiProgressData struct {
|
||||||
Page uint8
|
Page uint8
|
||||||
|
|||||||
102
server/channelserver/svc_tower.go
Normal file
102
server/channelserver/svc_tower.go
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
package channelserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"erupe-ce/common/stringsupport"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DonateRPResult holds the outcome of a guild tower RP donation.
|
||||||
|
type DonateRPResult struct {
|
||||||
|
ActualDonated uint16
|
||||||
|
Advanced bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// TowerService encapsulates tower business logic, sitting between handlers and repos.
|
||||||
|
type TowerService struct {
|
||||||
|
towerRepo TowerRepo
|
||||||
|
logger *zap.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTowerService creates a new TowerService.
|
||||||
|
func NewTowerService(tr TowerRepo, log *zap.Logger) *TowerService {
|
||||||
|
return &TowerService{
|
||||||
|
towerRepo: tr,
|
||||||
|
logger: log,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddGem adds quantity to a specific gem index for a character.
|
||||||
|
// This is a fetch-transform-save operation that reads the current gems CSV,
|
||||||
|
// updates the value at the given index, and writes back.
|
||||||
|
func (svc *TowerService) AddGem(charID uint32, gemIndex int, quantity int) error {
|
||||||
|
gems, err := svc.towerRepo.GetGems(charID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
newGems := stringsupport.CSVSetIndex(gems, gemIndex, stringsupport.CSVGetIndex(gems, gemIndex)+quantity)
|
||||||
|
return svc.towerRepo.UpdateGems(charID, newGems)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTenrouiraiProgressCapped returns the guild's tenrouirai progress with
|
||||||
|
// mission scores capped to their respective goals.
|
||||||
|
func (svc *TowerService) GetTenrouiraiProgressCapped(guildID uint32) (TenrouiraiProgressData, error) {
|
||||||
|
progress, err := svc.towerRepo.GetTenrouiraiProgress(guildID)
|
||||||
|
if err != nil {
|
||||||
|
return progress, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if progress.Page < 1 {
|
||||||
|
progress.Page = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
idx := int(progress.Page*3) - 3
|
||||||
|
if idx >= 0 && idx+2 < len(tenrouiraiData) {
|
||||||
|
if progress.Mission1 > tenrouiraiData[idx].Goal {
|
||||||
|
progress.Mission1 = tenrouiraiData[idx].Goal
|
||||||
|
}
|
||||||
|
if progress.Mission2 > tenrouiraiData[idx+1].Goal {
|
||||||
|
progress.Mission2 = tenrouiraiData[idx+1].Goal
|
||||||
|
}
|
||||||
|
if progress.Mission3 > tenrouiraiData[idx+2].Goal {
|
||||||
|
progress.Mission3 = tenrouiraiData[idx+2].Goal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return progress, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DonateGuildTowerRP processes a tower RP donation, advancing the mission page
|
||||||
|
// if the cumulative donation meets the requirement. Returns the actual RP consumed
|
||||||
|
// and whether the page was advanced.
|
||||||
|
func (svc *TowerService) DonateGuildTowerRP(guildID uint32, donatedRP uint16) (*DonateRPResult, error) {
|
||||||
|
page, donated, err := svc.towerRepo.GetGuildTowerPageAndRP(guildID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var requirement int
|
||||||
|
for i := 0; i < (page*3)+1 && i < len(tenrouiraiData); i++ {
|
||||||
|
requirement += int(tenrouiraiData[i].Cost)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := &DonateRPResult{
|
||||||
|
ActualDonated: donatedRP,
|
||||||
|
}
|
||||||
|
|
||||||
|
if donated+int(donatedRP) >= requirement {
|
||||||
|
if err := svc.towerRepo.AdvanceTenrouiraiPage(guildID); err != nil {
|
||||||
|
svc.logger.Error("Failed to advance tower mission page", zap.Error(err))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result.ActualDonated = uint16(requirement - donated)
|
||||||
|
result.Advanced = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := svc.towerRepo.DonateGuildTowerRP(guildID, result.ActualDonated); err != nil {
|
||||||
|
svc.logger.Error("Failed to update guild tower RP", zap.Error(err))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
185
server/channelserver/svc_tower_test.go
Normal file
185
server/channelserver/svc_tower_test.go
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
package channelserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newTestTowerService(mock *mockTowerRepo) *TowerService {
|
||||||
|
logger, _ := zap.NewDevelopment()
|
||||||
|
return NewTowerService(mock, logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- AddGem tests ---
|
||||||
|
|
||||||
|
func TestTowerService_AddGem_Success(t *testing.T) {
|
||||||
|
mock := &mockTowerRepo{gems: "0,0,5,0,0"}
|
||||||
|
svc := newTestTowerService(mock)
|
||||||
|
|
||||||
|
err := svc.AddGem(1, 2, 3)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("AddGem returned error: %v", err)
|
||||||
|
}
|
||||||
|
// Gem at index 2 was 5, added 3, so should be 8
|
||||||
|
if mock.updatedGems != "0,0,8,0,0" {
|
||||||
|
t.Errorf("updatedGems = %q, want %q", mock.updatedGems, "0,0,8,0,0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTowerService_AddGem_GetGemsError(t *testing.T) {
|
||||||
|
mock := &mockTowerRepo{gemsErr: errors.New("db error")}
|
||||||
|
svc := newTestTowerService(mock)
|
||||||
|
|
||||||
|
err := svc.AddGem(1, 0, 1)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("AddGem should return error when GetGems fails")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- GetTenrouiraiProgressCapped tests ---
|
||||||
|
|
||||||
|
func TestTowerService_GetTenrouiraiProgressCapped_CapsToGoals(t *testing.T) {
|
||||||
|
// Page 1 missions have goals: 80, 16, 50 (from tenrouiraiData indices 0,1,2)
|
||||||
|
mock := &mockTowerRepo{
|
||||||
|
progress: TenrouiraiProgressData{
|
||||||
|
Page: 1,
|
||||||
|
Mission1: 9999,
|
||||||
|
Mission2: 9999,
|
||||||
|
Mission3: 9999,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
svc := newTestTowerService(mock)
|
||||||
|
|
||||||
|
result, err := svc.GetTenrouiraiProgressCapped(10)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.Mission1 != tenrouiraiData[0].Goal {
|
||||||
|
t.Errorf("Mission1 = %d, want %d", result.Mission1, tenrouiraiData[0].Goal)
|
||||||
|
}
|
||||||
|
if result.Mission2 != tenrouiraiData[1].Goal {
|
||||||
|
t.Errorf("Mission2 = %d, want %d", result.Mission2, tenrouiraiData[1].Goal)
|
||||||
|
}
|
||||||
|
if result.Mission3 != tenrouiraiData[2].Goal {
|
||||||
|
t.Errorf("Mission3 = %d, want %d", result.Mission3, tenrouiraiData[2].Goal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTowerService_GetTenrouiraiProgressCapped_BelowGoals(t *testing.T) {
|
||||||
|
mock := &mockTowerRepo{
|
||||||
|
progress: TenrouiraiProgressData{
|
||||||
|
Page: 1,
|
||||||
|
Mission1: 10,
|
||||||
|
Mission2: 5,
|
||||||
|
Mission3: 20,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
svc := newTestTowerService(mock)
|
||||||
|
|
||||||
|
result, err := svc.GetTenrouiraiProgressCapped(10)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if result.Mission1 != 10 {
|
||||||
|
t.Errorf("Mission1 = %d, want 10", result.Mission1)
|
||||||
|
}
|
||||||
|
if result.Mission2 != 5 {
|
||||||
|
t.Errorf("Mission2 = %d, want 5", result.Mission2)
|
||||||
|
}
|
||||||
|
if result.Mission3 != 20 {
|
||||||
|
t.Errorf("Mission3 = %d, want 20", result.Mission3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTowerService_GetTenrouiraiProgressCapped_MinPage1(t *testing.T) {
|
||||||
|
mock := &mockTowerRepo{
|
||||||
|
progress: TenrouiraiProgressData{Page: 0},
|
||||||
|
}
|
||||||
|
svc := newTestTowerService(mock)
|
||||||
|
|
||||||
|
result, err := svc.GetTenrouiraiProgressCapped(10)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if result.Page != 1 {
|
||||||
|
t.Errorf("Page = %d, want 1", result.Page)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTowerService_GetTenrouiraiProgressCapped_DBError(t *testing.T) {
|
||||||
|
mock := &mockTowerRepo{progressErr: errors.New("db error")}
|
||||||
|
svc := newTestTowerService(mock)
|
||||||
|
|
||||||
|
_, err := svc.GetTenrouiraiProgressCapped(10)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error from DB failure")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- DonateGuildTowerRP tests ---
|
||||||
|
|
||||||
|
func TestTowerService_DonateGuildTowerRP_NoAdvance(t *testing.T) {
|
||||||
|
mock := &mockTowerRepo{
|
||||||
|
page: 1,
|
||||||
|
donated: 0,
|
||||||
|
}
|
||||||
|
svc := newTestTowerService(mock)
|
||||||
|
|
||||||
|
result, err := svc.DonateGuildTowerRP(10, 1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if result.Advanced {
|
||||||
|
t.Error("should not advance when donation < requirement")
|
||||||
|
}
|
||||||
|
if result.ActualDonated != 1 {
|
||||||
|
t.Errorf("ActualDonated = %d, want 1", result.ActualDonated)
|
||||||
|
}
|
||||||
|
if mock.advanceCalled {
|
||||||
|
t.Error("AdvanceTenrouiraiPage should not be called")
|
||||||
|
}
|
||||||
|
if mock.donatedRP != 1 {
|
||||||
|
t.Errorf("donatedRP = %d, want 1", mock.donatedRP)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTowerService_DonateGuildTowerRP_AdvancesPage(t *testing.T) {
|
||||||
|
// Compute the requirement for page 1: sum of Cost for indices 0..3
|
||||||
|
var requirement int
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
requirement += int(tenrouiraiData[i].Cost)
|
||||||
|
}
|
||||||
|
|
||||||
|
mock := &mockTowerRepo{
|
||||||
|
page: 1,
|
||||||
|
donated: requirement - 10, // 10 short of requirement
|
||||||
|
}
|
||||||
|
svc := newTestTowerService(mock)
|
||||||
|
|
||||||
|
result, err := svc.DonateGuildTowerRP(10, 100) // donating 100, but only 10 needed
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if !result.Advanced {
|
||||||
|
t.Error("should advance when donation meets requirement")
|
||||||
|
}
|
||||||
|
if result.ActualDonated != 10 {
|
||||||
|
t.Errorf("ActualDonated = %d, want 10 (capped to remaining)", result.ActualDonated)
|
||||||
|
}
|
||||||
|
if !mock.advanceCalled {
|
||||||
|
t.Error("AdvanceTenrouiraiPage should be called")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTowerService_DonateGuildTowerRP_DBError(t *testing.T) {
|
||||||
|
mock := &mockTowerRepo{pageRPErr: errors.New("db error")}
|
||||||
|
svc := newTestTowerService(mock)
|
||||||
|
|
||||||
|
_, err := svc.DonateGuildTowerRP(10, 100)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error from DB failure")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -75,6 +75,7 @@ type Server struct {
|
|||||||
guildService *GuildService
|
guildService *GuildService
|
||||||
achievementService *AchievementService
|
achievementService *AchievementService
|
||||||
gachaService *GachaService
|
gachaService *GachaService
|
||||||
|
towerService *TowerService
|
||||||
erupeConfig *cfg.Config
|
erupeConfig *cfg.Config
|
||||||
acceptConns chan net.Conn
|
acceptConns chan net.Conn
|
||||||
deleteConns chan net.Conn
|
deleteConns chan net.Conn
|
||||||
@@ -161,6 +162,7 @@ func NewServer(config *Config) *Server {
|
|||||||
s.guildService = NewGuildService(s.guildRepo, s.mailService, s.charRepo, s.logger)
|
s.guildService = NewGuildService(s.guildRepo, s.mailService, s.charRepo, s.logger)
|
||||||
s.achievementService = NewAchievementService(s.achievementRepo, s.logger)
|
s.achievementService = NewAchievementService(s.achievementRepo, s.logger)
|
||||||
s.gachaService = NewGachaService(s.gachaRepo, s.userRepo, s.charRepo, s.logger, config.ErupeConfig.GameplayOptions.MaximumNP)
|
s.gachaService = NewGachaService(s.gachaRepo, s.userRepo, s.charRepo, s.logger, config.ErupeConfig.GameplayOptions.MaximumNP)
|
||||||
|
s.towerService = NewTowerService(s.towerRepo, s.logger)
|
||||||
|
|
||||||
// Mezeporta
|
// Mezeporta
|
||||||
s.stages.Store("sl1Ns200p0a0u0", NewStage("sl1Ns200p0a0u0"))
|
s.stages.Store("sl1Ns200p0a0u0", NewStage("sl1Ns200p0a0u0"))
|
||||||
|
|||||||
@@ -78,6 +78,11 @@ func ensureGachaService(s *Server) {
|
|||||||
s.gachaService = NewGachaService(s.gachaRepo, s.userRepo, s.charRepo, s.logger, 100000)
|
s.gachaService = NewGachaService(s.gachaRepo, s.userRepo, s.charRepo, s.logger, 100000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ensureTowerService wires the TowerService from the server's current repos.
|
||||||
|
func ensureTowerService(s *Server) {
|
||||||
|
s.towerService = NewTowerService(s.towerRepo, s.logger)
|
||||||
|
}
|
||||||
|
|
||||||
// createMockSession creates a minimal Session for testing.
|
// createMockSession creates a minimal Session for testing.
|
||||||
// Imported from v9.2.x-stable and adapted for main.
|
// Imported from v9.2.x-stable and adapted for main.
|
||||||
func createMockSession(charID uint32, server *Server) *Session {
|
func createMockSession(charID uint32, server *Server) *Session {
|
||||||
|
|||||||
Reference in New Issue
Block a user