From c676ad32978dade20b2adac038aa8bcdbc505237 Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Thu, 25 Apr 2024 15:08:36 -0700 Subject: [PATCH 1/8] WIP --- internal/output/output2.go | 129 ++++++++++++++++++++++++++++++++ internal/output/output2_test.go | 109 +++++++++++++++++++++++++++ 2 files changed, 238 insertions(+) create mode 100644 internal/output/output2.go create mode 100644 internal/output/output2_test.go diff --git a/internal/output/output2.go b/internal/output/output2.go new file mode 100644 index 00000000..31f7844c --- /dev/null +++ b/internal/output/output2.go @@ -0,0 +1,129 @@ +package output + +import ( + "encoding/json" + "fmt" +) + +type resourceOutputDelete struct { + outputFn PlaintextOutputFn + resource resource +} +type resourceOutputCreate struct { + input []byte + outputFn PlaintextOutputFn + resource resource +} +type resourceOutputUpdate struct { + input []byte + outputFn PlaintextOutputFn + resource resource +} + +func (r resourceOutputCreate) JSON() string { + return string(r.input) +} +func (r resourceOutputDelete) JSON() string { + return "" +} +func (r resourceOutputUpdate) JSON() string { + return string(r.input) +} + +func (r resourceOutputCreate) String() string { + return fmt.Sprintf("%s %s", r.successMessage(), r.plaintext()) +} +func (r resourceOutputDelete) String() string { + return fmt.Sprintf("%s %s", r.successMessage(), r.plaintext()) +} +func (r resourceOutputUpdate) String() string { + return fmt.Sprintf("%s %s", r.successMessage(), r.plaintext()) +} + +func (r resourceOutputCreate) successMessage() string { + return "Successfully created" +} +func (r resourceOutputDelete) successMessage() string { + return "Successfully deleted" +} +func (r resourceOutputUpdate) successMessage() string { + return "Successfully updated" +} + +func (r resourceOutputCreate) plaintext() string { + return r.outputFn(r.resource) +} +func (r resourceOutputDelete) plaintext() string { + return r.outputFn(r.resource) +} +func (r resourceOutputUpdate) plaintext() string { + return r.outputFn(r.resource) +} + +func CmdOutputCreateResource(outputKind string, input []byte) (string, error) { + var r resource + err := json.Unmarshal(input, &r) + if err != nil { + return "", err + } + + o := resourceOutputCreate{ + input: input, + outputFn: SingularPlaintextOutputFn, + resource: r, + } + + switch outputKind { + case "json": + return o.JSON(), nil + case "plaintext": + return o.String(), nil + } + + return "", ErrInvalidOutputKind +} + +func CmdOutputDeleteResource(outputKind string, input []byte) (string, error) { + var r resource + err := json.Unmarshal(input, &r) + if err != nil { + return "", err + } + + o := resourceOutputDelete{ + outputFn: SingularPlaintextOutputFn, + resource: r, + } + + switch outputKind { + case "json": + return o.JSON(), nil + case "plaintext": + return o.String(), nil + } + + return "", ErrInvalidOutputKind +} + +func CmdOutputUpdateResource(outputKind string, input []byte) (string, error) { + var r resource + err := json.Unmarshal(input, &r) + if err != nil { + return "", err + } + + o := resourceOutputUpdate{ + input: input, + outputFn: SingularPlaintextOutputFn, + resource: r, + } + + switch outputKind { + case "json": + return o.JSON(), nil + case "plaintext": + return o.String(), nil + } + + return "", ErrInvalidOutputKind +} diff --git a/internal/output/output2_test.go b/internal/output/output2_test.go new file mode 100644 index 00000000..309b2bf7 --- /dev/null +++ b/internal/output/output2_test.go @@ -0,0 +1,109 @@ +package output_test + +import ( + "ldcli/internal/output" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestCmdOutputCreateResource(t *testing.T) { + input := `{ + "key": "test-key", + "name": "test-name", + "other": "other-value" + }` + + t.Run("with json output", func(t *testing.T) { + t.Run("returns the JSON", func(t *testing.T) { + result, err := output.CmdOutputCreateResource("json", []byte(input)) + + require.NoError(t, err) + assert.JSONEq(t, input, result) + }) + }) + + t.Run("with plaintext output", func(t *testing.T) { + t.Run("returns a success message", func(t *testing.T) { + expected := "Successfully created test-name (test-key)" + + result, err := output.CmdOutputCreateResource("plaintext", []byte(input)) + + require.NoError(t, err) + assert.Equal(t, expected, result) + }) + }) + // TODO: error handling +} + +func TestCmdOutputDeleteResource(t *testing.T) { + input := `{ + "key": "test-key", + "name": "test-name" + }` + + t.Run("with json output", func(t *testing.T) { + t.Run("does not return anything", func(t *testing.T) { + result, err := output.CmdOutputDeleteResource("json", []byte(input)) + + require.NoError(t, err) + assert.Equal(t, "", result) + }) + }) + + t.Run("with plaintext output", func(t *testing.T) { + t.Run("with a key and name", func(t *testing.T) { + t.Run("returns a success message", func(t *testing.T) { + expected := "Successfully deleted test-name (test-key)" + + result, err := output.CmdOutputDeleteResource("plaintext", []byte(input)) + + require.NoError(t, err) + assert.Equal(t, expected, result) + }) + }) + + t.Run("with a key", func(t *testing.T) { + t.Skip() + t.Run("returns a success message", func(t *testing.T) { + expected := "Successfully deleted test-key" + + result, err := output.CmdOutputDeleteResource("plaintext", []byte(input)) + + require.NoError(t, err) + assert.Equal(t, expected, result) + }) + }) + }) + // TODO: error handling +} + +func TestCmdOutputUpdateResource(t *testing.T) { + input := `{ + "key": "test-key", + "name": "test-name", + "other": "other-value" + }` + + t.Run("with json output", func(t *testing.T) { + t.Run("returns the JSON", func(t *testing.T) { + result, err := output.CmdOutputUpdateResource("json", []byte(input)) + + require.NoError(t, err) + assert.JSONEq(t, input, result) + }) + }) + + t.Run("with plaintext output", func(t *testing.T) { + t.Run("returns a success message", func(t *testing.T) { + expected := "Successfully updated test-name (test-key)" + + result, err := output.CmdOutputUpdateResource("plaintext", []byte(input)) + + require.NoError(t, err) + assert.Equal(t, expected, result) + }) + }) + // TODO: error handling +} From 49fb57720a459d626f320a597bf49a8b60a72175 Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Thu, 25 Apr 2024 15:35:12 -0700 Subject: [PATCH 2/8] Show successful delete resource message --- cmd/flags/create.go | 3 +- cmd/members/create.go | 3 +- cmd/projects/create.go | 3 +- internal/output/output2.go | 129 ------------------ internal/output/resource_output.go | 96 +++++++++++++ ...utput2_test.go => resource_output_test.go} | 3 - 6 files changed, 99 insertions(+), 138 deletions(-) delete mode 100644 internal/output/output2.go create mode 100644 internal/output/resource_output.go rename internal/output/{output2_test.go => resource_output_test.go} (97%) diff --git a/cmd/flags/create.go b/cmd/flags/create.go index aaabd9e0..d3b1b120 100644 --- a/cmd/flags/create.go +++ b/cmd/flags/create.go @@ -81,10 +81,9 @@ func runCreate(client flags.Client) func(*cobra.Command, []string) error { return errors.NewError(output) } - output, err := output.CmdOutputCreate( + output, err := output.CmdOutputCreateResource( viper.GetString(cliflags.OutputFlag), response, - output.SingularPlaintextOutputFn, ) if err != nil { return errors.NewError(err.Error()) diff --git a/cmd/members/create.go b/cmd/members/create.go index 154c06d8..d529f6a4 100644 --- a/cmd/members/create.go +++ b/cmd/members/create.go @@ -65,10 +65,9 @@ func runCreate(client members.Client) func(*cobra.Command, []string) error { return errors.NewError(output) } - output, err := output.CmdOutputSingular( + output, err := output.CmdOutputCreateResource( viper.GetString(cliflags.OutputFlag), response, - output.SingularPlaintextOutputFn, ) if err != nil { return errors.NewError(err.Error()) diff --git a/cmd/projects/create.go b/cmd/projects/create.go index 408984fa..8424137d 100644 --- a/cmd/projects/create.go +++ b/cmd/projects/create.go @@ -71,10 +71,9 @@ func runCreate(client projects.Client) func(*cobra.Command, []string) error { return errors.NewError(output) } - output, err := output.CmdOutputSingular( + output, err := output.CmdOutputCreateResource( viper.GetString(cliflags.OutputFlag), response, - output.SingularPlaintextOutputFn, ) if err != nil { return errors.NewError(err.Error()) diff --git a/internal/output/output2.go b/internal/output/output2.go deleted file mode 100644 index 31f7844c..00000000 --- a/internal/output/output2.go +++ /dev/null @@ -1,129 +0,0 @@ -package output - -import ( - "encoding/json" - "fmt" -) - -type resourceOutputDelete struct { - outputFn PlaintextOutputFn - resource resource -} -type resourceOutputCreate struct { - input []byte - outputFn PlaintextOutputFn - resource resource -} -type resourceOutputUpdate struct { - input []byte - outputFn PlaintextOutputFn - resource resource -} - -func (r resourceOutputCreate) JSON() string { - return string(r.input) -} -func (r resourceOutputDelete) JSON() string { - return "" -} -func (r resourceOutputUpdate) JSON() string { - return string(r.input) -} - -func (r resourceOutputCreate) String() string { - return fmt.Sprintf("%s %s", r.successMessage(), r.plaintext()) -} -func (r resourceOutputDelete) String() string { - return fmt.Sprintf("%s %s", r.successMessage(), r.plaintext()) -} -func (r resourceOutputUpdate) String() string { - return fmt.Sprintf("%s %s", r.successMessage(), r.plaintext()) -} - -func (r resourceOutputCreate) successMessage() string { - return "Successfully created" -} -func (r resourceOutputDelete) successMessage() string { - return "Successfully deleted" -} -func (r resourceOutputUpdate) successMessage() string { - return "Successfully updated" -} - -func (r resourceOutputCreate) plaintext() string { - return r.outputFn(r.resource) -} -func (r resourceOutputDelete) plaintext() string { - return r.outputFn(r.resource) -} -func (r resourceOutputUpdate) plaintext() string { - return r.outputFn(r.resource) -} - -func CmdOutputCreateResource(outputKind string, input []byte) (string, error) { - var r resource - err := json.Unmarshal(input, &r) - if err != nil { - return "", err - } - - o := resourceOutputCreate{ - input: input, - outputFn: SingularPlaintextOutputFn, - resource: r, - } - - switch outputKind { - case "json": - return o.JSON(), nil - case "plaintext": - return o.String(), nil - } - - return "", ErrInvalidOutputKind -} - -func CmdOutputDeleteResource(outputKind string, input []byte) (string, error) { - var r resource - err := json.Unmarshal(input, &r) - if err != nil { - return "", err - } - - o := resourceOutputDelete{ - outputFn: SingularPlaintextOutputFn, - resource: r, - } - - switch outputKind { - case "json": - return o.JSON(), nil - case "plaintext": - return o.String(), nil - } - - return "", ErrInvalidOutputKind -} - -func CmdOutputUpdateResource(outputKind string, input []byte) (string, error) { - var r resource - err := json.Unmarshal(input, &r) - if err != nil { - return "", err - } - - o := resourceOutputUpdate{ - input: input, - outputFn: SingularPlaintextOutputFn, - resource: r, - } - - switch outputKind { - case "json": - return o.JSON(), nil - case "plaintext": - return o.String(), nil - } - - return "", ErrInvalidOutputKind -} diff --git a/internal/output/resource_output.go b/internal/output/resource_output.go new file mode 100644 index 00000000..e5630a9a --- /dev/null +++ b/internal/output/resource_output.go @@ -0,0 +1,96 @@ +package output + +import ( + "encoding/json" + "fmt" +) + +type resourceOutput struct { + input []byte + outputFn PlaintextOutputFn + resource resource + successMessage string +} + +func (r resourceOutput) JSON() string { + return string(r.input) +} + +func (r resourceOutput) String() string { + return fmt.Sprintf("%s %s", r.successMessage, r.plaintext()) +} + +func (r resourceOutput) plaintext() string { + return r.outputFn(r.resource) +} + +type resourceOutputFn func(input []byte, outputFn PlaintextOutputFn, r resource) resourceOutput + +// CmdOutputCreateResource returns a response from a resource create action formatted based on the +// output flag. +func CmdOutputCreateResource(outputKind string, input []byte) (string, error) { + return cmdOutputResource( + outputKind, + input, + func(input []byte, outputFn PlaintextOutputFn, r resource) resourceOutput { + return resourceOutput{ + input: input, + outputFn: SingularPlaintextOutputFn, + resource: r, + successMessage: "Successfully created", + } + }, + ) +} + +// CmdOutputDeleteResource returns a response from a resource delete action formatted based on the +// output flag. +func CmdOutputDeleteResource(outputKind string, input []byte) (string, error) { + return cmdOutputResource( + outputKind, + input, + func(input []byte, outputFn PlaintextOutputFn, r resource) resourceOutput { + return resourceOutput{ + outputFn: SingularPlaintextOutputFn, + resource: r, + successMessage: "Successfully deleted", + } + }, + ) +} + +// CmdOutputUpdateResource returns a response from a resource update action formatted based on the +// output flag. +func CmdOutputUpdateResource(outputKind string, input []byte) (string, error) { + return cmdOutputResource( + outputKind, + input, + func(input []byte, outputFn PlaintextOutputFn, r resource) resourceOutput { + return resourceOutput{ + input: input, + outputFn: SingularPlaintextOutputFn, + resource: r, + successMessage: "Successfully updated", + } + }, + ) +} + +func cmdOutputResource(outputKind string, input []byte, constructor resourceOutputFn) (string, error) { + var r resource + err := json.Unmarshal(input, &r) + if err != nil { + return "", err + } + + o := constructor(input, SingularPlaintextOutputFn, r) + + switch outputKind { + case "json": + return o.JSON(), nil + case "plaintext": + return o.String(), nil + } + + return "", ErrInvalidOutputKind +} diff --git a/internal/output/output2_test.go b/internal/output/resource_output_test.go similarity index 97% rename from internal/output/output2_test.go rename to internal/output/resource_output_test.go index 309b2bf7..1b171fa2 100644 --- a/internal/output/output2_test.go +++ b/internal/output/resource_output_test.go @@ -34,7 +34,6 @@ func TestCmdOutputCreateResource(t *testing.T) { assert.Equal(t, expected, result) }) }) - // TODO: error handling } func TestCmdOutputDeleteResource(t *testing.T) { @@ -76,7 +75,6 @@ func TestCmdOutputDeleteResource(t *testing.T) { }) }) }) - // TODO: error handling } func TestCmdOutputUpdateResource(t *testing.T) { @@ -105,5 +103,4 @@ func TestCmdOutputUpdateResource(t *testing.T) { assert.Equal(t, expected, result) }) }) - // TODO: error handling } From 34931e3a43bca915902a2e39f8605d820c87e7cc Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Thu, 25 Apr 2024 15:37:17 -0700 Subject: [PATCH 3/8] Remove unused function --- cmd/flags/update.go | 3 +- internal/output/output.go | 34 +++--------------- internal/output/output_test.go | 65 ---------------------------------- 3 files changed, 5 insertions(+), 97 deletions(-) diff --git a/cmd/flags/update.go b/cmd/flags/update.go index 5c8bae11..0ee6625c 100644 --- a/cmd/flags/update.go +++ b/cmd/flags/update.go @@ -150,10 +150,9 @@ func runUpdate(client flags.Client) func(*cobra.Command, []string) error { return errors.NewError(output) } - output, err := output.CmdOutputUpdate( + output, err := output.CmdOutputUpdateResource( viper.GetString(cliflags.OutputFlag), response, - output.SingularPlaintextOutputFn, ) if err != nil { return errors.NewError(err.Error()) diff --git a/internal/output/output.go b/internal/output/output.go index c9bc51c0..63ace22d 100644 --- a/internal/output/output.go +++ b/internal/output/output.go @@ -73,33 +73,7 @@ func CmdOutputSingular(outputKind string, input []byte, fn PlaintextOutputFn) (s return "", err } - return outputFromKind(outputKind, "", SingularOutputter{ - outputFn: fn, - resource: r, - resourceJSON: input, - }) -} - -// CmdOutputCreate builds a command response based on the flag the user provided and the shape of -// the input with a successfully created message. The expected shape is a single JSON object. -func CmdOutputCreate(outputKind string, input []byte, fn PlaintextOutputFn) (string, error) { - return cmdOutputWithMessage(outputKind, "Successfully created ", input, fn) -} - -// CmdOutputUpdate builds a command response based on the flag the user provided and the shape of -// the input with a successfully created message. The expected shape is a single JSON object. -func CmdOutputUpdate(outputKind string, input []byte, fn PlaintextOutputFn) (string, error) { - return cmdOutputWithMessage(outputKind, "Successfully updated ", input, fn) -} - -func cmdOutputWithMessage(outputKind string, message string, input []byte, fn PlaintextOutputFn) (string, error) { - var r resource - err := json.Unmarshal(input, &r) - if err != nil { - return "", err - } - - return outputFromKind(outputKind, message, SingularOutputter{ + return outputFromKind(outputKind, SingularOutputter{ outputFn: fn, resource: r, resourceJSON: input, @@ -121,19 +95,19 @@ func CmdOutputMultiple(outputKind string, input []byte, fn PlaintextOutputFn) (s r.Items = rr } - return outputFromKind(outputKind, "", MultipleOutputter{ + return outputFromKind(outputKind, MultipleOutputter{ outputFn: fn, resources: r, resourceJSON: input, }) } -func outputFromKind(outputKind string, additional string, o Outputter) (string, error) { +func outputFromKind(outputKind string, o Outputter) (string, error) { switch outputKind { case "json": return o.JSON(), nil case "plaintext": - return additional + o.String(), nil + return o.String(), nil } return "", ErrInvalidOutputKind diff --git a/internal/output/output_test.go b/internal/output/output_test.go index 430e491f..2fc03e36 100644 --- a/internal/output/output_test.go +++ b/internal/output/output_test.go @@ -65,71 +65,6 @@ func TestCmdOutputSingular(t *testing.T) { } } -func TestCmdOutput_WithSuccessMessage(t *testing.T) { - tests := map[string]struct { - expected string - fn func(string, []byte, output.PlaintextOutputFn) (string, error) - input string - outputKind string - }{ - "when creating with json": { - expected: `{ - "key": "test-key", - "name": "test-name" - }`, - fn: output.CmdOutputCreate, - input: `{ - "key": "test-key", - "name": "test-name" - }`, - outputKind: "json", - }, - "when creating with plaintext": { - expected: "Successfully created test-name (test-key)", - fn: output.CmdOutputCreate, - input: `{ - "key": "test-key", - "name": "test-name" - }`, - outputKind: "plaintext", - }, - "when updating with json": { - expected: `{ - "key": "test-key", - "name": "test-name" - }`, - fn: output.CmdOutputUpdate, - input: `{ - "key": "test-key", - "name": "test-name" - }`, - outputKind: "json", - }, - "when updating with plaintext": { - expected: "Successfully updated test-name (test-key)", - fn: output.CmdOutputUpdate, - input: `{ - "key": "test-key", - "name": "test-name" - }`, - outputKind: "plaintext", - }, - } - for name, tt := range tests { - tt := tt - t.Run(name, func(t *testing.T) { - output, err := tt.fn( - tt.outputKind, - []byte(tt.input), - output.SingularPlaintextOutputFn, - ) - - require.NoError(t, err) - assert.Equal(t, tt.expected, output) - }) - } -} - func TestCmdOutputMultiple(t *testing.T) { tests := map[string]struct { expected string From 809ebafb65e2cf3e3e13287d1e3eae620a93ca3e Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Mon, 29 Apr 2024 08:34:25 -0700 Subject: [PATCH 4/8] Use single output function --- cmd/environments/get.go | 6 +- cmd/flags/create.go | 5 +- cmd/flags/get.go | 6 +- cmd/flags/update.go | 5 +- cmd/members/create.go | 5 +- cmd/members/invite.go | 6 +- cmd/projects/create.go | 5 +- cmd/projects/list.go | 6 +- internal/members/members.go | 4 +- internal/output/output.go | 17 +-- internal/output/output_test.go | 5 - internal/output/resource_output.go | 125 +++++++++++++--------- internal/output/resource_output_test.go | 134 ++++++++++++------------ 13 files changed, 161 insertions(+), 168 deletions(-) diff --git a/cmd/environments/get.go b/cmd/environments/get.go index e169adc4..0005788d 100644 --- a/cmd/environments/get.go +++ b/cmd/environments/get.go @@ -76,11 +76,7 @@ func runGet( return errors.NewError(output) } - output, err := output.CmdOutputSingular( - viper.GetString(cliflags.OutputFlag), - response, - output.SingularPlaintextOutputFn, - ) + output, err := output.CmdOutput("get", viper.GetString(cliflags.OutputFlag), response) if err != nil { return errors.NewError(err.Error()) } diff --git a/cmd/flags/create.go b/cmd/flags/create.go index d3b1b120..60618678 100644 --- a/cmd/flags/create.go +++ b/cmd/flags/create.go @@ -81,10 +81,7 @@ func runCreate(client flags.Client) func(*cobra.Command, []string) error { return errors.NewError(output) } - output, err := output.CmdOutputCreateResource( - viper.GetString(cliflags.OutputFlag), - response, - ) + output, err := output.CmdOutput("create", viper.GetString(cliflags.OutputFlag), response) if err != nil { return errors.NewError(err.Error()) } diff --git a/cmd/flags/get.go b/cmd/flags/get.go index 77912d51..cc1a906c 100644 --- a/cmd/flags/get.go +++ b/cmd/flags/get.go @@ -79,11 +79,7 @@ func runGet(client flags.Client) func(*cobra.Command, []string) error { return errors.NewError(output) } - output, err := output.CmdOutputSingular( - viper.GetString(cliflags.OutputFlag), - response, - output.SingularPlaintextOutputFn, - ) + output, err := output.CmdOutput("get", viper.GetString(cliflags.OutputFlag), response) if err != nil { return errors.NewError(err.Error()) } diff --git a/cmd/flags/update.go b/cmd/flags/update.go index 0ee6625c..b3f62bcf 100644 --- a/cmd/flags/update.go +++ b/cmd/flags/update.go @@ -150,10 +150,7 @@ func runUpdate(client flags.Client) func(*cobra.Command, []string) error { return errors.NewError(output) } - output, err := output.CmdOutputUpdateResource( - viper.GetString(cliflags.OutputFlag), - response, - ) + output, err := output.CmdOutput("update", viper.GetString(cliflags.OutputFlag), response) if err != nil { return errors.NewError(err.Error()) } diff --git a/cmd/members/create.go b/cmd/members/create.go index d529f6a4..3c2be7f6 100644 --- a/cmd/members/create.go +++ b/cmd/members/create.go @@ -65,10 +65,7 @@ func runCreate(client members.Client) func(*cobra.Command, []string) error { return errors.NewError(output) } - output, err := output.CmdOutputCreateResource( - viper.GetString(cliflags.OutputFlag), - response, - ) + output, err := output.CmdOutput("create", viper.GetString(cliflags.OutputFlag), response) if err != nil { return errors.NewError(err.Error()) } diff --git a/cmd/members/invite.go b/cmd/members/invite.go index 450d6412..1dd3b385 100644 --- a/cmd/members/invite.go +++ b/cmd/members/invite.go @@ -74,11 +74,7 @@ func runInvite(client members.Client) func(*cobra.Command, []string) error { return errors.NewError(output) } - output, err := output.CmdOutputMultiple( - viper.GetString(cliflags.OutputFlag), - response, - output.MultipleEmailPlaintextOutputFn, - ) + output, err := output.CmdOutput("create", viper.GetString(cliflags.OutputFlag), response) if err != nil { return errors.NewError(err.Error()) } diff --git a/cmd/projects/create.go b/cmd/projects/create.go index 8424137d..2f4d83f6 100644 --- a/cmd/projects/create.go +++ b/cmd/projects/create.go @@ -71,10 +71,7 @@ func runCreate(client projects.Client) func(*cobra.Command, []string) error { return errors.NewError(output) } - output, err := output.CmdOutputCreateResource( - viper.GetString(cliflags.OutputFlag), - response, - ) + output, err := output.CmdOutput("create", viper.GetString(cliflags.OutputFlag), response) if err != nil { return errors.NewError(err.Error()) } diff --git a/cmd/projects/list.go b/cmd/projects/list.go index 4a038995..666f32e3 100644 --- a/cmd/projects/list.go +++ b/cmd/projects/list.go @@ -46,11 +46,7 @@ func runList(client projects.Client) func(*cobra.Command, []string) error { return errors.NewError(output) } - output, err := output.CmdOutputMultiple( - viper.GetString(cliflags.OutputFlag), - response, - output.MultiplePlaintextOutputFn, - ) + output, err := output.CmdOutput("list", viper.GetString(cliflags.OutputFlag), response) if err != nil { return errors.NewError(err.Error()) } diff --git a/internal/members/members.go b/internal/members/members.go index 7ef553c7..4ca6de29 100644 --- a/internal/members/members.go +++ b/internal/members/members.go @@ -42,10 +42,10 @@ func (c MembersClient) Create(ctx context.Context, accessToken string, baseURI s if err != nil { return nil, errors.NewLDAPIError(err) } - memberJson, err := json.Marshal(members.Items) + membersJson, err := json.Marshal(members) if err != nil { return nil, err } - return memberJson, nil + return membersJson, nil } diff --git a/internal/output/output.go b/internal/output/output.go index 63ace22d..8dade9c8 100644 --- a/internal/output/output.go +++ b/internal/output/output.go @@ -6,7 +6,10 @@ import ( "ldcli/internal/errors" ) -var ErrInvalidOutputKind = errors.NewError("output is invalid") +var ( + ErrInvalidActionKind = errors.NewError("action is invalid") + ErrInvalidOutputKind = errors.NewError("output is invalid") +) type OutputKind string @@ -60,10 +63,6 @@ type resources struct { Items []resource `json:"items"` } -// resourcesBare is for responses that return a list of resources at the top level of the response, -// not as a value of an "items" property. -type resourcesBare []resource - // CmdOutputSingular builds a command response based on the flag the user provided and the shape of // the input. The expected shape is a single JSON object. func CmdOutputSingular(outputKind string, input []byte, fn PlaintextOutputFn) (string, error) { @@ -86,13 +85,7 @@ func CmdOutputMultiple(outputKind string, input []byte, fn PlaintextOutputFn) (s var r resources err := json.Unmarshal(input, &r) if err != nil { - // sometimes a response doesn't include each item in an "items" property - var rr resourcesBare - err := json.Unmarshal(input, &rr) - if err != nil { - return "", err - } - r.Items = rr + return "", err } return outputFromKind(outputKind, MultipleOutputter{ diff --git a/internal/output/output_test.go b/internal/output/output_test.go index 2fc03e36..cf101dc4 100644 --- a/internal/output/output_test.go +++ b/internal/output/output_test.go @@ -71,11 +71,6 @@ func TestCmdOutputMultiple(t *testing.T) { fn output.PlaintextOutputFn input string }{ - "with multiple emails not as items property": { - expected: "* test-email1 (test-id1)\n* test-email2 (test-id2)", - fn: output.MultipleEmailPlaintextOutputFn, - input: `[{"_id": "test-id1", "email": "test-email1"}, {"_id": "test-id2", "email": "test-email2"}]`, - }, "with multiple items": { expected: "* test-name1 (test-key1)\n* test-name2 (test-key2)", fn: output.MultiplePlaintextOutputFn, diff --git a/internal/output/resource_output.go b/internal/output/resource_output.go index e5630a9a..1c62fd90 100644 --- a/internal/output/resource_output.go +++ b/internal/output/resource_output.go @@ -5,6 +5,16 @@ import ( "fmt" ) +type ActionKind string + +var ( + ActionKindCreate = ActionKind("create") + ActionKindDelete = ActionKind("delete") + ActionKindGet = ActionKind("get") + ActionKindList = ActionKind("list") + ActionKindUpdate = ActionKind("update") +) + type resourceOutput struct { input []byte outputFn PlaintextOutputFn @@ -26,54 +36,75 @@ func (r resourceOutput) plaintext() string { type resourceOutputFn func(input []byte, outputFn PlaintextOutputFn, r resource) resourceOutput -// CmdOutputCreateResource returns a response from a resource create action formatted based on the -// output flag. -func CmdOutputCreateResource(outputKind string, input []byte) (string, error) { - return cmdOutputResource( - outputKind, - input, - func(input []byte, outputFn PlaintextOutputFn, r resource) resourceOutput { - return resourceOutput{ - input: input, - outputFn: SingularPlaintextOutputFn, - resource: r, - successMessage: "Successfully created", - } - }, - ) -} +// CmdOutput returns a response from a resource create action formatted based on the +// output flag along with an optional message based on the action. +func CmdOutput(action string, outputKind string, input []byte) (string, error) { + switch action { + case "create": + return cmdOutputResource( + outputKind, + input, + func(input []byte, outputFn PlaintextOutputFn, r resource) resourceOutput { + return resourceOutput{ + input: input, + outputFn: SingularPlaintextOutputFn, + resource: r, + successMessage: "Successfully created", + } + }, + ) + case "delete": + return cmdOutputResource( + outputKind, + input, + func(input []byte, outputFn PlaintextOutputFn, r resource) resourceOutput { + return resourceOutput{ + outputFn: SingularPlaintextOutputFn, + resource: r, + successMessage: "Successfully deleted", + } + }, + ) + case "get": + return cmdOutputResource( + outputKind, + input, + func(input []byte, outputFn PlaintextOutputFn, r resource) resourceOutput { + return resourceOutput{ + input: input, + outputFn: SingularPlaintextOutputFn, + resource: r, + } + }, + ) + case "list": + var r resources + err := json.Unmarshal(input, &r) + if err != nil { + return "", err + } -// CmdOutputDeleteResource returns a response from a resource delete action formatted based on the -// output flag. -func CmdOutputDeleteResource(outputKind string, input []byte) (string, error) { - return cmdOutputResource( - outputKind, - input, - func(input []byte, outputFn PlaintextOutputFn, r resource) resourceOutput { - return resourceOutput{ - outputFn: SingularPlaintextOutputFn, - resource: r, - successMessage: "Successfully deleted", - } - }, - ) -} - -// CmdOutputUpdateResource returns a response from a resource update action formatted based on the -// output flag. -func CmdOutputUpdateResource(outputKind string, input []byte) (string, error) { - return cmdOutputResource( - outputKind, - input, - func(input []byte, outputFn PlaintextOutputFn, r resource) resourceOutput { - return resourceOutput{ - input: input, - outputFn: SingularPlaintextOutputFn, - resource: r, - successMessage: "Successfully updated", - } - }, - ) + return outputFromKind(outputKind, MultipleOutputter{ + outputFn: MultiplePlaintextOutputFn, + resources: r, + resourceJSON: input, + }) + case "update": + return cmdOutputResource( + outputKind, + input, + func(input []byte, outputFn PlaintextOutputFn, r resource) resourceOutput { + return resourceOutput{ + input: input, + outputFn: SingularPlaintextOutputFn, + resource: r, + successMessage: "Successfully updated", + } + }, + ) + default: + return "", ErrInvalidActionKind + } } func cmdOutputResource(outputKind string, input []byte, constructor resourceOutputFn) (string, error) { diff --git a/internal/output/resource_output_test.go b/internal/output/resource_output_test.go index 1b171fa2..f770601c 100644 --- a/internal/output/resource_output_test.go +++ b/internal/output/resource_output_test.go @@ -8,99 +8,101 @@ import ( "github.com/stretchr/testify/require" ) -func TestCmdOutputCreateResource(t *testing.T) { - input := `{ - "key": "test-key", - "name": "test-name", - "other": "other-value" - }` - - t.Run("with json output", func(t *testing.T) { - t.Run("returns the JSON", func(t *testing.T) { - result, err := output.CmdOutputCreateResource("json", []byte(input)) - - require.NoError(t, err) - assert.JSONEq(t, input, result) +func TestCmdOutput(t *testing.T) { + t.Run("when creating a resource", func(t *testing.T) { + input := `{ + "key": "test-key", + "name": "test-name", + "other": "other-value" + }` + + t.Run("with json output", func(t *testing.T) { + t.Run("returns the JSON", func(t *testing.T) { + result, err := output.CmdOutput("create", "json", []byte(input)) + + require.NoError(t, err) + assert.JSONEq(t, input, result) + }) }) - }) - t.Run("with plaintext output", func(t *testing.T) { - t.Run("returns a success message", func(t *testing.T) { - expected := "Successfully created test-name (test-key)" + t.Run("with plaintext output", func(t *testing.T) { + t.Run("returns a success message", func(t *testing.T) { + expected := "Successfully created test-name (test-key)" - result, err := output.CmdOutputCreateResource("plaintext", []byte(input)) + result, err := output.CmdOutput("create", "plaintext", []byte(input)) - require.NoError(t, err) - assert.Equal(t, expected, result) + require.NoError(t, err) + assert.Equal(t, expected, result) + }) }) }) -} -func TestCmdOutputDeleteResource(t *testing.T) { - input := `{ - "key": "test-key", - "name": "test-name" - }` + t.Run("when deleting a resource", func(t *testing.T) { + input := `{ + "key": "test-key", + "name": "test-name" + }` - t.Run("with json output", func(t *testing.T) { - t.Run("does not return anything", func(t *testing.T) { - result, err := output.CmdOutputDeleteResource("json", []byte(input)) + t.Run("with json output", func(t *testing.T) { + t.Run("does not return anything", func(t *testing.T) { + result, err := output.CmdOutput("delete", "json", []byte(input)) - require.NoError(t, err) - assert.Equal(t, "", result) + require.NoError(t, err) + assert.Equal(t, "", result) + }) }) - }) - t.Run("with plaintext output", func(t *testing.T) { - t.Run("with a key and name", func(t *testing.T) { - t.Run("returns a success message", func(t *testing.T) { - expected := "Successfully deleted test-name (test-key)" + t.Run("with plaintext output", func(t *testing.T) { + t.Run("with a key and name", func(t *testing.T) { + t.Run("returns a success message", func(t *testing.T) { + expected := "Successfully deleted test-name (test-key)" - result, err := output.CmdOutputDeleteResource("plaintext", []byte(input)) + result, err := output.CmdOutput("delete", "plaintext", []byte(input)) - require.NoError(t, err) - assert.Equal(t, expected, result) + require.NoError(t, err) + assert.Equal(t, expected, result) + }) }) - }) - t.Run("with a key", func(t *testing.T) { - t.Skip() - t.Run("returns a success message", func(t *testing.T) { - expected := "Successfully deleted test-key" + t.Run("with a key", func(t *testing.T) { + t.Skip() + t.Run("returns a success message", func(t *testing.T) { + expected := "Successfully deleted test-key" - result, err := output.CmdOutputDeleteResource("plaintext", []byte(input)) + result, err := output.CmdOutput("delete", "plaintext", []byte(input)) - require.NoError(t, err) - assert.Equal(t, expected, result) + require.NoError(t, err) + assert.Equal(t, expected, result) + }) }) }) }) -} -func TestCmdOutputUpdateResource(t *testing.T) { - input := `{ - "key": "test-key", - "name": "test-name", - "other": "other-value" - }` + t.Run("when updating a resource", func(t *testing.T) { + input := `{ + "key": "test-key", + "name": "test-name", + "other": "other-value" + }` - t.Run("with json output", func(t *testing.T) { - t.Run("returns the JSON", func(t *testing.T) { - result, err := output.CmdOutputUpdateResource("json", []byte(input)) + t.Run("with json output", func(t *testing.T) { + t.Run("returns the JSON", func(t *testing.T) { + result, err := output.CmdOutput("update", "json", []byte(input)) - require.NoError(t, err) - assert.JSONEq(t, input, result) + require.NoError(t, err) + assert.JSONEq(t, input, result) + }) }) - }) - t.Run("with plaintext output", func(t *testing.T) { - t.Run("returns a success message", func(t *testing.T) { - expected := "Successfully updated test-name (test-key)" + t.Run("with plaintext output", func(t *testing.T) { + t.Run("returns a success message", func(t *testing.T) { + expected := "Successfully updated test-name (test-key)" - result, err := output.CmdOutputUpdateResource("plaintext", []byte(input)) + result, err := output.CmdOutput("update", "plaintext", []byte(input)) - require.NoError(t, err) - assert.Equal(t, expected, result) + require.NoError(t, err) + assert.Equal(t, expected, result) + }) }) }) } From b5d30edc410eb1f6ad84e2c5895cad96601a4287 Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Mon, 29 Apr 2024 10:17:32 -0700 Subject: [PATCH 5/8] Output singular and multiple resource in single function --- internal/output/resource_output.go | 147 +++++++----------------- internal/output/resource_output_test.go | 66 ++++++++++- 2 files changed, 108 insertions(+), 105 deletions(-) diff --git a/internal/output/resource_output.go b/internal/output/resource_output.go index 1c62fd90..49b48333 100644 --- a/internal/output/resource_output.go +++ b/internal/output/resource_output.go @@ -3,125 +3,64 @@ package output import ( "encoding/json" "fmt" + "strings" ) -type ActionKind string - -var ( - ActionKindCreate = ActionKind("create") - ActionKindDelete = ActionKind("delete") - ActionKindGet = ActionKind("get") - ActionKindList = ActionKind("list") - ActionKindUpdate = ActionKind("update") -) - -type resourceOutput struct { - input []byte - outputFn PlaintextOutputFn - resource resource - successMessage string -} - -func (r resourceOutput) JSON() string { - return string(r.input) -} - -func (r resourceOutput) String() string { - return fmt.Sprintf("%s %s", r.successMessage, r.plaintext()) -} - -func (r resourceOutput) plaintext() string { - return r.outputFn(r.resource) -} - -type resourceOutputFn func(input []byte, outputFn PlaintextOutputFn, r resource) resourceOutput - // CmdOutput returns a response from a resource create action formatted based on the // output flag along with an optional message based on the action. func CmdOutput(action string, outputKind string, input []byte) (string, error) { - switch action { - case "create": - return cmdOutputResource( - outputKind, - input, - func(input []byte, outputFn PlaintextOutputFn, r resource) resourceOutput { - return resourceOutput{ - input: input, - outputFn: SingularPlaintextOutputFn, - resource: r, - successMessage: "Successfully created", - } - }, - ) - case "delete": - return cmdOutputResource( - outputKind, - input, - func(input []byte, outputFn PlaintextOutputFn, r resource) resourceOutput { - return resourceOutput{ - outputFn: SingularPlaintextOutputFn, - resource: r, - successMessage: "Successfully deleted", - } - }, - ) - case "get": - return cmdOutputResource( - outputKind, - input, - func(input []byte, outputFn PlaintextOutputFn, r resource) resourceOutput { - return resourceOutput{ - input: input, - outputFn: SingularPlaintextOutputFn, - resource: r, - } - }, - ) - case "list": - var r resources - err := json.Unmarshal(input, &r) + if outputKind == "json" { + return string(input), nil + } + + var ( + maybeResource resource + maybeResources resources + isMultipleResponse bool + ) + + err := json.Unmarshal(input, &maybeResource) + _, isMultipleResponse = maybeResource["items"] + if err != nil || isMultipleResponse { + err := json.Unmarshal(input, &maybeResources) if err != nil { return "", err } + } - return outputFromKind(outputKind, MultipleOutputter{ - outputFn: MultiplePlaintextOutputFn, - resources: r, - resourceJSON: input, - }) + var successMessage string + switch action { + case "create": + successMessage = "Successfully created" + case "delete": + successMessage = "Successfully deleted" case "update": - return cmdOutputResource( - outputKind, - input, - func(input []byte, outputFn PlaintextOutputFn, r resource) resourceOutput { - return resourceOutput{ - input: input, - outputFn: SingularPlaintextOutputFn, - resource: r, - successMessage: "Successfully updated", - } - }, - ) + successMessage = "Successfully updated" default: - return "", ErrInvalidActionKind + // no success message } -} -func cmdOutputResource(outputKind string, input []byte, constructor resourceOutputFn) (string, error) { - var r resource - err := json.Unmarshal(input, &r) - if err != nil { - return "", err - } + if isMultipleResponse { + outputFn := MultiplePlaintextOutputFn + if _, ok := maybeResources.Items[0]["email"]; ok { + outputFn = MultipleEmailPlaintextOutputFn + } + + items := make([]string, 0, len(maybeResources.Items)) + for _, i := range maybeResources.Items { + items = append(items, outputFn(i)) + } - o := constructor(input, SingularPlaintextOutputFn, r) + if successMessage != "" { + return fmt.Sprintf("%s %s", successMessage, strings.Join(items, "\n")), nil + } + + return strings.Join(items, "\n"), nil + } - switch outputKind { - case "json": - return o.JSON(), nil - case "plaintext": - return o.String(), nil + if successMessage != "" { + return fmt.Sprintf("%s %s", successMessage, SingularPlaintextOutputFn(maybeResource)), nil } - return "", ErrInvalidOutputKind + return SingularPlaintextOutputFn(maybeResource), nil } diff --git a/internal/output/resource_output_test.go b/internal/output/resource_output_test.go index f770601c..06000e7c 100644 --- a/internal/output/resource_output_test.go +++ b/internal/output/resource_output_test.go @@ -37,6 +37,70 @@ func TestCmdOutput(t *testing.T) { }) }) + t.Run("when creating multiple resources", func(t *testing.T) { + input := `{ + "items": [ + { + "key": "test-key", + "name": "test-name", + "other": "other-value" + } + ] + }` + + t.Run("with json output", func(t *testing.T) { + t.Run("returns the JSON", func(t *testing.T) { + result, err := output.CmdOutput("create", "json", []byte(input)) + + require.NoError(t, err) + assert.JSONEq(t, input, result) + }) + }) + + t.Run("with plaintext output", func(t *testing.T) { + t.Run("returns a success message", func(t *testing.T) { + expected := "Successfully created * test-name (test-key)" + + result, err := output.CmdOutput("create", "plaintext", []byte(input)) + + require.NoError(t, err) + assert.Equal(t, expected, result) + }) + }) + }) + + t.Run("when creating multiple with an email instead of a key", func(t *testing.T) { + input := `{ + "items": [ + { + "_id": "test-id", + "email": "test-email", + "other": "other-value" + } + ] + }` + + t.Run("with json output", func(t *testing.T) { + t.Run("returns the JSON", func(t *testing.T) { + result, err := output.CmdOutput("create", "json", []byte(input)) + + require.NoError(t, err) + assert.JSONEq(t, input, result) + }) + }) + + t.Run("with plaintext output", func(t *testing.T) { + t.Run("returns a success message", func(t *testing.T) { + expected := "Successfully created * test-email (test-id)" + + result, err := output.CmdOutput("create", "plaintext", []byte(input)) + + require.NoError(t, err) + assert.Equal(t, expected, result) + }) + }) + }) + t.Run("when deleting a resource", func(t *testing.T) { input := `{ "key": "test-key", @@ -45,7 +109,7 @@ func TestCmdOutput(t *testing.T) { t.Run("with json output", func(t *testing.T) { t.Run("does not return anything", func(t *testing.T) { - result, err := output.CmdOutput("delete", "json", []byte(input)) + result, err := output.CmdOutput("delete", "json", []byte("")) require.NoError(t, err) assert.Equal(t, "", result) From 72311079c652ea1068d7bd7e20096ef73916b051 Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Mon, 29 Apr 2024 11:15:54 -0700 Subject: [PATCH 6/8] Add comments --- internal/output/resource_output.go | 16 +++++++++------- internal/output/resource_output_test.go | 4 ++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/internal/output/resource_output.go b/internal/output/resource_output.go index 49b48333..f4882ac8 100644 --- a/internal/output/resource_output.go +++ b/internal/output/resource_output.go @@ -19,6 +19,7 @@ func CmdOutput(action string, outputKind string, input []byte) (string, error) { isMultipleResponse bool ) + // unmarshal either a singular resource or a list of them err := json.Unmarshal(input, &maybeResource) _, isMultipleResponse = maybeResource["items"] if err != nil || isMultipleResponse { @@ -41,6 +42,7 @@ func CmdOutput(action string, outputKind string, input []byte) (string, error) { } if isMultipleResponse { + // the response could have various properties we want to show outputFn := MultiplePlaintextOutputFn if _, ok := maybeResources.Items[0]["email"]; ok { outputFn = MultipleEmailPlaintextOutputFn @@ -51,16 +53,16 @@ func CmdOutput(action string, outputKind string, input []byte) (string, error) { items = append(items, outputFn(i)) } - if successMessage != "" { - return fmt.Sprintf("%s %s", successMessage, strings.Join(items, "\n")), nil - } - - return strings.Join(items, "\n"), nil + return plaintextOutput("\n"+strings.Join(items, "\n"), successMessage), nil } + return plaintextOutput(SingularPlaintextOutputFn(maybeResource), successMessage), nil +} + +func plaintextOutput(out string, successMessage string) string { if successMessage != "" { - return fmt.Sprintf("%s %s", successMessage, SingularPlaintextOutputFn(maybeResource)), nil + return fmt.Sprintf("%s %s", successMessage, out) } - return SingularPlaintextOutputFn(maybeResource), nil + return out } diff --git a/internal/output/resource_output_test.go b/internal/output/resource_output_test.go index 06000e7c..e4a7e561 100644 --- a/internal/output/resource_output_test.go +++ b/internal/output/resource_output_test.go @@ -59,7 +59,7 @@ func TestCmdOutput(t *testing.T) { t.Run("with plaintext output", func(t *testing.T) { t.Run("returns a success message", func(t *testing.T) { - expected := "Successfully created * test-name (test-key)" + expected := "Successfully created \n* test-name (test-key)" result, err := output.CmdOutput("create", "plaintext", []byte(input)) @@ -91,7 +91,7 @@ func TestCmdOutput(t *testing.T) { t.Run("with plaintext output", func(t *testing.T) { t.Run("returns a success message", func(t *testing.T) { - expected := "Successfully created * test-email (test-id)" + expected := "Successfully created \n* test-email (test-id)" result, err := output.CmdOutput("create", "plaintext", []byte(input)) From ae363fdbb2adbc49ef08b206ca64184b8cfbca17 Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Mon, 29 Apr 2024 11:24:29 -0700 Subject: [PATCH 7/8] Remove unused var --- internal/output/output.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/internal/output/output.go b/internal/output/output.go index 8dade9c8..817fced6 100644 --- a/internal/output/output.go +++ b/internal/output/output.go @@ -6,10 +6,7 @@ import ( "ldcli/internal/errors" ) -var ( - ErrInvalidActionKind = errors.NewError("action is invalid") - ErrInvalidOutputKind = errors.NewError("output is invalid") -) +var ErrInvalidOutputKind = errors.NewError("output is invalid") type OutputKind string From 6f590842499566a59a67da75c8eae019763ee870 Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Mon, 29 Apr 2024 11:25:40 -0700 Subject: [PATCH 8/8] Remove unused code --- internal/output/output.go | 16 ---------------- internal/output/output_test.go | 27 --------------------------- 2 files changed, 43 deletions(-) diff --git a/internal/output/output.go b/internal/output/output.go index 817fced6..04ee0717 100644 --- a/internal/output/output.go +++ b/internal/output/output.go @@ -76,22 +76,6 @@ func CmdOutputSingular(outputKind string, input []byte, fn PlaintextOutputFn) (s }) } -// CmdOutputMultiple builds a command response based on the flag the user provided and the shape of -// the input. The expected shape is a list of JSON objects. -func CmdOutputMultiple(outputKind string, input []byte, fn PlaintextOutputFn) (string, error) { - var r resources - err := json.Unmarshal(input, &r) - if err != nil { - return "", err - } - - return outputFromKind(outputKind, MultipleOutputter{ - outputFn: fn, - resources: r, - resourceJSON: input, - }) -} - func outputFromKind(outputKind string, o Outputter) (string, error) { switch outputKind { case "json": diff --git a/internal/output/output_test.go b/internal/output/output_test.go index cf101dc4..12d58dee 100644 --- a/internal/output/output_test.go +++ b/internal/output/output_test.go @@ -64,30 +64,3 @@ func TestCmdOutputSingular(t *testing.T) { }) } } - -func TestCmdOutputMultiple(t *testing.T) { - tests := map[string]struct { - expected string - fn output.PlaintextOutputFn - input string - }{ - "with multiple items": { - expected: "* test-name1 (test-key1)\n* test-name2 (test-key2)", - fn: output.MultiplePlaintextOutputFn, - input: `{"items": [{"key": "test-key1", "name": "test-name1"}, {"key": "test-key2", "name": "test-name2"}]}`, - }, - } - for name, tt := range tests { - tt := tt - t.Run(name, func(t *testing.T) { - output, err := output.CmdOutputMultiple( - "plaintext", - []byte(tt.input), - tt.fn, - ) - - require.NoError(t, err) - assert.Equal(t, tt.expected, output) - }) - } -}