diff --git a/cli/paych_test.go b/cli/paych_test.go index 77a6e61eb..fcd9c99a3 100644 --- a/cli/paych_test.go +++ b/cli/paych_test.go @@ -1,9 +1,7 @@ package cli import ( - "bytes" "context" - "flag" "fmt" "os" "regexp" @@ -12,24 +10,21 @@ import ( "testing" "time" - "github.com/stretchr/testify/require" - "github.com/urfave/cli/v2" + clitest "github.com/filecoin-project/lotus/cli/test" "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" - cbor "github.com/ipfs/go-ipld-cbor" - "github.com/multiformats/go-multiaddr" - "github.com/filecoin-project/lotus/chain/actors/adt" "github.com/filecoin-project/lotus/chain/actors/builtin/paych" "github.com/filecoin-project/lotus/chain/actors/policy" + cbor "github.com/ipfs/go-ipld-cbor" + "github.com/stretchr/testify/require" "github.com/filecoin-project/lotus/api/apibstore" "github.com/filecoin-project/lotus/api/test" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/events" "github.com/filecoin-project/lotus/chain/types" - builder "github.com/filecoin-project/lotus/node/test" ) func init() { @@ -42,24 +37,24 @@ func init() { // commands func TestPaymentChannels(t *testing.T) { _ = os.Setenv("BELLMAN_NO_GPU", "1") + clitest.QuietMiningLogs() blocktime := 5 * time.Millisecond ctx := context.Background() - nodes, addrs := startTwoNodesOneMiner(ctx, t, blocktime) + nodes, addrs := clitest.StartTwoNodesOneMiner(ctx, t, blocktime) paymentCreator := nodes[0] paymentReceiver := nodes[1] creatorAddr := addrs[0] receiverAddr := addrs[1] // Create mock CLI - mockCLI := newMockCLI(t) - creatorCLI := mockCLI.client(paymentCreator.ListenAddr) - receiverCLI := mockCLI.client(paymentReceiver.ListenAddr) + mockCLI := clitest.NewMockCLI(t, Commands) + creatorCLI := mockCLI.Client(paymentCreator.ListenAddr) + receiverCLI := mockCLI.Client(paymentReceiver.ListenAddr) // creator: paych add-funds channelAmt := "100000" - cmd := []string{creatorAddr.String(), receiverAddr.String(), channelAmt} - chstr := creatorCLI.runCmd(paychAddFundsCmd, cmd) + chstr := creatorCLI.RunCmd("paych", "add-funds", creatorAddr.String(), receiverAddr.String(), channelAmt) chAddr, err := address.NewFromString(chstr) require.NoError(t, err) @@ -67,16 +62,13 @@ func TestPaymentChannels(t *testing.T) { // creator: paych voucher create voucherAmt := 100 vamt := strconv.Itoa(voucherAmt) - cmd = []string{chAddr.String(), vamt} - voucher := creatorCLI.runCmd(paychVoucherCreateCmd, cmd) + voucher := creatorCLI.RunCmd("paych", "voucher", "create", chAddr.String(), vamt) // receiver: paych voucher add - cmd = []string{chAddr.String(), voucher} - receiverCLI.runCmd(paychVoucherAddCmd, cmd) + receiverCLI.RunCmd("paych", "voucher", "add", chAddr.String(), voucher) // creator: paych settle - cmd = []string{chAddr.String()} - creatorCLI.runCmd(paychSettleCmd, cmd) + creatorCLI.RunCmd("paych", "settle", chAddr.String()) // Wait for the chain to reach the settle height chState := getPaychState(ctx, t, paymentReceiver, chAddr) @@ -85,8 +77,7 @@ func TestPaymentChannels(t *testing.T) { waitForHeight(ctx, t, paymentReceiver, sa) // receiver: paych collect - cmd = []string{chAddr.String()} - receiverCLI.runCmd(paychCloseCmd, cmd) + receiverCLI.RunCmd("paych", "collect", chAddr.String()) } type voucherSpec struct { @@ -98,20 +89,21 @@ type voucherSpec struct { // TestPaymentChannelStatus tests the payment channel status CLI command func TestPaymentChannelStatus(t *testing.T) { _ = os.Setenv("BELLMAN_NO_GPU", "1") + clitest.QuietMiningLogs() blocktime := 5 * time.Millisecond ctx := context.Background() - nodes, addrs := startTwoNodesOneMiner(ctx, t, blocktime) + nodes, addrs := clitest.StartTwoNodesOneMiner(ctx, t, blocktime) paymentCreator := nodes[0] creatorAddr := addrs[0] receiverAddr := addrs[1] // Create mock CLI - mockCLI := newMockCLI(t) - creatorCLI := mockCLI.client(paymentCreator.ListenAddr) + mockCLI := clitest.NewMockCLI(t, Commands) + creatorCLI := mockCLI.Client(paymentCreator.ListenAddr) - cmd := []string{creatorAddr.String(), receiverAddr.String()} - out := creatorCLI.runCmd(paychStatusByFromToCmd, cmd) + // creator: paych status-by-from-to + out := creatorCLI.RunCmd("paych", "status-by-from-to", creatorAddr.String(), receiverAddr.String()) fmt.Println(out) noChannelState := "Channel does not exist" require.Regexp(t, regexp.MustCompile(noChannelState), out) @@ -120,14 +112,17 @@ func TestPaymentChannelStatus(t *testing.T) { create := make(chan string) go func() { // creator: paych add-funds - cmd := []string{creatorAddr.String(), receiverAddr.String(), fmt.Sprintf("%d", channelAmt)} - create <- creatorCLI.runCmd(paychAddFundsCmd, cmd) + create <- creatorCLI.RunCmd( + "paych", + "add-funds", + creatorAddr.String(), + receiverAddr.String(), + fmt.Sprintf("%d", channelAmt)) }() // Wait for the output to stop being "Channel does not exist" for regexp.MustCompile(noChannelState).MatchString(out) { - cmd := []string{creatorAddr.String(), receiverAddr.String()} - out = creatorCLI.runCmd(paychStatusByFromToCmd, cmd) + out = creatorCLI.RunCmd("paych", "status-by-from-to", creatorAddr.String(), receiverAddr.String()) } fmt.Println(out) @@ -147,8 +142,7 @@ func TestPaymentChannelStatus(t *testing.T) { // Wait for create channel to complete chstr := <-create - cmd = []string{chstr} - out = creatorCLI.runCmd(paychStatusCmd, cmd) + out = creatorCLI.RunCmd("paych", "status", chstr) fmt.Println(out) // Output should have the channel address require.Regexp(t, regexp.MustCompile("Channel.*"+chstr), out) @@ -160,11 +154,9 @@ func TestPaymentChannelStatus(t *testing.T) { // creator: paych voucher create voucherAmt := uint64(10) - cmd = []string{chAddr.String(), fmt.Sprintf("%d", voucherAmt)} - creatorCLI.runCmd(paychVoucherCreateCmd, cmd) + creatorCLI.RunCmd("paych", "voucher", "create", chAddr.String(), fmt.Sprintf("%d", voucherAmt)) - cmd = []string{chstr} - out = creatorCLI.runCmd(paychStatusCmd, cmd) + out = creatorCLI.RunCmd("paych", "status", chstr) fmt.Println(out) voucherAmtAtto := types.BigMul(types.NewInt(voucherAmt), types.NewInt(build.FilecoinPrecision)) voucherAmtStr := fmt.Sprintf("%d", voucherAmtAtto) @@ -176,24 +168,24 @@ func TestPaymentChannelStatus(t *testing.T) { // channel voucher commands func TestPaymentChannelVouchers(t *testing.T) { _ = os.Setenv("BELLMAN_NO_GPU", "1") + clitest.QuietMiningLogs() blocktime := 5 * time.Millisecond ctx := context.Background() - nodes, addrs := startTwoNodesOneMiner(ctx, t, blocktime) + nodes, addrs := clitest.StartTwoNodesOneMiner(ctx, t, blocktime) paymentCreator := nodes[0] paymentReceiver := nodes[1] creatorAddr := addrs[0] receiverAddr := addrs[1] // Create mock CLI - mockCLI := newMockCLI(t) - creatorCLI := mockCLI.client(paymentCreator.ListenAddr) - receiverCLI := mockCLI.client(paymentReceiver.ListenAddr) + mockCLI := clitest.NewMockCLI(t, Commands) + creatorCLI := mockCLI.Client(paymentCreator.ListenAddr) + receiverCLI := mockCLI.Client(paymentReceiver.ListenAddr) // creator: paych add-funds channelAmt := "100000" - cmd := []string{creatorAddr.String(), receiverAddr.String(), channelAmt} - chstr := creatorCLI.runCmd(paychAddFundsCmd, cmd) + chstr := creatorCLI.RunCmd("paych", "add-funds", creatorAddr.String(), receiverAddr.String(), channelAmt) chAddr, err := address.NewFromString(chstr) require.NoError(t, err) @@ -203,39 +195,33 @@ func TestPaymentChannelVouchers(t *testing.T) { // creator: paych voucher create // Note: implied --lane=0 voucherAmt1 := 100 - cmd = []string{chAddr.String(), strconv.Itoa(voucherAmt1)} - voucher1 := creatorCLI.runCmd(paychVoucherCreateCmd, cmd) + voucher1 := creatorCLI.RunCmd("paych", "voucher", "create", chAddr.String(), strconv.Itoa(voucherAmt1)) vouchers = append(vouchers, voucherSpec{serialized: voucher1, lane: 0, amt: voucherAmt1}) // creator: paych voucher create --lane=5 lane5 := "--lane=5" voucherAmt2 := 50 - cmd = []string{lane5, chAddr.String(), strconv.Itoa(voucherAmt2)} - voucher2 := creatorCLI.runCmd(paychVoucherCreateCmd, cmd) + voucher2 := creatorCLI.RunCmd("paych", "voucher", "create", lane5, chAddr.String(), strconv.Itoa(voucherAmt2)) vouchers = append(vouchers, voucherSpec{serialized: voucher2, lane: 5, amt: voucherAmt2}) // creator: paych voucher create --lane=5 voucherAmt3 := 70 - cmd = []string{lane5, chAddr.String(), strconv.Itoa(voucherAmt3)} - voucher3 := creatorCLI.runCmd(paychVoucherCreateCmd, cmd) + voucher3 := creatorCLI.RunCmd("paych", "voucher", "create", lane5, chAddr.String(), strconv.Itoa(voucherAmt3)) vouchers = append(vouchers, voucherSpec{serialized: voucher3, lane: 5, amt: voucherAmt3}) // creator: paych voucher create --lane=5 voucherAmt4 := 80 - cmd = []string{lane5, chAddr.String(), strconv.Itoa(voucherAmt4)} - voucher4 := creatorCLI.runCmd(paychVoucherCreateCmd, cmd) + voucher4 := creatorCLI.RunCmd("paych", "voucher", "create", lane5, chAddr.String(), strconv.Itoa(voucherAmt4)) vouchers = append(vouchers, voucherSpec{serialized: voucher4, lane: 5, amt: voucherAmt4}) // creator: paych voucher list --export - cmd = []string{"--export", chAddr.String()} - list := creatorCLI.runCmd(paychVoucherListCmd, cmd) + list := creatorCLI.RunCmd("paych", "voucher", "list", "--export", chAddr.String()) // Check that voucher list output is correct on creator checkVoucherOutput(t, list, vouchers) // creator: paych voucher best-spendable - cmd = []string{"--export", chAddr.String()} - bestSpendable := creatorCLI.runCmd(paychVoucherBestSpendableCmd, cmd) + bestSpendable := creatorCLI.RunCmd("paych", "voucher", "best-spendable", "--export", chAddr.String()) // Check that best spendable output is correct on creator bestVouchers := []voucherSpec{ @@ -245,31 +231,25 @@ func TestPaymentChannelVouchers(t *testing.T) { checkVoucherOutput(t, bestSpendable, bestVouchers) // receiver: paych voucher add - cmd = []string{chAddr.String(), voucher1} - receiverCLI.runCmd(paychVoucherAddCmd, cmd) + receiverCLI.RunCmd("paych", "voucher", "add", chAddr.String(), voucher1) // receiver: paych voucher add - cmd = []string{chAddr.String(), voucher2} - receiverCLI.runCmd(paychVoucherAddCmd, cmd) + receiverCLI.RunCmd("paych", "voucher", "add", chAddr.String(), voucher2) // receiver: paych voucher add - cmd = []string{chAddr.String(), voucher3} - receiverCLI.runCmd(paychVoucherAddCmd, cmd) + receiverCLI.RunCmd("paych", "voucher", "add", chAddr.String(), voucher3) // receiver: paych voucher add - cmd = []string{chAddr.String(), voucher4} - receiverCLI.runCmd(paychVoucherAddCmd, cmd) + receiverCLI.RunCmd("paych", "voucher", "add", chAddr.String(), voucher4) // receiver: paych voucher list --export - cmd = []string{"--export", chAddr.String()} - list = receiverCLI.runCmd(paychVoucherListCmd, cmd) + list = receiverCLI.RunCmd("paych", "voucher", "list", "--export", chAddr.String()) // Check that voucher list output is correct on receiver checkVoucherOutput(t, list, vouchers) // receiver: paych voucher best-spendable - cmd = []string{"--export", chAddr.String()} - bestSpendable = receiverCLI.runCmd(paychVoucherBestSpendableCmd, cmd) + bestSpendable = receiverCLI.RunCmd("paych", "voucher", "best-spendable", "--export", chAddr.String()) // Check that best spendable output is correct on receiver bestVouchers = []voucherSpec{ @@ -279,12 +259,10 @@ func TestPaymentChannelVouchers(t *testing.T) { checkVoucherOutput(t, bestSpendable, bestVouchers) // receiver: paych voucher submit - cmd = []string{chAddr.String(), voucher1} - receiverCLI.runCmd(paychVoucherSubmitCmd, cmd) + receiverCLI.RunCmd("paych", "voucher", "submit", chAddr.String(), voucher1) // receiver: paych voucher best-spendable - cmd = []string{"--export", chAddr.String()} - bestSpendable = receiverCLI.runCmd(paychVoucherBestSpendableCmd, cmd) + bestSpendable = receiverCLI.RunCmd("paych", "voucher", "best-spendable", "--export", chAddr.String()) // Check that best spendable output no longer includes submitted voucher bestVouchers = []voucherSpec{ @@ -295,12 +273,10 @@ func TestPaymentChannelVouchers(t *testing.T) { // There are three vouchers in lane 5: 50, 70, 80 // Submit the voucher for 50. Best spendable should still be 80. // receiver: paych voucher submit - cmd = []string{chAddr.String(), voucher2} - receiverCLI.runCmd(paychVoucherSubmitCmd, cmd) + receiverCLI.RunCmd("paych", "voucher", "submit", chAddr.String(), voucher2) // receiver: paych voucher best-spendable - cmd = []string{"--export", chAddr.String()} - bestSpendable = receiverCLI.runCmd(paychVoucherBestSpendableCmd, cmd) + bestSpendable = receiverCLI.RunCmd("paych", "voucher", "best-spendable", "--export", chAddr.String()) // Check that best spendable output still includes the voucher for 80 bestVouchers = []voucherSpec{ @@ -310,12 +286,10 @@ func TestPaymentChannelVouchers(t *testing.T) { // Submit the voucher for 80 // receiver: paych voucher submit - cmd = []string{chAddr.String(), voucher4} - receiverCLI.runCmd(paychVoucherSubmitCmd, cmd) + receiverCLI.RunCmd("paych", "voucher", "submit", chAddr.String(), voucher4) // receiver: paych voucher best-spendable - cmd = []string{"--export", chAddr.String()} - bestSpendable = receiverCLI.runCmd(paychVoucherBestSpendableCmd, cmd) + bestSpendable = receiverCLI.RunCmd("paych", "voucher", "best-spendable", "--export", chAddr.String()) // Check that best spendable output no longer includes submitted voucher bestVouchers = []voucherSpec{} @@ -326,22 +300,27 @@ func TestPaymentChannelVouchers(t *testing.T) { // is greater than what's left in the channel, voucher create fails func TestPaymentChannelVoucherCreateShortfall(t *testing.T) { _ = os.Setenv("BELLMAN_NO_GPU", "1") + clitest.QuietMiningLogs() blocktime := 5 * time.Millisecond ctx := context.Background() - nodes, addrs := startTwoNodesOneMiner(ctx, t, blocktime) + nodes, addrs := clitest.StartTwoNodesOneMiner(ctx, t, blocktime) paymentCreator := nodes[0] creatorAddr := addrs[0] receiverAddr := addrs[1] // Create mock CLI - mockCLI := newMockCLI(t) - creatorCLI := mockCLI.client(paymentCreator.ListenAddr) + mockCLI := clitest.NewMockCLI(t, Commands) + creatorCLI := mockCLI.Client(paymentCreator.ListenAddr) // creator: paych add-funds channelAmt := 100 - cmd := []string{creatorAddr.String(), receiverAddr.String(), fmt.Sprintf("%d", channelAmt)} - chstr := creatorCLI.runCmd(paychAddFundsCmd, cmd) + chstr := creatorCLI.RunCmd( + "paych", + "add-funds", + creatorAddr.String(), + receiverAddr.String(), + fmt.Sprintf("%d", channelAmt)) chAddr, err := address.NewFromString(chstr) require.NoError(t, err) @@ -349,15 +328,25 @@ func TestPaymentChannelVoucherCreateShortfall(t *testing.T) { // creator: paych voucher create --lane=1 voucherAmt1 := 60 lane1 := "--lane=1" - cmd = []string{lane1, chAddr.String(), strconv.Itoa(voucherAmt1)} - voucher1 := creatorCLI.runCmd(paychVoucherCreateCmd, cmd) + voucher1 := creatorCLI.RunCmd( + "paych", + "voucher", + "create", + lane1, + chAddr.String(), + strconv.Itoa(voucherAmt1)) fmt.Println(voucher1) // creator: paych voucher create --lane=2 lane2 := "--lane=2" voucherAmt2 := 70 - cmd = []string{lane2, chAddr.String(), strconv.Itoa(voucherAmt2)} - _, err = creatorCLI.runCmdRaw(paychVoucherCreateCmd, cmd) + _, err = creatorCLI.RunCmdRaw( + "paych", + "voucher", + "create", + lane2, + chAddr.String(), + strconv.Itoa(voucherAmt2)) // Should fail because channel doesn't have required amount require.Error(t, err) @@ -388,129 +377,6 @@ func checkVoucherOutput(t *testing.T, list string, vouchers []voucherSpec) { } } -func startTwoNodesOneMiner(ctx context.Context, t *testing.T, blocktime time.Duration) ([]test.TestNode, []address.Address) { - n, sn := builder.RPCMockSbBuilder(t, test.TwoFull, test.OneMiner) - - paymentCreator := n[0] - paymentReceiver := n[1] - miner := sn[0] - - // Get everyone connected - addrs, err := paymentCreator.NetAddrsListen(ctx) - if err != nil { - t.Fatal(err) - } - - if err := paymentReceiver.NetConnect(ctx, addrs); err != nil { - t.Fatal(err) - } - - if err := miner.NetConnect(ctx, addrs); err != nil { - t.Fatal(err) - } - - // Start mining blocks - bm := test.NewBlockMiner(ctx, t, miner, blocktime) - bm.MineBlocks() - - // Send some funds to register the receiver - receiverAddr, err := paymentReceiver.WalletNew(ctx, types.KTSecp256k1) - if err != nil { - t.Fatal(err) - } - - test.SendFunds(ctx, t, paymentCreator, receiverAddr, abi.NewTokenAmount(1e18)) - - // Get the creator's address - creatorAddr, err := paymentCreator.WalletDefaultAddress(ctx) - if err != nil { - t.Fatal(err) - } - - // Create mock CLI - return n, []address.Address{creatorAddr, receiverAddr} -} - -type mockCLI struct { - t *testing.T - cctx *cli.Context - out *bytes.Buffer -} - -// TODO: refactor to use the methods in cli/test/mockcli.go -func newMockCLI(t *testing.T) *mockCLI { - // Create a CLI App with an --api-url flag so that we can specify which node - // the command should be executed against - app := cli.NewApp() - app.Flags = []cli.Flag{ - &cli.StringFlag{ - Name: "api-url", - Hidden: true, - }, - } - var out bytes.Buffer - app.Writer = &out - app.Setup() - - cctx := cli.NewContext(app, &flag.FlagSet{}, nil) - return &mockCLI{t: t, cctx: cctx, out: &out} -} - -func (c *mockCLI) client(addr multiaddr.Multiaddr) *mockCLIClient { - return &mockCLIClient{t: c.t, addr: addr, cctx: c.cctx, out: c.out} -} - -// mockCLIClient runs commands against a particular node -type mockCLIClient struct { - t *testing.T - addr multiaddr.Multiaddr - cctx *cli.Context - out *bytes.Buffer -} - -func (c *mockCLIClient) runCmd(cmd *cli.Command, input []string) string { - out, err := c.runCmdRaw(cmd, input) - require.NoError(c.t, err) - - return out -} - -func (c *mockCLIClient) runCmdRaw(cmd *cli.Command, input []string) (string, error) { - // prepend --api-url= - apiFlag := "--api-url=" + c.addr.String() - input = append([]string{apiFlag}, input...) - - fs := c.flagSet(cmd) - err := fs.Parse(input) - require.NoError(c.t, err) - - err = cmd.Action(cli.NewContext(c.cctx.App, fs, c.cctx)) - - // Get the output - str := strings.TrimSpace(c.out.String()) - c.out.Reset() - return str, err -} - -func (c *mockCLIClient) flagSet(cmd *cli.Command) *flag.FlagSet { - // Apply app level flags (so we can process --api-url flag) - fs := &flag.FlagSet{} - for _, f := range c.cctx.App.Flags { - err := f.Apply(fs) - if err != nil { - c.t.Fatal(err) - } - } - // Apply command level flags - for _, f := range cmd.Flags { - err := f.Apply(fs) - if err != nil { - c.t.Fatal(err) - } - } - return fs -} - // waitForHeight waits for the node to reach the given chain epoch func waitForHeight(ctx context.Context, t *testing.T, node test.TestNode, height abi.ChainEpoch) { atHeight := make(chan struct{}) diff --git a/cli/test/client.go b/cli/test/client.go index 3a5146219..c74f881b0 100644 --- a/cli/test/client.go +++ b/cli/test/client.go @@ -25,8 +25,8 @@ func RunClientTest(t *testing.T, cmds []*lcli.Command, clientNode test.TestNode) defer cancel() // Create mock CLI - mockCLI := newMockCLI(t, cmds) - clientCLI := mockCLI.client(clientNode.ListenAddr) + mockCLI := NewMockCLI(t, cmds) + clientCLI := mockCLI.Client(clientNode.ListenAddr) // Get the miner address addrs, err := clientNode.StateListMiners(ctx, types.EmptyTSK) @@ -37,10 +37,7 @@ func RunClientTest(t *testing.T, cmds []*lcli.Command, clientNode test.TestNode) fmt.Println("Miner:", minerAddr) // client query-ask - cmd := []string{ - "client", "query-ask", minerAddr.String(), - } - out := clientCLI.runCmd(cmd) + out := clientCLI.RunCmd("client", "query-ask", minerAddr.String()) require.Regexp(t, regexp.MustCompile("Ask:"), out) // Create a deal (non-interactive) @@ -50,10 +47,7 @@ func RunClientTest(t *testing.T, cmds []*lcli.Command, clientNode test.TestNode) dataCid := res.Root price := "1000000attofil" duration := fmt.Sprintf("%d", build.MinDealDuration) - cmd = []string{ - "client", "deal", dataCid.String(), minerAddr.String(), price, duration, - } - out = clientCLI.runCmd(cmd) + out = clientCLI.RunCmd("client", "deal", dataCid.String(), minerAddr.String(), price, duration) fmt.Println("client deal", out) // Create a deal (interactive) @@ -67,9 +61,7 @@ func RunClientTest(t *testing.T, cmds []*lcli.Command, clientNode test.TestNode) require.NoError(t, err) dataCid2 := res.Root duration = fmt.Sprintf("%d", build.MinDealDuration/builtin.EpochsInDay) - cmd = []string{ - "client", "deal", - } + cmd := []string{"client", "deal"} interactiveCmds := []string{ dataCid2.String(), duration, @@ -77,15 +69,14 @@ func RunClientTest(t *testing.T, cmds []*lcli.Command, clientNode test.TestNode) "no", "yes", } - out = clientCLI.runInteractiveCmd(cmd, interactiveCmds) + out = clientCLI.RunInteractiveCmd(cmd, interactiveCmds) fmt.Println("client deal:\n", out) // Wait for provider to start sealing deal dealStatus := "" for dealStatus != "StorageDealSealing" { // client list-deals - cmd = []string{"client", "list-deals"} - out = clientCLI.runCmd(cmd) + out = clientCLI.RunCmd("client", "list-deals") fmt.Println("list-deals:\n", out) lines := strings.Split(out, "\n") @@ -106,10 +97,7 @@ func RunClientTest(t *testing.T, cmds []*lcli.Command, clientNode test.TestNode) tmpdir, err := ioutil.TempDir(os.TempDir(), "test-cli-client") require.NoError(t, err) path := filepath.Join(tmpdir, "outfile.dat") - cmd = []string{ - "client", "retrieve", dataCid.String(), path, - } - out = clientCLI.runCmd(cmd) + out = clientCLI.RunCmd("client", "retrieve", dataCid.String(), path) fmt.Println("retrieve:\n", out) require.Regexp(t, regexp.MustCompile("Success"), out) } diff --git a/cli/test/mockcli.go b/cli/test/mockcli.go index c7eb70092..65b0bc30a 100644 --- a/cli/test/mockcli.go +++ b/cli/test/mockcli.go @@ -11,14 +11,14 @@ import ( lcli "github.com/urfave/cli/v2" ) -type mockCLI struct { +type MockCLI struct { t *testing.T cmds []*lcli.Command cctx *lcli.Context out *bytes.Buffer } -func newMockCLI(t *testing.T, cmds []*lcli.Command) *mockCLI { +func NewMockCLI(t *testing.T, cmds []*lcli.Command) *MockCLI { // Create a CLI App with an --api-url flag so that we can specify which node // the command should be executed against app := &lcli.App{ @@ -36,15 +36,15 @@ func newMockCLI(t *testing.T, cmds []*lcli.Command) *mockCLI { app.Setup() cctx := lcli.NewContext(app, &flag.FlagSet{}, nil) - return &mockCLI{t: t, cmds: cmds, cctx: cctx, out: &out} + return &MockCLI{t: t, cmds: cmds, cctx: cctx, out: &out} } -func (c *mockCLI) client(addr multiaddr.Multiaddr) *mockCLIClient { - return &mockCLIClient{t: c.t, cmds: c.cmds, addr: addr, cctx: c.cctx, out: c.out} +func (c *MockCLI) Client(addr multiaddr.Multiaddr) *MockCLIClient { + return &MockCLIClient{t: c.t, cmds: c.cmds, addr: addr, cctx: c.cctx, out: c.out} } -// mockCLIClient runs commands against a particular node -type mockCLIClient struct { +// MockCLIClient runs commands against a particular node +type MockCLIClient struct { t *testing.T cmds []*lcli.Command addr multiaddr.Multiaddr @@ -52,42 +52,48 @@ type mockCLIClient struct { out *bytes.Buffer } -func (c *mockCLIClient) run(cmd []string, params []string, args []string) string { - // Add parameter --api-url= - apiFlag := "--api-url=" + c.addr.String() - params = append([]string{apiFlag}, params...) - - err := c.cctx.App.Run(append(append(cmd, params...), args...)) - require.NoError(c.t, err) - - // Get the output - str := strings.TrimSpace(c.out.String()) - c.out.Reset() - return str -} - -func (c *mockCLIClient) runCmd(input []string) string { - cmd := c.cmdByNameSub(input[0], input[1]) - out, err := c.runCmdRaw(cmd, input[2:]) +func (c *MockCLIClient) RunCmd(input ...string) string { + out, err := c.RunCmdRaw(input...) require.NoError(c.t, err) return out } -func (c *mockCLIClient) cmdByNameSub(name string, sub string) *lcli.Command { - for _, c := range c.cmds { - if c.Name == name { - for _, s := range c.Subcommands { - if s.Name == sub { - return s - } - } +// Given an input, find the corresponding command or sub-command. +// eg "paych add-funds" +func (c *MockCLIClient) cmdByNameSub(input []string) (*lcli.Command, []string) { + name := input[0] + for _, cmd := range c.cmds { + if cmd.Name == name { + return c.findSubcommand(cmd, input[1:]) } } - return nil + return nil, []string{} } -func (c *mockCLIClient) runCmdRaw(cmd *lcli.Command, input []string) (string, error) { +func (c *MockCLIClient) findSubcommand(cmd *lcli.Command, input []string) (*lcli.Command, []string) { + // If there are no sub-commands, return the current command + if len(cmd.Subcommands) == 0 { + return cmd, input + } + + // Check each sub-command for a match against the name + subName := input[0] + for _, subCmd := range cmd.Subcommands { + if subCmd.Name == subName { + // Found a match, recursively search for sub-commands + return c.findSubcommand(subCmd, input[1:]) + } + } + return nil, []string{} +} + +func (c *MockCLIClient) RunCmdRaw(input ...string) (string, error) { + cmd, input := c.cmdByNameSub(input) + if cmd == nil { + panic("Could not find command " + input[0] + " " + input[1]) + } + // prepend --api-url= apiFlag := "--api-url=" + c.addr.String() input = append([]string{apiFlag}, input...) @@ -104,7 +110,7 @@ func (c *mockCLIClient) runCmdRaw(cmd *lcli.Command, input []string) (string, er return str, err } -func (c *mockCLIClient) flagSet(cmd *lcli.Command) *flag.FlagSet { +func (c *MockCLIClient) flagSet(cmd *lcli.Command) *flag.FlagSet { // Apply app level flags (so we can process --api-url flag) fs := &flag.FlagSet{} for _, f := range c.cctx.App.Flags { @@ -123,11 +129,11 @@ func (c *mockCLIClient) flagSet(cmd *lcli.Command) *flag.FlagSet { return fs } -func (c *mockCLIClient) runInteractiveCmd(cmd []string, interactive []string) string { +func (c *MockCLIClient) RunInteractiveCmd(cmd []string, interactive []string) string { c.toStdin(strings.Join(interactive, "\n") + "\n") - return c.runCmd(cmd) + return c.RunCmd(cmd...) } -func (c *mockCLIClient) toStdin(s string) { +func (c *MockCLIClient) toStdin(s string) { c.cctx.App.Metadata["stdin"] = bytes.NewBufferString(s) } diff --git a/cli/test/multisig.go b/cli/test/multisig.go index d2c0238d2..dd867d0b5 100644 --- a/cli/test/multisig.go +++ b/cli/test/multisig.go @@ -18,8 +18,8 @@ func RunMultisigTest(t *testing.T, cmds []*lcli.Command, clientNode test.TestNod ctx := context.Background() // Create mock CLI - mockCLI := newMockCLI(t, cmds) - clientCLI := mockCLI.client(clientNode.ListenAddr) + mockCLI := NewMockCLI(t, cmds) + clientCLI := mockCLI.Client(clientNode.ListenAddr) // Create some wallets on the node to use for testing multisig var walletAddrs []address.Address @@ -39,7 +39,7 @@ func RunMultisigTest(t *testing.T, cmds []*lcli.Command, clientNode test.TestNod paramDuration := "--duration=50" paramRequired := fmt.Sprintf("--required=%d", threshold) paramValue := fmt.Sprintf("--value=%dattofil", amtAtto) - cmd := []string{ + out := clientCLI.RunCmd( "msig", "create", paramRequired, paramDuration, @@ -47,8 +47,7 @@ func RunMultisigTest(t *testing.T, cmds []*lcli.Command, clientNode test.TestNod walletAddrs[0].String(), walletAddrs[1].String(), walletAddrs[2].String(), - } - out := clientCLI.runCmd(cmd) + ) fmt.Println(out) // Extract msig robust address from output @@ -62,18 +61,16 @@ func RunMultisigTest(t *testing.T, cmds []*lcli.Command, clientNode test.TestNod // Propose to add a new address to the msig // msig add-propose --from= paramFrom := fmt.Sprintf("--from=%s", walletAddrs[0]) - cmd = []string{ + out = clientCLI.RunCmd( "msig", "add-propose", paramFrom, msigRobustAddr, walletAddrs[3].String(), - } - out = clientCLI.runCmd(cmd) + ) fmt.Println(out) // msig inspect - cmd = []string{"msig", "inspect", "--vesting", "--decode-params", msigRobustAddr} - out = clientCLI.runCmd(cmd) + out = clientCLI.RunCmd("msig", "inspect", "--vesting", "--decode-params", msigRobustAddr) fmt.Println(out) // Expect correct balance @@ -87,7 +84,7 @@ func RunMultisigTest(t *testing.T, cmds []*lcli.Command, clientNode test.TestNod // msig add-approve --from= 0 false txnID := "0" paramFrom = fmt.Sprintf("--from=%s", walletAddrs[1]) - cmd = []string{ + out = clientCLI.RunCmd( "msig", "add-approve", paramFrom, msigRobustAddr, @@ -95,7 +92,6 @@ func RunMultisigTest(t *testing.T, cmds []*lcli.Command, clientNode test.TestNod txnID, walletAddrs[3].String(), "false", - } - out = clientCLI.runCmd(cmd) + ) fmt.Println(out) } diff --git a/cli/test/net.go b/cli/test/net.go index d13993d16..836b81a8f 100644 --- a/cli/test/net.go +++ b/cli/test/net.go @@ -5,6 +5,9 @@ import ( "testing" "time" + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/lotus/api/test" test2 "github.com/filecoin-project/lotus/node/test" @@ -39,3 +42,46 @@ func StartOneNodeOneMiner(ctx context.Context, t *testing.T, blocktime time.Dura // Create mock CLI return full, fullAddr } + +func StartTwoNodesOneMiner(ctx context.Context, t *testing.T, blocktime time.Duration) ([]test.TestNode, []address.Address) { + n, sn := test2.RPCMockSbBuilder(t, test.TwoFull, test.OneMiner) + + fullNode1 := n[0] + fullNode2 := n[1] + miner := sn[0] + + // Get everyone connected + addrs, err := fullNode1.NetAddrsListen(ctx) + if err != nil { + t.Fatal(err) + } + + if err := fullNode2.NetConnect(ctx, addrs); err != nil { + t.Fatal(err) + } + + if err := miner.NetConnect(ctx, addrs); err != nil { + t.Fatal(err) + } + + // Start mining blocks + bm := test.NewBlockMiner(ctx, t, miner, blocktime) + bm.MineBlocks() + + // Send some funds to register the second node + fullNodeAddr2, err := fullNode2.WalletNew(ctx, types.KTSecp256k1) + if err != nil { + t.Fatal(err) + } + + test.SendFunds(ctx, t, fullNode1, fullNodeAddr2, abi.NewTokenAmount(1e18)) + + // Get the first node's address + fullNodeAddr1, err := fullNode1.WalletDefaultAddress(ctx) + if err != nil { + t.Fatal(err) + } + + // Create mock CLI + return n, []address.Address{fullNodeAddr1, fullNodeAddr2} +}