Files
ThordekkCore/INTERACTIONMANAGER_INTEGRATION_TESTS.md
T
2026-01-20 21:33:16 -03:00

29 KiB

InteractionManager Integration Test Specification

Document Version: 1.0 Date: 2025-10-04 Author: Integration Test Orchestrator Agent Purpose: Comprehensive integration test suite for InteractionManager state machine architecture


Test Categories

1. State Machine Integration Tests

Test 1.1: Full State Transition Flow - Vendor Interaction

TEST(InteractionManager_StateMachine, FullVendorFlow_AllStatesTransition)
{
    // SETUP
    Player* bot = CreateTestBot(POSITION_NEAR_VENDOR);
    Creature* vendor = CreateTestVendor(VENDOR_POSITION);
    InteractionManager* mgr = InteractionManager::Instance();

    // EXECUTE
    InteractionResult result = mgr->StartInteraction(bot, vendor, InteractionType::Vendor);

    // VERIFY: Initial state
    ASSERT_EQ(result, InteractionResult::Pending);
    InteractionContext* ctx = mgr->GetInteractionContext(bot);
    ASSERT_NE(ctx, nullptr);
    ASSERT_EQ(ctx->state, InteractionState::Approaching);

    // SIMULATE: Bot approaches vendor
    SimulateMovement(bot, vendor, 4.0f);  // Within interaction range
    mgr->ProcessInteractionState(bot, 100);

    // VERIFY: Approaching -> Initiating transition
    ASSERT_EQ(ctx->state, InteractionState::Initiating);

    // SIMULATE: Initiating completes (face target, set selection)
    mgr->ProcessInteractionState(bot, 100);

    // VERIFY: Initiating -> ExecutingAction (no gossip required)
    ASSERT_EQ(ctx->state, InteractionState::ExecutingAction);

    // SIMULATE: VendorInteraction handler completes
    MockVendorInteractionSuccess();
    mgr->ProcessInteractionState(bot, 100);

    // VERIFY: ExecutingAction -> Completing
    ASSERT_EQ(ctx->state, InteractionState::Completing);

    // FINAL: Interaction completed and cleaned up
    ASSERT_FALSE(mgr->HasActiveInteraction(bot));

    // METRICS
    InteractionMetrics metrics = mgr->GetMetrics(InteractionType::Vendor);
    ASSERT_EQ(metrics.successCount, 1);
    ASSERT_GT(metrics.successRate, 99.0f);
}

Expected Outcome:

  • All state transitions occur in correct order
  • No states are skipped
  • Context is cleaned up on completion
  • Metrics reflect successful interaction

Test 1.2: Gossip Navigation - Multi-Step Interaction

TEST(InteractionManager_StateMachine, GossipNavigation_MultipleMenusBeforeVendor)
{
    // SETUP: Vendor requires gossip path: Menu1->Option0->Menu2->Option1->Vendor
    Player* bot = CreateTestBot();
    Creature* vendor = CreateTestVendorWithGossip();
    InteractionManager* mgr = InteractionManager::Instance();

    // EXECUTE
    mgr->StartInteraction(bot, vendor, InteractionType::Vendor);
    InteractionContext* ctx = mgr->GetInteractionContext(bot);

    // VERIFY: Gossip path detected
    ASSERT_TRUE(ctx->needsGossip);
    ASSERT_EQ(ctx->gossipPath.size(), 2);  // Two menu selections required

    // SIMULATE: Approaching -> Initiating -> WaitingGossip
    SimulateStateTransition(bot, InteractionState::WaitingGossip);

    // SIMULATE: Server sends gossip menu 1
    WorldPacket gossipPacket1 = CreateGossipMenuPacket(MENU_ID_1);
    mgr->HandleGossipMessage(bot, gossipPacket1);

    // VERIFY: WaitingGossip -> ProcessingMenu
    ASSERT_EQ(ctx->state, InteractionState::ProcessingMenu);

    // SIMULATE: Process menu selects option 0
    mgr->ProcessInteractionState(bot, 100);

    // VERIFY: Gossip path consumed first option
    ASSERT_EQ(ctx->gossipPath.size(), 1);  // One option remaining
    ASSERT_EQ(ctx->state, InteractionState::WaitingGossip);  // Back to waiting for menu 2

    // SIMULATE: Server sends gossip menu 2
    WorldPacket gossipPacket2 = CreateGossipMenuPacket(MENU_ID_2);
    mgr->HandleGossipMessage(bot, gossipPacket2);

    // SIMULATE: Process menu selects option 1 (final)
    mgr->ProcessInteractionState(bot, 100);

    // VERIFY: Gossip path fully consumed, ready for vendor
    ASSERT_EQ(ctx->gossipPath.size(), 0);
    ASSERT_EQ(ctx->state, InteractionState::ExecutingAction);
}

Expected Outcome:

  • Gossip path is correctly navigated
  • Each menu selection transitions to WaitingGossip for next menu
  • Final option leads to ExecutingAction
  • No gossip options are skipped or duplicated

Test 1.3: Timeout Handling - Interaction Expires

TEST(InteractionManager_StateMachine, Timeout_InteractionExpires)
{
    // SETUP
    Player* bot = CreateTestBot();
    Creature* vendor = CreateTestVendor();
    InteractionManager* mgr = InteractionManager::Instance();

    mgr->StartInteraction(bot, vendor, InteractionType::Vendor);
    InteractionContext* ctx = mgr->GetInteractionContext(bot);

    // CONFIGURE: Very short timeout
    ctx->timeout = std::chrono::milliseconds(100);

    // SIMULATE: Time passes beyond timeout
    std::this_thread::sleep_for(std::chrono::milliseconds(150));

    // EXECUTE: Update processes timeout
    mgr->ProcessInteractionState(bot, 150);

    // VERIFY: Interaction timed out and was removed
    ASSERT_FALSE(mgr->HasActiveInteraction(bot));

    // METRICS: Timeout recorded
    InteractionMetrics metrics = mgr->GetMetrics(InteractionType::Vendor);
    ASSERT_EQ(metrics.timeoutCount, 1);
    ASSERT_EQ(metrics.failureCount, 1);
}

Expected Outcome:

  • Timeout is detected by CheckTimeout()
  • Interaction is completed with failure
  • Context is cleaned up
  • Metrics reflect timeout

Test 1.4: Retry Logic - Transient Failure Recovery

TEST(InteractionManager_StateMachine, RetryLogic_RecoverFromTransientFailure)
{
    // SETUP
    Player* bot = CreateTestBot();
    Creature* vendor = CreateTestVendor();
    InteractionManager* mgr = InteractionManager::Instance();

    mgr->StartInteraction(bot, vendor, InteractionType::Vendor);
    InteractionContext* ctx = mgr->GetInteractionContext(bot);

    // SIMULATE: First attempt fails (too far away)
    SimulateStateTransition(bot, InteractionState::ExecutingAction);
    mgr->HandleInteractionError(bot, InteractionResult::TooFarAway);

    // VERIFY: Retry initiated
    ASSERT_EQ(ctx->attemptCount, 1);
    ASSERT_EQ(ctx->state, InteractionState::Approaching);  // Reset to beginning
    ASSERT_TRUE(mgr->HasActiveInteraction(bot));  // Not canceled

    // SIMULATE: Second attempt succeeds
    SimulateSuccessfulInteraction(bot, vendor);

    // VERIFY: Interaction completed successfully after retry
    ASSERT_FALSE(mgr->HasActiveInteraction(bot));

    // METRICS: Success after retry
    InteractionMetrics metrics = mgr->GetMetrics(InteractionType::Vendor);
    ASSERT_EQ(metrics.successCount, 1);
    ASSERT_EQ(metrics.totalAttempts, 1);  // Retries don't count as separate attempts
}

Expected Outcome:

  • First failure triggers retry logic
  • State machine resets to Approaching
  • Second attempt succeeds
  • Final result is success

Test 1.5: Max Retry Exhaustion - Permanent Failure

TEST(InteractionManager_StateMachine, MaxRetries_PermanentFailure)
{
    // SETUP
    Player* bot = CreateTestBot();
    Creature* vendor = CreateTestVendor();
    InteractionManager* mgr = InteractionManager::Instance();

    mgr->StartInteraction(bot, vendor, InteractionType::Vendor);
    InteractionContext* ctx = mgr->GetInteractionContext(bot);
    ctx->maxAttempts = 3;  // Allow 3 retries

    // SIMULATE: Fail 3 times
    for (int i = 0; i < 3; ++i)
    {
        SimulateStateTransition(bot, InteractionState::ExecutingAction);
        mgr->HandleInteractionError(bot, InteractionResult::InvalidTarget);
        ASSERT_EQ(ctx->attemptCount, i + 1);
    }

    // VERIFY: After 3rd failure, no more retries
    ASSERT_FALSE(mgr->HasActiveInteraction(bot));

    // METRICS: Failed after exhausting retries
    InteractionMetrics metrics = mgr->GetMetrics(InteractionType::Vendor);
    ASSERT_EQ(metrics.failureCount, 1);
    ASSERT_EQ(metrics.successCount, 0);
}

Expected Outcome:

  • Retry logic attempts up to maxAttempts
  • After final attempt, interaction fails permanently
  • Context is cleaned up
  • Metrics reflect permanent failure

2. Asynchronous Queue Integration Tests

Test 2.1: Queue Processing - FIFO with Priority

TEST(InteractionManager_Queue, QueueProcessing_PriorityOrder)
{
    // SETUP
    Player* bot = CreateTestBot();
    Creature* vendor1 = CreateTestVendor();
    Creature* vendor2 = CreateTestVendor();
    Creature* vendor3 = CreateTestVendor();
    InteractionManager* mgr = InteractionManager::Instance();

    // QUEUE: Three interactions with different priorities
    InteractionRequest req1;
    req1.botGuid = bot->GetGUID();
    req1.targetGuid = vendor1->GetGUID();
    req1.type = InteractionType::Vendor;
    req1.priority = 10;  // Low priority

    InteractionRequest req2;
    req2.botGuid = bot->GetGUID();
    req2.targetGuid = vendor2->GetGUID();
    req2.type = InteractionType::Vendor;
    req2.priority = 100;  // High priority

    InteractionRequest req3;
    req3.botGuid = bot->GetGUID();
    req3.targetGuid = vendor3->GetGUID();
    req3.type = InteractionType::Vendor;
    req3.priority = 50;  // Medium priority

    mgr->QueueInteraction(bot, req1);
    mgr->QueueInteraction(bot, req2);
    mgr->QueueInteraction(bot, req3);

    // VERIFY: Queue size
    ASSERT_EQ(mgr->GetQueuedInteractions(), 3);

    // EXECUTE: Process queue (maxConcurrentInteractions = 1)
    mgr->Update(0);

    // VERIFY: Highest priority processed first
    InteractionContext* ctx = mgr->GetInteractionContext(bot);
    ASSERT_EQ(ctx->targetGuid, vendor2->GetGUID());  // req2 (priority 100)

    // COMPLETE: First interaction
    SimulateSuccessfulInteraction(bot, vendor2);
    mgr->Update(0);

    // VERIFY: Medium priority processed second
    ctx = mgr->GetInteractionContext(bot);
    ASSERT_EQ(ctx->targetGuid, vendor3->GetGUID());  // req3 (priority 50)

    // COMPLETE: Second interaction
    SimulateSuccessfulInteraction(bot, vendor3);
    mgr->Update(0);

    // VERIFY: Low priority processed last
    ctx = mgr->GetInteractionContext(bot);
    ASSERT_EQ(ctx->targetGuid, vendor1->GetGUID());  // req1 (priority 10)
}

Expected Outcome:

  • Queue processes highest priority first
  • Concurrent interaction limit is respected
  • All queued interactions eventually process

Test 2.2: Rate Limiting - MIN_INTERACTION_DELAY Enforced

TEST(InteractionManager_Queue, RateLimiting_MinimumDelay)
{
    // SETUP
    Player* bot = CreateTestBot();
    Creature* vendor1 = CreateTestVendor();
    Creature* vendor2 = CreateTestVendor();
    InteractionManager* mgr = InteractionManager::Instance();

    // EXECUTE: Start first interaction
    auto start = std::chrono::steady_clock::now();
    mgr->StartInteraction(bot, vendor1, InteractionType::Vendor);
    SimulateSuccessfulInteraction(bot, vendor1);

    // QUEUE: Second interaction immediately after first
    InteractionRequest req;
    req.botGuid = bot->GetGUID();
    req.targetGuid = vendor2->GetGUID();
    req.type = InteractionType::Vendor;
    mgr->QueueInteraction(bot, req);

    // EXECUTE: Update immediately (should NOT process due to rate limit)
    mgr->Update(0);

    // VERIFY: Second interaction not started yet
    ASSERT_EQ(mgr->GetQueuedInteractions(), 1);
    ASSERT_FALSE(mgr->HasActiveInteraction(bot));

    // SIMULATE: Wait for MIN_INTERACTION_DELAY (100ms)
    std::this_thread::sleep_for(std::chrono::milliseconds(MIN_INTERACTION_DELAY + 10));

    // EXECUTE: Update after delay
    mgr->Update(MIN_INTERACTION_DELAY + 10);

    // VERIFY: Second interaction now started
    ASSERT_EQ(mgr->GetQueuedInteractions(), 0);
    ASSERT_TRUE(mgr->HasActiveInteraction(bot));

    auto end = std::chrono::steady_clock::now();
    auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);

    // VERIFY: At least MIN_INTERACTION_DELAY passed
    ASSERT_GE(elapsed.count(), MIN_INTERACTION_DELAY);
}

Expected Outcome:

  • Rate limiting prevents interactions within MIN_INTERACTION_DELAY
  • Queue holds interaction until delay passes
  • Delay is enforced per bot, not globally

Test 2.3: Concurrent Interaction Limit

TEST(InteractionManager_Queue, ConcurrentLimit_MaximumActive)
{
    // SETUP: 10 bots, maxConcurrentInteractions = 3
    std::vector<Player*> bots;
    std::vector<Creature*> vendors;
    InteractionManager* mgr = InteractionManager::Instance();
    mgr->m_config.maxConcurrentInteractions = 3;

    for (int i = 0; i < 10; ++i)
    {
        bots.push_back(CreateTestBot());
        vendors.push_back(CreateTestVendor());
    }

    // QUEUE: All 10 bots request interactions
    for (int i = 0; i < 10; ++i)
    {
        InteractionRequest req;
        req.botGuid = bots[i]->GetGUID();
        req.targetGuid = vendors[i]->GetGUID();
        req.type = InteractionType::Vendor;
        mgr->QueueInteraction(bots[i], req);
    }

    // VERIFY: Queue has 10 requests
    ASSERT_EQ(mgr->GetQueuedInteractions(), 10);

    // EXECUTE: Update processes queue
    mgr->Update(0);

    // VERIFY: Only 3 interactions active (concurrent limit)
    ASSERT_EQ(mgr->GetActiveInteractions(), 3);
    ASSERT_EQ(mgr->GetQueuedInteractions(), 7);  // 7 still queued

    // COMPLETE: 2 interactions
    SimulateSuccessfulInteraction(bots[0], vendors[0]);
    SimulateSuccessfulInteraction(bots[1], vendors[1]);

    // EXECUTE: Update processes more from queue
    mgr->Update(0);

    // VERIFY: 3 active again (2 completed, 2 new started)
    ASSERT_EQ(mgr->GetActiveInteractions(), 3);
    ASSERT_EQ(mgr->GetQueuedInteractions(), 5);
}

Expected Outcome:

  • Concurrent interaction limit is enforced
  • Queue processes additional interactions as slots open
  • No deadlocks or starvation

3. Handler Routing Integration Tests

Test 3.1: VendorInteraction Handler - BuyItem

TEST(InteractionManager_Handlers, VendorHandler_BuyItem)
{
    // SETUP
    Player* bot = CreateTestBot();
    bot->SetMoney(1000000);  // 100 gold
    Creature* vendor = CreateTestVendor();
    AddItemToVendor(vendor, ITEM_HEALTH_POTION, 10);  // 10 silver each
    InteractionManager* mgr = InteractionManager::Instance();

    // EXECUTE
    InteractionResult result = mgr->BuyItem(bot, vendor, ITEM_HEALTH_POTION, 5);

    // PROCESS: State machine to completion
    while (mgr->HasActiveInteraction(bot))
    {
        mgr->ProcessInteractionState(bot, 100);
        mgr->Update(100);
    }

    // VERIFY: Item purchased
    ASSERT_EQ(result, InteractionResult::Success);
    ASSERT_TRUE(bot->HasItemCount(ITEM_HEALTH_POTION, 5));
    ASSERT_EQ(bot->GetMoney(), 1000000 - (10 * 5));  // 50 silver spent

    // METRICS
    InteractionMetrics metrics = mgr->GetMetrics(InteractionType::Vendor);
    ASSERT_EQ(metrics.successCount, 1);
}

Expected Outcome:

  • VendorInteraction handler processes purchase
  • Item is added to bot inventory
  • Money is deducted correctly
  • Metrics reflect success

Test 3.2: TrainerInteraction Handler - LearnSpell

TEST(InteractionManager_Handlers, TrainerHandler_LearnSpell)
{
    // SETUP
    Player* bot = CreateTestBot();
    bot->SetLevel(10);
    bot->SetMoney(1000000);
    Creature* trainer = CreateTestTrainer();
    AddSpellToTrainer(trainer, SPELL_FIREBALL_RANK_2, 50);  // 50 silver cost, requires level 10
    InteractionManager* mgr = InteractionManager::Instance();

    // EXECUTE
    InteractionResult result = mgr->LearnOptimalSpells(bot, trainer);

    // PROCESS: State machine to completion
    while (mgr->HasActiveInteraction(bot))
    {
        mgr->ProcessInteractionState(bot, 100);
        mgr->Update(100);
    }

    // VERIFY: Spell learned
    ASSERT_EQ(result, InteractionResult::Success);
    ASSERT_TRUE(bot->HasSpell(SPELL_FIREBALL_RANK_2));
    ASSERT_EQ(bot->GetMoney(), 1000000 - 50);

    // METRICS
    InteractionMetrics metrics = mgr->GetMetrics(InteractionType::Trainer);
    ASSERT_EQ(metrics.successCount, 1);
}

Expected Outcome:

  • TrainerInteraction handler processes learning
  • Spell is added to bot spellbook
  • Training cost is deducted
  • Metrics reflect success

4. Performance and Scalability Tests

Test 4.1: 500-Bot Concurrent Interactions

TEST(InteractionManager_Performance, FiveHundredBots_ConcurrentLoad)
{
    // SETUP
    InteractionManager* mgr = InteractionManager::Instance();
    mgr->m_config.maxConcurrentInteractions = 100;

    std::vector<Player*> bots;
    std::vector<Creature*> vendors;

    for (int i = 0; i < 500; ++i)
    {
        bots.push_back(CreateTestBot());
        vendors.push_back(CreateTestVendor());
    }

    // MEASURE: Start time
    auto startTime = std::chrono::high_resolution_clock::now();
    uint64_t startCpu = GetProcessCpuTime();

    // EXECUTE: All 500 bots start interactions
    for (int i = 0; i < 500; ++i)
    {
        mgr->StartInteraction(bots[i], vendors[i], InteractionType::Vendor);
    }

    // PROCESS: All interactions to completion
    int updateCount = 0;
    while (mgr->GetActiveInteractions() > 0)
    {
        mgr->Update(100);
        for (auto bot : bots)
            mgr->ProcessInteractionState(bot, 100);
        ++updateCount;

        // SAFETY: Prevent infinite loop
        ASSERT_LT(updateCount, 10000);
    }

    // MEASURE: End time
    auto endTime = std::chrono::high_resolution_clock::now();
    uint64_t endCpu = GetProcessCpuTime();

    auto wallTime = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime);
    uint64_t cpuTime = endCpu - startCpu;
    float cpuPercent = (float)cpuTime / wallTime.count() / GetCpuCoreCount() * 100.0f;

    // VERIFY: Performance targets
    ASSERT_LT(wallTime.count(), 60000);  // All 500 complete in < 60 seconds
    ASSERT_LT(cpuPercent, 50.0f);        // CPU usage < 50% (target < 50% for 500 bots)

    // METRICS
    InteractionMetrics metrics = mgr->GetMetrics(InteractionType::Vendor);
    ASSERT_EQ(metrics.totalAttempts, 500);
    ASSERT_GT(metrics.successRate, 95.0f);  // > 95% success
    ASSERT_LT(metrics.avgDuration.count(), 200);  // < 200ms average

    // LOG RESULTS
    TC_LOG_INFO("test", "500-bot test completed:");
    TC_LOG_INFO("test", "  Wall time: {}ms", wallTime.count());
    TC_LOG_INFO("test", "  CPU usage: {:.2f}%", cpuPercent);
    TC_LOG_INFO("test", "  Success rate: {:.2f}%", metrics.successRate);
    TC_LOG_INFO("test", "  Avg duration: {}ms", metrics.avgDuration.count());
}

Expected Outcome:

  • All 500 interactions complete successfully
  • Wall time < 60 seconds
  • CPU usage < 50%
  • Success rate > 95%
  • Average interaction time < 200ms

Test 4.2: Memory Leak Detection - Long-Running Stress

TEST(InteractionManager_Performance, MemoryLeak_LongRunning)
{
    // SETUP
    InteractionManager* mgr = InteractionManager::Instance();
    Player* bot = CreateTestBot();
    Creature* vendor = CreateTestVendor();

    // MEASURE: Initial memory
    uint64_t initialMemory = GetProcessMemoryUsage();

    // EXECUTE: 10,000 interactions in a loop
    for (int i = 0; i < 10000; ++i)
    {
        mgr->StartInteraction(bot, vendor, InteractionType::Vendor);

        while (mgr->HasActiveInteraction(bot))
        {
            mgr->ProcessInteractionState(bot, 100);
            mgr->Update(100);
        }

        // VERIFY: No context leak
        ASSERT_FALSE(mgr->HasActiveInteraction(bot));
        ASSERT_EQ(mgr->GetActiveInteractions(), 0);
    }

    // MEASURE: Final memory
    uint64_t finalMemory = GetProcessMemoryUsage();
    uint64_t memoryGrowth = finalMemory - initialMemory;

    // VERIFY: Memory growth < 10MB (should be near zero for no leaks)
    ASSERT_LT(memoryGrowth, 10 * 1024 * 1024);

    TC_LOG_INFO("test", "Memory growth after 10,000 interactions: {} KB", memoryGrowth / 1024);
}

Expected Outcome:

  • No memory leaks after 10,000 interactions
  • Memory growth < 10 MB
  • All contexts properly cleaned up

Test 4.3: Thread Safety - Concurrent Access

TEST(InteractionManager_Performance, ThreadSafety_ConcurrentAccess)
{
    // SETUP
    InteractionManager* mgr = InteractionManager::Instance();
    std::vector<Player*> bots;
    std::vector<Creature*> vendors;

    for (int i = 0; i < 100; ++i)
    {
        bots.push_back(CreateTestBot());
        vendors.push_back(CreateTestVendor());
    }

    // EXECUTE: Multiple threads starting interactions concurrently
    std::vector<std::thread> threads;
    std::atomic<int> successCount{0};
    std::atomic<int> failureCount{0};

    for (int i = 0; i < 100; ++i)
    {
        threads.emplace_back([&, i]()
        {
            InteractionResult result = mgr->StartInteraction(bots[i], vendors[i], InteractionType::Vendor);
            if (result == InteractionResult::Pending)
                ++successCount;
            else
                ++failureCount;

            // Process to completion
            while (mgr->HasActiveInteraction(bots[i]))
            {
                mgr->ProcessInteractionState(bots[i], 100);
                std::this_thread::sleep_for(std::chrono::milliseconds(10));
            }
        });
    }

    // WAIT: All threads complete
    for (auto& thread : threads)
        thread.join();

    // VERIFY: No race conditions, all interactions processed
    ASSERT_EQ(successCount.load() + failureCount.load(), 100);
    ASSERT_EQ(mgr->GetActiveInteractions(), 0);  // All completed

    // VERIFY: No crashes, no deadlocks
    ASSERT_GT(successCount.load(), 90);  // At least 90% succeeded
}

Expected Outcome:

  • No race conditions or crashes
  • No deadlocks
  • All interactions eventually complete
  • Shared mutex protects concurrent access

5. TrinityCore API Compliance Tests

Test 5.1: Player API Usage - Movement and Facing

TEST(InteractionManager_TrinityCore, PlayerAPI_MovementAndFacing)
{
    // SETUP
    Player* bot = CreateTestBot(Position(0, 0, 0));
    Creature* vendor = CreateTestVendor(Position(10, 0, 0));  // 10 yards away
    InteractionManager* mgr = InteractionManager::Instance();

    // EXECUTE: Start interaction (requires movement)
    mgr->StartInteraction(bot, vendor, InteractionType::Vendor);
    InteractionContext* ctx = mgr->GetInteractionContext(bot);

    // VERIFY: State is Approaching
    ASSERT_EQ(ctx->state, InteractionState::Approaching);

    // VERIFY: TrinityCore movement API called
    ASSERT_TRUE(bot->GetMotionMaster()->HasMovementType(POINT_MOTION_TYPE));

    // SIMULATE: Bot reaches vendor
    bot->SetPosition(9, 0, 0, 0);  // 9 yards away, still out of range
    mgr->ProcessInteractionState(bot, 100);

    // VERIFY: Still approaching
    ASSERT_EQ(ctx->state, InteractionState::Approaching);

    // SIMULATE: Bot within range
    bot->SetPosition(4, 0, 0, 0);  // 4 yards away, in range
    mgr->ProcessInteractionState(bot, 100);

    // VERIFY: Transitioned to Initiating, facing set
    ASSERT_EQ(ctx->state, InteractionState::Initiating);
    ASSERT_FLOAT_EQ(bot->GetOrientation(), bot->GetAngle(vendor), 0.1f);  // Facing vendor
    ASSERT_EQ(bot->GetSelection(), vendor->GetGUID());  // Selection set
}

Expected Outcome:

  • TrinityCore movement APIs used correctly
  • Facing and selection APIs work as expected
  • No crashes or undefined behavior

Test 5.2: Creature API Usage - NPC Flags and Gossip

TEST(InteractionManager_TrinityCore, CreatureAPI_NPCFlagsAndGossip)
{
    // SETUP
    Creature* vendor = CreateTestCreature();
    vendor->SetNpcFlags(UNIT_NPC_FLAG_VENDOR | UNIT_NPC_FLAG_REPAIR);
    InteractionManager* mgr = InteractionManager::Instance();

    // EXECUTE: Detect NPC type
    InteractionType type = mgr->DetectNPCType(vendor);

    // VERIFY: Correctly detected as vendor
    ASSERT_EQ(type, InteractionType::Vendor);

    // VERIFY: NPC type cached for performance
    InteractionType cachedType = mgr->DetectNPCType(vendor);
    ASSERT_EQ(cachedType, InteractionType::Vendor);

    // SETUP: Add trainer flag
    vendor->SetNpcFlags(UNIT_NPC_FLAG_VENDOR | UNIT_NPC_FLAG_TRAINER);

    // CLEAR CACHE: Force re-detection
    mgr->m_npcTypeCache.clear();

    // EXECUTE: Detect again
    InteractionType newType = mgr->DetectNPCType(vendor);

    // VERIFY: Trainer takes priority over vendor
    ASSERT_EQ(newType, InteractionType::Trainer);
}

Expected Outcome:

  • NPC flags are read correctly via TrinityCore API
  • Flag priority is correct
  • Caching works properly

Test 5.3: ObjectAccessor API - Guid Resolution

TEST(InteractionManager_TrinityCore, ObjectAccessorAPI_GuidResolution)
{
    // SETUP
    Player* bot = CreateTestBot();
    Creature* vendor = CreateTestVendor();
    InteractionManager* mgr = InteractionManager::Instance();

    // EXECUTE: Start interaction
    mgr->StartInteraction(bot, vendor, InteractionType::Vendor);

    // VERIFY: Interaction context stores GUIDs
    InteractionContext* ctx = mgr->GetInteractionContext(bot);
    ASSERT_EQ(ctx->botGuid, bot->GetGUID());
    ASSERT_EQ(ctx->targetGuid, vendor->GetGUID());

    // SIMULATE: Vendor despawns (removed from world)
    RemoveCreatureFromWorld(vendor);

    // EXECUTE: Update tries to resolve GUID
    mgr->Update(100);

    // VERIFY: Interaction canceled due to invalid target
    ASSERT_FALSE(mgr->HasActiveInteraction(bot));

    // METRICS: Failure due to invalid target
    InteractionMetrics metrics = mgr->GetMetrics(InteractionType::Vendor);
    ASSERT_GT(metrics.failureCount, 0);
}

Expected Outcome:

  • ObjectAccessor correctly resolves GUIDs
  • Invalid GUIDs are handled gracefully
  • Interaction fails safely when target despawns

6. Edge Case and Error Handling Tests

Test 6.1: Bot Logs Out During Interaction

TEST(InteractionManager_EdgeCases, BotLogout_DuringInteraction)
{
    // SETUP
    Player* bot = CreateTestBot();
    Creature* vendor = CreateTestVendor();
    InteractionManager* mgr = InteractionManager::Instance();

    // EXECUTE: Start interaction
    mgr->StartInteraction(bot, vendor, InteractionType::Vendor);
    ASSERT_TRUE(mgr->HasActiveInteraction(bot));

    // SIMULATE: Bot logs out
    LogoutPlayer(bot);

    // EXECUTE: Update processes logout
    mgr->Update(100);

    // VERIFY: Interaction cleaned up (bot GUID no longer resolves)
    ASSERT_FALSE(mgr->HasActiveInteraction(bot));
}

Expected Outcome:

  • Interaction is cleaned up when bot logs out
  • No dangling contexts
  • No crashes

Test 6.2: Vendor Moves Out of Range During Interaction

TEST(InteractionManager_EdgeCases, VendorMoves_OutOfRange)
{
    // SETUP
    Player* bot = CreateTestBot(Position(0, 0, 0));
    Creature* vendor = CreateTestVendor(Position(4, 0, 0));  // In range
    InteractionManager* mgr = InteractionManager::Instance();

    // EXECUTE: Start interaction
    mgr->StartInteraction(bot, vendor, InteractionType::Vendor);

    // SIMULATE: Advance to ExecutingAction state
    SimulateStateTransition(bot, InteractionState::ExecutingAction);

    // SIMULATE: Vendor moves far away
    vendor->SetPosition(100, 0, 0, 0);  // Out of range

    // EXECUTE: Update detects out of range
    mgr->ProcessInteractionState(bot, 100);

    // VERIFY: Interaction fails or retries with movement
    InteractionContext* ctx = mgr->GetInteractionContext(bot);
    // Either failed or moved back to Approaching for retry
    ASSERT_TRUE(ctx->state == InteractionState::Failed ||
                ctx->state == InteractionState::Approaching);
}

Expected Outcome:

  • Out-of-range condition is detected
  • Interaction fails or retries
  • No infinite loops

Test Execution Strategy

Phase 1: Unit Tests (Isolated Components)

  • State machine state transitions
  • Context lifecycle
  • Metrics calculation
  • Handler routing logic

Phase 2: Integration Tests (Component Interactions)

  • State machine + queue
  • State machine + handlers
  • Queue + rate limiting
  • Metrics + performance

Phase 3: Performance Tests (Scalability)

  • 100-bot concurrent load
  • 500-bot concurrent load
  • Memory leak detection
  • Thread safety validation

Phase 4: TrinityCore Compliance Tests

  • All TrinityCore API usage
  • Guid resolution
  • Packet handling
  • Object lifecycle

Phase 5: Production Validation

  • Run with real bots in test environment
  • Monitor metrics for 24 hours
  • Identify performance bottlenecks
  • Tune configuration based on results

Success Criteria

Functionality

  • All state transitions work correctly
  • Gossip navigation handles multi-step paths
  • Retry logic recovers from transient failures
  • Timeout handling prevents infinite loops
  • Queue processes by priority
  • Rate limiting enforced correctly
  • All handlers route properly

Performance

  • 500 bots complete interactions in < 60 seconds
  • CPU usage < 50% for 500 bots
  • Average interaction time < 200ms
  • Success rate > 95%
  • Memory growth < 10 MB over 10,000 interactions
  • No crashes or deadlocks under concurrent load

TrinityCore Compliance

  • All TrinityCore APIs used correctly
  • No core file modifications
  • No memory leaks
  • Thread-safe concurrent access
  • Proper GUID resolution
  • Graceful handling of object despawns

Continuous Integration

Automated Test Runs

  • Daily: Full test suite on dev branch
  • Pre-Commit: State machine and handler tests
  • Pre-Release: Performance and scalability tests

Metrics Dashboard

  • Success rate trend over time
  • Average interaction duration trend
  • CPU/memory usage trends
  • Failure type distribution

Document Status: Complete Implementation Priority: High Next Steps: Implement test framework, run Phase 1 tests, iterate based on results