PanelQueryState.test.ts 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. import { toDataQueryError, PanelQueryState, getProcessedDataFrame } from './PanelQueryState';
  2. import { MockDataSourceApi } from 'test/mocks/datasource_srv';
  3. import { LoadingState } from '@grafana/data';
  4. import { DataQueryResponse } from '@grafana/ui';
  5. import { getQueryOptions } from 'test/helpers/getQueryOptions';
  6. describe('PanelQueryState', () => {
  7. it('converts anythign to an error', () => {
  8. let err = toDataQueryError(undefined);
  9. expect(err.message).toEqual('Query error');
  10. err = toDataQueryError('STRING ERRROR');
  11. expect(err.message).toEqual('STRING ERRROR');
  12. err = toDataQueryError({ message: 'hello' });
  13. expect(err.message).toEqual('hello');
  14. });
  15. it('keeps track of running queries', async () => {
  16. const state = new PanelQueryState();
  17. expect(state.getActiveRunner()).toBeFalsy();
  18. let hasRun = false;
  19. const dsRunner = new Promise<DataQueryResponse>((resolve, reject) => {
  20. // The status should be running when we get here
  21. expect(state.getActiveRunner()).toBeTruthy();
  22. resolve({ data: ['x', 'y'] });
  23. hasRun = true;
  24. });
  25. const ds = new MockDataSourceApi('test');
  26. ds.queryResolver = dsRunner;
  27. // should not actually run for an empty query
  28. let empty = await state.execute(ds, getQueryOptions({}));
  29. expect(state.getActiveRunner()).toBeFalsy();
  30. expect(empty.series.length).toBe(0);
  31. expect(hasRun).toBeFalsy();
  32. const query = getQueryOptions({
  33. targets: [{ hide: true, refId: 'X' }, { hide: true, refId: 'Y' }, { hide: true, refId: 'Z' }],
  34. });
  35. empty = await state.execute(ds, query);
  36. // should not run any hidden queries'
  37. expect(state.getActiveRunner()).toBeFalsy();
  38. expect(empty.series.length).toBe(0);
  39. expect(hasRun).toBeFalsy();
  40. // Check for the same query
  41. expect(state.isSameQuery(ds, query)).toBeTruthy();
  42. // Check for differnet queries
  43. expect(state.isSameQuery(new MockDataSourceApi('test'), query)).toBeFalsy();
  44. expect(state.isSameQuery(ds, getQueryOptions({ targets: [{ refId: 'differnet' }] }))).toBeFalsy();
  45. });
  46. });
  47. describe('getProcessedDataFrame', () => {
  48. it('converts timeseries to table skipping nulls', () => {
  49. const input1 = {
  50. target: 'Field Name',
  51. datapoints: [[100, 1], [200, 2]],
  52. };
  53. const input2 = {
  54. // without target
  55. target: '',
  56. datapoints: [[100, 1], [200, 2]],
  57. };
  58. const data = getProcessedDataFrame([null, input1, input2, null, null]);
  59. expect(data.length).toBe(2);
  60. expect(data[0].fields[0].name).toBe(input1.target);
  61. expect(data[0].rows).toBe(input1.datapoints);
  62. // Default name
  63. expect(data[1].fields[0].name).toEqual('Value');
  64. // Every colun should have a name and a type
  65. for (const table of data) {
  66. for (const column of table.fields) {
  67. expect(column.name).toBeDefined();
  68. expect(column.type).toBeDefined();
  69. }
  70. }
  71. });
  72. it('supports null values from query OK', () => {
  73. expect(getProcessedDataFrame([null, null, null, null])).toEqual([]);
  74. expect(getProcessedDataFrame(undefined)).toEqual([]);
  75. expect(getProcessedDataFrame((null as unknown) as any[])).toEqual([]);
  76. expect(getProcessedDataFrame([])).toEqual([]);
  77. });
  78. });
  79. function makeSeriesStub(refId: string) {
  80. return {
  81. fields: [{ name: 'a' }],
  82. rows: [],
  83. refId,
  84. };
  85. }
  86. describe('stream handling', () => {
  87. const state = new PanelQueryState();
  88. state.onStreamingDataUpdated = () => {
  89. // nothing
  90. };
  91. state.request = {
  92. requestId: '123',
  93. range: {
  94. raw: {
  95. from: 123, // if string it gets revaluated
  96. },
  97. },
  98. } as any;
  99. state.response = {
  100. state: LoadingState.Done,
  101. series: [makeSeriesStub('A'), makeSeriesStub('B')],
  102. };
  103. it('gets the response', () => {
  104. const data = state.validateStreamsAndGetPanelData();
  105. expect(data.series.length).toBe(2);
  106. expect(data.state).toBe(LoadingState.Done);
  107. expect(data.series[0].refId).toBe('A');
  108. });
  109. it('adds a stream event', () => {
  110. // Post a stream event
  111. state.dataStreamObserver({
  112. state: LoadingState.Loading,
  113. key: 'C',
  114. request: state.request, // From the same request
  115. series: [makeSeriesStub('C')],
  116. unsubscribe: () => {},
  117. });
  118. expect(state.streams.length).toBe(1);
  119. const data = state.validateStreamsAndGetPanelData();
  120. expect(data.series.length).toBe(3);
  121. expect(data.state).toBe(LoadingState.Streaming);
  122. expect(data.series[2].refId).toBe('C');
  123. });
  124. it('add another stream event (with a differnet key)', () => {
  125. // Post a stream event
  126. state.dataStreamObserver({
  127. state: LoadingState.Loading,
  128. key: 'D',
  129. request: state.request, // From the same request
  130. series: [makeSeriesStub('D')],
  131. unsubscribe: () => {},
  132. });
  133. expect(state.streams.length).toBe(2);
  134. const data = state.validateStreamsAndGetPanelData();
  135. expect(data.series.length).toBe(4);
  136. expect(data.state).toBe(LoadingState.Streaming);
  137. expect(data.series[3].refId).toBe('D');
  138. });
  139. it('replace the first stream value, but keep the order', () => {
  140. // Post a stream event
  141. state.dataStreamObserver({
  142. state: LoadingState.Loading,
  143. key: 'C', // The key to replace previous index 2
  144. request: state.request, // From the same request
  145. series: [makeSeriesStub('X')],
  146. unsubscribe: () => {},
  147. });
  148. expect(state.streams.length).toBe(2);
  149. const data = state.validateStreamsAndGetPanelData();
  150. expect(data.series[2].refId).toBe('X');
  151. });
  152. it('ignores streams from a differnet request', () => {
  153. // Post a stream event
  154. state.dataStreamObserver({
  155. state: LoadingState.Loading,
  156. key: 'Z', // Note with key 'A' it would still overwrite
  157. request: {
  158. ...state.request,
  159. requestId: 'XXX', // Different request and id
  160. } as any,
  161. series: [makeSeriesStub('C')],
  162. unsubscribe: () => {},
  163. });
  164. expect(state.streams.length).toBe(2); // no change
  165. const data = state.validateStreamsAndGetPanelData();
  166. expect(data.series.length).toBe(4);
  167. });
  168. it('removes streams when the query changes', () => {
  169. state.request = {
  170. ...state.request,
  171. requestId: 'somethine else',
  172. } as any;
  173. state.response = {
  174. state: LoadingState.Done,
  175. series: [makeSeriesStub('F')],
  176. };
  177. expect(state.streams.length).toBe(2); // unchanged
  178. const data = state.validateStreamsAndGetPanelData();
  179. expect(data.series.length).toBe(1);
  180. expect(data.series[0].refId).toBe('F');
  181. expect(state.streams.length).toBe(0); // no streams
  182. });
  183. });