nightsky_test.gno
6.87 Kb · 205 lines
1package nightsky
2
3import (
4 "testing"
5
6 "gno.land/p/nt/avl/v0"
7 "gno.land/p/nt/testutils/v0"
8 "gno.land/p/nt/uassert/v0"
9 "gno.land/p/nt/urequire/v0"
10 nightsky "gno.land/p/nym-vikbez000/nightsky0"
11)
12
13const scopeA = "gno.land/r/test/scopea"
14
15func reset() {
16 telescopes = avl.NewTree()
17 recentCaptures = nightsky.NewCaptureFeed(maxRecentCaptures)
18}
19
20func cfg(name, owner string) nightsky.TelescopeConfig {
21 return nightsky.NewTelescopeConfig(name, "Seestar S50", 1, 2, owner, "")
22}
23
24// registerAs simulates a telescope code realm at pkgPath crossing into the
25// registry to register itself.
26func registerAs(cur realm, pkgPath string, c nightsky.TelescopeConfig) {
27 testing.SetRealm(testing.NewCodeRealm(pkgPath))
28 Register(cross(cur), c)
29}
30
31func TestRegisterAndDedup(cur realm, t *testing.T) {
32 reset()
33
34 registerAs(cur, scopeA, cfg("Scope A", "g1owner"))
35 urequire.Equal(t, 1, GetTelescopeCount())
36
37 // RealmPath is stamped from the caller's pkgPath, not the (empty) input.
38 stored, ok := telescopes.Get(scopeA)
39 urequire.True(t, ok)
40 uassert.Equal(t, scopeA, stored.(nightsky.TelescopeConfig).RealmPath)
41
42 // Duplicate registration from the same realm is ignored.
43 registerAs(cur, scopeA, cfg("Scope A", "g1owner"))
44 uassert.Equal(t, 1, GetTelescopeCount())
45
46 // A second telescope from a different realm is added.
47 registerAs(cur, "gno.land/r/test/scopeb", cfg("Scope B", "g1owner2"))
48 uassert.Equal(t, 2, GetTelescopeCount())
49}
50
51func TestRegisterRejectsUserCall(cur realm, t *testing.T) {
52 reset()
53
54 // A direct user transaction (no intermediary realm) must be rejected.
55 testing.SetRealm(testing.NewUserRealm(testutils.TestAddress("eoa")))
56 uassert.AbortsContains(t, cur, "realm code", func() {
57 Register(cross(cur), cfg("Bad", "g1owner"))
58 })
59 uassert.Equal(t, 0, GetTelescopeCount())
60}
61
62func TestOnlineCountAndStatus(cur realm, t *testing.T) {
63 reset()
64
65 c := cfg("Scope A", "g1owner")
66 c.UpdateStatus("online")
67 registerAs(cur, scopeA, c)
68 uassert.Equal(t, 1, GetOnlineTelescopeCount())
69
70 // The telescope realm updates its status to offline.
71 testing.SetRealm(testing.NewCodeRealm(scopeA))
72 UpdateTelescopeStatus(cross(cur), "offline")
73 uassert.Equal(t, 0, GetOnlineTelescopeCount())
74 uassert.Equal(t, 1, GetTelescopeCount())
75}
76
77func TestSubmitCaptureFeed(cur realm, t *testing.T) {
78 reset()
79 registerAs(cur, scopeA, cfg("Scope A", "g1owner"))
80
81 testing.SetRealm(testing.NewCodeRealm(scopeA))
82 SubmitCapture(cross(cur), "https://img/1.png", 5, 10, 30, "g1owner")
83 SubmitCapture(cross(cur), "https://img/2.png", 6, 11, 40, "g1owner")
84
85 latest, ok := recentCaptures.Latest()
86 urequire.True(t, ok)
87 uassert.Equal(t, "https://img/2.png", latest.ImageURL)
88 uassert.Equal(t, 2, recentCaptures.Len())
89}
90
91func TestAdminRemoveAndAuth(cur realm, t *testing.T) {
92 reset()
93 registerAs(cur, scopeA, cfg("Scope A", "g1owner"))
94 urequire.Equal(t, 1, GetTelescopeCount())
95
96 // Non-admin cannot remove.
97 testing.SetRealm(testing.NewUserRealm(testutils.TestAddress("intruder")))
98 uassert.AbortsContains(t, cur, "not owner", func() {
99 RemoveTelescope(cross(cur), scopeA)
100 })
101 uassert.Equal(t, 1, GetTelescopeCount())
102
103 // Admin can remove.
104 testing.SetRealm(testing.NewUserRealm(OwnableMain.Owner()))
105 RemoveTelescope(cross(cur), scopeA)
106 uassert.Equal(t, 0, GetTelescopeCount())
107
108 // Removing a missing telescope aborts.
109 uassert.AbortsContains(t, cur, "not found", func() {
110 RemoveTelescope(cross(cur), scopeA)
111 })
112}
113
114// --- Adversarial ("steal a telescope") tests -------------------------------
115// An attacker realm or EOA tries to hijack, impersonate, or wipe telescopes
116// that are not theirs.
117
118const (
119 victimPath = "gno.land/r/victim/scope"
120 attackerPath = "gno.land/r/attacker/evil"
121)
122
123// TestAttackerRealmCannotTouchVictim: registry mutations are keyed by the
124// caller's own pkgPath (cur.Previous().PkgPath()). An unregistered attacker
125// realm therefore cannot update, capture-to, or unregister a victim's entry —
126// it only ever addresses its own (nonexistent) registration.
127func TestAttackerRealmCannotTouchVictim(cur realm, t *testing.T) {
128 reset()
129 registerAs(cur, victimPath, cfg("Victim Scope", "g1victim"))
130 urequire.Equal(t, 1, GetTelescopeCount())
131
132 testing.SetRealm(testing.NewCodeRealm(attackerPath))
133 uassert.AbortsContains(t, cur, "not registered", func() {
134 UpdateTelescopeStatus(cross(cur), "error")
135 })
136 uassert.AbortsContains(t, cur, "not registered", func() {
137 SubmitCapture(cross(cur), "https://evil/forged.png", 1, 1, 10, "g1victim")
138 })
139 uassert.AbortsContains(t, cur, "not registered", func() {
140 UnregisterTelescope(cross(cur))
141 })
142
143 // Victim's entry is untouched.
144 stored, ok := telescopes.Get(victimPath)
145 urequire.True(t, ok)
146 uassert.Equal(t, "Victim Scope", stored.(nightsky.TelescopeConfig).Name)
147 uassert.Equal(t, 1, GetTelescopeCount())
148}
149
150// TestAttackerCannotUnregisterVictim: even when the attacker is itself
151// registered, UnregisterTelescope removes only the caller's own entry — it
152// cannot take down a neighbour.
153func TestAttackerCannotUnregisterVictim(cur realm, t *testing.T) {
154 reset()
155 registerAs(cur, victimPath, cfg("Victim Scope", "g1victim"))
156 registerAs(cur, attackerPath, cfg("Evil Scope", "g1attacker"))
157 urequire.Equal(t, 2, GetTelescopeCount())
158
159 testing.SetRealm(testing.NewCodeRealm(attackerPath))
160 UnregisterTelescope(cross(cur)) // removes the attacker, nothing else
161
162 uassert.False(t, telescopes.Has(attackerPath))
163 uassert.True(t, telescopes.Has(victimPath)) // victim survives
164 uassert.Equal(t, 1, GetTelescopeCount())
165}
166
167// TestAttackerCaptureCannotImpersonateVictim: the network feed stamps each
168// capture's TelescopeID from the caller's own registration, so an attacker
169// cannot make a forged image appear to come from the victim's telescope —
170// regardless of the capturedBy value they pass.
171func TestAttackerCaptureCannotImpersonateVictim(cur realm, t *testing.T) {
172 reset()
173 registerAs(cur, victimPath, cfg("Victim Scope", "g1victim"))
174 registerAs(cur, attackerPath, cfg("Evil Scope", "g1attacker"))
175
176 testing.SetRealm(testing.NewCodeRealm(attackerPath))
177 SubmitCapture(cross(cur), "https://evil/forged.png", 1, 1, 10, "g1victim")
178
179 latest, ok := recentCaptures.Latest()
180 urequire.True(t, ok)
181 uassert.Equal(t, "Evil Scope", latest.TelescopeID) // not "Victim Scope"
182}
183
184// TestNonAdminCannotUseAdminPowers: the network-admin functions reject anyone
185// who is not the ownable owner, so an intruder cannot wipe the registry or the
186// capture feed.
187func TestNonAdminCannotUseAdminPowers(cur realm, t *testing.T) {
188 reset()
189 registerAs(cur, victimPath, cfg("Victim Scope", "g1victim"))
190 urequire.Equal(t, 1, GetTelescopeCount())
191
192 testing.SetRealm(testing.NewUserRealm(testutils.TestAddress("intruder")))
193 uassert.AbortsContains(t, cur, "not owner", func() {
194 RemoveTelescope(cross(cur), victimPath)
195 })
196 uassert.AbortsContains(t, cur, "not owner", func() {
197 ClearAllTelescopes(cross(cur))
198 })
199 uassert.AbortsContains(t, cur, "not owner", func() {
200 RemoveCapture(cross(cur), 0)
201 })
202
203 // Nothing was removed.
204 uassert.Equal(t, 1, GetTelescopeCount())
205}