github.com/waldiirawan/apm-agent-go/v2@v2.2.2/apmtest/httpsuite.go (about) 1 // Licensed to Elasticsearch B.V. under one or more contributor 2 // license agreements. See the NOTICE file distributed with 3 // this work for additional information regarding copyright 4 // ownership. Elasticsearch B.V. licenses this file to you under 5 // the Apache License, Version 2.0 (the "License"); you may 6 // not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, 12 // software distributed under the License is distributed on an 13 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 // KIND, either express or implied. See the License for the 15 // specific language governing permissions and limitations 16 // under the License. 17 18 package apmtest // import "github.com/waldiirawan/apm-agent-go/v2/apmtest" 19 20 import ( 21 "net/http" 22 "net/http/httptest" 23 "strings" 24 25 "github.com/stretchr/testify/require" 26 "github.com/stretchr/testify/suite" 27 28 "github.com/waldiirawan/apm-agent-go/v2" 29 "github.com/waldiirawan/apm-agent-go/v2/model" 30 "github.com/waldiirawan/apm-agent-go/v2/transport/transporttest" 31 ) 32 33 // HTTPTestSuite is a test suite for HTTP instrumentation modules. 34 type HTTPTestSuite struct { 35 suite.Suite 36 37 // Handler holds an instrumented HTTP handler. Handler must 38 // support the following routes: 39 // 40 // GET /implicit_write (no explicit write on the response) 41 // GET /panic_before_write (panic without writing response) 42 // GET /panic_after_write (panic after writing response) 43 // POST /explicit_error_capture (explicit CaptureError call) 44 // 45 Handler http.Handler 46 47 // Tracer is the apm.Tracer used to instrument Handler. 48 // 49 // HTTPTestSuite will close the tracer when all tests have 50 // been completed. 51 Tracer *apm.Tracer 52 53 // Recorder is the transport used as the transport for Tracer. 54 Recorder *transporttest.RecorderTransport 55 56 server *httptest.Server 57 } 58 59 // SetupTest runs before each test. 60 func (s *HTTPTestSuite) SetupTest() { 61 s.Recorder.ResetPayloads() 62 } 63 64 // SetupSuite runs before the tests in the suite are run. 65 func (s *HTTPTestSuite) SetupSuite() { 66 s.server = httptest.NewServer(s.Handler) 67 } 68 69 // TearDownSuite runs after the tests in the suite are run. 70 func (s *HTTPTestSuite) TearDownSuite() { 71 if s.server != nil { 72 s.server.Close() 73 } 74 s.Tracer.Close() 75 } 76 77 // TestImplicitWrite tests the behaviour of instrumented handlers 78 // for routes which do not explicitly write a response, but instead 79 // leave it to the framework to write an empty 200 response. 80 func (s *HTTPTestSuite) TestImplicitWrite() { 81 resp, err := http.Get(s.server.URL + "/implicit_write") 82 require.NoError(s.T(), err) 83 resp.Body.Close() 84 s.Equal(http.StatusOK, resp.StatusCode) 85 86 s.Tracer.Flush(nil) 87 ps := s.Recorder.Payloads() 88 require.Len(s.T(), ps.Transactions, 1) 89 90 tx := ps.Transactions[0] 91 s.Equal("HTTP 2xx", tx.Result) 92 s.Equal(resp.StatusCode, tx.Context.Response.StatusCode) 93 } 94 95 // TestPanicBeforeWrite tests the behaviour of instrumented handlers 96 // for routes which panic before any headers are written. The handler 97 // is expected to recover the panic and write an empty 500 response. 98 func (s *HTTPTestSuite) TestPanicBeforeWrite() { 99 resp, err := http.Get(s.server.URL + "/panic_before_write") 100 require.NoError(s.T(), err) 101 resp.Body.Close() 102 s.Equal(http.StatusInternalServerError, resp.StatusCode) 103 104 s.Tracer.Flush(nil) 105 ps := s.Recorder.Payloads() 106 require.Len(s.T(), ps.Transactions, 1) 107 require.Len(s.T(), ps.Errors, 1) 108 109 tx := ps.Transactions[0] 110 s.Equal("HTTP 5xx", tx.Result) 111 s.Equal(resp.StatusCode, tx.Context.Response.StatusCode) 112 113 e := ps.Errors[0] 114 s.Equal(tx.ID, e.ParentID) 115 s.Equal(resp.StatusCode, e.Context.Response.StatusCode) 116 } 117 118 // TestPanicAfterWrite tests the behaviour of instrumented handlers 119 // for routes which panic after writing headers. The handler is 120 // expected to recover the panic without otherwise affecting the 121 // response. 122 func (s *HTTPTestSuite) TestPanicAfterWrite() { 123 resp, err := http.Get(s.server.URL + "/panic_after_write") 124 require.NoError(s.T(), err) 125 resp.Body.Close() 126 s.Equal(http.StatusOK, resp.StatusCode) 127 128 s.Tracer.Flush(nil) 129 ps := s.Recorder.Payloads() 130 require.Len(s.T(), ps.Transactions, 1) 131 require.Len(s.T(), ps.Errors, 1) 132 133 tx := ps.Transactions[0] 134 s.Equal("HTTP 2xx", tx.Result) 135 s.Equal(resp.StatusCode, tx.Context.Response.StatusCode) 136 137 e := ps.Errors[0] 138 s.Equal(tx.ID, e.ParentID) 139 s.Equal(resp.StatusCode, e.Context.Response.StatusCode) 140 } 141 142 // TestExplicitErrorCapture tests that a CaptureError explicit call 143 // inside an HTTP request transaction captures the request body 144 func (s *HTTPTestSuite) TestExplicitErrorCapture() { 145 s.Tracer.SetCaptureBody(apm.CaptureBodyAll) 146 resp, err := http.Post( 147 s.server.URL+"/explicit_error_capture", 148 "application/x-www-form-urlencoded", 149 strings.NewReader("body"), 150 ) 151 require.NoError(s.T(), err) 152 defer resp.Body.Close() 153 s.Equal(http.StatusServiceUnavailable, resp.StatusCode) 154 155 s.Tracer.Flush(nil) 156 ps := s.Recorder.Payloads() 157 require.Len(s.T(), ps.Transactions, 1) 158 159 tx := ps.Transactions[0] 160 e := ps.Errors[0] 161 s.Equal(&model.RequestBody{Raw: "body"}, tx.Context.Request.Body) 162 s.Equal(&model.RequestBody{Raw: "body"}, e.Context.Request.Body) 163 }