package kit import ( "context" "fmt" "math/rand" "os" "path/filepath" "regexp" "strings" "testing" "time" "github.com/stretchr/testify/require" lcli "github.com/urfave/cli/v2" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/specs-actors/v2/actors/builtin" ) // RunClientTest exercises some of the Client CLI commands func RunClientTest(t *testing.T, cmds []*lcli.Command, clientNode *TestFullNode) { ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() // Create mock CLI mockCLI := NewMockCLI(ctx, t, cmds, api.NodeFull) clientCLI := mockCLI.Client(clientNode.ListenAddr) // Get the Miner address addrs, err := clientNode.StateListMiners(ctx, types.EmptyTSK) require.NoError(t, err) require.Len(t, addrs, 1) minerAddr := addrs[0] fmt.Println("Miner:", minerAddr) // client query-ask out := clientCLI.RunCmd("client", "query-ask", minerAddr.String()) require.Regexp(t, regexp.MustCompile("Ask:"), out) // Create a deal (non-interactive) // client deal --start-epoch= 1000000attofil res, _, _, err := CreateImportFile(ctx, clientNode, 1, 0) require.NoError(t, err) startEpoch := fmt.Sprintf("--start-epoch=%d", 2<<12) dataCid := res.Root price := "1000000attofil" duration := fmt.Sprintf("%d", build.MinDealDuration) out = clientCLI.RunCmd("client", "deal", startEpoch, dataCid.String(), minerAddr.String(), price, duration) fmt.Println("client deal", out) // Create a deal (interactive) // client deal // // (in days) // // "no" (verified Client) // "yes" (confirm deal) res, _, _, err = CreateImportFile(ctx, clientNode, 2, 0) require.NoError(t, err) dataCid2 := res.Root duration = fmt.Sprintf("%d", build.MinDealDuration/builtin.EpochsInDay) cmd := []string{"client", "deal"} interactiveCmds := []string{ dataCid2.String(), duration, minerAddr.String(), "no", "yes", } out = clientCLI.RunInteractiveCmd(cmd, interactiveCmds) fmt.Println("client deal:\n", out) // Wait for provider to start sealing deal dealStatus := "" for { // client list-deals out = clientCLI.RunCmd("client", "list-deals", "--show-failed") fmt.Println("list-deals:\n", out) lines := strings.Split(out, "\n") require.GreaterOrEqual(t, len(lines), 2) re := regexp.MustCompile(`\s+`) parts := re.Split(lines[1], -1) if len(parts) < 4 { require.Fail(t, "bad list-deals output format") } dealStatus = parts[3] fmt.Println(" Deal status:", dealStatus) st := CategorizeDealState(dealStatus) require.NotEqual(t, TestDealStateFailed, st) if st == TestDealStateComplete { break } time.Sleep(time.Second) } // client retrieval-ask --size=1 out = clientCLI.RunCmd("client", "retrieval-ask", "--size=1", minerAddr.String(), dataCid.String()) require.Regexp(t, regexp.MustCompile("Ask:"), out) fmt.Println("retrieval ask:\n", out) // Retrieve the first file from the Miner // client retrieve tmpdir, err := os.MkdirTemp(os.TempDir(), "test-cli-client") require.NoError(t, err) path := filepath.Join(tmpdir, "outfile.dat") // Wait for client retrieve to succeed. for { out = clientCLI.RunCmd("client", "retrieve", dataCid.String(), path) fmt.Println("retrieve:\n", out) if strings.Contains(out, "Success") { break } } } func CreateImportFile(ctx context.Context, client api.FullNode, rseed int, size int) (res *api.ImportRes, path string, data []byte, err error) { data, path, err = createRandomFile(rseed, size) 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, path, data, nil } func createRandomFile(rseed, size int) ([]byte, string, error) { if size == 0 { size = 1600 } data := make([]byte, size) rand.New(rand.NewSource(int64(rseed))).Read(data) dir, err := os.MkdirTemp(os.TempDir(), "test-make-deal-") if err != nil { return nil, "", err } path := filepath.Join(dir, "sourcefile.dat") err = os.WriteFile(path, data, 0644) if err != nil { return nil, "", err } return data, path, nil }