func TestImportImportRequestResponsePairs_CanImportAMultiplePairs(t *testing.T) { RegisterTestingT(t) cache := cache.NewInMemoryCache() cfg := Configuration{Webserver: false} requestMatcher := matching.RequestMatcher{RequestCache: cache, Webserver: &cfg.Webserver} hv := Hoverfly{RequestCache: cache, Cfg: &cfg, RequestMatcher: requestMatcher} RegisterTestingT(t) originalPair1 := v1.RequestResponsePairView{ Response: v1.ResponseDetailsView{ Status: 200, Body: "hello_world", EncodedBody: false, Headers: map[string][]string{"Hoverfly": []string{"testing"}}, }, Request: v1.RequestDetailsView{ Path: StringToPointer("/"), Method: StringToPointer("GET"), Destination: StringToPointer("/"), Scheme: StringToPointer("scheme"), Query: StringToPointer(""), Body: StringToPointer(""), Headers: map[string][]string{"Hoverfly": []string{"testing"}}}} originalPair2 := originalPair1 originalPair2.Request.Path = StringToPointer("/new/path") originalPair3 := originalPair1 originalPair3.Request.Path = StringToPointer("/newer/path") hv.ImportRequestResponsePairViews([]interfaces.RequestResponsePair{originalPair1, originalPair2, originalPair3}) pairBytes, err := cache.Get([]byte("9b114df98da7f7e2afdc975883dab4f2")) Expect(err).To(BeNil()) decodedPair1, err := models.NewRequestResponsePairFromBytes(pairBytes) Expect(err).To(BeNil()) Expect(*decodedPair1).To(Equal(models.NewRequestResponsePairFromRequestResponsePairView(originalPair1))) pairBytes, err = cache.Get([]byte("9c03e4af1f30542ff079a712bddad602")) Expect(err).To(BeNil()) decodedPair2, err := models.NewRequestResponsePairFromBytes(pairBytes) Expect(err).To(BeNil()) Expect(*decodedPair2).To(Equal(models.NewRequestResponsePairFromRequestResponsePairView(originalPair2))) pairBytes, err = cache.Get([]byte("fd099332afee48101edb7441b098cd4a")) Expect(err).To(BeNil()) decodedPair3, err := models.NewRequestResponsePairFromBytes(pairBytes) Expect(err).To(BeNil()) Expect(*decodedPair3).To(Equal(models.NewRequestResponsePairFromRequestResponsePairView(originalPair3))) }
// TestRequestBodyCaptured tests whether request body is recorded func TestRequestBodyCaptured(t *testing.T) { RegisterTestingT(t) server, dbClient := testTools(200, `{'message': 'here'}`) defer server.Close() requestBody := []byte("fizz=buzz") body := ioutil.NopCloser(bytes.NewBuffer(requestBody)) req, err := http.NewRequest("POST", "http://capture_body.com", body) Expect(err).To(BeNil()) _, err = dbClient.captureRequest(req) Expect(err).To(BeNil()) fp := matching.GetRequestFingerprint(req, requestBody, false) pairBytes, err := dbClient.RequestCache.Get([]byte(fp)) Expect(err).To(BeNil()) pair, err := models.NewRequestResponsePairFromBytes(pairBytes) Expect(err).To(BeNil()) Expect(pair.Request.Body).To(Equal("fizz=buzz")) }
func TestDecodeRandomBytes(t *testing.T) { RegisterTestingT(t) bytes := []byte("some random stuff here") _, err := models.NewRequestResponsePairFromBytes(bytes) Expect(err).ToNot(BeNil()) }
func rebuildHashes(db cache.Cache, webserver bool) { log.Info("Checking if keys in cache need rehashing") entries, err := db.GetAllEntries() if err != nil { log.Fatal("Unable to read from BoltDB cache") } for key, bytes := range entries { pair, err := models.NewRequestResponsePairFromBytes(bytes) if err != nil { log.WithFields(log.Fields{ "error": err.Error(), "value": string(bytes), "key": key, }).Error("Failed to decode payload") } var newKey string if webserver { newKey = pair.IdWithoutHost() } else { newKey = pair.Id() } if key != newKey { db.Delete([]byte(key)) db.Set([]byte(newKey), bytes) } } }
func (hf Hoverfly) GetRecords() ([]v1.RequestResponsePairView, error) { records, err := hf.RequestCache.GetAllEntries() if err != nil { return nil, err } var pairViews []v1.RequestResponsePairView for _, v := range records { if pair, err := models.NewRequestResponsePairFromBytes(v); err == nil { pairView := pair.ConvertToV1RequestResponsePairView() pairViews = append(pairViews, *pairView) } else { log.Error(err) return nil, err } } for _, v := range hf.RequestMatcher.TemplateStore { pairView := v.ConvertToV1RequestResponsePairView() pairViews = append(pairViews, pairView) } return pairViews, nil }
func TestGetMultipleRecords(t *testing.T) { RegisterTestingT(t) server, dbClient := testTools(201, `{'message': 'here'}`) defer server.Close() defer dbClient.RequestCache.DeleteData() // inserting some payloads for i := 0; i < 5; i++ { req, err := http.NewRequest("GET", fmt.Sprintf("http://example.com/q=%d", i), nil) Expect(err).To(BeNil()) dbClient.captureRequest(req) } // getting requests values, err := dbClient.RequestCache.GetAllValues() Expect(err).To(BeNil()) for _, value := range values { if pair, err := models.NewRequestResponsePairFromBytes(value); err == nil { Expect(pair.Request.Method).To(Equal("GET")) Expect(pair.Response.Status).To(Equal(201)) } else { t.Error(err) } } }
func TestRequestResponsePairEncodeEmpty(t *testing.T) { RegisterTestingT(t) pair := models.RequestResponsePair{} pairBytes, err := pair.Encode() Expect(err).To(BeNil()) _, err = models.NewRequestResponsePairFromBytes(pairBytes) Expect(err).To(BeNil()) }
func TestImportRequestResponsePairs_CanImportASinglePair(t *testing.T) { RegisterTestingT(t) cache := cache.NewInMemoryCache() cfg := Configuration{Webserver: false} requestMatcher := matching.RequestMatcher{RequestCache: cache, Webserver: &cfg.Webserver} hv := Hoverfly{RequestCache: cache, Cfg: &cfg, RequestMatcher: requestMatcher} RegisterTestingT(t) originalPair := v1.RequestResponsePairView{ Response: v1.ResponseDetailsView{ Status: 200, Body: "hello_world", EncodedBody: false, Headers: map[string][]string{"Content-Type": []string{"text/plain"}}}, Request: v1.RequestDetailsView{ Path: StringToPointer("/"), Method: StringToPointer("GET"), Destination: StringToPointer("/"), Scheme: StringToPointer("scheme"), Query: StringToPointer(""), Body: StringToPointer(""), Headers: map[string][]string{"Hoverfly": []string{"testing"}}}} hv.ImportRequestResponsePairViews([]interfaces.RequestResponsePair{originalPair}) value, _ := cache.Get([]byte("9b114df98da7f7e2afdc975883dab4f2")) decodedPair, _ := models.NewRequestResponsePairFromBytes(value) Expect(*decodedPair).To(Equal(models.RequestResponsePair{ Response: models.ResponseDetails{ Status: 200, Body: "hello_world", Headers: map[string][]string{"Content-Type": []string{"text/plain"}}, }, Request: models.RequestDetails{ Path: "/", Method: "GET", Destination: "/", Scheme: "scheme", Query: "", Body: "", Headers: map[string][]string{ "Content-Type": []string{"text/plain; charset=utf-8"}, "Hoverfly": []string{"testing"}, }, }, })) }
func TestRequestResponsePairEncodeDecode(t *testing.T) { RegisterTestingT(t) resp := models.ResponseDetails{ Status: 200, Body: "body here", } pair := models.RequestResponsePair{Response: resp} pairBytes, err := pair.Encode() Expect(err).To(BeNil()) pairFromBytes, err := models.NewRequestResponsePairFromBytes(pairBytes) Expect(err).To(BeNil()) Expect(pairFromBytes.Response.Body).To(Equal(resp.Body)) Expect(pairFromBytes.Response.Status).To(Equal(resp.Status)) }
func TestImportImportRequestResponsePairs_CanImportASingleBase64EncodedPair(t *testing.T) { cache := cache.NewInMemoryCache() cfg := Configuration{Webserver: false} requestMatcher := matching.RequestMatcher{RequestCache: cache, Webserver: &cfg.Webserver} hv := Hoverfly{RequestCache: cache, Cfg: &cfg, RequestMatcher: requestMatcher} RegisterTestingT(t) encodedPair := views.RequestResponsePairView{ Response: views.ResponseDetailsView{ Status: 200, Body: base64String("hello_world"), EncodedBody: true, Headers: map[string][]string{"Content-Encoding": []string{"gzip"}}}, Request: views.RequestDetailsView{ Path: "/", Method: "GET", Destination: "/", Scheme: "scheme", Query: "", Body: "", Headers: map[string][]string{"Hoverfly": []string{"testing"}}}} hv.ImportRequestResponsePairViews([]views.RequestResponsePairView{encodedPair}) value, err := cache.Get([]byte("9b114df98da7f7e2afdc975883dab4f2")) Expect(err).To(BeNil()) decodedPair, err := models.NewRequestResponsePairFromBytes(value) Expect(err).To(BeNil()) Expect(decodedPair).ToNot(Equal(models.RequestResponsePair{ Response: models.ResponseDetails{ Status: 200, Body: "hello_world", Headers: map[string][]string{"Content-Encoding": []string{"gzip"}}}, Request: models.RequestDetails{ Path: "/", Method: "GET", Destination: "/", Scheme: "scheme", Query: "", Body: "", Headers: map[string][]string{"Hoverfly": []string{"testing"}}}})) }
// AllRecordsHandler returns JSON content type http response func (d *Hoverfly) AllRecordsHandler(w http.ResponseWriter, req *http.Request, next http.HandlerFunc) { records, err := d.RequestCache.GetAllValues() if err != nil { log.WithFields(log.Fields{ "Error": err.Error(), }).Error("Failed to get data from cache!") w.Header().Set("Content-Type", "application/json; charset=UTF-8") w.WriteHeader(500) // can't process this entity return } var pairViews []views.RequestResponsePairView for _, v := range records { if pair, err := models.NewRequestResponsePairFromBytes(v); err == nil { pairView := pair.ConvertToRequestResponsePairView() pairViews = append(pairViews, *pairView) } else { log.Error(err) http.Error(w, err.Error(), http.StatusInternalServerError) return } } w.Header().Set("Content-Type", "application/json") var response views.RequestResponsePairPayload response.Data = pairViews b, err := json.Marshal(response) if err != nil { log.Error(err) http.Error(w, err.Error(), http.StatusInternalServerError) } w.Write(b) return }
func (hf Hoverfly) GetSimulation() (v2.SimulationView, error) { records, err := hf.RequestCache.GetAllEntries() if err != nil { return v2.SimulationView{}, err } pairViews := make([]v2.RequestResponsePairView, 0) for _, v := range records { if pair, err := models.NewRequestResponsePairFromBytes(v); err == nil { pairView := pair.ConvertToRequestResponsePairView() pairViews = append(pairViews, pairView) } else { log.Error(err) return v2.SimulationView{}, err } } for _, v := range hf.RequestMatcher.TemplateStore { pairViews = append(pairViews, v.ConvertToRequestResponsePairView()) } responseDelays := hf.ResponseDelays.ConvertToResponseDelayPayloadView() return v2.SimulationView{ MetaView: v2.MetaView{ HoverflyVersion: "v0.9.2", SchemaVersion: "v1", TimeExported: time.Now().Format(time.RFC3339), }, DataView: v2.DataView{ RequestResponsePairs: pairViews, GlobalActions: v2.GlobalActionsView{ Delays: responseDelays.Data, }, }, }, nil }
// getResponse returns stored response from cache func (this *RequestMatcher) GetResponse(req *models.RequestDetails) (*models.ResponseDetails, *MatchingError) { var key string if *this.Webserver { key = req.HashWithoutHost() } else { key = req.Hash() } pairBytes, err := this.RequestCache.Get([]byte(key)) if err != nil { log.WithFields(log.Fields{ "key": key, "error": err.Error(), "query": req.Query, "path": req.Path, "destination": req.Destination, "method": req.Method, }).Warn("Failed to retrieve response from cache") response, err := this.TemplateStore.GetResponse(*req, *this.Webserver) if err != nil { log.WithFields(log.Fields{ "key": key, "error": err.Error(), "query": req.Query, "path": req.Path, "destination": req.Destination, "method": req.Method, }).Warn("Failed to find matching request template from template store") return nil, &MatchingError{ StatusCode: 412, Description: "Could not find recorded request, please record it first!", } } log.WithFields(log.Fields{ "key": key, "query": req.Query, "path": req.Path, "destination": req.Destination, "method": req.Method, }).Info("Found template matching request from template store") return response, nil } // getting cache response pair, err := models.NewRequestResponsePairFromBytes(pairBytes) if err != nil { log.WithFields(log.Fields{ "error": err.Error(), "value": string(pairBytes), "key": key, }).Error("Failed to decode payload") return nil, &MatchingError{ StatusCode: 500, Description: "Failed to decode payload", } } log.WithFields(log.Fields{ "key": key, "path": req.Path, "rawQuery": req.Query, "method": req.Method, "destination": req.Destination, "status": pair.Response.Status, }).Info("Payload found from cache") return &pair.Response, nil }
func TestImportImportRequestResponsePairs_CanImportARequestResponsePair_AndRequestTemplateResponsePair(t *testing.T) { RegisterTestingT(t) cache := cache.NewInMemoryCache() cfg := Configuration{Webserver: false} requestMatcher := matching.RequestMatcher{RequestCache: cache, Webserver: &cfg.Webserver} hv := Hoverfly{RequestCache: cache, Cfg: &cfg, RequestMatcher: requestMatcher} RegisterTestingT(t) requestTemplate := v1.RequestDetailsView{ RequestType: StringToPointer("template"), Method: StringToPointer("GET"), } requestView := v1.RequestDetailsView{ Method: StringToPointer("GET"), Path: StringToPointer("/"), Destination: StringToPointer("test.com"), Scheme: StringToPointer("http"), } responseView := v1.ResponseDetailsView{ Status: 200, Body: "hello_world", EncodedBody: false, Headers: map[string][]string{"Hoverfly": []string{"testing"}}, } templatePair := v1.RequestResponsePairView{ Request: requestTemplate, Response: responseView, } ordinaryPair := v1.RequestResponsePairView{ Request: requestView, Response: responseView, } hv.ImportRequestResponsePairViews([]interfaces.RequestResponsePair{templatePair, ordinaryPair}) cacheCount, err := hv.RequestCache.RecordsCount() Expect(cacheCount).To(Equal(1)) Expect(err).To(BeNil()) Expect(len(hv.RequestMatcher.TemplateStore)).To(Equal(1)) request := models.NewRequestDetailsFromRequest(requestTemplate) response := models.NewResponseDetailsFromResponse(responseView) pairBytes, err := hv.RequestCache.Get([]byte("76cf08e38439f083de2658b0971df9bf")) Expect(err).To(BeNil()) savedPair, err := models.NewRequestResponsePairFromBytes(pairBytes) Expect(err).To(BeNil()) Expect(savedPair.Response).To(Equal(response)) responseFromCache, err := hv.RequestMatcher.TemplateStore.GetResponse(request, false) Expect(err).To(BeNil()) Expect(*responseFromCache).To(Equal(response)) }