PanelQueryRunner.test.ts 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. import { getProcessedSeriesData, PanelQueryRunner } from './PanelQueryRunner';
  2. import { PanelData, DataQueryRequest } from '@grafana/ui/src/types';
  3. import moment from 'moment';
  4. describe('PanelQueryRunner', () => {
  5. it('converts timeseries to table skipping nulls', () => {
  6. const input1 = {
  7. target: 'Field Name',
  8. datapoints: [[100, 1], [200, 2]],
  9. };
  10. const input2 = {
  11. // without target
  12. target: '',
  13. datapoints: [[100, 1], [200, 2]],
  14. };
  15. const data = getProcessedSeriesData([null, input1, input2, null, null]);
  16. expect(data.length).toBe(2);
  17. expect(data[0].fields[0].name).toBe(input1.target);
  18. expect(data[0].rows).toBe(input1.datapoints);
  19. // Default name
  20. expect(data[1].fields[0].name).toEqual('Value');
  21. // Every colun should have a name and a type
  22. for (const table of data) {
  23. for (const column of table.fields) {
  24. expect(column.name).toBeDefined();
  25. expect(column.type).toBeDefined();
  26. }
  27. }
  28. });
  29. it('supports null values from query OK', () => {
  30. expect(getProcessedSeriesData([null, null, null, null])).toEqual([]);
  31. expect(getProcessedSeriesData(undefined)).toEqual([]);
  32. expect(getProcessedSeriesData((null as unknown) as any[])).toEqual([]);
  33. expect(getProcessedSeriesData([])).toEqual([]);
  34. });
  35. });
  36. interface ScenarioContext {
  37. setup: (fn: () => void) => void;
  38. maxDataPoints?: number | null;
  39. widthPixels: number;
  40. dsInterval?: string;
  41. minInterval?: string;
  42. events?: PanelData[];
  43. res?: PanelData;
  44. queryCalledWith?: DataQueryRequest;
  45. }
  46. type ScenarioFn = (ctx: ScenarioContext) => void;
  47. function describeQueryRunnerScenario(description: string, scenarioFn: ScenarioFn) {
  48. describe(description, () => {
  49. let setupFn = () => {};
  50. const ctx: ScenarioContext = {
  51. widthPixels: 200,
  52. setup: (fn: () => void) => {
  53. setupFn = fn;
  54. },
  55. };
  56. let runner: PanelQueryRunner;
  57. const response: any = {
  58. data: [{ target: 'hello', datapoints: [] }],
  59. };
  60. beforeEach(async () => {
  61. setupFn();
  62. const datasource: any = {
  63. interval: ctx.dsInterval,
  64. query: (options: DataQueryRequest) => {
  65. ctx.queryCalledWith = options;
  66. return Promise.resolve(response);
  67. },
  68. testDatasource: jest.fn(),
  69. };
  70. const args: any = {
  71. datasource,
  72. minInterval: ctx.minInterval,
  73. widthPixels: ctx.widthPixels,
  74. maxDataPoints: ctx.maxDataPoints,
  75. timeRange: {
  76. from: moment().subtract(1, 'days'),
  77. to: moment(),
  78. raw: { from: '1h', to: 'now' },
  79. },
  80. panelId: 0,
  81. queries: [{ refId: 'A', test: 1 }],
  82. };
  83. runner = new PanelQueryRunner();
  84. runner.subscribe({
  85. next: (data: PanelData) => {
  86. ctx.events.push(data);
  87. },
  88. });
  89. ctx.events = [];
  90. ctx.res = await runner.run(args);
  91. });
  92. scenarioFn(ctx);
  93. });
  94. }
  95. describe('PanelQueryRunner', () => {
  96. describeQueryRunnerScenario('with no maxDataPoints or minInterval', ctx => {
  97. ctx.setup(() => {
  98. ctx.maxDataPoints = null;
  99. ctx.widthPixels = 200;
  100. });
  101. it('should return data', async () => {
  102. expect(ctx.res.error).toBeUndefined();
  103. expect(ctx.res.series.length).toBe(1);
  104. });
  105. it('should use widthPixels as maxDataPoints', async () => {
  106. expect(ctx.queryCalledWith.maxDataPoints).toBe(200);
  107. });
  108. it('should calculate interval based on width', async () => {
  109. expect(ctx.queryCalledWith.interval).toBe('5m');
  110. });
  111. it('fast query should only publish 1 data events', async () => {
  112. expect(ctx.events.length).toBe(1);
  113. });
  114. });
  115. describeQueryRunnerScenario('with no panel min interval but datasource min interval', ctx => {
  116. ctx.setup(() => {
  117. ctx.widthPixels = 20000;
  118. ctx.dsInterval = '15s';
  119. });
  120. it('should limit interval to data source min interval', async () => {
  121. expect(ctx.queryCalledWith.interval).toBe('15s');
  122. });
  123. });
  124. describeQueryRunnerScenario('with panel min interval and data source min interval', ctx => {
  125. ctx.setup(() => {
  126. ctx.widthPixels = 20000;
  127. ctx.dsInterval = '15s';
  128. ctx.minInterval = '30s';
  129. });
  130. it('should limit interval to panel min interval', async () => {
  131. expect(ctx.queryCalledWith.interval).toBe('30s');
  132. });
  133. });
  134. describeQueryRunnerScenario('with maxDataPoints', ctx => {
  135. ctx.setup(() => {
  136. ctx.maxDataPoints = 10;
  137. });
  138. it('should pass maxDataPoints if specified', async () => {
  139. expect(ctx.queryCalledWith.maxDataPoints).toBe(10);
  140. });
  141. });
  142. });