PanelQueryState.test.ts 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. import { toDataQueryError, PanelQueryState, getProcessedDataFrames } from './PanelQueryState';
  2. import { MockDataSourceApi } from 'test/mocks/datasource_srv';
  3. import { LoadingState, getDataFrameRow } from '@grafana/data';
  4. import { DataQueryResponse, DataQueryRequest, DataQuery } 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('When cancelling request', () => {
  48. it('Should call rejector', () => {
  49. const state = new PanelQueryState();
  50. state.request = {} as DataQueryRequest<DataQuery>;
  51. (state as any).rejector = (obj: any) => {
  52. expect(obj.cancelled).toBe(true);
  53. expect(obj.message).toBe('OHH');
  54. };
  55. state.cancel('OHH');
  56. });
  57. });
  58. describe('getProcessedDataFrame', () => {
  59. it('converts timeseries to table skipping nulls', () => {
  60. const input1 = {
  61. target: 'Field Name',
  62. datapoints: [[100, 1], [200, 2]],
  63. };
  64. const input2 = {
  65. // without target
  66. target: '',
  67. datapoints: [[100, 1], [200, 2]],
  68. };
  69. const data = getProcessedDataFrames([null, input1, input2, null, null]);
  70. expect(data.length).toBe(2);
  71. expect(data[0].fields[0].name).toBe(input1.target);
  72. const cmp = [getDataFrameRow(data[0], 0), getDataFrameRow(data[0], 1)];
  73. expect(cmp).toEqual(input1.datapoints);
  74. // Default name
  75. expect(data[1].fields[0].name).toEqual('Value');
  76. // Every colun should have a name and a type
  77. for (const table of data) {
  78. for (const field of table.fields) {
  79. expect(field.name).toBeDefined();
  80. expect(field.type).toBeDefined();
  81. }
  82. }
  83. });
  84. it('supports null values from query OK', () => {
  85. expect(getProcessedDataFrames([null, null, null, null])).toEqual([]);
  86. expect(getProcessedDataFrames(undefined)).toEqual([]);
  87. expect(getProcessedDataFrames((null as unknown) as any[])).toEqual([]);
  88. expect(getProcessedDataFrames([])).toEqual([]);
  89. });
  90. });
  91. function makeSeriesStub(refId: string) {
  92. return {
  93. fields: [{ name: undefined }],
  94. refId,
  95. } as any;
  96. }
  97. describe('stream handling', () => {
  98. const state = new PanelQueryState();
  99. state.onStreamingDataUpdated = () => {
  100. // nothing
  101. };
  102. state.request = {
  103. requestId: '123',
  104. range: {
  105. raw: {
  106. from: 123, // if string it gets revaluated
  107. },
  108. },
  109. } as any;
  110. state.response = {
  111. state: LoadingState.Done,
  112. series: [makeSeriesStub('A'), makeSeriesStub('B')],
  113. };
  114. it('gets the response', () => {
  115. const data = state.validateStreamsAndGetPanelData();
  116. expect(data.series.length).toBe(2);
  117. expect(data.state).toBe(LoadingState.Done);
  118. expect(data.series[0].refId).toBe('A');
  119. });
  120. it('adds a stream event', () => {
  121. // Post a stream event
  122. state.dataStreamObserver({
  123. state: LoadingState.Loading,
  124. key: 'C',
  125. request: state.request, // From the same request
  126. data: [makeSeriesStub('C')],
  127. unsubscribe: () => {},
  128. });
  129. expect(state.streams.length).toBe(1);
  130. const data = state.validateStreamsAndGetPanelData();
  131. expect(data.series.length).toBe(3);
  132. expect(data.state).toBe(LoadingState.Streaming);
  133. expect(data.series[2].refId).toBe('C');
  134. });
  135. it('add another stream event (with a differnet key)', () => {
  136. // Post a stream event
  137. state.dataStreamObserver({
  138. state: LoadingState.Loading,
  139. key: 'D',
  140. request: state.request, // From the same request
  141. data: [makeSeriesStub('D')],
  142. unsubscribe: () => {},
  143. });
  144. expect(state.streams.length).toBe(2);
  145. const data = state.validateStreamsAndGetPanelData();
  146. expect(data.series.length).toBe(4);
  147. expect(data.state).toBe(LoadingState.Streaming);
  148. expect(data.series[3].refId).toBe('D');
  149. });
  150. it('replace the first stream value, but keep the order', () => {
  151. // Post a stream event
  152. state.dataStreamObserver({
  153. state: LoadingState.Loading,
  154. key: 'C', // The key to replace previous index 2
  155. request: state.request, // From the same request
  156. data: [makeSeriesStub('X')],
  157. unsubscribe: () => {},
  158. });
  159. expect(state.streams.length).toBe(2);
  160. const data = state.validateStreamsAndGetPanelData();
  161. expect(data.series[2].refId).toBe('X');
  162. });
  163. it('ignores streams from a differnet request', () => {
  164. // Post a stream event
  165. state.dataStreamObserver({
  166. state: LoadingState.Loading,
  167. key: 'Z', // Note with key 'A' it would still overwrite
  168. request: {
  169. ...state.request,
  170. requestId: 'XXX', // Different request and id
  171. } as any,
  172. data: [makeSeriesStub('C')],
  173. unsubscribe: () => {},
  174. });
  175. expect(state.streams.length).toBe(2); // no change
  176. const data = state.validateStreamsAndGetPanelData();
  177. expect(data.series.length).toBe(4);
  178. });
  179. it('removes streams when the query changes', () => {
  180. state.request = {
  181. ...state.request,
  182. requestId: 'somethine else',
  183. } as any;
  184. state.response = {
  185. state: LoadingState.Done,
  186. series: [makeSeriesStub('F')],
  187. };
  188. expect(state.streams.length).toBe(2); // unchanged
  189. const data = state.validateStreamsAndGetPanelData();
  190. expect(data.series.length).toBe(1);
  191. expect(data.series[0].refId).toBe('F');
  192. expect(state.streams.length).toBe(0); // no streams
  193. });
  194. it('should close streams on error', () => {
  195. // Post a stream event
  196. state.dataStreamObserver({
  197. state: LoadingState.Error,
  198. key: 'C',
  199. error: { message: 'EEEEE' },
  200. data: [],
  201. request: state.request,
  202. unsubscribe: () => {},
  203. });
  204. expect(state.streams.length).toBe(0);
  205. expect(state.response.state).toBe(LoadingState.Error);
  206. });
  207. });