aboutsummaryrefslogtreecommitdiff
path: root/internal/world/grid_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/world/grid_test.go')
-rw-r--r--internal/world/grid_test.go181
1 files changed, 181 insertions, 0 deletions
diff --git a/internal/world/grid_test.go b/internal/world/grid_test.go
new file mode 100644
index 0000000..04d749c
--- /dev/null
+++ b/internal/world/grid_test.go
@@ -0,0 +1,181 @@
+package world
+
+import (
+ "os"
+ "path/filepath"
+ "strconv"
+ "testing"
+)
+
+func writeGridTestRoom(t *testing.T, dir string, id int, body string) {
+ t.Helper()
+ rooms := filepath.Join(dir, "rooms")
+ if err := os.MkdirAll(rooms, 0o755); err != nil {
+ t.Fatal(err)
+ }
+ if err := os.WriteFile(filepath.Join(rooms, strconv.Itoa(id)+".yaml"), []byte(body), 0o644); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func loadGridRoom(w *World) func(int) (*Room, bool) {
+ return func(id int) (*Room, bool) {
+ r, err := w.LoadRoom(id)
+ if err != nil {
+ return nil, false
+ }
+ return r, true
+ }
+}
+
+func hasConflictKind(conflicts []GridConflict, kind string) bool {
+ for _, c := range conflicts {
+ if c.Kind == kind {
+ return true
+ }
+ }
+ return false
+}
+
+func TestBuildGridConflictsClean(t *testing.T) {
+ dir := t.TempDir()
+ writeGridTestRoom(t, dir, 1, "exits:\n south: 3\n east: 2\n")
+ writeGridTestRoom(t, dir, 2, "exits:\n south: 4\n")
+ writeGridTestRoom(t, dir, 3, "exits:\n east: 4\n")
+ writeGridTestRoom(t, dir, 4, "name: corner\n")
+
+ w := New(dir)
+ if conflicts := BuildGridConflicts(1, loadGridRoom(w)); len(conflicts) != 0 {
+ t.Errorf("expected no conflicts, got: %+v", conflicts)
+ }
+}
+
+func TestBuildGridConflictsOverlap(t *testing.T) {
+ dir := t.TempDir()
+ writeGridTestRoom(t, dir, 1, "exits:\n south: 3\n east: 2\n")
+ writeGridTestRoom(t, dir, 2, "exits:\n south: 5\n")
+ writeGridTestRoom(t, dir, 3, "exits:\n east: 4\n")
+ writeGridTestRoom(t, dir, 4, "name: four\n")
+ writeGridTestRoom(t, dir, 5, "name: five\n")
+
+ w := New(dir)
+ conflicts := BuildGridConflicts(1, loadGridRoom(w))
+ if !hasConflictKind(conflicts, "overlap") {
+ t.Errorf("expected an overlap conflict, got: %+v", conflicts)
+ }
+}
+
+func TestBuildGridConflictsTwist(t *testing.T) {
+ dir := t.TempDir()
+ writeGridTestRoom(t, dir, 1, "exits:\n south: 4\n east: 2\n")
+ writeGridTestRoom(t, dir, 2, "exits:\n east: 3\n")
+ writeGridTestRoom(t, dir, 4, "exits:\n east: 3\n")
+ writeGridTestRoom(t, dir, 3, "name: three\n")
+
+ w := New(dir)
+ conflicts := BuildGridConflicts(1, loadGridRoom(w))
+ if !hasConflictKind(conflicts, "twist") {
+ t.Errorf("expected a twist conflict, got: %+v", conflicts)
+ }
+}
+
+func TestBuildGridConflictsDiagonalClean(t *testing.T) {
+ dir := t.TempDir()
+ writeGridTestRoom(t, dir, 1, "exits:\n northeast: 2\n")
+ writeGridTestRoom(t, dir, 2, "name: two\n exits:\n southwest: 1\n")
+
+ w := New(dir)
+ if conflicts := BuildGridConflicts(1, loadGridRoom(w)); len(conflicts) != 0 {
+ t.Errorf("expected no conflicts for clean diagonal, got: %+v", conflicts)
+ }
+}
+
+// TestBuildGridConflictsHypotheticalEdit models the insert-remove use case:
+// the load closure returns edited copies of rooms reflecting a proposed edit,
+// and BuildGridConflicts reports whether the resulting world is still clean.
+// Here we model "insert a new room 5 between 1 and 2 on the east axis" and
+// confirm the (clean) post-edit world has no conflicts.
+func TestBuildGridConflictsHypotheticalInsert(t *testing.T) {
+ dir := t.TempDir()
+ writeGridTestRoom(t, dir, 1, "name: one\nexits:\n east: 2\n")
+ writeGridTestRoom(t, dir, 2, "name: two\nexits:\n west: 1\n")
+ w := New(dir)
+
+ newRoom := &Room{Name: "new", Exits: map[ExitDir]ExitDef{
+ East: {Room: 2}, West: {Room: 1},
+ }}
+ load := func(id int) (*Room, bool) {
+ if id == 5 {
+ return newRoom, true
+ }
+ r, err := w.LoadRoom(id)
+ if err != nil {
+ return nil, false
+ }
+ copy := *r
+ if r.Exits != nil {
+ copy.Exits = make(map[ExitDir]ExitDef, len(r.Exits))
+ for k, v := range r.Exits {
+ if id == 1 && k == East {
+ copy.Exits[k] = ExitDef{Room: 5, Condition: v.Condition, BlockedMessage: v.BlockedMessage, SetFlags: v.SetFlags, SetPlayerFlags: v.SetPlayerFlags, Hidden: v.Hidden, AlwaysBlocked: v.AlwaysBlocked}
+ continue
+ }
+ if id == 2 && k == West {
+ copy.Exits[k] = ExitDef{Room: 5, Condition: v.Condition, BlockedMessage: v.BlockedMessage, SetFlags: v.SetFlags, SetPlayerFlags: v.SetPlayerFlags, Hidden: v.Hidden, AlwaysBlocked: v.AlwaysBlocked}
+ continue
+ }
+ copy.Exits[k] = v
+ }
+ }
+ return &copy, true
+ }
+ if conflicts := BuildGridConflicts(1, load); len(conflicts) != 0 {
+ t.Errorf("clean insert should produce no conflicts, got: %+v", conflicts)
+ }
+}
+
+// TestBuildGridConflictsHypotheticalRemoveOverlap models "remove room 2 and
+// pull the beyond-rooms back toward room 1". Layout:
+//
+// 1 east→2 east→3 south→6 (6 at (2,1,0))
+// 1 south→4 east→5 (5 at (1,1,0))
+//
+// After removing 2 and rewiring 1.Exits[East]=3, 3.Exits[West]=1, the BFS
+// re-places 3 at (1,0,0) and 6 at (1,1,0) — colliding with 5 which stays at
+// (1,1,0). The helper must report an overlap without anything being written.
+func TestBuildGridConflictsHypotheticalRemoveOverlap(t *testing.T) {
+ dir := t.TempDir()
+ writeGridTestRoom(t, dir, 1, "exits:\n east: 2\n south: 4\n")
+ writeGridTestRoom(t, dir, 2, "exits:\n east: 3\n west: 1\n")
+ writeGridTestRoom(t, dir, 3, "exits:\n south: 6\n west: 2\n")
+ writeGridTestRoom(t, dir, 4, "exits:\n east: 5\n north: 1\n")
+ writeGridTestRoom(t, dir, 5, "exits:\n west: 4\n")
+ writeGridTestRoom(t, dir, 6, "exits:\n north: 3\n")
+ w := New(dir)
+
+ load := func(id int) (*Room, bool) {
+ if id == 2 {
+ return nil, false // room removed
+ }
+ r, err := w.LoadRoom(id)
+ if err != nil {
+ return nil, false
+ }
+ rc := *r
+ rc.Exits = make(map[ExitDir]ExitDef, len(r.Exits))
+ for k, v := range r.Exits {
+ rc.Exits[k] = v
+ }
+ if id == 1 {
+ rc.Exits[East] = ExitDef{Room: 3}
+ }
+ if id == 3 {
+ rc.Exits[West] = ExitDef{Room: 1}
+ }
+ return &rc, true
+ }
+ conflicts := BuildGridConflicts(1, load)
+ if !hasConflictKind(conflicts, "overlap") {
+ t.Errorf("expected an overlap from pulling 6 onto 5's cell, got: %+v", conflicts)
+ }
+}