summaryrefslogtreecommitdiffstats
path: root/client
diff options
context:
space:
mode:
authorJon Marius Venstad <venstad@gmail.com>2021-11-24 11:30:55 +0100
committerJon Marius Venstad <venstad@gmail.com>2021-11-24 11:30:55 +0100
commita83829df8798c40d6b8d53f08f840a967c9bc722 (patch)
treea32b7b870a313e3673374e25b8acfa50a210b4e4 /client
parentbdd3bf47dde7a047f77dc2832ab7c92ba629e54e (diff)
Revert "Merge pull request #20181 from vespa-engine/revert-20098-jonmv/vespa-cli-test-runner"
This reverts commit bdd3bf47dde7a047f77dc2832ab7c92ba629e54e, reversing changes made to a0e159641f8361ee2fc1b39836b8a8f9364e9e3d.
Diffstat (limited to 'client')
-rw-r--r--client/go/cmd/config.go6
-rw-r--r--client/go/cmd/curl.go2
-rw-r--r--client/go/cmd/document.go2
-rw-r--r--client/go/cmd/document_test.go2
-rw-r--r--client/go/cmd/helpers.go47
-rw-r--r--client/go/cmd/query.go2
-rw-r--r--client/go/cmd/query_test.go2
-rw-r--r--client/go/cmd/test.go369
-rw-r--r--client/go/cmd/test_test.go136
-rw-r--r--client/go/cmd/testdata/tests/body.json12
-rw-r--r--client/go/cmd/testdata/tests/expected-suite.out269
-rw-r--r--client/go/cmd/testdata/tests/expected.out2
-rw-r--r--client/go/cmd/testdata/tests/response.json34
-rw-r--r--client/go/cmd/testdata/tests/staging-test/not-json1
-rw-r--r--client/go/cmd/testdata/tests/system-test/foo/body.json5
-rw-r--r--client/go/cmd/testdata/tests/system-test/foo/query.json3
-rw-r--r--client/go/cmd/testdata/tests/system-test/test.json64
-rw-r--r--client/go/cmd/testdata/tests/system-test/wrong-bool-value.json15
-rw-r--r--client/go/cmd/testdata/tests/system-test/wrong-element-count.json13
-rw-r--r--client/go/cmd/testdata/tests/system-test/wrong-field-name.json15
-rw-r--r--client/go/cmd/testdata/tests/system-test/wrong-float-value.json17
-rw-r--r--client/go/cmd/testdata/tests/system-test/wrong-int-value.json15
-rw-r--r--client/go/cmd/testdata/tests/system-test/wrong-null-value.json11
-rw-r--r--client/go/cmd/testdata/tests/system-test/wrong-string-value.json19
-rw-r--r--client/go/vespa/deploy.go2
-rw-r--r--client/go/vespa/target.go18
-rw-r--r--client/go/vespa/target_test.go14
27 files changed, 1069 insertions, 28 deletions
diff --git a/client/go/cmd/config.go b/client/go/cmd/config.go
index 750664e51b1..966080dbdc2 100644
--- a/client/go/cmd/config.go
+++ b/client/go/cmd/config.go
@@ -133,10 +133,16 @@ func (c *Config) Write() error {
}
func (c *Config) CertificatePath(app vespa.ApplicationID) (string, error) {
+ if override, ok := os.LookupEnv("VESPA_CLI_DATA_PLANE_CERT_FILE"); ok {
+ return override, nil
+ }
return c.applicationFilePath(app, "data-plane-public-cert.pem")
}
func (c *Config) PrivateKeyPath(app vespa.ApplicationID) (string, error) {
+ if override, ok := os.LookupEnv("VESPA_CLI_DATA_PLANE_KEY_FILE"); ok {
+ return override, nil
+ }
return c.applicationFilePath(app, "data-plane-private-key.pem")
}
diff --git a/client/go/cmd/curl.go b/client/go/cmd/curl.go
index bd9fad1b47e..2496ddc3abc 100644
--- a/client/go/cmd/curl.go
+++ b/client/go/cmd/curl.go
@@ -46,7 +46,7 @@ $ vespa curl -t local -- -v /search/?yql=query
fatalErr(err)
return
}
- service := getService("query", 0)
+ service := getService("query", 0, "")
url := joinURL(service.BaseURL, args[len(args)-1])
rawArgs := args[:len(args)-1]
c, err := curl.RawArgs(url, rawArgs...)
diff --git a/client/go/cmd/document.go b/client/go/cmd/document.go
index cd0170684cf..84c384e701e 100644
--- a/client/go/cmd/document.go
+++ b/client/go/cmd/document.go
@@ -118,7 +118,7 @@ var documentGetCmd = &cobra.Command{
},
}
-func documentService() *vespa.Service { return getService("document", 0) }
+func documentService() *vespa.Service { return getService("document", 0, "") }
func operationOptions() vespa.OperationOptions {
return vespa.OperationOptions{
diff --git a/client/go/cmd/document_test.go b/client/go/cmd/document_test.go
index 649aca8703a..f3a5fbe9543 100644
--- a/client/go/cmd/document_test.go
+++ b/client/go/cmd/document_test.go
@@ -161,5 +161,5 @@ func assertDocumentServerError(t *testing.T, status int, errorMessage string) {
}
func documentServiceURL(client *mockHttpClient) string {
- return getService("document", 0).BaseURL
+ return getService("document", 0, "").BaseURL
}
diff --git a/client/go/cmd/helpers.go b/client/go/cmd/helpers.go
index 79ba1fcef26..89ea87f198e 100644
--- a/client/go/cmd/helpers.go
+++ b/client/go/cmd/helpers.go
@@ -6,6 +6,7 @@ package cmd
import (
"crypto/tls"
+ "encoding/json"
"fmt"
"io/ioutil"
"log"
@@ -129,19 +130,21 @@ func getTargetType() string {
return target
}
-func getService(service string, sessionOrRunID int64) *vespa.Service {
+func getService(service string, sessionOrRunID int64, cluster string) *vespa.Service {
t := getTarget()
timeout := time.Duration(waitSecsArg) * time.Second
if timeout > 0 {
log.Printf("Waiting up to %d %s for service to become available ...", color.Cyan(waitSecsArg), color.Cyan("seconds"))
}
- s, err := t.Service(service, timeout, sessionOrRunID)
+ s, err := t.Service(service, timeout, sessionOrRunID, cluster)
if err != nil {
fatalErr(err, "Invalid service: ", service)
}
return s
}
+func getEndpointsOverride() string { return os.Getenv("VESPA_CLI_ENDPOINTS") }
+
func getSystem() string { return os.Getenv("VESPA_CLI_CLOUD_SYSTEM") }
func getSystemName() string {
@@ -175,15 +178,17 @@ func getTarget() vespa.Target {
case "local":
return vespa.LocalTarget()
case "cloud":
- deployment := deploymentFromArgs()
cfg, err := LoadConfig()
if err != nil {
fatalErr(err, "Could not load config")
return nil
}
+ deployment := deploymentFromArgs()
+ endpoints := getEndpointsFromEnv()
+
var apiKey []byte = nil
apiKey, err = ioutil.ReadFile(cfg.APIKeyPath(deployment.Application.Tenant))
- if !vespa.Auth0AccessTokenEnabled() {
+ if !vespa.Auth0AccessTokenEnabled() && endpoints == nil {
if err != nil {
fatalErrHint(err, "Deployment to cloud requires an API key. Try 'vespa api-key'")
}
@@ -228,14 +233,15 @@ func getTarget() vespa.Target {
},
cfg.AuthConfigPath(),
getSystemName(),
- cloudAuth)
+ cloudAuth,
+ endpoints)
}
fatalErrHint(fmt.Errorf("Invalid target: %s", targetType), "Valid targets are 'local', 'cloud' or an URL")
return nil
}
func waitForService(service string, sessionOrRunID int64) {
- s := getService(service, sessionOrRunID)
+ s := getService(service, sessionOrRunID, "")
timeout := time.Duration(waitSecsArg) * time.Second
if timeout > 0 {
log.Printf("Waiting up to %d %s for service to become ready ...", color.Cyan(waitSecsArg), color.Cyan("seconds"))
@@ -271,3 +277,32 @@ func getDeploymentOpts(cfg *Config, pkg vespa.ApplicationPackage, target vespa.T
}
return opts
}
+
+func getEndpointsFromEnv() map[string]string {
+ endpointsString := getEndpointsOverride()
+ if endpointsString == "" {
+ return nil
+ }
+
+ var endpoints endpoints
+ urlsByCluster := make(map[string]string)
+ if err := json.Unmarshal([]byte(endpointsString), &endpoints); err != nil {
+ fatalErrHint(err, "Endpoints must be valid JSON")
+ }
+ if len(endpoints.Endpoints) == 0 {
+ fatalErr(fmt.Errorf("endpoints must be non-empty"))
+ }
+ for _, endpoint := range endpoints.Endpoints {
+ urlsByCluster[endpoint.Cluster] = endpoint.URL
+ }
+ return urlsByCluster
+}
+
+type endpoints struct {
+ Endpoints []endpoint `json:"endpoints"`
+}
+
+type endpoint struct {
+ Cluster string `json:"cluster"`
+ URL string `json:"url"`
+}
diff --git a/client/go/cmd/query.go b/client/go/cmd/query.go
index 76688438fb4..6638c275330 100644
--- a/client/go/cmd/query.go
+++ b/client/go/cmd/query.go
@@ -39,7 +39,7 @@ can be set by the syntax [parameter-name]=[value].`,
}
func query(arguments []string) {
- service := getService("query", 0)
+ service := getService("query", 0, "")
url, _ := url.Parse(service.BaseURL + "/search/")
urlQuery := url.Query()
for i := 0; i < len(arguments); i++ {
diff --git a/client/go/cmd/query_test.go b/client/go/cmd/query_test.go
index ec6c3063906..55046ae49ba 100644
--- a/client/go/cmd/query_test.go
+++ b/client/go/cmd/query_test.go
@@ -75,5 +75,5 @@ func assertQueryServiceError(t *testing.T, status int, errorMessage string) {
}
func queryServiceURL(client *mockHttpClient) string {
- return getService("query", 0).BaseURL
+ return getService("query", 0, "").BaseURL
}
diff --git a/client/go/cmd/test.go b/client/go/cmd/test.go
new file mode 100644
index 00000000000..4c6d9ec265d
--- /dev/null
+++ b/client/go/cmd/test.go
@@ -0,0 +1,369 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// vespa test command
+// Author: jonmv
+
+package cmd
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "github.com/spf13/cobra"
+ "github.com/vespa-engine/vespa/client/go/util"
+ "github.com/vespa-engine/vespa/client/go/vespa"
+ "io/ioutil"
+ "math"
+ "net/http"
+ "net/url"
+ "os"
+ "path"
+ "path/filepath"
+ "strings"
+ "time"
+)
+
+func init() {
+ rootCmd.AddCommand(testCmd)
+}
+
+// TODO: add link to test doc at cloud.vespa.ai
+var testCmd = &cobra.Command{
+ Use: "test [tests directory or test file]",
+ Short: "Run a test suite, or a single test",
+ Long: `Run a test suite, or a single test
+
+Runs all JSON test files in the specified directory, or the single JSON
+test file specified.
+
+If no directory or file is specified, the working directory is used instead.`,
+ Example: `$ vespa test src/test/application/tests/system-test
+$ vespa test src/test/application/tests/system-test/feed-and-query.json`,
+ Args: cobra.MaximumNArgs(1),
+ DisableAutoGenTag: true,
+ Run: func(cmd *cobra.Command, args []string) {
+ target := getTarget()
+ testPath := "."
+ if len(args) > 0 {
+ testPath = args[0]
+ }
+ if count, failed := runTests(testPath, target); len(failed) != 0 {
+ fmt.Fprintf(stdout, "\nFailed %d of %d tests:\n", len(failed), count)
+ for _, test := range failed {
+ fmt.Fprintln(stdout, test)
+ }
+ exitFunc(3)
+ } else if count == 0 {
+ fmt.Fprintf(stdout, "Failed to find any tests at '%v'\n", testPath)
+ exitFunc(3)
+ } else {
+ fmt.Fprintf(stdout, "%d tests completed successfully\n", count)
+ }
+ },
+}
+
+func runTests(rootPath string, target vespa.Target) (int, []string) {
+ count := 0
+ failed := make([]string, 0)
+ if stat, err := os.Stat(rootPath); err != nil {
+ fatalErr(err, "Failed reading specified test path")
+ } else if stat.IsDir() {
+ tests, err := os.ReadDir(rootPath)
+ if err != nil {
+ fatalErr(err, "Failed reading specified test directory")
+ }
+ for _, test := range tests {
+ if !test.IsDir() && filepath.Ext(test.Name()) == ".json" {
+ testPath := path.Join(rootPath, test.Name())
+ failure := runTest(testPath, target)
+ if failure != "" {
+ failed = append(failed, failure)
+ }
+ count++
+ }
+ }
+ } else if strings.HasSuffix(stat.Name(), ".json") {
+ failure := runTest(rootPath, target)
+ if failure != "" {
+ failed = append(failed, failure)
+ }
+ count++
+ }
+ return count, failed
+}
+
+// Runs the test at the given path, and returns the specified test name if the test fails
+func runTest(testPath string, target vespa.Target) string {
+ var test test
+ testBytes, err := ioutil.ReadFile(testPath)
+ if err != nil {
+ fatalErr(err, fmt.Sprintf("Failed to read test file at '%s'", testPath))
+ }
+ if err = json.Unmarshal(testBytes, &test); err != nil {
+ fatalErr(err, fmt.Sprintf("Failed to parse test file at '%s", testPath))
+ }
+
+ testName := test.Name
+ if test.Name == "" {
+ testName = testPath
+ }
+ fmt.Fprintf(stdout, "Running %s:", testName)
+
+ defaultParameters, err := getParameters(test.Defaults.ParametersRaw, path.Dir(testPath))
+ if err != nil {
+ fatalErr(err, fmt.Sprintf("Invalid default parameters for '%s'", testName))
+ }
+
+ if len(test.Assertions) == 0 {
+ fatalErr(fmt.Errorf("a test must have at least one assertion, but none were found in '%s'", testPath))
+ }
+ for i, assertion := range test.Assertions {
+ assertionName := assertion.Name
+ if assertionName == "" {
+ assertionName = fmt.Sprintf("assertion %d", i)
+ }
+ failure, err := verify(assertion, path.Dir(testPath), test.Defaults.Cluster, defaultParameters, target)
+ if err != nil {
+ fatalErr(err, fmt.Sprintf("\nError verifying %s", assertionName))
+ }
+ if failure != "" {
+ fmt.Fprintf(stdout, "\nFailed verifying %s:\n%s\n", assertionName, failure)
+ return fmt.Sprintf("%v: %v", testName, assertionName)
+ }
+ if i == 0 {
+ fmt.Fprintf(stdout, " ")
+ }
+ fmt.Fprint(stdout, ".")
+ }
+ fmt.Fprintln(stdout, " OK!")
+ return ""
+}
+
+// Asserts specified response is obtained for request, or returns a failure message, or an error if this fails
+func verify(assertion assertion, testsPath string, defaultCluster string, defaultParameters map[string]string, target vespa.Target) (string, error) {
+ requestBody, err := getBody(assertion.Request.BodyRaw, testsPath)
+ if err != nil {
+ return "", err
+ }
+
+ parameters, err := getParameters(assertion.Request.ParametersRaw, testsPath)
+ if err != nil {
+ return "", err
+ }
+ for name, value := range defaultParameters {
+ if _, present := parameters[name]; !present {
+ parameters[name] = value
+ }
+ }
+
+ cluster := assertion.Request.Cluster
+ if cluster == "" {
+ cluster = defaultCluster
+ }
+
+ service, err := target.Service("query", 0, 0, cluster)
+ if err != nil {
+ return "", err
+ }
+
+ method := assertion.Request.Method
+ if method == "" {
+ method = "GET"
+ }
+
+ pathAndQuery := assertion.Request.URI
+ if pathAndQuery == "" {
+ pathAndQuery = "/search/"
+ }
+ requestUrl, err := url.ParseRequestURI(service.BaseURL + pathAndQuery)
+ if err != nil {
+ return "", err
+ }
+ query := requestUrl.Query()
+ for name, value := range parameters {
+ query.Add(name, value)
+ }
+ requestUrl.RawQuery = query.Encode()
+
+ header := http.Header{}
+ header.Add("Content-Type", "application/json") // TODO: Not guaranteed to be true ...
+
+ request := &http.Request{
+ URL: requestUrl,
+ Method: method,
+ Header: header,
+ Body: ioutil.NopCloser(bytes.NewReader(requestBody)),
+ }
+ defer request.Body.Close()
+
+ response, err := service.Do(request, 600*time.Second) // Vespa should provide a response within the given request timeout
+ if err != nil {
+ return "", err
+ }
+ defer response.Body.Close()
+
+ statusCode := assertion.Response.Code
+ if statusCode == 0 {
+ statusCode = 200
+ }
+ if statusCode != response.StatusCode {
+ return fmt.Sprintf("Expected status code (%d) does not match actual (%d). Response body:\n%s", statusCode, response.StatusCode, util.ReaderToJSON(response.Body)), nil
+ }
+
+ responseBodySpecBytes, err := getBody(assertion.Response.BodyRaw, testsPath)
+ if err != nil {
+ return "", err
+ }
+ if responseBodySpecBytes == nil {
+ return "", nil
+ }
+ var responseBodySpec interface{}
+ err = json.Unmarshal(responseBodySpecBytes, &responseBodySpec)
+ if err != nil {
+ return "", err
+ }
+
+ responseBodyBytes, err := ioutil.ReadAll(response.Body)
+ if err != nil {
+ return "", err
+ }
+ var responseBody interface{}
+ err = json.Unmarshal(responseBodyBytes, &responseBody)
+ if err != nil {
+ return "", fmt.Errorf("got non-JSON response; %w:\n%s", err, string(responseBodyBytes))
+ }
+
+ failure, err := compare(responseBodySpec, responseBody, "")
+ if failure != "" {
+ responsePretty, _ := json.MarshalIndent(responseBody, "", " ")
+ failure = failure + " Response body:\n" + string(responsePretty)
+ }
+ return failure, err
+}
+
+func compare(expected interface{}, actual interface{}, path string) (string, error) {
+ typeMatch := false
+ valueMatch := false
+ switch u := expected.(type) {
+ case nil:
+ typeMatch = actual == nil
+ valueMatch = actual == nil
+ case bool:
+ v, ok := actual.(bool)
+ typeMatch = ok
+ valueMatch = ok && u == v
+ case float64:
+ v, ok := actual.(float64)
+ typeMatch = ok
+ valueMatch = ok && math.Abs(u-v) < 1e-9
+ case string:
+ v, ok := actual.(string)
+ typeMatch = ok
+ valueMatch = ok && (u == v)
+ case []interface{}:
+ v, ok := actual.([]interface{})
+ typeMatch = ok
+ if ok {
+ if len(u) == len(v) {
+ for i, e := range u {
+ result, err := compare(e, v[i], fmt.Sprintf("%s/%d", path, i))
+ if result != "" || err != nil {
+ return result, err
+ }
+ }
+ valueMatch = true
+ } else {
+ return fmt.Sprintf("Expected number of elements at %s (%d) does not match actual (%d).", path, len(u), len(v)), nil
+ }
+ }
+ case map[string]interface{}:
+ v, ok := actual.(map[string]interface{})
+ typeMatch = ok
+ if ok {
+ for n, e := range u {
+ childPath := fmt.Sprintf("%s/%s", path, strings.ReplaceAll(strings.ReplaceAll(n, "~", "~0"), "/", "~1"))
+ f, ok := v[n]
+ if !ok {
+ return fmt.Sprintf("Expected field at %s not present in actual data.", childPath), nil
+ }
+ result, err := compare(e, f, childPath)
+ if result != "" || err != nil {
+ return result, err
+ }
+ }
+ valueMatch = true
+ }
+ default:
+ return "", fmt.Errorf("unexpected expected JSON type for value '%v'", expected)
+ }
+
+ if !(typeMatch && valueMatch) {
+ if path == "" {
+ path = "root"
+ }
+ expectedJson, _ := json.MarshalIndent(expected, "", " ")
+ actualJson, _ := json.MarshalIndent(actual, "", " ")
+ return fmt.Sprintf("Expected JSON at %s (%s) does not match actual (%s).", path, expectedJson, actualJson), nil
+ }
+ return "", nil
+}
+
+func getParameters(parametersRaw []byte, testsPath string) (map[string]string, error) {
+ if parametersRaw != nil {
+ var parametersPath string
+ if err := json.Unmarshal(parametersRaw, &parametersPath); err == nil {
+ resolvedParametersPath := path.Join(testsPath, parametersPath)
+ parametersRaw, err = ioutil.ReadFile(resolvedParametersPath)
+ if err != nil {
+ fatalErr(err, fmt.Sprintf("Failed to read request parameters file at '%s'", resolvedParametersPath))
+ }
+ }
+ var parameters map[string]string
+ if err := json.Unmarshal(parametersRaw, &parameters); err != nil {
+ return nil, fmt.Errorf("request parameters must be JSON with only string values: %w", err)
+ }
+ return parameters, nil
+ }
+ return make(map[string]string), nil
+}
+
+func getBody(bodyRaw []byte, testsPath string) ([]byte, error) {
+ var bodyPath string
+ if err := json.Unmarshal(bodyRaw, &bodyPath); err == nil {
+ resolvedBodyPath := path.Join(testsPath, bodyPath)
+ bodyRaw, err = ioutil.ReadFile(resolvedBodyPath)
+ if err != nil {
+ fatalErr(err, fmt.Sprintf("Failed to read body file at '%s'", resolvedBodyPath))
+ }
+ }
+ return bodyRaw, nil
+}
+
+type test struct {
+ Name string `json:"name"`
+ Defaults defaults `json:"defaults"`
+ Assertions []assertion `json:"assertions"`
+}
+
+type defaults struct {
+ Cluster string `json:"cluster"`
+ ParametersRaw json.RawMessage `json:"parameters"`
+}
+
+type assertion struct {
+ Name string `json:"name"`
+ Request request `json:"request"`
+ Response response `json:"response"`
+}
+
+type request struct {
+ Cluster string `json:"cluster"`
+ Method string `json:"method"`
+ URI string `json:"uri"`
+ ParametersRaw json.RawMessage `json:"parameters"`
+ BodyRaw json.RawMessage `json:"body"`
+}
+
+type response struct {
+ Code int `json:"code"`
+ BodyRaw json.RawMessage `json:"body"`
+}
diff --git a/client/go/cmd/test_test.go b/client/go/cmd/test_test.go
new file mode 100644
index 00000000000..9d92e285750
--- /dev/null
+++ b/client/go/cmd/test_test.go
@@ -0,0 +1,136 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// test command tests
+// Author: jonmv
+
+package cmd
+
+import (
+ "github.com/vespa-engine/vespa/client/go/util"
+ "github.com/vespa-engine/vespa/client/go/vespa"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestSuite(t *testing.T) {
+ client := &mockHttpClient{}
+ searchResponse, _ := ioutil.ReadFile("testdata/tests/response.json")
+ client.NextStatus(200)
+ client.NextStatus(200)
+ for i := 0; i < 9; i++ {
+ client.NextResponse(200, string(searchResponse))
+ }
+
+ expectedBytes, _ := ioutil.ReadFile("testdata/tests/expected-suite.out")
+ outBytes, errBytes := execute(command{args: []string{"test", "testdata/tests/system-test"}}, t, client)
+ assert.Equal(t, string(expectedBytes), outBytes)
+ assert.Equal(t, "", errBytes)
+
+ baseUrl := "http://127.0.0.1:8080"
+ urlWithQuery := baseUrl + "/search/?presentation.timing=true&query=artist%3A+foo&timeout=3.4s"
+ requests := []*http.Request{createFeedRequest(baseUrl), createFeedRequest(baseUrl), createSearchRequest(urlWithQuery), createSearchRequest(urlWithQuery)}
+ for i := 0; i < 7; i++ {
+ requests = append(requests, createSearchRequest(baseUrl+"/search/"))
+ }
+ assertRequests(requests, client, t)
+}
+
+func TestTestWithoutAssertions(t *testing.T) {
+ client := &mockHttpClient{}
+ _, errBytes := execute(command{args: []string{"test", "testdata/tests/system-test/foo/query.json"}}, t, client)
+ assert.Equal(t, "a test must have at least one assertion, but none were found in 'testdata/tests/system-test/foo/query.json'\n", errBytes)
+}
+
+func TestSuiteWithoutTests(t *testing.T) {
+ client := &mockHttpClient{}
+ outBytes, errBytes := execute(command{args: []string{"test", "testdata/tests/staging-test"}}, t, client)
+ assert.Equal(t, "Failed to find any tests at 'testdata/tests/staging-test'\n", outBytes)
+ assert.Equal(t, "", errBytes)
+}
+
+func TestSingleTest(t *testing.T) {
+ client := &mockHttpClient{}
+ searchResponse, _ := ioutil.ReadFile("testdata/tests/response.json")
+ client.NextStatus(200)
+ client.NextStatus(200)
+ client.NextResponse(200, string(searchResponse))
+ client.NextResponse(200, string(searchResponse))
+
+ expectedBytes, _ := ioutil.ReadFile("testdata/tests/expected.out")
+ outBytes, errBytes := execute(command{args: []string{"test", "testdata/tests/system-test/test.json"}}, t, client)
+ assert.Equal(t, string(expectedBytes), outBytes)
+ assert.Equal(t, "", errBytes)
+
+ baseUrl := "http://127.0.0.1:8080"
+ rawUrl := baseUrl + "/search/?presentation.timing=true&query=artist%3A+foo&timeout=3.4s"
+ assertRequests([]*http.Request{createFeedRequest(baseUrl), createFeedRequest(baseUrl), createSearchRequest(rawUrl), createSearchRequest(rawUrl)}, client, t)
+}
+
+func TestSingleTestWithCloudAndEndpoints(t *testing.T) {
+ cmd := command{args: []string{"test", "testdata/tests/system-test/test.json", "-t", "cloud", "-a", "t.a.i"}}
+ cmd.homeDir = filepath.Join(t.TempDir(), ".vespa")
+ os.MkdirAll(cmd.homeDir, 0700)
+ keyFile := filepath.Join(cmd.homeDir, "key")
+ certFile := filepath.Join(cmd.homeDir, "cert")
+
+ os.Setenv("VESPA_CLI_DATA_PLANE_KEY_FILE", keyFile)
+ os.Setenv("VESPA_CLI_DATA_PLANE_CERT_FILE", certFile)
+ os.Setenv("VESPA_CLI_ENDPOINTS", "{\"endpoints\":[{\"cluster\":\"container\",\"url\":\"https://url\"}]}")
+
+ kp, _ := vespa.CreateKeyPair()
+ ioutil.WriteFile(keyFile, kp.PrivateKey, 0600)
+ ioutil.WriteFile(certFile, kp.Certificate, 0600)
+
+ client := &mockHttpClient{}
+ searchResponse, _ := ioutil.ReadFile("testdata/tests/response.json")
+ client.NextStatus(200)
+ client.NextStatus(200)
+ client.NextResponse(200, string(searchResponse))
+ client.NextResponse(200, string(searchResponse))
+
+ expectedBytes, _ := ioutil.ReadFile("testdata/tests/expected.out")
+ outBytes, errBytes := execute(cmd, t, client)
+ assert.Equal(t, string(expectedBytes), outBytes)
+ assert.Equal(t, "", errBytes)
+
+ baseUrl := "https://url"
+ rawUrl := baseUrl + "/search/?presentation.timing=true&query=artist%3A+foo&timeout=3.4s"
+ assertRequests([]*http.Request{createFeedRequest(baseUrl), createFeedRequest(baseUrl), createSearchRequest(rawUrl), createSearchRequest(rawUrl)}, client, t)
+}
+
+func createFeedRequest(urlPrefix string) *http.Request {
+ return createRequest("POST",
+ urlPrefix+"/document/v1/test/music/docid/doc?timeout=3.4s",
+ "{\"fields\":{\"artist\":\"Foo Fighters\"}}")
+}
+
+func createSearchRequest(rawUrl string) *http.Request {
+ return createRequest("GET", rawUrl, "")
+}
+
+func createRequest(method string, uri string, body string) *http.Request {
+ requestUrl, _ := url.ParseRequestURI(uri)
+ return &http.Request{
+ URL: requestUrl,
+ Method: method,
+ Header: nil,
+ Body: ioutil.NopCloser(strings.NewReader(body)),
+ }
+}
+
+func assertRequests(requests []*http.Request, client *mockHttpClient, t *testing.T) {
+ if assert.Equal(t, len(requests), len(client.requests)) {
+ for i, e := range requests {
+ a := client.requests[i]
+ assert.Equal(t, e.URL.String(), a.URL.String())
+ assert.Equal(t, e.Method, a.Method)
+ assert.Equal(t, util.ReaderToJSON(e.Body), util.ReaderToJSON(a.Body))
+ }
+ }
+}
diff --git a/client/go/cmd/testdata/tests/body.json b/client/go/cmd/testdata/tests/body.json
new file mode 100644
index 00000000000..767330b1a2d
--- /dev/null
+++ b/client/go/cmd/testdata/tests/body.json
@@ -0,0 +1,12 @@
+{
+ "root": {
+ "id": "toplevel",
+ "coverage": {
+ "full": true
+ },
+ "fields": {
+ "totalCount" : 1
+ },
+ "children": [{}]
+ }
+} \ No newline at end of file
diff --git a/client/go/cmd/testdata/tests/expected-suite.out b/client/go/cmd/testdata/tests/expected-suite.out
new file mode 100644
index 00000000000..0fb8b897f4f
--- /dev/null
+++ b/client/go/cmd/testdata/tests/expected-suite.out
@@ -0,0 +1,269 @@
+Running testdata/tests/system-test/test.json: .... OK!
+Running testdata/tests/system-test/wrong-bool-value.json:
+Failed verifying assertion 0:
+Expected JSON at /root/coverage/full (false) does not match actual (true). Response body:
+{
+ "root": {
+ "children": [
+ {
+ "fields": {
+ "artist": "Foo Fighters",
+ "documentid": "id:test:music::doc",
+ "sddocname": "music"
+ },
+ "id": "id:test:music::doc",
+ "relevance": 0.38186238359951247,
+ "source": "music"
+ }
+ ],
+ "coverage": {
+ "coverage": 100,
+ "documents": 1,
+ "full": true,
+ "nodes": 1,
+ "results": 1,
+ "resultsFull": 1
+ },
+ "fields": {
+ "totalCount": 1
+ },
+ "id": "toplevel",
+ "relevance": 1
+ },
+ "timing": {
+ "querytime": 0.003,
+ "searchtime": 0.004,
+ "summaryfetchtime": 0
+ }
+}
+Running testdata/tests/system-test/wrong-element-count.json:
+Failed verifying assertion 0:
+Expected number of elements at /root/children (0) does not match actual (1). Response body:
+{
+ "root": {
+ "children": [
+ {
+ "fields": {
+ "artist": "Foo Fighters",
+ "documentid": "id:test:music::doc",
+ "sddocname": "music"
+ },
+ "id": "id:test:music::doc",
+ "relevance": 0.38186238359951247,
+ "source": "music"
+ }
+ ],
+ "coverage": {
+ "coverage": 100,
+ "documents": 1,
+ "full": true,
+ "nodes": 1,
+ "results": 1,
+ "resultsFull": 1
+ },
+ "fields": {
+ "totalCount": 1
+ },
+ "id": "toplevel",
+ "relevance": 1
+ },
+ "timing": {
+ "querytime": 0.003,
+ "searchtime": 0.004,
+ "summaryfetchtime": 0
+ }
+}
+Running testdata/tests/system-test/wrong-field-name.json:
+Failed verifying assertion 0:
+Expected field at /root/fields/totalCountDracula not present in actual data. Response body:
+{
+ "root": {
+ "children": [
+ {
+ "fields": {
+ "artist": "Foo Fighters",
+ "documentid": "id:test:music::doc",
+ "sddocname": "music"
+ },
+ "id": "id:test:music::doc",
+ "relevance": 0.38186238359951247,
+ "source": "music"
+ }
+ ],
+ "coverage": {
+ "coverage": 100,
+ "documents": 1,
+ "full": true,
+ "nodes": 1,
+ "results": 1,
+ "resultsFull": 1
+ },
+ "fields": {
+ "totalCount": 1
+ },
+ "id": "toplevel",
+ "relevance": 1
+ },
+ "timing": {
+ "querytime": 0.003,
+ "searchtime": 0.004,
+ "summaryfetchtime": 0
+ }
+}
+Running testdata/tests/system-test/wrong-float-value.json:
+Failed verifying assertion 0:
+Expected JSON at /root/children/0/relevance (0.381862373599) does not match actual (0.38186238359951247). Response body:
+{
+ "root": {
+ "children": [
+ {
+ "fields": {
+ "artist": "Foo Fighters",
+ "documentid": "id:test:music::doc",
+ "sddocname": "music"
+ },
+ "id": "id:test:music::doc",
+ "relevance": 0.38186238359951247,
+ "source": "music"
+ }
+ ],
+ "coverage": {
+ "coverage": 100,
+ "documents": 1,
+ "full": true,
+ "nodes": 1,
+ "results": 1,
+ "resultsFull": 1
+ },
+ "fields": {
+ "totalCount": 1
+ },
+ "id": "toplevel",
+ "relevance": 1
+ },
+ "timing": {
+ "querytime": 0.003,
+ "searchtime": 0.004,
+ "summaryfetchtime": 0
+ }
+}
+Running testdata/tests/system-test/wrong-int-value.json:
+Failed verifying assertion 0:
+Expected JSON at /root/fields/totalCount (2) does not match actual (1). Response body:
+{
+ "root": {
+ "children": [
+ {
+ "fields": {
+ "artist": "Foo Fighters",
+ "documentid": "id:test:music::doc",
+ "sddocname": "music"
+ },
+ "id": "id:test:music::doc",
+ "relevance": 0.38186238359951247,
+ "source": "music"
+ }
+ ],
+ "coverage": {
+ "coverage": 100,
+ "documents": 1,
+ "full": true,
+ "nodes": 1,
+ "results": 1,
+ "resultsFull": 1
+ },
+ "fields": {
+ "totalCount": 1
+ },
+ "id": "toplevel",
+ "relevance": 1
+ },
+ "timing": {
+ "querytime": 0.003,
+ "searchtime": 0.004,
+ "summaryfetchtime": 0
+ }
+}
+Running testdata/tests/system-test/wrong-null-value.json:
+Failed verifying assertion 0:
+Expected field at /boot not present in actual data. Response body:
+{
+ "root": {
+ "children": [
+ {
+ "fields": {
+ "artist": "Foo Fighters",
+ "documentid": "id:test:music::doc",
+ "sddocname": "music"
+ },
+ "id": "id:test:music::doc",
+ "relevance": 0.38186238359951247,
+ "source": "music"
+ }
+ ],
+ "coverage": {
+ "coverage": 100,
+ "documents": 1,
+ "full": true,
+ "nodes": 1,
+ "results": 1,
+ "resultsFull": 1
+ },
+ "fields": {
+ "totalCount": 1
+ },
+ "id": "toplevel",
+ "relevance": 1
+ },
+ "timing": {
+ "querytime": 0.003,
+ "searchtime": 0.004,
+ "summaryfetchtime": 0
+ }
+}
+Running testdata/tests/system-test/wrong-string-value.json:
+Failed verifying assertion 0:
+Expected JSON at /root/children/0/fields/artist ("Boo Fighters") does not match actual ("Foo Fighters"). Response body:
+{
+ "root": {
+ "children": [
+ {
+ "fields": {
+ "artist": "Foo Fighters",
+ "documentid": "id:test:music::doc",
+ "sddocname": "music"
+ },
+ "id": "id:test:music::doc",
+ "relevance": 0.38186238359951247,
+ "source": "music"
+ }
+ ],
+ "coverage": {
+ "coverage": 100,
+ "documents": 1,
+ "full": true,
+ "nodes": 1,
+ "results": 1,
+ "resultsFull": 1
+ },
+ "fields": {
+ "totalCount": 1
+ },
+ "id": "toplevel",
+ "relevance": 1
+ },
+ "timing": {
+ "querytime": 0.003,
+ "searchtime": 0.004,
+ "summaryfetchtime": 0
+ }
+}
+
+Failed 7 of 8 tests:
+testdata/tests/system-test/wrong-bool-value.json: assertion 0
+testdata/tests/system-test/wrong-element-count.json: assertion 0
+testdata/tests/system-test/wrong-field-name.json: assertion 0
+testdata/tests/system-test/wrong-float-value.json: assertion 0
+testdata/tests/system-test/wrong-int-value.json: assertion 0
+testdata/tests/system-test/wrong-null-value.json: assertion 0
+testdata/tests/system-test/wrong-string-value.json: assertion 0
diff --git a/client/go/cmd/testdata/tests/expected.out b/client/go/cmd/testdata/tests/expected.out
new file mode 100644
index 00000000000..f012ee30e95
--- /dev/null
+++ b/client/go/cmd/testdata/tests/expected.out
@@ -0,0 +1,2 @@
+Running testdata/tests/system-test/test.json: .... OK!
+1 tests completed successfully
diff --git a/client/go/cmd/testdata/tests/response.json b/client/go/cmd/testdata/tests/response.json
new file mode 100644
index 00000000000..48368b935a8
--- /dev/null
+++ b/client/go/cmd/testdata/tests/response.json
@@ -0,0 +1,34 @@
+{
+ "root": {
+ "children": [
+ {
+ "fields": {
+ "artist": "Foo Fighters",
+ "documentid": "id:test:music::doc",
+ "sddocname": "music"
+ },
+ "id": "id:test:music::doc",
+ "relevance": 0.38186238359951247,
+ "source": "music"
+ }
+ ],
+ "coverage": {
+ "coverage": 100,
+ "documents": 1,
+ "full": true,
+ "nodes": 1,
+ "results": 1,
+ "resultsFull": 1
+ },
+ "fields": {
+ "totalCount": 1
+ },
+ "id": "toplevel",
+ "relevance": 1
+ },
+ "timing": {
+ "querytime": 0.003,
+ "searchtime": 0.004,
+ "summaryfetchtime": 0
+ }
+} \ No newline at end of file
diff --git a/client/go/cmd/testdata/tests/staging-test/not-json b/client/go/cmd/testdata/tests/staging-test/not-json
new file mode 100644
index 00000000000..b6fc4c620b6
--- /dev/null
+++ b/client/go/cmd/testdata/tests/staging-test/not-json
@@ -0,0 +1 @@
+hello \ No newline at end of file
diff --git a/client/go/cmd/testdata/tests/system-test/foo/body.json b/client/go/cmd/testdata/tests/system-test/foo/body.json
new file mode 100644
index 00000000000..0bbf626eafe
--- /dev/null
+++ b/client/go/cmd/testdata/tests/system-test/foo/body.json
@@ -0,0 +1,5 @@
+{
+ "fields": {
+ "artist": "Foo Fighters"
+ }
+} \ No newline at end of file
diff --git a/client/go/cmd/testdata/tests/system-test/foo/query.json b/client/go/cmd/testdata/tests/system-test/foo/query.json
new file mode 100644
index 00000000000..25b8c5b0039
--- /dev/null
+++ b/client/go/cmd/testdata/tests/system-test/foo/query.json
@@ -0,0 +1,3 @@
+{
+ "query": "artist: foo"
+}
diff --git a/client/go/cmd/testdata/tests/system-test/test.json b/client/go/cmd/testdata/tests/system-test/test.json
new file mode 100644
index 00000000000..5aac76d29ff
--- /dev/null
+++ b/client/go/cmd/testdata/tests/system-test/test.json
@@ -0,0 +1,64 @@
+{
+ "defaults": {
+ "cluster": "container",
+ "parameters": {
+ "timeout": "3.4s"
+ }
+ },
+ "assertions": [
+ {
+ "name": "feed music",
+ "request": {
+ "method": "POST",
+ "body": "foo/body.json",
+ "uri": "/document/v1/test/music/docid/doc"
+ }
+ },
+ {
+ "name": "re-feed music",
+ "request": {
+ "method": "POST",
+ "body": {
+ "fields": {
+ "artist": "Foo Fighters"
+ }
+ },
+ "uri": "/document/v1/test/music/docid/doc"
+ }
+ },
+ {
+ "name": "query for foo",
+ "request": {
+ "uri": "/search/?presentation.timing=true",
+ "parameters": {
+ "query": "artist: foo"
+ }
+ },
+ "response": {
+ "code": 200,
+ "body": "../body.json"
+ }
+ },
+ {
+ "name": "query for foo again",
+ "request": {
+ "uri": "/search/?presentation.timing=true",
+ "parameters": "foo/query.json"
+ },
+ "response": {
+ "body": {
+ "root": {
+ "children": [
+ {
+ "fields": {
+ "artist": "Foo Fighters"
+ },
+ "relevance": 0.381862383599
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+}
diff --git a/client/go/cmd/testdata/tests/system-test/wrong-bool-value.json b/client/go/cmd/testdata/tests/system-test/wrong-bool-value.json
new file mode 100644
index 00000000000..ae6f9de8de8
--- /dev/null
+++ b/client/go/cmd/testdata/tests/system-test/wrong-bool-value.json
@@ -0,0 +1,15 @@
+{
+ "assertions": [
+ {
+ "response": {
+ "body": {
+ "root": {
+ "coverage": {
+ "full": false
+ }
+ }
+ }
+ }
+ }
+ ]
+}
diff --git a/client/go/cmd/testdata/tests/system-test/wrong-element-count.json b/client/go/cmd/testdata/tests/system-test/wrong-element-count.json
new file mode 100644
index 00000000000..77c687fa919
--- /dev/null
+++ b/client/go/cmd/testdata/tests/system-test/wrong-element-count.json
@@ -0,0 +1,13 @@
+{
+ "assertions": [
+ {
+ "response": {
+ "body": {
+ "root": {
+ "children": []
+ }
+ }
+ }
+ }
+ ]
+}
diff --git a/client/go/cmd/testdata/tests/system-test/wrong-field-name.json b/client/go/cmd/testdata/tests/system-test/wrong-field-name.json
new file mode 100644
index 00000000000..d020141ed12
--- /dev/null
+++ b/client/go/cmd/testdata/tests/system-test/wrong-field-name.json
@@ -0,0 +1,15 @@
+{
+ "assertions": [
+ {
+ "response": {
+ "body": {
+ "root": {
+ "fields": {
+ "totalCountDracula" : 1
+ }
+ }
+ }
+ }
+ }
+ ]
+}
diff --git a/client/go/cmd/testdata/tests/system-test/wrong-float-value.json b/client/go/cmd/testdata/tests/system-test/wrong-float-value.json
new file mode 100644
index 00000000000..804f2582176
--- /dev/null
+++ b/client/go/cmd/testdata/tests/system-test/wrong-float-value.json
@@ -0,0 +1,17 @@
+{
+ "assertions": [
+ {
+ "response": {
+ "body": {
+ "root": {
+ "children": [
+ {
+ "relevance": 0.381862373599
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+}
diff --git a/client/go/cmd/testdata/tests/system-test/wrong-int-value.json b/client/go/cmd/testdata/tests/system-test/wrong-int-value.json
new file mode 100644
index 00000000000..3cbf8acd1d8
--- /dev/null
+++ b/client/go/cmd/testdata/tests/system-test/wrong-int-value.json
@@ -0,0 +1,15 @@
+{
+ "assertions": [
+ {
+ "response": {
+ "body": {
+ "root": {
+ "fields": {
+ "totalCount" : 2
+ }
+ }
+ }
+ }
+ }
+ ]
+}
diff --git a/client/go/cmd/testdata/tests/system-test/wrong-null-value.json b/client/go/cmd/testdata/tests/system-test/wrong-null-value.json
new file mode 100644
index 00000000000..11425df7ad4
--- /dev/null
+++ b/client/go/cmd/testdata/tests/system-test/wrong-null-value.json
@@ -0,0 +1,11 @@
+{
+ "assertions": [
+ {
+ "response": {
+ "body": {
+ "boot": null
+ }
+ }
+ }
+ ]
+}
diff --git a/client/go/cmd/testdata/tests/system-test/wrong-string-value.json b/client/go/cmd/testdata/tests/system-test/wrong-string-value.json
new file mode 100644
index 00000000000..2cf0a5fdb38
--- /dev/null
+++ b/client/go/cmd/testdata/tests/system-test/wrong-string-value.json
@@ -0,0 +1,19 @@
+{
+ "assertions": [
+ {
+ "response": {
+ "body": {
+ "root": {
+ "children": [
+ {
+ "fields": {
+ "artist": "Boo Fighters"
+ }
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+}
diff --git a/client/go/vespa/deploy.go b/client/go/vespa/deploy.go
index 252a646bcfc..c1cc868e16f 100644
--- a/client/go/vespa/deploy.go
+++ b/client/go/vespa/deploy.go
@@ -73,7 +73,7 @@ func (d DeploymentOpts) String() string {
func (d *DeploymentOpts) IsCloud() bool { return d.Target.Type() == cloudTargetType }
func (d *DeploymentOpts) url(path string) (*url.URL, error) {
- service, err := d.Target.Service(deployService, 0, 0)
+ service, err := d.Target.Service(deployService, 0, 0, "")
if err != nil {
return nil, err
}
diff --git a/client/go/vespa/target.go b/client/go/vespa/target.go
index 093cb2b5cad..0b3223c0d2e 100644
--- a/client/go/vespa/target.go
+++ b/client/go/vespa/target.go
@@ -39,7 +39,6 @@ type Service struct {
BaseURL string
Name string
TLSOptions TLSOptions
- Target *Target
}
// Target represents a Vespa platform, running named Vespa services.
@@ -48,7 +47,7 @@ type Target interface {
Type() string
// Service returns the service for given name. If timeout is non-zero, wait for the service to converge.
- Service(name string, timeout time.Duration, sessionOrRunID int64) (*Service, error)
+ Service(name string, timeout time.Duration, sessionOrRunID int64, cluster string) (*Service, error)
// PrintLog writes the logs of this deployment using given options to control output.
PrintLog(options LogOptions) error
@@ -129,7 +128,7 @@ func (s *Service) Description() string {
func (t *customTarget) Type() string { return t.targetType }
-func (t *customTarget) Service(name string, timeout time.Duration, sessionID int64) (*Service, error) {
+func (t *customTarget) Service(name string, timeout time.Duration, sessionOrRunID int64, cluster string) (*Service, error) {
if timeout > 0 && name != deployService {
if err := t.waitForConvergence(timeout); err != nil {
return nil, err
@@ -171,7 +170,7 @@ func (t *customTarget) urlWithPort(serviceName string) (string, error) {
}
func (t *customTarget) waitForConvergence(timeout time.Duration) error {
- deployer, err := t.Service(deployService, 0, 0)
+ deployer, err := t.Service(deployService, 0, 0, "")
if err != nil {
return err
}
@@ -241,8 +240,8 @@ func (t *cloudTarget) resolveEndpoint(cluster string) (string, error) {
func (t *cloudTarget) Type() string { return t.targetType }
-func (t *cloudTarget) Service(name string, timeout time.Duration, runID int64) (*Service, error) {
- if name != deployService {
+func (t *cloudTarget) Service(name string, timeout time.Duration, runID int64, cluster string) (*Service, error) {
+ if name != deployService && t.urlsByCluster == nil {
if err := t.waitForEndpoints(timeout, runID); err != nil {
return nil, err
}
@@ -251,13 +250,13 @@ func (t *cloudTarget) Service(name string, timeout time.Duration, runID int64) (
case deployService:
return &Service{Name: name, BaseURL: t.apiURL}, nil
case queryService:
- queryURL, err := t.resolveEndpoint("")
+ queryURL, err := t.resolveEndpoint(cluster)
if err != nil {
return nil, err
}
return &Service{Name: name, BaseURL: queryURL, TLSOptions: t.tlsOptions}, nil
case documentService:
- documentURL, err := t.resolveEndpoint("")
+ documentURL, err := t.resolveEndpoint(cluster)
if err != nil {
return nil, err
}
@@ -489,7 +488,7 @@ func CustomTarget(baseURL string) Target {
// CloudTarget creates a Target for the Vespa Cloud platform.
func CloudTarget(apiURL string, deployment Deployment, apiKey []byte, tlsOptions TLSOptions, logOptions LogOptions,
- authConfigPath string, systemName string, cloudAuth string) Target {
+ authConfigPath string, systemName string, cloudAuth string, urlsByCluster map[string]string) Target {
return &cloudTarget{
apiURL: apiURL,
targetType: cloudTargetType,
@@ -500,6 +499,7 @@ func CloudTarget(apiURL string, deployment Deployment, apiKey []byte, tlsOptions
authConfigPath: authConfigPath,
systemName: systemName,
cloudAuth: cloudAuth,
+ urlsByCluster: urlsByCluster,
}
}
diff --git a/client/go/vespa/target_test.go b/client/go/vespa/target_test.go
index 9d2418897e3..0cfe9f1962c 100644
--- a/client/go/vespa/target_test.go
+++ b/client/go/vespa/target_test.go
@@ -82,11 +82,11 @@ func TestCustomTargetWait(t *testing.T) {
defer srv.Close()
target := CustomTarget(srv.URL)
- _, err := target.Service("query", time.Millisecond, 42)
+ _, err := target.Service("query", time.Millisecond, 42, "")
assert.NotNil(t, err)
vc.deploymentConverged = true
- _, err = target.Service("query", time.Millisecond, 42)
+ _, err = target.Service("query", time.Millisecond, 42, "")
assert.Nil(t, err)
assertServiceWait(t, 200, target, "deploy")
@@ -104,11 +104,11 @@ func TestCloudTargetWait(t *testing.T) {
target := createCloudTarget(t, srv.URL, &logWriter)
assertServiceWait(t, 200, target, "deploy")
- _, err := target.Service("query", time.Millisecond, 42)
+ _, err := target.Service("query", time.Millisecond, 42, "")
assert.NotNil(t, err)
vc.deploymentConverged = true
- _, err = target.Service("query", time.Millisecond, 42)
+ _, err = target.Service("query", time.Millisecond, 42, "")
assert.Nil(t, err)
assertServiceWait(t, 500, target, "query")
@@ -152,7 +152,7 @@ func createCloudTarget(t *testing.T, url string, logWriter io.Writer) Target {
target := CloudTarget("https://example.com", Deployment{
Application: ApplicationID{Tenant: "t1", Application: "a1", Instance: "i1"},
Zone: ZoneID{Environment: "dev", Region: "us-north-1"},
- }, apiKey, TLSOptions{KeyPair: x509KeyPair}, LogOptions{Writer: logWriter}, "", "", "")
+ }, apiKey, TLSOptions{KeyPair: x509KeyPair}, LogOptions{Writer: logWriter}, "", "", "", nil)
if ct, ok := target.(*cloudTarget); ok {
ct.apiURL = url
} else {
@@ -162,13 +162,13 @@ func createCloudTarget(t *testing.T, url string, logWriter io.Writer) Target {
}
func assertServiceURL(t *testing.T, url string, target Target, service string) {
- s, err := target.Service(service, 0, 42)
+ s, err := target.Service(service, 0, 42, "")
assert.Nil(t, err)
assert.Equal(t, url, s.BaseURL)
}
func assertServiceWait(t *testing.T, expectedStatus int, target Target, service string) {
- s, err := target.Service(service, 0, 42)
+ s, err := target.Service(service, 0, 42, "")
assert.Nil(t, err)
status, err := s.Wait(0)