Files
Erupe/server/channelserver/handlers_character_test.go
Houmgaor 0f1684564d test: expand channelserver coverage from 7.5% to 12%
Add comprehensive tests for channelserver package:
- handlers_character_test.go: CharacterSaveData, pointer constants
- handlers_data_test.go: grpToGR function with boundary tests
- handlers_quest_test.go: findSubSliceIndices, equal functions
- handlers_simple_test.go: simple handlers, ack responses
- handlers_util_test.go: stub handlers, ack helpers
- sys_channel_server_test.go: Server, Raviente, stages, semaphores
- sys_object_test.go: Object, Stage, stageBinaryKey structs

All tests pass with race detection enabled.
2026-02-02 11:25:08 +01:00

285 lines
7.8 KiB
Go

package channelserver
import (
"encoding/binary"
"testing"
)
func TestCharacterSaveDataStruct(t *testing.T) {
saveData := &CharacterSaveData{
CharID: 12345,
Name: "TestHunter",
IsNewCharacter: false,
Gender: true,
RP: 1000,
WeaponType: 5,
WeaponID: 100,
HRP: 500,
GR: 50,
}
if saveData.CharID != 12345 {
t.Errorf("CharID = %d, want 12345", saveData.CharID)
}
if saveData.Name != "TestHunter" {
t.Errorf("Name = %s, want TestHunter", saveData.Name)
}
if saveData.Gender != true {
t.Error("Gender should be true")
}
if saveData.RP != 1000 {
t.Errorf("RP = %d, want 1000", saveData.RP)
}
if saveData.WeaponType != 5 {
t.Errorf("WeaponType = %d, want 5", saveData.WeaponType)
}
if saveData.HRP != 500 {
t.Errorf("HRP = %d, want 500", saveData.HRP)
}
if saveData.GR != 50 {
t.Errorf("GR = %d, want 50", saveData.GR)
}
}
func TestCharacterSaveData_InitialValues(t *testing.T) {
saveData := &CharacterSaveData{}
if saveData.CharID != 0 {
t.Errorf("CharID should default to 0, got %d", saveData.CharID)
}
if saveData.IsNewCharacter != false {
t.Error("IsNewCharacter should default to false")
}
if saveData.Gender != false {
t.Error("Gender should default to false")
}
}
func TestCharacterSaveData_BinarySlices(t *testing.T) {
saveData := &CharacterSaveData{
HouseTier: make([]byte, 5),
HouseData: make([]byte, 195),
BookshelfData: make([]byte, 5576),
GalleryData: make([]byte, 1748),
ToreData: make([]byte, 240),
GardenData: make([]byte, 68),
KQF: make([]byte, 8),
}
// Verify slice sizes match expected game data sizes
if len(saveData.HouseTier) != 5 {
t.Errorf("HouseTier len = %d, want 5", len(saveData.HouseTier))
}
if len(saveData.HouseData) != 195 {
t.Errorf("HouseData len = %d, want 195", len(saveData.HouseData))
}
if len(saveData.BookshelfData) != 5576 {
t.Errorf("BookshelfData len = %d, want 5576", len(saveData.BookshelfData))
}
if len(saveData.GalleryData) != 1748 {
t.Errorf("GalleryData len = %d, want 1748", len(saveData.GalleryData))
}
if len(saveData.ToreData) != 240 {
t.Errorf("ToreData len = %d, want 240", len(saveData.ToreData))
}
if len(saveData.GardenData) != 68 {
t.Errorf("GardenData len = %d, want 68", len(saveData.GardenData))
}
if len(saveData.KQF) != 8 {
t.Errorf("KQF len = %d, want 8", len(saveData.KQF))
}
}
func TestPointerConstants(t *testing.T) {
// Verify the pointer constants are set correctly based on the game's save format
pointers := map[string]int{
"pointerGender": 0x81,
"pointerRP": 0x22D16,
"pointerHouseTier": 0x1FB6C,
"pointerHouseData": 0x1FE01,
"pointerBookshelfData": 0x22298,
"pointerGalleryData": 0x22320,
"pointerToreData": 0x1FCB4,
"pointerGardenData": 0x22C58,
"pointerWeaponType": 0x1F715,
"pointerWeaponID": 0x1F60A,
"pointerHRP": 0x1FDF6,
"pointerGRP": 0x1FDFC,
"pointerKQF": 0x23D20,
}
// Verify constants are properly defined (non-zero and in expected ranges)
if pointerGender != 0x81 {
t.Errorf("pointerGender = 0x%X, want 0x81", pointerGender)
}
if pointerRP != 0x22D16 {
t.Errorf("pointerRP = 0x%X, want 0x22D16", pointerRP)
}
if pointerKQF != 0x23D20 {
t.Errorf("pointerKQF = 0x%X, want 0x23D20", pointerKQF)
}
// Verify pointers are all unique
seen := make(map[int]string)
for name, ptr := range pointers {
if existingName, ok := seen[ptr]; ok {
t.Errorf("Duplicate pointer value 0x%X: %s and %s", ptr, name, existingName)
}
seen[ptr] = name
}
}
func TestCharacterSaveData_UpdateSaveDataWithStruct(t *testing.T) {
// Create a save with enough data to hold all pointers
// Maximum pointer is pointerKQF at 0x23D20 + 8 = 0x23D28
saveSize := 0x23D30 // A bit more than needed
saveData := &CharacterSaveData{
decompSave: make([]byte, saveSize),
RP: 1234,
KQF: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08},
}
saveData.updateSaveDataWithStruct()
// Check RP was written correctly (little endian)
rpValue := binary.LittleEndian.Uint16(saveData.decompSave[pointerRP : pointerRP+2])
if rpValue != 1234 {
t.Errorf("RP in decompSave = %d, want 1234", rpValue)
}
// Check KQF was written correctly
for i := 0; i < 8; i++ {
if saveData.decompSave[pointerKQF+i] != byte(i+1) {
t.Errorf("KQF[%d] = 0x%02X, want 0x%02X", i, saveData.decompSave[pointerKQF+i], i+1)
}
}
}
func TestCharacterSaveData_UpdateStructWithSaveData_Gender(t *testing.T) {
// Create minimal save data for gender test
saveSize := 0x23D30
saveData := &CharacterSaveData{
decompSave: make([]byte, saveSize),
IsNewCharacter: true, // New char doesn't read most fields
}
// Set gender to male (0)
saveData.decompSave[pointerGender] = 0
saveData.updateStructWithSaveData()
if saveData.Gender != false {
t.Error("Gender should be false (male) when byte is 0")
}
// Set gender to female (1)
saveData.decompSave[pointerGender] = 1
saveData.updateStructWithSaveData()
if saveData.Gender != true {
t.Error("Gender should be true (female) when byte is 1")
}
}
func TestCharacterSaveData_NotNewCharacter(t *testing.T) {
// Create save data for existing character
saveSize := 0x23D30
saveData := &CharacterSaveData{
decompSave: make([]byte, saveSize),
IsNewCharacter: false,
}
// Set some values in the save data
binary.LittleEndian.PutUint16(saveData.decompSave[pointerRP:], 5000)
binary.LittleEndian.PutUint16(saveData.decompSave[pointerHRP:], 500)
saveData.decompSave[pointerWeaponType] = 7
saveData.updateStructWithSaveData()
if saveData.RP != 5000 {
t.Errorf("RP = %d, want 5000", saveData.RP)
}
if saveData.HRP != 500 {
t.Errorf("HRP = %d, want 500", saveData.HRP)
}
if saveData.WeaponType != 7 {
t.Errorf("WeaponType = %d, want 7", saveData.WeaponType)
}
}
func TestCharacterSaveData_GR_MaxHRP(t *testing.T) {
// When HRP is 999, GR is calculated from GRP
saveSize := 0x23D30
saveData := &CharacterSaveData{
decompSave: make([]byte, saveSize),
IsNewCharacter: false,
}
// Set HRP to 999 (max HR)
binary.LittleEndian.PutUint16(saveData.decompSave[pointerHRP:], 999)
// Set GRP to 593400 (GR 100)
binary.LittleEndian.PutUint32(saveData.decompSave[pointerGRP:], 593400)
saveData.updateStructWithSaveData()
if saveData.HRP != 999 {
t.Errorf("HRP = %d, want 999", saveData.HRP)
}
// GR should be calculated via grpToGR
expectedGR := grpToGR(593400)
if saveData.GR != expectedGR {
t.Errorf("GR = %d, want %d", saveData.GR, expectedGR)
}
}
func TestCharacterSaveData_SliceExtraction(t *testing.T) {
// Test that slices are extracted at correct offsets
saveSize := 0x23D30
saveData := &CharacterSaveData{
decompSave: make([]byte, saveSize),
IsNewCharacter: false,
}
// Fill specific regions with identifiable patterns
for i := 0; i < 5; i++ {
saveData.decompSave[pointerHouseTier+i] = byte(0xAA)
}
for i := 0; i < 195; i++ {
saveData.decompSave[pointerHouseData+i] = byte(0xBB)
}
for i := 0; i < 8; i++ {
saveData.decompSave[pointerKQF+i] = byte(0xCC)
}
saveData.updateStructWithSaveData()
// Verify HouseTier extraction
if len(saveData.HouseTier) != 5 {
t.Fatalf("HouseTier len = %d, want 5", len(saveData.HouseTier))
}
for i, b := range saveData.HouseTier {
if b != 0xAA {
t.Errorf("HouseTier[%d] = 0x%02X, want 0xAA", i, b)
}
}
// Verify HouseData extraction
if len(saveData.HouseData) != 195 {
t.Fatalf("HouseData len = %d, want 195", len(saveData.HouseData))
}
for i, b := range saveData.HouseData {
if b != 0xBB {
t.Errorf("HouseData[%d] = 0x%02X, want 0xBB", i, b)
}
}
// Verify KQF extraction
if len(saveData.KQF) != 8 {
t.Fatalf("KQF len = %d, want 8", len(saveData.KQF))
}
for i, b := range saveData.KQF {
if b != 0xCC {
t.Errorf("KQF[%d] = 0x%02X, want 0xCC", i, b)
}
}
}