1551 lines
39 KiB
Go
1551 lines
39 KiB
Go
package appearances
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
"testing"
|
|
)
|
|
|
|
func TestNewAppearance(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
id int32
|
|
appearanceName string
|
|
minClientVersion int16
|
|
wantNil bool
|
|
}{
|
|
{
|
|
name: "valid appearance",
|
|
id: 100,
|
|
appearanceName: "Test Appearance",
|
|
minClientVersion: 1096,
|
|
wantNil: false,
|
|
},
|
|
{
|
|
name: "empty name returns nil",
|
|
id: 200,
|
|
appearanceName: "",
|
|
minClientVersion: 1096,
|
|
wantNil: true,
|
|
},
|
|
{
|
|
name: "negative id allowed",
|
|
id: -50,
|
|
appearanceName: "Negative ID",
|
|
minClientVersion: 0,
|
|
wantNil: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
app := NewAppearance(tt.id, tt.appearanceName, tt.minClientVersion)
|
|
|
|
if tt.wantNil {
|
|
if app != nil {
|
|
t.Errorf("expected nil appearance, got %v", app)
|
|
}
|
|
return
|
|
}
|
|
|
|
if app == nil {
|
|
t.Fatal("expected non-nil appearance, got nil")
|
|
}
|
|
|
|
if app.GetID() != tt.id {
|
|
t.Errorf("ID = %v, want %v", app.GetID(), tt.id)
|
|
}
|
|
|
|
if app.GetName() != tt.appearanceName {
|
|
t.Errorf("Name = %v, want %v", app.GetName(), tt.appearanceName)
|
|
}
|
|
|
|
if app.GetMinClientVersion() != tt.minClientVersion {
|
|
t.Errorf("MinClientVersion = %v, want %v", app.GetMinClientVersion(), tt.minClientVersion)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAppearanceGetters(t *testing.T) {
|
|
app := NewAppearance(123, "Test Appearance", 1096)
|
|
|
|
if id := app.GetID(); id != 123 {
|
|
t.Errorf("GetID() = %v, want 123", id)
|
|
}
|
|
|
|
if name := app.GetName(); name != "Test Appearance" {
|
|
t.Errorf("GetName() = %v, want Test Appearance", name)
|
|
}
|
|
|
|
if nameStr := app.GetNameString(); nameStr != "Test Appearance" {
|
|
t.Errorf("GetNameString() = %v, want Test Appearance", nameStr)
|
|
}
|
|
|
|
if minVer := app.GetMinClientVersion(); minVer != 1096 {
|
|
t.Errorf("GetMinClientVersion() = %v, want 1096", minVer)
|
|
}
|
|
}
|
|
|
|
func TestAppearanceSetters(t *testing.T) {
|
|
app := NewAppearance(100, "Original", 1000)
|
|
|
|
app.SetName("Modified Name")
|
|
if app.GetName() != "Modified Name" {
|
|
t.Errorf("SetName failed: got %v, want Modified Name", app.GetName())
|
|
}
|
|
|
|
app.SetMinClientVersion(2000)
|
|
if app.GetMinClientVersion() != 2000 {
|
|
t.Errorf("SetMinClientVersion failed: got %v, want 2000", app.GetMinClientVersion())
|
|
}
|
|
}
|
|
|
|
func TestIsCompatibleWithClient(t *testing.T) {
|
|
app := NewAppearance(100, "Test", 1096)
|
|
|
|
tests := []struct {
|
|
clientVersion int16
|
|
want bool
|
|
}{
|
|
{1095, false}, // Below minimum
|
|
{1096, true}, // Exact minimum
|
|
{1097, true}, // Above minimum
|
|
{2000, true}, // Well above minimum
|
|
{0, false}, // Zero version
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run("", func(t *testing.T) {
|
|
if got := app.IsCompatibleWithClient(tt.clientVersion); got != tt.want {
|
|
t.Errorf("IsCompatibleWithClient(%v) = %v, want %v", tt.clientVersion, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAppearanceClone(t *testing.T) {
|
|
original := NewAppearance(500, "Original Appearance", 1200)
|
|
clone := original.Clone()
|
|
|
|
if clone == nil {
|
|
t.Fatal("Clone returned nil")
|
|
}
|
|
|
|
if clone == original {
|
|
t.Error("Clone returned same pointer as original")
|
|
}
|
|
|
|
if clone.GetID() != original.GetID() {
|
|
t.Errorf("Clone ID = %v, want %v", clone.GetID(), original.GetID())
|
|
}
|
|
|
|
if clone.GetName() != original.GetName() {
|
|
t.Errorf("Clone Name = %v, want %v", clone.GetName(), original.GetName())
|
|
}
|
|
|
|
if clone.GetMinClientVersion() != original.GetMinClientVersion() {
|
|
t.Errorf("Clone MinClientVersion = %v, want %v", clone.GetMinClientVersion(), original.GetMinClientVersion())
|
|
}
|
|
|
|
// Verify modification independence
|
|
clone.SetName("Modified Clone")
|
|
if original.GetName() == "Modified Clone" {
|
|
t.Error("Modifying clone affected original")
|
|
}
|
|
}
|
|
|
|
func TestNewAppearances(t *testing.T) {
|
|
apps := NewAppearances()
|
|
|
|
if apps == nil {
|
|
t.Fatal("NewAppearances returned nil")
|
|
}
|
|
|
|
if count := apps.GetAppearanceCount(); count != 0 {
|
|
t.Errorf("New appearances collection should be empty, got count %v", count)
|
|
}
|
|
}
|
|
|
|
func TestAppearancesInsertAndFind(t *testing.T) {
|
|
apps := NewAppearances()
|
|
|
|
// Test nil insertion
|
|
err := apps.InsertAppearance(nil)
|
|
if err == nil {
|
|
t.Error("InsertAppearance(nil) should return error")
|
|
}
|
|
|
|
// Insert valid appearances
|
|
app1 := NewAppearance(100, "Appearance 1", 1000)
|
|
app2 := NewAppearance(200, "Appearance 2", 1100)
|
|
|
|
if err := apps.InsertAppearance(app1); err != nil {
|
|
t.Errorf("InsertAppearance failed: %v", err)
|
|
}
|
|
|
|
if err := apps.InsertAppearance(app2); err != nil {
|
|
t.Errorf("InsertAppearance failed: %v", err)
|
|
}
|
|
|
|
// Test finding by ID
|
|
found := apps.FindAppearanceByID(100)
|
|
if found == nil {
|
|
t.Error("FindAppearanceByID(100) returned nil")
|
|
} else if found.GetName() != "Appearance 1" {
|
|
t.Errorf("FindAppearanceByID(100) returned wrong appearance: %v", found.GetName())
|
|
}
|
|
|
|
// Test finding non-existent ID
|
|
notFound := apps.FindAppearanceByID(999)
|
|
if notFound != nil {
|
|
t.Errorf("FindAppearanceByID(999) should return nil, got %v", notFound)
|
|
}
|
|
}
|
|
|
|
func TestAppearancesHasAppearance(t *testing.T) {
|
|
apps := NewAppearances()
|
|
app := NewAppearance(300, "Test", 1000)
|
|
apps.InsertAppearance(app)
|
|
|
|
if !apps.HasAppearance(300) {
|
|
t.Error("HasAppearance(300) should return true")
|
|
}
|
|
|
|
if apps.HasAppearance(999) {
|
|
t.Error("HasAppearance(999) should return false")
|
|
}
|
|
}
|
|
|
|
func TestAppearancesGetAllAndCount(t *testing.T) {
|
|
apps := NewAppearances()
|
|
|
|
// Add multiple appearances
|
|
for i := int32(1); i <= 5; i++ {
|
|
app := NewAppearance(i*100, "Appearance", 1000)
|
|
apps.InsertAppearance(app)
|
|
}
|
|
|
|
if count := apps.GetAppearanceCount(); count != 5 {
|
|
t.Errorf("GetAppearanceCount() = %v, want 5", count)
|
|
}
|
|
|
|
all := apps.GetAllAppearances()
|
|
if len(all) != 5 {
|
|
t.Errorf("GetAllAppearances() returned %v items, want 5", len(all))
|
|
}
|
|
|
|
// Verify it's a copy by modifying returned map
|
|
delete(all, 100)
|
|
if apps.GetAppearanceCount() != 5 {
|
|
t.Error("Modifying returned map affected internal state")
|
|
}
|
|
}
|
|
|
|
func TestAppearancesGetIDs(t *testing.T) {
|
|
apps := NewAppearances()
|
|
expectedIDs := []int32{100, 200, 300}
|
|
|
|
for _, id := range expectedIDs {
|
|
app := NewAppearance(id, "Test", 1000)
|
|
apps.InsertAppearance(app)
|
|
}
|
|
|
|
ids := apps.GetAppearanceIDs()
|
|
if len(ids) != len(expectedIDs) {
|
|
t.Errorf("GetAppearanceIDs() returned %v IDs, want %v", len(ids), len(expectedIDs))
|
|
}
|
|
|
|
// Check all expected IDs are present
|
|
idMap := make(map[int32]bool)
|
|
for _, id := range ids {
|
|
idMap[id] = true
|
|
}
|
|
|
|
for _, expected := range expectedIDs {
|
|
if !idMap[expected] {
|
|
t.Errorf("Expected ID %v not found in returned IDs", expected)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAppearancesFindByName(t *testing.T) {
|
|
apps := NewAppearances()
|
|
|
|
apps.InsertAppearance(NewAppearance(1, "Human Male", 1000))
|
|
apps.InsertAppearance(NewAppearance(2, "Human Female", 1000))
|
|
apps.InsertAppearance(NewAppearance(3, "Elf Male", 1000))
|
|
apps.InsertAppearance(NewAppearance(4, "Dwarf Female", 1000))
|
|
|
|
// Test partial matching
|
|
humanApps := apps.FindAppearancesByName("Human")
|
|
if len(humanApps) != 2 {
|
|
t.Errorf("FindAppearancesByName('Human') returned %v results, want 2", len(humanApps))
|
|
}
|
|
|
|
// Test case sensitivity
|
|
maleApps := apps.FindAppearancesByName("Male")
|
|
if len(maleApps) != 2 {
|
|
t.Errorf("FindAppearancesByName('Male') returned %v results, want 2", len(maleApps))
|
|
}
|
|
|
|
// Test empty substring
|
|
allApps := apps.FindAppearancesByName("")
|
|
if len(allApps) != 4 {
|
|
t.Errorf("FindAppearancesByName('') returned %v results, want 4", len(allApps))
|
|
}
|
|
|
|
// Test no matches
|
|
noMatches := apps.FindAppearancesByName("Orc")
|
|
if len(noMatches) != 0 {
|
|
t.Errorf("FindAppearancesByName('Orc') returned %v results, want 0", len(noMatches))
|
|
}
|
|
}
|
|
|
|
func TestAppearancesFindByMinClient(t *testing.T) {
|
|
apps := NewAppearances()
|
|
|
|
apps.InsertAppearance(NewAppearance(1, "Old", 1000))
|
|
apps.InsertAppearance(NewAppearance(2, "Medium1", 1096))
|
|
apps.InsertAppearance(NewAppearance(3, "Medium2", 1096))
|
|
apps.InsertAppearance(NewAppearance(4, "New", 1200))
|
|
|
|
results := apps.FindAppearancesByMinClient(1096)
|
|
if len(results) != 2 {
|
|
t.Errorf("FindAppearancesByMinClient(1096) returned %v results, want 2", len(results))
|
|
}
|
|
|
|
results = apps.FindAppearancesByMinClient(999)
|
|
if len(results) != 0 {
|
|
t.Errorf("FindAppearancesByMinClient(999) returned %v results, want 0", len(results))
|
|
}
|
|
}
|
|
|
|
func TestAppearancesGetCompatible(t *testing.T) {
|
|
apps := NewAppearances()
|
|
|
|
apps.InsertAppearance(NewAppearance(1, "Old", 1000))
|
|
apps.InsertAppearance(NewAppearance(2, "Medium", 1096))
|
|
apps.InsertAppearance(NewAppearance(3, "New", 1200))
|
|
apps.InsertAppearance(NewAppearance(4, "Newer", 1300))
|
|
|
|
// Client version 1100 should get Old and Medium
|
|
compatible := apps.GetCompatibleAppearances(1100)
|
|
if len(compatible) != 2 {
|
|
t.Errorf("GetCompatibleAppearances(1100) returned %v results, want 2", len(compatible))
|
|
}
|
|
|
|
// Client version 1500 should get all
|
|
compatible = apps.GetCompatibleAppearances(1500)
|
|
if len(compatible) != 4 {
|
|
t.Errorf("GetCompatibleAppearances(1500) returned %v results, want 4", len(compatible))
|
|
}
|
|
|
|
// Client version 500 should get none
|
|
compatible = apps.GetCompatibleAppearances(500)
|
|
if len(compatible) != 0 {
|
|
t.Errorf("GetCompatibleAppearances(500) returned %v results, want 0", len(compatible))
|
|
}
|
|
}
|
|
|
|
func TestAppearancesRemove(t *testing.T) {
|
|
apps := NewAppearances()
|
|
app := NewAppearance(100, "Test", 1000)
|
|
apps.InsertAppearance(app)
|
|
|
|
// Remove existing
|
|
if !apps.RemoveAppearance(100) {
|
|
t.Error("RemoveAppearance(100) should return true")
|
|
}
|
|
|
|
if apps.HasAppearance(100) {
|
|
t.Error("Appearance 100 should have been removed")
|
|
}
|
|
|
|
// Remove non-existent
|
|
if apps.RemoveAppearance(100) {
|
|
t.Error("RemoveAppearance(100) should return false for non-existent ID")
|
|
}
|
|
}
|
|
|
|
func TestAppearancesUpdate(t *testing.T) {
|
|
apps := NewAppearances()
|
|
|
|
// Test updating nil
|
|
err := apps.UpdateAppearance(nil)
|
|
if err == nil {
|
|
t.Error("UpdateAppearance(nil) should return error")
|
|
}
|
|
|
|
// Insert and update
|
|
original := NewAppearance(100, "Original", 1000)
|
|
apps.InsertAppearance(original)
|
|
|
|
updated := NewAppearance(100, "Updated", 1100)
|
|
err = apps.UpdateAppearance(updated)
|
|
if err != nil {
|
|
t.Errorf("UpdateAppearance failed: %v", err)
|
|
}
|
|
|
|
found := apps.FindAppearanceByID(100)
|
|
if found.GetName() != "Updated" {
|
|
t.Errorf("Updated appearance name = %v, want Updated", found.GetName())
|
|
}
|
|
|
|
// Update non-existent (should insert)
|
|
new := NewAppearance(200, "New", 1200)
|
|
err = apps.UpdateAppearance(new)
|
|
if err != nil {
|
|
t.Errorf("UpdateAppearance failed: %v", err)
|
|
}
|
|
|
|
if !apps.HasAppearance(200) {
|
|
t.Error("UpdateAppearance should insert non-existent appearance")
|
|
}
|
|
}
|
|
|
|
func TestAppearancesGetByIDRange(t *testing.T) {
|
|
apps := NewAppearances()
|
|
|
|
// Insert appearances with various IDs
|
|
for _, id := range []int32{5, 10, 15, 20, 25, 30} {
|
|
apps.InsertAppearance(NewAppearance(id, "Test", 1000))
|
|
}
|
|
|
|
results := apps.GetAppearancesByIDRange(10, 20)
|
|
if len(results) != 3 { // Should get 10, 15, 20
|
|
t.Errorf("GetAppearancesByIDRange(10, 20) returned %v results, want 3", len(results))
|
|
}
|
|
|
|
// Verify correct IDs
|
|
idMap := make(map[int32]bool)
|
|
for _, app := range results {
|
|
idMap[app.GetID()] = true
|
|
}
|
|
|
|
for _, expectedID := range []int32{10, 15, 20} {
|
|
if !idMap[expectedID] {
|
|
t.Errorf("Expected ID %v not found in range results", expectedID)
|
|
}
|
|
}
|
|
|
|
// Test empty range
|
|
results = apps.GetAppearancesByIDRange(100, 200)
|
|
if len(results) != 0 {
|
|
t.Errorf("GetAppearancesByIDRange(100, 200) returned %v results, want 0", len(results))
|
|
}
|
|
}
|
|
|
|
func TestAppearancesValidate(t *testing.T) {
|
|
apps := NewAppearances()
|
|
|
|
// Valid appearances
|
|
apps.InsertAppearance(NewAppearance(100, "Valid", 1000))
|
|
|
|
issues := apps.ValidateAppearances()
|
|
if len(issues) != 0 {
|
|
t.Errorf("ValidateAppearances() returned issues for valid data: %v", issues)
|
|
}
|
|
|
|
if !apps.IsValid() {
|
|
t.Error("IsValid() should return true for valid data")
|
|
}
|
|
|
|
// Force invalid state by directly modifying map
|
|
apps.mutex.Lock()
|
|
apps.appearanceMap[200] = nil
|
|
apps.appearanceMap[300] = NewAppearance(301, "", 1000) // ID mismatch and empty name
|
|
apps.appearanceMap[400] = NewAppearance(400, "Negative", -100)
|
|
apps.mutex.Unlock()
|
|
|
|
issues = apps.ValidateAppearances()
|
|
if len(issues) < 3 {
|
|
t.Errorf("ValidateAppearances() should return at least 3 issues, got %v", len(issues))
|
|
}
|
|
|
|
if apps.IsValid() {
|
|
t.Error("IsValid() should return false for invalid data")
|
|
}
|
|
}
|
|
|
|
func TestAppearancesStatistics(t *testing.T) {
|
|
apps := NewAppearances()
|
|
|
|
// Add appearances with different client versions
|
|
apps.InsertAppearance(NewAppearance(10, "A", 1000))
|
|
apps.InsertAppearance(NewAppearance(20, "B", 1000))
|
|
apps.InsertAppearance(NewAppearance(30, "C", 1096))
|
|
apps.InsertAppearance(NewAppearance(40, "D", 1096))
|
|
apps.InsertAppearance(NewAppearance(50, "E", 1096))
|
|
|
|
stats := apps.GetStatistics()
|
|
|
|
if total, ok := stats["total_appearances"].(int); !ok || total != 5 {
|
|
t.Errorf("total_appearances = %v, want 5", stats["total_appearances"])
|
|
}
|
|
|
|
if minID, ok := stats["min_id"].(int32); !ok || minID != 10 {
|
|
t.Errorf("min_id = %v, want 10", stats["min_id"])
|
|
}
|
|
|
|
if maxID, ok := stats["max_id"].(int32); !ok || maxID != 50 {
|
|
t.Errorf("max_id = %v, want 50", stats["max_id"])
|
|
}
|
|
|
|
if idRange, ok := stats["id_range"].(int32); !ok || idRange != 40 {
|
|
t.Errorf("id_range = %v, want 40", stats["id_range"])
|
|
}
|
|
|
|
if versionCounts, ok := stats["appearances_by_min_client"].(map[int16]int); ok {
|
|
if versionCounts[1000] != 2 {
|
|
t.Errorf("appearances with min client 1000 = %v, want 2", versionCounts[1000])
|
|
}
|
|
if versionCounts[1096] != 3 {
|
|
t.Errorf("appearances with min client 1096 = %v, want 3", versionCounts[1096])
|
|
}
|
|
} else {
|
|
t.Error("appearances_by_min_client not found in statistics")
|
|
}
|
|
}
|
|
|
|
func TestAppearancesClearAndReset(t *testing.T) {
|
|
apps := NewAppearances()
|
|
|
|
// Add some appearances
|
|
for i := int32(1); i <= 3; i++ {
|
|
apps.InsertAppearance(NewAppearance(i*100, "Test", 1000))
|
|
}
|
|
|
|
if apps.GetAppearanceCount() != 3 {
|
|
t.Error("Setup failed: should have 3 appearances")
|
|
}
|
|
|
|
// Test ClearAppearances
|
|
apps.ClearAppearances()
|
|
if apps.GetAppearanceCount() != 0 {
|
|
t.Errorf("ClearAppearances() failed: count = %v, want 0", apps.GetAppearanceCount())
|
|
}
|
|
|
|
// Add again and test Reset
|
|
for i := int32(1); i <= 3; i++ {
|
|
apps.InsertAppearance(NewAppearance(i*100, "Test", 1000))
|
|
}
|
|
|
|
apps.Reset()
|
|
if apps.GetAppearanceCount() != 0 {
|
|
t.Errorf("Reset() failed: count = %v, want 0", apps.GetAppearanceCount())
|
|
}
|
|
}
|
|
|
|
func TestAppearancesConcurrency(t *testing.T) {
|
|
apps := NewAppearances()
|
|
var wg sync.WaitGroup
|
|
|
|
// Concurrent insertions
|
|
for i := 0; i < 100; i++ {
|
|
wg.Add(1)
|
|
go func(id int32) {
|
|
defer wg.Done()
|
|
app := NewAppearance(id, "Concurrent", 1000)
|
|
apps.InsertAppearance(app)
|
|
}(int32(i))
|
|
}
|
|
|
|
// Concurrent reads
|
|
for i := 0; i < 50; i++ {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
_ = apps.GetAppearanceCount()
|
|
_ = apps.GetAllAppearances()
|
|
_ = apps.FindAppearanceByID(25)
|
|
}()
|
|
}
|
|
|
|
// Concurrent searches
|
|
for i := 0; i < 20; i++ {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
_ = apps.FindAppearancesByName("Concurrent")
|
|
_ = apps.GetCompatibleAppearances(1100)
|
|
}()
|
|
}
|
|
|
|
wg.Wait()
|
|
|
|
// Verify all insertions succeeded
|
|
if count := apps.GetAppearanceCount(); count != 100 {
|
|
t.Errorf("After concurrent operations, count = %v, want 100", count)
|
|
}
|
|
}
|
|
|
|
func TestContainsFunction(t *testing.T) {
|
|
tests := []struct {
|
|
str string
|
|
substr string
|
|
want bool
|
|
}{
|
|
{"hello world", "world", true},
|
|
{"hello world", "World", false}, // Case sensitive
|
|
{"hello", "hello world", false},
|
|
{"hello", "", true},
|
|
{"", "hello", false},
|
|
{"", "", true},
|
|
{"abcdef", "cde", true},
|
|
{"abcdef", "xyz", false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run("", func(t *testing.T) {
|
|
if got := contains(tt.str, tt.substr); got != tt.want {
|
|
t.Errorf("contains(%q, %q) = %v, want %v", tt.str, tt.substr, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// Benchmarks
|
|
func BenchmarkAppearanceInsert(b *testing.B) {
|
|
apps := NewAppearances()
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
app := NewAppearance(int32(i), "Benchmark", 1000)
|
|
apps.InsertAppearance(app)
|
|
}
|
|
}
|
|
|
|
func BenchmarkAppearanceFindByID(b *testing.B) {
|
|
apps := NewAppearances()
|
|
|
|
// Pre-populate
|
|
for i := 0; i < 10000; i++ {
|
|
app := NewAppearance(int32(i), "Benchmark", 1000)
|
|
apps.InsertAppearance(app)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
apps.FindAppearanceByID(int32(i % 10000))
|
|
}
|
|
}
|
|
|
|
func BenchmarkAppearanceFindByName(b *testing.B) {
|
|
apps := NewAppearances()
|
|
|
|
// Pre-populate with varied names
|
|
names := []string{"Human Male", "Human Female", "Elf Male", "Elf Female", "Dwarf Male"}
|
|
for i := 0; i < 1000; i++ {
|
|
app := NewAppearance(int32(i), names[i%len(names)], 1000)
|
|
apps.InsertAppearance(app)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
apps.FindAppearancesByName("Male")
|
|
}
|
|
}
|
|
|
|
// Mock implementations
|
|
type MockDatabase struct {
|
|
appearances []*Appearance
|
|
saveError error
|
|
loadError error
|
|
deleteError error
|
|
}
|
|
|
|
func (m *MockDatabase) LoadAllAppearances() ([]*Appearance, error) {
|
|
if m.loadError != nil {
|
|
return nil, m.loadError
|
|
}
|
|
return m.appearances, nil
|
|
}
|
|
|
|
func (m *MockDatabase) SaveAppearance(appearance *Appearance) error {
|
|
return m.saveError
|
|
}
|
|
|
|
func (m *MockDatabase) DeleteAppearance(id int32) error {
|
|
return m.deleteError
|
|
}
|
|
|
|
func (m *MockDatabase) LoadAppearancesByClientVersion(minClientVersion int16) ([]*Appearance, error) {
|
|
var results []*Appearance
|
|
for _, app := range m.appearances {
|
|
if app.GetMinClientVersion() == minClientVersion {
|
|
results = append(results, app)
|
|
}
|
|
}
|
|
return results, nil
|
|
}
|
|
|
|
type MockLogger struct {
|
|
logs []string
|
|
}
|
|
|
|
func (m *MockLogger) LogInfo(message string, args ...any) {
|
|
m.logs = append(m.logs, fmt.Sprintf("INFO: "+message, args...))
|
|
}
|
|
|
|
func (m *MockLogger) LogError(message string, args ...any) {
|
|
m.logs = append(m.logs, fmt.Sprintf("ERROR: "+message, args...))
|
|
}
|
|
|
|
func (m *MockLogger) LogDebug(message string, args ...any) {
|
|
m.logs = append(m.logs, fmt.Sprintf("DEBUG: "+message, args...))
|
|
}
|
|
|
|
func (m *MockLogger) LogWarning(message string, args ...any) {
|
|
m.logs = append(m.logs, fmt.Sprintf("WARNING: "+message, args...))
|
|
}
|
|
|
|
type MockEntity struct {
|
|
id int32
|
|
name string
|
|
databaseID int32
|
|
}
|
|
|
|
func (m *MockEntity) GetID() int32 { return m.id }
|
|
func (m *MockEntity) GetName() string { return m.name }
|
|
func (m *MockEntity) GetDatabaseID() int32 { return m.databaseID }
|
|
|
|
type MockClient struct {
|
|
version int16
|
|
sendError error
|
|
}
|
|
|
|
func (m *MockClient) GetVersion() int16 { return m.version }
|
|
func (m *MockClient) SendAppearanceUpdate(appearanceID int32) error { return m.sendError }
|
|
|
|
// Manager tests
|
|
func TestNewManager(t *testing.T) {
|
|
db := &MockDatabase{}
|
|
logger := &MockLogger{}
|
|
|
|
manager := NewManager(db, logger)
|
|
|
|
if manager == nil {
|
|
t.Fatal("NewManager returned nil")
|
|
}
|
|
|
|
if manager.database != db {
|
|
t.Error("Manager database not set correctly")
|
|
}
|
|
|
|
if manager.logger != logger {
|
|
t.Error("Manager logger not set correctly")
|
|
}
|
|
|
|
if manager.appearances == nil {
|
|
t.Error("Manager appearances not initialized")
|
|
}
|
|
}
|
|
|
|
func TestManagerInitialize(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
database *MockDatabase
|
|
wantError bool
|
|
wantCount int
|
|
wantLogInfo bool
|
|
}{
|
|
{
|
|
name: "successful initialization",
|
|
database: &MockDatabase{
|
|
appearances: []*Appearance{
|
|
NewAppearance(1, "Test1", 1000),
|
|
NewAppearance(2, "Test2", 1096),
|
|
},
|
|
},
|
|
wantError: false,
|
|
wantCount: 2,
|
|
wantLogInfo: true,
|
|
},
|
|
{
|
|
name: "nil database",
|
|
database: nil,
|
|
wantError: false,
|
|
wantCount: 0,
|
|
wantLogInfo: true,
|
|
},
|
|
{
|
|
name: "database load error",
|
|
database: &MockDatabase{
|
|
loadError: fmt.Errorf("database error"),
|
|
},
|
|
wantError: true,
|
|
wantCount: 0,
|
|
wantLogInfo: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
logger := &MockLogger{}
|
|
var manager *Manager
|
|
if tt.database != nil {
|
|
manager = NewManager(tt.database, logger)
|
|
} else {
|
|
manager = &Manager{
|
|
appearances: NewAppearances(),
|
|
database: nil,
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
err := manager.Initialize()
|
|
|
|
if (err != nil) != tt.wantError {
|
|
t.Errorf("Initialize() error = %v, wantError %v", err, tt.wantError)
|
|
}
|
|
|
|
if count := manager.GetAppearanceCount(); count != tt.wantCount {
|
|
t.Errorf("GetAppearanceCount() = %v, want %v", count, tt.wantCount)
|
|
}
|
|
|
|
if tt.wantLogInfo && len(logger.logs) == 0 {
|
|
t.Error("Expected log messages, got none")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestManagerFindAppearanceByID(t *testing.T) {
|
|
db := &MockDatabase{
|
|
appearances: []*Appearance{
|
|
NewAppearance(100, "Test", 1000),
|
|
},
|
|
}
|
|
logger := &MockLogger{}
|
|
manager := NewManager(db, logger)
|
|
manager.Initialize()
|
|
|
|
// Test successful lookup
|
|
appearance := manager.FindAppearanceByID(100)
|
|
if appearance == nil {
|
|
t.Error("FindAppearanceByID(100) returned nil")
|
|
}
|
|
|
|
// Test failed lookup
|
|
appearance = manager.FindAppearanceByID(999)
|
|
if appearance != nil {
|
|
t.Error("FindAppearanceByID(999) should return nil")
|
|
}
|
|
|
|
// Check statistics
|
|
stats := manager.GetStatistics()
|
|
if totalLookups, ok := stats["total_lookups"].(int64); !ok || totalLookups != 2 {
|
|
t.Errorf("total_lookups = %v, want 2", stats["total_lookups"])
|
|
}
|
|
|
|
if successfulLookups, ok := stats["successful_lookups"].(int64); !ok || successfulLookups != 1 {
|
|
t.Errorf("successful_lookups = %v, want 1", stats["successful_lookups"])
|
|
}
|
|
|
|
if failedLookups, ok := stats["failed_lookups"].(int64); !ok || failedLookups != 1 {
|
|
t.Errorf("failed_lookups = %v, want 1", stats["failed_lookups"])
|
|
}
|
|
}
|
|
|
|
func TestManagerAddAppearance(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
appearance *Appearance
|
|
saveError error
|
|
existingID int32
|
|
wantError bool
|
|
errorContains string
|
|
}{
|
|
{
|
|
name: "successful add",
|
|
appearance: NewAppearance(100, "Test", 1000),
|
|
wantError: false,
|
|
},
|
|
{
|
|
name: "nil appearance",
|
|
appearance: nil,
|
|
wantError: true,
|
|
errorContains: "cannot be nil",
|
|
},
|
|
{
|
|
name: "empty name",
|
|
appearance: &Appearance{id: 100, name: "", minClient: 1000},
|
|
wantError: true,
|
|
errorContains: "name cannot be empty",
|
|
},
|
|
{
|
|
name: "invalid ID",
|
|
appearance: NewAppearance(0, "Test", 1000),
|
|
wantError: true,
|
|
errorContains: "must be positive",
|
|
},
|
|
{
|
|
name: "duplicate ID",
|
|
appearance: NewAppearance(100, "Test", 1000),
|
|
existingID: 100,
|
|
wantError: true,
|
|
errorContains: "already exists",
|
|
},
|
|
{
|
|
name: "database save error",
|
|
appearance: NewAppearance(200, "Test", 1000),
|
|
saveError: fmt.Errorf("save failed"),
|
|
wantError: true,
|
|
errorContains: "database",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
db := &MockDatabase{saveError: tt.saveError}
|
|
logger := &MockLogger{}
|
|
manager := NewManager(db, logger)
|
|
|
|
if tt.existingID > 0 {
|
|
manager.appearances.InsertAppearance(NewAppearance(tt.existingID, "Existing", 1000))
|
|
}
|
|
|
|
err := manager.AddAppearance(tt.appearance)
|
|
|
|
if (err != nil) != tt.wantError {
|
|
t.Errorf("AddAppearance() error = %v, wantError %v", err, tt.wantError)
|
|
}
|
|
|
|
if err != nil && tt.errorContains != "" && !contains(err.Error(), tt.errorContains) {
|
|
t.Errorf("Error message %q doesn't contain %q", err.Error(), tt.errorContains)
|
|
}
|
|
|
|
// Verify appearance was added/not added
|
|
if !tt.wantError && tt.appearance != nil {
|
|
if !manager.appearances.HasAppearance(tt.appearance.GetID()) {
|
|
t.Error("Appearance was not added to collection")
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestManagerUpdateAppearance(t *testing.T) {
|
|
db := &MockDatabase{}
|
|
logger := &MockLogger{}
|
|
manager := NewManager(db, logger)
|
|
|
|
// Add initial appearance
|
|
original := NewAppearance(100, "Original", 1000)
|
|
manager.AddAppearance(original)
|
|
|
|
// Test successful update
|
|
updated := NewAppearance(100, "Updated", 1100)
|
|
err := manager.UpdateAppearance(updated)
|
|
if err != nil {
|
|
t.Errorf("UpdateAppearance failed: %v", err)
|
|
}
|
|
|
|
found := manager.FindAppearanceByID(100)
|
|
if found.GetName() != "Updated" {
|
|
t.Error("Appearance was not updated")
|
|
}
|
|
|
|
// Test update non-existent
|
|
notExist := NewAppearance(999, "NotExist", 1000)
|
|
err = manager.UpdateAppearance(notExist)
|
|
if err == nil {
|
|
t.Error("UpdateAppearance should fail for non-existent appearance")
|
|
}
|
|
|
|
// Test nil appearance
|
|
err = manager.UpdateAppearance(nil)
|
|
if err == nil {
|
|
t.Error("UpdateAppearance should fail for nil appearance")
|
|
}
|
|
}
|
|
|
|
func TestManagerRemoveAppearance(t *testing.T) {
|
|
db := &MockDatabase{}
|
|
logger := &MockLogger{}
|
|
manager := NewManager(db, logger)
|
|
|
|
// Add appearance
|
|
app := NewAppearance(100, "Test", 1000)
|
|
manager.AddAppearance(app)
|
|
|
|
// Test successful removal
|
|
err := manager.RemoveAppearance(100)
|
|
if err != nil {
|
|
t.Errorf("RemoveAppearance failed: %v", err)
|
|
}
|
|
|
|
if manager.appearances.HasAppearance(100) {
|
|
t.Error("Appearance was not removed")
|
|
}
|
|
|
|
// Test removing non-existent
|
|
err = manager.RemoveAppearance(999)
|
|
if err == nil {
|
|
t.Error("RemoveAppearance should fail for non-existent appearance")
|
|
}
|
|
|
|
// Test database delete error
|
|
db.deleteError = fmt.Errorf("delete failed")
|
|
manager.AddAppearance(NewAppearance(200, "Test2", 1000))
|
|
err = manager.RemoveAppearance(200)
|
|
if err == nil {
|
|
t.Error("RemoveAppearance should fail when database delete fails")
|
|
}
|
|
}
|
|
|
|
func TestManagerCommands(t *testing.T) {
|
|
db := &MockDatabase{
|
|
appearances: []*Appearance{
|
|
NewAppearance(100, "Human Male", 1000),
|
|
NewAppearance(200, "Human Female", 1096),
|
|
},
|
|
}
|
|
logger := &MockLogger{}
|
|
manager := NewManager(db, logger)
|
|
manager.Initialize()
|
|
|
|
// Test stats command
|
|
result, err := manager.ProcessCommand("stats", nil)
|
|
if err != nil {
|
|
t.Errorf("ProcessCommand(stats) failed: %v", err)
|
|
}
|
|
if !contains(result, "Appearance System Statistics") {
|
|
t.Error("Stats command output incorrect")
|
|
}
|
|
|
|
// Test validate command
|
|
result, err = manager.ProcessCommand("validate", nil)
|
|
if err != nil {
|
|
t.Errorf("ProcessCommand(validate) failed: %v", err)
|
|
}
|
|
if !contains(result, "valid") {
|
|
t.Error("Validate command output incorrect")
|
|
}
|
|
|
|
// Test search command
|
|
result, err = manager.ProcessCommand("search", []string{"Human"})
|
|
if err != nil {
|
|
t.Errorf("ProcessCommand(search) failed: %v", err)
|
|
}
|
|
if !contains(result, "Found 2 appearances") {
|
|
t.Error("Search command output incorrect")
|
|
}
|
|
|
|
// Test search without args
|
|
_, err = manager.ProcessCommand("search", nil)
|
|
if err == nil {
|
|
t.Error("Search command should fail without arguments")
|
|
}
|
|
|
|
// Test info command
|
|
result, err = manager.ProcessCommand("info", []string{"100"})
|
|
if err != nil {
|
|
t.Errorf("ProcessCommand(info) failed: %v", err)
|
|
}
|
|
if !contains(result, "Human Male") {
|
|
t.Error("Info command output incorrect")
|
|
}
|
|
|
|
// Test info without args
|
|
_, err = manager.ProcessCommand("info", nil)
|
|
if err == nil {
|
|
t.Error("Info command should fail without arguments")
|
|
}
|
|
|
|
// Test unknown command
|
|
_, err = manager.ProcessCommand("unknown", nil)
|
|
if err == nil {
|
|
t.Error("Unknown command should return error")
|
|
}
|
|
}
|
|
|
|
func TestManagerResetStatistics(t *testing.T) {
|
|
manager := NewManager(nil, nil)
|
|
|
|
// Perform some lookups
|
|
manager.FindAppearanceByID(100)
|
|
manager.FindAppearanceByID(200)
|
|
|
|
stats := manager.GetStatistics()
|
|
if totalLookups, ok := stats["total_lookups"].(int64); !ok || totalLookups != 2 {
|
|
t.Error("Statistics not tracked correctly")
|
|
}
|
|
|
|
// Reset statistics
|
|
manager.ResetStatistics()
|
|
|
|
stats = manager.GetStatistics()
|
|
if totalLookups, ok := stats["total_lookups"].(int64); !ok || totalLookups != 0 {
|
|
t.Error("Statistics not reset correctly")
|
|
}
|
|
}
|
|
|
|
func TestManagerShutdown(t *testing.T) {
|
|
logger := &MockLogger{}
|
|
manager := NewManager(nil, logger)
|
|
|
|
manager.appearances.InsertAppearance(NewAppearance(100, "Test", 1000))
|
|
|
|
manager.Shutdown()
|
|
|
|
if manager.GetAppearanceCount() != 0 {
|
|
t.Error("Shutdown did not clear appearances")
|
|
}
|
|
|
|
// Check for shutdown log
|
|
found := false
|
|
for _, log := range logger.logs {
|
|
if contains(log, "Shutting down") {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Error("Shutdown did not log message")
|
|
}
|
|
}
|
|
|
|
// EntityAppearanceAdapter tests
|
|
func TestEntityAppearanceAdapter(t *testing.T) {
|
|
entity := &MockEntity{id: 1, name: "TestEntity", databaseID: 100}
|
|
logger := &MockLogger{}
|
|
manager := NewManager(nil, logger)
|
|
|
|
// Add test appearance
|
|
app := NewAppearance(500, "TestAppearance", 1000)
|
|
manager.AddAppearance(app)
|
|
|
|
adapter := NewEntityAppearanceAdapter(entity, manager, logger)
|
|
|
|
// Test initial state
|
|
if adapter.GetAppearanceID() != 0 {
|
|
t.Error("Initial appearance ID should be 0")
|
|
}
|
|
|
|
if adapter.GetAppearance() != nil {
|
|
t.Error("Initial appearance should be nil")
|
|
}
|
|
|
|
// Test setting appearance ID
|
|
adapter.SetAppearanceID(500)
|
|
if adapter.GetAppearanceID() != 500 {
|
|
t.Error("SetAppearanceID failed")
|
|
}
|
|
|
|
// Test getting appearance
|
|
appearance := adapter.GetAppearance()
|
|
if appearance == nil || appearance.GetID() != 500 {
|
|
t.Error("GetAppearance failed")
|
|
}
|
|
|
|
// Test appearance name
|
|
if name := adapter.GetAppearanceName(); name != "TestAppearance" {
|
|
t.Errorf("GetAppearanceName() = %v, want TestAppearance", name)
|
|
}
|
|
|
|
// Test validation
|
|
if err := adapter.ValidateAppearance(); err != nil {
|
|
t.Errorf("ValidateAppearance() failed: %v", err)
|
|
}
|
|
|
|
// Test invalid appearance
|
|
adapter.SetAppearanceID(999)
|
|
if err := adapter.ValidateAppearance(); err == nil {
|
|
t.Error("ValidateAppearance() should fail for invalid ID")
|
|
}
|
|
}
|
|
|
|
func TestEntityAppearanceAdapterClientCompatibility(t *testing.T) {
|
|
entity := &MockEntity{id: 1, name: "TestEntity"}
|
|
manager := NewManager(nil, nil)
|
|
|
|
// Add appearance with version requirement
|
|
app := NewAppearance(100, "Test", 1096)
|
|
manager.AddAppearance(app)
|
|
|
|
adapter := NewEntityAppearanceAdapter(entity, manager, nil)
|
|
adapter.SetAppearanceID(100)
|
|
|
|
// Test compatible client
|
|
if !adapter.IsCompatibleWithClient(1100) {
|
|
t.Error("Should be compatible with client version 1100")
|
|
}
|
|
|
|
// Test incompatible client
|
|
if adapter.IsCompatibleWithClient(1000) {
|
|
t.Error("Should not be compatible with client version 1000")
|
|
}
|
|
|
|
// Test no appearance (always compatible)
|
|
adapter.SetAppearanceID(0)
|
|
if !adapter.IsCompatibleWithClient(500) {
|
|
t.Error("No appearance should be compatible with all clients")
|
|
}
|
|
}
|
|
|
|
func TestEntityAppearanceAdapterSendToClient(t *testing.T) {
|
|
entity := &MockEntity{id: 1, name: "TestEntity"}
|
|
logger := &MockLogger{}
|
|
manager := NewManager(nil, logger)
|
|
|
|
app := NewAppearance(100, "Test", 1096)
|
|
manager.AddAppearance(app)
|
|
|
|
adapter := NewEntityAppearanceAdapter(entity, manager, logger)
|
|
adapter.SetAppearanceID(100)
|
|
|
|
// Test successful send
|
|
client := &MockClient{version: 1100}
|
|
err := adapter.SendAppearanceToClient(client)
|
|
if err != nil {
|
|
t.Errorf("SendAppearanceToClient failed: %v", err)
|
|
}
|
|
|
|
// Test incompatible client
|
|
lowClient := &MockClient{version: 1000}
|
|
err = adapter.SendAppearanceToClient(lowClient)
|
|
if err == nil {
|
|
t.Error("SendAppearanceToClient should fail for incompatible client")
|
|
}
|
|
|
|
// Test client send error
|
|
errorClient := &MockClient{version: 1100, sendError: fmt.Errorf("send failed")}
|
|
err = adapter.SendAppearanceToClient(errorClient)
|
|
if err == nil {
|
|
t.Error("SendAppearanceToClient should propagate client error")
|
|
}
|
|
|
|
// Test nil client
|
|
err = adapter.SendAppearanceToClient(nil)
|
|
if err == nil {
|
|
t.Error("SendAppearanceToClient should fail for nil client")
|
|
}
|
|
}
|
|
|
|
// Cache tests
|
|
func TestSimpleAppearanceCache(t *testing.T) {
|
|
cache := NewSimpleAppearanceCache()
|
|
|
|
app1 := NewAppearance(100, "Test1", 1000)
|
|
app2 := NewAppearance(200, "Test2", 1096)
|
|
|
|
// Test Set and Get
|
|
cache.Set(100, app1)
|
|
cache.Set(200, app2)
|
|
|
|
if got := cache.Get(100); got != app1 {
|
|
t.Error("Cache Get(100) failed")
|
|
}
|
|
|
|
if got := cache.Get(999); got != nil {
|
|
t.Error("Cache Get(999) should return nil")
|
|
}
|
|
|
|
// Test GetSize
|
|
if size := cache.GetSize(); size != 2 {
|
|
t.Errorf("GetSize() = %v, want 2", size)
|
|
}
|
|
|
|
// Test Remove
|
|
cache.Remove(100)
|
|
if cache.Get(100) != nil {
|
|
t.Error("Remove(100) failed")
|
|
}
|
|
|
|
if size := cache.GetSize(); size != 1 {
|
|
t.Errorf("GetSize() after remove = %v, want 1", size)
|
|
}
|
|
|
|
// Test Clear
|
|
cache.Clear()
|
|
if size := cache.GetSize(); size != 0 {
|
|
t.Errorf("GetSize() after clear = %v, want 0", size)
|
|
}
|
|
}
|
|
|
|
func TestCachedAppearanceManager(t *testing.T) {
|
|
db := &MockDatabase{}
|
|
logger := &MockLogger{}
|
|
baseManager := NewManager(db, logger)
|
|
|
|
// Add test appearances
|
|
app1 := NewAppearance(100, "Test1", 1000)
|
|
app2 := NewAppearance(200, "Test2", 1096)
|
|
baseManager.AddAppearance(app1)
|
|
baseManager.AddAppearance(app2)
|
|
|
|
cache := NewSimpleAppearanceCache()
|
|
cachedManager := NewCachedAppearanceManager(baseManager, cache)
|
|
|
|
// Test FindAppearanceByID with caching
|
|
found := cachedManager.FindAppearanceByID(100)
|
|
if found == nil || found.GetID() != 100 {
|
|
t.Error("FindAppearanceByID(100) failed")
|
|
}
|
|
|
|
// Verify it was cached
|
|
if cache.GetSize() != 1 {
|
|
t.Error("Appearance was not cached")
|
|
}
|
|
|
|
// Test cache hit
|
|
found2 := cachedManager.FindAppearanceByID(100)
|
|
if found2 != found {
|
|
t.Error("Should have returned cached appearance")
|
|
}
|
|
|
|
// Test AddAppearance updates cache
|
|
app3 := NewAppearance(300, "Test3", 1200)
|
|
err := cachedManager.AddAppearance(app3)
|
|
if err != nil {
|
|
t.Errorf("AddAppearance failed: %v", err)
|
|
}
|
|
|
|
if cache.Get(300) == nil {
|
|
t.Error("Added appearance was not cached")
|
|
}
|
|
|
|
// Test UpdateAppearance updates cache
|
|
updated := NewAppearance(100, "Updated", 1100)
|
|
err = cachedManager.UpdateAppearance(updated)
|
|
if err != nil {
|
|
t.Errorf("UpdateAppearance failed: %v", err)
|
|
}
|
|
|
|
cached := cache.Get(100)
|
|
if cached == nil || cached.GetName() != "Updated" {
|
|
t.Error("Cache was not updated")
|
|
}
|
|
|
|
// Test RemoveAppearance updates cache
|
|
err = cachedManager.RemoveAppearance(200)
|
|
if err != nil {
|
|
t.Errorf("RemoveAppearance failed: %v", err)
|
|
}
|
|
|
|
if cache.Get(200) != nil {
|
|
t.Error("Removed appearance still in cache")
|
|
}
|
|
|
|
// Test ClearCache
|
|
cachedManager.ClearCache()
|
|
if cache.GetSize() != 0 {
|
|
t.Error("ClearCache failed")
|
|
}
|
|
}
|
|
|
|
func TestCacheConcurrency(t *testing.T) {
|
|
cache := NewSimpleAppearanceCache()
|
|
var wg sync.WaitGroup
|
|
|
|
// Concurrent operations
|
|
for i := 0; i < 100; i++ {
|
|
wg.Add(3)
|
|
|
|
// Writer
|
|
go func(id int32) {
|
|
defer wg.Done()
|
|
app := NewAppearance(id, "Test", 1000)
|
|
cache.Set(id, app)
|
|
}(int32(i))
|
|
|
|
// Reader
|
|
go func(id int32) {
|
|
defer wg.Done()
|
|
_ = cache.Get(id)
|
|
}(int32(i))
|
|
|
|
// Size checker
|
|
go func() {
|
|
defer wg.Done()
|
|
_ = cache.GetSize()
|
|
}()
|
|
}
|
|
|
|
wg.Wait()
|
|
|
|
// Final size should be predictable
|
|
if size := cache.GetSize(); size > 100 {
|
|
t.Errorf("Cache size %v is too large", size)
|
|
}
|
|
}
|
|
|
|
// Additional tests for uncovered code paths
|
|
func TestEntityAppearanceAdapterErrorCases(t *testing.T) {
|
|
entity := &MockEntity{id: 1, name: "TestEntity"}
|
|
logger := &MockLogger{}
|
|
|
|
// Test with nil manager
|
|
adapter := NewEntityAppearanceAdapter(entity, nil, logger)
|
|
adapter.SetAppearanceID(100)
|
|
|
|
// GetAppearance should return nil and log error
|
|
appearance := adapter.GetAppearance()
|
|
if appearance != nil {
|
|
t.Error("GetAppearance should return nil with nil manager")
|
|
}
|
|
|
|
// Test UpdateAppearance with nil manager
|
|
err := adapter.UpdateAppearance(100)
|
|
if err == nil {
|
|
t.Error("UpdateAppearance should fail with nil manager")
|
|
}
|
|
|
|
// Test UpdateAppearance with non-existent appearance
|
|
manager := NewManager(nil, logger)
|
|
adapter = NewEntityAppearanceAdapter(entity, manager, logger)
|
|
err = adapter.UpdateAppearance(999)
|
|
if err == nil {
|
|
t.Error("UpdateAppearance should fail for non-existent appearance")
|
|
}
|
|
|
|
// Test SendAppearanceToClient with no appearance set
|
|
client := &MockClient{version: 1100}
|
|
adapter.SetAppearanceID(0)
|
|
err = adapter.SendAppearanceToClient(client)
|
|
if err != nil {
|
|
t.Errorf("SendAppearanceToClient should succeed with no appearance: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestManagerGetAppearances(t *testing.T) {
|
|
manager := NewManager(nil, nil)
|
|
|
|
appearances := manager.GetAppearances()
|
|
if appearances == nil {
|
|
t.Error("GetAppearances should not return nil")
|
|
}
|
|
|
|
if appearances != manager.appearances {
|
|
t.Error("GetAppearances should return internal appearances collection")
|
|
}
|
|
}
|
|
|
|
func TestManagerCompatibleAndSearch(t *testing.T) {
|
|
manager := NewManager(nil, nil)
|
|
|
|
// Add test appearances
|
|
app1 := NewAppearance(100, "Human Male", 1000)
|
|
app2 := NewAppearance(200, "Human Female", 1096)
|
|
manager.AddAppearance(app1)
|
|
manager.AddAppearance(app2)
|
|
|
|
// Test GetCompatibleAppearances
|
|
compatible := manager.GetCompatibleAppearances(1050)
|
|
if len(compatible) != 1 {
|
|
t.Errorf("GetCompatibleAppearances(1050) returned %v results, want 1", len(compatible))
|
|
}
|
|
|
|
// Test SearchAppearancesByName
|
|
results := manager.SearchAppearancesByName("Human")
|
|
if len(results) != 2 {
|
|
t.Errorf("SearchAppearancesByName('Human') returned %v results, want 2", len(results))
|
|
}
|
|
}
|
|
|
|
func TestManagerReloadFromDatabase(t *testing.T) {
|
|
// Test with nil database
|
|
manager := NewManager(nil, nil)
|
|
err := manager.ReloadFromDatabase()
|
|
if err == nil {
|
|
t.Error("ReloadFromDatabase should fail with nil database")
|
|
}
|
|
|
|
// Test successful reload
|
|
db := &MockDatabase{
|
|
appearances: []*Appearance{
|
|
NewAppearance(100, "Test", 1000),
|
|
},
|
|
}
|
|
manager = NewManager(db, nil)
|
|
|
|
// Add some appearances first
|
|
manager.AddAppearance(NewAppearance(200, "Existing", 1000))
|
|
|
|
err = manager.ReloadFromDatabase()
|
|
if err != nil {
|
|
t.Errorf("ReloadFromDatabase failed: %v", err)
|
|
}
|
|
|
|
// Should only have the database appearance now
|
|
if count := manager.GetAppearanceCount(); count != 1 {
|
|
t.Errorf("After reload, count = %v, want 1", count)
|
|
}
|
|
|
|
if !manager.appearances.HasAppearance(100) {
|
|
t.Error("Reloaded appearance not found")
|
|
}
|
|
|
|
if manager.appearances.HasAppearance(200) {
|
|
t.Error("Previous appearance should be cleared")
|
|
}
|
|
}
|
|
|
|
func TestManagerCommandEdgeCases(t *testing.T) {
|
|
manager := NewManager(nil, nil)
|
|
|
|
// Test reload command without database
|
|
result, err := manager.ProcessCommand("reload", nil)
|
|
if err == nil {
|
|
t.Error("Reload command should fail without database")
|
|
}
|
|
|
|
// Test info command with invalid ID
|
|
_, err = manager.ProcessCommand("info", []string{"invalid"})
|
|
if err == nil {
|
|
t.Error("Info command should fail with invalid ID")
|
|
}
|
|
|
|
// Test info command with non-existent ID
|
|
result, err = manager.ProcessCommand("info", []string{"999"})
|
|
if err != nil {
|
|
t.Errorf("Info command failed: %v", err)
|
|
}
|
|
if !contains(result, "not found") {
|
|
t.Error("Info command should indicate appearance not found")
|
|
}
|
|
|
|
// Test search with no results
|
|
result, err = manager.ProcessCommand("search", []string{"nonexistent"})
|
|
if err != nil {
|
|
t.Errorf("Search command failed: %v", err)
|
|
}
|
|
if !contains(result, "No appearances found") {
|
|
t.Error("Search command should indicate no results")
|
|
}
|
|
}
|
|
|
|
func TestManagerValidateAllAppearances(t *testing.T) {
|
|
manager := NewManager(nil, nil)
|
|
|
|
// Add valid appearance
|
|
manager.AddAppearance(NewAppearance(100, "Valid", 1000))
|
|
|
|
issues := manager.ValidateAllAppearances()
|
|
if len(issues) != 0 {
|
|
t.Errorf("ValidateAllAppearances returned issues for valid data: %v", issues)
|
|
}
|
|
}
|
|
|
|
func TestEntityAppearanceAdapterGetAppearanceName(t *testing.T) {
|
|
entity := &MockEntity{id: 1, name: "TestEntity"}
|
|
manager := NewManager(nil, nil)
|
|
adapter := NewEntityAppearanceAdapter(entity, manager, nil)
|
|
|
|
// Test with no appearance
|
|
if name := adapter.GetAppearanceName(); name != "" {
|
|
t.Errorf("GetAppearanceName() with no appearance = %v, want empty string", name)
|
|
}
|
|
|
|
// Test with appearance
|
|
app := NewAppearance(100, "TestName", 1000)
|
|
manager.AddAppearance(app)
|
|
adapter.SetAppearanceID(100)
|
|
|
|
if name := adapter.GetAppearanceName(); name != "TestName" {
|
|
t.Errorf("GetAppearanceName() = %v, want TestName", name)
|
|
}
|
|
}
|