172 lines
4.7 KiB
Go
172 lines
4.7 KiB
Go
package common
|
|
|
|
import (
|
|
"encoding/json"
|
|
"gac-hkrpg-go/internal/testutil"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"testing"
|
|
)
|
|
|
|
func chdirRepoRoot(t *testing.T) {
|
|
t.Helper()
|
|
_, source, _, ok := runtime.Caller(0)
|
|
if !ok {
|
|
t.Fatal("cannot locate test helper source")
|
|
}
|
|
root := filepath.Clean(filepath.Join(filepath.Dir(source), "..", ".."))
|
|
if _, err := os.Stat(filepath.Join(root, "go.mod")); err != nil {
|
|
t.Fatalf("cannot locate repository root from %s: %v", source, err)
|
|
}
|
|
temporary := t.TempDir()
|
|
if err := testutil.CopyRuntime(root, temporary); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Chdir(temporary)
|
|
}
|
|
|
|
func TestBannersConfigLoadAndVisibility(t *testing.T) {
|
|
chdirRepoRoot(t)
|
|
if err := LoadBannersConfig(); err != nil {
|
|
t.Fatalf("failed to load banners config: %v", err)
|
|
}
|
|
|
|
mode0 := GetVisibleBanners(0)
|
|
if len(mode0) < 13 {
|
|
t.Errorf("expected at least 13 visible banners for mode 0, got %d", len(mode0))
|
|
}
|
|
|
|
mode1 := GetVisibleBanners(1)
|
|
if len(mode1) < len(mode0) {
|
|
t.Errorf("expected mode 1 >= mode 0, got mode1=%d mode0=%d", len(mode1), len(mode0))
|
|
}
|
|
|
|
// Verify no duplicate IDs in mode 1
|
|
seen := make(map[uint32]bool)
|
|
for _, b := range mode1 {
|
|
if seen[b.ID] {
|
|
t.Fatalf("duplicate banner ID %d found in mode 1", b.ID)
|
|
}
|
|
seen[b.ID] = true
|
|
}
|
|
|
|
// Check mode 2 (character only)
|
|
mode2 := GetVisibleBanners(2)
|
|
if len(mode2) == 0 || len(mode2) >= len(mode1) {
|
|
t.Errorf("unexpected banner count for mode 2: %d", len(mode2))
|
|
}
|
|
|
|
// Check mode 3 (lightcone only)
|
|
mode3 := GetVisibleBanners(3)
|
|
if len(mode3) == 0 || len(mode3) >= len(mode1) {
|
|
t.Errorf("unexpected banner count for mode 3: %d", len(mode3))
|
|
}
|
|
}
|
|
|
|
func TestBannerProtoSerialization(t *testing.T) {
|
|
chdirRepoRoot(t)
|
|
if err := LoadBannersConfig(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Check banner 1001 (Standard)
|
|
b1001 := FindGachaBanner(1001, 1)
|
|
if b1001 == nil {
|
|
t.Fatal("banner 1001 not found")
|
|
}
|
|
proto1001 := b1001.ToProtoInfo()
|
|
if proto1001.GachaId != 1001 {
|
|
t.Fatalf("expected gacha_id 1001, got %d", proto1001.GachaId)
|
|
}
|
|
if proto1001.GachaCeiling == nil || !proto1001.GachaCeiling.IsClaimed || len(proto1001.GachaCeiling.AvatarList) != 7 {
|
|
t.Fatalf("expected claimed gacha ceiling with 7 avatars, got %+v", proto1001.GachaCeiling)
|
|
}
|
|
|
|
// Check banner 2120 (AvatarUp)
|
|
b2120 := FindGachaBanner(2120, 1)
|
|
if b2120 == nil {
|
|
t.Fatal("banner 2120 not found")
|
|
}
|
|
proto2120 := b2120.ToProtoInfo()
|
|
if proto2120.GachaId != 2120 {
|
|
t.Fatalf("expected gacha_id 2120, got %d", proto2120.GachaId)
|
|
}
|
|
if len(proto2120.ONIHOMCELJK) == 0 || proto2120.ONIHOMCELJK[0] != 1505 {
|
|
t.Fatalf("expected featured 5-star 1505 in wire #5, got %+v", proto2120.ONIHOMCELJK)
|
|
}
|
|
if len(proto2120.ItemDetailList) != 1 || proto2120.ItemDetailList[0] != 11 {
|
|
t.Fatalf("expected AvatarUp marker 11 in ItemDetailList, got %+v", proto2120.ItemDetailList)
|
|
}
|
|
if len(proto2120.PrizeItemList) == 0 {
|
|
t.Fatal("expected PrizeItemList to contain rateUp items")
|
|
}
|
|
}
|
|
|
|
func TestPityProgression(t *testing.T) {
|
|
var state GachaPityState
|
|
if FiveStarChance90(1) != 0.006 {
|
|
t.Fatalf("expected base 5-star chance 0.006, got %f", FiveStarChance90(1))
|
|
}
|
|
if FiveStarChance90(76) != 0.324 {
|
|
t.Fatalf("expected soft pity 5-star chance 0.324, got %f", FiveStarChance90(76))
|
|
}
|
|
if FiveStarChance90(90) != 1.0 {
|
|
t.Fatalf("expected hard pity 5-star chance 1.0, got %f", FiveStarChance90(90))
|
|
}
|
|
|
|
// Test pity state transitions
|
|
state.RecordThreeStar()
|
|
if state.PullsSinceFiveStar != 1 || state.PullsSinceFourStarOrHigher != 1 || state.TotalPulls != 1 {
|
|
t.Fatalf("unexpected state after 3-star: %+v", state)
|
|
}
|
|
|
|
state.RecordFourStar()
|
|
if state.PullsSinceFiveStar != 2 || state.PullsSinceFourStarOrHigher != 0 || state.TotalPulls != 2 {
|
|
t.Fatalf("unexpected state after 4-star: %+v", state)
|
|
}
|
|
|
|
state.RecordFiveStar()
|
|
if state.PullsSinceFiveStar != 0 || state.PullsSinceFourStarOrHigher != 0 || state.TotalPulls != 3 {
|
|
t.Fatalf("unexpected state after 5-star: %+v", state)
|
|
}
|
|
}
|
|
|
|
func TestGachaRollExecution(t *testing.T) {
|
|
chdirRepoRoot(t)
|
|
if err := LoadBannersConfig(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
player, err := LoadFreesrData()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
banner := FindGachaBanner(2120, 1)
|
|
if banner == nil {
|
|
t.Fatal("banner 2120 not found")
|
|
}
|
|
|
|
res, err := PerformGachaRolls(banner, 10, player)
|
|
if err != nil {
|
|
t.Fatalf("PerformGachaRolls failed: %v", err)
|
|
}
|
|
if len(res.Items) != 10 {
|
|
t.Fatalf("expected 10 items, got %d", len(res.Items))
|
|
}
|
|
|
|
// Verify persistence in freesr-data.json
|
|
raw, err := os.ReadFile("freesr-data.json")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var doc map[string]json.RawMessage
|
|
if err := json.Unmarshal(raw, &doc); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, ok := doc["gacha_pity"]; !ok {
|
|
t.Fatal("missing gacha_pity in freesr-data.json")
|
|
}
|
|
}
|