Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: cli: unit tests for sync related commands #8080

Merged
merged 10 commits into from
Feb 27, 2022
28 changes: 16 additions & 12 deletions cli/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ var SyncStatusCmd = &cli.Command{
Name: "status",
Usage: "check sync status",
Action: func(cctx *cli.Context) error {
afmt := NewAppFmt(cctx.App)

apic, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
Expand All @@ -45,9 +47,9 @@ var SyncStatusCmd = &cli.Command{
return err
}

fmt.Println("sync status:")
afmt.Println("sync status:")
for _, ss := range state.ActiveSyncs {
fmt.Printf("worker %d:\n", ss.WorkerID)
afmt.Printf("worker %d:\n", ss.WorkerID)
var base, target []cid.Cid
var heightDiff int64
var theight abi.ChainEpoch
Expand All @@ -62,20 +64,20 @@ var SyncStatusCmd = &cli.Command{
} else {
heightDiff = 0
}
fmt.Printf("\tBase:\t%s\n", base)
fmt.Printf("\tTarget:\t%s (%d)\n", target, theight)
fmt.Printf("\tHeight diff:\t%d\n", heightDiff)
fmt.Printf("\tStage: %s\n", ss.Stage)
fmt.Printf("\tHeight: %d\n", ss.Height)
afmt.Printf("\tBase:\t%s\n", base)
afmt.Printf("\tTarget:\t%s (%d)\n", target, theight)
afmt.Printf("\tHeight diff:\t%d\n", heightDiff)
afmt.Printf("\tStage: %s\n", ss.Stage)
afmt.Printf("\tHeight: %d\n", ss.Height)
if ss.End.IsZero() {
if !ss.Start.IsZero() {
fmt.Printf("\tElapsed: %s\n", time.Since(ss.Start))
afmt.Printf("\tElapsed: %s\n", time.Since(ss.Start))
}
} else {
fmt.Printf("\tElapsed: %s\n", ss.End.Sub(ss.Start))
afmt.Printf("\tElapsed: %s\n", ss.End.Sub(ss.Start))
}
if ss.Stage == api.StageSyncErrored {
fmt.Printf("\tError: %s\n", ss.Message)
afmt.Printf("\tError: %s\n", ss.Message)
}
}
return nil
Expand Down Expand Up @@ -168,6 +170,8 @@ var SyncCheckBadCmd = &cli.Command{
Usage: "check if the given block was marked bad, and for what reason",
ArgsUsage: "[blockCid]",
Action: func(cctx *cli.Context) error {
afmt := NewAppFmt(cctx.App)

napi, closer, err := GetFullNodeAPI(cctx)
if err != nil {
return err
Expand All @@ -190,11 +194,11 @@ var SyncCheckBadCmd = &cli.Command{
}

if reason == "" {
fmt.Println("block was not marked as bad")
afmt.Println("block was not marked as bad")
return nil
}

fmt.Println(reason)
afmt.Println(reason)
return nil
},
}
Expand Down
189 changes: 189 additions & 0 deletions cli/sync_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
package cli

import (
"context"
"fmt"
"testing"
"time"

"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/types/mock"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
)

func TestSyncStatus(t *testing.T) {
app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("sync", SyncStatusCmd))
defer done()

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

ts1 := mock.TipSet(mock.MkBlock(nil, 0, 0))
ts2 := mock.TipSet(mock.MkBlock(ts1, 0, 0))

start := time.Now()
end := start.Add(time.Minute)

state := &api.SyncState{
ActiveSyncs: []api.ActiveSync{{
WorkerID: 1,
Base: ts1,
Target: ts2,
Stage: api.StageMessages,
Height: abi.ChainEpoch(0),
Start: start,
End: end,
Message: "whatever",
}},
VMApplied: 0,
}

mockApi.EXPECT().SyncState(ctx).Return(state, nil)

//stm: @CLI_SYNC_STATUS_001
err := app.Run([]string{"sync", "status"})
assert.NoError(t, err)

out := buf.String()

// output is plaintext, had to do string matching
assert.Contains(t, out, fmt.Sprintf("Base:\t[%s]", ts1.Blocks()[0].Cid().String()))
assert.Contains(t, out, fmt.Sprintf("Target:\t[%s]", ts2.Blocks()[0].Cid().String()))
assert.Contains(t, out, "Height diff:\t1")
assert.Contains(t, out, "Stage: message sync")
assert.Contains(t, out, "Height: 0")
assert.Contains(t, out, "Elapsed: 1m0s")
}

func TestSyncMarkBad(t *testing.T) {
app, mockApi, _, done := NewMockAppWithFullAPI(t, WithCategory("sync", SyncMarkBadCmd))
defer done()

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

blk := mock.MkBlock(nil, 0, 0)

mockApi.EXPECT().SyncMarkBad(ctx, blk.Cid()).Return(nil)

//stm: @CLI_SYNC_MARK_BAD_001
err := app.Run([]string{"sync", "mark-bad", blk.Cid().String()})
assert.NoError(t, err)
}

func TestSyncUnmarkBad(t *testing.T) {
t.Run("one-block", func(t *testing.T) {
app, mockApi, _, done := NewMockAppWithFullAPI(t, WithCategory("sync", SyncUnmarkBadCmd))
defer done()

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

blk := mock.MkBlock(nil, 0, 0)

mockApi.EXPECT().SyncUnmarkBad(ctx, blk.Cid()).Return(nil)

//stm: @CLI_SYNC_UNMARK_BAD_001
err := app.Run([]string{"sync", "unmark-bad", blk.Cid().String()})
assert.NoError(t, err)
})

t.Run("all", func(t *testing.T) {
app, mockApi, _, done := NewMockAppWithFullAPI(t, WithCategory("sync", SyncUnmarkBadCmd))
defer done()

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

mockApi.EXPECT().SyncUnmarkAllBad(ctx).Return(nil)

//stm: @CLI_SYNC_UNMARK_BAD_002
err := app.Run([]string{"sync", "unmark-bad", "-all"})
assert.NoError(t, err)
})
}

func TestSyncCheckBad(t *testing.T) {
t.Run("not-bad", func(t *testing.T) {
app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("sync", SyncCheckBadCmd))
defer done()

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

blk := mock.MkBlock(nil, 0, 0)

mockApi.EXPECT().SyncCheckBad(ctx, blk.Cid()).Return("", nil)

//stm: @CLI_SYNC_CHECK_BAD_002
err := app.Run([]string{"sync", "check-bad", blk.Cid().String()})
assert.NoError(t, err)

assert.Contains(t, buf.String(), "block was not marked as bad")
})

t.Run("bad", func(t *testing.T) {
app, mockApi, buf, done := NewMockAppWithFullAPI(t, WithCategory("sync", SyncCheckBadCmd))
defer done()

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

blk := mock.MkBlock(nil, 0, 0)
reason := "whatever"

mockApi.EXPECT().SyncCheckBad(ctx, blk.Cid()).Return(reason, nil)

//stm: @CLI_SYNC_CHECK_BAD_001
err := app.Run([]string{"sync", "check-bad", blk.Cid().String()})
assert.NoError(t, err)

assert.Contains(t, buf.String(), reason)
})
}

func TestSyncCheckpoint(t *testing.T) {
t.Run("tipset", func(t *testing.T) {
app, mockApi, _, done := NewMockAppWithFullAPI(t, WithCategory("sync", SyncCheckpointCmd))
defer done()

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

blk := mock.MkBlock(nil, 0, 0)
ts := mock.TipSet(blk)

gomock.InOrder(
mockApi.EXPECT().ChainGetBlock(ctx, blk.Cid()).Return(blk, nil),
mockApi.EXPECT().SyncCheckpoint(ctx, ts.Key()).Return(nil),
)

//stm: @CLI_SYNC_CHECKPOINT_001
err := app.Run([]string{"sync", "checkpoint", blk.Cid().String()})
assert.NoError(t, err)
})

t.Run("epoch", func(t *testing.T) {
app, mockApi, _, done := NewMockAppWithFullAPI(t, WithCategory("sync", SyncCheckpointCmd))
defer done()

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

epoch := abi.ChainEpoch(0)
blk := mock.MkBlock(nil, 0, 0)
ts := mock.TipSet(blk)

gomock.InOrder(
mockApi.EXPECT().ChainGetTipSetByHeight(ctx, epoch, types.EmptyTSK).Return(ts, nil),
mockApi.EXPECT().SyncCheckpoint(ctx, ts.Key()).Return(nil),
)

//stm: @CLI_SYNC_CHECKPOINT_002
err := app.Run([]string{"sync", "checkpoint", fmt.Sprintf("-epoch=%d", epoch)})
assert.NoError(t, err)
})
}