diff --git a/.golangci.yml b/.golangci.yml index 616a1f2..ecbdb04 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,6 +1,7 @@ run: deadline: 5m tests: true + modules-download-mode: vendor linters: disable-all: true @@ -45,4 +46,3 @@ issues: exclude-use-default: false max-issues-per-linter: 0 max-same-issues: 0 - new: true diff --git a/Makefile b/Makefile index 513913d..680c9f4 100644 --- a/Makefile +++ b/Makefile @@ -82,7 +82,7 @@ verify: ## Verify 'vendor' dependencies .PHONY: lint lint: ## Run linter @ $(MAKE) --no-print-directory log-$@ - GO111MODULE=on golangci-lint run ./... + GO111MODULE=on golangci-lint run --tests=false ./... .PHONY: fmt fmt: ## Format go files @@ -90,8 +90,8 @@ fmt: ## Format go files goimports -w $(GOFILES) .PHONY: checkfmt -checkfmt: RESULT = $(shell goimports -l $(GOFILES) | tee >(if [ "$$(wc -l)" = 0 ]; then echo "OK"; fi)) -checkfmt: SHELL := /usr/bin/env bash +checkfmt: RESULT ?= $(shell goimports -l $(GOFILES) | tee >(if [ "$$(wc -l)" = 0 ]; then echo "OK"; fi)) +checkfmt: SHELL := /usr/bin/env bash checkfmt: ## Check formatting of go files @ $(MAKE) --no-print-directory log-$@ @ echo "$(RESULT)" diff --git a/cmd/cca/cca.go b/cmd/cca/cca.go index 066fce8..ebd4b03 100644 --- a/cmd/cca/cca.go +++ b/cmd/cca/cca.go @@ -22,9 +22,9 @@ import ( "github.com/cloud-ca/cca/cmd/cca/connection" "github.com/cloud-ca/cca/cmd/cca/version" "github.com/cloud-ca/cca/pkg/cli" - "github.com/cloud-ca/cca/pkg/client" - "github.com/cloud-ca/cca/pkg/flags" - "github.com/cloud-ca/cca/pkg/output" + "github.com/cloud-ca/cca/pkg/cli/client" + "github.com/cloud-ca/cca/pkg/cli/flags" + "github.com/cloud-ca/cca/pkg/cli/output" "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/viper" diff --git a/cmd/cca/completion/completion.go b/cmd/cca/completion/completion.go index 746868f..1f115d0 100644 --- a/cmd/cca/completion/completion.go +++ b/cmd/cca/completion/completion.go @@ -19,7 +19,7 @@ import ( "github.com/cloud-ca/cca/cmd/cca/completion/bash" "github.com/cloud-ca/cca/cmd/cca/completion/zsh" "github.com/cloud-ca/cca/pkg/cli" - "github.com/cloud-ca/cca/pkg/util" + "github.com/cloud-ca/cca/pkg/cli/util" "github.com/spf13/cobra" ) diff --git a/cmd/cca/connection/connection.go b/cmd/cca/connection/connection.go index 68fd926..624acc4 100644 --- a/cmd/cca/connection/connection.go +++ b/cmd/cca/connection/connection.go @@ -19,7 +19,7 @@ import ( "github.com/cloud-ca/cca/cmd/cca/connection/get" "github.com/cloud-ca/cca/cmd/cca/connection/list" "github.com/cloud-ca/cca/pkg/cli" - "github.com/cloud-ca/cca/pkg/util" + "github.com/cloud-ca/cca/pkg/cli/util" "github.com/spf13/cobra" ) diff --git a/cmd/cca/connection/get/get.go b/cmd/cca/connection/get/get.go index 54e4211..bed2029 100644 --- a/cmd/cca/connection/get/get.go +++ b/cmd/cca/connection/get/get.go @@ -17,7 +17,7 @@ package get import ( "github.com/cloud-ca/cca/pkg/cli" - "github.com/cloud-ca/cca/pkg/output" + "github.com/cloud-ca/cca/pkg/cli/output" "github.com/spf13/cobra" ) diff --git a/cmd/cca/connection/list/list.go b/cmd/cca/connection/list/list.go index 235dae4..3946a42 100644 --- a/cmd/cca/connection/list/list.go +++ b/cmd/cca/connection/list/list.go @@ -17,7 +17,7 @@ package list import ( "github.com/cloud-ca/cca/pkg/cli" - "github.com/cloud-ca/cca/pkg/output" + "github.com/cloud-ca/cca/pkg/cli/output" "github.com/spf13/cobra" ) diff --git a/go.mod b/go.mod index 5c5d31d..962bd0d 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,12 @@ module github.com/cloud-ca/cca go 1.12 require ( - github.com/cloud-ca/go-cloudca v1.4.0 + github.com/golang/mock v1.1.1 github.com/lithammer/dedent v1.1.0 github.com/sirupsen/logrus v1.4.2 github.com/spf13/cobra v0.0.5 github.com/spf13/viper v1.4.0 + github.com/stretchr/testify v1.2.2 github.com/tidwall/pretty v1.0.0 gopkg.in/yaml.v2 v2.2.2 sigs.k8s.io/kind v0.4.0 diff --git a/go.sum b/go.sum index ad433ac..21e0527 100644 --- a/go.sum +++ b/go.sum @@ -13,8 +13,6 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloud-ca/go-cloudca v1.4.0 h1:R8ZWca1EtZrMS3t05Un11LwGX21/phuPp7kpMNT82DU= -github.com/cloud-ca/go-cloudca v1.4.0/go.mod h1:+BpT2i2e5Zn+IDBVcZiqOBrZXSQpo/ObZ/MMTUh/RhI= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= @@ -24,6 +22,7 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= @@ -49,6 +48,7 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -102,6 +102,7 @@ github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= @@ -138,6 +139,7 @@ github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/y github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= @@ -164,6 +166,7 @@ golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190514140710-3ec191127204/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/pkg/client/client.go b/pkg/cli/client/client.go similarity index 89% rename from pkg/client/client.go rename to pkg/cli/client/client.go index 58daac4..f9ff3bb 100644 --- a/pkg/client/client.go +++ b/pkg/cli/client/client.go @@ -16,18 +16,18 @@ package client import ( - gocca "github.com/cloud-ca/go-cloudca" + "github.com/cloud-ca/cca/pkg/cloudca" ) // Client to interact with cloud.ca infrastructure type Client struct { - *gocca.CcaClient + *cloudca.Client } // NewClient returns a new client to interact with cloud.ca // infrastructure with provided API URL and Key func NewClient(url string, key string) *Client { return &Client{ - CcaClient: gocca.NewCcaClientWithURL(url, key), + Client: cloudca.NewClientWithURL(url, key), } } diff --git a/pkg/flags/defaults.go b/pkg/cli/flags/defaults.go similarity index 100% rename from pkg/flags/defaults.go rename to pkg/cli/flags/defaults.go diff --git a/pkg/flags/doc.go b/pkg/cli/flags/doc.go similarity index 100% rename from pkg/flags/doc.go rename to pkg/cli/flags/doc.go diff --git a/pkg/flags/flags.go b/pkg/cli/flags/flags.go similarity index 97% rename from pkg/flags/flags.go rename to pkg/cli/flags/flags.go index e0c5cc7..4600112 100644 --- a/pkg/flags/flags.go +++ b/pkg/cli/flags/flags.go @@ -15,7 +15,7 @@ package flags import ( - "github.com/cloud-ca/cca/pkg/output" + "github.com/cloud-ca/cca/pkg/cli/output" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) diff --git a/pkg/output/builder.go b/pkg/cli/output/builder.go similarity index 100% rename from pkg/output/builder.go rename to pkg/cli/output/builder.go diff --git a/pkg/output/doc.go b/pkg/cli/output/doc.go similarity index 100% rename from pkg/output/doc.go rename to pkg/cli/output/doc.go diff --git a/pkg/output/formatter.go b/pkg/cli/output/formatter.go similarity index 100% rename from pkg/output/formatter.go rename to pkg/cli/output/formatter.go diff --git a/pkg/output/types.go b/pkg/cli/output/types.go similarity index 100% rename from pkg/output/types.go rename to pkg/cli/output/types.go diff --git a/pkg/util/description.go b/pkg/cli/util/description.go similarity index 100% rename from pkg/util/description.go rename to pkg/cli/util/description.go diff --git a/pkg/cli/wrapper.go b/pkg/cli/wrapper.go index a0a362e..04011ce 100644 --- a/pkg/cli/wrapper.go +++ b/pkg/cli/wrapper.go @@ -16,9 +16,9 @@ package cli import ( - "github.com/cloud-ca/cca/pkg/client" - "github.com/cloud-ca/cca/pkg/flags" - "github.com/cloud-ca/cca/pkg/output" + "github.com/cloud-ca/cca/pkg/cli/client" + "github.com/cloud-ca/cca/pkg/cli/flags" + "github.com/cloud-ca/cca/pkg/cli/output" ) // Wrapper of different parts of cca cli diff --git a/pkg/cloudca/api/api.go b/pkg/cloudca/api/api.go new file mode 100755 index 0000000..38429a7 --- /dev/null +++ b/pkg/cloudca/api/api.go @@ -0,0 +1,111 @@ +// Copyright © 2019 cloud.ca Authors. +// +// 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 api + +import ( + "bytes" + "crypto/tls" + "fmt" + "io" + "net/http" + "net/url" + "strings" +) + +// Client Represent the Client interface for interacting with cloud.ca API +type Client interface { + Do(request Request) (*Response, error) + GetAPIURL() string + GetAPIKey() string +} + +// CcaClient for interacting with cloud.ca API +type CcaClient struct { + apiURL string + apiKey string + httpClient *http.Client +} + +// Do Execute the API call to server and returns a Response. cloud.ca errors will +// be returned in the Response body, not in the error return value. The error +// return value is reserved for unexpected errors. +func (c CcaClient) Do(request Request) (*Response, error) { + var bodyBuffer io.Reader + if request.Body != nil { + bodyBuffer = bytes.NewBuffer(request.Body) + } + method := request.Method + if method == "" { + method = "GET" + } + req, err := http.NewRequest(request.Method, c.buildURL(request.Endpoint, request.Options), bodyBuffer) + if err != nil { + return nil, err + } + req.Header.Add("MC-Api-Key", c.apiKey) + req.Header.Add("Content-Type", "application/json") + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, err + } + defer func() { + err := resp.Body.Close() + fmt.Printf("%s", err) + }() + return NewResponse(resp) +} + +// GetAPIKey Return the API key being used by API client +func (c CcaClient) GetAPIKey() string { + return c.apiKey +} + +// GetAPIURL Return the API URL being used by API client +func (c CcaClient) GetAPIURL() string { + return c.apiURL +} + +// NewClient Create a new Client with provided API URL and key +func NewClient(apiURL, apiKey string) Client { + return CcaClient{ + apiURL: apiURL, + apiKey: apiKey, + httpClient: &http.Client{}, + } +} + +// NewInsecureClient Create a new Client with provided API URL and key that accepts insecure connections +func NewInsecureClient(apiURL, apiKey string) Client { + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + return CcaClient{ + apiURL: apiURL, + apiKey: apiKey, + httpClient: &http.Client{Transport: tr}, + } +} + +// buildURL Builds a URL by using endpoint and options. Options will be set as query parameters. +func (c CcaClient) buildURL(endpoint string, options map[string]string) string { + query := url.Values{} + if options != nil { + for k, v := range options { + query.Add(k, v) + } + } + u, _ := url.Parse(c.apiURL + "/" + strings.Trim(endpoint, "/") + "?" + query.Encode()) + return u.String() +} diff --git a/pkg/cloudca/api/api_test.go b/pkg/cloudca/api/api_test.go new file mode 100644 index 0000000..703f4b9 --- /dev/null +++ b/pkg/cloudca/api/api_test.go @@ -0,0 +1,91 @@ +// Copyright © 2019 cloud.ca Authors. +// +// 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 api + +import ( + "fmt" + "net/http" + "net/http/httptest" + "net/url" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetTaskReturnTaskIfSuccess(t *testing.T) { + //given + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(200) + w.Header().Set("Content-Type", "application/json") + fmt.Fprintln(w, `{"taskId": "test_task_id", `+ + `"taskStatus": "test_task_status", `+ + `"data": {"key":"value"}, `+ + `"metadata": {"meta_key":"meta_value"}}`) + })) + defer server.Close() + + transport := &http.Transport{ + Proxy: func(req *http.Request) (*url.URL, error) { + return url.Parse(server.URL) + }, + } + + httpClient := &http.Client{Transport: transport} + ccaClient := CcaClient{server.URL, "api-key", httpClient} + + expectedResp := Response{ + TaskID: "test_task_id", + TaskStatus: "test_task_status", + Data: []byte(`{"key":"value"}`), + MetaData: map[string]interface{}{"meta_key": "meta_value"}, + StatusCode: 200, + } + + //when + resp, _ := ccaClient.Do(Request{Method: "GET", Endpoint: "/fooo"}) + + //then + assert.Equal(t, expectedResp, *resp) +} + +func TestGetTaskReturnErrorsIfErrorOccured(t *testing.T) { + //given + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(400) + w.Header().Set("Content-Type", "application/json") + fmt.Fprintln(w, `{"errors": [{"errorCode": "FOO_ERROR", "message": "message1"}, {"errorCode": "BAR_ERROR", "message":"message2"}]}`) + })) + defer server.Close() + + transport := &http.Transport{ + Proxy: func(req *http.Request) (*url.URL, error) { + return url.Parse(server.URL) + }, + } + + httpClient := &http.Client{Transport: transport} + ccaClient := CcaClient{server.URL, "api-key", httpClient} + + expectedResp := Response{ + Errors: []Error{{ErrorCode: "FOO_ERROR", Message: "message1"}, {ErrorCode: "BAR_ERROR", Message: "message2"}}, + StatusCode: 400, + } + + //when + resp, _ := ccaClient.Do(Request{Method: "GET", Endpoint: "/fooo"}) + + //then + assert.Equal(t, expectedResp, *resp) +} diff --git a/pkg/cloudca/api/doc.go b/pkg/cloudca/api/doc.go new file mode 100644 index 0000000..14429cb --- /dev/null +++ b/pkg/cloudca/api/doc.go @@ -0,0 +1,16 @@ +// Copyright © 2019 cloud.ca Authors. +// +// 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 api contains the implementation of API client +package api diff --git a/pkg/cloudca/api/request.go b/pkg/cloudca/api/request.go new file mode 100755 index 0000000..ae99354 --- /dev/null +++ b/pkg/cloudca/api/request.go @@ -0,0 +1,31 @@ +// Copyright © 2019 cloud.ca Authors. +// +// 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 api + +//nolint +const ( + GET = "GET" + POST = "POST" + DELETE = "DELETE" + PUT = "PUT" +) + +// Request a request object +type Request struct { + Method string + Endpoint string + Body []byte + Options map[string]string +} diff --git a/pkg/cloudca/api/response.go b/pkg/cloudca/api/response.go new file mode 100755 index 0000000..d78e871 --- /dev/null +++ b/pkg/cloudca/api/response.go @@ -0,0 +1,125 @@ +// Copyright © 2019 cloud.ca Authors. +// +// 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 api + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "strconv" +) + +//nolint +const ( + OK = 200 + MultipleChoices = 300 + BadRequest = 400 + NotFound = 404 +) + +// Error API error +type Error struct { + ErrorCode string `json:"errorCode"` + Message string `json:"message"` + Context map[string]interface{} `json:"context"` +} + +// Response API Response +type Response struct { + TaskID string + TaskStatus string + StatusCode int + Data []byte + Errors []Error + MetaData map[string]interface{} +} + +// NewResponse returns new response instance based on actual HTTP response +func NewResponse(r *http.Response) (*Response, error) { + respBody, err := ioutil.ReadAll(r.Body) + if err != nil { + return nil, err + } + response := Response{} + response.StatusCode = r.StatusCode + responseMap := map[string]*json.RawMessage{} + err = json.Unmarshal(respBody, &responseMap) + if err != nil { + return nil, err + } + + if val, ok := responseMap["taskId"]; ok { + err = json.Unmarshal(*val, &response.TaskID) + if err != nil { + return nil, err + } + } + + if val, ok := responseMap["taskStatus"]; ok { + err = json.Unmarshal(*val, &response.TaskStatus) + if err != nil { + return nil, err + } + } + + if val, ok := responseMap["data"]; ok { + response.Data = []byte(*val) + } + + if val, ok := responseMap["metadata"]; ok { + metadata := map[string]interface{}{} + err = json.Unmarshal(*val, &metadata) + if err != nil { + return nil, err + } + response.MetaData = metadata + } + + if val, ok := responseMap["errors"]; ok { + errors := []Error{} + err = json.Unmarshal(*val, &errors) + if err != nil { + return nil, err + } + response.Errors = errors + } else if !isInOKRange(r.StatusCode) { + return nil, fmt.Errorf("Unexpected. Received status " + r.Status + " but no errors in response body") + } + + return &response, nil +} + +// IsError returns true if API response has errors +func (r Response) IsError() bool { + return !isInOKRange(r.StatusCode) +} + +// ErrorResponse API Response with errors +type ErrorResponse Response + +// Error +func (r ErrorResponse) Error() string { + var errorStr = "[ERROR] Received HTTP status code " + strconv.Itoa(r.StatusCode) + "\n" + for _, e := range r.Errors { + context, _ := json.Marshal(e.Context) + errorStr += "[ERROR] Error Code: " + e.ErrorCode + ", Message: " + e.Message + ", Context: " + string(context) + "\n" + } + return errorStr +} + +func isInOKRange(statusCode int) bool { + return statusCode >= OK && statusCode < MultipleChoices +} diff --git a/pkg/cloudca/client.go b/pkg/cloudca/client.go new file mode 100755 index 0000000..0e0d40e --- /dev/null +++ b/pkg/cloudca/client.go @@ -0,0 +1,115 @@ +// Copyright © 2019 cloud.ca Authors. +// +// 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 cloudca contains the implementation of cloud.ca API +package cloudca + +import ( + "github.com/cloud-ca/cca/pkg/cloudca/api" + "github.com/cloud-ca/cca/pkg/cloudca/configuration" + "github.com/cloud-ca/cca/pkg/cloudca/services" + "github.com/cloud-ca/cca/pkg/cloudca/services/cloudca" +) + +// DefaultAPIURL default API URL to use +const DefaultAPIURL = "https://api.cloud.ca/v1/" + +// Client for interacting with cloud.ca API +type Client struct { + apiClient api.Client + Tasks services.TaskService + Environments configuration.EnvironmentService + Users configuration.UserService + ServiceConnections configuration.ServiceConnectionService + Organizations configuration.OrganizationService +} + +// GetAPIClient Get the API Client used by all the services +func (c Client) GetAPIClient() api.Client { + return c.apiClient +} + +// GetAPIURL Get the API url used to do he calls +func (c Client) GetAPIURL() string { + return c.GetAPIClient().GetAPIURL() +} + +// GetAPIKey Get the API key used in the calls +func (c Client) GetAPIKey() string { + return c.GetAPIClient().GetAPIKey() +} + +// GetResources get the Resources for a specific serviceCode and environmentName +// For now it assumes that the serviceCode belongs to a cloud.ca service type +func (c Client) GetResources(serviceCode string, environmentName string) (services.ServiceResources, error) { + //TODO: change to check service type of service code + return cloudca.NewResources(c.apiClient, serviceCode, environmentName), nil +} + +// NewClient Create a Client with the default URL +func NewClient(apiKey string) *Client { + return NewClientWithURL(DefaultAPIURL, apiKey) +} + +// NewClientWithURL Create a Client with a custom URL +func NewClientWithURL(apiURL string, apiKey string) *Client { + apiClient := api.NewClient(apiURL, apiKey) + return NewClientWithAPIClient(apiClient) +} + +// NewInsecureClientWithURL Create a Client with a custom URL that accepts insecure connections +func NewInsecureClientWithURL(apiURL string, apiKey string) *Client { + apiClient := api.NewInsecureClient(apiURL, apiKey) + return NewClientWithAPIClient(apiClient) +} + +// NewClientWithAPIClient Create a Client with a provided API client +func NewClientWithAPIClient(apiClient api.Client) *Client { + return &Client{ + apiClient: apiClient, + Tasks: services.NewTaskService(apiClient), + Environments: configuration.NewEnvironmentService(apiClient), + Users: configuration.NewUserService(apiClient), + ServiceConnections: configuration.NewServiceConnectionService(apiClient), + Organizations: configuration.NewOrganizationService(apiClient), + } +} + +/////////////////////////////////////////////////// +// Deperacated functions +/////////////////////////////////////////////////// + +// NewCcaClient Create a Client with the default URL +// **Deprecated, Use NewClient(apiKey string) instead** +func NewCcaClient(apiKey string) *Client { + return NewClient(apiKey) +} + +// NewCcaClientWithURL Create a Client with a custom URL +// **Deprecated, NewClientWithURL(apiKey string, apiKey string) instead** +func NewCcaClientWithURL(apiURL string, apiKey string) *Client { + return NewClientWithURL(apiURL, apiKey) +} + +// NewInsecureCcaClientWithURL Create a Client with a custom URL that accepts insecure connections +// **Deprecated, Use NewInsecureClientWithURL(apiURL string, apiKey string) instead** +func NewInsecureCcaClientWithURL(apiURL string, apiKey string) *Client { + return NewInsecureClientWithURL(apiURL, apiKey) +} + +// NewCcaClientWithAPIClient Create a Client with a provided API client +// **Deprecated, Use NewCcaClientWithApiClient(apiClient api.Client) instead** +func NewCcaClientWithAPIClient(apiClient api.Client) *Client { + return NewClientWithAPIClient(apiClient) +} diff --git a/vendor/github.com/cloud-ca/go-cloudca/configuration/configuration.go b/pkg/cloudca/configuration/configuration.go similarity index 98% rename from vendor/github.com/cloud-ca/go-cloudca/configuration/configuration.go rename to pkg/cloudca/configuration/configuration.go index b7d4b7b..1a9720f 100644 --- a/vendor/github.com/cloud-ca/go-cloudca/configuration/configuration.go +++ b/pkg/cloudca/configuration/configuration.go @@ -1,7 +1,7 @@ package configuration import ( - "github.com/cloud-ca/go-cloudca/api" + "github.com/cloud-ca/cca/pkg/cloudca/api" ) type ConfigurationService interface { diff --git a/vendor/github.com/cloud-ca/go-cloudca/configuration/environment.go b/pkg/cloudca/configuration/environment.go similarity index 98% rename from vendor/github.com/cloud-ca/go-cloudca/configuration/environment.go rename to pkg/cloudca/configuration/environment.go index e2414ed..a42fae0 100644 --- a/vendor/github.com/cloud-ca/go-cloudca/configuration/environment.go +++ b/pkg/cloudca/configuration/environment.go @@ -3,7 +3,7 @@ package configuration import ( "encoding/json" - "github.com/cloud-ca/go-cloudca/api" + "github.com/cloud-ca/cca/pkg/cloudca/api" ) const ( diff --git a/pkg/cloudca/configuration/environment_test.go b/pkg/cloudca/configuration/environment_test.go new file mode 100644 index 0000000..1a284b8 --- /dev/null +++ b/pkg/cloudca/configuration/environment_test.go @@ -0,0 +1,166 @@ +package configuration + +import ( + "testing" + + "github.com/cloud-ca/cca/pkg/cloudca/mocks/configuration_mocks" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" +) + +func TestConfigurationType(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // this is a weak test. It validates that the constant is the correct API endpoint, + // because the buildUrl method is unaccessible + assert.Equal(t, ENVIRONMENT_CONFIGURATION_TYPE, "environments") +} + +func TestGetEnvironmentReturnEnvironmentIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockConfigurationService := configuration_mocks.NewMockConfigurationService(ctrl) + environmentService := EnvironmentApi{ + configurationService: mockConfigurationService, + } + + response := `{"id":"13ca7410-9b4a-4fd7-ae2e-e5455b664faf", + "name":"patdev1", + "description":"pat dev 1", + "membership":"MANY_USERS", + "serviceConnection":{"id":"73983e63-e404-48aa-a89c-f41ca93af9cd","category":"IAAS","name":"patDev1","serviceCode":"dev1","type":"CloudCA"}, + "organization":{"id":"4b5e5c55-7aea-48e4-9287-d63b36457c51","entryPoint":"pat","name":"Test"}, + "users":[{"id":"062445f9-11d3-4e7b-9a84-908272a72250","userName":"pdube"}], + "roles":[{"id":"32a25a1e-0506-429f-a731-e8fcaaa01c4d","users":[],"isDefault":false,"name":"Read-only"}, + {"id":"517b40e5-20a8-44f0-a5d0-06ed20ee4d43","users":[{"id":"062445f9-11d3-4e7b-9a84-908272a72250","userName":"pdube"}],"isDefault":false,"name":"Environment Admin"} + ], + "deleted":false, + "version":5}` + + expectedEnvironment := Environment{ + Id: "13ca7410-9b4a-4fd7-ae2e-e5455b664faf", + Name: "patdev1", + Description: "pat dev 1", + ServiceConnection: ServiceConnection{ + Id: "73983e63-e404-48aa-a89c-f41ca93af9cd", + Name: "patDev1", + ServiceCode: "dev1", + }, + Organization: Organization{ + Id: "4b5e5c55-7aea-48e4-9287-d63b36457c51", + Name: "Test", + EntryPoint: "pat", + }, + Users: []User{ + { + Id: "062445f9-11d3-4e7b-9a84-908272a72250", + Username: "pdube", + }, + }, + Roles: []Role{ + { + Id: "32a25a1e-0506-429f-a731-e8fcaaa01c4d", + Name: "Read-only", + Users: []User{}, + }, + { + Id: "517b40e5-20a8-44f0-a5d0-06ed20ee4d43", + Name: "Environment Admin", + Users: []User{ + { + Id: "062445f9-11d3-4e7b-9a84-908272a72250", + Username: "pdube", + }, + }, + }, + }, + } + + mockConfigurationService.EXPECT().Get("13ca7410-9b4a-4fd7-ae2e-e5455b664faf", gomock.Any()).Return([]byte(response), nil) + + //when + environment, _ := environmentService.Get("13ca7410-9b4a-4fd7-ae2e-e5455b664faf") + + //then + if assert.NotNil(t, environment) { + assert.Equal(t, expectedEnvironment, *environment) + } +} + +func TestListEnvironmentReturnEnvironmentIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockConfigurationService := configuration_mocks.NewMockConfigurationService(ctrl) + environmentService := EnvironmentApi{ + configurationService: mockConfigurationService, + } + + response := `[{"id":"13ca7410-9b4a-4fd7-ae2e-e5455b664faf", + "name":"patdev1", + "description":"pat dev 1", + "membership":"MANY_USERS", + "serviceConnection":{"id":"73983e63-e404-48aa-a89c-f41ca93af9cd","category":"IAAS","name":"patDev1","serviceCode":"dev1","type":"CloudCA"}, + "organization":{"id":"4b5e5c55-7aea-48e4-9287-d63b36457c51","entryPoint":"pat","name":"Test"}, + "users":[{"id":"062445f9-11d3-4e7b-9a84-908272a72250","userName":"pdube"}], + "roles":[{"id":"32a25a1e-0506-429f-a731-e8fcaaa01c4d","users":[],"isDefault":false,"name":"Read-only"}, + {"id":"517b40e5-20a8-44f0-a5d0-06ed20ee4d43","users":[{"id":"062445f9-11d3-4e7b-9a84-908272a72250","userName":"pdube"}],"isDefault":false,"name":"Environment Admin"} + ], + "deleted":false, + "version":5}]` + + expectedEnvironments := []Environment{ + { + Id: "13ca7410-9b4a-4fd7-ae2e-e5455b664faf", + Name: "patdev1", + Description: "pat dev 1", + ServiceConnection: ServiceConnection{ + Id: "73983e63-e404-48aa-a89c-f41ca93af9cd", + Name: "patDev1", + ServiceCode: "dev1", + }, + Organization: Organization{ + Id: "4b5e5c55-7aea-48e4-9287-d63b36457c51", + Name: "Test", + EntryPoint: "pat", + }, + Users: []User{ + { + Id: "062445f9-11d3-4e7b-9a84-908272a72250", + Username: "pdube", + }, + }, + Roles: []Role{ + { + Id: "32a25a1e-0506-429f-a731-e8fcaaa01c4d", + Name: "Read-only", + Users: []User{}, + }, + { + Id: "517b40e5-20a8-44f0-a5d0-06ed20ee4d43", + Name: "Environment Admin", + Users: []User{ + { + Id: "062445f9-11d3-4e7b-9a84-908272a72250", + Username: "pdube", + }, + }, + }, + }, + }, + } + + mockConfigurationService.EXPECT().List(gomock.Any()).Return([]byte(response), nil) + + //when + environments, _ := environmentService.List() + + //then + if assert.NotNil(t, environments) { + assert.Equal(t, expectedEnvironments, environments) + } +} diff --git a/vendor/github.com/cloud-ca/go-cloudca/configuration/organization.go b/pkg/cloudca/configuration/organization.go similarity index 97% rename from vendor/github.com/cloud-ca/go-cloudca/configuration/organization.go rename to pkg/cloudca/configuration/organization.go index 22f1bef..4b9cb0b 100644 --- a/vendor/github.com/cloud-ca/go-cloudca/configuration/organization.go +++ b/pkg/cloudca/configuration/organization.go @@ -3,7 +3,7 @@ package configuration import ( "encoding/json" - "github.com/cloud-ca/go-cloudca/api" + "github.com/cloud-ca/cca/pkg/cloudca/api" ) type Organization struct { diff --git a/pkg/cloudca/configuration/organization_test.go b/pkg/cloudca/configuration/organization_test.go new file mode 100644 index 0000000..10a7cbe --- /dev/null +++ b/pkg/cloudca/configuration/organization_test.go @@ -0,0 +1,149 @@ +package configuration + +import ( + "encoding/json" + + "testing" + + "github.com/cloud-ca/cca/pkg/cloudca/mocks" + "github.com/cloud-ca/cca/pkg/cloudca/mocks/configuration_mocks" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" +) + +var ( + TEST_ORGANIZATION_ID = "org_id" + TEST_ORGANIZATION_NAME = "test_org_name" + TEST_ORGANIZATION_ENTRYPOINT = "test_entrypoint" + TEST_ORGANIZATION_USERS = []User{{Id: "test_user1"}, {Id: "test_user2"}} + TEST_ORGANIZATION_ENVIRONMENTS = []Environment{{Id: "test_env1"}, {Id: "test_env2"}} + TEST_ORGANIZATION_ROLES = []Role{{Id: "test_role"}} +) + +func buildOrganizationJsonResponse(organization *Organization) []byte { + j, _ := json.Marshal(organization) + return j +} + +func buildListOrganizationJsonResponse(organizations []Organization) []byte { + j, _ := json.Marshal(organizations) + return j +} + +func TestGetOrganizationReturnOrganizationIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockConfigurationService := configuration_mocks.NewMockConfigurationService(ctrl) + + organizationService := OrganizationApi{ + configurationService: mockConfigurationService, + } + + expectedOrganization := Organization{Id: TEST_ORGANIZATION_ID, + Name: TEST_ORGANIZATION_NAME, + EntryPoint: TEST_ORGANIZATION_ENTRYPOINT, + Users: TEST_ORGANIZATION_USERS, + Environments: TEST_ORGANIZATION_ENVIRONMENTS, + Roles: TEST_ORGANIZATION_ROLES} + + mockConfigurationService.EXPECT().Get(TEST_ORGANIZATION_ID, gomock.Any()).Return(buildOrganizationJsonResponse(&expectedOrganization), nil) + + //when + organization, _ := organizationService.Get(TEST_ORGANIZATION_ID) + + //then + if assert.NotNil(t, organization) { + assert.Equal(t, expectedOrganization, *organization) + } +} + +func TestGetOrganizationReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockConfigurationService := configuration_mocks.NewMockConfigurationService(ctrl) + + organizationService := OrganizationApi{ + configurationService: mockConfigurationService, + } + + mockError := mocks.MockError{"some_get_error"} + + mockConfigurationService.EXPECT().Get(TEST_ORGANIZATION_ID, gomock.Any()).Return(nil, mockError) + + //when + organization, err := organizationService.Get(TEST_ORGANIZATION_ID) + + //then + assert.Nil(t, organization) + assert.Equal(t, mockError, err) + +} + +func TestListOrganizationReturnDiskOfferingsIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockConfigurationService := configuration_mocks.NewMockConfigurationService(ctrl) + + organizationService := OrganizationApi{ + configurationService: mockConfigurationService, + } + + expectedOrganizations := []Organization{ + { + Id: "org_id_1", + Name: "org_name_1", + EntryPoint: "org_entrypoint_1", + Users: []User{{Id: "user1"}}, + Environments: []Environment{}, + Roles: []Role{{Id: "test_role_1"}}, + }, + { + Id: "org_id_2", + Name: "org_name_2", + EntryPoint: "org_entrypoint_2", + Users: []User{{Id: "user2"}}, + Environments: []Environment{{Id: "env1"}}, + Roles: []Role{{Id: "test_role_2"}}, + }, + } + + mockConfigurationService.EXPECT().List(gomock.Any()).Return(buildListOrganizationJsonResponse(expectedOrganizations), nil) + + //when + organizations, _ := organizationService.List() + + //then + if assert.NotNil(t, organizations) { + assert.Equal(t, expectedOrganizations, organizations) + } +} + +func TestListOrganizationReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockConfigurationService := configuration_mocks.NewMockConfigurationService(ctrl) + + organizationService := OrganizationApi{ + configurationService: mockConfigurationService, + } + + mockError := mocks.MockError{"some_list_error"} + + mockConfigurationService.EXPECT().List(gomock.Any()).Return(nil, mockError) + + //when + organizations, err := organizationService.List() + + //then + assert.Nil(t, organizations) + assert.Equal(t, mockError, err) + +} diff --git a/vendor/github.com/cloud-ca/go-cloudca/configuration/role.go b/pkg/cloudca/configuration/role.go similarity index 100% rename from vendor/github.com/cloud-ca/go-cloudca/configuration/role.go rename to pkg/cloudca/configuration/role.go diff --git a/vendor/github.com/cloud-ca/go-cloudca/configuration/service_connection.go b/pkg/cloudca/configuration/service_connection.go similarity index 97% rename from vendor/github.com/cloud-ca/go-cloudca/configuration/service_connection.go rename to pkg/cloudca/configuration/service_connection.go index 9cbe494..0f085f6 100644 --- a/vendor/github.com/cloud-ca/go-cloudca/configuration/service_connection.go +++ b/pkg/cloudca/configuration/service_connection.go @@ -2,7 +2,8 @@ package configuration import ( "encoding/json" - "github.com/cloud-ca/go-cloudca/api" + + "github.com/cloud-ca/cca/pkg/cloudca/api" ) type ServiceConnection struct { diff --git a/pkg/cloudca/configuration/service_connection_test.go b/pkg/cloudca/configuration/service_connection_test.go new file mode 100644 index 0000000..ecd3d23 --- /dev/null +++ b/pkg/cloudca/configuration/service_connection_test.go @@ -0,0 +1,137 @@ +package configuration + +import ( + "encoding/json" + + "testing" + + "github.com/cloud-ca/cca/pkg/cloudca/mocks" + "github.com/cloud-ca/cca/pkg/cloudca/mocks/configuration_mocks" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" +) + +var ( + TEST_SERVICE_CONNECTION_ID = "connection_id" + TEST_SERVICE_CONNECTION_NAME = "test_connection_name" + TEST_SERVICE_CONNECTION_SERVICE_CODE = "test_connection_code" +) + +func buildServiceConnectionJsonResponse(serviceConnection *ServiceConnection) []byte { + j, _ := json.Marshal(serviceConnection) + return j +} + +func buildListServiceConnectionJsonResponse(serviceConnections []ServiceConnection) []byte { + j, _ := json.Marshal(serviceConnections) + return j +} + +func TestGetServiceConnectionReturnServiceConnectionIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockConfigurationService := configuration_mocks.NewMockConfigurationService(ctrl) + + serviceConnectionService := ServiceConnectionApi{ + configurationService: mockConfigurationService, + } + + expectedServiceConnection := ServiceConnection{Id: TEST_SERVICE_CONNECTION_ID, + Name: TEST_SERVICE_CONNECTION_NAME, + ServiceCode: TEST_SERVICE_CONNECTION_SERVICE_CODE} + + mockConfigurationService.EXPECT().Get(TEST_SERVICE_CONNECTION_ID, gomock.Any()).Return(buildServiceConnectionJsonResponse(&expectedServiceConnection), nil) + + //when + serviceConnection, _ := serviceConnectionService.Get(TEST_SERVICE_CONNECTION_ID) + + //then + if assert.NotNil(t, serviceConnection) { + assert.Equal(t, expectedServiceConnection, *serviceConnection) + } +} + +func TestGetServiceConnectionReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockConfigurationService := configuration_mocks.NewMockConfigurationService(ctrl) + + serviceConnectionService := ServiceConnectionApi{ + configurationService: mockConfigurationService, + } + + mockError := mocks.MockError{"some_get_error"} + + mockConfigurationService.EXPECT().Get(TEST_SERVICE_CONNECTION_ID, gomock.Any()).Return(nil, mockError) + + //when + serviceConnection, err := serviceConnectionService.Get(TEST_SERVICE_CONNECTION_ID) + + //then + assert.Nil(t, serviceConnection) + assert.Equal(t, mockError, err) + +} + +func TestListServiceConnectionReturnDiskOfferingsIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockConfigurationService := configuration_mocks.NewMockConfigurationService(ctrl) + + serviceConnectionService := ServiceConnectionApi{ + configurationService: mockConfigurationService, + } + + expectedServiceConnections := []ServiceConnection{ + { + Id: "connection_1", + Name: "connection_name_1", + ServiceCode: "connection_code_1", + }, + { + Id: "connection_2", + Name: "connection_name_2", + ServiceCode: "connection_code_2", + }, + } + + mockConfigurationService.EXPECT().List(gomock.Any()).Return(buildListServiceConnectionJsonResponse(expectedServiceConnections), nil) + + //when + serviceConnections, _ := serviceConnectionService.List() + + //then + if assert.NotNil(t, serviceConnections) { + assert.Equal(t, expectedServiceConnections, serviceConnections) + } +} + +func TestListServiceConnectionReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockConfigurationService := configuration_mocks.NewMockConfigurationService(ctrl) + + serviceConnectionService := ServiceConnectionApi{ + configurationService: mockConfigurationService, + } + + mockError := mocks.MockError{"some_list_error"} + + mockConfigurationService.EXPECT().List(gomock.Any()).Return(nil, mockError) + + //when + serviceConnections, err := serviceConnectionService.List() + + //then + assert.Nil(t, serviceConnections) + assert.Equal(t, mockError, err) + +} diff --git a/vendor/github.com/cloud-ca/go-cloudca/configuration/user.go b/pkg/cloudca/configuration/user.go similarity index 97% rename from vendor/github.com/cloud-ca/go-cloudca/configuration/user.go rename to pkg/cloudca/configuration/user.go index a35d900..bfe14d3 100644 --- a/vendor/github.com/cloud-ca/go-cloudca/configuration/user.go +++ b/pkg/cloudca/configuration/user.go @@ -2,7 +2,8 @@ package configuration import ( "encoding/json" - "github.com/cloud-ca/go-cloudca/api" + + "github.com/cloud-ca/cca/pkg/cloudca/api" ) type User struct { diff --git a/pkg/cloudca/mocks/api_mocks/mock_api.go b/pkg/cloudca/mocks/api_mocks/mock_api.go new file mode 100644 index 0000000..046b81e --- /dev/null +++ b/pkg/cloudca/mocks/api_mocks/mock_api.go @@ -0,0 +1,61 @@ +// Automatically generated by MockGen. DO NOT EDIT! +// Source: api/api.go + +package api_mocks + +import ( + "github.com/cloud-ca/cca/pkg/cloudca/api" + gomock "github.com/golang/mock/gomock" +) + +// Mock of ApiClient interface +type MockApiClient struct { + ctrl *gomock.Controller + recorder *_MockApiClientRecorder +} + +// Recorder for MockApiClient (not exported) +type _MockApiClientRecorder struct { + mock *MockApiClient +} + +func NewMockApiClient(ctrl *gomock.Controller) *MockApiClient { + mock := &MockApiClient{ctrl: ctrl} + mock.recorder = &_MockApiClientRecorder{mock} + return mock +} + +func (_m *MockApiClient) EXPECT() *_MockApiClientRecorder { + return _m.recorder +} + +func (_m *MockApiClient) Do(request api.CcaRequest) (*api.CcaResponse, error) { + ret := _m.ctrl.Call(_m, "Do", request) + ret0, _ := ret[0].(*api.CcaResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +func (_mr *_MockApiClientRecorder) Do(arg0 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "Do", arg0) +} + +func (_m *MockApiClient) GetApiURL() string { + ret := _m.ctrl.Call(_m, "GetApiURL") + ret0, _ := ret[0].(string) + return ret0 +} + +func (_mr *_MockApiClientRecorder) GetApiURL() *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "GetApiURL") +} + +func (_m *MockApiClient) GetApiKey() string { + ret := _m.ctrl.Call(_m, "GetApiKey") + ret0, _ := ret[0].(string) + return ret0 +} + +func (_mr *_MockApiClientRecorder) GetApiKey() *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "GetApiKey") +} diff --git a/pkg/cloudca/mocks/configuration_mocks/mock_configuration.go b/pkg/cloudca/mocks/configuration_mocks/mock_configuration.go new file mode 100644 index 0000000..3824c7f --- /dev/null +++ b/pkg/cloudca/mocks/configuration_mocks/mock_configuration.go @@ -0,0 +1,84 @@ +// Automatically generated by MockGen. DO NOT EDIT! +// Source: configuration/configuration.go + +package configuration_mocks + +import ( + gomock "github.com/golang/mock/gomock" +) + +// Mock of ConfigurationService interface +type MockConfigurationService struct { + ctrl *gomock.Controller + recorder *_MockConfigurationServiceRecorder +} + +// Recorder for MockConfigurationService (not exported) +type _MockConfigurationServiceRecorder struct { + mock *MockConfigurationService +} + +func NewMockConfigurationService(ctrl *gomock.Controller) *MockConfigurationService { + mock := &MockConfigurationService{ctrl: ctrl} + mock.recorder = &_MockConfigurationServiceRecorder{mock} + return mock +} + +func (_m *MockConfigurationService) EXPECT() *_MockConfigurationServiceRecorder { + return _m.recorder +} + +func (_m *MockConfigurationService) Get(id string, options map[string]string) ([]byte, error) { + ret := _m.ctrl.Call(_m, "Get", id, options) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +func (_mr *_MockConfigurationServiceRecorder) Get(arg0, arg1 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "Get", arg0, arg1) +} + +func (_m *MockConfigurationService) List(options map[string]string) ([]byte, error) { + ret := _m.ctrl.Call(_m, "List", options) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +func (_mr *_MockConfigurationServiceRecorder) List(arg0 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "List", arg0) +} + +func (_m *MockConfigurationService) Create(body []byte, options map[string]string) ([]byte, error) { + ret := _m.ctrl.Call(_m, "Create", body, options) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +func (_mr *_MockConfigurationServiceRecorder) Create(arg0, arg1 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "Create", arg0, arg1) +} + +func (_m *MockConfigurationService) Update(id string, body []byte, options map[string]string) ([]byte, error) { + ret := _m.ctrl.Call(_m, "Update", id, body, options) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +func (_mr *_MockConfigurationServiceRecorder) Update(arg0, arg1, arg2 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "Update", arg0, arg1, arg2) +} + +func (_m *MockConfigurationService) Delete(id string, body []byte, options map[string]string) ([]byte, error) { + ret := _m.ctrl.Call(_m, "Delete", id, body, options) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +func (_mr *_MockConfigurationServiceRecorder) Delete(arg0, arg1, arg2 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "Delete", arg0, arg1, arg2) +} diff --git a/pkg/cloudca/mocks/mock_error.go b/pkg/cloudca/mocks/mock_error.go new file mode 100644 index 0000000..e9cf21a --- /dev/null +++ b/pkg/cloudca/mocks/mock_error.go @@ -0,0 +1,9 @@ +package mocks + +type MockError struct { + Message string +} + +func (mockError MockError) Error() string { + return mockError.Message +} diff --git a/pkg/cloudca/mocks/services_mocks/mock_entity.go b/pkg/cloudca/mocks/services_mocks/mock_entity.go new file mode 100644 index 0000000..c602fc5 --- /dev/null +++ b/pkg/cloudca/mocks/services_mocks/mock_entity.go @@ -0,0 +1,95 @@ +// Automatically generated by MockGen. DO NOT EDIT! +// Source: ./services/entity.go + +package services_mocks + +import ( + gomock "github.com/golang/mock/gomock" +) + +// Mock of EntityService interface +type MockEntityService struct { + ctrl *gomock.Controller + recorder *_MockEntityServiceRecorder +} + +// Recorder for MockEntityService (not exported) +type _MockEntityServiceRecorder struct { + mock *MockEntityService +} + +func NewMockEntityService(ctrl *gomock.Controller) *MockEntityService { + mock := &MockEntityService{ctrl: ctrl} + mock.recorder = &_MockEntityServiceRecorder{mock} + return mock +} + +func (_m *MockEntityService) EXPECT() *_MockEntityServiceRecorder { + return _m.recorder +} + +func (_m *MockEntityService) Get(id string, options map[string]string) ([]byte, error) { + ret := _m.ctrl.Call(_m, "Get", id, options) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +func (_mr *_MockEntityServiceRecorder) Get(arg0, arg1 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "Get", arg0, arg1) +} + +func (_m *MockEntityService) List(options map[string]string) ([]byte, error) { + ret := _m.ctrl.Call(_m, "List", options) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +func (_mr *_MockEntityServiceRecorder) List(arg0 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "List", arg0) +} + +func (_m *MockEntityService) Execute(id string, operation string, body []byte, options map[string]string) ([]byte, error) { + ret := _m.ctrl.Call(_m, "Execute", id, operation, body, options) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +func (_mr *_MockEntityServiceRecorder) Execute(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "Execute", arg0, arg1, arg2, arg3) +} + +func (_m *MockEntityService) Create(body []byte, options map[string]string) ([]byte, error) { + ret := _m.ctrl.Call(_m, "Create", body, options) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +func (_mr *_MockEntityServiceRecorder) Create(arg0, arg1 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "Create", arg0, arg1) +} + +func (_m *MockEntityService) Update(id string, body []byte, options map[string]string) ([]byte, error) { + ret := _m.ctrl.Call(_m, "Update", id, body, options) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +func (_mr *_MockEntityServiceRecorder) Update(arg0, arg1, arg2 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "Update", arg0, arg1, arg2) +} + +func (_m *MockEntityService) Delete(id string, body []byte, options map[string]string) ([]byte, error) { + ret := _m.ctrl.Call(_m, "Delete", id, body, options) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +func (_mr *_MockEntityServiceRecorder) Delete(arg0, arg1, arg2 interface{}) *gomock.Call { + return _mr.mock.ctrl.RecordCall(_mr.mock, "Delete", arg0, arg1, arg2) +} diff --git a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/affinity_groups.go b/pkg/cloudca/services/cloudca/affinity_groups.go similarity index 95% rename from vendor/github.com/cloud-ca/go-cloudca/services/cloudca/affinity_groups.go rename to pkg/cloudca/services/cloudca/affinity_groups.go index 6f3f50e..0129d76 100644 --- a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/affinity_groups.go +++ b/pkg/cloudca/services/cloudca/affinity_groups.go @@ -2,8 +2,9 @@ package cloudca import ( "encoding/json" - "github.com/cloud-ca/go-cloudca/api" - "github.com/cloud-ca/go-cloudca/services" + + "github.com/cloud-ca/cca/pkg/cloudca/api" + "github.com/cloud-ca/cca/pkg/cloudca/services" ) type AffinityGroup struct { diff --git a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/compute_offering.go b/pkg/cloudca/services/cloudca/compute_offering.go similarity index 95% rename from vendor/github.com/cloud-ca/go-cloudca/services/cloudca/compute_offering.go rename to pkg/cloudca/services/cloudca/compute_offering.go index c8694d1..a0c8569 100644 --- a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/compute_offering.go +++ b/pkg/cloudca/services/cloudca/compute_offering.go @@ -2,8 +2,9 @@ package cloudca import ( "encoding/json" - "github.com/cloud-ca/go-cloudca/api" - "github.com/cloud-ca/go-cloudca/services" + + "github.com/cloud-ca/cca/pkg/cloudca/api" + "github.com/cloud-ca/cca/pkg/cloudca/services" ) type ComputeOffering struct { diff --git a/pkg/cloudca/services/cloudca/compute_offering_test.go b/pkg/cloudca/services/cloudca/compute_offering_test.go new file mode 100644 index 0000000..1365a50 --- /dev/null +++ b/pkg/cloudca/services/cloudca/compute_offering_test.go @@ -0,0 +1,152 @@ +package cloudca + +import ( + "strconv" + "testing" + + "github.com/cloud-ca/cca/pkg/cloudca/mocks" + "github.com/cloud-ca/cca/pkg/cloudca/mocks/services_mocks" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" +) + +const ( + TEST_COMPUTE_OFFERING_ID = "some_id" + TEST_COMPUTE_OFFERING_NAME = "test_compute_offering" + TEST_COMPUTE_OFFERING_MEMORY = 4096 + TEST_COMPUTE_OFFERING_CPU_NUMBER = 2 + TEST_COMPUTE_OFFERING_CUSTOM = false +) + +func buildComputeOfferingJsonResponse(computeOffering *ComputeOffering) []byte { + return []byte(`{"id": "` + computeOffering.Id + + `","name":"` + computeOffering.Name + + `","memoryInMB":` + strconv.Itoa(computeOffering.MemoryInMB) + + `,"cpuCount": ` + strconv.Itoa(computeOffering.CpuCount) + + `,"custom": ` + strconv.FormatBool(computeOffering.Custom) + `}`) +} + +func buildListComputeOfferingJsonResponse(computeOfferings []ComputeOffering) []byte { + resp := `[` + for i, d := range computeOfferings { + resp += string(buildComputeOfferingJsonResponse(&d)) + if i != len(computeOfferings)-1 { + resp += `,` + } + } + resp += `]` + return []byte(resp) +} + +func TestGetComputeOfferingReturnComputeOfferingIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + computeOfferingService := ComputeOfferingApi{ + entityService: mockEntityService, + } + + expectedComputeOffering := ComputeOffering{Id: TEST_COMPUTE_OFFERING_ID, + Name: TEST_COMPUTE_OFFERING_NAME, + MemoryInMB: TEST_COMPUTE_OFFERING_MEMORY, + CpuCount: TEST_COMPUTE_OFFERING_CPU_NUMBER, + Custom: TEST_COMPUTE_OFFERING_CUSTOM} + + mockEntityService.EXPECT().Get(TEST_COMPUTE_OFFERING_ID, gomock.Any()).Return(buildComputeOfferingJsonResponse(&expectedComputeOffering), nil) + + //when + computeOffering, _ := computeOfferingService.Get(TEST_COMPUTE_OFFERING_ID) + + //then + if assert.NotNil(t, computeOffering) { + assert.Equal(t, expectedComputeOffering, *computeOffering) + } +} + +func TestGetComputeOfferingReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + computeOfferingService := ComputeOfferingApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_get_error"} + + mockEntityService.EXPECT().Get(TEST_COMPUTE_OFFERING_ID, gomock.Any()).Return(nil, mockError) + + //when + computeOffering, err := computeOfferingService.Get(TEST_COMPUTE_OFFERING_ID) + + //then + assert.Nil(t, computeOffering) + assert.Equal(t, mockError, err) + +} + +func TestListComputeOfferingReturnComputeOfferingsIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + computeOfferingService := ComputeOfferingApi{ + entityService: mockEntityService, + } + + expectedComputeOfferings := []ComputeOffering{ + { + Id: "list_id_1", + Name: "list_name_1", + MemoryInMB: 1024, + CpuCount: 1, + }, + { + Id: "list_id_2", + Name: "list_name_2", + MemoryInMB: 2048, + CpuCount: 2, + }, + } + + mockEntityService.EXPECT().List(gomock.Any()).Return(buildListComputeOfferingJsonResponse(expectedComputeOfferings), nil) + + //when + computeOfferings, _ := computeOfferingService.List() + + //then + if assert.NotNil(t, computeOfferings) { + assert.Equal(t, expectedComputeOfferings, computeOfferings) + } +} + +func TestListComputeOfferingReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + computeOfferingService := ComputeOfferingApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_list_error"} + + mockEntityService.EXPECT().List(gomock.Any()).Return(nil, mockError) + + //when + computeOfferings, err := computeOfferingService.List() + + //then + assert.Nil(t, computeOfferings) + assert.Equal(t, mockError, err) + +} diff --git a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/disk_offering.go b/pkg/cloudca/services/cloudca/disk_offering.go similarity index 95% rename from vendor/github.com/cloud-ca/go-cloudca/services/cloudca/disk_offering.go rename to pkg/cloudca/services/cloudca/disk_offering.go index 83b15f4..f3c6cfd 100644 --- a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/disk_offering.go +++ b/pkg/cloudca/services/cloudca/disk_offering.go @@ -3,8 +3,8 @@ package cloudca import ( "encoding/json" - "github.com/cloud-ca/go-cloudca/api" - "github.com/cloud-ca/go-cloudca/services" + "github.com/cloud-ca/cca/pkg/cloudca/api" + "github.com/cloud-ca/cca/pkg/cloudca/services" ) type DiskOffering struct { diff --git a/pkg/cloudca/services/cloudca/disk_offering_test.go b/pkg/cloudca/services/cloudca/disk_offering_test.go new file mode 100644 index 0000000..7bdc392 --- /dev/null +++ b/pkg/cloudca/services/cloudca/disk_offering_test.go @@ -0,0 +1,144 @@ +package cloudca + +import ( + "strconv" + "testing" + + "github.com/cloud-ca/cca/pkg/cloudca/mocks" + "github.com/cloud-ca/cca/pkg/cloudca/mocks/services_mocks" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" +) + +const ( + TEST_DISK_OFFERING_ID = "some_id" + TEST_DISK_OFFERING_NAME = "test_disk_offering" + TEST_DISK_OFFERING_GBSIZE = 50 +) + +func buildDiskOfferingJsonResponse(diskOffering *DiskOffering) []byte { + return []byte(`{"id": "` + diskOffering.Id + + `","name":"` + diskOffering.Name + + `","gbSize":` + strconv.Itoa(diskOffering.GbSize) + `}`) +} + +func buildListDiskOfferingJsonResponse(diskOfferings []DiskOffering) []byte { + resp := `[` + for i, d := range diskOfferings { + resp += string(buildDiskOfferingJsonResponse(&d)) + if i != len(diskOfferings)-1 { + resp += `,` + } + } + resp += `]` + return []byte(resp) +} + +func TestGetDiskOfferingReturnDiskOfferingIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + diskOfferingService := DiskOfferingApi{ + entityService: mockEntityService, + } + + expectedDiskOffering := DiskOffering{Id: TEST_DISK_OFFERING_ID, + Name: TEST_DISK_OFFERING_NAME, + GbSize: TEST_DISK_OFFERING_GBSIZE} + + mockEntityService.EXPECT().Get(TEST_DISK_OFFERING_ID, gomock.Any()).Return(buildDiskOfferingJsonResponse(&expectedDiskOffering), nil) + + //when + diskOffering, _ := diskOfferingService.Get(TEST_DISK_OFFERING_ID) + + //then + if assert.NotNil(t, diskOffering) { + assert.Equal(t, expectedDiskOffering, *diskOffering) + } +} + +func TestGetDiskOfferingReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + diskOfferingService := DiskOfferingApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_get_error"} + + mockEntityService.EXPECT().Get(TEST_DISK_OFFERING_ID, gomock.Any()).Return(nil, mockError) + + //when + diskOffering, err := diskOfferingService.Get(TEST_DISK_OFFERING_ID) + + //then + assert.Nil(t, diskOffering) + assert.Equal(t, mockError, err) + +} + +func TestListDiskOfferingReturnDiskOfferingsIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + diskOfferingService := DiskOfferingApi{ + entityService: mockEntityService, + } + + expectedDiskOfferings := []DiskOffering{ + { + Id: "list_id_1", + Name: "list_name_1", + GbSize: 51, + }, + { + Id: "list_id_2", + Name: "list_name_2", + GbSize: 52, + }, + } + + mockEntityService.EXPECT().List(gomock.Any()).Return(buildListDiskOfferingJsonResponse(expectedDiskOfferings), nil) + + //when + diskOfferings, _ := diskOfferingService.List() + + //then + if assert.NotNil(t, diskOfferings) { + assert.Equal(t, expectedDiskOfferings, diskOfferings) + } +} + +func TestListDiskOfferingReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + diskOfferingService := DiskOfferingApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_list_error"} + + mockEntityService.EXPECT().List(gomock.Any()).Return(nil, mockError) + + //when + diskOfferings, err := diskOfferingService.List() + + //then + assert.Nil(t, diskOfferings) + assert.Equal(t, mockError, err) + +} diff --git a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/entity_type.go b/pkg/cloudca/services/cloudca/entity_type.go similarity index 100% rename from vendor/github.com/cloud-ca/go-cloudca/services/cloudca/entity_type.go rename to pkg/cloudca/services/cloudca/entity_type.go diff --git a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/instance.go b/pkg/cloudca/services/cloudca/instance.go old mode 100644 new mode 100755 similarity index 99% rename from vendor/github.com/cloud-ca/go-cloudca/services/cloudca/instance.go rename to pkg/cloudca/services/cloudca/instance.go index 4edd61d..c0c1925 --- a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/instance.go +++ b/pkg/cloudca/services/cloudca/instance.go @@ -4,8 +4,8 @@ import ( "encoding/json" "strings" - "github.com/cloud-ca/go-cloudca/api" - "github.com/cloud-ca/go-cloudca/services" + "github.com/cloud-ca/cca/pkg/cloudca/api" + "github.com/cloud-ca/cca/pkg/cloudca/services" ) const ( diff --git a/pkg/cloudca/services/cloudca/instance_test.go b/pkg/cloudca/services/cloudca/instance_test.go new file mode 100644 index 0000000..b8729c9 --- /dev/null +++ b/pkg/cloudca/services/cloudca/instance_test.go @@ -0,0 +1,816 @@ +package cloudca + +import ( + "strconv" + "testing" + + "github.com/cloud-ca/cca/pkg/cloudca/api" + "github.com/cloud-ca/cca/pkg/cloudca/mocks" + "github.com/cloud-ca/cca/pkg/cloudca/mocks/services_mocks" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" +) + +const ( + TEST_INSTANCE_ID = "test_instance_id" + TEST_INSTANCE_NAME = "test_instance" + TEST_INSTANCE_STATE = "test_instance_state" + TEST_INSTANCE_TEMPLATE_ID = "test_instance_template_id" + TEST_INSTANCE_TEMPLATE_NAME = "test_instance_template_name" + TEST_INSTANCE_IS_PASSWORD_ENABLED = true + TEST_INSTANCE_IS_SSH_KEY_ENABLED = false + TEST_INSTANCE_USERNAME = "test_instance_username" + TEST_INSTANCE_COMPUTE_OFFERING_ID = "test_instance_compute_offering_id" + TEST_INSTANCE_COMPUTE_OFFERING_NAME = "test_instance_compute_offering_name" + TEST_INSTANCE_CPU_NUMBER = 2 + TEST_INSTANCE_MEMORY_IN_MB = 8000 + TEST_INSTANCE_ZONE_ID = "test_instance_zone_id" + TEST_INSTANCE_ZONE_NAME = "test_instance_zone_name" + TEST_INSTANCE_PROJECT_ID = "test_instance_project_id" + TEST_INSTANCE_NETWORK_ID = "test_instance_network_id" + TEST_INSTANCE_NETWORK_NAME = "test_instance_network_name" + TEST_INSTANCE_VPC_ID = "test_instance_vpc_id" + TEST_INSTANCE_VPC_NAME = "test_instance_vpc_name" + TEST_INSTANCE_MAC_ADDRESS = "test_instance_mac_address" + TEST_INSTANCE_IP_ADDRESS = "test_instance_ip_address" + TEST_INSTANCE_VOLUME_ID_TO_ATTACH = "test_volume_id_to_attach" + TEST_INSTANCE_USER_DATA = "test_instance_user_data" + TEST_INSTANCE_PUBLIC_KEY = "test_instance_public_key" +) + +func buildTestInstanceJsonResponse(instance *Instance) []byte { + return []byte(`{"id": "` + instance.Id + `", ` + + `"name":"` + instance.Name + `", ` + + `"state":"` + instance.State + `", ` + + `"templateId":"` + instance.TemplateId + `", ` + + `"templateName":"` + instance.TemplateName + `", ` + + `"isPasswordEnabled":` + strconv.FormatBool(instance.IsPasswordEnabled) + `, ` + + `"isSshKeyEnabled":` + strconv.FormatBool(instance.IsSSHKeyEnabled) + `, ` + + `"username":"` + instance.Username + `", ` + + `"computeOfferingId":"` + instance.ComputeOfferingId + `", ` + + `"computeOfferingName":"` + instance.ComputeOfferingName + `", ` + + `"cpuCount": ` + strconv.Itoa(instance.CpuCount) + `, ` + + `"memoryInMB": ` + strconv.Itoa(instance.MemoryInMB) + `, ` + + `"zoneId":"` + instance.ZoneId + `", ` + + `"zoneName":"` + instance.ZoneName + `", ` + + `"projectId":"` + instance.ProjectId + `", ` + + `"networkId":"` + instance.NetworkId + `", ` + + `"networkName":"` + instance.NetworkName + `", ` + + `"vpcId":"` + instance.VpcId + `", ` + + `"vpcName":"` + instance.VpcName + `", ` + + `"macAddress":"` + instance.MacAddress + `", ` + + `"ipAddress":"` + instance.IpAddress + `", ` + + `"volumeIdToAttach":"` + instance.VolumeIdToAttach + `", ` + + `"publicKey":"` + instance.PublicKey + `", ` + + `"userData":"` + instance.UserData + `"}`) +} + +func buildListTestInstanceJsonResponse(instances []Instance) []byte { + resp := `[` + for i, inst := range instances { + resp += string(buildTestInstanceJsonResponse(&inst)) + if i != len(instances)-1 { + resp += `,` + } + } + resp += `]` + return []byte(resp) +} + +func TestGetInstanceReturnInstanceIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + instanceService := InstanceApi{ + entityService: mockEntityService, + } + + expectedInstance := Instance{Id: TEST_INSTANCE_ID, + Name: TEST_INSTANCE_NAME, + State: TEST_INSTANCE_STATE, + TemplateId: TEST_INSTANCE_TEMPLATE_ID, + TemplateName: TEST_INSTANCE_TEMPLATE_NAME, + IsPasswordEnabled: TEST_INSTANCE_IS_PASSWORD_ENABLED, + IsSSHKeyEnabled: TEST_INSTANCE_IS_SSH_KEY_ENABLED, + Username: TEST_INSTANCE_USERNAME, + ComputeOfferingId: TEST_INSTANCE_COMPUTE_OFFERING_ID, + ComputeOfferingName: TEST_INSTANCE_COMPUTE_OFFERING_NAME, + CpuCount: TEST_INSTANCE_CPU_NUMBER, + MemoryInMB: TEST_INSTANCE_MEMORY_IN_MB, + ZoneId: TEST_INSTANCE_ZONE_ID, + ZoneName: TEST_INSTANCE_ZONE_NAME, + ProjectId: TEST_INSTANCE_PROJECT_ID, + NetworkId: TEST_INSTANCE_NETWORK_ID, + NetworkName: TEST_INSTANCE_NETWORK_NAME, + VpcId: TEST_INSTANCE_VPC_ID, + VpcName: TEST_INSTANCE_VPC_NAME, + MacAddress: TEST_INSTANCE_MAC_ADDRESS, + IpAddress: TEST_INSTANCE_IP_ADDRESS, + VolumeIdToAttach: TEST_INSTANCE_VOLUME_ID_TO_ATTACH, + PublicKey: TEST_INSTANCE_PUBLIC_KEY, + UserData: TEST_INSTANCE_USER_DATA} + + mockEntityService.EXPECT().Get(TEST_INSTANCE_ID, gomock.Any()).Return(buildTestInstanceJsonResponse(&expectedInstance), nil) + + //when + instance, _ := instanceService.Get(TEST_INSTANCE_ID) + + //then + if assert.NotNil(t, instance) { + assert.Equal(t, expectedInstance, *instance) + } +} + +func TestGetInstanceReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + instanceService := InstanceApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_get_error"} + + mockEntityService.EXPECT().Get(TEST_INSTANCE_ID, gomock.Any()).Return(nil, mockError) + + //when + instance, err := instanceService.Get(TEST_INSTANCE_ID) + + //then + assert.Nil(t, instance) + assert.Equal(t, mockError, err) + +} + +func TestListInstanceReturnInstancesIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + instanceService := InstanceApi{ + entityService: mockEntityService, + } + + expectedInstance1 := Instance{Id: "list_id_1", + Name: "list_name_1", + State: "list_state_1", + TemplateId: "list_template_id_1", + TemplateName: "list_template_name_1", + IsPasswordEnabled: false, + IsSSHKeyEnabled: true, + Username: "list_username_1", + ComputeOfferingId: "list_compute_offering_id_1", + ComputeOfferingName: "list_compute_offering_name_1", + CpuCount: 2, + MemoryInMB: 12425, + ZoneId: "list_zone_id_1", + ZoneName: "list_zone_name_1", + ProjectId: "list_project_id_1", + NetworkId: "list_network_id_1", + NetworkName: "list_network_name_1", + VpcId: "list_vpc_id_1", + VpcName: "list_vpc_name_1", + MacAddress: "list_mac_address_1", + VolumeIdToAttach: "list_volume_id_to_attach_1", + IpAddress: "list_ip_address_1", + PublicKey: "list_public_key_1", + UserData: "list_user_data_1"} + + expectedInstances := []Instance{expectedInstance1} + + mockEntityService.EXPECT().List(gomock.Any()).Return(buildListTestInstanceJsonResponse(expectedInstances), nil) + + //when + instances, _ := instanceService.List() + + //then + if assert.NotNil(t, instances) { + assert.Equal(t, expectedInstances, instances) + } +} + +func TestListInstanceReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + instanceService := InstanceApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_list_error"} + + mockEntityService.EXPECT().List(gomock.Any()).Return(nil, mockError) + + //when + instances, err := instanceService.List() + + //then + assert.Nil(t, instances) + assert.Equal(t, mockError, err) + +} + +func TestCreateInstanceReturnCreatedInstanceIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + instanceService := InstanceApi{ + entityService: mockEntityService, + } + + instanceToCreate := Instance{Id: "new_id", + Name: "new_name", + TemplateId: "templateId", + ComputeOfferingId: "computeOfferingId", + NetworkId: "networkId"} + + mockEntityService.EXPECT().Create(gomock.Any(), gomock.Any()).Return([]byte(`{"id":"new_id", "password": "new_password"}`), nil) + + //when + createdInstance, _ := instanceService.Create(instanceToCreate) + + //then + if assert.NotNil(t, createdInstance) { + assert.Equal(t, "new_password", createdInstance.Password) + } +} + +func TestCreateInstanceReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + instanceService := InstanceApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_create_instance_error"} + + mockEntityService.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil, mockError) + + instanceToCreate := Instance{Name: "new_name", + TemplateId: "templateId", + ComputeOfferingId: "computeOfferingId", + NetworkId: "networkId"} + + //when + createdInstance, err := instanceService.Create(instanceToCreate) + + //then + assert.Nil(t, createdInstance) + assert.Equal(t, mockError, err) + +} + +func TestPurgeInstanceReturnTrueIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + instanceService := InstanceApi{ + entityService: mockEntityService, + } + + mockEntityService.EXPECT().Execute(TEST_INSTANCE_ID, INSTANCE_PURGE_OPERATION, gomock.Any(), gomock.Any()).Return([]byte(`{}`), nil) + + //when + success, _ := instanceService.Purge(TEST_INSTANCE_ID) + + //then + assert.True(t, success) +} + +func TestPurgeInstanceReturnFalseIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + instanceService := InstanceApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_purge_instance_error"} + mockEntityService.EXPECT().Execute(TEST_INSTANCE_ID, INSTANCE_PURGE_OPERATION, gomock.Any(), gomock.Any()).Return([]byte(`{}`), mockError) + + //when + success, err := instanceService.Purge(TEST_INSTANCE_ID) + + //then + assert.False(t, success) + assert.Equal(t, mockError, err) + +} + +func TestStartInstanceReturnTrueIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + instanceService := InstanceApi{ + entityService: mockEntityService, + } + + mockEntityService.EXPECT().Execute(TEST_INSTANCE_ID, INSTANCE_START_OPERATION, gomock.Any(), gomock.Any()).Return([]byte(`{}`), nil) + + //when + success, _ := instanceService.Start(TEST_INSTANCE_ID) + + //then + assert.True(t, success) +} + +func TestStartInstanceReturnFalseIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + instanceService := InstanceApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_start_instance_error"} + mockEntityService.EXPECT().Execute(TEST_INSTANCE_ID, INSTANCE_START_OPERATION, gomock.Any(), gomock.Any()).Return([]byte(`{}`), mockError) + + //when + success, err := instanceService.Start(TEST_INSTANCE_ID) + + //then + assert.False(t, success) + assert.Equal(t, mockError, err) + +} + +func TestStopInstanceReturnTrueIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + instanceService := InstanceApi{ + entityService: mockEntityService, + } + + mockEntityService.EXPECT().Execute(TEST_INSTANCE_ID, INSTANCE_STOP_OPERATION, gomock.Any(), gomock.Any()).Return([]byte(`{}`), nil) + + //when + success, _ := instanceService.Stop(TEST_INSTANCE_ID) + + //then + assert.True(t, success) +} + +func TestStopInstanceReturnFalseIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + instanceService := InstanceApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_stop_instance_error"} + mockEntityService.EXPECT().Execute(TEST_INSTANCE_ID, INSTANCE_STOP_OPERATION, gomock.Any(), gomock.Any()).Return([]byte(`{}`), mockError) + + //when + success, err := instanceService.Stop(TEST_INSTANCE_ID) + + //then + assert.False(t, success) + assert.Equal(t, mockError, err) + +} + +func TestDestroyInstanceReturnTrueIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + instanceService := InstanceApi{ + entityService: mockEntityService, + } + + mockEntityService.EXPECT().Delete(TEST_INSTANCE_ID, gomock.Any(), gomock.Any()).Return([]byte(`{}`), nil) + + //when + success, _ := instanceService.Destroy(TEST_INSTANCE_ID, false) + + //then + assert.True(t, success) +} + +func TestDestroyInstanceReturnFalseIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + instanceService := InstanceApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_destroy_instance_error"} + mockEntityService.EXPECT().Delete(TEST_INSTANCE_ID, gomock.Any(), gomock.Any()).Return([]byte(`{}`), mockError) + + //when + success, err := instanceService.Destroy(TEST_INSTANCE_ID, true) + + //then + assert.False(t, success) + assert.Equal(t, mockError, err) +} + +func TestDestroyWithOptionsInstanceReturnTrueIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + instanceService := InstanceApi{ + entityService: mockEntityService, + } + + mockEntityService.EXPECT().Delete(TEST_INSTANCE_ID, gomock.Any(), gomock.Any()).Return([]byte(`{}`), nil) + + //when + success, _ := instanceService.DestroyWithOptions(TEST_INSTANCE_ID, DestroyOptions{DeleteSnapshots: true}) + + //then + assert.True(t, success) +} + +func TestDestroyWithOptionsInstanceReturnFalseIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + instanceService := InstanceApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_destroy_instance_error"} + mockEntityService.EXPECT().Delete(TEST_INSTANCE_ID, gomock.Any(), gomock.Any()).Return([]byte(`{}`), mockError) + + //when + success, err := instanceService.DestroyWithOptions(TEST_INSTANCE_ID, DestroyOptions{PurgeImmediately: true}) + + //then + assert.False(t, success) + assert.Equal(t, mockError, err) + +} + +func TestRecoverInstanceReturnTrueIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + instanceService := InstanceApi{ + entityService: mockEntityService, + } + + mockEntityService.EXPECT().Execute(TEST_INSTANCE_ID, INSTANCE_RECOVER_OPERATION, gomock.Any(), gomock.Any()).Return([]byte(`{}`), nil) + + //when + success, _ := instanceService.Recover(TEST_INSTANCE_ID) + + //then + assert.True(t, success) +} + +func TestRecoverInstanceReturnFalseIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + instanceService := InstanceApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_recover_instance_error"} + mockEntityService.EXPECT().Execute(TEST_INSTANCE_ID, INSTANCE_RECOVER_OPERATION, gomock.Any(), gomock.Any()).Return([]byte(`{}`), mockError) + + //when + success, err := instanceService.Recover(TEST_INSTANCE_ID) + + //then + assert.False(t, success) + assert.Equal(t, mockError, err) +} + +func TestRebootInstanceReturnTrueIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + instanceService := InstanceApi{ + entityService: mockEntityService, + } + + mockEntityService.EXPECT().Execute(TEST_INSTANCE_ID, INSTANCE_REBOOT_OPERATION, gomock.Any(), gomock.Any()).Return([]byte(`{}`), nil) + + //when + success, _ := instanceService.Reboot(TEST_INSTANCE_ID) + + //then + assert.True(t, success) +} + +func TestRebootInstanceReturnFalseIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + instanceService := InstanceApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_reboot_instance_error"} + mockEntityService.EXPECT().Execute(TEST_INSTANCE_ID, INSTANCE_REBOOT_OPERATION, gomock.Any(), gomock.Any()).Return([]byte(`{}`), mockError) + + //when + success, err := instanceService.Reboot(TEST_INSTANCE_ID) + + //then + assert.False(t, success) + assert.Equal(t, mockError, err) +} + +func TestAssociateSSHKeyReturnTrueIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + instanceService := InstanceApi{ + entityService: mockEntityService, + } + + mockEntityService.EXPECT().Execute(TEST_INSTANCE_ID, INSTANCE_ASSOCIATE_SSH_KEY_OPERATION, gomock.Any(), gomock.Any()).Return([]byte(`{}`), nil) + + //when + success, _ := instanceService.AssociateSSHKey(TEST_INSTANCE_ID, "new_ssh_key") + + //then + assert.True(t, success) +} + +func TestAssociateSSHKeyReturnFalseIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + instanceService := InstanceApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_associate_ssh_key_error"} + mockEntityService.EXPECT().Execute(TEST_INSTANCE_ID, INSTANCE_ASSOCIATE_SSH_KEY_OPERATION, gomock.Any(), gomock.Any()).Return([]byte(`{}`), mockError) + + //when + success, err := instanceService.AssociateSSHKey(TEST_INSTANCE_ID, "new_ssh_key") + + //then + assert.False(t, success) + assert.Equal(t, mockError, err) +} + +func TestChangeComputeOfferingReturnTrueIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + instanceService := InstanceApi{ + entityService: mockEntityService, + } + + instanceWithNewComputeOffering := Instance{ + Id: TEST_INSTANCE_ID, + ComputeOfferingId: "new_compute_offering", + } + + mockEntityService.EXPECT().Execute(TEST_INSTANCE_ID, INSTANCE_CHANGE_COMPUTE_OFFERING_OPERATION, gomock.Any(), gomock.Any()).Return([]byte(`{}`), nil) + + //when + success, _ := instanceService.ChangeComputeOffering(instanceWithNewComputeOffering) + + //then + assert.True(t, success) +} + +func TestChangeComputeOfferingReturnFalseIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + instanceService := InstanceApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_change_compute_offering_error"} + mockEntityService.EXPECT().Execute(TEST_INSTANCE_ID, INSTANCE_CHANGE_COMPUTE_OFFERING_OPERATION, gomock.Any(), gomock.Any()).Return([]byte(`{}`), mockError) + + instanceWithNewComputeOffering := Instance{ + Id: TEST_INSTANCE_ID, + ComputeOfferingId: "new_compute_offering", + } + + //when + success, err := instanceService.ChangeComputeOffering(instanceWithNewComputeOffering) + + //then + assert.False(t, success) + assert.Equal(t, mockError, err) +} + +func TestResetPasswordReturnNewPasswordIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + instanceService := InstanceApi{ + entityService: mockEntityService, + } + + mockEntityService.EXPECT().Execute(TEST_INSTANCE_ID, INSTANCE_RESET_PASSWORD_OPERATION, gomock.Any(), gomock.Any()).Return([]byte(`{"password":"new_password"}`), nil) + + //when + newPassword, _ := instanceService.ResetPassword(TEST_INSTANCE_ID) + + //then + assert.Equal(t, "new_password", newPassword) +} + +func TestResetPasswordReturnEmptyStringIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + instanceService := InstanceApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_reset_password_error"} + mockEntityService.EXPECT().Execute(TEST_INSTANCE_ID, INSTANCE_RESET_PASSWORD_OPERATION, gomock.Any(), gomock.Any()).Return([]byte(`{}`), mockError) + + //when + newPassword, err := instanceService.ResetPassword(TEST_INSTANCE_ID) + + //then + assert.Empty(t, newPassword) + assert.Equal(t, mockError, err) +} + +func TestCreateRecoveryPointReturnTrueIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + instanceService := InstanceApi{ + entityService: mockEntityService, + } + + mockEntityService.EXPECT().Execute(TEST_INSTANCE_ID, INSTANCE_CREATE_RECOVERY_POINT_OPERATION, gomock.Any(), gomock.Any()).Return([]byte(`{}`), nil) + + //when + success, _ := instanceService.CreateRecoveryPoint(TEST_INSTANCE_ID, RecoveryPoint{"new_recovery_point_name", "description"}) + + //then + assert.True(t, success) +} + +func TestCreateRecoveryPointReturnFalseIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + instanceService := InstanceApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_create_recovery_point_error"} + mockEntityService.EXPECT().Execute(TEST_INSTANCE_ID, INSTANCE_CREATE_RECOVERY_POINT_OPERATION, gomock.Any(), gomock.Any()).Return([]byte(`{}`), mockError) + + //when + success, err := instanceService.CreateRecoveryPoint(TEST_INSTANCE_ID, RecoveryPoint{"new_recovery_point_name", "description"}) + + //then + assert.False(t, success) + assert.Equal(t, mockError, err) +} + +func TestExistsReturnTrueIfInstanceExists(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + instanceService := InstanceApi{ + entityService: mockEntityService, + } + + mockEntityService.EXPECT().Get(TEST_INSTANCE_ID, gomock.Any()).Return([]byte(`{"id": "foo"}`), nil) + + //when + exists, _ := instanceService.Exists(TEST_INSTANCE_ID) + + //then + assert.True(t, exists) +} + +func TestExistsReturnFalseIfInstanceDoesntExist(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + instanceService := InstanceApi{ + entityService: mockEntityService, + } + + mockApiError := api.CcaErrorResponse(api.CcaResponse{StatusCode: api.NOT_FOUND}) + mockEntityService.EXPECT().Get(TEST_INSTANCE_ID, gomock.Any()).Return([]byte(`{}`), mockApiError) + + //when + exists, err := instanceService.Exists(TEST_INSTANCE_ID) + + //then + assert.Nil(t, err) + assert.False(t, exists) +} + +func TestExistsReturnErrorIfUnexpectedError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + instanceService := InstanceApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_exists_error"} + mockEntityService.EXPECT().Get(TEST_INSTANCE_ID, gomock.Any()).Return([]byte(`{}`), mockError) + + //when + _, err := instanceService.Exists(TEST_INSTANCE_ID) + + //then + assert.Equal(t, mockError, err) +} diff --git a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/load_balancer_rule.go b/pkg/cloudca/services/cloudca/load_balancer_rule.go similarity index 98% rename from vendor/github.com/cloud-ca/go-cloudca/services/cloudca/load_balancer_rule.go rename to pkg/cloudca/services/cloudca/load_balancer_rule.go index bcfafe8..74de694 100644 --- a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/load_balancer_rule.go +++ b/pkg/cloudca/services/cloudca/load_balancer_rule.go @@ -3,8 +3,8 @@ package cloudca import ( "encoding/json" - "github.com/cloud-ca/go-cloudca/api" - "github.com/cloud-ca/go-cloudca/services" + "github.com/cloud-ca/cca/pkg/cloudca/api" + "github.com/cloud-ca/cca/pkg/cloudca/services" ) const ( diff --git a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/network.go b/pkg/cloudca/services/cloudca/network.go similarity index 97% rename from vendor/github.com/cloud-ca/go-cloudca/services/cloudca/network.go rename to pkg/cloudca/services/cloudca/network.go index 3f69c7e..13be0e1 100644 --- a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/network.go +++ b/pkg/cloudca/services/cloudca/network.go @@ -3,8 +3,8 @@ package cloudca import ( "encoding/json" - "github.com/cloud-ca/go-cloudca/api" - "github.com/cloud-ca/go-cloudca/services" + "github.com/cloud-ca/cca/pkg/cloudca/api" + "github.com/cloud-ca/cca/pkg/cloudca/services" ) type Service struct { diff --git a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/network_acl.go b/pkg/cloudca/services/cloudca/network_acl.go similarity index 96% rename from vendor/github.com/cloud-ca/go-cloudca/services/cloudca/network_acl.go rename to pkg/cloudca/services/cloudca/network_acl.go index 1f9c17c..ef2c666 100644 --- a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/network_acl.go +++ b/pkg/cloudca/services/cloudca/network_acl.go @@ -2,8 +2,9 @@ package cloudca import ( "encoding/json" - "github.com/cloud-ca/go-cloudca/api" - "github.com/cloud-ca/go-cloudca/services" + + "github.com/cloud-ca/cca/pkg/cloudca/api" + "github.com/cloud-ca/cca/pkg/cloudca/services" ) type NetworkAcl struct { diff --git a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/network_acl_rule.go b/pkg/cloudca/services/cloudca/network_acl_rule.go similarity index 97% rename from vendor/github.com/cloud-ca/go-cloudca/services/cloudca/network_acl_rule.go rename to pkg/cloudca/services/cloudca/network_acl_rule.go index 99c4924..9b80e28 100644 --- a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/network_acl_rule.go +++ b/pkg/cloudca/services/cloudca/network_acl_rule.go @@ -3,8 +3,8 @@ package cloudca import ( "encoding/json" - "github.com/cloud-ca/go-cloudca/api" - "github.com/cloud-ca/go-cloudca/services" + "github.com/cloud-ca/cca/pkg/cloudca/api" + "github.com/cloud-ca/cca/pkg/cloudca/services" ) type NetworkAclRule struct { diff --git a/pkg/cloudca/services/cloudca/network_acl_rule_test.go b/pkg/cloudca/services/cloudca/network_acl_rule_test.go new file mode 100644 index 0000000..ce6364c --- /dev/null +++ b/pkg/cloudca/services/cloudca/network_acl_rule_test.go @@ -0,0 +1,327 @@ +package cloudca + +import ( + "fmt" + "testing" + + "github.com/cloud-ca/cca/pkg/cloudca/mocks" + "github.com/cloud-ca/cca/pkg/cloudca/mocks/services_mocks" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" +) + +const ACL_RULE_TEMPLATE = `{ + "id": "%s", + "networkAclId": "6145ea41-010c-41f2-a065-2a3a4e98d09d", + "ruleNumber": "1", + "cidr": "0.0.0.0/24", + "action": "Allow", + "protocol": "TCP", + "startPort": "80", + "endPort": "80", + "trafficType": "Ingress", + "state": "Active" +}` + +func setupMockForNetworkAclRule(t *testing.T) *services_mocks.MockEntityService { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + return services_mocks.NewMockEntityService(ctrl) +} + +func createNetworkAclRuleWithId(id string) *NetworkAclRule { + return &NetworkAclRule{ + Id: id, + NetworkAclId: "6145ea41-010c-41f2-a065-2a3a4e98d09d", + RuleNumber: "1", + Cidr: "0.0.0.0/24", + Action: "Allow", + Protocol: "TCP", + StartPort: "80", + EndPort: "80", + TrafficType: "Ingress", + State: "Active", + } +} + +func buildTestNetworkAclRuleJsonResponse(networkAclRule *NetworkAclRule) []byte { + return []byte(`{"id":"` + networkAclRule.Id + `",` + + ` "networkAclId":"` + networkAclRule.NetworkAclId + `",` + + ` "ruleNumber":"` + networkAclRule.RuleNumber + `",` + + ` "cidr":"` + networkAclRule.Cidr + `",` + + ` "action":"` + networkAclRule.Action + `",` + + ` "protocol":"` + networkAclRule.Protocol + `",` + + ` "startPort":"` + networkAclRule.StartPort + `",` + + ` "endPort":"` + networkAclRule.EndPort + `",` + + ` "trafficType":"` + networkAclRule.TrafficType + `",` + + ` "state":"` + networkAclRule.State + + `"}`) +} + +func buildListTestNetworkAclRulesJsonResponse(acls []NetworkAclRule) []byte { + resp := `[` + for i, t := range acls { + resp += string(buildTestNetworkAclRuleJsonResponse(&t)) + if i != len(acls)-1 { + resp += `,` + } + } + resp += `]` + return []byte(resp) +} + +func TestGetNetworkAclRuleByIdReturnAclRule_ifSuccess(t *testing.T) { + // given + mockEntityService := setupMockForNetworkAclRule(t) + networkAclRuleService := NetworkAclRuleApi{ + entityService: mockEntityService, + } + + expectedId := "rule_0" + expectedNetworkAclRule := *createNetworkAclRuleWithId(expectedId) + + response := fmt.Sprintf(ACL_RULE_TEMPLATE, expectedId) + mockEntityService.EXPECT().Get(expectedId, gomock.Any()).Return([]byte(response), nil) + + // when + networkAclRule, _ := networkAclRuleService.Get(expectedId) + + // then + assert.Equal(t, expectedNetworkAclRule, *networkAclRule) +} + +func TestGetNetworkAclRuleByIdReturnError_ifError(t *testing.T) { + // given + mockEntityService := setupMockForNetworkAclRule(t) + networkAclRuleService := NetworkAclRuleApi{ + entityService: mockEntityService, + } + + expectedId := "rule_0" + mockError := mocks.MockError{Message: "get error"} + mockEntityService.EXPECT().Get(expectedId, gomock.Any()).Return(nil, mockError) + + // when + networkAclRule, err := networkAclRuleService.Get(expectedId) + + // then + assert.Nil(t, networkAclRule) + assert.Equal(t, mockError, err) +} + +func TestListNetworkAclRuleReturnAclsIfSuccess(t *testing.T) { + //given + mockEntityService := setupMockForNetworkAclRule(t) + networkAclRuleService := NetworkAclRuleApi{ + entityService: mockEntityService, + } + + expectedId1 := "rule_1" + expectedNetworkAclRule1 := *createNetworkAclRuleWithId(expectedId1) + expectedId2 := "rule_2" + expectedNetworkAclRule2 := *createNetworkAclRuleWithId(expectedId2) + + expectedAcls := []NetworkAclRule{expectedNetworkAclRule1, expectedNetworkAclRule2} + + mockEntityService.EXPECT().List(gomock.Any()).Return(buildListTestNetworkAclRulesJsonResponse(expectedAcls), nil) + + //when + acls, _ := networkAclRuleService.List() + + //then + if assert.NotNil(t, acls) { + assert.Equal(t, expectedAcls, acls) + } +} + +func TestListNetworkAclRuleReturnNilWithErrorIfError(t *testing.T) { + //given + mockEntityService := setupMockForNetworkAclRule(t) + networkAclRuleService := NetworkAclRuleApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{Message: "some_list_error"} + mockEntityService.EXPECT().List(gomock.Any()).Return(nil, mockError) + + //when + acls, err := networkAclRuleService.List() + + //then + assert.Nil(t, acls) + assert.Equal(t, mockError, err) + +} + +func TestListNetworkAclRuleByAclIdReturnAcls_ifSuccess(t *testing.T) { + //given + mockEntityService := setupMockForNetworkAclRule(t) + networkAclRuleService := NetworkAclRuleApi{ + entityService: mockEntityService, + } + + expectedId1 := "rule_1" + expectedNetworkAclRule1 := *createNetworkAclRuleWithId(expectedId1) + expectedAcls := []NetworkAclRule{expectedNetworkAclRule1} + + mockEntityService.EXPECT().List(gomock.Any()).Return(buildListTestNetworkAclRulesJsonResponse(expectedAcls), nil) + + //when + acls, _ := networkAclRuleService.ListByNetworkAclId("acl1") + + //then + if assert.NotNil(t, acls) { + assert.Equal(t, expectedAcls, acls) + } +} + +func TestListNetworkAclRuleByAclIdReturnNilWithError_ifError(t *testing.T) { + //given + mockEntityService := setupMockForNetworkAclRule(t) + networkAclRuleService := NetworkAclRuleApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{Message: "some_list_error"} + mockEntityService.EXPECT().List(gomock.Any()).Return(nil, mockError) + + //when + acls, err := networkAclRuleService.ListByNetworkAclId("acl1") + + //then + assert.Nil(t, acls) + assert.Equal(t, mockError, err) + +} + +func TestListNetworkAclRulesWithOptions(t *testing.T) { + // given + mockEntityService := setupMockForNetworkAclRule(t) + networkAclRuleService := NetworkAclRuleApi{ + entityService: mockEntityService, + } + + id1, id2 := "1234", "4321" + rule1, rule2 := fmt.Sprintf(ACL_RULE_TEMPLATE, id1), fmt.Sprintf(ACL_RULE_TEMPLATE, id2) + response := fmt.Sprintf("[ %s, %s ]", rule1, rule2) + mockEntityService.EXPECT().List(gomock.Any()).Return([]byte(response), nil) + + // when + rules, _ := networkAclRuleService.ListWithOptions(map[string]string{}) + + // then + assert.Equal(t, id1, rules[0].Id) + assert.Equal(t, id2, rules[1].Id) +} + +func TestCreateNetworkAclRuleReturnsError_ifErrorWhileCreating(t *testing.T) { + // given + mockEntityService := setupMockForNetworkAclRule(t) + networkAclRuleService := NetworkAclRuleApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{Message: "creation error"} + mockEntityService.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil, mockError) + + // when + rule, err := networkAclRuleService.Create(NetworkAclRule{}) + + // then + assert.Nil(t, rule) + assert.Equal(t, mockError, err) +} + +func TestCreateNetworkAclRuleReturnsSuccess_ifNoErrorsOccur(t *testing.T) { + // given + mockEntityService := setupMockForNetworkAclRule(t) + networkAclRuleService := NetworkAclRuleApi{ + entityService: mockEntityService, + } + + expectedId := "adsf" + response := fmt.Sprintf(ACL_RULE_TEMPLATE, expectedId) + expectedNetworkAclRule := *createNetworkAclRuleWithId(expectedId) + + mockEntityService.EXPECT().Create(gomock.Any(), gomock.Any()).Return([]byte(response), nil) + + // when + rule, _ := networkAclRuleService.Create(expectedNetworkAclRule) + + // then + assert.Equal(t, expectedNetworkAclRule, *rule) +} + +func TestUpdateNetworkAclRuleReturnsError_ifErrorWhileCreating(t *testing.T) { + // given + mockEntityService := setupMockForNetworkAclRule(t) + networkAclRuleService := NetworkAclRuleApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{Message: "update error"} + mockEntityService.EXPECT().Update(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, mockError) + + // when + rule, err := networkAclRuleService.Update("1234", NetworkAclRule{}) + + // then + assert.Nil(t, rule) + assert.Equal(t, mockError, err) +} + +func TestUpdateNetworkAclRuleReturnsSuccess_ifNoErrorsOccur(t *testing.T) { + // given + mockEntityService := setupMockForNetworkAclRule(t) + networkAclRuleService := NetworkAclRuleApi{ + entityService: mockEntityService, + } + + expectedId := "adsf" + response := fmt.Sprintf(ACL_RULE_TEMPLATE, expectedId) + expectedNetworkAclRule := *createNetworkAclRuleWithId(expectedId) + + mockEntityService.EXPECT().Update(gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte(response), nil) + + // when + rule, _ := networkAclRuleService.Update(expectedId, expectedNetworkAclRule) + + // then + assert.Equal(t, expectedNetworkAclRule, *rule) +} + +func TestDeleteNetworkAclRuleReturnsError_ifErrorWhileDeleting(t *testing.T) { + // given + mockEntityService := setupMockForNetworkAclRule(t) + networkAclRuleService := NetworkAclRuleApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{Message: "deletion error"} + mockEntityService.EXPECT().Delete(gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte{}, mockError) + + // when + success, err := networkAclRuleService.Delete("123") + + // then + assert.False(t, success) + assert.Equal(t, mockError, err) +} + +func TestDeleteNetworkAclRuleReturnsSuccess_ifNoErrorsOccur(t *testing.T) { + // given + mockEntityService := setupMockForNetworkAclRule(t) + networkAclRuleService := NetworkAclRuleApi{ + entityService: mockEntityService, + } + + expectedId := "id0" + mockEntityService.EXPECT().Delete(expectedId, gomock.Any(), gomock.Any()).Return([]byte{}, nil) + + // when + success, _ := networkAclRuleService.Delete(expectedId) + + // then + assert.True(t, success) +} diff --git a/pkg/cloudca/services/cloudca/network_acl_test.go b/pkg/cloudca/services/cloudca/network_acl_test.go new file mode 100644 index 0000000..7cc3046 --- /dev/null +++ b/pkg/cloudca/services/cloudca/network_acl_test.go @@ -0,0 +1,242 @@ +package cloudca + +import ( + "testing" + + "github.com/cloud-ca/cca/pkg/cloudca/mocks" + "github.com/cloud-ca/cca/pkg/cloudca/mocks/services_mocks" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" +) + +const ( + TEST_NETWORK_ACL_ID = "test_network_acl_id" + TEST_NETWORK_ACL_NAME = "test_network_acl" + TEST_NETWORK_ACL_DESCRIPTION = "test_network_acl_description" + TEST_NETWORK_ACL_VPC_ID = "test_network_acl_vpc_id" +) + +func buildTestNetworkAclJsonResponse(networkAcl *NetworkAcl) []byte { + return []byte(`{"id":"` + networkAcl.Id + `",` + + ` "name":"` + networkAcl.Name + `",` + + ` "description":"` + networkAcl.Description + `",` + + ` "vpcId":"` + networkAcl.VpcId + `"}`) +} + +func buildListTestNetworkAclsJsonResponse(networkAcls []NetworkAcl) []byte { + resp := `[` + for i, t := range networkAcls { + resp += string(buildTestNetworkAclJsonResponse(&t)) + if i != len(networkAcls)-1 { + resp += `,` + } + } + resp += `]` + return []byte(resp) +} + +func TestGetNetworkAclReturnNetworkAclIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + networkAclService := NetworkAclApi{ + entityService: mockEntityService, + } + + expectedNetworkAcl := NetworkAcl{Id: TEST_NETWORK_ACL_ID, + Name: TEST_NETWORK_ACL_NAME, + Description: TEST_NETWORK_ACL_DESCRIPTION, + VpcId: TEST_NETWORK_ACL_VPC_ID} + mockEntityService.EXPECT().Get(TEST_NETWORK_ACL_ID, gomock.Any()).Return(buildTestNetworkAclJsonResponse(&expectedNetworkAcl), nil) + + //when + networkAcl, _ := networkAclService.Get(TEST_NETWORK_ACL_ID) + + //then + if assert.NotNil(t, networkAcl) { + assert.Equal(t, expectedNetworkAcl, *networkAcl) + } +} + +func TestGetNetworkAclReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + networkAclService := NetworkAclApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_get_error"} + + mockEntityService.EXPECT().Get(TEST_NETWORK_ACL_ID, gomock.Any()).Return(nil, mockError) + + //when + networkAcl, err := networkAclService.Get(TEST_NETWORK_ACL_ID) + + //then + assert.Nil(t, networkAcl) + assert.Equal(t, mockError, err) + +} + +func TestListNetworkAclReturnNetworkAclsIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + networkAclService := NetworkAclApi{ + entityService: mockEntityService, + } + + expectedNetworkAcl1 := NetworkAcl{Id: "list_id_1", + Name: "list_name_1", + Description: "list_description_1", + VpcId: "list_vpc_id_1"} + + expectedNetworkAcl2 := NetworkAcl{Id: "list_id_2", + Name: "list_name_2", + Description: "list_description_2", + VpcId: "list_vpc_id_2"} + + expectedNetworkAcls := []NetworkAcl{expectedNetworkAcl1, expectedNetworkAcl2} + + mockEntityService.EXPECT().List(gomock.Any()).Return(buildListTestNetworkAclsJsonResponse(expectedNetworkAcls), nil) + + //when + networkAcls, _ := networkAclService.List() + + //then + if assert.NotNil(t, networkAcls) { + assert.Equal(t, expectedNetworkAcls, networkAcls) + } +} + +func TestListNetworkAclReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + networkAclService := NetworkAclApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_list_error"} + + mockEntityService.EXPECT().List(gomock.Any()).Return(nil, mockError) + + //when + networkAcls, err := networkAclService.List() + + //then + assert.Nil(t, networkAcls) + assert.Equal(t, mockError, err) + +} + +func TestCreateNetworkAclReturnCreatedNetworkAclIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + networkAclService := NetworkAclApi{ + entityService: mockEntityService, + } + + networkAclToCreate := NetworkAcl{Name: "new_name", + Description: "new_description", + VpcId: "new_vpc", + } + + mockEntityService.EXPECT().Create(gomock.Any(), gomock.Any()).Return([]byte(`{"id":"new_id"}`), nil) + + //when + createdNetworkAcl, _ := networkAclService.Create(networkAclToCreate) + + //then + if assert.NotNil(t, createdNetworkAcl) { + assert.Equal(t, "new_id", createdNetworkAcl.Id) + } +} + +func TestCreateNetworkAclReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + networkAclService := NetworkAclApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_create_vpc_error"} + + mockEntityService.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil, mockError) + + networkAclToCreate := NetworkAcl{Name: "new_name", + Description: "new_description", + VpcId: "vpcId"} + + //when + createdNetworkAcl, err := networkAclService.Create(networkAclToCreate) + + //then + assert.Nil(t, createdNetworkAcl) + assert.Equal(t, mockError, err) + +} + +func TestDeleteReturnTrueIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + networkAclService := NetworkAclApi{ + entityService: mockEntityService, + } + + mockEntityService.EXPECT().Delete(gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte(`{}`), nil) + + //when + success, _ := networkAclService.Delete(TEST_NETWORK_ACL_ID) + + //then + assert.True(t, success) +} + +func TestDeleteReturnFalseIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + networkAclService := NetworkAclApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_delete_network_acl_id_error"} + mockEntityService.EXPECT().Delete(gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte(`{}`), mockError) + + //when + success, err := networkAclService.Delete(TEST_VPC_ID) + + //then + assert.False(t, success) + assert.Equal(t, mockError, err) + +} diff --git a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/network_offering.go b/pkg/cloudca/services/cloudca/network_offering.go similarity index 95% rename from vendor/github.com/cloud-ca/go-cloudca/services/cloudca/network_offering.go rename to pkg/cloudca/services/cloudca/network_offering.go index d028603..c1a455e 100644 --- a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/network_offering.go +++ b/pkg/cloudca/services/cloudca/network_offering.go @@ -2,8 +2,9 @@ package cloudca import ( "encoding/json" - "github.com/cloud-ca/go-cloudca/api" - "github.com/cloud-ca/go-cloudca/services" + + "github.com/cloud-ca/cca/pkg/cloudca/api" + "github.com/cloud-ca/cca/pkg/cloudca/services" ) type NetworkOffering struct { diff --git a/pkg/cloudca/services/cloudca/network_test.go b/pkg/cloudca/services/cloudca/network_test.go new file mode 100644 index 0000000..be1ea0a --- /dev/null +++ b/pkg/cloudca/services/cloudca/network_test.go @@ -0,0 +1,203 @@ +package cloudca + +import ( + "strconv" + "testing" + + "github.com/cloud-ca/cca/pkg/cloudca/mocks" + "github.com/cloud-ca/cca/pkg/cloudca/mocks/services_mocks" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" +) + +const ( + TEST_NETWORK_ID = "test_network_id" + TEST_NETWORK_NAME = "test_network" + TEST_NETWORK_ZONE_ID = "test_network_zone_id" + TEST_NETWORK_ZONE_NAME = "test_network_zone_name" + TEST_NETWORK_CIDR = "test_network_cidr" + TEST_NETWORK_TYPE = "test_network_type" + TEST_NETWORK_STATE = "test_network_state" + TEST_NETWORK_GATEWAY = "test_network_gateway" + TEST_NETWORK_NETWORK_OFFERING_ID = "test_network_network_offering_id" + TEST_NETWORK_IS_SYSTEM = false + TEST_NETWORK_VPC_ID = "test_network_vpc_id" + TEST_NETWORK_DOMAIN = "test_network_domain" + TEST_NETWORK_DOMAIN_ID = "test_network_domain_id" + TEST_NETWORK_PROJECT = "test_network_project" + TEST_NETWORK_PROJECT_ID = "test_network_project_id" + TEST_NETWORK_ACL_ID_REF = "test_network_acl_id" +) + +func buildTestNetworkJsonResponse(network *Network) []byte { + return []byte(`{"id":"` + network.Id + `",` + + ` "name":"` + network.Name + `",` + + ` "zoneid":"` + network.ZoneId + `",` + + ` "zonename":"` + network.ZoneName + `",` + + ` "cidr":"` + network.Cidr + `",` + + ` "type":"` + network.Type + `",` + + ` "state":"` + network.State + `",` + + ` "gateway":"` + network.Gateway + `",` + + ` "networkOfferingId":"` + network.NetworkOfferingId + `",` + + ` "issystem":` + strconv.FormatBool(network.IsSystem) + `,` + + ` "vpcId":"` + network.VpcId + `",` + + ` "domain":"` + network.Domain + `",` + + ` "domainid":"` + network.DomainId + `",` + + ` "project":"` + network.Project + `",` + + ` "projectid":"` + network.ProjectId + `",` + + ` "networkACLId":"` + network.NetworkAclId + `"}`) +} + +func buildListTestNetworkJsonResponse(networks []Network) []byte { + resp := `[` + for i, t := range networks { + resp += string(buildTestNetworkJsonResponse(&t)) + if i != len(networks)-1 { + resp += `,` + } + } + resp += `]` + return []byte(resp) +} + +func TestGetNetworkReturnNetworkIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + networkService := NetworkApi{ + entityService: mockEntityService, + } + + expectedNetwork := Network{Id: TEST_NETWORK_ID, + Name: TEST_NETWORK_NAME, + ZoneId: TEST_NETWORK_ZONE_ID, + ZoneName: TEST_NETWORK_ZONE_NAME, + Cidr: TEST_NETWORK_CIDR, + Type: TEST_NETWORK_TYPE, + Gateway: TEST_NETWORK_GATEWAY, + NetworkOfferingId: TEST_NETWORK_NETWORK_OFFERING_ID, + IsSystem: TEST_NETWORK_IS_SYSTEM, + VpcId: TEST_NETWORK_VPC_ID, + Domain: TEST_NETWORK_DOMAIN, + DomainId: TEST_NETWORK_DOMAIN_ID, + Project: TEST_NETWORK_PROJECT, + ProjectId: TEST_NETWORK_PROJECT_ID, + NetworkAclId: TEST_NETWORK_ACL_ID_REF} + + mockEntityService.EXPECT().Get(TEST_NETWORK_ID, gomock.Any()).Return(buildTestNetworkJsonResponse(&expectedNetwork), nil) + + //when + network, _ := networkService.Get(TEST_NETWORK_ID) + + //then + if assert.NotNil(t, network) { + assert.Equal(t, expectedNetwork, *network) + } +} + +func TestGetNetworkReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + networkService := NetworkApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_get_error"} + + mockEntityService.EXPECT().Get(TEST_NETWORK_ID, gomock.Any()).Return(nil, mockError) + + //when + network, err := networkService.Get(TEST_NETWORK_ID) + + //then + assert.Nil(t, network) + assert.Equal(t, mockError, err) + +} + +func TestListNetworkReturnNetworksIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + networkService := NetworkApi{ + entityService: mockEntityService, + } + + expectedNetwork1 := Network{Id: "list_id_1", + Name: "list_name_1", + ZoneId: "list_zone_id_1", + ZoneName: "list_zone_name_1", + Cidr: "list_cidr_1", + Type: "list_type_1", + Gateway: "list_gateway_1", + NetworkOfferingId: "list_network_offering_id_1", + IsSystem: true, + VpcId: "list_vpc_id_1", + Domain: "list_domain_1", + DomainId: "list_domain_id_1", + Project: "list_project_1", + ProjectId: "list_project_id_1", + NetworkAclId: "list_acl_id_1"} + + expectedNetwork2 := Network{Id: "list_id_2", + Name: "list_name_2", + ZoneId: "list_zone_id_2", + ZoneName: "list_zone_name_2", + Cidr: "list_cidr_2", + Type: "list_type_2", + Gateway: "list_gateway_2", + NetworkOfferingId: "list_network_offering_id_2", + IsSystem: false, + VpcId: "list_vpc_id_2", + Domain: "list_domain_2", + DomainId: "list_domain_id_2", + Project: "list_project_2", + ProjectId: "list_project_id_2", + NetworkAclId: "list_acl_id_2"} + + expectedNetworks := []Network{expectedNetwork1, expectedNetwork2} + + mockEntityService.EXPECT().List(gomock.Any()).Return(buildListTestNetworkJsonResponse(expectedNetworks), nil) + + //when + networks, _ := networkService.List() + + //then + if assert.NotNil(t, networks) { + assert.Equal(t, expectedNetworks, networks) + } +} + +func TestListNetworkReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + networkService := NetworkApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_list_error"} + + mockEntityService.EXPECT().List(gomock.Any()).Return(nil, mockError) + + //when + networks, err := networkService.List() + + //then + assert.Nil(t, networks) + assert.Equal(t, mockError, err) + +} diff --git a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/port_forwarding_rule.go b/pkg/cloudca/services/cloudca/port_forwarding_rule.go similarity index 97% rename from vendor/github.com/cloud-ca/go-cloudca/services/cloudca/port_forwarding_rule.go rename to pkg/cloudca/services/cloudca/port_forwarding_rule.go index edd9766..78031b4 100644 --- a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/port_forwarding_rule.go +++ b/pkg/cloudca/services/cloudca/port_forwarding_rule.go @@ -3,8 +3,8 @@ package cloudca import ( "encoding/json" - "github.com/cloud-ca/go-cloudca/api" - "github.com/cloud-ca/go-cloudca/services" + "github.com/cloud-ca/cca/pkg/cloudca/api" + "github.com/cloud-ca/cca/pkg/cloudca/services" ) const ( diff --git a/pkg/cloudca/services/cloudca/port_forwarding_rule_test.go b/pkg/cloudca/services/cloudca/port_forwarding_rule_test.go new file mode 100644 index 0000000..6184e42 --- /dev/null +++ b/pkg/cloudca/services/cloudca/port_forwarding_rule_test.go @@ -0,0 +1,150 @@ +package cloudca + +import ( + "fmt" + "testing" + + "github.com/cloud-ca/cca/pkg/cloudca/mocks" + "github.com/cloud-ca/cca/pkg/cloudca/mocks/services_mocks" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" +) + +const PFR_TEMPLATE = `{ + "id": "%s", + "instanceId": "1", + "instanceName": "instance0", + "networkId": "2", + "privateIp": "127.0.0.1", + "privateIpId": "3", + "privatePortStart": "8080", + "privatePortEnd": "8080", + "ipAddress": "192.168.0.1", + "ipAddressId": "4", + "publicPortStart": "80", + "publicPortEnd": "80", + "protocol": "TCP", + "state": "Active", + "vpcId": "5" +}` + +func setupMock(t *testing.T) *services_mocks.MockEntityService { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + return services_mocks.NewMockEntityService(ctrl) +} + +func createPfrWithId(id string) *PortForwardingRule { + return &PortForwardingRule{ + Id: id, + InstanceId: "1", + InstanceName: "instance0", + NetworkId: "2", + PrivateIp: "127.0.0.1", + PrivateIpId: "3", + PrivatePortStart: "8080", + PrivatePortEnd: "8080", + PublicIp: "192.168.0.1", + PublicIpId: "4", + PublicPortStart: "80", + PublicPortEnd: "80", + Protocol: "TCP", + State: "Active", + VpcId: "5", + } +} + +func TestGetById(t *testing.T) { + // given + mockEntityService := setupMock(t) + pfrService := PortForwardingRuleApi{ + entityService: mockEntityService, + } + + expectedId := "pfr_0" + expectedPfr := *createPfrWithId(expectedId) + + response := fmt.Sprintf(PFR_TEMPLATE, expectedId) + mockEntityService.EXPECT().Get(expectedId, gomock.Any()).Return([]byte(response), nil) + + // when + pfr, _ := pfrService.Get(expectedId) + + // then + assert.Equal(t, expectedPfr, *pfr) +} + +func TestListWithOptions(t *testing.T) { + // given + mockEntityService := setupMock(t) + pfrService := PortForwardingRuleApi{ + entityService: mockEntityService, + } + + id1, id2 := "1234", "4321" + pfr1, pfr2 := fmt.Sprintf(PFR_TEMPLATE, id1), fmt.Sprintf(PFR_TEMPLATE, id2) + response := fmt.Sprintf("[ %s, %s ]", pfr1, pfr2) + mockEntityService.EXPECT().List(gomock.Any()).Return([]byte(response), nil) + + // when + pfrs, _ := pfrService.ListWithOptions(map[string]string{}) + + // then + assert.Equal(t, id1, pfrs[0].Id) + assert.Equal(t, id2, pfrs[1].Id) +} + +func TestCreate(t *testing.T) { + // given + mockEntityService := setupMock(t) + pfrService := PortForwardingRuleApi{ + entityService: mockEntityService, + } + + expectedId := "adsf" + response := fmt.Sprintf(PFR_TEMPLATE, expectedId) + expectedPfr := *createPfrWithId(expectedId) + + mockEntityService.EXPECT().Create(gomock.Any(), gomock.Any()).Return([]byte(response), nil) + + // when + pfr, _ := pfrService.Create(expectedPfr) + + // then + assert.Equal(t, expectedPfr, *pfr) +} + +func TestDeleteReturnsSuccess_ifNoErrorsOccur(t *testing.T) { + // given + mockEntityService := setupMock(t) + pfrService := PortForwardingRuleApi{ + entityService: mockEntityService, + } + + expectedId := "id0" + mockEntityService.EXPECT().Delete(expectedId, gomock.Any(), gomock.Any()).Return([]byte{}, nil) + + // when + success, _ := pfrService.Delete(expectedId) + + // then + assert.True(t, success) +} + +func TestDeleteReturnsFailure_ifErrorOccurred(t *testing.T) { + // given + mockEntityService := setupMock(t) + pfrService := PortForwardingRuleApi{ + entityService: mockEntityService, + } + + expectedId := "id0" + mockEntityService.EXPECT().Delete(expectedId, gomock.Any(), gomock.Any()).Return(nil, mocks.MockError{"asdf"}) + + // when + success, _ := pfrService.Delete(expectedId) + + // then + assert.False(t, success) +} diff --git a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/public_ip.go b/pkg/cloudca/services/cloudca/public_ip.go similarity index 97% rename from vendor/github.com/cloud-ca/go-cloudca/services/cloudca/public_ip.go rename to pkg/cloudca/services/cloudca/public_ip.go index 1809562..ed7c4a2 100644 --- a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/public_ip.go +++ b/pkg/cloudca/services/cloudca/public_ip.go @@ -2,8 +2,9 @@ package cloudca import ( "encoding/json" - "github.com/cloud-ca/go-cloudca/api" - "github.com/cloud-ca/go-cloudca/services" + + "github.com/cloud-ca/cca/pkg/cloudca/api" + "github.com/cloud-ca/cca/pkg/cloudca/services" ) const ( diff --git a/pkg/cloudca/services/cloudca/public_ip_test.go b/pkg/cloudca/services/cloudca/public_ip_test.go new file mode 100644 index 0000000..b3005cc --- /dev/null +++ b/pkg/cloudca/services/cloudca/public_ip_test.go @@ -0,0 +1,325 @@ +package cloudca + +import ( + "testing" + + "github.com/cloud-ca/cca/pkg/cloudca/mocks" + "github.com/cloud-ca/cca/pkg/cloudca/mocks/services_mocks" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" +) + +const ( + TEST_PUBLIC_IP_ID = "test_public_ip_id" + TEST_IP_ADDRESS = "172.31.3.208" +) + +func buildTestPublicIpJsonResponse(publicIp *PublicIp) []byte { + return []byte(`{"id":"` + publicIp.Id + `",` + + ` "ipAddress":"` + publicIp.IpAddress + `"}`) +} + +func buildListTestPublicIpJsonResponse(publicIps []PublicIp) []byte { + resp := `[` + for i, t := range publicIps { + resp += string(buildTestPublicIpJsonResponse(&t)) + if i != len(publicIps)-1 { + resp += `,` + } + } + resp += `]` + return []byte(resp) +} + +func TestGetPublicIpReturnPublicIpIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + publicIpService := PublicIpApi{ + entityService: mockEntityService, + } + + expectedPublicIp := PublicIp{Id: TEST_PUBLIC_IP_ID, + IpAddress: TEST_IP_ADDRESS, + } + + mockEntityService.EXPECT().Get(TEST_PUBLIC_IP_ID, gomock.Any()).Return(buildTestPublicIpJsonResponse(&expectedPublicIp), nil) + + //when + publicIp, _ := publicIpService.Get(TEST_PUBLIC_IP_ID) + + //then + if assert.NotNil(t, publicIp) { + assert.Equal(t, expectedPublicIp, *publicIp) + } +} + +func TestGetPublicIpReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + publicIpService := PublicIpApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_get_error"} + + mockEntityService.EXPECT().Get(TEST_PUBLIC_IP_ID, gomock.Any()).Return(nil, mockError) + + //when + publicIp, err := publicIpService.Get(TEST_PUBLIC_IP_ID) + + //then + assert.Nil(t, publicIp) + assert.Equal(t, mockError, err) + +} + +func TestListPublicIpReturnNetworksIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + publicIpService := PublicIpApi{ + entityService: mockEntityService, + } + + expectedPublicIp1 := PublicIp{Id: "list_id_1", + IpAddress: "list_ip_address_1"} + + expectedPublicIp2 := PublicIp{Id: "list_id_2", + IpAddress: "list_ip_address_2"} + + expectedPublicIps := []PublicIp{expectedPublicIp1, expectedPublicIp2} + + mockEntityService.EXPECT().List(gomock.Any()).Return(buildListTestPublicIpJsonResponse(expectedPublicIps), nil) + + //when + publicIps, _ := publicIpService.List() + + //then + if assert.NotNil(t, publicIps) { + assert.Equal(t, expectedPublicIps, publicIps) + } +} + +func TestListPublicIpReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + publicIpService := PublicIpApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_list_error"} + + mockEntityService.EXPECT().List(gomock.Any()).Return(nil, mockError) + + //when + publicIps, err := publicIpService.List() + + //then + assert.Nil(t, publicIps) + assert.Equal(t, mockError, err) + +} + +func TestPublicIpReturnAcquiredPublicIpIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + publicIpService := PublicIpApi{ + entityService: mockEntityService, + } + + publicIpToAcquire := PublicIp{VpcId: "vpcId"} + + mockEntityService.EXPECT().Create(gomock.Any(), gomock.Any()).Return([]byte(`{"id":"new_id", "ipAddress": "new_ip_address"}`), nil) + + //when + acquiredPublicIp, _ := publicIpService.Acquire(publicIpToAcquire) + + //then + if assert.NotNil(t, acquiredPublicIp) { + assert.Equal(t, "new_id", acquiredPublicIp.Id) + assert.Equal(t, "new_ip_address", acquiredPublicIp.IpAddress) + } +} + +func TestPublicIpReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + publicIpService := PublicIpApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_create_instance_error"} + + mockEntityService.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil, mockError) + + publicIpToAcquire := PublicIp{VpcId: "vpcId"} + + //when + acquiredPublicIp, err := publicIpService.Acquire(publicIpToAcquire) + + //then + assert.Nil(t, acquiredPublicIp) + assert.Equal(t, mockError, err) +} + +func TestPublicIpReleaseReturnTrueIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + publicIpService := PublicIpApi{ + entityService: mockEntityService, + } + + mockEntityService.EXPECT().Delete(TEST_PUBLIC_IP_ID, gomock.Any(), gomock.Any()).Return([]byte(`{}`), nil) + + //when + success, _ := publicIpService.Release(TEST_PUBLIC_IP_ID) + + //then + assert.True(t, success) +} + +func TestPublicIpReleaseReturnFalseIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + publicIpService := PublicIpApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_purge_instance_error"} + mockEntityService.EXPECT().Delete(TEST_PUBLIC_IP_ID, gomock.Any(), gomock.Any()).Return([]byte(`{}`), mockError) + + //when + success, err := publicIpService.Release(TEST_PUBLIC_IP_ID) + + //then + assert.False(t, success) + assert.Equal(t, mockError, err) + +} + +func TestPublicIpEnableStaticNatReturnTrueIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + publicIpService := PublicIpApi{ + entityService: mockEntityService, + } + + publicIp := PublicIp{ + Id: TEST_PUBLIC_IP_ID, + PrivateIpId: "private_ip_id", + } + + mockEntityService.EXPECT().Execute(TEST_PUBLIC_IP_ID, PUBLIC_IP_ENABLE_STATIC_NAT_OPERATION, gomock.Any(), gomock.Any()).Return([]byte(`{}`), nil) + + //when + success, _ := publicIpService.EnableStaticNat(publicIp) + + //then + assert.True(t, success) +} + +func TestPublicIpEnableStaticNatReturnFalseIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + publicIpService := PublicIpApi{ + entityService: mockEntityService, + } + + publicIp := PublicIp{ + Id: TEST_PUBLIC_IP_ID, + PrivateIpId: "private_ip_id", + } + + mockError := mocks.MockError{"some_purge_instance_error"} + mockEntityService.EXPECT().Execute(TEST_PUBLIC_IP_ID, PUBLIC_IP_ENABLE_STATIC_NAT_OPERATION, gomock.Any(), gomock.Any()).Return([]byte(`{}`), mockError) + + //when + success, err := publicIpService.EnableStaticNat(publicIp) + + //then + assert.False(t, success) + assert.Equal(t, mockError, err) + +} + +func TestPublicIpDisableStaticNatReturnTrueIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + publicIpService := PublicIpApi{ + entityService: mockEntityService, + } + + mockEntityService.EXPECT().Execute(TEST_PUBLIC_IP_ID, PUBLIC_IP_DISABLE_STATIC_NAT_OPERATION, gomock.Any(), gomock.Any()).Return([]byte(`{}`), nil) + + //when + success, _ := publicIpService.DisableStaticNat(TEST_PUBLIC_IP_ID) + + //then + assert.True(t, success) +} + +func TestPublicIpDisableStaticNatReturnFalseIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + publicIpService := PublicIpApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_purge_instance_error"} + mockEntityService.EXPECT().Execute(TEST_PUBLIC_IP_ID, PUBLIC_IP_DISABLE_STATIC_NAT_OPERATION, gomock.Any(), gomock.Any()).Return([]byte(`{}`), mockError) + + //when + success, err := publicIpService.DisableStaticNat(TEST_PUBLIC_IP_ID) + + //then + assert.False(t, success) + assert.Equal(t, mockError, err) + +} diff --git a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/recovery_point.go b/pkg/cloudca/services/cloudca/recovery_point.go similarity index 100% rename from vendor/github.com/cloud-ca/go-cloudca/services/cloudca/recovery_point.go rename to pkg/cloudca/services/cloudca/recovery_point.go diff --git a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/remote_access_vpn.go b/pkg/cloudca/services/cloudca/remote_access_vpn.go similarity index 97% rename from vendor/github.com/cloud-ca/go-cloudca/services/cloudca/remote_access_vpn.go rename to pkg/cloudca/services/cloudca/remote_access_vpn.go index d9b08e6..e8b1db9 100644 --- a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/remote_access_vpn.go +++ b/pkg/cloudca/services/cloudca/remote_access_vpn.go @@ -3,8 +3,8 @@ package cloudca import ( "encoding/json" - "github.com/cloud-ca/go-cloudca/api" - "github.com/cloud-ca/go-cloudca/services" + "github.com/cloud-ca/cca/pkg/cloudca/api" + "github.com/cloud-ca/cca/pkg/cloudca/services" ) const ( diff --git a/pkg/cloudca/services/cloudca/remote_access_vpn_test.go b/pkg/cloudca/services/cloudca/remote_access_vpn_test.go new file mode 100644 index 0000000..675d37b --- /dev/null +++ b/pkg/cloudca/services/cloudca/remote_access_vpn_test.go @@ -0,0 +1,242 @@ +package cloudca + +import ( + "testing" + + "github.com/cloud-ca/cca/pkg/cloudca/mocks" + "github.com/cloud-ca/cca/pkg/cloudca/mocks/services_mocks" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" +) + +const ( + TEST_VPN_CERTIFICATE = "test_vpn_certificate" + TEST_VPN_ID = "test_vpn_id" + TEST_VPN_PRESHARED_KEY = "test_vpn_preshared_key" + TEST_VPN_PUBLIC_IP_ADDRESS = "test_vpn_public_ip_address" + TEST_VPN_PUBLIC_IP_ADDRESS_ID = "test_vpn_public_ip_address_id" + TEST_VPN_STATE = "test_vpn_state" + TEST_VPN_TYPE = "test_vpn_type" +) + +func buildTestRemoteAccessVpnJsonResponse(remoteAccessVpn *RemoteAccessVpn) []byte { + return []byte(`{"certificate":"` + remoteAccessVpn.Certificate + `",` + + ` "id":"` + remoteAccessVpn.Id + `",` + + ` "presharedKey":"` + remoteAccessVpn.PresharedKey + `",` + + ` "publicIpAddress":"` + remoteAccessVpn.PublicIpAddress + `",` + + ` "publicIpAddressId":"` + remoteAccessVpn.PublicIpAddressId + `",` + + ` "state":"` + remoteAccessVpn.State + `",` + + ` "type":"` + remoteAccessVpn.Type + `"}`) +} + +func buildListTestRemoteAccessVpnJsonResponse(remoteAccessVpns []RemoteAccessVpn) []byte { + resp := `[` + for i, remoteAccessVpn := range remoteAccessVpns { + resp += string(buildTestRemoteAccessVpnJsonResponse(&remoteAccessVpn)) + if i != len(remoteAccessVpns)-1 { + resp += `,` + } + } + resp += `]` + return []byte(resp) +} + +func TestGetRemoteAccessVpnReturnVpnIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + remoteAccessVpnService := RemoteAccessVpnApi{ + entityService: mockEntityService, + } + + expectedRemoteAccessVpn := RemoteAccessVpn{ + Certificate: TEST_VPN_CERTIFICATE, + Id: TEST_VPN_ID, + PresharedKey: TEST_VPN_PRESHARED_KEY, + PublicIpAddress: TEST_VPN_PUBLIC_IP_ADDRESS, + PublicIpAddressId: TEST_VPN_PUBLIC_IP_ADDRESS_ID, + State: TEST_VPN_STATE, + Type: TEST_VPN_TYPE, + } + + mockEntityService.EXPECT().Get(TEST_VPN_ID, gomock.Any()).Return(buildTestRemoteAccessVpnJsonResponse(&expectedRemoteAccessVpn), nil) + + //when + remoteAccessVpn, _ := remoteAccessVpnService.Get(TEST_VPN_ID) + + //then + if assert.NotNil(t, remoteAccessVpn) { + assert.Equal(t, expectedRemoteAccessVpn, *remoteAccessVpn) + } +} + +func TestGetRemoteAccessVpnReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + remoteAccessVpnService := RemoteAccessVpnApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_get_error"} + + mockEntityService.EXPECT().Get(TEST_VPN_ID, gomock.Any()).Return(nil, mockError) + + //when + remoteAccessVpn, err := remoteAccessVpnService.Get(TEST_VPN_ID) + + //then + assert.Nil(t, remoteAccessVpn) + assert.Equal(t, mockError, err) + +} + +func TestListRemoteAccessVpnReturnVpnsIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + remoteAccessVpnService := RemoteAccessVpnApi{ + entityService: mockEntityService, + } + + expectedRemoteAccessVpn := RemoteAccessVpn{ + Certificate: TEST_VPN_CERTIFICATE, + Id: TEST_VPN_ID, + PresharedKey: TEST_VPN_PRESHARED_KEY, + PublicIpAddress: TEST_VPN_PUBLIC_IP_ADDRESS, + PublicIpAddressId: TEST_VPN_PUBLIC_IP_ADDRESS_ID, + State: TEST_VPN_STATE, + Type: TEST_VPN_TYPE, + } + + expectedRemoteAccessVpns := []RemoteAccessVpn{expectedRemoteAccessVpn} + + mockEntityService.EXPECT().List(gomock.Any()).Return(buildListTestRemoteAccessVpnJsonResponse(expectedRemoteAccessVpns), nil) + + //when + remoteAccessVpns, _ := remoteAccessVpnService.List() + + //then + if assert.NotNil(t, remoteAccessVpns) { + assert.Equal(t, expectedRemoteAccessVpns, remoteAccessVpns) + } +} + +func TestListRemoteAccessVpnReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + remoteAccessVpnService := RemoteAccessVpnApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_list_error"} + + mockEntityService.EXPECT().List(gomock.Any()).Return(nil, mockError) + + //when + remoteAccessVpns, err := remoteAccessVpnService.List() + + //then + assert.Nil(t, remoteAccessVpns) + assert.Equal(t, mockError, err) + +} + +func TestEnableRemoteAccessVpnReturnTrueIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + remoteAccessVpnService := RemoteAccessVpnApi{ + entityService: mockEntityService, + } + + mockEntityService.EXPECT().Execute(TEST_VPN_ID, REMOTE_ACCESS_VPN_ENABLE_OPERATION, gomock.Any(), gomock.Any()).Return([]byte(`{}`), nil) + + //when + success, _ := remoteAccessVpnService.Enable(TEST_VPN_ID) + + //then + assert.True(t, success) +} + +func TestEnableRemoteAccessVpnReturnFalseIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + remoteAccessVpnService := RemoteAccessVpnApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_vpn_enable_error"} + mockEntityService.EXPECT().Execute(TEST_VPN_ID, REMOTE_ACCESS_VPN_ENABLE_OPERATION, gomock.Any(), gomock.Any()).Return([]byte(`{}`), mockError) + + //when + success, err := remoteAccessVpnService.Enable(TEST_VPN_ID) + + //then + assert.False(t, success) + assert.Equal(t, mockError, err) + +} + +func TestDisableRemoteAccessVpnReturnTrueIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + remoteAccessVpnService := RemoteAccessVpnApi{ + entityService: mockEntityService, + } + + mockEntityService.EXPECT().Execute(TEST_VPN_ID, REMOTE_ACCESS_VPN_DISABLE_OPERATION, gomock.Any(), gomock.Any()).Return([]byte(`{}`), nil) + + //when + success, _ := remoteAccessVpnService.Disable(TEST_VPN_ID) + + //then + assert.True(t, success) +} + +func TestDisableRemoteAccessVpnReturnFalseIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + remoteAccessVpnService := RemoteAccessVpnApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_vpn_disable_error"} + mockEntityService.EXPECT().Execute(TEST_VPN_ID, REMOTE_ACCESS_VPN_DISABLE_OPERATION, gomock.Any(), gomock.Any()).Return([]byte(`{}`), mockError) + + //when + success, err := remoteAccessVpnService.Disable(TEST_VPN_ID) + + //then + assert.False(t, success) + assert.Equal(t, mockError, err) + +} diff --git a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/remote_access_vpn_user.go b/pkg/cloudca/services/cloudca/remote_access_vpn_user.go similarity index 96% rename from vendor/github.com/cloud-ca/go-cloudca/services/cloudca/remote_access_vpn_user.go rename to pkg/cloudca/services/cloudca/remote_access_vpn_user.go index 63dc970..553af63 100644 --- a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/remote_access_vpn_user.go +++ b/pkg/cloudca/services/cloudca/remote_access_vpn_user.go @@ -3,8 +3,8 @@ package cloudca import ( "encoding/json" - "github.com/cloud-ca/go-cloudca/api" - "github.com/cloud-ca/go-cloudca/services" + "github.com/cloud-ca/cca/pkg/cloudca/api" + "github.com/cloud-ca/cca/pkg/cloudca/services" ) // RemoteAccessVpnUser is an environment wide user for a RemoteAccessVpn diff --git a/pkg/cloudca/services/cloudca/remote_access_vpn_user_test.go b/pkg/cloudca/services/cloudca/remote_access_vpn_user_test.go new file mode 100644 index 0000000..b5f7f79 --- /dev/null +++ b/pkg/cloudca/services/cloudca/remote_access_vpn_user_test.go @@ -0,0 +1,236 @@ +package cloudca + +import ( + "testing" + + "github.com/cloud-ca/cca/pkg/cloudca/mocks" + "github.com/cloud-ca/cca/pkg/cloudca/mocks/services_mocks" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" +) + +const ( + TEST_VPN_USER_ID = "test_vpn_user_id" + TEST_VPN_USER_USERNAME = "test_vpn_user_username" + TEST_VPN_USER_PASSWORD = "test_vpn_user_password" +) + +func buildTestRemoteAccessVpnUserJsonResponse(remoteAccessVpnUser *RemoteAccessVpnUser) []byte { + return []byte(`{` + + ` "id":"` + remoteAccessVpnUser.Id + `",` + + ` "username":"` + remoteAccessVpnUser.Username + `"}`) +} + +func buildListTestRemoteAccessVpnUserJsonResponse(remoteAccessVpnUsers []RemoteAccessVpnUser) []byte { + resp := `[` + for i, remoteAccessVpnUser := range remoteAccessVpnUsers { + resp += string(buildTestRemoteAccessVpnUserJsonResponse(&remoteAccessVpnUser)) + if i != len(remoteAccessVpnUsers)-1 { + resp += `,` + } + } + resp += `]` + return []byte(resp) +} + +func TestGetRemoteAccessVpnUserReturnVpnUserIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + remoteAccessVpnUserService := RemoteAccessVpnUserApi{ + entityService: mockEntityService, + } + + expectedRemoteAccessVpnUser := RemoteAccessVpnUser{ + Id: TEST_VPN_USER_ID, + Username: TEST_VPN_USER_USERNAME, + } + + mockEntityService.EXPECT().Get(TEST_VPN_USER_ID, gomock.Any()).Return(buildTestRemoteAccessVpnUserJsonResponse(&expectedRemoteAccessVpnUser), nil) + + //when + remoteAccessVpnUser, _ := remoteAccessVpnUserService.Get(TEST_VPN_USER_ID) + + //then + if assert.NotNil(t, remoteAccessVpnUser) { + assert.Equal(t, expectedRemoteAccessVpnUser, *remoteAccessVpnUser) + } +} + +func TestGetRemoteAccessVpnUserReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + remoteAccessVpnUserService := RemoteAccessVpnUserApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_get_error"} + + mockEntityService.EXPECT().Get(TEST_VPN_USER_ID, gomock.Any()).Return(nil, mockError) + + //when + remoteAccessVpnUser, err := remoteAccessVpnUserService.Get(TEST_VPN_USER_ID) + + //then + assert.Nil(t, remoteAccessVpnUser) + assert.Equal(t, mockError, err) + +} + +func TestListRemoteAccessVpnUsersReturnVpnUsersIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + remoteAccessVpnUserService := RemoteAccessVpnUserApi{ + entityService: mockEntityService, + } + + expectedRemoteAccessVpnUser := RemoteAccessVpnUser{ + Id: TEST_VPN_USER_ID, + Username: TEST_VPN_USER_USERNAME, + } + + expectedRemoteAccessVpnUsers := []RemoteAccessVpnUser{expectedRemoteAccessVpnUser} + + mockEntityService.EXPECT().List(gomock.Any()).Return(buildListTestRemoteAccessVpnUserJsonResponse(expectedRemoteAccessVpnUsers), nil) + + //when + remoteAccessVpnUsers, _ := remoteAccessVpnUserService.List() + + //then + if assert.NotNil(t, remoteAccessVpnUsers) { + assert.Equal(t, expectedRemoteAccessVpnUsers, remoteAccessVpnUsers) + } +} + +func TestListRemoteAccessVpnUsersReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + remoteAccessVpnUserService := RemoteAccessVpnUserApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_list_error"} + + mockEntityService.EXPECT().List(gomock.Any()).Return(nil, mockError) + + //when + remoteAccessVpnUsers, err := remoteAccessVpnUserService.List() + + //then + assert.Nil(t, remoteAccessVpnUsers) + assert.Equal(t, mockError, err) +} + +func TestCreateRemoteAccessVpnUserReturnVpnUserIfSuccess(t *testing.T) { + // given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + remoteAccessVpnUserService := RemoteAccessVpnUserApi{ + entityService: mockEntityService, + } + + createRemoteAccessVpnUser := RemoteAccessVpnUser{ + Username: TEST_VPN_USER_USERNAME, + Password: TEST_VPN_USER_PASSWORD, + } + + mockEntityService.EXPECT().Create(gomock.Any(), gomock.Any()).Return([]byte(`{}`), nil) + + // when + success, _ := remoteAccessVpnUserService.Create(createRemoteAccessVpnUser) + + // then + assert.True(t, success) +} + +func TestCreateRemoteAccessVpnUserReturnNilWithErrorIfError(t *testing.T) { + // given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + remoteAccessVpnUserService := RemoteAccessVpnUserApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_create_error"} + mockEntityService.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil, mockError) + + // when + success, err := remoteAccessVpnUserService.Create(RemoteAccessVpnUser{}) + + // then + assert.False(t, success) + assert.Equal(t, mockError, err) +} + +func TestDeleteRemoteAccessVpnUserReturnTrueIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + remoteAccessVpnUserService := RemoteAccessVpnUserApi{ + entityService: mockEntityService, + } + + deleteRemoteAccessVpnUser := RemoteAccessVpnUser{ + Username: TEST_VPN_USER_USERNAME, + Password: TEST_VPN_USER_PASSWORD, + } + + mockEntityService.EXPECT().Delete(gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte(`{}`), nil) + + //when + success, _ := remoteAccessVpnUserService.Delete(deleteRemoteAccessVpnUser) + + //then + assert.True(t, success) +} + +func TestDeleteRemoteAccessVpnUserReturnFalseIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + remoteAccessVpnUserService := RemoteAccessVpnUserApi{ + entityService: mockEntityService, + } + + deleteRemoteAccessVpnUser := RemoteAccessVpnUser{ + Username: TEST_VPN_USER_USERNAME, + Password: TEST_VPN_USER_PASSWORD, + } + + mockError := mocks.MockError{"some_delete_error"} + mockEntityService.EXPECT().Delete(gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte(`{}`), mockError) + //when + success, err := remoteAccessVpnUserService.Delete(deleteRemoteAccessVpnUser) + + //then + assert.False(t, success) + assert.Equal(t, mockError, err) + +} diff --git a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/resources.go b/pkg/cloudca/services/cloudca/resources.go old mode 100644 new mode 100755 similarity index 98% rename from vendor/github.com/cloud-ca/go-cloudca/services/cloudca/resources.go rename to pkg/cloudca/services/cloudca/resources.go index 9af736f..b7467ed --- a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/resources.go +++ b/pkg/cloudca/services/cloudca/resources.go @@ -1,7 +1,7 @@ package cloudca import ( - "github.com/cloud-ca/go-cloudca/api" + "github.com/cloud-ca/cca/pkg/cloudca/api" ) const ( diff --git a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/ssh_key.go b/pkg/cloudca/services/cloudca/ssh_key.go similarity index 96% rename from vendor/github.com/cloud-ca/go-cloudca/services/cloudca/ssh_key.go rename to pkg/cloudca/services/cloudca/ssh_key.go index 3e54b03..2e81791 100644 --- a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/ssh_key.go +++ b/pkg/cloudca/services/cloudca/ssh_key.go @@ -3,8 +3,8 @@ package cloudca import ( "encoding/json" - "github.com/cloud-ca/go-cloudca/api" - "github.com/cloud-ca/go-cloudca/services" + "github.com/cloud-ca/cca/pkg/cloudca/api" + "github.com/cloud-ca/cca/pkg/cloudca/services" ) type SSHKey struct { diff --git a/pkg/cloudca/services/cloudca/ssh_key_test.go b/pkg/cloudca/services/cloudca/ssh_key_test.go new file mode 100644 index 0000000..53139bf --- /dev/null +++ b/pkg/cloudca/services/cloudca/ssh_key_test.go @@ -0,0 +1,138 @@ +package cloudca + +import ( + "testing" + + "github.com/cloud-ca/cca/pkg/cloudca/mocks" + "github.com/cloud-ca/cca/pkg/cloudca/mocks/services_mocks" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" +) + +const ( + TEST_SSH_KEY_NAME = "test_ssh_key" + TEST_SSH_KEY_FINGERPRINT = "test_fingerprint" +) + +func buildSSHKeyJsonResponse(sshKey *SSHKey) []byte { + return []byte(`{"name": "` + sshKey.Name + + `","fingerprint":"` + sshKey.Fingerprint + `"}`) +} + +func buildListSSHKeyJsonResponse(sshKeys []SSHKey) []byte { + resp := `[` + for i, s := range sshKeys { + resp += string(buildSSHKeyJsonResponse(&s)) + if i != len(sshKeys)-1 { + resp += `,` + } + } + resp += `]` + return []byte(resp) +} + +func TestGetSSHKeyReturnSSHKeyIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + sshKeyService := SSHKeyApi{ + entityService: mockEntityService, + } + + expectedSSHKey := SSHKey{Name: TEST_SSH_KEY_NAME, + Fingerprint: TEST_SSH_KEY_FINGERPRINT} + + mockEntityService.EXPECT().Get(TEST_SSH_KEY_NAME, gomock.Any()).Return(buildSSHKeyJsonResponse(&expectedSSHKey), nil) + + //when + sshKey, _ := sshKeyService.Get(TEST_SSH_KEY_NAME) + + //then + if assert.NotNil(t, sshKey) { + assert.Equal(t, expectedSSHKey, *sshKey) + } +} + +func TestGetSSHKeyReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + sshKeyService := SSHKeyApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_get_error"} + + mockEntityService.EXPECT().Get(TEST_SSH_KEY_NAME, gomock.Any()).Return(nil, mockError) + + //when + sshKey, err := sshKeyService.Get(TEST_SSH_KEY_NAME) + + //then + assert.Nil(t, sshKey) + assert.Equal(t, mockError, err) + +} + +func TestListSSHKeyReturnSSHKeysIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + sshKeyService := SSHKeyApi{ + entityService: mockEntityService, + } + + expectedSSHKeys := []SSHKey{ + { + Name: "list_name_1", + Fingerprint: "list_fingerprint_1", + }, + { + Name: "list_name_2", + Fingerprint: "list_fingerprint_2", + }, + } + + mockEntityService.EXPECT().List(gomock.Any()).Return(buildListSSHKeyJsonResponse(expectedSSHKeys), nil) + + //when + sshKeys, _ := sshKeyService.List() + + //then + if assert.NotNil(t, sshKeys) { + assert.Equal(t, expectedSSHKeys, sshKeys) + } +} + +func TestListSSHKeyReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + sshKeyService := SSHKeyApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_list_error"} + + mockEntityService.EXPECT().List(gomock.Any()).Return(nil, mockError) + + //when + sshKeys, err := sshKeyService.List() + + //then + assert.Nil(t, sshKeys) + assert.Equal(t, mockError, err) + +} diff --git a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/template.go b/pkg/cloudca/services/cloudca/template.go similarity index 97% rename from vendor/github.com/cloud-ca/go-cloudca/services/cloudca/template.go rename to pkg/cloudca/services/cloudca/template.go index 66ac2f6..3fa98e7 100644 --- a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/template.go +++ b/pkg/cloudca/services/cloudca/template.go @@ -3,8 +3,8 @@ package cloudca import ( "encoding/json" - "github.com/cloud-ca/go-cloudca/api" - "github.com/cloud-ca/go-cloudca/services" + "github.com/cloud-ca/cca/pkg/cloudca/api" + "github.com/cloud-ca/cca/pkg/cloudca/services" ) type Template struct { diff --git a/pkg/cloudca/services/cloudca/template_test.go b/pkg/cloudca/services/cloudca/template_test.go new file mode 100644 index 0000000..50c0eaf --- /dev/null +++ b/pkg/cloudca/services/cloudca/template_test.go @@ -0,0 +1,194 @@ +package cloudca + +import ( + "strconv" + "testing" + + "github.com/cloud-ca/cca/pkg/cloudca/mocks" + "github.com/cloud-ca/cca/pkg/cloudca/mocks/services_mocks" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" +) + +const ( + TEST_TEMPLATE_ID = "test_template_id" + TEST_TEMPLATE_NAME = "test_template" + TEST_TEMPLATE_DESCRIPTION = "test_template_description" + TEST_TEMPLATE_SIZE = 60 + TEST_TEMPLATE_AVAILABLE_PUBLICLY = true + TEST_TEMPLATE_IS_READY = true + TEST_TEMPLATE_SSH_KEY_ENABLED = false + TEST_TEMPLATE_EXTRACTABLE = true + TEST_TEMPLATE_OS_TYPE = "test_template_os_type" + TEST_TEMPLATE_OS_TYPE_ID = "test_template_os_type_id" + TEST_TEMPLATE_HYPERVISOR = "test_template_hypervisor" + TEST_TEMPLATE_FORMAT = "test_template_format" + TEST_TEMPLATE_ZONE_NAME = "test_template_zone_name" + TEST_TEMPLATE_PROJECT_ID = "test_template_project_id" +) + +func buildTemplateJsonResponse(template *Template) []byte { + return []byte(`{"id":"` + template.ID + `",` + + ` "name": "` + template.Name + `",` + + ` "description": "` + template.Description + `",` + + ` "size": ` + strconv.Itoa(template.Size) + `,` + + ` "availablePublicly": ` + strconv.FormatBool(template.AvailablePublicly) + `,` + + ` "ready": ` + strconv.FormatBool(template.Ready) + `,` + + ` "sshKeyEnabled": ` + strconv.FormatBool(template.SSHKeyEnabled) + `,` + + ` "extractable": ` + strconv.FormatBool(template.Extractable) + `,` + + ` "osType": "` + template.OSType + `",` + + ` "osTypeId": "` + template.OSTypeID + `",` + + ` "hypervisor": "` + template.Hypervisor + `",` + + ` "format": "` + template.Format + `",` + + ` "projectId": "` + template.ProjectID + `"}`) +} + +func buildListTemplateJsonResponse(templates []Template) []byte { + resp := `[` + for i, t := range templates { + resp += string(buildTemplateJsonResponse(&t)) + if i != len(templates)-1 { + resp += `,` + } + } + resp += `]` + return []byte(resp) +} + +func TestGetTemplateReturnTemplateIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + templateService := TemplateApi{ + entityService: mockEntityService, + } + + expectedTemplate := Template{ID: TEST_TEMPLATE_ID, + Name: TEST_TEMPLATE_NAME, + Description: TEST_TEMPLATE_DESCRIPTION, + Size: TEST_TEMPLATE_SIZE, + AvailablePublicly: TEST_TEMPLATE_AVAILABLE_PUBLICLY, + Ready: TEST_TEMPLATE_IS_READY, + SSHKeyEnabled: TEST_TEMPLATE_SSH_KEY_ENABLED, + Extractable: TEST_TEMPLATE_EXTRACTABLE, + OSType: TEST_TEMPLATE_OS_TYPE, + OSTypeID: TEST_TEMPLATE_OS_TYPE_ID, + Hypervisor: TEST_TEMPLATE_HYPERVISOR, + Format: TEST_TEMPLATE_FORMAT, + ProjectID: TEST_TEMPLATE_PROJECT_ID, + } + + mockEntityService.EXPECT().Get(TEST_TEMPLATE_ID, gomock.Any()).Return(buildTemplateJsonResponse(&expectedTemplate), nil) + + //when + template, _ := templateService.Get(TEST_TEMPLATE_ID) + + //then + if assert.NotNil(t, template) { + assert.Equal(t, expectedTemplate, *template) + } +} + +func TestGetTemplateReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + templateService := TemplateApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_get_error"} + + mockEntityService.EXPECT().Get(TEST_TEMPLATE_ID, gomock.Any()).Return(nil, mockError) + + //when + template, err := templateService.Get(TEST_TEMPLATE_ID) + + //then + assert.Nil(t, template) + assert.Equal(t, mockError, err) + +} + +func TestListTemplateReturnTemplatesIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + templateService := TemplateApi{ + entityService: mockEntityService, + } + + expectedTemplate1 := Template{ID: "list_id_1", + Name: "list_name_1", + Description: "list_description_1", + Size: 132123, + AvailablePublicly: true, + Ready: true, + SSHKeyEnabled: true, + Extractable: true, + OSType: "list_os_type_1", + OSTypeID: "list_os_type_id_1", + Hypervisor: "list_hypervisor_1", + Format: "list_format_1", + ProjectID: "list_project_id_1", + } + expectedTemplate2 := Template{ID: "list_id_2", + Name: "list_name_2", + Description: "list_description_2", + Size: 4525, + AvailablePublicly: false, + Ready: false, + SSHKeyEnabled: false, + Extractable: false, + OSType: "list_os_type_2", + OSTypeID: "list_os_type_id_2", + Hypervisor: "list_hypervisor_2", + Format: "list_format_2", + ProjectID: "list_project_id_2", + } + + expectedTemplates := []Template{expectedTemplate1, expectedTemplate2} + + mockEntityService.EXPECT().List(gomock.Any()).Return(buildListTemplateJsonResponse(expectedTemplates), nil) + + //when + templates, _ := templateService.List() + + //then + if assert.NotNil(t, templates) { + assert.Equal(t, expectedTemplates, templates) + } +} + +func TestListTemplateReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + templateService := TemplateApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_list_error"} + + mockEntityService.EXPECT().List(gomock.Any()).Return(nil, mockError) + + //when + templates, err := templateService.List() + + //then + assert.Nil(t, templates) + assert.Equal(t, mockError, err) + +} diff --git a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/volume.go b/pkg/cloudca/services/cloudca/volume.go similarity index 97% rename from vendor/github.com/cloud-ca/go-cloudca/services/cloudca/volume.go rename to pkg/cloudca/services/cloudca/volume.go index 930d85c..1d97952 100644 --- a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/volume.go +++ b/pkg/cloudca/services/cloudca/volume.go @@ -3,8 +3,8 @@ package cloudca import ( "encoding/json" - "github.com/cloud-ca/go-cloudca/api" - "github.com/cloud-ca/go-cloudca/services" + "github.com/cloud-ca/cca/pkg/cloudca/api" + "github.com/cloud-ca/cca/pkg/cloudca/services" ) const ( diff --git a/pkg/cloudca/services/cloudca/volume_test.go b/pkg/cloudca/services/cloudca/volume_test.go new file mode 100644 index 0000000..dd69c5c --- /dev/null +++ b/pkg/cloudca/services/cloudca/volume_test.go @@ -0,0 +1,349 @@ +package cloudca + +import ( + "strconv" + "testing" + + "github.com/cloud-ca/cca/pkg/cloudca/mocks" + "github.com/cloud-ca/cca/pkg/cloudca/mocks/services_mocks" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" +) + +const ( + TEST_VOLUME_ID = "test_volume_id" + TEST_VOLUME_NAME = "test_volume" + TEST_VOLUME_TYPE = "test_volume_type" + TEST_VOLUME_CREATION_DATE = "test_volume_creation_date" + TEST_VOLUME_SIZE = 500 + TEST_VOLUME_DISK_OFFERING_ID = "test_volume_disk_offering_id" + TEST_VOLUME_TEMPLATE_ID = "test_volume_template_id" + TEST_VOLUME_ZONE_NAME = "test_volume_zone_name" + TEST_VOLUME_STATE = "test_volume_state" + TEST_VOLUME_INSTANCE_NAME = "test_volume_instance_name" + TEST_VOLUME_INSTANCE_ID = "test_volume_instance_id" + TEST_VOLUME_INSTANCE_STATE = "test_volume_instance_state" +) + +func buildVolumeJsonResponse(volume *Volume) []byte { + return []byte(`{"id":"` + volume.Id + `",` + + ` "name": "` + volume.Name + `",` + + ` "type": "` + volume.Type + `",` + + ` "creationDate": "` + volume.CreationDate + `",` + + ` "size": ` + strconv.Itoa(volume.Size) + `,` + + ` "sizeInGb": ` + strconv.Itoa(volume.GbSize) + `,` + + ` "diskOfferingId": "` + volume.DiskOfferingId + `",` + + ` "templateId": "` + volume.TemplateId + `",` + + ` "zoneName": "` + volume.ZoneName + `",` + + ` "state": "` + volume.State + `",` + + ` "instanceName": "` + volume.InstanceName + `",` + + ` "instanceId": "` + volume.InstanceId + `",` + + ` "instanceState": "` + volume.InstanceState + `"}`) +} + +func buildListVolumeJsonResponse(volumes []Volume) []byte { + resp := `[` + for i, v := range volumes { + resp += string(buildVolumeJsonResponse(&v)) + if i != len(volumes)-1 { + resp += `,` + } + } + resp += `]` + return []byte(resp) +} + +func TestGetVolumeReturnVolumeIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + volumeService := VolumeApi{ + entityService: mockEntityService, + } + + expectedVolume := Volume{Id: TEST_VOLUME_ID, + Name: TEST_VOLUME_NAME, + Type: TEST_VOLUME_TYPE, + CreationDate: TEST_VOLUME_CREATION_DATE, + Size: TEST_VOLUME_SIZE, + DiskOfferingId: TEST_VOLUME_DISK_OFFERING_ID, + TemplateId: TEST_VOLUME_TEMPLATE_ID, + ZoneName: TEST_VOLUME_ZONE_NAME, + State: TEST_VOLUME_STATE, + InstanceName: TEST_VOLUME_INSTANCE_NAME, + InstanceId: TEST_VOLUME_INSTANCE_ID, + InstanceState: TEST_VOLUME_INSTANCE_STATE} + + mockEntityService.EXPECT().Get(TEST_VOLUME_ID, gomock.Any()).Return(buildVolumeJsonResponse(&expectedVolume), nil) + + //when + volume, _ := volumeService.Get(TEST_VOLUME_ID) + + //then + if assert.NotNil(t, volume) { + assert.Equal(t, expectedVolume, *volume) + } +} + +func TestGetVolumeReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + volumeService := VolumeApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_get_error"} + + mockEntityService.EXPECT().Get(TEST_VOLUME_ID, gomock.Any()).Return(nil, mockError) + + //when + volume, err := volumeService.Get(TEST_VOLUME_ID) + + //then + assert.Nil(t, volume) + assert.Equal(t, mockError, err) + +} + +func TestListVolumeReturnVolumesIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + volumeService := VolumeApi{ + entityService: mockEntityService, + } + + expectedVolume1 := Volume{Id: "list_id_1", + Name: "list_name_1", + Type: "list_type_1", + CreationDate: "list_creation_date_1", + Size: 1215, + DiskOfferingId: "list_disk_offering_id_1", + TemplateId: "list_template_id_1", + ZoneName: "list_zone_name_1", + State: "list_state_1", + InstanceName: "list_instance_name_1", + InstanceId: "list_instance_id_1", + InstanceState: "list_instance_state_1"} + expectedVolume2 := Volume{Id: "list_id_2", + Name: "list_name_2", + Type: "list_type_2", + CreationDate: "list_creation_date_2", + Size: 54582, + DiskOfferingId: "list_disk_offering_id_2", + TemplateId: "list_template_id_2", + ZoneName: "list_zone_name_2", + State: "list_state_2", + InstanceName: "list_instance_name_2", + InstanceId: "list_instance_id_2", + InstanceState: "list_instance_state_2"} + + expectedVolumes := []Volume{expectedVolume1, expectedVolume2} + + mockEntityService.EXPECT().List(gomock.Any()).Return(buildListVolumeJsonResponse(expectedVolumes), nil) + + //when + volumes, _ := volumeService.List() + + //then + if assert.NotNil(t, volumes) { + assert.Equal(t, expectedVolumes, volumes) + } +} + +func TestListVolumeReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + volumeService := VolumeApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_list_error"} + + mockEntityService.EXPECT().List(gomock.Any()).Return(nil, mockError) + + //when + volumes, err := volumeService.List() + + //then + assert.Nil(t, volumes) + assert.Equal(t, mockError, err) + +} + +func TestCreateReturnsErrorIfErrorWhileCreating(t *testing.T) { + // given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockEntityService := services_mocks.NewMockEntityService(ctrl) + volumeService := VolumeApi{ + entityService: mockEntityService, + } + mockError := mocks.MockError{"creation error"} + mockEntityService.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil, mockError) + + // when + volume, err := volumeService.Create(Volume{}) + + // then + assert.Nil(t, volume) + assert.Equal(t, mockError, err) +} + +func TestCreateSucceeds(t *testing.T) { + // given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockEntityService := services_mocks.NewMockEntityService(ctrl) + volumeService := VolumeApi{ + entityService: mockEntityService, + } + expected := Volume{ + Id: "expected", + } + mockEntityService.EXPECT().Create(gomock.Any(), gomock.Any()).Return(buildVolumeJsonResponse(&expected), nil) + + // when + volume, err := volumeService.Create(Volume{}) + + // then + assert.Equal(t, expected, *volume) + assert.Nil(t, err) +} + +func TestAttachToInstanceFailure(t *testing.T) { + // given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockEntityService := services_mocks.NewMockEntityService(ctrl) + volumeService := VolumeApi{ + entityService: mockEntityService, + } + toAttach := &Volume{ + Id: "toAttach", + } + mockError := mocks.MockError{"attach error"} + mockEntityService.EXPECT().Execute(toAttach.Id, "attachToInstance", gomock.Any(), gomock.Any()).Return(nil, mockError) + + // when + err := volumeService.AttachToInstance(toAttach, "some instance") + + // then + assert.Equal(t, mockError, err) +} + +func TestAttachToInstanceSuccess(t *testing.T) { + // given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockEntityService := services_mocks.NewMockEntityService(ctrl) + volumeService := VolumeApi{ + entityService: mockEntityService, + } + toAttach := &Volume{ + Id: "toAttach", + } + mockEntityService.EXPECT().Execute(toAttach.Id, "attachToInstance", gomock.Any(), gomock.Any()).Return([]byte("success!"), nil) + + // when + err := volumeService.AttachToInstance(toAttach, "some instance") + + // then + assert.Nil(t, err) +} + +func TestDetachFromInstanceFailure(t *testing.T) { + // given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockEntityService := services_mocks.NewMockEntityService(ctrl) + volumeService := VolumeApi{ + entityService: mockEntityService, + } + toDetach := &Volume{ + Id: "toDetach", + } + mockError := mocks.MockError{"Detach error"} + mockEntityService.EXPECT().Execute(toDetach.Id, "detachFromInstance", gomock.Any(), gomock.Any()).Return(nil, mockError) + + // when + err := volumeService.DetachFromInstance(toDetach) + + // then + assert.Equal(t, mockError, err) +} + +func TestDetachFromInstanceSuccess(t *testing.T) { + // given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockEntityService := services_mocks.NewMockEntityService(ctrl) + volumeService := VolumeApi{ + entityService: mockEntityService, + } + toDetach := &Volume{ + Id: "toDetach", + } + mockEntityService.EXPECT().Execute(toDetach.Id, "detachFromInstance", gomock.Any(), gomock.Any()).Return([]byte("success!"), nil) + + // when + err := volumeService.DetachFromInstance(toDetach) + + // then + assert.Nil(t, err) +} + +func TestDeleteSuccess(t *testing.T) { + // given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockEntityService := services_mocks.NewMockEntityService(ctrl) + volumeService := VolumeApi{ + entityService: mockEntityService, + } + toDelete := &Volume{ + Id: "toDelete", + } + mockEntityService.EXPECT().Delete(toDelete.Id, gomock.Any(), gomock.Any()).Return([]byte("success!"), nil) + + // when + err := volumeService.Delete(toDelete.Id) + + // then + assert.Nil(t, err) +} + +func TestDeleteFailure(t *testing.T) { + // given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockEntityService := services_mocks.NewMockEntityService(ctrl) + volumeService := VolumeApi{ + entityService: mockEntityService, + } + toDelete := &Volume{ + Id: "toDelete", + } + mockError := mocks.MockError{"delete error"} + mockEntityService.EXPECT().Delete(toDelete.Id, gomock.Any(), gomock.Any()).Return(nil, mockError) + + // when + err := volumeService.Delete(toDelete.Id) + + // then + assert.Equal(t, mockError, err) +} diff --git a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/vpc.go b/pkg/cloudca/services/cloudca/vpc.go similarity index 97% rename from vendor/github.com/cloud-ca/go-cloudca/services/cloudca/vpc.go rename to pkg/cloudca/services/cloudca/vpc.go index bbde7a4..2af3d07 100644 --- a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/vpc.go +++ b/pkg/cloudca/services/cloudca/vpc.go @@ -2,8 +2,9 @@ package cloudca import ( "encoding/json" - "github.com/cloud-ca/go-cloudca/api" - "github.com/cloud-ca/go-cloudca/services" + + "github.com/cloud-ca/cca/pkg/cloudca/api" + "github.com/cloud-ca/cca/pkg/cloudca/services" ) const ( diff --git a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/vpc_offering.go b/pkg/cloudca/services/cloudca/vpc_offering.go similarity index 95% rename from vendor/github.com/cloud-ca/go-cloudca/services/cloudca/vpc_offering.go rename to pkg/cloudca/services/cloudca/vpc_offering.go index 1f5b52d..4250b52 100644 --- a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/vpc_offering.go +++ b/pkg/cloudca/services/cloudca/vpc_offering.go @@ -2,8 +2,9 @@ package cloudca import ( "encoding/json" - "github.com/cloud-ca/go-cloudca/api" - "github.com/cloud-ca/go-cloudca/services" + + "github.com/cloud-ca/cca/pkg/cloudca/api" + "github.com/cloud-ca/cca/pkg/cloudca/services" ) type VpcOffering struct { diff --git a/pkg/cloudca/services/cloudca/vpc_offering_test.go b/pkg/cloudca/services/cloudca/vpc_offering_test.go new file mode 100644 index 0000000..e31d624 --- /dev/null +++ b/pkg/cloudca/services/cloudca/vpc_offering_test.go @@ -0,0 +1,138 @@ +package cloudca + +import ( + "testing" + + "github.com/cloud-ca/cca/pkg/cloudca/mocks" + "github.com/cloud-ca/cca/pkg/cloudca/mocks/services_mocks" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" +) + +const ( + TEST_VPC_OFFERING_ID = "test_vpc_offering_id" + TEST_VPC_OFFERING_NAME = "test_vpc_offering" + TEST_VPC_OFFERING_STATE = "test_vpc_offering_state" +) + +func buildTestVpcOfferingJsonResponse(vpcOffering *VpcOffering) []byte { + return []byte(`{"id": "` + vpcOffering.Id + `", ` + + `"name":"` + vpcOffering.Name + `", ` + + `"state":"` + vpcOffering.State + `"}`) +} + +func buildListTestVpcOfferingJsonResponse(vpcOfferings []VpcOffering) []byte { + resp := `[` + for i, vpcOffering := range vpcOfferings { + resp += string(buildTestVpcOfferingJsonResponse(&vpcOffering)) + if i != len(vpcOfferings)-1 { + resp += `,` + } + } + resp += `]` + return []byte(resp) +} + +func TestGetVpcOfferingReturnVpcOfferingIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + vpcOfferingService := VpcOfferingApi{ + entityService: mockEntityService, + } + + expectedVpcOffering := VpcOffering{Id: TEST_VPC_OFFERING_ID, + Name: TEST_VPC_OFFERING_NAME, + State: TEST_VPC_OFFERING_STATE, + } + + mockEntityService.EXPECT().Get(TEST_VPC_OFFERING_ID, gomock.Any()).Return(buildTestVpcOfferingJsonResponse(&expectedVpcOffering), nil) + + //when + vpcOffering, _ := vpcOfferingService.Get(TEST_VPC_OFFERING_ID) + + //then + if assert.NotNil(t, vpcOffering) { + assert.Equal(t, expectedVpcOffering, *vpcOffering) + } +} + +func TestGetVpcOfferingReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + vpcOfferingService := VpcOfferingApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_get_error"} + + mockEntityService.EXPECT().Get(TEST_VPC_OFFERING_ID, gomock.Any()).Return(nil, mockError) + + //when + vpcOffering, err := vpcOfferingService.Get(TEST_VPC_OFFERING_ID) + + //then + assert.Nil(t, vpcOffering) + assert.Equal(t, mockError, err) + +} + +func TestListVpcOfferingReturnVpcsIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + vpcOfferingService := VpcOfferingApi{ + entityService: mockEntityService, + } + + expectedVpcOffering1 := VpcOffering{Id: TEST_VPC_OFFERING_ID + "1", + Name: TEST_VPC_OFFERING_NAME + "1", + State: TEST_VPC_OFFERING_STATE + "1", + } + + expectedVpcOfferings := []VpcOffering{expectedVpcOffering1} + + mockEntityService.EXPECT().List(gomock.Any()).Return(buildListTestVpcOfferingJsonResponse(expectedVpcOfferings), nil) + + //when + vpcOfferings, _ := vpcOfferingService.List() + + //then + if assert.NotNil(t, vpcOfferings) { + assert.Equal(t, expectedVpcOfferings, vpcOfferings) + } +} + +func TestListVpcOfferingReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + vpcOfferingService := VpcOfferingApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_list_error"} + + mockEntityService.EXPECT().List(gomock.Any()).Return(nil, mockError) + + //when + vpcOfferings, err := vpcOfferingService.List() + + //then + assert.Nil(t, vpcOfferings) + assert.Equal(t, mockError, err) + +} diff --git a/pkg/cloudca/services/cloudca/vpc_test.go b/pkg/cloudca/services/cloudca/vpc_test.go new file mode 100644 index 0000000..fc5beb0 --- /dev/null +++ b/pkg/cloudca/services/cloudca/vpc_test.go @@ -0,0 +1,272 @@ +package cloudca + +import ( + "testing" + + "github.com/cloud-ca/cca/pkg/cloudca/mocks" + "github.com/cloud-ca/cca/pkg/cloudca/mocks/services_mocks" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" +) + +const ( + TEST_VPC_ID = "test_vpc_id" + TEST_VPC_NAME = "test_vpc" + TEST_VPC_DESCRIPTION = "test_vpc_description" + TEST_VPC_STATE = "test_vpc_state" + TEST_VPC_CIDR = "test_vpc_cidr" + TEST_VPC_ZONE_ID = "test_vpc_zone_id" + TEST_VPC_ZONE_NAME = "test_vpc_zone_name" + TEST_VPC_NETWORK_DOMAIN = "test_vpc_network_domain" + TEST_VPC_SOURCE_NAT_IP = "test_vpc_source_nat_ip" + TEST_VPC_VPN_STATUS = "test_vpc_vpn_status" + TEST_VPC_TYPE = "test_vpc_type" + TEST_VPC_VPC_OFFERING_ID = "test_vpc_offering_id" +) + +func buildTestVpcJsonResponse(vpc *Vpc) []byte { + return []byte(`{"id": "` + vpc.Id + `", ` + + `"name":"` + vpc.Name + `", ` + + `"description":"` + vpc.Description + `", ` + + `"state":"` + vpc.State + `", ` + + `"cidr":"` + vpc.Cidr + `", ` + + `"zoneId":"` + vpc.ZoneId + `", ` + + `"zoneName":"` + vpc.ZoneName + `", ` + + `"networkDomain":"` + vpc.NetworkDomain + `", ` + + `"sourceNatIp":"` + vpc.SourceNatIp + `", ` + + `"vpnStatus":"` + vpc.VpnStatus + `", ` + + `"type":"` + vpc.Type + `", ` + + `"vpcOfferingId":"` + vpc.VpcOfferingId + `"}`) +} + +func buildListTestVpcJsonResponse(vpcs []Vpc) []byte { + resp := `[` + for i, vpc := range vpcs { + resp += string(buildTestVpcJsonResponse(&vpc)) + if i != len(vpcs)-1 { + resp += `,` + } + } + resp += `]` + return []byte(resp) +} + +func TestGetVpcReturnVpcIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + vpcService := VpcApi{ + entityService: mockEntityService, + } + + expectedVpc := Vpc{Id: TEST_VPC_ID, + Name: TEST_VPC_NAME, + Description: TEST_VPC_DESCRIPTION, + State: TEST_VPC_STATE, + Cidr: TEST_VPC_CIDR, + ZoneId: TEST_VPC_ZONE_ID, + ZoneName: TEST_VPC_ZONE_NAME, + NetworkDomain: TEST_VPC_NETWORK_DOMAIN, + SourceNatIp: TEST_VPC_SOURCE_NAT_IP, + VpnStatus: TEST_VPC_VPN_STATUS, + Type: TEST_VPC_TYPE, + VpcOfferingId: TEST_VPC_VPC_OFFERING_ID, + } + + mockEntityService.EXPECT().Get(TEST_VPC_ID, gomock.Any()).Return(buildTestVpcJsonResponse(&expectedVpc), nil) + + //when + vpc, _ := vpcService.Get(TEST_VPC_ID) + + //then + if assert.NotNil(t, vpc) { + assert.Equal(t, expectedVpc, *vpc) + } +} + +func TestGetVpcReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + vpcService := VpcApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_get_error"} + + mockEntityService.EXPECT().Get(TEST_VPC_ID, gomock.Any()).Return(nil, mockError) + + //when + vpc, err := vpcService.Get(TEST_VPC_ID) + + //then + assert.Nil(t, vpc) + assert.Equal(t, mockError, err) + +} + +func TestListVpcReturnVpcsIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + vpcService := VpcApi{ + entityService: mockEntityService, + } + + expectedVpc1 := Vpc{Id: TEST_VPC_ID + "1", + Name: TEST_VPC_NAME + "1", + Description: TEST_VPC_DESCRIPTION + "1", + State: TEST_VPC_STATE + "1", + Cidr: TEST_VPC_CIDR + "1", + ZoneId: TEST_VPC_ZONE_ID + "1", + ZoneName: TEST_VPC_ZONE_NAME + "1", + NetworkDomain: TEST_VPC_NETWORK_DOMAIN + "1", + SourceNatIp: TEST_VPC_SOURCE_NAT_IP + "1", + VpnStatus: TEST_VPC_VPN_STATUS + "1", + Type: TEST_VPC_TYPE + "1", + VpcOfferingId: TEST_VPC_VPC_OFFERING_ID + "1", + } + + expectedVpcs := []Vpc{expectedVpc1} + + mockEntityService.EXPECT().List(gomock.Any()).Return(buildListTestVpcJsonResponse(expectedVpcs), nil) + + //when + vpcs, _ := vpcService.List() + + //then + if assert.NotNil(t, vpcs) { + assert.Equal(t, expectedVpcs, vpcs) + } +} + +func TestListVpcReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + vpcService := VpcApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_list_error"} + + mockEntityService.EXPECT().List(gomock.Any()).Return(nil, mockError) + + //when + vpcs, err := vpcService.List() + + //then + assert.Nil(t, vpcs) + assert.Equal(t, mockError, err) + +} + +func TestCreateVpcReturnCreatedInstanceIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + vpcService := VpcApi{ + entityService: mockEntityService, + } + + vpcToCreate := Vpc{Name: "new_name", + Description: "new_description", + VpcOfferingId: "vpc_offering_id", + } + + mockEntityService.EXPECT().Create(gomock.Any(), gomock.Any()).Return([]byte(`{"id":"new_id"}`), nil) + + //when + createdVpc, _ := vpcService.Create(vpcToCreate) + + //then + if assert.NotNil(t, createdVpc) { + assert.Equal(t, "new_id", createdVpc.Id) + } +} + +func TestCreateVpcReturnNilWithErrorIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + vpcService := VpcApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_create_vpc_error"} + + mockEntityService.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil, mockError) + + vpcToCreate := Vpc{Name: "new_name", + Description: "new_description", + VpcOfferingId: "vpcOfferingId"} + + //when + createdVpc, err := vpcService.Create(vpcToCreate) + + //then + assert.Nil(t, createdVpc) + assert.Equal(t, mockError, err) + +} + +func TestRestartRouterReturnTrueIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + vpcService := VpcApi{ + entityService: mockEntityService, + } + + mockEntityService.EXPECT().Execute(TEST_VPC_ID, VPC_RESTART_ROUTER_OPERATION, gomock.Any(), gomock.Any()).Return([]byte(`{}`), nil) + + //when + success, _ := vpcService.RestartRouter(TEST_VPC_ID) + + //then + assert.True(t, success) +} + +func TestRestartRouterReturnFalseIfError(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockEntityService := services_mocks.NewMockEntityService(ctrl) + + vpcService := VpcApi{ + entityService: mockEntityService, + } + + mockError := mocks.MockError{"some_purge_vpc_error"} + mockEntityService.EXPECT().Execute(TEST_VPC_ID, VPC_RESTART_ROUTER_OPERATION, gomock.Any(), gomock.Any()).Return([]byte(`{}`), mockError) + + //when + success, err := vpcService.RestartRouter(TEST_VPC_ID) + + //then + assert.False(t, success) + assert.Equal(t, mockError, err) + +} diff --git a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/zone.go b/pkg/cloudca/services/cloudca/zone.go similarity index 93% rename from vendor/github.com/cloud-ca/go-cloudca/services/cloudca/zone.go rename to pkg/cloudca/services/cloudca/zone.go index c64d491..74a3a62 100644 --- a/vendor/github.com/cloud-ca/go-cloudca/services/cloudca/zone.go +++ b/pkg/cloudca/services/cloudca/zone.go @@ -2,8 +2,9 @@ package cloudca import ( "encoding/json" - "github.com/cloud-ca/go-cloudca/api" - "github.com/cloud-ca/go-cloudca/services" + + "github.com/cloud-ca/cca/pkg/cloudca/api" + "github.com/cloud-ca/cca/pkg/cloudca/services" ) type Zone struct { diff --git a/pkg/cloudca/services/cloudca/zone_test.go b/pkg/cloudca/services/cloudca/zone_test.go new file mode 100644 index 0000000..ec0e01b --- /dev/null +++ b/pkg/cloudca/services/cloudca/zone_test.go @@ -0,0 +1,103 @@ +package cloudca + +import ( + "testing" + + "github.com/cloud-ca/cca/pkg/cloudca/mocks" + "github.com/cloud-ca/cca/pkg/cloudca/mocks/services_mocks" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" +) + +func TestGetZoneByIdSuccess(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + assert := assert.New(t) + + // given + mockEntityService := services_mocks.NewMockEntityService(ctrl) + zoneService := ZoneApi{ + entityService: mockEntityService, + } + zoneId := "zoneid" + mockEntityService.EXPECT().Get(zoneId, gomock.Any()).Return([]byte(`{"id":"zoneid","name":"zonename"}`), nil) + + // when + zone, err := zoneService.Get(zoneId) + + // then + assert.Nil(err) + assert.Equal(zoneId, zone.Id) +} + +func TestGetZoneByIdFailure(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + assert := assert.New(t) + + // given + mockEntityService := services_mocks.NewMockEntityService(ctrl) + zoneService := ZoneApi{ + entityService: mockEntityService, + } + zoneId := "zoneid" + mockError := mocks.MockError{"fetch error"} + mockEntityService.EXPECT().Get(zoneId, gomock.Any()).Return(nil, mockError) + + // when + zone, err := zoneService.Get(zoneId) + + // then + assert.Nil(zone) + assert.Equal(mockError, err) +} + +func TestListZonesSuccess(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + assert := assert.New(t) + + // given + mockEntityService := services_mocks.NewMockEntityService(ctrl) + zoneService := ZoneApi{ + entityService: mockEntityService, + } + zone1 := Zone{ + Id: "zoneid1", + Name: "zone1", + } + zone2 := Zone{ + Id: "zoneid2", + Name: "zone2", + } + allZones := []Zone{zone1, zone2} + mockEntityService.EXPECT().List(gomock.Any()).Return([]byte(`[{"id":"zoneid1","name":"zone1"},{"id":"zoneid2","name":"zone2"}]`), nil) + + // when + zones, err := zoneService.List() + + // then + assert.Nil(err) + assert.Equal(zones, allZones) +} + +func TestListZonesFailure(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + assert := assert.New(t) + + // given + mockEntityService := services_mocks.NewMockEntityService(ctrl) + zoneService := ZoneApi{ + entityService: mockEntityService, + } + mockError := mocks.MockError{"list error"} + mockEntityService.EXPECT().List(gomock.Any()).Return(nil, mockError) + + // when + zones, err := zoneService.List() + + // then + assert.Nil(zones) + assert.Equal(mockError, err) +} diff --git a/vendor/github.com/cloud-ca/go-cloudca/services/entity.go b/pkg/cloudca/services/entity.go old mode 100644 new mode 100755 similarity index 99% rename from vendor/github.com/cloud-ca/go-cloudca/services/entity.go rename to pkg/cloudca/services/entity.go index f0e5398..f6fbbea --- a/vendor/github.com/cloud-ca/go-cloudca/services/entity.go +++ b/pkg/cloudca/services/entity.go @@ -1,7 +1,7 @@ package services import ( - "github.com/cloud-ca/go-cloudca/api" + "github.com/cloud-ca/cca/pkg/cloudca/api" ) //A generic service to access any entity diff --git a/vendor/github.com/cloud-ca/go-cloudca/services/service.go b/pkg/cloudca/services/service.go similarity index 100% rename from vendor/github.com/cloud-ca/go-cloudca/services/service.go rename to pkg/cloudca/services/service.go diff --git a/vendor/github.com/cloud-ca/go-cloudca/services/task.go b/pkg/cloudca/services/task.go old mode 100644 new mode 100755 similarity index 98% rename from vendor/github.com/cloud-ca/go-cloudca/services/task.go rename to pkg/cloudca/services/task.go index 966bd76..df48952 --- a/vendor/github.com/cloud-ca/go-cloudca/services/task.go +++ b/pkg/cloudca/services/task.go @@ -2,9 +2,10 @@ package services import ( "encoding/json" - "github.com/cloud-ca/go-cloudca/api" "strings" "time" + + "github.com/cloud-ca/cca/pkg/cloudca/api" ) //Task status diff --git a/pkg/cloudca/services/task_test.go b/pkg/cloudca/services/task_test.go new file mode 100644 index 0000000..4d1888d --- /dev/null +++ b/pkg/cloudca/services/task_test.go @@ -0,0 +1,183 @@ +package services + +import ( + "testing" + + "github.com/cloud-ca/cca/pkg/cloudca/api" + "github.com/cloud-ca/cca/pkg/cloudca/mocks" + "github.com/cloud-ca/cca/pkg/cloudca/mocks/api_mocks" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" +) + +const ( + TEST_TASK_ID = "test_task_id" +) + +func TestGetTaskReturnTaskIfSuccess(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockCcaClient := api_mocks.NewMockApiClient(ctrl) + + taskService := TaskApi{ + apiClient: mockCcaClient, + } + + expectedTask := Task{ + Id: TEST_TASK_ID, + Status: "SUCCESS", + Created: "2015-07-07", + Result: []byte(`{"key": "value"}`), + } + + mockCcaClient.EXPECT().Do(api.CcaRequest{ + Method: api.GET, + Endpoint: "tasks/" + TEST_TASK_ID, + }).Return(&api.CcaResponse{ + StatusCode: 200, + Data: []byte(`{"id":"` + TEST_TASK_ID + `", "status":"SUCCESS", "created":"2015-07-07", "result":{"key": "value"}}`), + }, nil) + + //when + task, _ := taskService.Get(TEST_TASK_ID) + + //then + assert.Equal(t, expectedTask, *task) +} + +func TestGetTaskReturnErrorIfHasCcaErrors(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockCcaClient := api_mocks.NewMockApiClient(ctrl) + + taskService := TaskApi{ + apiClient: mockCcaClient, + } + + ccaResponse := api.CcaResponse{ + StatusCode: 400, + Errors: []api.CcaError{{}}, + } + mockCcaClient.EXPECT().Do(api.CcaRequest{ + Method: api.GET, + Endpoint: "tasks/" + TEST_TASK_ID, + }).Return(&ccaResponse, nil) + + //when + task, err := taskService.Get(TEST_TASK_ID) + + //then + assert.Nil(t, task) + assert.Equal(t, api.CcaErrorResponse(ccaResponse), err) +} + +func TestGetTaskReturnErrorIfHasUnexpectedErrors(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockCcaClient := api_mocks.NewMockApiClient(ctrl) + + taskService := TaskApi{ + apiClient: mockCcaClient, + } + + mockError := mocks.MockError{"some_get_task_error"} + + mockCcaClient.EXPECT().Do(api.CcaRequest{ + Method: api.GET, + Endpoint: "tasks/" + TEST_TASK_ID, + }).Return(nil, mockError) + + //when + task, err := taskService.Get(TEST_TASK_ID) + + //then + assert.Nil(t, task) + assert.Equal(t, mockError, err) +} + +func TestPollingReturnTaskResultOnSuccessfulComplete(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockCcaClient := api_mocks.NewMockApiClient(ctrl) + + taskService := TaskApi{ + apiClient: mockCcaClient, + } + + request := api.CcaRequest{ + Method: api.GET, + Endpoint: "tasks/" + TEST_TASK_ID, + } + + expectedResult := []byte(`{"foo":"bar"}`) + + pendingResponse := &api.CcaResponse{ + StatusCode: 200, + Data: []byte(`{"id":"` + TEST_TASK_ID + `", "status":"PENDING", "created":"2015-07-07"}`), + } + successResponse := &api.CcaResponse{ + StatusCode: 200, + Data: []byte(`{"id":"` + TEST_TASK_ID + `", "status":"SUCCESS", "created":"2015-07-07", "result":` + string(expectedResult) + `}`), + } + gomock.InOrder( + mockCcaClient.EXPECT().Do(request).Return(pendingResponse, nil), + mockCcaClient.EXPECT().Do(request).Return(pendingResponse, nil), + mockCcaClient.EXPECT().Do(request).Return(successResponse, nil), + ) + + //when + result, _ := taskService.Poll(TEST_TASK_ID, 10) + + //then + if assert.NotNil(t, result) { + assert.Equal(t, expectedResult, result) + } +} + +func TestPollingGetErrorOnTaskFailure(t *testing.T) { + //given + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockCcaClient := api_mocks.NewMockApiClient(ctrl) + + taskService := TaskApi{ + apiClient: mockCcaClient, + } + + request := api.CcaRequest{ + Method: api.GET, + Endpoint: "tasks/" + TEST_TASK_ID, + } + + expectedResult := []byte(`{"foo":"bar"}`) + + pendingResponse := &api.CcaResponse{ + StatusCode: 200, + Data: []byte(`{"id":"` + TEST_TASK_ID + `", "status":"PENDING", "created":"2015-07-07"}`), + } + failedResponse := &api.CcaResponse{ + StatusCode: 400, + Data: []byte(`{"id":"` + TEST_TASK_ID + `", "status":"FAILED", "created":"2015-07-07", "result":` + string(expectedResult) + `}`), + } + gomock.InOrder( + mockCcaClient.EXPECT().Do(request).Return(pendingResponse, nil), + mockCcaClient.EXPECT().Do(request).Return(pendingResponse, nil), + mockCcaClient.EXPECT().Do(request).Return(failedResponse, nil), + ) + + //when + _, err := taskService.Poll(TEST_TASK_ID, 10) + + //then + assert.NotNil(t, err) + +} diff --git a/vendor/github.com/cloud-ca/go-cloudca/.gometalinter.json b/vendor/github.com/cloud-ca/go-cloudca/.gometalinter.json deleted file mode 100644 index d179497..0000000 --- a/vendor/github.com/cloud-ca/go-cloudca/.gometalinter.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "Concurrency": 2, - "Deadline": "10m", - "DisableAll": true, - "EnableGC": true, - "Enable": [ - "vet", - "golint", - "varcheck", - "structcheck", - "staticcheck", - "errcheck", - "misspell", - "unconvert" - ], - "Exclude": [ - "composite literal uses unkeyed fields" - ], - "Linters": { - }, - "Sort": [ - "path", - "line" - ], - "Vendor": true, - "WarnUnmatchedDirective": true -} diff --git a/vendor/github.com/cloud-ca/go-cloudca/LICENSE b/vendor/github.com/cloud-ca/go-cloudca/LICENSE deleted file mode 100644 index 1c81b1c..0000000 --- a/vendor/github.com/cloud-ca/go-cloudca/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 CloudOps - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/cloud-ca/go-cloudca/Makefile b/vendor/github.com/cloud-ca/go-cloudca/Makefile deleted file mode 100644 index dcf64d3..0000000 --- a/vendor/github.com/cloud-ca/go-cloudca/Makefile +++ /dev/null @@ -1,115 +0,0 @@ -# Project variables -NAME := go-cloudca -DESCRIPTION := A cloud.ca client for the Go programming language -VENDOR := cloud.ca -URL := https://github.com/cloud-ca/go-cloudca -LICENSE := MIT - -# Build variables -BUILD_DIR := bin -COMMIT_HASH := $(shell git rev-parse --short HEAD 2>/dev/null) -VERSION := $(shell git describe --tags --exact-match 2>/dev/null || git describe --tags 2>/dev/null || echo "v0.0.0-$(COMMIT_HASH)") -BUILD_DATE := $(shell date +%FT%T%z) - -# Go variables -GOPKGS := $(shell go list ./... | grep -v /vendor) -GOFILES := $(shell find . -type f -name '*.go' -not -path "./vendor/*") - -# General variables -BLUE_COLOR := \033[36m -NO_COLOR := \033[0m - -.PHONY: version -version: ## Show version of provider - @echo "$(NAME) - $(VERSION) - $(BUILD_DATE)" - -######################### -## Development targets ## -######################### -.PHONY: clean -clean: log-clean ## Clean builds - rm -rf ./$(BUILD_DIR) $(NAME) - -.PHONY: vendor -vendor: log-vendor ## Install 'vendor' dependencies - GO111MODULE=on go mod vendor - -.PHONY: verify -verify: log-verify ## Verify 'vendor' dependencies - GO111MODULE=on go mod verify - -.PHONY: lint -lint: log-lint ## Run linter - @bash -c "GO111MODULE=off gometalinter -d ./... 2> >(egrep '(^DEBUG.*linter took|^DEBUG.*total elapsed|^[^D])' >&2)" - -.PHONY: format -format: log-format ## Format all go files - go fmt $(GOPKGS) - -.PHONY: checkfmt -checkfmt: SHELL :=/bin/bash -checkfmt: RESULT = $(shell gofmt -l $(GOFILES) | tee >(if [ "$$(wc -l)" = 0 ]; then echo "OK"; fi)) -checkfmt: log-checkfmt ## Check formatting of all go files - @echo "$(RESULT)" - @if [ "$(RESULT)" != "OK" ]; then exit 1; fi - -.PHONY: test -test: log-test ## Run tests - go test -v $(GOPKGS) - -.PHONY: tools -tools: log-tools ## Install required tools - @cd $$GOPATH && curl -L https://git.io/vp6lP | sh # gometalinter - @cd /tmp && go get -v -u github.com/git-chglog/git-chglog/cmd/git-chglog # git-chglog - -##################### -## Release targets ## -##################### -.PHONY: authors -authors: log-authors ## Generate list of Authors - @echo "# Authors\n" > AUTHORS.md - git log --all --format='- %aN \<%aE\>' | sort -u | egrep -v noreply >> AUTHORS.md - -.PHONY: changelog -changelog: log-changelog ## Generate content of Changelog - git-chglog --output CHANGELOG.md - -.PHONY: release patch minor major -PATTERN = - -release: version ?= $(shell echo $(VERSION) | sed 's/^v//' | awk -F'[ .]' '{print $(PATTERN)}') -release: push ?= false -release: SHELL :=/bin/bash -release: log-release ## Prepare Module release - @ if [ -z "$(version)" ]; then \ - echo "Error: missing value for 'version'. e.g. 'make release version=x.y.z'"; \ - elif [ "v$(version)" = "$(VERSION)" ] ; then \ - echo "Error: provided version (v$(version)) exists."; \ - else \ - git tag --annotate --message "v$(version) Release" v$(version); \ - echo "Tag v$(version) Release"; \ - if [ $(push) = "true" ]; then \ - git push origin v$(version); \ - echo "Push v$(version) Release"; \ - fi \ - fi - -patch: PATTERN = '\$$1\".\"\$$2\".\"\$$3+1' -patch: release ## Prepare Module Patch release - -minor: PATTERN = '\$$1\".\"\$$2+1\".0\"' -minor: release ## Prepare Module Minor release - -major: PATTERN = '\$$1+1\".0.0\"' -major: release ## Prepare Module Major release - -#################################### -## Self-Documenting Makefile Help ## -#################################### -.PHONY: help -.DEFAULT_GOAL := help -help: - @grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "$(BLUE_COLOR)%-20s$(NO_COLOR) %s\n", $$1, $$2}' - -log-%: - @grep -h -E '^$*:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "$(BLUE_COLOR)==> %s$(NO_COLOR)\n", $$2}' diff --git a/vendor/github.com/cloud-ca/go-cloudca/README.md b/vendor/github.com/cloud-ca/go-cloudca/README.md deleted file mode 100644 index 620b232..0000000 --- a/vendor/github.com/cloud-ca/go-cloudca/README.md +++ /dev/null @@ -1,118 +0,0 @@ -# go-cloudca - -[![GoDoc](https://godoc.org/github.com/cloud-ca/go-cloudca?status.svg)](https://godoc.org/github.com/cloud-ca/go-cloudca) -[![Build Status](https://circleci.com/gh/cloud-ca/go-cloudca.svg?style=svg)](https://circleci.com/gh/cloud-ca/go-cloudca) -[![license](https://img.shields.io/github/license/cloud-ca/go-cloudca.svg)](https://github.com/cloud-ca/go-cloudca/blob/master/LICENSE) - -A cloud.ca client for the Go programming language - -## How to use - -Import - -```go -import "github.com/cloud-ca/go-cloudca" - -/* import the services you need */ -import "github.com/cloud-ca/go-cloudca/services/cloudca" -``` - -Create a new CcaClient. - -```go -ccaClient := cca.NewCcaClient("[your-api-key]") -``` - -Retrieve the list of environments - -```go -environments, _ := ccaClient.Environments.List() -``` - -Get the ServiceResources object for a specific environment and service. Here, we assume that it is a cloudca service. - -```go -resources, _ := ccaClient.GetResources("[service-code]", "[environment-name]") -ccaResources := resources.(cloudca.Resources) -``` - -Now with the cloudca ServiceResources object, we can execute operations on cloudca resources in the specified environment. - -Retrieve the list of instances in the environment. - -```go -instances, _ := ccaResources.Instances.List() -``` - -Get a specific volume in the environment. - -```go -volume, _ := ccaResources.Volumes.Get("[some-volume-id]") -``` - -Create a new instance in the environment. - -```go -createdInstance, _ := ccaResources.Instances.Create(cloudca.Instance{ - Name: "[new-instance-name]", - TemplateId: "[some-template-id]", - ComputeOfferingId:"[some-compute-offering-id]", - NetworkId:"[some-network-id]", -}) -``` - -## Handling Errors - -When trying to get a volume with a bogus id, an error will be returned. - -```go -// Get a volume with a bogus id -_, err := ccaResources.Volumes.Get("[some-volume-id]") -``` - -Two types of error can occur: an unexpected error (ex: unable to connect to server) or an API error (ex: service resource not found) -If an error has occured, then we first try to cast the error into a CcaErrorResponse. This object contains the HTTP status code returned by the server, an error code and a list of CcaError objects. If it's not a CcaErrorResponse, then the error was not returned by the API. - -```go -if err != nil { - if errorResponse, ok := err.(api.CcaErrorResponse); ok { - if errorResponse.StatusCode == api.NOT_FOUND { - fmt.Println("Volume was not found") - } else { - // Can get more details from the CcaErrors - fmt.Println(errorResponse.Errors) - } - } else { - // handle unexpected error - panic("Unexpected error") - } -} -``` - -## License - -This project is licensed under the terms of the MIT license. - -```txt -The MIT License (MIT) - -Copyright (c) 2019 cloud.ca - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -``` diff --git a/vendor/github.com/cloud-ca/go-cloudca/api/api.go b/vendor/github.com/cloud-ca/go-cloudca/api/api.go deleted file mode 100644 index bd00e53..0000000 --- a/vendor/github.com/cloud-ca/go-cloudca/api/api.go +++ /dev/null @@ -1,88 +0,0 @@ -package api - -import ( - "bytes" - "crypto/tls" - "io" - "net/http" - "net/url" - "strings" -) - -type ApiClient interface { - Do(request CcaRequest) (*CcaResponse, error) - GetApiURL() string - GetApiKey() string -} - -type CcaApiClient struct { - apiURL string - apiKey string - httpClient *http.Client -} - -const API_KEY_HEADER = "MC-Api-Key" - -func NewApiClient(apiURL, apiKey string) ApiClient { - return CcaApiClient{ - apiURL: apiURL, - apiKey: apiKey, - httpClient: &http.Client{}, - } -} - -func NewInsecureApiClient(apiURL, apiKey string) ApiClient { - tr := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - } - return CcaApiClient{ - apiURL: apiURL, - apiKey: apiKey, - httpClient: &http.Client{Transport: tr}, - } -} - -//Build a URL by using endpoint and options. Options will be set as query parameters. -func (ccaClient CcaApiClient) buildUrl(endpoint string, options map[string]string) string { - query := url.Values{} - if options != nil { - for k, v := range options { - query.Add(k, v) - } - } - u, _ := url.Parse(ccaClient.apiURL + "/" + strings.Trim(endpoint, "/") + "?" + query.Encode()) - return u.String() -} - -//Does the API call to server and returns a CCAResponse. Cloud.ca errors will be returned in the -//CCAResponse body, not in the error return value. The error return value is reserved for unexpected errors. -func (ccaClient CcaApiClient) Do(request CcaRequest) (*CcaResponse, error) { - var bodyBuffer io.Reader - if request.Body != nil { - bodyBuffer = bytes.NewBuffer(request.Body) - } - method := request.Method - if method == "" { - method = "GET" - } - req, err := http.NewRequest(request.Method, ccaClient.buildUrl(request.Endpoint, request.Options), bodyBuffer) - if err != nil { - return nil, err - } - req.Header.Add(API_KEY_HEADER, ccaClient.apiKey) - req.Header.Add("Content-Type", "application/json") - resp, err := ccaClient.httpClient.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - return NewCcaResponse(resp) -} - -func (ccaClient CcaApiClient) GetApiKey() string { - return ccaClient.apiKey -} - -func (ccaClient CcaApiClient) GetApiURL() string { - return ccaClient.apiURL -} diff --git a/vendor/github.com/cloud-ca/go-cloudca/api/request.go b/vendor/github.com/cloud-ca/go-cloudca/api/request.go deleted file mode 100644 index 3697532..0000000 --- a/vendor/github.com/cloud-ca/go-cloudca/api/request.go +++ /dev/null @@ -1,17 +0,0 @@ -package api - -//HTTP Methods -const ( - GET = "GET" - POST = "POST" - DELETE = "DELETE" - PUT = "PUT" -) - -//A request object -type CcaRequest struct { - Method string - Endpoint string - Body []byte - Options map[string]string -} diff --git a/vendor/github.com/cloud-ca/go-cloudca/api/response.go b/vendor/github.com/cloud-ca/go-cloudca/api/response.go deleted file mode 100644 index 7a2b9da..0000000 --- a/vendor/github.com/cloud-ca/go-cloudca/api/response.go +++ /dev/null @@ -1,93 +0,0 @@ -package api - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "strconv" -) - -//Status codes -const ( - OK = 200 - MUTIPLE_CHOICES = 300 - BAD_REQUEST = 400 - NOT_FOUND = 404 -) - -//An API error -type CcaError struct { - ErrorCode string `json:"errorCode"` - Message string `json:"message"` - Context map[string]interface{} `json:"context"` -} - -//An Api Response -type CcaResponse struct { - TaskId string - TaskStatus string - StatusCode int - Data []byte - Errors []CcaError - MetaData map[string]interface{} -} - -//Returns true if API response has errors -func (ccaResponse CcaResponse) IsError() bool { - return !isInOKRange(ccaResponse.StatusCode) -} - -//An Api Response with errors -type CcaErrorResponse CcaResponse - -func (errorResponse CcaErrorResponse) Error() string { - var errorStr string = "[ERROR] Received HTTP status code " + strconv.Itoa(errorResponse.StatusCode) + "\n" - for _, e := range errorResponse.Errors { - context, _ := json.Marshal(e.Context) - errorStr += "[ERROR] Error Code: " + e.ErrorCode + ", Message: " + e.Message + ", Context: " + string(context) + "\n" - } - return errorStr -} - -func NewCcaResponse(response *http.Response) (*CcaResponse, error) { - respBody, err := ioutil.ReadAll(response.Body) - if err != nil { - return nil, err - } - ccaResponse := CcaResponse{} - ccaResponse.StatusCode = response.StatusCode - responseMap := map[string]*json.RawMessage{} - json.Unmarshal(respBody, &responseMap) - - if val, ok := responseMap["taskId"]; ok { - json.Unmarshal(*val, &ccaResponse.TaskId) - } - - if val, ok := responseMap["taskStatus"]; ok { - json.Unmarshal(*val, &ccaResponse.TaskStatus) - } - - if val, ok := responseMap["data"]; ok { - ccaResponse.Data = []byte(*val) - } - - if val, ok := responseMap["metadata"]; ok { - metadata := map[string]interface{}{} - json.Unmarshal(*val, &metadata) - ccaResponse.MetaData = metadata - } - - if val, ok := responseMap["errors"]; ok { - errors := []CcaError{} - json.Unmarshal(*val, &errors) - ccaResponse.Errors = errors - } else if !isInOKRange(response.StatusCode) { - return nil, fmt.Errorf("Unexpected. Received status " + response.Status + " but no errors in response body") - } - return &ccaResponse, nil -} - -func isInOKRange(statusCode int) bool { - return statusCode >= OK && statusCode < MUTIPLE_CHOICES -} diff --git a/vendor/github.com/cloud-ca/go-cloudca/client.go b/vendor/github.com/cloud-ca/go-cloudca/client.go deleted file mode 100644 index b087c24..0000000 --- a/vendor/github.com/cloud-ca/go-cloudca/client.go +++ /dev/null @@ -1,72 +0,0 @@ -package cca - -import ( - "github.com/cloud-ca/go-cloudca/api" - "github.com/cloud-ca/go-cloudca/configuration" - "github.com/cloud-ca/go-cloudca/services" - "github.com/cloud-ca/go-cloudca/services/cloudca" -) - -const ( - DEFAULT_API_URL = "https://api.cloud.ca/v1/" -) - -type CcaClient struct { - apiClient api.ApiClient - Tasks services.TaskService - Environments configuration.EnvironmentService - Users configuration.UserService - ServiceConnections configuration.ServiceConnectionService - Organizations configuration.OrganizationService -} - -//Create a CcaClient with the default URL -func NewCcaClient(apiKey string) *CcaClient { - return NewCcaClientWithURL(DEFAULT_API_URL, apiKey) -} - -//Create a CcaClient with a custom URL -func NewCcaClientWithURL(apiURL string, apiKey string) *CcaClient { - apiClient := api.NewApiClient(apiURL, apiKey) - return NewCcaClientWithApiClient(apiClient) -} - -//Create a CcaClient with a custom URL that accepts insecure connections -func NewInsecureCcaClientWithURL(apiURL string, apiKey string) *CcaClient { - apiClient := api.NewInsecureApiClient(apiURL, apiKey) - return NewCcaClientWithApiClient(apiClient) -} - -func NewCcaClientWithApiClient(apiClient api.ApiClient) *CcaClient { - ccaClient := CcaClient{ - apiClient: apiClient, - Tasks: services.NewTaskService(apiClient), - Environments: configuration.NewEnvironmentService(apiClient), - Users: configuration.NewUserService(apiClient), - ServiceConnections: configuration.NewServiceConnectionService(apiClient), - Organizations: configuration.NewOrganizationService(apiClient), - } - return &ccaClient -} - -//Get the Resources for a specific serviceCode and environmentName -//For now it assumes that the serviceCode belongs to a cloud.ca service type -func (c CcaClient) GetResources(serviceCode string, environmentName string) (services.ServiceResources, error) { - //TODO: change to check service type of service code - return cloudca.NewResources(c.apiClient, serviceCode, environmentName), nil -} - -//Get the API url used to do he calls -func (c CcaClient) GetApiURL() string { - return c.apiClient.GetApiURL() -} - -//Get the API key used in the calls -func (c CcaClient) GetApiKey() string { - return c.apiClient.GetApiKey() -} - -//Get the API Client used by all the services -func (c CcaClient) GetApiClient() api.ApiClient { - return c.apiClient -} diff --git a/vendor/github.com/cloud-ca/go-cloudca/go.mod b/vendor/github.com/cloud-ca/go-cloudca/go.mod deleted file mode 100644 index d46d1a8..0000000 --- a/vendor/github.com/cloud-ca/go-cloudca/go.mod +++ /dev/null @@ -1,8 +0,0 @@ -module github.com/cloud-ca/go-cloudca - -go 1.12 - -require ( - github.com/golang/mock v1.2.0 - github.com/stretchr/testify v1.3.0 -) diff --git a/vendor/github.com/cloud-ca/go-cloudca/go.sum b/vendor/github.com/cloud-ca/go-cloudca/go.sum deleted file mode 100644 index e5e94ca..0000000 --- a/vendor/github.com/cloud-ca/go-cloudca/go.sum +++ /dev/null @@ -1,9 +0,0 @@ -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= diff --git a/vendor/github.com/davecgh/go-spew/LICENSE b/vendor/github.com/davecgh/go-spew/LICENSE new file mode 100644 index 0000000..bc52e96 --- /dev/null +++ b/vendor/github.com/davecgh/go-spew/LICENSE @@ -0,0 +1,15 @@ +ISC License + +Copyright (c) 2012-2016 Dave Collins + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/vendor/github.com/davecgh/go-spew/spew/bypass.go b/vendor/github.com/davecgh/go-spew/spew/bypass.go new file mode 100644 index 0000000..7929947 --- /dev/null +++ b/vendor/github.com/davecgh/go-spew/spew/bypass.go @@ -0,0 +1,145 @@ +// Copyright (c) 2015-2016 Dave Collins +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +// NOTE: Due to the following build constraints, this file will only be compiled +// when the code is not running on Google App Engine, compiled by GopherJS, and +// "-tags safe" is not added to the go build command line. The "disableunsafe" +// tag is deprecated and thus should not be used. +// Go versions prior to 1.4 are disabled because they use a different layout +// for interfaces which make the implementation of unsafeReflectValue more complex. +// +build !js,!appengine,!safe,!disableunsafe,go1.4 + +package spew + +import ( + "reflect" + "unsafe" +) + +const ( + // UnsafeDisabled is a build-time constant which specifies whether or + // not access to the unsafe package is available. + UnsafeDisabled = false + + // ptrSize is the size of a pointer on the current arch. + ptrSize = unsafe.Sizeof((*byte)(nil)) +) + +type flag uintptr + +var ( + // flagRO indicates whether the value field of a reflect.Value + // is read-only. + flagRO flag + + // flagAddr indicates whether the address of the reflect.Value's + // value may be taken. + flagAddr flag +) + +// flagKindMask holds the bits that make up the kind +// part of the flags field. In all the supported versions, +// it is in the lower 5 bits. +const flagKindMask = flag(0x1f) + +// Different versions of Go have used different +// bit layouts for the flags type. This table +// records the known combinations. +var okFlags = []struct { + ro, addr flag +}{{ + // From Go 1.4 to 1.5 + ro: 1 << 5, + addr: 1 << 7, +}, { + // Up to Go tip. + ro: 1<<5 | 1<<6, + addr: 1 << 8, +}} + +var flagValOffset = func() uintptr { + field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") + if !ok { + panic("reflect.Value has no flag field") + } + return field.Offset +}() + +// flagField returns a pointer to the flag field of a reflect.Value. +func flagField(v *reflect.Value) *flag { + return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset)) +} + +// unsafeReflectValue converts the passed reflect.Value into a one that bypasses +// the typical safety restrictions preventing access to unaddressable and +// unexported data. It works by digging the raw pointer to the underlying +// value out of the protected value and generating a new unprotected (unsafe) +// reflect.Value to it. +// +// This allows us to check for implementations of the Stringer and error +// interfaces to be used for pretty printing ordinarily unaddressable and +// inaccessible values such as unexported struct fields. +func unsafeReflectValue(v reflect.Value) reflect.Value { + if !v.IsValid() || (v.CanInterface() && v.CanAddr()) { + return v + } + flagFieldPtr := flagField(&v) + *flagFieldPtr &^= flagRO + *flagFieldPtr |= flagAddr + return v +} + +// Sanity checks against future reflect package changes +// to the type or semantics of the Value.flag field. +func init() { + field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") + if !ok { + panic("reflect.Value has no flag field") + } + if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() { + panic("reflect.Value flag field has changed kind") + } + type t0 int + var t struct { + A t0 + // t0 will have flagEmbedRO set. + t0 + // a will have flagStickyRO set + a t0 + } + vA := reflect.ValueOf(t).FieldByName("A") + va := reflect.ValueOf(t).FieldByName("a") + vt0 := reflect.ValueOf(t).FieldByName("t0") + + // Infer flagRO from the difference between the flags + // for the (otherwise identical) fields in t. + flagPublic := *flagField(&vA) + flagWithRO := *flagField(&va) | *flagField(&vt0) + flagRO = flagPublic ^ flagWithRO + + // Infer flagAddr from the difference between a value + // taken from a pointer and not. + vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A") + flagNoPtr := *flagField(&vA) + flagPtr := *flagField(&vPtrA) + flagAddr = flagNoPtr ^ flagPtr + + // Check that the inferred flags tally with one of the known versions. + for _, f := range okFlags { + if flagRO == f.ro && flagAddr == f.addr { + return + } + } + panic("reflect.Value read-only flag has changed semantics") +} diff --git a/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go b/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go new file mode 100644 index 0000000..205c28d --- /dev/null +++ b/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go @@ -0,0 +1,38 @@ +// Copyright (c) 2015-2016 Dave Collins +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +// NOTE: Due to the following build constraints, this file will only be compiled +// when the code is running on Google App Engine, compiled by GopherJS, or +// "-tags safe" is added to the go build command line. The "disableunsafe" +// tag is deprecated and thus should not be used. +// +build js appengine safe disableunsafe !go1.4 + +package spew + +import "reflect" + +const ( + // UnsafeDisabled is a build-time constant which specifies whether or + // not access to the unsafe package is available. + UnsafeDisabled = true +) + +// unsafeReflectValue typically converts the passed reflect.Value into a one +// that bypasses the typical safety restrictions preventing access to +// unaddressable and unexported data. However, doing this relies on access to +// the unsafe package. This is a stub version which simply returns the passed +// reflect.Value when the unsafe package is not available. +func unsafeReflectValue(v reflect.Value) reflect.Value { + return v +} diff --git a/vendor/github.com/davecgh/go-spew/spew/common.go b/vendor/github.com/davecgh/go-spew/spew/common.go new file mode 100644 index 0000000..1be8ce9 --- /dev/null +++ b/vendor/github.com/davecgh/go-spew/spew/common.go @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "bytes" + "fmt" + "io" + "reflect" + "sort" + "strconv" +) + +// Some constants in the form of bytes to avoid string overhead. This mirrors +// the technique used in the fmt package. +var ( + panicBytes = []byte("(PANIC=") + plusBytes = []byte("+") + iBytes = []byte("i") + trueBytes = []byte("true") + falseBytes = []byte("false") + interfaceBytes = []byte("(interface {})") + commaNewlineBytes = []byte(",\n") + newlineBytes = []byte("\n") + openBraceBytes = []byte("{") + openBraceNewlineBytes = []byte("{\n") + closeBraceBytes = []byte("}") + asteriskBytes = []byte("*") + colonBytes = []byte(":") + colonSpaceBytes = []byte(": ") + openParenBytes = []byte("(") + closeParenBytes = []byte(")") + spaceBytes = []byte(" ") + pointerChainBytes = []byte("->") + nilAngleBytes = []byte("") + maxNewlineBytes = []byte("\n") + maxShortBytes = []byte("") + circularBytes = []byte("") + circularShortBytes = []byte("") + invalidAngleBytes = []byte("") + openBracketBytes = []byte("[") + closeBracketBytes = []byte("]") + percentBytes = []byte("%") + precisionBytes = []byte(".") + openAngleBytes = []byte("<") + closeAngleBytes = []byte(">") + openMapBytes = []byte("map[") + closeMapBytes = []byte("]") + lenEqualsBytes = []byte("len=") + capEqualsBytes = []byte("cap=") +) + +// hexDigits is used to map a decimal value to a hex digit. +var hexDigits = "0123456789abcdef" + +// catchPanic handles any panics that might occur during the handleMethods +// calls. +func catchPanic(w io.Writer, v reflect.Value) { + if err := recover(); err != nil { + w.Write(panicBytes) + fmt.Fprintf(w, "%v", err) + w.Write(closeParenBytes) + } +} + +// handleMethods attempts to call the Error and String methods on the underlying +// type the passed reflect.Value represents and outputes the result to Writer w. +// +// It handles panics in any called methods by catching and displaying the error +// as the formatted value. +func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) { + // We need an interface to check if the type implements the error or + // Stringer interface. However, the reflect package won't give us an + // interface on certain things like unexported struct fields in order + // to enforce visibility rules. We use unsafe, when it's available, + // to bypass these restrictions since this package does not mutate the + // values. + if !v.CanInterface() { + if UnsafeDisabled { + return false + } + + v = unsafeReflectValue(v) + } + + // Choose whether or not to do error and Stringer interface lookups against + // the base type or a pointer to the base type depending on settings. + // Technically calling one of these methods with a pointer receiver can + // mutate the value, however, types which choose to satisify an error or + // Stringer interface with a pointer receiver should not be mutating their + // state inside these interface methods. + if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() { + v = unsafeReflectValue(v) + } + if v.CanAddr() { + v = v.Addr() + } + + // Is it an error or Stringer? + switch iface := v.Interface().(type) { + case error: + defer catchPanic(w, v) + if cs.ContinueOnMethod { + w.Write(openParenBytes) + w.Write([]byte(iface.Error())) + w.Write(closeParenBytes) + w.Write(spaceBytes) + return false + } + + w.Write([]byte(iface.Error())) + return true + + case fmt.Stringer: + defer catchPanic(w, v) + if cs.ContinueOnMethod { + w.Write(openParenBytes) + w.Write([]byte(iface.String())) + w.Write(closeParenBytes) + w.Write(spaceBytes) + return false + } + w.Write([]byte(iface.String())) + return true + } + return false +} + +// printBool outputs a boolean value as true or false to Writer w. +func printBool(w io.Writer, val bool) { + if val { + w.Write(trueBytes) + } else { + w.Write(falseBytes) + } +} + +// printInt outputs a signed integer value to Writer w. +func printInt(w io.Writer, val int64, base int) { + w.Write([]byte(strconv.FormatInt(val, base))) +} + +// printUint outputs an unsigned integer value to Writer w. +func printUint(w io.Writer, val uint64, base int) { + w.Write([]byte(strconv.FormatUint(val, base))) +} + +// printFloat outputs a floating point value using the specified precision, +// which is expected to be 32 or 64bit, to Writer w. +func printFloat(w io.Writer, val float64, precision int) { + w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision))) +} + +// printComplex outputs a complex value using the specified float precision +// for the real and imaginary parts to Writer w. +func printComplex(w io.Writer, c complex128, floatPrecision int) { + r := real(c) + w.Write(openParenBytes) + w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision))) + i := imag(c) + if i >= 0 { + w.Write(plusBytes) + } + w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision))) + w.Write(iBytes) + w.Write(closeParenBytes) +} + +// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x' +// prefix to Writer w. +func printHexPtr(w io.Writer, p uintptr) { + // Null pointer. + num := uint64(p) + if num == 0 { + w.Write(nilAngleBytes) + return + } + + // Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix + buf := make([]byte, 18) + + // It's simpler to construct the hex string right to left. + base := uint64(16) + i := len(buf) - 1 + for num >= base { + buf[i] = hexDigits[num%base] + num /= base + i-- + } + buf[i] = hexDigits[num] + + // Add '0x' prefix. + i-- + buf[i] = 'x' + i-- + buf[i] = '0' + + // Strip unused leading bytes. + buf = buf[i:] + w.Write(buf) +} + +// valuesSorter implements sort.Interface to allow a slice of reflect.Value +// elements to be sorted. +type valuesSorter struct { + values []reflect.Value + strings []string // either nil or same len and values + cs *ConfigState +} + +// newValuesSorter initializes a valuesSorter instance, which holds a set of +// surrogate keys on which the data should be sorted. It uses flags in +// ConfigState to decide if and how to populate those surrogate keys. +func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface { + vs := &valuesSorter{values: values, cs: cs} + if canSortSimply(vs.values[0].Kind()) { + return vs + } + if !cs.DisableMethods { + vs.strings = make([]string, len(values)) + for i := range vs.values { + b := bytes.Buffer{} + if !handleMethods(cs, &b, vs.values[i]) { + vs.strings = nil + break + } + vs.strings[i] = b.String() + } + } + if vs.strings == nil && cs.SpewKeys { + vs.strings = make([]string, len(values)) + for i := range vs.values { + vs.strings[i] = Sprintf("%#v", vs.values[i].Interface()) + } + } + return vs +} + +// canSortSimply tests whether a reflect.Kind is a primitive that can be sorted +// directly, or whether it should be considered for sorting by surrogate keys +// (if the ConfigState allows it). +func canSortSimply(kind reflect.Kind) bool { + // This switch parallels valueSortLess, except for the default case. + switch kind { + case reflect.Bool: + return true + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + return true + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + return true + case reflect.Float32, reflect.Float64: + return true + case reflect.String: + return true + case reflect.Uintptr: + return true + case reflect.Array: + return true + } + return false +} + +// Len returns the number of values in the slice. It is part of the +// sort.Interface implementation. +func (s *valuesSorter) Len() int { + return len(s.values) +} + +// Swap swaps the values at the passed indices. It is part of the +// sort.Interface implementation. +func (s *valuesSorter) Swap(i, j int) { + s.values[i], s.values[j] = s.values[j], s.values[i] + if s.strings != nil { + s.strings[i], s.strings[j] = s.strings[j], s.strings[i] + } +} + +// valueSortLess returns whether the first value should sort before the second +// value. It is used by valueSorter.Less as part of the sort.Interface +// implementation. +func valueSortLess(a, b reflect.Value) bool { + switch a.Kind() { + case reflect.Bool: + return !a.Bool() && b.Bool() + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + return a.Int() < b.Int() + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + return a.Uint() < b.Uint() + case reflect.Float32, reflect.Float64: + return a.Float() < b.Float() + case reflect.String: + return a.String() < b.String() + case reflect.Uintptr: + return a.Uint() < b.Uint() + case reflect.Array: + // Compare the contents of both arrays. + l := a.Len() + for i := 0; i < l; i++ { + av := a.Index(i) + bv := b.Index(i) + if av.Interface() == bv.Interface() { + continue + } + return valueSortLess(av, bv) + } + } + return a.String() < b.String() +} + +// Less returns whether the value at index i should sort before the +// value at index j. It is part of the sort.Interface implementation. +func (s *valuesSorter) Less(i, j int) bool { + if s.strings == nil { + return valueSortLess(s.values[i], s.values[j]) + } + return s.strings[i] < s.strings[j] +} + +// sortValues is a sort function that handles both native types and any type that +// can be converted to error or Stringer. Other inputs are sorted according to +// their Value.String() value to ensure display stability. +func sortValues(values []reflect.Value, cs *ConfigState) { + if len(values) == 0 { + return + } + sort.Sort(newValuesSorter(values, cs)) +} diff --git a/vendor/github.com/davecgh/go-spew/spew/config.go b/vendor/github.com/davecgh/go-spew/spew/config.go new file mode 100644 index 0000000..2e3d22f --- /dev/null +++ b/vendor/github.com/davecgh/go-spew/spew/config.go @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "bytes" + "fmt" + "io" + "os" +) + +// ConfigState houses the configuration options used by spew to format and +// display values. There is a global instance, Config, that is used to control +// all top-level Formatter and Dump functionality. Each ConfigState instance +// provides methods equivalent to the top-level functions. +// +// The zero value for ConfigState provides no indentation. You would typically +// want to set it to a space or a tab. +// +// Alternatively, you can use NewDefaultConfig to get a ConfigState instance +// with default settings. See the documentation of NewDefaultConfig for default +// values. +type ConfigState struct { + // Indent specifies the string to use for each indentation level. The + // global config instance that all top-level functions use set this to a + // single space by default. If you would like more indentation, you might + // set this to a tab with "\t" or perhaps two spaces with " ". + Indent string + + // MaxDepth controls the maximum number of levels to descend into nested + // data structures. The default, 0, means there is no limit. + // + // NOTE: Circular data structures are properly detected, so it is not + // necessary to set this value unless you specifically want to limit deeply + // nested data structures. + MaxDepth int + + // DisableMethods specifies whether or not error and Stringer interfaces are + // invoked for types that implement them. + DisableMethods bool + + // DisablePointerMethods specifies whether or not to check for and invoke + // error and Stringer interfaces on types which only accept a pointer + // receiver when the current type is not a pointer. + // + // NOTE: This might be an unsafe action since calling one of these methods + // with a pointer receiver could technically mutate the value, however, + // in practice, types which choose to satisify an error or Stringer + // interface with a pointer receiver should not be mutating their state + // inside these interface methods. As a result, this option relies on + // access to the unsafe package, so it will not have any effect when + // running in environments without access to the unsafe package such as + // Google App Engine or with the "safe" build tag specified. + DisablePointerMethods bool + + // DisablePointerAddresses specifies whether to disable the printing of + // pointer addresses. This is useful when diffing data structures in tests. + DisablePointerAddresses bool + + // DisableCapacities specifies whether to disable the printing of capacities + // for arrays, slices, maps and channels. This is useful when diffing + // data structures in tests. + DisableCapacities bool + + // ContinueOnMethod specifies whether or not recursion should continue once + // a custom error or Stringer interface is invoked. The default, false, + // means it will print the results of invoking the custom error or Stringer + // interface and return immediately instead of continuing to recurse into + // the internals of the data type. + // + // NOTE: This flag does not have any effect if method invocation is disabled + // via the DisableMethods or DisablePointerMethods options. + ContinueOnMethod bool + + // SortKeys specifies map keys should be sorted before being printed. Use + // this to have a more deterministic, diffable output. Note that only + // native types (bool, int, uint, floats, uintptr and string) and types + // that support the error or Stringer interfaces (if methods are + // enabled) are supported, with other types sorted according to the + // reflect.Value.String() output which guarantees display stability. + SortKeys bool + + // SpewKeys specifies that, as a last resort attempt, map keys should + // be spewed to strings and sorted by those strings. This is only + // considered if SortKeys is true. + SpewKeys bool +} + +// Config is the active configuration of the top-level functions. +// The configuration can be changed by modifying the contents of spew.Config. +var Config = ConfigState{Indent: " "} + +// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the formatted string as a value that satisfies error. See NewFormatter +// for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Errorf(format, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Errorf(format string, a ...interface{}) (err error) { + return fmt.Errorf(format, c.convertArgs(a)...) +} + +// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprint(w, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Fprint(w io.Writer, a ...interface{}) (n int, err error) { + return fmt.Fprint(w, c.convertArgs(a)...) +} + +// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprintf(w, format, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { + return fmt.Fprintf(w, format, c.convertArgs(a)...) +} + +// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it +// passed with a Formatter interface returned by c.NewFormatter. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprintln(w, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) { + return fmt.Fprintln(w, c.convertArgs(a)...) +} + +// Print is a wrapper for fmt.Print that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Print(c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Print(a ...interface{}) (n int, err error) { + return fmt.Print(c.convertArgs(a)...) +} + +// Printf is a wrapper for fmt.Printf that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Printf(format, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Printf(format string, a ...interface{}) (n int, err error) { + return fmt.Printf(format, c.convertArgs(a)...) +} + +// Println is a wrapper for fmt.Println that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Println(c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Println(a ...interface{}) (n int, err error) { + return fmt.Println(c.convertArgs(a)...) +} + +// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprint(c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Sprint(a ...interface{}) string { + return fmt.Sprint(c.convertArgs(a)...) +} + +// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were +// passed with a Formatter interface returned by c.NewFormatter. It returns +// the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprintf(format, c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Sprintf(format string, a ...interface{}) string { + return fmt.Sprintf(format, c.convertArgs(a)...) +} + +// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it +// were passed with a Formatter interface returned by c.NewFormatter. It +// returns the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprintln(c.NewFormatter(a), c.NewFormatter(b)) +func (c *ConfigState) Sprintln(a ...interface{}) string { + return fmt.Sprintln(c.convertArgs(a)...) +} + +/* +NewFormatter returns a custom formatter that satisfies the fmt.Formatter +interface. As a result, it integrates cleanly with standard fmt package +printing functions. The formatter is useful for inline printing of smaller data +types similar to the standard %v format specifier. + +The custom formatter only responds to the %v (most compact), %+v (adds pointer +addresses), %#v (adds types), and %#+v (adds types and pointer addresses) verb +combinations. Any other verbs such as %x and %q will be sent to the the +standard fmt package for formatting. In addition, the custom formatter ignores +the width and precision arguments (however they will still work on the format +specifiers not handled by the custom formatter). + +Typically this function shouldn't be called directly. It is much easier to make +use of the custom formatter by calling one of the convenience functions such as +c.Printf, c.Println, or c.Printf. +*/ +func (c *ConfigState) NewFormatter(v interface{}) fmt.Formatter { + return newFormatter(c, v) +} + +// Fdump formats and displays the passed arguments to io.Writer w. It formats +// exactly the same as Dump. +func (c *ConfigState) Fdump(w io.Writer, a ...interface{}) { + fdump(c, w, a...) +} + +/* +Dump displays the passed parameters to standard out with newlines, customizable +indentation, and additional debug information such as complete types and all +pointer addresses used to indirect to the final value. It provides the +following features over the built-in printing facilities provided by the fmt +package: + + * Pointers are dereferenced and followed + * Circular data structures are detected and handled properly + * Custom Stringer/error interfaces are optionally invoked, including + on unexported types + * Custom types which only implement the Stringer/error interfaces via + a pointer receiver are optionally invoked when passing non-pointer + variables + * Byte arrays and slices are dumped like the hexdump -C command which + includes offsets, byte values in hex, and ASCII output + +The configuration options are controlled by modifying the public members +of c. See ConfigState for options documentation. + +See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to +get the formatted result as a string. +*/ +func (c *ConfigState) Dump(a ...interface{}) { + fdump(c, os.Stdout, a...) +} + +// Sdump returns a string with the passed arguments formatted exactly the same +// as Dump. +func (c *ConfigState) Sdump(a ...interface{}) string { + var buf bytes.Buffer + fdump(c, &buf, a...) + return buf.String() +} + +// convertArgs accepts a slice of arguments and returns a slice of the same +// length with each argument converted to a spew Formatter interface using +// the ConfigState associated with s. +func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) { + formatters = make([]interface{}, len(args)) + for index, arg := range args { + formatters[index] = newFormatter(c, arg) + } + return formatters +} + +// NewDefaultConfig returns a ConfigState with the following default settings. +// +// Indent: " " +// MaxDepth: 0 +// DisableMethods: false +// DisablePointerMethods: false +// ContinueOnMethod: false +// SortKeys: false +func NewDefaultConfig() *ConfigState { + return &ConfigState{Indent: " "} +} diff --git a/vendor/github.com/davecgh/go-spew/spew/doc.go b/vendor/github.com/davecgh/go-spew/spew/doc.go new file mode 100644 index 0000000..aacaac6 --- /dev/null +++ b/vendor/github.com/davecgh/go-spew/spew/doc.go @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* +Package spew implements a deep pretty printer for Go data structures to aid in +debugging. + +A quick overview of the additional features spew provides over the built-in +printing facilities for Go data types are as follows: + + * Pointers are dereferenced and followed + * Circular data structures are detected and handled properly + * Custom Stringer/error interfaces are optionally invoked, including + on unexported types + * Custom types which only implement the Stringer/error interfaces via + a pointer receiver are optionally invoked when passing non-pointer + variables + * Byte arrays and slices are dumped like the hexdump -C command which + includes offsets, byte values in hex, and ASCII output (only when using + Dump style) + +There are two different approaches spew allows for dumping Go data structures: + + * Dump style which prints with newlines, customizable indentation, + and additional debug information such as types and all pointer addresses + used to indirect to the final value + * A custom Formatter interface that integrates cleanly with the standard fmt + package and replaces %v, %+v, %#v, and %#+v to provide inline printing + similar to the default %v while providing the additional functionality + outlined above and passing unsupported format verbs such as %x and %q + along to fmt + +Quick Start + +This section demonstrates how to quickly get started with spew. See the +sections below for further details on formatting and configuration options. + +To dump a variable with full newlines, indentation, type, and pointer +information use Dump, Fdump, or Sdump: + spew.Dump(myVar1, myVar2, ...) + spew.Fdump(someWriter, myVar1, myVar2, ...) + str := spew.Sdump(myVar1, myVar2, ...) + +Alternatively, if you would prefer to use format strings with a compacted inline +printing style, use the convenience wrappers Printf, Fprintf, etc with +%v (most compact), %+v (adds pointer addresses), %#v (adds types), or +%#+v (adds types and pointer addresses): + spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) + spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) + spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) + spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) + +Configuration Options + +Configuration of spew is handled by fields in the ConfigState type. For +convenience, all of the top-level functions use a global state available +via the spew.Config global. + +It is also possible to create a ConfigState instance that provides methods +equivalent to the top-level functions. This allows concurrent configuration +options. See the ConfigState documentation for more details. + +The following configuration options are available: + * Indent + String to use for each indentation level for Dump functions. + It is a single space by default. A popular alternative is "\t". + + * MaxDepth + Maximum number of levels to descend into nested data structures. + There is no limit by default. + + * DisableMethods + Disables invocation of error and Stringer interface methods. + Method invocation is enabled by default. + + * DisablePointerMethods + Disables invocation of error and Stringer interface methods on types + which only accept pointer receivers from non-pointer variables. + Pointer method invocation is enabled by default. + + * DisablePointerAddresses + DisablePointerAddresses specifies whether to disable the printing of + pointer addresses. This is useful when diffing data structures in tests. + + * DisableCapacities + DisableCapacities specifies whether to disable the printing of + capacities for arrays, slices, maps and channels. This is useful when + diffing data structures in tests. + + * ContinueOnMethod + Enables recursion into types after invoking error and Stringer interface + methods. Recursion after method invocation is disabled by default. + + * SortKeys + Specifies map keys should be sorted before being printed. Use + this to have a more deterministic, diffable output. Note that + only native types (bool, int, uint, floats, uintptr and string) + and types which implement error or Stringer interfaces are + supported with other types sorted according to the + reflect.Value.String() output which guarantees display + stability. Natural map order is used by default. + + * SpewKeys + Specifies that, as a last resort attempt, map keys should be + spewed to strings and sorted by those strings. This is only + considered if SortKeys is true. + +Dump Usage + +Simply call spew.Dump with a list of variables you want to dump: + + spew.Dump(myVar1, myVar2, ...) + +You may also call spew.Fdump if you would prefer to output to an arbitrary +io.Writer. For example, to dump to standard error: + + spew.Fdump(os.Stderr, myVar1, myVar2, ...) + +A third option is to call spew.Sdump to get the formatted output as a string: + + str := spew.Sdump(myVar1, myVar2, ...) + +Sample Dump Output + +See the Dump example for details on the setup of the types and variables being +shown here. + + (main.Foo) { + unexportedField: (*main.Bar)(0xf84002e210)({ + flag: (main.Flag) flagTwo, + data: (uintptr) + }), + ExportedField: (map[interface {}]interface {}) (len=1) { + (string) (len=3) "one": (bool) true + } + } + +Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C +command as shown. + ([]uint8) (len=32 cap=32) { + 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... | + 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0| + 00000020 31 32 |12| + } + +Custom Formatter + +Spew provides a custom formatter that implements the fmt.Formatter interface +so that it integrates cleanly with standard fmt package printing functions. The +formatter is useful for inline printing of smaller data types similar to the +standard %v format specifier. + +The custom formatter only responds to the %v (most compact), %+v (adds pointer +addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb +combinations. Any other verbs such as %x and %q will be sent to the the +standard fmt package for formatting. In addition, the custom formatter ignores +the width and precision arguments (however they will still work on the format +specifiers not handled by the custom formatter). + +Custom Formatter Usage + +The simplest way to make use of the spew custom formatter is to call one of the +convenience functions such as spew.Printf, spew.Println, or spew.Printf. The +functions have syntax you are most likely already familiar with: + + spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) + spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) + spew.Println(myVar, myVar2) + spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) + spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) + +See the Index for the full list convenience functions. + +Sample Formatter Output + +Double pointer to a uint8: + %v: <**>5 + %+v: <**>(0xf8400420d0->0xf8400420c8)5 + %#v: (**uint8)5 + %#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5 + +Pointer to circular struct with a uint8 field and a pointer to itself: + %v: <*>{1 <*>} + %+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)} + %#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)} + %#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)} + +See the Printf example for details on the setup of variables being shown +here. + +Errors + +Since it is possible for custom Stringer/error interfaces to panic, spew +detects them and handles them internally by printing the panic information +inline with the output. Since spew is intended to provide deep pretty printing +capabilities on structures, it intentionally does not return any errors. +*/ +package spew diff --git a/vendor/github.com/davecgh/go-spew/spew/dump.go b/vendor/github.com/davecgh/go-spew/spew/dump.go new file mode 100644 index 0000000..f78d89f --- /dev/null +++ b/vendor/github.com/davecgh/go-spew/spew/dump.go @@ -0,0 +1,509 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "bytes" + "encoding/hex" + "fmt" + "io" + "os" + "reflect" + "regexp" + "strconv" + "strings" +) + +var ( + // uint8Type is a reflect.Type representing a uint8. It is used to + // convert cgo types to uint8 slices for hexdumping. + uint8Type = reflect.TypeOf(uint8(0)) + + // cCharRE is a regular expression that matches a cgo char. + // It is used to detect character arrays to hexdump them. + cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`) + + // cUnsignedCharRE is a regular expression that matches a cgo unsigned + // char. It is used to detect unsigned character arrays to hexdump + // them. + cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`) + + // cUint8tCharRE is a regular expression that matches a cgo uint8_t. + // It is used to detect uint8_t arrays to hexdump them. + cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`) +) + +// dumpState contains information about the state of a dump operation. +type dumpState struct { + w io.Writer + depth int + pointers map[uintptr]int + ignoreNextType bool + ignoreNextIndent bool + cs *ConfigState +} + +// indent performs indentation according to the depth level and cs.Indent +// option. +func (d *dumpState) indent() { + if d.ignoreNextIndent { + d.ignoreNextIndent = false + return + } + d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth)) +} + +// unpackValue returns values inside of non-nil interfaces when possible. +// This is useful for data types like structs, arrays, slices, and maps which +// can contain varying types packed inside an interface. +func (d *dumpState) unpackValue(v reflect.Value) reflect.Value { + if v.Kind() == reflect.Interface && !v.IsNil() { + v = v.Elem() + } + return v +} + +// dumpPtr handles formatting of pointers by indirecting them as necessary. +func (d *dumpState) dumpPtr(v reflect.Value) { + // Remove pointers at or below the current depth from map used to detect + // circular refs. + for k, depth := range d.pointers { + if depth >= d.depth { + delete(d.pointers, k) + } + } + + // Keep list of all dereferenced pointers to show later. + pointerChain := make([]uintptr, 0) + + // Figure out how many levels of indirection there are by dereferencing + // pointers and unpacking interfaces down the chain while detecting circular + // references. + nilFound := false + cycleFound := false + indirects := 0 + ve := v + for ve.Kind() == reflect.Ptr { + if ve.IsNil() { + nilFound = true + break + } + indirects++ + addr := ve.Pointer() + pointerChain = append(pointerChain, addr) + if pd, ok := d.pointers[addr]; ok && pd < d.depth { + cycleFound = true + indirects-- + break + } + d.pointers[addr] = d.depth + + ve = ve.Elem() + if ve.Kind() == reflect.Interface { + if ve.IsNil() { + nilFound = true + break + } + ve = ve.Elem() + } + } + + // Display type information. + d.w.Write(openParenBytes) + d.w.Write(bytes.Repeat(asteriskBytes, indirects)) + d.w.Write([]byte(ve.Type().String())) + d.w.Write(closeParenBytes) + + // Display pointer information. + if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 { + d.w.Write(openParenBytes) + for i, addr := range pointerChain { + if i > 0 { + d.w.Write(pointerChainBytes) + } + printHexPtr(d.w, addr) + } + d.w.Write(closeParenBytes) + } + + // Display dereferenced value. + d.w.Write(openParenBytes) + switch { + case nilFound: + d.w.Write(nilAngleBytes) + + case cycleFound: + d.w.Write(circularBytes) + + default: + d.ignoreNextType = true + d.dump(ve) + } + d.w.Write(closeParenBytes) +} + +// dumpSlice handles formatting of arrays and slices. Byte (uint8 under +// reflection) arrays and slices are dumped in hexdump -C fashion. +func (d *dumpState) dumpSlice(v reflect.Value) { + // Determine whether this type should be hex dumped or not. Also, + // for types which should be hexdumped, try to use the underlying data + // first, then fall back to trying to convert them to a uint8 slice. + var buf []uint8 + doConvert := false + doHexDump := false + numEntries := v.Len() + if numEntries > 0 { + vt := v.Index(0).Type() + vts := vt.String() + switch { + // C types that need to be converted. + case cCharRE.MatchString(vts): + fallthrough + case cUnsignedCharRE.MatchString(vts): + fallthrough + case cUint8tCharRE.MatchString(vts): + doConvert = true + + // Try to use existing uint8 slices and fall back to converting + // and copying if that fails. + case vt.Kind() == reflect.Uint8: + // We need an addressable interface to convert the type + // to a byte slice. However, the reflect package won't + // give us an interface on certain things like + // unexported struct fields in order to enforce + // visibility rules. We use unsafe, when available, to + // bypass these restrictions since this package does not + // mutate the values. + vs := v + if !vs.CanInterface() || !vs.CanAddr() { + vs = unsafeReflectValue(vs) + } + if !UnsafeDisabled { + vs = vs.Slice(0, numEntries) + + // Use the existing uint8 slice if it can be + // type asserted. + iface := vs.Interface() + if slice, ok := iface.([]uint8); ok { + buf = slice + doHexDump = true + break + } + } + + // The underlying data needs to be converted if it can't + // be type asserted to a uint8 slice. + doConvert = true + } + + // Copy and convert the underlying type if needed. + if doConvert && vt.ConvertibleTo(uint8Type) { + // Convert and copy each element into a uint8 byte + // slice. + buf = make([]uint8, numEntries) + for i := 0; i < numEntries; i++ { + vv := v.Index(i) + buf[i] = uint8(vv.Convert(uint8Type).Uint()) + } + doHexDump = true + } + } + + // Hexdump the entire slice as needed. + if doHexDump { + indent := strings.Repeat(d.cs.Indent, d.depth) + str := indent + hex.Dump(buf) + str = strings.Replace(str, "\n", "\n"+indent, -1) + str = strings.TrimRight(str, d.cs.Indent) + d.w.Write([]byte(str)) + return + } + + // Recursively call dump for each item. + for i := 0; i < numEntries; i++ { + d.dump(d.unpackValue(v.Index(i))) + if i < (numEntries - 1) { + d.w.Write(commaNewlineBytes) + } else { + d.w.Write(newlineBytes) + } + } +} + +// dump is the main workhorse for dumping a value. It uses the passed reflect +// value to figure out what kind of object we are dealing with and formats it +// appropriately. It is a recursive function, however circular data structures +// are detected and handled properly. +func (d *dumpState) dump(v reflect.Value) { + // Handle invalid reflect values immediately. + kind := v.Kind() + if kind == reflect.Invalid { + d.w.Write(invalidAngleBytes) + return + } + + // Handle pointers specially. + if kind == reflect.Ptr { + d.indent() + d.dumpPtr(v) + return + } + + // Print type information unless already handled elsewhere. + if !d.ignoreNextType { + d.indent() + d.w.Write(openParenBytes) + d.w.Write([]byte(v.Type().String())) + d.w.Write(closeParenBytes) + d.w.Write(spaceBytes) + } + d.ignoreNextType = false + + // Display length and capacity if the built-in len and cap functions + // work with the value's kind and the len/cap itself is non-zero. + valueLen, valueCap := 0, 0 + switch v.Kind() { + case reflect.Array, reflect.Slice, reflect.Chan: + valueLen, valueCap = v.Len(), v.Cap() + case reflect.Map, reflect.String: + valueLen = v.Len() + } + if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 { + d.w.Write(openParenBytes) + if valueLen != 0 { + d.w.Write(lenEqualsBytes) + printInt(d.w, int64(valueLen), 10) + } + if !d.cs.DisableCapacities && valueCap != 0 { + if valueLen != 0 { + d.w.Write(spaceBytes) + } + d.w.Write(capEqualsBytes) + printInt(d.w, int64(valueCap), 10) + } + d.w.Write(closeParenBytes) + d.w.Write(spaceBytes) + } + + // Call Stringer/error interfaces if they exist and the handle methods flag + // is enabled + if !d.cs.DisableMethods { + if (kind != reflect.Invalid) && (kind != reflect.Interface) { + if handled := handleMethods(d.cs, d.w, v); handled { + return + } + } + } + + switch kind { + case reflect.Invalid: + // Do nothing. We should never get here since invalid has already + // been handled above. + + case reflect.Bool: + printBool(d.w, v.Bool()) + + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + printInt(d.w, v.Int(), 10) + + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + printUint(d.w, v.Uint(), 10) + + case reflect.Float32: + printFloat(d.w, v.Float(), 32) + + case reflect.Float64: + printFloat(d.w, v.Float(), 64) + + case reflect.Complex64: + printComplex(d.w, v.Complex(), 32) + + case reflect.Complex128: + printComplex(d.w, v.Complex(), 64) + + case reflect.Slice: + if v.IsNil() { + d.w.Write(nilAngleBytes) + break + } + fallthrough + + case reflect.Array: + d.w.Write(openBraceNewlineBytes) + d.depth++ + if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { + d.indent() + d.w.Write(maxNewlineBytes) + } else { + d.dumpSlice(v) + } + d.depth-- + d.indent() + d.w.Write(closeBraceBytes) + + case reflect.String: + d.w.Write([]byte(strconv.Quote(v.String()))) + + case reflect.Interface: + // The only time we should get here is for nil interfaces due to + // unpackValue calls. + if v.IsNil() { + d.w.Write(nilAngleBytes) + } + + case reflect.Ptr: + // Do nothing. We should never get here since pointers have already + // been handled above. + + case reflect.Map: + // nil maps should be indicated as different than empty maps + if v.IsNil() { + d.w.Write(nilAngleBytes) + break + } + + d.w.Write(openBraceNewlineBytes) + d.depth++ + if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { + d.indent() + d.w.Write(maxNewlineBytes) + } else { + numEntries := v.Len() + keys := v.MapKeys() + if d.cs.SortKeys { + sortValues(keys, d.cs) + } + for i, key := range keys { + d.dump(d.unpackValue(key)) + d.w.Write(colonSpaceBytes) + d.ignoreNextIndent = true + d.dump(d.unpackValue(v.MapIndex(key))) + if i < (numEntries - 1) { + d.w.Write(commaNewlineBytes) + } else { + d.w.Write(newlineBytes) + } + } + } + d.depth-- + d.indent() + d.w.Write(closeBraceBytes) + + case reflect.Struct: + d.w.Write(openBraceNewlineBytes) + d.depth++ + if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { + d.indent() + d.w.Write(maxNewlineBytes) + } else { + vt := v.Type() + numFields := v.NumField() + for i := 0; i < numFields; i++ { + d.indent() + vtf := vt.Field(i) + d.w.Write([]byte(vtf.Name)) + d.w.Write(colonSpaceBytes) + d.ignoreNextIndent = true + d.dump(d.unpackValue(v.Field(i))) + if i < (numFields - 1) { + d.w.Write(commaNewlineBytes) + } else { + d.w.Write(newlineBytes) + } + } + } + d.depth-- + d.indent() + d.w.Write(closeBraceBytes) + + case reflect.Uintptr: + printHexPtr(d.w, uintptr(v.Uint())) + + case reflect.UnsafePointer, reflect.Chan, reflect.Func: + printHexPtr(d.w, v.Pointer()) + + // There were not any other types at the time this code was written, but + // fall back to letting the default fmt package handle it in case any new + // types are added. + default: + if v.CanInterface() { + fmt.Fprintf(d.w, "%v", v.Interface()) + } else { + fmt.Fprintf(d.w, "%v", v.String()) + } + } +} + +// fdump is a helper function to consolidate the logic from the various public +// methods which take varying writers and config states. +func fdump(cs *ConfigState, w io.Writer, a ...interface{}) { + for _, arg := range a { + if arg == nil { + w.Write(interfaceBytes) + w.Write(spaceBytes) + w.Write(nilAngleBytes) + w.Write(newlineBytes) + continue + } + + d := dumpState{w: w, cs: cs} + d.pointers = make(map[uintptr]int) + d.dump(reflect.ValueOf(arg)) + d.w.Write(newlineBytes) + } +} + +// Fdump formats and displays the passed arguments to io.Writer w. It formats +// exactly the same as Dump. +func Fdump(w io.Writer, a ...interface{}) { + fdump(&Config, w, a...) +} + +// Sdump returns a string with the passed arguments formatted exactly the same +// as Dump. +func Sdump(a ...interface{}) string { + var buf bytes.Buffer + fdump(&Config, &buf, a...) + return buf.String() +} + +/* +Dump displays the passed parameters to standard out with newlines, customizable +indentation, and additional debug information such as complete types and all +pointer addresses used to indirect to the final value. It provides the +following features over the built-in printing facilities provided by the fmt +package: + + * Pointers are dereferenced and followed + * Circular data structures are detected and handled properly + * Custom Stringer/error interfaces are optionally invoked, including + on unexported types + * Custom types which only implement the Stringer/error interfaces via + a pointer receiver are optionally invoked when passing non-pointer + variables + * Byte arrays and slices are dumped like the hexdump -C command which + includes offsets, byte values in hex, and ASCII output + +The configuration options are controlled by an exported package global, +spew.Config. See ConfigState for options documentation. + +See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to +get the formatted result as a string. +*/ +func Dump(a ...interface{}) { + fdump(&Config, os.Stdout, a...) +} diff --git a/vendor/github.com/davecgh/go-spew/spew/format.go b/vendor/github.com/davecgh/go-spew/spew/format.go new file mode 100644 index 0000000..b04edb7 --- /dev/null +++ b/vendor/github.com/davecgh/go-spew/spew/format.go @@ -0,0 +1,419 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "bytes" + "fmt" + "reflect" + "strconv" + "strings" +) + +// supportedFlags is a list of all the character flags supported by fmt package. +const supportedFlags = "0-+# " + +// formatState implements the fmt.Formatter interface and contains information +// about the state of a formatting operation. The NewFormatter function can +// be used to get a new Formatter which can be used directly as arguments +// in standard fmt package printing calls. +type formatState struct { + value interface{} + fs fmt.State + depth int + pointers map[uintptr]int + ignoreNextType bool + cs *ConfigState +} + +// buildDefaultFormat recreates the original format string without precision +// and width information to pass in to fmt.Sprintf in the case of an +// unrecognized type. Unless new types are added to the language, this +// function won't ever be called. +func (f *formatState) buildDefaultFormat() (format string) { + buf := bytes.NewBuffer(percentBytes) + + for _, flag := range supportedFlags { + if f.fs.Flag(int(flag)) { + buf.WriteRune(flag) + } + } + + buf.WriteRune('v') + + format = buf.String() + return format +} + +// constructOrigFormat recreates the original format string including precision +// and width information to pass along to the standard fmt package. This allows +// automatic deferral of all format strings this package doesn't support. +func (f *formatState) constructOrigFormat(verb rune) (format string) { + buf := bytes.NewBuffer(percentBytes) + + for _, flag := range supportedFlags { + if f.fs.Flag(int(flag)) { + buf.WriteRune(flag) + } + } + + if width, ok := f.fs.Width(); ok { + buf.WriteString(strconv.Itoa(width)) + } + + if precision, ok := f.fs.Precision(); ok { + buf.Write(precisionBytes) + buf.WriteString(strconv.Itoa(precision)) + } + + buf.WriteRune(verb) + + format = buf.String() + return format +} + +// unpackValue returns values inside of non-nil interfaces when possible and +// ensures that types for values which have been unpacked from an interface +// are displayed when the show types flag is also set. +// This is useful for data types like structs, arrays, slices, and maps which +// can contain varying types packed inside an interface. +func (f *formatState) unpackValue(v reflect.Value) reflect.Value { + if v.Kind() == reflect.Interface { + f.ignoreNextType = false + if !v.IsNil() { + v = v.Elem() + } + } + return v +} + +// formatPtr handles formatting of pointers by indirecting them as necessary. +func (f *formatState) formatPtr(v reflect.Value) { + // Display nil if top level pointer is nil. + showTypes := f.fs.Flag('#') + if v.IsNil() && (!showTypes || f.ignoreNextType) { + f.fs.Write(nilAngleBytes) + return + } + + // Remove pointers at or below the current depth from map used to detect + // circular refs. + for k, depth := range f.pointers { + if depth >= f.depth { + delete(f.pointers, k) + } + } + + // Keep list of all dereferenced pointers to possibly show later. + pointerChain := make([]uintptr, 0) + + // Figure out how many levels of indirection there are by derferencing + // pointers and unpacking interfaces down the chain while detecting circular + // references. + nilFound := false + cycleFound := false + indirects := 0 + ve := v + for ve.Kind() == reflect.Ptr { + if ve.IsNil() { + nilFound = true + break + } + indirects++ + addr := ve.Pointer() + pointerChain = append(pointerChain, addr) + if pd, ok := f.pointers[addr]; ok && pd < f.depth { + cycleFound = true + indirects-- + break + } + f.pointers[addr] = f.depth + + ve = ve.Elem() + if ve.Kind() == reflect.Interface { + if ve.IsNil() { + nilFound = true + break + } + ve = ve.Elem() + } + } + + // Display type or indirection level depending on flags. + if showTypes && !f.ignoreNextType { + f.fs.Write(openParenBytes) + f.fs.Write(bytes.Repeat(asteriskBytes, indirects)) + f.fs.Write([]byte(ve.Type().String())) + f.fs.Write(closeParenBytes) + } else { + if nilFound || cycleFound { + indirects += strings.Count(ve.Type().String(), "*") + } + f.fs.Write(openAngleBytes) + f.fs.Write([]byte(strings.Repeat("*", indirects))) + f.fs.Write(closeAngleBytes) + } + + // Display pointer information depending on flags. + if f.fs.Flag('+') && (len(pointerChain) > 0) { + f.fs.Write(openParenBytes) + for i, addr := range pointerChain { + if i > 0 { + f.fs.Write(pointerChainBytes) + } + printHexPtr(f.fs, addr) + } + f.fs.Write(closeParenBytes) + } + + // Display dereferenced value. + switch { + case nilFound: + f.fs.Write(nilAngleBytes) + + case cycleFound: + f.fs.Write(circularShortBytes) + + default: + f.ignoreNextType = true + f.format(ve) + } +} + +// format is the main workhorse for providing the Formatter interface. It +// uses the passed reflect value to figure out what kind of object we are +// dealing with and formats it appropriately. It is a recursive function, +// however circular data structures are detected and handled properly. +func (f *formatState) format(v reflect.Value) { + // Handle invalid reflect values immediately. + kind := v.Kind() + if kind == reflect.Invalid { + f.fs.Write(invalidAngleBytes) + return + } + + // Handle pointers specially. + if kind == reflect.Ptr { + f.formatPtr(v) + return + } + + // Print type information unless already handled elsewhere. + if !f.ignoreNextType && f.fs.Flag('#') { + f.fs.Write(openParenBytes) + f.fs.Write([]byte(v.Type().String())) + f.fs.Write(closeParenBytes) + } + f.ignoreNextType = false + + // Call Stringer/error interfaces if they exist and the handle methods + // flag is enabled. + if !f.cs.DisableMethods { + if (kind != reflect.Invalid) && (kind != reflect.Interface) { + if handled := handleMethods(f.cs, f.fs, v); handled { + return + } + } + } + + switch kind { + case reflect.Invalid: + // Do nothing. We should never get here since invalid has already + // been handled above. + + case reflect.Bool: + printBool(f.fs, v.Bool()) + + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + printInt(f.fs, v.Int(), 10) + + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + printUint(f.fs, v.Uint(), 10) + + case reflect.Float32: + printFloat(f.fs, v.Float(), 32) + + case reflect.Float64: + printFloat(f.fs, v.Float(), 64) + + case reflect.Complex64: + printComplex(f.fs, v.Complex(), 32) + + case reflect.Complex128: + printComplex(f.fs, v.Complex(), 64) + + case reflect.Slice: + if v.IsNil() { + f.fs.Write(nilAngleBytes) + break + } + fallthrough + + case reflect.Array: + f.fs.Write(openBracketBytes) + f.depth++ + if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { + f.fs.Write(maxShortBytes) + } else { + numEntries := v.Len() + for i := 0; i < numEntries; i++ { + if i > 0 { + f.fs.Write(spaceBytes) + } + f.ignoreNextType = true + f.format(f.unpackValue(v.Index(i))) + } + } + f.depth-- + f.fs.Write(closeBracketBytes) + + case reflect.String: + f.fs.Write([]byte(v.String())) + + case reflect.Interface: + // The only time we should get here is for nil interfaces due to + // unpackValue calls. + if v.IsNil() { + f.fs.Write(nilAngleBytes) + } + + case reflect.Ptr: + // Do nothing. We should never get here since pointers have already + // been handled above. + + case reflect.Map: + // nil maps should be indicated as different than empty maps + if v.IsNil() { + f.fs.Write(nilAngleBytes) + break + } + + f.fs.Write(openMapBytes) + f.depth++ + if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { + f.fs.Write(maxShortBytes) + } else { + keys := v.MapKeys() + if f.cs.SortKeys { + sortValues(keys, f.cs) + } + for i, key := range keys { + if i > 0 { + f.fs.Write(spaceBytes) + } + f.ignoreNextType = true + f.format(f.unpackValue(key)) + f.fs.Write(colonBytes) + f.ignoreNextType = true + f.format(f.unpackValue(v.MapIndex(key))) + } + } + f.depth-- + f.fs.Write(closeMapBytes) + + case reflect.Struct: + numFields := v.NumField() + f.fs.Write(openBraceBytes) + f.depth++ + if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { + f.fs.Write(maxShortBytes) + } else { + vt := v.Type() + for i := 0; i < numFields; i++ { + if i > 0 { + f.fs.Write(spaceBytes) + } + vtf := vt.Field(i) + if f.fs.Flag('+') || f.fs.Flag('#') { + f.fs.Write([]byte(vtf.Name)) + f.fs.Write(colonBytes) + } + f.format(f.unpackValue(v.Field(i))) + } + } + f.depth-- + f.fs.Write(closeBraceBytes) + + case reflect.Uintptr: + printHexPtr(f.fs, uintptr(v.Uint())) + + case reflect.UnsafePointer, reflect.Chan, reflect.Func: + printHexPtr(f.fs, v.Pointer()) + + // There were not any other types at the time this code was written, but + // fall back to letting the default fmt package handle it if any get added. + default: + format := f.buildDefaultFormat() + if v.CanInterface() { + fmt.Fprintf(f.fs, format, v.Interface()) + } else { + fmt.Fprintf(f.fs, format, v.String()) + } + } +} + +// Format satisfies the fmt.Formatter interface. See NewFormatter for usage +// details. +func (f *formatState) Format(fs fmt.State, verb rune) { + f.fs = fs + + // Use standard formatting for verbs that are not v. + if verb != 'v' { + format := f.constructOrigFormat(verb) + fmt.Fprintf(fs, format, f.value) + return + } + + if f.value == nil { + if fs.Flag('#') { + fs.Write(interfaceBytes) + } + fs.Write(nilAngleBytes) + return + } + + f.format(reflect.ValueOf(f.value)) +} + +// newFormatter is a helper function to consolidate the logic from the various +// public methods which take varying config states. +func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter { + fs := &formatState{value: v, cs: cs} + fs.pointers = make(map[uintptr]int) + return fs +} + +/* +NewFormatter returns a custom formatter that satisfies the fmt.Formatter +interface. As a result, it integrates cleanly with standard fmt package +printing functions. The formatter is useful for inline printing of smaller data +types similar to the standard %v format specifier. + +The custom formatter only responds to the %v (most compact), %+v (adds pointer +addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb +combinations. Any other verbs such as %x and %q will be sent to the the +standard fmt package for formatting. In addition, the custom formatter ignores +the width and precision arguments (however they will still work on the format +specifiers not handled by the custom formatter). + +Typically this function shouldn't be called directly. It is much easier to make +use of the custom formatter by calling one of the convenience functions such as +Printf, Println, or Fprintf. +*/ +func NewFormatter(v interface{}) fmt.Formatter { + return newFormatter(&Config, v) +} diff --git a/vendor/github.com/davecgh/go-spew/spew/spew.go b/vendor/github.com/davecgh/go-spew/spew/spew.go new file mode 100644 index 0000000..32c0e33 --- /dev/null +++ b/vendor/github.com/davecgh/go-spew/spew/spew.go @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2013-2016 Dave Collins + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package spew + +import ( + "fmt" + "io" +) + +// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the formatted string as a value that satisfies error. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b)) +func Errorf(format string, a ...interface{}) (err error) { + return fmt.Errorf(format, convertArgs(a)...) +} + +// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b)) +func Fprint(w io.Writer, a ...interface{}) (n int, err error) { + return fmt.Fprint(w, convertArgs(a)...) +} + +// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b)) +func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { + return fmt.Fprintf(w, format, convertArgs(a)...) +} + +// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it +// passed with a default Formatter interface returned by NewFormatter. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b)) +func Fprintln(w io.Writer, a ...interface{}) (n int, err error) { + return fmt.Fprintln(w, convertArgs(a)...) +} + +// Print is a wrapper for fmt.Print that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b)) +func Print(a ...interface{}) (n int, err error) { + return fmt.Print(convertArgs(a)...) +} + +// Printf is a wrapper for fmt.Printf that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b)) +func Printf(format string, a ...interface{}) (n int, err error) { + return fmt.Printf(format, convertArgs(a)...) +} + +// Println is a wrapper for fmt.Println that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the number of bytes written and any write error encountered. See +// NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b)) +func Println(a ...interface{}) (n int, err error) { + return fmt.Println(convertArgs(a)...) +} + +// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b)) +func Sprint(a ...interface{}) string { + return fmt.Sprint(convertArgs(a)...) +} + +// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were +// passed with a default Formatter interface returned by NewFormatter. It +// returns the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b)) +func Sprintf(format string, a ...interface{}) string { + return fmt.Sprintf(format, convertArgs(a)...) +} + +// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it +// were passed with a default Formatter interface returned by NewFormatter. It +// returns the resulting string. See NewFormatter for formatting details. +// +// This function is shorthand for the following syntax: +// +// fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b)) +func Sprintln(a ...interface{}) string { + return fmt.Sprintln(convertArgs(a)...) +} + +// convertArgs accepts a slice of arguments and returns a slice of the same +// length with each argument converted to a default spew Formatter interface. +func convertArgs(args []interface{}) (formatters []interface{}) { + formatters = make([]interface{}, len(args)) + for index, arg := range args { + formatters[index] = NewFormatter(arg) + } + return formatters +} diff --git a/vendor/github.com/golang/mock/AUTHORS b/vendor/github.com/golang/mock/AUTHORS new file mode 100644 index 0000000..660b8cc --- /dev/null +++ b/vendor/github.com/golang/mock/AUTHORS @@ -0,0 +1,12 @@ +# This is the official list of GoMock authors for copyright purposes. +# This file is distinct from the CONTRIBUTORS files. +# See the latter for an explanation. + +# Names should be added to this file as +# Name or Organization +# The email address is not required for organizations. + +# Please keep the list sorted. + +Alex Reece +Google Inc. diff --git a/vendor/github.com/golang/mock/CONTRIBUTORS b/vendor/github.com/golang/mock/CONTRIBUTORS new file mode 100644 index 0000000..def849c --- /dev/null +++ b/vendor/github.com/golang/mock/CONTRIBUTORS @@ -0,0 +1,37 @@ +# This is the official list of people who can contribute (and typically +# have contributed) code to the gomock repository. +# The AUTHORS file lists the copyright holders; this file +# lists people. For example, Google employees are listed here +# but not in AUTHORS, because Google holds the copyright. +# +# The submission process automatically checks to make sure +# that people submitting code are listed in this file (by email address). +# +# Names should be added to this file only after verifying that +# the individual or the individual's organization has agreed to +# the appropriate Contributor License Agreement, found here: +# +# http://code.google.com/legal/individual-cla-v1.0.html +# http://code.google.com/legal/corporate-cla-v1.0.html +# +# The agreement for individuals can be filled out on the web. +# +# When adding J Random Contributor's name to this file, +# either J's name or J's organization's name should be +# added to the AUTHORS file, depending on whether the +# individual or corporate CLA was used. + +# Names should be added to this file like so: +# Name +# +# An entry with two email addresses specifies that the +# first address should be used in the submit logs and +# that the second address should be recognized as the +# same person when interacting with Rietveld. + +# Please keep the list sorted. + +Aaron Jacobs +Alex Reece +David Symonds +Ryan Barrett diff --git a/vendor/github.com/golang/mock/LICENSE b/vendor/github.com/golang/mock/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/vendor/github.com/golang/mock/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/vendor/github.com/golang/mock/gomock/call.go b/vendor/github.com/golang/mock/gomock/call.go new file mode 100644 index 0000000..a3fa1ae --- /dev/null +++ b/vendor/github.com/golang/mock/gomock/call.go @@ -0,0 +1,428 @@ +// Copyright 2010 Google Inc. +// +// 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 gomock + +import ( + "fmt" + "reflect" + "strconv" + "strings" +) + +// Call represents an expected call to a mock. +type Call struct { + t TestReporter // for triggering test failures on invalid call setup + + receiver interface{} // the receiver of the method call + method string // the name of the method + methodType reflect.Type // the type of the method + args []Matcher // the args + origin string // file and line number of call setup + + preReqs []*Call // prerequisite calls + + // Expectations + minCalls, maxCalls int + + numCalls int // actual number made + + // actions are called when this Call is called. Each action gets the args and + // can set the return values by returning a non-nil slice. Actions run in the + // order they are created. + actions []func([]interface{}) []interface{} +} + +// newCall creates a *Call. It requires the method type in order to support +// unexported methods. +func newCall(t TestReporter, receiver interface{}, method string, methodType reflect.Type, args ...interface{}) *Call { + if h, ok := t.(testHelper); ok { + h.Helper() + } + + // TODO: check arity, types. + margs := make([]Matcher, len(args)) + for i, arg := range args { + if m, ok := arg.(Matcher); ok { + margs[i] = m + } else if arg == nil { + // Handle nil specially so that passing a nil interface value + // will match the typed nils of concrete args. + margs[i] = Nil() + } else { + margs[i] = Eq(arg) + } + } + + origin := callerInfo(3) + actions := []func([]interface{}) []interface{}{func([]interface{}) []interface{} { + // Synthesize the zero value for each of the return args' types. + rets := make([]interface{}, methodType.NumOut()) + for i := 0; i < methodType.NumOut(); i++ { + rets[i] = reflect.Zero(methodType.Out(i)).Interface() + } + return rets + }} + return &Call{t: t, receiver: receiver, method: method, methodType: methodType, + args: margs, origin: origin, minCalls: 1, maxCalls: 1, actions: actions} +} + +// AnyTimes allows the expectation to be called 0 or more times +func (c *Call) AnyTimes() *Call { + c.minCalls, c.maxCalls = 0, 1e8 // close enough to infinity + return c +} + +// MinTimes requires the call to occur at least n times. If AnyTimes or MaxTimes have not been called, MinTimes also +// sets the maximum number of calls to infinity. +func (c *Call) MinTimes(n int) *Call { + c.minCalls = n + if c.maxCalls == 1 { + c.maxCalls = 1e8 + } + return c +} + +// MaxTimes limits the number of calls to n times. If AnyTimes or MinTimes have not been called, MaxTimes also +// sets the minimum number of calls to 0. +func (c *Call) MaxTimes(n int) *Call { + c.maxCalls = n + if c.minCalls == 1 { + c.minCalls = 0 + } + return c +} + +// DoAndReturn declares the action to run when the call is matched. +// The return values from this function are returned by the mocked function. +// It takes an interface{} argument to support n-arity functions. +func (c *Call) DoAndReturn(f interface{}) *Call { + // TODO: Check arity and types here, rather than dying badly elsewhere. + v := reflect.ValueOf(f) + + c.addAction(func(args []interface{}) []interface{} { + vargs := make([]reflect.Value, len(args)) + ft := v.Type() + for i := 0; i < len(args); i++ { + if args[i] != nil { + vargs[i] = reflect.ValueOf(args[i]) + } else { + // Use the zero value for the arg. + vargs[i] = reflect.Zero(ft.In(i)) + } + } + vrets := v.Call(vargs) + rets := make([]interface{}, len(vrets)) + for i, ret := range vrets { + rets[i] = ret.Interface() + } + return rets + }) + return c +} + +// Do declares the action to run when the call is matched. The function's +// return values are ignored to retain backward compatibility. To use the +// return values call DoAndReturn. +// It takes an interface{} argument to support n-arity functions. +func (c *Call) Do(f interface{}) *Call { + // TODO: Check arity and types here, rather than dying badly elsewhere. + v := reflect.ValueOf(f) + + c.addAction(func(args []interface{}) []interface{} { + vargs := make([]reflect.Value, len(args)) + ft := v.Type() + for i := 0; i < len(args); i++ { + if args[i] != nil { + vargs[i] = reflect.ValueOf(args[i]) + } else { + // Use the zero value for the arg. + vargs[i] = reflect.Zero(ft.In(i)) + } + } + v.Call(vargs) + return nil + }) + return c +} + +// Return declares the values to be returned by the mocked function call. +func (c *Call) Return(rets ...interface{}) *Call { + if h, ok := c.t.(testHelper); ok { + h.Helper() + } + + mt := c.methodType + if len(rets) != mt.NumOut() { + c.t.Fatalf("wrong number of arguments to Return for %T.%v: got %d, want %d [%s]", + c.receiver, c.method, len(rets), mt.NumOut(), c.origin) + } + for i, ret := range rets { + if got, want := reflect.TypeOf(ret), mt.Out(i); got == want { + // Identical types; nothing to do. + } else if got == nil { + // Nil needs special handling. + switch want.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + // ok + default: + c.t.Fatalf("argument %d to Return for %T.%v is nil, but %v is not nillable [%s]", + i, c.receiver, c.method, want, c.origin) + } + } else if got.AssignableTo(want) { + // Assignable type relation. Make the assignment now so that the generated code + // can return the values with a type assertion. + v := reflect.New(want).Elem() + v.Set(reflect.ValueOf(ret)) + rets[i] = v.Interface() + } else { + c.t.Fatalf("wrong type of argument %d to Return for %T.%v: %v is not assignable to %v [%s]", + i, c.receiver, c.method, got, want, c.origin) + } + } + + c.addAction(func([]interface{}) []interface{} { + return rets + }) + + return c +} + +// Times declares the exact number of times a function call is expected to be executed. +func (c *Call) Times(n int) *Call { + c.minCalls, c.maxCalls = n, n + return c +} + +// SetArg declares an action that will set the nth argument's value, +// indirected through a pointer. Or, in the case of a slice, SetArg +// will copy value's elements into the nth argument. +func (c *Call) SetArg(n int, value interface{}) *Call { + if h, ok := c.t.(testHelper); ok { + h.Helper() + } + + mt := c.methodType + // TODO: This will break on variadic methods. + // We will need to check those at invocation time. + if n < 0 || n >= mt.NumIn() { + c.t.Fatalf("SetArg(%d, ...) called for a method with %d args [%s]", + n, mt.NumIn(), c.origin) + } + // Permit setting argument through an interface. + // In the interface case, we don't (nay, can't) check the type here. + at := mt.In(n) + switch at.Kind() { + case reflect.Ptr: + dt := at.Elem() + if vt := reflect.TypeOf(value); !vt.AssignableTo(dt) { + c.t.Fatalf("SetArg(%d, ...) argument is a %v, not assignable to %v [%s]", + n, vt, dt, c.origin) + } + case reflect.Interface: + // nothing to do + case reflect.Slice: + // nothing to do + default: + c.t.Fatalf("SetArg(%d, ...) referring to argument of non-pointer non-interface non-slice type %v [%s]", + n, at, c.origin) + } + + c.addAction(func(args []interface{}) []interface{} { + v := reflect.ValueOf(value) + switch reflect.TypeOf(args[n]).Kind() { + case reflect.Slice: + setSlice(args[n], v) + default: + reflect.ValueOf(args[n]).Elem().Set(v) + } + return nil + }) + return c +} + +// isPreReq returns true if other is a direct or indirect prerequisite to c. +func (c *Call) isPreReq(other *Call) bool { + for _, preReq := range c.preReqs { + if other == preReq || preReq.isPreReq(other) { + return true + } + } + return false +} + +// After declares that the call may only match after preReq has been exhausted. +func (c *Call) After(preReq *Call) *Call { + if h, ok := c.t.(testHelper); ok { + h.Helper() + } + + if c == preReq { + c.t.Fatalf("A call isn't allowed to be its own prerequisite") + } + if preReq.isPreReq(c) { + c.t.Fatalf("Loop in call order: %v is a prerequisite to %v (possibly indirectly).", c, preReq) + } + + c.preReqs = append(c.preReqs, preReq) + return c +} + +// Returns true if the minimum number of calls have been made. +func (c *Call) satisfied() bool { + return c.numCalls >= c.minCalls +} + +// Returns true iff the maximum number of calls have been made. +func (c *Call) exhausted() bool { + return c.numCalls >= c.maxCalls +} + +func (c *Call) String() string { + args := make([]string, len(c.args)) + for i, arg := range c.args { + args[i] = arg.String() + } + arguments := strings.Join(args, ", ") + return fmt.Sprintf("%T.%v(%s) %s", c.receiver, c.method, arguments, c.origin) +} + +// Tests if the given call matches the expected call. +// If yes, returns nil. If no, returns error with message explaining why it does not match. +func (c *Call) matches(args []interface{}) error { + if !c.methodType.IsVariadic() { + if len(args) != len(c.args) { + return fmt.Errorf("Expected call at %s has the wrong number of arguments. Got: %d, want: %d", + c.origin, len(args), len(c.args)) + } + + for i, m := range c.args { + if !m.Matches(args[i]) { + return fmt.Errorf("Expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v", + c.origin, strconv.Itoa(i), args[i], m) + } + } + } else { + if len(c.args) < c.methodType.NumIn()-1 { + return fmt.Errorf("Expected call at %s has the wrong number of matchers. Got: %d, want: %d", + c.origin, len(c.args), c.methodType.NumIn()-1) + } + if len(c.args) != c.methodType.NumIn() && len(args) != len(c.args) { + return fmt.Errorf("Expected call at %s has the wrong number of arguments. Got: %d, want: %d", + c.origin, len(args), len(c.args)) + } + if len(args) < len(c.args)-1 { + return fmt.Errorf("Expected call at %s has the wrong number of arguments. Got: %d, want: greater than or equal to %d", + c.origin, len(args), len(c.args)-1) + } + + for i, m := range c.args { + if i < c.methodType.NumIn()-1 { + // Non-variadic args + if !m.Matches(args[i]) { + return fmt.Errorf("Expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v", + c.origin, strconv.Itoa(i), args[i], m) + } + continue + } + // The last arg has a possibility of a variadic argument, so let it branch + + // sample: Foo(a int, b int, c ...int) + if i < len(c.args) && i < len(args) { + if m.Matches(args[i]) { + // Got Foo(a, b, c) want Foo(matcherA, matcherB, gomock.Any()) + // Got Foo(a, b, c) want Foo(matcherA, matcherB, someSliceMatcher) + // Got Foo(a, b, c) want Foo(matcherA, matcherB, matcherC) + // Got Foo(a, b) want Foo(matcherA, matcherB) + // Got Foo(a, b, c, d) want Foo(matcherA, matcherB, matcherC, matcherD) + continue + } + } + + // The number of actual args don't match the number of matchers, + // or the last matcher is a slice and the last arg is not. + // If this function still matches it is because the last matcher + // matches all the remaining arguments or the lack of any. + // Convert the remaining arguments, if any, into a slice of the + // expected type. + vargsType := c.methodType.In(c.methodType.NumIn() - 1) + vargs := reflect.MakeSlice(vargsType, 0, len(args)-i) + for _, arg := range args[i:] { + vargs = reflect.Append(vargs, reflect.ValueOf(arg)) + } + if m.Matches(vargs.Interface()) { + // Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, gomock.Any()) + // Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, someSliceMatcher) + // Got Foo(a, b) want Foo(matcherA, matcherB, gomock.Any()) + // Got Foo(a, b) want Foo(matcherA, matcherB, someEmptySliceMatcher) + break + } + // Wrong number of matchers or not match. Fail. + // Got Foo(a, b) want Foo(matcherA, matcherB, matcherC, matcherD) + // Got Foo(a, b, c) want Foo(matcherA, matcherB, matcherC, matcherD) + // Got Foo(a, b, c, d) want Foo(matcherA, matcherB, matcherC, matcherD, matcherE) + // Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, matcherC, matcherD) + // Got Foo(a, b, c) want Foo(matcherA, matcherB) + return fmt.Errorf("Expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v", + c.origin, strconv.Itoa(i), args[i:], c.args[i]) + + } + } + + // Check that all prerequisite calls have been satisfied. + for _, preReqCall := range c.preReqs { + if !preReqCall.satisfied() { + return fmt.Errorf("Expected call at %s doesn't have a prerequisite call satisfied:\n%v\nshould be called before:\n%v", + c.origin, preReqCall, c) + } + } + + // Check that the call is not exhausted. + if c.exhausted() { + return fmt.Errorf("Expected call at %s has already been called the max number of times.", c.origin) + } + + return nil +} + +// dropPrereqs tells the expected Call to not re-check prerequisite calls any +// longer, and to return its current set. +func (c *Call) dropPrereqs() (preReqs []*Call) { + preReqs = c.preReqs + c.preReqs = nil + return +} + +func (c *Call) call(args []interface{}) []func([]interface{}) []interface{} { + c.numCalls++ + return c.actions +} + +// InOrder declares that the given calls should occur in order. +func InOrder(calls ...*Call) { + for i := 1; i < len(calls); i++ { + calls[i].After(calls[i-1]) + } +} + +func setSlice(arg interface{}, v reflect.Value) { + va := reflect.ValueOf(arg) + for i := 0; i < v.Len(); i++ { + va.Index(i).Set(v.Index(i)) + } +} + +func (c *Call) addAction(action func([]interface{}) []interface{}) { + c.actions = append(c.actions, action) +} diff --git a/vendor/github.com/golang/mock/gomock/callset.go b/vendor/github.com/golang/mock/gomock/callset.go new file mode 100644 index 0000000..c44a8a5 --- /dev/null +++ b/vendor/github.com/golang/mock/gomock/callset.go @@ -0,0 +1,108 @@ +// Copyright 2011 Google Inc. +// +// 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 gomock + +import ( + "bytes" + "fmt" +) + +// callSet represents a set of expected calls, indexed by receiver and method +// name. +type callSet struct { + // Calls that are still expected. + expected map[callSetKey][]*Call + // Calls that have been exhausted. + exhausted map[callSetKey][]*Call +} + +// callSetKey is the key in the maps in callSet +type callSetKey struct { + receiver interface{} + fname string +} + +func newCallSet() *callSet { + return &callSet{make(map[callSetKey][]*Call), make(map[callSetKey][]*Call)} +} + +// Add adds a new expected call. +func (cs callSet) Add(call *Call) { + key := callSetKey{call.receiver, call.method} + m := cs.expected + if call.exhausted() { + m = cs.exhausted + } + m[key] = append(m[key], call) +} + +// Remove removes an expected call. +func (cs callSet) Remove(call *Call) { + key := callSetKey{call.receiver, call.method} + calls := cs.expected[key] + for i, c := range calls { + if c == call { + // maintain order for remaining calls + cs.expected[key] = append(calls[:i], calls[i+1:]...) + cs.exhausted[key] = append(cs.exhausted[key], call) + break + } + } +} + +// FindMatch searches for a matching call. Returns error with explanation message if no call matched. +func (cs callSet) FindMatch(receiver interface{}, method string, args []interface{}) (*Call, error) { + key := callSetKey{receiver, method} + + // Search through the expected calls. + expected := cs.expected[key] + var callsErrors bytes.Buffer + for _, call := range expected { + err := call.matches(args) + if err != nil { + fmt.Fprintf(&callsErrors, "\n%v", err) + } else { + return call, nil + } + } + + // If we haven't found a match then search through the exhausted calls so we + // get useful error messages. + exhausted := cs.exhausted[key] + for _, call := range exhausted { + if err := call.matches(args); err != nil { + fmt.Fprintf(&callsErrors, "\n%v", err) + } + } + + if len(expected)+len(exhausted) == 0 { + fmt.Fprintf(&callsErrors, "there are no expected calls of the method %q for that receiver", method) + } + + return nil, fmt.Errorf(callsErrors.String()) +} + +// Failures returns the calls that are not satisfied. +func (cs callSet) Failures() []*Call { + failures := make([]*Call, 0, len(cs.expected)) + for _, calls := range cs.expected { + for _, call := range calls { + if !call.satisfied() { + failures = append(failures, call) + } + } + } + return failures +} diff --git a/vendor/github.com/golang/mock/gomock/controller.go b/vendor/github.com/golang/mock/gomock/controller.go new file mode 100644 index 0000000..a7b7918 --- /dev/null +++ b/vendor/github.com/golang/mock/gomock/controller.go @@ -0,0 +1,217 @@ +// Copyright 2010 Google Inc. +// +// 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. + +// GoMock - a mock framework for Go. +// +// Standard usage: +// (1) Define an interface that you wish to mock. +// type MyInterface interface { +// SomeMethod(x int64, y string) +// } +// (2) Use mockgen to generate a mock from the interface. +// (3) Use the mock in a test: +// func TestMyThing(t *testing.T) { +// mockCtrl := gomock.NewController(t) +// defer mockCtrl.Finish() +// +// mockObj := something.NewMockMyInterface(mockCtrl) +// mockObj.EXPECT().SomeMethod(4, "blah") +// // pass mockObj to a real object and play with it. +// } +// +// By default, expected calls are not enforced to run in any particular order. +// Call order dependency can be enforced by use of InOrder and/or Call.After. +// Call.After can create more varied call order dependencies, but InOrder is +// often more convenient. +// +// The following examples create equivalent call order dependencies. +// +// Example of using Call.After to chain expected call order: +// +// firstCall := mockObj.EXPECT().SomeMethod(1, "first") +// secondCall := mockObj.EXPECT().SomeMethod(2, "second").After(firstCall) +// mockObj.EXPECT().SomeMethod(3, "third").After(secondCall) +// +// Example of using InOrder to declare expected call order: +// +// gomock.InOrder( +// mockObj.EXPECT().SomeMethod(1, "first"), +// mockObj.EXPECT().SomeMethod(2, "second"), +// mockObj.EXPECT().SomeMethod(3, "third"), +// ) +// +// TODO: +// - Handle different argument/return types (e.g. ..., chan, map, interface). +package gomock + +import ( + "fmt" + "golang.org/x/net/context" + "reflect" + "runtime" + "sync" +) + +// A TestReporter is something that can be used to report test failures. +// It is satisfied by the standard library's *testing.T. +type TestReporter interface { + Errorf(format string, args ...interface{}) + Fatalf(format string, args ...interface{}) +} + +// A Controller represents the top-level control of a mock ecosystem. +// It defines the scope and lifetime of mock objects, as well as their expectations. +// It is safe to call Controller's methods from multiple goroutines. +type Controller struct { + mu sync.Mutex + t TestReporter + expectedCalls *callSet + finished bool +} + +func NewController(t TestReporter) *Controller { + return &Controller{ + t: t, + expectedCalls: newCallSet(), + } +} + +type cancelReporter struct { + t TestReporter + cancel func() +} + +func (r *cancelReporter) Errorf(format string, args ...interface{}) { r.t.Errorf(format, args...) } +func (r *cancelReporter) Fatalf(format string, args ...interface{}) { + defer r.cancel() + r.t.Fatalf(format, args...) +} + +// WithContext returns a new Controller and a Context, which is cancelled on any +// fatal failure. +func WithContext(ctx context.Context, t TestReporter) (*Controller, context.Context) { + ctx, cancel := context.WithCancel(ctx) + return NewController(&cancelReporter{t, cancel}), ctx +} + +func (ctrl *Controller) RecordCall(receiver interface{}, method string, args ...interface{}) *Call { + if h, ok := ctrl.t.(testHelper); ok { + h.Helper() + } + + recv := reflect.ValueOf(receiver) + for i := 0; i < recv.Type().NumMethod(); i++ { + if recv.Type().Method(i).Name == method { + return ctrl.RecordCallWithMethodType(receiver, method, recv.Method(i).Type(), args...) + } + } + ctrl.t.Fatalf("gomock: failed finding method %s on %T", method, receiver) + panic("unreachable") +} + +func (ctrl *Controller) RecordCallWithMethodType(receiver interface{}, method string, methodType reflect.Type, args ...interface{}) *Call { + if h, ok := ctrl.t.(testHelper); ok { + h.Helper() + } + + call := newCall(ctrl.t, receiver, method, methodType, args...) + + ctrl.mu.Lock() + defer ctrl.mu.Unlock() + ctrl.expectedCalls.Add(call) + + return call +} + +func (ctrl *Controller) Call(receiver interface{}, method string, args ...interface{}) []interface{} { + if h, ok := ctrl.t.(testHelper); ok { + h.Helper() + } + + // Nest this code so we can use defer to make sure the lock is released. + actions := func() []func([]interface{}) []interface{} { + ctrl.mu.Lock() + defer ctrl.mu.Unlock() + + expected, err := ctrl.expectedCalls.FindMatch(receiver, method, args) + if err != nil { + origin := callerInfo(2) + ctrl.t.Fatalf("Unexpected call to %T.%v(%v) at %s because: %s", receiver, method, args, origin, err) + } + + // Two things happen here: + // * the matching call no longer needs to check prerequite calls, + // * and the prerequite calls are no longer expected, so remove them. + preReqCalls := expected.dropPrereqs() + for _, preReqCall := range preReqCalls { + ctrl.expectedCalls.Remove(preReqCall) + } + + actions := expected.call(args) + if expected.exhausted() { + ctrl.expectedCalls.Remove(expected) + } + return actions + }() + + var rets []interface{} + for _, action := range actions { + if r := action(args); r != nil { + rets = r + } + } + + return rets +} + +func (ctrl *Controller) Finish() { + if h, ok := ctrl.t.(testHelper); ok { + h.Helper() + } + + ctrl.mu.Lock() + defer ctrl.mu.Unlock() + + if ctrl.finished { + ctrl.t.Fatalf("Controller.Finish was called more than once. It has to be called exactly once.") + } + ctrl.finished = true + + // If we're currently panicking, probably because this is a deferred call, + // pass through the panic. + if err := recover(); err != nil { + panic(err) + } + + // Check that all remaining expected calls are satisfied. + failures := ctrl.expectedCalls.Failures() + for _, call := range failures { + ctrl.t.Errorf("missing call(s) to %v", call) + } + if len(failures) != 0 { + ctrl.t.Fatalf("aborting test due to missing call(s)") + } +} + +func callerInfo(skip int) string { + if _, file, line, ok := runtime.Caller(skip + 1); ok { + return fmt.Sprintf("%s:%d", file, line) + } + return "unknown file" +} + +type testHelper interface { + TestReporter + Helper() +} diff --git a/vendor/github.com/golang/mock/gomock/matchers.go b/vendor/github.com/golang/mock/gomock/matchers.go new file mode 100644 index 0000000..e8b1ddc --- /dev/null +++ b/vendor/github.com/golang/mock/gomock/matchers.go @@ -0,0 +1,99 @@ +//go:generate mockgen -destination mock_matcher/mock_matcher.go github.com/golang/mock/gomock Matcher + +// Copyright 2010 Google Inc. +// +// 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 gomock + +import ( + "fmt" + "reflect" +) + +// A Matcher is a representation of a class of values. +// It is used to represent the valid or expected arguments to a mocked method. +type Matcher interface { + // Matches returns whether x is a match. + Matches(x interface{}) bool + + // String describes what the matcher matches. + String() string +} + +type anyMatcher struct{} + +func (anyMatcher) Matches(x interface{}) bool { + return true +} + +func (anyMatcher) String() string { + return "is anything" +} + +type eqMatcher struct { + x interface{} +} + +func (e eqMatcher) Matches(x interface{}) bool { + return reflect.DeepEqual(e.x, x) +} + +func (e eqMatcher) String() string { + return fmt.Sprintf("is equal to %v", e.x) +} + +type nilMatcher struct{} + +func (nilMatcher) Matches(x interface{}) bool { + if x == nil { + return true + } + + v := reflect.ValueOf(x) + switch v.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, + reflect.Ptr, reflect.Slice: + return v.IsNil() + } + + return false +} + +func (nilMatcher) String() string { + return "is nil" +} + +type notMatcher struct { + m Matcher +} + +func (n notMatcher) Matches(x interface{}) bool { + return !n.m.Matches(x) +} + +func (n notMatcher) String() string { + // TODO: Improve this if we add a NotString method to the Matcher interface. + return "not(" + n.m.String() + ")" +} + +// Constructors +func Any() Matcher { return anyMatcher{} } +func Eq(x interface{}) Matcher { return eqMatcher{x} } +func Nil() Matcher { return nilMatcher{} } +func Not(x interface{}) Matcher { + if m, ok := x.(Matcher); ok { + return notMatcher{m} + } + return notMatcher{Eq(x)} +} diff --git a/vendor/github.com/pmezard/go-difflib/LICENSE b/vendor/github.com/pmezard/go-difflib/LICENSE new file mode 100644 index 0000000..c67dad6 --- /dev/null +++ b/vendor/github.com/pmezard/go-difflib/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2013, Patrick Mezard +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + The names of its contributors may not be used to endorse or promote +products derived from this software without specific prior written +permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/pmezard/go-difflib/difflib/difflib.go b/vendor/github.com/pmezard/go-difflib/difflib/difflib.go new file mode 100644 index 0000000..003e99f --- /dev/null +++ b/vendor/github.com/pmezard/go-difflib/difflib/difflib.go @@ -0,0 +1,772 @@ +// Package difflib is a partial port of Python difflib module. +// +// It provides tools to compare sequences of strings and generate textual diffs. +// +// The following class and functions have been ported: +// +// - SequenceMatcher +// +// - unified_diff +// +// - context_diff +// +// Getting unified diffs was the main goal of the port. Keep in mind this code +// is mostly suitable to output text differences in a human friendly way, there +// are no guarantees generated diffs are consumable by patch(1). +package difflib + +import ( + "bufio" + "bytes" + "fmt" + "io" + "strings" +) + +func min(a, b int) int { + if a < b { + return a + } + return b +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func calculateRatio(matches, length int) float64 { + if length > 0 { + return 2.0 * float64(matches) / float64(length) + } + return 1.0 +} + +type Match struct { + A int + B int + Size int +} + +type OpCode struct { + Tag byte + I1 int + I2 int + J1 int + J2 int +} + +// SequenceMatcher compares sequence of strings. The basic +// algorithm predates, and is a little fancier than, an algorithm +// published in the late 1980's by Ratcliff and Obershelp under the +// hyperbolic name "gestalt pattern matching". The basic idea is to find +// the longest contiguous matching subsequence that contains no "junk" +// elements (R-O doesn't address junk). The same idea is then applied +// recursively to the pieces of the sequences to the left and to the right +// of the matching subsequence. This does not yield minimal edit +// sequences, but does tend to yield matches that "look right" to people. +// +// SequenceMatcher tries to compute a "human-friendly diff" between two +// sequences. Unlike e.g. UNIX(tm) diff, the fundamental notion is the +// longest *contiguous* & junk-free matching subsequence. That's what +// catches peoples' eyes. The Windows(tm) windiff has another interesting +// notion, pairing up elements that appear uniquely in each sequence. +// That, and the method here, appear to yield more intuitive difference +// reports than does diff. This method appears to be the least vulnerable +// to synching up on blocks of "junk lines", though (like blank lines in +// ordinary text files, or maybe "

" lines in HTML files). That may be +// because this is the only method of the 3 that has a *concept* of +// "junk" . +// +// Timing: Basic R-O is cubic time worst case and quadratic time expected +// case. SequenceMatcher is quadratic time for the worst case and has +// expected-case behavior dependent in a complicated way on how many +// elements the sequences have in common; best case time is linear. +type SequenceMatcher struct { + a []string + b []string + b2j map[string][]int + IsJunk func(string) bool + autoJunk bool + bJunk map[string]struct{} + matchingBlocks []Match + fullBCount map[string]int + bPopular map[string]struct{} + opCodes []OpCode +} + +func NewMatcher(a, b []string) *SequenceMatcher { + m := SequenceMatcher{autoJunk: true} + m.SetSeqs(a, b) + return &m +} + +func NewMatcherWithJunk(a, b []string, autoJunk bool, + isJunk func(string) bool) *SequenceMatcher { + + m := SequenceMatcher{IsJunk: isJunk, autoJunk: autoJunk} + m.SetSeqs(a, b) + return &m +} + +// Set two sequences to be compared. +func (m *SequenceMatcher) SetSeqs(a, b []string) { + m.SetSeq1(a) + m.SetSeq2(b) +} + +// Set the first sequence to be compared. The second sequence to be compared is +// not changed. +// +// SequenceMatcher computes and caches detailed information about the second +// sequence, so if you want to compare one sequence S against many sequences, +// use .SetSeq2(s) once and call .SetSeq1(x) repeatedly for each of the other +// sequences. +// +// See also SetSeqs() and SetSeq2(). +func (m *SequenceMatcher) SetSeq1(a []string) { + if &a == &m.a { + return + } + m.a = a + m.matchingBlocks = nil + m.opCodes = nil +} + +// Set the second sequence to be compared. The first sequence to be compared is +// not changed. +func (m *SequenceMatcher) SetSeq2(b []string) { + if &b == &m.b { + return + } + m.b = b + m.matchingBlocks = nil + m.opCodes = nil + m.fullBCount = nil + m.chainB() +} + +func (m *SequenceMatcher) chainB() { + // Populate line -> index mapping + b2j := map[string][]int{} + for i, s := range m.b { + indices := b2j[s] + indices = append(indices, i) + b2j[s] = indices + } + + // Purge junk elements + m.bJunk = map[string]struct{}{} + if m.IsJunk != nil { + junk := m.bJunk + for s, _ := range b2j { + if m.IsJunk(s) { + junk[s] = struct{}{} + } + } + for s, _ := range junk { + delete(b2j, s) + } + } + + // Purge remaining popular elements + popular := map[string]struct{}{} + n := len(m.b) + if m.autoJunk && n >= 200 { + ntest := n/100 + 1 + for s, indices := range b2j { + if len(indices) > ntest { + popular[s] = struct{}{} + } + } + for s, _ := range popular { + delete(b2j, s) + } + } + m.bPopular = popular + m.b2j = b2j +} + +func (m *SequenceMatcher) isBJunk(s string) bool { + _, ok := m.bJunk[s] + return ok +} + +// Find longest matching block in a[alo:ahi] and b[blo:bhi]. +// +// If IsJunk is not defined: +// +// Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where +// alo <= i <= i+k <= ahi +// blo <= j <= j+k <= bhi +// and for all (i',j',k') meeting those conditions, +// k >= k' +// i <= i' +// and if i == i', j <= j' +// +// In other words, of all maximal matching blocks, return one that +// starts earliest in a, and of all those maximal matching blocks that +// start earliest in a, return the one that starts earliest in b. +// +// If IsJunk is defined, first the longest matching block is +// determined as above, but with the additional restriction that no +// junk element appears in the block. Then that block is extended as +// far as possible by matching (only) junk elements on both sides. So +// the resulting block never matches on junk except as identical junk +// happens to be adjacent to an "interesting" match. +// +// If no blocks match, return (alo, blo, 0). +func (m *SequenceMatcher) findLongestMatch(alo, ahi, blo, bhi int) Match { + // CAUTION: stripping common prefix or suffix would be incorrect. + // E.g., + // ab + // acab + // Longest matching block is "ab", but if common prefix is + // stripped, it's "a" (tied with "b"). UNIX(tm) diff does so + // strip, so ends up claiming that ab is changed to acab by + // inserting "ca" in the middle. That's minimal but unintuitive: + // "it's obvious" that someone inserted "ac" at the front. + // Windiff ends up at the same place as diff, but by pairing up + // the unique 'b's and then matching the first two 'a's. + besti, bestj, bestsize := alo, blo, 0 + + // find longest junk-free match + // during an iteration of the loop, j2len[j] = length of longest + // junk-free match ending with a[i-1] and b[j] + j2len := map[int]int{} + for i := alo; i != ahi; i++ { + // look at all instances of a[i] in b; note that because + // b2j has no junk keys, the loop is skipped if a[i] is junk + newj2len := map[int]int{} + for _, j := range m.b2j[m.a[i]] { + // a[i] matches b[j] + if j < blo { + continue + } + if j >= bhi { + break + } + k := j2len[j-1] + 1 + newj2len[j] = k + if k > bestsize { + besti, bestj, bestsize = i-k+1, j-k+1, k + } + } + j2len = newj2len + } + + // Extend the best by non-junk elements on each end. In particular, + // "popular" non-junk elements aren't in b2j, which greatly speeds + // the inner loop above, but also means "the best" match so far + // doesn't contain any junk *or* popular non-junk elements. + for besti > alo && bestj > blo && !m.isBJunk(m.b[bestj-1]) && + m.a[besti-1] == m.b[bestj-1] { + besti, bestj, bestsize = besti-1, bestj-1, bestsize+1 + } + for besti+bestsize < ahi && bestj+bestsize < bhi && + !m.isBJunk(m.b[bestj+bestsize]) && + m.a[besti+bestsize] == m.b[bestj+bestsize] { + bestsize += 1 + } + + // Now that we have a wholly interesting match (albeit possibly + // empty!), we may as well suck up the matching junk on each + // side of it too. Can't think of a good reason not to, and it + // saves post-processing the (possibly considerable) expense of + // figuring out what to do with it. In the case of an empty + // interesting match, this is clearly the right thing to do, + // because no other kind of match is possible in the regions. + for besti > alo && bestj > blo && m.isBJunk(m.b[bestj-1]) && + m.a[besti-1] == m.b[bestj-1] { + besti, bestj, bestsize = besti-1, bestj-1, bestsize+1 + } + for besti+bestsize < ahi && bestj+bestsize < bhi && + m.isBJunk(m.b[bestj+bestsize]) && + m.a[besti+bestsize] == m.b[bestj+bestsize] { + bestsize += 1 + } + + return Match{A: besti, B: bestj, Size: bestsize} +} + +// Return list of triples describing matching subsequences. +// +// Each triple is of the form (i, j, n), and means that +// a[i:i+n] == b[j:j+n]. The triples are monotonically increasing in +// i and in j. It's also guaranteed that if (i, j, n) and (i', j', n') are +// adjacent triples in the list, and the second is not the last triple in the +// list, then i+n != i' or j+n != j'. IOW, adjacent triples never describe +// adjacent equal blocks. +// +// The last triple is a dummy, (len(a), len(b), 0), and is the only +// triple with n==0. +func (m *SequenceMatcher) GetMatchingBlocks() []Match { + if m.matchingBlocks != nil { + return m.matchingBlocks + } + + var matchBlocks func(alo, ahi, blo, bhi int, matched []Match) []Match + matchBlocks = func(alo, ahi, blo, bhi int, matched []Match) []Match { + match := m.findLongestMatch(alo, ahi, blo, bhi) + i, j, k := match.A, match.B, match.Size + if match.Size > 0 { + if alo < i && blo < j { + matched = matchBlocks(alo, i, blo, j, matched) + } + matched = append(matched, match) + if i+k < ahi && j+k < bhi { + matched = matchBlocks(i+k, ahi, j+k, bhi, matched) + } + } + return matched + } + matched := matchBlocks(0, len(m.a), 0, len(m.b), nil) + + // It's possible that we have adjacent equal blocks in the + // matching_blocks list now. + nonAdjacent := []Match{} + i1, j1, k1 := 0, 0, 0 + for _, b := range matched { + // Is this block adjacent to i1, j1, k1? + i2, j2, k2 := b.A, b.B, b.Size + if i1+k1 == i2 && j1+k1 == j2 { + // Yes, so collapse them -- this just increases the length of + // the first block by the length of the second, and the first + // block so lengthened remains the block to compare against. + k1 += k2 + } else { + // Not adjacent. Remember the first block (k1==0 means it's + // the dummy we started with), and make the second block the + // new block to compare against. + if k1 > 0 { + nonAdjacent = append(nonAdjacent, Match{i1, j1, k1}) + } + i1, j1, k1 = i2, j2, k2 + } + } + if k1 > 0 { + nonAdjacent = append(nonAdjacent, Match{i1, j1, k1}) + } + + nonAdjacent = append(nonAdjacent, Match{len(m.a), len(m.b), 0}) + m.matchingBlocks = nonAdjacent + return m.matchingBlocks +} + +// Return list of 5-tuples describing how to turn a into b. +// +// Each tuple is of the form (tag, i1, i2, j1, j2). The first tuple +// has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the +// tuple preceding it, and likewise for j1 == the previous j2. +// +// The tags are characters, with these meanings: +// +// 'r' (replace): a[i1:i2] should be replaced by b[j1:j2] +// +// 'd' (delete): a[i1:i2] should be deleted, j1==j2 in this case. +// +// 'i' (insert): b[j1:j2] should be inserted at a[i1:i1], i1==i2 in this case. +// +// 'e' (equal): a[i1:i2] == b[j1:j2] +func (m *SequenceMatcher) GetOpCodes() []OpCode { + if m.opCodes != nil { + return m.opCodes + } + i, j := 0, 0 + matching := m.GetMatchingBlocks() + opCodes := make([]OpCode, 0, len(matching)) + for _, m := range matching { + // invariant: we've pumped out correct diffs to change + // a[:i] into b[:j], and the next matching block is + // a[ai:ai+size] == b[bj:bj+size]. So we need to pump + // out a diff to change a[i:ai] into b[j:bj], pump out + // the matching block, and move (i,j) beyond the match + ai, bj, size := m.A, m.B, m.Size + tag := byte(0) + if i < ai && j < bj { + tag = 'r' + } else if i < ai { + tag = 'd' + } else if j < bj { + tag = 'i' + } + if tag > 0 { + opCodes = append(opCodes, OpCode{tag, i, ai, j, bj}) + } + i, j = ai+size, bj+size + // the list of matching blocks is terminated by a + // sentinel with size 0 + if size > 0 { + opCodes = append(opCodes, OpCode{'e', ai, i, bj, j}) + } + } + m.opCodes = opCodes + return m.opCodes +} + +// Isolate change clusters by eliminating ranges with no changes. +// +// Return a generator of groups with up to n lines of context. +// Each group is in the same format as returned by GetOpCodes(). +func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode { + if n < 0 { + n = 3 + } + codes := m.GetOpCodes() + if len(codes) == 0 { + codes = []OpCode{OpCode{'e', 0, 1, 0, 1}} + } + // Fixup leading and trailing groups if they show no changes. + if codes[0].Tag == 'e' { + c := codes[0] + i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 + codes[0] = OpCode{c.Tag, max(i1, i2-n), i2, max(j1, j2-n), j2} + } + if codes[len(codes)-1].Tag == 'e' { + c := codes[len(codes)-1] + i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 + codes[len(codes)-1] = OpCode{c.Tag, i1, min(i2, i1+n), j1, min(j2, j1+n)} + } + nn := n + n + groups := [][]OpCode{} + group := []OpCode{} + for _, c := range codes { + i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 + // End the current group and start a new one whenever + // there is a large range with no changes. + if c.Tag == 'e' && i2-i1 > nn { + group = append(group, OpCode{c.Tag, i1, min(i2, i1+n), + j1, min(j2, j1+n)}) + groups = append(groups, group) + group = []OpCode{} + i1, j1 = max(i1, i2-n), max(j1, j2-n) + } + group = append(group, OpCode{c.Tag, i1, i2, j1, j2}) + } + if len(group) > 0 && !(len(group) == 1 && group[0].Tag == 'e') { + groups = append(groups, group) + } + return groups +} + +// Return a measure of the sequences' similarity (float in [0,1]). +// +// Where T is the total number of elements in both sequences, and +// M is the number of matches, this is 2.0*M / T. +// Note that this is 1 if the sequences are identical, and 0 if +// they have nothing in common. +// +// .Ratio() is expensive to compute if you haven't already computed +// .GetMatchingBlocks() or .GetOpCodes(), in which case you may +// want to try .QuickRatio() or .RealQuickRation() first to get an +// upper bound. +func (m *SequenceMatcher) Ratio() float64 { + matches := 0 + for _, m := range m.GetMatchingBlocks() { + matches += m.Size + } + return calculateRatio(matches, len(m.a)+len(m.b)) +} + +// Return an upper bound on ratio() relatively quickly. +// +// This isn't defined beyond that it is an upper bound on .Ratio(), and +// is faster to compute. +func (m *SequenceMatcher) QuickRatio() float64 { + // viewing a and b as multisets, set matches to the cardinality + // of their intersection; this counts the number of matches + // without regard to order, so is clearly an upper bound + if m.fullBCount == nil { + m.fullBCount = map[string]int{} + for _, s := range m.b { + m.fullBCount[s] = m.fullBCount[s] + 1 + } + } + + // avail[x] is the number of times x appears in 'b' less the + // number of times we've seen it in 'a' so far ... kinda + avail := map[string]int{} + matches := 0 + for _, s := range m.a { + n, ok := avail[s] + if !ok { + n = m.fullBCount[s] + } + avail[s] = n - 1 + if n > 0 { + matches += 1 + } + } + return calculateRatio(matches, len(m.a)+len(m.b)) +} + +// Return an upper bound on ratio() very quickly. +// +// This isn't defined beyond that it is an upper bound on .Ratio(), and +// is faster to compute than either .Ratio() or .QuickRatio(). +func (m *SequenceMatcher) RealQuickRatio() float64 { + la, lb := len(m.a), len(m.b) + return calculateRatio(min(la, lb), la+lb) +} + +// Convert range to the "ed" format +func formatRangeUnified(start, stop int) string { + // Per the diff spec at http://www.unix.org/single_unix_specification/ + beginning := start + 1 // lines start numbering with one + length := stop - start + if length == 1 { + return fmt.Sprintf("%d", beginning) + } + if length == 0 { + beginning -= 1 // empty ranges begin at line just before the range + } + return fmt.Sprintf("%d,%d", beginning, length) +} + +// Unified diff parameters +type UnifiedDiff struct { + A []string // First sequence lines + FromFile string // First file name + FromDate string // First file time + B []string // Second sequence lines + ToFile string // Second file name + ToDate string // Second file time + Eol string // Headers end of line, defaults to LF + Context int // Number of context lines +} + +// Compare two sequences of lines; generate the delta as a unified diff. +// +// Unified diffs are a compact way of showing line changes and a few +// lines of context. The number of context lines is set by 'n' which +// defaults to three. +// +// By default, the diff control lines (those with ---, +++, or @@) are +// created with a trailing newline. This is helpful so that inputs +// created from file.readlines() result in diffs that are suitable for +// file.writelines() since both the inputs and outputs have trailing +// newlines. +// +// For inputs that do not have trailing newlines, set the lineterm +// argument to "" so that the output will be uniformly newline free. +// +// The unidiff format normally has a header for filenames and modification +// times. Any or all of these may be specified using strings for +// 'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'. +// The modification times are normally expressed in the ISO 8601 format. +func WriteUnifiedDiff(writer io.Writer, diff UnifiedDiff) error { + buf := bufio.NewWriter(writer) + defer buf.Flush() + wf := func(format string, args ...interface{}) error { + _, err := buf.WriteString(fmt.Sprintf(format, args...)) + return err + } + ws := func(s string) error { + _, err := buf.WriteString(s) + return err + } + + if len(diff.Eol) == 0 { + diff.Eol = "\n" + } + + started := false + m := NewMatcher(diff.A, diff.B) + for _, g := range m.GetGroupedOpCodes(diff.Context) { + if !started { + started = true + fromDate := "" + if len(diff.FromDate) > 0 { + fromDate = "\t" + diff.FromDate + } + toDate := "" + if len(diff.ToDate) > 0 { + toDate = "\t" + diff.ToDate + } + if diff.FromFile != "" || diff.ToFile != "" { + err := wf("--- %s%s%s", diff.FromFile, fromDate, diff.Eol) + if err != nil { + return err + } + err = wf("+++ %s%s%s", diff.ToFile, toDate, diff.Eol) + if err != nil { + return err + } + } + } + first, last := g[0], g[len(g)-1] + range1 := formatRangeUnified(first.I1, last.I2) + range2 := formatRangeUnified(first.J1, last.J2) + if err := wf("@@ -%s +%s @@%s", range1, range2, diff.Eol); err != nil { + return err + } + for _, c := range g { + i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 + if c.Tag == 'e' { + for _, line := range diff.A[i1:i2] { + if err := ws(" " + line); err != nil { + return err + } + } + continue + } + if c.Tag == 'r' || c.Tag == 'd' { + for _, line := range diff.A[i1:i2] { + if err := ws("-" + line); err != nil { + return err + } + } + } + if c.Tag == 'r' || c.Tag == 'i' { + for _, line := range diff.B[j1:j2] { + if err := ws("+" + line); err != nil { + return err + } + } + } + } + } + return nil +} + +// Like WriteUnifiedDiff but returns the diff a string. +func GetUnifiedDiffString(diff UnifiedDiff) (string, error) { + w := &bytes.Buffer{} + err := WriteUnifiedDiff(w, diff) + return string(w.Bytes()), err +} + +// Convert range to the "ed" format. +func formatRangeContext(start, stop int) string { + // Per the diff spec at http://www.unix.org/single_unix_specification/ + beginning := start + 1 // lines start numbering with one + length := stop - start + if length == 0 { + beginning -= 1 // empty ranges begin at line just before the range + } + if length <= 1 { + return fmt.Sprintf("%d", beginning) + } + return fmt.Sprintf("%d,%d", beginning, beginning+length-1) +} + +type ContextDiff UnifiedDiff + +// Compare two sequences of lines; generate the delta as a context diff. +// +// Context diffs are a compact way of showing line changes and a few +// lines of context. The number of context lines is set by diff.Context +// which defaults to three. +// +// By default, the diff control lines (those with *** or ---) are +// created with a trailing newline. +// +// For inputs that do not have trailing newlines, set the diff.Eol +// argument to "" so that the output will be uniformly newline free. +// +// The context diff format normally has a header for filenames and +// modification times. Any or all of these may be specified using +// strings for diff.FromFile, diff.ToFile, diff.FromDate, diff.ToDate. +// The modification times are normally expressed in the ISO 8601 format. +// If not specified, the strings default to blanks. +func WriteContextDiff(writer io.Writer, diff ContextDiff) error { + buf := bufio.NewWriter(writer) + defer buf.Flush() + var diffErr error + wf := func(format string, args ...interface{}) { + _, err := buf.WriteString(fmt.Sprintf(format, args...)) + if diffErr == nil && err != nil { + diffErr = err + } + } + ws := func(s string) { + _, err := buf.WriteString(s) + if diffErr == nil && err != nil { + diffErr = err + } + } + + if len(diff.Eol) == 0 { + diff.Eol = "\n" + } + + prefix := map[byte]string{ + 'i': "+ ", + 'd': "- ", + 'r': "! ", + 'e': " ", + } + + started := false + m := NewMatcher(diff.A, diff.B) + for _, g := range m.GetGroupedOpCodes(diff.Context) { + if !started { + started = true + fromDate := "" + if len(diff.FromDate) > 0 { + fromDate = "\t" + diff.FromDate + } + toDate := "" + if len(diff.ToDate) > 0 { + toDate = "\t" + diff.ToDate + } + if diff.FromFile != "" || diff.ToFile != "" { + wf("*** %s%s%s", diff.FromFile, fromDate, diff.Eol) + wf("--- %s%s%s", diff.ToFile, toDate, diff.Eol) + } + } + + first, last := g[0], g[len(g)-1] + ws("***************" + diff.Eol) + + range1 := formatRangeContext(first.I1, last.I2) + wf("*** %s ****%s", range1, diff.Eol) + for _, c := range g { + if c.Tag == 'r' || c.Tag == 'd' { + for _, cc := range g { + if cc.Tag == 'i' { + continue + } + for _, line := range diff.A[cc.I1:cc.I2] { + ws(prefix[cc.Tag] + line) + } + } + break + } + } + + range2 := formatRangeContext(first.J1, last.J2) + wf("--- %s ----%s", range2, diff.Eol) + for _, c := range g { + if c.Tag == 'r' || c.Tag == 'i' { + for _, cc := range g { + if cc.Tag == 'd' { + continue + } + for _, line := range diff.B[cc.J1:cc.J2] { + ws(prefix[cc.Tag] + line) + } + } + break + } + } + } + return diffErr +} + +// Like WriteContextDiff but returns the diff a string. +func GetContextDiffString(diff ContextDiff) (string, error) { + w := &bytes.Buffer{} + err := WriteContextDiff(w, diff) + return string(w.Bytes()), err +} + +// Split a string on "\n" while preserving them. The output can be used +// as input for UnifiedDiff and ContextDiff structures. +func SplitLines(s string) []string { + lines := strings.SplitAfter(s, "\n") + lines[len(lines)-1] += "\n" + return lines +} diff --git a/vendor/github.com/stretchr/testify/LICENSE b/vendor/github.com/stretchr/testify/LICENSE new file mode 100644 index 0000000..473b670 --- /dev/null +++ b/vendor/github.com/stretchr/testify/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell + +Please consider promoting this project if you find it useful. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/stretchr/testify/assert/assertion_format.go b/vendor/github.com/stretchr/testify/assert/assertion_format.go new file mode 100644 index 0000000..aa1c2b9 --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/assertion_format.go @@ -0,0 +1,484 @@ +/* +* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen +* THIS FILE MUST NOT BE EDITED BY HAND + */ + +package assert + +import ( + http "net/http" + url "net/url" + time "time" +) + +// Conditionf uses a Comparison to assert a complex condition. +func Conditionf(t TestingT, comp Comparison, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Condition(t, comp, append([]interface{}{msg}, args...)...) +} + +// Containsf asserts that the specified string, list(array, slice...) or map contains the +// specified substring or element. +// +// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted") +// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") +// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") +func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Contains(t, s, contains, append([]interface{}{msg}, args...)...) +} + +// DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. +func DirExistsf(t TestingT, path string, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return DirExists(t, path, append([]interface{}{msg}, args...)...) +} + +// ElementsMatchf asserts that the specified listA(array, slice...) is equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should match. +// +// assert.ElementsMatchf(t, [1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted") +func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return ElementsMatch(t, listA, listB, append([]interface{}{msg}, args...)...) +} + +// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// assert.Emptyf(t, obj, "error message %s", "formatted") +func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Empty(t, object, append([]interface{}{msg}, args...)...) +} + +// Equalf asserts that two objects are equal. +// +// assert.Equalf(t, 123, 123, "error message %s", "formatted") +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). Function equality +// cannot be determined and will always fail. +func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Equal(t, expected, actual, append([]interface{}{msg}, args...)...) +} + +// EqualErrorf asserts that a function returned an error (i.e. not `nil`) +// and that it is equal to the provided error. +// +// actualObj, err := SomeFunction() +// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") +func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return EqualError(t, theError, errString, append([]interface{}{msg}, args...)...) +} + +// EqualValuesf asserts that two objects are equal or convertable to the same types +// and equal. +// +// assert.EqualValuesf(t, uint32(123, "error message %s", "formatted"), int32(123)) +func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return EqualValues(t, expected, actual, append([]interface{}{msg}, args...)...) +} + +// Errorf asserts that a function returned an error (i.e. not `nil`). +// +// actualObj, err := SomeFunction() +// if assert.Errorf(t, err, "error message %s", "formatted") { +// assert.Equal(t, expectedErrorf, err) +// } +func Errorf(t TestingT, err error, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Error(t, err, append([]interface{}{msg}, args...)...) +} + +// Exactlyf asserts that two objects are equal in value and type. +// +// assert.Exactlyf(t, int32(123, "error message %s", "formatted"), int64(123)) +func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Exactly(t, expected, actual, append([]interface{}{msg}, args...)...) +} + +// Failf reports a failure through +func Failf(t TestingT, failureMessage string, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Fail(t, failureMessage, append([]interface{}{msg}, args...)...) +} + +// FailNowf fails test +func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return FailNow(t, failureMessage, append([]interface{}{msg}, args...)...) +} + +// Falsef asserts that the specified value is false. +// +// assert.Falsef(t, myBool, "error message %s", "formatted") +func Falsef(t TestingT, value bool, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return False(t, value, append([]interface{}{msg}, args...)...) +} + +// FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. +func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return FileExists(t, path, append([]interface{}{msg}, args...)...) +} + +// HTTPBodyContainsf asserts that a specified handler returns a +// body that contains a string. +// +// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return HTTPBodyContains(t, handler, method, url, values, str, append([]interface{}{msg}, args...)...) +} + +// HTTPBodyNotContainsf asserts that a specified handler returns a +// body that does not contain a string. +// +// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return HTTPBodyNotContains(t, handler, method, url, values, str, append([]interface{}{msg}, args...)...) +} + +// HTTPErrorf asserts that a specified handler returns an error status code. +// +// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). +func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return HTTPError(t, handler, method, url, values, append([]interface{}{msg}, args...)...) +} + +// HTTPRedirectf asserts that a specified handler returns a redirect status code. +// +// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). +func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return HTTPRedirect(t, handler, method, url, values, append([]interface{}{msg}, args...)...) +} + +// HTTPSuccessf asserts that a specified handler returns a success status code. +// +// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return HTTPSuccess(t, handler, method, url, values, append([]interface{}{msg}, args...)...) +} + +// Implementsf asserts that an object is implemented by the specified interface. +// +// assert.Implementsf(t, (*MyInterface, "error message %s", "formatted")(nil), new(MyObject)) +func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Implements(t, interfaceObject, object, append([]interface{}{msg}, args...)...) +} + +// InDeltaf asserts that the two numerals are within delta of each other. +// +// assert.InDeltaf(t, math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01) +func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return InDelta(t, expected, actual, delta, append([]interface{}{msg}, args...)...) +} + +// InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. +func InDeltaMapValuesf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return InDeltaMapValues(t, expected, actual, delta, append([]interface{}{msg}, args...)...) +} + +// InDeltaSlicef is the same as InDelta, except it compares two slices. +func InDeltaSlicef(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return InDeltaSlice(t, expected, actual, delta, append([]interface{}{msg}, args...)...) +} + +// InEpsilonf asserts that expected and actual have a relative error less than epsilon +func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return InEpsilon(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...) +} + +// InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices. +func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return InEpsilonSlice(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...) +} + +// IsTypef asserts that the specified objects are of the same type. +func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return IsType(t, expectedType, object, append([]interface{}{msg}, args...)...) +} + +// JSONEqf asserts that two JSON strings are equivalent. +// +// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return JSONEq(t, expected, actual, append([]interface{}{msg}, args...)...) +} + +// Lenf asserts that the specified object has specific length. +// Lenf also fails if the object has a type that len() not accept. +// +// assert.Lenf(t, mySlice, 3, "error message %s", "formatted") +func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Len(t, object, length, append([]interface{}{msg}, args...)...) +} + +// Nilf asserts that the specified object is nil. +// +// assert.Nilf(t, err, "error message %s", "formatted") +func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Nil(t, object, append([]interface{}{msg}, args...)...) +} + +// NoErrorf asserts that a function returned no error (i.e. `nil`). +// +// actualObj, err := SomeFunction() +// if assert.NoErrorf(t, err, "error message %s", "formatted") { +// assert.Equal(t, expectedObj, actualObj) +// } +func NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NoError(t, err, append([]interface{}{msg}, args...)...) +} + +// NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the +// specified substring or element. +// +// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") +func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NotContains(t, s, contains, append([]interface{}{msg}, args...)...) +} + +// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// if assert.NotEmptyf(t, obj, "error message %s", "formatted") { +// assert.Equal(t, "two", obj[1]) +// } +func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NotEmpty(t, object, append([]interface{}{msg}, args...)...) +} + +// NotEqualf asserts that the specified values are NOT equal. +// +// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). +func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NotEqual(t, expected, actual, append([]interface{}{msg}, args...)...) +} + +// NotNilf asserts that the specified object is not nil. +// +// assert.NotNilf(t, err, "error message %s", "formatted") +func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NotNil(t, object, append([]interface{}{msg}, args...)...) +} + +// NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. +// +// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") +func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NotPanics(t, f, append([]interface{}{msg}, args...)...) +} + +// NotRegexpf asserts that a specified regexp does not match a string. +// +// assert.NotRegexpf(t, regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting") +// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") +func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NotRegexp(t, rx, str, append([]interface{}{msg}, args...)...) +} + +// NotSubsetf asserts that the specified list(array, slice...) contains not all +// elements given in the specified subset(array, slice...). +// +// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NotSubset(t, list, subset, append([]interface{}{msg}, args...)...) +} + +// NotZerof asserts that i is not the zero value for its type. +func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NotZero(t, i, append([]interface{}{msg}, args...)...) +} + +// Panicsf asserts that the code inside the specified PanicTestFunc panics. +// +// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") +func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Panics(t, f, append([]interface{}{msg}, args...)...) +} + +// PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that +// the recovered panic value equals the expected panic value. +// +// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return PanicsWithValue(t, expected, f, append([]interface{}{msg}, args...)...) +} + +// Regexpf asserts that a specified regexp matches a string. +// +// assert.Regexpf(t, regexp.MustCompile("start", "error message %s", "formatted"), "it's starting") +// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") +func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Regexp(t, rx, str, append([]interface{}{msg}, args...)...) +} + +// Subsetf asserts that the specified list(array, slice...) contains all +// elements given in the specified subset(array, slice...). +// +// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Subset(t, list, subset, append([]interface{}{msg}, args...)...) +} + +// Truef asserts that the specified value is true. +// +// assert.Truef(t, myBool, "error message %s", "formatted") +func Truef(t TestingT, value bool, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return True(t, value, append([]interface{}{msg}, args...)...) +} + +// WithinDurationf asserts that the two times are within duration delta of each other. +// +// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") +func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...) +} + +// Zerof asserts that i is the zero value for its type. +func Zerof(t TestingT, i interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return Zero(t, i, append([]interface{}{msg}, args...)...) +} diff --git a/vendor/github.com/stretchr/testify/assert/assertion_format.go.tmpl b/vendor/github.com/stretchr/testify/assert/assertion_format.go.tmpl new file mode 100644 index 0000000..d2bb0b8 --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/assertion_format.go.tmpl @@ -0,0 +1,5 @@ +{{.CommentFormat}} +func {{.DocInfo.Name}}f(t TestingT, {{.ParamsFormat}}) bool { + if h, ok := t.(tHelper); ok { h.Helper() } + return {{.DocInfo.Name}}(t, {{.ForwardedParamsFormat}}) +} diff --git a/vendor/github.com/stretchr/testify/assert/assertion_forward.go b/vendor/github.com/stretchr/testify/assert/assertion_forward.go new file mode 100644 index 0000000..de39f79 --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/assertion_forward.go @@ -0,0 +1,956 @@ +/* +* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen +* THIS FILE MUST NOT BE EDITED BY HAND + */ + +package assert + +import ( + http "net/http" + url "net/url" + time "time" +) + +// Condition uses a Comparison to assert a complex condition. +func (a *Assertions) Condition(comp Comparison, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Condition(a.t, comp, msgAndArgs...) +} + +// Conditionf uses a Comparison to assert a complex condition. +func (a *Assertions) Conditionf(comp Comparison, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Conditionf(a.t, comp, msg, args...) +} + +// Contains asserts that the specified string, list(array, slice...) or map contains the +// specified substring or element. +// +// a.Contains("Hello World", "World") +// a.Contains(["Hello", "World"], "World") +// a.Contains({"Hello": "World"}, "Hello") +func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Contains(a.t, s, contains, msgAndArgs...) +} + +// Containsf asserts that the specified string, list(array, slice...) or map contains the +// specified substring or element. +// +// a.Containsf("Hello World", "World", "error message %s", "formatted") +// a.Containsf(["Hello", "World"], "World", "error message %s", "formatted") +// a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted") +func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Containsf(a.t, s, contains, msg, args...) +} + +// DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. +func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return DirExists(a.t, path, msgAndArgs...) +} + +// DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. +func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return DirExistsf(a.t, path, msg, args...) +} + +// ElementsMatch asserts that the specified listA(array, slice...) is equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should match. +// +// a.ElementsMatch([1, 3, 2, 3], [1, 3, 3, 2]) +func (a *Assertions) ElementsMatch(listA interface{}, listB interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return ElementsMatch(a.t, listA, listB, msgAndArgs...) +} + +// ElementsMatchf asserts that the specified listA(array, slice...) is equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should match. +// +// a.ElementsMatchf([1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted") +func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return ElementsMatchf(a.t, listA, listB, msg, args...) +} + +// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// a.Empty(obj) +func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Empty(a.t, object, msgAndArgs...) +} + +// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// a.Emptyf(obj, "error message %s", "formatted") +func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Emptyf(a.t, object, msg, args...) +} + +// Equal asserts that two objects are equal. +// +// a.Equal(123, 123) +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). Function equality +// cannot be determined and will always fail. +func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Equal(a.t, expected, actual, msgAndArgs...) +} + +// EqualError asserts that a function returned an error (i.e. not `nil`) +// and that it is equal to the provided error. +// +// actualObj, err := SomeFunction() +// a.EqualError(err, expectedErrorString) +func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return EqualError(a.t, theError, errString, msgAndArgs...) +} + +// EqualErrorf asserts that a function returned an error (i.e. not `nil`) +// and that it is equal to the provided error. +// +// actualObj, err := SomeFunction() +// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted") +func (a *Assertions) EqualErrorf(theError error, errString string, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return EqualErrorf(a.t, theError, errString, msg, args...) +} + +// EqualValues asserts that two objects are equal or convertable to the same types +// and equal. +// +// a.EqualValues(uint32(123), int32(123)) +func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return EqualValues(a.t, expected, actual, msgAndArgs...) +} + +// EqualValuesf asserts that two objects are equal or convertable to the same types +// and equal. +// +// a.EqualValuesf(uint32(123, "error message %s", "formatted"), int32(123)) +func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return EqualValuesf(a.t, expected, actual, msg, args...) +} + +// Equalf asserts that two objects are equal. +// +// a.Equalf(123, 123, "error message %s", "formatted") +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). Function equality +// cannot be determined and will always fail. +func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Equalf(a.t, expected, actual, msg, args...) +} + +// Error asserts that a function returned an error (i.e. not `nil`). +// +// actualObj, err := SomeFunction() +// if a.Error(err) { +// assert.Equal(t, expectedError, err) +// } +func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Error(a.t, err, msgAndArgs...) +} + +// Errorf asserts that a function returned an error (i.e. not `nil`). +// +// actualObj, err := SomeFunction() +// if a.Errorf(err, "error message %s", "formatted") { +// assert.Equal(t, expectedErrorf, err) +// } +func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Errorf(a.t, err, msg, args...) +} + +// Exactly asserts that two objects are equal in value and type. +// +// a.Exactly(int32(123), int64(123)) +func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Exactly(a.t, expected, actual, msgAndArgs...) +} + +// Exactlyf asserts that two objects are equal in value and type. +// +// a.Exactlyf(int32(123, "error message %s", "formatted"), int64(123)) +func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Exactlyf(a.t, expected, actual, msg, args...) +} + +// Fail reports a failure through +func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Fail(a.t, failureMessage, msgAndArgs...) +} + +// FailNow fails test +func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return FailNow(a.t, failureMessage, msgAndArgs...) +} + +// FailNowf fails test +func (a *Assertions) FailNowf(failureMessage string, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return FailNowf(a.t, failureMessage, msg, args...) +} + +// Failf reports a failure through +func (a *Assertions) Failf(failureMessage string, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Failf(a.t, failureMessage, msg, args...) +} + +// False asserts that the specified value is false. +// +// a.False(myBool) +func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return False(a.t, value, msgAndArgs...) +} + +// Falsef asserts that the specified value is false. +// +// a.Falsef(myBool, "error message %s", "formatted") +func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Falsef(a.t, value, msg, args...) +} + +// FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. +func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return FileExists(a.t, path, msgAndArgs...) +} + +// FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. +func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return FileExistsf(a.t, path, msg, args...) +} + +// HTTPBodyContains asserts that a specified handler returns a +// body that contains a string. +// +// a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return HTTPBodyContains(a.t, handler, method, url, values, str, msgAndArgs...) +} + +// HTTPBodyContainsf asserts that a specified handler returns a +// body that contains a string. +// +// a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return HTTPBodyContainsf(a.t, handler, method, url, values, str, msg, args...) +} + +// HTTPBodyNotContains asserts that a specified handler returns a +// body that does not contain a string. +// +// a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return HTTPBodyNotContains(a.t, handler, method, url, values, str, msgAndArgs...) +} + +// HTTPBodyNotContainsf asserts that a specified handler returns a +// body that does not contain a string. +// +// a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return HTTPBodyNotContainsf(a.t, handler, method, url, values, str, msg, args...) +} + +// HTTPError asserts that a specified handler returns an error status code. +// +// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return HTTPError(a.t, handler, method, url, values, msgAndArgs...) +} + +// HTTPErrorf asserts that a specified handler returns an error status code. +// +// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). +func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return HTTPErrorf(a.t, handler, method, url, values, msg, args...) +} + +// HTTPRedirect asserts that a specified handler returns a redirect status code. +// +// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return HTTPRedirect(a.t, handler, method, url, values, msgAndArgs...) +} + +// HTTPRedirectf asserts that a specified handler returns a redirect status code. +// +// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false). +func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return HTTPRedirectf(a.t, handler, method, url, values, msg, args...) +} + +// HTTPSuccess asserts that a specified handler returns a success status code. +// +// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return HTTPSuccess(a.t, handler, method, url, values, msgAndArgs...) +} + +// HTTPSuccessf asserts that a specified handler returns a success status code. +// +// a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") +// +// Returns whether the assertion was successful (true) or not (false). +func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return HTTPSuccessf(a.t, handler, method, url, values, msg, args...) +} + +// Implements asserts that an object is implemented by the specified interface. +// +// a.Implements((*MyInterface)(nil), new(MyObject)) +func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Implements(a.t, interfaceObject, object, msgAndArgs...) +} + +// Implementsf asserts that an object is implemented by the specified interface. +// +// a.Implementsf((*MyInterface, "error message %s", "formatted")(nil), new(MyObject)) +func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Implementsf(a.t, interfaceObject, object, msg, args...) +} + +// InDelta asserts that the two numerals are within delta of each other. +// +// a.InDelta(math.Pi, (22 / 7.0), 0.01) +func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return InDelta(a.t, expected, actual, delta, msgAndArgs...) +} + +// InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. +func (a *Assertions) InDeltaMapValues(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return InDeltaMapValues(a.t, expected, actual, delta, msgAndArgs...) +} + +// InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. +func (a *Assertions) InDeltaMapValuesf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return InDeltaMapValuesf(a.t, expected, actual, delta, msg, args...) +} + +// InDeltaSlice is the same as InDelta, except it compares two slices. +func (a *Assertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return InDeltaSlice(a.t, expected, actual, delta, msgAndArgs...) +} + +// InDeltaSlicef is the same as InDelta, except it compares two slices. +func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return InDeltaSlicef(a.t, expected, actual, delta, msg, args...) +} + +// InDeltaf asserts that the two numerals are within delta of each other. +// +// a.InDeltaf(math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01) +func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return InDeltaf(a.t, expected, actual, delta, msg, args...) +} + +// InEpsilon asserts that expected and actual have a relative error less than epsilon +func (a *Assertions) InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...) +} + +// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. +func (a *Assertions) InEpsilonSlice(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return InEpsilonSlice(a.t, expected, actual, epsilon, msgAndArgs...) +} + +// InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices. +func (a *Assertions) InEpsilonSlicef(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return InEpsilonSlicef(a.t, expected, actual, epsilon, msg, args...) +} + +// InEpsilonf asserts that expected and actual have a relative error less than epsilon +func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return InEpsilonf(a.t, expected, actual, epsilon, msg, args...) +} + +// IsType asserts that the specified objects are of the same type. +func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsType(a.t, expectedType, object, msgAndArgs...) +} + +// IsTypef asserts that the specified objects are of the same type. +func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return IsTypef(a.t, expectedType, object, msg, args...) +} + +// JSONEq asserts that two JSON strings are equivalent. +// +// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return JSONEq(a.t, expected, actual, msgAndArgs...) +} + +// JSONEqf asserts that two JSON strings are equivalent. +// +// a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return JSONEqf(a.t, expected, actual, msg, args...) +} + +// Len asserts that the specified object has specific length. +// Len also fails if the object has a type that len() not accept. +// +// a.Len(mySlice, 3) +func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Len(a.t, object, length, msgAndArgs...) +} + +// Lenf asserts that the specified object has specific length. +// Lenf also fails if the object has a type that len() not accept. +// +// a.Lenf(mySlice, 3, "error message %s", "formatted") +func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Lenf(a.t, object, length, msg, args...) +} + +// Nil asserts that the specified object is nil. +// +// a.Nil(err) +func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Nil(a.t, object, msgAndArgs...) +} + +// Nilf asserts that the specified object is nil. +// +// a.Nilf(err, "error message %s", "formatted") +func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Nilf(a.t, object, msg, args...) +} + +// NoError asserts that a function returned no error (i.e. `nil`). +// +// actualObj, err := SomeFunction() +// if a.NoError(err) { +// assert.Equal(t, expectedObj, actualObj) +// } +func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NoError(a.t, err, msgAndArgs...) +} + +// NoErrorf asserts that a function returned no error (i.e. `nil`). +// +// actualObj, err := SomeFunction() +// if a.NoErrorf(err, "error message %s", "formatted") { +// assert.Equal(t, expectedObj, actualObj) +// } +func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NoErrorf(a.t, err, msg, args...) +} + +// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the +// specified substring or element. +// +// a.NotContains("Hello World", "Earth") +// a.NotContains(["Hello", "World"], "Earth") +// a.NotContains({"Hello": "World"}, "Earth") +func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotContains(a.t, s, contains, msgAndArgs...) +} + +// NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the +// specified substring or element. +// +// a.NotContainsf("Hello World", "Earth", "error message %s", "formatted") +// a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted") +// a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted") +func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotContainsf(a.t, s, contains, msg, args...) +} + +// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// if a.NotEmpty(obj) { +// assert.Equal(t, "two", obj[1]) +// } +func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotEmpty(a.t, object, msgAndArgs...) +} + +// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// if a.NotEmptyf(obj, "error message %s", "formatted") { +// assert.Equal(t, "two", obj[1]) +// } +func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotEmptyf(a.t, object, msg, args...) +} + +// NotEqual asserts that the specified values are NOT equal. +// +// a.NotEqual(obj1, obj2) +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). +func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotEqual(a.t, expected, actual, msgAndArgs...) +} + +// NotEqualf asserts that the specified values are NOT equal. +// +// a.NotEqualf(obj1, obj2, "error message %s", "formatted") +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). +func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotEqualf(a.t, expected, actual, msg, args...) +} + +// NotNil asserts that the specified object is not nil. +// +// a.NotNil(err) +func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotNil(a.t, object, msgAndArgs...) +} + +// NotNilf asserts that the specified object is not nil. +// +// a.NotNilf(err, "error message %s", "formatted") +func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotNilf(a.t, object, msg, args...) +} + +// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. +// +// a.NotPanics(func(){ RemainCalm() }) +func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotPanics(a.t, f, msgAndArgs...) +} + +// NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. +// +// a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted") +func (a *Assertions) NotPanicsf(f PanicTestFunc, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotPanicsf(a.t, f, msg, args...) +} + +// NotRegexp asserts that a specified regexp does not match a string. +// +// a.NotRegexp(regexp.MustCompile("starts"), "it's starting") +// a.NotRegexp("^start", "it's not starting") +func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotRegexp(a.t, rx, str, msgAndArgs...) +} + +// NotRegexpf asserts that a specified regexp does not match a string. +// +// a.NotRegexpf(regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting") +// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") +func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotRegexpf(a.t, rx, str, msg, args...) +} + +// NotSubset asserts that the specified list(array, slice...) contains not all +// elements given in the specified subset(array, slice...). +// +// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotSubset(a.t, list, subset, msgAndArgs...) +} + +// NotSubsetf asserts that the specified list(array, slice...) contains not all +// elements given in the specified subset(array, slice...). +// +// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotSubsetf(a.t, list, subset, msg, args...) +} + +// NotZero asserts that i is not the zero value for its type. +func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotZero(a.t, i, msgAndArgs...) +} + +// NotZerof asserts that i is not the zero value for its type. +func (a *Assertions) NotZerof(i interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotZerof(a.t, i, msg, args...) +} + +// Panics asserts that the code inside the specified PanicTestFunc panics. +// +// a.Panics(func(){ GoCrazy() }) +func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Panics(a.t, f, msgAndArgs...) +} + +// PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that +// the recovered panic value equals the expected panic value. +// +// a.PanicsWithValue("crazy error", func(){ GoCrazy() }) +func (a *Assertions) PanicsWithValue(expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return PanicsWithValue(a.t, expected, f, msgAndArgs...) +} + +// PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that +// the recovered panic value equals the expected panic value. +// +// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +func (a *Assertions) PanicsWithValuef(expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return PanicsWithValuef(a.t, expected, f, msg, args...) +} + +// Panicsf asserts that the code inside the specified PanicTestFunc panics. +// +// a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted") +func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Panicsf(a.t, f, msg, args...) +} + +// Regexp asserts that a specified regexp matches a string. +// +// a.Regexp(regexp.MustCompile("start"), "it's starting") +// a.Regexp("start...$", "it's not starting") +func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Regexp(a.t, rx, str, msgAndArgs...) +} + +// Regexpf asserts that a specified regexp matches a string. +// +// a.Regexpf(regexp.MustCompile("start", "error message %s", "formatted"), "it's starting") +// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") +func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Regexpf(a.t, rx, str, msg, args...) +} + +// Subset asserts that the specified list(array, slice...) contains all +// elements given in the specified subset(array, slice...). +// +// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Subset(a.t, list, subset, msgAndArgs...) +} + +// Subsetf asserts that the specified list(array, slice...) contains all +// elements given in the specified subset(array, slice...). +// +// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Subsetf(a.t, list, subset, msg, args...) +} + +// True asserts that the specified value is true. +// +// a.True(myBool) +func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return True(a.t, value, msgAndArgs...) +} + +// Truef asserts that the specified value is true. +// +// a.Truef(myBool, "error message %s", "formatted") +func (a *Assertions) Truef(value bool, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Truef(a.t, value, msg, args...) +} + +// WithinDuration asserts that the two times are within duration delta of each other. +// +// a.WithinDuration(time.Now(), time.Now(), 10*time.Second) +func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return WithinDuration(a.t, expected, actual, delta, msgAndArgs...) +} + +// WithinDurationf asserts that the two times are within duration delta of each other. +// +// a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") +func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return WithinDurationf(a.t, expected, actual, delta, msg, args...) +} + +// Zero asserts that i is the zero value for its type. +func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Zero(a.t, i, msgAndArgs...) +} + +// Zerof asserts that i is the zero value for its type. +func (a *Assertions) Zerof(i interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return Zerof(a.t, i, msg, args...) +} diff --git a/vendor/github.com/stretchr/testify/assert/assertion_forward.go.tmpl b/vendor/github.com/stretchr/testify/assert/assertion_forward.go.tmpl new file mode 100644 index 0000000..188bb9e --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/assertion_forward.go.tmpl @@ -0,0 +1,5 @@ +{{.CommentWithoutT "a"}} +func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) bool { + if h, ok := a.t.(tHelper); ok { h.Helper() } + return {{.DocInfo.Name}}(a.t, {{.ForwardedParams}}) +} diff --git a/vendor/github.com/stretchr/testify/assert/assertions.go b/vendor/github.com/stretchr/testify/assert/assertions.go new file mode 100644 index 0000000..5bdec56 --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/assertions.go @@ -0,0 +1,1394 @@ +package assert + +import ( + "bufio" + "bytes" + "encoding/json" + "errors" + "fmt" + "math" + "os" + "reflect" + "regexp" + "runtime" + "strings" + "time" + "unicode" + "unicode/utf8" + + "github.com/davecgh/go-spew/spew" + "github.com/pmezard/go-difflib/difflib" +) + +//go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_format.go.tmpl + +// TestingT is an interface wrapper around *testing.T +type TestingT interface { + Errorf(format string, args ...interface{}) +} + +// ComparisonAssertionFunc is a common function prototype when comparing two values. Can be useful +// for table driven tests. +type ComparisonAssertionFunc func(TestingT, interface{}, interface{}, ...interface{}) bool + +// ValueAssertionFunc is a common function prototype when validating a single value. Can be useful +// for table driven tests. +type ValueAssertionFunc func(TestingT, interface{}, ...interface{}) bool + +// BoolAssertionFunc is a common function prototype when validating a bool value. Can be useful +// for table driven tests. +type BoolAssertionFunc func(TestingT, bool, ...interface{}) bool + +// ValuesAssertionFunc is a common function prototype when validating an error value. Can be useful +// for table driven tests. +type ErrorAssertionFunc func(TestingT, error, ...interface{}) bool + +// Comparison a custom function that returns true on success and false on failure +type Comparison func() (success bool) + +/* + Helper functions +*/ + +// ObjectsAreEqual determines if two objects are considered equal. +// +// This function does no assertion of any kind. +func ObjectsAreEqual(expected, actual interface{}) bool { + if expected == nil || actual == nil { + return expected == actual + } + + exp, ok := expected.([]byte) + if !ok { + return reflect.DeepEqual(expected, actual) + } + + act, ok := actual.([]byte) + if !ok { + return false + } + if exp == nil || act == nil { + return exp == nil && act == nil + } + return bytes.Equal(exp, act) +} + +// ObjectsAreEqualValues gets whether two objects are equal, or if their +// values are equal. +func ObjectsAreEqualValues(expected, actual interface{}) bool { + if ObjectsAreEqual(expected, actual) { + return true + } + + actualType := reflect.TypeOf(actual) + if actualType == nil { + return false + } + expectedValue := reflect.ValueOf(expected) + if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) { + // Attempt comparison after type conversion + return reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), actual) + } + + return false +} + +/* CallerInfo is necessary because the assert functions use the testing object +internally, causing it to print the file:line of the assert method, rather than where +the problem actually occurred in calling code.*/ + +// CallerInfo returns an array of strings containing the file and line number +// of each stack frame leading from the current test to the assert call that +// failed. +func CallerInfo() []string { + + pc := uintptr(0) + file := "" + line := 0 + ok := false + name := "" + + callers := []string{} + for i := 0; ; i++ { + pc, file, line, ok = runtime.Caller(i) + if !ok { + // The breaks below failed to terminate the loop, and we ran off the + // end of the call stack. + break + } + + // This is a huge edge case, but it will panic if this is the case, see #180 + if file == "" { + break + } + + f := runtime.FuncForPC(pc) + if f == nil { + break + } + name = f.Name() + + // testing.tRunner is the standard library function that calls + // tests. Subtests are called directly by tRunner, without going through + // the Test/Benchmark/Example function that contains the t.Run calls, so + // with subtests we should break when we hit tRunner, without adding it + // to the list of callers. + if name == "testing.tRunner" { + break + } + + parts := strings.Split(file, "/") + file = parts[len(parts)-1] + if len(parts) > 1 { + dir := parts[len(parts)-2] + if (dir != "assert" && dir != "mock" && dir != "require") || file == "mock_test.go" { + callers = append(callers, fmt.Sprintf("%s:%d", file, line)) + } + } + + // Drop the package + segments := strings.Split(name, ".") + name = segments[len(segments)-1] + if isTest(name, "Test") || + isTest(name, "Benchmark") || + isTest(name, "Example") { + break + } + } + + return callers +} + +// Stolen from the `go test` tool. +// isTest tells whether name looks like a test (or benchmark, according to prefix). +// It is a Test (say) if there is a character after Test that is not a lower-case letter. +// We don't want TesticularCancer. +func isTest(name, prefix string) bool { + if !strings.HasPrefix(name, prefix) { + return false + } + if len(name) == len(prefix) { // "Test" is ok + return true + } + rune, _ := utf8.DecodeRuneInString(name[len(prefix):]) + return !unicode.IsLower(rune) +} + +func messageFromMsgAndArgs(msgAndArgs ...interface{}) string { + if len(msgAndArgs) == 0 || msgAndArgs == nil { + return "" + } + if len(msgAndArgs) == 1 { + return msgAndArgs[0].(string) + } + if len(msgAndArgs) > 1 { + return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...) + } + return "" +} + +// Aligns the provided message so that all lines after the first line start at the same location as the first line. +// Assumes that the first line starts at the correct location (after carriage return, tab, label, spacer and tab). +// The longestLabelLen parameter specifies the length of the longest label in the output (required becaues this is the +// basis on which the alignment occurs). +func indentMessageLines(message string, longestLabelLen int) string { + outBuf := new(bytes.Buffer) + + for i, scanner := 0, bufio.NewScanner(strings.NewReader(message)); scanner.Scan(); i++ { + // no need to align first line because it starts at the correct location (after the label) + if i != 0 { + // append alignLen+1 spaces to align with "{{longestLabel}}:" before adding tab + outBuf.WriteString("\n\t" + strings.Repeat(" ", longestLabelLen+1) + "\t") + } + outBuf.WriteString(scanner.Text()) + } + + return outBuf.String() +} + +type failNower interface { + FailNow() +} + +// FailNow fails test +func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + Fail(t, failureMessage, msgAndArgs...) + + // We cannot extend TestingT with FailNow() and + // maintain backwards compatibility, so we fallback + // to panicking when FailNow is not available in + // TestingT. + // See issue #263 + + if t, ok := t.(failNower); ok { + t.FailNow() + } else { + panic("test failed and t is missing `FailNow()`") + } + return false +} + +// Fail reports a failure through +func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + content := []labeledContent{ + {"Error Trace", strings.Join(CallerInfo(), "\n\t\t\t")}, + {"Error", failureMessage}, + } + + // Add test name if the Go version supports it + if n, ok := t.(interface { + Name() string + }); ok { + content = append(content, labeledContent{"Test", n.Name()}) + } + + message := messageFromMsgAndArgs(msgAndArgs...) + if len(message) > 0 { + content = append(content, labeledContent{"Messages", message}) + } + + t.Errorf("\n%s", ""+labeledOutput(content...)) + + return false +} + +type labeledContent struct { + label string + content string +} + +// labeledOutput returns a string consisting of the provided labeledContent. Each labeled output is appended in the following manner: +// +// \t{{label}}:{{align_spaces}}\t{{content}}\n +// +// The initial carriage return is required to undo/erase any padding added by testing.T.Errorf. The "\t{{label}}:" is for the label. +// If a label is shorter than the longest label provided, padding spaces are added to make all the labels match in length. Once this +// alignment is achieved, "\t{{content}}\n" is added for the output. +// +// If the content of the labeledOutput contains line breaks, the subsequent lines are aligned so that they start at the same location as the first line. +func labeledOutput(content ...labeledContent) string { + longestLabel := 0 + for _, v := range content { + if len(v.label) > longestLabel { + longestLabel = len(v.label) + } + } + var output string + for _, v := range content { + output += "\t" + v.label + ":" + strings.Repeat(" ", longestLabel-len(v.label)) + "\t" + indentMessageLines(v.content, longestLabel) + "\n" + } + return output +} + +// Implements asserts that an object is implemented by the specified interface. +// +// assert.Implements(t, (*MyInterface)(nil), new(MyObject)) +func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + interfaceType := reflect.TypeOf(interfaceObject).Elem() + + if object == nil { + return Fail(t, fmt.Sprintf("Cannot check if nil implements %v", interfaceType), msgAndArgs...) + } + if !reflect.TypeOf(object).Implements(interfaceType) { + return Fail(t, fmt.Sprintf("%T must implement %v", object, interfaceType), msgAndArgs...) + } + + return true +} + +// IsType asserts that the specified objects are of the same type. +func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + if !ObjectsAreEqual(reflect.TypeOf(object), reflect.TypeOf(expectedType)) { + return Fail(t, fmt.Sprintf("Object expected to be of type %v, but was %v", reflect.TypeOf(expectedType), reflect.TypeOf(object)), msgAndArgs...) + } + + return true +} + +// Equal asserts that two objects are equal. +// +// assert.Equal(t, 123, 123) +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). Function equality +// cannot be determined and will always fail. +func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if err := validateEqualArgs(expected, actual); err != nil { + return Fail(t, fmt.Sprintf("Invalid operation: %#v == %#v (%s)", + expected, actual, err), msgAndArgs...) + } + + if !ObjectsAreEqual(expected, actual) { + diff := diff(expected, actual) + expected, actual = formatUnequalValues(expected, actual) + return Fail(t, fmt.Sprintf("Not equal: \n"+ + "expected: %s\n"+ + "actual : %s%s", expected, actual, diff), msgAndArgs...) + } + + return true + +} + +// formatUnequalValues takes two values of arbitrary types and returns string +// representations appropriate to be presented to the user. +// +// If the values are not of like type, the returned strings will be prefixed +// with the type name, and the value will be enclosed in parenthesis similar +// to a type conversion in the Go grammar. +func formatUnequalValues(expected, actual interface{}) (e string, a string) { + if reflect.TypeOf(expected) != reflect.TypeOf(actual) { + return fmt.Sprintf("%T(%#v)", expected, expected), + fmt.Sprintf("%T(%#v)", actual, actual) + } + + return fmt.Sprintf("%#v", expected), + fmt.Sprintf("%#v", actual) +} + +// EqualValues asserts that two objects are equal or convertable to the same types +// and equal. +// +// assert.EqualValues(t, uint32(123), int32(123)) +func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + if !ObjectsAreEqualValues(expected, actual) { + diff := diff(expected, actual) + expected, actual = formatUnequalValues(expected, actual) + return Fail(t, fmt.Sprintf("Not equal: \n"+ + "expected: %s\n"+ + "actual : %s%s", expected, actual, diff), msgAndArgs...) + } + + return true + +} + +// Exactly asserts that two objects are equal in value and type. +// +// assert.Exactly(t, int32(123), int64(123)) +func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + aType := reflect.TypeOf(expected) + bType := reflect.TypeOf(actual) + + if aType != bType { + return Fail(t, fmt.Sprintf("Types expected to match exactly\n\t%v != %v", aType, bType), msgAndArgs...) + } + + return Equal(t, expected, actual, msgAndArgs...) + +} + +// NotNil asserts that the specified object is not nil. +// +// assert.NotNil(t, err) +func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if !isNil(object) { + return true + } + return Fail(t, "Expected value not to be nil.", msgAndArgs...) +} + +// isNil checks if a specified object is nil or not, without Failing. +func isNil(object interface{}) bool { + if object == nil { + return true + } + + value := reflect.ValueOf(object) + kind := value.Kind() + if kind >= reflect.Chan && kind <= reflect.Slice && value.IsNil() { + return true + } + + return false +} + +// Nil asserts that the specified object is nil. +// +// assert.Nil(t, err) +func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if isNil(object) { + return true + } + return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...) +} + +// isEmpty gets whether the specified object is considered empty or not. +func isEmpty(object interface{}) bool { + + // get nil case out of the way + if object == nil { + return true + } + + objValue := reflect.ValueOf(object) + + switch objValue.Kind() { + // collection types are empty when they have no element + case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice: + return objValue.Len() == 0 + // pointers are empty if nil or if the value they point to is empty + case reflect.Ptr: + if objValue.IsNil() { + return true + } + deref := objValue.Elem().Interface() + return isEmpty(deref) + // for all other types, compare against the zero value + default: + zero := reflect.Zero(objValue.Type()) + return reflect.DeepEqual(object, zero.Interface()) + } +} + +// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// assert.Empty(t, obj) +func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + pass := isEmpty(object) + if !pass { + Fail(t, fmt.Sprintf("Should be empty, but was %v", object), msgAndArgs...) + } + + return pass + +} + +// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either +// a slice or a channel with len == 0. +// +// if assert.NotEmpty(t, obj) { +// assert.Equal(t, "two", obj[1]) +// } +func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + pass := !isEmpty(object) + if !pass { + Fail(t, fmt.Sprintf("Should NOT be empty, but was %v", object), msgAndArgs...) + } + + return pass + +} + +// getLen try to get length of object. +// return (false, 0) if impossible. +func getLen(x interface{}) (ok bool, length int) { + v := reflect.ValueOf(x) + defer func() { + if e := recover(); e != nil { + ok = false + } + }() + return true, v.Len() +} + +// Len asserts that the specified object has specific length. +// Len also fails if the object has a type that len() not accept. +// +// assert.Len(t, mySlice, 3) +func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + ok, l := getLen(object) + if !ok { + return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", object), msgAndArgs...) + } + + if l != length { + return Fail(t, fmt.Sprintf("\"%s\" should have %d item(s), but has %d", object, length, l), msgAndArgs...) + } + return true +} + +// True asserts that the specified value is true. +// +// assert.True(t, myBool) +func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if h, ok := t.(interface { + Helper() + }); ok { + h.Helper() + } + + if value != true { + return Fail(t, "Should be true", msgAndArgs...) + } + + return true + +} + +// False asserts that the specified value is false. +// +// assert.False(t, myBool) +func False(t TestingT, value bool, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + if value != false { + return Fail(t, "Should be false", msgAndArgs...) + } + + return true + +} + +// NotEqual asserts that the specified values are NOT equal. +// +// assert.NotEqual(t, obj1, obj2) +// +// Pointer variable equality is determined based on the equality of the +// referenced values (as opposed to the memory addresses). +func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if err := validateEqualArgs(expected, actual); err != nil { + return Fail(t, fmt.Sprintf("Invalid operation: %#v != %#v (%s)", + expected, actual, err), msgAndArgs...) + } + + if ObjectsAreEqual(expected, actual) { + return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...) + } + + return true + +} + +// containsElement try loop over the list check if the list includes the element. +// return (false, false) if impossible. +// return (true, false) if element was not found. +// return (true, true) if element was found. +func includeElement(list interface{}, element interface{}) (ok, found bool) { + + listValue := reflect.ValueOf(list) + elementValue := reflect.ValueOf(element) + defer func() { + if e := recover(); e != nil { + ok = false + found = false + } + }() + + if reflect.TypeOf(list).Kind() == reflect.String { + return true, strings.Contains(listValue.String(), elementValue.String()) + } + + if reflect.TypeOf(list).Kind() == reflect.Map { + mapKeys := listValue.MapKeys() + for i := 0; i < len(mapKeys); i++ { + if ObjectsAreEqual(mapKeys[i].Interface(), element) { + return true, true + } + } + return true, false + } + + for i := 0; i < listValue.Len(); i++ { + if ObjectsAreEqual(listValue.Index(i).Interface(), element) { + return true, true + } + } + return true, false + +} + +// Contains asserts that the specified string, list(array, slice...) or map contains the +// specified substring or element. +// +// assert.Contains(t, "Hello World", "World") +// assert.Contains(t, ["Hello", "World"], "World") +// assert.Contains(t, {"Hello": "World"}, "Hello") +func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + ok, found := includeElement(s, contains) + if !ok { + return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...) + } + if !found { + return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", s, contains), msgAndArgs...) + } + + return true + +} + +// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the +// specified substring or element. +// +// assert.NotContains(t, "Hello World", "Earth") +// assert.NotContains(t, ["Hello", "World"], "Earth") +// assert.NotContains(t, {"Hello": "World"}, "Earth") +func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + ok, found := includeElement(s, contains) + if !ok { + return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...) + } + if found { + return Fail(t, fmt.Sprintf("\"%s\" should not contain \"%s\"", s, contains), msgAndArgs...) + } + + return true + +} + +// Subset asserts that the specified list(array, slice...) contains all +// elements given in the specified subset(array, slice...). +// +// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if subset == nil { + return true // we consider nil to be equal to the nil set + } + + subsetValue := reflect.ValueOf(subset) + defer func() { + if e := recover(); e != nil { + ok = false + } + }() + + listKind := reflect.TypeOf(list).Kind() + subsetKind := reflect.TypeOf(subset).Kind() + + if listKind != reflect.Array && listKind != reflect.Slice { + return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...) + } + + if subsetKind != reflect.Array && subsetKind != reflect.Slice { + return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) + } + + for i := 0; i < subsetValue.Len(); i++ { + element := subsetValue.Index(i).Interface() + ok, found := includeElement(list, element) + if !ok { + return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...) + } + if !found { + return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", list, element), msgAndArgs...) + } + } + + return true +} + +// NotSubset asserts that the specified list(array, slice...) contains not all +// elements given in the specified subset(array, slice...). +// +// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if subset == nil { + return Fail(t, fmt.Sprintf("nil is the empty set which is a subset of every set"), msgAndArgs...) + } + + subsetValue := reflect.ValueOf(subset) + defer func() { + if e := recover(); e != nil { + ok = false + } + }() + + listKind := reflect.TypeOf(list).Kind() + subsetKind := reflect.TypeOf(subset).Kind() + + if listKind != reflect.Array && listKind != reflect.Slice { + return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...) + } + + if subsetKind != reflect.Array && subsetKind != reflect.Slice { + return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) + } + + for i := 0; i < subsetValue.Len(); i++ { + element := subsetValue.Index(i).Interface() + ok, found := includeElement(list, element) + if !ok { + return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...) + } + if !found { + return true + } + } + + return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...) +} + +// ElementsMatch asserts that the specified listA(array, slice...) is equal to specified +// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, +// the number of appearances of each of them in both lists should match. +// +// assert.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2]) +func ElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface{}) (ok bool) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if isEmpty(listA) && isEmpty(listB) { + return true + } + + aKind := reflect.TypeOf(listA).Kind() + bKind := reflect.TypeOf(listB).Kind() + + if aKind != reflect.Array && aKind != reflect.Slice { + return Fail(t, fmt.Sprintf("%q has an unsupported type %s", listA, aKind), msgAndArgs...) + } + + if bKind != reflect.Array && bKind != reflect.Slice { + return Fail(t, fmt.Sprintf("%q has an unsupported type %s", listB, bKind), msgAndArgs...) + } + + aValue := reflect.ValueOf(listA) + bValue := reflect.ValueOf(listB) + + aLen := aValue.Len() + bLen := bValue.Len() + + if aLen != bLen { + return Fail(t, fmt.Sprintf("lengths don't match: %d != %d", aLen, bLen), msgAndArgs...) + } + + // Mark indexes in bValue that we already used + visited := make([]bool, bLen) + for i := 0; i < aLen; i++ { + element := aValue.Index(i).Interface() + found := false + for j := 0; j < bLen; j++ { + if visited[j] { + continue + } + if ObjectsAreEqual(bValue.Index(j).Interface(), element) { + visited[j] = true + found = true + break + } + } + if !found { + return Fail(t, fmt.Sprintf("element %s appears more times in %s than in %s", element, aValue, bValue), msgAndArgs...) + } + } + + return true +} + +// Condition uses a Comparison to assert a complex condition. +func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + result := comp() + if !result { + Fail(t, "Condition failed!", msgAndArgs...) + } + return result +} + +// PanicTestFunc defines a func that should be passed to the assert.Panics and assert.NotPanics +// methods, and represents a simple func that takes no arguments, and returns nothing. +type PanicTestFunc func() + +// didPanic returns true if the function passed to it panics. Otherwise, it returns false. +func didPanic(f PanicTestFunc) (bool, interface{}) { + + didPanic := false + var message interface{} + func() { + + defer func() { + if message = recover(); message != nil { + didPanic = true + } + }() + + // call the target function + f() + + }() + + return didPanic, message + +} + +// Panics asserts that the code inside the specified PanicTestFunc panics. +// +// assert.Panics(t, func(){ GoCrazy() }) +func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + if funcDidPanic, panicValue := didPanic(f); !funcDidPanic { + return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) + } + + return true +} + +// PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that +// the recovered panic value equals the expected panic value. +// +// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) +func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + funcDidPanic, panicValue := didPanic(f) + if !funcDidPanic { + return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) + } + if panicValue != expected { + return Fail(t, fmt.Sprintf("func %#v should panic with value:\t%#v\n\tPanic value:\t%#v", f, expected, panicValue), msgAndArgs...) + } + + return true +} + +// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. +// +// assert.NotPanics(t, func(){ RemainCalm() }) +func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + if funcDidPanic, panicValue := didPanic(f); funcDidPanic { + return Fail(t, fmt.Sprintf("func %#v should not panic\n\tPanic value:\t%v", f, panicValue), msgAndArgs...) + } + + return true +} + +// WithinDuration asserts that the two times are within duration delta of each other. +// +// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) +func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + dt := expected.Sub(actual) + if dt < -delta || dt > delta { + return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...) + } + + return true +} + +func toFloat(x interface{}) (float64, bool) { + var xf float64 + xok := true + + switch xn := x.(type) { + case uint8: + xf = float64(xn) + case uint16: + xf = float64(xn) + case uint32: + xf = float64(xn) + case uint64: + xf = float64(xn) + case int: + xf = float64(xn) + case int8: + xf = float64(xn) + case int16: + xf = float64(xn) + case int32: + xf = float64(xn) + case int64: + xf = float64(xn) + case float32: + xf = float64(xn) + case float64: + xf = float64(xn) + case time.Duration: + xf = float64(xn) + default: + xok = false + } + + return xf, xok +} + +// InDelta asserts that the two numerals are within delta of each other. +// +// assert.InDelta(t, math.Pi, (22 / 7.0), 0.01) +func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + af, aok := toFloat(expected) + bf, bok := toFloat(actual) + + if !aok || !bok { + return Fail(t, fmt.Sprintf("Parameters must be numerical"), msgAndArgs...) + } + + if math.IsNaN(af) { + return Fail(t, fmt.Sprintf("Expected must not be NaN"), msgAndArgs...) + } + + if math.IsNaN(bf) { + return Fail(t, fmt.Sprintf("Expected %v with delta %v, but was NaN", expected, delta), msgAndArgs...) + } + + dt := af - bf + if dt < -delta || dt > delta { + return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...) + } + + return true +} + +// InDeltaSlice is the same as InDelta, except it compares two slices. +func InDeltaSlice(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if expected == nil || actual == nil || + reflect.TypeOf(actual).Kind() != reflect.Slice || + reflect.TypeOf(expected).Kind() != reflect.Slice { + return Fail(t, fmt.Sprintf("Parameters must be slice"), msgAndArgs...) + } + + actualSlice := reflect.ValueOf(actual) + expectedSlice := reflect.ValueOf(expected) + + for i := 0; i < actualSlice.Len(); i++ { + result := InDelta(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), delta, msgAndArgs...) + if !result { + return result + } + } + + return true +} + +// InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. +func InDeltaMapValues(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if expected == nil || actual == nil || + reflect.TypeOf(actual).Kind() != reflect.Map || + reflect.TypeOf(expected).Kind() != reflect.Map { + return Fail(t, "Arguments must be maps", msgAndArgs...) + } + + expectedMap := reflect.ValueOf(expected) + actualMap := reflect.ValueOf(actual) + + if expectedMap.Len() != actualMap.Len() { + return Fail(t, "Arguments must have the same number of keys", msgAndArgs...) + } + + for _, k := range expectedMap.MapKeys() { + ev := expectedMap.MapIndex(k) + av := actualMap.MapIndex(k) + + if !ev.IsValid() { + return Fail(t, fmt.Sprintf("missing key %q in expected map", k), msgAndArgs...) + } + + if !av.IsValid() { + return Fail(t, fmt.Sprintf("missing key %q in actual map", k), msgAndArgs...) + } + + if !InDelta( + t, + ev.Interface(), + av.Interface(), + delta, + msgAndArgs..., + ) { + return false + } + } + + return true +} + +func calcRelativeError(expected, actual interface{}) (float64, error) { + af, aok := toFloat(expected) + if !aok { + return 0, fmt.Errorf("expected value %q cannot be converted to float", expected) + } + if af == 0 { + return 0, fmt.Errorf("expected value must have a value other than zero to calculate the relative error") + } + bf, bok := toFloat(actual) + if !bok { + return 0, fmt.Errorf("actual value %q cannot be converted to float", actual) + } + + return math.Abs(af-bf) / math.Abs(af), nil +} + +// InEpsilon asserts that expected and actual have a relative error less than epsilon +func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + actualEpsilon, err := calcRelativeError(expected, actual) + if err != nil { + return Fail(t, err.Error(), msgAndArgs...) + } + if actualEpsilon > epsilon { + return Fail(t, fmt.Sprintf("Relative error is too high: %#v (expected)\n"+ + " < %#v (actual)", epsilon, actualEpsilon), msgAndArgs...) + } + + return true +} + +// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. +func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if expected == nil || actual == nil || + reflect.TypeOf(actual).Kind() != reflect.Slice || + reflect.TypeOf(expected).Kind() != reflect.Slice { + return Fail(t, fmt.Sprintf("Parameters must be slice"), msgAndArgs...) + } + + actualSlice := reflect.ValueOf(actual) + expectedSlice := reflect.ValueOf(expected) + + for i := 0; i < actualSlice.Len(); i++ { + result := InEpsilon(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), epsilon) + if !result { + return result + } + } + + return true +} + +/* + Errors +*/ + +// NoError asserts that a function returned no error (i.e. `nil`). +// +// actualObj, err := SomeFunction() +// if assert.NoError(t, err) { +// assert.Equal(t, expectedObj, actualObj) +// } +func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if err != nil { + return Fail(t, fmt.Sprintf("Received unexpected error:\n%+v", err), msgAndArgs...) + } + + return true +} + +// Error asserts that a function returned an error (i.e. not `nil`). +// +// actualObj, err := SomeFunction() +// if assert.Error(t, err) { +// assert.Equal(t, expectedError, err) +// } +func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + if err == nil { + return Fail(t, "An error is expected but got nil.", msgAndArgs...) + } + + return true +} + +// EqualError asserts that a function returned an error (i.e. not `nil`) +// and that it is equal to the provided error. +// +// actualObj, err := SomeFunction() +// assert.EqualError(t, err, expectedErrorString) +func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if !Error(t, theError, msgAndArgs...) { + return false + } + expected := errString + actual := theError.Error() + // don't need to use deep equals here, we know they are both strings + if expected != actual { + return Fail(t, fmt.Sprintf("Error message not equal:\n"+ + "expected: %q\n"+ + "actual : %q", expected, actual), msgAndArgs...) + } + return true +} + +// matchRegexp return true if a specified regexp matches a string. +func matchRegexp(rx interface{}, str interface{}) bool { + + var r *regexp.Regexp + if rr, ok := rx.(*regexp.Regexp); ok { + r = rr + } else { + r = regexp.MustCompile(fmt.Sprint(rx)) + } + + return (r.FindStringIndex(fmt.Sprint(str)) != nil) + +} + +// Regexp asserts that a specified regexp matches a string. +// +// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") +// assert.Regexp(t, "start...$", "it's not starting") +func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + match := matchRegexp(rx, str) + + if !match { + Fail(t, fmt.Sprintf("Expect \"%v\" to match \"%v\"", str, rx), msgAndArgs...) + } + + return match +} + +// NotRegexp asserts that a specified regexp does not match a string. +// +// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") +// assert.NotRegexp(t, "^start", "it's not starting") +func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + match := matchRegexp(rx, str) + + if match { + Fail(t, fmt.Sprintf("Expect \"%v\" to NOT match \"%v\"", str, rx), msgAndArgs...) + } + + return !match + +} + +// Zero asserts that i is the zero value for its type. +func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if i != nil && !reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) { + return Fail(t, fmt.Sprintf("Should be zero, but was %v", i), msgAndArgs...) + } + return true +} + +// NotZero asserts that i is not the zero value for its type. +func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if i == nil || reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) { + return Fail(t, fmt.Sprintf("Should not be zero, but was %v", i), msgAndArgs...) + } + return true +} + +// FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. +func FileExists(t TestingT, path string, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + info, err := os.Lstat(path) + if err != nil { + if os.IsNotExist(err) { + return Fail(t, fmt.Sprintf("unable to find file %q", path), msgAndArgs...) + } + return Fail(t, fmt.Sprintf("error when running os.Lstat(%q): %s", path, err), msgAndArgs...) + } + if info.IsDir() { + return Fail(t, fmt.Sprintf("%q is a directory", path), msgAndArgs...) + } + return true +} + +// DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. +func DirExists(t TestingT, path string, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + info, err := os.Lstat(path) + if err != nil { + if os.IsNotExist(err) { + return Fail(t, fmt.Sprintf("unable to find file %q", path), msgAndArgs...) + } + return Fail(t, fmt.Sprintf("error when running os.Lstat(%q): %s", path, err), msgAndArgs...) + } + if !info.IsDir() { + return Fail(t, fmt.Sprintf("%q is a file", path), msgAndArgs...) + } + return true +} + +// JSONEq asserts that two JSON strings are equivalent. +// +// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + var expectedJSONAsInterface, actualJSONAsInterface interface{} + + if err := json.Unmarshal([]byte(expected), &expectedJSONAsInterface); err != nil { + return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid json.\nJSON parsing error: '%s'", expected, err.Error()), msgAndArgs...) + } + + if err := json.Unmarshal([]byte(actual), &actualJSONAsInterface); err != nil { + return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid json.\nJSON parsing error: '%s'", actual, err.Error()), msgAndArgs...) + } + + return Equal(t, expectedJSONAsInterface, actualJSONAsInterface, msgAndArgs...) +} + +func typeAndKind(v interface{}) (reflect.Type, reflect.Kind) { + t := reflect.TypeOf(v) + k := t.Kind() + + if k == reflect.Ptr { + t = t.Elem() + k = t.Kind() + } + return t, k +} + +// diff returns a diff of both values as long as both are of the same type and +// are a struct, map, slice or array. Otherwise it returns an empty string. +func diff(expected interface{}, actual interface{}) string { + if expected == nil || actual == nil { + return "" + } + + et, ek := typeAndKind(expected) + at, _ := typeAndKind(actual) + + if et != at { + return "" + } + + if ek != reflect.Struct && ek != reflect.Map && ek != reflect.Slice && ek != reflect.Array && ek != reflect.String { + return "" + } + + var e, a string + if ek != reflect.String { + e = spewConfig.Sdump(expected) + a = spewConfig.Sdump(actual) + } else { + e = expected.(string) + a = actual.(string) + } + + diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ + A: difflib.SplitLines(e), + B: difflib.SplitLines(a), + FromFile: "Expected", + FromDate: "", + ToFile: "Actual", + ToDate: "", + Context: 1, + }) + + return "\n\nDiff:\n" + diff +} + +// validateEqualArgs checks whether provided arguments can be safely used in the +// Equal/NotEqual functions. +func validateEqualArgs(expected, actual interface{}) error { + if isFunction(expected) || isFunction(actual) { + return errors.New("cannot take func type as argument") + } + return nil +} + +func isFunction(arg interface{}) bool { + if arg == nil { + return false + } + return reflect.TypeOf(arg).Kind() == reflect.Func +} + +var spewConfig = spew.ConfigState{ + Indent: " ", + DisablePointerAddresses: true, + DisableCapacities: true, + SortKeys: true, +} + +type tHelper interface { + Helper() +} diff --git a/vendor/github.com/stretchr/testify/assert/doc.go b/vendor/github.com/stretchr/testify/assert/doc.go new file mode 100644 index 0000000..c9dccc4 --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/doc.go @@ -0,0 +1,45 @@ +// Package assert provides a set of comprehensive testing tools for use with the normal Go testing system. +// +// Example Usage +// +// The following is a complete example using assert in a standard test function: +// import ( +// "testing" +// "github.com/stretchr/testify/assert" +// ) +// +// func TestSomething(t *testing.T) { +// +// var a string = "Hello" +// var b string = "Hello" +// +// assert.Equal(t, a, b, "The two words should be the same.") +// +// } +// +// if you assert many times, use the format below: +// +// import ( +// "testing" +// "github.com/stretchr/testify/assert" +// ) +// +// func TestSomething(t *testing.T) { +// assert := assert.New(t) +// +// var a string = "Hello" +// var b string = "Hello" +// +// assert.Equal(a, b, "The two words should be the same.") +// } +// +// Assertions +// +// Assertions allow you to easily write test code, and are global funcs in the `assert` package. +// All assertion functions take, as the first argument, the `*testing.T` object provided by the +// testing framework. This allows the assertion funcs to write the failings and other details to +// the correct place. +// +// Every assertion function also takes an optional string message as the final argument, +// allowing custom error messages to be appended to the message the assertion method outputs. +package assert diff --git a/vendor/github.com/stretchr/testify/assert/errors.go b/vendor/github.com/stretchr/testify/assert/errors.go new file mode 100644 index 0000000..ac9dc9d --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/errors.go @@ -0,0 +1,10 @@ +package assert + +import ( + "errors" +) + +// AnError is an error instance useful for testing. If the code does not care +// about error specifics, and only needs to return the error for example, this +// error should be used to make the test code more readable. +var AnError = errors.New("assert.AnError general error for testing") diff --git a/vendor/github.com/stretchr/testify/assert/forward_assertions.go b/vendor/github.com/stretchr/testify/assert/forward_assertions.go new file mode 100644 index 0000000..9ad5685 --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/forward_assertions.go @@ -0,0 +1,16 @@ +package assert + +// Assertions provides assertion methods around the +// TestingT interface. +type Assertions struct { + t TestingT +} + +// New makes a new Assertions object for the specified TestingT. +func New(t TestingT) *Assertions { + return &Assertions{ + t: t, + } +} + +//go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_forward.go.tmpl -include-format-funcs diff --git a/vendor/github.com/stretchr/testify/assert/http_assertions.go b/vendor/github.com/stretchr/testify/assert/http_assertions.go new file mode 100644 index 0000000..df46fa7 --- /dev/null +++ b/vendor/github.com/stretchr/testify/assert/http_assertions.go @@ -0,0 +1,143 @@ +package assert + +import ( + "fmt" + "net/http" + "net/http/httptest" + "net/url" + "strings" +) + +// httpCode is a helper that returns HTTP code of the response. It returns -1 and +// an error if building a new request fails. +func httpCode(handler http.HandlerFunc, method, url string, values url.Values) (int, error) { + w := httptest.NewRecorder() + req, err := http.NewRequest(method, url, nil) + if err != nil { + return -1, err + } + req.URL.RawQuery = values.Encode() + handler(w, req) + return w.Code, nil +} + +// HTTPSuccess asserts that a specified handler returns a success status code. +// +// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + code, err := httpCode(handler, method, url, values) + if err != nil { + Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) + return false + } + + isSuccessCode := code >= http.StatusOK && code <= http.StatusPartialContent + if !isSuccessCode { + Fail(t, fmt.Sprintf("Expected HTTP success status code for %q but received %d", url+"?"+values.Encode(), code)) + } + + return isSuccessCode +} + +// HTTPRedirect asserts that a specified handler returns a redirect status code. +// +// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + code, err := httpCode(handler, method, url, values) + if err != nil { + Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) + return false + } + + isRedirectCode := code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect + if !isRedirectCode { + Fail(t, fmt.Sprintf("Expected HTTP redirect status code for %q but received %d", url+"?"+values.Encode(), code)) + } + + return isRedirectCode +} + +// HTTPError asserts that a specified handler returns an error status code. +// +// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + code, err := httpCode(handler, method, url, values) + if err != nil { + Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) + return false + } + + isErrorCode := code >= http.StatusBadRequest + if !isErrorCode { + Fail(t, fmt.Sprintf("Expected HTTP error status code for %q but received %d", url+"?"+values.Encode(), code)) + } + + return isErrorCode +} + +// HTTPBody is a helper that returns HTTP body of the response. It returns +// empty string if building a new request fails. +func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string { + w := httptest.NewRecorder() + req, err := http.NewRequest(method, url+"?"+values.Encode(), nil) + if err != nil { + return "" + } + handler(w, req) + return w.Body.String() +} + +// HTTPBodyContains asserts that a specified handler returns a +// body that contains a string. +// +// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + body := HTTPBody(handler, method, url, values) + + contains := strings.Contains(body, fmt.Sprint(str)) + if !contains { + Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body)) + } + + return contains +} + +// HTTPBodyNotContains asserts that a specified handler returns a +// body that does not contain a string. +// +// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// +// Returns whether the assertion was successful (true) or not (false). +func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + body := HTTPBody(handler, method, url, values) + + contains := strings.Contains(body, fmt.Sprint(str)) + if contains { + Fail(t, fmt.Sprintf("Expected response body for \"%s\" to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body)) + } + + return !contains +} diff --git a/vendor/golang.org/x/net/AUTHORS b/vendor/golang.org/x/net/AUTHORS new file mode 100644 index 0000000..15167cd --- /dev/null +++ b/vendor/golang.org/x/net/AUTHORS @@ -0,0 +1,3 @@ +# This source code refers to The Go Authors for copyright purposes. +# The master list of authors is in the main Go distribution, +# visible at http://tip.golang.org/AUTHORS. diff --git a/vendor/golang.org/x/net/CONTRIBUTORS b/vendor/golang.org/x/net/CONTRIBUTORS new file mode 100644 index 0000000..1c4577e --- /dev/null +++ b/vendor/golang.org/x/net/CONTRIBUTORS @@ -0,0 +1,3 @@ +# This source code was written by the Go contributors. +# The master list of contributors is in the main Go distribution, +# visible at http://tip.golang.org/CONTRIBUTORS. diff --git a/vendor/golang.org/x/net/LICENSE b/vendor/golang.org/x/net/LICENSE new file mode 100644 index 0000000..6a66aea --- /dev/null +++ b/vendor/golang.org/x/net/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/golang.org/x/net/PATENTS b/vendor/golang.org/x/net/PATENTS new file mode 100644 index 0000000..7330990 --- /dev/null +++ b/vendor/golang.org/x/net/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/vendor/golang.org/x/net/context/context.go b/vendor/golang.org/x/net/context/context.go new file mode 100644 index 0000000..a3c021d --- /dev/null +++ b/vendor/golang.org/x/net/context/context.go @@ -0,0 +1,56 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package context defines the Context type, which carries deadlines, +// cancelation signals, and other request-scoped values across API boundaries +// and between processes. +// As of Go 1.7 this package is available in the standard library under the +// name context. https://golang.org/pkg/context. +// +// Incoming requests to a server should create a Context, and outgoing calls to +// servers should accept a Context. The chain of function calls between must +// propagate the Context, optionally replacing it with a modified copy created +// using WithDeadline, WithTimeout, WithCancel, or WithValue. +// +// Programs that use Contexts should follow these rules to keep interfaces +// consistent across packages and enable static analysis tools to check context +// propagation: +// +// Do not store Contexts inside a struct type; instead, pass a Context +// explicitly to each function that needs it. The Context should be the first +// parameter, typically named ctx: +// +// func DoSomething(ctx context.Context, arg Arg) error { +// // ... use ctx ... +// } +// +// Do not pass a nil Context, even if a function permits it. Pass context.TODO +// if you are unsure about which Context to use. +// +// Use context Values only for request-scoped data that transits processes and +// APIs, not for passing optional parameters to functions. +// +// The same Context may be passed to functions running in different goroutines; +// Contexts are safe for simultaneous use by multiple goroutines. +// +// See http://blog.golang.org/context for example code for a server that uses +// Contexts. +package context // import "golang.org/x/net/context" + +// Background returns a non-nil, empty Context. It is never canceled, has no +// values, and has no deadline. It is typically used by the main function, +// initialization, and tests, and as the top-level Context for incoming +// requests. +func Background() Context { + return background +} + +// TODO returns a non-nil, empty Context. Code should use context.TODO when +// it's unclear which Context to use or it is not yet available (because the +// surrounding function has not yet been extended to accept a Context +// parameter). TODO is recognized by static analysis tools that determine +// whether Contexts are propagated correctly in a program. +func TODO() Context { + return todo +} diff --git a/vendor/golang.org/x/net/context/go17.go b/vendor/golang.org/x/net/context/go17.go new file mode 100644 index 0000000..d20f52b --- /dev/null +++ b/vendor/golang.org/x/net/context/go17.go @@ -0,0 +1,72 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.7 + +package context + +import ( + "context" // standard library's context, as of Go 1.7 + "time" +) + +var ( + todo = context.TODO() + background = context.Background() +) + +// Canceled is the error returned by Context.Err when the context is canceled. +var Canceled = context.Canceled + +// DeadlineExceeded is the error returned by Context.Err when the context's +// deadline passes. +var DeadlineExceeded = context.DeadlineExceeded + +// WithCancel returns a copy of parent with a new Done channel. The returned +// context's Done channel is closed when the returned cancel function is called +// or when the parent context's Done channel is closed, whichever happens first. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { + ctx, f := context.WithCancel(parent) + return ctx, CancelFunc(f) +} + +// WithDeadline returns a copy of the parent context with the deadline adjusted +// to be no later than d. If the parent's deadline is already earlier than d, +// WithDeadline(parent, d) is semantically equivalent to parent. The returned +// context's Done channel is closed when the deadline expires, when the returned +// cancel function is called, or when the parent context's Done channel is +// closed, whichever happens first. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) { + ctx, f := context.WithDeadline(parent, deadline) + return ctx, CancelFunc(f) +} + +// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)). +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete: +// +// func slowOperationWithTimeout(ctx context.Context) (Result, error) { +// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) +// defer cancel() // releases resources if slowOperation completes before timeout elapses +// return slowOperation(ctx) +// } +func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { + return WithDeadline(parent, time.Now().Add(timeout)) +} + +// WithValue returns a copy of parent in which the value associated with key is +// val. +// +// Use context Values only for request-scoped data that transits processes and +// APIs, not for passing optional parameters to functions. +func WithValue(parent Context, key interface{}, val interface{}) Context { + return context.WithValue(parent, key, val) +} diff --git a/vendor/golang.org/x/net/context/go19.go b/vendor/golang.org/x/net/context/go19.go new file mode 100644 index 0000000..d88bd1d --- /dev/null +++ b/vendor/golang.org/x/net/context/go19.go @@ -0,0 +1,20 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.9 + +package context + +import "context" // standard library's context, as of Go 1.7 + +// A Context carries a deadline, a cancelation signal, and other values across +// API boundaries. +// +// Context's methods may be called by multiple goroutines simultaneously. +type Context = context.Context + +// A CancelFunc tells an operation to abandon its work. +// A CancelFunc does not wait for the work to stop. +// After the first call, subsequent calls to a CancelFunc do nothing. +type CancelFunc = context.CancelFunc diff --git a/vendor/golang.org/x/net/context/pre_go17.go b/vendor/golang.org/x/net/context/pre_go17.go new file mode 100644 index 0000000..0f35592 --- /dev/null +++ b/vendor/golang.org/x/net/context/pre_go17.go @@ -0,0 +1,300 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.7 + +package context + +import ( + "errors" + "fmt" + "sync" + "time" +) + +// An emptyCtx is never canceled, has no values, and has no deadline. It is not +// struct{}, since vars of this type must have distinct addresses. +type emptyCtx int + +func (*emptyCtx) Deadline() (deadline time.Time, ok bool) { + return +} + +func (*emptyCtx) Done() <-chan struct{} { + return nil +} + +func (*emptyCtx) Err() error { + return nil +} + +func (*emptyCtx) Value(key interface{}) interface{} { + return nil +} + +func (e *emptyCtx) String() string { + switch e { + case background: + return "context.Background" + case todo: + return "context.TODO" + } + return "unknown empty Context" +} + +var ( + background = new(emptyCtx) + todo = new(emptyCtx) +) + +// Canceled is the error returned by Context.Err when the context is canceled. +var Canceled = errors.New("context canceled") + +// DeadlineExceeded is the error returned by Context.Err when the context's +// deadline passes. +var DeadlineExceeded = errors.New("context deadline exceeded") + +// WithCancel returns a copy of parent with a new Done channel. The returned +// context's Done channel is closed when the returned cancel function is called +// or when the parent context's Done channel is closed, whichever happens first. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { + c := newCancelCtx(parent) + propagateCancel(parent, c) + return c, func() { c.cancel(true, Canceled) } +} + +// newCancelCtx returns an initialized cancelCtx. +func newCancelCtx(parent Context) *cancelCtx { + return &cancelCtx{ + Context: parent, + done: make(chan struct{}), + } +} + +// propagateCancel arranges for child to be canceled when parent is. +func propagateCancel(parent Context, child canceler) { + if parent.Done() == nil { + return // parent is never canceled + } + if p, ok := parentCancelCtx(parent); ok { + p.mu.Lock() + if p.err != nil { + // parent has already been canceled + child.cancel(false, p.err) + } else { + if p.children == nil { + p.children = make(map[canceler]bool) + } + p.children[child] = true + } + p.mu.Unlock() + } else { + go func() { + select { + case <-parent.Done(): + child.cancel(false, parent.Err()) + case <-child.Done(): + } + }() + } +} + +// parentCancelCtx follows a chain of parent references until it finds a +// *cancelCtx. This function understands how each of the concrete types in this +// package represents its parent. +func parentCancelCtx(parent Context) (*cancelCtx, bool) { + for { + switch c := parent.(type) { + case *cancelCtx: + return c, true + case *timerCtx: + return c.cancelCtx, true + case *valueCtx: + parent = c.Context + default: + return nil, false + } + } +} + +// removeChild removes a context from its parent. +func removeChild(parent Context, child canceler) { + p, ok := parentCancelCtx(parent) + if !ok { + return + } + p.mu.Lock() + if p.children != nil { + delete(p.children, child) + } + p.mu.Unlock() +} + +// A canceler is a context type that can be canceled directly. The +// implementations are *cancelCtx and *timerCtx. +type canceler interface { + cancel(removeFromParent bool, err error) + Done() <-chan struct{} +} + +// A cancelCtx can be canceled. When canceled, it also cancels any children +// that implement canceler. +type cancelCtx struct { + Context + + done chan struct{} // closed by the first cancel call. + + mu sync.Mutex + children map[canceler]bool // set to nil by the first cancel call + err error // set to non-nil by the first cancel call +} + +func (c *cancelCtx) Done() <-chan struct{} { + return c.done +} + +func (c *cancelCtx) Err() error { + c.mu.Lock() + defer c.mu.Unlock() + return c.err +} + +func (c *cancelCtx) String() string { + return fmt.Sprintf("%v.WithCancel", c.Context) +} + +// cancel closes c.done, cancels each of c's children, and, if +// removeFromParent is true, removes c from its parent's children. +func (c *cancelCtx) cancel(removeFromParent bool, err error) { + if err == nil { + panic("context: internal error: missing cancel error") + } + c.mu.Lock() + if c.err != nil { + c.mu.Unlock() + return // already canceled + } + c.err = err + close(c.done) + for child := range c.children { + // NOTE: acquiring the child's lock while holding parent's lock. + child.cancel(false, err) + } + c.children = nil + c.mu.Unlock() + + if removeFromParent { + removeChild(c.Context, c) + } +} + +// WithDeadline returns a copy of the parent context with the deadline adjusted +// to be no later than d. If the parent's deadline is already earlier than d, +// WithDeadline(parent, d) is semantically equivalent to parent. The returned +// context's Done channel is closed when the deadline expires, when the returned +// cancel function is called, or when the parent context's Done channel is +// closed, whichever happens first. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) { + if cur, ok := parent.Deadline(); ok && cur.Before(deadline) { + // The current deadline is already sooner than the new one. + return WithCancel(parent) + } + c := &timerCtx{ + cancelCtx: newCancelCtx(parent), + deadline: deadline, + } + propagateCancel(parent, c) + d := deadline.Sub(time.Now()) + if d <= 0 { + c.cancel(true, DeadlineExceeded) // deadline has already passed + return c, func() { c.cancel(true, Canceled) } + } + c.mu.Lock() + defer c.mu.Unlock() + if c.err == nil { + c.timer = time.AfterFunc(d, func() { + c.cancel(true, DeadlineExceeded) + }) + } + return c, func() { c.cancel(true, Canceled) } +} + +// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to +// implement Done and Err. It implements cancel by stopping its timer then +// delegating to cancelCtx.cancel. +type timerCtx struct { + *cancelCtx + timer *time.Timer // Under cancelCtx.mu. + + deadline time.Time +} + +func (c *timerCtx) Deadline() (deadline time.Time, ok bool) { + return c.deadline, true +} + +func (c *timerCtx) String() string { + return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, c.deadline.Sub(time.Now())) +} + +func (c *timerCtx) cancel(removeFromParent bool, err error) { + c.cancelCtx.cancel(false, err) + if removeFromParent { + // Remove this timerCtx from its parent cancelCtx's children. + removeChild(c.cancelCtx.Context, c) + } + c.mu.Lock() + if c.timer != nil { + c.timer.Stop() + c.timer = nil + } + c.mu.Unlock() +} + +// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)). +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete: +// +// func slowOperationWithTimeout(ctx context.Context) (Result, error) { +// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) +// defer cancel() // releases resources if slowOperation completes before timeout elapses +// return slowOperation(ctx) +// } +func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { + return WithDeadline(parent, time.Now().Add(timeout)) +} + +// WithValue returns a copy of parent in which the value associated with key is +// val. +// +// Use context Values only for request-scoped data that transits processes and +// APIs, not for passing optional parameters to functions. +func WithValue(parent Context, key interface{}, val interface{}) Context { + return &valueCtx{parent, key, val} +} + +// A valueCtx carries a key-value pair. It implements Value for that key and +// delegates all other calls to the embedded Context. +type valueCtx struct { + Context + key, val interface{} +} + +func (c *valueCtx) String() string { + return fmt.Sprintf("%v.WithValue(%#v, %#v)", c.Context, c.key, c.val) +} + +func (c *valueCtx) Value(key interface{}) interface{} { + if c.key == key { + return c.val + } + return c.Context.Value(key) +} diff --git a/vendor/golang.org/x/net/context/pre_go19.go b/vendor/golang.org/x/net/context/pre_go19.go new file mode 100644 index 0000000..b105f80 --- /dev/null +++ b/vendor/golang.org/x/net/context/pre_go19.go @@ -0,0 +1,109 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.9 + +package context + +import "time" + +// A Context carries a deadline, a cancelation signal, and other values across +// API boundaries. +// +// Context's methods may be called by multiple goroutines simultaneously. +type Context interface { + // Deadline returns the time when work done on behalf of this context + // should be canceled. Deadline returns ok==false when no deadline is + // set. Successive calls to Deadline return the same results. + Deadline() (deadline time.Time, ok bool) + + // Done returns a channel that's closed when work done on behalf of this + // context should be canceled. Done may return nil if this context can + // never be canceled. Successive calls to Done return the same value. + // + // WithCancel arranges for Done to be closed when cancel is called; + // WithDeadline arranges for Done to be closed when the deadline + // expires; WithTimeout arranges for Done to be closed when the timeout + // elapses. + // + // Done is provided for use in select statements: + // + // // Stream generates values with DoSomething and sends them to out + // // until DoSomething returns an error or ctx.Done is closed. + // func Stream(ctx context.Context, out chan<- Value) error { + // for { + // v, err := DoSomething(ctx) + // if err != nil { + // return err + // } + // select { + // case <-ctx.Done(): + // return ctx.Err() + // case out <- v: + // } + // } + // } + // + // See http://blog.golang.org/pipelines for more examples of how to use + // a Done channel for cancelation. + Done() <-chan struct{} + + // Err returns a non-nil error value after Done is closed. Err returns + // Canceled if the context was canceled or DeadlineExceeded if the + // context's deadline passed. No other values for Err are defined. + // After Done is closed, successive calls to Err return the same value. + Err() error + + // Value returns the value associated with this context for key, or nil + // if no value is associated with key. Successive calls to Value with + // the same key returns the same result. + // + // Use context values only for request-scoped data that transits + // processes and API boundaries, not for passing optional parameters to + // functions. + // + // A key identifies a specific value in a Context. Functions that wish + // to store values in Context typically allocate a key in a global + // variable then use that key as the argument to context.WithValue and + // Context.Value. A key can be any type that supports equality; + // packages should define keys as an unexported type to avoid + // collisions. + // + // Packages that define a Context key should provide type-safe accessors + // for the values stores using that key: + // + // // Package user defines a User type that's stored in Contexts. + // package user + // + // import "golang.org/x/net/context" + // + // // User is the type of value stored in the Contexts. + // type User struct {...} + // + // // key is an unexported type for keys defined in this package. + // // This prevents collisions with keys defined in other packages. + // type key int + // + // // userKey is the key for user.User values in Contexts. It is + // // unexported; clients use user.NewContext and user.FromContext + // // instead of using this key directly. + // var userKey key = 0 + // + // // NewContext returns a new Context that carries value u. + // func NewContext(ctx context.Context, u *User) context.Context { + // return context.WithValue(ctx, userKey, u) + // } + // + // // FromContext returns the User value stored in ctx, if any. + // func FromContext(ctx context.Context) (*User, bool) { + // u, ok := ctx.Value(userKey).(*User) + // return u, ok + // } + Value(key interface{}) interface{} +} + +// A CancelFunc tells an operation to abandon its work. +// A CancelFunc does not wait for the work to stop. +// After the first call, subsequent calls to a CancelFunc do nothing. +type CancelFunc func() diff --git a/vendor/modules.txt b/vendor/modules.txt index 192552a..aabaf28 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,11 +1,9 @@ -# github.com/cloud-ca/go-cloudca v1.4.0 -github.com/cloud-ca/go-cloudca -github.com/cloud-ca/go-cloudca/api -github.com/cloud-ca/go-cloudca/configuration -github.com/cloud-ca/go-cloudca/services -github.com/cloud-ca/go-cloudca/services/cloudca +# github.com/davecgh/go-spew v1.1.1 +github.com/davecgh/go-spew/spew # github.com/fsnotify/fsnotify v1.4.7 github.com/fsnotify/fsnotify +# github.com/golang/mock v1.1.1 +github.com/golang/mock/gomock # github.com/hashicorp/hcl v1.0.0 github.com/hashicorp/hcl github.com/hashicorp/hcl/hcl/printer @@ -29,6 +27,8 @@ github.com/magiconair/properties github.com/mitchellh/mapstructure # github.com/pelletier/go-toml v1.2.0 github.com/pelletier/go-toml +# github.com/pmezard/go-difflib v1.0.0 +github.com/pmezard/go-difflib/difflib # github.com/sirupsen/logrus v1.4.2 github.com/sirupsen/logrus # github.com/spf13/afero v1.1.2 @@ -44,10 +44,14 @@ github.com/spf13/jwalterweatherman github.com/spf13/pflag # github.com/spf13/viper v1.4.0 github.com/spf13/viper +# github.com/stretchr/testify v1.2.2 +github.com/stretchr/testify/assert # github.com/tidwall/pretty v1.0.0 github.com/tidwall/pretty # golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 golang.org/x/crypto/ssh/terminal +# golang.org/x/net v0.0.0-20190522155817-f3200d17e092 +golang.org/x/net/context # golang.org/x/sys v0.0.0-20190422165155-953cdadca894 golang.org/x/sys/unix golang.org/x/sys/windows