Merge pull request #12 from filecoin-project/test/net

Wire up tests
This commit is contained in:
Łukasz Magiera 2019-07-10 15:10:17 +02:00 committed by GitHub
commit 0aacd4048c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 188 additions and 36 deletions

View File

@ -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

View File

@ -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)
}

View File

@ -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")
}
}

View File

@ -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
},

View File

@ -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{}

View File

@ -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
View 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)
}

View File

@ -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
View 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),
)
}