mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-22 15:43:49 +01:00
test: increase total coverage from 40.7% to 46.1%
Add comprehensive mhfpacket Parse/Build/Opcode tests covering nearly all packet types (78.3% -> 95.7%). Add channelserver tests for BackportQuest and GuildIcon Scan/Value round-trips.
This commit is contained in:
249
server/channelserver/handlers_guild_icon_test.go
Normal file
249
server/channelserver/handlers_guild_icon_test.go
Normal file
@@ -0,0 +1,249 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGuildIconScan_Bytes(t *testing.T) {
|
||||
jsonData := []byte(`{"Parts":[{"Index":1,"ID":100,"Page":2,"Size":3,"Rotation":4,"Red":255,"Green":128,"Blue":0,"PosX":50,"PosY":60}]}`)
|
||||
|
||||
gi := &GuildIcon{}
|
||||
err := gi.Scan(jsonData)
|
||||
if err != nil {
|
||||
t.Fatalf("Scan([]byte) error = %v", err)
|
||||
}
|
||||
|
||||
if len(gi.Parts) != 1 {
|
||||
t.Fatalf("Parts length = %d, want 1", len(gi.Parts))
|
||||
}
|
||||
|
||||
part := gi.Parts[0]
|
||||
if part.Index != 1 {
|
||||
t.Errorf("Index = %d, want 1", part.Index)
|
||||
}
|
||||
if part.ID != 100 {
|
||||
t.Errorf("ID = %d, want 100", part.ID)
|
||||
}
|
||||
if part.Page != 2 {
|
||||
t.Errorf("Page = %d, want 2", part.Page)
|
||||
}
|
||||
if part.Size != 3 {
|
||||
t.Errorf("Size = %d, want 3", part.Size)
|
||||
}
|
||||
if part.Rotation != 4 {
|
||||
t.Errorf("Rotation = %d, want 4", part.Rotation)
|
||||
}
|
||||
if part.Red != 255 {
|
||||
t.Errorf("Red = %d, want 255", part.Red)
|
||||
}
|
||||
if part.Green != 128 {
|
||||
t.Errorf("Green = %d, want 128", part.Green)
|
||||
}
|
||||
if part.Blue != 0 {
|
||||
t.Errorf("Blue = %d, want 0", part.Blue)
|
||||
}
|
||||
if part.PosX != 50 {
|
||||
t.Errorf("PosX = %d, want 50", part.PosX)
|
||||
}
|
||||
if part.PosY != 60 {
|
||||
t.Errorf("PosY = %d, want 60", part.PosY)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGuildIconScan_String(t *testing.T) {
|
||||
jsonStr := `{"Parts":[{"Index":5,"ID":200,"Page":1,"Size":2,"Rotation":0,"Red":100,"Green":50,"Blue":25,"PosX":300,"PosY":400}]}`
|
||||
|
||||
gi := &GuildIcon{}
|
||||
err := gi.Scan(jsonStr)
|
||||
if err != nil {
|
||||
t.Fatalf("Scan(string) error = %v", err)
|
||||
}
|
||||
|
||||
if len(gi.Parts) != 1 {
|
||||
t.Fatalf("Parts length = %d, want 1", len(gi.Parts))
|
||||
}
|
||||
if gi.Parts[0].ID != 200 {
|
||||
t.Errorf("ID = %d, want 200", gi.Parts[0].ID)
|
||||
}
|
||||
if gi.Parts[0].PosX != 300 {
|
||||
t.Errorf("PosX = %d, want 300", gi.Parts[0].PosX)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGuildIconScan_MultipleParts(t *testing.T) {
|
||||
jsonData := []byte(`{"Parts":[{"Index":0,"ID":1,"Page":0,"Size":0,"Rotation":0,"Red":0,"Green":0,"Blue":0,"PosX":0,"PosY":0},{"Index":1,"ID":2,"Page":0,"Size":0,"Rotation":0,"Red":0,"Green":0,"Blue":0,"PosX":0,"PosY":0},{"Index":2,"ID":3,"Page":0,"Size":0,"Rotation":0,"Red":0,"Green":0,"Blue":0,"PosX":0,"PosY":0}]}`)
|
||||
|
||||
gi := &GuildIcon{}
|
||||
err := gi.Scan(jsonData)
|
||||
if err != nil {
|
||||
t.Fatalf("Scan() error = %v", err)
|
||||
}
|
||||
|
||||
if len(gi.Parts) != 3 {
|
||||
t.Fatalf("Parts length = %d, want 3", len(gi.Parts))
|
||||
}
|
||||
for i, part := range gi.Parts {
|
||||
if part.Index != uint16(i) {
|
||||
t.Errorf("Parts[%d].Index = %d, want %d", i, part.Index, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGuildIconScan_EmptyParts(t *testing.T) {
|
||||
gi := &GuildIcon{}
|
||||
err := gi.Scan([]byte(`{"Parts":[]}`))
|
||||
if err != nil {
|
||||
t.Fatalf("Scan() error = %v", err)
|
||||
}
|
||||
if len(gi.Parts) != 0 {
|
||||
t.Errorf("Parts length = %d, want 0", len(gi.Parts))
|
||||
}
|
||||
}
|
||||
|
||||
func TestGuildIconScan_InvalidJSON(t *testing.T) {
|
||||
gi := &GuildIcon{}
|
||||
err := gi.Scan([]byte(`{invalid`))
|
||||
if err == nil {
|
||||
t.Error("Scan() with invalid JSON should return error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGuildIconScan_InvalidJSONString(t *testing.T) {
|
||||
gi := &GuildIcon{}
|
||||
err := gi.Scan("{invalid")
|
||||
if err == nil {
|
||||
t.Error("Scan() with invalid JSON string should return error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGuildIconScan_UnsupportedType(t *testing.T) {
|
||||
gi := &GuildIcon{}
|
||||
// Passing an unsupported type should not error (just no-op)
|
||||
err := gi.Scan(12345)
|
||||
if err != nil {
|
||||
t.Errorf("Scan(int) unexpected error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGuildIconValue(t *testing.T) {
|
||||
gi := &GuildIcon{
|
||||
Parts: []GuildIconPart{
|
||||
{Index: 1, ID: 100, Page: 2, Size: 3, Rotation: 4, Red: 255, Green: 128, Blue: 0, PosX: 50, PosY: 60},
|
||||
},
|
||||
}
|
||||
|
||||
val, err := gi.Value()
|
||||
if err != nil {
|
||||
t.Fatalf("Value() error = %v", err)
|
||||
}
|
||||
|
||||
jsonBytes, ok := val.([]byte)
|
||||
if !ok {
|
||||
t.Fatalf("Value() returned %T, want []byte", val)
|
||||
}
|
||||
|
||||
// Verify round-trip
|
||||
gi2 := &GuildIcon{}
|
||||
err = json.Unmarshal(jsonBytes, gi2)
|
||||
if err != nil {
|
||||
t.Fatalf("json.Unmarshal error = %v", err)
|
||||
}
|
||||
|
||||
if len(gi2.Parts) != 1 {
|
||||
t.Fatalf("round-trip Parts length = %d, want 1", len(gi2.Parts))
|
||||
}
|
||||
if gi2.Parts[0].ID != 100 {
|
||||
t.Errorf("round-trip ID = %d, want 100", gi2.Parts[0].ID)
|
||||
}
|
||||
if gi2.Parts[0].Red != 255 {
|
||||
t.Errorf("round-trip Red = %d, want 255", gi2.Parts[0].Red)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGuildIconValue_Empty(t *testing.T) {
|
||||
gi := &GuildIcon{}
|
||||
val, err := gi.Value()
|
||||
if err != nil {
|
||||
t.Fatalf("Value() error = %v", err)
|
||||
}
|
||||
|
||||
if val == nil {
|
||||
t.Error("Value() should not return nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGuildIconScanValueRoundTrip(t *testing.T) {
|
||||
original := &GuildIcon{
|
||||
Parts: []GuildIconPart{
|
||||
{Index: 0, ID: 10, Page: 1, Size: 2, Rotation: 45, Red: 200, Green: 150, Blue: 100, PosX: 500, PosY: 600},
|
||||
{Index: 1, ID: 20, Page: 3, Size: 4, Rotation: 90, Red: 50, Green: 75, Blue: 255, PosX: 100, PosY: 200},
|
||||
},
|
||||
}
|
||||
|
||||
// Value -> Scan round trip
|
||||
val, err := original.Value()
|
||||
if err != nil {
|
||||
t.Fatalf("Value() error = %v", err)
|
||||
}
|
||||
|
||||
restored := &GuildIcon{}
|
||||
err = restored.Scan(val)
|
||||
if err != nil {
|
||||
t.Fatalf("Scan() error = %v", err)
|
||||
}
|
||||
|
||||
if len(restored.Parts) != len(original.Parts) {
|
||||
t.Fatalf("Parts length = %d, want %d", len(restored.Parts), len(original.Parts))
|
||||
}
|
||||
|
||||
for i := range original.Parts {
|
||||
if restored.Parts[i] != original.Parts[i] {
|
||||
t.Errorf("Parts[%d] mismatch: got %+v, want %+v", i, restored.Parts[i], original.Parts[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFestivalColourCodes(t *testing.T) {
|
||||
tests := []struct {
|
||||
colour FestivalColour
|
||||
code uint8
|
||||
}{
|
||||
{FestivalColourBlue, 0x00},
|
||||
{FestivalColourRed, 0x01},
|
||||
{FestivalColourNone, 0xFF},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(string(tt.colour), func(t *testing.T) {
|
||||
code, ok := FestivalColourCodes[tt.colour]
|
||||
if !ok {
|
||||
t.Fatalf("FestivalColourCodes missing key %s", tt.colour)
|
||||
}
|
||||
if code != tt.code {
|
||||
t.Errorf("FestivalColourCodes[%s] = %d, want %d", tt.colour, code, tt.code)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFestivalColourConstants(t *testing.T) {
|
||||
if FestivalColourNone != "none" {
|
||||
t.Errorf("FestivalColourNone = %s, want none", FestivalColourNone)
|
||||
}
|
||||
if FestivalColourRed != "red" {
|
||||
t.Errorf("FestivalColourRed = %s, want red", FestivalColourRed)
|
||||
}
|
||||
if FestivalColourBlue != "blue" {
|
||||
t.Errorf("FestivalColourBlue = %s, want blue", FestivalColourBlue)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGuildApplicationTypeConstants(t *testing.T) {
|
||||
if GuildApplicationTypeApplied != "applied" {
|
||||
t.Errorf("GuildApplicationTypeApplied = %s, want applied", GuildApplicationTypeApplied)
|
||||
}
|
||||
if GuildApplicationTypeInvited != "invited" {
|
||||
t.Errorf("GuildApplicationTypeInvited = %s, want invited", GuildApplicationTypeInvited)
|
||||
}
|
||||
}
|
||||
128
server/channelserver/handlers_quest_backport_test.go
Normal file
128
server/channelserver/handlers_quest_backport_test.go
Normal file
@@ -0,0 +1,128 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"testing"
|
||||
|
||||
_config "erupe-ce/config"
|
||||
)
|
||||
|
||||
func TestBackportQuest_Basic(t *testing.T) {
|
||||
// Set up config for the test
|
||||
oldConfig := _config.ErupeConfig
|
||||
defer func() { _config.ErupeConfig = oldConfig }()
|
||||
|
||||
_config.ErupeConfig = &_config.Config{}
|
||||
_config.ErupeConfig.RealClientMode = _config.ZZ
|
||||
|
||||
// Create a quest data buffer large enough for BackportQuest to work with.
|
||||
// The function reads a uint32 from data[0:4] as offset, then works at offset+96.
|
||||
// We need at least offset + 96 + 108 + 6*8 bytes.
|
||||
// Set offset (wp base) = 0, so wp starts at 96, rp at 100.
|
||||
data := make([]byte, 512)
|
||||
binary.LittleEndian.PutUint32(data[0:4], 0) // offset = 0
|
||||
|
||||
// Fill some data at the rp positions so we can verify copies
|
||||
for i := 100; i < 400; i++ {
|
||||
data[i] = byte(i & 0xFF)
|
||||
}
|
||||
|
||||
result := BackportQuest(data)
|
||||
if result == nil {
|
||||
t.Fatal("BackportQuest returned nil")
|
||||
}
|
||||
if len(result) != len(data) {
|
||||
t.Errorf("BackportQuest changed data length: got %d, want %d", len(result), len(data))
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackportQuest_S6Mode(t *testing.T) {
|
||||
oldConfig := _config.ErupeConfig
|
||||
defer func() { _config.ErupeConfig = oldConfig }()
|
||||
|
||||
_config.ErupeConfig = &_config.Config{}
|
||||
_config.ErupeConfig.RealClientMode = _config.S6
|
||||
|
||||
data := make([]byte, 512)
|
||||
binary.LittleEndian.PutUint32(data[0:4], 0)
|
||||
|
||||
for i := 0; i < len(data); i++ {
|
||||
data[i+4] = byte(i % 256)
|
||||
if i+4 >= len(data)-1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Set some values at data[8:12] so we can check they get copied to data[16:20]
|
||||
binary.LittleEndian.PutUint32(data[8:12], 0xDEADBEEF)
|
||||
|
||||
result := BackportQuest(data)
|
||||
if result == nil {
|
||||
t.Fatal("BackportQuest returned nil")
|
||||
}
|
||||
|
||||
// In S6 mode, data[16:20] should be copied from data[8:12]
|
||||
got := binary.LittleEndian.Uint32(result[16:20])
|
||||
if got != 0xDEADBEEF {
|
||||
t.Errorf("S6 mode: data[16:20] = 0x%X, want 0xDEADBEEF", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackportQuest_G91Mode_PatternReplacement(t *testing.T) {
|
||||
oldConfig := _config.ErupeConfig
|
||||
defer func() { _config.ErupeConfig = oldConfig }()
|
||||
|
||||
_config.ErupeConfig = &_config.Config{}
|
||||
_config.ErupeConfig.RealClientMode = _config.G91
|
||||
|
||||
data := make([]byte, 512)
|
||||
binary.LittleEndian.PutUint32(data[0:4], 0)
|
||||
|
||||
// Insert an armor sphere pattern at a known location
|
||||
// Pattern: 0x0A, 0x00, 0x01, 0x33 -> should replace bytes at +2 with 0xD7, 0x00
|
||||
offset := 300
|
||||
data[offset] = 0x0A
|
||||
data[offset+1] = 0x00
|
||||
data[offset+2] = 0x01
|
||||
data[offset+3] = 0x33
|
||||
|
||||
result := BackportQuest(data)
|
||||
|
||||
// After BackportQuest, the pattern's last 2 bytes should be replaced
|
||||
if result[offset+2] != 0xD7 || result[offset+3] != 0x00 {
|
||||
t.Errorf("G91 pattern replacement failed: got [0x%X, 0x%X], want [0xD7, 0x00]",
|
||||
result[offset+2], result[offset+3])
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackportQuest_F5Mode(t *testing.T) {
|
||||
oldConfig := _config.ErupeConfig
|
||||
defer func() { _config.ErupeConfig = oldConfig }()
|
||||
|
||||
_config.ErupeConfig = &_config.Config{}
|
||||
_config.ErupeConfig.RealClientMode = _config.F5
|
||||
|
||||
data := make([]byte, 512)
|
||||
binary.LittleEndian.PutUint32(data[0:4], 0)
|
||||
|
||||
result := BackportQuest(data)
|
||||
if result == nil {
|
||||
t.Fatal("BackportQuest returned nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackportQuest_G101Mode(t *testing.T) {
|
||||
oldConfig := _config.ErupeConfig
|
||||
defer func() { _config.ErupeConfig = oldConfig }()
|
||||
|
||||
_config.ErupeConfig = &_config.Config{}
|
||||
_config.ErupeConfig.RealClientMode = _config.G101
|
||||
|
||||
data := make([]byte, 512)
|
||||
binary.LittleEndian.PutUint32(data[0:4], 0)
|
||||
|
||||
result := BackportQuest(data)
|
||||
if result == nil {
|
||||
t.Fatal("BackportQuest returned nil")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user