aboutsummaryrefslogtreecommitdiff
path: root/internal/game/hardening_admin_player_file_online_test.go
blob: f0c64f3aba52e2da96c5cbe6f9b2fb8792e47b2d (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
package game

// Regression coverage for Game.AdminPlayerFileOnline, the hook the admin raw
// file editor uses (admin.AdminServer.SetPlayerFileOnlineCheck) to refuse
// editing a character/account file whose character is live.

import "testing"

func TestAdminPlayerFileOnlineRefusesLivePlayerFiles(t *testing.T) {
	h := newHarness(t)
	h.seedAccount("alice")
	h.seedCharacter("alice", "alice")
	// A second character of the same account, offline, so the account check
	// cannot pass merely because the file exists.
	h.seedCharacter("alicia", "alice")
	h.seedAccount("carol")
	h.seedCharacter("carol", "carol")

	sess, _ := h.connect("alice")
	if sess.Player == nil || sess.Player.Name != "alice" {
		t.Fatalf("alice is not registered as online: %+v", sess.Player)
	}

	cases := []struct {
		name string
		path string
		want bool
	}{
		{"online character", "players/characters/alice.yaml", true},
		{"offline character of a live account", "players/characters/alicia.yaml", false},
		{"offline character", "players/characters/carol.yaml", false},
		{"unknown character", "players/characters/nobody.yaml", false},
		{"account of an online character", "players/accounts/alice.yaml", true},
		{"account with no online character", "players/accounts/carol.yaml", false},
		{"unknown account", "players/accounts/nobody.yaml", false},
		{"leading slash", "/players/characters/alice.yaml", true},
		{"data/ prefix", "data/players/accounts/alice.yaml", true},
		{"non-yaml file", "players/characters/alice.txt", false},
		{"bare directory", "players/characters", false},
		{"nested path", "players/characters/sub/alice.yaml", false},
		{"non-player file", "rooms/1.yaml", false},
		{"empty path", "", false},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			if got := h.g.AdminPlayerFileOnline(tc.path); got != tc.want {
				t.Fatalf("AdminPlayerFileOnline(%q) = %v, want %v", tc.path, got, tc.want)
			}
		})
	}
}

// TestAdminPlayerFileOnlineIgnoresMenuOnlySession pins that an account sitting
// in the character menu is NOT treated as an online character: only a live
// character (a loggedInChars entry) makes its file, or its account's file,
// editable-refused for that reason.
func TestAdminPlayerFileOnlineIgnoresMenuOnlySession(t *testing.T) {
	h := newHarness(t)
	h.seedAccount("dave")
	h.seedCharacter("dave", "dave")
	h.connectMenu("dave")

	if h.g.AdminPlayerFileOnline("players/characters/dave.yaml") {
		t.Fatal("a menu session was treated as a live character")
	}
	if h.g.AdminPlayerFileOnline("players/accounts/dave.yaml") {
		t.Fatal("a menu session was treated as a live account character")
	}
}