Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

pausable_test.gno

5.71 Kb · 161 lines
  1package pausable
  2
  3import (
  4	"testing"
  5
  6	"gno.land/p/nt/uassert/v0"
  7)
  8
  9func TestParse(t *testing.T) {
 10	tests := []struct {
 11		raw        string
 12		wantLevel  Level
 13		wantReason string
 14		wantOK     bool
 15	}{
 16		{"", Running, "", true},
 17		{"   ", Running, "", true},
 18		{"running", Running, "", true},
 19		{"readonly", ReadOnly, "", true},
 20		{"paused", Paused, "", true},
 21		{"Paused", Paused, "", true},   // case-insensitive
 22		{"  paused  ", Paused, "", true},
 23		{"paused: migrating storage", Paused, "migrating storage", true},
 24		{"readonly:  spaced  ", ReadOnly, "spaced", true},
 25		{"running: nothing wrong", Running, "nothing wrong", true},
 26		// A reason containing the separator keeps everything after the first
 27		// one, so a URL or a time survives.
 28		{"paused: back at 14:00 UTC", Paused, "back at 14:00 UTC", true},
 29		// Not recognised. These are exactly the values that must NOT read as
 30		// Running.
 31		{"yes", Running, "", false},
 32		{"true", Running, "", false},
 33		{"1", Running, "", false},
 34		{"stop", Running, "", false},
 35		{"read-only", Running, "", false},
 36	}
 37	for _, tt := range tests {
 38		got, ok := Parse(tt.raw)
 39		uassert.Equal(t, tt.wantOK, ok, "Parse("+tt.raw+") ok")
 40		if !ok {
 41			continue
 42		}
 43		uassert.Equal(t, int(tt.wantLevel), int(got.Level), "Parse("+tt.raw+") level")
 44		uassert.Equal(t, tt.wantReason, got.Reason, "Parse("+tt.raw+") reason")
 45	}
 46}
 47
 48// TestMustParseFailsClosed is the whole safety argument of this package. Every
 49// value here is something a person might plausibly type, and every one of them
 50// has to stop the realm rather than quietly leave it running.
 51func TestMustParseFailsClosed(t *testing.T) {
 52	for _, raw := range []string{"yes", "true", "1", "on", "stop", "PAUSE", "pasued"} {
 53		got := MustParse(raw)
 54		uassert.Equal(t, int(Paused), int(got.Level), "MustParse("+raw+") must fail closed")
 55		uassert.False(t, got.AllowsWrite(), "MustParse("+raw+") must refuse writes")
 56		uassert.True(t, len(got.Reason) > 0, "MustParse("+raw+") must say why")
 57	}
 58
 59	// And the values it does understand are untouched.
 60	uassert.Equal(t, int(Running), int(MustParse("").Level))
 61	uassert.Equal(t, int(ReadOnly), int(MustParse("readonly").Level))
 62}
 63
 64func TestAllows(t *testing.T) {
 65	tests := []struct {
 66		level     Level
 67		read      bool
 68		write     bool
 69		isPaused  bool
 70	}{
 71		{Running, true, true, false},
 72		{ReadOnly, true, false, true},
 73		{Paused, false, false, true},
 74	}
 75	for _, tt := range tests {
 76		s := State{Level: tt.level}
 77		uassert.Equal(t, tt.read, s.AllowsRead(), tt.level.String()+" read")
 78		uassert.Equal(t, tt.write, s.AllowsWrite(), tt.level.String()+" write")
 79		uassert.Equal(t, tt.isPaused, s.IsPaused(), tt.level.String()+" isPaused")
 80	}
 81}
 82
 83func TestStrictest(t *testing.T) {
 84	run := State{Level: Running}
 85	ro := State{Level: ReadOnly, Reason: "scoped"}
 86	paused := State{Level: Paused, Reason: "global"}
 87
 88	uassert.Equal(t, int(ReadOnly), int(Strictest(run, ro).Level))
 89	uassert.Equal(t, int(ReadOnly), int(Strictest(ro, run).Level))
 90	uassert.Equal(t, int(Paused), int(Strictest(paused, ro).Level))
 91	uassert.Equal(t, int(Paused), int(Strictest(ro, paused).Level))
 92
 93	// A stale per-realm entry can never re-open a realm during a global halt.
 94	// This is the property the whole precedence rule exists for.
 95	uassert.Equal(t, int(Paused), int(Strictest(paused, run).Level),
 96		"a per-realm 'running' cannot defeat a global pause")
 97
 98	// The reason travels with the level that won.
 99	uassert.Equal(t, "global", Strictest(paused, ro).Reason)
100	uassert.Equal(t, "scoped", Strictest(run, ro).Reason)
101
102	// On a tie the first argument wins, so a caller passing (global, scoped)
103	// keeps the global explanation.
104	a := State{Level: Paused, Reason: "first"}
105	b := State{Level: Paused, Reason: "second"}
106	uassert.Equal(t, "first", Strictest(a, b).Reason)
107}
108
109func TestStringRoundTrips(t *testing.T) {
110	for _, raw := range []string{"", "readonly", "paused", "paused: migrating storage"} {
111		s, ok := Parse(raw)
112		uassert.True(t, ok, "Parse("+raw+")")
113		again, ok := Parse(s.String())
114		uassert.True(t, ok, "Parse(String()) for "+raw)
115		uassert.Equal(t, int(s.Level), int(again.Level), "level round-trip for "+raw)
116		uassert.Equal(t, s.Reason, again.Reason, "reason round-trip for "+raw)
117	}
118
119	// The zero State renders empty, so an unset setting stays unset rather
120	// than being written back as the word "running".
121	uassert.Equal(t, "", State{}.String())
122	uassert.Equal(t, "running: why", State{Reason: "why"}.String())
123}
124
125func TestAsserts(cur realm, t *testing.T) {
126	State{}.AssertWritable()
127	State{}.AssertReadable()
128	State{Level: ReadOnly}.AssertReadable()
129
130	uassert.PanicsWithMessage(t, cur, "writes are paused", func() {
131		State{Level: ReadOnly}.AssertWritable()
132	})
133	uassert.PanicsWithMessage(t, cur, "writes are paused: migrating", func() {
134		State{Level: Paused, Reason: "migrating"}.AssertWritable()
135	})
136	uassert.PanicsWithMessage(t, cur, "paused: migrating", func() {
137		State{Level: Paused, Reason: "migrating"}.AssertReadable()
138	})
139}
140
141func TestNotice(t *testing.T) {
142	uassert.Equal(t, "", State{}.Notice(), "a running realm shows no banner")
143
144	uassert.Equal(t,
145		"> **Read-only.** This realm is still readable, but not accepting changes.",
146		State{Level: ReadOnly}.Notice())
147
148	uassert.Equal(t,
149		"> **Paused.** This realm is not accepting anything right now. back in an hour",
150		State{Level: Paused, Reason: "back in an hour"}.Notice())
151}
152
153// TestNoticeStaysOneBlock covers the reason a manager typed. It lands inside a
154// markdown blockquote, where a newline ends the quote and lets the rest render
155// as ordinary page content, so it is folded to one line.
156func TestNoticeStaysOneBlock(t *testing.T) {
157	s := State{Level: Paused, Reason: "line one\n\nline two\n# not a heading"}
158	uassert.Equal(t,
159		"> **Paused.** This realm is not accepting anything right now. line one line two # not a heading",
160		s.Notice())
161}