commit
0aacd4048c
@ -40,7 +40,7 @@ type API interface {
|
|||||||
|
|
||||||
NetPeers(context.Context) ([]peer.AddrInfo, error)
|
NetPeers(context.Context) ([]peer.AddrInfo, error)
|
||||||
NetConnect(context.Context, peer.AddrInfo) error
|
NetConnect(context.Context, peer.AddrInfo) error
|
||||||
NetAddrsListen(context.Context) (MultiaddrSlice, error)
|
NetAddrsListen(context.Context) (peer.AddrInfo, error)
|
||||||
// // ping
|
// // ping
|
||||||
|
|
||||||
// Struct
|
// Struct
|
||||||
|
@ -20,7 +20,7 @@ type Struct struct {
|
|||||||
|
|
||||||
NetPeers func(context.Context) ([]peer.AddrInfo, error)
|
NetPeers func(context.Context) ([]peer.AddrInfo, error)
|
||||||
NetConnect func(context.Context, peer.AddrInfo) error
|
NetConnect func(context.Context, peer.AddrInfo) error
|
||||||
NetAddrsListen func(context.Context) (MultiaddrSlice, error)
|
NetAddrsListen func(context.Context) (peer.AddrInfo, error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ func (c *Struct) NetConnect(ctx context.Context, p peer.AddrInfo) error {
|
|||||||
return c.Internal.NetConnect(ctx, p)
|
return c.Internal.NetConnect(ctx, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Struct) NetAddrsListen(ctx context.Context) (MultiaddrSlice, error) {
|
func (c *Struct) NetAddrsListen(ctx context.Context) (peer.AddrInfo, error) {
|
||||||
return c.Internal.NetAddrsListen(ctx)
|
return c.Internal.NetAddrsListen(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ package test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/filecoin-project/go-lotus/api"
|
"github.com/filecoin-project/go-lotus/api"
|
||||||
@ -10,25 +11,28 @@ import (
|
|||||||
|
|
||||||
// APIBuilder is a function which is invoked in test suite to provide
|
// APIBuilder is a function which is invoked in test suite to provide
|
||||||
// test nodes and networks
|
// test nodes and networks
|
||||||
type APIBuilder func() api.API
|
type APIBuilder func(t *testing.T, n int) []api.API
|
||||||
type testSuite struct {
|
type testSuite struct {
|
||||||
makeNode APIBuilder
|
makeNodes APIBuilder
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestApis is the entry point to API test suite
|
// TestApis is the entry point to API test suite
|
||||||
func TestApis(t *testing.T, b APIBuilder) {
|
func TestApis(t *testing.T, b APIBuilder) {
|
||||||
ts := testSuite{
|
ts := testSuite{
|
||||||
makeNode: b,
|
makeNodes: b,
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("version", ts.testVersion)
|
t.Run("version", ts.testVersion)
|
||||||
|
t.Run("id", ts.testID)
|
||||||
|
t.Run("testConnectTwo", ts.testConnectTwo)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ts *testSuite) testVersion(t *testing.T) {
|
func (ts *testSuite) testVersion(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
fc := ts.makeNode()
|
api := ts.makeNodes(t, 1)[0]
|
||||||
|
|
||||||
v, err := fc.Version(ctx)
|
v, err := api.Version(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -36,3 +40,62 @@ func (ts *testSuite) testVersion(t *testing.T) {
|
|||||||
t.Error("Version didn't work properly")
|
t.Error("Version didn't work properly")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ts *testSuite) testID(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
api := ts.makeNodes(t, 1)[0]
|
||||||
|
|
||||||
|
id, err := api.ID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(id.Pretty(), "Qm") {
|
||||||
|
t.Error("expected identity to be Qm..")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ts *testSuite) testConnectTwo(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
apis := ts.makeNodes(t, 2)
|
||||||
|
|
||||||
|
p, err := apis[0].NetPeers(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(p) != 0 {
|
||||||
|
t.Error("Node 0 has a peer")
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err = apis[1].NetPeers(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(p) != 0 {
|
||||||
|
t.Error("Node 1 has a peer")
|
||||||
|
}
|
||||||
|
|
||||||
|
addrs, err := apis[1].NetAddrsListen(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := apis[0].NetConnect(ctx, addrs); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err = apis[0].NetPeers(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(p) != 1 {
|
||||||
|
t.Error("Node 0 doesn't have 1 peer")
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err = apis[1].NetPeers(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(p) != 1 {
|
||||||
|
t.Error("Node 0 doesn't have 1 peer")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -53,8 +53,8 @@ var netListen = &cli.Command{
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, peer := range addrs {
|
for _, peer := range addrs.Addrs {
|
||||||
fmt.Println(peer)
|
fmt.Printf("%s/p2p/%s\n", peer, addrs.ID)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
@ -64,8 +64,11 @@ func (a *API) NetConnect(ctx context.Context, p peer.AddrInfo) error {
|
|||||||
return a.Host.Connect(ctx, p)
|
return a.Host.Connect(ctx, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *API) NetAddrsListen(context.Context) (api.MultiaddrSlice, error) {
|
func (a *API) NetAddrsListen(context.Context) (peer.AddrInfo, error) {
|
||||||
return a.Host.Addrs(), nil
|
return peer.AddrInfo{
|
||||||
|
ID: a.Host.ID(),
|
||||||
|
Addrs: a.Host.Addrs(),
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ api.API = &API{}
|
var _ api.API = &API{}
|
||||||
|
@ -68,7 +68,7 @@ const (
|
|||||||
_nInvokes // keep this last
|
_nInvokes // keep this last
|
||||||
)
|
)
|
||||||
|
|
||||||
type settings struct {
|
type Settings struct {
|
||||||
// modules is a map of constructors for DI
|
// modules is a map of constructors for DI
|
||||||
//
|
//
|
||||||
// In most cases the index will be a reflect. Type of element returned by
|
// In most cases the index will be a reflect. Type of element returned by
|
||||||
@ -80,13 +80,13 @@ type settings struct {
|
|||||||
// type, and must be applied in correct order
|
// type, and must be applied in correct order
|
||||||
invokes []fx.Option
|
invokes []fx.Option
|
||||||
|
|
||||||
online bool // Online option applied
|
Online bool // Online option applied
|
||||||
config bool // Config option applied
|
Config bool // Config option applied
|
||||||
}
|
}
|
||||||
|
|
||||||
// Override option changes constructor for a given type
|
// Override option changes constructor for a given type
|
||||||
func Override(typ, constructor interface{}) Option {
|
func Override(typ, constructor interface{}) Option {
|
||||||
return func(s *settings) error {
|
return func(s *Settings) error {
|
||||||
if i, ok := typ.(invoke); ok {
|
if i, ok := typ.(invoke); ok {
|
||||||
s.invokes[i] = fx.Invoke(constructor)
|
s.invokes[i] = fx.Invoke(constructor)
|
||||||
return nil
|
return nil
|
||||||
@ -106,18 +106,20 @@ func Override(typ, constructor interface{}) Option {
|
|||||||
|
|
||||||
var defConf = config.Default()
|
var defConf = config.Default()
|
||||||
|
|
||||||
var defaults = []Option{
|
func defaults() []Option {
|
||||||
Override(new(helpers.MetricsCtx), context.Background),
|
return []Option{
|
||||||
|
Override(new(helpers.MetricsCtx), context.Background),
|
||||||
|
|
||||||
randomIdentity(),
|
randomIdentity(),
|
||||||
|
|
||||||
Override(new(datastore.Batching), testing.MapDatastore),
|
Override(new(datastore.Batching), testing.MapDatastore),
|
||||||
Override(new(blockstore.Blockstore), testing.MapBlockstore), // NOT on top of ds above
|
Override(new(blockstore.Blockstore), testing.MapBlockstore), // NOT on top of ds above
|
||||||
Override(new(record.Validator), modules.RecordValidator),
|
Override(new(record.Validator), modules.RecordValidator),
|
||||||
|
|
||||||
// Filecoin modules
|
// Filecoin modules
|
||||||
|
|
||||||
Override(new(*chain.ChainStore), chain.NewChainStore),
|
Override(new(*chain.ChainStore), chain.NewChainStore),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Online sets up basic libp2p node
|
// Online sets up basic libp2p node
|
||||||
@ -125,8 +127,8 @@ func Online() Option {
|
|||||||
return Options(
|
return Options(
|
||||||
// make sure that online is applied before Config.
|
// make sure that online is applied before Config.
|
||||||
// This is important because Config overrides some of Online units
|
// This is important because Config overrides some of Online units
|
||||||
func(s *settings) error { s.online = true; return nil },
|
func(s *Settings) error { s.Online = true; return nil },
|
||||||
applyIf(func(s *settings) bool { return s.config },
|
ApplyIf(func(s *Settings) bool { return s.Config },
|
||||||
Error(errors.New("the Online option must be set before Config option")),
|
Error(errors.New("the Online option must be set before Config option")),
|
||||||
),
|
),
|
||||||
|
|
||||||
@ -180,12 +182,12 @@ func Online() Option {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config sets up constructors based on the provided config
|
// Config sets up constructors based on the provided Config
|
||||||
func Config(cfg *config.Root) Option {
|
func Config(cfg *config.Root) Option {
|
||||||
return Options(
|
return Options(
|
||||||
func(s *settings) error { s.config = true; return nil },
|
func(s *Settings) error { s.Config = true; return nil },
|
||||||
|
|
||||||
applyIf(func(s *settings) bool { return s.online },
|
ApplyIf(func(s *Settings) bool { return s.Online },
|
||||||
Override(StartListeningKey, lp2p.StartListening(cfg.Libp2p.ListenAddresses)),
|
Override(StartListeningKey, lp2p.StartListening(cfg.Libp2p.ListenAddresses)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -194,13 +196,13 @@ func Config(cfg *config.Root) Option {
|
|||||||
// New builds and starts new Filecoin node
|
// New builds and starts new Filecoin node
|
||||||
func New(ctx context.Context, opts ...Option) (api.API, error) {
|
func New(ctx context.Context, opts ...Option) (api.API, error) {
|
||||||
resAPI := &API{}
|
resAPI := &API{}
|
||||||
settings := settings{
|
settings := Settings{
|
||||||
modules: map[interface{}]fx.Option{},
|
modules: map[interface{}]fx.Option{},
|
||||||
invokes: make([]fx.Option, _nInvokes),
|
invokes: make([]fx.Option, _nInvokes),
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply module options in the right order
|
// apply module options in the right order
|
||||||
if err := Options(Options(defaults...), Options(opts...))(&settings); err != nil {
|
if err := Options(Options(defaults()...), Options(opts...))(&settings); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,6 +224,8 @@ func New(ctx context.Context, opts ...Option) (api.API, error) {
|
|||||||
fx.Options(settings.invokes...),
|
fx.Options(settings.invokes...),
|
||||||
|
|
||||||
fx.Extract(resAPI),
|
fx.Extract(resAPI),
|
||||||
|
|
||||||
|
fx.NopLogger,
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: we probably should have a 'firewall' for Closing signal
|
// TODO: we probably should have a 'firewall' for Closing signal
|
||||||
|
62
node/node_test.go
Normal file
62
node/node_test.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package node_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/filecoin-project/go-lotus/node"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-lotus/api"
|
||||||
|
"github.com/filecoin-project/go-lotus/api/client"
|
||||||
|
"github.com/filecoin-project/go-lotus/api/test"
|
||||||
|
"github.com/filecoin-project/go-lotus/lib/jsonrpc"
|
||||||
|
|
||||||
|
mocknet "github.com/libp2p/go-libp2p/p2p/net/mock"
|
||||||
|
)
|
||||||
|
|
||||||
|
func builder(t *testing.T, n int) []api.API {
|
||||||
|
ctx := context.Background()
|
||||||
|
mn := mocknet.New(ctx)
|
||||||
|
|
||||||
|
out := make([]api.API, n)
|
||||||
|
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
var err error
|
||||||
|
out[i], err = node.New(ctx,
|
||||||
|
node.Online(),
|
||||||
|
MockHost(mn),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mn.LinkAll(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAPI(t *testing.T) {
|
||||||
|
test.TestApis(t, builder)
|
||||||
|
}
|
||||||
|
|
||||||
|
var nextApi int
|
||||||
|
|
||||||
|
func rpcBuilder(t *testing.T, n int) []api.API {
|
||||||
|
nodeApis := builder(t, n)
|
||||||
|
out := make([]api.API, n)
|
||||||
|
|
||||||
|
for i, a := range nodeApis {
|
||||||
|
rpcServer := jsonrpc.NewServer()
|
||||||
|
rpcServer.Register("Filecoin", a)
|
||||||
|
testServ := httptest.NewServer(rpcServer) // todo: close
|
||||||
|
out[i] = client.NewRPC(testServ.URL)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAPIRPC(t *testing.T) {
|
||||||
|
test.TestApis(t, rpcBuilder)
|
||||||
|
}
|
@ -8,11 +8,11 @@ import (
|
|||||||
// change how the node is constructed
|
// change how the node is constructed
|
||||||
//
|
//
|
||||||
// Options are applied in sequence
|
// Options are applied in sequence
|
||||||
type Option func(*settings) error
|
type Option func(*Settings) error
|
||||||
|
|
||||||
// Options groups multiple options into one
|
// Options groups multiple options into one
|
||||||
func Options(opts ...Option) Option {
|
func Options(opts ...Option) Option {
|
||||||
return func(s *settings) error {
|
return func(s *Settings) error {
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
if err := opt(s); err != nil {
|
if err := opt(s); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -24,13 +24,13 @@ func Options(opts ...Option) Option {
|
|||||||
|
|
||||||
// Error is a special option which returns an error when applied
|
// Error is a special option which returns an error when applied
|
||||||
func Error(err error) Option {
|
func Error(err error) Option {
|
||||||
return func(_ *settings) error {
|
return func(_ *Settings) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func applyIf(check func(s *settings) bool, opts ...Option) Option {
|
func ApplyIf(check func(s *Settings) bool, opts ...Option) Option {
|
||||||
return func(s *settings) error {
|
return func(s *Settings) error {
|
||||||
if check(s) {
|
if check(s) {
|
||||||
return Options(opts...)(s)
|
return Options(opts...)(s)
|
||||||
}
|
}
|
||||||
|
20
node/opts_test.go
Normal file
20
node/opts_test.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package node_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/filecoin-project/go-lotus/node"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-lotus/node/modules/lp2p"
|
||||||
|
mocknet "github.com/libp2p/go-libp2p/p2p/net/mock"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MockHost(mn mocknet.Mocknet) node.Option {
|
||||||
|
return node.Options(
|
||||||
|
node.ApplyIf(func(s *node.Settings) bool { return !s.Online },
|
||||||
|
node.Error(errors.New("MockHost must be specified after Online")),
|
||||||
|
),
|
||||||
|
|
||||||
|
node.Override(new(lp2p.RawHost), lp2p.MockHost),
|
||||||
|
node.Override(new(mocknet.Mocknet), mn),
|
||||||
|
)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user