query_ctrl.test.ts 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. import { StackdriverQueryCtrl } from '../query_ctrl';
  2. import { TemplateSrvStub } from 'test/specs/helpers';
  3. describe('StackdriverQueryCtrl', () => {
  4. let ctrl;
  5. let result;
  6. describe('when initializing query editor', () => {
  7. beforeEach(() => {
  8. const existingFilters = ['key1', '=', 'val1', 'AND', 'key2', '=', 'val2'];
  9. ctrl = createCtrlWithFakes(existingFilters);
  10. });
  11. it('should initialize filter segments using the target filter values', () => {
  12. expect(ctrl.filterSegments.filterSegments.length).toBe(8);
  13. expect(ctrl.filterSegments.filterSegments[0].type).toBe('key');
  14. expect(ctrl.filterSegments.filterSegments[1].type).toBe('operator');
  15. expect(ctrl.filterSegments.filterSegments[2].type).toBe('value');
  16. expect(ctrl.filterSegments.filterSegments[3].type).toBe('condition');
  17. expect(ctrl.filterSegments.filterSegments[4].type).toBe('key');
  18. expect(ctrl.filterSegments.filterSegments[5].type).toBe('operator');
  19. expect(ctrl.filterSegments.filterSegments[6].type).toBe('value');
  20. expect(ctrl.filterSegments.filterSegments[7].type).toBe('plus-button');
  21. });
  22. });
  23. describe('group bys', () => {
  24. beforeEach(() => {
  25. ctrl = createCtrlWithFakes();
  26. });
  27. describe('when labels are fetched', () => {
  28. beforeEach(async () => {
  29. ctrl.metricLabels = { 'metric-key-1': ['metric-value-1'] };
  30. ctrl.resourceLabels = { 'resource-key-1': ['resource-value-1'] };
  31. result = await ctrl.getGroupBys();
  32. });
  33. it('should populate group bys segments', () => {
  34. expect(result.length).toBe(3);
  35. expect(result[0].value).toBe('metric.label.metric-key-1');
  36. expect(result[1].value).toBe('resource.label.resource-key-1');
  37. expect(result[2].value).toBe('-- remove group by --');
  38. });
  39. });
  40. describe('when a group by label is selected', () => {
  41. beforeEach(async () => {
  42. ctrl.metricLabels = {
  43. 'metric-key-1': ['metric-value-1'],
  44. 'metric-key-2': ['metric-value-2'],
  45. };
  46. ctrl.resourceLabels = {
  47. 'resource-key-1': ['resource-value-1'],
  48. 'resource-key-2': ['resource-value-2'],
  49. };
  50. ctrl.target.aggregation.groupBys = ['metric.label.metric-key-1', 'resource.label.resource-key-1'];
  51. result = await ctrl.getGroupBys();
  52. });
  53. it('should not be used to populate group bys segments', () => {
  54. expect(result.length).toBe(3);
  55. expect(result[0].value).toBe('metric.label.metric-key-2');
  56. expect(result[1].value).toBe('resource.label.resource-key-2');
  57. expect(result[2].value).toBe('-- remove group by --');
  58. });
  59. });
  60. describe('when a group by is selected', () => {
  61. beforeEach(() => {
  62. const removeSegment = { fake: true, value: '-- remove group by --' };
  63. const segment = { value: 'groupby1' };
  64. ctrl.groupBySegments = [segment, removeSegment];
  65. ctrl.groupByChanged(segment);
  66. });
  67. it('should be added to group bys list', () => {
  68. expect(ctrl.target.aggregation.groupBys.length).toBe(1);
  69. });
  70. });
  71. describe('when a selected group by is removed', () => {
  72. beforeEach(() => {
  73. const removeSegment = { fake: true, value: '-- remove group by --' };
  74. const segment = { value: 'groupby1' };
  75. ctrl.groupBySegments = [segment, removeSegment];
  76. ctrl.groupByChanged(removeSegment);
  77. });
  78. it('should be added to group bys list', () => {
  79. expect(ctrl.target.aggregation.groupBys.length).toBe(0);
  80. });
  81. });
  82. });
  83. describe('filters', () => {
  84. beforeEach(() => {
  85. ctrl = createCtrlWithFakes();
  86. });
  87. describe('when values for a condition filter part are fetched', () => {
  88. beforeEach(async () => {
  89. const segment = { type: 'condition' };
  90. result = await ctrl.getFilters(segment, 0);
  91. });
  92. it('should populate condition segments', () => {
  93. expect(result.length).toBe(1);
  94. expect(result[0].value).toBe('AND');
  95. });
  96. });
  97. describe('when values for a operator filter part are fetched', () => {
  98. beforeEach(async () => {
  99. const segment = { type: 'operator' };
  100. result = await ctrl.getFilters(segment, 0);
  101. });
  102. it('should populate group bys segments', () => {
  103. expect(result.length).toBe(4);
  104. expect(result[0].value).toBe('=');
  105. expect(result[1].value).toBe('!=');
  106. expect(result[2].value).toBe('=~');
  107. expect(result[3].value).toBe('!=~');
  108. });
  109. });
  110. describe('when values for a key filter part are fetched', () => {
  111. beforeEach(async () => {
  112. ctrl.metricLabels = {
  113. 'metric-key-1': ['metric-value-1'],
  114. 'metric-key-2': ['metric-value-2'],
  115. };
  116. ctrl.resourceLabels = {
  117. 'resource-key-1': ['resource-value-1'],
  118. 'resource-key-2': ['resource-value-2'],
  119. };
  120. const segment = { type: 'key' };
  121. result = await ctrl.getFilters(segment, 0);
  122. });
  123. it('should populate filter key segments', () => {
  124. expect(result.length).toBe(5);
  125. expect(result[0].value).toBe('metric.label.metric-key-1');
  126. expect(result[1].value).toBe('metric.label.metric-key-2');
  127. expect(result[2].value).toBe('resource.label.resource-key-1');
  128. expect(result[3].value).toBe('resource.label.resource-key-2');
  129. expect(result[4].value).toBe('-- remove filter --');
  130. });
  131. });
  132. describe('when values for a value filter part are fetched', () => {
  133. beforeEach(async () => {
  134. ctrl.metricLabels = {
  135. 'metric-key-1': ['metric-value-1'],
  136. 'metric-key-2': ['metric-value-2'],
  137. };
  138. ctrl.resourceLabels = {
  139. 'resource-key-1': ['resource-value-1'],
  140. 'resource-key-2': ['resource-value-2'],
  141. };
  142. ctrl.filterSegments.filterSegments = [
  143. { type: 'key', value: 'metric.label.metric-key-1' },
  144. { type: 'operator', value: '=' },
  145. ];
  146. const segment = { type: 'value' };
  147. result = await ctrl.getFilters(segment, 2);
  148. });
  149. it('should populate filter value segments', () => {
  150. expect(result.length).toBe(1);
  151. expect(result[0].value).toBe('metric-value-1');
  152. });
  153. });
  154. describe('when a filter is created by clicking on plus button', () => {
  155. describe('and there are no other filters', () => {
  156. beforeEach(() => {
  157. const segment = { value: 'filterkey1', type: 'plus-button' };
  158. ctrl.filterSegments.filterSegments = [segment];
  159. ctrl.filterSegmentUpdated(segment, 0);
  160. });
  161. it('should transform the plus button segment to a key segment', () => {
  162. expect(ctrl.filterSegments.filterSegments[0].type).toBe('key');
  163. });
  164. it('should add an operator, value segment and plus button segment', () => {
  165. expect(ctrl.filterSegments.filterSegments.length).toBe(3);
  166. expect(ctrl.filterSegments.filterSegments[1].type).toBe('operator');
  167. expect(ctrl.filterSegments.filterSegments[2].type).toBe('value');
  168. });
  169. });
  170. });
  171. describe('when has one existing filter', () => {
  172. describe('and user clicks on key segment', () => {
  173. beforeEach(() => {
  174. const existingKeySegment = { value: 'filterkey1', type: 'key' };
  175. const existingOperatorSegment = { value: '=', type: 'operator' };
  176. const existingValueSegment = { value: 'filtervalue', type: 'value' };
  177. const plusSegment = { value: '', type: 'plus-button' };
  178. ctrl.filterSegments.filterSegments = [
  179. existingKeySegment,
  180. existingOperatorSegment,
  181. existingValueSegment,
  182. plusSegment,
  183. ];
  184. ctrl.filterSegmentUpdated(existingKeySegment, 0);
  185. });
  186. it('should not add any new segments', () => {
  187. expect(ctrl.filterSegments.filterSegments.length).toBe(4);
  188. expect(ctrl.filterSegments.filterSegments[0].type).toBe('key');
  189. expect(ctrl.filterSegments.filterSegments[1].type).toBe('operator');
  190. expect(ctrl.filterSegments.filterSegments[2].type).toBe('value');
  191. });
  192. });
  193. describe('and user clicks on value segment and value not equal to fake value', () => {
  194. beforeEach(() => {
  195. const existingKeySegment = { value: 'filterkey1', type: 'key' };
  196. const existingOperatorSegment = { value: '=', type: 'operator' };
  197. const existingValueSegment = { value: 'filtervalue', type: 'value' };
  198. ctrl.filterSegments.filterSegments = [existingKeySegment, existingOperatorSegment, existingValueSegment];
  199. ctrl.filterSegmentUpdated(existingValueSegment, 2);
  200. });
  201. it('should ensure that plus segment exists', () => {
  202. expect(ctrl.filterSegments.filterSegments.length).toBe(4);
  203. expect(ctrl.filterSegments.filterSegments[0].type).toBe('key');
  204. expect(ctrl.filterSegments.filterSegments[1].type).toBe('operator');
  205. expect(ctrl.filterSegments.filterSegments[2].type).toBe('value');
  206. expect(ctrl.filterSegments.filterSegments[3].type).toBe('plus-button');
  207. });
  208. });
  209. describe('and user clicks on value segment and value is equal to fake value', () => {
  210. beforeEach(() => {
  211. const existingKeySegment = { value: 'filterkey1', type: 'key' };
  212. const existingOperatorSegment = { value: '=', type: 'operator' };
  213. const existingValueSegment = { value: ctrl.defaultFilterValue, type: 'value' };
  214. ctrl.filterSegments.filterSegments = [existingKeySegment, existingOperatorSegment, existingValueSegment];
  215. ctrl.filterSegmentUpdated(existingValueSegment, 2);
  216. });
  217. it('should not add plus segment', () => {
  218. expect(ctrl.filterSegments.filterSegments.length).toBe(3);
  219. expect(ctrl.filterSegments.filterSegments[0].type).toBe('key');
  220. expect(ctrl.filterSegments.filterSegments[1].type).toBe('operator');
  221. expect(ctrl.filterSegments.filterSegments[2].type).toBe('value');
  222. });
  223. });
  224. describe('and user removes key segment', () => {
  225. beforeEach(() => {
  226. const existingKeySegment = { value: ctrl.defaultRemoveFilterValue, type: 'key' };
  227. const existingOperatorSegment = { value: '=', type: 'operator' };
  228. const existingValueSegment = { value: 'filtervalue', type: 'value' };
  229. const plusSegment = { value: '', type: 'plus-button' };
  230. ctrl.filterSegments.filterSegments = [
  231. existingKeySegment,
  232. existingOperatorSegment,
  233. existingValueSegment,
  234. plusSegment,
  235. ];
  236. ctrl.filterSegmentUpdated(existingKeySegment, 0);
  237. });
  238. it('should remove filter segments', () => {
  239. expect(ctrl.filterSegments.filterSegments.length).toBe(1);
  240. expect(ctrl.filterSegments.filterSegments[0].type).toBe('plus-button');
  241. });
  242. });
  243. describe('and user removes key segment and there is a previous filter', () => {
  244. beforeEach(() => {
  245. const existingKeySegment1 = { value: ctrl.defaultRemoveFilterValue, type: 'key' };
  246. const existingKeySegment2 = { value: ctrl.defaultRemoveFilterValue, type: 'key' };
  247. const existingOperatorSegment = { value: '=', type: 'operator' };
  248. const existingValueSegment = { value: 'filtervalue', type: 'value' };
  249. const conditionSegment = { value: 'AND', type: 'condition' };
  250. const plusSegment = { value: '', type: 'plus-button' };
  251. ctrl.filterSegments.filterSegments = [
  252. existingKeySegment1,
  253. existingOperatorSegment,
  254. existingValueSegment,
  255. conditionSegment,
  256. existingKeySegment2,
  257. Object.assign({}, existingOperatorSegment),
  258. Object.assign({}, existingValueSegment),
  259. plusSegment,
  260. ];
  261. ctrl.filterSegmentUpdated(existingKeySegment2, 4);
  262. });
  263. it('should remove filter segments and the condition segment', () => {
  264. expect(ctrl.filterSegments.filterSegments.length).toBe(4);
  265. expect(ctrl.filterSegments.filterSegments[0].type).toBe('key');
  266. expect(ctrl.filterSegments.filterSegments[1].type).toBe('operator');
  267. expect(ctrl.filterSegments.filterSegments[2].type).toBe('value');
  268. expect(ctrl.filterSegments.filterSegments[3].type).toBe('plus-button');
  269. });
  270. });
  271. describe('and user removes key segment and there is a filter after it', () => {
  272. beforeEach(() => {
  273. const existingKeySegment1 = { value: ctrl.defaultRemoveFilterValue, type: 'key' };
  274. const existingKeySegment2 = { value: ctrl.defaultRemoveFilterValue, type: 'key' };
  275. const existingOperatorSegment = { value: '=', type: 'operator' };
  276. const existingValueSegment = { value: 'filtervalue', type: 'value' };
  277. const conditionSegment = { value: 'AND', type: 'condition' };
  278. const plusSegment = { value: '', type: 'plus-button' };
  279. ctrl.filterSegments.filterSegments = [
  280. existingKeySegment1,
  281. existingOperatorSegment,
  282. existingValueSegment,
  283. conditionSegment,
  284. existingKeySegment2,
  285. Object.assign({}, existingOperatorSegment),
  286. Object.assign({}, existingValueSegment),
  287. plusSegment,
  288. ];
  289. ctrl.filterSegmentUpdated(existingKeySegment1, 0);
  290. });
  291. it('should remove filter segments and the condition segment', () => {
  292. expect(ctrl.filterSegments.filterSegments.length).toBe(4);
  293. expect(ctrl.filterSegments.filterSegments[0].type).toBe('key');
  294. expect(ctrl.filterSegments.filterSegments[1].type).toBe('operator');
  295. expect(ctrl.filterSegments.filterSegments[2].type).toBe('value');
  296. expect(ctrl.filterSegments.filterSegments[3].type).toBe('plus-button');
  297. });
  298. });
  299. describe('and user clicks on plus button', () => {
  300. beforeEach(() => {
  301. const existingKeySegment = { value: 'filterkey1', type: 'key' };
  302. const existingOperatorSegment = { value: '=', type: 'operator' };
  303. const existingValueSegment = { value: 'filtervalue', type: 'value' };
  304. const plusSegment = { value: 'filterkey2', type: 'plus-button' };
  305. ctrl.filterSegments.filterSegments = [
  306. existingKeySegment,
  307. existingOperatorSegment,
  308. existingValueSegment,
  309. plusSegment,
  310. ];
  311. ctrl.filterSegmentUpdated(plusSegment, 3);
  312. });
  313. it('should condition segment and new filter segments', () => {
  314. expect(ctrl.filterSegments.filterSegments.length).toBe(7);
  315. expect(ctrl.filterSegments.filterSegments[0].type).toBe('key');
  316. expect(ctrl.filterSegments.filterSegments[1].type).toBe('operator');
  317. expect(ctrl.filterSegments.filterSegments[2].type).toBe('value');
  318. expect(ctrl.filterSegments.filterSegments[3].type).toBe('condition');
  319. expect(ctrl.filterSegments.filterSegments[4].type).toBe('key');
  320. expect(ctrl.filterSegments.filterSegments[5].type).toBe('operator');
  321. expect(ctrl.filterSegments.filterSegments[6].type).toBe('value');
  322. });
  323. });
  324. });
  325. });
  326. });
  327. function createCtrlWithFakes(existingFilters?: string[]) {
  328. StackdriverQueryCtrl.prototype.panelCtrl = {
  329. events: { on: () => {} },
  330. panel: { scopedVars: [], targets: [] },
  331. refresh: () => {},
  332. };
  333. StackdriverQueryCtrl.prototype.target = createTarget(existingFilters);
  334. StackdriverQueryCtrl.prototype.getMetricTypes = () => {
  335. return Promise.resolve();
  336. };
  337. StackdriverQueryCtrl.prototype.getLabels = () => {
  338. return Promise.resolve();
  339. };
  340. const fakeSegmentServer = {
  341. newKey: val => {
  342. return { value: val, type: 'key' };
  343. },
  344. newKeyValue: val => {
  345. return { value: val, type: 'value' };
  346. },
  347. newSegment: obj => {
  348. return { value: obj.value ? obj.value : obj };
  349. },
  350. newOperators: ops => {
  351. return ops.map(o => {
  352. return { type: 'operator', value: o };
  353. });
  354. },
  355. newFake: (value, type, cssClass) => {
  356. return { value, type, cssClass };
  357. },
  358. newOperator: op => {
  359. return { value: op, type: 'operator' };
  360. },
  361. newPlusButton: () => {
  362. return { type: 'plus-button' };
  363. },
  364. newCondition: val => {
  365. return { type: 'condition', value: val };
  366. },
  367. };
  368. return new StackdriverQueryCtrl(null, null, fakeSegmentServer, null, new TemplateSrvStub());
  369. }
  370. function createTarget(existingFilters?: string[]) {
  371. return {
  372. project: {
  373. id: '',
  374. name: '',
  375. },
  376. metricType: 'ametric',
  377. refId: 'A',
  378. aggregation: {
  379. crossSeriesReducer: '',
  380. alignmentPeriod: '',
  381. perSeriesAligner: '',
  382. groupBys: [],
  383. },
  384. filters: existingFilters || [],
  385. aliasBy: '',
  386. };
  387. }