diff options
Diffstat (limited to 'internal/admin/api_rooms_create_test.go')
| -rw-r--r-- | internal/admin/api_rooms_create_test.go | 78 |
1 files changed, 78 insertions, 0 deletions
diff --git a/internal/admin/api_rooms_create_test.go b/internal/admin/api_rooms_create_test.go new file mode 100644 index 0000000..38d6fae --- /dev/null +++ b/internal/admin/api_rooms_create_test.go @@ -0,0 +1,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) + } +} |
