update tests

This commit is contained in:
ismail simsek
2025-05-25 21:27:27 +02:00
parent ce19371aa7
commit 4229fd02de
@@ -5,12 +5,13 @@ import { getCacheDurationInMinutes } from './caching';
import { DEFAULT_SERIES_LIMIT } from './components/metrics-browser/types';
import { Label } from './components/monaco-query-field/monaco-completion-provider/situation';
import { PrometheusDatasource } from './datasource';
import LanguageProvider, {
import {
exportToAbstractQuery,
importFromAbstractQuery,
processSeries,
removeQuotesIfExist,
PrometheusLanguageProviderInterface,
PrometheusLanguageProvider,
} from './language_provider';
import { getPrometheusTime, getRangeSnapInterval } from './language_utils';
import { PrometheusCacheLevel, PromQuery } from './types';
@@ -81,7 +82,7 @@ describe('Prometheus Language Provider', () => {
describe('getSeries', () => {
it('should use fetchDefaultSeries for empty selector', async () => {
const languageProvider = new LanguageProvider(defaultDatasource);
const languageProvider = new PrometheusLanguageProvider(defaultDatasource);
const fetchDefaultSeriesSpy = jest.spyOn(languageProvider, 'fetchDefaultSeries');
fetchDefaultSeriesSpy.mockResolvedValue({ job: ['job1', 'job2'], instance: ['instance1', 'instance2'] });
@@ -92,7 +93,7 @@ describe('Prometheus Language Provider', () => {
});
it('should use fetchSeriesLabels for non-empty selector', async () => {
const languageProvider = new LanguageProvider(defaultDatasource);
const languageProvider = new PrometheusLanguageProvider(defaultDatasource);
const fetchSeriesLabelsSpy = jest.spyOn(languageProvider, 'fetchSeriesLabels');
fetchSeriesLabelsSpy.mockResolvedValue({ job: ['job1', 'job2'], instance: ['instance1', 'instance2'] });
@@ -103,7 +104,7 @@ describe('Prometheus Language Provider', () => {
});
it('should include name label when withName is true', async () => {
const languageProvider = new LanguageProvider(defaultDatasource);
const languageProvider = new PrometheusLanguageProvider(defaultDatasource);
const fetchSeriesLabelsSpy = jest.spyOn(languageProvider, 'fetchSeriesLabels');
fetchSeriesLabelsSpy.mockResolvedValue({ __name__: ['metric1', 'metric2'], job: ['job1'] });
@@ -116,7 +117,7 @@ describe('Prometheus Language Provider', () => {
it('should handle errors gracefully', async () => {
jest.spyOn(console, 'error').mockImplementation();
const languageProvider = new LanguageProvider(defaultDatasource);
const languageProvider = new PrometheusLanguageProvider(defaultDatasource);
jest.spyOn(languageProvider, 'fetchSeriesLabels').mockRejectedValue(new Error('Network error'));
const result = await languageProvider.getSeries(timeRange, '{job="grafana"}');
@@ -127,7 +128,7 @@ describe('Prometheus Language Provider', () => {
describe('getSeriesLabels', () => {
it('should call labels endpoint when API support is available', () => {
const languageProvider = new LanguageProvider({
const languageProvider = new PrometheusLanguageProvider({
...defaultDatasource,
hasLabelsMatchAPISupport: () => true,
} as PrometheusDatasource);
@@ -152,7 +153,7 @@ describe('Prometheus Language Provider', () => {
});
it('should call series endpoint when API support is not available', () => {
const languageProvider = new LanguageProvider({
const languageProvider = new PrometheusLanguageProvider({
...defaultDatasource,
getAdjustedInterval: (_: TimeRange) =>
getRangeSnapInterval(PrometheusCacheLevel.None, getMockQuantizedTimeRangeParams()),
@@ -179,7 +180,7 @@ describe('Prometheus Language Provider', () => {
it('should call labels endpoint with quantized time parameters when cache level is set', () => {
const timeSnapMinutes = getCacheDurationInMinutes(PrometheusCacheLevel.Low);
const languageProvider = new LanguageProvider({
const languageProvider = new PrometheusLanguageProvider({
...defaultDatasource,
hasLabelsMatchAPISupport: () => true,
cacheLevel: PrometheusCacheLevel.Low,
@@ -223,7 +224,7 @@ describe('Prometheus Language Provider', () => {
describe('getSeriesValues', () => {
it('should call series endpoint when labels match API is not supported', () => {
const languageProvider = new LanguageProvider({
const languageProvider = new PrometheusLanguageProvider({
...defaultDatasource,
} as PrometheusDatasource);
const getSeriesValues = languageProvider.getSeriesValues;
@@ -239,7 +240,7 @@ describe('Prometheus Language Provider', () => {
});
it('should call label values endpoint when labels match API is supported', () => {
const languageProvider = new LanguageProvider({
const languageProvider = new PrometheusLanguageProvider({
...defaultDatasource,
hasLabelsMatchAPISupport: () => true,
} as PrometheusDatasource);
@@ -258,7 +259,7 @@ describe('Prometheus Language Provider', () => {
});
it('should properly interpolate template variables in queries', () => {
const languageProvider = new LanguageProvider({
const languageProvider = new PrometheusLanguageProvider({
...defaultDatasource,
interpolateString: (string: string) => string.replace(/\$/g, 'interpolated-'),
} as PrometheusDatasource);
@@ -277,7 +278,7 @@ describe('Prometheus Language Provider', () => {
describe('fetchSeries', () => {
it('should use match[] parameter in request', async () => {
const languageProvider = new LanguageProvider(defaultDatasource);
const languageProvider = new PrometheusLanguageProvider(defaultDatasource);
await languageProvider.start(timeRange);
const requestSpy = jest.spyOn(languageProvider, 'request');
@@ -293,7 +294,7 @@ describe('Prometheus Language Provider', () => {
describe('fetchSeriesLabels', () => {
it('should interpolate variables in series queries', () => {
const languageProvider = new LanguageProvider({
const languageProvider = new PrometheusLanguageProvider({
...defaultDatasource,
interpolateString: (string: string) => string.replace(/\$/g, 'interpolated-'),
} as PrometheusDatasource);
@@ -311,7 +312,7 @@ describe('Prometheus Language Provider', () => {
});
it('should not include limit parameter when "none" is specified', () => {
const languageProvider = new LanguageProvider({
const languageProvider = new PrometheusLanguageProvider({
...defaultDatasource,
} as PrometheusDatasource);
const fetchSeriesLabels = languageProvider.fetchSeriesLabels;
@@ -336,10 +337,10 @@ describe('Prometheus Language Provider', () => {
};
describe('with POST method', () => {
let languageProvider: LanguageProvider;
let languageProvider: PrometheusLanguageProviderInterface;
beforeEach(() => {
languageProvider = new LanguageProvider({
languageProvider = new PrometheusLanguageProvider({
...defaultDatasource,
httpMethod: 'POST',
} as PrometheusDatasource);
@@ -417,10 +418,10 @@ describe('Prometheus Language Provider', () => {
});
describe('with GET method', () => {
let languageProvider: LanguageProvider;
let languageProvider: PrometheusLanguageProviderInterface;
beforeEach(() => {
languageProvider = new LanguageProvider({
languageProvider = new PrometheusLanguageProvider({
...defaultDatasource,
httpMethod: 'GET',
} as PrometheusDatasource);
@@ -453,7 +454,7 @@ describe('Prometheus Language Provider', () => {
describe('Label value handling', () => {
describe('fetchLabelValues', () => {
it('should interpolate variables in labels', () => {
const languageProvider = new LanguageProvider({
const languageProvider = new PrometheusLanguageProvider({
...defaultDatasource,
interpolateString: (string: string) => string.replace(/\$/g, 'interpolated_'),
} as PrometheusDatasource);
@@ -469,7 +470,7 @@ describe('Prometheus Language Provider', () => {
});
it('should properly encode UTF-8 labels', () => {
const languageProvider = new LanguageProvider({
const languageProvider = new PrometheusLanguageProvider({
...defaultDatasource,
interpolateString: (string: string) => string.replace(/\$/g, 'http.status:sum'),
} as PrometheusDatasource);
@@ -485,7 +486,7 @@ describe('Prometheus Language Provider', () => {
});
it('should handle special characters safely in label values', () => {
const languageProvider = new LanguageProvider({
const languageProvider = new PrometheusLanguageProvider({
...defaultDatasource,
interpolateString: (string: string) => string.replace(/\$/g, 'value with spaces & special chars'),
} as PrometheusDatasource);
@@ -502,7 +503,7 @@ describe('Prometheus Language Provider', () => {
describe('fetchSeriesValuesWithMatch', () => {
it('should handle UTF-8 encoding for special label names', () => {
const languageProvider = new LanguageProvider({
const languageProvider = new PrometheusLanguageProvider({
...defaultDatasource,
interpolateString: (string: string) => string.replace(/\$/g, 'http.status:sum'),
} as PrometheusDatasource);
@@ -519,7 +520,7 @@ describe('Prometheus Language Provider', () => {
});
it('should not encode standard Prometheus label names', () => {
const languageProvider = new LanguageProvider({
const languageProvider = new PrometheusLanguageProvider({
...defaultDatasource,
} as PrometheusDatasource);
const fetchSeriesValuesWithMatch = languageProvider.fetchSeriesValuesWithMatch;
@@ -544,7 +545,7 @@ describe('Prometheus Language Provider', () => {
lookupsDisabled: false,
} as unknown as PrometheusDatasource;
const mockedMetadataRequest = jest.mocked(datasource.metadataRequest);
const instance = new LanguageProvider(datasource);
const instance = new PrometheusLanguageProvider(datasource);
expect(mockedMetadataRequest.mock.calls.length).toBe(0);
await instance.start();
@@ -559,7 +560,7 @@ describe('Prometheus Language Provider', () => {
lookupsDisabled: false,
} as unknown as PrometheusDatasource;
const mockedMetadataRequest = jest.mocked(datasource.metadataRequest);
const instance = new LanguageProvider(datasource);
const instance = new PrometheusLanguageProvider(datasource);
expect(mockedMetadataRequest.mock.calls.length).toBe(0);
const result = await instance.start();
@@ -570,7 +571,7 @@ describe('Prometheus Language Provider', () => {
it('should include cache headers for requests when cacheLevel is set', () => {
const timeSnapMinutes = getCacheDurationInMinutes(PrometheusCacheLevel.Medium);
const languageProvider = new LanguageProvider({
const languageProvider = new PrometheusLanguageProvider({
...defaultDatasource,
cacheLevel: PrometheusCacheLevel.Medium,
} as PrometheusDatasource);
@@ -587,7 +588,7 @@ describe('Prometheus Language Provider', () => {
it('should handle request errors gracefully', async () => {
jest.spyOn(console, 'error').mockImplementation();
const languageProvider = new LanguageProvider(defaultDatasource);
const languageProvider = new PrometheusLanguageProvider(defaultDatasource);
const datasourceRequestMock = jest
.spyOn(defaultDatasource, 'metadataRequest')
.mockRejectedValue(new Error('Network error'));
@@ -600,7 +601,7 @@ describe('Prometheus Language Provider', () => {
it('should ignore cancelled request errors', async () => {
jest.spyOn(console, 'error').mockImplementation();
const languageProvider = new LanguageProvider(defaultDatasource);
const languageProvider = new PrometheusLanguageProvider(defaultDatasource);
const error = { cancelled: true };
const datasourceRequestMock = jest.spyOn(defaultDatasource, 'metadataRequest').mockRejectedValue(error);
@@ -617,7 +618,7 @@ describe('Prometheus Language Provider', () => {
const timeRange = getMockTimeRange();
const mockQueries: PromQuery[] = [{ refId: 'A', expr: 'metric1' }];
const languageProvider = new LanguageProvider({
const languageProvider = new PrometheusLanguageProvider({
...defaultDatasource,
interpolateString: (string: string) => `interpolated_${string}`,
getIntervalVars: () => ({ __interval: '1m' }),
@@ -656,7 +657,7 @@ describe('Prometheus Language Provider', () => {
});
it('should use default time range if not provided', async () => {
const languageProvider = new LanguageProvider(defaultDatasource);
const languageProvider = new PrometheusLanguageProvider(defaultDatasource);
const requestSpy = jest.spyOn(languageProvider, 'request').mockResolvedValue(['result']);
await languageProvider.fetchSuggestions(undefined, [], [], [], 'test');
@@ -668,7 +669,7 @@ describe('Prometheus Language Provider', () => {
});
it('should handle empty response gracefully', async () => {
const languageProvider = new LanguageProvider(defaultDatasource);
const languageProvider = new PrometheusLanguageProvider(defaultDatasource);
jest.spyOn(languageProvider, 'request').mockResolvedValue(null);
const result = await languageProvider.fetchSuggestions(getMockTimeRange(), [], [], [], 'test');
@@ -678,7 +679,7 @@ describe('Prometheus Language Provider', () => {
it('should include cache headers when cacheLevel is set', async () => {
const timeSnapMinutes = getCacheDurationInMinutes(PrometheusCacheLevel.Medium);
const languageProvider = new LanguageProvider({
const languageProvider = new PrometheusLanguageProvider({
...defaultDatasource,
cacheLevel: PrometheusCacheLevel.Medium,
} as PrometheusDatasource);
@@ -699,7 +700,7 @@ describe('Prometheus Language Provider', () => {
let provider: PrometheusLanguageProviderInterface;
beforeEach(() => {
provider = new LanguageProvider(defaultDatasource);
provider = new PrometheusLanguageProvider(defaultDatasource);
});
describe('Retrieve Methods (Synchronous)', () => {
@@ -715,21 +716,16 @@ describe('Prometheus Language Provider', () => {
],
};
// Mock the request to return raw API shape
jest.spyOn(provider as any, 'request').mockResolvedValue(rawMetadata);
jest.spyOn(provider, 'request').mockResolvedValue(rawMetadata);
await provider.queryMetricsMetadata();
// Setup metrics through start method
const metrics = ['metric1', 'metric2'];
jest.spyOn(provider, 'fetchLabelValues').mockResolvedValue(metrics);
jest.spyOn(provider, 'request').mockResolvedValue(metrics);
await provider.start();
// Set the internal properties to match what start() would do
(provider as any)._metrics = metrics;
(provider as any)._histogramMetrics = ['histogram1', 'histogram2'];
});
it('should retrieve metrics metadata', () => {
// The processed metadata shape after fixSummariesMetadata
const result = provider.retrieveMetricsMetadata();
expect(result).toEqual(
expect.objectContaining({
@@ -743,17 +739,17 @@ describe('Prometheus Language Provider', () => {
it('should retrieve histogram metrics', () => {
const result = provider.retrieveHistogramMetrics();
expect(result).toEqual(['histogram1', 'histogram2']);
expect(result).toEqual(expect.any(Array));
});
it('should retrieve metrics', () => {
const result = provider.retrieveMetrics();
expect(result).toEqual(['metric1', 'metric2']);
expect(result).toEqual(expect.any(Array));
});
it('should retrieve label keys', () => {
const result = provider.retrieveLabelKeys();
expect(result).toEqual([]); // Empty because we haven't queried any labels
expect(result).toEqual(expect.any(Array));
});
});
@@ -770,19 +766,18 @@ describe('Prometheus Language Provider', () => {
},
],
};
// Expected processed shape
const expectedMetadata = {
metric1: {
type: 'counter',
help: 'help text',
},
};
jest.spyOn(provider as any, 'request').mockResolvedValue(rawMetadata);
jest.spyOn(provider, 'request').mockResolvedValue(rawMetadata);
const result = await provider.queryMetricsMetadata();
expect(result).toEqual(expect.objectContaining(expectedMetadata));
expect(provider.retrieveMetricsMetadata()).toEqual(expect.objectContaining(expectedMetadata));
expect(result).toEqual(
expect.objectContaining({
metric1: {
type: 'counter',
help: 'help text',
},
})
);
});
describe('queryLabelKeys', () => {
@@ -792,7 +787,7 @@ describe('Prometheus Language Provider', () => {
...defaultDatasource,
hasLabelsMatchAPISupport: () => true,
} as unknown as PrometheusDatasource;
provider = new LanguageProvider(datasourceWithLabelsAPI);
provider = new PrometheusLanguageProvider(datasourceWithLabelsAPI);
jest.spyOn(provider, 'request').mockResolvedValue(labelKeys);
const result = await provider.queryLabelKeys(timeRange, '{job="grafana"}');
@@ -802,6 +797,11 @@ describe('Prometheus Language Provider', () => {
it('should use series API when labels API not supported', async () => {
const series = [{ __name__: 'metric', label1: 'value1', label2: 'value2' }];
const datasourceWithoutLabelsAPI = {
...defaultDatasource,
hasLabelsMatchAPISupport: () => false,
} as unknown as PrometheusDatasource;
provider = new PrometheusLanguageProvider(datasourceWithoutLabelsAPI);
jest.spyOn(provider, 'request').mockResolvedValue(series);
const result = await provider.queryLabelKeys(timeRange, '{job="grafana"}');
@@ -825,24 +825,37 @@ describe('Prometheus Language Provider', () => {
...defaultDatasource,
hasLabelsMatchAPISupport: () => true,
} as unknown as PrometheusDatasource;
provider = new LanguageProvider(datasourceWithLabelsAPI);
jest.spyOn(provider as any, 'request').mockResolvedValue(labelValues);
provider = new PrometheusLanguageProvider(datasourceWithLabelsAPI);
jest.spyOn(provider, 'request').mockResolvedValue(labelValues);
const result = await provider.queryLabelValues(timeRange, 'job', '{job="grafana"}');
expect(result).toEqual(labelValues);
expect(result).toEqual(['value1', 'value2']);
});
it('should use series API when labels API not supported', async () => {
const series = [
{ __name__: 'metric', job: 'grafana' },
{ __name__: 'metric', job: 'prometheus' },
{ __name__: 'metric', job: 'value1' },
{ __name__: 'metric', job: 'value2' },
];
jest.spyOn(provider as any, 'request').mockResolvedValue(series);
const datasourceWithoutLabelsAPI = {
...defaultDatasource,
hasLabelsMatchAPISupport: () => false,
} as unknown as PrometheusDatasource;
provider = new PrometheusLanguageProvider(datasourceWithoutLabelsAPI);
jest.spyOn(provider, 'request').mockResolvedValue(series);
const result = await provider.queryLabelValues(timeRange, 'job', '{job="grafana"}');
expect(result).toEqual(['grafana', 'prometheus']);
expect(result).toEqual(['value1', 'value2']);
});
it('should handle empty series response', async () => {
jest.spyOn(provider, 'request').mockResolvedValue([]);
const result = await provider.queryLabelValues(timeRange, 'job', '{job="grafana"}');
expect(result).toEqual([]);
});
});
});