test: mock the API requests for the huawei registry (#15009)

Signed-off-by: He Weiwei <hweiwei@vmware.com>
This commit is contained in:
He Weiwei 2021-05-28 17:09:48 +08:00 committed by GitHub
parent 77b44a62e7
commit c7a4af3170
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 3278 additions and 47 deletions

View File

@ -82,6 +82,11 @@ type Client struct {
client *http.Client
}
// GetClient returns the http.Client
func (c *Client) GetClient() *http.Client {
return c.client
}
// GetHTTPTransport returns HttpTransport based on insecure configuration
func GetHTTPTransport(clientType uint) *http.Transport {
switch clientType {

View File

@ -73,6 +73,7 @@ require (
gopkg.in/dancannon/gorethink.v3 v3.0.5 // indirect
gopkg.in/fatih/pool.v2 v2.0.0 // indirect
gopkg.in/gorethink/gorethink.v3 v3.0.5 // indirect
gopkg.in/h2non/gock.v1 v1.0.16
gopkg.in/yaml.v2 v2.3.0
helm.sh/helm/v3 v3.4.2
k8s.io/api v0.19.4

View File

@ -535,6 +535,8 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
@ -736,6 +738,7 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
github.com/ncw/swift v1.0.49 h1:eQaKIjSt/PXLKfYgzg01nevmO+CMXfXGRhB1gOhDs7E=
github.com/ncw/swift v1.0.49/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
@ -992,9 +995,9 @@ go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/ratelimit v0.2.0 h1:UQE2Bgi7p2B85uP5dC2bbRtig0C+OeNRnNEafLjsLPA=
go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
@ -1076,6 +1079,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191021144547-ec77196f6094/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -1285,6 +1289,9 @@ gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKW
gopkg.in/gorethink/gorethink.v3 v3.0.5 h1:e2Uc/Xe+hpcVQFsj6MuHlYog3r0JYpnTzwDj/y2O4MU=
gopkg.in/gorethink/gorethink.v3 v3.0.5/go.mod h1:+3yIIHJUGMBK+wyPH+iN5TP+88ikFDfZdqTlK3Y9q8I=
gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw=
gopkg.in/h2non/gentleman.v1 v1.0.4/go.mod h1:JYuHVdFzS4MKOXe0o+chKJ4hCe6tqKKw9XH9YP6WFrg=
gopkg.in/h2non/gock.v1 v1.0.16 h1:F11k+OafeuFENsjei5t2vMTSTs9L62AdyTe4E1cgdG8=
gopkg.in/h2non/gock.v1 v1.0.16/go.mod h1:XVuDAssexPLwgxCLMvDTWNU5eqklsydR6I5phZ9oPB8=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=

View File

@ -1,14 +1,29 @@
// Copyright Project Harbor 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 huawei
import (
"encoding/json"
"fmt"
"github.com/goharbor/harbor/src/pkg/registry/auth/basic"
"io/ioutil"
"net/http"
"regexp"
"strings"
"github.com/goharbor/harbor/src/pkg/registry/auth/basic"
common_http "github.com/goharbor/harbor/src/common/http"
"github.com/goharbor/harbor/src/common/http/modifier"
"github.com/goharbor/harbor/src/lib/log"

View File

@ -1,12 +1,27 @@
// Copyright Project Harbor 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 huawei
import (
"os"
"strings"
"testing"
adp "github.com/goharbor/harbor/src/pkg/reg/adapter"
"github.com/goharbor/harbor/src/pkg/reg/model"
"github.com/stretchr/testify/assert"
gock "gopkg.in/h2non/gock.v1"
)
var hwAdapter adp.Adapter
@ -29,6 +44,9 @@ func init() {
os.Exit(1)
}
a := hwAdapter.(*adapter)
gock.InterceptClient(a.client.GetClient())
gock.InterceptClient(a.oriClient)
}
func TestAdapter_Info(t *testing.T) {
@ -40,6 +58,15 @@ func TestAdapter_Info(t *testing.T) {
}
func TestAdapter_PrepareForPush(t *testing.T) {
defer gock.Off()
gock.Observe(gock.DumpRequest)
mockRequest().Get("/dockyard/v2/namespaces/domain_repo_new").
Reply(200).BodyString("{}")
mockRequest().Post("/dockyard/v2/namespaces").BodyString(`{"namespace":"domain_repo_new"}`).
Reply(200)
repository := &model.Repository{
Name: "domain_repo_new",
Metadata: make(map[string]interface{}),
@ -50,18 +77,13 @@ func TestAdapter_PrepareForPush(t *testing.T) {
}
resource.Metadata = metadata
err := hwAdapter.PrepareForPush([]*model.Resource{resource})
if err != nil {
if strings.HasPrefix(err.Error(), "[401]") {
t.Log("huawei ak/sk is not available", err.Error())
} else {
t.Error(err)
}
} else {
t.Log("success prepare for push")
}
assert.NoError(t, err)
}
func TestAdapter_HealthCheck(t *testing.T) {
defer gock.Off()
gock.Observe(gock.DumpRequest)
health, err := hwAdapter.HealthCheck()
if err != nil {
t.Error(err)

View File

@ -1,14 +1,29 @@
// Copyright Project Harbor 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 huawei
import (
"encoding/json"
"fmt"
"github.com/docker/distribution"
"github.com/goharbor/harbor/src/pkg/reg/model"
"io/ioutil"
"net/http"
"strconv"
"time"
"github.com/docker/distribution"
"github.com/goharbor/harbor/src/pkg/reg/model"
)
// FetchArtifacts gets resources from Huawei SWR

View File

@ -1,11 +1,28 @@
// Copyright Project Harbor 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 huawei
import (
"fmt"
"os"
"strings"
"testing"
"github.com/docker/distribution"
"github.com/goharbor/harbor/src/pkg/reg/model"
"github.com/stretchr/testify/assert"
gock "gopkg.in/h2non/gock.v1"
)
var HWAdapter adapter
@ -26,47 +43,64 @@ func init() {
os.Exit(1)
}
HWAdapter = *adp.(*adapter)
gock.InterceptClient(HWAdapter.client.GetClient())
gock.InterceptClient(HWAdapter.oriClient)
}
func mockRequest() *gock.Request {
return gock.New("https://swr.cn-north-1.myhuaweicloud.com")
}
func mockGetJwtToken(repository string) {
mockRequest().Get("/swr/auth/v2/registry/auth").
MatchParam("scope", fmt.Sprintf("repository:%s:push,pull", repository)).
Reply(200).
JSON(jwtToken{
Token: "token",
})
}
func TestAdapter_FetchArtifacts(t *testing.T) {
defer gock.Off()
gock.Observe(gock.DumpRequest)
mockRequest().Get("/dockyard/v2/repositories").MatchParam("filter", "center::self").
BasicAuth("cn-north-1@IJYZLFBKBFN8LOUITAH", "f31e8e2b948265afdae32e83722a7705fd43e154585ff69e64108247750e5d").
Reply(200).
JSON([]hwRepoQueryResult{
{Name: "name1"},
{Name: "name2"},
})
resources, err := HWAdapter.FetchArtifacts(nil)
if err != nil {
if strings.HasPrefix(err.Error(), "[401]") {
t.Log("huawei ak/sk is not available", err.Error())
} else {
t.Error(err)
}
} else {
for _, resource := range resources {
t.Log(*resource)
}
}
assert.NoError(t, err)
assert.Len(t, resources, 2)
}
func TestAdapter_ManifestExist(t *testing.T) {
exist, desc, err := HWAdapter.ManifestExist("", "")
if err != nil {
if strings.HasPrefix(err.Error(), "[401]") {
t.Log("huawei ak/sk is not available", err.Error())
} else {
t.Error(err)
}
} else {
if exist {
t.Log(desc.Digest)
}
}
defer gock.Off()
gock.Observe(gock.DumpRequest)
mockGetJwtToken("sundaymango_mango/hello-world")
mockRequest().Get("/v2/sundaymango_mango/hello-world/manifests/latest").
Reply(200).
JSON(hwManifest{
MediaType: distribution.ManifestMediaTypes()[0],
})
exist, _, err := HWAdapter.ManifestExist("sundaymango_mango/hello-world", "latest")
assert.NoError(t, err)
assert.True(t, exist)
}
func TestAdapter_DeleteManifest(t *testing.T) {
defer gock.Off()
gock.Observe(gock.DumpRequest)
mockGetJwtToken("sundaymango_mango/hello-world")
mockRequest().Delete("/v2/sundaymango_mango/hello-world/manifests/latest").Reply(200)
err := HWAdapter.DeleteManifest("sundaymango_mango/hello-world", "latest")
if err != nil {
if strings.HasPrefix(err.Error(), "[401]") {
t.Log("huawei ak/sk is not available", err.Error())
} else {
t.Error(err)
}
} else {
t.Error("the manifest is deleted")
}
assert.NoError(t, err)
}

22
src/vendor/github.com/h2non/parth/LICENSE generated vendored Normal file
View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2018 codemodus
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.

196
src/vendor/github.com/h2non/parth/README.md generated vendored Normal file
View File

@ -0,0 +1,196 @@
# parth
go get -u github.com/h2non/parth
Package parth provides path parsing for segment unmarshaling and slicing. In
other words, parth provides simple and flexible access to (URL) path parameters.
Along with string, all basic non-alias types are supported. An interface is
available for implementation by user-defined types. When handling an int, uint,
or float of any size, the first valid value within the specified segment will be
used.
## Usage
```go
Variables
func Segment(path string, i int, v interface{}) error
func Sequent(path, key string, v interface{}) error
func Span(path string, i, j int) (string, error)
func SubSeg(path, key string, i int, v interface{}) error
func SubSpan(path, key string, i, j int) (string, error)
type Parth
func New(path string) *Parth
func NewBySpan(path string, i, j int) *Parth
func NewBySubSpan(path, key string, i, j int) *Parth
func (p *Parth) Err() error
func (p *Parth) Segment(i int, v interface{})
func (p *Parth) Sequent(key string, v interface{})
func (p *Parth) Span(i, j int) string
func (p *Parth) SubSeg(key string, i int, v interface{})
func (p *Parth) SubSpan(key string, i, j int) string
type Unmarshaler
```
### Setup ("By Index")
```go
import (
"fmt"
"github.com/codemodus/parth"
)
func handler(w http.ResponseWriter, r *http.Request) {
var s string
if err := parth.Segment(r.URL.Path, 4, &s); err != nil {
fmt.Fprintln(os.Stderr, err)
}
fmt.Println(r.URL.Path)
fmt.Printf("%v (%T)\n", s, s)
// Output:
// /some/path/things/42/others/3
// others (string)
}
```
### Setup ("By Key")
```go
import (
"fmt"
"github.com/codemodus/parth"
)
func handler(w http.ResponseWriter, r *http.Request) {
var i int64
if err := parth.Sequent(r.URL.Path, "things", &i); err != nil {
fmt.Fprintln(os.Stderr, err)
}
fmt.Println(r.URL.Path)
fmt.Printf("%v (%T)\n", i, i)
// Output:
// /some/path/things/42/others/3
// 42 (int64)
}
```
### Setup (Parth Type)
```go
import (
"fmt"
"github.com/codemodus/parth"
)
func handler(w http.ResponseWriter, r *http.Request) {
var s string
var f float32
p := parth.New(r.URL.Path)
p.Segment(2, &s)
p.SubSeg("key", 1, &f)
if err := p.Err(); err != nil {
fmt.Fprintln(os.Stderr, err)
}
fmt.Println(r.URL.Path)
fmt.Printf("%v (%T)\n", s, s)
fmt.Printf("%v (%T)\n", f, f)
// Output:
// /zero/one/two/key/four/5.5/six
// two (string)
// 5.5 (float32)
}
```
### Setup (Unmarshaler)
```go
import (
"fmt"
"github.com/codemodus/parth"
)
func handler(w http.ResponseWriter, r *http.Request) {
/*
type mytype []byte
func (m *mytype) UnmarshalSegment(seg string) error {
*m = []byte(seg)
}
*/
var m mytype
if err := parth.Segment(r.URL.Path, 4, &m); err != nil {
fmt.Fprintln(os.Stderr, err)
}
fmt.Println(r.URL.Path)
fmt.Printf("%v == %q (%T)\n", m, m, m)
// Output:
// /zero/one/two/key/four/5.5/six
// [102 111 117 114] == "four" (mypkg.mytype)
}
```
## More Info
### Keep Using http.HandlerFunc And Minimize context.Context Usage
The most obvious use case for parth is when working with any URL path such as
the one found at http.Request.URL.Path. parth is fast enough that it can be used
multiple times in place of a single use of similar router-parameter schemes or
even context.Context. There is no need to use an alternate http handler function
definition in order to pass data that is already being passed. The http.Request
type already holds URL data and parth is great at handling it. Additionally,
parth takes care of parsing selected path segments into the types actually
needed. Parth not only does more, it's usually faster and less intrusive than
the alternatives.
### Indexes
If an index is negative, the negative count begins with the last segment.
Providing a 0 for the second index is a special case which acts as an alias for
the end of the path. An error is returned if: 1. Any index is out of range of
the path; 2. When there are two indexes, the first index does not precede the
second index.
### Keys
If a key is involved, functions will only handle the portion of the path
subsequent to the provided key. An error is returned if the key cannot be found
in the path.
### First Whole, First Decimal (Restated - Important!)
When handling an int, uint, or float of any size, the first valid value within
the specified segment will be used.
## Documentation
View the [GoDoc](http://godoc.org/github.com/codemodus/parth)
## Benchmarks
Go 1.11
benchmark iter time/iter bytes alloc allocs
--------- ---- --------- ----------- ------
BenchmarkSegmentString-8 30000000 39.60 ns/op 0 B/op 0 allocs/op
BenchmarkSegmentInt-8 20000000 65.60 ns/op 0 B/op 0 allocs/op
BenchmarkSegmentIntNegIndex-8 20000000 86.60 ns/op 0 B/op 0 allocs/op
BenchmarkSpan-8 100000000 18.20 ns/op 0 B/op 0 allocs/op
BenchmarkStdlibSegmentString-8 5000000 454.00 ns/op 50 B/op 2 allocs/op
BenchmarkStdlibSegmentInt-8 3000000 526.00 ns/op 50 B/op 2 allocs/op
BenchmarkStdlibSpan-8 3000000 518.00 ns/op 69 B/op 2 allocs/op
BenchmarkContextLookupSetGet-8 1000000 1984.00 ns/op 480 B/op 6 allocs/op

1
src/vendor/github.com/h2non/parth/go.mod generated vendored Normal file
View File

@ -0,0 +1 @@
module github.com/h2non/parth

349
src/vendor/github.com/h2non/parth/parth.go generated vendored Normal file
View File

@ -0,0 +1,349 @@
// Package parth provides path parsing for segment unmarshaling and slicing. In
// other words, parth provides simple and flexible access to (URL) path
// parameters.
//
// Along with string, all basic non-alias types are supported. An interface is
// available for implementation by user-defined types. When handling an int,
// uint, or float of any size, the first valid value within the specified
// segment will be used.
package parth
import (
"errors"
)
// Unmarshaler is the interface implemented by types that can unmarshal a path
// segment representation of themselves. It is safe to assume that the segment
// data will not include slashes.
type Unmarshaler interface {
UnmarshalSegment(string) error
}
// Err{Name} values facilitate error identification.
var (
ErrUnknownType = errors.New("unknown type provided")
ErrFirstSegNotFound = errors.New("first segment not found by index")
ErrLastSegNotFound = errors.New("last segment not found by index")
ErrSegOrderReversed = errors.New("first segment must precede last segment")
ErrKeySegNotFound = errors.New("segment not found by key")
ErrDataUnparsable = errors.New("data cannot be parsed")
)
// Segment locates the path segment indicated by the index i and unmarshals it
// into the provided type v. If the index is negative, the negative count
// begins with the last segment. An error is returned if: 1. The type is not a
// pointer to an instance of one of the basic non-alias types and does not
// implement the Unmarshaler interface; 2. The index is out of range of the
// path; 3. The located path segment data cannot be parsed as the provided type
// or if an error is returned when using a provided Unmarshaler implementation.
func Segment(path string, i int, v interface{}) error { //nolint
var err error
switch v := v.(type) {
case *bool:
*v, err = segmentToBool(path, i)
case *float32:
var f float64
f, err = segmentToFloatN(path, i, 32)
*v = float32(f)
case *float64:
*v, err = segmentToFloatN(path, i, 64)
case *int:
var n int64
n, err = segmentToIntN(path, i, 0)
*v = int(n)
case *int16:
var n int64
n, err = segmentToIntN(path, i, 16)
*v = int16(n)
case *int32:
var n int64
n, err = segmentToIntN(path, i, 32)
*v = int32(n)
case *int64:
*v, err = segmentToIntN(path, i, 64)
case *int8:
var n int64
n, err = segmentToIntN(path, i, 8)
*v = int8(n)
case *string:
*v, err = segmentToString(path, i)
case *uint:
var n uint64
n, err = segmentToUintN(path, i, 0)
*v = uint(n)
case *uint16:
var n uint64
n, err = segmentToUintN(path, i, 16)
*v = uint16(n)
case *uint32:
var n uint64
n, err = segmentToUintN(path, i, 32)
*v = uint32(n)
case *uint64:
*v, err = segmentToUintN(path, i, 64)
case *uint8:
var n uint64
n, err = segmentToUintN(path, i, 8)
*v = uint8(n)
case Unmarshaler:
var s string
s, err = segmentToString(path, i)
if err == nil {
err = v.UnmarshalSegment(s)
}
default:
err = ErrUnknownType
}
return err
}
// Sequent is similar to Segment, but uses a key to locate a segment and then
// unmarshal the subsequent segment. It is a simple wrapper over SubSeg with an
// index of 0.
func Sequent(path, key string, v interface{}) error {
return SubSeg(path, key, 0, v)
}
// Span returns the path segments between two segment indexes i and j including
// the first segment. If an index is negative, the negative count begins with
// the last segment. Providing a 0 for the last index j is a special case which
// acts as an alias for the end of the path. If the first segment does not begin
// with a slash and it is part of the requested span, no slash will be added. An
// error is returned if: 1. Either index is out of range of the path; 2. The
// first index i does not precede the last index j.
func Span(path string, i, j int) (string, error) {
var f, l int
var ok bool
if i < 0 {
f, ok = segStartIndexFromEnd(path, i)
} else {
f, ok = segStartIndexFromStart(path, i)
}
if !ok {
return "", ErrFirstSegNotFound
}
if j > 0 {
l, ok = segEndIndexFromStart(path, j)
} else {
l, ok = segEndIndexFromEnd(path, j)
}
if !ok {
return "", ErrLastSegNotFound
}
if f == l {
return "", nil
}
if f > l {
return "", ErrSegOrderReversed
}
return path[f:l], nil
}
// SubSeg is similar to Segment, but only handles the portion of the path
// subsequent to the provided key. For example, to access the segment
// immediately after a key, an index of 0 should be provided (see Sequent). An
// error is returned if the key cannot be found in the path.
func SubSeg(path, key string, i int, v interface{}) error { //nolint
var err error
switch v := v.(type) {
case *bool:
*v, err = subSegToBool(path, key, i)
case *float32:
var f float64
f, err = subSegToFloatN(path, key, i, 32)
*v = float32(f)
case *float64:
*v, err = subSegToFloatN(path, key, i, 64)
case *int:
var n int64
n, err = subSegToIntN(path, key, i, 0)
*v = int(n)
case *int16:
var n int64
n, err = subSegToIntN(path, key, i, 16)
*v = int16(n)
case *int32:
var n int64
n, err = subSegToIntN(path, key, i, 32)
*v = int32(n)
case *int64:
*v, err = subSegToIntN(path, key, i, 64)
case *int8:
var n int64
n, err = subSegToIntN(path, key, i, 8)
*v = int8(n)
case *string:
*v, err = subSegToString(path, key, i)
case *uint:
var n uint64
n, err = subSegToUintN(path, key, i, 0)
*v = uint(n)
case *uint16:
var n uint64
n, err = subSegToUintN(path, key, i, 16)
*v = uint16(n)
case *uint32:
var n uint64
n, err = subSegToUintN(path, key, i, 32)
*v = uint32(n)
case *uint64:
*v, err = subSegToUintN(path, key, i, 64)
case *uint8:
var n uint64
n, err = subSegToUintN(path, key, i, 8)
*v = uint8(n)
case Unmarshaler:
var s string
s, err = subSegToString(path, key, i)
if err == nil {
err = v.UnmarshalSegment(s)
}
default:
err = ErrUnknownType
}
return err
}
// SubSpan is similar to Span, but only handles the portion of the path
// subsequent to the provided key. An error is returned if the key cannot be
// found in the path.
func SubSpan(path, key string, i, j int) (string, error) {
si, ok := segIndexByKey(path, key)
if !ok {
return "", ErrKeySegNotFound
}
if i >= 0 {
i++
}
if j > 0 {
j++
}
s, err := Span(path[si:], i, j)
if err != nil {
return "", err
}
return s, nil
}
// Parth manages path and error data for processing a single path multiple
// times while error checking only once. Only the first encountered error is
// stored as all subsequent calls to Parth methods that can error are elided.
type Parth struct {
path string
err error
}
// New constructs a pointer to an instance of Parth around the provided path.
func New(path string) *Parth {
return &Parth{path: path}
}
// NewBySpan constructs a pointer to an instance of Parth after preprocessing
// the provided path with Span.
func NewBySpan(path string, i, j int) *Parth {
s, err := Span(path, i, j)
return &Parth{s, err}
}
// NewBySubSpan constructs a pointer to an instance of Parth after
// preprocessing the provided path with SubSpan.
func NewBySubSpan(path, key string, i, j int) *Parth {
s, err := SubSpan(path, key, i, j)
return &Parth{s, err}
}
// Err returns the first error encountered by the *Parth receiver.
func (p *Parth) Err() error {
return p.err
}
// Segment operates the same as the package-level function Segment.
func (p *Parth) Segment(i int, v interface{}) {
if p.err != nil {
return
}
p.err = Segment(p.path, i, v)
}
// Sequent operates the same as the package-level function Sequent.
func (p *Parth) Sequent(key string, v interface{}) {
p.SubSeg(key, 0, v)
}
// Span operates the same as the package-level function Span.
func (p *Parth) Span(i, j int) string {
if p.err != nil {
return ""
}
s, err := Span(p.path, i, j)
p.err = err
return s
}
// SubSeg operates the same as the package-level function SubSeg.
func (p *Parth) SubSeg(key string, i int, v interface{}) {
if p.err != nil {
return
}
p.err = SubSeg(p.path, key, i, v)
}
// SubSpan operates the same as the package-level function SubSpan.
func (p *Parth) SubSpan(key string, i, j int) string {
if p.err != nil {
return ""
}
s, err := SubSpan(p.path, key, i, j)
p.err = err
return s
}

118
src/vendor/github.com/h2non/parth/segindex.go generated vendored Normal file
View File

@ -0,0 +1,118 @@
package parth
func segStartIndexFromStart(path string, seg int) (int, bool) {
if seg < 0 {
return 0, false
}
for n, ct := 0, 0; n < len(path); n++ {
if n > 0 && path[n] == '/' {
ct++
}
if ct == seg {
return n, true
}
}
return 0, false
}
func segStartIndexFromEnd(path string, seg int) (int, bool) {
if seg > -1 {
return 0, false
}
for n, ct := len(path)-1, 0; n >= 0; n-- {
if path[n] == '/' || n == 0 {
ct--
}
if ct == seg {
return n, true
}
}
return 0, false
}
func segEndIndexFromStart(path string, seg int) (int, bool) {
if seg < 1 {
return 0, false
}
for n, ct := 0, 0; n < len(path); n++ {
if path[n] == '/' && n > 0 {
ct++
}
if ct == seg {
return n, true
}
if n+1 == len(path) && ct+1 == seg {
return n + 1, true
}
}
return 0, false
}
func segEndIndexFromEnd(path string, seg int) (int, bool) {
if seg > 0 {
return 0, false
}
if seg == 0 {
return len(path), true
}
if len(path) == 1 && path[0] == '/' {
return 0, true
}
for n, ct := len(path)-1, 0; n >= 0; n-- {
if n == 0 || path[n] == '/' {
ct--
}
if ct == seg {
return n, true
}
}
return 0, false
}
func segIndexByKey(path, key string) (int, bool) { //nolint
if path == "" || key == "" {
return 0, false
}
for n := 0; n < len(path); n++ {
si, ok := segStartIndexFromStart(path, n)
if !ok {
return 0, false
}
if len(path[si:]) == len(key)+1 {
if path[si+1:] == key {
return si, true
}
return 0, false
}
tmpEI, ok := segStartIndexFromStart(path[si:], 1)
if !ok {
return 0, false
}
if path[si+1:tmpEI+si] == key || n == 0 && path[0] != '/' && path[si:tmpEI+si] == key {
return si, true
}
}
return 0, false
}

305
src/vendor/github.com/h2non/parth/segtostr.go generated vendored Normal file
View File

@ -0,0 +1,305 @@
package parth
import (
"strconv"
"unicode"
)
func segmentToBool(path string, i int) (bool, error) {
s, err := segmentToString(path, i)
if err != nil {
return false, err
}
v, err := strconv.ParseBool(s)
if err != nil {
return false, ErrDataUnparsable
}
return v, nil
}
func segmentToFloatN(path string, i, size int) (float64, error) {
ss, err := segmentToString(path, i)
if err != nil {
return 0.0, err
}
s, ok := firstFloatFromString(ss)
if !ok {
return 0.0, err
}
v, err := strconv.ParseFloat(s, size)
if err != nil {
return 0.0, ErrDataUnparsable
}
return v, nil
}
func segmentToIntN(path string, i, size int) (int64, error) {
ss, err := segmentToString(path, i)
if err != nil {
return 0, err
}
s, ok := firstIntFromString(ss)
if !ok {
return 0, ErrDataUnparsable
}
v, err := strconv.ParseInt(s, 10, size)
if err != nil {
return 0, ErrDataUnparsable
}
return v, nil
}
func segmentToString(path string, i int) (string, error) {
j := i + 1
if i < 0 {
i--
}
s, err := Span(path, i, j)
if err != nil {
return "", err
}
if s[0] == '/' {
s = s[1:]
}
return s, nil
}
func segmentToUintN(path string, i, size int) (uint64, error) {
ss, err := segmentToString(path, i)
if err != nil {
return 0, err
}
s, ok := firstUintFromString(ss)
if !ok {
return 0, ErrDataUnparsable
}
v, err := strconv.ParseUint(s, 10, size)
if err != nil {
return 0, ErrDataUnparsable
}
return v, nil
}
func subSegToBool(path, key string, i int) (bool, error) {
s, err := subSegToString(path, key, i)
if err != nil {
return false, err
}
v, err := strconv.ParseBool(s)
if err != nil {
return false, ErrDataUnparsable
}
return v, nil
}
func subSegToFloatN(path, key string, i, size int) (float64, error) {
ss, err := subSegToString(path, key, i)
if err != nil {
return 0.0, err
}
s, ok := firstFloatFromString(ss)
if !ok {
return 0.0, ErrDataUnparsable
}
v, err := strconv.ParseFloat(s, size)
if err != nil {
return 0.0, ErrDataUnparsable
}
return v, nil
}
func subSegToIntN(path, key string, i, size int) (int64, error) {
ss, err := subSegToString(path, key, i)
if err != nil {
return 0, err
}
s, ok := firstIntFromString(ss)
if !ok {
return 0, ErrDataUnparsable
}
v, err := strconv.ParseInt(s, 10, size)
if err != nil {
return 0, ErrDataUnparsable
}
return v, nil
}
func subSegToString(path, key string, i int) (string, error) {
ki, ok := segIndexByKey(path, key)
if !ok {
return "", ErrKeySegNotFound
}
i++
s, err := segmentToString(path[ki:], i)
if err != nil {
return "", err
}
return s, nil
}
func subSegToUintN(path, key string, i, size int) (uint64, error) {
ss, err := subSegToString(path, key, i)
if err != nil {
return 0, err
}
s, ok := firstUintFromString(ss)
if !ok {
return 0, ErrDataUnparsable
}
v, err := strconv.ParseUint(s, 10, size)
if err != nil {
return 0, ErrDataUnparsable
}
return v, nil
}
func firstUintFromString(s string) (string, bool) {
ind, l := 0, 0
for n := 0; n < len(s); n++ {
if unicode.IsDigit(rune(s[n])) {
if l == 0 {
ind = n
}
l++
} else {
if l == 0 && s[n] == '.' {
if n+1 < len(s) && unicode.IsDigit(rune(s[n+1])) {
return "0", true
}
break
}
if l > 0 {
break
}
}
}
if l == 0 {
return "", false
}
return s[ind : ind+l], true
}
func firstIntFromString(s string) (string, bool) { //nolint
ind, l := 0, 0
for n := 0; n < len(s); n++ {
if unicode.IsDigit(rune(s[n])) {
if l == 0 {
ind = n
}
l++
} else if s[n] == '-' {
if l == 0 {
ind = n
l++
} else {
break
}
} else {
if l == 0 && s[n] == '.' {
if n+1 < len(s) && unicode.IsDigit(rune(s[n+1])) {
return "0", true
}
break
}
if l > 0 {
break
}
}
}
if l == 0 {
return "", false
}
return s[ind : ind+l], true
}
func firstFloatFromString(s string) (string, bool) { //nolint
c, ind, l := 0, 0, 0
for n := 0; n < len(s); n++ {
if unicode.IsDigit(rune(s[n])) {
if l == 0 {
ind = n
}
l++
} else if s[n] == '-' {
if l == 0 {
ind = n
l++
} else {
break
}
} else if s[n] == '.' {
if l == 0 {
ind = n
}
if c > 0 {
break
}
l++
c++
} else if s[n] == 'e' && l > 0 && n+1 < len(s) && s[n+1] == '+' {
l++
} else if s[n] == '+' && l > 0 && s[n-1] == 'e' {
if n+1 < len(s) && unicode.IsDigit(rune(s[n+1])) {
l++
continue
}
l--
break
} else {
if l > 0 {
break
}
}
}
if l == 0 || s[ind:ind+l] == "." {
return "", false
}
return s[ind : ind+l], true
}

30
src/vendor/gopkg.in/h2non/gock.v1/.gitignore generated vendored Normal file
View File

@ -0,0 +1,30 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
go.sum
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
.idea/
*.iml
*.out
*.tmp

29
src/vendor/gopkg.in/h2non/gock.v1/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,29 @@
language: go
go:
# - "1.14"
- "1.13"
# - "1.12"
- "1.11"
- "1.10"
# - "stable"
before_install:
- go get -u github.com/nbio/st
- go get -u github.com/codemodus/parth
- go get -u gopkg.in/h2non/gentleman.v1
- go get -u -v github.com/axw/gocov/gocov
- go get -u -v github.com/mattn/goveralls
- go get -v -u golang.org/x/lint/golint
- mkdir -p $GOPATH/src/gopkg.in/h2non/gock.v1
- cp -r . $GOPATH/src/gopkg.in/h2non/gock.v1
script:
- diff -u <(echo -n) <(gofmt -s -d ./)
- diff -u <(echo -n) <(go vet ./...)
- diff -u <(echo -n) <(golint ./...)
- go test -v -race -covermode=atomic -coverprofile=coverage.out
- go test ./_examples/*/ -v -race
after_success:
- goveralls -coverprofile=coverage.out -service=travis-ci -repotoken $COVERALLS_TOKEN

128
src/vendor/gopkg.in/h2non/gock.v1/History.md generated vendored Normal file
View File

@ -0,0 +1,128 @@
## v1.0.16 / 2020-11-23
* Fix regexp matching issues in headers (#59)
## v1.0.15 / 2019-07-03
* NewMatcher() will now return objects that completely separate one another. (#55)
* add request Options (#49)
* fix typo: function -> func (#52)
* feat(docs): change note
* feat(docs): add net/http support
* Add Basic Auth (#47)
* Update LICENSE (#46)
## v1.0.14 / 2019-01-31
* feat(version): bump to v1.0.14
* feat: add go.mod
## v1.0.13 / 2019-01-30
* Add PathParam matcher (#42)
## v1.0.12 / 2018-11-13
* Fix possible data race. (#41)
## v1.0.11 / 2018-10-29
* Do not reset response body (#40)
* refactor(travis): remove unsupported versions for golint based on Go release policy support
* feat(gock): add gock.Observe to support inspection of the outgoing intercepted HTTP traffic (#38)
## v1.0.10 / 2018-09-09
* Support multiple response headers with same name #35 (#36)
## v1.0.9 / 2018-06-14
* fix(url-encoding) add exact match test in MatchPath (#34)
* fix(travis): use string notation for Go versions
## v1.0.8 / 2018-02-28
* chore(LICENSE): update year ;)
* feat(docs): add additional tips and examples
* feat(gock): ignore already intercepted http.Client
## v1.0.7 / 2017-12-21
* Make MatchHost case insensitive. (#31)
* refactor(docs): remove codesponsor :(
* add example when request reply with error (#28)
* feat(docs): add sponsor ad
* Add example networking partially enabled (#23)
## v1.0.6 / 2017-07-27
* fix(#23): mock transport deadlock
## v1.0.5 / 2017-07-26
* feat(#25, #24): use content type only if missing while matching JSON/XML
* feat(#24): add CleanUnmatchedRequests() and OffAll() public functions
* feat(version): bump to v1.0.5
* fix(store): use proper indent style
* fix(mutex): use different mutex for store
* feat(travis): add Go 1.8 CI support
## v1.0.4 / 2017-02-14
* Update README to include most up to date version (#17)
* Update MatchBody() to compare if key + value pairs of JSON match regardless of order they are in. (#16)
* feat(examples): add new example for unmatch case
* refactor(docs): add pook reference
## 1.0.3 / 14-11-2016
- feat(#13): adds `GetUnmatchedRequests()` and `HasUnmatchedRequests()` API functions.
## 1.0.2 / 10-11-2016
- fix(#11): adds `Compression()` method for output HTTP traffic body compression processing and matching.
## 1.0.1 / 07-09-2016
- fix(#9): missing URL query param matcher.
## 1.0.0 / 19-04-2016
- feat(version): first major version release.
## 0.1.6 / 19-04-2016
- fix(#7): if error configured, RoundTripper should reply with `nil` response.
## 0.1.5 / 09-04-2016
- feat(#5): support `ReplyFunc` for convenience.
## 0.1.4 / 16-03-2016
- feat(api): add `IsDone()` method.
- fix(responder): return mock error if present.
- feat(#4): support define request/response body from file disk.
## 0.1.3 / 09-03-2016
- feat(matcher): add content type matcher helper method supporting aliases.
- feat(interceptor): add function to restore HTTP client transport.
- feat(matcher): add URL scheme matcher function.
- fix(request): ignore base slash path.
- feat(api): add Off() method for easier restore and clean up.
- feat(store): add public API for pending mocks.
## 0.1.2 / 04-03-2016
- fix(matcher): body matchers no used by default.
- feat(matcher): add matcher factories for multiple cases.
## 0.1.1 / 04-03-2016
- fix(params): persist query params accordingly.
## 0.1.0 / 02-03-2016
- First release.

24
src/vendor/gopkg.in/h2non/gock.v1/LICENSE generated vendored Normal file
View File

@ -0,0 +1,24 @@
The MIT License
Copyright (c) 2016-2019 Tomas Aparicio
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.

373
src/vendor/gopkg.in/h2non/gock.v1/README.md generated vendored Normal file
View File

@ -0,0 +1,373 @@
# gock [![Build Status](https://travis-ci.org/h2non/gock.svg?branch=master)](https://travis-ci.org/h2non/gock) [![GitHub release](https://img.shields.io/badge/version-v1.0-orange.svg?style=flat)](https://github.com/h2non/gock/releases) [![GoDoc](https://godoc.org/github.com/h2non/gock?status.svg)](https://godoc.org/github.com/h2non/gock) [![Coverage Status](https://coveralls.io/repos/github/h2non/gock/badge.svg?branch=master)](https://coveralls.io/github/h2non/gock?branch=master) [![Go Report Card](https://img.shields.io/badge/go_report-A+-brightgreen.svg)](https://goreportcard.com/report/github.com/h2non/gock) [![license](https://img.shields.io/badge/license-MIT-blue.svg)]()
Versatile HTTP mocking made easy in [Go](https://golang.org) that works with any `net/http` based stdlib implementation.
Heavily inspired by [nock](https://github.com/node-nock/nock).
There is also its Python port, [pook](https://github.com/h2non/pook).
To get started, take a look to the [examples](#examples).
## Features
- Simple, expressive, fluent API.
- Semantic API DSL for declarative HTTP mock declarations.
- Built-in helpers for easy JSON/XML mocking.
- Supports persistent and volatile TTL-limited mocks.
- Full regular expressions capable HTTP request mock matching.
- Designed for both testing and runtime scenarios.
- Match request by method, URL params, headers and bodies.
- Extensible and pluggable HTTP matching rules.
- Ability to switch between mock and real networking modes.
- Ability to filter/map HTTP requests for accurate mock matching.
- Supports map and filters to handle mocks easily.
- Wide compatible HTTP interceptor using `http.RoundTripper` interface.
- Works with any `net/http` compatible client, such as [gentleman](https://github.com/h2non/gentleman).
- Network delay simulation (beta).
- Extensible and hackable API.
- Dependency free.
## Installation
```bash
go get -u gopkg.in/h2non/gock.v1
```
## API
See [godoc reference](https://godoc.org/github.com/h2non/gock) for detailed API documentation.
## How it mocks
1. Intercepts any HTTP outgoing request via `http.DefaultTransport` or custom `http.Transport` used by any `http.Client`.
2. Matches outgoing HTTP requests against a pool of defined HTTP mock expectations in FIFO declaration order.
3. If at least one mock matches, it will be used in order to compose the mock HTTP response.
4. If no mock can be matched, it will resolve the request with an error, unless real networking mode is enable, in which case a real HTTP request will be performed.
## Tips
#### Testing
Declare your mocks before you start declaring the concrete test logic:
```go
func TestFoo(t *testing.T) {
defer gock.Off() // Flush pending mocks after test execution
gock.New("http://server.com").
Get("/bar").
Reply(200).
JSON(map[string]string{"foo": "bar"})
// Your test code starts here...
}
```
#### Race conditions
If you're running concurrent code, be aware that your mocks are declared first to avoid unexpected
race conditions while configuring `gock` or intercepting custom HTTP clients.
`gock` is not fully thread-safe, but sensible parts are.
Any help making `gock` more reliable in this sense is appreciated.
#### Define complex mocks first
If you're mocking a bunch of mocks in the same test suite, it's recommended to define the more
concrete mocks first, and then the generic ones.
This approach usually avoids matching unexpected generic mocks (e.g: specific header, body payload...) instead of the generic ones that performs less complex matches.
#### Disable `gock` traffic interception once done
In other to minimize potential side effects within your test code, it's a good practice
disabling `gock` once you are done with your HTTP testing logic.
A Go idiomatic approach for doing this can be using it in a `defer` statement, such as:
```go
func TestGock (t *testing.T) {
defer gock.Off()
// ... my test code goes here
}
```
#### Intercept an `http.Client` just once
You don't need to intercept multiple times the same `http.Client` instance.
Just call `gock.InterceptClient(client)` once, typically at the beginning of your test scenarios.
#### Restore an `http.Client` after interception
**NOTE**: this is not required is you are using `http.DefaultClient` or `http.DefaultTransport`.
As a good testing pattern, you should call `gock.RestoreClient(client)` after running your test scenario, typically as after clean up hook.
You can also use a `defer` statement for doing it, as you do with `gock.Off()`, such as:
```go
func TestGock (t *testing.T) {
defer gock.Off()
defer gock.RestoreClient(client)
// ... my test code goes here
}
```
## Examples
See [examples](https://github.com/h2non/gock/tree/master/_examples) directory for more featured use cases.
#### Simple mocking via tests
```go
package test
import (
"github.com/nbio/st"
"gopkg.in/h2non/gock.v1"
"io/ioutil"
"net/http"
"testing"
)
func TestSimple(t *testing.T) {
defer gock.Off()
gock.New("http://foo.com").
Get("/bar").
Reply(200).
JSON(map[string]string{"foo": "bar"})
res, err := http.Get("http://foo.com/bar")
st.Expect(t, err, nil)
st.Expect(t, res.StatusCode, 200)
body, _ := ioutil.ReadAll(res.Body)
st.Expect(t, string(body)[:13], `{"foo":"bar"}`)
// Verify that we don't have pending mocks
st.Expect(t, gock.IsDone(), true)
}
```
#### Request headers matching
```go
package test
import (
"github.com/nbio/st"
"gopkg.in/h2non/gock.v1"
"io/ioutil"
"net/http"
"testing"
)
func TestMatchHeaders(t *testing.T) {
defer gock.Off()
gock.New("http://foo.com").
MatchHeader("Authorization", "^foo bar$").
MatchHeader("API", "1.[0-9]+").
HeaderPresent("Accept").
Reply(200).
BodyString("foo foo")
req, err := http.NewRequest("GET", "http://foo.com", nil)
req.Header.Set("Authorization", "foo bar")
req.Header.Set("API", "1.0")
req.Header.Set("Accept", "text/plain")
res, err := (&http.Client{}).Do(req)
st.Expect(t, err, nil)
st.Expect(t, res.StatusCode, 200)
body, _ := ioutil.ReadAll(res.Body)
st.Expect(t, string(body), "foo foo")
// Verify that we don't have pending mocks
st.Expect(t, gock.IsDone(), true)
}
```
#### Request param matching
```go
package test
import (
"github.com/nbio/st"
"gopkg.in/h2non/gock.v1"
"io/ioutil"
"net/http"
"testing"
)
func TestMatchParams(t *testing.T) {
defer gock.Off()
gock.New("http://foo.com").
MatchParam("page", "1").
MatchParam("per_page", "10").
Reply(200).
BodyString("foo foo")
req, err := http.NewRequest("GET", "http://foo.com?page=1&per_page=10", nil)
res, err := (&http.Client{}).Do(req)
st.Expect(t, err, nil)
st.Expect(t, res.StatusCode, 200)
body, _ := ioutil.ReadAll(res.Body)
st.Expect(t, string(body), "foo foo")
// Verify that we don't have pending mocks
st.Expect(t, gock.IsDone(), true)
}
```
#### JSON body matching and response
```go
package test
import (
"bytes"
"github.com/nbio/st"
"gopkg.in/h2non/gock.v1"
"io/ioutil"
"net/http"
"testing"
)
func TestMockSimple(t *testing.T) {
defer gock.Off()
gock.New("http://foo.com").
Post("/bar").
MatchType("json").
JSON(map[string]string{"foo": "bar"}).
Reply(201).
JSON(map[string]string{"bar": "foo"})
body := bytes.NewBuffer([]byte(`{"foo":"bar"}`))
res, err := http.Post("http://foo.com/bar", "application/json", body)
st.Expect(t, err, nil)
st.Expect(t, res.StatusCode, 201)
resBody, _ := ioutil.ReadAll(res.Body)
st.Expect(t, string(resBody)[:13], `{"bar":"foo"}`)
// Verify that we don't have pending mocks
st.Expect(t, gock.IsDone(), true)
}
```
#### Mocking a custom http.Client and http.RoundTripper
```go
package test
import (
"github.com/nbio/st"
"gopkg.in/h2non/gock.v1"
"io/ioutil"
"net/http"
"testing"
)
func TestClient(t *testing.T) {
defer gock.Off()
gock.New("http://foo.com").
Reply(200).
BodyString("foo foo")
req, err := http.NewRequest("GET", "http://foo.com", nil)
client := &http.Client{Transport: &http.Transport{}}
gock.InterceptClient(client)
res, err := client.Do(req)
st.Expect(t, err, nil)
st.Expect(t, res.StatusCode, 200)
body, _ := ioutil.ReadAll(res.Body)
st.Expect(t, string(body), "foo foo")
// Verify that we don't have pending mocks
st.Expect(t, gock.IsDone(), true)
}
```
#### Enable real networking
```go
package main
import (
"fmt"
"gopkg.in/h2non/gock.v1"
"io/ioutil"
"net/http"
)
func main() {
defer gock.Off()
defer gock.DisableNetworking()
gock.EnableNetworking()
gock.New("http://httpbin.org").
Get("/get").
Reply(201).
SetHeader("Server", "gock")
res, err := http.Get("http://httpbin.org/get")
if err != nil {
fmt.Errorf("Error: %s", err)
}
// The response status comes from the mock
fmt.Printf("Status: %d\n", res.StatusCode)
// The server header comes from mock as well
fmt.Printf("Server header: %s\n", res.Header.Get("Server"))
// Response body is the original
body, _ := ioutil.ReadAll(res.Body)
fmt.Printf("Body: %s", string(body))
}
```
#### Debug intercepted http requests
```go
package main
import (
"bytes"
"gopkg.in/h2non/gock.v1"
"net/http"
)
func main() {
defer gock.Off()
gock.Observe(gock.DumpRequest)
gock.New("http://foo.com").
Post("/bar").
MatchType("json").
JSON(map[string]string{"foo": "bar"}).
Reply(200)
body := bytes.NewBuffer([]byte(`{"foo":"bar"}`))
http.Post("http://foo.com/bar", "application/json", body)
}
```
## Hacking it!
You can easily hack `gock` defining custom matcher functions with own matching rules.
See [add matcher functions](https://github.com/h2non/gock/blob/master/_examples/add_matchers/matchers.go) and [custom matching layer](https://github.com/h2non/gock/blob/master/_examples/custom_matcher/matcher.go) examples for further details.
## License
MIT - Tomas Aparicio

8
src/vendor/gopkg.in/h2non/gock.v1/go.mod generated vendored Normal file
View File

@ -0,0 +1,8 @@
module gopkg.in/h2non/gock.v1
require (
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32
golang.org/x/net v0.0.0-20191021144547-ec77196f6094 // indirect
gopkg.in/h2non/gentleman.v1 v1.0.4
)

178
src/vendor/gopkg.in/h2non/gock.v1/gock.go generated vendored Normal file
View File

@ -0,0 +1,178 @@
package gock
import (
"fmt"
"net/http"
"net/http/httputil"
"net/url"
"regexp"
"sync"
)
// mutex is used interally for locking thread-sensitive functions.
var mutex = &sync.Mutex{}
// config global singleton store.
var config = struct {
Networking bool
NetworkingFilters []FilterRequestFunc
Observer ObserverFunc
}{}
// ObserverFunc is implemented by users to inspect the outgoing intercepted HTTP traffic
type ObserverFunc func(*http.Request, Mock)
// DumpRequest is a default implementation of ObserverFunc that dumps
// the HTTP/1.x wire representation of the http request
var DumpRequest ObserverFunc = func(request *http.Request, mock Mock) {
bytes, _ := httputil.DumpRequestOut(request, true)
fmt.Println(string(bytes))
fmt.Printf("\nMatches: %v\n---\n", mock != nil)
}
// track unmatched requests so they can be tested for
var unmatchedRequests = []*http.Request{}
// New creates and registers a new HTTP mock with
// default settings and returns the Request DSL for HTTP mock
// definition and set up.
func New(uri string) *Request {
Intercept()
res := NewResponse()
req := NewRequest()
req.URLStruct, res.Error = url.Parse(normalizeURI(uri))
// Create the new mock expectation
exp := NewMock(req, res)
Register(exp)
return req
}
// Intercepting returns true if gock is currently able to intercept.
func Intercepting() bool {
mutex.Lock()
defer mutex.Unlock()
return http.DefaultTransport == DefaultTransport
}
// Intercept enables HTTP traffic interception via http.DefaultTransport.
// If you are using a custom HTTP transport, you have to use `gock.Transport()`
func Intercept() {
if !Intercepting() {
mutex.Lock()
http.DefaultTransport = DefaultTransport
mutex.Unlock()
}
}
// InterceptClient allows the developer to intercept HTTP traffic using
// a custom http.Client who uses a non default http.Transport/http.RoundTripper implementation.
func InterceptClient(cli *http.Client) {
_, ok := cli.Transport.(*Transport)
if ok {
return // if transport already intercepted, just ignore it
}
trans := NewTransport()
trans.Transport = cli.Transport
cli.Transport = trans
}
// RestoreClient allows the developer to disable and restore the
// original transport in the given http.Client.
func RestoreClient(cli *http.Client) {
trans, ok := cli.Transport.(*Transport)
if !ok {
return
}
cli.Transport = trans.Transport
}
// Disable disables HTTP traffic interception by gock.
func Disable() {
mutex.Lock()
defer mutex.Unlock()
http.DefaultTransport = NativeTransport
}
// Off disables the default HTTP interceptors and removes
// all the registered mocks, even if they has not been intercepted yet.
func Off() {
Flush()
Disable()
}
// OffAll is like `Off()`, but it also removes the unmatched requests registry.
func OffAll() {
Flush()
Disable()
CleanUnmatchedRequest()
}
// Observe provides a hook to support inspection of the request and matched mock
func Observe(fn ObserverFunc) {
mutex.Lock()
defer mutex.Unlock()
config.Observer = fn
}
// EnableNetworking enables real HTTP networking
func EnableNetworking() {
mutex.Lock()
defer mutex.Unlock()
config.Networking = true
}
// DisableNetworking disables real HTTP networking
func DisableNetworking() {
mutex.Lock()
defer mutex.Unlock()
config.Networking = false
}
// NetworkingFilter determines if an http.Request should be triggered or not.
func NetworkingFilter(fn FilterRequestFunc) {
mutex.Lock()
defer mutex.Unlock()
config.NetworkingFilters = append(config.NetworkingFilters, fn)
}
// DisableNetworkingFilters disables registered networking filters.
func DisableNetworkingFilters() {
mutex.Lock()
defer mutex.Unlock()
config.NetworkingFilters = []FilterRequestFunc{}
}
// GetUnmatchedRequests returns all requests that have been received but haven't matched any mock
func GetUnmatchedRequests() []*http.Request {
mutex.Lock()
defer mutex.Unlock()
return unmatchedRequests
}
// HasUnmatchedRequest returns true if gock has received any requests that didn't match a mock
func HasUnmatchedRequest() bool {
return len(GetUnmatchedRequests()) > 0
}
// CleanUnmatchedRequest cleans the unmatched requests internal registry.
func CleanUnmatchedRequest() {
mutex.Lock()
defer mutex.Unlock()
unmatchedRequests = []*http.Request{}
}
func trackUnmatchedRequest(req *http.Request) {
mutex.Lock()
defer mutex.Unlock()
unmatchedRequests = append(unmatchedRequests, req)
}
func normalizeURI(uri string) string {
if ok, _ := regexp.MatchString("^http[s]?", uri); !ok {
return "http://" + uri
}
return uri
}

137
src/vendor/gopkg.in/h2non/gock.v1/matcher.go generated vendored Normal file
View File

@ -0,0 +1,137 @@
package gock
import "net/http"
// MatchersHeader exposes an slice of HTTP header specific mock matchers.
var MatchersHeader = []MatchFunc{
MatchMethod,
MatchScheme,
MatchHost,
MatchPath,
MatchHeaders,
MatchQueryParams,
MatchPathParams,
}
// MatchersBody exposes an slice of HTTP body specific built-in mock matchers.
var MatchersBody = []MatchFunc{
MatchBody,
}
// Matchers stores all the built-in mock matchers.
var Matchers = append(MatchersHeader, MatchersBody...)
// DefaultMatcher stores the default Matcher instance used to match mocks.
var DefaultMatcher = NewMatcher()
// MatchFunc represents the required function
// interface implemented by matchers.
type MatchFunc func(*http.Request, *Request) (bool, error)
// Matcher represents the required interface implemented by mock matchers.
type Matcher interface {
// Get returns a slice of registered function matchers.
Get() []MatchFunc
// Add adds a new matcher function.
Add(MatchFunc)
// Set sets the matchers functions stack.
Set([]MatchFunc)
// Flush flushes the current matchers function stack.
Flush()
// Match matches the given http.Request with a mock Request.
Match(*http.Request, *Request) (bool, error)
}
// MockMatcher implements a mock matcher
type MockMatcher struct {
Matchers []MatchFunc
}
// NewMatcher creates a new mock matcher
// using the default matcher functions.
func NewMatcher() *MockMatcher {
m := NewEmptyMatcher()
for _, matchFn := range Matchers {
m.Add(matchFn)
}
return m
}
// NewBasicMatcher creates a new matcher with header only mock matchers.
func NewBasicMatcher() *MockMatcher {
m := NewEmptyMatcher()
for _, matchFn := range MatchersHeader {
m.Add(matchFn)
}
return m
}
// NewEmptyMatcher creates a new empty matcher without default matchers.
func NewEmptyMatcher() *MockMatcher {
return &MockMatcher{Matchers: []MatchFunc{}}
}
// Get returns a slice of registered function matchers.
func (m *MockMatcher) Get() []MatchFunc {
mutex.Lock()
defer mutex.Unlock()
return m.Matchers
}
// Add adds a new function matcher.
func (m *MockMatcher) Add(fn MatchFunc) {
m.Matchers = append(m.Matchers, fn)
}
// Set sets a new stack of matchers functions.
func (m *MockMatcher) Set(stack []MatchFunc) {
m.Matchers = stack
}
// Flush flushes the current matcher
func (m *MockMatcher) Flush() {
m.Matchers = []MatchFunc{}
}
// Clone returns a separate MockMatcher instance that has a copy of the same MatcherFuncs
func (m *MockMatcher) Clone() *MockMatcher {
m2 := NewEmptyMatcher()
for _, mFn := range m.Get() {
m2.Add(mFn)
}
return m2
}
// Match matches the given http.Request with a mock request
// returning true in case that the request matches, otherwise false.
func (m *MockMatcher) Match(req *http.Request, ereq *Request) (bool, error) {
for _, matcher := range m.Matchers {
matches, err := matcher(req, ereq)
if err != nil {
return false, err
}
if !matches {
return false, nil
}
}
return true, nil
}
// MatchMock is a helper function that matches the given http.Request
// in the list of registered mocks, returning it if matches or error if it fails.
func MatchMock(req *http.Request) (Mock, error) {
for _, mock := range GetAll() {
matches, err := mock.Match(req)
if err != nil {
return nil, err
}
if matches {
return mock, nil
}
}
return nil, nil
}

261
src/vendor/gopkg.in/h2non/gock.v1/matchers.go generated vendored Normal file
View File

@ -0,0 +1,261 @@
package gock
import (
"compress/gzip"
"encoding/json"
"io"
"io/ioutil"
"net/http"
"reflect"
"regexp"
"strings"
"github.com/h2non/parth"
)
// EOL represents the end of line character.
const EOL = 0xa
// BodyTypes stores the supported MIME body types for matching.
// Currently only text-based types.
var BodyTypes = []string{
"text/html",
"text/plain",
"application/json",
"application/xml",
"multipart/form-data",
"application/x-www-form-urlencoded",
}
// BodyTypeAliases stores a generic MIME type by alias.
var BodyTypeAliases = map[string]string{
"html": "text/html",
"text": "text/plain",
"json": "application/json",
"xml": "application/xml",
"form": "multipart/form-data",
"url": "application/x-www-form-urlencoded",
}
// CompressionSchemes stores the supported Content-Encoding types for decompression.
var CompressionSchemes = []string{
"gzip",
}
// MatchMethod matches the HTTP method of the given request.
func MatchMethod(req *http.Request, ereq *Request) (bool, error) {
return ereq.Method == "" || req.Method == ereq.Method, nil
}
// MatchScheme matches the request URL protocol scheme.
func MatchScheme(req *http.Request, ereq *Request) (bool, error) {
return ereq.URLStruct.Scheme == "" || req.URL.Scheme == "" || ereq.URLStruct.Scheme == req.URL.Scheme, nil
}
// MatchHost matches the HTTP host header field of the given request.
func MatchHost(req *http.Request, ereq *Request) (bool, error) {
url := ereq.URLStruct
if strings.EqualFold(url.Host, req.URL.Host) {
return true, nil
}
if !ereq.Options.DisableRegexpHost {
return regexp.MatchString(url.Host, req.URL.Host)
}
return false, nil
}
// MatchPath matches the HTTP URL path of the given request.
func MatchPath(req *http.Request, ereq *Request) (bool, error) {
if req.URL.Path == ereq.URLStruct.Path {
return true, nil
}
return regexp.MatchString(ereq.URLStruct.Path, req.URL.Path)
}
// MatchHeaders matches the headers fields of the given request.
func MatchHeaders(req *http.Request, ereq *Request) (bool, error) {
for key, value := range ereq.Header {
var err error
var match bool
var matchEscaped bool
for _, field := range req.Header[key] {
match, err = regexp.MatchString(value[0], field)
//Some values may contain reserved regex params e.g. "()", try matching with these escaped
matchEscaped, err = regexp.MatchString(regexp.QuoteMeta(value[0]), field)
if err != nil {
return false, err
}
if match || matchEscaped {
break
}
}
if !match && !matchEscaped {
return false, nil
}
}
return true, nil
}
// MatchQueryParams matches the URL query params fields of the given request.
func MatchQueryParams(req *http.Request, ereq *Request) (bool, error) {
for key, value := range ereq.URLStruct.Query() {
var err error
var match bool
for _, field := range req.URL.Query()[key] {
match, err = regexp.MatchString(value[0], field)
if err != nil {
return false, err
}
if match {
break
}
}
if !match {
return false, nil
}
}
return true, nil
}
// MatchPathParams matches the URL path parameters of the given request.
func MatchPathParams(req *http.Request, ereq *Request) (bool, error) {
for key, value := range ereq.PathParams {
var s string
if err := parth.Sequent(req.URL.Path, key, &s); err != nil {
return false, nil
}
if s != value {
return false, nil
}
}
return true, nil
}
// MatchBody tries to match the request body.
// TODO: not too smart now, needs several improvements.
func MatchBody(req *http.Request, ereq *Request) (bool, error) {
// If match body is empty, just continue
if req.Method == "HEAD" || len(ereq.BodyBuffer) == 0 {
return true, nil
}
// Only can match certain MIME body types
if !supportedType(req) {
return false, nil
}
// Can only match certain compression schemes
if !supportedCompressionScheme(req) {
return false, nil
}
// Create a reader for the body depending on compression type
bodyReader := req.Body
if ereq.CompressionScheme != "" {
if ereq.CompressionScheme != req.Header.Get("Content-Encoding") {
return false, nil
}
compressedBodyReader, err := compressionReader(req.Body, ereq.CompressionScheme)
if err != nil {
return false, err
}
bodyReader = compressedBodyReader
}
// Read the whole request body
body, err := ioutil.ReadAll(bodyReader)
if err != nil {
return false, err
}
// Restore body reader stream
req.Body = createReadCloser(body)
// If empty, ignore the match
if len(body) == 0 && len(ereq.BodyBuffer) != 0 {
return false, nil
}
// Match body by atomic string comparison
bodyStr := castToString(body)
matchStr := castToString(ereq.BodyBuffer)
if bodyStr == matchStr {
return true, nil
}
// Match request body by regexp
match, _ := regexp.MatchString(matchStr, bodyStr)
if match == true {
return true, nil
}
// todo - add conditional do only perform the conversion of body bytes
// representation of JSON to a map and then compare them for equality.
// Check if the key + value pairs match
var bodyMap map[string]interface{}
var matchMap map[string]interface{}
// Ensure that both byte bodies that that should be JSON can be converted to maps.
umErr := json.Unmarshal(body, &bodyMap)
umErr2 := json.Unmarshal(ereq.BodyBuffer, &matchMap)
if umErr == nil && umErr2 == nil && reflect.DeepEqual(bodyMap, matchMap) {
return true, nil
}
return false, nil
}
func supportedType(req *http.Request) bool {
mime := req.Header.Get("Content-Type")
if mime == "" {
return true
}
for _, kind := range BodyTypes {
if match, _ := regexp.MatchString(kind, mime); match {
return true
}
}
return false
}
func supportedCompressionScheme(req *http.Request) bool {
encoding := req.Header.Get("Content-Encoding")
if encoding == "" {
return true
}
for _, kind := range CompressionSchemes {
if match, _ := regexp.MatchString(kind, encoding); match {
return true
}
}
return false
}
func castToString(buf []byte) string {
str := string(buf)
tail := len(str) - 1
if str[tail] == EOL {
str = str[:tail]
}
return str
}
func compressionReader(r io.ReadCloser, scheme string) (io.ReadCloser, error) {
switch scheme {
case "gzip":
return gzip.NewReader(r)
default:
return r, nil
}
}

146
src/vendor/gopkg.in/h2non/gock.v1/mock.go generated vendored Normal file
View File

@ -0,0 +1,146 @@
package gock
import (
"net/http"
"sync"
)
// Mock represents the required interface that must
// be implemented by HTTP mock instances.
type Mock interface {
// Disable disables the current mock manually.
Disable()
// Done returns true if the current mock is disabled.
Done() bool
// Request returns the mock Request instance.
Request() *Request
// Response returns the mock Response instance.
Response() *Response
// Match matches the given http.Request with the current mock.
Match(*http.Request) (bool, error)
// AddMatcher adds a new matcher function.
AddMatcher(MatchFunc)
// SetMatcher uses a new matcher implementation.
SetMatcher(Matcher)
}
// Mocker implements a Mock capable interface providing
// a default mock configuration used internally to store mocks.
type Mocker struct {
// disabled stores if the current mock is disabled.
disabled bool
// mutex stores the mock mutex for thread safity.
mutex sync.Mutex
// matcher stores a Matcher capable instance to match the given http.Request.
matcher Matcher
// request stores the mock Request to match.
request *Request
// response stores the mock Response to use in case of match.
response *Response
}
// NewMock creates a new HTTP mock based on the given request and response instances.
// It's mostly used internally.
func NewMock(req *Request, res *Response) *Mocker {
mock := &Mocker{
request: req,
response: res,
matcher: DefaultMatcher.Clone(),
}
res.Mock = mock
req.Mock = mock
req.Response = res
return mock
}
// Disable disables the current mock manually.
func (m *Mocker) Disable() {
m.disabled = true
}
// Done returns true in case that the current mock
// instance is disabled and therefore must be removed.
func (m *Mocker) Done() bool {
m.mutex.Lock()
defer m.mutex.Unlock()
return m.disabled || (!m.request.Persisted && m.request.Counter == 0)
}
// Request returns the Request instance
// configured for the current HTTP mock.
func (m *Mocker) Request() *Request {
return m.request
}
// Response returns the Response instance
// configured for the current HTTP mock.
func (m *Mocker) Response() *Response {
return m.response
}
// Match matches the given http.Request with the current Request
// mock expectation, returning true if matches.
func (m *Mocker) Match(req *http.Request) (bool, error) {
if m.disabled {
return false, nil
}
// Filter
for _, filter := range m.request.Filters {
if !filter(req) {
return false, nil
}
}
// Map
for _, mapper := range m.request.Mappers {
if treq := mapper(req); treq != nil {
req = treq
}
}
// Match
matches, err := m.matcher.Match(req, m.request)
if matches {
m.decrement()
}
return matches, err
}
// SetMatcher sets a new matcher implementation
// for the current mock expectation.
func (m *Mocker) SetMatcher(matcher Matcher) {
m.matcher = matcher
}
// AddMatcher adds a new matcher function
// for the current mock expectation.
func (m *Mocker) AddMatcher(fn MatchFunc) {
m.matcher.Add(fn)
}
// decrement decrements the current mock Request counter.
func (m *Mocker) decrement() {
if m.request.Persisted {
return
}
m.mutex.Lock()
defer m.mutex.Unlock()
m.request.Counter--
if m.request.Counter == 0 {
m.disabled = true
}
}

8
src/vendor/gopkg.in/h2non/gock.v1/options.go generated vendored Normal file
View File

@ -0,0 +1,8 @@
package gock
// Options represents customized option for gock
type Options struct {
// DisableRegexpHost stores if the host is only a plain string rather than regular expression,
// if DisableRegexpHost is true, host sets in gock.New(...) will be treated as plain string
DisableRegexpHost bool
}

325
src/vendor/gopkg.in/h2non/gock.v1/request.go generated vendored Normal file
View File

@ -0,0 +1,325 @@
package gock
import (
"encoding/base64"
"io"
"io/ioutil"
"net/http"
"net/url"
"strings"
)
// MapRequestFunc represents the required function interface for request mappers.
type MapRequestFunc func(*http.Request) *http.Request
// FilterRequestFunc represents the required function interface for request filters.
type FilterRequestFunc func(*http.Request) bool
// Request represents the high-level HTTP request used to store
// request fields used to match intercepted requests.
type Request struct {
// Mock stores the parent mock reference for the current request mock used for method delegation.
Mock Mock
// Response stores the current Response instance for the current matches Request.
Response *Response
// Error stores the latest mock request configuration error.
Error error
// Counter stores the pending times that the current mock should be active.
Counter int
// Persisted stores if the current mock should be always active.
Persisted bool
// Options stores options for current Request.
Options Options
// URLStruct stores the parsed URL as *url.URL struct.
URLStruct *url.URL
// Method stores the Request HTTP method to match.
Method string
// CompressionScheme stores the Request Compression scheme to match and use for decompression.
CompressionScheme string
// Header stores the HTTP header fields to match.
Header http.Header
// Cookies stores the Request HTTP cookies values to match.
Cookies []*http.Cookie
// PathParams stores the path parameters to match.
PathParams map[string]string
// BodyBuffer stores the body data to match.
BodyBuffer []byte
// Mappers stores the request functions mappers used for matching.
Mappers []MapRequestFunc
// Filters stores the request functions filters used for matching.
Filters []FilterRequestFunc
}
// NewRequest creates a new Request instance.
func NewRequest() *Request {
return &Request{
Counter: 1,
URLStruct: &url.URL{},
Header: make(http.Header),
PathParams: make(map[string]string),
}
}
// URL defines the mock URL to match.
func (r *Request) URL(uri string) *Request {
r.URLStruct, r.Error = url.Parse(uri)
return r
}
// SetURL defines the url.URL struct to be used for matching.
func (r *Request) SetURL(u *url.URL) *Request {
r.URLStruct = u
return r
}
// Path defines the mock URL path value to match.
func (r *Request) Path(path string) *Request {
r.URLStruct.Path = path
return r
}
// Get specifies the GET method and the given URL path to match.
func (r *Request) Get(path string) *Request {
return r.method("GET", path)
}
// Post specifies the POST method and the given URL path to match.
func (r *Request) Post(path string) *Request {
return r.method("POST", path)
}
// Put specifies the PUT method and the given URL path to match.
func (r *Request) Put(path string) *Request {
return r.method("PUT", path)
}
// Delete specifies the DELETE method and the given URL path to match.
func (r *Request) Delete(path string) *Request {
return r.method("DELETE", path)
}
// Patch specifies the PATCH method and the given URL path to match.
func (r *Request) Patch(path string) *Request {
return r.method("PATCH", path)
}
// Head specifies the HEAD method and the given URL path to match.
func (r *Request) Head(path string) *Request {
return r.method("HEAD", path)
}
// method is a DRY shortcut used to declare the expected HTTP method and URL path.
func (r *Request) method(method, path string) *Request {
if path != "/" {
r.URLStruct.Path = path
}
r.Method = strings.ToUpper(method)
return r
}
// Body defines the body data to match based on a io.Reader interface.
func (r *Request) Body(body io.Reader) *Request {
r.BodyBuffer, r.Error = ioutil.ReadAll(body)
return r
}
// BodyString defines the body to match based on a given string.
func (r *Request) BodyString(body string) *Request {
r.BodyBuffer = []byte(body)
return r
}
// File defines the body to match based on the given file path string.
func (r *Request) File(path string) *Request {
r.BodyBuffer, r.Error = ioutil.ReadFile(path)
return r
}
// Compression defines the request compression scheme, and enables automatic body decompression.
// Supports only the "gzip" scheme so far.
func (r *Request) Compression(scheme string) *Request {
r.Header.Set("Content-Encoding", scheme)
r.CompressionScheme = scheme
return r
}
// JSON defines the JSON body to match based on a given structure.
func (r *Request) JSON(data interface{}) *Request {
if r.Header.Get("Content-Type") == "" {
r.Header.Set("Content-Type", "application/json")
}
r.BodyBuffer, r.Error = readAndDecode(data, "json")
return r
}
// XML defines the XML body to match based on a given structure.
func (r *Request) XML(data interface{}) *Request {
if r.Header.Get("Content-Type") == "" {
r.Header.Set("Content-Type", "application/xml")
}
r.BodyBuffer, r.Error = readAndDecode(data, "xml")
return r
}
// MatchType defines the request Content-Type MIME header field.
// Supports type alias. E.g: json, xml, form, text...
func (r *Request) MatchType(kind string) *Request {
mime := BodyTypeAliases[kind]
if mime != "" {
kind = mime
}
r.Header.Set("Content-Type", kind)
return r
}
// BasicAuth defines a username and password for HTTP Basic Authentication
func (r *Request) BasicAuth(username, password string) *Request {
r.Header.Set("Authorization", "Basic "+basicAuth(username, password))
return r
}
// MatchHeader defines a new key and value header to match.
func (r *Request) MatchHeader(key, value string) *Request {
r.Header[key] = []string{value}
return r
}
// HeaderPresent defines that a header field must be present in the request.
func (r *Request) HeaderPresent(key string) *Request {
r.Header[key] = []string{".*"}
return r
}
// MatchHeaders defines a map of key-value headers to match.
func (r *Request) MatchHeaders(headers map[string]string) *Request {
for key, value := range headers {
r.Header[key] = []string{value}
}
return r
}
// MatchParam defines a new key and value URL query param to match.
func (r *Request) MatchParam(key, value string) *Request {
query := r.URLStruct.Query()
query.Set(key, value)
r.URLStruct.RawQuery = query.Encode()
return r
}
// MatchParams defines a map of URL query param key-value to match.
func (r *Request) MatchParams(params map[string]string) *Request {
query := r.URLStruct.Query()
for key, value := range params {
query.Set(key, value)
}
r.URLStruct.RawQuery = query.Encode()
return r
}
// ParamPresent matches if the given query param key is present in the URL.
func (r *Request) ParamPresent(key string) *Request {
r.MatchParam(key, ".*")
return r
}
// PathParam matches if a given path parameter key is present in the URL.
//
// The value is representative of the restful resource the key defines, e.g.
// // /users/123/name
// r.PathParam("users", "123")
// would match.
func (r *Request) PathParam(key, val string) *Request {
r.PathParams[key] = val
return r
}
// Persist defines the current HTTP mock as persistent and won't be removed after intercepting it.
func (r *Request) Persist() *Request {
r.Persisted = true
return r
}
// WithOptions sets the options for the request.
func (r *Request) WithOptions(options Options) *Request {
r.Options = options
return r
}
// Times defines the number of times that the current HTTP mock should remain active.
func (r *Request) Times(num int) *Request {
r.Counter = num
return r
}
// AddMatcher adds a new matcher function to match the request.
func (r *Request) AddMatcher(fn MatchFunc) *Request {
r.Mock.AddMatcher(fn)
return r
}
// SetMatcher sets a new matcher function to match the request.
func (r *Request) SetMatcher(matcher Matcher) *Request {
r.Mock.SetMatcher(matcher)
return r
}
// Map adds a new request mapper function to map http.Request before the matching process.
func (r *Request) Map(fn MapRequestFunc) *Request {
r.Mappers = append(r.Mappers, fn)
return r
}
// Filter filters a new request filter function to filter http.Request before the matching process.
func (r *Request) Filter(fn FilterRequestFunc) *Request {
r.Filters = append(r.Filters, fn)
return r
}
// EnableNetworking enables the use real networking for the current mock.
func (r *Request) EnableNetworking() *Request {
if r.Response != nil {
r.Response.UseNetwork = true
}
return r
}
// Reply defines the Response status code and returns the mock Response DSL.
func (r *Request) Reply(status int) *Response {
return r.Response.Status(status)
}
// ReplyError defines the Response simulated error.
func (r *Request) ReplyError(err error) *Response {
return r.Response.SetError(err)
}
// ReplyFunc allows the developer to define the mock response via a custom function.
func (r *Request) ReplyFunc(replier func(*Response)) *Response {
replier(r.Response)
return r.Response
}
// See 2 (end of page 4) https://www.ietf.org/rfc/rfc2617.txt
// "To receive authorization, the client sends the userid and password,
// separated by a single colon (":") character, within a base64
// encoded string in the credentials."
// It is not meant to be urlencoded.
func basicAuth(username, password string) string {
auth := username + ":" + password
return base64.StdEncoding.EncodeToString([]byte(auth))
}

87
src/vendor/gopkg.in/h2non/gock.v1/responder.go generated vendored Normal file
View File

@ -0,0 +1,87 @@
package gock
import (
"bytes"
"io"
"io/ioutil"
"net/http"
"strconv"
"time"
)
// Responder builds a mock http.Response based on the given Response mock.
func Responder(req *http.Request, mock *Response, res *http.Response) (*http.Response, error) {
// If error present, reply it
err := mock.Error
if err != nil {
return nil, err
}
if res == nil {
res = createResponse(req)
}
// Apply response filter
for _, filter := range mock.Filters {
if !filter(res) {
return res, nil
}
}
// Define mock status code
if mock.StatusCode != 0 {
res.Status = strconv.Itoa(mock.StatusCode) + " " + http.StatusText(mock.StatusCode)
res.StatusCode = mock.StatusCode
}
// Define headers by merging fields
res.Header = mergeHeaders(res, mock)
// Define mock body, if present
if len(mock.BodyBuffer) > 0 {
res.ContentLength = int64(len(mock.BodyBuffer))
res.Body = createReadCloser(mock.BodyBuffer)
}
// Apply response mappers
for _, mapper := range mock.Mappers {
if tres := mapper(res); tres != nil {
res = tres
}
}
// Sleep to simulate delay, if necessary
if mock.ResponseDelay > 0 {
time.Sleep(mock.ResponseDelay)
}
return res, err
}
// createResponse creates a new http.Response with default fields.
func createResponse(req *http.Request) *http.Response {
return &http.Response{
ProtoMajor: 1,
ProtoMinor: 1,
Proto: "HTTP/1.1",
Request: req,
Header: make(http.Header),
Body: createReadCloser([]byte{}),
}
}
// mergeHeaders copies the mock headers.
func mergeHeaders(res *http.Response, mres *Response) http.Header {
for key, values := range mres.Header {
for _, value := range values {
res.Header.Add(key, value)
}
}
return res.Header
}
// createReadCloser creates an io.ReadCloser from a byte slice that is suitable for use as an
// http response body.
func createReadCloser(body []byte) io.ReadCloser {
return ioutil.NopCloser(bytes.NewReader(body))
}

186
src/vendor/gopkg.in/h2non/gock.v1/response.go generated vendored Normal file
View File

@ -0,0 +1,186 @@
package gock
import (
"bytes"
"encoding/json"
"encoding/xml"
"io"
"io/ioutil"
"net/http"
"time"
)
// MapResponseFunc represents the required function interface impletemed by response mappers.
type MapResponseFunc func(*http.Response) *http.Response
// FilterResponseFunc represents the required function interface impletemed by response filters.
type FilterResponseFunc func(*http.Response) bool
// Response represents high-level HTTP fields to configure
// and define HTTP responses intercepted by gock.
type Response struct {
// Mock stores the parent mock reference for the current response mock used for method delegation.
Mock Mock
// Error stores the latest response configuration or injected error.
Error error
// UseNetwork enables the use of real network for the current mock.
UseNetwork bool
// StatusCode stores the response status code.
StatusCode int
// Headers stores the response headers.
Header http.Header
// Cookies stores the response cookie fields.
Cookies []*http.Cookie
// BodyBuffer stores the array of bytes to use as body.
BodyBuffer []byte
// ResponseDelay stores the simulated response delay.
ResponseDelay time.Duration
// Mappers stores the request functions mappers used for matching.
Mappers []MapResponseFunc
// Filters stores the request functions filters used for matching.
Filters []FilterResponseFunc
}
// NewResponse creates a new Response.
func NewResponse() *Response {
return &Response{Header: make(http.Header)}
}
// Status defines the desired HTTP status code to reply in the current response.
func (r *Response) Status(code int) *Response {
r.StatusCode = code
return r
}
// Type defines the response Content-Type MIME header field.
// Supports type alias. E.g: json, xml, form, text...
func (r *Response) Type(kind string) *Response {
mime := BodyTypeAliases[kind]
if mime != "" {
kind = mime
}
r.Header.Set("Content-Type", kind)
return r
}
// SetHeader sets a new header field in the mock response.
func (r *Response) SetHeader(key, value string) *Response {
r.Header.Set(key, value)
return r
}
// AddHeader adds a new header field in the mock response
// with out removing an existent one.
func (r *Response) AddHeader(key, value string) *Response {
r.Header.Add(key, value)
return r
}
// SetHeaders sets a map of header fields in the mock response.
func (r *Response) SetHeaders(headers map[string]string) *Response {
for key, value := range headers {
r.Header.Add(key, value)
}
return r
}
// Body sets the HTTP response body to be used.
func (r *Response) Body(body io.Reader) *Response {
r.BodyBuffer, r.Error = ioutil.ReadAll(body)
return r
}
// BodyString defines the response body as string.
func (r *Response) BodyString(body string) *Response {
r.BodyBuffer = []byte(body)
return r
}
// File defines the response body reading the data
// from disk based on the file path string.
func (r *Response) File(path string) *Response {
r.BodyBuffer, r.Error = ioutil.ReadFile(path)
return r
}
// JSON defines the response body based on a JSON based input.
func (r *Response) JSON(data interface{}) *Response {
r.Header.Set("Content-Type", "application/json")
r.BodyBuffer, r.Error = readAndDecode(data, "json")
return r
}
// XML defines the response body based on a XML based input.
func (r *Response) XML(data interface{}) *Response {
r.Header.Set("Content-Type", "application/xml")
r.BodyBuffer, r.Error = readAndDecode(data, "xml")
return r
}
// SetError defines the response simulated error.
func (r *Response) SetError(err error) *Response {
r.Error = err
return r
}
// Delay defines the response simulated delay.
// This feature is still experimental and will be improved in the future.
func (r *Response) Delay(delay time.Duration) *Response {
r.ResponseDelay = delay
return r
}
// Map adds a new response mapper function to map http.Response before the matching process.
func (r *Response) Map(fn MapResponseFunc) *Response {
r.Mappers = append(r.Mappers, fn)
return r
}
// Filter filters a new request filter function to filter http.Request before the matching process.
func (r *Response) Filter(fn FilterResponseFunc) *Response {
r.Filters = append(r.Filters, fn)
return r
}
// EnableNetworking enables the use real networking for the current mock.
func (r *Response) EnableNetworking() *Response {
r.UseNetwork = true
return r
}
// Done returns true if the mock was done and disabled.
func (r *Response) Done() bool {
return r.Mock.Done()
}
func readAndDecode(data interface{}, kind string) ([]byte, error) {
buf := &bytes.Buffer{}
switch data.(type) {
case string:
buf.WriteString(data.(string))
case []byte:
buf.Write(data.([]byte))
default:
var err error
if kind == "xml" {
err = xml.NewEncoder(buf).Encode(data)
} else {
err = json.NewEncoder(buf).Encode(data)
}
if err != nil {
return nil, err
}
}
return ioutil.ReadAll(buf)
}

100
src/vendor/gopkg.in/h2non/gock.v1/store.go generated vendored Normal file
View File

@ -0,0 +1,100 @@
package gock
import (
"sync"
)
// storeMutex is used interally for store synchronization.
var storeMutex = sync.RWMutex{}
// mocks is internally used to store registered mocks.
var mocks = []Mock{}
// Register registers a new mock in the current mocks stack.
func Register(mock Mock) {
if Exists(mock) {
return
}
// Make ops thread safe
storeMutex.Lock()
defer storeMutex.Unlock()
// Expose mock in request/response for delegation
mock.Request().Mock = mock
mock.Response().Mock = mock
// Registers the mock in the global store
mocks = append(mocks, mock)
}
// GetAll returns the current stack of registed mocks.
func GetAll() []Mock {
storeMutex.RLock()
defer storeMutex.RUnlock()
return mocks
}
// Exists checks if the given Mock is already registered.
func Exists(m Mock) bool {
storeMutex.RLock()
defer storeMutex.RUnlock()
for _, mock := range mocks {
if mock == m {
return true
}
}
return false
}
// Remove removes a registered mock by reference.
func Remove(m Mock) {
for i, mock := range mocks {
if mock == m {
storeMutex.Lock()
mocks = append(mocks[:i], mocks[i+1:]...)
storeMutex.Unlock()
}
}
}
// Flush flushes the current stack of registered mocks.
func Flush() {
storeMutex.Lock()
defer storeMutex.Unlock()
mocks = []Mock{}
}
// Pending returns an slice of pending mocks.
func Pending() []Mock {
Clean()
storeMutex.RLock()
defer storeMutex.RUnlock()
return mocks
}
// IsDone returns true if all the registered mocks has been triggered successfully.
func IsDone() bool {
return !IsPending()
}
// IsPending returns true if there are pending mocks.
func IsPending() bool {
return len(Pending()) > 0
}
// Clean cleans the mocks store removing disabled or obsolete mocks.
func Clean() {
storeMutex.Lock()
defer storeMutex.Unlock()
buf := []Mock{}
for _, mock := range mocks {
if mock.Done() {
continue
}
buf = append(buf, mock)
}
mocks = buf
}

112
src/vendor/gopkg.in/h2non/gock.v1/transport.go generated vendored Normal file
View File

@ -0,0 +1,112 @@
package gock
import (
"errors"
"net/http"
"sync"
)
// var mutex *sync.Mutex = &sync.Mutex{}
var (
// DefaultTransport stores the default mock transport used by gock.
DefaultTransport = NewTransport()
// NativeTransport stores the native net/http default transport
// in order to restore it when needed.
NativeTransport = http.DefaultTransport
)
var (
// ErrCannotMatch store the error returned in case of no matches.
ErrCannotMatch = errors.New("gock: cannot match any request")
)
// Transport implements http.RoundTripper, which fulfills single http requests issued by
// an http.Client.
//
// gock's Transport encapsulates a given or default http.Transport for further
// delegation, if needed.
type Transport struct {
// mutex is used to make transport thread-safe of concurrent uses across goroutines.
mutex sync.Mutex
// Transport encapsulates the original http.RoundTripper transport interface for delegation.
Transport http.RoundTripper
}
// NewTransport creates a new *Transport with no responders.
func NewTransport() *Transport {
return &Transport{Transport: NativeTransport}
}
// RoundTrip receives HTTP requests and routes them to the appropriate responder. It is required to
// implement the http.RoundTripper interface. You will not interact with this directly, instead
// the *http.Client you are using will call it for you.
func (m *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
// Just act as a proxy if not intercepting
if !Intercepting() {
return m.Transport.RoundTrip(req)
}
m.mutex.Lock()
defer Clean()
var err error
var res *http.Response
// Match mock for the incoming http.Request
mock, err := MatchMock(req)
if err != nil {
m.mutex.Unlock()
return nil, err
}
// Invoke the observer with the intercepted http.Request and matched mock
if config.Observer != nil {
config.Observer(req, mock)
}
// Verify if should use real networking
networking := shouldUseNetwork(req, mock)
if !networking && mock == nil {
m.mutex.Unlock()
trackUnmatchedRequest(req)
return nil, ErrCannotMatch
}
// Ensure me unlock the mutex before building the response
m.mutex.Unlock()
// Perform real networking via original transport
if networking {
res, err = m.Transport.RoundTrip(req)
// In no mock matched, continue with the response
if err != nil || mock == nil {
return res, err
}
}
return Responder(req, mock.Response(), res)
}
// CancelRequest is a no-op function.
func (m *Transport) CancelRequest(req *http.Request) {}
func shouldUseNetwork(req *http.Request, mock Mock) bool {
if mock != nil && mock.Response().UseNetwork {
return true
}
if !config.Networking {
return false
}
if len(config.NetworkingFilters) == 0 {
return true
}
for _, filter := range config.NetworkingFilters {
if !filter(req) {
return false
}
}
return true
}

4
src/vendor/gopkg.in/h2non/gock.v1/version.go generated vendored Normal file
View File

@ -0,0 +1,4 @@
package gock
// Version defines the current package semantic version.
const Version = "1.0.16"

View File

@ -342,6 +342,8 @@ github.com/graph-gophers/dataloader
# github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7
github.com/gregjones/httpcache
github.com/gregjones/httpcache/diskcache
# github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542
github.com/h2non/parth
# github.com/hashicorp/errwrap v1.0.0
github.com/hashicorp/errwrap
# github.com/hashicorp/go-multierror v1.1.0
@ -673,6 +675,9 @@ google.golang.org/protobuf/types/known/timestamppb
## explicit
# gopkg.in/gorethink/gorethink.v3 v3.0.5
## explicit
# gopkg.in/h2non/gock.v1 v1.0.16
## explicit
gopkg.in/h2non/gock.v1
# gopkg.in/inf.v0 v0.9.1
gopkg.in/inf.v0
# gopkg.in/ini.v1 v1.51.0