aboutsummaryrefslogtreecommitdiff
path: root/internal/world/room_test.go
blob: 855eba5bc65063824f23eee6d48f82e4591c2736 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
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")
	}
}