Files
gogobee/internal/plugin/zone_graph_map_test.go
prosolis 893d3dacad Branching zones G6: dependent surfaces re-keyed on graph nodes
- Harvest tables (expedition + standalone) now keyed by node_id; legacy
  room-idx entries auto-migrate via read-fallback + drop-on-save.
- Narration salts swapped from run.CurrentRoom to narrationCadence(run)
  (= len(visited_nodes)-1) so flavor pickers survive G9 column drop.
- !zone map renders the graph (BFS by PosX/PosY) when the gate is on:
  ✓/▶/· status, ╳ for locked-only edges, secrets hidden until visited.
- Region-boundary hook in graph advance/!zone go updates expedition
  CurrentRegion + visited list when from/to nodes differ — without
  burning supplies or retiring runs (the graph IS the run state).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 15:17:30 -07:00

82 lines
2.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package plugin
import (
"strings"
"testing"
)
// TestRenderZoneGraphMap_LinearLooksFamiliar exercises the linear-graph
// case so the new renderer doesn't regress the legacy single-row look.
func TestRenderZoneGraphMap_LinearLooksFamiliar(t *testing.T) {
g := BuildLinearGraph(ZoneGoblinWarrens, []ZoneNodeKind{
NodeKindEntry, NodeKindExploration, NodeKindTrap, NodeKindBoss,
})
run := &DungeonRun{
ZoneID: ZoneGoblinWarrens,
CurrentNode: "goblin_warrens.r2",
VisitedNodes: []string{"goblin_warrens.r1", "goblin_warrens.r2"},
}
got := renderZoneGraphMap(g, run)
// Top row should contain glyphs in order.
if !strings.Contains(got, "E──?──T──☠") {
t.Errorf("missing linear glyph row in:\n%s", got)
}
// Status row: first cleared, second current, rest pending.
if !strings.Contains(got, "✓") || !strings.Contains(got, "▶") || !strings.Contains(got, "·") {
t.Errorf("missing status markers in:\n%s", got)
}
}
// TestRenderZoneGraphMap_HidesUnvisitedSecrets confirms the spoiler
// guard: secret nodes don't render until the player has reached them.
func TestRenderZoneGraphMap_HidesUnvisitedSecrets(t *testing.T) {
nodes := []ZoneNode{
{NodeID: "z.entry", Kind: NodeKindEntry, IsEntry: true, PosX: 0},
{NodeID: "z.boss", Kind: NodeKindBoss, IsBoss: true, PosX: 1},
{NodeID: "z.secret", Kind: NodeKindSecret, PosX: 1, PosY: 1},
}
edges := []ZoneEdge{
{From: "z.entry", To: "z.boss"},
{From: "z.entry", To: "z.secret", Lock: LockPerception, LockData: map[string]any{"dc": 12}},
}
g := BuildGraph("test_zone", nodes, edges)
hidden := renderZoneGraphMap(g, &DungeonRun{
CurrentNode: "z.entry",
VisitedNodes: []string{"z.entry"},
})
if strings.Contains(hidden, "⚿") {
t.Errorf("unvisited secret should not render, got:\n%s", hidden)
}
// Once visited, the glyph appears.
revealed := renderZoneGraphMap(g, &DungeonRun{
CurrentNode: "z.secret",
VisitedNodes: []string{"z.entry", "z.secret"},
})
if !strings.Contains(revealed, "⚿") {
t.Errorf("visited secret should render, got:\n%s", revealed)
}
}
// TestRenderZoneGraphMap_LockedEdgeRenderedAsCross verifies locked-only
// approaches show instead of ──.
func TestRenderZoneGraphMap_LockedEdgeRenderedAsCross(t *testing.T) {
nodes := []ZoneNode{
{NodeID: "z.entry", Kind: NodeKindEntry, IsEntry: true, PosX: 0},
{NodeID: "z.gate", Kind: NodeKindExploration, PosX: 1},
{NodeID: "z.boss", Kind: NodeKindBoss, IsBoss: true, PosX: 2},
}
edges := []ZoneEdge{
{From: "z.entry", To: "z.gate", Lock: LockKey, LockData: map[string]any{"key_id": "rune"}},
{From: "z.gate", To: "z.boss"},
}
g := BuildGraph("test_zone_locked", nodes, edges)
got := renderZoneGraphMap(g, &DungeonRun{
CurrentNode: "z.entry",
VisitedNodes: []string{"z.entry"},
})
if !strings.Contains(got, "") {
t.Errorf("locked edge should render as , got:\n%s", got)
}
}