pax_global_header00006660000000000000000000000064141022576150014515gustar00rootroot0000000000000052 comment=5b72870cdcba531fee7998aae31e18b1bcabf103 gock-1.1.2/000077500000000000000000000000001410225761500124415ustar00rootroot00000000000000gock-1.1.2/.editorconfig000066400000000000000000000002721410225761500151170ustar00rootroot00000000000000root = true [*] indent_style = tab indent_size = 2 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.md] trim_trailing_whitespace = false gock-1.1.2/.gitignore000066400000000000000000000004531410225761500144330ustar00rootroot00000000000000# 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 gock-1.1.2/.travis.yml000066400000000000000000000013511410225761500145520ustar00rootroot00000000000000language: go go: - "1.15" - "1.14" - "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 gock-1.1.2/History.md000066400000000000000000000072271410225761500144340ustar00rootroot00000000000000## v1.1.2 / 2021-08-03 * fix(mock): fix race condition in mock.go file (#92) ## v1.1.1 / 2021-07-14 * feat(matchers): Support custom MIME types (#88) ## v1.1.0 / 2021-06-02 * Add context expiration cancellation support (#86) ## 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. gock-1.1.2/LICENSE000066400000000000000000000020701410225761500134450ustar00rootroot00000000000000The 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. gock-1.1.2/README.md000066400000000000000000000233311410225761500137220ustar00rootroot00000000000000# 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 timeout/cancelation delay simulation. - 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 gock-1.1.2/_examples/000077500000000000000000000000001410225761500144165ustar00rootroot00000000000000gock-1.1.2/_examples/README.md000066400000000000000000000007521410225761500157010ustar00rootroot00000000000000# Examples gock's examples showing multiple built-in features and common practical use cases. ### Set up ```bash go get -u github.com/nbio/st ``` ```bash go get -u gopkg.in/h2non/gock.v1 ``` ### Usage #### Test based examples ```bash go test ./_examples/ ``` ##### Example ```bash go test ./_examples/basic ``` #### Executable examples ```bash go run ./_examples//.go ``` ##### Example ```bash go run ./_examples/enable_networking/networking.go ``` gock-1.1.2/_examples/add_matchers/000077500000000000000000000000001410225761500170345ustar00rootroot00000000000000gock-1.1.2/_examples/add_matchers/matchers.go000066400000000000000000000011671410225761500211760ustar00rootroot00000000000000package main import ( "fmt" "net/http" "gopkg.in/h2non/gock.v1" ) func main() { defer gock.Off() gock.New("http://httpbin.org"). Get("/"). AddMatcher(func(req *http.Request, ereq *gock.Request) (bool, error) { return req.URL.Scheme == "http", nil }). AddMatcher(func(req *http.Request, ereq *gock.Request) (bool, error) { return req.Method == ereq.Method, nil }). Reply(204). SetHeader("Server", "gock") res, err := http.Get("http://httpbin.org/get") if err != nil { fmt.Errorf("Error: %s", err) } fmt.Printf("Status: %d\n", res.StatusCode) fmt.Printf("Server header: %s\n", res.Header.Get("Server")) } gock-1.1.2/_examples/basic/000077500000000000000000000000001410225761500154775ustar00rootroot00000000000000gock-1.1.2/_examples/basic/basic_test.go000066400000000000000000000010251410225761500201440ustar00rootroot00000000000000package 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) } gock-1.1.2/_examples/body_file/000077500000000000000000000000001410225761500163525ustar00rootroot00000000000000gock-1.1.2/_examples/body_file/data.json000066400000000000000000000000161410225761500201530ustar00rootroot00000000000000{"foo":"bar"} gock-1.1.2/_examples/body_file/file_test.go000066400000000000000000000011021410225761500206510ustar00rootroot00000000000000package test import ( "bytes" "github.com/nbio/st" "gopkg.in/h2non/gock.v1" "io/ioutil" "net/http" "testing" ) func TestMockBodyFile(t *testing.T) { defer gock.Off() gock.New("http://foo.com"). Post("/bar"). MatchType("json"). File("data.json"). Reply(201). File("response.json") 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"}`) } gock-1.1.2/_examples/body_file/response.json000066400000000000000000000000161410225761500211000ustar00rootroot00000000000000{"bar":"foo"} gock-1.1.2/_examples/body_match/000077500000000000000000000000001410225761500165275ustar00rootroot00000000000000gock-1.1.2/_examples/body_match/body_test.go000066400000000000000000000011441410225761500210520ustar00rootroot00000000000000package 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"}`) } gock-1.1.2/_examples/cannot_match/000077500000000000000000000000001410225761500170545ustar00rootroot00000000000000gock-1.1.2/_examples/cannot_match/cannot_match.go000066400000000000000000000005031410225761500220370ustar00rootroot00000000000000package main import ( "fmt" "net/http" "gopkg.in/h2non/gock.v1" ) func main() { // gock enabled but cannot match any mock gock.New("http://httpbin.org"). Get("/foo"). Reply(201). SetHeader("Server", "gock") _, err := http.Get("http://httpbin.org/bar") if err != nil { fmt.Printf("Error: %s\n", err) } } gock-1.1.2/_examples/clean/000077500000000000000000000000001410225761500155005ustar00rootroot00000000000000gock-1.1.2/_examples/clean/clean.go000066400000000000000000000014251410225761500171130ustar00rootroot00000000000000package main import ( "fmt" "gopkg.in/h2non/gock.v1" "net/http" ) func main() { defer gock.Disable() defer gock.Flush() // Flush all the registered mocks, including the pending ones. defer gock.Clean() // Clean all the finished mocks, but keeping the pending ones. // defer gock.Off() -> Or you can simply call Off() method gock.New("http://httpbin.org"). Get("/get"). Filter(func(req *http.Request) bool { return req.URL.Host == "httpbin.org" }). Filter(func(req *http.Request) bool { return req.URL.Path == "/get" }). Reply(204). SetHeader("Server", "gock") res, err := http.Get("http://httpbin.org/get") if err != nil { fmt.Errorf("Error: %s", err) } fmt.Printf("Status: %d\n", res.StatusCode) fmt.Printf("Server header: %s\n", res.Header.Get("Server")) } gock-1.1.2/_examples/compressed_body/000077500000000000000000000000001410225761500175775ustar00rootroot00000000000000gock-1.1.2/_examples/compressed_body/compression_test.go000066400000000000000000000015471410225761500235350ustar00rootroot00000000000000package test import ( "bytes" "compress/gzip" "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"). Compression("gzip"). JSON(map[string]string{"foo": "bar"}). Reply(201). JSON(map[string]string{"bar": "foo"}) var compressed bytes.Buffer w := gzip.NewWriter(&compressed) w.Write([]byte(`{"foo":"bar"}`)) w.Close() req, err := http.NewRequest("POST", "http://foo.com/bar", &compressed) st.Expect(t, err, nil) req.Header.Set("Content-Encoding", "gzip") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) st.Expect(t, err, nil) st.Expect(t, res.StatusCode, 201) resBody, _ := ioutil.ReadAll(res.Body) st.Expect(t, string(resBody)[:13], `{"bar":"foo"}`) } gock-1.1.2/_examples/custom_client/000077500000000000000000000000001410225761500172665ustar00rootroot00000000000000gock-1.1.2/_examples/custom_client/client_test.go000066400000000000000000000010321410225761500221260ustar00rootroot00000000000000package 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") } gock-1.1.2/_examples/custom_matcher/000077500000000000000000000000001410225761500174335ustar00rootroot00000000000000gock-1.1.2/_examples/custom_matcher/matcher.go000066400000000000000000000012651410225761500214110ustar00rootroot00000000000000package main import ( "fmt" "gopkg.in/h2non/gock.v1" "net/http" ) func main() { defer gock.Off() // Create a new custom matcher with HTTP headers only matchers matcher := gock.NewBasicMatcher() // Add a custom match function matcher.Add(func(req *http.Request, ereq *gock.Request) (bool, error) { return req.URL.Scheme == "http", nil }) // Define the mock gock.New("http://httpbin.org"). SetMatcher(matcher). Get("/"). Reply(204). SetHeader("Server", "gock") res, err := http.Get("http://httpbin.org/get") if err != nil { fmt.Errorf("Error: %s", err) } fmt.Printf("Status: %d\n", res.StatusCode) fmt.Printf("Server header: %s\n", res.Header.Get("Server")) } gock-1.1.2/_examples/filters/000077500000000000000000000000001410225761500160665ustar00rootroot00000000000000gock-1.1.2/_examples/filters/filter.go000066400000000000000000000010571410225761500177050ustar00rootroot00000000000000package main import ( "fmt" "gopkg.in/h2non/gock.v1" "net/http" ) func main() { defer gock.Off() gock.New("http://httpbin.org"). Get("/get"). Filter(func(req *http.Request) bool { return req.URL.Host == "httpbin.org" }). Filter(func(req *http.Request) bool { return req.URL.Path == "/get" }). Reply(204). SetHeader("Server", "gock") res, err := http.Get("http://httpbin.org/get") if err != nil { fmt.Errorf("Error: %s", err) } fmt.Printf("Status: %d\n", res.StatusCode) fmt.Printf("Server header: %s\n", res.Header.Get("Server")) } gock-1.1.2/_examples/gentleman/000077500000000000000000000000001410225761500163705ustar00rootroot00000000000000gock-1.1.2/_examples/gentleman/gentleman.go000066400000000000000000000013451410225761500206740ustar00rootroot00000000000000package main import ( "fmt" "gopkg.in/h2non/gentleman.v1" "gopkg.in/h2non/gentleman.v1/context" "gopkg.in/h2non/gock.v1" ) // Usege example with gentleman HTTP client toolkit. // See also: https://github.com/h2non/gentleman-mock func main() { defer gock.Off() gock.New("http://httpbin.org"). Get("/*"). Reply(204). SetHeader("Server", "gock") cli := gentleman.New() cli.UseHandler("before dial", func(ctx *context.Context, h context.Handler) { gock.InterceptClient(ctx.Client) h.Next(ctx) }) res, err := cli.Request().URL("http://httpbin.org/get").Send() if err != nil { fmt.Errorf("Error: %s", err) } fmt.Printf("Status: %d\n", res.StatusCode) fmt.Printf("Server header: %s\n", res.Header.Get("Server")) } gock-1.1.2/_examples/mapper/000077500000000000000000000000001410225761500157025ustar00rootroot00000000000000gock-1.1.2/_examples/mapper/map.go000066400000000000000000000011011410225761500167770ustar00rootroot00000000000000package main import ( "fmt" "gopkg.in/h2non/gock.v1" "net/http" ) func main() { defer gock.Disable() gock.New("http://httpbin.org"). Get("/"). Map(func(req *http.Request) *http.Request { req.URL.Host = "httpbin.org"; return req }). Map(func(req *http.Request) *http.Request { req.URL.Path = "/"; return req }). Reply(204). SetHeader("Server", "gock") res, err := http.Get("http://httpbin.org/get") if err != nil { fmt.Errorf("Error: %s", err) } fmt.Printf("Status: %d\n", res.StatusCode) fmt.Printf("Server header: %s\n", res.Header.Get("Server")) } gock-1.1.2/_examples/match_headers/000077500000000000000000000000001410225761500172055ustar00rootroot00000000000000gock-1.1.2/_examples/match_headers/headers_test.go000066400000000000000000000012671410225761500222140ustar00rootroot00000000000000package test import ( "io/ioutil" "net/http" "testing" "github.com/nbio/st" "gopkg.in/h2non/gock.v1" ) func TestMatchHeaders(t *testing.T) { defer gock.Disable() 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") } gock-1.1.2/_examples/match_query/000077500000000000000000000000001410225761500167375ustar00rootroot00000000000000gock-1.1.2/_examples/match_query/query_test.go000066400000000000000000000011061410225761500214700ustar00rootroot00000000000000package test import ( "github.com/nbio/st" "gopkg.in/h2non/gock.v1" "io/ioutil" "net/http" "testing" ) func TestMatchQueryParams(t *testing.T) { defer gock.Disable() gock.New("http://foo.com"). MatchParam("foo", "^bar$"). MatchParam("bar", "baz"). ParamPresent("baz"). Reply(200). BodyString("foo foo") req, err := http.NewRequest("GET", "http://foo.com?foo=bar&bar=baz&baz=foo", 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") } gock-1.1.2/_examples/match_url/000077500000000000000000000000001410225761500163745ustar00rootroot00000000000000gock-1.1.2/_examples/match_url/url_test.go000066400000000000000000000006351410225761500205700ustar00rootroot00000000000000package test import ( "github.com/nbio/st" "gopkg.in/h2non/gock.v1" "io/ioutil" "net/http" "testing" ) func TestMatchURL(t *testing.T) { defer gock.Disable() gock.New("http://(.*).com"). Reply(200). BodyString("foo foo") res, err := http.Get("http://foo.com") st.Expect(t, err, nil) st.Expect(t, res.StatusCode, 200) body, _ := ioutil.ReadAll(res.Body) st.Expect(t, string(body), "foo foo") } gock-1.1.2/_examples/multiple/000077500000000000000000000000001410225761500162515ustar00rootroot00000000000000gock-1.1.2/_examples/multiple/multiple_test.go000066400000000000000000000016411410225761500214740ustar00rootroot00000000000000package test import ( "github.com/nbio/st" "gopkg.in/h2non/gock.v1" "io/ioutil" "net/http" "testing" ) func TestMultipleMocks(t *testing.T) { defer gock.Disable() gock.New("http://server.com"). Get("/foo"). Reply(200). JSON(map[string]string{"value": "foo"}) gock.New("http://server.com"). Get("/bar"). Reply(200). JSON(map[string]string{"value": "bar"}) gock.New("http://server.com"). Get("/baz"). Reply(200). JSON(map[string]string{"value": "baz"}) tests := []struct { path string }{ {"/foo"}, {"/bar"}, {"/baz"}, } for _, test := range tests { res, err := http.Get("http://server.com" + test.path) st.Expect(t, err, nil) st.Expect(t, res.StatusCode, 200) body, _ := ioutil.ReadAll(res.Body) st.Expect(t, string(body)[:15], `{"value":"`+test.path[1:]+`"}`) } // Failed request after mocks expires _, err := http.Get("http://server.com/foo") st.Reject(t, err, nil) } gock-1.1.2/_examples/networking/000077500000000000000000000000001410225761500166055ustar00rootroot00000000000000gock-1.1.2/_examples/networking/networking.go000066400000000000000000000012461410225761500213260ustar00rootroot00000000000000package main import ( "fmt" "io/ioutil" "net/http" "gopkg.in/h2non/gock.v1" ) func main() { defer gock.Disable() 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.Printf("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)) } gock-1.1.2/_examples/networking_filters/000077500000000000000000000000001410225761500203355ustar00rootroot00000000000000gock-1.1.2/_examples/networking_filters/filters.go000066400000000000000000000015211410225761500223330ustar00rootroot00000000000000package main import ( "fmt" "io/ioutil" "net/http" "gopkg.in/h2non/gock.v1" ) func main() { defer gock.Disable() defer gock.DisableNetworking() defer gock.DisableNetworkingFilters() gock.EnableNetworking() // Register a networking filter gock.NetworkingFilter(func(req *http.Request) bool { return req.URL.Host == "httpbin.org" }) gock.New("http://httpbin.org"). Get("/get"). Reply(201). SetHeader("Server", "gock") res, err := http.Get("http://httpbin.org/get") if err != nil { fmt.Printf("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)) } gock-1.1.2/_examples/networking_partially_enabled/000077500000000000000000000000001410225761500223405ustar00rootroot00000000000000gock-1.1.2/_examples/networking_partially_enabled/networking.go000066400000000000000000000033331410225761500250600ustar00rootroot00000000000000// This example shows how to enable the networking for a request to a local server // and mock a second request to a remote server. package main import ( "fmt" "io" "io/ioutil" "net/http" "net/http/httptest" "gopkg.in/h2non/gock.v1" ) // Starts a local HTTP server in background func startHTTPServer() *httptest.Server { return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Println("Local server received a GET request") res, err := http.Get("http://httpbin.org/nope") if err != nil { msg := fmt.Sprintf("Error from request to httpbin: %s", err) http.Error(w, msg, http.StatusInternalServerError) return } body, _ := ioutil.ReadAll(res.Body) // MUST NOT get original body since the networking // wasn't enabled for this request fmt.Printf("Body From httpbin: %s\n", string(body)) fmt.Printf("Status From httpbin: %s\n", res.Status) io.WriteString(w, "Local Response="+res.Header.Get("Server")) })) } func main() { defer gock.Disable() defer gock.DisableNetworking() srv := startHTTPServer() defer srv.Close() // Register our local server gock.New(srv.URL). EnableNetworking() gock.New("http://httpbin.org"). Get("/nope"). Reply(201). SetHeader("Server", "gock") res, err := http.Get(srv.URL) if err != nil { fmt.Printf("Error from request to localhost: %s", err) return } // 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")) // MUST get original response since the networking was enabled for this request body, _ := ioutil.ReadAll(res.Body) fmt.Printf("Body From Local Server: %s", string(body)) } gock-1.1.2/_examples/observe/000077500000000000000000000000001410225761500160635ustar00rootroot00000000000000gock-1.1.2/_examples/observe/observe.go000066400000000000000000000005621410225761500200620ustar00rootroot00000000000000package 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) } gock-1.1.2/_examples/pending/000077500000000000000000000000001410225761500160425ustar00rootroot00000000000000gock-1.1.2/_examples/pending/pending.go000066400000000000000000000010541410225761500200150ustar00rootroot00000000000000package main import ( "fmt" "gopkg.in/h2non/gock.v1" "net/http" ) func main() { defer gock.Off() gock.New("http://httpbin.org"). Get("/get"). Reply(204). SetHeader("Server", "gock") fmt.Printf("Pending mocks before request: %d\n", len(gock.Pending())) fmt.Printf("Is pending before request: %#v\n", gock.IsPending()) _, err := http.Get("http://httpbin.org/get") if err != nil { fmt.Errorf("Error: %s", err) } fmt.Printf("Pending mocks after request: %d\n", len(gock.Pending())) fmt.Printf("Is pending: %#v\n", gock.IsPending()) } gock-1.1.2/_examples/persistent/000077500000000000000000000000001410225761500166165ustar00rootroot00000000000000gock-1.1.2/_examples/persistent/persistent_test.go000066400000000000000000000007721410225761500224120ustar00rootroot00000000000000package test import ( "github.com/nbio/st" "gopkg.in/h2non/gock.v1" "io/ioutil" "net/http" "testing" ) func TestPersistent(t *testing.T) { defer gock.Disable() gock.New("http://foo.com"). Get("/bar"). Persist(). Reply(200). JSON(map[string]string{"foo": "bar"}) for i := 0; i < 5; i++ { 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"}`) } } gock-1.1.2/_examples/persistent/times_test.go000066400000000000000000000010701410225761500213230ustar00rootroot00000000000000package test import ( "github.com/nbio/st" "gopkg.in/h2non/gock.v1" "io/ioutil" "net/http" "testing" ) func TestTimes(t *testing.T) { defer gock.Disable() gock.New("http://127.0.0.1:1234"). Get("/bar"). Times(4). Reply(200). JSON(map[string]string{"foo": "bar"}) for i := 0; i < 5; i++ { res, err := http.Get("http://127.0.0.1:1234/bar") if i == 4 { st.Reject(t, err, nil) break } st.Expect(t, err, nil) st.Expect(t, res.StatusCode, 200) body, _ := ioutil.ReadAll(res.Body) st.Expect(t, string(body)[:13], `{"foo":"bar"}`) } } gock-1.1.2/_examples/regexp_matching/000077500000000000000000000000001410225761500175625ustar00rootroot00000000000000gock-1.1.2/_examples/regexp_matching/regexp_test.go000066400000000000000000000014071410225761500224440ustar00rootroot00000000000000package test import ( "bytes" "github.com/nbio/st" "gopkg.in/h2non/gock.v1" "io/ioutil" "net/http" "testing" ) func TestRegExpMatching(t *testing.T) { defer gock.Disable() gock.New("http://foo.com"). Post("/bar"). MatchHeader("Authorization", "Bearer (.*)"). BodyString(`{"foo":".*"}`). Reply(200). SetHeader("Server", "gock"). JSON(map[string]string{"foo": "bar"}) req, _ := http.NewRequest("POST", "http://foo.com/bar", bytes.NewBuffer([]byte(`{"foo":"baz"}`))) req.Header.Set("Authorization", "Bearer s3cr3t") res, err := http.DefaultClient.Do(req) st.Expect(t, err, nil) st.Expect(t, res.StatusCode, 200) st.Expect(t, res.Header.Get("Server"), "gock") body, _ := ioutil.ReadAll(res.Body) st.Expect(t, string(body)[:13], `{"foo":"bar"}`) } gock-1.1.2/_examples/reply_error/000077500000000000000000000000001410225761500167625ustar00rootroot00000000000000gock-1.1.2/_examples/reply_error/reply_error_test.go000066400000000000000000000007041410225761500227150ustar00rootroot00000000000000package test import ( "errors" "net/http" "strings" "testing" "github.com/nbio/st" "gopkg.in/h2non/gock.v1" ) func TestReplyError(t *testing.T) { defer gock.Off() gock.New("http://foo.com"). Get("/bar"). ReplyError(errors.New("Error dude!")) _, err := http.Get("http://foo.com/bar") st.Expect(t, strings.HasSuffix(err.Error(), ": Error dude!"), true) // Verify that we don't have pending mocks st.Expect(t, gock.IsDone(), true) } gock-1.1.2/go.mod000066400000000000000000000002461410225761500135510ustar00rootroot00000000000000module gopkg.in/h2non/gock.v1 go 1.13 require ( github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 ) gock-1.1.2/gock.go000066400000000000000000000107431410225761500137200ustar00rootroot00000000000000package 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 } gock-1.1.2/gock_test.go000066400000000000000000000261401410225761500147550ustar00rootroot00000000000000package gock import ( "bytes" "compress/gzip" "fmt" "io/ioutil" "net/http" "net/http/httptest" "strings" "testing" "github.com/nbio/st" ) func TestMockSimple(t *testing.T) { defer after() New("http://foo.com").Reply(201).JSON(map[string]string{"foo": "bar"}) res, err := http.Get("http://foo.com") st.Expect(t, err, nil) st.Expect(t, res.StatusCode, 201) body, _ := ioutil.ReadAll(res.Body) st.Expect(t, string(body)[:13], `{"foo":"bar"}`) } func TestMockOff(t *testing.T) { New("http://foo.com").Reply(201).JSON(map[string]string{"foo": "bar"}) Off() _, err := http.Get("http://127.0.0.1:3123") st.Reject(t, err, nil) } func TestMockBodyStringResponse(t *testing.T) { defer after() New("http://foo.com").Reply(200).BodyString("foo bar") res, err := http.Get("http://foo.com") st.Expect(t, err, nil) st.Expect(t, res.StatusCode, 200) body, _ := ioutil.ReadAll(res.Body) st.Expect(t, string(body), "foo bar") } func TestMockBodyMatch(t *testing.T) { defer after() New("http://foo.com").BodyString("foo bar").Reply(201).BodyString("foo foo") res, err := http.Post("http://foo.com", "text/plain", bytes.NewBuffer([]byte("foo bar"))) st.Expect(t, err, nil) st.Expect(t, res.StatusCode, 201) body, _ := ioutil.ReadAll(res.Body) st.Expect(t, string(body), "foo foo") } func TestMockBodyCannotMatch(t *testing.T) { defer after() New("http://foo.com").BodyString("foo foo").Reply(201).BodyString("foo foo") _, err := http.Post("http://foo.com", "text/plain", bytes.NewBuffer([]byte("foo bar"))) st.Reject(t, err, nil) } func TestMockBodyMatchCompressed(t *testing.T) { defer after() New("http://foo.com").Compression("gzip").BodyString("foo bar").Reply(201).BodyString("foo foo") var compressed bytes.Buffer w := gzip.NewWriter(&compressed) w.Write([]byte("foo bar")) w.Close() req, err := http.NewRequest("POST", "http://foo.com", &compressed) st.Expect(t, err, nil) req.Header.Set("Content-Encoding", "gzip") req.Header.Set("Content-Type", "text/plain") res, err := http.DefaultClient.Do(req) st.Expect(t, err, nil) st.Expect(t, res.StatusCode, 201) body, _ := ioutil.ReadAll(res.Body) st.Expect(t, string(body), "foo foo") } func TestMockBodyCannotMatchCompressed(t *testing.T) { defer after() New("http://foo.com").Compression("gzip").BodyString("foo bar").Reply(201).BodyString("foo foo") _, err := http.Post("http://foo.com", "text/plain", bytes.NewBuffer([]byte("foo bar"))) st.Reject(t, err, nil) } func TestMockBodyMatchJSON(t *testing.T) { defer after() New("http://foo.com"). Post("/bar"). JSON(map[string]string{"foo": "bar"}). Reply(201). JSON(map[string]string{"bar": "foo"}) res, err := http.Post("http://foo.com/bar", "application/json", bytes.NewBuffer([]byte(`{"foo":"bar"}`))) st.Expect(t, err, nil) st.Expect(t, res.StatusCode, 201) body, _ := ioutil.ReadAll(res.Body) st.Expect(t, string(body)[:13], `{"bar":"foo"}`) } func TestMockBodyCannotMatchJSON(t *testing.T) { defer after() New("http://foo.com"). Post("/bar"). JSON(map[string]string{"bar": "bar"}). Reply(201). JSON(map[string]string{"bar": "foo"}) _, err := http.Post("http://foo.com/bar", "application/json", bytes.NewBuffer([]byte(`{"foo":"bar"}`))) st.Reject(t, err, nil) } func TestMockBodyMatchCompressedJSON(t *testing.T) { defer after() New("http://foo.com"). Post("/bar"). Compression("gzip"). JSON(map[string]string{"foo": "bar"}). Reply(201). JSON(map[string]string{"bar": "foo"}) var compressed bytes.Buffer w := gzip.NewWriter(&compressed) w.Write([]byte(`{"foo":"bar"}`)) w.Close() req, err := http.NewRequest("POST", "http://foo.com/bar", &compressed) st.Expect(t, err, nil) req.Header.Set("Content-Encoding", "gzip") req.Header.Set("Content-Type", "application/json") res, err := http.DefaultClient.Do(req) st.Expect(t, err, nil) st.Expect(t, res.StatusCode, 201) body, _ := ioutil.ReadAll(res.Body) st.Expect(t, string(body)[:13], `{"bar":"foo"}`) } func TestMockBodyCannotMatchCompressedJSON(t *testing.T) { defer after() New("http://foo.com"). Post("/bar"). JSON(map[string]string{"bar": "bar"}). Reply(201). JSON(map[string]string{"bar": "foo"}) var compressed bytes.Buffer w := gzip.NewWriter(&compressed) w.Write([]byte(`{"foo":"bar"}`)) w.Close() req, err := http.NewRequest("POST", "http://foo.com/bar", &compressed) st.Expect(t, err, nil) req.Header.Set("Content-Encoding", "gzip") req.Header.Set("Content-Type", "application/json") _, err = http.DefaultClient.Do(req) st.Reject(t, err, nil) } func TestMockMatchHeaders(t *testing.T) { defer after() New("http://foo.com"). MatchHeader("Content-Type", "(.*)/plain"). Reply(200). BodyString("foo foo") res, err := http.Post("http://foo.com", "text/plain", bytes.NewBuffer([]byte("foo bar"))) st.Expect(t, err, nil) st.Expect(t, res.StatusCode, 200) body, _ := ioutil.ReadAll(res.Body) st.Expect(t, string(body), "foo foo") } func TestMockMap(t *testing.T) { defer after() mock := New("http://bar.com") mock.Map(func(req *http.Request) *http.Request { req.URL.Host = "bar.com" return req }) mock.Reply(201).JSON(map[string]string{"foo": "bar"}) res, err := http.Get("http://foo.com") st.Expect(t, err, nil) st.Expect(t, res.StatusCode, 201) body, _ := ioutil.ReadAll(res.Body) st.Expect(t, string(body)[:13], `{"foo":"bar"}`) } func TestMockFilter(t *testing.T) { defer after() mock := New("http://foo.com") mock.Filter(func(req *http.Request) bool { return req.URL.Host == "foo.com" }) mock.Reply(201).JSON(map[string]string{"foo": "bar"}) res, err := http.Get("http://foo.com") st.Expect(t, err, nil) st.Expect(t, res.StatusCode, 201) body, _ := ioutil.ReadAll(res.Body) st.Expect(t, string(body)[:13], `{"foo":"bar"}`) } func TestMockCounterDisabled(t *testing.T) { defer after() New("http://foo.com").Reply(204) st.Expect(t, len(GetAll()), 1) res, err := http.Get("http://foo.com") st.Expect(t, err, nil) st.Expect(t, res.StatusCode, 204) st.Expect(t, len(GetAll()), 0) } func TestMockEnableNetwork(t *testing.T) { defer after() ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Hello, world") })) defer ts.Close() EnableNetworking() defer DisableNetworking() New(ts.URL).Reply(204) st.Expect(t, len(GetAll()), 1) res, err := http.Get(ts.URL) st.Expect(t, err, nil) st.Expect(t, res.StatusCode, 204) st.Expect(t, len(GetAll()), 0) res, err = http.Get(ts.URL) st.Expect(t, err, nil) st.Expect(t, res.StatusCode, 200) } func TestMockEnableNetworkFilter(t *testing.T) { defer after() ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Hello, world") })) defer ts.Close() EnableNetworking() defer DisableNetworking() NetworkingFilter(func(req *http.Request) bool { return strings.Contains(req.URL.Host, "127.0.0.1") }) defer DisableNetworkingFilters() New(ts.URL).Reply(0).SetHeader("foo", "bar") st.Expect(t, len(GetAll()), 1) res, err := http.Get(ts.URL) st.Expect(t, err, nil) st.Expect(t, res.StatusCode, 200) st.Expect(t, res.Header.Get("foo"), "bar") st.Expect(t, len(GetAll()), 0) } func TestMockPersistent(t *testing.T) { defer after() New("http://foo.com"). Get("/bar"). Persist(). Reply(200). JSON(map[string]string{"foo": "bar"}) for i := 0; i < 5; i++ { 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"}`) } } func TestMockPersistTimes(t *testing.T) { defer after() New("http://127.0.0.1:1234"). Get("/bar"). Times(4). Reply(200). JSON(map[string]string{"foo": "bar"}) for i := 0; i < 5; i++ { res, err := http.Get("http://127.0.0.1:1234/bar") if i == 4 { st.Reject(t, err, nil) break } st.Expect(t, err, nil) st.Expect(t, res.StatusCode, 200) body, _ := ioutil.ReadAll(res.Body) st.Expect(t, string(body)[:13], `{"foo":"bar"}`) } } func TestUnmatched(t *testing.T) { defer after() // clear out any unmatchedRequests from other tests unmatchedRequests = []*http.Request{} Intercept() _, err := http.Get("http://server.com/unmatched") st.Reject(t, err, nil) unmatched := GetUnmatchedRequests() st.Expect(t, len(unmatched), 1) st.Expect(t, unmatched[0].URL.Host, "server.com") st.Expect(t, unmatched[0].URL.Path, "/unmatched") st.Expect(t, HasUnmatchedRequest(), true) } func TestMultipleMocks(t *testing.T) { defer Disable() New("http://server.com"). Get("/foo"). Reply(200). JSON(map[string]string{"value": "foo"}) New("http://server.com"). Get("/bar"). Reply(200). JSON(map[string]string{"value": "bar"}) New("http://server.com"). Get("/baz"). Reply(200). JSON(map[string]string{"value": "baz"}) tests := []struct { path string }{ {"/foo"}, {"/bar"}, {"/baz"}, } for _, test := range tests { res, err := http.Get("http://server.com" + test.path) st.Expect(t, err, nil) st.Expect(t, res.StatusCode, 200) body, _ := ioutil.ReadAll(res.Body) st.Expect(t, string(body)[:15], `{"value":"`+test.path[1:]+`"}`) } _, err := http.Get("http://server.com/foo") st.Reject(t, err, nil) } func TestInterceptClient(t *testing.T) { defer after() New("http://foo.com").Reply(204) st.Expect(t, len(GetAll()), 1) req, err := http.NewRequest("GET", "http://foo.com", nil) client := &http.Client{Transport: &http.Transport{}} InterceptClient(client) res, err := client.Do(req) st.Expect(t, err, nil) st.Expect(t, res.StatusCode, 204) } func TestRestoreClient(t *testing.T) { defer after() New("http://foo.com").Reply(204) st.Expect(t, len(GetAll()), 1) req, err := http.NewRequest("GET", "http://foo.com", nil) client := &http.Client{Transport: &http.Transport{}} InterceptClient(client) trans := client.Transport res, err := client.Do(req) st.Expect(t, err, nil) st.Expect(t, res.StatusCode, 204) RestoreClient(client) st.Reject(t, trans, client.Transport) } func TestMockRegExpMatching(t *testing.T) { defer after() New("http://foo.com"). Post("/bar"). MatchHeader("Authorization", "Bearer (.*)"). BodyString(`{"foo":".*"}`). Reply(200). SetHeader("Server", "gock"). JSON(map[string]string{"foo": "bar"}) req, _ := http.NewRequest("POST", "http://foo.com/bar", bytes.NewBuffer([]byte(`{"foo":"baz"}`))) req.Header.Set("Authorization", "Bearer s3cr3t") res, err := http.DefaultClient.Do(req) st.Expect(t, err, nil) st.Expect(t, res.StatusCode, 200) st.Expect(t, res.Header.Get("Server"), "gock") body, _ := ioutil.ReadAll(res.Body) st.Expect(t, string(body)[:13], `{"foo":"bar"}`) } func TestObserve(t *testing.T) { defer after() var observedRequest *http.Request var observedMock Mock Observe(func(request *http.Request, mock Mock) { observedRequest = request observedMock = mock }) New("http://observe-foo.com").Reply(200) req, _ := http.NewRequest("POST", "http://observe-foo.com", nil) http.DefaultClient.Do(req) st.Expect(t, observedRequest.Host, "observe-foo.com") st.Expect(t, observedMock.Request().URLStruct.Host, "observe-foo.com") } func TestTryCreatingRacesInNew(t *testing.T) { defer after() for i := 0; i < 10; i++ { go func() { New("http://example.com") }() } } func after() { Flush() Disable() } gock-1.1.2/matcher.go000066400000000000000000000064231410225761500144200ustar00rootroot00000000000000package 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 } gock-1.1.2/matcher_test.go000066400000000000000000000102141410225761500154500ustar00rootroot00000000000000package gock import ( "net/http" "net/url" "testing" "github.com/nbio/st" ) func TestRegisteredMatchers(t *testing.T) { st.Expect(t, len(MatchersHeader), 7) st.Expect(t, len(MatchersBody), 1) } func TestNewMatcher(t *testing.T) { matcher := NewMatcher() // Funcs are not comparable, checking slice length as it's better than nothing // See https://golang.org/pkg/reflect/#DeepEqual st.Expect(t, len(matcher.Matchers), len(Matchers)) st.Expect(t, len(matcher.Get()), len(Matchers)) } func TestNewBasicMatcher(t *testing.T) { matcher := NewBasicMatcher() // Funcs are not comparable, checking slice length as it's better than nothing // See https://golang.org/pkg/reflect/#DeepEqual st.Expect(t, len(matcher.Matchers), len(MatchersHeader)) st.Expect(t, len(matcher.Get()), len(MatchersHeader)) } func TestNewEmptyMatcher(t *testing.T) { matcher := NewEmptyMatcher() st.Expect(t, len(matcher.Matchers), 0) st.Expect(t, len(matcher.Get()), 0) } func TestMatcherAdd(t *testing.T) { matcher := NewMatcher() st.Expect(t, len(matcher.Matchers), len(Matchers)) matcher.Add(func(req *http.Request, ereq *Request) (bool, error) { return true, nil }) st.Expect(t, len(matcher.Get()), len(Matchers)+1) } func TestMatcherSet(t *testing.T) { matcher := NewMatcher() matchers := []MatchFunc{} st.Expect(t, len(matcher.Matchers), len(Matchers)) matcher.Set(matchers) st.Expect(t, matcher.Matchers, matchers) st.Expect(t, len(matcher.Get()), 0) } func TestMatcherGet(t *testing.T) { matcher := NewMatcher() matchers := []MatchFunc{} matcher.Set(matchers) st.Expect(t, matcher.Get(), matchers) } func TestMatcherFlush(t *testing.T) { matcher := NewMatcher() st.Expect(t, len(matcher.Matchers), len(Matchers)) matcher.Add(func(req *http.Request, ereq *Request) (bool, error) { return true, nil }) st.Expect(t, len(matcher.Get()), len(Matchers)+1) matcher.Flush() st.Expect(t, len(matcher.Get()), 0) } func TestMatcherClone(t *testing.T) { matcher := DefaultMatcher.Clone() st.Expect(t, len(matcher.Get()), len(DefaultMatcher.Get())) } func TestMatcher(t *testing.T) { cases := []struct { method string url string matches bool }{ {"GET", "http://foo.com/bar", true}, {"GET", "http://foo.com/baz", true}, {"GET", "http://foo.com/foo", false}, {"POST", "http://foo.com/bar", false}, {"POST", "http://bar.com/bar", false}, {"GET", "http://foo.com", false}, } matcher := NewMatcher() matcher.Flush() st.Expect(t, len(matcher.Matchers), 0) matcher.Add(func(req *http.Request, ereq *Request) (bool, error) { return req.Method == "GET", nil }) matcher.Add(func(req *http.Request, ereq *Request) (bool, error) { return req.URL.Host == "foo.com", nil }) matcher.Add(func(req *http.Request, ereq *Request) (bool, error) { return req.URL.Path == "/baz" || req.URL.Path == "/bar", nil }) for _, test := range cases { u, _ := url.Parse(test.url) req := &http.Request{Method: test.method, URL: u} matches, err := matcher.Match(req, nil) st.Expect(t, err, nil) st.Expect(t, matches, test.matches) } } func TestMatchMock(t *testing.T) { cases := []struct { method string url string matches bool }{ {"GET", "http://foo.com/bar", true}, {"GET", "http://foo.com/baz", true}, {"GET", "http://foo.com/foo", false}, {"POST", "http://foo.com/bar", false}, {"POST", "http://bar.com/bar", false}, {"GET", "http://foo.com", false}, } matcher := DefaultMatcher matcher.Flush() st.Expect(t, len(matcher.Matchers), 0) matcher.Add(func(req *http.Request, ereq *Request) (bool, error) { return req.Method == "GET", nil }) matcher.Add(func(req *http.Request, ereq *Request) (bool, error) { return req.URL.Host == "foo.com", nil }) matcher.Add(func(req *http.Request, ereq *Request) (bool, error) { return req.URL.Path == "/baz" || req.URL.Path == "/bar", nil }) for _, test := range cases { Flush() mock := New(test.url).method(test.method, "").Mock u, _ := url.Parse(test.url) req := &http.Request{Method: test.method, URL: u} match, err := MatchMock(req) st.Expect(t, err, nil) if test.matches { st.Expect(t, match, mock) } else { st.Expect(t, match, nil) } } DefaultMatcher.Matchers = Matchers } gock-1.1.2/matchers.go000066400000000000000000000144031410225761500146000ustar00rootroot00000000000000package 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, ereq) { 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, ereq *Request) bool { mime := req.Header.Get("Content-Type") if mime == "" { return true } mimeToMatch := ereq.Header.Get("Content-Type") if mimeToMatch != "" { return mime == mimeToMatch } 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 } } gock-1.1.2/matchers_test.go000066400000000000000000000162321410225761500156410ustar00rootroot00000000000000package gock import ( "net/http" "net/url" "testing" "github.com/nbio/st" ) func TestMatchMethod(t *testing.T) { cases := []struct { value string method string matches bool }{ {"GET", "GET", true}, {"POST", "POST", true}, {"", "POST", true}, {"POST", "GET", false}, {"PUT", "GET", false}, } for _, test := range cases { req := &http.Request{Method: test.method} ereq := &Request{Method: test.value} matches, err := MatchMethod(req, ereq) st.Expect(t, err, nil) st.Expect(t, matches, test.matches) } } func TestMatchScheme(t *testing.T) { cases := []struct { value string scheme string matches bool }{ {"http", "http", true}, {"https", "https", true}, {"http", "https", false}, {"", "https", true}, {"https", "", true}, } for _, test := range cases { req := &http.Request{URL: &url.URL{Scheme: test.scheme}} ereq := &Request{URLStruct: &url.URL{Scheme: test.value}} matches, err := MatchScheme(req, ereq) st.Expect(t, err, nil) st.Expect(t, matches, test.matches) } } func TestMatchHost(t *testing.T) { cases := []struct { value string url string matches bool matchesNonRegexp bool }{ {"foo.com", "foo.com", true, true}, {"FOO.com", "foo.com", true, true}, {"foo.net", "foo.com", false, false}, {"foo.bar.net", "foo-bar.net", true, false}, {"foo", "foo.com", true, false}, {"(.*).com", "foo.com", true, false}, {"127.0.0.1", "127.0.0.1", true, true}, {"127.0.0.2", "127.0.0.1", false, false}, {"127.0.0.*", "127.0.0.1", true, false}, {"127.0.0.[0-9]", "127.0.0.7", true, false}, } for _, test := range cases { req := &http.Request{URL: &url.URL{Host: test.url}} ereq := &Request{URLStruct: &url.URL{Host: test.value}} matches, err := MatchHost(req, ereq) st.Expect(t, err, nil) st.Expect(t, matches, test.matches) ereq.WithOptions(Options{DisableRegexpHost: true}) matches, err = MatchHost(req, ereq) st.Expect(t, err, nil) st.Expect(t, matches, test.matchesNonRegexp) } } func TestMatchPath(t *testing.T) { cases := []struct { value string path string matches bool }{ {"/foo", "/foo", true}, {"/foo", "/foo/bar", true}, {"bar", "/foo/bar", true}, {"foo", "/foo/bar", true}, {"bar$", "/foo/bar", true}, {"/foo/*", "/foo/bar", true}, {"/foo/[a-z]+", "/foo/bar", true}, {"/foo/baz", "/foo/bar", false}, {"/foo/baz", "/foo/bar", false}, {"/foo/bar%3F+%C3%A9", "/foo/bar%3F+%C3%A9", true}, } for _, test := range cases { u, _ := url.Parse("http://foo.com" + test.path) mu, _ := url.Parse("http://foo.com" + test.value) req := &http.Request{URL: u} ereq := &Request{URLStruct: mu} matches, err := MatchPath(req, ereq) st.Expect(t, err, nil) st.Expect(t, matches, test.matches) } } func TestMatchHeaders(t *testing.T) { cases := []struct { values http.Header headers http.Header matches bool }{ {http.Header{"foo": []string{"bar"}}, http.Header{"foo": []string{"bar"}}, true}, {http.Header{"foo": []string{"bar"}}, http.Header{"foo": []string{"barbar"}}, true}, {http.Header{"bar": []string{"bar"}}, http.Header{"foo": []string{"bar"}}, false}, {http.Header{"foofoo": []string{"bar"}}, http.Header{"foo": []string{"bar"}}, false}, {http.Header{"foo": []string{"bar(.*)"}}, http.Header{"foo": []string{"barbar"}}, true}, {http.Header{"foo": []string{"b(.*)"}}, http.Header{"foo": []string{"barbar"}}, true}, {http.Header{"foo": []string{"^bar$"}}, http.Header{"foo": []string{"bar"}}, true}, {http.Header{"foo": []string{"^bar$"}}, http.Header{"foo": []string{"barbar"}}, false}, {http.Header{"UPPERCASE": []string{"bar"}}, http.Header{"UPPERCASE": []string{"bar"}}, true}, {http.Header{"Mixed-CASE": []string{"bar"}}, http.Header{"Mixed-CASE": []string{"bar"}}, true}, {http.Header{"User-Agent": []string{"Agent (version1.0)"}}, http.Header{"User-Agent": []string{"Agent (version1.0)"}}, true}, {http.Header{"Content-Type": []string{"(.*)/plain"}}, http.Header{"Content-Type": []string{"text/plain"}}, true}, } for _, test := range cases { req := &http.Request{Header: test.headers} ereq := &Request{Header: test.values} matches, err := MatchHeaders(req, ereq) st.Expect(t, err, nil) st.Expect(t, matches, test.matches) } } func TestMatchQueryParams(t *testing.T) { cases := []struct { value string path string matches bool }{ {"foo=bar", "foo=bar", true}, {"foo=bar", "foo=foo&foo=bar", true}, {"foo=b*", "foo=bar", true}, {"foo=.*", "foo=bar", true}, {"foo=f[o]{2}", "foo=foo", true}, {"foo=bar&bar=foo", "foo=bar&foo=foo&bar=foo", true}, {"foo=", "foo=bar", true}, {"foo=foo", "foo=bar", false}, {"bar=bar", "foo=bar bar", false}, } for _, test := range cases { u, _ := url.Parse("http://foo.com/?" + test.path) mu, _ := url.Parse("http://foo.com/?" + test.value) req := &http.Request{URL: u} ereq := &Request{URLStruct: mu} matches, err := MatchQueryParams(req, ereq) st.Expect(t, err, nil) st.Expect(t, matches, test.matches) } } func TestMatchPathParams(t *testing.T) { cases := []struct { key string value string path string matches bool }{ {"foo", "bar", "/foo/bar", true}, {"foo", "bar", "/foo/test/bar", false}, {"foo", "bar", "/test/foo/bar/ack", true}, {"foo", "bar", "/foo", false}, } for i, test := range cases { u, _ := url.Parse("http://foo.com" + test.path) mu, _ := url.Parse("http://foo.com" + test.path) req := &http.Request{URL: u} ereq := &Request{ URLStruct: mu, PathParams: map[string]string{test.key: test.value}, } matches, err := MatchPathParams(req, ereq) st.Expect(t, err, nil, i) st.Expect(t, matches, test.matches, i) } } func TestMatchBody(t *testing.T) { cases := []struct { value string body string matches bool }{ {"foo bar", "foo bar\n", true}, {"foo", "foo bar\n", true}, {"f[o]+", "foo\n", true}, {`"foo"`, `{"foo":"bar"}\n`, true}, {`{"foo":"bar"}`, `{"foo":"bar"}\n`, true}, {`{"foo":"foo"}`, `{"foo":"bar"}\n`, false}, {`{"foo":"bar","bar":"foo"}`, `{"bar":"foo","foo":"bar"}`, true}, {`{"bar":"foo","foo":{"two":"three","three":"two"}}`, `{"foo":{"three":"two","two":"three"},"bar":"foo"}`, true}, } for _, test := range cases { req := &http.Request{Body: createReadCloser([]byte(test.body))} ereq := &Request{BodyBuffer: []byte(test.value)} matches, err := MatchBody(req, ereq) st.Expect(t, err, nil) st.Expect(t, matches, test.matches) } } func TestMatchBody_MatchType(t *testing.T) { body := `{"foo":"bar"}` cases := []struct { body string requestContentType string customBodyType string matches bool }{ {body, "application/vnd.apiname.v1+json", "foobar", false}, {body, "application/vnd.apiname.v1+json", "application/vnd.apiname.v1+json", true}, {body, "application/json", "foobar", false}, {body, "application/json", "", true}, {"", "", "", true}, } for _, test := range cases { req := &http.Request{ Header: http.Header{"Content-Type": []string{test.requestContentType}}, Body: createReadCloser([]byte(test.body)), } ereq := NewRequest().BodyString(test.body).MatchType(test.customBodyType) matches, err := MatchBody(req, ereq) st.Expect(t, err, nil) st.Expect(t, matches, test.matches) } } gock-1.1.2/mock.go000066400000000000000000000072741410225761500137330ustar00rootroot00000000000000package 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 { // disabler stores a disabler for thread safety checking current mock is disabled disabler *disabler // mutex stores the mock mutex for thread safety. 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 } type disabler struct { // disabled stores if the current mock is disabled. disabled bool // mutex stores the disabler mutex for thread safety. mutex sync.RWMutex } func (d *disabler) isDisabled() bool { d.mutex.RLock() defer d.mutex.RUnlock() return d.disabled } func (d *disabler) Disable() { d.mutex.Lock() defer d.mutex.Unlock() d.disabled = true } // 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{ disabler: new(disabler), 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.disabler.Disable() } // Done returns true in case that the current mock // instance is disabled and therefore must be removed. func (m *Mocker) Done() bool { // prevent deadlock with m.mutex if m.disabler.isDisabled() { return true } m.mutex.Lock() defer m.mutex.Unlock() return !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.disabler.isDisabled() { 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.disabler.Disable() } } gock-1.1.2/mock_test.go000066400000000000000000000060641410225761500147660ustar00rootroot00000000000000package gock import ( "net/http" "testing" "github.com/nbio/st" ) func TestNewMock(t *testing.T) { defer after() req := NewRequest() res := NewResponse() mock := NewMock(req, res) st.Expect(t, mock.disabler.isDisabled(), false) st.Expect(t, len(mock.matcher.Get()), len(DefaultMatcher.Get())) st.Expect(t, mock.Request(), req) st.Expect(t, mock.Request().Mock, mock) st.Expect(t, mock.Response(), res) st.Expect(t, mock.Response().Mock, mock) } func TestMockDisable(t *testing.T) { defer after() req := NewRequest() res := NewResponse() mock := NewMock(req, res) st.Expect(t, mock.disabler.isDisabled(), false) mock.Disable() st.Expect(t, mock.disabler.isDisabled(), true) matches, err := mock.Match(&http.Request{}) st.Expect(t, err, nil) st.Expect(t, matches, false) } func TestMockDone(t *testing.T) { defer after() req := NewRequest() res := NewResponse() mock := NewMock(req, res) st.Expect(t, mock.disabler.isDisabled(), false) st.Expect(t, mock.Done(), false) mock = NewMock(req, res) st.Expect(t, mock.disabler.isDisabled(), false) mock.Disable() st.Expect(t, mock.Done(), true) mock = NewMock(req, res) st.Expect(t, mock.disabler.isDisabled(), false) mock.request.Counter = 0 st.Expect(t, mock.Done(), true) mock = NewMock(req, res) st.Expect(t, mock.disabler.isDisabled(), false) mock.request.Persisted = true st.Expect(t, mock.Done(), false) } func TestMockSetMatcher(t *testing.T) { defer after() req := NewRequest() res := NewResponse() mock := NewMock(req, res) st.Expect(t, len(mock.matcher.Get()), len(DefaultMatcher.Get())) matcher := NewMatcher() matcher.Flush() matcher.Add(func(req *http.Request, ereq *Request) (bool, error) { return true, nil }) mock.SetMatcher(matcher) st.Expect(t, len(mock.matcher.Get()), 1) st.Expect(t, mock.disabler.isDisabled(), false) matches, err := mock.Match(&http.Request{}) st.Expect(t, err, nil) st.Expect(t, matches, true) } func TestMockAddMatcher(t *testing.T) { defer after() req := NewRequest() res := NewResponse() mock := NewMock(req, res) st.Expect(t, len(mock.matcher.Get()), len(DefaultMatcher.Get())) matcher := NewMatcher() matcher.Flush() mock.SetMatcher(matcher) mock.AddMatcher(func(req *http.Request, ereq *Request) (bool, error) { return true, nil }) st.Expect(t, mock.disabler.isDisabled(), false) st.Expect(t, mock.matcher, matcher) matches, err := mock.Match(&http.Request{}) st.Expect(t, err, nil) st.Expect(t, matches, true) } func TestMockMatch(t *testing.T) { defer after() req := NewRequest() res := NewResponse() mock := NewMock(req, res) matcher := NewMatcher() matcher.Flush() mock.SetMatcher(matcher) calls := 0 mock.AddMatcher(func(req *http.Request, ereq *Request) (bool, error) { calls++ return true, nil }) mock.AddMatcher(func(req *http.Request, ereq *Request) (bool, error) { calls++ return true, nil }) st.Expect(t, mock.disabler.isDisabled(), false) st.Expect(t, mock.matcher, matcher) matches, err := mock.Match(&http.Request{}) st.Expect(t, err, nil) st.Expect(t, calls, 2) st.Expect(t, matches, true) } gock-1.1.2/options.go000066400000000000000000000004541410225761500144660ustar00rootroot00000000000000package 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 } gock-1.1.2/request.go000066400000000000000000000220761410225761500144670ustar00rootroot00000000000000package 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 custom MIME types and type aliases. 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.Set(key, value) return r } // HeaderPresent defines that a header field must be present in the request. func (r *Request) HeaderPresent(key string) *Request { r.Header.Set(key, ".*") 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.Set(key, 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)) } gock-1.1.2/request_test.go000066400000000000000000000160271410225761500155250ustar00rootroot00000000000000package gock import ( "bytes" "net/http" "net/url" "testing" "github.com/nbio/st" ) func TestNewRequest(t *testing.T) { req := NewRequest() req.URL("http://foo.com") st.Expect(t, req.URLStruct.Host, "foo.com") st.Expect(t, req.URLStruct.Scheme, "http") req.MatchHeader("foo", "bar") st.Expect(t, req.Header.Get("foo"), "bar") } func TestRequestSetURL(t *testing.T) { req := NewRequest() req.URL("http://foo.com") req.SetURL(&url.URL{Host: "bar.com", Path: "/foo"}) st.Expect(t, req.URLStruct.Host, "bar.com") st.Expect(t, req.URLStruct.Path, "/foo") } func TestRequestPath(t *testing.T) { req := NewRequest() req.URL("http://foo.com") req.Path("/foo") st.Expect(t, req.URLStruct.Scheme, "http") st.Expect(t, req.URLStruct.Host, "foo.com") st.Expect(t, req.URLStruct.Path, "/foo") } func TestRequestBody(t *testing.T) { req := NewRequest() req.Body(bytes.NewBuffer([]byte("foo bar"))) st.Expect(t, string(req.BodyBuffer), "foo bar") } func TestRequestBodyString(t *testing.T) { req := NewRequest() req.BodyString("foo bar") st.Expect(t, string(req.BodyBuffer), "foo bar") } func TestRequestFile(t *testing.T) { req := NewRequest() req.File("version.go") st.Expect(t, string(req.BodyBuffer)[:12], "package gock") } func TestRequestJSON(t *testing.T) { req := NewRequest() req.JSON(map[string]string{"foo": "bar"}) st.Expect(t, string(req.BodyBuffer)[:13], `{"foo":"bar"}`) st.Expect(t, req.Header.Get("Content-Type"), "application/json") } func TestRequestXML(t *testing.T) { req := NewRequest() type xml struct { Data string `xml:"data"` } req.XML(xml{Data: "foo"}) st.Expect(t, string(req.BodyBuffer), `foo`) st.Expect(t, req.Header.Get("Content-Type"), "application/xml") } func TestRequestMatchType(t *testing.T) { req := NewRequest() req.MatchType("json") st.Expect(t, req.Header.Get("Content-Type"), "application/json") req = NewRequest() req.MatchType("html") st.Expect(t, req.Header.Get("Content-Type"), "text/html") req = NewRequest() req.MatchType("foo/bar") st.Expect(t, req.Header.Get("Content-Type"), "foo/bar") } func TestRequestBasicAuth(t *testing.T) { req := NewRequest() req.BasicAuth("bob", "qwerty") st.Expect(t, req.Header.Get("Authorization"), "Basic Ym9iOnF3ZXJ0eQ==") } func TestRequestMatchHeader(t *testing.T) { req := NewRequest() req.MatchHeader("foo", "bar") req.MatchHeader("bar", "baz") req.MatchHeader("UPPERCASE", "bat") req.MatchHeader("Mixed-CASE", "foo") st.Expect(t, req.Header.Get("foo"), "bar") st.Expect(t, req.Header.Get("bar"), "baz") st.Expect(t, req.Header.Get("UPPERCASE"), "bat") st.Expect(t, req.Header.Get("Mixed-CASE"), "foo") } func TestRequestHeaderPresent(t *testing.T) { req := NewRequest() req.HeaderPresent("foo") req.HeaderPresent("bar") req.HeaderPresent("UPPERCASE") req.HeaderPresent("Mixed-CASE") st.Expect(t, req.Header.Get("foo"), ".*") st.Expect(t, req.Header.Get("bar"), ".*") st.Expect(t, req.Header.Get("UPPERCASE"), ".*") st.Expect(t, req.Header.Get("Mixed-CASE"), ".*") } func TestRequestMatchParam(t *testing.T) { req := NewRequest() req.MatchParam("foo", "bar") req.MatchParam("bar", "baz") st.Expect(t, req.URLStruct.Query().Get("foo"), "bar") st.Expect(t, req.URLStruct.Query().Get("bar"), "baz") } func TestRequestMatchParams(t *testing.T) { req := NewRequest() req.MatchParams(map[string]string{"foo": "bar", "bar": "baz"}) st.Expect(t, req.URLStruct.Query().Get("foo"), "bar") st.Expect(t, req.URLStruct.Query().Get("bar"), "baz") } func TestRequestPresentParam(t *testing.T) { req := NewRequest() req.ParamPresent("key") st.Expect(t, req.URLStruct.Query().Get("key"), ".*") } func TestRequestPathParam(t *testing.T) { req := NewRequest() req.PathParam("key", "value") st.Expect(t, req.PathParams["key"], "value") } func TestRequestPersist(t *testing.T) { req := NewRequest() st.Expect(t, req.Persisted, false) req.Persist() st.Expect(t, req.Persisted, true) } func TestRequestTimes(t *testing.T) { req := NewRequest() st.Expect(t, req.Counter, 1) req.Times(3) st.Expect(t, req.Counter, 3) } func TestRequestMap(t *testing.T) { req := NewRequest() st.Expect(t, len(req.Mappers), 0) req.Map(func(req *http.Request) *http.Request { return req }) st.Expect(t, len(req.Mappers), 1) } func TestRequestFilter(t *testing.T) { req := NewRequest() st.Expect(t, len(req.Filters), 0) req.Filter(func(req *http.Request) bool { return true }) st.Expect(t, len(req.Filters), 1) } func TestRequestEnableNetworking(t *testing.T) { req := NewRequest() req.Response = &Response{} st.Expect(t, req.Response.UseNetwork, false) req.EnableNetworking() st.Expect(t, req.Response.UseNetwork, true) } func TestRequestResponse(t *testing.T) { req := NewRequest() res := NewResponse() req.Response = res chain := req.Reply(200) st.Expect(t, chain, res) st.Expect(t, chain.StatusCode, 200) } func TestRequestReplyFunc(t *testing.T) { req := NewRequest() res := NewResponse() req.Response = res chain := req.ReplyFunc(func(r *Response) { r.Status(204) }) st.Expect(t, chain, res) st.Expect(t, chain.StatusCode, 204) } func TestRequestMethods(t *testing.T) { req := NewRequest() req.Get("/foo") st.Expect(t, req.Method, "GET") st.Expect(t, req.URLStruct.Path, "/foo") req = NewRequest() req.Post("/foo") st.Expect(t, req.Method, "POST") st.Expect(t, req.URLStruct.Path, "/foo") req = NewRequest() req.Put("/foo") st.Expect(t, req.Method, "PUT") st.Expect(t, req.URLStruct.Path, "/foo") req = NewRequest() req.Delete("/foo") st.Expect(t, req.Method, "DELETE") st.Expect(t, req.URLStruct.Path, "/foo") req = NewRequest() req.Patch("/foo") st.Expect(t, req.Method, "PATCH") st.Expect(t, req.URLStruct.Path, "/foo") req = NewRequest() req.Head("/foo") st.Expect(t, req.Method, "HEAD") st.Expect(t, req.URLStruct.Path, "/foo") } func TestRequestSetMatcher(t *testing.T) { defer after() matcher := NewEmptyMatcher() matcher.Add(func(req *http.Request, ereq *Request) (bool, error) { return req.URL.Host == "foo.com", nil }) matcher.Add(func(req *http.Request, ereq *Request) (bool, error) { return req.Header.Get("foo") == "bar", nil }) ereq := NewRequest() mock := NewMock(ereq, &Response{}) mock.SetMatcher(matcher) ereq.Mock = mock headers := make(http.Header) headers.Set("foo", "bar") req := &http.Request{ URL: &url.URL{Host: "foo.com", Path: "/bar"}, Header: headers, } match, err := ereq.Mock.Match(req) st.Expect(t, err, nil) st.Expect(t, match, true) } func TestRequestAddMatcher(t *testing.T) { defer after() ereq := NewRequest() mock := NewMock(ereq, &Response{}) mock.matcher = NewMatcher() ereq.Mock = mock ereq.AddMatcher(func(req *http.Request, ereq *Request) (bool, error) { return req.URL.Host == "foo.com", nil }) ereq.AddMatcher(func(req *http.Request, ereq *Request) (bool, error) { return req.Header.Get("foo") == "bar", nil }) headers := make(http.Header) headers.Set("foo", "bar") req := &http.Request{ URL: &url.URL{Host: "foo.com", Path: "/bar"}, Header: headers, } match, err := ereq.Mock.Match(req) st.Expect(t, err, nil) st.Expect(t, match, true) } gock-1.1.2/responder.go000066400000000000000000000050101410225761500147650ustar00rootroot00000000000000package 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 { // allow escaping from sleep due to request context expiration or cancellation t := time.NewTimer(mock.ResponseDelay) select { case <-t.C: case <-req.Context().Done(): // cleanly stop the timer if !t.Stop() { <-t.C } } } // check if the request context has ended. we could put this up in the delay code above, but putting it here // has the added benefit of working even when there is no delay (very small timeouts, already-done contexts, etc.) if err = req.Context().Err(); err != nil { // cleanly close the response and return the context error io.Copy(ioutil.Discard, res.Body) res.Body.Close() return nil, err } 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)) } gock-1.1.2/responder_test.go000066400000000000000000000063461410225761500160410ustar00rootroot00000000000000package gock import ( "context" "errors" "io/ioutil" "net/http" "testing" "time" "github.com/nbio/st" ) func TestResponder(t *testing.T) { defer after() mres := New("http://foo.com").Reply(200).BodyString("foo") req := &http.Request{} res, err := Responder(req, mres, nil) st.Expect(t, err, nil) st.Expect(t, res.Status, "200 OK") st.Expect(t, res.StatusCode, 200) body, _ := ioutil.ReadAll(res.Body) st.Expect(t, string(body), "foo") } func TestResponder_ReadTwice(t *testing.T) { defer after() mres := New("http://foo.com").Reply(200).BodyString("foo") req := &http.Request{} res, err := Responder(req, mres, nil) st.Expect(t, err, nil) st.Expect(t, res.Status, "200 OK") st.Expect(t, res.StatusCode, 200) body, _ := ioutil.ReadAll(res.Body) st.Expect(t, string(body), "foo") body, err = ioutil.ReadAll(res.Body) st.Expect(t, err, nil) st.Expect(t, body, []byte{}) } func TestResponderSupportsMultipleHeadersWithSameKey(t *testing.T) { defer after() mres := New("http://foo"). Reply(200). AddHeader("Set-Cookie", "a=1"). AddHeader("Set-Cookie", "b=2") req := &http.Request{} res, err := Responder(req, mres, nil) st.Expect(t, err, nil) st.Expect(t, res.Header, http.Header{"Set-Cookie": []string{"a=1", "b=2"}}) } func TestResponderError(t *testing.T) { defer after() mres := New("http://foo.com").ReplyError(errors.New("error")) req := &http.Request{} res, err := Responder(req, mres, nil) st.Expect(t, err.Error(), "error") st.Expect(t, res == nil, true) } func TestResponderCancelledContext(t *testing.T) { defer after() mres := New("http://foo.com").Get("").Reply(200).Delay(20 * time.Millisecond).BodyString("foo") // create a context and schedule a call to cancel in 10ms ctx, cancel := context.WithCancel(context.Background()) go func() { time.Sleep(10 * time.Millisecond) cancel() }() req, _ := http.NewRequestWithContext(ctx, http.MethodGet, "http://foo.com", nil) res, err := Responder(req, mres, nil) // verify that we got a context cancellation error and nil response st.Expect(t, err, context.Canceled) st.Expect(t, res == nil, true) } func TestResponderExpiredContext(t *testing.T) { defer after() mres := New("http://foo.com").Get("").Reply(200).Delay(20 * time.Millisecond).BodyString("foo") // create a context that is set to expire in 10ms ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond) defer cancel() req, _ := http.NewRequestWithContext(ctx, http.MethodGet, "http://foo.com", nil) res, err := Responder(req, mres, nil) // verify that we got a context cancellation error and nil response st.Expect(t, err, context.DeadlineExceeded) st.Expect(t, res == nil, true) } func TestResponderPreExpiredContext(t *testing.T) { defer after() mres := New("http://foo.com").Get("").Reply(200).BodyString("foo") // create a context and wait to ensure it is expired ctx, cancel := context.WithTimeout(context.Background(), 500*time.Microsecond) defer cancel() time.Sleep(1 * time.Millisecond) req, _ := http.NewRequestWithContext(ctx, http.MethodGet, "http://foo.com", nil) res, err := Responder(req, mres, nil) // verify that we got a context cancellation error and nil response st.Expect(t, err, context.DeadlineExceeded) st.Expect(t, res == nil, true) } gock-1.1.2/response.go000066400000000000000000000114731410225761500146340ustar00rootroot00000000000000package 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) } gock-1.1.2/response_test.go000066400000000000000000000072141410225761500156710ustar00rootroot00000000000000package gock import ( "bytes" "errors" "net/http" "testing" "time" "github.com/nbio/st" ) func TestNewResponse(t *testing.T) { res := NewResponse() res.Status(200) st.Expect(t, res.StatusCode, 200) res.SetHeader("foo", "bar") st.Expect(t, res.Header.Get("foo"), "bar") res.Delay(1000 * time.Millisecond) st.Expect(t, res.ResponseDelay, 1000*time.Millisecond) res.EnableNetworking() st.Expect(t, res.UseNetwork, true) } func TestResponseStatus(t *testing.T) { res := NewResponse() st.Expect(t, res.StatusCode, 0) res.Status(200) st.Expect(t, res.StatusCode, 200) } func TestResponseType(t *testing.T) { res := NewResponse() res.Type("json") st.Expect(t, res.Header.Get("Content-Type"), "application/json") res = NewResponse() res.Type("xml") st.Expect(t, res.Header.Get("Content-Type"), "application/xml") res = NewResponse() res.Type("foo/bar") st.Expect(t, res.Header.Get("Content-Type"), "foo/bar") } func TestResponseSetHeader(t *testing.T) { res := NewResponse() res.SetHeader("foo", "bar") res.SetHeader("bar", "baz") st.Expect(t, res.Header.Get("foo"), "bar") st.Expect(t, res.Header.Get("bar"), "baz") } func TestResponseAddHeader(t *testing.T) { res := NewResponse() res.AddHeader("foo", "bar") res.AddHeader("foo", "baz") st.Expect(t, res.Header.Get("foo"), "bar") st.Expect(t, res.Header["Foo"][1], "baz") } func TestResponseSetHeaders(t *testing.T) { res := NewResponse() res.SetHeaders(map[string]string{"foo": "bar", "bar": "baz"}) st.Expect(t, res.Header.Get("foo"), "bar") st.Expect(t, res.Header.Get("bar"), "baz") } func TestResponseBody(t *testing.T) { res := NewResponse() res.Body(bytes.NewBuffer([]byte("foo bar"))) st.Expect(t, string(res.BodyBuffer), "foo bar") } func TestResponseBodyString(t *testing.T) { res := NewResponse() res.BodyString("foo bar") st.Expect(t, string(res.BodyBuffer), "foo bar") } func TestResponseFile(t *testing.T) { res := NewResponse() res.File("version.go") st.Expect(t, string(res.BodyBuffer)[:12], "package gock") } func TestResponseJSON(t *testing.T) { res := NewResponse() res.JSON(map[string]string{"foo": "bar"}) st.Expect(t, string(res.BodyBuffer)[:13], `{"foo":"bar"}`) st.Expect(t, res.Header.Get("Content-Type"), "application/json") } func TestResponseXML(t *testing.T) { res := NewResponse() type xml struct { Data string `xml:"data"` } res.XML(xml{Data: "foo"}) st.Expect(t, string(res.BodyBuffer), `foo`) st.Expect(t, res.Header.Get("Content-Type"), "application/xml") } func TestResponseMap(t *testing.T) { res := NewResponse() st.Expect(t, len(res.Mappers), 0) res.Map(func(res *http.Response) *http.Response { return res }) st.Expect(t, len(res.Mappers), 1) } func TestResponseFilter(t *testing.T) { res := NewResponse() st.Expect(t, len(res.Filters), 0) res.Filter(func(res *http.Response) bool { return true }) st.Expect(t, len(res.Filters), 1) } func TestResponseSetError(t *testing.T) { res := NewResponse() st.Expect(t, res.Error, nil) res.SetError(errors.New("foo error")) st.Expect(t, res.Error.Error(), "foo error") } func TestResponseDelay(t *testing.T) { res := NewResponse() st.Expect(t, res.ResponseDelay, 0*time.Microsecond) res.Delay(100 * time.Millisecond) st.Expect(t, res.ResponseDelay, 100*time.Millisecond) } func TestResponseEnableNetworking(t *testing.T) { res := NewResponse() st.Expect(t, res.UseNetwork, false) res.EnableNetworking() st.Expect(t, res.UseNetwork, true) } func TestResponseDone(t *testing.T) { res := NewResponse() res.Mock = &Mocker{request: &Request{Counter: 1}, disabler: new(disabler)} st.Expect(t, res.Done(), false) res.Mock.Disable() st.Expect(t, res.Done(), true) } gock-1.1.2/store.go000066400000000000000000000036201410225761500141250ustar00rootroot00000000000000package 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 } gock-1.1.2/store_test.go000066400000000000000000000032271410225761500151670ustar00rootroot00000000000000package gock import ( "testing" "github.com/nbio/st" ) func TestStoreRegister(t *testing.T) { defer after() st.Expect(t, len(mocks), 0) mock := New("foo").Mock Register(mock) st.Expect(t, len(mocks), 1) st.Expect(t, mock.Request().Mock, mock) st.Expect(t, mock.Response().Mock, mock) } func TestStoreGetAll(t *testing.T) { defer after() st.Expect(t, len(mocks), 0) mock := New("foo").Mock store := GetAll() st.Expect(t, len(mocks), 1) st.Expect(t, len(store), 1) st.Expect(t, store[0], mock) } func TestStoreExists(t *testing.T) { defer after() st.Expect(t, len(mocks), 0) mock := New("foo").Mock st.Expect(t, len(mocks), 1) st.Expect(t, Exists(mock), true) } func TestStorePending(t *testing.T) { defer after() New("foo") st.Expect(t, mocks, Pending()) } func TestStoreIsPending(t *testing.T) { defer after() New("foo") st.Expect(t, IsPending(), true) Flush() st.Expect(t, IsPending(), false) } func TestStoreIsDone(t *testing.T) { defer after() New("foo") st.Expect(t, IsDone(), false) Flush() st.Expect(t, IsDone(), true) } func TestStoreRemove(t *testing.T) { defer after() st.Expect(t, len(mocks), 0) mock := New("foo").Mock st.Expect(t, len(mocks), 1) st.Expect(t, Exists(mock), true) Remove(mock) st.Expect(t, Exists(mock), false) Remove(mock) st.Expect(t, Exists(mock), false) } func TestStoreFlush(t *testing.T) { defer after() st.Expect(t, len(mocks), 0) mock1 := New("foo").Mock mock2 := New("foo").Mock st.Expect(t, len(mocks), 2) st.Expect(t, Exists(mock1), true) st.Expect(t, Exists(mock2), true) Flush() st.Expect(t, len(mocks), 0) st.Expect(t, Exists(mock1), false) st.Expect(t, Exists(mock2), false) } gock-1.1.2/transport.go000066400000000000000000000054561410225761500150360ustar00rootroot00000000000000package 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 } gock-1.1.2/transport_test.go000066400000000000000000000021541410225761500160650ustar00rootroot00000000000000package gock import ( "fmt" "net/http" "net/http/httptest" "net/url" "testing" "github.com/nbio/st" ) func TestTransportMatch(t *testing.T) { defer after() const uri = "http://foo.com" New(uri).Reply(204) u, _ := url.Parse(uri) req := &http.Request{URL: u} res, err := NewTransport().RoundTrip(req) st.Expect(t, err, nil) st.Expect(t, res.StatusCode, 204) st.Expect(t, res.Request, req) } func TestTransportCannotMatch(t *testing.T) { defer after() New("http://foo.com").Reply(204) u, _ := url.Parse("http://127.0.0.1:1234") req := &http.Request{URL: u} _, err := NewTransport().RoundTrip(req) st.Expect(t, err, ErrCannotMatch) } func TestTransportNotIntercepting(t *testing.T) { defer after() ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Hello, world") })) defer ts.Close() New(ts.URL).Reply(200) Disable() u, _ := url.Parse(ts.URL) req := &http.Request{URL: u, Header: make(http.Header)} res, err := NewTransport().RoundTrip(req) st.Expect(t, err, nil) st.Expect(t, Intercepting(), false) st.Expect(t, res.StatusCode, 200) } gock-1.1.2/version.go000066400000000000000000000001371410225761500144560ustar00rootroot00000000000000package gock // Version defines the current package semantic version. const Version = "1.1.2"