local function read(path) local f = assert(io.open(path, "r"), "cannot open " .. path) local c = f:read("*a") f:close() return c end local function has(content, needle) return string.find(content, needle, 1, true) ~= nil end describe("QuestieLearner data source mode", function() it("adds a mode selector and explicit fallback options in the database tab", function() local dbOptions = read("Modules/Options/DatabaseTab/QuestieOptionsDatabase.lua") local advanced = read("Modules/Options/AdvancedTab/QuestieOptionsAdvanced.lua") assert.is_true(has(dbOptions, "Data Source Mode")) assert.is_true(has(dbOptions, "auto = l10n(\"Auto (current behavior)\")")) assert.is_true(has(dbOptions, "learner = l10n(\"Learner Only\")")) assert.is_true(has(dbOptions, "static = l10n(\"Static Only\")")) assert.is_true(has(dbOptions, "none = l10n(\"Neither (base DB only)\")")) assert.is_true(has(dbOptions, "local function GetLearnerSelectedMode()")) assert.is_true(has(dbOptions, "get = function() return GetLearnerSelectedMode() end")) assert.is_true(has(dbOptions, "Runtime mode:")) assert.is_true(has(advanced, "local function RefreshLearnerRuntime()")) assert.is_true(has(advanced, "RefreshLearnerRuntime()")) end) it("defaults the learner mode to auto and exposes the live refresh hook", function() local learner = read("Modules/QuestieLearner.lua") local defaults = read("Modules/Options/QuestieOptionsDefaults.lua") assert.is_true(has(defaults, "dataSourceMode = \"auto\"")) assert.is_true(has(learner, "function QuestieLearner:GetDataSourceMode()")) assert.is_true(has(learner, "function QuestieLearner:IsLearnerLiveEnabled()")) assert.is_true(has(learner, "function QuestieLearner:ApplyDataSourceMode()")) assert.is_true(has(learner, "function QuestieLearner:RefreshLiveState()")) end) it("gates static suppression and tooltip fallback on the selected mode", function() local quest = read("Modules/Quest/QuestieQuest.lua") local priv = read("Modules/Quest/QuestieQuestPrivates.lua") local tip = read("Modules/Tooltips/Tooltip.lua") assert.is_true(has(quest, "dataSourceMode == \"auto\" or dataSourceMode == \"learner\"")) assert.is_true(has(priv, "dataSourceMode == \"none\"")) assert.is_true(has(tip, "mode ~= \"static\" and mode ~= \"none\"")) end) it("exposes learner tooltip controls and tooltip resize support", function() local general = read("Modules/Options/GeneralTab/QuestieOptionsGeneral.lua") local defaults = read("Modules/Options/QuestieOptionsDefaults.lua") local advanced = read("Modules/Options/AdvancedTab/QuestieOptionsAdvanced.lua") local tip = read("Modules/Tooltips/Tooltip.lua") local handler = read("Modules/Tooltips/TooltipHandler.lua") local learner = read("Modules/QuestieLearner.lua") assert.is_true(has(defaults, "learnerTooltips = true")) assert.is_true(has(defaults, "learnerTooltipShowSpawn = true")) assert.is_true(has(defaults, "learnerTooltipShowConfidence = true")) assert.is_true(has(defaults, "learnerTooltipShowTotalSpawns = true")) assert.is_true(has(defaults, "learnerTooltipAutoResize = true")) assert.is_true(has(defaults, "learnerTooltipUseSecondary = false")) assert.is_true(has(general, "Learner Tooltips")) assert.is_true(has(general, "Enable learner tooltips")) assert.is_true(has(general, "Show total spawns learned")) assert.is_true(has(general, "Use secondary learner tooltip")) assert.is_true(has(tip, "function QuestieTooltips:ResizeTooltip(tooltip)")) assert.is_true(has(handler, "QuestieTooltips:ResizeTooltip(GameTooltip)")) assert.is_true(has(handler, "QuestieTooltips:ResizeTooltip(self)")) assert.is_true(has(learner, "learnerTooltipShowSpawn")) assert.is_true(has(learner, "_CountLearnedNpcSpawns(entry)")) assert.is_true(has(learner, "_ShowLearnerTooltipFrame(GameTooltip, rendered)")) assert.is_true(has(advanced, "QuestieOptionsUtils:Delay(0.05, QuestieQuest.SmoothReset")) assert.is_true(has(advanced, "QuestieOptionsUtils:Delay(0.05, QuestieOptions.ClusterRedraw")) end) it("allows learner mode to draw pins from a single learned spawn when needed", function() local priv = read("Modules/Quest/QuestieQuestPrivates.lua") assert.is_true(has(priv, "local staticHasSpawns = spawns and next(spawns) ~= nil")) assert.is_true(has(priv, "local canUseLearnerSpawns = dataSourceMode == \"learner\"")) assert.is_true(has(priv, "or not staticHasSpawns")) end) it("keeps learner-only pin builders off the static DB lookup path", function() local priv = read("Modules/Quest/QuestieQuestPrivates.lua") assert.is_true(has(priv, "local npcData = QuestieDB:GetNPC(npcId)")) assert.is_true(has(priv, "local name = npcData and npcData.name or nil")) assert.is_true(has(priv, "local spawns = npcData and npcData.spawns or {}")) assert.is_true(has(priv, "local rank = npcData and npcData.rank")) assert.is_true(has(priv, "local objectData = QuestieDB:GetObject(objectId)")) assert.is_true(has(priv, "local name = objectData and objectData.name or nil")) assert.is_true(has(priv, "local spawns = objectData and objectData.spawns or {}")) end) it("maps object objectives from learner object captures before refresh", function() local learner = read("Modules/QuestieLearner.lua") assert.is_true(has(learner, "_Learner.recentObjects = _Learner.recentObjects or {}")) assert.is_true(has(learner, "function QuestieLearner:LearnQuestObjectiveObject(questId, objectId, objText, objectiveIndex)")) assert.is_true(has(learner, "objType == \"object\"")) assert.is_true(has(learner, "self:LearnQuestObjectiveObject(questId, objectId, objText, j)")) assert.is_true(has(learner, "_Learner.recentObjects[objectId]")) end) end) describe("QuestieLearner missing base DB fallback", function() local QuestieLearner before_each(function() dofile("Tests/wow_api_mock.lua") Questie.dbLearner.global.settings.enabled = false Questie.dbLearner.global.settings.dataSourceMode = "static" QuestieDB.baseDatabaseMissing = true QuestieDB.IsBaseDatabaseMissing = function() return true end QuestieLearner = dofile("Modules/QuestieLearner.lua") end) it("forces learner mode and live recording when the base DB is missing", function() assert.equals("learner", QuestieLearner:GetDataSourceMode()) assert.is_true(QuestieLearner:IsEnabled()) end) end) describe("QuestieLearner learner mode activation", function() local QuestieLearner before_each(function() dofile("Tests/wow_api_mock.lua") Questie.dbLearner.global.settings.enabled = false Questie.dbLearner.global.settings.dataSourceMode = "learner" QuestieDB.baseDatabaseMissing = false QuestieDB.IsBaseDatabaseMissing = function() return false end QuestieLearner = dofile("Modules/QuestieLearner.lua") end) it("re-enables learner recording when learner mode is applied", function() QuestieLearner:ApplyDataSourceMode() assert.is_true(Questie.dbLearner.global.settings.enabled) assert.is_true(QuestieLearner:IsEnabled()) end) it("refreshes live learner caches and quest pins when runtime settings change", function() local clearCount = 0 local resetCount = 0 QuestieDB.ClearModeCaches = function() clearCount = clearCount + 1 end QuestieQuest.SmoothReset = function() resetCount = resetCount + 1 end QuestieLearner:RefreshLiveState() assert.is_true(clearCount >= 1) assert.is_true(resetCount >= 1) end) end) describe("QuestieDB learner mode merges learner overrides", function() before_each(function() dofile("Tests/wow_api_mock.lua") dofile("Database/QuestieDB.lua") dofile("Database/npcDB.lua") Questie.dbLearner.global.settings.enabled = true Questie.dbLearner.global.settings.dataSourceMode = "learner" Questie.dbLearner.global.npcs = { [7001] = { [1] = "Learner NPC", }, } QuestieDB.npcDataOverrides = { [7001] = { [1] = "Learner NPC", [7] = { [44] = { { 12.5, 34.5 }, }, }, }, } QuestieDB.private = QuestieDB.private or {} QuestieDB.private.npcCache = {} end) it("prefers the learner override spawn table when raw learner data is incomplete", function() local npc = QuestieDB:GetNPC(7001) assert.is_table(npc) assert.is_table(npc.spawns) assert.is_table(npc.spawns[44]) assert.equals(1, table.getn(npc.spawns[44])) assert.equals(12.5, npc.spawns[44][1][1]) assert.equals(34.5, npc.spawns[44][1][2]) end) end) describe("QuestieDB quest build guards missing objective tables", function() local originalImport local originalQueryItemSingle before_each(function() dofile("Tests/wow_api_mock.lua") local questieLib = { GetTbcLevel = function() return 1, 1 end, } local questieCorrections = { hiddenQuests = {}, killCreditObjectiveFirst = {}, } originalImport = QuestieLoader.ImportModule QuestieLoader.ImportModule = function(self, name) if name == "QuestieLib" then return questieLib end if name == "QuestieCorrections" then return questieCorrections end return originalImport(self, name) end dofile("Database/QuestieDB.lua") dofile("Database/questDB.lua") Questie.dbLearner.global.settings.enabled = true Questie.dbLearner.global.settings.dataSourceMode = "learner" QuestieDB.private.questCache = {} QuestieDB.questDataOverrides = {} originalQueryItemSingle = QuestieDB.QueryItemSingle QuestieDB.QueryItemSingle = function(itemId, field) if field == "name" then return "Test Item " .. tostring(itemId) end return originalQueryItemSingle(itemId, field) end end) after_each(function() if originalQueryItemSingle then QuestieDB.QueryItemSingle = originalQueryItemSingle end if originalImport then QuestieLoader.ImportModule = originalImport end end) it("does not crash when required source items exist but objectives are missing", function() local questId = 900001 QuestieDB.questDataOverrides[questId] = { [1] = "Source Item Quest", [21] = { [1] = 20482, }, } local ok, quest = pcall(function() return QuestieDB:GetQuest(questId) end) assert.is_true(ok) assert.is_table(quest) assert.is_table(quest.SpecialObjectives) assert.is_table(quest.SpecialObjectives[20482]) assert.equals("Test Item 20482", quest.SpecialObjectives[20482].Description) end) end) describe("QuestieDB missing quest logs are deduplicated", function() local originalImport local originalDebug local originalQueryQuest before_each(function() dofile("Tests/wow_api_mock.lua") local questieLib = { GetTbcLevel = function() return 1, 1 end, } local questieCorrections = { hiddenQuests = {}, killCreditObjectiveFirst = {}, } originalImport = QuestieLoader.ImportModule QuestieLoader.ImportModule = function(self, name) if name == "QuestieLib" then return questieLib end if name == "QuestieCorrections" then return questieCorrections end return originalImport(self, name) end dofile("Database/QuestieDB.lua") dofile("Database/questDB.lua") QuestieDB.private.questCache = {} QuestieDB.private.missingQuestLog = {} originalDebug = Questie.Debug Questie.Debug = function(self, level, ...) _G._questie_debug_calls = (_G._questie_debug_calls or 0) + 1 end originalQueryQuest = QuestieDB.QueryQuest QuestieDB.QueryQuest = function() return nil end end) after_each(function() if originalQueryQuest then QuestieDB.QueryQuest = originalQueryQuest end if originalDebug then Questie.Debug = originalDebug end if originalImport then QuestieLoader.ImportModule = originalImport end _G._questie_debug_calls = nil end) it("logs a missing quest only once per quest id", function() local questId = 900002 assert.is_nil(QuestieDB:GetQuest(questId)) assert.is_nil(QuestieDB:GetQuest(questId)) assert.equals(1, _G._questie_debug_calls) end) end) describe("QuestieLearner quest accept resolution", function() local QuestieLearner local originalGetNumQuestLogEntries local originalGetQuestLogSelection local originalGetQuestLogTitle local originalGetQuestIDFromLogIndex local originalGetQuestLogIndexByID local originalLearnQuest local originalUnitGUID before_each(function() dofile("Tests/wow_api_mock.lua") Questie.dbLearner.global.settings.enabled = true Questie.dbLearner.global.settings.dataSourceMode = "learner" originalGetNumQuestLogEntries = _G.GetNumQuestLogEntries originalGetQuestLogSelection = QuestieCompat.GetQuestLogSelection originalGetQuestLogTitle = QuestieCompat.GetQuestLogTitle originalGetQuestIDFromLogIndex = QuestieCompat.GetQuestIDFromLogIndex originalGetQuestLogIndexByID = QuestieCompat.GetQuestLogIndexByID originalUnitGUID = _G.UnitGUID _G.GetNumQuestLogEntries = function() return 1 end _G.UnitGUID = function() return nil end QuestieCompat.GetQuestLogSelection = function() return 1 end QuestieCompat.GetQuestLogTitle = function(index) if index == 1 then return "Real Quest", 10, nil, false, nil, nil, nil, 4321 end return nil end QuestieCompat.GetQuestIDFromLogIndex = function(index) if index == 1 then return 4321 end return nil end QuestieCompat.GetQuestLogIndexByID = function(questId) if questId == 4321 then return 1 end return nil end QuestieLearner = dofile("Modules/QuestieLearner.lua") originalLearnQuest = QuestieLearner.LearnQuest end) after_each(function() QuestieLearner.LearnQuest = originalLearnQuest _G.GetNumQuestLogEntries = originalGetNumQuestLogEntries _G.UnitGUID = originalUnitGUID QuestieCompat.GetQuestLogSelection = originalGetQuestLogSelection QuestieCompat.GetQuestLogTitle = originalGetQuestLogTitle QuestieCompat.GetQuestIDFromLogIndex = originalGetQuestIDFromLogIndex QuestieCompat.GetQuestLogIndexByID = originalGetQuestLogIndexByID end) it("uses a real quest log entry instead of raw accepted event args", function() local capturedQuestId = nil QuestieLearner.LearnQuest = function(self, questId, data) capturedQuestId = questId end QuestieLearner:OnQuestAccepted(615514513, nil) assert.equals(4321, capturedQuestId) end) it("refuses impossible quest ids when they do not resolve to the quest log", function() local capturedQuestId = nil QuestieCompat.GetQuestLogSelection = function() return nil end QuestieCompat.GetQuestLogTitle = function() return nil end QuestieCompat.GetQuestIDFromLogIndex = function() return nil end QuestieCompat.GetQuestLogIndexByID = function() return nil end QuestieLearner.LearnQuest = function(self, questId, data) capturedQuestId = questId end QuestieLearner:OnQuestAccepted(615514513, nil) assert.is_nil(capturedQuestId) end) end) describe("QuestieLearner quest turn-in resolution", function() local QuestieLearner local originalLearnQuest local originalGetQuestID local originalGetRewardText local originalUnitGUID before_each(function() dofile("Tests/wow_api_mock.lua") originalGetQuestID = _G.GetQuestID originalGetRewardText = _G.GetRewardText originalUnitGUID = _G.UnitGUID Questie.dbLearner.global.settings.enabled = true Questie.dbLearner.global.settings.dataSourceMode = "learner" _G.GetQuestID = function() return 4321 end _G.GetRewardText = function() return "Reward text" end _G.UnitGUID = function() return nil end QuestieLearner = dofile("Modules/QuestieLearner.lua") originalLearnQuest = QuestieLearner.LearnQuest end) after_each(function() QuestieLearner.LearnQuest = originalLearnQuest _G.GetQuestID = originalGetQuestID _G.GetRewardText = originalGetRewardText _G.UnitGUID = originalUnitGUID end) it("rejects malformed turn-in quest ids unless they resolve to a recent completion", function() local capturedQuestId = nil QuestieLearner.LearnQuest = function(self, questId, data) capturedQuestId = questId end QuestieLearner:OnQuestTurnedIn(545915281, nil, nil) assert.is_nil(capturedQuestId) end) it("uses the recent quest-complete cache when the raw turn-in quest id is malformed", function() local capturedQuestId = nil QuestieLearner.LearnQuest = function(self, questId, data) capturedQuestId = questId end QuestieLearner:OnQuestComplete() QuestieLearner:OnQuestTurnedIn(545915281, nil, nil) assert.equals(4321, capturedQuestId) end) end) describe("QuestieLearner GUID and loot learning", function() local QuestieLearner local originalGetNPC local originalGetItemInfo local originalLearnItem local originalLearnItemDrop local originalGetLootSourceInfo local originalGetNumLootItems local originalGetLootSlotInfo local originalGetLootSlotLink before_each(function() dofile("Tests/wow_api_mock.lua") Questie.dbLearner.global.settings.enabled = true Questie.dbLearner.global.settings.dataSourceMode = "learner" Questie.dbLearner.global.settings.learnItems = true Questie.dbLearner.global.settings.learnNpcs = true QuestieDB.npcData = { [15297] = { [1] = "Arcanist Helion" }, } originalGetNPC = QuestieDB.GetNPC QuestieDB.GetNPC = function(self, id) if id == 168 then return { name = "Something Else" } end return originalGetNPC and originalGetNPC(self, id) or nil end originalGetItemInfo = _G.GetItemInfo _G.GetItemInfo = function(link) if link == "item:20470" then return "Quest Token", nil, nil, 1, 1, 3, 1, nil, nil, nil, nil, 1, 0 end return nil end originalGetLootSourceInfo = _G.GetLootSourceInfo _G.GetLootSourceInfo = function() return "Creature-0-0-0-0-15297-0000000000", 1 end originalGetNumLootItems = _G.GetNumLootItems _G.GetNumLootItems = function() return 1 end originalGetLootSlotInfo = _G.GetLootSlotInfo _G.GetLootSlotInfo = function() return nil, "Quest Token", nil, nil, 1 end originalGetLootSlotLink = _G.GetLootSlotLink _G.GetLootSlotLink = function() return "item:20470" end dofile("Modules/QuestieLearner.lua") QuestieLearner = _G.QuestieLearner originalLearnItem = QuestieLearner.LearnItem originalLearnItemDrop = QuestieLearner.LearnItemDrop end) after_each(function() QuestieDB.GetNPC = originalGetNPC _G.GetItemInfo = originalGetItemInfo _G.GetLootSourceInfo = originalGetLootSourceInfo _G.GetNumLootItems = originalGetNumLootItems _G.GetLootSlotInfo = originalGetLootSlotInfo _G.GetLootSlotLink = originalGetLootSlotLink if QuestieLearner then QuestieLearner.LearnItem = originalLearnItem QuestieLearner.LearnItemDrop = originalLearnItemDrop end end) it("prefers the exact NPC name over a mismatched GUID entry id", function() local resolvedId, unitType = QuestieLearner:ResolveNpcIdFromGuidAndName("Creature-0-0-0-0-168-0000000000", "Arcanist Helion") assert.equals(15297, resolvedId) assert.equals("Creature", unitType) end) it("ignores non-quest loot items and only learns quest-item drops", function() local learnedItemId = nil local dropItemId = nil local dropNpcId = nil QuestieLearner.LearnItem = function(self, itemId, name, itemLevel, requiredLevel, itemClassId, itemSubClassId) if itemClassId == 12 then learnedItemId = itemId return true end return false end QuestieLearner.LearnItemDrop = function(self, itemId, npcId) dropItemId = itemId dropNpcId = npcId end local originalGetItemInfo = _G.GetItemInfo _G.GetItemInfo = function(link) if link == "item:20470" then return "Quest Token", nil, nil, 1, 1, nil, nil, nil, nil, nil, nil, 1, 0 end return originalGetItemInfo(link) end QuestieLearner:OnLootOpened() assert.is_nil(learnedItemId) assert.is_nil(dropItemId) assert.is_nil(dropNpcId) _G.GetItemInfo = originalGetItemInfo end) end) describe("QuestieDB learner source fallback", function() before_each(function() dofile("Tests/wow_api_mock.lua") dofile("Database/QuestieDB.lua") dofile("Database/npcDB.lua") dofile("Database/objectDB.lua") dofile("Database/questDB.lua") dofile("Database/itemDB.lua") Questie.dbLearner.global.settings.enabled = true Questie.dbLearner.global.settings.dataSourceMode = "learner" Questie.dbLearner.global.npcs = { [9001] = { [1] = "Learner Whelp", [7] = { [44] = { { 12.3, 45.6 }, }, }, [8] = { [44] = { { 13.3, 46.6 }, }, }, [9] = 44, }, } Questie.dbLearner.global.objects = { [9002] = { [1] = "Learner Cache", [4] = { [44] = { { 11.1, 22.2 }, }, }, [5] = 44, }, } QuestieDB.QueryNPC = function() return nil end QuestieDB.QueryObject = function() return nil end QuestieDB.QueryQuest = function() return nil end QuestieDB.QueryItem = function() return nil end QuestieDB.private.npcCache = {} QuestieDB.private.objectCache = {} QuestieDB.private.questCache = {} QuestieDB.private.itemCache = {} end) it("returns learner NPC data when static queries are unavailable", function() local npc = QuestieDB:GetNPC(9001) assert.is_table(npc) assert.equals("Learner Whelp", npc.name) assert.is_table(npc.spawns) assert.is_table(npc.waypoints) assert.equals(44, npc.zoneID) end) it("turns learner kill evidence into NPC spawn coordinates immediately in learner mode", function() Questie.dbLearner.global.npcs[9003] = { [1] = "Learner Kill", [8] = { [101] = { zoneId = 44, x = 18.5, y = 27.25, }, }, } local npc = QuestieDB:GetNPC(9003) assert.is_table(npc) assert.is_table(npc.spawns) assert.is_table(npc.spawns[44]) assert.equals(1, #npc.spawns[44]) assert.equals(18.5, npc.spawns[44][1][1]) assert.equals(27.25, npc.spawns[44][1][2]) end) it("collapses many nearby kills (distinct GUIDs) into one spawn pin", function() -- Five kills of respawns at the same spot: distinct GUID keys, slightly -- drifting player coords. This must render as ONE pin, not five. Questie.dbLearner.global.npcs[9005] = { [1] = "Respawning Boar", [8] = { [201] = { zoneId = 44, x = 50.0, y = 50.0 }, [202] = { zoneId = 44, x = 50.4, y = 50.3 }, [203] = { zoneId = 44, x = 49.7, y = 50.6 }, [204] = { zoneId = 44, x = 50.9, y = 49.8 }, [205] = { zoneId = 44, x = 50.2, y = 50.1 }, }, } local npc = QuestieDB:GetNPC(9005) assert.is_table(npc) assert.is_table(npc.spawns[44]) assert.equals(1, #npc.spawns[44]) end) it("keeps genuinely separate spawn locations as distinct pins", function() Questie.dbLearner.global.npcs[9006] = { [1] = "Field Boars", [8] = { [301] = { zoneId = 44, x = 20.0, y = 20.0 }, [302] = { zoneId = 44, x = 20.3, y = 20.2 }, -- same spot as 301 [303] = { zoneId = 44, x = 70.0, y = 65.0 }, -- far corner }, } local npc = QuestieDB:GetNPC(9006) assert.is_table(npc) assert.is_table(npc.spawns[44]) assert.equals(2, #npc.spawns[44]) end) it("honors the spawn dedup radius knob (0 disables proximity merge)", function() Questie.dbLearner.global.settings.spawnDedupRadius = 0 Questie.dbLearner.global.npcs[9007] = { [1] = "Drifting Kills", [8] = { [401] = { zoneId = 44, x = 50.0, y = 50.0 }, [402] = { zoneId = 44, x = 50.4, y = 50.3 }, [403] = { zoneId = 44, x = 49.7, y = 50.6 }, [404] = { zoneId = 44, x = 50.9, y = 49.8 }, [405] = { zoneId = 44, x = 50.2, y = 50.1 }, }, } local npc = QuestieDB:GetNPC(9007) assert.is_table(npc) assert.is_table(npc.spawns[44]) -- With merging off, each distinct kill coordinate stays its own pin. assert.equals(5, #npc.spawns[44]) end) it("widens merging when the dedup radius is increased", function() Questie.dbLearner.global.settings.spawnDedupRadius = 12 Questie.dbLearner.global.npcs[9008] = { [1] = "Loose Cluster", [8] = { [501] = { zoneId = 44, x = 40.0, y = 40.0 }, [502] = { zoneId = 44, x = 48.0, y = 46.0 }, -- ~10 away: merges at radius 12 }, } local npc = QuestieDB:GetNPC(9008) assert.is_table(npc) assert.is_table(npc.spawns[44]) assert.equals(1, #npc.spawns[44]) end) it("returns learner object data when static queries are unavailable", function() local obj = QuestieDB:GetObject(9002) assert.is_table(obj) assert.equals("Learner Cache", obj.name) assert.is_table(obj.spawns) assert.equals(44, obj.zoneID) end) end) describe("QuestieDB partial base DB missing", function() before_each(function() dofile("Tests/wow_api_mock.lua") dofile("Database/QuestieDB.lua") end) it("does not report the base DB missing when only one store failed", function() QuestieDB.baseDatabaseMissing = true QuestieDB.baseDatabaseMissingKeys = { itemData = true } assert.is_false(QuestieDB:IsBaseDatabaseMissing()) assert.is_true(QuestieDB:IsStoreMissing("itemData")) assert.is_false(QuestieDB:IsStoreMissing("npcData")) end) it("reports the base DB missing only when every core store failed", function() QuestieDB.baseDatabaseMissing = true QuestieDB.baseDatabaseMissingKeys = { npcData = true, objectData = true, questData = true, itemData = true, } assert.is_true(QuestieDB:IsBaseDatabaseMissing()) end) it("honors the static selection for a present store even when another is missing", function() QuestieDB.baseDatabaseMissing = true QuestieDB.baseDatabaseMissingKeys = { itemData = true } Questie.dbLearner.global.settings.dataSourceMode = "static" Questie.dbLearner.global.npcs = { [9100] = { [1] = "Should Not Win", [7] = { [44] = { { 1, 2 } } }, [9] = 44 }, } QuestieDB.private.npcCache = {} local queried = false QuestieDB.QueryNPC = function() queried = true; return nil end pcall(function() QuestieDB:GetNPC(9100) end) assert.is_true(queried) end) end) describe("QuestieDB mode cohesion", function() before_each(function() dofile("Tests/wow_api_mock.lua") dofile("Database/QuestieDB.lua") dofile("Database/npcDB.lua") Questie.dbLearner.global.settings.enabled = true Questie.dbLearner.global.npcs = { [9200] = { [1] = "Learner Only NPC", [7] = { [44] = { { 5, 6 } } }, [9] = 44 }, } QuestieDB.QueryNPC = function() return nil end QuestieDB.baseDatabaseMissing = false QuestieDB.baseDatabaseMissingKeys = {} QuestieDB.private.npcCache = {} end) it("does NOT leak learner data into static mode when the store is present", function() Questie.dbLearner.global.settings.dataSourceMode = "static" QuestieDB.private.npcCache = {} assert.is_nil(QuestieDB:GetNPC(9200)) end) it("does NOT leak learner data into none mode when the store is present", function() Questie.dbLearner.global.settings.dataSourceMode = "none" QuestieDB.private.npcCache = {} assert.is_nil(QuestieDB:GetNPC(9200)) end) it("DOES overlay learner data in auto mode when the static DB lacks it", function() Questie.dbLearner.global.settings.dataSourceMode = "auto" QuestieDB.private.npcCache = {} local npc = QuestieDB:GetNPC(9200) assert.is_table(npc) assert.equals("Learner Only NPC", npc.name) end) it("clears every cache including the zone cache on mode switch", function() QuestieDB.private.questCache[1] = {} QuestieDB.private.zoneCache[1] = {} QuestieDB:ClearModeCaches() assert.is_nil(QuestieDB.private.questCache[1]) assert.is_nil(QuestieDB.private.zoneCache[1]) end) end) describe("QuestieLearner mode switch redraw wiring", function() it("drives a full real-time refresh via SmoothReset, not the mis-named event handler", function() local function read(path) local f = assert(io.open(path, "r")); local c = f:read("*a"); f:close(); return c end local dbOptions = read("Modules/Options/DatabaseTab/QuestieOptionsDatabase.lua") assert.is_true(string.find(dbOptions, "QuestieQuest:SmoothReset()", 1, true) ~= nil) -- The old import name resolved to nil and silently skipped the redraw. assert.is_nil(string.find(dbOptions, "ImportModule(\"QuestieEventHandler\")", 1, true)) end) end)