github.com/epsagon/epsagon-go@v1.39.0/wrappers/net/http/handler_wrapper_test.go (about)

     1  package epsagonhttp
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"io/ioutil"
     8  	"net/http"
     9  	"net/http/httptest"
    10  
    11  	"github.com/epsagon/epsagon-go/epsagon"
    12  	"github.com/epsagon/epsagon-go/protocol"
    13  	"github.com/epsagon/epsagon-go/tracer"
    14  	. "github.com/onsi/ginkgo"
    15  	. "github.com/onsi/gomega"
    16  )
    17  
    18  var _ = Describe("http_wrapper", func() {
    19  	Describe("WrapHandleFunc", func() {
    20  		var (
    21  			events         []*protocol.Event
    22  			exceptions     []*protocol.Exception
    23  			request        *http.Request
    24  			responseWriter *httptest.ResponseRecorder
    25  			called         bool
    26  			config         *epsagon.Config
    27  		)
    28  		BeforeEach(func() {
    29  			called = false
    30  			config = &epsagon.Config{Config: tracer.Config{
    31  				Disable:  true,
    32  				TestMode: true,
    33  			}}
    34  			events = make([]*protocol.Event, 0)
    35  			exceptions = make([]*protocol.Exception, 0)
    36  			tracer.GlobalTracer = &tracer.MockedEpsagonTracer{
    37  				Events:     &events,
    38  				Exceptions: &exceptions,
    39  				Labels:     make(map[string]interface{}),
    40  				Config:     &config.Config,
    41  			}
    42  			body := []byte("hello")
    43  			request = httptest.NewRequest("POST", "https://www.help.com", ioutil.NopCloser(bytes.NewReader(body)))
    44  			responseWriter = httptest.NewRecorder()
    45  		})
    46  		Context("Happy Flows", func() {
    47  			It("calls the wrapped function", func() {
    48  				wrapper := WrapHandleFunc(
    49  					config,
    50  					func(rw http.ResponseWriter, req *http.Request) {
    51  						called = true
    52  					},
    53  				)
    54  				wrapper(responseWriter, request)
    55  				Expect(called).To(Equal(true))
    56  			})
    57  			It("passes the tracer through request context", func() {
    58  				wrapper := WrapHandleFunc(
    59  					config,
    60  					func(rw http.ResponseWriter, req *http.Request) {
    61  						called = true
    62  						tracer := epsagon.ExtractTracer([]context.Context{req.Context()})
    63  						tracer.AddLabel("test", "ok")
    64  					},
    65  				)
    66  				wrapper(responseWriter, request)
    67  				Expect(called).To(Equal(true))
    68  				Expect(
    69  					tracer.GlobalTracer.(*tracer.MockedEpsagonTracer).Labels["test"],
    70  				).To(Equal("ok"))
    71  			})
    72  			It("Creates a runner and trigger events for handler invocation", func() {
    73  				wrapper := WrapHandleFunc(
    74  					config,
    75  					func(rw http.ResponseWriter, req *http.Request) {
    76  						called = true
    77  					},
    78  					"test-handler",
    79  				)
    80  				wrapper(responseWriter, request)
    81  				Expect(len(events)).To(Equal(2))
    82  				var runnerEvent *protocol.Event
    83  				for _, event := range events {
    84  					if event.Origin == "runner" {
    85  						runnerEvent = event
    86  					}
    87  				}
    88  				Expect(runnerEvent).NotTo(Equal(nil))
    89  				Expect(runnerEvent.Resource.Type).To(Equal("go-function"))
    90  				Expect(runnerEvent.Resource.Name).To(Equal("test-handler"))
    91  			})
    92  			It("Adds correct trigger event", func() {
    93  				body := []byte("hello world")
    94  				request = httptest.NewRequest(
    95  					"POST",
    96  					"https://www.help.com/test?hello=world&good=bye",
    97  					ioutil.NopCloser(bytes.NewReader(body)))
    98  				wrapper := WrapHandleFunc(
    99  					config,
   100  					func(rw http.ResponseWriter, req *http.Request) {
   101  						called = true
   102  						internalHandlerBody, err := ioutil.ReadAll(req.Body)
   103  						if err != nil {
   104  							Expect(true).To(Equal(false))
   105  						}
   106  						Expect(internalHandlerBody).To(Equal(body))
   107  						resp, err := json.Marshal(map[string]string{"hello": "world"})
   108  						if err != nil {
   109  							Expect(true).To(Equal(false))
   110  						}
   111  						rw.Header().Add("Content-Type", "application/json; charset=utf-8")
   112  						_, err = rw.Write(resp)
   113  						if err != nil {
   114  							Expect(true).To(Equal(false))
   115  						}
   116  
   117  					},
   118  					"test-handler",
   119  				)
   120  				wrapper(responseWriter, request)
   121  				Expect(len(events)).To(Equal(2))
   122  				var triggerEvent *protocol.Event
   123  				for _, event := range events {
   124  					if event.Origin == "trigger" {
   125  						triggerEvent = event
   126  					}
   127  				}
   128  				Expect(triggerEvent).NotTo(Equal(nil))
   129  				Expect(triggerEvent.Resource.Name).To(Equal("www.help.com"))
   130  				Expect(triggerEvent.Resource.Type).To(Equal("http"))
   131  				Expect(triggerEvent.Resource.Operation).To(Equal("POST"))
   132  				expectedQuery, _ := json.Marshal(map[string][]string{
   133  					"hello": {"world"}, "good": {"bye"}})
   134  				Expect(triggerEvent.Resource.Metadata["query_string_parameters"]).To(
   135  					Equal(string(expectedQuery)))
   136  				Expect(triggerEvent.Resource.Metadata["path"]).To(
   137  					Equal("/test"))
   138  				Expect(triggerEvent.Resource.Metadata["request_body"]).To(
   139  					Equal(string(body)))
   140  				Expect(triggerEvent.Resource.Metadata["response_body"]).To(
   141  					Equal("{\"hello\":\"world\"}"))
   142  				Expect(triggerEvent.Resource.Metadata["response_headers"]).To(
   143  					Equal("{\"Content-Type\":\"application/json; charset=utf-8\"}"))
   144  				Expect(triggerEvent.Resource.Metadata["status_code"]).To(
   145  					Equal("200"))
   146  			})
   147  			It("Doesn't collect body and headers if MetadataOnly", func() {
   148  				config.MetadataOnly = true
   149  				body := []byte("hello world")
   150  				request = httptest.NewRequest(
   151  					"POST",
   152  					"https://www.help.com/test?hello=world&good=bye",
   153  					ioutil.NopCloser(bytes.NewReader(body)))
   154  				wrapper := WrapHandleFunc(
   155  					config,
   156  					func(rw http.ResponseWriter, req *http.Request) {
   157  						called = true
   158  						internalHandlerBody, err := ioutil.ReadAll(req.Body)
   159  						if err != nil {
   160  							Expect(true).To(Equal(false))
   161  						}
   162  						Expect(internalHandlerBody).To(Equal(body))
   163  						resp, err := json.Marshal(map[string]string{"hello": "world"})
   164  						if err != nil {
   165  							Expect(true).To(Equal(false))
   166  						}
   167  						rw.Header().Add("Content-Type", "application/json; charset=utf-8")
   168  						_, err = rw.Write(resp)
   169  						if err != nil {
   170  							Expect(true).To(Equal(false))
   171  						}
   172  
   173  					},
   174  					"test-handler",
   175  				)
   176  				wrapper(responseWriter, request)
   177  				Expect(len(events)).To(Equal(2))
   178  				var triggerEvent *protocol.Event
   179  				for _, event := range events {
   180  					if event.Origin == "trigger" {
   181  						triggerEvent = event
   182  					}
   183  				}
   184  				Expect(triggerEvent).NotTo(Equal(nil))
   185  				Expect(triggerEvent.Resource.Metadata["request_body"]).To(
   186  					Equal(""))
   187  				Expect(triggerEvent.Resource.Metadata["response_body"]).To(
   188  					Equal(""))
   189  				Expect(triggerEvent.Resource.Metadata["response_headers"]).To(
   190  					Equal(""))
   191  				Expect(triggerEvent.Resource.Metadata["query_string_parameters"]).To(
   192  					Equal(""))
   193  			})
   194  		})
   195  		Context("Error Flows", func() {
   196  			It("Adds Exception if handler explodes", func() {
   197  				errorMessage := "boom"
   198  				Expect(func() {
   199  					wrapper := WrapHandleFunc(
   200  						config,
   201  						func(rw http.ResponseWriter, req *http.Request) {
   202  							called = true
   203  							panic(errorMessage)
   204  						},
   205  						"test-handler",
   206  					)
   207  					wrapper(responseWriter, request)
   208  				}).To(
   209  					PanicWith(epsagon.MatchUserError(errorMessage)))
   210  				Expect(called).To(Equal(true))
   211  				Expect(len(events)).To(Equal(2))
   212  				var runnerEvent, triggerEvent *protocol.Event
   213  				for _, event := range events {
   214  					if event.Origin == "runner" {
   215  						runnerEvent = event
   216  					}
   217  					if event.Origin == "trigger" {
   218  						triggerEvent = event
   219  					}
   220  				}
   221  				Expect(runnerEvent.Exception).NotTo(Equal(nil))
   222  				Expect(triggerEvent.Exception).NotTo(Equal(nil))
   223  				Expect(triggerEvent.Resource.Metadata["status_code"]).To(
   224  					Equal("500"))
   225  			})
   226  		})
   227  	})
   228  })