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

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

nightsky_test.gno

7.69 Kb · 271 lines
  1package nightsky
  2
  3import (
  4	"testing"
  5	"time"
  6
  7	"gno.land/p/nt/avl/v0"
  8	"gno.land/p/nt/uassert/v0"
  9	"gno.land/p/nt/urequire/v0"
 10)
 11
 12const (
 13	owner = "g1owner"
 14	guest = "g1guest"
 15)
 16
 17func newTestRealm() *TelescopeRealm {
 18	cfg := NewTelescopeConfig("Test Scope", "Seestar S50", 49.0, 1.0, owner, "/r/test/telescope")
 19	return NewTelescopeRealm(&cfg)
 20}
 21
 22func TestNewTelescopeConfig(t *testing.T) {
 23	cfg := NewTelescopeConfig("Scope", "Model", 1.5, 2.5, owner, "/r/x/y")
 24	uassert.Equal(t, "Scope", cfg.Name)
 25	uassert.Equal(t, "offline", cfg.Status) // starts offline
 26	uassert.Equal(t, owner, cfg.Owner)
 27
 28	cfg.UpdateStatus("online")
 29	uassert.Equal(t, "online", cfg.Status)
 30}
 31
 32func TestValidateCommand(cur realm, t *testing.T) {
 33	cfg := NewTelescopeConfig("Scope", "Model", 0, 0, owner, "/r/x/y")
 34
 35	// Valid capture and stop should not panic.
 36	uassert.NotPanics(t, cur, func() {
 37		ValidateCommand(TelescopeCommand{CommandType: "capture", TargetRA: 5.5, TargetDec: 22.5, Exposure: 60}, cfg)
 38	})
 39	uassert.NotPanics(t, cur, func() {
 40		// stop ignores coordinates entirely.
 41		ValidateCommand(TelescopeCommand{CommandType: "stop", TargetRA: 999, TargetDec: 999, Exposure: 0}, cfg)
 42	})
 43
 44	cases := []struct {
 45		name string
 46		cmd  TelescopeCommand
 47		msg  string
 48	}{
 49		{"bad type", TelescopeCommand{CommandType: "explode"}, "invalid command type"},
 50		{"RA low", TelescopeCommand{CommandType: "capture", TargetRA: -1, Exposure: 10}, "invalid RA"},
 51		{"RA high", TelescopeCommand{CommandType: "capture", TargetRA: 24, Exposure: 10}, "invalid RA"},
 52		{"Dec low", TelescopeCommand{CommandType: "capture", TargetRA: 1, TargetDec: -91, Exposure: 10}, "invalid Dec"},
 53		{"Dec high", TelescopeCommand{CommandType: "capture", TargetRA: 1, TargetDec: 91, Exposure: 10}, "invalid Dec"},
 54		{"exp low", TelescopeCommand{CommandType: "capture", TargetRA: 1, Exposure: 0}, "invalid exposure"},
 55		{"exp high", TelescopeCommand{CommandType: "capture", TargetRA: 1, Exposure: 301}, "invalid exposure"},
 56	}
 57	for _, tc := range cases {
 58		cmd := tc.cmd
 59		uassert.PanicsContains(t, cur, tc.msg, func() { ValidateCommand(cmd, cfg) }, tc.name)
 60	}
 61}
 62
 63func TestIsAccessAllowed(t *testing.T) {
 64	rules := avl.NewTree()
 65
 66	// Unknown address: denied.
 67	uassert.False(t, IsAccessAllowed(guest, rules))
 68
 69	// Never-expires rule (zero time): allowed.
 70	rules.Set(guest, AccessRule{AllowedAddress: guest, ExpiresAt: time.Time{}})
 71	uassert.True(t, IsAccessAllowed(guest, rules))
 72
 73	// Future expiry: allowed.
 74	rules.Set(guest, AccessRule{AllowedAddress: guest, ExpiresAt: time.Now().Add(time.Hour)})
 75	uassert.True(t, IsAccessAllowed(guest, rules))
 76
 77	// Past expiry: denied.
 78	rules.Set(guest, AccessRule{AllowedAddress: guest, ExpiresAt: time.Now().Add(-time.Hour)})
 79	uassert.False(t, IsAccessAllowed(guest, rules))
 80}
 81
 82func TestAccessFlow(cur realm, t *testing.T) {
 83	r := newTestRealm()
 84
 85	// Owner can always submit.
 86	urequire.NotPanics(t, cur, func() {
 87		r.SubmitCommand(owner, "capture", 5, 10, 30)
 88	})
 89
 90	// Guest without a grant is denied.
 91	uassert.PanicsContains(t, cur, "access denied", func() {
 92		r.SubmitCommand(guest, "capture", 5, 10, 30)
 93	})
 94
 95	// Grant, then guest can submit.
 96	r.GrantAccess(owner, guest, 0) // 0 = never expires
 97	uassert.Equal(t, 1, r.GetAccessRuleCount())
 98	urequire.NotPanics(t, cur, func() {
 99		r.SubmitCommand(guest, "capture", 5, 10, 30)
100	})
101
102	// Re-granting the same address does not duplicate the rule.
103	r.GrantAccess(owner, guest, 5)
104	uassert.Equal(t, 1, r.GetAccessRuleCount())
105
106	// Revoke, then guest is denied again.
107	r.RevokeAccess(owner, guest)
108	uassert.Equal(t, 0, r.GetAccessRuleCount())
109	uassert.PanicsContains(t, cur, "access denied", func() {
110		r.SubmitCommand(guest, "capture", 5, 10, 30)
111	})
112
113	// Non-owner cannot grant access.
114	uassert.PanicsContains(t, cur, "owner only", func() {
115		r.GrantAccess(guest, "g1evil", 0)
116	})
117}
118
119func TestCommandQueueFIFO(cur realm, t *testing.T) {
120	r := newTestRealm()
121
122	r.SubmitCommand(owner, "capture", 1, 1, 10)
123	r.SubmitCommand(owner, "capture", 2, 2, 20)
124	r.SubmitCommand(owner, "stop", 0, 0, 0)
125	uassert.Equal(t, 3, r.GetCommandCount())
126
127	// FIFO order.
128	c1 := r.GetNextCommand(owner)
129	uassert.Equal(t, "capture", c1.CommandType)
130	uassert.Equal(t, int64(10), int64(c1.Exposure))
131
132	c2 := r.GetNextCommand(owner)
133	uassert.Equal(t, int64(20), int64(c2.Exposure))
134
135	c3 := r.GetNextCommand(owner)
136	uassert.Equal(t, "stop", c3.CommandType)
137
138	uassert.Equal(t, 0, r.GetCommandCount())
139
140	// Draining an empty queue panics.
141	uassert.PanicsContains(t, cur, "no commands", func() { r.GetNextCommand(owner) })
142
143	// Only the owner may pull commands.
144	r.SubmitCommand(owner, "stop", 0, 0, 0)
145	uassert.PanicsContains(t, cur, "owner only", func() { r.GetNextCommand(guest) })
146
147	// Emergency stop clears everything and sets status online.
148	r.ClearCommandQueue(owner)
149	uassert.Equal(t, 0, r.GetCommandCount())
150	uassert.Equal(t, "online", r.Config.Status)
151}
152
153func TestRecordCaptureUpdatesStatus(cur realm, t *testing.T) {
154	r := newTestRealm()
155
156	// Submitting a command flips status to busy.
157	r.SubmitCommand(owner, "capture", 5, 10, 30)
158	uassert.Equal(t, "busy", r.Config.Status)
159
160	// Recording a capture flips it back to online and stores the capture.
161	r.RecordCapture(owner, "https://img/1.png", 5, 10, 30, owner)
162	uassert.Equal(t, "online", r.Config.Status)
163	uassert.Equal(t, 1, r.GetCaptureCount())
164
165	// Empty image URL is rejected.
166	uassert.PanicsContains(t, cur, "image URL", func() {
167		r.RecordCapture(owner, "", 5, 10, 30, owner)
168	})
169
170	// Non-owner cannot record.
171	uassert.PanicsContains(t, cur, "owner only", func() {
172		r.RecordCapture(guest, "https://img/2.png", 5, 10, 30, guest)
173	})
174}
175
176// --- CaptureFeed tests ------------------------------------------------------
177
178func mkCap(url string) CaptureResult {
179	return CaptureResult{ImageURL: url}
180}
181
182func TestCaptureFeedOrderAndLatest(t *testing.T) {
183	f := NewCaptureFeed(10)
184	uassert.Equal(t, 0, f.Len())
185
186	_, ok := f.Latest()
187	uassert.False(t, ok)
188
189	f.Add(mkCap("a"))
190	f.Add(mkCap("b"))
191	f.Add(mkCap("c"))
192	uassert.Equal(t, 3, f.Len())
193
194	latest, ok := f.Latest()
195	urequire.True(t, ok)
196	uassert.Equal(t, "c", latest.ImageURL)
197
198	// Newest-first iteration order.
199	var got []string
200	f.IterateNewest(func(c CaptureResult) bool {
201		got = append(got, c.ImageURL)
202		return false
203	})
204	urequire.Equal(t, 3, len(got))
205	uassert.Equal(t, "c", got[0])
206	uassert.Equal(t, "b", got[1])
207	uassert.Equal(t, "a", got[2])
208
209	// Early stop.
210	count := 0
211	f.IterateNewest(func(c CaptureResult) bool {
212		count++
213		return true // stop after first
214	})
215	uassert.Equal(t, 1, count)
216}
217
218func TestCaptureFeedTrim(t *testing.T) {
219	f := NewCaptureFeed(3)
220	f.Add(mkCap("1"))
221	f.Add(mkCap("2"))
222	f.Add(mkCap("3"))
223	f.Add(mkCap("4")) // evicts "1"
224	f.Add(mkCap("5")) // evicts "2"
225
226	uassert.Equal(t, 3, f.Len())
227
228	var got []string
229	f.IterateNewest(func(c CaptureResult) bool {
230		got = append(got, c.ImageURL)
231		return false
232	})
233	// Newest-first: 5, 4, 3 — oldest two evicted.
234	uassert.Equal(t, "5", got[0])
235	uassert.Equal(t, "4", got[1])
236	uassert.Equal(t, "3", got[2])
237}
238
239func TestCaptureFeedRemoveAt(t *testing.T) {
240	f := NewCaptureFeed(10)
241	f.Add(mkCap("a"))
242	f.Add(mkCap("b"))
243	f.Add(mkCap("c")) // newest-first: c(0), b(1), a(2)
244
245	// Out-of-range is a no-op returning false.
246	uassert.False(t, f.RemoveAt(-1))
247	uassert.False(t, f.RemoveAt(3))
248	uassert.Equal(t, 3, f.Len())
249
250	// Remove the middle (b).
251	uassert.True(t, f.RemoveAt(1))
252	uassert.Equal(t, 2, f.Len())
253
254	var got []string
255	f.IterateNewest(func(c CaptureResult) bool {
256		got = append(got, c.ImageURL)
257		return false
258	})
259	uassert.Equal(t, "c", got[0])
260	uassert.Equal(t, "a", got[1])
261
262	// Removing newest (c).
263	uassert.True(t, f.RemoveAt(0))
264	latest, ok := f.Latest()
265	urequire.True(t, ok)
266	uassert.Equal(t, "a", latest.ImageURL)
267}
268
269func TestNewCaptureFeedRejectsNonPositive(cur realm, t *testing.T) {
270	uassert.PanicsContains(t, cur, "must be positive", func() { NewCaptureFeed(0) })
271}