github.com/newrelic/go-agent@v3.26.0+incompatible/_integrations/nrgin/v1/nrgin_test.go (about) 1 // Copyright 2020 New Relic Corporation. All rights reserved. 2 // SPDX-License-Identifier: Apache-2.0 3 4 package nrgin 5 6 import ( 7 "errors" 8 "net/http" 9 "net/http/httptest" 10 "testing" 11 12 "github.com/gin-gonic/gin" 13 newrelic "github.com/newrelic/go-agent" 14 "github.com/newrelic/go-agent/internal" 15 "github.com/newrelic/go-agent/internal/integrationsupport" 16 ) 17 18 var ( 19 pkg = "github.com/newrelic/go-agent/_integrations/nrgin/v1" 20 ) 21 22 func hello(c *gin.Context) { 23 c.Writer.WriteString("hello response") 24 } 25 26 func TestBasicRoute(t *testing.T) { 27 app := integrationsupport.NewBasicTestApp() 28 router := gin.Default() 29 router.Use(Middleware(app)) 30 router.GET("/hello", hello) 31 32 response := httptest.NewRecorder() 33 req, err := http.NewRequest("GET", "/hello", nil) 34 if err != nil { 35 t.Fatal(err) 36 } 37 router.ServeHTTP(response, req) 38 if respBody := response.Body.String(); respBody != "hello response" { 39 t.Error("wrong response body", respBody) 40 } 41 app.ExpectTxnMetrics(t, internal.WantTxn{ 42 Name: pkg + ".hello", 43 IsWeb: true, 44 }) 45 } 46 47 func TestRouterGroup(t *testing.T) { 48 app := integrationsupport.NewBasicTestApp() 49 router := gin.Default() 50 router.Use(Middleware(app)) 51 group := router.Group("/group") 52 group.GET("/hello", hello) 53 54 response := httptest.NewRecorder() 55 req, err := http.NewRequest("GET", "/group/hello", nil) 56 if err != nil { 57 t.Fatal(err) 58 } 59 router.ServeHTTP(response, req) 60 if respBody := response.Body.String(); respBody != "hello response" { 61 t.Error("wrong response body", respBody) 62 } 63 app.ExpectTxnMetrics(t, internal.WantTxn{ 64 Name: pkg + ".hello", 65 IsWeb: true, 66 }) 67 } 68 69 func TestAnonymousHandler(t *testing.T) { 70 app := integrationsupport.NewBasicTestApp() 71 router := gin.Default() 72 router.Use(Middleware(app)) 73 router.GET("/anon", func(c *gin.Context) { 74 c.Writer.WriteString("anonymous function handler") 75 }) 76 77 response := httptest.NewRecorder() 78 req, err := http.NewRequest("GET", "/anon", nil) 79 if err != nil { 80 t.Fatal(err) 81 } 82 router.ServeHTTP(response, req) 83 if respBody := response.Body.String(); respBody != "anonymous function handler" { 84 t.Error("wrong response body", respBody) 85 } 86 app.ExpectTxnMetrics(t, internal.WantTxn{ 87 Name: pkg + ".TestAnonymousHandler.func1", 88 IsWeb: true, 89 }) 90 } 91 92 func multipleWriteHeader(c *gin.Context) { 93 // Unlike http.ResponseWriter, gin.ResponseWriter does not immediately 94 // write the first WriteHeader. Instead, it gets buffered until the 95 // first Write call. 96 c.Writer.WriteHeader(200) 97 c.Writer.WriteHeader(500) 98 c.Writer.WriteString("multipleWriteHeader") 99 } 100 101 func TestMultipleWriteHeader(t *testing.T) { 102 app := integrationsupport.NewBasicTestApp() 103 router := gin.Default() 104 router.Use(Middleware(app)) 105 router.GET("/header", multipleWriteHeader) 106 107 response := httptest.NewRecorder() 108 req, err := http.NewRequest("GET", "/header", nil) 109 if err != nil { 110 t.Fatal(err) 111 } 112 router.ServeHTTP(response, req) 113 if respBody := response.Body.String(); respBody != "multipleWriteHeader" { 114 t.Error("wrong response body", respBody) 115 } 116 if response.Code != 500 { 117 t.Error("wrong response code", response.Code) 118 } 119 // Error metrics test the 500 response code capture. 120 app.ExpectTxnMetrics(t, internal.WantTxn{ 121 Name: pkg + ".multipleWriteHeader", 122 IsWeb: true, 123 NumErrors: 1, 124 }) 125 } 126 127 func accessTransactionGinContext(c *gin.Context) { 128 if txn := Transaction(c); nil != txn { 129 txn.NoticeError(errors.New("problem")) 130 } 131 c.Writer.WriteString("accessTransactionGinContext") 132 } 133 134 func TestContextTransaction(t *testing.T) { 135 app := integrationsupport.NewBasicTestApp() 136 router := gin.Default() 137 router.Use(Middleware(app)) 138 router.GET("/txn", accessTransactionGinContext) 139 140 response := httptest.NewRecorder() 141 req, err := http.NewRequest("GET", "/txn", nil) 142 if err != nil { 143 t.Fatal(err) 144 } 145 router.ServeHTTP(response, req) 146 if respBody := response.Body.String(); respBody != "accessTransactionGinContext" { 147 t.Error("wrong response body", respBody) 148 } 149 if response.Code != 200 { 150 t.Error("wrong response code", response.Code) 151 } 152 app.ExpectTxnMetrics(t, internal.WantTxn{ 153 Name: pkg + ".accessTransactionGinContext", 154 IsWeb: true, 155 NumErrors: 1, 156 }) 157 } 158 159 func TestNilApp(t *testing.T) { 160 var app newrelic.Application 161 router := gin.Default() 162 router.Use(Middleware(app)) 163 router.GET("/hello", hello) 164 165 response := httptest.NewRecorder() 166 req, err := http.NewRequest("GET", "/hello", nil) 167 if err != nil { 168 t.Fatal(err) 169 } 170 router.ServeHTTP(response, req) 171 if respBody := response.Body.String(); respBody != "hello response" { 172 t.Error("wrong response body", respBody) 173 } 174 } 175 176 func errorStatus(c *gin.Context) { 177 c.String(500, "an error happened") 178 } 179 180 func TestStatusCodes(t *testing.T) { 181 // Test that we are correctly able to collect status code. 182 // This behavior changed with this pull request: https://github.com/gin-gonic/gin/pull/1606 183 // In Gin v1.4.0 and below, we always recorded a 200 status, whereas with 184 // newer Gin versions we now correctly capture the status. 185 app := integrationsupport.NewBasicTestApp() 186 router := gin.Default() 187 router.Use(Middleware(app)) 188 router.GET("/err", errorStatus) 189 190 response := httptest.NewRecorder() 191 req, err := http.NewRequest("GET", "/err", nil) 192 if err != nil { 193 t.Fatal(err) 194 } 195 router.ServeHTTP(response, req) 196 if respBody := response.Body.String(); respBody != "an error happened" { 197 t.Error("wrong response body", respBody) 198 } 199 if response.Code != 500 { 200 t.Error("wrong response code", response.Code) 201 } 202 app.ExpectTxnEvents(t, []internal.WantEvent{{ 203 Intrinsics: map[string]interface{}{ 204 "name": "WebTransaction/Go/" + pkg + ".errorStatus", 205 "nr.apdexPerfZone": internal.MatchAnything, 206 }, 207 UserAttributes: map[string]interface{}{}, 208 AgentAttributes: map[string]interface{}{ 209 "httpResponseCode": 500, 210 "request.method": "GET", 211 "request.uri": "/err", 212 "response.headers.contentType": "text/plain; charset=utf-8", 213 }, 214 }}) 215 } 216 217 func noBody(c *gin.Context) { 218 c.Status(500) 219 } 220 221 func TestNoResponseBody(t *testing.T) { 222 // Test that when no response body is sent (i.e. c.Writer.Write is never 223 // called) that we still capture status code. 224 app := integrationsupport.NewBasicTestApp() 225 router := gin.Default() 226 router.Use(Middleware(app)) 227 router.GET("/nobody", noBody) 228 229 response := httptest.NewRecorder() 230 req, err := http.NewRequest("GET", "/nobody", nil) 231 if err != nil { 232 t.Fatal(err) 233 } 234 router.ServeHTTP(response, req) 235 if respBody := response.Body.String(); respBody != "" { 236 t.Error("wrong response body", respBody) 237 } 238 if response.Code != 500 { 239 t.Error("wrong response code", response.Code) 240 } 241 app.ExpectTxnEvents(t, []internal.WantEvent{{ 242 Intrinsics: map[string]interface{}{ 243 "name": "WebTransaction/Go/" + pkg + ".noBody", 244 "nr.apdexPerfZone": internal.MatchAnything, 245 }, 246 UserAttributes: map[string]interface{}{}, 247 AgentAttributes: map[string]interface{}{ 248 "httpResponseCode": 500, 249 "request.method": "GET", 250 "request.uri": "/nobody", 251 }, 252 }}) 253 }