grafana/packages/grafana-runtime/src/services/pluginMeta/plugins.test.ts
Hugo Häggmark eebacf0b2d
Plugins: add error handling for plugin meta calls (#117111)
* Plugins: add error handling for plugin meta calls

* chore: update after PR feedback
2026-02-03 06:33:15 +01:00

141 lines
4.5 KiB
TypeScript

import { evaluateBooleanFlag } from '../../internal/openFeature';
import { invalidateCache, setLogger } from '../../utils/getCachedPromise';
import { type MonitoringLogger } from '../../utils/logging';
import { initPluginMetas } from './plugins';
import { v0alpha1Meta } from './test-fixtures/v0alpha1Response';
jest.mock('../../internal/openFeature', () => ({
...jest.requireActual('../../internal/openFeature'),
evaluateBooleanFlag: jest.fn(),
}));
const evaluateBooleanFlagMock = jest.mocked(evaluateBooleanFlag);
const originalFetch = global.fetch;
let loggerMock: MonitoringLogger;
beforeEach(() => {
jest.clearAllMocks();
invalidateCache();
loggerMock = {
logDebug: jest.fn(),
logError: jest.fn(),
logInfo: jest.fn(),
logMeasurement: jest.fn(),
logWarning: jest.fn(),
};
setLogger(loggerMock);
});
afterEach(() => {
global.fetch = originalFetch;
});
describe('when useMTPlugins toggle is enabled and cache is not initialized', () => {
beforeEach(() => {
evaluateBooleanFlagMock.mockReturnValue(true);
});
it('initPluginMetas should call loadPluginMetas and return correct result if response is ok', async () => {
global.fetch = jest.fn().mockResolvedValue({
ok: true,
status: 200,
json: () => Promise.resolve({ items: [v0alpha1Meta] }),
});
const response = await initPluginMetas();
expect(response.items).toHaveLength(1);
expect(response.items[0]).toBe(v0alpha1Meta);
expect(global.fetch).toHaveBeenCalledTimes(1);
expect(global.fetch).toHaveBeenCalledWith('/apis/plugins.grafana.app/v0alpha1/namespaces/default/metas');
});
});
describe('when useMTPlugins toggle is enabled and errors occur', () => {
beforeEach(() => {
evaluateBooleanFlagMock.mockReturnValue(true);
});
it('initPluginMetas should log when fetch fails', async () => {
global.fetch = jest
.fn()
.mockResolvedValueOnce({
ok: false,
statusText: 'Internal Server Error',
status: 500,
})
.mockResolvedValue({
ok: true,
status: 200,
json: () => Promise.resolve({ items: [v0alpha1Meta] }),
});
await initPluginMetas();
await initPluginMetas();
await initPluginMetas();
expect(global.fetch).toHaveBeenCalledTimes(2); // first + second (because first throws), third is cached
expect(global.fetch).toHaveBeenCalledWith('/apis/plugins.grafana.app/v0alpha1/namespaces/default/metas');
expect(loggerMock.logError).toHaveBeenCalledTimes(1);
expect(loggerMock.logError).toHaveBeenCalledWith(new Error(`Something failed while resolving a cached promise`), {
message: 'Failed to load plugin metas 500:Internal Server Error',
stack: expect.any(String),
key: 'loadPluginMetas',
});
});
it('initPluginMetas should log when fetch rejects', async () => {
global.fetch = jest
.fn()
.mockRejectedValueOnce(new Error('Network Error'))
.mockResolvedValue({
ok: true,
status: 200,
json: () => Promise.resolve({ items: [v0alpha1Meta] }),
});
await initPluginMetas();
await initPluginMetas();
await initPluginMetas();
expect(global.fetch).toHaveBeenCalledTimes(2); // first + second (because first throws), third is cached
expect(global.fetch).toHaveBeenCalledWith('/apis/plugins.grafana.app/v0alpha1/namespaces/default/metas');
expect(loggerMock.logError).toHaveBeenCalledTimes(1);
expect(loggerMock.logError).toHaveBeenCalledWith(new Error(`Something failed while resolving a cached promise`), {
message: 'Network Error',
stack: expect.any(String),
key: 'loadPluginMetas',
});
});
});
describe('when useMTPlugins toggle is disabled and cache is not initialized', () => {
beforeEach(() => {
global.fetch = jest.fn();
evaluateBooleanFlagMock.mockReturnValue(false);
});
it('initPluginMetas should call loadPluginMetas and return correct result if response is ok', async () => {
const response = await initPluginMetas();
expect(response.items).toHaveLength(0);
expect(global.fetch).not.toHaveBeenCalled();
});
});
describe('when useMTPlugins toggle is disabled and cache is initialized', () => {
beforeEach(() => {
global.fetch = jest.fn();
evaluateBooleanFlagMock.mockReturnValue(false);
});
it('initPluginMetas should return cache', async () => {
const original = await initPluginMetas();
const cached = await initPluginMetas();
expect(original).toBe(cached);
expect(global.fetch).not.toHaveBeenCalled();
});
});