Skip to content

Commit

Permalink
admin_db_clean_cmd test Part II (#6437)
Browse files Browse the repository at this point in the history
* Admin_db_clean_cmd test

* fmt

* delete redundant test file after run

* Add more tests for admin_db_clean_cmds

* add test for success case

* resolve conflict

* add more tests inside of fixExecution

* fix naming

* remove nil check before manager close; add error check in context set flags

* add test for opt functions; rename database mock

* add database test file

* fmt
  • Loading branch information
bowenxia authored Oct 30, 2024
1 parent 6716380 commit 21b40bd
Show file tree
Hide file tree
Showing 12 changed files with 686 additions and 169 deletions.
2 changes: 1 addition & 1 deletion tools/cli/admin_commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ func AdminDeleteWorkflow(c *cli.Context) error {
if err != nil {
return commoncli.Problem("Error in Admin delete WF: ", err)
}
exeStore, err := getDeps(c).initializeExecutionStore(c, shardIDInt)
exeStore, err := getDeps(c).initializeExecutionManager(c, shardIDInt)
if err != nil {
return commoncli.Problem("Error in Admin delete WF: ", err)
}
Expand Down
5 changes: 4 additions & 1 deletion tools/cli/admin_commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ type cliTestData struct {
mockAdminClient *admin.MockClient
ioHandler *testIOHandler
app *cli.App
mockManagerFactory *MockManagerFactory
}

func newCLITestData(t *testing.T) *cliTestData {
Expand All @@ -62,15 +63,17 @@ func newCLITestData(t *testing.T) *cliTestData {

td.mockFrontendClient = frontend.NewMockClient(ctrl)
td.mockAdminClient = admin.NewMockClient(ctrl)
td.mockManagerFactory = NewMockManagerFactory(ctrl)
td.ioHandler = &testIOHandler{}

// make a new CLI App with client factory returning our frontend and admin clients
// Create a new CLI app with client factory and persistence manager factory
td.app = NewCliApp(
&clientFactoryMock{
serverFrontendClient: td.mockFrontendClient,
serverAdminClient: td.mockAdminClient,
},
WithIOHandler(td.ioHandler),
WithManagerFactory(td.mockManagerFactory), // Inject the mocked persistence manager factory
)
return &td
}
Expand Down
24 changes: 16 additions & 8 deletions tools/cli/admin_db_clean_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ func AdminDBClean(c *cli.Context) error {
continue
}

fmt.Println(string(data))
output := getDeps(c).Output()
output.Write([]byte(string(data) + "\n"))
}
return nil
}
Expand All @@ -131,16 +132,18 @@ func fixExecution(
invariants []executions.InvariantFactory,
execution *store.ScanOutputEntity,
) (invariant.ManagerFixResult, error) {
execManager, err := getDeps(c).initializeExecutionStore(c, execution.Execution.(entity.Entity).GetShardID())
defer execManager.Close()
execManager, err := getDeps(c).initializeExecutionManager(c, execution.Execution.(entity.Entity).GetShardID())
if err != nil {
return invariant.ManagerFixResult{}, fmt.Errorf("Error in fix execution: %w", err)
return invariant.ManagerFixResult{}, err
}
defer execManager.Close()

historyV2Mgr, err := getDeps(c).initializeHistoryManager(c)
defer historyV2Mgr.Close()
if err != nil {
return invariant.ManagerFixResult{}, fmt.Errorf("Error in fix execution: %w", err)
return invariant.ManagerFixResult{}, err
}
defer historyV2Mgr.Close()

pr := persistence.NewPersistenceRetryer(
execManager,
historyV2Mgr,
Expand All @@ -156,7 +159,12 @@ func fixExecution(
ctx, cancel, err := newContext(c)
defer cancel()
if err != nil {
return invariant.ManagerFixResult{}, fmt.Errorf("Context not created: %w", err)
return invariant.ManagerFixResult{}, err
}
invariantManager, err := getDeps(c).initializeInvariantManager(ivs)
if err != nil {
return invariant.ManagerFixResult{}, err
}
return invariant.NewInvariantManager(ivs).RunFixes(ctx, execution.Execution), nil

return invariantManager.RunFixes(ctx, execution.Execution.(entity.Entity)), nil
}
194 changes: 186 additions & 8 deletions tools/cli/admin_db_clean_command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,47 @@ package cli

import (
"flag"
"fmt"
"os"
"testing"

"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"

"github.com/uber/cadence/common/persistence"
"github.com/uber/cadence/common/reconciliation/invariant"
)

func TestAdminDBClean_errorCases(t *testing.T) {
func TestAdminDBClean_noFixExecution(t *testing.T) {
tests := []struct {
name string
setupContext func(app *cli.App) *cli.Context
inputFileData string // Simulate the content of the input file
expectedOutput string
expectedError string
}{
{
name: "SuccessCase_emptyResult",
setupContext: func(app *cli.App) *cli.Context {
set := flag.NewFlagSet("test", 0)
// Define flags
set.String(FlagScanType, "", "scan type flag")
set.String(FlagInputFile, "", "Input file flag")
// Use a StringSliceFlag to simulate multiple collection values
set.Var(cli.NewStringSlice("CollectionHistory", "CollectionDomain"), FlagInvariantCollection, "invariant collection flag")

// Set actual values for the flags
require.NoError(t, set.Set(FlagScanType, "ConcreteExecutionType"))
require.NoError(t, set.Set(FlagInputFile, "input.json"))

return cli.NewContext(app, set, nil)
},
inputFileData: `[{"Execution": {"ShardID": 1}}]`, // Simulate the content of input file
expectedOutput: ``,
expectedError: "",
},
{
name: "MissingRequiredFlagScanType",
setupContext: func(app *cli.App) *cli.Context {
Expand All @@ -58,8 +84,8 @@ func TestAdminDBClean_errorCases(t *testing.T) {
set := flag.NewFlagSet("test", 0)
set.String(FlagScanType, "", "scan type flag")
set.String(FlagInputFile, "", "Input file flag")
_ = set.Set(FlagScanType, "unknown")
_ = set.Set(FlagInputFile, "input.json")
require.NoError(t, set.Set(FlagScanType, "unknown"))
require.NoError(t, set.Set(FlagInputFile, "input.json"))
return cli.NewContext(app, set, nil)
},
inputFileData: ``,
Expand All @@ -78,8 +104,9 @@ func TestAdminDBClean_errorCases(t *testing.T) {
set.Var(cli.NewStringSlice("invalid_collection", "history"), FlagInvariantCollection, "invariant collection flag")

// Set actual values for the flags
_ = set.Set(FlagScanType, "ConcreteExecutionType")
_ = set.Set(FlagInputFile, "input.json")
require.NoError(t, set.Set(FlagScanType, "ConcreteExecutionType"))
require.NoError(t, set.Set(FlagInputFile, "input.json"))

return cli.NewContext(app, set, nil)
},
inputFileData: ``,
Expand All @@ -93,9 +120,11 @@ func TestAdminDBClean_errorCases(t *testing.T) {
set.String(FlagScanType, "", "scan type flag")
set.String(FlagInvariantCollection, "", "invariant collection flag")
set.String(FlagInputFile, "", "Input file flag")
_ = set.Set(FlagScanType, "ConcreteExecutionType")
_ = set.Set(FlagInvariantCollection, "invalid_collection") // Collection will trigger no invariants
_ = set.Set(FlagInputFile, "input.json")

require.NoError(t, set.Set(FlagScanType, "ConcreteExecutionType"))
require.NoError(t, set.Set(FlagInputFile, "input.json"))
require.NoError(t, set.Set(FlagInvariantCollection, "invalid_collection"))

return cli.NewContext(app, set, nil)
},
inputFileData: `[{"Execution": {"ShardID": 1}}]`,
Expand Down Expand Up @@ -144,3 +173,152 @@ func TestAdminDBClean_errorCases(t *testing.T) {
})
}
}

func TestAdminDBClean_inFixExecution(t *testing.T) {
tests := []struct {
name string
contextSetup func(td *cliTestData, inputFilePath string) *cli.Context
mockSetup func(td *cliTestData)
inputFileData string
expectedError string
expectedOutput string
}{
{
name: "Success",
contextSetup: func(td *cliTestData, inputFilePath string) *cli.Context {
set := flag.NewFlagSet("test", 0)
set.String(FlagScanType, "", "scan type flag")
set.String(FlagInputFile, "", "Input file flag")
set.Var(cli.NewStringSlice("CollectionHistory", "CollectionDomain"), FlagInvariantCollection, "invariant collection flag")

require.NoError(t, set.Set(FlagScanType, "ConcreteExecutionType"))
require.NoError(t, set.Set(FlagInputFile, inputFilePath))

return cli.NewContext(td.app, set, nil)
},
mockSetup: func(td *cliTestData) {
ctrl := gomock.NewController(t)
mockExecManager := persistence.NewMockExecutionManager(ctrl)
mockHistoryManager := persistence.NewMockHistoryManager(ctrl)
mockInvariantManager := invariant.NewMockManager(ctrl)

mockExecManager.EXPECT().Close().Times(1)
mockHistoryManager.EXPECT().Close().Times(1)

mockInvariantManager.EXPECT().RunFixes(gomock.Any(), gomock.Any()).Return(invariant.ManagerFixResult{}).Times(1)

td.mockManagerFactory.EXPECT().initializeExecutionManager(gomock.Any(), gomock.Any()).Return(mockExecManager, nil).Times(1)
td.mockManagerFactory.EXPECT().initializeHistoryManager(gomock.Any()).Return(mockHistoryManager, nil).Times(1)
td.mockManagerFactory.EXPECT().initializeInvariantManager(gomock.Any()).Return(mockInvariantManager, nil).Times(1)

},
inputFileData: `{"Execution": {"ShardID": 1}, "Result": {}}`,
expectedOutput: `{"Execution":{"BranchToken":null,"TreeID":"","BranchID":"","ShardID":1,"DomainID":"","WorkflowID":"","RunID":"","State":0},"Input":{"Execution":{"BranchToken":null,"TreeID":"","BranchID":"","ShardID":1,"DomainID":"","WorkflowID":"","RunID":"","State":0},"Result":{"CheckResultType":"","DeterminingInvariantType":null,"CheckResults":null}},"Result":{"FixResultType":"","DeterminingInvariantName":null,"FixResults":null}}
`,
expectedError: "",
},
{
name: "init invariant manager error",
contextSetup: func(td *cliTestData, inputFilePath string) *cli.Context {
set := flag.NewFlagSet("test", 0)
set.String(FlagScanType, "", "scan type flag")
set.String(FlagInputFile, "", "Input file flag")
set.Var(cli.NewStringSlice("CollectionHistory", "CollectionDomain"), FlagInvariantCollection, "invariant collection flag")

require.NoError(t, set.Set(FlagScanType, "ConcreteExecutionType"))
require.NoError(t, set.Set(FlagInputFile, inputFilePath))

return cli.NewContext(td.app, set, nil)
},
mockSetup: func(td *cliTestData) {
ctrl := gomock.NewController(t)
mockExecManager := persistence.NewMockExecutionManager(ctrl)
mockHistoryManager := persistence.NewMockHistoryManager(ctrl)

mockExecManager.EXPECT().Close().Times(1)
mockHistoryManager.EXPECT().Close().Times(1)

td.mockManagerFactory.EXPECT().initializeExecutionManager(gomock.Any(), gomock.Any()).Return(mockExecManager, nil).Times(1)
td.mockManagerFactory.EXPECT().initializeHistoryManager(gomock.Any()).Return(mockHistoryManager, nil).Times(1)
td.mockManagerFactory.EXPECT().initializeInvariantManager(gomock.Any()).Return(nil, fmt.Errorf("init invariant manager error")).Times(1)

},
inputFileData: `{"Execution": {"ShardID": 1}, "Result": {}}`,
expectedOutput: ``,
expectedError: "Error in fix execution: : init invariant manager error",
},
{
name: "init execution manager error",
contextSetup: func(td *cliTestData, inputFilePath string) *cli.Context {
set := flag.NewFlagSet("test", 0)
set.String(FlagScanType, "", "scan type flag")
set.String(FlagInputFile, "", "Input file flag")
set.Var(cli.NewStringSlice("CollectionHistory", "CollectionDomain"), FlagInvariantCollection, "invariant collection flag")

require.NoError(t, set.Set(FlagScanType, "ConcreteExecutionType"))
require.NoError(t, set.Set(FlagInputFile, inputFilePath))

return cli.NewContext(td.app, set, nil)
},
mockSetup: func(td *cliTestData) {
td.mockManagerFactory.EXPECT().initializeExecutionManager(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("init execution manager error")).Times(1)
},
inputFileData: `{"Execution": {"ShardID": 1}, "Result": {}}`,
expectedOutput: ``,
expectedError: "Error in fix execution: : init execution manager error",
},
{
name: "init history manager error",
contextSetup: func(td *cliTestData, inputFilePath string) *cli.Context {
set := flag.NewFlagSet("test", 0)
set.String(FlagScanType, "", "scan type flag")
set.String(FlagInputFile, "", "Input file flag")
set.Var(cli.NewStringSlice("CollectionHistory", "CollectionDomain"), FlagInvariantCollection, "invariant collection flag")

require.NoError(t, set.Set(FlagScanType, "ConcreteExecutionType"))
require.NoError(t, set.Set(FlagInputFile, inputFilePath))

return cli.NewContext(td.app, set, nil)
},
mockSetup: func(td *cliTestData) {
ctrl := gomock.NewController(t)
mockExecManager := persistence.NewMockExecutionManager(ctrl)
mockExecManager.EXPECT().Close().Times(1)

td.mockManagerFactory.EXPECT().initializeExecutionManager(gomock.Any(), gomock.Any()).Return(mockExecManager, nil).Times(1)
td.mockManagerFactory.EXPECT().initializeHistoryManager(gomock.Any()).Return(nil, fmt.Errorf("init history manager error")).Times(1)
},
inputFileData: `{"Execution": {"ShardID": 1}, "Result": {}}`,
expectedOutput: ``,
expectedError: "Error in fix execution: : init history manager error",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
td := newCLITestData(t)
tt.mockSetup(td)

inputFile, err := os.CreateTemp("", "test_input_*.json")
assert.NoError(t, err)
defer os.Remove(inputFile.Name())

if tt.inputFileData != "" {
_, err = inputFile.WriteString(tt.inputFileData)
assert.NoError(t, err)
}
inputFile.Close()

c := tt.contextSetup(td, inputFile.Name())

err = AdminDBClean(c)
if tt.expectedError != "" {
assert.ErrorContains(t, err, tt.expectedError)
} else {
assert.NoError(t, err)
}

assert.Equal(t, tt.expectedOutput, td.consoleOutput())
})
}
}
4 changes: 2 additions & 2 deletions tools/cli/admin_db_scan_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ func checkExecution(
invariants []executions.InvariantFactory,
fetcher executions.ExecutionFetcher,
) (interface{}, invariant.ManagerCheckResult, error) {
execManager, err := getDeps(c).initializeExecutionStore(c, common.WorkflowIDToHistoryShard(req.WorkflowID, numberOfShards))
execManager, err := getDeps(c).initializeExecutionManager(c, common.WorkflowIDToHistoryShard(req.WorkflowID, numberOfShards))
defer execManager.Close()
if err != nil {
return nil, invariant.ManagerCheckResult{}, fmt.Errorf("Error in execution check: %w", err)
Expand Down Expand Up @@ -200,7 +200,7 @@ func listExecutionsByShardID(
outputFile *os.File,
) error {

client, err := getDeps(c).initializeExecutionStore(c, shardID)
client, err := getDeps(c).initializeExecutionManager(c, shardID)
defer client.Close()
if err != nil {
commoncli.Problem("Error in Admin DB unsupported WF scan: ", err)
Expand Down
2 changes: 1 addition & 1 deletion tools/cli/admin_timers.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func NewDBLoadCloser(c *cli.Context) (LoadCloser, error) {
return nil, fmt.Errorf("error in NewDBLoadCloser: failed to get shard ID: %w", err)
}

executionManager, err := getDeps(c).initializeExecutionStore(c, shardID)
executionManager, err := getDeps(c).initializeExecutionManager(c, shardID)
if err != nil {
return nil, fmt.Errorf("error in NewDBLoadCloser: failed to initialize execution store: %w", err)
}
Expand Down
22 changes: 19 additions & 3 deletions tools/cli/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,22 @@ func WithIOHandler(h IOHandler) CLIAppOptions {
}
}

// WithManagerFactory sets the ManagerFactory for the CLI app.
func WithManagerFactory(factory ManagerFactory) CLIAppOptions {
return func(app *cli.App) {
if app.Metadata == nil {
return
}

d, ok := app.Metadata[depsKey].(*deps)
if !ok {
return
}

d.ManagerFactory = factory
}
}

// NewCliApp instantiates a new instance of the CLI application
func NewCliApp(cf ClientFactory, opts ...CLIAppOptions) *cli.App {
version := fmt.Sprintf("CLI feature version: %v \n"+
Expand All @@ -63,7 +79,7 @@ func NewCliApp(cf ClientFactory, opts ...CLIAppOptions) *cli.App {
app.Usage = "A command-line tool for cadence users"
app.Version = version
app.Metadata = map[string]any{
depsKey: &deps{ClientFactory: cf, IOHandler: &defaultIOHandler{app: app}, PersistenceManagerFactory: &defaultPersistenceManagerFactory{}},
depsKey: &deps{ClientFactory: cf, IOHandler: &defaultIOHandler{app: app}, ManagerFactory: &defaultManagerFactory{}},
}
app.Flags = []cli.Flag{
&cli.StringFlag{
Expand Down Expand Up @@ -255,7 +271,7 @@ func getDeps(ctx *cli.Context) cliDeps {
type cliDeps interface {
ClientFactory
IOHandler
PersistenceManagerFactory
ManagerFactory
}

type IOHandler interface {
Expand Down Expand Up @@ -306,5 +322,5 @@ var _ cliDeps = &deps{}
type deps struct {
ClientFactory
IOHandler
PersistenceManagerFactory
ManagerFactory
}
Loading

0 comments on commit 21b40bd

Please sign in to comment.