language_provider.test.ts 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. // @ts-ignore
  2. import Plain from 'slate-plain-serializer';
  3. import LanguageProvider, { LABEL_REFRESH_INTERVAL, rangeToParams } from './language_provider';
  4. import { AbsoluteTimeRange } from '@grafana/data';
  5. import { advanceTo, clear, advanceBy } from 'jest-date-mock';
  6. import { beforeEach } from 'test/lib/common';
  7. import { DataQueryResponseData } from '@grafana/ui';
  8. describe('Language completion provider', () => {
  9. const datasource = {
  10. metadataRequest: () => ({ data: { data: [] as DataQueryResponseData[] } }),
  11. };
  12. const rangeMock: AbsoluteTimeRange = {
  13. from: 1560153109000,
  14. to: 1560163909000,
  15. };
  16. describe('empty query suggestions', () => {
  17. it('returns no suggestions on empty context', () => {
  18. const instance = new LanguageProvider(datasource);
  19. const value = Plain.deserialize('');
  20. const result = instance.provideCompletionItems({ text: '', prefix: '', value, wrapperClasses: [] });
  21. expect(result.context).toBeUndefined();
  22. expect(result.refresher).toBeUndefined();
  23. expect(result.suggestions.length).toEqual(0);
  24. });
  25. it('returns default suggestions with history on empty context when history was provided', () => {
  26. const instance = new LanguageProvider(datasource);
  27. const value = Plain.deserialize('');
  28. const history = [
  29. {
  30. query: { refId: '1', expr: '{app="foo"}' },
  31. },
  32. ];
  33. const result = instance.provideCompletionItems(
  34. { text: '', prefix: '', value, wrapperClasses: [] },
  35. { history, absoluteRange: rangeMock }
  36. );
  37. expect(result.context).toBeUndefined();
  38. expect(result.refresher).toBeUndefined();
  39. expect(result.suggestions).toMatchObject([
  40. {
  41. label: 'History',
  42. items: [
  43. {
  44. label: '{app="foo"}',
  45. },
  46. ],
  47. },
  48. ]);
  49. });
  50. it('returns no suggestions within regexp', () => {
  51. const instance = new LanguageProvider(datasource);
  52. const value = Plain.deserialize('{} ()');
  53. const range = value.selection.merge({
  54. anchorOffset: 4,
  55. });
  56. const valueWithSelection = value.change().select(range).value;
  57. const history = [
  58. {
  59. query: { refId: '1', expr: '{app="foo"}' },
  60. },
  61. ];
  62. const result = instance.provideCompletionItems(
  63. {
  64. text: '',
  65. prefix: '',
  66. value: valueWithSelection,
  67. wrapperClasses: [],
  68. },
  69. { history }
  70. );
  71. expect(result.context).toBeUndefined();
  72. expect(result.refresher).toBeUndefined();
  73. expect(result.suggestions.length).toEqual(0);
  74. });
  75. });
  76. describe('label suggestions', () => {
  77. it('returns default label suggestions on label context', () => {
  78. const instance = new LanguageProvider(datasource);
  79. const value = Plain.deserialize('{}');
  80. const range = value.selection.merge({
  81. anchorOffset: 1,
  82. });
  83. const valueWithSelection = value.change().select(range).value;
  84. const result = instance.provideCompletionItems(
  85. {
  86. text: '',
  87. prefix: '',
  88. wrapperClasses: ['context-labels'],
  89. value: valueWithSelection,
  90. },
  91. { absoluteRange: rangeMock }
  92. );
  93. expect(result.context).toBe('context-labels');
  94. expect(result.suggestions).toEqual([{ items: [{ label: 'job' }, { label: 'namespace' }], label: 'Labels' }]);
  95. });
  96. });
  97. });
  98. describe('Request URL', () => {
  99. it('should contain range params', async () => {
  100. const rangeMock: AbsoluteTimeRange = {
  101. from: 1560153109000,
  102. to: 1560163909000,
  103. };
  104. const datasourceWithLabels = {
  105. metadataRequest: (url: string) => {
  106. if (url.slice(0, 15) === '/api/prom/label') {
  107. return { data: { data: ['other'] } };
  108. } else {
  109. return { data: { data: [] } };
  110. }
  111. },
  112. };
  113. const datasourceSpy = jest.spyOn(datasourceWithLabels, 'metadataRequest');
  114. const instance = new LanguageProvider(datasourceWithLabels, { initialRange: rangeMock });
  115. await instance.refreshLogLabels(rangeMock, true);
  116. const expectedUrl = '/api/prom/label';
  117. expect(datasourceSpy).toHaveBeenCalledWith(expectedUrl, rangeToParams(rangeMock));
  118. });
  119. });
  120. describe('Query imports', () => {
  121. const datasource = {
  122. metadataRequest: () => ({ data: { data: [] as DataQueryResponseData[] } }),
  123. };
  124. const rangeMock: AbsoluteTimeRange = {
  125. from: 1560153109000,
  126. to: 1560163909000,
  127. };
  128. it('returns empty queries for unknown origin datasource', async () => {
  129. const instance = new LanguageProvider(datasource, { initialRange: rangeMock });
  130. const result = await instance.importQueries([{ refId: 'bar', expr: 'foo' }], 'unknown');
  131. expect(result).toEqual([{ refId: 'bar', expr: '' }]);
  132. });
  133. describe('prometheus query imports', () => {
  134. it('returns empty query from metric-only query', async () => {
  135. const instance = new LanguageProvider(datasource, { initialRange: rangeMock });
  136. const result = await instance.importPrometheusQuery('foo');
  137. expect(result).toEqual('');
  138. });
  139. it('returns empty query from selector query if label is not available', async () => {
  140. const datasourceWithLabels = {
  141. metadataRequest: (url: string) =>
  142. url.slice(0, 15) === '/api/prom/label'
  143. ? { data: { data: ['other'] } }
  144. : { data: { data: [] as DataQueryResponseData[] } },
  145. };
  146. const instance = new LanguageProvider(datasourceWithLabels, { initialRange: rangeMock });
  147. const result = await instance.importPrometheusQuery('{foo="bar"}');
  148. expect(result).toEqual('{}');
  149. });
  150. it('returns selector query from selector query with common labels', async () => {
  151. const datasourceWithLabels = {
  152. metadataRequest: (url: string) =>
  153. url.slice(0, 15) === '/api/prom/label'
  154. ? { data: { data: ['foo'] } }
  155. : { data: { data: [] as DataQueryResponseData[] } },
  156. };
  157. const instance = new LanguageProvider(datasourceWithLabels, { initialRange: rangeMock });
  158. const result = await instance.importPrometheusQuery('metric{foo="bar",baz="42"}');
  159. expect(result).toEqual('{foo="bar"}');
  160. });
  161. it('returns selector query from selector query with all labels if logging label list is empty', async () => {
  162. const datasourceWithLabels = {
  163. metadataRequest: (url: string) =>
  164. url.slice(0, 15) === '/api/prom/label'
  165. ? { data: { data: [] as DataQueryResponseData[] } }
  166. : { data: { data: [] as DataQueryResponseData[] } },
  167. };
  168. const instance = new LanguageProvider(datasourceWithLabels, { initialRange: rangeMock });
  169. const result = await instance.importPrometheusQuery('metric{foo="bar",baz="42"}');
  170. expect(result).toEqual('{baz="42",foo="bar"}');
  171. });
  172. });
  173. });
  174. describe('Labels refresh', () => {
  175. const datasource = {
  176. metadataRequest: () => ({ data: { data: [] as DataQueryResponseData[] } }),
  177. };
  178. const instance = new LanguageProvider(datasource);
  179. const rangeMock: AbsoluteTimeRange = {
  180. from: 1560153109000,
  181. to: 1560163909000,
  182. };
  183. beforeEach(() => {
  184. instance.fetchLogLabels = jest.fn();
  185. });
  186. afterEach(() => {
  187. jest.clearAllMocks();
  188. clear();
  189. });
  190. it("should not refresh labels if refresh interval hasn't passed", () => {
  191. advanceTo(new Date(2019, 1, 1, 0, 0, 0));
  192. instance.logLabelFetchTs = Date.now();
  193. advanceBy(LABEL_REFRESH_INTERVAL / 2);
  194. instance.refreshLogLabels(rangeMock);
  195. expect(instance.fetchLogLabels).not.toBeCalled();
  196. });
  197. it('should refresh labels if refresh interval passed', () => {
  198. advanceTo(new Date(2019, 1, 1, 0, 0, 0));
  199. instance.logLabelFetchTs = Date.now();
  200. advanceBy(LABEL_REFRESH_INTERVAL + 1);
  201. instance.refreshLogLabels(rangeMock);
  202. expect(instance.fetchLogLabels).toBeCalled();
  203. });
  204. });