From 09f04dae6b7602251d68ea37c8664cdc0bb61019 Mon Sep 17 00:00:00 2001 From: Petri Ovaska Date: Tue, 16 Nov 2021 08:36:13 +0200 Subject: [PATCH] SDL CLI 'get' reads keys data in the given namespace Implement SDL CLI 'get' command that reads keys data in the given namespace. The 'get' reads one or multiple keys data: sdlcli get [ ... ] Issue-Id: RIC-113 Change-Id: Ibe2cad1a986fa48bee4dba20eb4e886f62b09bf0 Signed-off-by: Petri Ovaska --- internal/cli/get.go | 61 +++++++++++++++++++-- internal/cli/get_test.go | 88 ++++++++++++++++++++++++++++-- internal/cli/types.go | 1 + internal/mocks/db_mocks_private_testing.go | 5 ++ 4 files changed, 145 insertions(+), 10 deletions(-) diff --git a/internal/cli/get.go b/internal/cli/get.go index 5577663..de4f80c 100644 --- a/internal/cli/get.go +++ b/internal/cli/get.go @@ -23,10 +23,18 @@ package cli import ( + "fmt" + "os" + "sort" + + "gerrit.o-ran-sc.org/r/ric-plt/sdlgo" + "gerrit.o-ran-sc.org/r/ric-plt/sdlgo/internal/sdlgoredis" "github.com/spf13/cobra" ) -var getCmd = newGetCmd() +var getCmd = newGetCmd(func() ISyncStorage { + return sdlgo.NewSyncStorage() +}) func init() { rootCmd.AddCommand(getCmd) @@ -35,17 +43,60 @@ func init() { var ( getLong = `Display one or many resources. -Prints important information about the specified resources.` +Prints keys and keys data in the given namespace.` + + getExample = ` # Get reads keys data in the given namespace. + sdlcli get sdlns key1 + + # Get reads multiple keys data in the given namespace. + sdlcli get sdlns key1 key2 key3 - getExample = ` # List keys in the given namespace. + # List keys in the given namespace. sdlcli get keys sdlns` ) -func newGetCmd() *cobra.Command { +func newGetCmd(sdlCb SyncStorageCreateCb) *cobra.Command { return &cobra.Command{ - Use: "get", + Use: "get [ ... ]", Short: "Display one or many resources", Long: getLong, Example: getExample, + RunE: func(cmd *cobra.Command, args []string) error { + sdlgoredis.SetDbLogger(&buf) + if len(args) < 2 { + return fmt.Errorf("accepts command or arguments, received %d", len(args)) + } + data, err := runGet(sdlCb, args) + if err != nil { + fmt.Fprintf(os.Stderr, "%s", buf.String()) + return err + } + printData(cmd, data) + return nil + }, + } +} + +func runGet(sdlCb SyncStorageCreateCb, args []string) (map[string]interface{}, error) { + data, err := sdlCb().Get(args[0], args[1:]) + if err != nil { + return nil, err + } + return data, nil +} + +func printData(cmd *cobra.Command, data map[string]interface{}) { + var str string + var sortedKeys []string + for key := range data { + sortedKeys = append(sortedKeys, key) + } + sort.Strings(sortedKeys) + for _, k := range sortedKeys { + value, ok := data[k] + if ok && value != nil { + str = fmt.Sprintf("%s:%s", k, value) + cmd.Printf(str + "\n") + } } } diff --git a/internal/cli/get_test.go b/internal/cli/get_test.go index 759ab11..22301b9 100644 --- a/internal/cli/get_test.go +++ b/internal/cli/get_test.go @@ -26,14 +26,55 @@ import ( "bytes" "errors" "gerrit.o-ran-sc.org/r/ric-plt/sdlgo/internal/cli" + "gerrit.o-ran-sc.org/r/ric-plt/sdlgo/internal/mocks" "github.com/stretchr/testify/assert" "testing" ) +var getMocks *GetMocks + +type GetMocks struct { + sdlIface *mocks.MockSdlApi + ns string + keys []string + result map[string]interface{} + ret error +} + +func setupGetMock(ns string, keys []string, result map[string]interface{}, ret error) { + getMocks = new(GetMocks) + getMocks.ns = ns + getMocks.keys = keys + getMocks.result = result + getMocks.ret = ret +} + +func newSdlGetApiMock() cli.ISyncStorage { + getMocks.sdlIface = new(mocks.MockSdlApi) + getMocks.sdlIface.On("Get", getMocks.ns, getMocks.keys).Return(getMocks.result, getMocks.ret) + return getMocks.sdlIface +} + +func runGetCmdCli() (string, string, error) { + bufOut := new(bytes.Buffer) + bufErr := new(bytes.Buffer) + + cmd := cli.NewGetCmdForTest(newSdlGetApiMock) + cmd.SetOut(bufOut) + cmd.SetErr(bufErr) + args := []string{getMocks.ns} + args = append(args, getMocks.keys...) + cmd.SetArgs(args) + err := cmd.Execute() + + return bufOut.String(), bufErr.String(), err +} + func TestGetCmdShowHelp(t *testing.T) { var expOkErr error - expHelp := "Display one or many resources.\n\nPrints important information about the specified resources." - expExamples := "Examples:\n # List keys in the given namespace." + expHelp := "Display one or many resources.\n\nPrints keys and keys data in the given namespace." + expHelpUsage := "Usage:\n get [ ... ] [flags]" + expArgsErr := errors.New("accepts command or arguments, received 0") expNokErr := errors.New("unknown flag: --ff") tests := []struct { args []string @@ -42,13 +83,13 @@ func TestGetCmdShowHelp(t *testing.T) { }{ {args: []string{"-h"}, expErr: expOkErr, expOut: expHelp}, {args: []string{"--help"}, expErr: expOkErr, expOut: expHelp}, - {args: []string{}, expErr: expOkErr, expOut: expHelp}, - {args: []string{"--ff"}, expErr: expNokErr, expOut: expExamples}, + {args: []string{}, expErr: expArgsErr, expOut: expHelpUsage}, + {args: []string{"--ff"}, expErr: expNokErr, expOut: expHelpUsage}, } for _, test := range tests { buf := new(bytes.Buffer) - cmd := cli.NewGetCmdForTest() + cmd := cli.NewGetCmdForTest(newSdlGetApiMock) cmd.SetOut(buf) cmd.SetErr(buf) cmd.SetArgs(test.args) @@ -59,3 +100,40 @@ func TestGetCmdShowHelp(t *testing.T) { assert.Contains(t, result, test.expOut) } } + +func TestGetCmdSuccess(t *testing.T) { + + tests := []struct { + ns string + keys []string + result map[string]interface{} + expOut string + expErr error + }{ + {ns: "testns", keys: []string{"key1"}, result: map[string]interface{}{}, expOut: "", expErr: nil}, + {ns: "testns", keys: []string{"key1"}, result: map[string]interface{}{"key1": "1"}, expOut: "key1:1\n", expErr: nil}, + {ns: "testns", keys: []string{"key1 key2"}, result: map[string]interface{}{"key1": "1", "key2": "2"}, expOut: "key1:1\nkey2:2\n", expErr: nil}, + {ns: "testns", keys: []string{"key1 key3 key2"}, result: map[string]interface{}{"key1": "1", "key3": "3", "key2": "2"}, expOut: "key1:1\nkey2:2\nkey3:3\n", expErr: nil}, + {ns: "testns", keys: []string{"key1 keyDoesNotExist"}, result: map[string]interface{}{"key1": "1"}, expOut: "key1:1\n", expErr: nil}, + } + for _, test := range tests { + setupGetMock(test.ns, test.keys, test.result, test.expErr) + stdout, stderr, err := runGetCmdCli() + + assert.Nil(t, err) + assert.Equal(t, test.expOut, stdout) + assert.Equal(t, "", stderr) + getMocks.sdlIface.AssertExpectations(t) + } +} + +func TestGetCmdFails(t *testing.T) { + expErr := errors.New("Boom!") + + setupGetMock("testns", []string{"key1"}, map[string]interface{}{}, expErr) + _, stderr, err := runGetCmdCli() + + assert.Equal(t, expErr, err) + assert.Contains(t, stderr, expErr.Error()) + getMocks.sdlIface.AssertExpectations(t) +} diff --git a/internal/cli/types.go b/internal/cli/types.go index dbe410b..53953a8 100644 --- a/internal/cli/types.go +++ b/internal/cli/types.go @@ -41,6 +41,7 @@ type DbCreateCb func() *Database //iSyncStorage is an interface towards SDL SyncStorage API type ISyncStorage interface { + Get(ns string, keys []string) (map[string]interface{}, error) ListKeys(ns string, pattern string) ([]string, error) Set(ns string, pairs ...interface{}) error Remove(ns string, keys []string) error diff --git a/internal/mocks/db_mocks_private_testing.go b/internal/mocks/db_mocks_private_testing.go index 06927cb..a17f7d3 100644 --- a/internal/mocks/db_mocks_private_testing.go +++ b/internal/mocks/db_mocks_private_testing.go @@ -59,3 +59,8 @@ func (m *MockSdlApi) Remove(ns string, keys []string) error { a := m.Called(ns, keys) return a.Error(0) } + +func (m *MockSdlApi) Get(ns string, keys []string) (map[string]interface{}, error) { + a := m.Called(ns, keys) + return a.Get(0).(map[string]interface{}), a.Error(1) +} -- 2.16.6