@@ -25,11 +25,31 @@ import (
2525 "github.com/algorand/go-algorand/config"
2626 "github.com/algorand/go-algorand/daemon/algod/api/server/v2/generated/model"
2727 "github.com/algorand/go-algorand/data/basics"
28+ "github.com/algorand/go-algorand/data/basics/testing/roundtrip"
2829 ledgertesting "github.com/algorand/go-algorand/ledger/testing"
2930 "github.com/algorand/go-algorand/protocol"
3031 "github.com/algorand/go-algorand/test/partitiontest"
3132)
3233
34+ // makeAccountConverters creates conversion functions for round-trip testing between
35+ // basics.AccountData and model.Account.
36+ func makeAccountConverters (t * testing.T , addrStr string , round basics.Round , proto * config.ConsensusParams , withoutRewards basics.MicroAlgos ) (
37+ toModel func (basics.AccountData ) model.Account ,
38+ toBasics func (model.Account ) basics.AccountData ,
39+ ) {
40+ toModel = func (ad basics.AccountData ) model.Account {
41+ converted , err := AccountDataToAccount (addrStr , & ad , round , proto , withoutRewards )
42+ require .NoError (t , err )
43+ return converted
44+ }
45+ toBasics = func (acc model.Account ) basics.AccountData {
46+ converted , err := AccountToAccountData (& acc )
47+ require .NoError (t , err )
48+ return converted
49+ }
50+ return toModel , toBasics
51+ }
52+
3353func TestAccount (t * testing.T ) {
3454 partitiontest .PartitionTest (t )
3555 t .Parallel ()
@@ -106,8 +126,9 @@ func TestAccount(t *testing.T) {
106126 b := a .WithUpdatedRewards (proto .RewardUnit , 100 )
107127
108128 addr := basics.Address {}.String ()
109- conv , err := AccountDataToAccount (addr , & b , round , & proto , a .MicroAlgos )
110- require .NoError (t , err )
129+ toModel , toBasics := makeAccountConverters (t , addr , round , & proto , a .MicroAlgos )
130+
131+ conv := toModel (b )
111132 require .Equal (t , addr , conv .Address )
112133 require .Equal (t , b .MicroAlgos .Raw , conv .Amount )
113134 require .Equal (t , a .MicroAlgos .Raw , conv .AmountWithoutPendingRewards )
@@ -145,6 +166,21 @@ func TestAccount(t *testing.T) {
145166 verifyCreatedApp (0 , appIdx1 , appParams1 )
146167 verifyCreatedApp (1 , appIdx2 , appParams2 )
147168
169+ appRoundTrip := func (idx basics.AppIndex , params basics.AppParams ) {
170+ require .True (t , roundtrip .Check (t , params ,
171+ func (ap basics.AppParams ) model.Application {
172+ return AppParamsToApplication (addr , idx , & ap )
173+ },
174+ func (app model.Application ) basics.AppParams {
175+ converted , err := ApplicationParamsToAppParams (& app .Params )
176+ require .NoError (t , err )
177+ return converted
178+ }))
179+ }
180+
181+ appRoundTrip (appIdx1 , appParams1 )
182+ appRoundTrip (appIdx2 , appParams2 )
183+
148184 makeTKV := func (k string , v interface {}) model.TealKeyValue {
149185 value := model.TealValue {}
150186 switch v .(type ) {
@@ -198,9 +234,7 @@ func TestAccount(t *testing.T) {
198234 verifyCreatedAsset (0 , assetIdx1 , assetParams1 )
199235 verifyCreatedAsset (1 , assetIdx2 , assetParams2 )
200236
201- c , err := AccountToAccountData (& conv )
202- require .NoError (t , err )
203- require .Equal (t , b , c )
237+ require .True (t , roundtrip .Check (t , b , toModel , toBasics ))
204238
205239 t .Run ("IsDeterministic" , func (t * testing.T ) {
206240 // convert the same account a few more times to make sure we always
@@ -223,11 +257,91 @@ func TestAccountRandomRoundTrip(t *testing.T) {
223257 for addr , acct := range accts {
224258 round := basics .Round (2 )
225259 proto := config .Consensus [protocol .ConsensusFuture ]
226- conv , err := AccountDataToAccount (addr .String (), & acct , round , & proto , acct .MicroAlgos )
227- require .NoError (t , err )
228- c , err := AccountToAccountData (& conv )
260+ toModel , toBasics := makeAccountConverters (t , addr .String (), round , & proto , acct .MicroAlgos )
261+ // AccountData has constraints (Status field must be valid), and this test
262+ // already uses RandomAccounts to generate valid random accounts
263+ require .True (t , roundtrip .Check (t , acct , toModel , toBasics , roundtrip .NoRandomCases ()))
264+ }
265+ }
266+ }
267+
268+ func TestConvertTealKeyValueRoundTrip (t * testing.T ) {
269+ partitiontest .PartitionTest (t )
270+ t .Parallel ()
271+
272+ t .Run ("nil input" , func (t * testing.T ) {
273+ require .Nil (t , convertTKVToGenerated (nil ))
274+ result , err := convertGeneratedTKV (nil )
275+ require .NoError (t , err )
276+ require .Nil (t , result )
277+ })
278+
279+ t .Run ("empty map treated as nil" , func (t * testing.T ) {
280+ empty := basics.TealKeyValue {}
281+ require .Nil (t , convertTKVToGenerated (& empty ))
282+ result , err := convertGeneratedTKV (convertTKVToGenerated (& empty ))
283+ require .NoError (t , err )
284+ require .Nil (t , result )
285+ })
286+
287+ t .Run ("round-trip non-empty map" , func (t * testing.T ) {
288+ kv := basics.TealKeyValue {
289+ "alpha" : {Type : basics .TealUintType , Uint : 17 },
290+ "beta" : {Type : basics .TealBytesType , Bytes : "\x00 \x01 binary" },
291+ }
292+
293+ toGenerated := func (val basics.TealKeyValue ) * model.TealKeyValueStore {
294+ return convertTKVToGenerated (& val )
295+ }
296+ toBasics := func (store * model.TealKeyValueStore ) basics.TealKeyValue {
297+ converted , err := convertGeneratedTKV (store )
229298 require .NoError (t , err )
230- require . Equal ( t , acct , c )
299+ return converted
231300 }
301+
302+ require .True (t , roundtrip .Check (t , kv , toGenerated , toBasics ))
303+ })
304+ }
305+
306+ func TestAppLocalStateRoundTrip (t * testing.T ) {
307+ partitiontest .PartitionTest (t )
308+ t .Parallel ()
309+
310+ appIdx := basics .AppIndex (42 )
311+ cases := map [string ]basics.AppLocalState {
312+ "empty kv" : {
313+ Schema : basics.StateSchema {NumUint : 1 , NumByteSlice : 0 },
314+ KeyValue : nil ,
315+ },
316+ "mixed kv" : {
317+ Schema : basics.StateSchema {NumUint : 2 , NumByteSlice : 3 },
318+ KeyValue : basics.TealKeyValue {
319+ "counter" : {Type : basics .TealUintType , Uint : 99 },
320+ "note" : {Type : basics .TealBytesType , Bytes : "hello world" },
321+ },
322+ },
323+ }
324+
325+ for name , state := range cases {
326+ state := state
327+ t .Run (name , func (t * testing.T ) {
328+ modelState := AppLocalState (state , appIdx )
329+ modelStates := []model.ApplicationLocalState {modelState }
330+
331+ acc := model.Account {
332+ Status : basics .Offline .String (),
333+ Amount : 0 ,
334+ AppsLocalState : & modelStates ,
335+ }
336+
337+ ad , err := AccountToAccountData (& acc )
338+ require .NoError (t , err )
339+
340+ require .NotNil (t , ad .AppLocalStates )
341+ got , ok := ad .AppLocalStates [appIdx ]
342+ require .True (t , ok )
343+ require .Equal (t , state .Schema , got .Schema )
344+ require .Equal (t , state .KeyValue , got .KeyValue )
345+ })
232346 }
233347}
0 commit comments