1
0
mirror of https://github.com/Unleash/unleash.git synced 2024-12-22 19:07:54 +01:00
unleash.unleash/src/lib/middleware/cors-origin-middleware.test.ts
Nuno Góis 87d9497be9
refactor: prefer eventService.storeEvent methods (#4830)
https://linear.app/unleash/issue/2-1403/consider-refactoring-the-way-tags-are-fetched-for-the-events

This adds 2 methods to `EventService`:
 - `storeEvent`;
 - `storeEvents`;

This allows us to run event-specific logic inside these methods. In the
case of this PR, this means fetching the feature tags in case the event
contains a `featureName` and there are no tags specified in the event.

This prevents us from having to remember to fetch the tags in order to
store feature-related events except for very specific cases, like the
deletion of a feature - You can't fetch tags for a feature that no
longer exists, so in that case we need to pre-fetch the tags before
deleting the feature.

This also allows us to do any event-specific post-processing to the
event before reaching the DB layer.
In general I think it's also nicer that we reference the event service
instead of the event store directly.

There's a lot of changes and a lot of files touched, but most of it is
boilerplate to inject the `eventService` where needed instead of using
the `eventStore` directly.

Hopefully this will be a better approach than
https://github.com/Unleash/unleash/pull/4729

---------

Co-authored-by: Gastón Fournier <gaston@getunleash.io>
2023-09-27 14:23:05 +01:00

157 lines
5.1 KiB
TypeScript

import { resolveOrigin } from './cors-origin-middleware';
import FakeSettingStore from '../../test/fixtures/fake-setting-store';
import { createTestConfig } from '../../test/config/test-config';
import FakeEventStore from '../../test/fixtures/fake-event-store';
import { randomId } from '../util/random-id';
import FakeProjectStore from '../../test/fixtures/fake-project-store';
import { EventService, ProxyService, SettingService } from '../../lib/services';
import { ISettingStore } from '../../lib/types';
import { frontendSettingsKey } from '../../lib/types/settings/frontend-settings';
import { minutesToMilliseconds } from 'date-fns';
import FakeFeatureTagStore from '../../test/fixtures/fake-feature-tag-store';
const createSettingService = (
frontendApiOrigins: string[],
): { proxyService: ProxyService; settingStore: ISettingStore } => {
const config = createTestConfig({ frontendApiOrigins });
const stores = {
settingStore: new FakeSettingStore(),
eventStore: new FakeEventStore(),
featureTagStore: new FakeFeatureTagStore(),
projectStore: new FakeProjectStore(),
};
const eventService = new EventService(stores, config);
const services = {
settingService: new SettingService(stores, config, eventService),
};
return {
//@ts-ignore
proxyService: new ProxyService(config, stores, services),
settingStore: stores.settingStore,
};
};
test('resolveOrigin', () => {
const dotCom = 'https://example.com';
const dotOrg = 'https://example.org';
expect(resolveOrigin([])).toEqual('*');
expect(resolveOrigin(['*'])).toEqual('*');
expect(resolveOrigin([dotOrg])).toEqual([dotOrg]);
expect(resolveOrigin([dotCom, dotOrg])).toEqual([dotCom, dotOrg]);
expect(resolveOrigin([dotOrg, '*'])).toEqual('*');
});
test('corsOriginMiddleware origin validation', async () => {
const { proxyService } = createSettingService([]);
const userName = randomId();
await expect(() =>
proxyService.setFrontendSettings(
{ frontendApiOrigins: ['a'] },
userName,
),
).rejects.toThrow('Invalid origin: a');
proxyService.destroy();
});
test('corsOriginMiddleware without config', async () => {
const { proxyService, settingStore } = createSettingService([]);
const userName = randomId();
expect(await proxyService.getFrontendSettings(false)).toEqual({
frontendApiOrigins: [],
});
await proxyService.setFrontendSettings(
{ frontendApiOrigins: [] },
userName,
);
expect(await proxyService.getFrontendSettings(false)).toEqual({
frontendApiOrigins: [],
});
await proxyService.setFrontendSettings(
{ frontendApiOrigins: ['*'] },
userName,
);
expect(await proxyService.getFrontendSettings(false)).toEqual({
frontendApiOrigins: ['*'],
});
await settingStore.delete(frontendSettingsKey);
expect(await proxyService.getFrontendSettings(false)).toEqual({
frontendApiOrigins: [],
});
proxyService.destroy();
});
test('corsOriginMiddleware with config', async () => {
const { proxyService, settingStore } = createSettingService(['*']);
const userName = randomId();
expect(await proxyService.getFrontendSettings(false)).toEqual({
frontendApiOrigins: ['*'],
});
await proxyService.setFrontendSettings(
{ frontendApiOrigins: [] },
userName,
);
expect(await proxyService.getFrontendSettings(false)).toEqual({
frontendApiOrigins: [],
});
await proxyService.setFrontendSettings(
{ frontendApiOrigins: ['https://example.com', 'https://example.org'] },
userName,
);
expect(await proxyService.getFrontendSettings(false)).toEqual({
frontendApiOrigins: ['https://example.com', 'https://example.org'],
});
await settingStore.delete(frontendSettingsKey);
expect(await proxyService.getFrontendSettings(false)).toEqual({
frontendApiOrigins: ['*'],
});
proxyService.destroy();
});
test('corsOriginMiddleware with caching enabled', async () => {
jest.useFakeTimers();
const { proxyService } = createSettingService([]);
const userName = randomId();
expect(await proxyService.getFrontendSettings()).toEqual({
frontendApiOrigins: [],
});
//setting
await proxyService.setFrontendSettings(
{ frontendApiOrigins: ['*'] },
userName,
);
//still get cached value
expect(await proxyService.getFrontendSettings()).toEqual({
frontendApiOrigins: [],
});
jest.advanceTimersByTime(minutesToMilliseconds(2));
jest.useRealTimers();
/*
This is needed because it is not enough to fake time to test the
updated cache, we also need to make sure that all promises are
executed and completed, in the right order.
*/
await new Promise<void>((resolve) =>
process.nextTick(async () => {
const settings = await proxyService.getFrontendSettings();
expect(settings).toEqual({
frontendApiOrigins: ['*'],
});
resolve();
}),
);
proxyService.destroy();
});