mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-26 10:38:00 +01:00
test: mock the API requests for the huawei registry (#15009)
Signed-off-by: He Weiwei <hweiwei@vmware.com>
This commit is contained in:
parent
77b44a62e7
commit
c7a4af3170
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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=
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
22
src/vendor/github.com/h2non/parth/LICENSE
generated
vendored
Normal 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
196
src/vendor/github.com/h2non/parth/README.md
generated
vendored
Normal 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
1
src/vendor/github.com/h2non/parth/go.mod
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
module github.com/h2non/parth
|
349
src/vendor/github.com/h2non/parth/parth.go
generated
vendored
Normal file
349
src/vendor/github.com/h2non/parth/parth.go
generated
vendored
Normal 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
118
src/vendor/github.com/h2non/parth/segindex.go
generated
vendored
Normal 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
305
src/vendor/github.com/h2non/parth/segtostr.go
generated
vendored
Normal 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
30
src/vendor/gopkg.in/h2non/gock.v1/.gitignore
generated
vendored
Normal 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
29
src/vendor/gopkg.in/h2non/gock.v1/.travis.yml
generated
vendored
Normal 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
128
src/vendor/gopkg.in/h2non/gock.v1/History.md
generated
vendored
Normal 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
24
src/vendor/gopkg.in/h2non/gock.v1/LICENSE
generated
vendored
Normal 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
373
src/vendor/gopkg.in/h2non/gock.v1/README.md
generated
vendored
Normal 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
8
src/vendor/gopkg.in/h2non/gock.v1/go.mod
generated
vendored
Normal 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
178
src/vendor/gopkg.in/h2non/gock.v1/gock.go
generated
vendored
Normal 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
137
src/vendor/gopkg.in/h2non/gock.v1/matcher.go
generated
vendored
Normal 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
261
src/vendor/gopkg.in/h2non/gock.v1/matchers.go
generated
vendored
Normal 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
146
src/vendor/gopkg.in/h2non/gock.v1/mock.go
generated
vendored
Normal 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
8
src/vendor/gopkg.in/h2non/gock.v1/options.go
generated
vendored
Normal 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
325
src/vendor/gopkg.in/h2non/gock.v1/request.go
generated
vendored
Normal 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
87
src/vendor/gopkg.in/h2non/gock.v1/responder.go
generated
vendored
Normal 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
186
src/vendor/gopkg.in/h2non/gock.v1/response.go
generated
vendored
Normal 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
100
src/vendor/gopkg.in/h2non/gock.v1/store.go
generated
vendored
Normal 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
112
src/vendor/gopkg.in/h2non/gock.v1/transport.go
generated
vendored
Normal 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
4
src/vendor/gopkg.in/h2non/gock.v1/version.go
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
package gock
|
||||
|
||||
// Version defines the current package semantic version.
|
||||
const Version = "1.0.16"
|
5
src/vendor/modules.txt
vendored
5
src/vendor/modules.txt
vendored
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user