PanelQueryState.test.ts 6.1 KB

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