PanelQueryRunner.test.ts 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. import { PanelQueryRunner } from './PanelQueryRunner';
  2. import { PanelData, DataQueryRequest } from '@grafana/ui';
  3. import { dateTime, ScopedVars } from '@grafana/data';
  4. import { PanelModel } from './PanelModel';
  5. jest.mock('app/core/services/backend_srv');
  6. // Defined within setup functions
  7. const panelsForCurrentDashboardMock: { [key: number]: PanelModel } = {};
  8. jest.mock('app/features/dashboard/services/DashboardSrv', () => ({
  9. getDashboardSrv: () => {
  10. return {
  11. getCurrent: () => {
  12. return {
  13. getPanelById: (id: number) => {
  14. return panelsForCurrentDashboardMock[id];
  15. },
  16. };
  17. },
  18. };
  19. },
  20. }));
  21. interface ScenarioContext {
  22. setup: (fn: () => void) => void;
  23. // Options used in setup
  24. maxDataPoints?: number | null;
  25. widthPixels: number;
  26. dsInterval?: string;
  27. minInterval?: string;
  28. scopedVars: ScopedVars;
  29. // Filled in by the Scenario runner
  30. events?: PanelData[];
  31. res?: PanelData;
  32. queryCalledWith?: DataQueryRequest;
  33. runner: PanelQueryRunner;
  34. }
  35. type ScenarioFn = (ctx: ScenarioContext) => void;
  36. function describeQueryRunnerScenario(description: string, scenarioFn: ScenarioFn) {
  37. describe(description, () => {
  38. let setupFn = () => {};
  39. const ctx: ScenarioContext = {
  40. widthPixels: 200,
  41. scopedVars: {
  42. server: { text: 'Server1', value: 'server-1' },
  43. },
  44. runner: new PanelQueryRunner(),
  45. setup: (fn: () => void) => {
  46. setupFn = fn;
  47. },
  48. };
  49. const response: any = {
  50. data: [{ target: 'hello', datapoints: [[1, 1000], [2, 2000]] }],
  51. };
  52. beforeEach(async () => {
  53. setupFn();
  54. const datasource: any = {
  55. name: 'TestDB',
  56. interval: ctx.dsInterval,
  57. query: (options: DataQueryRequest) => {
  58. ctx.queryCalledWith = options;
  59. return Promise.resolve(response);
  60. },
  61. testDatasource: jest.fn(),
  62. };
  63. const args: any = {
  64. datasource,
  65. scopedVars: ctx.scopedVars,
  66. minInterval: ctx.minInterval,
  67. widthPixels: ctx.widthPixels,
  68. maxDataPoints: ctx.maxDataPoints,
  69. timeRange: {
  70. from: dateTime().subtract(1, 'days'),
  71. to: dateTime(),
  72. raw: { from: '1h', to: 'now' },
  73. },
  74. panelId: 1,
  75. queries: [{ refId: 'A', test: 1 }],
  76. };
  77. ctx.runner = new PanelQueryRunner();
  78. ctx.runner.getData().subscribe({
  79. next: (data: PanelData) => {
  80. ctx.res = data;
  81. ctx.events.push(data);
  82. },
  83. });
  84. panelsForCurrentDashboardMock[1] = {
  85. id: 1,
  86. getQueryRunner: () => {
  87. return ctx.runner;
  88. },
  89. } as PanelModel;
  90. ctx.events = [];
  91. ctx.runner.run(args);
  92. });
  93. scenarioFn(ctx);
  94. });
  95. }
  96. describe('PanelQueryRunner', () => {
  97. describeQueryRunnerScenario('simple scenario', ctx => {
  98. it('should set requestId on request', async () => {
  99. expect(ctx.queryCalledWith.requestId).toBe('Q100');
  100. });
  101. it('should set datasource name on request', async () => {
  102. expect(ctx.queryCalledWith.targets[0].datasource).toBe('TestDB');
  103. });
  104. it('should pass scopedVars to datasource with interval props', async () => {
  105. expect(ctx.queryCalledWith.scopedVars.server.text).toBe('Server1');
  106. expect(ctx.queryCalledWith.scopedVars.__interval.text).toBe('5m');
  107. expect(ctx.queryCalledWith.scopedVars.__interval_ms.text).toBe('300000');
  108. });
  109. });
  110. describeQueryRunnerScenario('with no maxDataPoints or minInterval', ctx => {
  111. ctx.setup(() => {
  112. ctx.maxDataPoints = null;
  113. ctx.widthPixels = 200;
  114. });
  115. it('should return data', async () => {
  116. expect(ctx.res.error).toBeUndefined();
  117. expect(ctx.res.series.length).toBe(1);
  118. });
  119. it('should use widthPixels as maxDataPoints', async () => {
  120. expect(ctx.queryCalledWith.maxDataPoints).toBe(200);
  121. });
  122. it('should calculate interval based on width', async () => {
  123. expect(ctx.queryCalledWith.interval).toBe('5m');
  124. });
  125. it('fast query should only publish 1 data events', async () => {
  126. expect(ctx.events.length).toBe(1);
  127. });
  128. });
  129. describeQueryRunnerScenario('with no panel min interval but datasource min interval', ctx => {
  130. ctx.setup(() => {
  131. ctx.widthPixels = 20000;
  132. ctx.dsInterval = '15s';
  133. });
  134. it('should limit interval to data source min interval', async () => {
  135. expect(ctx.queryCalledWith.interval).toBe('15s');
  136. });
  137. });
  138. describeQueryRunnerScenario('with panel min interval and data source min interval', ctx => {
  139. ctx.setup(() => {
  140. ctx.widthPixels = 20000;
  141. ctx.dsInterval = '15s';
  142. ctx.minInterval = '30s';
  143. });
  144. it('should limit interval to panel min interval', async () => {
  145. expect(ctx.queryCalledWith.interval).toBe('30s');
  146. });
  147. });
  148. describeQueryRunnerScenario('with maxDataPoints', ctx => {
  149. ctx.setup(() => {
  150. ctx.maxDataPoints = 10;
  151. });
  152. it('should pass maxDataPoints if specified', async () => {
  153. expect(ctx.queryCalledWith.maxDataPoints).toBe(10);
  154. });
  155. });
  156. });