aboutsummaryrefslogtreecommitdiff
path: root/internal/admin/api_rooms_create_test.go
blob: 38d6faece7b37367c2813575e01b87d3ab4dabde (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
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)
	}
}