mirror of
https://github.com/n8n-io/n8n.git
synced 2026-06-04 10:39:23 +02:00
178 lines
5.4 KiB
TypeScript
178 lines
5.4 KiB
TypeScript
import { Logger } from '@n8n/backend-common';
|
|
import { mockInstance, mockLogger, testDb, testModules } from '@n8n/backend-test-utils';
|
|
import { SecretsProviderConnectionRepository, SettingsRepository } from '@n8n/db';
|
|
import { Container } from '@n8n/di';
|
|
import { Cipher } from 'n8n-core';
|
|
|
|
import { ExternalSecretsProviders } from '@/modules/external-secrets.ee/external-secrets-providers.ee';
|
|
import { ExternalSecretsConfig } from '@/modules/external-secrets.ee/external-secrets.config';
|
|
import { ExternalSecretsModule } from '@/modules/external-secrets.ee/external-secrets.module';
|
|
import { PubSubEventBus } from '@/scaling/pubsub/pubsub.eventbus';
|
|
import { PubSubRegistry } from '@/scaling/pubsub/pubsub.registry';
|
|
|
|
import {
|
|
AnotherDummyProvider,
|
|
DummyProvider,
|
|
MockProviders,
|
|
} from '../../shared/external-secrets/utils';
|
|
|
|
const mockProvidersInstance = new MockProviders();
|
|
mockInstance(ExternalSecretsProviders, mockProvidersInstance);
|
|
|
|
const waitForEventHandler = async (ms = 500) => {
|
|
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
};
|
|
|
|
const testReloadEvent = async (
|
|
pubsubEventBus: PubSubEventBus,
|
|
{ connectsAllProviders = false }: { connectsAllProviders?: boolean } = {},
|
|
) => {
|
|
const initSpy = jest.spyOn(DummyProvider.prototype, 'init');
|
|
const connectSpy = jest.spyOn(DummyProvider.prototype, 'connect');
|
|
const disconnectSpy = jest.spyOn(DummyProvider.prototype, 'disconnect');
|
|
const updateSpy = jest.spyOn(DummyProvider.prototype, 'update');
|
|
|
|
const initAnotherSpy = jest.spyOn(AnotherDummyProvider.prototype, 'init');
|
|
const connectAnotherSpy = jest.spyOn(AnotherDummyProvider.prototype, 'connect');
|
|
const disconnectAnotherSpy = jest.spyOn(AnotherDummyProvider.prototype, 'disconnect');
|
|
const updateAnotherSpy = jest.spyOn(AnotherDummyProvider.prototype, 'update');
|
|
|
|
// Emit event and wait for async handler to complete
|
|
pubsubEventBus.emit('reload-external-secrets-providers');
|
|
await waitForEventHandler();
|
|
|
|
expect(disconnectSpy).toHaveBeenCalled();
|
|
expect(initSpy).toHaveBeenCalled();
|
|
expect(connectSpy).toHaveBeenCalled();
|
|
expect(updateSpy).toHaveBeenCalled();
|
|
|
|
expect(disconnectAnotherSpy).toHaveBeenCalled();
|
|
expect(initAnotherSpy).toHaveBeenCalled();
|
|
if (connectsAllProviders) {
|
|
expect(connectAnotherSpy).toHaveBeenCalled();
|
|
expect(updateAnotherSpy).toHaveBeenCalled();
|
|
} else {
|
|
expect(connectAnotherSpy).not.toHaveBeenCalled();
|
|
expect(updateAnotherSpy).not.toHaveBeenCalled();
|
|
}
|
|
};
|
|
|
|
describe('External Secrets Event Handling', () => {
|
|
let pubsubEventBus: PubSubEventBus;
|
|
let pubSubRegistry: PubSubRegistry;
|
|
|
|
beforeAll(async () => {
|
|
await testModules.loadModules(['external-secrets']);
|
|
await testDb.init();
|
|
|
|
Container.set(Logger, mockLogger());
|
|
|
|
// Get pub/sub infrastructure from DI container
|
|
pubsubEventBus = Container.get(PubSubEventBus);
|
|
pubSubRegistry = Container.get(PubSubRegistry);
|
|
});
|
|
|
|
afterEach(() => {
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await testDb.terminate();
|
|
});
|
|
|
|
describe('using settings store', () => {
|
|
let module: ExternalSecretsModule;
|
|
|
|
beforeAll(async () => {
|
|
mockProvidersInstance.setProviders({
|
|
dummy: DummyProvider,
|
|
another_dummy: AnotherDummyProvider,
|
|
});
|
|
|
|
const config = Container.get(ExternalSecretsConfig);
|
|
(config as any).externalSecretsForProjects = false;
|
|
(config as any).externalSecretsMultipleConnections = false;
|
|
|
|
const settingsRepository = Container.get(SettingsRepository);
|
|
const cipher = Container.get(Cipher);
|
|
|
|
const settings = {
|
|
dummy: {
|
|
connected: true,
|
|
connectedAt: new Date(),
|
|
settings: {},
|
|
},
|
|
another_dummy: {
|
|
connected: false,
|
|
connectedAt: new Date(),
|
|
settings: {},
|
|
},
|
|
};
|
|
|
|
await settingsRepository.save({
|
|
key: 'feature.externalSecrets',
|
|
value: cipher.encrypt(settings),
|
|
loadOnStartup: false,
|
|
});
|
|
|
|
module = Container.get(ExternalSecretsModule);
|
|
await module.init();
|
|
|
|
// IMPORTANT: Initialize PubSubRegistry AFTER module.init() to wire up decorators
|
|
pubSubRegistry.init();
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await module.shutdown();
|
|
});
|
|
|
|
it('should reload providers when reload-external-secrets-providers event is emitted', async () =>
|
|
await testReloadEvent(pubsubEventBus));
|
|
});
|
|
|
|
describe('using providers connections', () => {
|
|
let module: ExternalSecretsModule;
|
|
|
|
beforeAll(async () => {
|
|
mockProvidersInstance.setProviders({
|
|
dummy: DummyProvider,
|
|
another_dummy: AnotherDummyProvider,
|
|
});
|
|
|
|
const config = Container.get(ExternalSecretsConfig);
|
|
(config as any).externalSecretsForProjects = true;
|
|
|
|
const cipher = Container.get(Cipher);
|
|
const encryptedSettings = cipher.encrypt(JSON.stringify({}));
|
|
|
|
const connectionRepository = Container.get(SecretsProviderConnectionRepository);
|
|
await connectionRepository.save({
|
|
providerKey: 'my-vault',
|
|
type: 'dummy',
|
|
encryptedSettings,
|
|
isEnabled: true,
|
|
});
|
|
|
|
await connectionRepository.save({
|
|
providerKey: 'another-vault',
|
|
type: 'another_dummy',
|
|
encryptedSettings,
|
|
isEnabled: true,
|
|
});
|
|
|
|
module = Container.get(ExternalSecretsModule);
|
|
await module.init();
|
|
|
|
// IMPORTANT: Initialize PubSubRegistry AFTER module.init() to wire up decorators
|
|
pubSubRegistry.init();
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await module.shutdown();
|
|
});
|
|
|
|
it('should reload providers when reload-external-secrets-providers event is emitted', async () =>
|
|
await testReloadEvent(pubsubEventBus, { connectsAllProviders: true }));
|
|
});
|
|
});
|