344 lines
7.8 KiB
Go
344 lines
7.8 KiB
Go
package eq2net
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"testing"
|
|
)
|
|
|
|
// Test database configuration
|
|
const (
|
|
testDBHost = "localhost"
|
|
testDBPort = "3306"
|
|
testDBUser = "root"
|
|
testDBPass = "Root12!"
|
|
testDBName = "eq2test"
|
|
)
|
|
|
|
// getTestDB creates a test database connection
|
|
func getTestDB(t *testing.T) *sql.DB {
|
|
// Connect without database first to create it if needed
|
|
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/", testDBUser, testDBPass, testDBHost, testDBPort)
|
|
db, err := sql.Open("mysql", dsn)
|
|
if err != nil {
|
|
t.Skipf("Cannot connect to MySQL: %v", err)
|
|
return nil
|
|
}
|
|
|
|
// Create test database if it doesn't exist
|
|
_, err = db.Exec("CREATE DATABASE IF NOT EXISTS " + testDBName)
|
|
if err != nil {
|
|
t.Skipf("Cannot create test database: %v", err)
|
|
return nil
|
|
}
|
|
db.Close()
|
|
|
|
// Connect to test database
|
|
dsn = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?parseTime=true",
|
|
testDBUser, testDBPass, testDBHost, testDBPort, testDBName)
|
|
|
|
db, err = ConnectDB(dsn)
|
|
if err != nil {
|
|
t.Skipf("Cannot connect to test database: %v", err)
|
|
return nil
|
|
}
|
|
|
|
// Create tables
|
|
if err := CreateOpcodeTable(db); err != nil {
|
|
t.Fatalf("Failed to create opcodes table: %v", err)
|
|
}
|
|
|
|
return db
|
|
}
|
|
|
|
// cleanupTestDB removes test data
|
|
func cleanupTestDB(db *sql.DB) {
|
|
if db != nil {
|
|
db.Exec("DELETE FROM opcodes WHERE version >= 9999") // Clean test versions
|
|
db.Close()
|
|
}
|
|
}
|
|
|
|
func TestOpcodeManager(t *testing.T) {
|
|
manager := NewOpcodeManager(1193)
|
|
|
|
// Test loading opcodes
|
|
opcodes := map[string]uint16{
|
|
"OP_LoginRequestMsg": 0x0001,
|
|
"OP_LoginReplyMsg": 0x0002,
|
|
"OP_WorldListMsg": 0x0003,
|
|
"OP_PlayCharacterRequestMsg": 0x0004,
|
|
}
|
|
|
|
err := manager.LoadOpcodes(opcodes)
|
|
if err != nil {
|
|
t.Fatalf("Failed to load opcodes: %v", err)
|
|
}
|
|
|
|
// Test EmuToEQ conversion
|
|
eq := manager.EmuToEQ(OP_LoginRequestMsg)
|
|
if eq != 0x0001 {
|
|
t.Errorf("Expected EQ opcode 0x0001, got 0x%04x", eq)
|
|
}
|
|
|
|
// Test EQToEmu conversion
|
|
emu := manager.EQToEmu(0x0002)
|
|
if emu != OP_LoginReplyMsg {
|
|
t.Errorf("Expected emu opcode %v, got %v", OP_LoginReplyMsg, emu)
|
|
}
|
|
|
|
// Test unknown opcode
|
|
eq = manager.EmuToEQ(OP_Unknown)
|
|
if eq != 0xFFFF {
|
|
t.Errorf("Expected 0xFFFF for unknown opcode, got 0x%04x", eq)
|
|
}
|
|
|
|
// Test name lookups
|
|
name := manager.EmuToName(OP_LoginRequestMsg)
|
|
if name != "OP_LoginRequestMsg" {
|
|
t.Errorf("Expected 'OP_LoginRequestMsg', got '%s'", name)
|
|
}
|
|
|
|
name = manager.EQToName(0x0003)
|
|
if name != "OP_WorldListMsg" {
|
|
t.Errorf("Expected 'OP_WorldListMsg', got '%s'", name)
|
|
}
|
|
}
|
|
|
|
func TestOpcodeService(t *testing.T) {
|
|
db := getTestDB(t)
|
|
if db == nil {
|
|
return // Test skipped
|
|
}
|
|
defer cleanupTestDB(db)
|
|
|
|
service := NewOpcodeService(db)
|
|
|
|
// Insert test opcodes
|
|
testVersion := uint16(9999)
|
|
testOpcodes := map[string]uint16{
|
|
"OP_LoginRequestMsg": 0x1001,
|
|
"OP_LoginReplyMsg": 0x1002,
|
|
"OP_WorldListMsg": 0x1003,
|
|
"OP_PlayCharacterRequestMsg": 0x1004,
|
|
"OP_DeleteCharacterRequestMsg": 0x1005,
|
|
}
|
|
|
|
// Import opcodes
|
|
err := service.ImportOpcodes(testVersion, testOpcodes)
|
|
if err != nil {
|
|
t.Fatalf("Failed to import opcodes: %v", err)
|
|
}
|
|
|
|
// Get manager for version
|
|
manager, err := service.GetManager(testVersion)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get manager: %v", err)
|
|
}
|
|
|
|
if manager.GetVersion() != testVersion {
|
|
t.Errorf("Expected version %d, got %d", testVersion, manager.GetVersion())
|
|
}
|
|
|
|
// Test opcode conversions
|
|
eq := manager.EmuToEQ(OP_LoginRequestMsg)
|
|
if eq != 0x1001 {
|
|
t.Errorf("Expected EQ opcode 0x1001, got 0x%04x", eq)
|
|
}
|
|
|
|
emu := manager.EQToEmu(0x1002)
|
|
if emu != OP_LoginReplyMsg {
|
|
t.Errorf("Expected emu opcode %v, got %v", OP_LoginReplyMsg, emu)
|
|
}
|
|
|
|
// Test single opcode save
|
|
err = service.SaveOpcode(testVersion, "OP_ServerListRequestMsg", 0x1006)
|
|
if err != nil {
|
|
t.Fatalf("Failed to save opcode: %v", err)
|
|
}
|
|
|
|
// Force reload and verify
|
|
service.mu.Lock()
|
|
delete(service.managers, testVersion)
|
|
service.mu.Unlock()
|
|
|
|
manager, err = service.GetManager(testVersion)
|
|
if err != nil {
|
|
t.Fatalf("Failed to reload manager: %v", err)
|
|
}
|
|
|
|
eq = manager.EmuToEQ(OP_ServerListRequestMsg)
|
|
if eq != 0x1006 {
|
|
t.Errorf("Expected EQ opcode 0x1006 after save, got 0x%04x", eq)
|
|
}
|
|
}
|
|
|
|
func TestGetSupportedVersions(t *testing.T) {
|
|
db := getTestDB(t)
|
|
if db == nil {
|
|
return // Test skipped
|
|
}
|
|
defer cleanupTestDB(db)
|
|
|
|
service := NewOpcodeService(db)
|
|
|
|
// Add opcodes for multiple versions
|
|
versions := []uint16{9999, 9998, 9997}
|
|
for _, v := range versions {
|
|
opcodes := map[string]uint16{
|
|
"OP_LoginRequestMsg": uint16(v),
|
|
}
|
|
if err := service.ImportOpcodes(v, opcodes); err != nil {
|
|
t.Fatalf("Failed to import opcodes for version %d: %v", v, err)
|
|
}
|
|
}
|
|
|
|
// Get supported versions
|
|
supported, err := service.GetSupportedVersions()
|
|
if err != nil {
|
|
t.Fatalf("Failed to get supported versions: %v", err)
|
|
}
|
|
|
|
// Check that our test versions are included
|
|
found := make(map[uint16]bool)
|
|
for _, v := range supported {
|
|
found[v] = true
|
|
}
|
|
|
|
for _, v := range versions {
|
|
if !found[v] {
|
|
t.Errorf("Version %d not found in supported versions", v)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRefreshAll(t *testing.T) {
|
|
db := getTestDB(t)
|
|
if db == nil {
|
|
return // Test skipped
|
|
}
|
|
defer cleanupTestDB(db)
|
|
|
|
service := NewOpcodeService(db)
|
|
|
|
// Add opcodes for multiple versions
|
|
versions := []uint16{9999, 9998}
|
|
for _, v := range versions {
|
|
opcodes := map[string]uint16{
|
|
"OP_LoginRequestMsg": uint16(v),
|
|
"OP_LoginReplyMsg": uint16(v + 1000),
|
|
}
|
|
if err := service.ImportOpcodes(v, opcodes); err != nil {
|
|
t.Fatalf("Failed to import opcodes for version %d: %v", v, err)
|
|
}
|
|
}
|
|
|
|
// Refresh all
|
|
err := service.RefreshAll()
|
|
if err != nil {
|
|
t.Fatalf("Failed to refresh all: %v", err)
|
|
}
|
|
|
|
// Verify all versions are loaded
|
|
for _, v := range versions {
|
|
manager, err := service.GetManager(v)
|
|
if err != nil {
|
|
t.Errorf("Failed to get manager for version %d after refresh: %v", v, err)
|
|
}
|
|
|
|
eq := manager.EmuToEQ(OP_LoginRequestMsg)
|
|
if eq != v {
|
|
t.Errorf("Version %d: expected opcode %d, got %d", v, v, eq)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGetOpcodeVersion(t *testing.T) {
|
|
tests := []struct {
|
|
clientVersion uint16
|
|
expectedOpcode uint16
|
|
}{
|
|
{800, 1},
|
|
{899, 1},
|
|
{900, 900},
|
|
{1099, 900},
|
|
{1100, 1100},
|
|
{1192, 1100},
|
|
{1193, 1193},
|
|
{1199, 1193},
|
|
{1200, 1193},
|
|
{1300, 1300},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
result := GetOpcodeVersion(tt.clientVersion)
|
|
if result != tt.expectedOpcode {
|
|
t.Errorf("GetOpcodeVersion(%d) = %d, want %d",
|
|
tt.clientVersion, result, tt.expectedOpcode)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestOpcodeNames(t *testing.T) {
|
|
// Verify all defined opcodes have names
|
|
knownOpcodes := []EmuOpcode{
|
|
OP_Unknown,
|
|
OP_LoginRequestMsg,
|
|
OP_LoginByNumRequestMsg,
|
|
OP_WSLoginRequestMsg,
|
|
OP_ESLoginRequestMsg,
|
|
OP_LoginReplyMsg,
|
|
OP_WSStatusReplyMsg,
|
|
OP_WorldListMsg,
|
|
OP_WorldStatusMsg,
|
|
OP_DeleteCharacterRequestMsg,
|
|
OP_DeleteCharacterReplyMsg,
|
|
OP_CreateCharacterRequestMsg,
|
|
OP_CreateCharacterReplyMsg,
|
|
OP_PlayCharacterRequestMsg,
|
|
OP_PlayCharacterReplyMsg,
|
|
OP_ServerListRequestMsg,
|
|
OP_ServerListReplyMsg,
|
|
OP_CharacterListRequestMsg,
|
|
OP_CharacterListReplyMsg,
|
|
}
|
|
|
|
for _, opcode := range knownOpcodes {
|
|
name, exists := OpcodeNames[opcode]
|
|
if !exists {
|
|
t.Errorf("Opcode %v has no name defined", opcode)
|
|
}
|
|
if name == "" {
|
|
t.Errorf("Opcode %v has empty name", opcode)
|
|
}
|
|
}
|
|
}
|
|
|
|
// BenchmarkOpcodeConversion benchmarks opcode conversion performance
|
|
func BenchmarkOpcodeConversion(b *testing.B) {
|
|
manager := NewOpcodeManager(1193)
|
|
opcodes := make(map[string]uint16)
|
|
|
|
// Add many opcodes
|
|
for i := uint16(1); i <= 100; i++ {
|
|
name := fmt.Sprintf("OP_Test%d", i)
|
|
opcodes[name] = i
|
|
OpcodeNames[EmuOpcode(i)] = name
|
|
}
|
|
|
|
manager.LoadOpcodes(opcodes)
|
|
|
|
b.ResetTimer()
|
|
|
|
b.Run("EmuToEQ", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
_ = manager.EmuToEQ(EmuOpcode(i%100 + 1))
|
|
}
|
|
})
|
|
|
|
b.Run("EQToEmu", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
_ = manager.EQToEmu(uint16(i%100 + 1))
|
|
}
|
|
})
|
|
} |