queryBuilder.spec.jsx 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. import createQueryBuilder from 'app/views/discover/queryBuilder';
  2. import {openModal} from 'app/actionCreators/modal';
  3. import ConfigStore from 'app/stores/configStore';
  4. jest.mock('app/actionCreators/modal');
  5. describe('Query Builder', function() {
  6. afterEach(function() {
  7. jest.clearAllMocks();
  8. });
  9. describe('applyDefaults()', function() {
  10. it('generates default query with all projects', function() {
  11. const queryBuilder = createQueryBuilder(
  12. {},
  13. TestStubs.Organization({projects: [TestStubs.Project()]})
  14. );
  15. const external = queryBuilder.getExternal();
  16. expect(external.projects).toEqual([2]);
  17. expect(external.fields).toEqual(expect.arrayContaining([expect.any(String)]));
  18. expect(external.fields).toHaveLength(5);
  19. expect(external.conditions).toHaveLength(0);
  20. expect(external.aggregations).toHaveLength(0);
  21. expect(external.orderby).toBe('-timestamp');
  22. expect(external.limit).toBe(1000);
  23. });
  24. });
  25. describe('loads()', function() {
  26. let discoverMock;
  27. beforeEach(function() {
  28. discoverMock = MockApiClient.addMockResponse({
  29. url: '/organizations/org-slug/discover/query/?per_page=1000&cursor=0:0:1',
  30. method: 'POST',
  31. body: {
  32. data: [{tags_key: 'tag1', count: 5}, {tags_key: 'tag2', count: 1}],
  33. },
  34. });
  35. });
  36. afterEach(function() {
  37. MockApiClient.clearMockResponses();
  38. });
  39. it('loads tags for projects with membership', async function() {
  40. const queryBuilder = createQueryBuilder(
  41. {},
  42. TestStubs.Organization({
  43. projects: [
  44. TestStubs.Project({id: 1, isMember: false}),
  45. TestStubs.Project({id: 2}),
  46. ],
  47. access: ['org:read', 'org:write'],
  48. })
  49. );
  50. await queryBuilder.load();
  51. expect(discoverMock).toHaveBeenCalledWith(
  52. '/organizations/org-slug/discover/query/?per_page=1000&cursor=0:0:1',
  53. expect.objectContaining({
  54. data: expect.objectContaining({
  55. fields: ['tags_key'],
  56. aggregations: [['count()', null, 'count']],
  57. orderby: '-count',
  58. projects: [2],
  59. range: '90d',
  60. }),
  61. })
  62. );
  63. expect(queryBuilder.getColumns()).toContainEqual({
  64. name: 'tag1',
  65. type: 'string',
  66. isTag: true,
  67. });
  68. expect(queryBuilder.getColumns()).toContainEqual({
  69. name: 'tag2',
  70. type: 'string',
  71. isTag: true,
  72. });
  73. expect(queryBuilder.getColumns()).not.toContainEqual({
  74. name: 'environment',
  75. type: 'string',
  76. isTag: true,
  77. });
  78. });
  79. it("loads all project's tags for superuser", async function() {
  80. ConfigStore.set('user', TestStubs.User({isSuperuser: true}));
  81. const queryBuilder = createQueryBuilder(
  82. {},
  83. TestStubs.Organization({
  84. projects: [
  85. TestStubs.Project({id: 1, isMember: false}),
  86. TestStubs.Project({id: 2}),
  87. ],
  88. })
  89. );
  90. await queryBuilder.load();
  91. expect(discoverMock).toHaveBeenCalledWith(
  92. '/organizations/org-slug/discover/query/?per_page=1000&cursor=0:0:1',
  93. expect.objectContaining({
  94. data: expect.objectContaining({
  95. fields: ['tags_key'],
  96. aggregations: [['count()', null, 'count']],
  97. orderby: '-count',
  98. projects: [1, 2],
  99. range: '90d',
  100. }),
  101. })
  102. );
  103. expect(queryBuilder.getColumns()).toContainEqual({
  104. name: 'tag1',
  105. type: 'string',
  106. isTag: true,
  107. });
  108. expect(queryBuilder.getColumns()).toContainEqual({
  109. name: 'tag2',
  110. type: 'string',
  111. isTag: true,
  112. });
  113. expect(queryBuilder.getColumns()).not.toContainEqual({
  114. name: 'environment',
  115. type: 'string',
  116. isTag: true,
  117. });
  118. });
  119. it("loads all project's tags for org admins", async function() {
  120. ConfigStore.set('user', TestStubs.User());
  121. const queryBuilder = createQueryBuilder(
  122. {},
  123. TestStubs.Organization({
  124. projects: [
  125. TestStubs.Project({id: 1, isMember: false}),
  126. TestStubs.Project({id: 2}),
  127. ],
  128. access: ['org:admin'],
  129. })
  130. );
  131. await queryBuilder.load();
  132. expect(discoverMock).toHaveBeenCalledWith(
  133. '/organizations/org-slug/discover/query/?per_page=1000&cursor=0:0:1',
  134. expect.objectContaining({
  135. data: expect.objectContaining({
  136. fields: ['tags_key'],
  137. aggregations: [['count()', null, 'count']],
  138. orderby: '-count',
  139. projects: [1, 2],
  140. range: '90d',
  141. }),
  142. })
  143. );
  144. });
  145. it('loads hardcoded tags when API request fails', async function() {
  146. discoverMock = MockApiClient.addMockResponse({
  147. url: '/organizations/org-slug/discover/query/?per_page=1000&cursor=0:0:1',
  148. method: 'POST',
  149. });
  150. const queryBuilder = createQueryBuilder(
  151. {},
  152. TestStubs.Organization({projects: [TestStubs.Project()]})
  153. );
  154. await queryBuilder.load();
  155. expect(discoverMock).toHaveBeenCalled();
  156. expect(queryBuilder.getColumns()).toContainEqual({
  157. name: 'environment',
  158. type: 'string',
  159. isTag: true,
  160. });
  161. expect(queryBuilder.getColumns()).not.toContainEqual({
  162. name: 'tag1',
  163. type: 'string',
  164. isTag: true,
  165. });
  166. });
  167. });
  168. describe('fetch()', function() {
  169. let queryBuilder, discoverMock;
  170. beforeEach(function() {
  171. queryBuilder = createQueryBuilder(
  172. {},
  173. TestStubs.Organization({projects: [TestStubs.Project()]})
  174. );
  175. discoverMock = MockApiClient.addMockResponse({
  176. url: '/organizations/org-slug/discover/query/?per_page=1000&cursor=0:0:1',
  177. method: 'POST',
  178. body: {
  179. data: [],
  180. timing: {},
  181. meta: [],
  182. },
  183. });
  184. });
  185. afterEach(function() {
  186. MockApiClient.clearMockResponses();
  187. });
  188. it('makes request', async function() {
  189. const data = {projects: [1], fields: ['id']};
  190. await queryBuilder.fetch(data);
  191. expect(discoverMock).toHaveBeenCalledWith(
  192. '/organizations/org-slug/discover/query/?per_page=1000&cursor=0:0:1',
  193. expect.objectContaining({
  194. data,
  195. })
  196. );
  197. });
  198. it('handles no projects', async function() {
  199. const result = queryBuilder.fetch({projects: []});
  200. await expect(result).rejects.toMatchObject({
  201. message: 'No projects selected',
  202. });
  203. expect(discoverMock).not.toHaveBeenCalled();
  204. });
  205. });
  206. describe('updateField()', function() {
  207. let queryBuilder;
  208. beforeEach(function() {
  209. queryBuilder = createQueryBuilder(
  210. {},
  211. TestStubs.Organization({projects: [TestStubs.Project()]})
  212. );
  213. });
  214. it('updates field', function() {
  215. queryBuilder.updateField('projects', [5]);
  216. queryBuilder.updateField('conditions', [['id', '=', 'event1']]);
  217. const query = queryBuilder.getInternal();
  218. expect(query.conditions).toEqual([['id', '=', 'event1']]);
  219. });
  220. it('updates orderby if there is an aggregation and value is not a valid field', function() {
  221. queryBuilder.updateField('fields', ['id']);
  222. queryBuilder.updateField('aggregations', [['count()', null, 'count']]);
  223. const query = queryBuilder.getInternal();
  224. expect(query.orderby).toBe('-count');
  225. });
  226. it('updates orderby if there is no aggregation and value is not a valid field', function() {
  227. queryBuilder.updateField('fields', ['id']);
  228. queryBuilder.updateField('aggregations', [['count()', null, 'count']]);
  229. expect(queryBuilder.getInternal().orderby).toBe('-count');
  230. queryBuilder.updateField('aggregations', []);
  231. expect(queryBuilder.getInternal().orderby).toBe('-timestamp');
  232. });
  233. });
  234. describe('reset()', function() {
  235. let queryBuilder;
  236. beforeEach(function() {
  237. const project = TestStubs.Project({id: '1'});
  238. const projectWithoutMembership = TestStubs.Project({id: '2', isMember: false});
  239. queryBuilder = createQueryBuilder(
  240. {},
  241. TestStubs.Organization({projects: [project, projectWithoutMembership]})
  242. );
  243. });
  244. it('displays warning if invalid project is provided', function() {
  245. queryBuilder.reset({
  246. fields: ['id'],
  247. projects: [3],
  248. });
  249. expect(openModal).toHaveBeenCalled();
  250. });
  251. it('displays warning if user does not have project access', function() {
  252. queryBuilder.reset({
  253. fields: ['id'],
  254. projects: [2],
  255. });
  256. expect(openModal).toHaveBeenCalled();
  257. });
  258. it('does not display warning if user has access to all requested projects', function() {
  259. queryBuilder.reset({
  260. fields: ['id'],
  261. projects: [1],
  262. });
  263. expect(openModal).not.toHaveBeenCalled();
  264. });
  265. });
  266. describe('getColumns()', function() {
  267. let queryBuilder;
  268. beforeEach(async function() {
  269. queryBuilder = createQueryBuilder(
  270. {},
  271. TestStubs.Organization({projects: [TestStubs.Project()]})
  272. );
  273. await queryBuilder.load();
  274. });
  275. it('returns columns and tags', function() {
  276. expect(queryBuilder.getColumns()).toContainEqual({
  277. name: 'id',
  278. type: 'string',
  279. isTag: false,
  280. });
  281. expect(queryBuilder.getColumns()).toContainEqual({
  282. name: 'logger',
  283. type: 'string',
  284. isTag: true,
  285. });
  286. });
  287. });
  288. });