Files
gogobee/internal/plugin/zone_graph_sunken_temple_test.go
prosolis 89edc76829 Long expeditions D1-b: crypt_valdris + T2 zones to new room bands
Three more zones follow the goblin_warrens pattern from D1-a: add the
missing Trap anchor and deepen the graph so the longest entry→boss walk
lands in its tier's target band.

  crypt_valdris  (T1):  8 → 17 nodes, longest 13 (band 12–14)
  forest_shadows (T2):  9 → 19 nodes, longest 16 (band 16–20)
  sunken_temple  (T2): 10 → 26 nodes, longest 16 (band 16–20)

Topology preserved per zone:
- Crypt diamond + Perception-DC-15 secret off side_chapel.
- Forest asymmetric main fork (long branch carries the elite) +
  WIS-DC-14 secret pre-boss.
- Sunken Temple sequential forks with no mid-path merge (four leaves
  all terminate at the single boss room).

MinRooms/MaxRooms re-pitched to the §2 bands so the dice fallback also
lands in band for any future graphless zone.
2026-05-27 17:32:29 -07:00

85 lines
2.6 KiB
Go

package plugin
import "testing"
func TestSunkenTempleGraph_Registered(t *testing.T) {
g, ok := zoneGraphRegistry[ZoneSunkenTemple]
if !ok {
t.Fatal("zoneSunkenTempleGraph not registered")
}
if g.Entry != "sunken_temple.entry" {
t.Errorf("entry node = %q", g.Entry)
}
if g.Boss != "sunken_temple.boss" {
t.Errorf("boss node = %q", g.Boss)
}
// Long-expedition D1-b widened this zone from 10 → 26 nodes so the
// longest entry→boss walk lands in the T2 [16,20] traversal band.
// (Sunken Temple is wide by design — no mid-merge means each leaf
// owns its own chain to the boss.)
if len(g.Nodes) != 26 {
t.Errorf("nodes = %d, want 26", len(g.Nodes))
}
}
// TestSunkenTempleGraph_FourDistinctPaths verifies the four leaves
// (kuo_toa_pen, glyph_chamber, aboleth_thralls, coral_reliquary) all
// reach the boss. Together with the no-merge invariant below, this
// confirms the design intent: four committed paths, not a diamond.
func TestSunkenTempleGraph_FourDistinctPaths(t *testing.T) {
g := zoneSunkenTempleGraph()
for _, leaf := range []string{
"sunken_temple.kuo_toa_pen",
"sunken_temple.glyph_chamber",
"sunken_temple.aboleth_thralls",
"sunken_temple.coral_reliquary",
} {
if !reachable(g, leaf, "sunken_temple.boss") {
t.Errorf("%s unreachable to boss", leaf)
}
}
}
// TestSunkenTempleGraph_NoMidPathMerge ensures no node other than the
// boss has more than one incoming edge. If a future edit collapses the
// two subtrees into a converging diamond, this test fails — that's the
// signal to either rename the zone's shape comment or re-author.
func TestSunkenTempleGraph_NoMidPathMerge(t *testing.T) {
g := zoneSunkenTempleGraph()
incoming := map[string]int{}
for _, outs := range g.Edges {
for _, e := range outs {
incoming[e.To]++
}
}
for id, n := range g.Nodes {
if n.IsBoss {
continue
}
if incoming[id] > 1 {
t.Errorf("node %q has %d incoming edges, want ≤1 (no mid-path merge)", id, incoming[id])
}
}
// Boss intentionally has 4 incoming edges (one per leaf).
if incoming["sunken_temple.boss"] != 4 {
t.Errorf("boss incoming = %d, want 4", incoming["sunken_temple.boss"])
}
}
func TestSunkenTempleGraph_LockKindCoverage(t *testing.T) {
g := zoneSunkenTempleGraph()
kinds := map[ZoneEdgeLockKind]bool{}
for _, outs := range g.Edges {
for _, e := range outs {
kinds[e.Lock] = true
}
}
// Sunken Temple deliberately exercises Perception + StatCheck +
// LockNone in one zone.
for _, k := range []ZoneEdgeLockKind{LockNone, LockPerception, LockStatCheck} {
if !kinds[k] {
t.Errorf("missing lock kind %s — variety check", k)
}
}
}