1208 lines
31 KiB
Go
1208 lines
31 KiB
Go
package rules
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"zombiezen.com/go/sqlite"
|
|
)
|
|
|
|
// Test Rule creation and basic functionality
|
|
func TestNewRule(t *testing.T) {
|
|
rule := NewRule()
|
|
if rule == nil {
|
|
t.Fatal("NewRule() returned nil")
|
|
}
|
|
|
|
if rule.GetCategory() != 0 {
|
|
t.Errorf("Expected category 0, got %d", rule.GetCategory())
|
|
}
|
|
|
|
if rule.GetType() != 0 {
|
|
t.Errorf("Expected type 0, got %d", rule.GetType())
|
|
}
|
|
|
|
if rule.GetValue() != "" {
|
|
t.Errorf("Expected empty value, got %s", rule.GetValue())
|
|
}
|
|
|
|
if rule.GetCombined() != "NONE" {
|
|
t.Errorf("Expected combined 'NONE', got %s", rule.GetCombined())
|
|
}
|
|
}
|
|
|
|
func TestNewRuleWithValues(t *testing.T) {
|
|
rule := NewRuleWithValues(CategoryPlayer, PlayerMaxLevel, "50", "Player:MaxLevel")
|
|
if rule == nil {
|
|
t.Fatal("NewRuleWithValues() returned nil")
|
|
}
|
|
|
|
if rule.GetCategory() != CategoryPlayer {
|
|
t.Errorf("Expected category %d, got %d", CategoryPlayer, rule.GetCategory())
|
|
}
|
|
|
|
if rule.GetType() != PlayerMaxLevel {
|
|
t.Errorf("Expected type %d, got %d", PlayerMaxLevel, rule.GetType())
|
|
}
|
|
|
|
if rule.GetValue() != "50" {
|
|
t.Errorf("Expected value '50', got %s", rule.GetValue())
|
|
}
|
|
|
|
if rule.GetCombined() != "Player:MaxLevel" {
|
|
t.Errorf("Expected combined 'Player:MaxLevel', got %s", rule.GetCombined())
|
|
}
|
|
}
|
|
|
|
func TestNewRuleFromRule(t *testing.T) {
|
|
// Test with nil source
|
|
rule := NewRuleFromRule(nil)
|
|
if rule == nil {
|
|
t.Error("NewRuleFromRule(nil) should return default rule, not nil")
|
|
}
|
|
|
|
// Test with valid source
|
|
original := NewRuleWithValues(CategoryCombat, CombatMaxRange, "4.0", "Combat:MaxCombatRange")
|
|
copy := NewRuleFromRule(original)
|
|
|
|
if copy == nil {
|
|
t.Fatal("NewRuleFromRule() returned nil")
|
|
}
|
|
|
|
if copy.GetCategory() != original.GetCategory() {
|
|
t.Errorf("Copy category mismatch: expected %d, got %d", original.GetCategory(), copy.GetCategory())
|
|
}
|
|
|
|
if copy.GetType() != original.GetType() {
|
|
t.Errorf("Copy type mismatch: expected %d, got %d", original.GetType(), copy.GetType())
|
|
}
|
|
|
|
if copy.GetValue() != original.GetValue() {
|
|
t.Errorf("Copy value mismatch: expected %s, got %s", original.GetValue(), copy.GetValue())
|
|
}
|
|
|
|
if copy.GetCombined() != original.GetCombined() {
|
|
t.Errorf("Copy combined mismatch: expected %s, got %s", original.GetCombined(), copy.GetCombined())
|
|
}
|
|
|
|
// Test that they are independent copies
|
|
copy.SetValue("8.0")
|
|
if original.GetValue() == copy.GetValue() {
|
|
t.Error("Original and copy are not independent after modification")
|
|
}
|
|
}
|
|
|
|
func TestRuleSetValue(t *testing.T) {
|
|
rule := NewRule()
|
|
rule.SetValue("test")
|
|
|
|
if rule.GetValue() != "test" {
|
|
t.Errorf("SetValue failed: expected 'test', got %s", rule.GetValue())
|
|
}
|
|
}
|
|
|
|
func TestRuleTypeConversions(t *testing.T) {
|
|
rule := NewRuleWithValues(CategoryPlayer, PlayerMaxLevel, "50", "Player:MaxLevel")
|
|
|
|
// Test integer conversions
|
|
if rule.GetInt8() != 50 {
|
|
t.Errorf("Expected int8 50, got %d", rule.GetInt8())
|
|
}
|
|
|
|
if rule.GetInt16() != 50 {
|
|
t.Errorf("Expected int16 50, got %d", rule.GetInt16())
|
|
}
|
|
|
|
if rule.GetInt32() != 50 {
|
|
t.Errorf("Expected int32 50, got %d", rule.GetInt32())
|
|
}
|
|
|
|
if rule.GetInt64() != 50 {
|
|
t.Errorf("Expected int64 50, got %d", rule.GetInt64())
|
|
}
|
|
|
|
// Test unsigned integer conversions
|
|
if rule.GetUInt8() != 50 {
|
|
t.Errorf("Expected uint8 50, got %d", rule.GetUInt8())
|
|
}
|
|
|
|
if rule.GetUInt16() != 50 {
|
|
t.Errorf("Expected uint16 50, got %d", rule.GetUInt16())
|
|
}
|
|
|
|
if rule.GetUInt32() != 50 {
|
|
t.Errorf("Expected uint32 50, got %d", rule.GetUInt32())
|
|
}
|
|
|
|
if rule.GetUInt64() != 50 {
|
|
t.Errorf("Expected uint64 50, got %d", rule.GetUInt64())
|
|
}
|
|
|
|
// Test boolean conversion (> 0 = true)
|
|
if !rule.GetBool() {
|
|
t.Error("Expected bool true for value '50'")
|
|
}
|
|
|
|
// Test float conversions
|
|
rule.SetValue("3.14")
|
|
if rule.GetFloat32() != 3.14 {
|
|
t.Errorf("Expected float32 3.14, got %f", rule.GetFloat32())
|
|
}
|
|
|
|
if rule.GetFloat64() != 3.14 {
|
|
t.Errorf("Expected float64 3.14, got %f", rule.GetFloat64())
|
|
}
|
|
|
|
// Test character conversion
|
|
rule.SetValue("Hello")
|
|
if rule.GetChar() != 'H' {
|
|
t.Errorf("Expected char 'H', got %c", rule.GetChar())
|
|
}
|
|
|
|
// Test string conversion
|
|
if rule.GetString() != "Hello" {
|
|
t.Errorf("Expected string 'Hello', got %s", rule.GetString())
|
|
}
|
|
}
|
|
|
|
func TestRuleTypeConversionsInvalid(t *testing.T) {
|
|
rule := NewRuleWithValues(CategoryPlayer, PlayerMaxLevel, "invalid", "Player:MaxLevel")
|
|
|
|
// Invalid conversions should return zero values
|
|
if rule.GetInt32() != 0 {
|
|
t.Errorf("Expected int32 0 for invalid value, got %d", rule.GetInt32())
|
|
}
|
|
|
|
if rule.GetFloat64() != 0.0 {
|
|
t.Errorf("Expected float64 0.0 for invalid value, got %f", rule.GetFloat64())
|
|
}
|
|
|
|
if rule.GetBool() != false {
|
|
t.Error("Expected bool false for invalid value")
|
|
}
|
|
}
|
|
|
|
func TestRuleIsValid(t *testing.T) {
|
|
// Test invalid rule (default)
|
|
rule := NewRule()
|
|
if rule.IsValid() {
|
|
t.Error("Default rule should not be valid")
|
|
}
|
|
|
|
// Test valid rule
|
|
validRule := NewRuleWithValues(CategoryPlayer, PlayerMaxLevel, "50", "Player:MaxLevel")
|
|
if !validRule.IsValid() {
|
|
t.Error("Rule with valid data should be valid")
|
|
}
|
|
}
|
|
|
|
func TestRuleString(t *testing.T) {
|
|
rule := NewRuleWithValues(CategoryPlayer, PlayerMaxLevel, "50", "Player:MaxLevel")
|
|
str := rule.String()
|
|
|
|
expected := "Rule{Player:MaxLevel: 50}"
|
|
if str != expected {
|
|
t.Errorf("Expected string %s, got %s", expected, str)
|
|
}
|
|
}
|
|
|
|
// Test RuleSet creation and operations
|
|
func TestNewRuleSet(t *testing.T) {
|
|
ruleSet := NewRuleSet()
|
|
if ruleSet == nil {
|
|
t.Fatal("NewRuleSet() returned nil")
|
|
}
|
|
|
|
if ruleSet.GetID() != 0 {
|
|
t.Errorf("Expected ID 0, got %d", ruleSet.GetID())
|
|
}
|
|
|
|
if ruleSet.GetName() != "" {
|
|
t.Errorf("Expected empty name, got %s", ruleSet.GetName())
|
|
}
|
|
|
|
if ruleSet.Size() != 0 {
|
|
t.Errorf("Expected size 0, got %d", ruleSet.Size())
|
|
}
|
|
}
|
|
|
|
func TestRuleSetIDAndName(t *testing.T) {
|
|
ruleSet := NewRuleSet()
|
|
|
|
// Test SetID and SetName
|
|
ruleSet.SetID(1)
|
|
ruleSet.SetName("Test Rule Set")
|
|
|
|
if ruleSet.GetID() != 1 {
|
|
t.Errorf("Expected ID 1, got %d", ruleSet.GetID())
|
|
}
|
|
|
|
if ruleSet.GetName() != "Test Rule Set" {
|
|
t.Errorf("Expected name 'Test Rule Set', got %s", ruleSet.GetName())
|
|
}
|
|
}
|
|
|
|
func TestRuleSetAddRule(t *testing.T) {
|
|
ruleSet := NewRuleSet()
|
|
|
|
// Test adding nil rule
|
|
ruleSet.AddRule(nil) // Should not crash
|
|
if ruleSet.Size() != 0 {
|
|
t.Error("Adding nil rule should not increase size")
|
|
}
|
|
|
|
// Test adding valid rules
|
|
rule1 := NewRuleWithValues(CategoryPlayer, PlayerMaxLevel, "50", "Player:MaxLevel")
|
|
rule2 := NewRuleWithValues(CategoryCombat, CombatMaxRange, "4.0", "Combat:MaxCombatRange")
|
|
|
|
ruleSet.AddRule(rule1)
|
|
ruleSet.AddRule(rule2)
|
|
|
|
if ruleSet.Size() != 2 {
|
|
t.Errorf("Expected size 2, got %d", ruleSet.Size())
|
|
}
|
|
}
|
|
|
|
func TestRuleSetGetRule(t *testing.T) {
|
|
ruleSet := NewRuleSet()
|
|
rule1 := NewRuleWithValues(CategoryPlayer, PlayerMaxLevel, "50", "Player:MaxLevel")
|
|
ruleSet.AddRule(rule1)
|
|
|
|
// Test rule retrieval
|
|
retrievedRule := ruleSet.GetRule(CategoryPlayer, PlayerMaxLevel)
|
|
if retrievedRule == nil {
|
|
t.Fatal("GetRule() returned nil for added rule")
|
|
}
|
|
|
|
if retrievedRule.GetValue() != "50" {
|
|
t.Errorf("Retrieved rule value mismatch: expected '50', got %s", retrievedRule.GetValue())
|
|
}
|
|
|
|
// Test non-existent rule
|
|
nonExistentRule := ruleSet.GetRule(CategorySpawn, SpawnSpeedMultiplier)
|
|
if nonExistentRule != nil {
|
|
t.Error("GetRule() should return nil for non-existent rule")
|
|
}
|
|
}
|
|
|
|
func TestRuleSetGetRuleByName(t *testing.T) {
|
|
ruleSet := NewRuleSet()
|
|
rule := NewRuleWithValues(CategoryCombat, CombatMaxRange, "4.0", "Combat:MaxCombatRange")
|
|
ruleSet.AddRule(rule)
|
|
|
|
// Test rule retrieval by name
|
|
retrievedRule := ruleSet.GetRuleByName("Combat", "MaxCombatRange")
|
|
if retrievedRule == nil {
|
|
t.Fatal("GetRuleByName() returned nil for added rule")
|
|
}
|
|
|
|
if retrievedRule.GetValue() != "4.0" {
|
|
t.Errorf("Retrieved rule value mismatch: expected '4.0', got %s", retrievedRule.GetValue())
|
|
}
|
|
|
|
// Test non-existent rule
|
|
nonExistentRule := ruleSet.GetRuleByName("NonExistent", "Rule")
|
|
if nonExistentRule != nil {
|
|
t.Error("GetRuleByName() should return nil for non-existent rule")
|
|
}
|
|
}
|
|
|
|
func TestRuleSetHasRule(t *testing.T) {
|
|
ruleSet := NewRuleSet()
|
|
rule := NewRuleWithValues(CategoryPlayer, PlayerMaxLevel, "50", "Player:MaxLevel")
|
|
ruleSet.AddRule(rule)
|
|
|
|
// Test HasRule
|
|
if !ruleSet.HasRule(CategoryPlayer, PlayerMaxLevel) {
|
|
t.Error("HasRule() returned false for existing rule")
|
|
}
|
|
|
|
if ruleSet.HasRule(CategorySpawn, SpawnSpeedMultiplier) {
|
|
t.Error("HasRule() returned true for non-existing rule")
|
|
}
|
|
}
|
|
|
|
func TestRuleSetGetRulesByCategory(t *testing.T) {
|
|
ruleSet := NewRuleSet()
|
|
|
|
// Add multiple player rules
|
|
rule1 := NewRuleWithValues(CategoryPlayer, PlayerMaxLevel, "50", "Player:MaxLevel")
|
|
rule2 := NewRuleWithValues(CategoryPlayer, PlayerVitalityAmount, "0.5", "Player:VitalityAmount")
|
|
rule3 := NewRuleWithValues(CategoryCombat, CombatMaxRange, "4.0", "Combat:MaxCombatRange")
|
|
|
|
ruleSet.AddRule(rule1)
|
|
ruleSet.AddRule(rule2)
|
|
ruleSet.AddRule(rule3)
|
|
|
|
// Test getting rules by category
|
|
playerRules := ruleSet.GetRulesByCategory(CategoryPlayer)
|
|
if len(playerRules) != 2 {
|
|
t.Errorf("Expected 2 player rules, got %d", len(playerRules))
|
|
}
|
|
|
|
// Test empty category
|
|
spawnRules := ruleSet.GetRulesByCategory(CategorySpawn)
|
|
if len(spawnRules) != 0 {
|
|
t.Errorf("Expected 0 spawn rules, got %d", len(spawnRules))
|
|
}
|
|
}
|
|
|
|
func TestRuleSetClearRules(t *testing.T) {
|
|
ruleSet := NewRuleSet()
|
|
rule := NewRuleWithValues(CategoryPlayer, PlayerMaxLevel, "50", "Player:MaxLevel")
|
|
ruleSet.AddRule(rule)
|
|
|
|
if ruleSet.Size() != 1 {
|
|
t.Error("Rule set should have 1 rule before clearing")
|
|
}
|
|
|
|
ruleSet.ClearRules()
|
|
|
|
if ruleSet.Size() != 0 {
|
|
t.Error("Rule set should be empty after clearing")
|
|
}
|
|
}
|
|
|
|
func TestRuleSetCopyRulesInto(t *testing.T) {
|
|
source := NewRuleSet()
|
|
source.SetID(1)
|
|
source.SetName("Source")
|
|
|
|
rule1 := NewRuleWithValues(CategoryPlayer, PlayerMaxLevel, "50", "Player:MaxLevel")
|
|
rule2 := NewRuleWithValues(CategoryCombat, CombatMaxRange, "4.0", "Combat:MaxCombatRange")
|
|
source.AddRule(rule1)
|
|
source.AddRule(rule2)
|
|
|
|
target := NewRuleSet()
|
|
target.SetID(2)
|
|
target.SetName("Target")
|
|
|
|
// Test copying rules
|
|
target.CopyRulesInto(source)
|
|
|
|
if target.Size() != 2 {
|
|
t.Errorf("Target should have 2 rules after copying, got %d", target.Size())
|
|
}
|
|
|
|
// Test that target still has its own ID and name
|
|
if target.GetID() != 2 {
|
|
t.Errorf("Target ID should remain 2, got %d", target.GetID())
|
|
}
|
|
|
|
if target.GetName() != "Target" {
|
|
t.Errorf("Target name should remain 'Target', got %s", target.GetName())
|
|
}
|
|
}
|
|
|
|
func TestNewRuleSetFromRuleSet(t *testing.T) {
|
|
// Test with nil source
|
|
nilCopy := NewRuleSetFromRuleSet(nil)
|
|
if nilCopy == nil {
|
|
t.Error("NewRuleSetFromRuleSet(nil) should return empty rule set, not nil")
|
|
}
|
|
|
|
// Test with valid source
|
|
original := NewRuleSet()
|
|
original.SetID(1)
|
|
original.SetName("Original")
|
|
|
|
rule1 := NewRuleWithValues(CategoryPlayer, PlayerMaxLevel, "50", "Player:MaxLevel")
|
|
rule2 := NewRuleWithValues(CategoryCombat, CombatMaxRange, "4.0", "Combat:MaxCombatRange")
|
|
original.AddRule(rule1)
|
|
original.AddRule(rule2)
|
|
|
|
// Test copy constructor
|
|
copy := NewRuleSetFromRuleSet(original)
|
|
if copy == nil {
|
|
t.Fatal("NewRuleSetFromRuleSet() returned nil")
|
|
}
|
|
|
|
if copy.GetID() != original.GetID() {
|
|
t.Errorf("Copy ID mismatch: expected %d, got %d", original.GetID(), copy.GetID())
|
|
}
|
|
|
|
if copy.GetName() != original.GetName() {
|
|
t.Errorf("Copy name mismatch: expected %s, got %s", original.GetName(), copy.GetName())
|
|
}
|
|
|
|
if copy.Size() != original.Size() {
|
|
t.Errorf("Copy size mismatch: expected %d, got %d", original.Size(), copy.Size())
|
|
}
|
|
|
|
// Test that rules were copied correctly
|
|
copyRule := copy.GetRule(CategoryPlayer, PlayerMaxLevel)
|
|
if copyRule == nil {
|
|
t.Fatal("Copied rule set is missing expected rule")
|
|
}
|
|
|
|
if copyRule.GetValue() != "50" {
|
|
t.Errorf("Copied rule value mismatch: expected '50', got %s", copyRule.GetValue())
|
|
}
|
|
}
|
|
|
|
func TestRuleSetString(t *testing.T) {
|
|
ruleSet := NewRuleSet()
|
|
ruleSet.SetID(1)
|
|
ruleSet.SetName("Test Set")
|
|
|
|
rule := NewRuleWithValues(CategoryPlayer, PlayerMaxLevel, "50", "Player:MaxLevel")
|
|
ruleSet.AddRule(rule)
|
|
|
|
str := ruleSet.String()
|
|
expected := "RuleSet{ID: 1, Name: Test Set, Rules: 1}"
|
|
if str != expected {
|
|
t.Errorf("Expected string %s, got %s", expected, str)
|
|
}
|
|
}
|
|
|
|
// Test RuleManager initialization and operations
|
|
func TestNewRuleManager(t *testing.T) {
|
|
ruleManager := NewRuleManager()
|
|
if ruleManager == nil {
|
|
t.Fatal("NewRuleManager() returned nil")
|
|
}
|
|
|
|
if !ruleManager.IsInitialized() {
|
|
t.Error("RuleManager should be initialized after creation")
|
|
}
|
|
}
|
|
|
|
func TestRuleManagerGetGlobalRule(t *testing.T) {
|
|
ruleManager := NewRuleManager()
|
|
|
|
// Test getting a default rule that should be initialized
|
|
rule := ruleManager.GetGlobalRule(CategoryPlayer, PlayerMaxLevel)
|
|
if rule == nil {
|
|
t.Fatal("GetGlobalRule() returned nil for default rule")
|
|
}
|
|
|
|
// Should return the default value from Init()
|
|
if rule.GetValue() != "50" {
|
|
t.Errorf("Expected default value '50', got %s", rule.GetValue())
|
|
}
|
|
|
|
// Test getting rule by name
|
|
rule2 := ruleManager.GetGlobalRuleByName("Player", "MaxLevel")
|
|
if rule2 == nil {
|
|
t.Fatal("GetGlobalRuleByName() returned nil for default rule")
|
|
}
|
|
|
|
if rule2.GetValue() != "50" {
|
|
t.Errorf("Expected default value '50', got %s", rule2.GetValue())
|
|
}
|
|
}
|
|
|
|
func TestRuleManagerBlankRule(t *testing.T) {
|
|
ruleManager := NewRuleManager()
|
|
|
|
// Test blank rule for non-existent rule
|
|
blankRule := ruleManager.GetGlobalRule(9999, 9999)
|
|
if blankRule == nil {
|
|
t.Fatal("GetGlobalRule() should return blank rule for non-existent rule")
|
|
}
|
|
|
|
if blankRule.IsValid() {
|
|
t.Error("Blank rule should not be valid")
|
|
}
|
|
|
|
// Test GetBlankRule method
|
|
blankRule2 := ruleManager.GetBlankRule()
|
|
if blankRule2 == nil {
|
|
t.Fatal("GetBlankRule() should not return nil")
|
|
}
|
|
|
|
if blankRule2.IsValid() {
|
|
t.Error("Blank rule should not be valid")
|
|
}
|
|
}
|
|
|
|
func TestRuleManagerRuleSetOperations(t *testing.T) {
|
|
ruleManager := NewRuleManager()
|
|
|
|
// Create a test rule set
|
|
ruleSet := NewRuleSet()
|
|
ruleSet.SetID(1)
|
|
ruleSet.SetName("Test Set")
|
|
|
|
// Add some rules to it
|
|
rule1 := NewRuleWithValues(CategoryPlayer, PlayerMaxLevel, "60", "Player:MaxLevel")
|
|
ruleSet.AddRule(rule1)
|
|
|
|
// Add the rule set to the manager
|
|
if !ruleManager.AddRuleSet(ruleSet) {
|
|
t.Fatal("AddRuleSet() returned false")
|
|
}
|
|
|
|
// Test getting the rule set
|
|
retrievedRuleSet := ruleManager.GetRuleSet(1)
|
|
if retrievedRuleSet == nil {
|
|
t.Fatal("GetRuleSet() returned nil")
|
|
}
|
|
|
|
if retrievedRuleSet.GetName() != "Test Set" {
|
|
t.Errorf("Retrieved rule set name mismatch: expected 'Test Set', got %s", retrievedRuleSet.GetName())
|
|
}
|
|
|
|
// Test duplicate rule set
|
|
duplicateRuleSet := NewRuleSet()
|
|
duplicateRuleSet.SetID(1)
|
|
duplicateRuleSet.SetName("Duplicate")
|
|
|
|
if ruleManager.AddRuleSet(duplicateRuleSet) {
|
|
t.Error("AddRuleSet() should return false for duplicate ID")
|
|
}
|
|
|
|
// Test GetNumRuleSets
|
|
if ruleManager.GetNumRuleSets() != 1 {
|
|
t.Errorf("Expected 1 rule set, got %d", ruleManager.GetNumRuleSets())
|
|
}
|
|
}
|
|
|
|
func TestRuleManagerGlobalRuleSet(t *testing.T) {
|
|
ruleManager := NewRuleManager()
|
|
|
|
// Create a test rule set with modified rules
|
|
ruleSet := NewRuleSet()
|
|
ruleSet.SetID(1)
|
|
ruleSet.SetName("Test Set")
|
|
|
|
// Load coded defaults first, then modify
|
|
ruleManager.LoadCodedDefaultsIntoRuleSet(ruleSet)
|
|
|
|
// Override a rule
|
|
playerMaxLevelRule := ruleSet.GetRule(CategoryPlayer, PlayerMaxLevel)
|
|
if playerMaxLevelRule != nil {
|
|
playerMaxLevelRule.SetValue("60")
|
|
}
|
|
|
|
ruleManager.AddRuleSet(ruleSet)
|
|
|
|
// Test setting global rule set
|
|
if !ruleManager.SetGlobalRuleSet(1) {
|
|
t.Fatal("SetGlobalRuleSet() returned false")
|
|
}
|
|
|
|
// Test that global rule now returns the overridden value
|
|
globalRule := ruleManager.GetGlobalRule(CategoryPlayer, PlayerMaxLevel)
|
|
if globalRule == nil {
|
|
t.Fatal("GetGlobalRule() returned nil after setting global rule set")
|
|
}
|
|
|
|
if globalRule.GetValue() != "60" {
|
|
t.Errorf("Expected overridden value '60', got %s", globalRule.GetValue())
|
|
}
|
|
}
|
|
|
|
func TestRuleManagerZoneRules(t *testing.T) {
|
|
ruleManager := NewRuleManager()
|
|
|
|
// Create a zone-specific rule set
|
|
zoneRuleSet := NewRuleSet()
|
|
zoneRuleSet.SetID(100)
|
|
zoneRuleSet.SetName("Zone Rules")
|
|
|
|
// Load coded defaults and modify a rule
|
|
ruleManager.LoadCodedDefaultsIntoRuleSet(zoneRuleSet)
|
|
playerMaxLevelRule := zoneRuleSet.GetRule(CategoryPlayer, PlayerMaxLevel)
|
|
if playerMaxLevelRule != nil {
|
|
playerMaxLevelRule.SetValue("70")
|
|
}
|
|
|
|
ruleManager.AddRuleSet(zoneRuleSet)
|
|
|
|
// Set zone rule set
|
|
zoneID := int32(1)
|
|
if !ruleManager.SetZoneRuleSet(zoneID, 100) {
|
|
t.Fatal("SetZoneRuleSet() returned false")
|
|
}
|
|
|
|
// Test zone rule lookup
|
|
zoneRule := ruleManager.GetZoneRule(zoneID, CategoryPlayer, PlayerMaxLevel)
|
|
if zoneRule == nil {
|
|
t.Fatal("GetZoneRule() returned nil")
|
|
}
|
|
|
|
if zoneRule.GetValue() != "70" {
|
|
t.Errorf("Expected zone rule value '70', got %s", zoneRule.GetValue())
|
|
}
|
|
|
|
// Test fallback to global rule for non-zone rule
|
|
globalFallback := ruleManager.GetZoneRule(999, CategoryPlayer, PlayerMaxLevel)
|
|
if globalFallback == nil {
|
|
t.Fatal("GetZoneRule() should fallback to global rule")
|
|
}
|
|
}
|
|
|
|
func TestRuleManagerFlush(t *testing.T) {
|
|
ruleManager := NewRuleManager()
|
|
|
|
// Add a rule set
|
|
ruleSet := NewRuleSet()
|
|
ruleSet.SetID(1)
|
|
ruleSet.SetName("Test Set")
|
|
ruleManager.AddRuleSet(ruleSet)
|
|
|
|
// Test flush with reinit
|
|
ruleManager.Flush(true)
|
|
|
|
if ruleManager.GetNumRuleSets() != 0 {
|
|
t.Error("Rule sets should be cleared after flush")
|
|
}
|
|
|
|
if !ruleManager.IsInitialized() {
|
|
t.Error("Rule manager should be initialized after flush(true)")
|
|
}
|
|
|
|
// Test flush without reinit
|
|
ruleManager.Flush(false)
|
|
if ruleManager.IsInitialized() {
|
|
t.Error("Rule manager should not be initialized after flush(false)")
|
|
}
|
|
}
|
|
|
|
func TestRuleManagerClearOperations(t *testing.T) {
|
|
ruleManager := NewRuleManager()
|
|
|
|
// Add rule sets
|
|
ruleSet1 := NewRuleSet()
|
|
ruleSet1.SetID(1)
|
|
ruleManager.AddRuleSet(ruleSet1)
|
|
|
|
ruleSet2 := NewRuleSet()
|
|
ruleSet2.SetID(2)
|
|
ruleManager.AddRuleSet(ruleSet2)
|
|
|
|
// Set zone rule set
|
|
ruleManager.SetZoneRuleSet(1, 1)
|
|
|
|
// Test ClearRuleSets
|
|
ruleManager.ClearRuleSets()
|
|
if ruleManager.GetNumRuleSets() != 0 {
|
|
t.Error("Rule sets should be cleared")
|
|
}
|
|
|
|
// Test ClearZoneRuleSets
|
|
ruleManager.ClearZoneRuleSets()
|
|
zoneRule := ruleManager.GetZoneRule(1, CategoryPlayer, PlayerMaxLevel)
|
|
// Should fallback to global rule, not zone rule
|
|
if zoneRule == nil {
|
|
t.Error("Should fallback to global rule after clearing zone rule sets")
|
|
}
|
|
}
|
|
|
|
func TestRuleManagerGetAllRuleSets(t *testing.T) {
|
|
ruleManager := NewRuleManager()
|
|
|
|
// Add multiple rule sets
|
|
for i := 1; i <= 3; i++ {
|
|
ruleSet := NewRuleSet()
|
|
ruleSet.SetID(int32(i))
|
|
ruleSet.SetName("Rule Set " + string(rune(i+'0')))
|
|
ruleManager.AddRuleSet(ruleSet)
|
|
}
|
|
|
|
allRuleSets := ruleManager.GetAllRuleSets()
|
|
if len(allRuleSets) != 3 {
|
|
t.Errorf("Expected 3 rule sets, got %d", len(allRuleSets))
|
|
}
|
|
}
|
|
|
|
func TestRuleManagerValidateRule(t *testing.T) {
|
|
ruleManager := NewRuleManager()
|
|
|
|
// Test valid rule
|
|
err := ruleManager.ValidateRule(CategoryPlayer, PlayerMaxLevel, "50")
|
|
if err != nil {
|
|
t.Errorf("ValidateRule() returned error for valid rule: %v", err)
|
|
}
|
|
|
|
// Test rule value too long
|
|
longValue := make([]byte, MaxRuleValueLength+1)
|
|
for i := range longValue {
|
|
longValue[i] = 'a'
|
|
}
|
|
|
|
err = ruleManager.ValidateRule(CategoryPlayer, PlayerMaxLevel, string(longValue))
|
|
if err != ErrRuleValueTooLong {
|
|
t.Error("ValidateRule() should return ErrRuleValueTooLong for long value")
|
|
}
|
|
}
|
|
|
|
func TestRuleManagerGetRuleInfo(t *testing.T) {
|
|
ruleManager := NewRuleManager()
|
|
|
|
info := ruleManager.GetRuleInfo(CategoryPlayer, PlayerMaxLevel)
|
|
expected := "Rule: Player:MaxLevel = 50"
|
|
if info != expected {
|
|
t.Errorf("Expected rule info %s, got %s", expected, info)
|
|
}
|
|
|
|
// Test non-existent rule
|
|
info = ruleManager.GetRuleInfo(9999, 9999)
|
|
if info != "Rule not found" {
|
|
t.Errorf("Expected 'Rule not found', got %s", info)
|
|
}
|
|
}
|
|
|
|
func TestRuleManagerStatistics(t *testing.T) {
|
|
ruleManager := NewRuleManager()
|
|
|
|
// Get initial stats
|
|
stats := ruleManager.GetStats()
|
|
|
|
// Add rule sets and perform operations to change stats
|
|
ruleSet := NewRuleSet()
|
|
ruleSet.SetID(1)
|
|
ruleManager.AddRuleSet(ruleSet)
|
|
|
|
// Get rule to increment lookup counter
|
|
ruleManager.GetGlobalRule(CategoryPlayer, PlayerMaxLevel)
|
|
|
|
// Get updated stats
|
|
newStats := ruleManager.GetStats()
|
|
if newStats.TotalRuleSets != 1 {
|
|
t.Errorf("Expected 1 total rule set, got %d", newStats.TotalRuleSets)
|
|
}
|
|
|
|
if newStats.RuleGetOperations <= stats.RuleGetOperations {
|
|
t.Error("Rule get operations should have increased")
|
|
}
|
|
|
|
// Test reset stats
|
|
ruleManager.ResetStats()
|
|
resetStats := ruleManager.GetStats()
|
|
if resetStats.RuleGetOperations != 0 {
|
|
t.Error("Stats should be reset to 0")
|
|
}
|
|
}
|
|
|
|
func TestRuleManagerString(t *testing.T) {
|
|
ruleManager := NewRuleManager()
|
|
str := ruleManager.String()
|
|
|
|
// Should contain basic information about rule sets, rules, etc.
|
|
if str == "" {
|
|
t.Error("String() should return non-empty string")
|
|
}
|
|
}
|
|
|
|
// Test category name functions
|
|
func TestCategoryNames(t *testing.T) {
|
|
// Test GetCategoryName
|
|
name := GetCategoryName(CategoryPlayer)
|
|
if name != "Player" {
|
|
t.Errorf("Expected category name 'Player', got %s", name)
|
|
}
|
|
|
|
// Test unknown category
|
|
unknownName := GetCategoryName(9999)
|
|
if unknownName != "Unknown" {
|
|
t.Errorf("Expected 'Unknown' for invalid category, got %s", unknownName)
|
|
}
|
|
|
|
// Test GetCategoryByName
|
|
category, exists := GetCategoryByName("Player")
|
|
if !exists {
|
|
t.Error("GetCategoryByName() should find 'Player' category")
|
|
}
|
|
|
|
if category != CategoryPlayer {
|
|
t.Errorf("Expected category %d, got %d", CategoryPlayer, category)
|
|
}
|
|
|
|
// Test unknown category name
|
|
_, exists = GetCategoryByName("NonExistent")
|
|
if exists {
|
|
t.Error("GetCategoryByName() should not find non-existent category")
|
|
}
|
|
}
|
|
|
|
// Test RuleManagerAdapter
|
|
func TestRuleManagerAdapter(t *testing.T) {
|
|
ruleManager := NewRuleManager()
|
|
adapter := NewRuleManagerAdapter(ruleManager, 0)
|
|
|
|
if adapter == nil {
|
|
t.Fatal("NewRuleManagerAdapter() returned nil")
|
|
}
|
|
|
|
// Test basic rule access
|
|
rule := adapter.GetRule(CategoryPlayer, PlayerMaxLevel)
|
|
if rule == nil {
|
|
t.Fatal("Adapter GetRule() returned nil")
|
|
}
|
|
|
|
if rule.GetValue() != "50" {
|
|
t.Errorf("Expected value '50', got %s", rule.GetValue())
|
|
}
|
|
|
|
// Test convenience methods
|
|
intValue := adapter.GetInt32(CategoryPlayer, PlayerMaxLevel)
|
|
if intValue != 50 {
|
|
t.Errorf("Expected int32 50, got %d", intValue)
|
|
}
|
|
|
|
boolValue := adapter.GetBool(CategoryPlayer, PlayerMaxLevel)
|
|
if !boolValue {
|
|
t.Error("Expected bool true for value '50'")
|
|
}
|
|
|
|
stringValue := adapter.GetString(CategoryPlayer, PlayerMaxLevel)
|
|
if stringValue != "50" {
|
|
t.Errorf("Expected string '50', got %s", stringValue)
|
|
}
|
|
|
|
floatValue := adapter.GetFloat64(CategoryPlayer, PlayerMaxLevel)
|
|
if floatValue != 50.0 {
|
|
t.Errorf("Expected float64 50.0, got %f", floatValue)
|
|
}
|
|
|
|
// Test zone ID
|
|
if adapter.GetZoneID() != 0 {
|
|
t.Errorf("Expected zone ID 0, got %d", adapter.GetZoneID())
|
|
}
|
|
|
|
adapter.SetZoneID(100)
|
|
if adapter.GetZoneID() != 100 {
|
|
t.Errorf("Expected zone ID 100 after SetZoneID, got %d", adapter.GetZoneID())
|
|
}
|
|
}
|
|
|
|
func TestRuleManagerAdapterWithZone(t *testing.T) {
|
|
ruleManager := NewRuleManager()
|
|
|
|
// Create zone-specific rule set
|
|
zoneRuleSet := NewRuleSet()
|
|
zoneRuleSet.SetID(100)
|
|
ruleManager.LoadCodedDefaultsIntoRuleSet(zoneRuleSet)
|
|
|
|
// Override a rule
|
|
if rule := zoneRuleSet.GetRule(CategoryPlayer, PlayerMaxLevel); rule != nil {
|
|
rule.SetValue("70")
|
|
}
|
|
|
|
ruleManager.AddRuleSet(zoneRuleSet)
|
|
ruleManager.SetZoneRuleSet(1, 100)
|
|
|
|
// Test adapter with zone
|
|
adapter := NewRuleManagerAdapter(ruleManager, 1)
|
|
intValue := adapter.GetInt32(CategoryPlayer, PlayerMaxLevel)
|
|
if intValue != 70 {
|
|
t.Errorf("Expected zone-specific value 70, got %d", intValue)
|
|
}
|
|
}
|
|
|
|
// Test RuleManagerStats operations
|
|
func TestRuleManagerStats(t *testing.T) {
|
|
stats := &RuleManagerStats{}
|
|
|
|
// Test increment operations
|
|
initialGets := stats.RuleGetOperations
|
|
stats.IncrementRuleGetOperations()
|
|
if stats.RuleGetOperations != initialGets+1 {
|
|
t.Error("IncrementRuleGetOperations() did not increment correctly")
|
|
}
|
|
|
|
initialSets := stats.RuleSetOperations
|
|
stats.IncrementRuleSetOperations()
|
|
if stats.RuleSetOperations != initialSets+1 {
|
|
t.Error("IncrementRuleSetOperations() did not increment correctly")
|
|
}
|
|
|
|
initialDB := stats.DatabaseOperations
|
|
stats.IncrementDatabaseOperations()
|
|
if stats.DatabaseOperations != initialDB+1 {
|
|
t.Error("IncrementDatabaseOperations() did not increment correctly")
|
|
}
|
|
|
|
// Test snapshot
|
|
snapshot := stats.GetSnapshot()
|
|
if snapshot.RuleGetOperations != stats.RuleGetOperations {
|
|
t.Error("Snapshot should match current stats")
|
|
}
|
|
|
|
// Test reset
|
|
stats.Reset()
|
|
if stats.RuleGetOperations != 0 || stats.RuleSetOperations != 0 || stats.DatabaseOperations != 0 {
|
|
t.Error("Reset() should zero all counters")
|
|
}
|
|
}
|
|
|
|
// Test error constants
|
|
func TestErrorConstants(t *testing.T) {
|
|
errors := []error{
|
|
ErrRuleNotFound,
|
|
ErrRuleSetNotFound,
|
|
ErrInvalidRuleCategory,
|
|
ErrInvalidRuleType,
|
|
ErrInvalidRuleValue,
|
|
ErrDuplicateRuleSet,
|
|
ErrRuleSetNotActive,
|
|
ErrGlobalRuleSetNotSet,
|
|
ErrZoneRuleSetNotFound,
|
|
ErrRuleValueTooLong,
|
|
ErrRuleNameTooLong,
|
|
}
|
|
|
|
for _, err := range errors {
|
|
if err == nil {
|
|
t.Error("Error constant should not be nil")
|
|
}
|
|
if err.Error() == "" {
|
|
t.Error("Error message should not be empty")
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test constants
|
|
func TestConstants(t *testing.T) {
|
|
// Test some rule categories
|
|
if CategoryClient != 0 {
|
|
t.Errorf("CategoryClient should be 0, got %d", CategoryClient)
|
|
}
|
|
if CategoryPlayer != 3 {
|
|
t.Errorf("CategoryPlayer should be 3, got %d", CategoryPlayer)
|
|
}
|
|
|
|
// Test some rule types
|
|
if PlayerMaxLevel != 0 {
|
|
t.Errorf("PlayerMaxLevel should be 0, got %d", PlayerMaxLevel)
|
|
}
|
|
|
|
// Test validation constants
|
|
if MaxRuleValueLength != 1024 {
|
|
t.Errorf("MaxRuleValueLength should be 1024, got %d", MaxRuleValueLength)
|
|
}
|
|
if MaxRuleCombinedLength != 2048 {
|
|
t.Errorf("MaxRuleCombinedLength should be 2048, got %d", MaxRuleCombinedLength)
|
|
}
|
|
|
|
// Test database constants
|
|
if TableRuleSets != "rulesets" {
|
|
t.Errorf("TableRuleSets should be 'rulesets', got %s", TableRuleSets)
|
|
}
|
|
if DefaultRuleSetIDVar != "default_ruleset_id" {
|
|
t.Errorf("DefaultRuleSetIDVar should be 'default_ruleset_id', got %s", DefaultRuleSetIDVar)
|
|
}
|
|
}
|
|
|
|
// Test DatabaseService with in-memory SQLite
|
|
func TestDatabaseService(t *testing.T) {
|
|
// Create in-memory database
|
|
conn, err := sqlite.OpenConn(":memory:", 0)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create in-memory database: %v", err)
|
|
}
|
|
defer conn.Close()
|
|
|
|
ds := NewDatabaseService(conn)
|
|
if ds == nil {
|
|
t.Fatal("NewDatabaseService() returned nil")
|
|
}
|
|
|
|
// Test CreateRulesTables
|
|
err = ds.CreateRulesTables()
|
|
if err != nil {
|
|
t.Fatalf("CreateRulesTables() failed: %v", err)
|
|
}
|
|
|
|
// Test ValidateDatabase
|
|
err = ds.ValidateDatabase()
|
|
if err != nil {
|
|
t.Fatalf("ValidateDatabase() failed after creating tables: %v", err)
|
|
}
|
|
|
|
// Test SetDefaultRuleSet and GetDefaultRuleSetID
|
|
testRuleSetID := int32(42)
|
|
err = ds.SetDefaultRuleSet(testRuleSetID)
|
|
if err != nil {
|
|
t.Fatalf("SetDefaultRuleSet() failed: %v", err)
|
|
}
|
|
|
|
retrievedID, err := ds.GetDefaultRuleSetID()
|
|
if err != nil {
|
|
t.Fatalf("GetDefaultRuleSetID() failed: %v", err)
|
|
}
|
|
|
|
if retrievedID != testRuleSetID {
|
|
t.Errorf("Expected rule set ID %d, got %d", testRuleSetID, retrievedID)
|
|
}
|
|
}
|
|
|
|
func TestDatabaseServiceRuleSetOperations(t *testing.T) {
|
|
// Create in-memory database
|
|
conn, err := sqlite.OpenConn(":memory:", 0)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create in-memory database: %v", err)
|
|
}
|
|
defer conn.Close()
|
|
|
|
ds := NewDatabaseService(conn)
|
|
ds.CreateRulesTables()
|
|
|
|
// Create a test rule set
|
|
ruleSet := NewRuleSet()
|
|
ruleSet.SetID(1)
|
|
ruleSet.SetName("Test Rule Set")
|
|
|
|
// Add some rules
|
|
rule1 := NewRuleWithValues(CategoryPlayer, PlayerMaxLevel, "60", "Player:MaxLevel")
|
|
rule2 := NewRuleWithValues(CategoryCombat, CombatMaxRange, "5.0", "Combat:MaxCombatRange")
|
|
ruleSet.AddRule(rule1)
|
|
ruleSet.AddRule(rule2)
|
|
|
|
// Test SaveRuleSet
|
|
err = ds.SaveRuleSet(ruleSet)
|
|
if err != nil {
|
|
t.Fatalf("SaveRuleSet() failed: %v", err)
|
|
}
|
|
|
|
// Test GetRuleSetList
|
|
ruleSets, err := ds.GetRuleSetList()
|
|
if err != nil {
|
|
t.Fatalf("GetRuleSetList() failed: %v", err)
|
|
}
|
|
|
|
if len(ruleSets) != 1 {
|
|
t.Errorf("Expected 1 rule set, got %d", len(ruleSets))
|
|
}
|
|
|
|
if ruleSets[0].ID != 1 {
|
|
t.Errorf("Expected rule set ID 1, got %d", ruleSets[0].ID)
|
|
}
|
|
|
|
if ruleSets[0].Name != "Test Rule Set" {
|
|
t.Errorf("Expected rule set name 'Test Rule Set', got %s", ruleSets[0].Name)
|
|
}
|
|
|
|
// Test DeleteRuleSet
|
|
err = ds.DeleteRuleSet(1)
|
|
if err != nil {
|
|
t.Fatalf("DeleteRuleSet() failed: %v", err)
|
|
}
|
|
|
|
// Verify deletion
|
|
ruleSets, err = ds.GetRuleSetList()
|
|
if err != nil {
|
|
t.Fatalf("GetRuleSetList() failed after deletion: %v", err)
|
|
}
|
|
|
|
if len(ruleSets) != 0 {
|
|
t.Errorf("Expected 0 rule sets after deletion, got %d", len(ruleSets))
|
|
}
|
|
}
|
|
|
|
// Test RuleService functionality
|
|
func TestRuleService(t *testing.T) {
|
|
config := RuleServiceConfig{
|
|
DatabaseEnabled: false,
|
|
CacheEnabled: false,
|
|
CacheTTL: 3600,
|
|
MaxCacheSize: 1024 * 1024,
|
|
}
|
|
|
|
service := NewRuleService(config)
|
|
if service == nil {
|
|
t.Fatal("NewRuleService() returned nil")
|
|
}
|
|
|
|
err := service.Initialize()
|
|
if err != nil {
|
|
t.Fatalf("Initialize() failed: %v", err)
|
|
}
|
|
|
|
ruleManager := service.GetRuleManager()
|
|
if ruleManager == nil {
|
|
t.Fatal("GetRuleManager() returned nil")
|
|
}
|
|
|
|
adapter := service.GetAdapter(0)
|
|
if adapter == nil {
|
|
t.Fatal("GetAdapter() returned nil")
|
|
}
|
|
|
|
err = service.Shutdown()
|
|
if err != nil {
|
|
t.Fatalf("Shutdown() failed: %v", err)
|
|
}
|
|
}
|
|
|
|
// Test splitCombined helper function
|
|
func TestSplitCombined(t *testing.T) {
|
|
// Test valid combined string
|
|
parts := splitCombined("Player:MaxLevel")
|
|
if len(parts) != 2 {
|
|
t.Errorf("Expected 2 parts, got %d", len(parts))
|
|
}
|
|
if parts[0] != "Player" || parts[1] != "MaxLevel" {
|
|
t.Errorf("Expected ['Player', 'MaxLevel'], got %v", parts)
|
|
}
|
|
|
|
// Test invalid combined string (no colon)
|
|
parts = splitCombined("PlayerMaxLevel")
|
|
if len(parts) != 1 {
|
|
t.Errorf("Expected 1 part for invalid string, got %d", len(parts))
|
|
}
|
|
if parts[0] != "PlayerMaxLevel" {
|
|
t.Errorf("Expected ['PlayerMaxLevel'], got %v", parts)
|
|
}
|
|
|
|
// Test empty string
|
|
parts = splitCombined("")
|
|
if len(parts) != 1 {
|
|
t.Errorf("Expected 1 part for empty string, got %d", len(parts))
|
|
}
|
|
if parts[0] != "" {
|
|
t.Errorf("Expected [''], got %v", parts)
|
|
}
|
|
}
|
|
|
|
// Benchmark tests
|
|
func BenchmarkRuleAccess(b *testing.B) {
|
|
ruleManager := NewRuleManager()
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = ruleManager.GetGlobalRule(CategoryPlayer, PlayerMaxLevel)
|
|
}
|
|
}
|
|
|
|
func BenchmarkRuleManagerCreation(b *testing.B) {
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = NewRuleManager()
|
|
}
|
|
}
|
|
|
|
func BenchmarkRuleSetAddRule(b *testing.B) {
|
|
ruleSet := NewRuleSet()
|
|
rules := make([]*Rule, b.N)
|
|
|
|
// Pre-create rules
|
|
for i := 0; i < b.N; i++ {
|
|
rules[i] = NewRuleWithValues(CategoryPlayer, PlayerMaxLevel+RuleType(i), "50", "Player:MaxLevel")
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
ruleSet.AddRule(rules[i])
|
|
}
|
|
}
|
|
|
|
func BenchmarkRuleTypeConversion(b *testing.B) {
|
|
rule := NewRuleWithValues(CategoryPlayer, PlayerMaxLevel, "12345", "Player:MaxLevel")
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = rule.GetInt32()
|
|
}
|
|
}
|
|
|
|
func BenchmarkRuleManagerAdapter(b *testing.B) {
|
|
ruleManager := NewRuleManager()
|
|
adapter := NewRuleManagerAdapter(ruleManager, 0)
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = adapter.GetInt32(CategoryPlayer, PlayerMaxLevel)
|
|
}
|
|
} |