SEBA-688 implement mock gRPC server and unit test the version command
Change-Id: Ia10a8ea5b00ce5d5100f8fffbefba96f234d4b32
diff --git a/commands/command.go b/commands/command.go
index 6d975cf..4a38d5b 100644
--- a/commands/command.go
+++ b/commands/command.go
@@ -22,6 +22,7 @@
"github.com/opencord/cordctl/format"
"google.golang.org/grpc"
"gopkg.in/yaml.v2"
+ "io"
"io/ioutil"
"log"
"os"
@@ -37,6 +38,9 @@
OUTPUT_YAML
)
+// Make it easy to override output stream for testing
+var OutputStream io.Writer = os.Stdout
+
var CharReplacer = strings.NewReplacer("\\t", "\t", "\\n", "\n")
type GrpcConfigSpec struct {
@@ -55,6 +59,7 @@
Server string `yaml:"server"`
Username string `yaml:"username"`
Password string `yaml:"password"`
+ Protoset string `yaml:"protoset"`
Tls TlsConfigSpec `yaml:"tls"`
Grpc GrpcConfigSpec
}
@@ -74,6 +79,7 @@
Server string `short:"s" long:"server" default:"" value-name:"SERVER:PORT" description:"IP/Host and port of XOS"`
Username string `short:"u" long:"username" value-name:"USERNAME" default:"" description:"Username to authenticate with XOS"`
Password string `short:"p" long:"password" value-name:"PASSWORD" default:"" description:"Password to authenticate with XOS"`
+ Protoset string `long:"protoset" value-name:"FILENAME" description:"Load protobuf definitions from protoset instead of reflection api"`
Debug bool `short:"d" long:"debug" description:"Enable debug mode"`
UseTLS bool `long:"tls" description:"Use TLS"`
CACert string `long:"tlscacert" value-name:"CA_CERT_FILE" description:"Trust certs signed only by this CA"`
@@ -133,6 +139,25 @@
}
}
+ // Override from environment
+ // in particualr, for passing env vars via `go test`
+ env_server, present := os.LookupEnv("CORDCTL_SERVER")
+ if present {
+ GlobalConfig.Server = env_server
+ }
+ env_username, present := os.LookupEnv("CORDCTL_USERNAME")
+ if present {
+ GlobalConfig.Username = env_username
+ }
+ env_password, present := os.LookupEnv("CORDCTL_PASSWORD")
+ if present {
+ GlobalConfig.Password = env_password
+ }
+ env_protoset, present := os.LookupEnv("CORDCTL_PROTOSET")
+ if present {
+ GlobalConfig.Protoset = env_protoset
+ }
+
// Override from command line
if GlobalOptions.Server != "" {
GlobalConfig.Server = GlobalOptions.Server
@@ -143,6 +168,9 @@
if GlobalOptions.Password != "" {
GlobalConfig.Password = GlobalOptions.Password
}
+ if GlobalOptions.Protoset != "" {
+ GlobalConfig.Protoset = GlobalOptions.Protoset
+ }
// Generate error messages for required settings
if GlobalConfig.Server == "" {
@@ -165,19 +193,19 @@
if result != nil && result.Data != nil {
if result.OutputAs == OUTPUT_TABLE {
tableFormat := format.Format(result.Format)
- tableFormat.Execute(os.Stdout, true, result.Data)
+ tableFormat.Execute(OutputStream, true, result.Data)
} else if result.OutputAs == OUTPUT_JSON {
asJson, err := json.Marshal(&result.Data)
if err != nil {
panic(err)
}
- fmt.Printf("%s", asJson)
+ fmt.Fprintf(OutputStream, "%s", asJson)
} else if result.OutputAs == OUTPUT_YAML {
asYaml, err := yaml.Marshal(&result.Data)
if err != nil {
panic(err)
}
- fmt.Printf("%s", asYaml)
+ fmt.Fprintf(OutputStream, "%s", asYaml)
}
}
}
diff --git a/commands/common.go b/commands/common.go
index 9810300..f07e705 100644
--- a/commands/common.go
+++ b/commands/common.go
@@ -47,7 +47,19 @@
refClient := grpcreflect.NewClient(context.Background(), reflectpb.NewServerReflectionClient(conn))
defer refClient.Reset()
- descriptor := grpcurl.DescriptorSourceFromServer(context.Background(), refClient)
+ // Intended method of use is to download the protos via reflection API. Loading the
+ // protos from a file is supported for unit testing, as the mock server does not
+ // support the reflection API.
+
+ var descriptor grpcurl.DescriptorSource
+ if GlobalConfig.Protoset != "" {
+ descriptor, err = grpcurl.DescriptorSourceFromProtoSets(GlobalConfig.Protoset)
+ if err != nil {
+ return nil, nil, err
+ }
+ } else {
+ descriptor = grpcurl.DescriptorSourceFromServer(context.Background(), refClient)
+ }
return conn, descriptor, nil
}
diff --git a/commands/version.go b/commands/version.go
index 1941589..38d2f7a 100644
--- a/commands/version.go
+++ b/commands/version.go
@@ -101,26 +101,20 @@
const DefaultFormat = ClientFormat + ServerFormat
func (options *VersionOpts) Execute(args []string) error {
-
if !options.ClientOnly {
- conn, err := NewConnection()
+ conn, descriptor, err := InitReflectionClient()
if err != nil {
return err
}
defer conn.Close()
- descriptor, method, err := GetReflectionMethod(conn, "xos.utility.GetVersion")
- if err != nil {
- return err
- }
-
ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
defer cancel()
headers := GenerateHeaders()
h := &RpcEventHandler{}
- err = grpcurl.InvokeRPC(ctx, descriptor, conn, method, headers, h, h.GetParams)
+ err = grpcurl.InvokeRPC(ctx, descriptor, conn, "xos.utility.GetVersion", headers, h, h.GetParams)
if err != nil {
return err
}
diff --git a/commands/version_test.go b/commands/version_test.go
new file mode 100644
index 0000000..1ebe7c3
--- /dev/null
+++ b/commands/version_test.go
@@ -0,0 +1,100 @@
+/*
+ * Portions copyright 2019-present Open Networking Foundation
+ * Original copyright 2019-present Ciena Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package commands
+
+import (
+ "bytes"
+ "fmt"
+ "github.com/opencord/cordctl/testutils"
+ "os"
+ "testing"
+)
+
+func TestVersionClientOnly(t *testing.T) {
+ expected := "" +
+ "Client:\n" +
+ " Version unknown-version\n" +
+ " Go version: unknown-goversion\n" +
+ " Git commit: unknown-gitcommit\n" +
+ " Git dirty: unknown-gitdirty\n" +
+ " Built: unknown-buildtime\n" +
+ " OS/Arch: unknown-os/unknown-arch\n" +
+ "\n"
+
+ got := new(bytes.Buffer)
+ OutputStream = got
+
+ var options VersionOpts
+ options.ClientOnly = true
+ err := options.Execute([]string{})
+
+ if err != nil {
+ t.Errorf("%s: Received error %v", t.Name(), err)
+ return
+ }
+
+ if got.String() != expected {
+ t.Logf("RECEIVED:\n%s\n", got.String())
+ t.Logf("EXPECTED:\n%s\n", expected)
+ t.Errorf("%s: expected and received did not match", t.Name())
+ }
+}
+
+func TestVersionClientAndServer(t *testing.T) {
+ expected := "" +
+ "Client:\n" +
+ " Version unknown-version\n" +
+ " Go version: unknown-goversion\n" +
+ " Git commit: unknown-gitcommit\n" +
+ " Git dirty: unknown-gitdirty\n" +
+ " Built: unknown-buildtime\n" +
+ " OS/Arch: unknown-os/unknown-arch\n" +
+ "\n" +
+ "Server:\n" +
+ " Version 3.2.6\n" +
+ " Python version: 2.7.16 (default, May 6 2019, 19:35:26)\n" +
+ " Git commit: b0df1bf6ed1698285eda6a6725c5da0c80aa4aee\n" +
+ " Built: 2019-05-20T17:04:14Z\n" +
+ " OS/Arch: linux/x86_64\n" +
+ "\n"
+
+ got := new(bytes.Buffer)
+ OutputStream = got
+
+ var options VersionOpts
+ err := options.Execute([]string{})
+
+ if err != nil {
+ t.Errorf("%s: Received error %v", t.Name(), err)
+ return
+ }
+
+ if got.String() != expected {
+ t.Logf("RECEIVED:\n%s\n", got.String())
+ t.Logf("EXPECTED:\n%s\n", expected)
+ t.Errorf("%s: expected and received did not match", t.Name())
+ }
+}
+
+func TestMain(m *testing.M) {
+ err := testutils.StartMockServer("data.json")
+ if err != nil {
+ fmt.Printf("Error when initializing mock server %v", err)
+ os.Exit(-1)
+ }
+ os.Exit(m.Run())
+}