diff --git a/http/mock/example_test.go b/http/mock/example_test.go deleted file mode 100644 index 7789545..0000000 --- a/http/mock/example_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package checkhttpmock_test - -import ( - "bytes" - "fmt" - "github.com/NETWAYS/go-check/http/mock" - "github.com/jarcoal/httpmock" - "io/ioutil" - "net/http" -) - -func Example() { - // Activate httpmock as normal - httpmock.Activate() - defer httpmock.DeactivateAndReset() - - // Use any normal responder - httpmock.RegisterResponder("GET", "https://example.com/test.json", - func(request *http.Request) (*http.Response, error) { - return httpmock.NewStringResponse(200, `{"allgood":true}`), nil - }) - - req, _ := http.NewRequest("GET", "https://example.com/test.json", nil) //nolint:noctx - requestAndDump(req) - - // Use additional responders - checkhttpmock.RegisterQueryMapResponder("POST", "https://exampleapi.com/", - checkhttpmock.QueryMap{ - "test=1": "test.json", - }) - - req, _ = http.NewRequest("POST", "https://exampleapi.com/", bytes.NewBufferString("test=1")) //nolint:noctx - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - requestAndDump(req) - - // Output: - // {"allgood":true} - // {"example":true} -} - -func requestAndDump(req *http.Request) { - resp, err := http.DefaultTransport.RoundTrip(req) - if err != nil { - panic(err) - } - - defer resp.Body.Close() - - data, _ := ioutil.ReadAll(resp.Body) - fmt.Println(string(data)) -} diff --git a/http/mock/mock.go b/http/mock/mock.go deleted file mode 100644 index fc498d5..0000000 --- a/http/mock/mock.go +++ /dev/null @@ -1,7 +0,0 @@ -// Helper package to add more functions to httpmock (github.com/jarcoal/httpmock) -// -// Features: -// - RegisterQueryMapResponder form query based based on a QueryMap -// - ActivateRecorder to record HTTP requests during the development of tests and examples -// Deprecated: Will be removed in a future version -package checkhttpmock diff --git a/http/mock/record.go b/http/mock/record.go deleted file mode 100644 index 1d82108..0000000 --- a/http/mock/record.go +++ /dev/null @@ -1,61 +0,0 @@ -package checkhttpmock - -import ( - "fmt" - log "github.com/sirupsen/logrus" - "gopkg.in/yaml.v3" - "io" - "net/http" -) - -// Data structure to store information about a http.Request and http.Response in a simplified way -type Record struct { - URL string - Method string - Query string - Status string - Body string -} - -// Build a new Record from an http.Request -func NewRecord(request *http.Request) (r *Record) { - r = &Record{ - URL: request.URL.String(), - Method: request.Method, - } - - // read the query from the request - r.Query = extractFormQuery(request) - - log.WithFields(log.Fields{ - "url": r.URL, - "method": r.Method, - }).Info("recording request") - - return -} - -// Update the Record with a http.Response to get Body and Status -func (r *Record) Complete(response *http.Response) { - body, newReader := dumpAndBuffer(response.Body) - response.Body = newReader - - r.Status = response.Status - r.Body = body - - log.WithFields(log.Fields{ - "status": response.Status, - }).Info("recording response") -} - -// Write a YAML representation of the Record to an io.Writer -func (r Record) EmitYAML(w io.Writer) (err error) { - out := yaml.NewEncoder(w) - out.SetIndent(2) - - _, _ = fmt.Fprintln(w, "---") - - err = out.Encode(r) - - return -} diff --git a/http/mock/recorder.go b/http/mock/recorder.go deleted file mode 100644 index 6c20cf5..0000000 --- a/http/mock/recorder.go +++ /dev/null @@ -1,80 +0,0 @@ -package checkhttpmock - -import ( - "fmt" - "github.com/jarcoal/httpmock" - "io" - "net/http" - "os" - "path" -) - -var ( - CurrentRecorder *Recorder - // Default storage location for the recorded data - RecordFile = TestData + "/httpmock-record.yml" -) - -// Helper to record http.Response and http.Request for httpmock when no responser was found -type Recorder struct { - RecordFile string - Writer io.Writer -} - -// Activate a Recorder and set it as noResponder with httpmock -// -// Usually you would use this during developing unit tests, and not for finished tests. -// -// By default records to RecordFile -func ActivateRecorder() (rec *Recorder) { - rec = &Recorder{RecordFile: RecordFile} - - httpmock.RegisterNoResponder(rec.Respond) - - CurrentRecorder = rec - - return -} - -// Open the Writer when needed -func (rec *Recorder) writer() io.Writer { - if rec.Writer != nil { - return rec.Writer - } else if rec.RecordFile != "-" && rec.RecordFile != "" { - // Ensure directory is writable - dir := path.Dir(rec.RecordFile) - _ = os.MkdirAll(dir, 0755) - - // Open file in append mode - f, err := os.OpenFile(RecordFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - if err == nil { - rec.Writer = f - } - } - - if rec.Writer == nil { - rec.Writer = os.Stdout - } - - return rec.Writer -} - -// Handle http.request for httpmock, and execute a real HTTP connection using httpmock.InitialTransport -// -// Recording and returning the real http.Response -func (rec *Recorder) Respond(request *http.Request) (response *http.Response, err error) { - r := NewRecord(request) - - // Do a real request bypassing mock - response, err = httpmock.InitialTransport.RoundTrip(request) - if err != nil { - err = fmt.Errorf("could not execute HTTP request: %w", err) - return - } - - r.Complete(response) - - err = r.EmitYAML(rec.writer()) - - return -} diff --git a/http/mock/recorder_test.go b/http/mock/recorder_test.go deleted file mode 100644 index f3d0878..0000000 --- a/http/mock/recorder_test.go +++ /dev/null @@ -1,64 +0,0 @@ -package checkhttpmock_test - -import ( - "fmt" - "github.com/NETWAYS/go-check/http/mock" - "github.com/jarcoal/httpmock" - "io" - "io/ioutil" - "net/http" - "os" -) - -func ExampleActivateRecorder() { - // Activate the normal httpmock - httpmock.Activate() - defer httpmock.DeactivateAndReset() - - // Activate recorder - _ = os.Remove(checkhttpmock.RecordFile) // Remove any prior recording - checkhttpmock.ActivateRecorder() - - // We don't set any mock examples here - //httpmock.RegisterResponder("GET", "http://localhost:8080/test", - // func(request *http.Request) (*http.Response, error) { - // return httpmock.NewStringResponse(200, "Hello World"), nil - // }) - - // Start a simple HTTP server - runHTTP() - - // Do any HTTP request - resp, err := http.Get("http://localhost:64888/test") // nolint:noctx - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - // Print response body - data, _ := ioutil.ReadAll(resp.Body) - fmt.Printf("%s\n", data) - - // Print recording - data, _ = ioutil.ReadFile(checkhttpmock.RecordFile) - fmt.Printf("%s\n", data) - - _ = resp.Body.Close() - - // Output: - // Hello World - // --- - // url: http://localhost:64888/test - // method: GET - // query: "" - // status: 200 OK - // body: Hello World -} - -func runHTTP() { - http.HandleFunc("/test", func(w http.ResponseWriter, req *http.Request) { - _, _ = io.WriteString(w, `Hello World`) - }) - - go http.ListenAndServe(":64888", nil) //nolint:errcheck -} diff --git a/http/mock/responder.go b/http/mock/responder.go deleted file mode 100644 index 44d9917..0000000 --- a/http/mock/responder.go +++ /dev/null @@ -1,52 +0,0 @@ -package checkhttpmock - -import ( - "fmt" - "github.com/jarcoal/httpmock" - "io/ioutil" - "net/http" - "path" - "strings" -) - -// Where response data is stored, relative to the package being tested -const TestData = "./testdata" - -// Mapping a partial form request to a response -// -// The response will be expected and read from the local testdata directory of your package. -// -// QueryMap{ -// "test=1": "response.json", -// } -type QueryMap map[string]string - -// Register a NewQueryMapResponder with httpmock -// -// See QueryMap and NewQueryMapResponder -func RegisterQueryMapResponder(method, url string, queryMap QueryMap) { - httpmock.RegisterResponder(method, url, NewQueryMapResponder(queryMap)) //nolint:bodyclose -} - -// Return a responder function for httpmock, to return different results based on a QueryMap -// -// Queries from the QueryMap are matched partially and the response is read from `./testdata` -func NewQueryMapResponder(queryMap QueryMap) func(request *http.Request) (*http.Response, error) { - return func(request *http.Request) (*http.Response, error) { - query := extractFormQuery(request) - - for part, file := range queryMap { - if strings.Contains(query, part) { - body, err := ioutil.ReadFile(path.Join(TestData, file)) - return httpmock.NewStringResponse(200, string(body)), err - } - } - - // When a recorder is enabled use it - we don't have a way to access the NoResponder from here - if CurrentRecorder != nil { - return CurrentRecorder.Respond(request) - } - - return nil, fmt.Errorf("no matching query found for: %s", query) - } -} diff --git a/http/mock/testdata/test.json b/http/mock/testdata/test.json deleted file mode 100644 index 46a2603..0000000 --- a/http/mock/testdata/test.json +++ /dev/null @@ -1 +0,0 @@ -{"example":true} diff --git a/http/mock/util.go b/http/mock/util.go deleted file mode 100644 index 28aa4bf..0000000 --- a/http/mock/util.go +++ /dev/null @@ -1,38 +0,0 @@ -package checkhttpmock - -import ( - "bytes" - "io" - "io/ioutil" - "net/http" - "strings" -) - -const contentTypeHeader = "Content-Type" -const contentTypeUrlencoded = "application/x-www-form-urlencoded" - -// Read all data from a io.ReadCloser, return the data as string and return a new io.ReadCloser to pass on -// -// This can be quite tricky and is only used for mocking and testing here. -func dumpAndBuffer(r io.ReadCloser) (string, io.ReadCloser) { - data, err := ioutil.ReadAll(r) - if err != nil { - panic(err) - } - - _ = r.Close() - - return string(data), ioutil.NopCloser(bytes.NewReader(data)) -} - -// Extract a URL query from the request body, when the Content-Type is set to be urlencoded -func extractFormQuery(request *http.Request) string { - if strings.Contains(request.Header.Get(contentTypeHeader), contentTypeUrlencoded) { - query, newReader := dumpAndBuffer(request.Body) - request.Body = newReader - - return query - } - - return "" -}