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
|
package admin
// Regression: a failed room create (POST /api/rooms with link_from) must
// not leave the source room with a dangling exit pointing at a room that
// was never written, and must not push an undo entry for work that never
// committed. The destination is committed before the source exit so the
// failure happens before any source-side mutation.
import (
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"
"thehouseoficarus/internal/config"
"thehouseoficarus/internal/world"
)
func TestFailedRoomCreateRollsBackSourceExit(t *testing.T) {
s := newTestAdminServer(t, map[int]string{1: "id: 1\nname: Origin\n"})
s.cfg = config.Default()
s.cfg.StartingRoom = 1
// A directory at the destination path makes the destination file write
// fail deterministically while the source room is still readable.
if err := os.Mkdir(filepath.Join(s.dataDir, "rooms", "2.yaml"), 0o755); err != nil {
t.Fatal(err)
}
rec := httptest.NewRecorder()
s.handleRooms(rec, httptest.NewRequest(http.MethodPost, "/api/rooms",
strings.NewReader(`{"name":"Next","link_from":1,"link_dir":"north"}`)))
if !strings.Contains(rec.Body.String(), "error") {
t.Fatalf("expected the create to fail; got %s", rec.Body.String())
}
src, err := s.world.LoadRoom(1)
if err != nil {
t.Fatal(err)
}
if exit, ok := src.Exits[world.North]; ok {
t.Fatalf("failed create left a dangling exit to nonexistent room %d", exit.Room)
}
if info := s.undoStack.Info(); info.StackSize != 0 {
t.Fatalf("failed create pushed an undo entry: %+v", info)
}
}
// The reciprocal exit still lands in the written destination file when the
// create succeeds.
func TestSuccessfulRoomCreateWritesReciprocalExit(t *testing.T) {
s := newTestAdminServer(t, map[int]string{1: "id: 1\nname: Origin\n"})
s.cfg = config.Default()
s.cfg.StartingRoom = 1
rec := httptest.NewRecorder()
s.handleRooms(rec, httptest.NewRequest(http.MethodPost, "/api/rooms",
strings.NewReader(`{"name":"Next","link_from":1,"link_dir":"north"}`)))
if strings.Contains(rec.Body.String(), "error") {
t.Fatalf("create failed: %s", rec.Body.String())
}
dest, err := s.world.LoadRoom(2)
if err != nil {
t.Fatalf("destination room not loadable: %v", err)
}
if dest.Exits[world.South].Room != 1 {
t.Fatalf("destination room missing its reciprocal exit south: %+v", dest.Exits)
}
src, err := s.world.LoadRoom(1)
if err != nil {
t.Fatal(err)
}
if src.Exits[world.North].Room != 2 {
t.Fatalf("source room missing its north exit: %+v", src.Exits)
}
}
|