# Tests

> How the tests run, and the performance budgets they hold the addon to.

Page: https://polestar.guide/docs/contributors/architecture/tests (Polestar docs > Contributors > Architecture)

Layers, stubs, helpers and the tests that need local data: [tests/README.md](https://polestar.guide/docs/contributors/tests).

`npm test` = `node --test "tests/**/*.test.js"`. Lua runs under real `lua5.1` through
`tests/helpers/lua.js`, which loads `tests/lua/wow_stub.lua` and then the
addon files listed by the TOC/XML manifests. Extend the stub per module in
`tests/lua/stub_<module>.lua`; `wow_stub.lua` holds only the generic client
pieces every module shares. Keep tests deterministic and behavioural.

Performance is tested, not assumed. `tests/perf.test.js` loads the whole addon
with every module on and the local data packs, and holds these budgets of addon
CPU time (event handlers, message listeners, OnUpdate scripts, timers): a
`accept`/`turnin` also carry the quest frame's rewards when it shows any:
`items` and `choices` (item ids, `GetQuestItemInfo`) and `rewardMoney`.

Finds (`Finds.lua`), for what the client tables cannot give the Codex:

- `rare {npc, npcName, cls, npcLvl, src, map, x, y, inst}`: a unit whose
  `UnitClassification` is `rare`, `rareelite` or `worldboss` (`src`
  `nameplate`, `target` or `mouseover`, the player's position), or the
  creature behind a vignette (`src` `vignette`, the vignette's position, `cls`
  only when the unit was seen). Once per npc per 50 yards per session.
- `treasure {object, objName, src, map, x, y}`: the game object behind a
  vignette, de-duplicated the same way.
- `slain {npc, npcName, cls, npcLvl, map, x, y, inst, diff, denied?}`: a
  creature the player or pet killed (`PARTY_KILL`), or whose corpse the
  player looted; `denied` when `CanLootUnit` a second later says it has loot
  the player may not take (tapped, round-robin).
- `drop {npc|object, npcName, items: [ { item, n, q? } ], gather?, inst,
  diff, map, x, y}`: one loot window per source (`GetLootSourceInfo`, else
  the dead target), each corpse or object once per session; `q` marks quest
  items, `gather` (`herb`, `mine`, `skin`) a window a gathering cast opened.
  Fishing is left out.
- `enter {inst, instName, instType, diff, map, x, y}`: zoning into a dungeon
  or raid (`GetInstanceInfo`), with the last outdoor position (taken at
  `PLAYER_LEAVING_WORLD`) as the entrance.
- `encounter {enc, encName, diff, size, ok, inst, npcs, map, x, y}`:
  `ENCOUNTER_END`; `npcs` are the bosses its `encounterUnitStatus` lists,
  else the creatures that died since `ENCOUNTER_START`.

`data build` reads every session's finds together, so counts merge across
sessions (`tools/lib/data/recordings.js`, `createFinds`):

- npc `rank` from the latest classification (`rare` 4, `rareelite` 2,
  `elite` 1, `worldboss` 3, `normal` 0); levels widen `level`/`levelMax` to
  what was seen; rares and vignette creatures get the `rare` role.
- Drop chance (items `drops`, percent, two decimals) = windows holding the
  item / kills of the npc (`slain` without `denied`, never fewer than its
  windows); for objects, windows holding it / windows opened. From 10 kills
  or opens up it replaces the Era quest database's or the community quest database's chance, below that it only
  fills a missing or unknown (`null`) one; `seen` keeps `{ n, of }` either way. Skinning windows
  are no drops.
- An object whose window held a non-quest item, or seen behind a vignette,
  gets the `chest` role; gathering windows give `herb` or `mine`.
- Sighting and opening positions join the entity's spawns: a map without
  spawns gets them as `"l"`, a live-frame map takes them in, and so does an
  Era map whose frame Forever kept (`meta.liveFrameMaps`). Kill positions
  only fill a map without spawns.
- `inst` adds to `instances`; encounter bosses get `encounter`; entries and
  encounters fill `instances.json` (entrances clustered within 1.5 map
  percent, the most used first; encounters with their bosses and wins).
- Quest `rewards` from the frames, a turn-in's over an accept's.

`meta.counts` gives `recordedKills`, `recordedLoots`, `recordedSightings`,
`recordedDropChances`, `recordedTreasures`, `recordedInstances` and
`recordedEncounters`.
burst of 50 `QUEST_LOG_UPDATE` and 50 `BAG_UPDATE` in one frame under 4 ms, a
walking frame under 1 ms, `SetGuide` and every step change of a leveling
guide under 8 ms (with what they schedule), and a gathering guide played to
the end in under 15 s. When one fails, `node tests/helpers/profile.js
<guide> [race] [class] --packs [--sample=1000]` plays a guide with every
handler timed by module (`tests/lua/profile.lua`) and, with `--sample`, a
stack sampler for the functions and lines that cost the time.

---

Next page: [The addon, file by file](https://polestar.guide/docs/contributors/addon.md) · Every docs page: https://polestar.guide/llms.txt · All docs in one file: https://polestar.guide/llms-full.txt
