Skip to content

Commit

Permalink
integration test + cleanup outut
Browse files Browse the repository at this point in the history
  • Loading branch information
jcodybaker committed Feb 5, 2024
1 parent 8508e1b commit 0be1629
Show file tree
Hide file tree
Showing 10 changed files with 319 additions and 16 deletions.
7 changes: 4 additions & 3 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cmd

import (
"context"
"encoding/json"
"errors"
"fmt"
"os"
Expand Down Expand Up @@ -33,7 +34,7 @@ func init() {
rootCmd.Help()
}
rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "warn", "threshold for outputing logs: trace, debug, info, warn, error, fatal, panic")
rootCmd.PersistentFlags().StringVarP(&outputFormat, "output-format", "o", "text", "desired output format: json, text, log")
rootCmd.PersistentFlags().StringVarP(&outputFormat, "output-format", "o", "text", "desired output format: json, min-json, yaml, text, log")

rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
Expand Down Expand Up @@ -81,6 +82,6 @@ func Execute() {
}
}

func Output(ctx context.Context, msg, field string, f any) error {
return activeOutputter(ctx, msg, field, f)
func Output(ctx context.Context, msg, field string, f any, raw json.RawMessage) error {
return activeOutputter(ctx, msg, field, f, raw)
}
3 changes: 2 additions & 1 deletion cmd/shelly.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func init() {
}
ll.Info().Any("request_body", req).Str("method", req.Method()).Msg("sending request")
resp := req.NewResponse()
_, err = shelly.Do(ctx, conn, d.AuthCallback(ctx), req, resp)
raw, err := shelly.Do(ctx, conn, d.AuthCallback(ctx), req, resp)
if err != nil {
return fmt.Errorf("executing %s: %w", req.Method(), err)
}
Expand All @@ -69,6 +69,7 @@ func init() {
fmt.Sprintf("Response to %s command for %s", req.Method(), d.BestName()),
"response",
resp,
raw.Response,
)
}
return nil
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.21.3
require (
github.com/go-logr/zerologr v1.2.3
github.com/hashicorp/mdns v1.0.5
github.com/jcodybaker/go-shelly v0.0.0-20240129013438-f78ef39340b5
github.com/jcodybaker/go-shelly v0.0.0-20240205025506-cf7de7b6cbf3
github.com/mongoose-os/mos v0.0.0-20230313140341-b44964e63a92
github.com/prometheus/client_golang v1.18.0
github.com/rs/zerolog v1.31.0
Expand Down Expand Up @@ -68,6 +68,7 @@ require (
github.com/spf13/cast v1.6.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/tinygo-org/cbgo v0.0.4 // indirect
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 // indirect
go.opencensus.io v0.24.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,10 @@ github.com/jcodybaker/go-shelly v0.0.0-20240129011241-4d585546b813 h1:kpVAdNxk4H
github.com/jcodybaker/go-shelly v0.0.0-20240129011241-4d585546b813/go.mod h1:EfKnkqHSomR+wV7AoVgv6wU+kz1Xm4RSaEKaWMKWgWg=
github.com/jcodybaker/go-shelly v0.0.0-20240129013438-f78ef39340b5 h1:iL7n3gCzkNlClPBPuKd/51gLELI7jwEC6d0+BUqw20k=
github.com/jcodybaker/go-shelly v0.0.0-20240129013438-f78ef39340b5/go.mod h1:EfKnkqHSomR+wV7AoVgv6wU+kz1Xm4RSaEKaWMKWgWg=
github.com/jcodybaker/go-shelly v0.0.0-20240205023821-52dd211a5e6f h1:/eO2ZIjuibDRCqP0u0vK2DB7PYfnInhgsV4H7ZalXmY=
github.com/jcodybaker/go-shelly v0.0.0-20240205023821-52dd211a5e6f/go.mod h1:EfKnkqHSomR+wV7AoVgv6wU+kz1Xm4RSaEKaWMKWgWg=
github.com/jcodybaker/go-shelly v0.0.0-20240205025506-cf7de7b6cbf3 h1:TuMjUPXvwQIw8T/s9cyUMspK7U9usPf7pfb7hM0D4hc=
github.com/jcodybaker/go-shelly v0.0.0-20240205025506-cf7de7b6cbf3/go.mod h1:EfKnkqHSomR+wV7AoVgv6wU+kz1Xm4RSaEKaWMKWgWg=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
Expand Down Expand Up @@ -848,6 +852,8 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:
github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
Expand Down
57 changes: 57 additions & 0 deletions integration_test/helpers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package integrationtest

import (
"bytes"
"context"
"encoding/json"
"errors"
"os/exec"
"strings"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/yalp/jsonpath"
)

func run(ctx context.Context, t *testing.T, wDevice bool, args ...string) (out, logs string, exitCode int) {
if wDevice {
// echo-min-json roundtrips the response through the parser so we can also verify the parsing.
args = append([]string{"--host", iTest.uri, "-o", "echo-min-json"}, args...)
}
t.Logf("Running: %s %s", iTest.binPath, strings.Join(args, " "))
cmd := exec.CommandContext(iTest.ctx, iTest.binPath, args...)
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
var eErr *exec.ExitError
if errors.As(err, &eErr) {
return stdout.String(), stderr.String(), eErr.ExitCode()
}
t.Fatalf("cmd %s %s err: %v", iTest.binPath, strings.Join(args, " "), err)
}
return stdout.String(), stderr.String(), 0
}

func jsonGet(t *testing.T, actual, path string) any {
t.Helper()
var data any
err := json.Unmarshal([]byte(actual), &data)
require.NoError(t, err)
v, err := jsonpath.Read(data, path)
require.NoError(t, err)
return v
}

func jsonAssertEqual(t *testing.T, actual, path string, expect any, msg ...any) {
t.Helper()
v := jsonGet(t, actual, path)
assert.Equal(t, expect, v, msg...)
}

func jsonAssertExists(t *testing.T, actual, path string, msg ...any) {
t.Helper()
v := jsonGet(t, actual, path)
assert.NotNil(t, v, msg...)
}
117 changes: 117 additions & 0 deletions integration_test/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package integrationtest

import (
"context"
"flag"
"net/url"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"testing"

"github.com/rs/zerolog/log"

"github.com/jcodybaker/go-shelly"
"github.com/mongoose-os/mos/common/mgrpc"
)

func init() {
flag.StringVar(&iTest.uri, "device-uri", "", "device for test")
iTest.ctx = context.Background()
}

var iTest = struct {
ctx context.Context
uri string
srcPath string
binDir string
binPath string
deviceInfo *shelly.ShellyGetDeviceInfoResponse
spec shelly.DeviceSpecs
}{}

func TestMain(m *testing.M) {
if !flag.Parsed() {
flag.Parse()
}
defer cleanup()
build()
cleanupURI()
getDeviceInfo()
os.Exit(m.Run())
}

func cleanupURI() {
if iTest.uri == "" {
log.Fatal().Msg("the -device-uri parameter is required")
}
var u *url.URL
if !strings.Contains(iTest.uri, "://") {
iTest.uri = "http://" + iTest.uri
}
u, err := url.Parse(iTest.uri)
if err != nil {
log.Fatal().Err(err).Msg("parsing device-uri parameter")
}
log.Info().Str("uri.path", u.Path).Msg("using URI path")
if u.Path == "" {
u.Path = "/rpc"
}
iTest.uri = u.String()
log.Info().Str("uri", iTest.uri).Msg("using URI")
}

func getDeviceInfo() {
ctx := context.Background()
c, err := mgrpc.New(ctx, iTest.uri, mgrpc.UseHTTPPost())
if err != nil {
log.Fatal().Err(err).Msg("establishing connection to test device")
}
defer c.Disconnect(ctx)

iTest.deviceInfo, _, err = (&shelly.ShellyGetDeviceInfoRequest{}).Do(ctx, c, nil)
if err != nil {
log.Fatal().Err(err).Msg("requesting device info")
}

iTest.spec, err = shelly.AppToDeviceSpecs(iTest.deviceInfo.App, iTest.deviceInfo.Profile)
if err != nil {
log.Fatal().Err(err).Msg("resolving device specs")
}
}

func build() {
_, file, _, ok := runtime.Caller(0)
if !ok {
log.Fatal().Msg("finding path of src for test")
}
dir := filepath.Dir(file)
splitPath := strings.Split(dir, string(filepath.Separator))
if len(splitPath) < 2 {
log.Fatal().Strs("path", splitPath).Msg("src directory was improbably short")
}
last := splitPath[len(splitPath)-1]
if last != "integration_test" {
log.Fatal().Msg("src directory has wrong format")
}
iTest.srcPath = string(filepath.Separator) + filepath.Join(splitPath[0:len(splitPath)-1]...)
var err error
iTest.binDir, err = os.MkdirTemp("", "shellyctl")
if err != nil {
log.Fatal().Err(err).Msg("making temp dir for binary")
}
iTest.binPath = filepath.Join(iTest.binDir, "shellyctl")
cmd := exec.CommandContext(iTest.ctx, "go", "build", "-o", iTest.binPath, iTest.srcPath)
out, err := cmd.CombinedOutput()
if err != nil {
log.Fatal().Err(err).Str("out", string(out)).Msg("making temp dir for binary")
}
}

func cleanup() {
if iTest.binDir != "" {
os.RemoveAll(iTest.binDir)
}
}
72 changes: 72 additions & 0 deletions integration_test/shelly_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package integrationtest

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestShellyGetDeviceInfo(t *testing.T) {
out, logs, exit := run(iTest.ctx, t, true, "shelly", "get-device-info")
require.Equal(t, 0, exit)
t.Log(out)
t.Log(logs)
t.Logf("cmd exited: %d", exit)
jsonAssertEqual(t, out, "$.auth_en", false)
}

func TestShellyGetStatus(t *testing.T) {
out, logs, exit := run(iTest.ctx, t, true, "shelly", "get-status")
require.Equal(t, 0, exit)
t.Log(out)
t.Log(logs)
t.Logf("cmd exited: %d", exit)
jsonAssertExists(t, out, "$.sys")
jsonAssertExists(t, out, "$.cloud")
if iTest.spec.Ethernet {
jsonAssertExists(t, out, "$.eth")
}
jsonAssertExists(t, out, "$.wifi")
jsonAssertExists(t, out, "$.ble")
jsonAssertExists(t, out, "$.mqtt")
if iTest.spec.Switches > 0 {
jsonAssertExists(t, out, "$.switches")
}
if iTest.spec.Covers > 0 {
jsonAssertExists(t, out, "$.covers")
}
if iTest.spec.Inputs > 0 {
jsonAssertExists(t, out, "$.inputs")
}
if iTest.spec.Lights > 0 {
jsonAssertExists(t, out, "$.lights")
}
}

func TestShellyGetConfig(t *testing.T) {
out, logs, exit := run(iTest.ctx, t, true, "shelly", "get-config")
require.Equal(t, 0, exit)
t.Log(out)
t.Log(logs)
t.Logf("cmd exited: %d", exit)
jsonAssertExists(t, out, "$.sys")
jsonAssertExists(t, out, "$.cloud")
if iTest.spec.Ethernet {
jsonAssertExists(t, out, "$.eth")
}
jsonAssertExists(t, out, "$.wifi")
jsonAssertExists(t, out, "$.ble")
jsonAssertExists(t, out, "$.mqtt")
if iTest.spec.Switches > 0 {
jsonAssertExists(t, out, "$.switches")
}
if iTest.spec.Covers > 0 {
jsonAssertExists(t, out, "$.covers")
}
if iTest.spec.Inputs > 0 {
jsonAssertExists(t, out, "$.inputs")
}
if iTest.spec.Lights > 0 {
jsonAssertExists(t, out, "$.lights")
}
}
2 changes: 1 addition & 1 deletion pkg/discovery/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func (d *Device) resolveSpecs(ctx context.Context) error {
if err != nil {
return fmt.Errorf("requesting device info for spec resolve: %w", err)
}
d.Specs, err = shelly.MDNSAppToDeviceSpecs(resp.App, resp.Profile)
d.Specs, err = shelly.AppToDeviceSpecs(resp.App, resp.Profile)
if err != nil {
return fmt.Errorf("resolving device info to spec: %w", err)
}
Expand Down
4 changes: 3 additions & 1 deletion pkg/gencobra/gencobra.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,17 @@ func RequestToCmd(req shelly.RPCRequestBody, baggage *Baggage) (*cobra.Command,
}
}()
resp := req.NewResponse()
_, err = shelly.Do(ctx, conn, d.AuthCallback(ctx), req, resp)
raw, err := shelly.Do(ctx, conn, d.AuthCallback(ctx), req, resp)
if err != nil {
return fmt.Errorf("executing %s: %w", req.Method(), err)
}
ll.Debug().RawJSON("raw_response", raw.Response).Msg("got raw response")
baggage.Output(
ctx,
fmt.Sprintf("Response to %s command for %s", req.Method(), d.BestName()),
"response",
resp,
raw.Response,
)
}
return nil
Expand Down
Loading

0 comments on commit 0be1629

Please sign in to comment.