@@ -4,16 +4,22 @@ package itests
44import (
55 "context"
66 "errors"
7+ "fmt"
8+ "reflect"
79 "strings"
810 "testing"
911 "time"
1012
1113 logging "github.com/ipfs/go-log/v2"
14+ "github.com/ipld/go-ipld-prime"
15+ "github.com/ipld/go-ipld-prime/codec/dagjson"
16+ "github.com/ipld/go-ipld-prime/traversal"
1217 "github.com/libp2p/go-libp2p/core/peer"
1318 "github.com/stretchr/testify/require"
1419
1520 "github.com/filecoin-project/go-address"
1621 "github.com/filecoin-project/go-jsonrpc"
22+ "github.com/filecoin-project/go-state-types/abi"
1723 "github.com/filecoin-project/go-state-types/big"
1824 "github.com/filecoin-project/go-state-types/exitcode"
1925
@@ -23,6 +29,7 @@ import (
2329 "github.com/filecoin-project/lotus/chain/actors/policy"
2430 "github.com/filecoin-project/lotus/chain/types"
2531 "github.com/filecoin-project/lotus/itests/kit"
32+ "github.com/filecoin-project/lotus/lib/must"
2633)
2734
2835func TestAPI (t * testing.T ) {
@@ -322,54 +329,239 @@ func (ts *apiSuite) testNonGenesisMiner(t *testing.T) {
322329
323330func TestAPIV2 (t * testing.T ) {
324331 req := require .New (t )
332+ ctx , cancel := context .WithTimeout (context .Background (), 2 * time .Minute )
333+ defer cancel ()
325334
326335 kit .QuietMiningLogs ()
327- full , _ , ens := kit .EnsembleMinimal (t )
328- ens .BeginMining (20 * time .Millisecond )
336+ full , _ , ens := kit .EnsembleMinimal (t , kit . ThroughRPC () )
337+ ens .BeginMining (10 * time .Millisecond )
329338
330339 full .WaitTillChain (context .Background (), kit .HeightAtLeast (policy .ChainFinality + 20 ))
331340
332- ts , err := full .ChainGetTipSetByHeight (context .Background (), 15 , types .EmptyTSK )
333- req .NoError (err )
334- req .NotNil (ts )
335- t .Logf ("/v1/ChainGetTipSetByHeight(15, []): %d" , ts .Height ())
336-
337- ts , err = full .V2 .ChainGetTipSetByHeight (context .Background (), 15 , types .NewTipSetSelector (types .EmptyTSK ))
338- req .NoError (err )
339- req .NotNil (ts )
340- t .Logf (`/v2/ChainGetTipSetByHeight(15, []): %d` , ts .Height ())
341-
342- ts , err = full .V2 .ChainGetTipSetByHeight (context .Background (), 15 , types .TipSetSelectorLatest )
343- req .NoError (err )
344- req .NotNil (ts )
345- t .Logf (`/v2/ChainGetTipSetByHeight(15, "latest"): %d` , ts .Height ())
346-
347- ts , err = full .V2 .ChainGetTipSetByHeight (context .Background (), 15 , types .TipSetSelectorFinalized )
348- req .NoError (err )
349- req .NotNil (ts )
350- t .Logf (`/v2/ChainGetTipSetByHeight(15, "finalized"): %d` , ts .Height ())
351-
352- _ , err = full .V2 .ChainGetTipSetByHeight (context .Background (), 200 , types .TipSetSelectorFinalized )
353- req .Error (err )
354- t .Logf (`/v2/ChainGetTipSetByHeight(15, "finalized"): %s` , err .Error ())
355-
356- ts , err = full .ChainHead (context .Background ())
357- req .NoError (err )
358- req .NotNil (ts )
359- t .Logf ("/v1/ChainHead(): %d" , ts .Height ())
360-
361- ts , err = full .V2 .ChainHead (context .Background (), nil )
362- req .NoError (err )
363- req .NotNil (ts )
364- t .Logf ("/v2/ChainHead(null): %d" , ts .Height ())
365-
366- ts , err = full .V2 .ChainHead (context .Background (), jsonrpc .RawParams (`["latest"]` ))
367- req .NoError (err )
368- req .NotNil (ts )
369- t .Logf (`/v2/ChainHead("latest"): %d` , ts .Height ())
370-
371- ts , err = full .V2 .ChainHead (context .Background (), jsonrpc .RawParams (`["finalized"]` ))
372- req .NoError (err )
373- req .NotNil (ts )
374- t .Logf (`/v2/ChainHead("finalized"): %d` , ts .Height ())
341+ callAndVerify := func (version int , method , params , responsePath string , expectValue any , verify func (req * require.Assertions , head * types.TipSet , node ipld.Node )) {
342+ var response string
343+ head , err := full .ChainHead (ctx )
344+ for {
345+ req .NoError (err )
346+ var statusCode int
347+ statusCode , response = full .HttpRpcRequest (version , fmt .Sprintf (`{"jsonrpc":"2.0","method":"%s","params":%s,"id":1}` , method , params ))
348+ req .Equal (200 , statusCode )
349+ afterHead , err := full .ChainHead (ctx )
350+ req .NoError (err )
351+ if head .Height () == afterHead .Height () {
352+ // chain hasn't advanced while we were waiting for the response
353+ break
354+ }
355+ head = afterHead
356+ }
357+ node , err := ipld .Decode ([]byte (response ), dagjson .Decode )
358+ req .NoError (err , "failed to decode JSON response" )
359+ req .Equal (ipld .Kind_Map , node .Kind ())
360+ result , err := traversal .Get (node , ipld .ParsePath (responsePath ))
361+ req .NoError (err )
362+ if verify != nil {
363+ verify (req , head , result )
364+ } else {
365+ switch v := expectValue .(type ) {
366+ case int :
367+ req .Equal (ipld .Kind_Int , result .Kind ())
368+ req .EqualValues (v , must .One (result .AsInt ()))
369+ default :
370+ req .FailNowf ("unexpected type" , "%T, maybe add support for this?" , expectValue )
371+ }
372+ }
373+ t .Logf (`/rpc/v1 {"method":"%s","params":%s}: %s` , method , params , string (must .One (ipld .Encode (result , dagjson .Encode ))))
374+ }
375+
376+ testCases := []struct {
377+ name string
378+ method string
379+ version int // 1 or 2
380+ apiArgs []any
381+ apiVerify func (req * require.Assertions , head * types.TipSet , result any ) // nil to skip api call
382+ rpcParams string // empty to skip rpc call, otherwise JSON array
383+ rpcVerifyPath string // IPLD path to verify
384+ rpcVerifyValue any // expected value at path
385+ rpcVerify func (req * require.Assertions , head * types.TipSet , node ipld.Node ) // if not using rpcVerifyValue, use this
386+ }{
387+ {
388+ name : "v1/ChainGetTipSetByHeight/15/head" ,
389+ method : "ChainGetTipSetByHeight" ,
390+ version : 1 ,
391+ apiArgs : []any {abi .ChainEpoch (15 ), types .EmptyTSK },
392+ apiVerify : func (req * require.Assertions , head * types.TipSet , result any ) {
393+ ts , ok := result .(* types.TipSet )
394+ req .True (ok )
395+ req .EqualValues (15 , ts .Height ())
396+ },
397+ rpcParams : `[15,[]]` ,
398+ rpcVerifyPath : "result/Height" ,
399+ rpcVerifyValue : 15 ,
400+ },
401+ {
402+ name : "v2/ChainGetTipSetByHeight/15/head" ,
403+ method : "ChainGetTipSetByHeight" ,
404+ version : 2 ,
405+ apiArgs : []any {abi .ChainEpoch (15 ), types .NewTipSetSelector (types .EmptyTSK )},
406+ apiVerify : func (req * require.Assertions , head * types.TipSet , result any ) {
407+ ts , ok := result .(* types.TipSet )
408+ req .True (ok )
409+ req .EqualValues (15 , ts .Height ())
410+ },
411+ rpcParams : `[15,[]]` ,
412+ rpcVerifyPath : "result/Height" ,
413+ rpcVerifyValue : 15 ,
414+ },
415+ {
416+ name : "v2/ChainGetTipSetByHeight/15/latest" ,
417+ method : "ChainGetTipSetByHeight" ,
418+ version : 2 ,
419+ apiArgs : []any {abi .ChainEpoch (15 ), types .TipSetSelectorLatest },
420+ apiVerify : func (req * require.Assertions , head * types.TipSet , result any ) {
421+ ts , ok := result .(* types.TipSet )
422+ req .True (ok )
423+ req .EqualValues (15 , ts .Height ())
424+ },
425+ rpcParams : `[15,"latest"]` ,
426+ rpcVerifyPath : "result/Height" ,
427+ rpcVerifyValue : 15 ,
428+ },
429+ {
430+ name : "v2/ChainGetTipSetByHeight/15/-" ,
431+ method : "ChainGetTipSetByHeight" ,
432+ version : 2 ,
433+ rpcParams : `[15,""]` ,
434+ rpcVerifyPath : "result/Height" ,
435+ rpcVerifyValue : 15 ,
436+ },
437+ {
438+ name : "v2/ChainGetTipSetByHeight/15/finalized" ,
439+ method : "ChainGetTipSetByHeight" ,
440+ version : 2 ,
441+ apiArgs : []any {abi .ChainEpoch (15 ), types .TipSetSelectorFinalized },
442+ apiVerify : func (req * require.Assertions , head * types.TipSet , result any ) {
443+ ts , ok := result .(* types.TipSet )
444+ req .True (ok )
445+ req .EqualValues (15 , ts .Height ())
446+ },
447+ rpcParams : `[15,"finalized"]` ,
448+ rpcVerifyPath : "result/Height" ,
449+ rpcVerifyValue : 15 ,
450+ },
451+ {
452+ name : "v1/ChainHead" ,
453+ method : "ChainHead" ,
454+ version : 1 ,
455+ rpcParams : `[]` ,
456+ rpcVerifyPath : "result/Height" ,
457+ rpcVerify : func (req * require.Assertions , head * types.TipSet , node ipld.Node ) {
458+ req .EqualValues (head .Height (), must .One (node .AsInt ()))
459+ },
460+ },
461+ {
462+ name : "v2/ChainHead" ,
463+ method : "ChainHead" ,
464+ version : 2 ,
465+ apiArgs : []any {(jsonrpc .RawParams )(nil )},
466+ apiVerify : func (req * require.Assertions , head * types.TipSet , result any ) {
467+ ts , ok := result .(* types.TipSet )
468+ req .True (ok )
469+ req .EqualValues (head .Height (), ts .Height ())
470+ },
471+ rpcParams : `[]` ,
472+ rpcVerifyPath : "result/Height" ,
473+ rpcVerify : func (req * require.Assertions , head * types.TipSet , node ipld.Node ) {
474+ req .EqualValues (head .Height (), must .One (node .AsInt ()))
475+ },
476+ },
477+ {
478+ name : "v2/ChainHead/latest" ,
479+ method : "ChainHead" ,
480+ version : 2 ,
481+ apiArgs : []any {jsonrpc .RawParams (`["latest"]` )},
482+ apiVerify : func (req * require.Assertions , head * types.TipSet , result any ) {
483+ ts , ok := result .(* types.TipSet )
484+ req .True (ok )
485+ req .EqualValues (head .Height (), ts .Height ())
486+ },
487+ rpcParams : `["latest"]` ,
488+ rpcVerifyPath : "result/Height" ,
489+ rpcVerify : func (req * require.Assertions , head * types.TipSet , node ipld.Node ) {
490+ req .EqualValues (head .Height (), must .One (node .AsInt ()))
491+ },
492+ },
493+ {
494+ name : "v2/ChainHead/-" ,
495+ method : "ChainHead" ,
496+ version : 2 ,
497+ apiArgs : []any {jsonrpc .RawParams (`[""]` )},
498+ apiVerify : func (req * require.Assertions , head * types.TipSet , result any ) {
499+ ts , ok := result .(* types.TipSet )
500+ req .True (ok )
501+ req .EqualValues (head .Height (), ts .Height ())
502+ },
503+ rpcParams : `[""]` ,
504+ rpcVerifyPath : "result/Height" ,
505+ rpcVerify : func (req * require.Assertions , head * types.TipSet , node ipld.Node ) {
506+ req .EqualValues (head .Height (), must .One (node .AsInt ()))
507+ },
508+ },
509+ {
510+ name : "v2/ChainHead/finalized" ,
511+ method : "ChainHead" ,
512+ version : 2 ,
513+ apiArgs : []any {jsonrpc .RawParams (`["finalized"]` )},
514+ apiVerify : func (req * require.Assertions , head * types.TipSet , result any ) {
515+ ts , ok := result .(* types.TipSet )
516+ req .True (ok )
517+ req .EqualValues (head .Height ()- policy .ChainFinality , ts .Height ())
518+ },
519+ rpcParams : `["finalized"]` ,
520+ rpcVerifyPath : "result/Height" ,
521+ rpcVerify : func (req * require.Assertions , head * types.TipSet , node ipld.Node ) {
522+ req .EqualValues (head .Height ()- policy .ChainFinality , must .One (node .AsInt ()))
523+ },
524+ },
525+ }
526+
527+ for _ , tc := range testCases {
528+ t .Run (tc .name , func (t * testing.T ) {
529+ if tc .apiVerify != nil {
530+ args := []reflect.Value {reflect .ValueOf (ctx )}
531+ for _ , arg := range tc .apiArgs {
532+ if arg == nil {
533+ args = append (args , reflect .Zero (reflect .TypeOf (arg )))
534+ } else {
535+ args = append (args , reflect .ValueOf (arg ))
536+ }
537+ }
538+ api := reflect .ValueOf (full )
539+ if tc .version == 2 {
540+ api = reflect .ValueOf (full .V2 )
541+ }
542+ method := api .MethodByName (tc .method )
543+ head , err := full .ChainHead (ctx )
544+ req .NoError (err )
545+ var response any
546+ for {
547+ resp := method .Call (args )
548+ req .Len (resp , 2 )
549+ req .Nil (resp [1 ].Interface (), "error in API call" )
550+ afterHead , err := full .ChainHead (ctx )
551+ req .NoError (err )
552+ if head .Height () == afterHead .Height () {
553+ // chain hasn't advanced while we were waiting for the response
554+ response = resp [0 ].Interface ()
555+ break
556+ }
557+ head = afterHead
558+ }
559+ tc .apiVerify (req , head , response )
560+ }
561+
562+ if tc .rpcParams != "" {
563+ callAndVerify (tc .version , "Filecoin." + tc .method , tc .rpcParams , tc .rpcVerifyPath , tc .rpcVerifyValue , tc .rpcVerify )
564+ }
565+ })
566+ }
375567}
0 commit comments