utils.spec.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. import {EventFixture} from 'sentry-fixture/event';
  2. import {
  3. getCurlCommand,
  4. getCurrentThread,
  5. getThreadById,
  6. objectToSortedTupleArray,
  7. removeFilterMaskedEntries,
  8. stringifyQueryList,
  9. } from 'sentry/components/events/interfaces/utils';
  10. import {MetaProxy, withMeta} from 'sentry/components/events/meta/metaProxy';
  11. import {FILTER_MASK} from 'sentry/constants';
  12. import {EntryType} from 'sentry/types/event';
  13. describe('components/interfaces/utils', function () {
  14. describe('getCurlCommand()', function () {
  15. it('should convert an http request object to an equivalent unix curl command string', function () {
  16. expect(
  17. getCurlCommand({
  18. apiTarget: null,
  19. cookies: [
  20. ['foo', 'bar'],
  21. ['biz', 'baz'],
  22. ],
  23. url: 'http://example.com/foo',
  24. headers: [
  25. ['Referer', 'http://example.com'],
  26. [
  27. 'User-Agent',
  28. 'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.72 Safari/537.36',
  29. ],
  30. ['Content-Type', 'application/json'],
  31. ],
  32. env: {
  33. ENV: 'prod',
  34. },
  35. fragment: '',
  36. query: [['foo', 'bar']],
  37. data: '{"hello": "world"}',
  38. method: 'GET',
  39. })
  40. ).toEqual(
  41. 'curl \\\n' +
  42. ' -H "Content-Type: application/json" \\\n' +
  43. ' -H "Referer: http://example.com" \\\n' +
  44. ' -H "User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.72 Safari/537.36" \\\n' +
  45. ' --data "{\\"hello\\": \\"world\\"}" \\\n' +
  46. ' "http://example.com/foo?foo=bar"'
  47. );
  48. // --compressed (because Accept-Encoding: gzip)
  49. expect(
  50. getCurlCommand({
  51. apiTarget: null,
  52. url: 'http://example.com/foo',
  53. headers: [
  54. ['Content-Type', 'application/json'],
  55. ['Referer', 'http://example.com'],
  56. ['Accept-Encoding', 'gzip'],
  57. ],
  58. env: {
  59. ENV: 'prod',
  60. },
  61. fragment: '',
  62. query: [['foo', 'bar']],
  63. data: '{"hello": "world"}',
  64. method: 'GET',
  65. })
  66. ).toEqual(
  67. 'curl \\\n' +
  68. ' --compressed \\\n' +
  69. ' -H "Accept-Encoding: gzip" \\\n' +
  70. ' -H "Content-Type: application/json" \\\n' +
  71. ' -H "Referer: http://example.com" \\\n' +
  72. ' --data "{\\"hello\\": \\"world\\"}" \\\n' +
  73. ' "http://example.com/foo?foo=bar"'
  74. );
  75. // Do not add `data` if `data` is missing
  76. expect(
  77. getCurlCommand({
  78. apiTarget: null,
  79. url: 'http://example.com/foo',
  80. headers: [],
  81. env: {
  82. ENV: 'prod',
  83. },
  84. fragment: '',
  85. query: [['foo', 'bar']],
  86. method: 'GET',
  87. })
  88. ).toEqual('curl \\\n "http://example.com/foo?foo=bar"');
  89. // Do not add `data` if `data` is empty object
  90. expect(
  91. getCurlCommand({
  92. apiTarget: null,
  93. url: 'http://example.com/foo',
  94. headers: [],
  95. env: {
  96. ENV: 'prod',
  97. },
  98. inferredContentType: null,
  99. fragment: '',
  100. data: {},
  101. method: 'GET',
  102. })
  103. ).toEqual('curl \\\n "http://example.com/foo"');
  104. // Filter out undefined headers
  105. expect(
  106. getCurlCommand({
  107. apiTarget: null,
  108. url: 'http://example.com/foo',
  109. headers: [
  110. ['Referer', 'http://example.com'],
  111. ['Content-Type', 'application/json'],
  112. undefined as any,
  113. ],
  114. data: '{"hello": "world"}',
  115. method: 'GET',
  116. })
  117. ).toEqual(
  118. 'curl \\\n' +
  119. ' -H "Content-Type: application/json" \\\n' +
  120. ' -H "Referer: http://example.com" \\\n' +
  121. ' --data "{\\"hello\\": \\"world\\"}" \\\n' +
  122. ' "http://example.com/foo"'
  123. );
  124. // Filter out null headers
  125. expect(
  126. getCurlCommand({
  127. apiTarget: null,
  128. url: 'http://example.com/foo',
  129. headers: [
  130. ['Referer', 'http://example.com'],
  131. ['Content-Type', 'application/json'],
  132. null as any,
  133. ],
  134. data: '{"hello": "world"}',
  135. method: 'GET',
  136. })
  137. ).toEqual(
  138. 'curl \\\n' +
  139. ' -H "Content-Type: application/json" \\\n' +
  140. ' -H "Referer: http://example.com" \\\n' +
  141. ' --data "{\\"hello\\": \\"world\\"}" \\\n' +
  142. ' "http://example.com/foo"'
  143. );
  144. // Escape escaped strings.
  145. expect(
  146. getCurlCommand({
  147. apiTarget: null,
  148. cookies: [
  149. ['foo', 'bar'],
  150. ['biz', 'baz'],
  151. ],
  152. url: 'http://example.com/foo',
  153. headers: [
  154. ['Referer', 'http://example.com'],
  155. [
  156. 'User-Agent',
  157. 'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.72 Safari/537.36',
  158. ],
  159. ['Content-Type', 'application/json'],
  160. ],
  161. env: {
  162. ENV: 'prod',
  163. },
  164. fragment: '',
  165. query: [],
  166. data: '{"a":"b\\"c"}',
  167. method: 'GET',
  168. })
  169. ).toEqual(
  170. 'curl \\\n' +
  171. ' -H "Content-Type: application/json" \\\n' +
  172. ' -H "Referer: http://example.com" \\\n' +
  173. ' -H "User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.72 Safari/537.36" \\\n' +
  174. ' --data "{\\"a\\":\\"b\\\\\\"c\\"}" \\\n' +
  175. ' "http://example.com/foo"'
  176. );
  177. // Escape strings with special bash characters
  178. expect(
  179. getCurlCommand({
  180. apiTarget: null,
  181. url: 'http://example.com/foo${not_a_variable}',
  182. headers: [
  183. ['Referer', 'http://example.com'],
  184. [
  185. 'User-Agent',
  186. 'Mozilla/5.0 ("Windows" NT 6.2; WOW64) $not_a_variable `test`',
  187. ],
  188. ['Content-Type', 'application/json'],
  189. ],
  190. fragment: '',
  191. query: [],
  192. data: '{"a$TEST":"b\\"c"}',
  193. method: 'GET',
  194. })
  195. ).toEqual(
  196. 'curl \\\n' +
  197. ' -H "Content-Type: application/json" \\\n' +
  198. ' -H "Referer: http://example.com" \\\n' +
  199. ' -H "User-Agent: Mozilla/5.0 (\\"Windows\\" NT 6.2; WOW64) \\$not_a_variable \\`test\\`" \\\n' +
  200. ' --data "{\\"a\\$TEST\\":\\"b\\\\\\"c\\"}" \\\n' +
  201. ' "http://example.com/foo\\${not_a_variable}"'
  202. );
  203. });
  204. it('works with a Proxy', function () {
  205. const spy = jest.spyOn(MetaProxy.prototype, 'get');
  206. const data = {
  207. apiTarget: null,
  208. fragment: '',
  209. cookies: [],
  210. inferredContentType: null,
  211. env: {
  212. SERVER_NAME: 'sentry',
  213. SERVER_PORT: '443',
  214. REMOTE_ADDR: '127.0.0.1',
  215. },
  216. headers: [
  217. ['Accept-Language', 'en'],
  218. ['Referer', 'http://example.com'],
  219. [
  220. 'User-Agent',
  221. 'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.72 Safari/537.36',
  222. ],
  223. ['Content-Type', 'application/json'],
  224. ['Referer', 'http://example.com'],
  225. ['Accept-Encoding', 'gzip'],
  226. ] as [string, string][],
  227. url: 'https://www.sentry.io',
  228. query: [],
  229. data: null,
  230. method: 'GET',
  231. };
  232. const eventWithProxy = withMeta(data);
  233. getCurlCommand(eventWithProxy);
  234. // This may need to change, but we should aim to keep this low
  235. expect(spy.mock.calls.length).toBeLessThan(200);
  236. });
  237. });
  238. describe('objectToSortedTupleArray()', function () {
  239. it('should convert a key/value object to a sorted array of key/value tuples', function () {
  240. expect(
  241. objectToSortedTupleArray({
  242. foo: ['bar', 'baz'],
  243. })
  244. ).toEqual([
  245. ['foo', 'bar'],
  246. ['foo', 'baz'],
  247. ]);
  248. });
  249. });
  250. describe('removeFilterMaskedEntries()', function () {
  251. const rawData = {
  252. id: '26',
  253. name: FILTER_MASK,
  254. username: 'maiseythedog',
  255. email: FILTER_MASK,
  256. };
  257. it('should remove filtered values', function () {
  258. const result = removeFilterMaskedEntries(rawData);
  259. expect(result).not.toHaveProperty('name');
  260. expect(result).not.toHaveProperty('email');
  261. });
  262. it('should preserve unfiltered values', function () {
  263. const result = removeFilterMaskedEntries(rawData);
  264. expect(result).toHaveProperty('id');
  265. expect(result.id).toEqual('26');
  266. expect(result).toHaveProperty('username');
  267. expect(result.username).toEqual('maiseythedog');
  268. });
  269. });
  270. describe('stringifyQueryList()', function () {
  271. it('should return query if it is a string', function () {
  272. const query = stringifyQueryList('query');
  273. expect(query).toEqual('query');
  274. });
  275. it('should parse query tuples', function () {
  276. const query = stringifyQueryList([
  277. ['field', 'ops.http'],
  278. ['field', 'ops.db'],
  279. ['field', 'total.time'],
  280. ['numBuckets', '100'],
  281. ]);
  282. expect(query).toEqual(
  283. 'field=ops.http&field=ops.db&field=total.time&numBuckets=100'
  284. );
  285. });
  286. });
  287. describe('getCurrentThread()', function () {
  288. it('should return current thread if available', function () {
  289. const thread = getCurrentThread(
  290. EventFixture({
  291. entries: [
  292. {
  293. data: {
  294. values: [
  295. {
  296. id: 13920,
  297. current: true,
  298. crashed: true,
  299. name: 'puma 002',
  300. stacktrace: null,
  301. rawStacktrace: null,
  302. state: 'WAITING',
  303. },
  304. ],
  305. },
  306. type: EntryType.THREADS,
  307. },
  308. ],
  309. })
  310. );
  311. expect(thread?.name).toEqual('puma 002');
  312. });
  313. });
  314. describe('getThreadById()', function () {
  315. it('should return thread by given id if available', function () {
  316. const thread = getThreadById(
  317. EventFixture({
  318. entries: [
  319. {
  320. data: {
  321. values: [
  322. {
  323. id: 13920,
  324. current: true,
  325. crashed: true,
  326. name: 'puma 002',
  327. stacktrace: null,
  328. rawStacktrace: null,
  329. state: 'WAITING',
  330. },
  331. ],
  332. },
  333. type: EntryType.THREADS,
  334. },
  335. ],
  336. }),
  337. 13920
  338. );
  339. expect(thread?.name).toEqual('puma 002');
  340. });
  341. });
  342. });