package companion import ( "strings" "testing" ) func TestCompileAIProfileProducesDeterministicLuaAndReportsGaps(t *testing.T) { profile := DefaultAIProfile(9) one, err := CompileAIProfile(profile, DefaultStrategyCapabilities()) if err != nil { t.Fatal(err) } if !one.ReadyForStaticAIC() || one.Status != "compiled_with_gaps" { t.Fatalf("compile status=%+v", one) } if one.LuaSHA256 == "" || !strings.Contains(one.LuaSource, "behavior = \"assist\"") { t.Fatalf("lua artifact missing: %+v", one) } two, err := CompileAIProfile(profile, DefaultStrategyCapabilities()) if err != nil { t.Fatal(err) } if one.LuaSHA256 != two.LuaSHA256 || one.LuaSource != two.LuaSource { t.Fatalf("compiler is not deterministic: %q %q", one.LuaSHA256, two.LuaSHA256) } for _, want := range []StrategyCapability{CapabilityLeaderFollow, CapabilityLeaderBuff, CapabilityTargetSticky, CapabilityStuckDetour} { found := false for _, got := range one.MissingCapabilities { if got == want { found = true break } } if !found { t.Fatalf("missing capability %q not reported: %v", want, one.MissingCapabilities) } } } func TestCompileAIProfileAggressiveOrdersBuffBeforeAttack(t *testing.T) { profile := DefaultAIProfile(1) profile.Behavior = "aggressive" profile.ComboPreset = "sword_guard" result, err := CompileAIProfile(profile, StrategyCapabilities{ ThinkTerm: true, Distance: true, Cooldown: true, ActionStream: true, EnemyCount: true, }) if err != nil { t.Fatal(err) } if !result.ReadyForStaticAIC() { t.Fatalf("status=%s missing=%v", result.Status, result.MissingCapabilities) } if len(result.Steps) == 0 || result.Steps[0].Name != "buff_first" { t.Fatalf("steps=%+v", result.Steps) } if !strings.Contains(result.LuaSource, "combo_preset = \"sword_guard\"") { t.Fatalf("lua preset missing: %s", result.LuaSource) } } func TestAITuningRejectsUnsafeValues(t *testing.T) { profile := DefaultAIProfile(1) profile.Tuning = &AITuning{ThinkIntervalMS: 1} if err := profile.Validate(); err != ErrSelection { t.Fatalf("err=%v", err) } profile.Tuning = &AITuning{HealLeaderPercent: 101} if err := profile.Validate(); err != ErrSelection { t.Fatalf("heal err=%v", err) } } func TestSelectAITemplateUsesNativeBaselineForClassAndBehavior(t *testing.T) { assist := SelectAITemplate(DefaultAIProfile(4), 4) aggressiveProfile := DefaultAIProfile(4) aggressiveProfile.Behavior = "aggressive" aggressive := SelectAITemplate(aggressiveProfile, 4) other := SelectAITemplate(DefaultAIProfile(9), 2) for name, selector := range map[string]AITemplateSelector{ "assist": assist, "aggressive": aggressive, "other": other, } { if !selector.Valid() { t.Fatalf("%s selector invalid: %+v", name, selector) } } if assist.APCCode != 20902 || assist.AICPath != "aicharacter/_bizarre/priest/mirror_priest/mirror_priest.aic" { t.Fatalf("priest assist did not use native baseline: %+v", assist) } if aggressive.APCCode != assist.APCCode || aggressive.AICPath != assist.AICPath { t.Fatalf("behavior selectors must patch the same native baseline: assist=%+v aggressive=%+v", assist, aggressive) } if assist.APCCode == other.APCCode || assist.AICPath == other.AICPath { t.Fatalf("class selectors aliased: assist=%+v other=%+v", assist, other) } }