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,332 @@
|
||||
import type { IDataObject, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { ExternalSecretsProviders } from '@/modules/external-secrets.ee/external-secrets-providers.ee';
|
||||
import { SecretsProvider } from '@/modules/external-secrets.ee/types';
|
||||
import type { SecretsProviderSettings } from '@/modules/external-secrets.ee/types';
|
||||
|
||||
export class MockProviders extends ExternalSecretsProviders {
|
||||
override providers: Record<string, { new (): SecretsProvider }> = {
|
||||
dummy: DummyProvider,
|
||||
};
|
||||
|
||||
setProviders(providers: Record<string, { new (): SecretsProvider }>) {
|
||||
this.providers = providers;
|
||||
}
|
||||
}
|
||||
|
||||
export class DummyProvider extends SecretsProvider {
|
||||
properties: INodeProperties[] = [
|
||||
{
|
||||
name: 'username',
|
||||
displayName: 'Username',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'other',
|
||||
displayName: 'Other',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
name: 'password',
|
||||
displayName: 'Password',
|
||||
type: 'string',
|
||||
default: '',
|
||||
typeOptions: {
|
||||
password: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
secrets: Record<string, string> = {};
|
||||
|
||||
displayName = 'Dummy Provider';
|
||||
|
||||
name = 'dummy';
|
||||
|
||||
_updateSecrets: Record<string, string> = {
|
||||
test1: 'value1',
|
||||
test2: 'value2',
|
||||
};
|
||||
|
||||
async init(_settings: SecretsProviderSettings<IDataObject>): Promise<void> {}
|
||||
|
||||
protected async doConnect(): Promise<void> {
|
||||
// Connected successfully - base class will set state
|
||||
}
|
||||
|
||||
async disconnect(): Promise<void> {}
|
||||
|
||||
async update(): Promise<void> {
|
||||
this.secrets = this._updateSecrets;
|
||||
}
|
||||
|
||||
async test(): Promise<[boolean] | [boolean, string]> {
|
||||
return [true];
|
||||
}
|
||||
|
||||
getSecret(name: string): IDataObject | undefined {
|
||||
return this.secrets[name] as unknown as IDataObject | undefined;
|
||||
}
|
||||
|
||||
hasSecret(name: string): boolean {
|
||||
return name in this.secrets;
|
||||
}
|
||||
|
||||
getSecretNames(): string[] {
|
||||
return Object.keys(this.secrets);
|
||||
}
|
||||
}
|
||||
|
||||
export class AnotherDummyProvider extends SecretsProvider {
|
||||
properties: INodeProperties[] = [
|
||||
{
|
||||
name: 'username',
|
||||
displayName: 'Username',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
|
||||
secrets: Record<string, string> = {};
|
||||
|
||||
displayName = 'Another Dummy Provider';
|
||||
|
||||
name = 'another_dummy';
|
||||
|
||||
_updateSecrets: Record<string, string> = {
|
||||
test1: 'value1',
|
||||
test2: 'value2',
|
||||
};
|
||||
|
||||
async init(_settings: SecretsProviderSettings<IDataObject>): Promise<void> {}
|
||||
|
||||
protected async doConnect(): Promise<void> {
|
||||
// Connected successfully - base class will set state
|
||||
}
|
||||
|
||||
async disconnect(): Promise<void> {}
|
||||
|
||||
async update(): Promise<void> {
|
||||
this.secrets = this._updateSecrets;
|
||||
}
|
||||
|
||||
async test(): Promise<[boolean] | [boolean, string]> {
|
||||
return [true];
|
||||
}
|
||||
|
||||
getSecret(name: string): IDataObject | undefined {
|
||||
return this.secrets[name] as unknown as IDataObject | undefined;
|
||||
}
|
||||
|
||||
hasSecret(name: string): boolean {
|
||||
return name in this.secrets;
|
||||
}
|
||||
|
||||
getSecretNames(): string[] {
|
||||
return Object.keys(this.secrets);
|
||||
}
|
||||
}
|
||||
export class ErrorProvider extends SecretsProvider {
|
||||
secrets: Record<string, string> = {};
|
||||
|
||||
displayName = 'Error Provider';
|
||||
|
||||
name = 'dummy';
|
||||
|
||||
properties = [];
|
||||
|
||||
async init(_settings: SecretsProviderSettings<IDataObject>): Promise<void> {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
protected async doConnect(): Promise<void> {
|
||||
throw new Error('Connection failed');
|
||||
}
|
||||
|
||||
async disconnect(): Promise<void> {
|
||||
// no-op
|
||||
}
|
||||
|
||||
async update(): Promise<void> {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
async test(): Promise<[boolean] | [boolean, string]> {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
getSecret(_name: string): IDataObject | undefined {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
hasSecret(_name: string): boolean {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
getSecretNames(): string[] {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
export class FailedProvider extends SecretsProvider {
|
||||
secrets: Record<string, string> = {};
|
||||
|
||||
displayName = 'Failed Provider';
|
||||
|
||||
name = 'dummy';
|
||||
|
||||
properties = [];
|
||||
|
||||
async init(_settings: SecretsProviderSettings<IDataObject>): Promise<void> {}
|
||||
|
||||
protected async doConnect(): Promise<void> {
|
||||
throw new Error('Failed to connect');
|
||||
}
|
||||
|
||||
async disconnect(): Promise<void> {}
|
||||
|
||||
async update(): Promise<void> {}
|
||||
|
||||
async test(): Promise<[boolean] | [boolean, string]> {
|
||||
return [true];
|
||||
}
|
||||
|
||||
getSecret(name: string): IDataObject | undefined {
|
||||
return this.secrets[name] as unknown as IDataObject | undefined;
|
||||
}
|
||||
|
||||
hasSecret(name: string): boolean {
|
||||
return name in this.secrets;
|
||||
}
|
||||
|
||||
getSecretNames(): string[] {
|
||||
return Object.keys(this.secrets);
|
||||
}
|
||||
}
|
||||
|
||||
export class TestFailProvider extends SecretsProvider {
|
||||
secrets: Record<string, string> = {};
|
||||
|
||||
displayName = 'Test Failed Provider';
|
||||
|
||||
name = 'dummy';
|
||||
|
||||
properties = [];
|
||||
|
||||
_updateSecrets: Record<string, string> = {
|
||||
test1: 'value1',
|
||||
test2: 'value2',
|
||||
};
|
||||
|
||||
async init(_settings: SecretsProviderSettings<IDataObject>): Promise<void> {}
|
||||
|
||||
protected async doConnect(): Promise<void> {
|
||||
// Connected successfully - base class will set state
|
||||
}
|
||||
|
||||
async disconnect(): Promise<void> {}
|
||||
|
||||
async update(): Promise<void> {
|
||||
this.secrets = this._updateSecrets;
|
||||
}
|
||||
|
||||
async test(): Promise<[boolean] | [boolean, string]> {
|
||||
return [false];
|
||||
}
|
||||
|
||||
getSecret(name: string): IDataObject | undefined {
|
||||
return this.secrets[name] as unknown as IDataObject | undefined;
|
||||
}
|
||||
|
||||
hasSecret(name: string): boolean {
|
||||
return name in this.secrets;
|
||||
}
|
||||
|
||||
getSecretNames(): string[] {
|
||||
return Object.keys(this.secrets);
|
||||
}
|
||||
}
|
||||
|
||||
export function createDummyProvider({
|
||||
name,
|
||||
secrets,
|
||||
displayName,
|
||||
properties,
|
||||
}: {
|
||||
name: string;
|
||||
displayName?: string;
|
||||
secrets?: Record<string, string>;
|
||||
properties?: INodeProperties[];
|
||||
}): { new (): SecretsProvider } {
|
||||
const defaultSecrets = secrets ?? {
|
||||
test1: 'value1',
|
||||
test2: 'value2',
|
||||
};
|
||||
|
||||
class FreshProvider extends SecretsProvider {
|
||||
name = name;
|
||||
displayName = displayName ?? name;
|
||||
|
||||
properties: INodeProperties[] = properties ?? [
|
||||
{
|
||||
name: 'username',
|
||||
displayName: 'Username',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'other',
|
||||
displayName: 'Other',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
name: 'password',
|
||||
displayName: 'Password',
|
||||
type: 'string',
|
||||
default: '',
|
||||
typeOptions: {
|
||||
password: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
secrets: Record<string, string> = { ...defaultSecrets };
|
||||
|
||||
_updateSecrets: Record<string, string> = { ...defaultSecrets };
|
||||
|
||||
async init(_settings: SecretsProviderSettings<IDataObject>): Promise<void> {}
|
||||
|
||||
protected async doConnect(): Promise<void> {
|
||||
// Connected successfully - base class will set state
|
||||
}
|
||||
|
||||
async disconnect(): Promise<void> {}
|
||||
|
||||
async update(): Promise<void> {
|
||||
this.secrets = this._updateSecrets;
|
||||
}
|
||||
|
||||
async test(): Promise<[boolean] | [boolean, string]> {
|
||||
return [true];
|
||||
}
|
||||
|
||||
getSecret(name: string): IDataObject | undefined {
|
||||
return this.secrets[name] as unknown as IDataObject | undefined;
|
||||
}
|
||||
|
||||
hasSecret(name: string): boolean {
|
||||
return name in this.secrets;
|
||||
}
|
||||
|
||||
getSecretNames(): string[] {
|
||||
return Object.keys(this.secrets);
|
||||
}
|
||||
}
|
||||
return FreshProvider;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import {
|
||||
NodeConnectionTypes,
|
||||
type IDataObject,
|
||||
type ITaskData,
|
||||
type NodeConnectionType,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
type TaskData = {
|
||||
data: IDataObject;
|
||||
outputIndex?: number;
|
||||
nodeConnectionType?: NodeConnectionType;
|
||||
};
|
||||
|
||||
export function toITaskData(taskData: TaskData[], overrides?: Partial<ITaskData>): ITaskData {
|
||||
const result: ITaskData = {
|
||||
executionStatus: 'success',
|
||||
executionTime: 0,
|
||||
startTime: 0,
|
||||
executionIndex: 0,
|
||||
source: [],
|
||||
data: {},
|
||||
...(overrides ?? {}),
|
||||
};
|
||||
|
||||
// NOTE: Here to make TS happy.
|
||||
result.data = result.data ?? {};
|
||||
for (const taskDatum of taskData) {
|
||||
const type = taskDatum.nodeConnectionType ?? NodeConnectionTypes.Main;
|
||||
const outputIndex = taskDatum.outputIndex ?? 0;
|
||||
|
||||
result.data[type] = result.data[type] ?? [];
|
||||
const dataConnection = result.data[type];
|
||||
dataConnection[outputIndex] = [{ json: taskDatum.data }];
|
||||
}
|
||||
|
||||
for (const [type, dataConnection] of Object.entries(result.data)) {
|
||||
for (const [index, maybe] of dataConnection.entries()) {
|
||||
result.data[type][index] = maybe ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import {
|
||||
randomCredentialPayload,
|
||||
randomEmail,
|
||||
randomName,
|
||||
uniqueId,
|
||||
} from '@n8n/backend-test-utils';
|
||||
import { CredentialsEntity, Project, User } from '@n8n/db';
|
||||
import { randomInt } from 'n8n-workflow';
|
||||
|
||||
export const mockCredential = (): CredentialsEntity =>
|
||||
Object.assign(new CredentialsEntity(), randomCredentialPayload());
|
||||
|
||||
export const mockUser = (): User =>
|
||||
Object.assign(new User(), {
|
||||
id: randomInt(1000),
|
||||
email: randomEmail(),
|
||||
firstName: randomName(),
|
||||
lastName: randomName(),
|
||||
});
|
||||
|
||||
export const mockProject = (): Project =>
|
||||
Object.assign(new Project(), {
|
||||
id: uniqueId(),
|
||||
type: 'personal',
|
||||
name: 'Nathan Fillion <nathan.fillion@n8n.io>',
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
import { mockInstance } from '@n8n/backend-test-utils';
|
||||
import { DataSource, EntityManager, type EntityMetadata } from '@n8n/typeorm';
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import type { Cipher, Class } from 'n8n-core';
|
||||
|
||||
export const mockEntityManager = (entityClass: Class) => {
|
||||
const entityManager = mockInstance(EntityManager);
|
||||
const dataSource = mockInstance(DataSource, {
|
||||
manager: entityManager,
|
||||
getMetadata: () => mock<EntityMetadata>({ target: entityClass }),
|
||||
});
|
||||
Object.assign(entityManager, { connection: dataSource });
|
||||
return entityManager;
|
||||
};
|
||||
|
||||
export const mockCipher = () =>
|
||||
mock<Cipher>({
|
||||
encrypt: (data) => (typeof data === 'string' ? data : JSON.stringify(data)),
|
||||
decrypt: (data) => data,
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
export const badPasswords = {
|
||||
pass: 'Password must be 8 to 64 characters long. Password must contain at least 1 number. Password must contain at least 1 uppercase letter.',
|
||||
password:
|
||||
'Password must contain at least 1 number. Password must contain at least 1 uppercase letter.',
|
||||
password1: 'Password must contain at least 1 uppercase letter.',
|
||||
};
|
||||
Reference in New Issue
Block a user