commit
0aacd4048c
@ -40,7 +40,7 @@ type API interface {
|
||||
|
||||
NetPeers(context.Context) ([]peer.AddrInfo, error)
|
||||
NetConnect(context.Context, peer.AddrInfo) error
|
||||
NetAddrsListen(context.Context) (MultiaddrSlice, error)
|
||||
NetAddrsListen(context.Context) (peer.AddrInfo, error)
|
||||
// // ping
|
||||
|
||||
// Struct
|
||||
|
@ -20,7 +20,7 @@ type Struct struct {
|
||||
|
||||
NetPeers 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)
|
||||
}
|
||||
|
||||
func (c *Struct) NetAddrsListen(ctx context.Context) (MultiaddrSlice, error) {
|
||||
func (c *Struct) NetAddrsListen(ctx context.Context) (peer.AddrInfo, error) {
|
||||
return c.Internal.NetAddrsListen(ctx)
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/filecoin-project/go-lotus/api"
|
||||
@ -10,25 +11,28 @@ import (
|
||||
|
||||
// APIBuilder is a function which is invoked in test suite to provide
|
||||
// test nodes and networks
|
||||
type APIBuilder func() api.API
|
||||
type APIBuilder func(t *testing.T, n int) []api.API
|
||||
type testSuite struct {
|
||||
makeNode APIBuilder
|
||||
makeNodes APIBuilder
|
||||
}
|
||||
|
||||
// TestApis is the entry point to API test suite
|
||||
func TestApis(t *testing.T, b APIBuilder) {
|
||||
ts := testSuite{
|
||||
makeNode: b,
|
||||
makeNodes: b,
|
||||
}
|
||||
|
||||
t.Run("version", ts.testVersion)
|
||||
t.Run("id", ts.testID)
|
||||
t.Run("testConnectTwo", ts.testConnectTwo)
|
||||
|
||||
}
|
||||
|
||||
func (ts *testSuite) testVersion(t *testing.T) {
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -36,3 +40,62 @@ func (ts *testSuite) testVersion(t *testing.T) {
|
||||
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
|
||||
}
|
||||
|
||||
for _, peer := range addrs {
|
||||
fmt.Println(peer)
|
||||
for _, peer := range addrs.Addrs {
|
||||
fmt.Printf("%s/p2p/%s\n", peer, addrs.ID)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
|
@ -64,8 +64,11 @@ func (a *API) NetConnect(ctx context.Context, p peer.AddrInfo) error {
|
||||
return a.Host.Connect(ctx, p)
|
||||
}
|
||||
|
||||
func (a *API) NetAddrsListen(context.Context) (api.MultiaddrSlice, error) {
|
||||
return a.Host.Addrs(), nil
|
||||
func (a *API) NetAddrsListen(context.Context) (peer.AddrInfo, error) {
|
||||
return peer.AddrInfo{
|
||||
ID: a.Host.ID(),
|
||||
Addrs: a.Host.Addrs(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
var _ api.API = &API{}
|
||||
|
@ -68,7 +68,7 @@ const (
|
||||
_nInvokes // keep this last
|
||||
)
|
||||
|
||||
type settings struct {
|
||||
type Settings struct {
|
||||
// modules is a map of constructors for DI
|
||||
//
|
||||
// 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
|
||||
invokes []fx.Option
|
||||
|
||||
online bool // Online option applied
|
||||
config bool // Config option applied
|
||||
Online bool // Online option applied
|
||||
Config bool // Config option applied
|
||||
}
|
||||
|
||||
// Override option changes constructor for a given type
|
||||
func Override(typ, constructor interface{}) Option {
|
||||
return func(s *settings) error {
|
||||
return func(s *Settings) error {
|
||||
if i, ok := typ.(invoke); ok {
|
||||
s.invokes[i] = fx.Invoke(constructor)
|
||||
return nil
|
||||
@ -106,18 +106,20 @@ func Override(typ, constructor interface{}) Option {
|
||||
|
||||
var defConf = config.Default()
|
||||
|
||||
var defaults = []Option{
|
||||
Override(new(helpers.MetricsCtx), context.Background),
|
||||
func defaults() []Option {
|
||||
return []Option{
|
||||
Override(new(helpers.MetricsCtx), context.Background),
|
||||
|
||||
randomIdentity(),
|
||||
randomIdentity(),
|
||||
|
||||
Override(new(datastore.Batching), testing.MapDatastore),
|
||||
Override(new(blockstore.Blockstore), testing.MapBlockstore), // NOT on top of ds above
|
||||
Override(new(record.Validator), modules.RecordValidator),
|
||||
Override(new(datastore.Batching), testing.MapDatastore),
|
||||
Override(new(blockstore.Blockstore), testing.MapBlockstore), // NOT on top of ds above
|
||||
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
|
||||
@ -125,8 +127,8 @@ func Online() Option {
|
||||
return Options(
|
||||
// make sure that online is applied before Config.
|
||||
// This is important because Config overrides some of Online units
|
||||
func(s *settings) error { s.online = true; return nil },
|
||||
applyIf(func(s *settings) bool { return s.config },
|
||||
func(s *Settings) error { s.Online = true; return nil },
|
||||
ApplyIf(func(s *Settings) bool { return s.Config },
|
||||
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 {
|
||||
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)),
|
||||
),
|
||||
)
|
||||
@ -194,13 +196,13 @@ func Config(cfg *config.Root) Option {
|
||||
// New builds and starts new Filecoin node
|
||||
func New(ctx context.Context, opts ...Option) (api.API, error) {
|
||||
resAPI := &API{}
|
||||
settings := settings{
|
||||
settings := Settings{
|
||||
modules: map[interface{}]fx.Option{},
|
||||
invokes: make([]fx.Option, _nInvokes),
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
@ -222,6 +224,8 @@ func New(ctx context.Context, opts ...Option) (api.API, error) {
|
||||
fx.Options(settings.invokes...),
|
||||
|
||||
fx.Extract(resAPI),
|
||||
|
||||
fx.NopLogger,
|
||||
)
|
||||
|
||||
// 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
|
||||
//
|
||||
// Options are applied in sequence
|
||||
type Option func(*settings) error
|
||||
type Option func(*Settings) error
|
||||
|
||||
// Options groups multiple options into one
|
||||
func Options(opts ...Option) Option {
|
||||
return func(s *settings) error {
|
||||
return func(s *Settings) error {
|
||||
for _, opt := range opts {
|
||||
if err := opt(s); err != nil {
|
||||
return err
|
||||
@ -24,13 +24,13 @@ func Options(opts ...Option) Option {
|
||||
|
||||
// Error is a special option which returns an error when applied
|
||||
func Error(err error) Option {
|
||||
return func(_ *settings) error {
|
||||
return func(_ *Settings) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func applyIf(check func(s *settings) bool, opts ...Option) Option {
|
||||
return func(s *settings) error {
|
||||
func ApplyIf(check func(s *Settings) bool, opts ...Option) Option {
|
||||
return func(s *Settings) error {
|
||||
if check(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