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