first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
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,
|
||||
});
|
||||
|
||||
await connectionRepository.save({
|
||||
providerKey: 'another-vault',
|
||||
type: 'another_dummy',
|
||||
encryptedSettings,
|
||||
});
|
||||
|
||||
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 }));
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user