diff --git a/cmd/get/get.go b/cmd/get/get.go index d8c61ea..049826d 100644 --- a/cmd/get/get.go +++ b/cmd/get/get.go @@ -4,6 +4,7 @@ import ( "github.com/spf13/cobra" "golang.org/x/net/context" "openhue-cli/openhue" + "openhue-cli/openhue/gen" "openhue-cli/util" ) @@ -14,7 +15,7 @@ type GetFlags struct { var GetConfig GetFlags // NewCmdGet returns an initialized Command instance for 'get' sub command -func NewCmdGet(api *openhue.ClientWithResponses) *cobra.Command { +func NewCmdGet(ctx *openhue.Context) *cobra.Command { cmd := &cobra.Command{ Use: "get", Short: "Display one or many resources", @@ -23,7 +24,7 @@ func NewCmdGet(api *openhue.ClientWithResponses) *cobra.Command { Retrieve information for any kind of resources exposed by your Hue Bridge: lights, rooms, scenes, etc. `, Run: func(cmd *cobra.Command, args []string) { - resp, err := api.GetResourcesWithResponse(context.Background()) + resp, err := ctx.Api.GetResourcesWithResponse(context.Background()) cobra.CheckErr(err) resources := *(*resp.JSON200).Data @@ -33,7 +34,7 @@ Retrieve information for any kind of resources exposed by your Hue Bridge: light // filter resources by type n := 0 for _, r := range resources { - if *r.Type == openhue.ResourceGetType(typeFlag) { + if *r.Type == gen.ResourceGetType(typeFlag) { resources[n] = r n++ } @@ -42,9 +43,9 @@ Retrieve information for any kind of resources exposed by your Hue Bridge: light } if GetConfig.Json { - util.PrintJson(resources) + util.PrintJson(ctx.Io, resources) } else { - util.PrintTable(resources, PrintResource, "Resource ID", "Resource Type") + util.PrintTable(ctx.Io, resources, PrintResource, "Resource ID", "Resource Type") } }, } @@ -56,12 +57,12 @@ Retrieve information for any kind of resources exposed by your Hue Bridge: light cmd.PersistentFlags().BoolVar(&GetConfig.Json, "json", false, "Format output as JSON") // sub commands - cmd.AddCommand(NewCmdGetLight(api)) - cmd.AddCommand(NewCmdGetRoom(api)) + cmd.AddCommand(NewCmdGetLight(ctx)) + cmd.AddCommand(NewCmdGetRoom(ctx)) return cmd } -func PrintResource(resource openhue.ResourceGet) string { +func PrintResource(resource gen.ResourceGet) string { return *resource.Id + "\t" + string(*resource.Type) } diff --git a/cmd/get/get_light.go b/cmd/get/get_light.go index 0f7fee1..e307f4b 100644 --- a/cmd/get/get_light.go +++ b/cmd/get/get_light.go @@ -5,6 +5,7 @@ import ( "github.com/spf13/cobra" "golang.org/x/net/context" "openhue-cli/openhue" + "openhue-cli/openhue/gen" "openhue-cli/util" "os" ) @@ -34,7 +35,7 @@ func NewGetLightOptions() *LightOptions { } // NewCmdGetLight returns initialized Command instance for the 'get light' sub command -func NewCmdGetLight(api *openhue.ClientWithResponses) *cobra.Command { +func NewCmdGetLight(ctx *openhue.Context) *cobra.Command { o := NewGetLightOptions() @@ -46,7 +47,7 @@ func NewCmdGetLight(api *openhue.ClientWithResponses) *cobra.Command { Args: cobra.MatchAll(cobra.RangeArgs(0, 1), cobra.OnlyValidArgs), Run: func(cmd *cobra.Command, args []string) { o.PrepareGetRoomCmd(args) - o.RunGetLightCmd(api) + o.RunGetLightCmd(ctx) }, } @@ -59,11 +60,11 @@ func (o *LightOptions) PrepareGetRoomCmd(args []string) { } } -func (o *LightOptions) RunGetLightCmd(api *openhue.ClientWithResponses) { - var lights *[]openhue.LightGet +func (o *LightOptions) RunGetLightCmd(ctx *openhue.Context) { + var lights *[]gen.LightGet if len(o.LightId) > 0 { - resp, err := api.GetLightWithResponse(context.Background(), o.LightId) + resp, err := ctx.Api.GetLightWithResponse(context.Background(), o.LightId) cobra.CheckErr(err) if resp.JSON200 == nil { @@ -73,19 +74,19 @@ func (o *LightOptions) RunGetLightCmd(api *openhue.ClientWithResponses) { lights = (*resp.JSON200).Data } else { - resp, err := api.GetLightsWithResponse(context.Background()) + resp, err := ctx.Api.GetLightsWithResponse(context.Background()) cobra.CheckErr(err) lights = (*resp.JSON200).Data } if !GetConfig.Json { - util.PrintTable(*lights, PrintLight, "ID", "Name", "Type", "Status", "Brightness") + util.PrintTable(ctx.Io, *lights, PrintLight, "ID", "Name", "Type", "Status", "Brightness") } else { - util.PrintJsonArray(*lights) + util.PrintJsonArray(ctx.Io, *lights) } } -func PrintLight(light openhue.LightGet) string { +func PrintLight(light gen.LightGet) string { status := "[ ]" brightness := "N/A" diff --git a/cmd/get/get_room.go b/cmd/get/get_room.go index 6fef752..b92d012 100644 --- a/cmd/get/get_room.go +++ b/cmd/get/get_room.go @@ -5,6 +5,7 @@ import ( "github.com/spf13/cobra" "golang.org/x/net/context" "openhue-cli/openhue" + "openhue-cli/openhue/gen" "openhue-cli/util" "os" ) @@ -34,7 +35,7 @@ func NewGetRoomOptions() *RoomOptions { } // NewCmdGetRoom returns initialized Command instance for the 'get light' sub command -func NewCmdGetRoom(api *openhue.ClientWithResponses) *cobra.Command { +func NewCmdGetRoom(ctx *openhue.Context) *cobra.Command { o := NewGetRoomOptions() @@ -46,7 +47,7 @@ func NewCmdGetRoom(api *openhue.ClientWithResponses) *cobra.Command { Args: cobra.MatchAll(cobra.RangeArgs(0, 1), cobra.OnlyValidArgs), Run: func(cmd *cobra.Command, args []string) { o.PrepareGetRoomCmd(args) - o.RunGetRoomCmd(api) + o.RunGetRoomCmd(ctx) }, } @@ -59,11 +60,11 @@ func (o *RoomOptions) PrepareGetRoomCmd(args []string) { } } -func (o *RoomOptions) RunGetRoomCmd(api *openhue.ClientWithResponses) { - var rooms *[]openhue.RoomGet +func (o *RoomOptions) RunGetRoomCmd(ctx *openhue.Context) { + var rooms *[]gen.RoomGet if len(o.RoomId) > 0 { - resp, err := api.GetRoomWithResponse(context.Background(), o.RoomId) + resp, err := ctx.Api.GetRoomWithResponse(context.Background(), o.RoomId) cobra.CheckErr(err) if resp.JSON200 == nil { @@ -73,18 +74,18 @@ func (o *RoomOptions) RunGetRoomCmd(api *openhue.ClientWithResponses) { rooms = (*resp.JSON200).Data } else { - resp, err := api.GetRoomsWithResponse(context.Background()) + resp, err := ctx.Api.GetRoomsWithResponse(context.Background()) cobra.CheckErr(err) rooms = (*resp.JSON200).Data } if !GetConfig.Json { - util.PrintTable(*rooms, PrintRoom, "ID", "Name", "Type") + util.PrintTable(ctx.Io, *rooms, PrintRoom, "ID", "Name", "Type") } else { - util.PrintJsonArray(*rooms) + util.PrintJsonArray(ctx.Io, *rooms) } } -func PrintRoom(room openhue.RoomGet) string { +func PrintRoom(room gen.RoomGet) string { return *room.Id + "\t" + *room.Metadata.Name + "\t" + string(*room.Metadata.Archetype) } diff --git a/cmd/root.go b/cmd/root.go index b8aa06c..aac5d77 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -6,8 +6,7 @@ import ( "openhue-cli/cmd/set" "openhue-cli/cmd/setup" "openhue-cli/cmd/version" - "openhue-cli/config" - "openhue-cli/util" + "openhue-cli/openhue" ) // NewCmdOpenHue represents the `openhue` base command, AKA entry point of the CLI @@ -27,31 +26,32 @@ openhue controls your Philips Hue lighting system // Execute adds all child commands to the root command and sets flags appropriately. // This is called by main.main(). It only needs to happen once to the rootCmd. -func Execute(buildInfo *util.BuildInfo) { +func Execute(buildInfo *openhue.BuildInfo) { // load the configuration - c := config.Config{} - c.Load() + c := openhue.Config{} + c.LoadConfig() // get the API Client api := c.NewOpenHueClient() + ctx := openhue.NewContext(openhue.NewIOSteams(), buildInfo, api) // create the root command - cmd := NewCmdOpenHue() + root := NewCmdOpenHue() // init groups - initGroups(cmd) + initGroups(root) // add sub commands - cmd.AddCommand(version.NewCmdVersion(buildInfo)) - cmd.AddCommand(setup.NewCmdAuth()) - cmd.AddCommand(setup.NewCmdDiscover()) - cmd.AddCommand(setup.NewCmdConfigure()) - cmd.AddCommand(set.NewCmdSet(api)) - cmd.AddCommand(get.NewCmdGet(api)) + root.AddCommand(version.NewCmdVersion(ctx)) + root.AddCommand(setup.NewCmdAuth()) + root.AddCommand(setup.NewCmdDiscover()) + root.AddCommand(setup.NewCmdConfigure()) + root.AddCommand(set.NewCmdSet(ctx)) + root.AddCommand(get.NewCmdGet(ctx)) // execute the root command - err := cmd.Execute() + err := root.Execute() cobra.CheckErr(err) } diff --git a/cmd/set/set.go b/cmd/set/set.go index d85330d..cb6d93d 100644 --- a/cmd/set/set.go +++ b/cmd/set/set.go @@ -6,7 +6,7 @@ import ( ) // NewCmdSet returns an initialized Command instance for 'set' sub command -func NewCmdSet(api *openhue.ClientWithResponses) *cobra.Command { +func NewCmdSet(ctx *openhue.Context) *cobra.Command { cmd := &cobra.Command{ Use: "set", @@ -17,7 +17,7 @@ Set the values for a specific resource `, } - cmd.AddCommand(NewCmdSetLight(api)) + cmd.AddCommand(NewCmdSetLight(ctx)) return cmd } diff --git a/cmd/set/set_light.go b/cmd/set/set_light.go index 197928c..141d041 100644 --- a/cmd/set/set_light.go +++ b/cmd/set/set_light.go @@ -5,6 +5,7 @@ import ( "github.com/spf13/cobra" "golang.org/x/net/context" "openhue-cli/openhue" + "openhue-cli/openhue/gen" "openhue-cli/util/color" ) @@ -66,7 +67,7 @@ func NewSetLightOptions() *LightOptions { } // NewCmdSetLight returns initialized Command instance for the 'set light' sub command -func NewCmdSetLight(api *openhue.ClientWithResponses) *cobra.Command { +func NewCmdSetLight(ctx *openhue.Context) *cobra.Command { o := NewSetLightOptions() f := LightFlags{} @@ -79,7 +80,7 @@ func NewCmdSetLight(api *openhue.ClientWithResponses) *cobra.Command { Args: cobra.MatchAll(cobra.RangeArgs(1, 10), cobra.OnlyValidArgs), Run: func(cmd *cobra.Command, args []string) { cobra.CheckErr(o.PrepareCmdSetLight(&f)) - cobra.CheckErr(o.RunCmdSetLight(api, args)) + cobra.CheckErr(o.RunCmdSetLight(ctx.Api, args)) }, } @@ -168,25 +169,25 @@ func (o *LightOptions) PrepareCmdSetLight(flags *LightFlags) error { } // RunCmdSetLight executes the light update command logic -func (o *LightOptions) RunCmdSetLight(api *openhue.ClientWithResponses, args []string) error { +func (o *LightOptions) RunCmdSetLight(api *gen.ClientWithResponses, args []string) error { - request := &openhue.UpdateLightJSONRequestBody{} + request := &gen.UpdateLightJSONRequestBody{} if o.Status != Undefined { - request.On = &openhue.On{ + request.On = &gen.On{ On: ToBool(o.Status), } } if o.Brightness >= 0 && o.Brightness <= 100.0 { - request.Dimming = &openhue.Dimming{ + request.Dimming = &gen.Dimming{ Brightness: &o.Brightness, } } if o.Color != color.UndefinedColor { - request.Color = &openhue.Color{ - Xy: &openhue.GamutPosition{ + request.Color = &gen.Color{ + Xy: &gen.GamutPosition{ X: &o.Color.X, Y: &o.Color.Y, }, diff --git a/cmd/setup/auth.go b/cmd/setup/auth.go index 8df4d0d..663b87c 100644 --- a/cmd/setup/auth.go +++ b/cmd/setup/auth.go @@ -4,8 +4,8 @@ import ( "context" "fmt" "github.com/spf13/cobra" - "openhue-cli/config" "openhue-cli/openhue" + "openhue-cli/openhue/gen" "os" ) @@ -25,9 +25,9 @@ func NewCmdAuth() *cobra.Command { Long: `Authenticate to retrieve the Hue Application Key. Requires to go and press the button on the bridge`, Run: func(cmd *cobra.Command, args []string) { - client := config.NewOpenHueClientNoAuth(bridge) + client := openhue.NewOpenHueClientNoAuth(bridge) - body := new(openhue.AuthenticateJSONRequestBody) + body := new(gen.AuthenticateJSONRequestBody) body.Devicetype = &deviceType body.Generateclientkey = &generateClientKey resp, err := client.AuthenticateWithResponse(context.Background(), *body) diff --git a/cmd/version/version.go b/cmd/version/version.go index 02329f3..33fac58 100644 --- a/cmd/version/version.go +++ b/cmd/version/version.go @@ -3,10 +3,14 @@ package version import ( "fmt" "github.com/spf13/cobra" - "openhue-cli/util" + "openhue-cli/openhue" ) -func NewCmdVersion(buildInfo *util.BuildInfo) *cobra.Command { +const ( + BaseCommitUrl = "https://github.com/openhue/openhue-cli/commit/" +) + +func NewCmdVersion(ctx *openhue.Context) *cobra.Command { cmd := &cobra.Command{ Use: "version", @@ -18,12 +22,11 @@ openhue version `, Run: func(cmd *cobra.Command, args []string) { - fmt.Println("\n# Version\t", buildInfo.Version) - fmt.Println("# Commit\t", buildInfo.Commit) - fmt.Println("# Time\t", buildInfo.Date) + fmt.Fprintln(ctx.Io.Out, "\n# Version\t", ctx.BuildInfo.Version) + fmt.Fprintln(ctx.Io.Out, "# Commit\t", BaseCommitUrl+ctx.BuildInfo.Commit) + fmt.Fprintln(ctx.Io.Out, "# Built at\t", ctx.BuildInfo.Date) }, } return cmd - } diff --git a/cmd/version/version_test.go b/cmd/version/version_test.go new file mode 100644 index 0000000..4d8b99a --- /dev/null +++ b/cmd/version/version_test.go @@ -0,0 +1,32 @@ +package version + +import ( + "openhue-cli/openhue" + "openhue-cli/openhue/test" + "strings" + "testing" +) + +const ( + Line1 = "# Version\t 1.0.0" + Line2 = "# Commit\t https://github.com/openhue/openhue-cli/commit/1234" + Line3 = "# Built at\t now" +) + +func TestNewCmdVersion(t *testing.T) { + + ctx, out := openhue.NewTestContextWithoutApi() + + cmd := NewCmdVersion(ctx) + err := cmd.Execute() + if err != nil { + t.Fatalf("Failed to execute the `version` command: %s", err) + } + + lines := strings.Split(out.String(), "\n") + + test.AssertThatLineEqualsTo(t, lines, 1, Line1) + test.AssertThatLineEqualsTo(t, lines, 2, Line2) + test.AssertThatLineEqualsTo(t, lines, 3, Line3) + +} diff --git a/main.go b/main.go index 614fb2d..e63c212 100644 --- a/main.go +++ b/main.go @@ -1,10 +1,10 @@ package main -//go:generate oapi-codegen --package=openhue -generate=client,types -o ./openhue/openhue.gen.go https://api.redocly.com/registry/bundle/openhue/openhue/v2/openapi.yaml?branch=main +//go:generate oapi-codegen --package=gen -generate=client,types -o ./openhue/gen/openhue.gen.go https://api.redocly.com/registry/bundle/openhue/openhue/v2/openapi.yaml?branch=main import ( "openhue-cli/cmd" - "openhue-cli/util" + "openhue-cli/openhue" ) var ( @@ -14,5 +14,5 @@ var ( ) func main() { - cmd.Execute(util.NewBuildInfo(version, commit, date)) + cmd.Execute(openhue.NewBuildInfo(version, commit, date)) } diff --git a/util/build.go b/openhue/build.go similarity index 57% rename from util/build.go rename to openhue/build.go index 63442ee..df0e6e5 100644 --- a/util/build.go +++ b/openhue/build.go @@ -1,4 +1,4 @@ -package util +package openhue type BuildInfo struct { Version string @@ -14,3 +14,12 @@ func NewBuildInfo(version string, commit string, date string) *BuildInfo { Date: date, } } + +// NewTestBuildInfo returns a valid openhue.BuildInfo just for testing +func NewTestBuildInfo() *BuildInfo { + return &BuildInfo{ + Version: "1.0.0", + Commit: "1234", + Date: "now", + } +} diff --git a/config/config.go b/openhue/config.go similarity index 84% rename from config/config.go rename to openhue/config.go index c6791f9..4356b9c 100644 --- a/config/config.go +++ b/openhue/config.go @@ -1,4 +1,4 @@ -package config +package openhue import ( "crypto/tls" @@ -7,7 +7,7 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" "net/http" - "openhue-cli/openhue" + "openhue-cli/openhue/gen" "os" "path/filepath" "slices" @@ -20,7 +20,7 @@ type Config struct { key string } -func (c *Config) Load() { +func (c *Config) LoadConfig() { // Find home directory. home, err := os.UserHomeDir() @@ -49,14 +49,14 @@ func (c *Config) Load() { // NewOpenHueClient Creates a new NewClientWithResponses for a given server and hueApplicationKey. // This function will also skip SSL verification, as the Philips HUE Bridge exposes a self-signed certificate. -func (c *Config) NewOpenHueClient() *openhue.ClientWithResponses { +func (c *Config) NewOpenHueClient() *gen.ClientWithResponses { p, err := sp.NewSecurityProviderApiKey("header", "hue-application-key", c.key) cobra.CheckErr(err) // skip SSL Verification http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} - client, err := openhue.NewClientWithResponses("https://"+c.bridge, openhue.WithRequestEditorFn(p.Intercept)) + client, err := gen.NewClientWithResponses("https://"+c.bridge, gen.WithRequestEditorFn(p.Intercept)) cobra.CheckErr(err) return client @@ -64,12 +64,12 @@ func (c *Config) NewOpenHueClient() *openhue.ClientWithResponses { // NewOpenHueClientNoAuth Creates a new NewClientWithResponses for a given server and no application key. // This function will also skip SSL verification, as the Philips HUE Bridge exposes a self-signed certificate. -func NewOpenHueClientNoAuth(ip string) *openhue.ClientWithResponses { +func NewOpenHueClientNoAuth(ip string) *gen.ClientWithResponses { // skip SSL Verification http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} - client, err := openhue.NewClientWithResponses("https://" + ip) + client, err := gen.NewClientWithResponses("https://" + ip) cobra.CheckErr(err) return client diff --git a/openhue/context.go b/openhue/context.go new file mode 100644 index 0000000..63cff15 --- /dev/null +++ b/openhue/context.go @@ -0,0 +1,28 @@ +package openhue + +import ( + "bytes" + "openhue-cli/openhue/gen" +) + +// Context contains the common objects for the different commands. +type Context struct { + Io IOStreams + BuildInfo *BuildInfo + Api *gen.ClientWithResponses +} + +// NewContext returns an initialized Context from a given gen.ClientWithResponses API with default IOStreams +func NewContext(io IOStreams, buildInfo *BuildInfo, api *gen.ClientWithResponses) *Context { + return &Context{ + Io: io, + BuildInfo: buildInfo, + Api: api, + } +} + +func NewTestContextWithoutApi() (*Context, *bytes.Buffer) { + streams, _, out, _ := NewTestIOStreams() + ctx := NewContext(streams, NewTestBuildInfo(), nil) + return ctx, out +} diff --git a/openhue/openhue.gen.go b/openhue/gen/openhue.gen.go similarity index 99% rename from openhue/openhue.gen.go rename to openhue/gen/openhue.gen.go index fc4c18f..930c712 100644 --- a/openhue/openhue.gen.go +++ b/openhue/gen/openhue.gen.go @@ -1,7 +1,7 @@ -// Package openhue provides primitives to interact with the openapi HTTP API. +// Package gen provides primitives to interact with the openapi HTTP API. // // Code generated by github.com/deepmap/oapi-codegen version v1.15.0 DO NOT EDIT. -package openhue +package gen import ( "bytes" diff --git a/openhue/io.go b/openhue/io.go new file mode 100644 index 0000000..efbb622 --- /dev/null +++ b/openhue/io.go @@ -0,0 +1,50 @@ +package openhue + +import ( + "bytes" + "io" + "os" +) + +// IOStreams provides the standard names for io streams. +// This is useful for embedding and for unit testing. +type IOStreams struct { + // In think, os.Stdin + In io.Reader + // Out think, os.Stdout + Out io.Writer + // ErrOut think, os.Stderr + ErrOut io.Writer +} + +// NewIOSteams returns a valid IOStreams with the default os.Stdin, os.Stdout and os.Stderr thinks +func NewIOSteams() IOStreams { + return IOStreams{ + In: os.Stdin, + Out: os.Stdout, + ErrOut: os.Stderr, + } +} + +// NewTestIOStreams returns a valid IOStreams and in, out, errOut buffers for unit tests +func NewTestIOStreams() (IOStreams, *bytes.Buffer, *bytes.Buffer, *bytes.Buffer) { + in := &bytes.Buffer{} + out := &bytes.Buffer{} + errOut := &bytes.Buffer{} + + return IOStreams{ + In: in, + Out: out, + ErrOut: errOut, + }, in, out, errOut +} + +// NewTestIOStreamsDiscard returns a valid IOStreams that just discards +func NewTestIOStreamsDiscard() IOStreams { + in := &bytes.Buffer{} + return IOStreams{ + In: in, + Out: io.Discard, + ErrOut: io.Discard, + } +} diff --git a/openhue/test/assert.go b/openhue/test/assert.go new file mode 100644 index 0000000..dc99bfe --- /dev/null +++ b/openhue/test/assert.go @@ -0,0 +1,10 @@ +package test + +import "testing" + +// AssertThatLineEqualsTo verifies that an `idx` line contained in the `lines` slice should be equal to the `expected` value +func AssertThatLineEqualsTo(t *testing.T, lines []string, idx int, expected string) { + if lines[idx] != expected { + t.Fatalf("expected \"%s\", obtained \"%s\"", expected, lines[idx]) + } +} diff --git a/testing/utils.go b/openhue/test/utils.go similarity index 96% rename from testing/utils.go rename to openhue/test/utils.go index b3f4cfa..32add9f 100644 --- a/testing/utils.go +++ b/openhue/test/utils.go @@ -1,6 +1,8 @@ -package testing +package test -import "math" +import ( + "math" +) // AlmostEqual32 returns true if a and b are equal within a relative error of e. // See http://floating-point-gui.de/errors/comparison/ for the details of the applied method. diff --git a/util/color/constant_test.go b/util/color/constant_test.go index 0af629c..73dcc2d 100644 --- a/util/color/constant_test.go +++ b/util/color/constant_test.go @@ -1,7 +1,7 @@ package color import ( - test "openhue-cli/testing" + "openhue-cli/openhue/test" "testing" ) diff --git a/util/utils.go b/util/utils.go index 778c569..eb6372d 100644 --- a/util/utils.go +++ b/util/utils.go @@ -3,30 +3,30 @@ package util import ( "encoding/json" "fmt" - "os" + "openhue-cli/openhue" "text/tabwriter" ) // PrintJsonArray formats the input array as JSON and prints it. If the length of the array is equal to 1, // it will print it as a single object -func PrintJsonArray[T any](array []T) { +func PrintJsonArray[T any](streams openhue.IOStreams, array []T) { if len(array) == 1 { - PrintJson(array[0]) + PrintJson(streams, array[0]) } else { - PrintJson(array) + PrintJson(streams, array) } } -func PrintJson[T any](array T) { +func PrintJson[T any](streams openhue.IOStreams, array T) { var out []byte out, _ = json.MarshalIndent(array, "", " ") - fmt.Println(string(out)) + fmt.Fprintln(streams.Out, string(out)) } // PrintTable prints each line of the objects contained in the table value -func PrintTable[T any](table []T, lineFn func(T) string, header ...string) { +func PrintTable[T any](streams openhue.IOStreams, table []T, lineFn func(T) string, header ...string) { - w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) + w := tabwriter.NewWriter(streams.Out, 0, 0, 3, ' ', 0) for _, h := range header { _, _ = fmt.Fprint(w, h+"\t") diff --git a/util/utils_test.go b/util/utils_test.go index 69b2cdb..f66e824 100644 --- a/util/utils_test.go +++ b/util/utils_test.go @@ -1,6 +1,11 @@ package util -import "testing" +import ( + "openhue-cli/openhue" + "openhue-cli/openhue/test" + "strings" + "testing" +) type Light struct { id string @@ -15,5 +20,14 @@ func TestPrintTable(t *testing.T) { lights := []Light{{id: "1234", name: "Light 1"}, {id: "4321", name: "Light 2"}} - PrintTable(lights, PrintLightLineProcessor, "id", "name") + streams, _, out, _ := openhue.NewTestIOStreams() + + PrintTable(streams, lights, PrintLightLineProcessor, "id", "name") + + lines := strings.Split(out.String(), "\n") + + test.AssertThatLineEqualsTo(t, lines, 0, "id name ") + test.AssertThatLineEqualsTo(t, lines, 1, "---- ---- ") + test.AssertThatLineEqualsTo(t, lines, 2, "1234 Light 1") + test.AssertThatLineEqualsTo(t, lines, 3, "4321 Light 2") }