package world import ( "os" "path/filepath" "testing" "gopkg.in/yaml.v3" ) func writeRoom(t *testing.T, dir string, body string) { t.Helper() rooms := filepath.Join(dir, "rooms") if err := os.MkdirAll(rooms, 0o755); err != nil { t.Fatal(err) } path := filepath.Join(rooms, "1001.yaml") if err := os.WriteFile(path, []byte(body), 0o644); err != nil { t.Fatal(err) } } func TestRoomObjectReferenceVsLocal(t *testing.T) { dir := t.TempDir() body := `name: Test Room objects: - id: workbench - name: window hidden: true description: - text: "A small window." - id: control name: instrument panel aliases: [cockpit] description: - text: "A panel." ` // rebuild the path index so LoadRoom can find the room file writeRoom(t, dir, body) w := New(dir) room, err := w.LoadRoom(1001) if err != nil { t.Fatalf("LoadRoom: %v", err) } if len(room.Objects) != 3 { t.Fatalf("expected 3 objects, got %d", len(room.Objects)) } // reference if ref := room.Objects[0]; ref.Local != nil || ref.ID != "workbench" { t.Errorf("object 0 should be a reference to workbench, got %+v (local=%v)", ref, ref.Local) } // local, id derived from the normalized name in := room.Objects[1] if in.Local == nil { t.Fatalf("object 1 should be local") } if in.ID != "window" { t.Errorf("local id should derive from name to %q, got %q", "window", in.ID) } if !in.Local.Hidden || in.Local.Name != "window" { t.Errorf("local fields not parsed: %+v", in.Local) } // local with a multi-word name: id is the normalized name (spaces kept), // and an explicit `id:` is NOT used as identity (it is preserved on the // def only so validation can flag it). ex := room.Objects[2] if ex.Local == nil { t.Fatalf("object 2 should be local") } if ex.ID != "instrument panel" { t.Errorf("local id should be normalized name %q, got %q", "instrument panel", ex.ID) } if ex.Local.ID != "control" { t.Errorf("explicit id should be preserved on def for validation, got %q", ex.Local.ID) } if len(ex.Local.Aliases) != 1 || ex.Local.Aliases[0] != "cockpit" { t.Errorf("local aliases not parsed: %+v", ex.Local.Aliases) } } func TestNormalizeObjectName(t *testing.T) { cases := map[string]string{ "window": "window", "Instrument Panel": "instrument panel", " Safety Card ": "safety card", } for in, want := range cases { if got := NormalizeObjectName(in); got != want { t.Errorf("NormalizeObjectName(%q) = %q, want %q", in, got, want) } } } func TestExitDefUnmarshalAlwaysBlocked(t *testing.T) { y := `exits: north: room: 5 always_blocked: true south: room: 3 east: room: 7 hidden: true ` var room struct { Exits map[ExitDir]ExitDef `yaml:"exits"` } if err := yaml.Unmarshal([]byte(y), &room); err != nil { t.Fatalf("unmarshal: %v", err) } if len(room.Exits) != 3 { t.Fatalf("expected 3 exits, got %d", len(room.Exits)) } n := room.Exits[North] if !n.AlwaysBlocked { t.Error("north exit should have AlwaysBlocked=true") } if n.Room != 5 { t.Errorf("north target = %d, want 5", n.Room) } s := room.Exits[South] if s.Room != 3 { t.Errorf("south target = %d, want 3", s.Room) } if s.AlwaysBlocked { t.Error("south exit should not have AlwaysBlocked") } e := room.Exits[East] if !e.Hidden { t.Error("east exit should have Hidden=true") } if e.AlwaysBlocked { t.Error("east exit should not have AlwaysBlocked") } }