Skip to content

Commit

Permalink
feat: lite-mode - CLI tests for lotus client commands
Browse files Browse the repository at this point in the history
  • Loading branch information
dirkmc committed Oct 21, 2020
1 parent e6ee52f commit ed274cb
Show file tree
Hide file tree
Showing 15 changed files with 375 additions and 156 deletions.
1 change: 1 addition & 0 deletions api/api_gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,6 @@ type GatewayAPI interface {
StateMarketStorageDeal(ctx context.Context, dealId abi.DealID, tsk types.TipSetKey) (*MarketDeal, error)
StateMinerInfo(ctx context.Context, actor address.Address, tsk types.TipSetKey) (miner.MinerInfo, error)
StateNetworkVersion(context.Context, types.TipSetKey) (network.Version, error)
StateVerifiedClientStatus(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*abi.StoragePower, error)
StateWaitMsg(ctx context.Context, msg cid.Cid, confidence uint64) (*MsgLookup, error)
}
5 changes: 5 additions & 0 deletions api/apistruct/struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@ type GatewayStruct struct {
StateMarketBalance func(ctx context.Context, addr address.Address, tsk types.TipSetKey) (api.MarketBalance, error)
StateMarketStorageDeal func(ctx context.Context, dealId abi.DealID, tsk types.TipSetKey) (*api.MarketDeal, error)
StateNetworkVersion func(ctx context.Context, tsk types.TipSetKey) (stnetwork.Version, error)
StateVerifiedClientStatus func(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*abi.StoragePower, error)
StateWaitMsg func(ctx context.Context, msg cid.Cid, confidence uint64) (*api.MsgLookup, error)
}
}
Expand Down Expand Up @@ -1526,6 +1527,10 @@ func (g GatewayStruct) StateNetworkVersion(ctx context.Context, tsk types.TipSet
return g.Internal.StateNetworkVersion(ctx, tsk)
}

func (g GatewayStruct) StateVerifiedClientStatus(ctx context.Context, addr address.Address, tsk types.TipSetKey) (*abi.StoragePower, error) {
return g.Internal.StateVerifiedClientStatus(ctx, addr, tsk)
}

func (g GatewayStruct) StateWaitMsg(ctx context.Context, msg cid.Cid, confidence uint64) (*api.MsgLookup, error) {
return g.Internal.StateWaitMsg(ctx, msg, confidence)
}
Expand Down
38 changes: 23 additions & 15 deletions api/test/deals.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,21 +121,7 @@ func TestDoubleDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration) {
}

func MakeDeal(t *testing.T, ctx context.Context, rseed int, client api.FullNode, miner TestStorageNode, carExport, fastRet bool) {
data := make([]byte, 1600)
rand.New(rand.NewSource(int64(rseed))).Read(data)

dir, err := ioutil.TempDir(os.TempDir(), "test-make-deal-")
if err != nil {
t.Fatal(err)
}

path := filepath.Join(dir, "sourcefile.dat")
err = ioutil.WriteFile(path, data, 0644)
if err != nil {
t.Fatal(err)
}

res, err := client.ClientImport(ctx, api.FileRef{Path: path})
res, data, err := CreateClientFile(ctx, client, rseed)
if err != nil {
t.Fatal(err)
}
Expand All @@ -156,6 +142,28 @@ func MakeDeal(t *testing.T, ctx context.Context, rseed int, client api.FullNode,
testRetrieval(t, ctx, client, fcid, &info.PieceCID, carExport, data)
}

func CreateClientFile(ctx context.Context, client api.FullNode, rseed int) (*api.ImportRes, []byte, error) {
data := make([]byte, 1600)
rand.New(rand.NewSource(int64(rseed))).Read(data)

dir, err := ioutil.TempDir(os.TempDir(), "test-make-deal-")
if err != nil {
return nil, nil, err
}

path := filepath.Join(dir, "sourcefile.dat")
err = ioutil.WriteFile(path, data, 0644)
if err != nil {
return nil, nil, err
}

res, err := client.ClientImport(ctx, api.FileRef{Path: path})
if err != nil {
return nil, nil, err
}
return res, data, nil
}

func TestFastRetrievalDealFlow(t *testing.T, b APIBuilder, blocktime time.Duration) {
_ = os.Setenv("BELLMAN_NO_GPU", "1")

Expand Down
68 changes: 36 additions & 32 deletions cli/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ var clientDealCmd = &cli.Command{
}
defer closer()
ctx := ReqContext(cctx)
afmt := NewAppFmt(cctx.App)

if cctx.NArg() != 4 {
return xerrors.New("expected 4 args: dataCid, miner, price, duration")
Expand Down Expand Up @@ -455,7 +456,7 @@ var clientDealCmd = &cli.Command{
return err
}

fmt.Println(encoder.Encode(*proposal))
afmt.Println(encoder.Encode(*proposal))

return nil
},
Expand All @@ -468,6 +469,7 @@ func interactiveDeal(cctx *cli.Context) error {
}
defer closer()
ctx := ReqContext(cctx)
afmt := NewAppFmt(cctx.App)

state := "import"

Expand Down Expand Up @@ -506,10 +508,10 @@ func interactiveDeal(cctx *cli.Context) error {

switch state {
case "import":
fmt.Print("Data CID (from " + color.YellowString("lotus client import") + "): ")
afmt.Print("Data CID (from " + color.YellowString("lotus client import") + "): ")

var cidStr string
_, err := fmt.Scan(&cidStr)
_, err := afmt.Scan(&cidStr)
if err != nil {
printErr(xerrors.Errorf("reading cid string: %w", err))
continue
Expand All @@ -523,9 +525,9 @@ func interactiveDeal(cctx *cli.Context) error {

state = "duration"
case "duration":
fmt.Print("Deal duration (days): ")
afmt.Print("Deal duration (days): ")

_, err := fmt.Scan(&days)
_, err := afmt.Scan(&days)
if err != nil {
printErr(xerrors.Errorf("parsing duration: %w", err))
continue
Expand All @@ -538,10 +540,10 @@ func interactiveDeal(cctx *cli.Context) error {

state = "miner"
case "miner":
fmt.Print("Miner Address (f0..): ")
afmt.Print("Miner Address (f0..): ")
var maddrStr string

_, err := fmt.Scan(&maddrStr)
_, err := afmt.Scan(&maddrStr)
if err != nil {
printErr(xerrors.Errorf("reading miner address: %w", err))
continue
Expand Down Expand Up @@ -603,10 +605,10 @@ func interactiveDeal(cctx *cli.Context) error {
continue
}

fmt.Print("\nMake this a verified deal? (yes/no): ")
afmt.Print("\nMake this a verified deal? (yes/no): ")

var yn string
_, err = fmt.Scan(&yn)
_, err = afmt.Scan(&yn)
if err != nil {
return err
}
Expand All @@ -617,7 +619,7 @@ func interactiveDeal(cctx *cli.Context) error {
case "no":
verified = false
default:
fmt.Println("Type in full 'yes' or 'no'")
afmt.Println("Type in full 'yes' or 'no'")
continue
}

Expand Down Expand Up @@ -650,21 +652,21 @@ func interactiveDeal(cctx *cli.Context) error {
epochPrice = types.BigDiv(types.BigMul(pricePerGib, types.NewInt(uint64(ds.PieceSize))), gib)
totalPrice := types.BigMul(epochPrice, types.NewInt(uint64(epochs)))

fmt.Printf("-----\n")
fmt.Printf("Proposing from %s\n", a)
fmt.Printf("\tBalance: %s\n", types.FIL(fromBal))
fmt.Printf("\n")
fmt.Printf("Piece size: %s (Payload size: %s)\n", units.BytesSize(float64(ds.PieceSize)), units.BytesSize(float64(ds.PayloadSize)))
fmt.Printf("Duration: %s\n", dur)
fmt.Printf("Total price: ~%s (%s per epoch)\n", types.FIL(totalPrice), types.FIL(epochPrice))
fmt.Printf("Verified: %v\n", verified)
afmt.Printf("-----\n")
afmt.Printf("Proposing from %s\n", a)
afmt.Printf("\tBalance: %s\n", types.FIL(fromBal))
afmt.Printf("\n")
afmt.Printf("Piece size: %s (Payload size: %s)\n", units.BytesSize(float64(ds.PieceSize)), units.BytesSize(float64(ds.PayloadSize)))
afmt.Printf("Duration: %s\n", dur)
afmt.Printf("Total price: ~%s (%s per epoch)\n", types.FIL(totalPrice), types.FIL(epochPrice))
afmt.Printf("Verified: %v\n", verified)

state = "accept"
case "accept":
fmt.Print("\nAccept (yes/no): ")
afmt.Print("\nAccept (yes/no): ")

var yn string
_, err := fmt.Scan(&yn)
_, err := afmt.Scan(&yn)
if err != nil {
return err
}
Expand All @@ -674,7 +676,7 @@ func interactiveDeal(cctx *cli.Context) error {
}

if yn != "yes" {
fmt.Println("Type in full 'yes' or 'no'")
afmt.Println("Type in full 'yes' or 'no'")
continue
}

Expand Down Expand Up @@ -703,7 +705,7 @@ func interactiveDeal(cctx *cli.Context) error {
return err
}

fmt.Println("\nDeal CID:", color.GreenString(encoder.Encode(*proposal)))
afmt.Println("\nDeal CID:", color.GreenString(encoder.Encode(*proposal)))
return nil
default:
return xerrors.Errorf("unknown state: %s", state)
Expand Down Expand Up @@ -815,6 +817,7 @@ var clientRetrieveCmd = &cli.Command{
}
defer closer()
ctx := ReqContext(cctx)
afmt := NewAppFmt(cctx.App)

var payer address.Address
if cctx.String("from") != "" {
Expand Down Expand Up @@ -923,14 +926,14 @@ var clientRetrieveCmd = &cli.Command{
select {
case evt, ok := <-updates:
if ok {
fmt.Printf("> Recv: %s, Paid %s, %s (%s)\n",
afmt.Printf("> Recv: %s, Paid %s, %s (%s)\n",
types.SizeStr(types.NewInt(evt.BytesReceived)),
types.FIL(evt.FundsSpent),
retrievalmarket.ClientEvents[evt.Event],
retrievalmarket.DealStatuses[evt.Status],
)
} else {
fmt.Println("Success")
afmt.Println("Success")
return nil
}

Expand Down Expand Up @@ -963,8 +966,9 @@ var clientQueryAskCmd = &cli.Command{
},
},
Action: func(cctx *cli.Context) error {
afmt := NewAppFmt(cctx.App)
if cctx.NArg() != 1 {
fmt.Println("Usage: query-ask [minerAddress]")
afmt.Println("Usage: query-ask [minerAddress]")
return nil
}

Expand Down Expand Up @@ -1005,23 +1009,23 @@ var clientQueryAskCmd = &cli.Command{
return err
}

fmt.Printf("Ask: %s\n", maddr)
fmt.Printf("Price per GiB: %s\n", types.FIL(ask.Price))
fmt.Printf("Verified Price per GiB: %s\n", types.FIL(ask.VerifiedPrice))
fmt.Printf("Max Piece size: %s\n", types.SizeStr(types.NewInt(uint64(ask.MaxPieceSize))))
afmt.Printf("Ask: %s\n", maddr)
afmt.Printf("Price per GiB: %s\n", types.FIL(ask.Price))
afmt.Printf("Verified Price per GiB: %s\n", types.FIL(ask.VerifiedPrice))
afmt.Printf("Max Piece size: %s\n", types.SizeStr(types.NewInt(uint64(ask.MaxPieceSize))))

size := cctx.Int64("size")
if size == 0 {
return nil
}
perEpoch := types.BigDiv(types.BigMul(ask.Price, types.NewInt(uint64(size))), types.NewInt(1<<30))
fmt.Printf("Price per Block: %s\n", types.FIL(perEpoch))
afmt.Printf("Price per Block: %s\n", types.FIL(perEpoch))

duration := cctx.Int64("duration")
if duration == 0 {
return nil
}
fmt.Printf("Total Price: %s\n", types.FIL(types.BigMul(perEpoch, types.NewInt(uint64(duration)))))
afmt.Printf("Total Price: %s\n", types.FIL(types.BigMul(perEpoch, types.NewInt(uint64(duration)))))

return nil
},
Expand Down Expand Up @@ -1104,7 +1108,7 @@ var clientListDeals = &cli.Command{
}
}

return outputStorageDeals(ctx, os.Stdout, api, localDeals, cctx.Bool("verbose"), cctx.Bool("color"), showFailed)
return outputStorageDeals(ctx, cctx.App.Writer, api, localDeals, cctx.Bool("verbose"), cctx.Bool("color"), showFailed)
},
}

Expand Down
22 changes: 22 additions & 0 deletions cli/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package cli

import (
"context"
"os"
"testing"
"time"

clitest "github.com/filecoin-project/lotus/cli/test"
)

// TestClient does a basic test to exercise the client CLI
// commands
func TestClient(t *testing.T) {
_ = os.Setenv("BELLMAN_NO_GPU", "1")
clitest.QuietMiningLogs()

blocktime := 5 * time.Millisecond
ctx := context.Background()
clientNode, _ := clitest.StartOneNodeOneMiner(ctx, t, blocktime)
clitest.RunClientTest(t, Commands, clientNode)
}
43 changes: 38 additions & 5 deletions cli/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ package cli

import (
"fmt"
"io"
"os"

"github.com/urfave/cli/v2"
ufcli "github.com/urfave/cli/v2"
"golang.org/x/xerrors"
)

type PrintHelpErr struct {
Err error
Ctx *cli.Context
Ctx *ufcli.Context
}

func (e *PrintHelpErr) Error() string {
Expand All @@ -26,11 +27,11 @@ func (e *PrintHelpErr) Is(o error) bool {
return ok
}

func ShowHelp(cctx *cli.Context, err error) error {
func ShowHelp(cctx *ufcli.Context, err error) error {
return &PrintHelpErr{Err: err, Ctx: cctx}
}

func RunApp(app *cli.App) {
func RunApp(app *ufcli.App) {
if err := app.Run(os.Args); err != nil {
if os.Getenv("LOTUS_DEV") != "" {
log.Warnf("%+v", err)
Expand All @@ -39,8 +40,40 @@ func RunApp(app *cli.App) {
}
var phe *PrintHelpErr
if xerrors.As(err, &phe) {
_ = cli.ShowCommandHelp(phe.Ctx, phe.Ctx.Command.Name)
_ = ufcli.ShowCommandHelp(phe.Ctx, phe.Ctx.Command.Name)
}
os.Exit(1)
}
}

type AppFmt struct {
app *ufcli.App
stdin io.Reader
}

func NewAppFmt(a *ufcli.App) *AppFmt {
var stdin io.Reader
istdin, ok := a.Metadata["stdin"]
if ok {
stdin = istdin.(io.Reader)
} else {
stdin = os.Stdin
}
return &AppFmt{app: a, stdin: stdin}
}

func (a *AppFmt) Print(args ...interface{}) {
fmt.Fprint(a.app.Writer, args...)
}

func (a *AppFmt) Println(args ...interface{}) {
fmt.Fprintln(a.app.Writer, args...)
}

func (a *AppFmt) Printf(fmtstr string, args ...interface{}) {
fmt.Fprintf(a.app.Writer, fmtstr, args...)
}

func (a *AppFmt) Scan(args ...interface{}) (int, error) {
return fmt.Fscan(a.stdin, args...)
}
Loading

0 comments on commit ed274cb

Please sign in to comment.