mri.spec.tsx 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. import type {MetricType, MRI, ParsedMRI, UseCase} from 'sentry/types/metrics';
  2. import {
  3. formatMRI,
  4. getUseCaseFromMRI,
  5. isExtractedCustomMetric,
  6. parseField,
  7. parseMRI,
  8. toMRI,
  9. } from 'sentry/utils/metrics/mri';
  10. describe('parseMRI', () => {
  11. it('should handle falsy values', () => {
  12. expect(parseMRI('')).toEqual(null);
  13. expect(parseMRI()).toEqual(null);
  14. expect(parseMRI(null)).toEqual(null);
  15. expect(parseMRI(undefined)).toEqual(null);
  16. });
  17. it.each(['c', 'd', 'e', 'g', 's'])(
  18. 'should correctly parse a valid MRI string - metric type %s',
  19. metricType => {
  20. const mri: MRI = `${metricType as MetricType}:custom/xyz@test`;
  21. const parsedMRI = {
  22. type: metricType,
  23. name: 'xyz',
  24. unit: 'test',
  25. useCase: 'custom',
  26. };
  27. expect(parseMRI(mri)).toEqual(parsedMRI);
  28. }
  29. );
  30. it.each(['spans', 'transactions', 'custom'])(
  31. 'should correctly parse a valid MRI string - use case %s',
  32. useCase => {
  33. const mri: MRI = `c:${useCase as UseCase}/xyz@test`;
  34. const parsedMRI = {
  35. type: 'c',
  36. name: `xyz`,
  37. unit: 'test',
  38. useCase,
  39. };
  40. expect(parseMRI(mri)).toEqual(parsedMRI);
  41. }
  42. );
  43. it.each(['sessions'])(
  44. 'should correctly parse a valid MRI string - use case %s',
  45. useCase => {
  46. const mri: MRI = `c:${useCase as UseCase}/xyz@test`;
  47. const parsedMRI = {
  48. type: 'c',
  49. name: `${useCase}.xyz`,
  50. unit: 'test',
  51. useCase,
  52. };
  53. expect(parseMRI(mri)).toEqual(parsedMRI);
  54. }
  55. );
  56. it.each(['foo', 'foo_bar', 'foo_9-bar', '12-!foo][]312bar'])(
  57. 'should correctly parse a valid MRI string - name %s',
  58. name => {
  59. const mri: MRI = `c:custom/${name}@test`;
  60. const parsedMRI = {
  61. type: 'c',
  62. name,
  63. unit: 'test',
  64. useCase: 'custom',
  65. };
  66. expect(parseMRI(mri)).toEqual(parsedMRI);
  67. }
  68. );
  69. it.each(['ms', 'none', 'KiB'])(
  70. 'should correctly parse a valid MRI string - name %s',
  71. unit => {
  72. const mri: MRI = `c:custom/foo@${unit}`;
  73. const parsedMRI = {
  74. type: 'c',
  75. name: 'foo',
  76. unit,
  77. useCase: 'custom',
  78. };
  79. expect(parseMRI(mri)).toEqual(parsedMRI);
  80. }
  81. );
  82. it.each([
  83. ['d:transactions/duration@millisecond', 'transaction.duration'],
  84. ['d:spans/duration@millisecond', 'span.duration'],
  85. ['d:spans/exclusive_time@millisecond', 'span.exclusive_time'],
  86. ['g:spans/self_time@millisecond', 'span.self_time'],
  87. ])('should remap certain mri names', (mri, name) => {
  88. expect(parseMRI(mri)?.name).toEqual(name);
  89. });
  90. });
  91. describe('getUseCaseFromMRI', () => {
  92. it('should return "custom" for mri containing "custom/"', () => {
  93. const mri = 'd:custom/sentry.events.symbolicator.query_task@second';
  94. const result = getUseCaseFromMRI(mri);
  95. expect(result).toBe('custom');
  96. });
  97. it('should return "transactions" for mri containing "transactions/"', () => {
  98. const mri = 'd:transactions/duration@second';
  99. const result = getUseCaseFromMRI(mri);
  100. expect(result).toBe('transactions');
  101. });
  102. it('should return undefined for invalid mris', () => {
  103. const mri = 'foobar';
  104. const result = getUseCaseFromMRI(mri);
  105. expect(result).toBeUndefined();
  106. });
  107. });
  108. describe('parseField', () => {
  109. it('should return the correct mri and aggregation from field', () => {
  110. const field = 'aggregation(c:test/project)';
  111. const result = parseField(field);
  112. expect(result).toEqual({
  113. mri: 'c:test/project',
  114. aggregation: 'aggregation',
  115. });
  116. });
  117. it('should do nothing for already formatted field', () => {
  118. const field = 'sum(my-metric)';
  119. const result = parseField(field);
  120. expect(result?.mri).toBe('my-metric');
  121. expect(result?.aggregation).toBe('sum');
  122. });
  123. it('should return null mri invalid field', () => {
  124. const field = 'invalid-field';
  125. const result = parseField(field);
  126. expect(result).toBeNull();
  127. });
  128. });
  129. describe('toMRI', () => {
  130. it.each(['c', 'd', 'e', 'g', 's'])(
  131. 'should correctly parse a valid MRI string - metric type %s',
  132. metricType => {
  133. const mri = `${metricType as MetricType}:custom/xyz@test`;
  134. const parsedMRI: ParsedMRI = {
  135. type: metricType as MetricType,
  136. name: 'xyz',
  137. unit: 'test',
  138. useCase: 'custom',
  139. };
  140. expect(toMRI(parsedMRI)).toEqual(mri);
  141. }
  142. );
  143. it.each(['sessions', 'transactions', 'custom'])(
  144. 'should correctly parse a valid MRI string - use case %s',
  145. useCase => {
  146. const mri: MRI = `c:${useCase as UseCase}/xyz@test`;
  147. const parsedMRI: ParsedMRI = {
  148. type: 'c',
  149. name: 'xyz',
  150. unit: 'test',
  151. useCase: useCase as UseCase,
  152. };
  153. expect(toMRI(parsedMRI)).toEqual(mri);
  154. }
  155. );
  156. it.each(['foo', 'foo_bar', 'foo_9-bar', '12-!foo][]312bar'])(
  157. 'should correctly parse a valid MRI string - name %s',
  158. name => {
  159. const mri: MRI = `c:custom/${name}@test`;
  160. const parsedMRI: ParsedMRI = {
  161. type: 'c',
  162. name,
  163. unit: 'test',
  164. useCase: 'custom',
  165. };
  166. expect(toMRI(parsedMRI)).toEqual(mri);
  167. }
  168. );
  169. it.each(['ms', 'none', 'KiB'])(
  170. 'should correctly parse a valid MRI string - name %s',
  171. unit => {
  172. const mri: MRI = `c:custom/foo@${unit}`;
  173. const parsedMRI: ParsedMRI = {
  174. type: 'c',
  175. name: 'foo',
  176. unit,
  177. useCase: 'custom',
  178. };
  179. expect(toMRI(parsedMRI)).toEqual(mri);
  180. }
  181. );
  182. });
  183. describe('formatMRI', () => {
  184. it('returns the metric name', () => {
  185. expect(formatMRI('c:custom/foo@none')).toEqual('foo');
  186. expect(formatMRI('c:custom/bar@ms')).toEqual('bar');
  187. expect(formatMRI('d:transactions/baz@ms')).toEqual('baz');
  188. });
  189. it('strips the projectId from virtual metrics', () => {
  190. expect(formatMRI('v:custom/foo|123@none')).toEqual('foo');
  191. expect(formatMRI('v:custom/bar|456@ms')).toEqual('bar');
  192. });
  193. });
  194. describe('isExtractedCustomMetric', () => {
  195. it('should return true if the metric name is prefixed', () => {
  196. expect(isExtractedCustomMetric({mri: 'c:custom/span_attribute_123@none'})).toBe(true);
  197. expect(isExtractedCustomMetric({mri: 's:custom/span_attribute_foo@none'})).toBe(true);
  198. expect(isExtractedCustomMetric({mri: 'g:custom/span_attribute_baz@none'})).toBe(true);
  199. });
  200. it('should return false if the metric name is not prefixed', () => {
  201. expect(isExtractedCustomMetric({mri: 'c:custom/12span_attribute_@none'})).toBe(false);
  202. expect(isExtractedCustomMetric({mri: 's:custom/foo@none'})).toBe(false);
  203. expect(isExtractedCustomMetric({mri: 'd:custom/_span_attribute_@none'})).toBe(false);
  204. expect(isExtractedCustomMetric({mri: 'g:custom/span_attributebaz@none'})).toBe(false);
  205. });
  206. });