config_test.gno
12.99 Kb ยท 309 lines
1package config
2
3import (
4 "chain"
5 "chain/runtime"
6 "errors"
7 "testing"
8
9 "gno.land/p/moul/authz"
10 "gno.land/p/nt/testutils"
11 "gno.land/p/nt/uassert"
12)
13
14var (
15 originAddr = testutils.TestAddress("origin")
16 manager1Addr = testutils.TestAddress("manager1")
17 manager2Addr = testutils.TestAddress("manager2")
18 nonManagerAddr = testutils.TestAddress("nonManager")
19)
20
21// Helper to reset the Authorizer for each test, simulating initialization.
22func setupTest(cur realm, t *testing.T) {
23 t.Helper()
24
25 // Set the initial caller context
26 testing.SetRealm(testing.NewUserRealm(originAddr))
27 // Initialize the Authorizer with the originAddr as the sole member,
28 // simulating the state after NewWithOrigin() in a real deployment.
29 Authorizer = authz.NewWithAuthority(authz.NewMemberAuthority(originAddr))
30 // Ensure the origin address is the initial manager
31 uassert.True(t, HasManager(cur, originAddr), "origin should be the initial manager")
32}
33
34func TestAddManager(cur realm, t *testing.T) {
35 setupTest(cur, t)
36
37 // Origin adds manager1 - Should succeed
38 testing.SetRealm(testing.NewUserRealm(originAddr))
39 err := AddManager(cross, manager1Addr)
40 uassert.NoError(t, err, "origin adding manager1 should succeed")
41 uassert.True(t, HasManager(cur, manager1Addr), "manager1 should now be a manager")
42
43 // Non-manager tries to add manager2 - Should fail
44 testing.SetRealm(testing.NewUserRealm(nonManagerAddr))
45 err = AddManager(cross, manager2Addr)
46 uassert.Error(t, err, "non-manager adding manager2 should fail")
47 uassert.False(t, HasManager(cur, manager2Addr), "manager2 should not have been added")
48
49 // Manager1 adds manager2 - Should succeed
50 testing.SetRealm(testing.NewUserRealm(manager1Addr))
51 err = AddManager(cross, manager2Addr)
52 uassert.NoError(t, err, "manager1 adding manager2 should succeed")
53 uassert.True(t, HasManager(cur, manager2Addr), "manager2 should now be a manager")
54
55 // Transfer authority away from MemberAuthority
56 testing.SetRealm(testing.NewUserRealm(originAddr)) // Origin transfers
57 err = TransferManagement(cross, authz.NewAutoAcceptAuthority())
58 uassert.NoError(t, err, "transferring authority should succeed")
59
60 // Try adding after transfer - Should fail (wrong authority type)
61 testing.SetRealm(testing.NewUserRealm(manager1Addr))
62 err = AddManager(cross, nonManagerAddr) // Try adding someone new
63 uassert.ErrorContains(t, err, "current authority is not a MemberAuthority", "adding manager should fail after transfer")
64}
65
66func TestRemoveManager(cur realm, t *testing.T) {
67 setupTest(cur, t)
68
69 // Add manager1 first
70 testing.SetRealm(testing.NewUserRealm(originAddr))
71 err := AddManager(cross, manager1Addr)
72 uassert.NoError(t, err, "setup: failed to add manager1")
73 uassert.True(t, HasManager(cur, manager1Addr), "setup: manager1 should be added")
74
75 // Non-manager tries to remove manager1 - Should fail
76 testing.SetRealm(testing.NewUserRealm(nonManagerAddr))
77 err = RemoveManager(cross, manager1Addr)
78 uassert.Error(t, err, "non-manager removing manager1 should fail")
79 uassert.True(t, HasManager(cur, manager1Addr), "manager1 should still be a manager")
80
81 // Origin removes manager1 - Should succeed
82 testing.SetRealm(testing.NewUserRealm(originAddr))
83 err = RemoveManager(cross, manager1Addr)
84 uassert.NoError(t, err, "origin removing manager1 should succeed")
85 uassert.False(t, HasManager(cur, manager1Addr), "manager1 should now be removed")
86
87 // Add manager1 again for next test case
88 testing.SetRealm(testing.NewUserRealm(originAddr))
89 err = AddManager(cross, manager1Addr)
90 uassert.NoError(t, err, "setup: failed to re-add manager1")
91
92 // Transfer authority
93 testing.SetRealm(testing.NewUserRealm(originAddr))
94 err = TransferManagement(cross, authz.NewAutoAcceptAuthority())
95 uassert.NoError(t, err, "transferring authority should succeed")
96
97 // Try removing after transfer - Should fail (wrong authority type)
98 testing.SetRealm(testing.NewUserRealm(originAddr)) // Use origin, doesn't matter which user now
99 err = RemoveManager(cross, manager1Addr)
100 uassert.ErrorContains(t, err, "current authority is not a MemberAuthority", "removing manager should fail after transfer")
101}
102
103func TestListManagers(cur realm, t *testing.T) {
104 setupTest(cur, t)
105 initialList := ListManagers(cross)
106 assertAddrSliceEqual(t, []address{originAddr}, initialList)
107 // Add manager1 and manager2
108 testing.SetRealm(testing.NewUserRealm(originAddr))
109 err := AddManager(cross, manager1Addr)
110 uassert.NoError(t, err)
111 err = AddManager(cross, manager2Addr)
112 uassert.NoError(t, err)
113
114 // List should contain origin, manager1, manager2
115 list1 := ListManagers(cross)
116 expected1 := []address{manager2Addr, manager1Addr, originAddr}
117 assertAddrSliceEqual(t, expected1, list1)
118
119 // Remove manager1
120 testing.SetRealm(testing.NewUserRealm(originAddr)) // Can be origin or manager2
121 err = RemoveManager(cross, manager1Addr)
122 uassert.NoError(t, err)
123
124 // List should contain origin, manager2
125 list2 := ListManagers(cross)
126 expected2 := []address{manager2Addr, originAddr}
127 assertAddrSliceEqual(t, expected2, list2)
128
129 // Transfer authority
130 testing.SetRealm(testing.NewUserRealm(originAddr))
131 err = TransferManagement(cross, authz.NewAutoAcceptAuthority())
132 uassert.NoError(t, err)
133
134 // List should be empty after transfer
135 list3 := ListManagers(cross)
136 uassert.True(t, len(list3) == 0, "manager list should be empty after transfer")
137}
138
139func TestHasManager(cur realm, t *testing.T) {
140 setupTest(cur, t)
141
142 // Initially, only origin is manager
143 uassert.True(t, HasManager(cross, originAddr), "origin should initially be a manager")
144 uassert.False(t, HasManager(cross, manager1Addr), "manager1 should not initially be a manager")
145 uassert.False(t, HasManager(cross, nonManagerAddr), "nonManager should not initially be a manager")
146
147 // Add manager1
148 testing.SetRealm(testing.NewUserRealm(originAddr))
149 err := AddManager(cross, manager1Addr)
150 uassert.NoError(t, err)
151
152 // Check again
153 uassert.True(t, HasManager(cross, originAddr), "origin should still be a manager")
154 uassert.True(t, HasManager(cross, manager1Addr), "manager1 should now be a manager")
155 uassert.False(t, HasManager(cross, nonManagerAddr), "nonManager should still not be a manager")
156
157 // Transfer authority
158 testing.SetRealm(testing.NewUserRealm(originAddr))
159 err = TransferManagement(cross, authz.NewAutoAcceptAuthority())
160 uassert.NoError(t, err)
161
162 // After transfer, HasManager should always return false for MemberAuthority checks
163 uassert.False(t, HasManager(cross, originAddr), "HasManager should be false after transfer")
164 uassert.False(t, HasManager(cross, manager1Addr), "HasManager should be false after transfer")
165 uassert.False(t, HasManager(cross, nonManagerAddr), "HasManager should be false after transfer")
166}
167
168func TestTransferManagement(cur realm, t *testing.T) {
169 setupTest(cur, t)
170
171 // Add manager1
172 testing.SetRealm(testing.NewUserRealm(originAddr))
173 err := AddManager(cross, manager1Addr)
174 uassert.NoError(t, err)
175
176 // Create a new authority (MemberAuthority with manager2)
177 newAuthority := authz.NewMemberAuthority(manager2Addr)
178
179 // Non-manager tries to transfer - Should fail
180 testing.SetRealm(testing.NewUserRealm(nonManagerAddr))
181 err = TransferManagement(cross, newAuthority)
182 uassert.Error(t, err, "non-manager transfer should fail")
183 _, isMemberAuth := Authorizer.Authority().(*authz.MemberAuthority)
184 uassert.True(t, isMemberAuth, "authority should still be MemberAuthority") // Verify it didn't change
185
186 // Manager1 tries to transfer - Should succeed
187 testing.SetRealm(testing.NewUserRealm(manager1Addr))
188 err = TransferManagement(cross, newAuthority)
189 uassert.NoError(t, err, "manager1 transfer should succeed")
190
191 // Verify current authority is the new one
192 currentAuth := Authorizer.Authority()
193 uassert.True(t, currentAuth == newAuthority, "current authority should be the new one")
194
195 // Verify origin is no longer a manager under the *new* authority
196 testing.SetRealm(testing.NewUserRealm(manager2Addr)) // Need new manager to check
197 uassert.False(t, HasManager(cross, originAddr), "origin should not be manager under new authority")
198 uassert.False(t, HasManager(cross, manager1Addr), "manager1 should not be manager under new authority")
199 uassert.True(t, HasManager(cross, manager2Addr), "manager2 should be manager under new authority")
200
201 // Try adding a manager using the old origin - Should fail
202 testing.SetRealm(testing.NewUserRealm(originAddr))
203 err = AddManager(cross, nonManagerAddr)
204 uassert.Error(t, err, "origin should not be able to add manager after transfer")
205
206 // Try adding a manager using the new manager (manager2) - Should succeed
207 testing.SetRealm(testing.NewUserRealm(manager2Addr))
208 err = AddManager(cross, nonManagerAddr)
209 uassert.NoError(t, err, "new manager (manager2) should be able to add managers")
210 uassert.True(t, HasManager(cross, nonManagerAddr), "nonManager should be added by manager2")
211
212 // Try transferring to nil - Should fail
213 testing.SetRealm(testing.NewUserRealm(manager2Addr))
214 err = TransferManagement(cross, nil)
215 uassert.ErrorContains(t, err, "new authority cannot be nil", "transferring to nil should fail")
216}
217
218func TestTransferToContractAuthority(cur realm, t *testing.T) {
219 setupTest(cur, t) // Origin is the initial manager
220
221 contractPath := "gno.land/r/testcontract"
222 contractRealm := testing.NewCodeRealm(contractPath) // Simulate contract realm
223
224 // Define a simple contract authority handler
225 handlerExecuted := false // Track if the handler itself gets called
226 contractAuth := authz.NewContractAuthority(contractPath, func(title string, action authz.PrivilegedAction) error {
227 // Simulate contract checking the caller *before* executing
228 caller := runtime.CurrentRealm().Address()
229 expectedContractAddr := chain.PackageAddress(contractPath)
230 if caller != expectedContractAddr {
231 // Fail before marking executed or running action
232 // Note: In a real scenario, this handler might just ignore the call
233 // if the caller isn't right, rather than returning an error,
234 // depending on the desired contract logic. Returning an error
235 // here helps the test verify the handler wasn't improperly called.
236 return errors.New("handler: caller is not the contract")
237 }
238
239 // Only mark executed and run action if caller is correct
240 handlerExecuted = true
241 return action()
242 })
243
244 // Origin transfers management to the contract authority
245 testing.SetRealm(testing.NewUserRealm(originAddr))
246 err := TransferManagement(cross, contractAuth)
247 uassert.NoError(t, err, "transfer to contract authority failed")
248 uassert.True(t, Authorizer.Authority() == contractAuth, "authority should now be the contract authority")
249
250 // Now, actions like AddManager/RemoveManager should fail because the current
251 // authority is no longer a MemberAuthority. The contract would need its own
252 // logic executed via Authorizer.DoByCurrent() to manage members if desired.
253
254 // Try adding a manager (will check authority type) - Should fail
255 testing.SetRealm(testing.NewUserRealm(originAddr)) // Caller doesn't matter for this check
256 err = AddManager(cross, manager1Addr)
257 uassert.ErrorContains(t, err, "current authority is not a MemberAuthority", "AddManager should fail with ContractAuthority")
258
259 // Simulate an action authorized *by the contract* using Authorizer.Do
260 var contractActionExecuted bool
261 handlerExecuted = false // Reset tracker
262 testing.SetRealm(contractRealm) // Call must originate from the contract now
263 err = Authorizer.DoByCurrent("some_contract_action", func() error {
264 contractActionExecuted = true
265 // Imagine contract logic here
266 return nil
267 })
268 uassert.NoError(t, err, "contract action via Authorizer.Do failed")
269 uassert.True(t, handlerExecuted, "handler should have been executed by contract call") // Verify handler ran
270 uassert.True(t, contractActionExecuted, "contract action should have been executed")
271
272 // Simulate an action from a user - Should fail before handler is called
273 var userActionExecuted bool
274 handlerExecuted = false // Reset tracker
275 testing.SetRealm(testing.NewUserRealm(nonManagerAddr))
276 err = Authorizer.DoByCurrent("some_user_action", func() error {
277 userActionExecuted = true
278 return nil
279 })
280 // The ContractAuthority.Authorize method should return an error
281 // because the handler now returns an error if the caller isn't the contract.
282 uassert.Error(t, err, "user action via Authorizer.Do should fail when contract is authority")
283 uassert.ErrorContains(t, err, "handler: caller is not the contract", "error should originate from handler check") // Check specific error
284 uassert.False(t, handlerExecuted, "handler should NOT have been executed by user call") // Verify handler didn't run past the check
285 uassert.False(t, userActionExecuted, "user action should not have been executed")
286}
287
288// Helper to check if a slice contains a specific address
289func containsAddr(list []address, addr address) bool {
290 for _, item := range list {
291 if item == addr {
292 return true
293 }
294 }
295 return false
296}
297
298func assertAddrSliceEqual(t *testing.T, expected, actual []address) {
299 t.Helper()
300 if len(expected) != len(actual) {
301 t.Fatalf("expected slice length %d, got %d. Expected: %v, Got: %v", len(expected), len(actual), expected, actual)
302 }
303
304 for i := range expected {
305 if expected[i] != actual[i] {
306 t.Fatalf("slices differ at index %d. Expected: %v, Got: %v", i, expected, actual)
307 }
308 }
309}