gitignore: re-add dev test files and tooling configs

Tests/, .busted, selene.toml, and wow_classic.yml are dev-only
files — not part of the release artifact.
This commit is contained in:
Xurkon
2026-05-16 07:54:19 -05:00
parent db91ec9fd4
commit d0d000cf3f
6 changed files with 5 additions and 847 deletions
-15
View File
@@ -1,15 +0,0 @@
return {
_all = {
lpath = "?.lua;Modules/?.lua;Modules/?/init.lua;Compat/?.lua;Database/?.lua",
},
default = {
verbose = true,
output = "utfTerminal",
ROOT = { "tests/" },
},
ci = {
ROOT = { "tests/" },
output = "TAP",
coverage = true,
},
}
+5
View File
@@ -37,6 +37,11 @@ verify_*.lua
.history/
Research/
Tools/
Tests/
tests/
.busted
selene.toml
wow_classic.yml
workflow/
__pycache__/
.agents/
-339
View File
@@ -1,339 +0,0 @@
-- Tests/QuestieLearner_spec.lua
require("Tests/wow_api_mock")
describe("QuestieLearner", function()
local QuestieLearner
setup(function()
-- Mocking QuestieLoader module resolution for tests
_G.QuestieLoader.ImportModule = function(_, name)
if name == "QuestieDB" then return _G.QuestieDB end
if name == "QuestieQuest" then return _G.QuestieQuest end
if name == "QuestiePlayer" then return _G.QuestiePlayer end
if name == "QuestLogCache" then return _G.QuestLogCache end
if name == "QuestieCompat" then return _G.QuestieCompat end
if name == "ZoneDB" then return _G.ZoneDB end
if name == "l10n" then return _G.l10n end
if name == "QuestieTooltips" then return _G.QuestieTooltips end
if name == "QuestieLib" then return {} end
return {}
end
_G.QuestieLoader.CreateModule = function(_, name)
_G[name] = {}
return _G[name]
end
-- Reset mock state
_G._lastRegisteredTooltip = nil
_G._mock_uiMapId = nil
_G._mock_questObjectives = nil
_G._mock_npcFlags = 2
-- Reset QuestLogCache.GetQuest to default
_G.QuestLogCache.GetQuest = function(_, id) return nil end
-- Load the module
package.loaded["Modules/QuestieLearner"] = nil
QuestieLearner = require("Modules/QuestieLearner")
-- Initialize sets up dbLearner.global.settings, InjectsLearnedData, etc.
QuestieLearner:Initialize()
end)
--==========================================================================
-- Existing test: coordinate scaling in OnCombatLogEvent
--==========================================================================
it("should scale coordinates by 100 in OnCombatLogEvent", function()
local unitGUID = "Creature-0-1234-567-89-21878-0000000000"
local unitName = "Felboar"
_G.QuestieCompat.GetCurrentPlayerPosition = function()
return 946, 0.35, 0.45
end
QuestieLearner:OnCombatLogEvent(GetTime(), "UNIT_DIED", nil, nil, nil, unitGUID, unitName, nil, nil, nil)
local cached = QuestieLearner.private.recentKills[unitGUID]
assert.is_not_nil(cached)
assert.are.equal(35.0, cached.x)
assert.are.equal(45.0, cached.y)
end)
--==========================================================================
-- Existing test: spell cast learning
--==========================================================================
it("should learn spell casts into dbLearner quest objectives when they match a quest objective", function()
local questId = 12345
local spellId = 29228
local targetGUID = "Creature-0-1234-567-89-21878-0000000000"
Questie.dbLearner.global.quests = {}
_G.QuestieCompat.GetQuestLogTitle = function(index)
return "Test Quest", 1, nil, false, nil, nil, nil, questId
end
_G.QuestLogCache.GetQuest = function(id)
return {
objectives = {
{ type = "spell", text = "Flame Shock the enemy" }
}
}
end
QuestieLearner:LearnSpellCast(spellId, "Flame Shock", targetGUID, "Enemy NPC")
assert.is_not_nil(Questie.dbLearner.global.quests[questId])
assert.is_not_nil(Questie.dbLearner.global.quests[questId][10])
assert.is_not_nil(Questie.dbLearner.global.quests[questId][10][1])
assert.are.same({ 21878, "Flame Shock" }, Questie.dbLearner.global.quests[questId][10][1][1])
end)
--==========================================================================
-- InjectLearnedData zone key migration (uiMapId → areaId)
--==========================================================================
describe("InjectLearnedData zone migration", function()
it("should migrate NPC spawn zone keys from uiMapId to areaId", function()
Questie.dbLearner.global.npcs = {
[21878] = {
[1] = "Felboar",
[7] = {
[1241] = { -- uiMapId (Sunstrider) → should migrate to 3430
{ 35.0, 45.0 },
{ 40.0, 50.0 },
},
},
},
}
Questie.dbLearner.global.quests = {}
Questie.dbLearner.global.objects = {}
Questie.dbLearner.global.items = {}
-- Reload the data by calling InjectLearnedData (already called in Initialize, but re-call with updated data)
QuestieLearner:InjectLearnedData()
local npcs = Questie.dbLearner.global.npcs
assert.is_nil(npcs[21878][7][1241],
"Old uiMapId key 1241 should be removed after migration")
assert.is_not_nil(npcs[21878][7][3430],
"New areaId key 3430 should exist after migration")
assert.are.equal(2, #npcs[21878][7][3430],
"Should have 2 coords entries in migrated zone")
assert.are.same({ 35.0, 45.0 }, npcs[21878][7][3430][1])
assert.are.same({ 40.0, 50.0 }, npcs[21878][7][3430][2])
end)
it("should merge migrated spawn data with existing areaId data", function()
Questie.dbLearner.global.npcs = {
[21878] = {
[1] = "Felboar",
[7] = {
[1241] = { -- uiMapId → merge into 3430
{ 35.0, 45.0 },
},
[3430] = { -- Already correct areaId
{ 50.0, 55.0 },
},
},
},
}
Questie.dbLearner.global.quests = {}
Questie.dbLearner.global.objects = {}
Questie.dbLearner.global.items = {}
QuestieLearner:InjectLearnedData()
local npcs = Questie.dbLearner.global.npcs
assert.is_nil(npcs[21878][7][1241], "Old uiMapId key should be removed")
assert.are.equal(2, #npcs[21878][7][3430],
"Merged: 1 existing + 1 migrated = 2 entries")
end)
it("should migrate object spawn zone keys from uiMapId to areaId", function()
Questie.dbLearner.global.npcs = {}
Questie.dbLearner.global.quests = {}
Questie.dbLearner.global.items = {}
Questie.dbLearner.global.objects = {
[181345] = {
[1] = "Mysterious Pedestal",
[4] = {
[1241] = { -- uiMapId → migrate to 3430
{ 30.0, 40.0 },
},
},
},
}
QuestieLearner:InjectLearnedData()
local objects = Questie.dbLearner.global.objects
assert.is_nil(objects[181345][4][1241],
"Old uiMapId key should be removed from objects")
assert.is_not_nil(objects[181345][4][3430],
"Migrated areaId key should exist")
assert.are.same({ 30.0, 40.0 }, objects[181345][4][3430][1])
end)
it("should leave non-mappable zone keys unchanged", function()
Questie.dbLearner.global.npcs = {
[21878] = {
[1] = "Felboar",
[7] = {
[3430] = { -- Already correct areaId — stay
{ 35.0, 45.0 },
},
[530] = { -- No ZoneDB mapping — stay
{ 60.0, 30.0 },
},
},
},
}
Questie.dbLearner.global.quests = {}
Questie.dbLearner.global.objects = {}
Questie.dbLearner.global.items = {}
QuestieLearner:InjectLearnedData()
local npcs = Questie.dbLearner.global.npcs
assert.is_not_nil(npcs[21878][7][3430], "3430 areaId should remain")
assert.is_not_nil(npcs[21878][7][530], "530 (no mapping) should remain")
assert.are.equal(1, #npcs[21878][7][3430], "3430 should still have 1 entry")
assert.are.equal(1, #npcs[21878][7][530], "530 should still have 1 entry")
end)
end)
--==========================================================================
-- LearnQuestObjectiveNPC icon preservation
--==========================================================================
describe("LearnQuestObjectiveNPC icon preservation", function()
it("should pass the objective icon from QuestLogCache to RegisterObjectiveTooltip", function()
local questId = 1001
local npcId = 21878
local objText = "Slay 10 felboars"
local objectiveIndex = 1
Questie.dbLearner.global.quests = {}
Questie.dbLearner.global.npcs = {}
-- Mock QuestLogCache.GetQuestObjectives returns objectives with Icon 136006
_G._mock_questObjectives = {
{ text = "Slay 10 felboars", Icon = 136006, objectiveType = "slay" },
}
QuestieLearner:LearnQuestObjectiveNPC(questId, npcId, objText, objectiveIndex)
-- Verify RegisterObjectiveTooltip was called with the icon
assert.is_not_nil(_G._lastRegisteredTooltip,
"RegisterObjectiveTooltip should have been called")
assert.are.equal(questId, _G._lastRegisteredTooltip.questId)
assert.are.equal("m_" .. npcId, _G._lastRegisteredTooltip.identifier)
assert.are.equal(objText, _G._lastRegisteredTooltip.data.Description)
assert.are.equal(136006, _G._lastRegisteredTooltip.data.Icon,
"Icon from QuestLogCache should be passed to tooltip registration")
end)
it("should work when QuestLogCache has no matching objective text", function()
local questId = 1002
local npcId = 21879
local objText = "Gather 5 moonflowers"
local objectiveIndex = 1
Questie.dbLearner.global.quests = {}
Questie.dbLearner.global.npcs = {}
-- Objectives don't include "moonflowers"
_G._mock_questObjectives = {
{ text = "Slay 10 felboars", Icon = 136006 },
{ text = "Collect 5 herbs", Icon = 134217 },
}
QuestieLearner:LearnQuestObjectiveNPC(questId, npcId, objText, objectiveIndex)
-- Should still register, but with nil Icon (graceful fallback)
assert.is_not_nil(_G._lastRegisteredTooltip,
"RegisterObjectiveTooltip should still be called without matching icon")
assert.is_nil(_G._lastRegisteredTooltip.data.Icon,
"Icon should be nil when no matching objective text is found")
end)
it("should work when QuestLogCache.GetQuestObjectives is nil", function()
local questId = 1003
local npcId = 21880
local objText = "Kill 3 worgs"
local objectiveIndex = 1
Questie.dbLearner.global.quests = {}
Questie.dbLearner.global.npcs = {}
-- nil/empty so no icon lookup happens
_G._mock_questObjectives = nil
QuestieLearner:LearnQuestObjectiveNPC(questId, npcId, objText, objectiveIndex)
assert.is_not_nil(_G._lastRegisteredTooltip,
"RegisterObjectiveTooltip should be called even with nil objectives")
assert.is_nil(_G._lastRegisteredTooltip.data.Icon,
"Icon should be nil when GetQuestObjectives returns nil")
end)
end)
--==========================================================================
-- MIN_CONFIDENCE_PINS = 1
--==========================================================================
describe("Settings defaults", function()
it("should have minConfidencePins default to 1 on fresh data", function()
-- After Initialize, settings should be set with minConfidencePins = 1
local settings = QuestieLearner:GetSettings()
assert.are.equal(1, settings.minConfidencePins,
"minConfidencePins should be 1 for Ascension play")
end)
it("should backfill legacy SavedVariables with minConfidencePins = 1", function()
-- Simulate legacy data without minConfidencePins
if Questie.dbLearner.global.settings then
Questie.dbLearner.global.settings.minConfidencePins = nil
end
-- Re-initialize to trigger backfill
QuestieLearner:Initialize()
local settings = QuestieLearner:GetSettings()
assert.are.equal(1, settings.minConfidencePins,
"Legacy settings should be backfilled with minConfidencePins = 1")
end)
end)
--==========================================================================
-- NPC learning API
--==========================================================================
describe("LearnNPC spawn zone tracking", function()
it("should store NPC spawn zoneId correctly", function()
local npcId = 21878
local spawnZoneId = 3430 -- Eversong Woods
Questie.dbLearner.global.quests = {}
Questie.dbLearner.global.npcs = {}
-- Mock player position
_G.QuestieCompat.GetCurrentPlayerPosition = function()
return 3430, 0.35, 0.45
end
-- Mock coords
_G.UnitPosition = function(unit) return -8940, -137, 82 end
QuestieLearner:LearnNPC(npcId, "Felboar", 60, nil, 2, nil, nil, nil, spawnZoneId)
assert.is_not_nil(Questie.dbLearner.global.npcs[npcId],
"NPC should be learned")
assert.are.equal("Felboar", Questie.dbLearner.global.npcs[npcId][1],
"NPC name should be stored")
-- NPC spawn coords should be stored under the areaId
if Questie.dbLearner.global.npcs[npcId][7] then
assert.is_not_nil(Questie.dbLearner.global.npcs[npcId][7][spawnZoneId],
"Spawn coords should be stored under zoneId " .. spawnZoneId)
end
end)
end)
end)
-161
View File
@@ -1,161 +0,0 @@
-- Tests/wow_api_mock.lua
-- Minimal mock of World of Warcraft API for Busted unit tests
_G = _G or {}
-- Mock Globals
_G.Questie = {
DEBUG_LEARNER = "LEARNER",
DEBUG_DEVELOP = "DEVELOP",
db = {
global = {
learnedData = {
npcs = {},
quests = {},
items = {},
objects = {},
settings = {
learnQuests = true,
learnNPCs = true,
learnItems = true,
learnObjects = true,
}
}
},
profile = {
learnedData = {
settings = {
learnQuests = true,
learnNPCs = true,
}
}
}
},
dbLearner = {
global = {
npcs = {},
quests = {},
items = {},
objects = {},
}
},
Debug = function(self, level, ...)
-- print("[" .. tostring(level) .. "]", ...)
end,
Print = function(self, ...)
-- print(...)
end,
Error = function(self, ...)
-- print("[ERROR]", ...)
end
}
_G.QuestieLoader = {
ImportModule = function(self, name)
if name == "QuestieDB" then return _G.QuestieDB end
if name == "QuestieQuest" then return _G.QuestieQuest end
if name == "QuestiePlayer" then return _G.QuestiePlayer end
if name == "QuestLogCache" then return _G.QuestLogCache end
if name == "QuestieLib" then return {} end
if name == "QuestieCompat" then return _G.QuestieCompat end
if name == "ZoneDB" then return _G.ZoneDB end
if name == "l10n" then return _G.l10n end
if name == "QuestieTooltips" then return _G.QuestieTooltips end
return {}
end,
CreateModule = function(self, name)
_G[name] = {}
return _G[name]
end
}
_G.QuestieDB = {
npcDataOverrides = {},
objectDataOverrides = {},
questDataOverrides = {},
itemDataOverrides = {},
QueryNPCSingle = function() return nil end,
GetQuest = function() return nil end,
}
_G.QuestieQuest = {}
_G.QuestieCompat = {
GetCurrentPlayerPosition = function() return 1, 0.5, 0.5 end,
GetQuestLogTitle = function(index)
return "Test Quest", 1, nil, false, nil, nil, nil, 123
end,
C_Timer = {
After = function(delay, fn) fn() end,
NewTicker = function(delay, fn) return { Cancel = function() end } end,
},
}
_G.QuestiePlayer = {
GetPlayerLevel = function() return 70 end,
}
_G.QuestLogCache = {
GetQuestID = function() return 123 end,
GetQuest = function(_, questId)
return nil
end,
GetQuestObjectives = function(self, questId)
return _G._mock_questObjectives or {
{ text = "Slay 10 felboars", Icon = 136006, objectiveType = "slay" },
{ text = "Collect 5 herbs", Icon = 134217, objectiveType = "loot" },
}
end,
}
_G.ZoneDB = {
GetAreaIdByUiMapId = function(self, uiMapId)
-- Sunstrider Isle (1241) → Eversong Woods (3430)
if uiMapId == 1241 then return 3430 end
return nil
end,
}
_G.l10n = {
GetAreaId = function(self)
return 3430
end,
}
_G.QuestieTooltips = {
RegisterObjectiveTooltip = function(self, questId, identifier, data)
_G._lastRegisteredTooltip = { questId = questId, identifier = identifier, data = data }
end,
}
_G.C_Map = {
GetBestMapForUnit = function(unit)
return _G._mock_uiMapId or 530
end,
GetPlayerMapPosition = function(uiMapId, unit)
return 0.5, 0.5
end,
}
-- string.trim is a WoW API extension
_G.string.trim = function(s)
if s then return s:match("^%s*(.-)%s*$") or "" end
return ""
end
_G.GetNumQuestLogEntries = function() return 1 end
_G.GetTime = function() return os.time() end
_G.time = os.time
_G.floor = math.floor
_G.UnitName = function(unit) return "TestUnit" end
_G.UnitLevel = function(unit) return 70 end
_G.UnitGUID = function(unit) return "Creature-0-1234-567-89-1000-0000000000" end
_G.GetRealZoneText = function() return "Shadowmoon Valley" end
_G.GetInstanceInfo = function() return "Shadowmoon Valley", nil, nil, nil, nil, nil, nil, 530 end
_G.CreateFrame = function() return { RegisterEvent = function() end, SetScript = function() end } end
_G.GetPlayerMapPosition = function(unit)
return 0.5, 0.5
end
return _G
-11
View File
@@ -1,11 +0,0 @@
std = "wow_classic"
[lints]
global_usage = "allow"
unused_variable = "allow"
duplicate_key = "allow"
ambiguous_if = "allow"
incorrect_standard_library_use = "allow"
undefined_variable = "allow"
if_same_then_else = "allow"
constant_table_comparison = "allow"
-321
View File
@@ -1,321 +0,0 @@
name: wow_classic
globals:
UIParent:
property: new-fields
WorldFrame:
property: new-fields
GameTooltip:
property: new-fields
Minimap:
property: new-fields
WorldMapDetailFrame:
property: new-fields
StaticPopupDialogs:
property: new-fields
QuestLogFrame:
property: new-fields
DEFAULT_CHAT_FRAME:
property: new-fields
ChatFrame1:
property: new-fields
CreateFrame:
property: new-fields
GetTime:
property: new-fields
UnitName:
property: new-fields
UnitGUID:
property: new-fields
UnitLevel:
property: new-fields
UnitFactionGroup:
property: new-fields
GetMinimapZoneText:
property: new-fields
GetZoneText:
property: new-fields
GetSubZoneText:
property: new-fields
SetMapToCurrentZone:
property: new-fields
SetMapByID:
property: new-fields
GetPlayerMapPosition:
property: new-fields
GetCurrentMapAreaID:
property: new-fields
GetCurrentMapZone:
property: new-fields
C_Map:
property: new-fields
C_Timer:
property: new-fields
LibStub:
property: new-fields
HBD:
property: new-fields
HBDp:
property: new-fields
RegisterEvent:
property: new-fields
UnregisterEvent:
property: new-fields
UnregisterAllEvents:
property: new-fields
SetScript:
property: new-fields
hooksecurefunc:
property: new-fields
InCombatLockdown:
property: new-fields
RegisterStateDriver:
property: new-fields
UnregisterStateDriver:
property: new-fields
geterrorhandler:
property: new-fields
debugstack:
property: new-fields
print:
property: new-fields
wipe:
property: new-fields
select:
property: new-fields
format:
property: new-fields
strsplit:
property: new-fields
pcall:
property: new-fields
error:
property: new-fields
next:
property: new-fields
pairs:
property: new-fields
ipairs:
property: new-fields
Ambiguate:
property: new-fields
PlaySound:
property: new-fields
GetAddOnInfo:
property: new-fields
IsAddOnLoaded:
property: new-fields
GetLocale:
property: new-fields
GetRealmName:
property: new-fields
GetScreenWidth:
property: new-fields
GetScreenHeight:
property: new-fields
GetItemInfo:
property: new-fields
GetItemCount:
property: new-fields
GetQuestLogTitle:
property: new-fields
GetQuestID:
property: new-fields
SelectQuestLogEntry:
property: new-fields
AcceptQuest:
property: new-fields
CompleteQuest:
property: new-fields
GetQuestReward:
property: new-fields
CloseQuest:
property: new-fields
GetTitleText:
property: new-fields
GetGreetingText:
property: new-fields
SendChatMessage:
property: new-fields
RAID_CLASS_COLORS:
property: new-fields
Enum:
property: new-fields
arg:
property: new-fields
this:
property: new-fields
tinsert:
property: new-fields
tremove:
property: new-fields
abs:
property: new-fields
max:
property: new-fields
min:
property: new-fields
Questie:
property: new-fields
QuestieLoader:
property: new-fields
QuestieCompat:
property: new-fields
QuestieDB:
property: new-fields
Questie_SV:
property: new-fields
QuestieCompartment:
property: new-fields
QuestieArrow:
property: new-fields
AceAddon-3.0:
property: new-fields
AceConsole-3.0:
property: new-fields
AceConfig-3.0:
property: new-fields
AceDB-3.0:
property: new-fields
AceDBOptions-3.0:
property: new-fields
AceEvent-3.0:
property: new-fields
AceHook-3.0:
property: new-fields
AceLocale-3.0:
property: new-fields
AceSerializer-3.0:
property: new-fields
CallbackHandler-1.0:
property: new-fields
string:
property: new-fields
table:
property: new-fields
math:
property: new-fields
unpack:
property: new-fields
tonumber:
property: new-fields
tostring:
property: new-fields
type:
property: new-fields
setmetatable:
property: new-fields
getmetatable:
property: new-fields
rawget:
property: new-fields
rawset:
property: new-fields
rawequal:
property: new-fields
loadstring:
property: new-fields
time:
property: new-fields
date:
property: new-fields
floor:
property: new-fields
ceil:
property: new-fields
mod:
property: new-fields
gsub:
property: new-fields
gmatch:
property: new-fields
match:
property: new-fields
find:
property: new-fields
format:
property: new-fields
sub:
property: new-fields
lower:
property: new-fields
upper:
property: new-fields
len:
property: new-fields
reverse:
property: new-fields
concat:
property: new-fields
insert:
property: new-fields
remove:
property: new-fields
sort:
property: new-fields
IsShiftKeyDown:
property: new-fields
IsControlKeyDown:
property: new-fields
IsAltKeyDown:
property: new-fields
GossipFrame:
property: new-fields
GossipFrameGreetingPanel:
property: new-fields
QuestFrameGreetingPanel:
property: new-fields
QuestFrameDetailPanel:
property: new-fields
QuestFrameProgressPanel:
property: new-fields
QuestFrameRewardPanel:
property: new-fields
QuestFrameGoodbyeButton:
property: new-fields
IsQuestCompletable:
property: new-fields
SelectAvailableQuest:
property: new-fields
ConfirmAcceptQuest:
property: new-fields
GetNumActiveQuests:
property: new-fields
GetActiveTitle:
property: new-fields
SelectActiveQuest:
property: new-fields
GetNumAvailableQuests:
property: new-fields
GetNumQuestChoices:
property: new-fields
_G:
property: new-fields
GameFontHighlightLarge:
property: new-fields
GameFontHighlightSmall:
property: new-fields
GameFontHighlight:
property: new-fields
GameFontNormal:
property: new-fields
UISpecialFrames:
property: new-fields
QuestFrameCloseButton:
property: new-fields
WorldMapTooltip:
property: new-fields
IsModifierKeyDown:
property: new-fields
ChatEdit_GetActiveWindow:
property: new-fields
StaticPopup_Show:
property: new-fields
ChatEdit_InsertLink:
property: new-fields
TomTom:
property: new-fields
MBB_Ignore:
property: new-fields
coroutine:
property: new-fields