flygard-core/.agents/docs/e2e-policy.md
2026-08-27 14:57:14 +02:00

11 KiB
Raw Blame History

E2E policy

Mandatory when writing or changing live-stack e2e (e2e/). When to write tests. How: AzerothGhost LLM_GUIDE.md / EXAMPLES.md (this file does not replace them). Inventory: e2e/README.md. Stack: AC 3.3.5a auth+world+MySQL; E2E_* may point at a gateway.

Terms

Term Meaning
Unit Isolated C++/Go; no live stack. src/test/.
Integration In-process multi-component; no client login.
E2E //go:build e2e Go test: e2eharness bots on a running realm; assert protocol / object cache / DB.
Player-visible A real client can observe it.
Tracked issue AC issue/PR id for ConfirmedBugf.

When

First match wins:

If Then
Pure calc / parse / bit math / no world UNIT. NEVER e2e.
Covered by existing unit/mocks (SpellProc*, …) without login/map/DBC fidelity UNIT or in-process integration. E2E only if unit cannot reach the path.
SQL-only static (text, displayid, non-script loot) No e2e. Manual optional.
SmartAI / conditions / waypoints; outcome only in-world E2E SHOULD if player-visible and deterministic; else document manual repro.
Multi-system on a live session, or protocol/client-observed, or player-repro bugfix E2E MUST (or update existing). Unit MAY still cover pure pieces.
Refactor, no behaviour intent Update tests that break. Add e2e only if a critical system below lacks an oracle.

Bias: player-visible and MVT fits → prefer e2e over unit-only. Not a MUST trigger and unsure → do not invent e2e; file a gap.

MUST add or update e2e when any of: player-visible bugfix (encode the repro); protocol change; multi-system (combat/aura/death/quest/loot/mount/pet/vehicle/instance/PvP/guild/relog/DB after .save); crash/hang/freeze on client action (ProbeWorldAlive / AssertWorldAlive); existing TestAC_* path touched (update, do not drop); critical system with no oracle; claimed blizzlike fix for a tracked issue (ConfirmedBugf until fixed).

MUST NOT treat “tested in-game” as a substitute when MVT is feasible.

Harness cannot express it: extend harness or file a harness gap; inventory the gap; still unit-test pure logic.

NEVER / do instead

Situation Do instead
Pure math, flags, calendar src/test/
Isolated spell-proc already unit-covered; no new player-visible interaction extend unit
SQL-only static, no behavioural branch SQL review
Rename / comments / refactor; no behaviour; not a critical system existing tests
Human judgment, nondeterminism, hours-long lockouts manual; optional one deterministic slice
Same oracle as an existing test extend or share a helper
“World boots” with no behaviour oracle not feature e2e
Invent harness APIs / test the harness contribute to AzerothGhost

SHOULD NOT e2e a GM command that is not on the player path (GM setup is fine; assert player-relevant state).

Authoring (PR gate)

Consumer suite (e2e/suites/ / e2e/smoke/), not only Ghost. //go:build e2e. Import e2eharness + blank MySQL driver.

Fixture: NewSolo / NewScenario (BotSpec / ByRole if roles differ). Prefer ScenarioBot over raw Session except guild charter/bank.

meta.Begin serial by default. Prefix ≤7 chars (auth name max 17 = Prefix+10). Tag parallel only when pad-safe.

Name: TestArea_Behaviour or TestAC_<issue>_<Short>. Comment the issue/PR URL.

Flow: fixture → place → setup (GM ok) → CombatReady if pull → drive → assert.

Place: Teleport / TeleportPad / TeleNamed / GoCreatureID. Melee: GoCreatureID after named tele. Tele clears object cache — re-WaitUnit.

Combat: CombatReady / CombatReadyFull before pulls. NEVER .gm on during aggro. Damage: Damage / DamageKill only — NEVER .gm on mid-fight for .damage.

Casts: Cast / CastMust / CastOrGM / CastAtPosition; fail reason via SpellFailReasonName.

Waiters: Arm → Send → Wait. NEVER re-arm during Wait. NEVER replace waiters with long fixed sleeps. After fire-and-forget GM that later casts depend on (.pvp on, .gm off), FlushWorld on that bot (world-thread ack) or a state waiter (WaitUnitPvP).

Quest DB: only after Save / QuestStatusAfterSave. Spell-summon: NEVER .npc add instead of the spell. Set race so GM text uses the characters language (Horde ≠ Common).

Oracle = behaviour (protocol / cache; DB after Save), not “no error”. SHOULD t.Logf("PASS …"). SHOULD log GUIDs/spell ids/statuses.

MVT (all required or it is not coverage): one primary oracle; real client path; setup failures are Preconditionf; correct severity; no footguns above; minutes not hours (long boss waves only if the oracle needs them); runnable with E2E_* on stock AC; name+comment findable from issue or mechanic.

//go:build e2e
// Issue: https://github.com/azerothcore/azerothcore-wotlk/issues/N
func TestAC_N_ShortOracle(t *testing.T) {
	meta.Begin(t, meta.TestMeta{Tags: []string{"short", "issue"}, Runtime: "short", Issue: N})
	bot := e2eharness.NewSolo(t, e2eharness.ScenarioOpts{
		Prefix: "Short", Race: e2eharness.RaceHuman,
		Class: e2eharness.ClassWarrior, Level: 80, LearnAllClass: true,
	})
	// place → setup → (CombatReady) → drive → assert
}

Run: go test -tags=e2e -run TestName -count=1 -v green on fixed core (or only CONFIRMED BUG on unfixed). SHOULD -count=2 stable. Document if exclusive realm needed.

NEVER: invent harness APIs; wrap GM/map without logs; bare t.Fatalf("CONFIRMED BUG"); copy whole examples; one test for five issues.

Fail helpers

Use these, not bare t.Fatalf, for classified outcomes:

Helper Prefix When
Preconditionf precondition: Setup never reached a judgeable state
ConfirmedBugf(t, N, …) AC#N CONFIRMED BUG: Tracked issue; core wrong (fails CI)
HarnessFailf / Assertf harness: / assert: Infra or fixed-core regression (fails CI)
SoftWarnf WARNING: Non-fatal soft deviation
Open / unfixed issue Comment out the entire test: TODO(e2e): re-enable when AC#N is fixed + issue URL. Re-enable body MUST hard-fail. NEVER log+return soft-pass.

Critical systems

A PR that implements, fixes, or refactors a row MUST add or update e2e for that oracle, or keep an existing test that still asserts it. If harness cannot: gap in e2e/README.md + unit for pure logic.

System Oracle / drive
Aura apply/strip/consume apply → action → AssertAuraRemains / AssertAuraConsumed / aura waiters. Paths: Spells, aura scripts, spell_*.cpp
Cast / charge / pathing Cast/CastMust + fail reason / SMSG_SPELL_GO; charge stays on bridge; ground AoE landing
Death / repop / corpse DieAndRepop (or staged) + surviving system (STAY_ALIVE, Raise Dead)
Quest + persistence AddQuest → action → SaveAssertQuestStatus
Relog / extra_flags / GM vis set → SaveRelog → DB or protocol
Threat / evade / engage CombatReadyEngage → still in combat; no evade from bad GM
Spell summon cast the summon spell (not .npc add) + summoned unit properties
Instance boss AI place → CombatReadyEngage → spawn/target/interval (AssertIntervalNotAccelerated)
Guild charter / bank Session guild helpers
Account / realm GM .account set gmlevel scoped to target realm (#27088 style)
Crash-prone cast repro + ProbeWorldAlive / AssertWorldAlive
Pure helper extracted from the above unit the helper and keep e2e on the player path

Coverage

Search consumer suite + Ghost examples/ + TestAC_* for issue, spell, quest, creature, mechanic before adding.

Same oracle → same test (extend / table-drive). Same setup, different oracle → helper or t.Run, not a second login. NEVER copy Ghost examples/ without changing the oracle. One tracked issue → one primary TestAC_<id>_…; extra edges as t.Run or siblings only if isolation requires. Same root cause + same assertion surface → merge.

Inventory (e2e/README.md + suite comments): category, one-sentence oracle, P0P3, covered (test name) / gap / blocked-harness / manual-only, issue links, note. MUST update when adding coverage or finding a gap. blocked-harness MUST name the missing API (opcode, waiter, multi-realm, …). Closing a P0/P1 gap is preferred when touching that subsystem. When pruning, merge redundant tests and refresh the inventory.

Prefer one solid test per merged player-visible bugfix. Deepen critical categories over new weak ones. Every test: unique oracle + owner (issue or feature). Delete or merge tests that no longer map to a behaviour.

Priority (next test)

Highest feasible MVT:

Pri Cover
P0 Crash / hang / corruption on client action; already-regressed player bug with issue id
P1 Aura strip/consume/persist; death+quest; relog flags; evade/engage; charge/position; totem/grounding; spell-summon (not GM spawn)
P2 Single-boss timers/waves/targeting; guild charter/bank; multi-bot PvP; realm-scoped GM / persisted visibility
P3 QoL, display, nondeterministic farm; full raid clears (out of default growth)

On a PR: implement e2e for the highest-priority trigger the PR activates. Do not add P3 in the same change unless asked.

Isolation, comments, flakes

Unique Prefix; cleanup via t.Cleanup. Do not depend on other tests characters/guilds/instance saves. Time oracles: bounded windows with documented thresholds, not “sleep 60s”.

Tracked issues: URL in comment + id in ConfirmedBugf. State expected green-on-fixed vs CONFIRMED-BUG-on-unfixed. Do not claim blizzlike without issue, PR, or reviewed wowhead/web evidence.

Flake: classify (infra, timing, pad collision, dirty world, core race). Fix waiters / Arm-Send-Wait / CombatReady / unique prefixes / re-wait after tele. Intermittent fail on a correct core is a harness/test bug until proven otherwise. Quarantine (t.Skip + reason) only for environmental flake, rare and ticketed. NEVER silent retries or endless timeout bumps.

Open issues

Core Test
Unfixed Comment out the whole test + TODO + URL. Re-enable MUST hard-fail.
Fixed Uncomment; MUST go green. Keep TestAC_<n>_ and comment.
Invalid / cannot repro Delete the disabled block; no lying TODOs.
Env cannot run Preconditionf or inventory blocked-*; not a soft PASS.

NEVER let a test PASS while the product oracle is wrong. NEVER multi-retry + soft-exit to hide flakes or open bugs. NEVER delete a green regression because the issue closed.

Update tests in the same PR as the behaviour change. Harness renames: update consumers in the same landing window. Ghost e2e/examples/ are patterns; AC regressions live in the consumer suite.

Scratch MUST be e2e/local/ (gitignored except local/README.md). NEVER commit throwaways. Promote into e2e/suites/ next to related tests in the same PR as the fix. Prefer live e2e over ad-hoc GM when the stack is up.

Local (from e2e/): go test -tags=e2e ./.... Official-repo PR and master CI: full suite after nopch clang-18 (reuses those binaries). Dispatch -f scope=smoke for a smaller run. Touch .github/workflows/e2e-live.yml only when changing CI.