PageRenderTime 39ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/test/unit/grails/plugins/airbrake/NoticeConstructorSpec.groovy

https://bitbucket.org/cavneb/airbrake-grails
Groovy | 459 lines | 366 code | 93 blank | 0 comment | 72 complexity | 7b5117b3d96fe224efcb505c214ca763 MD5 | raw file
  1. package grails.plugins.airbrake
  2. import grails.test.mixin.TestMixin
  3. import grails.test.mixin.support.GrailsUnitTestMixin
  4. import grails.util.GrailsWebUtil
  5. import org.codehaus.groovy.grails.plugins.testing.GrailsMockHttpServletRequest
  6. import org.codehaus.groovy.grails.plugins.testing.GrailsMockHttpServletResponse
  7. import spock.lang.Specification
  8. import spock.lang.Unroll
  9. import org.springframework.web.context.request.RequestContextHolder
  10. import org.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequest
  11. import org.codehaus.groovy.grails.exceptions.DefaultStackTraceFilterer
  12. @TestMixin(GrailsUnitTestMixin)
  13. class NoticeConstructorSpec extends Specification {
  14. def mockRequest = new GrailsMockHttpServletRequest()
  15. def mockResponse = new GrailsMockHttpServletResponse()
  16. def 'constructor supports simple properties'() {
  17. given:
  18. def exception = new RuntimeException()
  19. when:
  20. def notice = new Notice(
  21. apiKey: 'abcd',
  22. env: 'production',
  23. throwable: exception,
  24. projectRoot: '/some/root',
  25. notifierName: 'notifier',
  26. notifierUrl: 'http://my.notifier.com/about',
  27. notifierVersion: '0.1.2',
  28. paramsFilteredKeys: ['password'],
  29. sessionFilteredKeys: ['userId'],
  30. cgiDataFilteredKeys: ['dbConnectionString'],
  31. user: [id: '12345']
  32. )
  33. then:
  34. notice.apiKey == 'abcd'
  35. notice.env == 'production'
  36. notice.throwable == exception
  37. notice.projectRoot == '/some/root'
  38. notice.notifierName == 'notifier'
  39. notice.notifierUrl == 'http://my.notifier.com/about'
  40. notice.notifierVersion == '0.1.2'
  41. notice.paramsFilteredKeys == ['password']
  42. notice.sessionFilteredKeys == ['userId']
  43. notice.cgiDataFilteredKeys == ['dbConnectionString']
  44. notice.user == [id: '12345']
  45. }
  46. def 'no url is no web request'() {
  47. when:
  48. def notice = new Notice()
  49. then:
  50. notice.url == null
  51. }
  52. def 'url should use supplied url over the url of the current webRequest'() {
  53. given:
  54. mockRequest.scheme = 'http'
  55. mockRequest.serverName = 'myhost.com'
  56. mockRequest.serverPort = 80
  57. mockRequest.forwardURI = '/controller/action'
  58. bindMockRequest()
  59. when:
  60. def notice = new Notice(url: 'http://myhost.com/some/custom/url')
  61. then:
  62. notice.url == 'http://myhost.com/some/custom/url'
  63. }
  64. @Unroll
  65. def 'url should handle protocol: #scheme and port: #port'() {
  66. given:
  67. mockRequest.scheme = scheme
  68. mockRequest.serverName = host
  69. mockRequest.serverPort = port
  70. mockRequest.forwardURI = '/'
  71. bindMockRequest()
  72. when:
  73. def notice = new Notice()
  74. then:
  75. notice.url == expectedUrl
  76. where:
  77. scheme | host | port || expectedUrl
  78. 'http' | 'myhost.com' | 80 || 'http://myhost.com/'
  79. 'http' | 'myhost.com' | 8080 || 'http://myhost.com:8080/'
  80. 'https' | 'myhost.com' | 443 || 'https://myhost.com/'
  81. 'https' | 'myhost.com' | 4434 || 'https://myhost.com:4434/'
  82. }
  83. def 'url should include forwardUri'() {
  84. given:
  85. mockRequest.scheme = 'http'
  86. mockRequest.serverName = 'myhost.com'
  87. mockRequest.serverPort = 80
  88. mockRequest.forwardURI = '/controller/action'
  89. bindMockRequest()
  90. when:
  91. def notice = new Notice()
  92. then:
  93. notice.url == 'http://myhost.com/controller/action'
  94. }
  95. def 'url should handle no forwardUri '() {
  96. given:
  97. mockRequest.scheme = 'http'
  98. mockRequest.serverName = 'myhost.com'
  99. mockRequest.serverPort = 80
  100. mockRequest.forwardURI = null
  101. bindMockRequest()
  102. when:
  103. def notice = new Notice()
  104. then:
  105. notice.url == 'http://myhost.com'
  106. }
  107. @Unroll
  108. def 'url should handle queryString: #queryString'() {
  109. given:
  110. mockRequest.scheme = 'http'
  111. mockRequest.serverName = 'myhost.com'
  112. mockRequest.serverPort = 80
  113. mockRequest.forwardURI = '/controller/action'
  114. mockRequest.queryString = queryString
  115. bindMockRequest()
  116. when:
  117. def notice = new Notice()
  118. then:
  119. notice.url == expectedUrl
  120. where:
  121. queryString || expectedUrl
  122. null || 'http://myhost.com/controller/action'
  123. '' || 'http://myhost.com/controller/action'
  124. 'id=1234' || 'http://myhost.com/controller/action?id=1234'
  125. }
  126. @Unroll
  127. def 'component should be based on component, controller or controllerName'() {
  128. given:
  129. def webRequest = bindMockRequest()
  130. webRequest.controllerName = controllerName
  131. when:
  132. def notice = new Notice(component: component, controller: controller)
  133. then:
  134. notice.component == expectedComponent
  135. where:
  136. component | controller | controllerName || expectedComponent
  137. 'component' | 'controller' | 'controllerName' || 'component'
  138. null | 'controller' | 'controllerName' || 'controller'
  139. null | null | 'controllerName' || 'controllerName'
  140. null | null | null || null
  141. }
  142. @Unroll
  143. def 'action should be based on action or actionName'() {
  144. given:
  145. def webRequest = bindMockRequest()
  146. webRequest.actionName = actionName
  147. when:
  148. def notice = new Notice(action: action)
  149. then:
  150. notice.action == expectedAction
  151. where:
  152. action | actionName || expectedAction
  153. 'action' | 'actionName' || 'action'
  154. null | 'actionName' || 'actionName'
  155. null | null || null
  156. }
  157. @Unroll
  158. def 'params should be based on params or webRequest.paramsMap'() {
  159. given:
  160. if (paramsMap) {
  161. mockRequest.addParameters(paramsMap)
  162. }
  163. def webRequest = bindMockRequest()
  164. when:
  165. def notice = new Notice(params: params)
  166. then:
  167. notice.params == expectedParams
  168. where:
  169. params | paramsMap || expectedParams
  170. [one: 'two'] | [three: 'four'] || [one: 'two']
  171. null | [three: 'four'] || [three: 'four']
  172. null | null || [:]
  173. }
  174. def 'cgiData should be based on headers'() {
  175. given:
  176. mockRequest.addHeader('User-Agent', 'Mozilla')
  177. mockRequest.addHeader('Referer', 'http://your.referrer.com')
  178. bindMockRequest()
  179. when:
  180. def notice = new Notice()
  181. then:
  182. notice.cgiData == ['User-Agent': 'Mozilla', Referer: 'http://your.referrer.com']
  183. }
  184. def 'cgiData should uses supplied cgiData over headers'() {
  185. given:
  186. mockRequest.addHeader('ignored', 'header')
  187. bindMockRequest()
  188. when:
  189. def notice = new Notice(cgiData: [custom: 'header'])
  190. then:
  191. notice.cgiData == [custom: 'header']
  192. }
  193. def 'session should be based on session'() {
  194. given:
  195. mockRequest.session.setAttribute('one', 'two')
  196. mockRequest.session.setAttribute('three','four')
  197. bindMockRequest()
  198. when:
  199. def notice = new Notice()
  200. then:
  201. notice.session == [one: 'two', three: 'four']
  202. }
  203. def 'session should uses supplied session over webRequest session'() {
  204. given:
  205. mockRequest.session.setAttribute('ignored', 'session')
  206. bindMockRequest()
  207. when:
  208. def notice = new Notice(session: [custom: 'session'])
  209. then:
  210. notice.session == [custom: 'session']
  211. }
  212. def 'hostname'() {
  213. when:
  214. def notice = new Notice()
  215. then:
  216. notice.hostname == InetAddress.localHost.hostName
  217. }
  218. @Unroll
  219. def 'constructor should filter #paramsMap'() {
  220. when: 'have some some paramsFilters'
  221. String filteredKeysString = "${paramsMap}FilteredKeys".toString()
  222. def notice = new Notice((filteredKeysString): ['ask'], (paramsMap): [ask: 'me', something: 'interesting'] )
  223. then:
  224. notice."$paramsMap" == [ask: '[FILTERED]', something: 'interesting']
  225. where:
  226. paramsMap << ['params', 'session', 'cgiData']
  227. }
  228. @Unroll
  229. def 'constructor should filter #paramsMap using regular expression filters'() {
  230. when: 'we construct the notice'
  231. String filteredKeysString = "${paramsMap}FilteredKeys".toString()
  232. def notice = new Notice((filteredKeysString): ['a.*'], (paramsMap): [ask: 'me', something: 'interesting'] )
  233. then:
  234. notice."$paramsMap" == [ask: '[FILTERED]', something: 'interesting']
  235. where:
  236. paramsMap << ['params', 'session', 'cgiData']
  237. }
  238. @Unroll
  239. def 'errorClass is based on throwable or the errroClass argument'() {
  240. when:
  241. def notice = new Notice(throwable: exception, errorClass: errorClass)
  242. then:
  243. notice.errorClass == expectedErrorClass
  244. where:
  245. exception | errorClass || expectedErrorClass
  246. new RuntimeException() | 'customErrorClass' || 'java.lang.RuntimeException'
  247. null | 'customErrorClass' || 'customErrorClass'
  248. null | null || null
  249. }
  250. @Unroll
  251. def 'backtrace is based on throwable backtrace or the backtrace argument'() {
  252. when:
  253. def notice = new Notice(throwable: exception, backtrace: backtrace)
  254. then:
  255. notice.backtrace == expectedBacktrace
  256. where:
  257. exception | backtrace || expectedBacktrace
  258. new RuntimeException(stackTrace: exceptionStackTrace) | argsStackTrace || exceptionBacktrace
  259. null | argsStackTrace || argsBacktrace
  260. null | null || []
  261. }
  262. def 'constructor filters the stackTrace'() {
  263. given:
  264. def exception = new RuntimeException('Damn that Rabbit')
  265. def anyStackTraceFilter = GroovySpy(DefaultStackTraceFilterer, global: true)
  266. when:
  267. new Notice(throwable: exception, stackTraceFilterer: new DefaultStackTraceFilterer())
  268. then:
  269. 1 * anyStackTraceFilter.filter(exception)
  270. }
  271. def 'constructor should not try to filter the stackTrace of a null throwable'() {
  272. given:
  273. def anyStackTraceFilter = GroovySpy(DefaultStackTraceFilterer, global: true)
  274. when:
  275. new Notice(throwable: null)
  276. then:
  277. 0 * anyStackTraceFilter.filter(_)
  278. }
  279. def 'constructor should not try to filter the stackTrace of a null stackTraceFilterer'() {
  280. given:
  281. def exception = new RuntimeException('Damn that Rabbit')
  282. def anyStackTraceFilter = GroovySpy(DefaultStackTraceFilterer, global: true)
  283. when:
  284. new Notice(throwable: exception)
  285. then:
  286. 0 * anyStackTraceFilter.filter(_)
  287. }
  288. private getExceptionStackTrace() {
  289. [new StackTraceElement('com.acme.RabbitTraps', 'catch', 'RabbitTraps.groovy', 10 )]
  290. }
  291. private getArgsStackTrace() {
  292. [new StackTraceElement('com.acme.RabbitTrapsController', 'net', 'RabbitTrapsController.groovy', 5 )]
  293. }
  294. private getExceptionBacktrace() {
  295. [new Backtrace('com.acme.RabbitTraps', 'catch', 'RabbitTraps.groovy', 10 )]
  296. }
  297. private getArgsBacktrace() {
  298. [new Backtrace('com.acme.RabbitTrapsController', 'net', 'RabbitTrapsController.groovy', 5 )]
  299. }
  300. @Unroll
  301. def 'backtrace parsed from a list of maps as the backtrace argument'() {
  302. when:
  303. def backtraceMaps = [
  304. [className: 'com.acme.RabbitTraps', methodName: 'catch', fileName: 'RabbitTraps.groovy', lineNumber: 10],
  305. [className: 'com.acme.RabbitTrapsController', methodName: 'net', fileName: 'RabbitTrapsController.groovy', lineNumber: 5]
  306. ]
  307. def notice = new Notice(backtrace: backtraceMaps)
  308. then:
  309. notice.backtrace == [
  310. new Backtrace('com.acme.RabbitTraps', 'catch', 'RabbitTraps.groovy', 10 ),
  311. new Backtrace('com.acme.RabbitTrapsController', 'net', 'RabbitTrapsController.groovy', 5 )
  312. ]
  313. }
  314. def 'constructor creates a Notice with the right error message'() {
  315. when:
  316. def notice = new Notice(errorMessage: 'That rascally rabbit escaped')
  317. then:
  318. notice.errorMessage == 'That rascally rabbit escaped'
  319. notice.backtrace == []
  320. notice.errorClass == null
  321. }
  322. def 'constructor creates a Notice with the right error message, class and backtrace'() {
  323. given:
  324. def stackTrace = [new StackTraceElement('com.acme.RabbitTraps', 'catch', 'RabbitTraps.groovy', 10 )]
  325. def exception = new RuntimeException('That rascally rabbit escaped')
  326. exception.stackTrace = stackTrace
  327. when:
  328. def notice = new Notice(throwable: exception)
  329. then:
  330. notice.errorMessage == 'That rascally rabbit escaped'
  331. notice.errorClass == 'java.lang.RuntimeException'
  332. notice.backtrace == [new Backtrace('com.acme.RabbitTraps', 'catch', 'RabbitTraps.groovy', 10 )]
  333. }
  334. def 'constructor creates a Notice with the the exception message rather than supplied message'() {
  335. given:
  336. def exception = new RuntimeException('Damn that Rabbit')
  337. when:
  338. def notice = new Notice(throwable: exception, errorMessage: 'That rascally rabbit escaped')
  339. then:
  340. notice.errorMessage == 'Damn that Rabbit'
  341. }
  342. def 'RequestContext should override webRequest data'() {
  343. given:
  344. mockRequest.scheme = 'http'
  345. mockRequest.serverName = 'myhost.com'
  346. mockRequest.serverPort = 80
  347. mockRequest.forwardURI = '/controller/action'
  348. bindMockRequest()
  349. and:
  350. NoticeContextHolder.addNoticeContext('customComponent', 'customAction', [some: 'param'])
  351. when:
  352. def notice = new Notice()
  353. then:
  354. notice.url == 'http://myhost.com/controller/action' // this is not changed by the NoticeContext
  355. notice.component == 'customComponent'
  356. notice.action == 'customAction'
  357. notice.params == [some: 'param']
  358. }
  359. private GrailsWebRequest bindMockRequest() {
  360. GrailsWebUtil.bindMockWebRequest(applicationContext, mockRequest, mockResponse)
  361. }
  362. def cleanup() {
  363. NoticeContextHolder.clearNoticeContext()
  364. RequestContextHolder.requestAttributes = null
  365. }
  366. }