package validate import ( "os" "path/filepath" "strconv" "strings" "testing" "thehouseoficarus/internal/world" ) func writeGridRoom(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 runGridCheck(t *testing.T, rooms map[int]string, roots ...int) []Issue { t.Helper() dir := t.TempDir() for id, body := range rooms { writeGridRoom(t, dir, id, body) } return validateRoomGrid(Source{World: world.New(dir), RootRooms: roots}) } func containsMsg(issues []Issue, substr string) bool { for _, iss := range issues { if strings.Contains(iss.Message, substr) { return true } } return false } func TestGridCleanLayout(t *testing.T) { // A consistent 2x2 block: 4 is reached the same way from 2 and from 3. rooms := map[int]string{ 1: "exits:\n south: 3\n east: 2\n", 2: "exits:\n south: 4\n", 3: "exits:\n east: 4\n", 4: "name: corner\n", } if issues := runGridCheck(t, rooms, 1); len(issues) != 0 { t.Errorf("expected no grid issues, got: %+v", issues) } } func TestGridOverlap(t *testing.T) { // rooms 4 and 5 both resolve to grid (1,1). rooms := map[int]string{ 1: "exits:\n south: 3\n east: 2\n", 2: "exits:\n south: 5\n", 3: "exits:\n east: 4\n", 4: "name: four\n", 5: "name: five\n", } issues := runGridCheck(t, rooms, 1) if !containsMsg(issues, "Grid overlap") { t.Errorf("expected a grid overlap, got: %+v", issues) } for _, iss := range issues { if iss.Level != "ERROR" { t.Errorf("grid issues should be ERROR, got %q", iss.Level) } } } func TestGridTwist(t *testing.T) { // room 3 is forced onto two different cells. rooms := map[int]string{ 1: "exits:\n south: 4\n east: 2\n", 2: "exits:\n east: 3\n", 4: "exits:\n east: 3\n", 3: "name: three\n", } issues := runGridCheck(t, rooms, 1) if !containsMsg(issues, "Grid twist") { t.Errorf("expected a grid twist, got: %+v", issues) } } func TestGridMultiPlaneViaUpDown(t *testing.T) { // Plane A is just room 1. Going up seeds plane B (origin 10), which has an // overlap. This proves up/down crosses planes and frames are independent // (room 1 and room 10 both sit at (0,0) without conflicting). rooms := map[int]string{ 1: "exits:\n up: 10\n", 10: "exits:\n south: 12\n east: 11\n", 11: "exits:\n south: 14\n", 12: "exits:\n east: 13\n", 13: "name: thirteen\n", 14: "name: fourteen\n", } issues := runGridCheck(t, rooms, 1) if !containsMsg(issues, "plane origin 10") { t.Errorf("expected an overlap in plane origin 10, got: %+v", issues) } } func TestGridDiagonalClean(t *testing.T) { // Room 1 -> NE -> Room 2 (at 1,-1). Clean diagonal placement. rooms := map[int]string{ 1: "exits:\n northeast: 2\n", 2: "name: two\n exits:\n southwest: 1\n", } if issues := runGridCheck(t, rooms, 1); len(issues) != 0 { t.Errorf("expected no grid issues, got: %+v", issues) } } func TestGridDiagonalOverlap(t *testing.T) { // Two different rooms both resolve to (2,1): // 1→SE→2→E→4 lands 4 at (2,1). // 1→E→3→SE→5 lands 5 at (2,1). Room 4 != 5 → overlap. rooms := map[int]string{ 1: "exits:\n southeast: 2\n east: 3\n", 2: "exits:\n east: 4\n", 3: "exits:\n southeast: 5\n", 4: "name: four\n", 5: "name: five\n", } issues := runGridCheck(t, rooms, 1) if !containsMsg(issues, "Grid overlap") { t.Errorf("expected a grid overlap via diagonal, got: %+v", issues) } } func TestGridDiagonalTwist(t *testing.T) { // Room 4 forced onto two different cells: // path1: 1→E→2 (2 at 1,0). 2→NE→4 places 4 at (2,-1). // path2: 1→N→3 (3 at 0,-1). 3→E→5 (5 at 1,-1). 5→N→4 wants 4 at (1,-2). // 4 already placed at (2,-1) but wanted at (1,-2) → twist. rooms := map[int]string{ 1: "exits:\n east: 2\n north: 3\n", 2: "exits:\n northeast: 4\n", 3: "exits:\n east: 5\n", 4: "name: four\n", 5: "exits:\n north: 4\n", } issues := runGridCheck(t, rooms, 1) if !containsMsg(issues, "Grid twist") { t.Errorf("expected a grid twist via diagonal, got: %+v", issues) } }