ci(core): Reduce memory usage in tests (part-1) (no-changelog) (#7654)

This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™ 2023-11-08 16:29:39 +01:00 committed by GitHub
parent 6a53c2a375
commit 0346b211a7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
58 changed files with 1223 additions and 1189 deletions

View file

@ -66,23 +66,20 @@ import type {
WorkflowRequest,
} from '@/requests';
import { registerController } from '@/decorators';
import {
AuthController,
LdapController,
MeController,
MFAController,
NodeTypesController,
OAuth1CredentialController,
OAuth2CredentialController,
OwnerController,
PasswordResetController,
TagsController,
TranslationController,
UsersController,
WorkflowStatisticsController,
} from '@/controllers';
import { BinaryDataController } from './controllers/binaryData.controller';
import { AuthController } from '@/controllers/auth.controller';
import { BinaryDataController } from '@/controllers/binaryData.controller';
import { LdapController } from '@/controllers/ldap.controller';
import { MeController } from '@/controllers/me.controller';
import { MFAController } from '@/controllers/mfa.controller';
import { NodeTypesController } from '@/controllers/nodeTypes.controller';
import { OAuth1CredentialController } from '@/controllers/oauth/oAuth1Credential.controller';
import { OAuth2CredentialController } from '@/controllers/oauth/oAuth2Credential.controller';
import { OwnerController } from '@/controllers/owner.controller';
import { PasswordResetController } from '@/controllers/passwordReset.controller';
import { TagsController } from '@/controllers/tags.controller';
import { TranslationController } from '@/controllers/translation.controller';
import { UsersController } from '@/controllers/users.controller';
import { WorkflowStatisticsController } from '@/controllers/workflowStatistics.controller';
import { ExternalSecretsController } from '@/ExternalSecrets/ExternalSecrets.controller.ee';
import { executionsController } from '@/executions/executions.controller';
import { isApiEnabled, loadPublicApiVersions } from '@/PublicApi';

View file

@ -48,8 +48,6 @@ import { generateFailedExecutionFromError } from '@/WorkflowHelpers';
import { initErrorHandling } from '@/ErrorReporting';
import { PermissionChecker } from '@/UserManagement/PermissionChecker';
import { Push } from '@/push';
import { eventBus } from './eventbus';
import { recoverExecutionDataFromEventLogMessages } from './eventbus/MessageEventBus/recoverEvents';
import { Container } from 'typedi';
import { InternalHooks } from './InternalHooks';
import { ExecutionRepository } from '@db/repositories';
@ -131,9 +129,13 @@ export class WorkflowRunner {
// does contain those messages.
try {
// Search for messages for this executionId in event logs
const { eventBus } = await import('./eventbus');
const eventLogMessages = await eventBus.getEventsByExecutionId(executionId);
// Attempt to recover more better runData from these messages (but don't update the execution db entry yet)
if (eventLogMessages.length > 0) {
const { recoverExecutionDataFromEventLogMessages } = await import(
'./eventbus/MessageEventBus/recoverEvents'
);
const eventLogExecutionData = await recoverExecutionDataFromEventLogMessages(
executionId,
eventLogMessages,

View file

@ -1,13 +0,0 @@
export { AuthController } from './auth.controller';
export { LdapController } from './ldap.controller';
export { MeController } from './me.controller';
export { MFAController } from './mfa.controller';
export { NodeTypesController } from './nodeTypes.controller';
export { OAuth1CredentialController } from './oauth/oAuth1Credential.controller';
export { OAuth2CredentialController } from './oauth/oAuth2Credential.controller';
export { OwnerController } from './owner.controller';
export { PasswordResetController } from './passwordReset.controller';
export { TagsController } from './tags.controller';
export { TranslationController } from './translation.controller';
export { UsersController } from './users.controller';
export { WorkflowStatisticsController } from './workflowStatistics.controller';

View file

@ -1,7 +1,10 @@
import { Service } from 'typedi';
import { Authorized, Delete, Get, Post, RestController } from '@/decorators';
import { AuthenticatedRequest, MFA } from '@/requests';
import { BadRequestError } from '@/ResponseHelper';
import { MfaService } from '@/Mfa/mfa.service';
@Service()
@Authorized()
@RestController('/mfa')
export class MFAController {

View file

@ -22,6 +22,8 @@ import type { WebhookEntity } from '@/databases/entities/WebhookEntity';
import { NodeTypes } from '@/NodeTypes';
import { chooseRandomly } from './shared/random';
import { MultiMainInstancePublisher } from '@/services/orchestration/main/MultiMainInstance.publisher.ee';
import { createOwner } from './shared/db/users';
import { createWorkflow } from './shared/db/workflows';
mockInstance(ActiveExecutions);
mockInstance(ActiveWorkflows);
@ -50,7 +52,7 @@ beforeAll(async () => {
await testDb.init();
activeWorkflowRunner = Container.get(ActiveWorkflowRunner);
owner = await testDb.createOwner();
owner = await createOwner();
});
afterEach(async () => {
@ -88,7 +90,7 @@ describe('init()', () => {
});
test('should start with one active workflow', async () => {
await testDb.createWorkflow({ active: true }, owner);
await createWorkflow({ active: true }, owner);
await activeWorkflowRunner.init();
@ -100,8 +102,8 @@ describe('init()', () => {
});
test('should start with multiple active workflows', async () => {
await testDb.createWorkflow({ active: true }, owner);
await testDb.createWorkflow({ active: true }, owner);
await createWorkflow({ active: true }, owner);
await createWorkflow({ active: true }, owner);
await activeWorkflowRunner.init();
@ -113,8 +115,8 @@ describe('init()', () => {
});
test('should pre-check that every workflow can be activated', async () => {
await testDb.createWorkflow({ active: true }, owner);
await testDb.createWorkflow({ active: true }, owner);
await createWorkflow({ active: true }, owner);
await createWorkflow({ active: true }, owner);
const precheckSpy = jest
.spyOn(Workflow.prototype, 'checkIfWorkflowCanBeActivated')
@ -128,8 +130,8 @@ describe('init()', () => {
describe('removeAll()', () => {
test('should remove all active workflows from memory', async () => {
await testDb.createWorkflow({ active: true }, owner);
await testDb.createWorkflow({ active: true }, owner);
await createWorkflow({ active: true }, owner);
await createWorkflow({ active: true }, owner);
await activeWorkflowRunner.init();
await activeWorkflowRunner.removeAll();
@ -141,7 +143,7 @@ describe('removeAll()', () => {
describe('remove()', () => {
test('should call `ActiveWorkflowRunner.clearWebhooks()`', async () => {
const workflow = await testDb.createWorkflow({ active: true }, owner);
const workflow = await createWorkflow({ active: true }, owner);
const clearWebhooksSpy = jest.spyOn(activeWorkflowRunner, 'clearWebhooks');
await activeWorkflowRunner.init();
@ -153,7 +155,7 @@ describe('remove()', () => {
describe('isActive()', () => {
test('should return `true` for active workflow in storage', async () => {
const workflow = await testDb.createWorkflow({ active: true }, owner);
const workflow = await createWorkflow({ active: true }, owner);
await activeWorkflowRunner.init();
@ -162,7 +164,7 @@ describe('isActive()', () => {
});
test('should return `false` for inactive workflow in storage', async () => {
const workflow = await testDb.createWorkflow({ active: false }, owner);
const workflow = await createWorkflow({ active: false }, owner);
await activeWorkflowRunner.init();
@ -173,7 +175,7 @@ describe('isActive()', () => {
describe('runWorkflow()', () => {
test('should call `WorkflowRunner.run()`', async () => {
const workflow = await testDb.createWorkflow({ active: true }, owner);
const workflow = await createWorkflow({ active: true }, owner);
await activeWorkflowRunner.init();
@ -193,7 +195,7 @@ describe('runWorkflow()', () => {
describe('executeErrorWorkflow()', () => {
test('should call `WorkflowExecuteAdditionalData.executeErrorWorkflow()`', async () => {
const workflow = await testDb.createWorkflow({ active: true }, owner);
const workflow = await createWorkflow({ active: true }, owner);
const [node] = workflow.nodes;
const error = new NodeOperationError(node, 'Fake error message');
const executeSpy = jest.spyOn(AdditionalData, 'executeErrorWorkflow');
@ -205,7 +207,7 @@ describe('executeErrorWorkflow()', () => {
});
test('should be called on failure to activate due to 401', async () => {
const storedWorkflow = await testDb.createWorkflow({ active: true }, owner);
const storedWorkflow = await createWorkflow({ active: true }, owner);
const [node] = storedWorkflow.nodes;
const executeSpy = jest.spyOn(activeWorkflowRunner, 'executeErrorWorkflow');
@ -230,7 +232,7 @@ describe('add()', () => {
test('leader should add webhooks, triggers and pollers', async () => {
const mode = chooseRandomly(NON_LEADERSHIP_CHANGE_MODES);
const workflow = await testDb.createWorkflow({ active: true }, owner);
const workflow = await createWorkflow({ active: true }, owner);
const addWebhooksSpy = jest.spyOn(activeWorkflowRunner, 'addWebhooks');
const addTriggersAndPollersSpy = jest.spyOn(activeWorkflowRunner, 'addTriggersAndPollers');
@ -256,7 +258,7 @@ describe('add()', () => {
mockInstance(MultiMainInstancePublisher, { isLeader: true });
const workflow = await testDb.createWorkflow({ active: true }, owner);
const workflow = await createWorkflow({ active: true }, owner);
const addWebhooksSpy = jest.spyOn(activeWorkflowRunner, 'addWebhooks');
const addTriggersAndPollersSpy = jest.spyOn(activeWorkflowRunner, 'addTriggersAndPollers');
@ -278,7 +280,7 @@ describe('add()', () => {
mockInstance(MultiMainInstancePublisher, { isLeader: true });
const workflow = await testDb.createWorkflow({ active: true }, owner);
const workflow = await createWorkflow({ active: true }, owner);
const addWebhooksSpy = jest.spyOn(activeWorkflowRunner, 'addWebhooks');
const addTriggersAndPollersSpy = jest.spyOn(activeWorkflowRunner, 'addTriggersAndPollers');
@ -302,7 +304,7 @@ describe('add()', () => {
mockInstance(MultiMainInstancePublisher, { isLeader: false });
const workflow = await testDb.createWorkflow({ active: true }, owner);
const workflow = await createWorkflow({ active: true }, owner);
const addWebhooksSpy = jest.spyOn(activeWorkflowRunner, 'addWebhooks');
const addTriggersAndPollersSpy = jest.spyOn(activeWorkflowRunner, 'addTriggersAndPollers');
@ -330,7 +332,7 @@ describe('addWebhooks()', () => {
const additionalData = await AdditionalData.getBase('fake-user-id');
const dbWorkflow = await testDb.createWorkflow({ active: true }, owner);
const dbWorkflow = await createWorkflow({ active: true }, owner);
const workflow = new Workflow({
id: dbWorkflow.id,

View file

@ -1,6 +1,5 @@
import type { SuperAgentTest } from 'supertest';
import { License } from '@/License';
import * as testDb from '../shared/testDb';
import * as utils from '../shared/utils/';
import type { ExternalSecretsSettings, SecretsProviderState } from '@/Interfaces';
import { Cipher } from 'n8n-core';
@ -18,6 +17,7 @@ import { ExternalSecretsManager } from '@/ExternalSecrets/ExternalSecretsManager
import { CREDENTIAL_BLANKING_VALUE } from '@/constants';
import { jsonParse, type IDataObject } from 'n8n-workflow';
import { mock } from 'jest-mock-extended';
import { createOwner, createUser } from '../shared/db/users';
let authOwnerAgent: SuperAgentTest;
let authMemberAgent: SuperAgentTest;
@ -97,9 +97,9 @@ const getDummyProviderData = ({
};
beforeAll(async () => {
const owner = await testDb.createOwner();
const owner = await createOwner();
authOwnerAgent = testServer.authAgentFor(owner);
const member = await testDb.createUser();
const member = await createUser();
authMemberAgent = testServer.authAgentFor(member);
config.set('userManagement.isInstanceOwnerSetUp', true);
});

View file

@ -11,6 +11,8 @@ import { LOGGED_OUT_RESPONSE_BODY } from './shared/constants';
import { randomValidPassword } from './shared/random';
import * as testDb from './shared/testDb';
import * as utils from './shared/utils/';
import { getGlobalMemberRole, getGlobalOwnerRole } from './shared/db/roles';
import { createUser, createUserShell } from './shared/db/users';
let globalOwnerRole: Role;
let globalMemberRole: Role;
@ -21,8 +23,8 @@ const ownerPassword = randomValidPassword();
const testServer = utils.setupTestServer({ endpointGroups: ['auth'] });
beforeAll(async () => {
globalOwnerRole = await testDb.getGlobalOwnerRole();
globalMemberRole = await testDb.getGlobalMemberRole();
globalOwnerRole = await getGlobalOwnerRole();
globalMemberRole = await getGlobalMemberRole();
});
beforeEach(async () => {
@ -33,7 +35,7 @@ beforeEach(async () => {
describe('POST /login', () => {
beforeEach(async () => {
owner = await testDb.createUser({
owner = await createUser({
password: ownerPassword,
globalRole: globalOwnerRole,
});
@ -69,7 +71,7 @@ describe('POST /login', () => {
test('should throw AuthError for non-owner if not within users limit quota', async () => {
jest.spyOn(Container.get(License), 'isWithinUsersLimit').mockReturnValueOnce(false);
const password = 'testpassword';
const member = await testDb.createUser({
const member = await createUser({
password,
});
@ -82,7 +84,7 @@ describe('POST /login', () => {
test('should not throw AuthError for owner if not within users limit quota', async () => {
jest.spyOn(Container.get(License), 'isWithinUsersLimit').mockReturnValueOnce(false);
const ownerUser = await testDb.createUser({
const ownerUser = await createUser({
password: randomValidPassword(),
globalRole: globalOwnerRole,
isOwner: true,
@ -104,7 +106,7 @@ describe('GET /login', () => {
});
test('should return cookie if UM is disabled and no cookie is already set', async () => {
await testDb.createUserShell(globalOwnerRole);
await createUserShell(globalOwnerRole);
await utils.setInstanceOwnerSetUp(false);
const response = await testServer.authlessAgent.get('/login');
@ -127,7 +129,7 @@ describe('GET /login', () => {
});
test('should return logged-in owner shell', async () => {
const ownerShell = await testDb.createUserShell(globalOwnerRole);
const ownerShell = await createUserShell(globalOwnerRole);
const response = await testServer.authAgentFor(ownerShell).get('/login');
@ -153,7 +155,7 @@ describe('GET /login', () => {
});
test('should return logged-in member shell', async () => {
const memberShell = await testDb.createUserShell(globalMemberRole);
const memberShell = await createUserShell(globalMemberRole);
const response = await testServer.authAgentFor(memberShell).get('/login');
@ -179,7 +181,7 @@ describe('GET /login', () => {
});
test('should return logged-in owner', async () => {
const owner = await testDb.createUser({ globalRole: globalOwnerRole });
const owner = await createUser({ globalRole: globalOwnerRole });
const response = await testServer.authAgentFor(owner).get('/login');
@ -205,7 +207,7 @@ describe('GET /login', () => {
});
test('should return logged-in member', async () => {
const member = await testDb.createUser({ globalRole: globalMemberRole });
const member = await createUser({ globalRole: globalMemberRole });
const response = await testServer.authAgentFor(member).get('/login');
@ -233,7 +235,7 @@ describe('GET /login', () => {
describe('GET /resolve-signup-token', () => {
beforeEach(async () => {
owner = await testDb.createUser({
owner = await createUser({
password: ownerPassword,
globalRole: globalOwnerRole,
});
@ -241,7 +243,7 @@ describe('GET /resolve-signup-token', () => {
});
test('should validate invite token', async () => {
const memberShell = await testDb.createUserShell(globalMemberRole);
const memberShell = await createUserShell(globalMemberRole);
const response = await authOwnerAgent
.get('/resolve-signup-token')
@ -261,7 +263,7 @@ describe('GET /resolve-signup-token', () => {
test('should return 403 if user quota reached', async () => {
jest.spyOn(Container.get(License), 'isWithinUsersLimit').mockReturnValueOnce(false);
const memberShell = await testDb.createUserShell(globalMemberRole);
const memberShell = await createUserShell(globalMemberRole);
const response = await authOwnerAgent
.get('/resolve-signup-token')
@ -272,7 +274,7 @@ describe('GET /resolve-signup-token', () => {
});
test('should fail with invalid inputs', async () => {
const { id: inviteeId } = await testDb.createUser({ globalRole: globalMemberRole });
const { id: inviteeId } = await createUser({ globalRole: globalMemberRole });
const first = await authOwnerAgent.get('/resolve-signup-token').query({ inviterId: owner.id });
@ -304,7 +306,7 @@ describe('GET /resolve-signup-token', () => {
describe('POST /logout', () => {
test('should log user out', async () => {
const owner = await testDb.createUser({ globalRole: globalOwnerRole });
const owner = await createUser({ globalRole: globalOwnerRole });
const response = await testServer.authAgentFor(owner).post('/logout');

View file

@ -1,6 +1,7 @@
import type { SuperAgentTest } from 'supertest';
import * as testDb from './shared/testDb';
import * as utils from './shared/utils/';
import { getGlobalMemberRole } from './shared/db/roles';
import { createUser } from './shared/db/users';
describe('Auth Middleware', () => {
const testServer = utils.setupTestServer({ endpointGroups: ['me', 'auth', 'owner', 'users'] });
@ -36,8 +37,8 @@ describe('Auth Middleware', () => {
describe('Routes requiring Authorization', () => {
let authMemberAgent: SuperAgentTest;
beforeAll(async () => {
const globalMemberRole = await testDb.getGlobalMemberRole();
const member = await testDb.createUser({ globalRole: globalMemberRole });
const globalMemberRole = await getGlobalMemberRole();
const member = await createUser({ globalRole: globalMemberRole });
authMemberAgent = testServer.authAgentFor(member);
});

View file

@ -1,9 +1,9 @@
import fsp from 'node:fs/promises';
import { Readable } from 'node:stream';
import { BinaryDataService, FileNotFoundError } from 'n8n-core';
import * as testDb from './shared/testDb';
import { mockInstance, setupTestServer } from './shared/utils';
import type { SuperAgentTest } from 'supertest';
import { createOwner } from './shared/db/users';
jest.mock('fs/promises');
@ -16,7 +16,7 @@ let testServer = setupTestServer({ endpointGroups: ['binaryData'] });
let authOwnerAgent: SuperAgentTest;
beforeAll(async () => {
const owner = await testDb.createOwner();
const owner = await createOwner();
authOwnerAgent = testServer.authAgentFor(owner);
});

View file

@ -5,6 +5,7 @@ import { ImportCredentialsCommand } from '@/commands/import/credentials';
import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials';
import * as testDb from '../shared/testDb';
import { mockInstance } from '../shared/utils';
import { getAllCredentials } from '../shared/db/credentials';
beforeAll(async () => {
mockInstance(InternalHooks);
@ -22,7 +23,7 @@ afterAll(async () => {
test('import:credentials should import a credential', async () => {
const config: Config.IConfig = new Config.Config({ root: __dirname });
const before = await testDb.getAllCredentials();
const before = await getAllCredentials();
expect(before.length).toBe(0);
const importer = new ImportCredentialsCommand(
['--input=./test/integration/commands/importCredentials/credentials.json'],
@ -38,7 +39,7 @@ test('import:credentials should import a credential', async () => {
} catch (error) {
expect(error.message).toBe('process.exit');
}
const after = await testDb.getAllCredentials();
const after = await getAllCredentials();
expect(after.length).toBe(1);
expect(after[0].name).toBe('cred-aws-test');
expect(after[0].id).toBe('123');

View file

@ -5,6 +5,7 @@ import { ImportWorkflowsCommand } from '@/commands/import/workflow';
import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials';
import * as testDb from '../shared/testDb';
import { mockInstance } from '../shared/utils/';
import { getAllWorkflows } from '../shared/db/workflows';
beforeAll(async () => {
mockInstance(InternalHooks);
@ -22,7 +23,7 @@ afterAll(async () => {
test('import:workflow should import active workflow and deactivate it', async () => {
const config: Config.IConfig = new Config.Config({ root: __dirname });
const before = await testDb.getAllWorkflows();
const before = await getAllWorkflows();
expect(before.length).toBe(0);
const importer = new ImportWorkflowsCommand(
['--separate', '--input=./test/integration/commands/importWorkflows/separate'],
@ -38,7 +39,7 @@ test('import:workflow should import active workflow and deactivate it', async ()
} catch (error) {
expect(error.message).toBe('process.exit');
}
const after = await testDb.getAllWorkflows();
const after = await getAllWorkflows();
expect(after.length).toBe(2);
expect(after[0].name).toBe('active-workflow');
expect(after[0].active).toBe(false);
@ -49,7 +50,7 @@ test('import:workflow should import active workflow and deactivate it', async ()
test('import:workflow should import active workflow from combined file and deactivate it', async () => {
const config: Config.IConfig = new Config.Config({ root: __dirname });
const before = await testDb.getAllWorkflows();
const before = await getAllWorkflows();
expect(before.length).toBe(0);
const importer = new ImportWorkflowsCommand(
['--input=./test/integration/commands/importWorkflows/combined/combined.json'],
@ -65,7 +66,7 @@ test('import:workflow should import active workflow from combined file and deact
} catch (error) {
expect(error.message).toBe('process.exit');
}
const after = await testDb.getAllWorkflows();
const after = await getAllWorkflows();
expect(after.length).toBe(2);
expect(after[0].name).toBe('active-workflow');
expect(after[0].active).toBe(false);

View file

@ -6,6 +6,8 @@ import { mockInstance } from '../shared/utils/';
import { InternalHooks } from '@/InternalHooks';
import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials';
import { NodeTypes } from '@/NodeTypes';
import { getGlobalOwnerRole } from '../shared/db/roles';
import { createUser } from '../shared/db/users';
let globalOwnerRole: Role;
@ -15,7 +17,7 @@ beforeAll(async () => {
mockInstance(NodeTypes);
await testDb.init();
globalOwnerRole = await testDb.getGlobalOwnerRole();
globalOwnerRole = await getGlobalOwnerRole();
});
beforeEach(async () => {
@ -28,7 +30,7 @@ afterAll(async () => {
// eslint-disable-next-line n8n-local-rules/no-skipped-tests
test.skip('user-management:reset should reset DB to default user state', async () => {
await testDb.createUser({ globalRole: globalOwnerRole });
await createUser({ globalRole: globalOwnerRole });
await Reset.run();

View file

@ -8,7 +8,6 @@ import { Push } from '@/push';
import { CommunityPackagesService } from '@/services/communityPackages.service';
import { COMMUNITY_PACKAGE_VERSION } from './shared/constants';
import * as testDb from './shared/testDb';
import {
mockInstance,
setupTestServer,
@ -16,6 +15,7 @@ import {
mockNode,
mockPackageName,
} from './shared/utils';
import { createOwner } from './shared/db/users';
const communityPackagesService = mockInstance(CommunityPackagesService, {
hasMissingPackages: false,
@ -40,7 +40,7 @@ const parsedNpmPackageName = {
let authAgent: SuperAgentTest;
beforeAll(async () => {
const ownerShell = await testDb.createOwner();
const ownerShell = await createOwner();
authAgent = testServer.authAgentFor(ownerShell);
});

View file

@ -1,13 +1,15 @@
import * as testDb from './shared/testDb';
import * as utils from './shared/utils/';
import { randomCredentialPayload as payload } from './shared/random';
import type { Credentials } from '@/requests';
import type { User } from '@db/entities/User';
import * as testDb from './shared/testDb';
import { setupTestServer } from './shared/utils/';
import { randomCredentialPayload as payload } from './shared/random';
import { saveCredential } from './shared/db/credentials';
import { createMember, createOwner } from './shared/db/users';
import { getCredentialOwnerRole } from './shared/db/roles';
const { any } = expect;
const testServer = utils.setupTestServer({ endpointGroups: ['credentials'] });
const testServer = setupTestServer({ endpointGroups: ['credentials'] });
let owner: User;
let member: User;
@ -15,8 +17,8 @@ let member: User;
beforeEach(async () => {
await testDb.truncate(['SharedCredentials', 'Credentials']);
owner = await testDb.createOwner();
member = await testDb.createMember();
owner = await createOwner();
member = await createMember();
});
type GetAllResponse = { body: { data: Credentials.WithOwnedByAndSharedWith[] } };
@ -24,10 +26,10 @@ type GetAllResponse = { body: { data: Credentials.WithOwnedByAndSharedWith[] } }
describe('GET /credentials', () => {
describe('should return', () => {
test('all credentials for owner', async () => {
const role = await testDb.getCredentialOwnerRole();
const role = await getCredentialOwnerRole();
const { id: id1 } = await testDb.saveCredential(payload(), { user: owner, role });
const { id: id2 } = await testDb.saveCredential(payload(), { user: member, role });
const { id: id1 } = await saveCredential(payload(), { user: owner, role });
const { id: id2 } = await saveCredential(payload(), { user: member, role });
const response: GetAllResponse = await testServer
.authAgentFor(owner)
@ -45,13 +47,13 @@ describe('GET /credentials', () => {
});
test('only own credentials for member', async () => {
const role = await testDb.getCredentialOwnerRole();
const role = await getCredentialOwnerRole();
const firstMember = member;
const secondMember = await testDb.createMember();
const secondMember = await createMember();
const c1 = await testDb.saveCredential(payload(), { user: firstMember, role });
const c2 = await testDb.saveCredential(payload(), { user: secondMember, role });
const c1 = await saveCredential(payload(), { user: firstMember, role });
const c2 = await saveCredential(payload(), { user: secondMember, role });
const response: GetAllResponse = await testServer
.authAgentFor(firstMember)
@ -70,8 +72,8 @@ describe('GET /credentials', () => {
describe('filter', () => {
test('should filter credentials by field: name - full match', async () => {
const role = await testDb.getCredentialOwnerRole();
const savedCred = await testDb.saveCredential(payload(), { user: owner, role });
const role = await getCredentialOwnerRole();
const savedCred = await saveCredential(payload(), { user: owner, role });
const response: GetAllResponse = await testServer
.authAgentFor(owner)
@ -95,8 +97,8 @@ describe('GET /credentials', () => {
});
test('should filter credentials by field: name - partial match', async () => {
const role = await testDb.getCredentialOwnerRole();
const savedCred = await testDb.saveCredential(payload(), { user: owner, role });
const role = await getCredentialOwnerRole();
const savedCred = await saveCredential(payload(), { user: owner, role });
const partialName = savedCred.name.slice(3);
@ -122,9 +124,9 @@ describe('GET /credentials', () => {
});
test('should filter credentials by field: type - full match', async () => {
const role = await testDb.getCredentialOwnerRole();
const role = await getCredentialOwnerRole();
const savedCred = await testDb.saveCredential(payload(), { user: owner, role });
const savedCred = await saveCredential(payload(), { user: owner, role });
const response: GetAllResponse = await testServer
.authAgentFor(owner)
@ -148,9 +150,9 @@ describe('GET /credentials', () => {
});
test('should filter credentials by field: type - partial match', async () => {
const role = await testDb.getCredentialOwnerRole();
const role = await getCredentialOwnerRole();
const savedCred = await testDb.saveCredential(payload(), { user: owner, role });
const savedCred = await saveCredential(payload(), { user: owner, role });
const partialType = savedCred.type.slice(3);
@ -178,10 +180,10 @@ describe('GET /credentials', () => {
describe('select', () => {
test('should select credential field: id', async () => {
const role = await testDb.getCredentialOwnerRole();
const role = await getCredentialOwnerRole();
await testDb.saveCredential(payload(), { user: owner, role });
await testDb.saveCredential(payload(), { user: owner, role });
await saveCredential(payload(), { user: owner, role });
await saveCredential(payload(), { user: owner, role });
const response: GetAllResponse = await testServer
.authAgentFor(owner)
@ -195,10 +197,10 @@ describe('GET /credentials', () => {
});
test('should select credential field: name', async () => {
const role = await testDb.getCredentialOwnerRole();
const role = await getCredentialOwnerRole();
await testDb.saveCredential(payload(), { user: owner, role });
await testDb.saveCredential(payload(), { user: owner, role });
await saveCredential(payload(), { user: owner, role });
await saveCredential(payload(), { user: owner, role });
const response: GetAllResponse = await testServer
.authAgentFor(owner)
@ -212,10 +214,10 @@ describe('GET /credentials', () => {
});
test('should select credential field: type', async () => {
const role = await testDb.getCredentialOwnerRole();
const role = await getCredentialOwnerRole();
await testDb.saveCredential(payload(), { user: owner, role });
await testDb.saveCredential(payload(), { user: owner, role });
await saveCredential(payload(), { user: owner, role });
await saveCredential(payload(), { user: owner, role });
const response: GetAllResponse = await testServer
.authAgentFor(owner)
@ -231,10 +233,10 @@ describe('GET /credentials', () => {
describe('take', () => {
test('should return n credentials or less, without skip', async () => {
const role = await testDb.getCredentialOwnerRole();
const role = await getCredentialOwnerRole();
await testDb.saveCredential(payload(), { user: owner, role });
await testDb.saveCredential(payload(), { user: owner, role });
await saveCredential(payload(), { user: owner, role });
await saveCredential(payload(), { user: owner, role });
const response = await testServer
.authAgentFor(owner)
@ -258,10 +260,10 @@ describe('GET /credentials', () => {
});
test('should return n credentials or less, with skip', async () => {
const role = await testDb.getCredentialOwnerRole();
const role = await getCredentialOwnerRole();
await testDb.saveCredential(payload(), { user: owner, role });
await testDb.saveCredential(payload(), { user: owner, role });
await saveCredential(payload(), { user: owner, role });
await saveCredential(payload(), { user: owner, role });
const response = await testServer
.authAgentFor(owner)

View file

@ -7,10 +7,14 @@ import type { Credentials } from '@/requests';
import * as UserManagementHelpers from '@/UserManagement/UserManagementHelper';
import type { Role } from '@db/entities/Role';
import type { User } from '@db/entities/User';
import { randomCredentialPayload } from './shared/random';
import * as testDb from './shared/testDb';
import type { SaveCredentialFunction } from './shared/types';
import * as utils from './shared/utils/';
import { affixRoleToSaveCredential, shareCredentialWithUsers } from './shared/db/credentials';
import { getCredentialOwnerRole, getGlobalMemberRole, getGlobalOwnerRole } from './shared/db/roles';
import { createManyUsers, createUser, createUserShell } from './shared/db/users';
const sharingSpy = jest.spyOn(UserManagementHelpers, 'isSharingEnabled').mockReturnValue(true);
const testServer = utils.setupTestServer({ endpointGroups: ['credentials'] });
@ -22,16 +26,16 @@ let authOwnerAgent: SuperAgentTest;
let saveCredential: SaveCredentialFunction;
beforeAll(async () => {
const globalOwnerRole = await testDb.getGlobalOwnerRole();
globalMemberRole = await testDb.getGlobalMemberRole();
const credentialOwnerRole = await testDb.getCredentialOwnerRole();
const globalOwnerRole = await getGlobalOwnerRole();
globalMemberRole = await getGlobalMemberRole();
const credentialOwnerRole = await getCredentialOwnerRole();
owner = await testDb.createUser({ globalRole: globalOwnerRole });
member = await testDb.createUser({ globalRole: globalMemberRole });
owner = await createUser({ globalRole: globalOwnerRole });
member = await createUser({ globalRole: globalMemberRole });
authOwnerAgent = testServer.authAgentFor(owner);
saveCredential = testDb.affixRoleToSaveCredential(credentialOwnerRole);
saveCredential = affixRoleToSaveCredential(credentialOwnerRole);
});
beforeEach(async () => {
@ -75,7 +79,7 @@ describe('router should switch based on flag', () => {
// ----------------------------------------
describe('GET /credentials', () => {
test('should return all creds for owner', async () => {
const [member1, member2, member3] = await testDb.createManyUsers(3, {
const [member1, member2, member3] = await createManyUsers(3, {
globalRole: globalMemberRole,
});
@ -83,7 +87,7 @@ describe('GET /credentials', () => {
await saveCredential(randomCredentialPayload(), { user: member1 });
const sharedWith = [member1, member2, member3];
await testDb.shareCredentialWithUsers(savedCredential, sharedWith);
await shareCredentialWithUsers(savedCredential, sharedWith);
const response = await authOwnerAgent.get('/credentials');
@ -139,7 +143,7 @@ describe('GET /credentials', () => {
});
test('should return only relevant creds for member', async () => {
const [member1, member2] = await testDb.createManyUsers(2, {
const [member1, member2] = await createManyUsers(2, {
globalRole: globalMemberRole,
});
@ -148,7 +152,7 @@ describe('GET /credentials', () => {
user: member1,
});
await testDb.shareCredentialWithUsers(savedMemberCredential, [member2]);
await shareCredentialWithUsers(savedMemberCredential, [member2]);
const response = await testServer.authAgentFor(member1).get('/credentials');
@ -215,12 +219,12 @@ describe('GET /credentials/:id', () => {
});
test('should retrieve non-owned cred for owner', async () => {
const [member1, member2] = await testDb.createManyUsers(2, {
const [member1, member2] = await createManyUsers(2, {
globalRole: globalMemberRole,
});
const savedCredential = await saveCredential(randomCredentialPayload(), { user: member1 });
await testDb.shareCredentialWithUsers(savedCredential, [member2]);
await shareCredentialWithUsers(savedCredential, [member2]);
const response1 = await authOwnerAgent.get(`/credentials/${savedCredential.id}`);
@ -254,12 +258,12 @@ describe('GET /credentials/:id', () => {
});
test('should retrieve owned cred for member', async () => {
const [member1, member2, member3] = await testDb.createManyUsers(3, {
const [member1, member2, member3] = await createManyUsers(3, {
globalRole: globalMemberRole,
});
const authMemberAgent = testServer.authAgentFor(member1);
const savedCredential = await saveCredential(randomCredentialPayload(), { user: member1 });
await testDb.shareCredentialWithUsers(savedCredential, [member2, member3]);
await shareCredentialWithUsers(savedCredential, [member2, member3]);
const firstResponse = await authMemberAgent.get(`/credentials/${savedCredential.id}`);
@ -322,12 +326,12 @@ describe('PUT /credentials/:id/share', () => {
test('should share the credential with the provided userIds and unshare it for missing ones', async () => {
const savedCredential = await saveCredential(randomCredentialPayload(), { user: owner });
const [member1, member2, member3, member4, member5] = await testDb.createManyUsers(5, {
const [member1, member2, member3, member4, member5] = await createManyUsers(5, {
globalRole: globalMemberRole,
});
const shareWithIds = [member1.id, member2.id, member3.id];
await testDb.shareCredentialWithUsers(savedCredential, [member4, member5]);
await shareCredentialWithUsers(savedCredential, [member4, member5]);
const response = await authOwnerAgent
.put(`/credentials/${savedCredential.id}/share`)
@ -357,7 +361,7 @@ describe('PUT /credentials/:id/share', () => {
});
test('should share the credential with the provided userIds', async () => {
const [member1, member2, member3] = await testDb.createManyUsers(3, {
const [member1, member2, member3] = await createManyUsers(3, {
globalRole: globalMemberRole,
});
const memberIds = [member1.id, member2.id, member3.id];
@ -412,7 +416,7 @@ describe('PUT /credentials/:id/share', () => {
});
test('should ignore pending sharee', async () => {
const memberShell = await testDb.createUserShell(globalMemberRole);
const memberShell = await createUserShell(globalMemberRole);
const savedCredential = await saveCredential(randomCredentialPayload(), { user: owner });
const response = await authOwnerAgent
@ -459,11 +463,11 @@ describe('PUT /credentials/:id/share', () => {
test('should unshare the credential', async () => {
const savedCredential = await saveCredential(randomCredentialPayload(), { user: owner });
const [member1, member2] = await testDb.createManyUsers(2, {
const [member1, member2] = await createManyUsers(2, {
globalRole: globalMemberRole,
});
await testDb.shareCredentialWithUsers(savedCredential, [member1, member2]);
await shareCredentialWithUsers(savedCredential, [member1, member2]);
const response = await authOwnerAgent
.put(`/credentials/${savedCredential.id}/share`)

View file

@ -6,10 +6,14 @@ import * as UserManagementHelpers from '@/UserManagement/UserManagementHelper';
import type { Credentials } from '@/requests';
import type { Role } from '@db/entities/Role';
import type { User } from '@db/entities/User';
import { randomCredentialPayload, randomName, randomString } from './shared/random';
import * as testDb from './shared/testDb';
import type { SaveCredentialFunction } from './shared/types';
import * as utils from './shared/utils/';
import { affixRoleToSaveCredential } from './shared/db/credentials';
import { getCredentialOwnerRole, getGlobalMemberRole, getGlobalOwnerRole } from './shared/db/roles';
import { createManyUsers, createUser } from './shared/db/users';
// mock that credentialsSharing is not enabled
jest.spyOn(UserManagementHelpers, 'isSharingEnabled').mockReturnValue(false);
@ -24,14 +28,14 @@ let authMemberAgent: SuperAgentTest;
let saveCredential: SaveCredentialFunction;
beforeAll(async () => {
globalOwnerRole = await testDb.getGlobalOwnerRole();
globalMemberRole = await testDb.getGlobalMemberRole();
const credentialOwnerRole = await testDb.getCredentialOwnerRole();
globalOwnerRole = await getGlobalOwnerRole();
globalMemberRole = await getGlobalMemberRole();
const credentialOwnerRole = await getCredentialOwnerRole();
owner = await testDb.createUser({ globalRole: globalOwnerRole });
member = await testDb.createUser({ globalRole: globalMemberRole });
owner = await createUser({ globalRole: globalOwnerRole });
member = await createUser({ globalRole: globalMemberRole });
saveCredential = testDb.affixRoleToSaveCredential(credentialOwnerRole);
saveCredential = affixRoleToSaveCredential(credentialOwnerRole);
authOwnerAgent = testServer.authAgentFor(owner);
authMemberAgent = testServer.authAgentFor(member);
@ -65,7 +69,7 @@ describe('GET /credentials', () => {
});
test('should return only own creds for member', async () => {
const [member1, member2] = await testDb.createManyUsers(2, {
const [member1, member2] = await createManyUsers(2, {
globalRole: globalMemberRole,
});

View file

@ -1,6 +1,5 @@
import type { SuperAgentTest } from 'supertest';
import { SOURCE_CONTROL_API_ROOT } from '@/environments/sourceControl/constants';
import * as testDb from '../shared/testDb';
import * as utils from '../shared/utils/';
import type { User } from '@db/entities/User';
import * as UserManagementHelpers from '@/UserManagement/UserManagementHelper';
@ -10,6 +9,8 @@ import { License } from '@/License';
import { SourceControlPreferencesService } from '@/environments/sourceControl/sourceControlPreferences.service.ee';
import { SourceControlService } from '@/environments/sourceControl/sourceControl.service.ee';
import type { SourceControlledFile } from '@/environments/sourceControl/types/sourceControlledFile';
import { getGlobalMemberRole, getGlobalOwnerRole } from '../shared/db/roles';
import { createUser } from '../shared/db/users';
let authOwnerAgent: SuperAgentTest;
let authMemberAgent: SuperAgentTest;
@ -24,10 +25,10 @@ const testServer = utils.setupTestServer({
});
beforeAll(async () => {
const globalOwnerRole = await testDb.getGlobalOwnerRole();
const globalMemberRole = await testDb.getGlobalMemberRole();
owner = await testDb.createUser({ globalRole: globalOwnerRole });
member = await testDb.createUser({ globalRole: globalMemberRole });
const globalOwnerRole = await getGlobalOwnerRole();
const globalMemberRole = await getGlobalMemberRole();
owner = await createUser({ globalRole: globalOwnerRole });
member = await createUser({ globalRole: globalMemberRole });
authOwnerAgent = testServer.authAgentFor(owner);
authMemberAgent = testServer.authAgentFor(member);

View file

@ -3,8 +3,6 @@ import axios from 'axios';
import syslog from 'syslog-client';
import { v4 as uuid } from 'uuid';
import type { SuperAgentTest } from 'supertest';
import * as utils from './shared/utils';
import * as testDb from './shared/testDb';
import type { Role } from '@db/entities/Role';
import type { User } from '@db/entities/User';
import type {
@ -27,6 +25,10 @@ import type { EventNamesTypes } from '@/eventbus/EventMessageClasses';
import { EventMessageWorkflow } from '@/eventbus/EventMessageClasses/EventMessageWorkflow';
import { EventMessageNode } from '@/eventbus/EventMessageClasses/EventMessageNode';
import * as utils from './shared/utils';
import { getGlobalOwnerRole } from './shared/db/roles';
import { createUser } from './shared/db/users';
jest.unmock('@/eventbus/MessageEventBus/MessageEventBus');
jest.mock('axios');
const mockedAxios = axios as jest.Mocked<typeof axios>;
@ -83,8 +85,8 @@ const testServer = utils.setupTestServer({
});
beforeAll(async () => {
globalOwnerRole = await testDb.getGlobalOwnerRole();
owner = await testDb.createUser({ globalRole: globalOwnerRole });
globalOwnerRole = await getGlobalOwnerRole();
owner = await createUser({ globalRole: globalOwnerRole });
authOwnerAgent = testServer.authAgentFor(owner);
mockedSyslog.createClient.mockImplementation(() => new syslog.Client());

View file

@ -1,8 +1,9 @@
import type { SuperAgentTest } from 'supertest';
import * as utils from './shared/utils/';
import * as testDb from './shared/testDb';
import type { Role } from '@db/entities/Role';
import type { User } from '@db/entities/User';
import { getGlobalOwnerRole } from './shared/db/roles';
import { createUser } from './shared/db/users';
/**
* NOTE: due to issues with mocking the MessageEventBus in multiple tests running in parallel,
@ -20,8 +21,8 @@ const testServer = utils.setupTestServer({
});
beforeAll(async () => {
globalOwnerRole = await testDb.getGlobalOwnerRole();
owner = await testDb.createUser({ globalRole: globalOwnerRole });
globalOwnerRole = await getGlobalOwnerRole();
owner = await createUser({ globalRole: globalOwnerRole });
authOwnerAgent = testServer.authAgentFor(owner);
});

View file

@ -1,19 +1,22 @@
import type { User } from '@/databases/entities/User';
import { createSuccessfulExecution, getAllExecutions } from './shared/db/executions';
import { createOwner } from './shared/db/users';
import { createWorkflow } from './shared/db/workflows';
import * as testDb from './shared/testDb';
import { setupTestServer } from './shared/utils';
import type { User } from '@/databases/entities/User';
let testServer = setupTestServer({ endpointGroups: ['executions'] });
let owner: User;
const saveExecution = async ({ belongingTo }: { belongingTo: User }) => {
const workflow = await testDb.createWorkflow({}, belongingTo);
return testDb.createSuccessfulExecution(workflow);
const workflow = await createWorkflow({}, belongingTo);
return createSuccessfulExecution(workflow);
};
beforeEach(async () => {
await testDb.truncate(['Execution', 'Workflow', 'SharedWorkflow']);
owner = await testDb.createOwner();
owner = await createOwner();
});
describe('POST /executions/delete', () => {
@ -32,7 +35,7 @@ describe('POST /executions/delete', () => {
.send({ ids: [execution.id] })
.expect(200);
const executions = await testDb.getAllExecutions();
const executions = await getAllExecutions();
expect(executions).toHaveLength(0);
});

View file

@ -19,6 +19,8 @@ import * as testDb from './../shared/testDb';
import * as utils from '../shared/utils/';
import Container from 'typedi';
import { Cipher } from 'n8n-core';
import { getGlobalMemberRole, getGlobalOwnerRole } from '../shared/db/roles';
import { createLdapUser, createUser, getAllUsers, getLdapIdentities } from '../shared/db/users';
jest.mock('@/telemetry');
@ -46,11 +48,14 @@ const testServer = utils.setupTestServer({
});
beforeAll(async () => {
const [globalOwnerRole, fetchedGlobalMemberRole] = await testDb.getAllRoles();
const [globalOwnerRole, fetchedGlobalMemberRole] = await Promise.all([
getGlobalOwnerRole(),
getGlobalMemberRole(),
]);
globalMemberRole = fetchedGlobalMemberRole;
owner = await testDb.createUser({ globalRole: globalOwnerRole, password: 'password' });
owner = await createUser({ globalRole: globalOwnerRole, password: 'password' });
authOwnerAgent = testServer.authAgentFor(owner);
defaultLdapConfig.bindingAdminPassword = Container.get(Cipher).encrypt(
@ -90,7 +95,7 @@ const createLdapConfig = async (attributes: Partial<LdapConfig> = {}): Promise<L
};
test('Member role should not be able to access ldap routes', async () => {
const member = await testDb.createUser({ globalRole: globalMemberRole });
const member = await createUser({ globalRole: globalMemberRole });
const authAgent = testServer.authAgentFor(member);
await authAgent.get('/ldap/config').expect(403);
await authAgent.put('/ldap/config').expect(403);
@ -162,7 +167,7 @@ describe('PUT /ldap/config', () => {
const ldapConfig = await createLdapConfig();
LdapManager.updateConfig(ldapConfig);
const member = await testDb.createLdapUser({ globalRole: globalMemberRole }, uniqueId());
const member = await createLdapUser({ globalRole: globalMemberRole }, uniqueId());
const configuration = ldapConfig;
@ -170,7 +175,7 @@ describe('PUT /ldap/config', () => {
await authOwnerAgent.put('/ldap/config').send({ ...configuration, loginEnabled: false });
const emailUser = await Db.collections.User.findOneByOrFail({ id: member.id });
const localLdapIdentities = await testDb.getLdapIdentities();
const localLdapIdentities = await getLdapIdentities();
expect(getCurrentAuthenticationMethod()).toBe('email');
expect(emailUser.email).toBe(member.email);
@ -272,7 +277,7 @@ describe('POST /ldap/sync', () => {
const ldapUserEmail = randomEmail();
const ldapUserId = uniqueId();
const member = await testDb.createLdapUser(
const member = await createLdapUser(
{ globalRole: globalMemberRole, email: ldapUserEmail },
ldapUserId,
);
@ -290,7 +295,7 @@ describe('POST /ldap/sync', () => {
expect(synchronization.updated).toBe(1);
// Make sure the changes in the "LDAP server" were not persisted in the database
const localLdapIdentities = await testDb.getLdapIdentities();
const localLdapIdentities = await getLdapIdentities();
const localLdapUsers = localLdapIdentities.map(({ user }) => user);
expect(localLdapUsers.length).toBe(1);
expect(localLdapUsers[0].id).toBe(member.id);
@ -301,7 +306,7 @@ describe('POST /ldap/sync', () => {
const ldapUserEmail = randomEmail();
const ldapUserId = uniqueId();
const member = await testDb.createLdapUser(
const member = await createLdapUser(
{ globalRole: globalMemberRole, email: ldapUserEmail },
ldapUserId,
);
@ -311,7 +316,7 @@ describe('POST /ldap/sync', () => {
expect(synchronization.disabled).toBe(1);
// Make sure the changes in the "LDAP server" were not persisted in the database
const localLdapIdentities = await testDb.getLdapIdentities();
const localLdapIdentities = await getLdapIdentities();
const localLdapUsers = localLdapIdentities.map(({ user }) => user);
expect(localLdapUsers.length).toBe(1);
expect(localLdapUsers[0].id).toBe(member.id);
@ -355,7 +360,7 @@ describe('POST /ldap/sync', () => {
expect(synchronization.created).toBe(1);
// Make sure the changes in the "LDAP server" were persisted in the database
const allUsers = await testDb.getAllUsers();
const allUsers = await getAllUsers();
expect(allUsers.length).toBe(2);
const ownerUser = allUsers.find((u) => u.email === owner.email)!;
@ -366,7 +371,7 @@ describe('POST /ldap/sync', () => {
expect(memberUser.lastName).toBe(ldapUser.sn);
expect(memberUser.firstName).toBe(ldapUser.givenName);
const authIdentities = await testDb.getLdapIdentities();
const authIdentities = await getLdapIdentities();
expect(authIdentities.length).toBe(1);
expect(authIdentities[0].providerId).toBe(ldapUser.uid);
expect(authIdentities[0].providerType).toBe('ldap');
@ -381,7 +386,7 @@ describe('POST /ldap/sync', () => {
uid: uniqueId(),
};
await testDb.createLdapUser(
await createLdapUser(
{
globalRole: globalMemberRole,
email: ldapUser.mail,
@ -395,7 +400,7 @@ describe('POST /ldap/sync', () => {
expect(synchronization.updated).toBe(1);
// Make sure the changes in the "LDAP server" were persisted in the database
const localLdapIdentities = await testDb.getLdapIdentities();
const localLdapIdentities = await getLdapIdentities();
const localLdapUsers = localLdapIdentities.map(({ user }) => user);
expect(localLdapUsers.length).toBe(1);
@ -414,7 +419,7 @@ describe('POST /ldap/sync', () => {
uid: uniqueId(),
};
await testDb.createLdapUser(
await createLdapUser(
{
globalRole: globalMemberRole,
email: ldapUser.mail,
@ -428,7 +433,7 @@ describe('POST /ldap/sync', () => {
expect(synchronization.disabled).toBe(1);
// Make sure the changes in the "LDAP server" were persisted in the database
const allUsers = await testDb.getAllUsers();
const allUsers = await getAllUsers();
expect(allUsers.length).toBe(2);
const ownerUser = allUsers.find((u) => u.email === owner.email)!;
@ -440,12 +445,12 @@ describe('POST /ldap/sync', () => {
expect(memberUser.firstName).toBe(ldapUser.givenName);
expect(memberUser.disabled).toBe(true);
const authIdentities = await testDb.getLdapIdentities();
const authIdentities = await getLdapIdentities();
expect(authIdentities.length).toBe(0);
});
test('should remove user instance access once the user is disabled during synchronization', async () => {
const member = await testDb.createLdapUser({ globalRole: globalMemberRole }, uniqueId());
const member = await createLdapUser({ globalRole: globalMemberRole }, uniqueId());
jest.spyOn(LdapService.prototype, 'searchWithAdminBinding').mockResolvedValue([]);
@ -499,7 +504,7 @@ describe('POST /login', () => {
expect(response.headers['set-cookie'][0] as string).toContain('n8n-auth=');
// Make sure the changes in the "LDAP server" were persisted in the database
const localLdapIdentities = await testDb.getLdapIdentities();
const localLdapIdentities = await getLdapIdentities();
const localLdapUsers = localLdapIdentities.map(({ user }) => user);
expect(localLdapUsers.length).toBe(1);
@ -530,7 +535,7 @@ describe('POST /login', () => {
uid: uniqueId(),
};
await testDb.createLdapUser(
await createLdapUser(
{
globalRole: globalMemberRole,
email: ldapUser.mail,
@ -565,7 +570,7 @@ describe('POST /login', () => {
uid: uniqueId(),
};
await testDb.createUser({
await createUser({
globalRole: globalMemberRole,
email: ldapUser.mail,
firstName: ldapUser.givenName,
@ -581,7 +586,7 @@ describe('Instance owner should able to delete LDAP users', () => {
const ldapConfig = await createLdapConfig();
LdapManager.updateConfig(ldapConfig);
const member = await testDb.createLdapUser({ globalRole: globalMemberRole }, uniqueId());
const member = await createLdapUser({ globalRole: globalMemberRole }, uniqueId());
await authOwnerAgent.post(`/users/${member.id}`);
});
@ -590,7 +595,7 @@ describe('Instance owner should able to delete LDAP users', () => {
const ldapConfig = await createLdapConfig();
LdapManager.updateConfig(ldapConfig);
const member = await testDb.createLdapUser({ globalRole: globalMemberRole }, uniqueId());
const member = await createLdapUser({ globalRole: globalMemberRole }, uniqueId());
// delete the LDAP member and transfer its workflows/credentials to instance owner
await authOwnerAgent.post(`/users/${member.id}?transferId=${owner.id}`);

View file

@ -5,6 +5,8 @@ import type { ILicensePostResponse, ILicenseReadResponse } from '@/Interfaces';
import { License } from '@/License';
import * as testDb from './shared/testDb';
import * as utils from './shared/utils/';
import { getGlobalMemberRole, getGlobalOwnerRole } from './shared/db/roles';
import { createUserShell } from './shared/db/users';
const MOCK_SERVER_URL = 'https://server.com/v1';
const MOCK_RENEW_OFFSET = 259200;
@ -17,10 +19,10 @@ let authMemberAgent: SuperAgentTest;
const testServer = utils.setupTestServer({ endpointGroups: ['license'] });
beforeAll(async () => {
const globalOwnerRole = await testDb.getGlobalOwnerRole();
const globalMemberRole = await testDb.getGlobalMemberRole();
owner = await testDb.createUserShell(globalOwnerRole);
member = await testDb.createUserShell(globalMemberRole);
const globalOwnerRole = await getGlobalOwnerRole();
const globalMemberRole = await getGlobalMemberRole();
owner = await createUserShell(globalOwnerRole);
member = await createUserShell(globalMemberRole);
authOwnerAgent = testServer.authAgentFor(owner);
authMemberAgent = testServer.authAgentFor(member);

View file

@ -14,6 +14,8 @@ import {
} from './shared/random';
import * as testDb from './shared/testDb';
import * as utils from './shared/utils/';
import { getGlobalMemberRole, getGlobalOwnerRole } from './shared/db/roles';
import { addApiKey, createUser, createUserShell } from './shared/db/users';
const testServer = utils.setupTestServer({ endpointGroups: ['me'] });
@ -21,8 +23,8 @@ let globalOwnerRole: Role;
let globalMemberRole: Role;
beforeAll(async () => {
globalOwnerRole = await testDb.getGlobalOwnerRole();
globalMemberRole = await testDb.getGlobalMemberRole();
globalOwnerRole = await getGlobalOwnerRole();
globalMemberRole = await getGlobalMemberRole();
});
beforeEach(async () => {
@ -34,8 +36,8 @@ describe('Owner shell', () => {
let authOwnerShellAgent: SuperAgentTest;
beforeEach(async () => {
ownerShell = await testDb.createUserShell(globalOwnerRole);
await testDb.addApiKey(ownerShell);
ownerShell = await createUserShell(globalOwnerRole);
await addApiKey(ownerShell);
authOwnerShellAgent = testServer.authAgentFor(ownerShell);
});
@ -172,7 +174,7 @@ describe('Member', () => {
let authMemberAgent: SuperAgentTest;
beforeEach(async () => {
member = await testDb.createUser({
member = await createUser({
password: memberPassword,
globalRole: globalMemberRole,
apiKey: randomApiKey(),
@ -314,7 +316,7 @@ describe('Owner', () => {
});
test('PATCH /me should succeed with valid inputs', async () => {
const owner = await testDb.createUser({ globalRole: globalOwnerRole });
const owner = await createUser({ globalRole: globalOwnerRole });
const authOwnerAgent = testServer.authAgentFor(owner);
for (const validPayload of VALID_PATCH_ME_PAYLOADS) {

View file

@ -9,6 +9,7 @@ import { UserService } from '@/services/user.service';
import { randomDigit, randomString, randomValidPassword, uniqueId } from '../shared/random';
import * as testDb from '../shared/testDb';
import * as utils from '../shared/utils';
import { createUser, createUserWithMfaEnabled } from '../shared/db/users';
jest.mock('@/telemetry');
@ -22,7 +23,7 @@ const testServer = utils.setupTestServer({
beforeEach(async () => {
await testDb.truncate(['User']);
owner = await testDb.createUser({ globalRole: globalOwnerRole });
owner = await createUser({ globalRole: globalOwnerRole });
config.set('userManagement.disabled', false);
});
@ -174,7 +175,7 @@ describe('Enable MFA setup', () => {
describe('Disable MFA setup', () => {
test('POST /disable should disable login with MFA', async () => {
const { user } = await testDb.createUserWithMfaEnabled();
const { user } = await createUserWithMfaEnabled();
const response = await testServer.authAgentFor(user).delete('/mfa/disable');
@ -193,7 +194,7 @@ describe('Disable MFA setup', () => {
describe('Change password with MFA enabled', () => {
test('PATCH /me/password should fail due to missing MFA token', async () => {
const { user, rawPassword } = await testDb.createUserWithMfaEnabled();
const { user, rawPassword } = await createUserWithMfaEnabled();
const newPassword = randomPassword();
@ -206,7 +207,7 @@ describe('Change password with MFA enabled', () => {
});
test('POST /change-password should fail due to missing MFA token', async () => {
await testDb.createUserWithMfaEnabled();
await createUserWithMfaEnabled();
const newPassword = randomValidPassword();
@ -220,7 +221,7 @@ describe('Change password with MFA enabled', () => {
});
test('POST /change-password should fail due to invalid MFA token', async () => {
await testDb.createUserWithMfaEnabled();
await createUserWithMfaEnabled();
const newPassword = randomValidPassword();
@ -236,7 +237,7 @@ describe('Change password with MFA enabled', () => {
});
test('POST /change-password should update password', async () => {
const { user, rawSecret } = await testDb.createUserWithMfaEnabled();
const { user, rawSecret } = await createUserWithMfaEnabled();
const newPassword = randomValidPassword();
@ -272,7 +273,7 @@ describe('Login', () => {
test('POST /login with email/password should succeed when mfa is disabled', async () => {
const password = randomPassword();
const user = await testDb.createUser({ password });
const user = await createUser({ password });
const response = await testServer.authlessAgent
.post('/login')
@ -303,7 +304,7 @@ describe('Login', () => {
});
test('POST /login with email/password should fail when mfa is enabled', async () => {
const { user, rawPassword } = await testDb.createUserWithMfaEnabled();
const { user, rawPassword } = await createUserWithMfaEnabled();
const response = await testServer.authlessAgent
.post('/login')
@ -314,7 +315,7 @@ describe('Login', () => {
describe('Login with MFA token', () => {
test('POST /login should fail due to invalid MFA token', async () => {
const { user, rawPassword } = await testDb.createUserWithMfaEnabled();
const { user, rawPassword } = await createUserWithMfaEnabled();
const response = await testServer.authlessAgent
.post('/login')
@ -324,7 +325,7 @@ describe('Login', () => {
});
test('POST /login should fail due two MFA step needed', async () => {
const { user, rawPassword } = await testDb.createUserWithMfaEnabled();
const { user, rawPassword } = await createUserWithMfaEnabled();
const response = await testServer.authlessAgent
.post('/login')
@ -335,7 +336,7 @@ describe('Login', () => {
});
test('POST /login should succeed with MFA token', async () => {
const { user, rawSecret, rawPassword } = await testDb.createUserWithMfaEnabled();
const { user, rawSecret, rawPassword } = await createUserWithMfaEnabled();
const token = new TOTPService().generateTOTP(rawSecret);
@ -352,7 +353,7 @@ describe('Login', () => {
describe('Login with recovery code', () => {
test('POST /login should fail due to invalid MFA recovery code', async () => {
const { user, rawPassword } = await testDb.createUserWithMfaEnabled();
const { user, rawPassword } = await createUserWithMfaEnabled();
const response = await testServer.authlessAgent
.post('/login')
@ -362,7 +363,7 @@ describe('Login', () => {
});
test('POST /login should succeed with MFA recovery code', async () => {
const { user, rawPassword, rawRecoveryCodes } = await testDb.createUserWithMfaEnabled();
const { user, rawPassword, rawRecoveryCodes } = await createUserWithMfaEnabled();
const response = await testServer.authlessAgent
.post('/login')
@ -385,7 +386,7 @@ describe('Login', () => {
});
test('POST /login with MFA recovery code should update hasRecoveryCodesLeft property', async () => {
const { user, rawPassword, rawRecoveryCodes } = await testDb.createUserWithMfaEnabled({
const { user, rawPassword, rawRecoveryCodes } = await createUserWithMfaEnabled({
numberOfRecoveryCodes: 1,
});

View file

@ -13,6 +13,8 @@ import {
} from './shared/random';
import * as testDb from './shared/testDb';
import * as utils from './shared/utils/';
import { getGlobalOwnerRole } from './shared/db/roles';
import { createUserShell } from './shared/db/users';
const testServer = utils.setupTestServer({ endpointGroups: ['owner'] });
@ -21,11 +23,11 @@ let ownerShell: User;
let authOwnerShellAgent: SuperAgentTest;
beforeAll(async () => {
globalOwnerRole = await testDb.getGlobalOwnerRole();
globalOwnerRole = await getGlobalOwnerRole();
});
beforeEach(async () => {
ownerShell = await testDb.createUserShell(globalOwnerRole);
ownerShell = await createUserShell(globalOwnerRole);
authOwnerShellAgent = testServer.authAgentFor(ownerShell);
config.set('userManagement.isInstanceOwnerSetUp', false);
});

View file

@ -23,6 +23,8 @@ import {
randomValidPassword,
} from './shared/random';
import * as testDb from './shared/testDb';
import { getGlobalMemberRole, getGlobalOwnerRole } from './shared/db/roles';
import { createUser } from './shared/db/users';
config.set('userManagement.jwtSecret', randomString(5, 10));
@ -38,14 +40,14 @@ const jwtService = Container.get(JwtService);
let userService: UserService;
beforeAll(async () => {
globalOwnerRole = await testDb.getGlobalOwnerRole();
globalMemberRole = await testDb.getGlobalMemberRole();
globalOwnerRole = await getGlobalOwnerRole();
globalMemberRole = await getGlobalMemberRole();
});
beforeEach(async () => {
await testDb.truncate(['User']);
owner = await testDb.createUser({ globalRole: globalOwnerRole });
member = await testDb.createUser({ globalRole: globalMemberRole });
owner = await createUser({ globalRole: globalOwnerRole });
member = await createUser({ globalRole: globalMemberRole });
externalHooks.run.mockReset();
jest.replaceProperty(mailer, 'isEmailSetUp', true);
userService = Container.get(UserService);
@ -53,7 +55,7 @@ beforeEach(async () => {
describe('POST /forgot-password', () => {
test('should send password reset email', async () => {
const member = await testDb.createUser({
const member = await createUser({
email: 'test@test.com',
globalRole: globalMemberRole,
});
@ -79,7 +81,7 @@ describe('POST /forgot-password', () => {
test('should fail if SAML is authentication method', async () => {
await setCurrentAuthenticationMethod('saml');
const member = await testDb.createUser({
const member = await createUser({
email: 'test@test.com',
globalRole: globalMemberRole,
});

View file

@ -1,21 +1,24 @@
import config from '@/config';
import * as Db from '@/Db';
import { BinaryDataService } from 'n8n-core';
import type { ExecutionStatus } from 'n8n-workflow';
import * as testDb from './shared/testDb';
import type { ExecutionStatus } from 'n8n-workflow';
import type { ExecutionEntity } from '@/databases/entities/ExecutionEntity';
import type { ExecutionEntity } from '@db/entities/ExecutionEntity';
import type { WorkflowEntity } from '@db/entities/WorkflowEntity';
import { TIME } from '@/constants';
import { PruningService } from '@/services/pruning.service';
import { BinaryDataService } from 'n8n-core';
import { Logger } from '@/Logger';
import { mockInstance } from './shared/utils';
import { createWorkflow } from './shared/db/workflows';
import { createExecution, createSuccessfulExecution } from './shared/db/executions';
describe('softDeleteOnPruningCycle()', () => {
let pruningService: PruningService;
const now = new Date();
const yesterday = new Date(Date.now() - TIME.DAY);
let workflow: Awaited<ReturnType<typeof testDb.createWorkflow>>;
let workflow: WorkflowEntity;
beforeAll(async () => {
await testDb.init();
@ -26,7 +29,7 @@ describe('softDeleteOnPruningCycle()', () => {
mockInstance(BinaryDataService),
);
workflow = await testDb.createWorkflow();
workflow = await createWorkflow();
});
beforeEach(async () => {
@ -56,9 +59,9 @@ describe('softDeleteOnPruningCycle()', () => {
test('should mark as deleted based on EXECUTIONS_DATA_PRUNE_MAX_COUNT', async () => {
const executions = [
await testDb.createSuccessfulExecution(workflow),
await testDb.createSuccessfulExecution(workflow),
await testDb.createSuccessfulExecution(workflow),
await createSuccessfulExecution(workflow),
await createSuccessfulExecution(workflow),
await createSuccessfulExecution(workflow),
];
await pruningService.softDeleteOnPruningCycle();
@ -73,11 +76,11 @@ describe('softDeleteOnPruningCycle()', () => {
test('should not re-mark already marked executions', async () => {
const executions = [
await testDb.createExecution(
await createExecution(
{ status: 'success', finished: true, startedAt: now, stoppedAt: now, deletedAt: now },
workflow,
),
await testDb.createSuccessfulExecution(workflow),
await createSuccessfulExecution(workflow),
];
await pruningService.softDeleteOnPruningCycle();
@ -98,8 +101,8 @@ describe('softDeleteOnPruningCycle()', () => {
['success', { finished: true, startedAt: now, stoppedAt: now }],
])('should prune %s executions', async (status, attributes) => {
const executions = [
await testDb.createExecution({ status, ...attributes }, workflow),
await testDb.createSuccessfulExecution(workflow),
await createExecution({ status, ...attributes }, workflow),
await createSuccessfulExecution(workflow),
];
await pruningService.softDeleteOnPruningCycle();
@ -117,8 +120,8 @@ describe('softDeleteOnPruningCycle()', () => {
['waiting', { startedAt: now, stoppedAt: now, waitTill: now }],
])('should not prune %s executions', async (status, attributes) => {
const executions = [
await testDb.createExecution({ status, ...attributes }, workflow),
await testDb.createSuccessfulExecution(workflow),
await createExecution({ status, ...attributes }, workflow),
await createSuccessfulExecution(workflow),
];
await pruningService.softDeleteOnPruningCycle();
@ -139,11 +142,11 @@ describe('softDeleteOnPruningCycle()', () => {
test('should mark as deleted based on EXECUTIONS_DATA_MAX_AGE', async () => {
const executions = [
await testDb.createExecution(
await createExecution(
{ finished: true, startedAt: yesterday, stoppedAt: yesterday, status: 'success' },
workflow,
),
await testDb.createExecution(
await createExecution(
{ finished: true, startedAt: now, stoppedAt: now, status: 'success' },
workflow,
),
@ -160,7 +163,7 @@ describe('softDeleteOnPruningCycle()', () => {
test('should not re-mark already marked executions', async () => {
const executions = [
await testDb.createExecution(
await createExecution(
{
status: 'success',
finished: true,
@ -170,7 +173,7 @@ describe('softDeleteOnPruningCycle()', () => {
},
workflow,
),
await testDb.createSuccessfulExecution(workflow),
await createSuccessfulExecution(workflow),
];
await pruningService.softDeleteOnPruningCycle();
@ -190,7 +193,7 @@ describe('softDeleteOnPruningCycle()', () => {
['failed', { startedAt: yesterday, stoppedAt: yesterday }],
['success', { finished: true, startedAt: yesterday, stoppedAt: yesterday }],
])('should prune %s executions', async (status, attributes) => {
const execution = await testDb.createExecution({ status, ...attributes }, workflow);
const execution = await createExecution({ status, ...attributes }, workflow);
await pruningService.softDeleteOnPruningCycle();
@ -206,8 +209,8 @@ describe('softDeleteOnPruningCycle()', () => {
['waiting', { startedAt: yesterday, stoppedAt: yesterday, waitTill: yesterday }],
])('should not prune %s executions', async (status, attributes) => {
const executions = [
await testDb.createExecution({ status, ...attributes }, workflow),
await testDb.createSuccessfulExecution(workflow),
await createExecution({ status, ...attributes }, workflow),
await createSuccessfulExecution(workflow),
];
await pruningService.softDeleteOnPruningCycle();

View file

@ -7,6 +7,9 @@ import { randomApiKey, randomName, randomString } from '../shared/random';
import * as utils from '../shared/utils/';
import type { CredentialPayload, SaveCredentialFunction } from '../shared/types';
import * as testDb from '../shared/testDb';
import { affixRoleToSaveCredential } from '../shared/db/credentials';
import { getAllRoles } from '../shared/db/roles';
import { addApiKey, createUser, createUserShell } from '../shared/db/users';
let globalMemberRole: Role;
let credentialOwnerRole: Role;
@ -21,18 +24,18 @@ const testServer = utils.setupTestServer({ endpointGroups: ['publicApi'] });
beforeAll(async () => {
const [globalOwnerRole, fetchedGlobalMemberRole, _, fetchedCredentialOwnerRole] =
await testDb.getAllRoles();
await getAllRoles();
globalMemberRole = fetchedGlobalMemberRole;
credentialOwnerRole = fetchedCredentialOwnerRole;
owner = await testDb.addApiKey(await testDb.createUserShell(globalOwnerRole));
member = await testDb.createUser({ globalRole: globalMemberRole, apiKey: randomApiKey() });
owner = await addApiKey(await createUserShell(globalOwnerRole));
member = await createUser({ globalRole: globalMemberRole, apiKey: randomApiKey() });
authOwnerAgent = testServer.publicApiAgentFor(owner);
authMemberAgent = testServer.publicApiAgentFor(member);
saveCredential = testDb.affixRoleToSaveCredential(credentialOwnerRole);
saveCredential = affixRoleToSaveCredential(credentialOwnerRole);
await utils.initCredentialsTypes();
});
@ -150,7 +153,7 @@ describe('DELETE /credentials/:id', () => {
});
test('should delete owned cred for member but leave others untouched', async () => {
const anotherMember = await testDb.createUser({
const anotherMember = await createUser({
globalRole: globalMemberRole,
apiKey: randomApiKey(),
});

View file

@ -5,6 +5,19 @@ import type { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
import { randomApiKey } from '../shared/random';
import * as utils from '../shared/utils/';
import * as testDb from '../shared/testDb';
import { getGlobalMemberRole, getGlobalOwnerRole } from '../shared/db/roles';
import { createUser } from '../shared/db/users';
import {
createManyWorkflows,
createWorkflow,
shareWorkflowWithUsers,
} from '../shared/db/workflows';
import {
createErrorExecution,
createManyExecutions,
createSuccessfulExecution,
createWaitingExecution,
} from '../shared/db/executions';
let owner: User;
let user1: User;
@ -17,11 +30,11 @@ let workflowRunner: ActiveWorkflowRunner;
const testServer = utils.setupTestServer({ endpointGroups: ['publicApi'] });
beforeAll(async () => {
const globalOwnerRole = await testDb.getGlobalOwnerRole();
const globalUserRole = await testDb.getGlobalMemberRole();
owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
user1 = await testDb.createUser({ globalRole: globalUserRole, apiKey: randomApiKey() });
user2 = await testDb.createUser({ globalRole: globalUserRole, apiKey: randomApiKey() });
const globalOwnerRole = await getGlobalOwnerRole();
const globalUserRole = await getGlobalMemberRole();
owner = await createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
user1 = await createUser({ globalRole: globalUserRole, apiKey: randomApiKey() });
user2 = await createUser({ globalRole: globalUserRole, apiKey: randomApiKey() });
// TODO: mock BinaryDataService instead
await utils.initBinaryDataService();
@ -62,9 +75,9 @@ describe('GET /executions/:id', () => {
test('should fail due to invalid API Key', testWithAPIKey('get', '/executions/1', 'abcXYZ'));
test('owner should be able to get an execution owned by him', async () => {
const workflow = await testDb.createWorkflow({}, owner);
const workflow = await createWorkflow({}, owner);
const execution = await testDb.createSuccessfulExecution(workflow);
const execution = await createSuccessfulExecution(workflow);
const response = await authOwnerAgent.get(`/executions/${execution.id}`);
@ -94,8 +107,8 @@ describe('GET /executions/:id', () => {
});
test('owner should be able to read executions of other users', async () => {
const workflow = await testDb.createWorkflow({}, user1);
const execution = await testDb.createSuccessfulExecution(workflow);
const workflow = await createWorkflow({}, user1);
const execution = await createSuccessfulExecution(workflow);
const response = await authOwnerAgent.get(`/executions/${execution.id}`);
@ -103,8 +116,8 @@ describe('GET /executions/:id', () => {
});
test('member should be able to fetch his own executions', async () => {
const workflow = await testDb.createWorkflow({}, user1);
const execution = await testDb.createSuccessfulExecution(workflow);
const workflow = await createWorkflow({}, user1);
const execution = await createSuccessfulExecution(workflow);
const response = await authUser1Agent.get(`/executions/${execution.id}`);
@ -112,9 +125,9 @@ describe('GET /executions/:id', () => {
});
test('member should not get an execution of another user without the workflow being shared', async () => {
const workflow = await testDb.createWorkflow({}, owner);
const workflow = await createWorkflow({}, owner);
const execution = await testDb.createSuccessfulExecution(workflow);
const execution = await createSuccessfulExecution(workflow);
const response = await authUser1Agent.get(`/executions/${execution.id}`);
@ -122,11 +135,11 @@ describe('GET /executions/:id', () => {
});
test('member should be able to fetch executions of workflows shared with him', async () => {
const workflow = await testDb.createWorkflow({}, user1);
const workflow = await createWorkflow({}, user1);
const execution = await testDb.createSuccessfulExecution(workflow);
const execution = await createSuccessfulExecution(workflow);
await testDb.shareWorkflowWithUsers(workflow, [user2]);
await shareWorkflowWithUsers(workflow, [user2]);
const response = await authUser2Agent.get(`/executions/${execution.id}`);
@ -140,8 +153,8 @@ describe('DELETE /executions/:id', () => {
test('should fail due to invalid API Key', testWithAPIKey('delete', '/executions/1', 'abcXYZ'));
test('should delete an execution', async () => {
const workflow = await testDb.createWorkflow({}, owner);
const execution = await testDb.createSuccessfulExecution(workflow);
const workflow = await createWorkflow({}, owner);
const execution = await createSuccessfulExecution(workflow);
const response = await authOwnerAgent.delete(`/executions/${execution.id}`);
@ -179,11 +192,11 @@ describe('GET /executions', () => {
test('should fail due to invalid API Key', testWithAPIKey('get', '/executions', 'abcXYZ'));
test('should retrieve all successful executions', async () => {
const workflow = await testDb.createWorkflow({}, owner);
const workflow = await createWorkflow({}, owner);
const successfulExecution = await testDb.createSuccessfulExecution(workflow);
const successfulExecution = await createSuccessfulExecution(workflow);
await testDb.createErrorExecution(workflow);
await createErrorExecution(workflow);
const response = await authOwnerAgent.get('/executions').query({
status: 'success',
@ -219,13 +232,13 @@ describe('GET /executions', () => {
// failing on Postgres and MySQL - ref: https://github.com/n8n-io/n8n/pull/3834
// eslint-disable-next-line n8n-local-rules/no-skipped-tests
test.skip('should paginate two executions', async () => {
const workflow = await testDb.createWorkflow({}, owner);
const workflow = await createWorkflow({}, owner);
const firstSuccessfulExecution = await testDb.createSuccessfulExecution(workflow);
const firstSuccessfulExecution = await createSuccessfulExecution(workflow);
const secondSuccessfulExecution = await testDb.createSuccessfulExecution(workflow);
const secondSuccessfulExecution = await createSuccessfulExecution(workflow);
await testDb.createErrorExecution(workflow);
await createErrorExecution(workflow);
const firstExecutionResponse = await authOwnerAgent.get('/executions').query({
status: 'success',
@ -275,11 +288,11 @@ describe('GET /executions', () => {
});
test('should retrieve all error executions', async () => {
const workflow = await testDb.createWorkflow({}, owner);
const workflow = await createWorkflow({}, owner);
await testDb.createSuccessfulExecution(workflow);
await createSuccessfulExecution(workflow);
const errorExecution = await testDb.createErrorExecution(workflow);
const errorExecution = await createErrorExecution(workflow);
const response = await authOwnerAgent.get('/executions').query({
status: 'error',
@ -313,13 +326,13 @@ describe('GET /executions', () => {
});
test('should return all waiting executions', async () => {
const workflow = await testDb.createWorkflow({}, owner);
const workflow = await createWorkflow({}, owner);
await testDb.createSuccessfulExecution(workflow);
await createSuccessfulExecution(workflow);
await testDb.createErrorExecution(workflow);
await createErrorExecution(workflow);
const waitingExecution = await testDb.createWaitingExecution(workflow);
const waitingExecution = await createWaitingExecution(workflow);
const response = await authOwnerAgent.get('/executions').query({
status: 'waiting',
@ -353,14 +366,10 @@ describe('GET /executions', () => {
});
test('should retrieve all executions of specific workflow', async () => {
const [workflow, workflow2] = await testDb.createManyWorkflows(2, {}, owner);
const [workflow, workflow2] = await createManyWorkflows(2, {}, owner);
const savedExecutions = await testDb.createManyExecutions(
2,
workflow,
testDb.createSuccessfulExecution,
);
await testDb.createManyExecutions(2, workflow2, testDb.createSuccessfulExecution);
const savedExecutions = await createManyExecutions(2, workflow, createSuccessfulExecution);
await createManyExecutions(2, workflow2, createSuccessfulExecution);
const response = await authOwnerAgent.get('/executions').query({
workflowId: workflow.id,
@ -396,21 +405,13 @@ describe('GET /executions', () => {
});
test('owner should retrieve all executions regardless of ownership', async () => {
const [firstWorkflowForUser1, secondWorkflowForUser1] = await testDb.createManyWorkflows(
2,
{},
user1,
);
await testDb.createManyExecutions(2, firstWorkflowForUser1, testDb.createSuccessfulExecution);
await testDb.createManyExecutions(2, secondWorkflowForUser1, testDb.createSuccessfulExecution);
const [firstWorkflowForUser1, secondWorkflowForUser1] = await createManyWorkflows(2, {}, user1);
await createManyExecutions(2, firstWorkflowForUser1, createSuccessfulExecution);
await createManyExecutions(2, secondWorkflowForUser1, createSuccessfulExecution);
const [firstWorkflowForUser2, secondWorkflowForUser2] = await testDb.createManyWorkflows(
2,
{},
user2,
);
await testDb.createManyExecutions(2, firstWorkflowForUser2, testDb.createSuccessfulExecution);
await testDb.createManyExecutions(2, secondWorkflowForUser2, testDb.createSuccessfulExecution);
const [firstWorkflowForUser2, secondWorkflowForUser2] = await createManyWorkflows(2, {}, user2);
await createManyExecutions(2, firstWorkflowForUser2, createSuccessfulExecution);
await createManyExecutions(2, secondWorkflowForUser2, createSuccessfulExecution);
const response = await authOwnerAgent.get('/executions');
@ -420,21 +421,13 @@ describe('GET /executions', () => {
});
test('member should not see executions of workflows not shared with him', async () => {
const [firstWorkflowForUser1, secondWorkflowForUser1] = await testDb.createManyWorkflows(
2,
{},
user1,
);
await testDb.createManyExecutions(2, firstWorkflowForUser1, testDb.createSuccessfulExecution);
await testDb.createManyExecutions(2, secondWorkflowForUser1, testDb.createSuccessfulExecution);
const [firstWorkflowForUser1, secondWorkflowForUser1] = await createManyWorkflows(2, {}, user1);
await createManyExecutions(2, firstWorkflowForUser1, createSuccessfulExecution);
await createManyExecutions(2, secondWorkflowForUser1, createSuccessfulExecution);
const [firstWorkflowForUser2, secondWorkflowForUser2] = await testDb.createManyWorkflows(
2,
{},
user2,
);
await testDb.createManyExecutions(2, firstWorkflowForUser2, testDb.createSuccessfulExecution);
await testDb.createManyExecutions(2, secondWorkflowForUser2, testDb.createSuccessfulExecution);
const [firstWorkflowForUser2, secondWorkflowForUser2] = await createManyWorkflows(2, {}, user2);
await createManyExecutions(2, firstWorkflowForUser2, createSuccessfulExecution);
await createManyExecutions(2, secondWorkflowForUser2, createSuccessfulExecution);
const response = await authUser1Agent.get('/executions');
@ -444,23 +437,15 @@ describe('GET /executions', () => {
});
test('member should also see executions of workflows shared with him', async () => {
const [firstWorkflowForUser1, secondWorkflowForUser1] = await testDb.createManyWorkflows(
2,
{},
user1,
);
await testDb.createManyExecutions(2, firstWorkflowForUser1, testDb.createSuccessfulExecution);
await testDb.createManyExecutions(2, secondWorkflowForUser1, testDb.createSuccessfulExecution);
const [firstWorkflowForUser1, secondWorkflowForUser1] = await createManyWorkflows(2, {}, user1);
await createManyExecutions(2, firstWorkflowForUser1, createSuccessfulExecution);
await createManyExecutions(2, secondWorkflowForUser1, createSuccessfulExecution);
const [firstWorkflowForUser2, secondWorkflowForUser2] = await testDb.createManyWorkflows(
2,
{},
user2,
);
await testDb.createManyExecutions(2, firstWorkflowForUser2, testDb.createSuccessfulExecution);
await testDb.createManyExecutions(2, secondWorkflowForUser2, testDb.createSuccessfulExecution);
const [firstWorkflowForUser2, secondWorkflowForUser2] = await createManyWorkflows(2, {}, user2);
await createManyExecutions(2, firstWorkflowForUser2, createSuccessfulExecution);
await createManyExecutions(2, secondWorkflowForUser2, createSuccessfulExecution);
await testDb.shareWorkflowWithUsers(firstWorkflowForUser2, [user1]);
await shareWorkflowWithUsers(firstWorkflowForUser2, [user1]);
const response = await authUser1Agent.get('/executions');

View file

@ -8,6 +8,8 @@ import { License } from '@/License';
import { randomApiKey } from '../shared/random';
import * as utils from '../shared/utils/';
import * as testDb from '../shared/testDb';
import { getGlobalMemberRole, getGlobalOwnerRole } from '../shared/db/roles';
import { createUser, createUserShell } from '../shared/db/users';
utils.mockInstance(License, {
getUsersLimit: jest.fn().mockReturnValue(-1),
@ -19,7 +21,10 @@ let globalOwnerRole: Role;
let globalMemberRole: Role;
beforeAll(async () => {
[globalOwnerRole, globalMemberRole] = await testDb.getAllRoles();
[globalOwnerRole, globalMemberRole] = await Promise.all([
getGlobalOwnerRole(),
getGlobalMemberRole(),
]);
});
beforeEach(async () => {
@ -29,13 +34,13 @@ beforeEach(async () => {
describe('With license unlimited quota:users', () => {
describe('GET /users', () => {
test('should fail due to missing API Key', async () => {
const owner = await testDb.createUser({ globalRole: globalOwnerRole });
const owner = await createUser({ globalRole: globalOwnerRole });
const authOwnerAgent = testServer.publicApiAgentFor(owner);
await authOwnerAgent.get('/users').expect(401);
});
test('should fail due to invalid API Key', async () => {
const owner = await testDb.createUser({
const owner = await createUser({
globalRole: globalOwnerRole,
apiKey: randomApiKey(),
});
@ -45,20 +50,20 @@ describe('With license unlimited quota:users', () => {
});
test('should fail due to member trying to access owner only endpoint', async () => {
const member = await testDb.createUser({ apiKey: randomApiKey() });
const member = await createUser({ apiKey: randomApiKey() });
const authMemberAgent = testServer.publicApiAgentFor(member);
await authMemberAgent.get('/users').expect(403);
});
test('should return all users', async () => {
const owner = await testDb.createUser({
const owner = await createUser({
globalRole: globalOwnerRole,
apiKey: randomApiKey(),
});
const authOwnerAgent = testServer.publicApiAgentFor(owner);
await testDb.createUser();
await createUser();
const response = await authOwnerAgent.get('/users').expect(200);
expect(response.body.data.length).toBe(2);
@ -94,13 +99,13 @@ describe('With license unlimited quota:users', () => {
describe('GET /users/:id', () => {
test('should fail due to missing API Key', async () => {
const owner = await testDb.createUser({ globalRole: globalOwnerRole });
const owner = await createUser({ globalRole: globalOwnerRole });
const authOwnerAgent = testServer.publicApiAgentFor(owner);
await authOwnerAgent.get(`/users/${owner.id}`).expect(401);
});
test('should fail due to invalid API Key', async () => {
const owner = await testDb.createUser({
const owner = await createUser({
globalRole: globalOwnerRole,
apiKey: randomApiKey(),
});
@ -110,12 +115,12 @@ describe('With license unlimited quota:users', () => {
});
test('should fail due to member trying to access owner only endpoint', async () => {
const member = await testDb.createUser({ apiKey: randomApiKey() });
const member = await createUser({ apiKey: randomApiKey() });
const authMemberAgent = testServer.publicApiAgentFor(member);
await authMemberAgent.get(`/users/${member.id}`).expect(403);
});
test('should return 404 for non-existing id ', async () => {
const owner = await testDb.createUser({
const owner = await createUser({
globalRole: globalOwnerRole,
apiKey: randomApiKey(),
});
@ -124,12 +129,12 @@ describe('With license unlimited quota:users', () => {
});
test('should return a pending user', async () => {
const owner = await testDb.createUser({
const owner = await createUser({
globalRole: globalOwnerRole,
apiKey: randomApiKey(),
});
const { id: memberId } = await testDb.createUserShell(globalMemberRole);
const { id: memberId } = await createUserShell(globalMemberRole);
const authOwnerAgent = testServer.publicApiAgentFor(owner);
const response = await authOwnerAgent.get(`/users/${memberId}`).expect(200);
@ -163,7 +168,7 @@ describe('With license unlimited quota:users', () => {
describe('GET /users/:email', () => {
test('with non-existing email should return 404', async () => {
const owner = await testDb.createUser({
const owner = await createUser({
globalRole: globalOwnerRole,
apiKey: randomApiKey(),
});
@ -172,7 +177,7 @@ describe('With license unlimited quota:users', () => {
});
test('should return a user', async () => {
const owner = await testDb.createUser({
const owner = await createUser({
globalRole: globalOwnerRole,
apiKey: randomApiKey(),
});
@ -213,7 +218,7 @@ describe('With license without quota:users', () => {
beforeEach(async () => {
utils.mockInstance(License, { getUsersLimit: jest.fn().mockReturnValue(null) });
const owner = await testDb.createUser({
const owner = await createUser({
globalRole: globalOwnerRole,
apiKey: randomApiKey(),
});

View file

@ -13,6 +13,10 @@ import { STARTING_NODES } from '@/constants';
import { License } from '@/License';
import { WorkflowHistoryRepository } from '@/databases/repositories';
import Container from 'typedi';
import { getAllRoles } from '../shared/db/roles';
import { createUser } from '../shared/db/users';
import { createWorkflow, createWorkflowWithTrigger } from '../shared/db/workflows';
import { createTag } from '../shared/db/tags';
let workflowOwnerRole: Role;
let owner: User;
@ -29,16 +33,16 @@ const licenseLike = utils.mockInstance(License, {
});
beforeAll(async () => {
const [globalOwnerRole, globalMemberRole, fetchedWorkflowOwnerRole] = await testDb.getAllRoles();
const [globalOwnerRole, globalMemberRole, fetchedWorkflowOwnerRole] = await getAllRoles();
workflowOwnerRole = fetchedWorkflowOwnerRole;
owner = await testDb.createUser({
owner = await createUser({
globalRole: globalOwnerRole,
apiKey: randomApiKey(),
});
member = await testDb.createUser({
member = await createUser({
globalRole: globalMemberRole,
apiKey: randomApiKey(),
});
@ -80,9 +84,9 @@ describe('GET /workflows', () => {
test('should return all owned workflows', async () => {
await Promise.all([
testDb.createWorkflow({}, member),
testDb.createWorkflow({}, member),
testDb.createWorkflow({}, member),
createWorkflow({}, member),
createWorkflow({}, member),
createWorkflow({}, member),
]);
const response = await authMemberAgent.get('/workflows');
@ -120,9 +124,9 @@ describe('GET /workflows', () => {
test('should return all owned workflows with pagination', async () => {
await Promise.all([
testDb.createWorkflow({}, member),
testDb.createWorkflow({}, member),
testDb.createWorkflow({}, member),
createWorkflow({}, member),
createWorkflow({}, member),
createWorkflow({}, member),
]);
const response = await authMemberAgent.get('/workflows?limit=1');
@ -173,11 +177,11 @@ describe('GET /workflows', () => {
});
test('should return all owned workflows filtered by tag', async () => {
const tag = await testDb.createTag({});
const tag = await createTag({});
const [workflow] = await Promise.all([
testDb.createWorkflow({ tags: [tag] }, member),
testDb.createWorkflow({}, member),
createWorkflow({ tags: [tag] }, member),
createWorkflow({}, member),
]);
const response = await authMemberAgent.get(`/workflows?tags=${tag.name}`);
@ -213,15 +217,15 @@ describe('GET /workflows', () => {
});
test('should return all owned workflows filtered by tags', async () => {
const tags = await Promise.all([await testDb.createTag({}), await testDb.createTag({})]);
const tags = await Promise.all([await createTag({}), await createTag({})]);
const tagNames = tags.map((tag) => tag.name).join(',');
const [workflow1, workflow2] = await Promise.all([
testDb.createWorkflow({ tags }, member),
testDb.createWorkflow({ tags }, member),
testDb.createWorkflow({}, member),
testDb.createWorkflow({ tags: [tags[0]] }, member),
testDb.createWorkflow({ tags: [tags[1]] }, member),
createWorkflow({ tags }, member),
createWorkflow({ tags }, member),
createWorkflow({}, member),
createWorkflow({ tags: [tags[0]] }, member),
createWorkflow({ tags: [tags[1]] }, member),
]);
const response = await authMemberAgent.get(`/workflows?tags=${tagNames}`);
@ -254,11 +258,11 @@ describe('GET /workflows', () => {
test('should return all workflows for owner', async () => {
await Promise.all([
testDb.createWorkflow({}, owner),
testDb.createWorkflow({}, member),
testDb.createWorkflow({}, owner),
testDb.createWorkflow({}, member),
testDb.createWorkflow({}, owner),
createWorkflow({}, owner),
createWorkflow({}, member),
createWorkflow({}, owner),
createWorkflow({}, member),
createWorkflow({}, owner),
]);
const response = await authOwnerAgent.get('/workflows');
@ -307,7 +311,7 @@ describe('GET /workflows/:id', () => {
test('should retrieve workflow', async () => {
// create and assign workflow to owner
const workflow = await testDb.createWorkflow({}, member);
const workflow = await createWorkflow({}, member);
const response = await authMemberAgent.get(`/workflows/${workflow.id}`);
@ -340,7 +344,7 @@ describe('GET /workflows/:id', () => {
test('should retrieve non-owned workflow for owner', async () => {
// create and assign workflow to owner
const workflow = await testDb.createWorkflow({}, member);
const workflow = await createWorkflow({}, member);
const response = await authOwnerAgent.get(`/workflows/${workflow.id}`);
@ -373,7 +377,7 @@ describe('DELETE /workflows/:id', () => {
test('should delete the workflow', async () => {
// create and assign workflow to owner
const workflow = await testDb.createWorkflow({}, member);
const workflow = await createWorkflow({}, member);
const response = await authMemberAgent.delete(`/workflows/${workflow.id}`);
@ -402,7 +406,7 @@ describe('DELETE /workflows/:id', () => {
test('should delete non-owned workflow when owner', async () => {
// create and assign workflow to owner
const workflow = await testDb.createWorkflow({}, member);
const workflow = await createWorkflow({}, member);
const response = await authMemberAgent.delete(`/workflows/${workflow.id}`);
@ -444,13 +448,13 @@ describe('POST /workflows/:id/activate', () => {
});
test('should fail due to trying to activate a workflow without a trigger', async () => {
const workflow = await testDb.createWorkflow({}, owner);
const workflow = await createWorkflow({}, owner);
const response = await authOwnerAgent.post(`/workflows/${workflow.id}/activate`);
expect(response.statusCode).toBe(400);
});
test('should set workflow as active', async () => {
const workflow = await testDb.createWorkflowWithTrigger({}, member);
const workflow = await createWorkflowWithTrigger({}, member);
const response = await authMemberAgent.post(`/workflows/${workflow.id}/activate`);
@ -485,7 +489,7 @@ describe('POST /workflows/:id/activate', () => {
});
test('should set non-owned workflow as active when owner', async () => {
const workflow = await testDb.createWorkflowWithTrigger({}, member);
const workflow = await createWorkflowWithTrigger({}, member);
const response = await authMemberAgent.post(`/workflows/${workflow.id}/activate`);
@ -546,7 +550,7 @@ describe('POST /workflows/:id/deactivate', () => {
});
test('should deactivate workflow', async () => {
const workflow = await testDb.createWorkflowWithTrigger({}, member);
const workflow = await createWorkflowWithTrigger({}, member);
await authMemberAgent.post(`/workflows/${workflow.id}/activate`);
@ -583,7 +587,7 @@ describe('POST /workflows/:id/deactivate', () => {
});
test('should deactivate non-owned workflow when owner', async () => {
const workflow = await testDb.createWorkflowWithTrigger({}, member);
const workflow = await createWorkflowWithTrigger({}, member);
await authMemberAgent.post(`/workflows/${workflow.id}/activate`);
@ -869,7 +873,7 @@ describe('PUT /workflows/:id', () => {
});
test('should update workflow', async () => {
const workflow = await testDb.createWorkflow({}, member);
const workflow = await createWorkflow({}, member);
const payload = {
name: 'name updated',
nodes: [
@ -936,7 +940,7 @@ describe('PUT /workflows/:id', () => {
test('should create workflow history version when licensed', async () => {
licenseLike.isWorkflowHistoryLicensed.mockReturnValue(true);
const workflow = await testDb.createWorkflow({}, member);
const workflow = await createWorkflow({}, member);
const payload = {
name: 'name updated',
nodes: [
@ -991,7 +995,7 @@ describe('PUT /workflows/:id', () => {
test('should not create workflow history when not licensed', async () => {
licenseLike.isWorkflowHistoryLicensed.mockReturnValue(false);
const workflow = await testDb.createWorkflow({}, member);
const workflow = await createWorkflow({}, member);
const payload = {
name: 'name updated',
nodes: [
@ -1037,7 +1041,7 @@ describe('PUT /workflows/:id', () => {
});
test('should update non-owned workflow if owner', async () => {
const workflow = await testDb.createWorkflow({}, member);
const workflow = await createWorkflow({}, member);
const payload = {
name: 'name owner updated',

View file

@ -10,9 +10,9 @@ import { SamlService } from '@/sso/saml/saml.service.ee';
import type { SamlUserAttributes } from '@/sso/saml/types/samlUserAttributes';
import { randomEmail, randomName, randomValidPassword } from '../shared/random';
import * as testDb from '../shared/testDb';
import * as utils from '../shared/utils/';
import { sampleConfig } from './sampleMetadata';
import { createOwner, createUser } from '../shared/db/users';
let someUser: User;
let owner: User;
@ -29,8 +29,8 @@ const testServer = utils.setupTestServer({
});
beforeAll(async () => {
owner = await testDb.createOwner();
someUser = await testDb.createUser();
owner = await createOwner();
someUser = await createUser();
authOwnerAgent = testServer.authAgentFor(owner);
authMemberAgent = testServer.authAgentFor(someUser);
});

View file

@ -0,0 +1,67 @@
import { CredentialsEntity } from '@db/entities/CredentialsEntity';
import type { User } from '@db/entities/User';
import type { Role } from '@db/entities/Role';
import type { ICredentialsDb } from '@/Interfaces';
import { RoleService } from '@/services/role.service';
import type { CredentialPayload } from '../types';
import Container from 'typedi';
import { CredentialsRepository, SharedCredentialsRepository } from '@/databases/repositories';
async function encryptCredentialData(credential: CredentialsEntity) {
const { createCredentialsFromCredentialsEntity } = await import('@/CredentialsHelper');
const coreCredential = createCredentialsFromCredentialsEntity(credential, true);
// @ts-ignore
coreCredential.setData(credential.data);
return coreCredential.getDataToSave() as ICredentialsDb;
}
/**
* Save a credential to the test DB, sharing it with a user.
*/
export async function saveCredential(
credentialPayload: CredentialPayload,
{ user, role }: { user: User; role: Role },
) {
const newCredential = new CredentialsEntity();
Object.assign(newCredential, credentialPayload);
const encryptedData = await encryptCredentialData(newCredential);
Object.assign(newCredential, encryptedData);
const savedCredential = await Container.get(CredentialsRepository).save(newCredential);
savedCredential.data = newCredential.data;
await Container.get(SharedCredentialsRepository).save({
user,
credentials: savedCredential,
role,
});
return savedCredential;
}
export async function shareCredentialWithUsers(credential: CredentialsEntity, users: User[]) {
const role = await Container.get(RoleService).findCredentialUserRole();
const newSharedCredentials = users.map((user) =>
Container.get(SharedCredentialsRepository).create({
userId: user.id,
credentialsId: credential.id,
roleId: role?.id,
}),
);
return Container.get(SharedCredentialsRepository).save(newSharedCredentials);
}
export function affixRoleToSaveCredential(role: Role) {
return async (credentialPayload: CredentialPayload, { user }: { user: User }) =>
saveCredential(credentialPayload, { user, role });
}
export async function getAllCredentials() {
return Container.get(CredentialsRepository).find();
}

View file

@ -0,0 +1,68 @@
import Container from 'typedi';
import type { ExecutionData } from '@db/entities/ExecutionData';
import type { ExecutionEntity } from '@db/entities/ExecutionEntity';
import type { WorkflowEntity } from '@db/entities/WorkflowEntity';
import { ExecutionDataRepository, ExecutionRepository } from '@db/repositories';
export async function createManyExecutions(
amount: number,
workflow: WorkflowEntity,
callback: (workflow: WorkflowEntity) => Promise<ExecutionEntity>,
) {
const executionsRequests = [...Array(amount)].map(async (_) => callback(workflow));
return Promise.all(executionsRequests);
}
/**
* Store a execution in the DB and assign it to a workflow.
*/
export async function createExecution(
attributes: Partial<ExecutionEntity & ExecutionData>,
workflow: WorkflowEntity,
) {
const { data, finished, mode, startedAt, stoppedAt, waitTill, status, deletedAt } = attributes;
const execution = await Container.get(ExecutionRepository).save({
finished: finished ?? true,
mode: mode ?? 'manual',
startedAt: startedAt ?? new Date(),
...(workflow !== undefined && { workflowId: workflow.id }),
stoppedAt: stoppedAt ?? new Date(),
waitTill: waitTill ?? null,
status,
deletedAt,
});
await Container.get(ExecutionDataRepository).save({
data: data ?? '[]',
workflowData: workflow ?? {},
executionId: execution.id,
});
return execution;
}
/**
* Store a successful execution in the DB and assign it to a workflow.
*/
export async function createSuccessfulExecution(workflow: WorkflowEntity) {
return createExecution({ finished: true, status: 'success' }, workflow);
}
/**
* Store an error execution in the DB and assign it to a workflow.
*/
export async function createErrorExecution(workflow: WorkflowEntity) {
return createExecution({ finished: false, stoppedAt: new Date(), status: 'failed' }, workflow);
}
/**
* Store a waiting execution in the DB and assign it to a workflow.
*/
export async function createWaitingExecution(workflow: WorkflowEntity) {
return createExecution({ finished: false, waitTill: new Date(), status: 'waiting' }, workflow);
}
export async function getAllExecutions() {
return Container.get(ExecutionRepository).find();
}

View file

@ -0,0 +1,31 @@
import Container from 'typedi';
import { RoleService } from '@/services/role.service';
export async function getGlobalOwnerRole() {
return Container.get(RoleService).findGlobalOwnerRole();
}
export async function getGlobalMemberRole() {
return Container.get(RoleService).findGlobalMemberRole();
}
export async function getWorkflowOwnerRole() {
return Container.get(RoleService).findWorkflowOwnerRole();
}
export async function getWorkflowEditorRole() {
return Container.get(RoleService).findWorkflowEditorRole();
}
export async function getCredentialOwnerRole() {
return Container.get(RoleService).findCredentialOwnerRole();
}
export async function getAllRoles() {
return Promise.all([
getGlobalOwnerRole(),
getGlobalMemberRole(),
getWorkflowOwnerRole(),
getCredentialOwnerRole(),
]);
}

View file

@ -0,0 +1,25 @@
import Container from 'typedi';
import type { TagEntity } from '@db/entities/TagEntity';
import type { WorkflowEntity } from '@db/entities/WorkflowEntity';
import { TagRepository, WorkflowTagMappingRepository } from '@db/repositories';
import { generateNanoId } from '@db/utils/generators';
import { randomName } from '../random';
export async function createTag(attributes: Partial<TagEntity> = {}, workflow?: WorkflowEntity) {
const { name } = attributes;
const tag = await Container.get(TagRepository).save({
id: generateNanoId(),
name: name ?? randomName(),
...attributes,
});
if (workflow) {
const mappingRepository = Container.get(WorkflowTagMappingRepository);
const mapping = mappingRepository.create({ tagId: tag.id, workflowId: workflow.id });
await mappingRepository.save(mapping);
}
return tag;
}

View file

@ -0,0 +1,134 @@
import Container from 'typedi';
import { hash } from 'bcryptjs';
import { AuthIdentity } from '@db/entities/AuthIdentity';
import type { Role } from '@db/entities/Role';
import type { User } from '@db/entities/User';
import { AuthIdentityRepository, UserRepository } from '@db/repositories';
import { TOTPService } from '@/Mfa/totp.service';
import { MfaService } from '@/Mfa/mfa.service';
import { randomApiKey, randomEmail, randomName, randomValidPassword } from '../random';
import { getGlobalMemberRole, getGlobalOwnerRole } from './roles';
/**
* Store a user in the DB, defaulting to a `member`.
*/
export async function createUser(attributes: Partial<User> = {}): Promise<User> {
const { email, password, firstName, lastName, globalRole, ...rest } = attributes;
const user: Partial<User> = {
email: email ?? randomEmail(),
password: await hash(password ?? randomValidPassword(), 10),
firstName: firstName ?? randomName(),
lastName: lastName ?? randomName(),
globalRoleId: (globalRole ?? (await getGlobalMemberRole())).id,
globalRole,
...rest,
};
return Container.get(UserRepository).save(user);
}
export async function createLdapUser(attributes: Partial<User>, ldapId: string): Promise<User> {
const user = await createUser(attributes);
await Container.get(AuthIdentityRepository).save(AuthIdentity.create(user, ldapId, 'ldap'));
return user;
}
export async function createUserWithMfaEnabled(
data: { numberOfRecoveryCodes: number } = { numberOfRecoveryCodes: 10 },
) {
const email = randomEmail();
const password = randomValidPassword();
const toptService = new TOTPService();
const secret = toptService.generateSecret();
const mfaService = Container.get(MfaService);
const recoveryCodes = mfaService.generateRecoveryCodes(data.numberOfRecoveryCodes);
const { encryptedSecret, encryptedRecoveryCodes } = mfaService.encryptSecretAndRecoveryCodes(
secret,
recoveryCodes,
);
return {
user: await createUser({
mfaEnabled: true,
password,
email,
mfaSecret: encryptedSecret,
mfaRecoveryCodes: encryptedRecoveryCodes,
}),
rawPassword: password,
rawSecret: secret,
rawRecoveryCodes: recoveryCodes,
};
}
export async function createOwner() {
return createUser({ globalRole: await getGlobalOwnerRole() });
}
export async function createMember() {
return createUser({ globalRole: await getGlobalMemberRole() });
}
export async function createUserShell(globalRole: Role): Promise<User> {
if (globalRole.scope !== 'global') {
throw new Error(`Invalid role received: ${JSON.stringify(globalRole)}`);
}
const shell: Partial<User> = { globalRoleId: globalRole.id };
if (globalRole.name !== 'owner') {
shell.email = randomEmail();
}
return Container.get(UserRepository).save(shell);
}
/**
* Create many users in the DB, defaulting to a `member`.
*/
export async function createManyUsers(
amount: number,
attributes: Partial<User> = {},
): Promise<User[]> {
let { email, password, firstName, lastName, globalRole, ...rest } = attributes;
if (!globalRole) {
globalRole = await getGlobalMemberRole();
}
const users = await Promise.all(
[...Array(amount)].map(async () =>
Container.get(UserRepository).create({
email: email ?? randomEmail(),
password: await hash(password ?? randomValidPassword(), 10),
firstName: firstName ?? randomName(),
lastName: lastName ?? randomName(),
globalRole,
...rest,
}),
),
);
return Container.get(UserRepository).save(users);
}
export async function addApiKey(user: User): Promise<User> {
user.apiKey = randomApiKey();
return Container.get(UserRepository).save(user);
}
export const getAllUsers = async () =>
Container.get(UserRepository).find({
relations: ['globalRole', 'authIdentities'],
});
export const getLdapIdentities = async () =>
Container.get(AuthIdentityRepository).find({
where: { providerType: 'ldap' },
relations: ['user'],
});

View file

@ -0,0 +1,43 @@
import Container from 'typedi';
import { v4 as uuid } from 'uuid';
import type { WorkflowHistory } from '@db/entities/WorkflowHistory';
import { WorkflowHistoryRepository } from '@db/repositories';
export async function createWorkflowHistoryItem(
workflowId: string,
data?: Partial<WorkflowHistory>,
) {
return Container.get(WorkflowHistoryRepository).save({
authors: 'John Smith',
connections: {},
nodes: [
{
id: 'uuid-1234',
name: 'Start',
parameters: {},
position: [-20, 260],
type: 'n8n-nodes-base.start',
typeVersion: 1,
},
],
versionId: uuid(),
...(data ?? {}),
workflowId,
});
}
export async function createManyWorkflowHistoryItems(
workflowId: string,
count: number,
time?: Date,
) {
const baseTime = (time ?? new Date()).valueOf();
return Promise.all(
[...Array(count)].map(async (_, i) =>
createWorkflowHistoryItem(workflowId, {
createdAt: new Date(baseTime + i),
updatedAt: new Date(baseTime + i),
}),
),
);
}

View file

@ -0,0 +1,118 @@
import Container from 'typedi';
import { v4 as uuid } from 'uuid';
import type { User } from '@db/entities/User';
import type { WorkflowEntity } from '@db/entities/WorkflowEntity';
import { SharedWorkflowRepository, WorkflowRepository } from '@db/repositories';
import { getWorkflowEditorRole, getWorkflowOwnerRole } from './roles';
export async function createManyWorkflows(
amount: number,
attributes: Partial<WorkflowEntity> = {},
user?: User,
) {
const workflowRequests = [...Array(amount)].map(async (_) => createWorkflow(attributes, user));
return Promise.all(workflowRequests);
}
/**
* Store a workflow in the DB (without a trigger) and optionally assign it to a user.
* @param attributes workflow attributes
* @param user user to assign the workflow to
*/
export async function createWorkflow(attributes: Partial<WorkflowEntity> = {}, user?: User) {
const { active, name, nodes, connections, versionId } = attributes;
const workflowEntity = Container.get(WorkflowRepository).create({
active: active ?? false,
name: name ?? 'test workflow',
nodes: nodes ?? [
{
id: 'uuid-1234',
name: 'Schedule Trigger',
parameters: {},
position: [-20, 260],
type: 'n8n-nodes-base.scheduleTrigger',
typeVersion: 1,
},
],
connections: connections ?? {},
versionId: versionId ?? uuid(),
...attributes,
});
const workflow = await Container.get(WorkflowRepository).save(workflowEntity);
if (user) {
await Container.get(SharedWorkflowRepository).save({
user,
workflow,
role: await getWorkflowOwnerRole(),
});
}
return workflow;
}
export async function shareWorkflowWithUsers(workflow: WorkflowEntity, users: User[]) {
const role = await getWorkflowEditorRole();
const sharedWorkflows = users.map((user) => ({
user,
workflow,
role,
}));
return Container.get(SharedWorkflowRepository).save(sharedWorkflows);
}
export async function getWorkflowSharing(workflow: WorkflowEntity) {
return Container.get(SharedWorkflowRepository).findBy({
workflowId: workflow.id,
});
}
/**
* Store a workflow in the DB (with a trigger) and optionally assign it to a user.
* @param user user to assign the workflow to
*/
export async function createWorkflowWithTrigger(
attributes: Partial<WorkflowEntity> = {},
user?: User,
) {
const workflow = await createWorkflow(
{
nodes: [
{
id: 'uuid-1',
parameters: {},
name: 'Start',
type: 'n8n-nodes-base.start',
typeVersion: 1,
position: [240, 300],
},
{
id: 'uuid-2',
parameters: { triggerTimes: { item: [{ mode: 'everyMinute' }] } },
name: 'Cron',
type: 'n8n-nodes-base.cron',
typeVersion: 1,
position: [500, 300],
},
{
id: 'uuid-3',
parameters: { options: {} },
name: 'Set',
type: 'n8n-nodes-base.set',
typeVersion: 1,
position: [780, 300],
},
],
connections: { Cron: { main: [[{ node: 'Set', type: 'main', index: 0 }]] } },
...attributes,
},
user,
);
return workflow;
}
export async function getAllWorkflows() {
return Container.get(WorkflowRepository).find();
}

View file

@ -1,42 +1,19 @@
import type { DataSourceOptions as ConnectionOptions, Repository } from 'typeorm';
import { DataSource as Connection } from 'typeorm';
import { Container } from 'typedi';
import { v4 as uuid } from 'uuid';
import config from '@/config';
import * as Db from '@/Db';
import { createCredentialsFromCredentialsEntity } from '@/CredentialsHelper';
import { entities } from '@db/entities';
import { CredentialsEntity } from '@db/entities/CredentialsEntity';
import { mysqlMigrations } from '@db/migrations/mysqldb';
import { postgresMigrations } from '@db/migrations/postgresdb';
import { sqliteMigrations } from '@db/migrations/sqlite';
import { hashPassword } from '@/UserManagement/UserManagementHelper';
import { AuthIdentity } from '@db/entities/AuthIdentity';
import type { ExecutionEntity } from '@db/entities/ExecutionEntity';
import type { Role } from '@db/entities/Role';
import type { TagEntity } from '@db/entities/TagEntity';
import type { User } from '@db/entities/User';
import type { WorkflowEntity } from '@db/entities/WorkflowEntity';
import type { ICredentialsDb } from '@/Interfaces';
import { DB_INITIALIZATION_TIMEOUT } from './constants';
import { randomApiKey, randomEmail, randomName, randomString, randomValidPassword } from './random';
import type { CollectionName, CredentialPayload, PostgresSchemaSection } from './types';
import type { ExecutionData } from '@db/entities/ExecutionData';
import { generateNanoId } from '@db/utils/generators';
import { RoleService } from '@/services/role.service';
import { VariablesService } from '@/environments/variables/variables.service';
import {
TagRepository,
WorkflowHistoryRepository,
WorkflowTagMappingRepository,
} from '@/databases/repositories';
import { separate } from '@/utils';
import { TagRepository, WorkflowTagMappingRepository } from '@/databases/repositories';
import { randomPassword } from '@/Ldap/helpers';
import { TOTPService } from '@/Mfa/totp.service';
import { MfaService } from '@/Mfa/mfa.service';
import type { WorkflowHistory } from '@/databases/entities/WorkflowHistory';
import { DB_INITIALIZATION_TIMEOUT } from './constants';
import { randomString } from './random';
import type { CollectionName, PostgresSchemaSection } from './types';
import { separate } from '@/utils';
export type TestDBType = 'postgres' | 'mysql';
@ -132,503 +109,6 @@ export async function truncate(collections: CollectionName[]) {
}
}
// ----------------------------------
// credential creation
// ----------------------------------
/**
* Save a credential to the test DB, sharing it with a user.
*/
export async function saveCredential(
credentialPayload: CredentialPayload,
{ user, role }: { user: User; role: Role },
) {
const newCredential = new CredentialsEntity();
Object.assign(newCredential, credentialPayload);
const encryptedData = await encryptCredentialData(newCredential);
Object.assign(newCredential, encryptedData);
const savedCredential = await Db.collections.Credentials.save(newCredential);
savedCredential.data = newCredential.data;
await Db.collections.SharedCredentials.save({
user,
credentials: savedCredential,
role,
});
return savedCredential;
}
export async function shareCredentialWithUsers(credential: CredentialsEntity, users: User[]) {
const role = await Container.get(RoleService).findCredentialUserRole();
const newSharedCredentials = users.map((user) =>
Db.collections.SharedCredentials.create({
userId: user.id,
credentialsId: credential.id,
roleId: role?.id,
}),
);
return Db.collections.SharedCredentials.save(newSharedCredentials);
}
export function affixRoleToSaveCredential(role: Role) {
return async (credentialPayload: CredentialPayload, { user }: { user: User }) =>
saveCredential(credentialPayload, { user, role });
}
export async function getAllCredentials() {
return Db.collections.Credentials.find();
}
// ----------------------------------
// user creation
// ----------------------------------
/**
* Store a user in the DB, defaulting to a `member`.
*/
export async function createUser(attributes: Partial<User> = {}): Promise<User> {
const { email, password, firstName, lastName, globalRole, ...rest } = attributes;
const user: Partial<User> = {
email: email ?? randomEmail(),
password: await hashPassword(password ?? randomValidPassword()),
firstName: firstName ?? randomName(),
lastName: lastName ?? randomName(),
globalRoleId: (globalRole ?? (await getGlobalMemberRole())).id,
globalRole,
...rest,
};
return Db.collections.User.save(user);
}
export async function createLdapUser(attributes: Partial<User>, ldapId: string): Promise<User> {
const user = await createUser(attributes);
await Db.collections.AuthIdentity.save(AuthIdentity.create(user, ldapId, 'ldap'));
return user;
}
export async function createUserWithMfaEnabled(
data: { numberOfRecoveryCodes: number } = { numberOfRecoveryCodes: 10 },
) {
const email = randomEmail();
const password = randomPassword();
const toptService = new TOTPService();
const secret = toptService.generateSecret();
const mfaService = Container.get(MfaService);
const recoveryCodes = mfaService.generateRecoveryCodes(data.numberOfRecoveryCodes);
const { encryptedSecret, encryptedRecoveryCodes } = mfaService.encryptSecretAndRecoveryCodes(
secret,
recoveryCodes,
);
return {
user: await createUser({
mfaEnabled: true,
password,
email,
mfaSecret: encryptedSecret,
mfaRecoveryCodes: encryptedRecoveryCodes,
}),
rawPassword: password,
rawSecret: secret,
rawRecoveryCodes: recoveryCodes,
};
}
export async function createOwner() {
return createUser({ globalRole: await getGlobalOwnerRole() });
}
export async function createMember() {
return createUser({ globalRole: await getGlobalMemberRole() });
}
export async function createUserShell(globalRole: Role): Promise<User> {
if (globalRole.scope !== 'global') {
throw new Error(`Invalid role received: ${JSON.stringify(globalRole)}`);
}
const shell: Partial<User> = { globalRoleId: globalRole.id };
if (globalRole.name !== 'owner') {
shell.email = randomEmail();
}
return Db.collections.User.save(shell);
}
/**
* Create many users in the DB, defaulting to a `member`.
*/
export async function createManyUsers(
amount: number,
attributes: Partial<User> = {},
): Promise<User[]> {
let { email, password, firstName, lastName, globalRole, ...rest } = attributes;
if (!globalRole) {
globalRole = await getGlobalMemberRole();
}
const users = await Promise.all(
[...Array(amount)].map(async () =>
Db.collections.User.create({
email: email ?? randomEmail(),
password: await hashPassword(password ?? randomValidPassword()),
firstName: firstName ?? randomName(),
lastName: lastName ?? randomName(),
globalRole,
...rest,
}),
),
);
return Db.collections.User.save(users);
}
export async function addApiKey(user: User): Promise<User> {
user.apiKey = randomApiKey();
return Db.collections.User.save(user);
}
// ----------------------------------
// role fetchers
// ----------------------------------
export async function getGlobalOwnerRole() {
return Container.get(RoleService).findGlobalOwnerRole();
}
export async function getGlobalMemberRole() {
return Container.get(RoleService).findGlobalMemberRole();
}
export async function getWorkflowOwnerRole() {
return Container.get(RoleService).findWorkflowOwnerRole();
}
export async function getWorkflowEditorRole() {
return Container.get(RoleService).findWorkflowEditorRole();
}
export async function getCredentialOwnerRole() {
return Container.get(RoleService).findCredentialOwnerRole();
}
export async function getAllRoles() {
return Promise.all([
getGlobalOwnerRole(),
getGlobalMemberRole(),
getWorkflowOwnerRole(),
getCredentialOwnerRole(),
]);
}
export const getAllUsers = async () =>
Db.collections.User.find({
relations: ['globalRole', 'authIdentities'],
});
export const getLdapIdentities = async () =>
Db.collections.AuthIdentity.find({
where: { providerType: 'ldap' },
relations: ['user'],
});
// ----------------------------------
// Execution helpers
// ----------------------------------
export async function createManyExecutions(
amount: number,
workflow: WorkflowEntity,
callback: (workflow: WorkflowEntity) => Promise<ExecutionEntity>,
) {
const executionsRequests = [...Array(amount)].map(async (_) => callback(workflow));
return Promise.all(executionsRequests);
}
/**
* Store a execution in the DB and assign it to a workflow.
*/
export async function createExecution(
attributes: Partial<ExecutionEntity & ExecutionData>,
workflow: WorkflowEntity,
) {
const { data, finished, mode, startedAt, stoppedAt, waitTill, status, deletedAt } = attributes;
const execution = await Db.collections.Execution.save({
finished: finished ?? true,
mode: mode ?? 'manual',
startedAt: startedAt ?? new Date(),
...(workflow !== undefined && { workflowId: workflow.id }),
stoppedAt: stoppedAt ?? new Date(),
waitTill: waitTill ?? null,
status,
deletedAt,
});
await Db.collections.ExecutionData.save({
data: data ?? '[]',
workflowData: workflow ?? {},
executionId: execution.id,
});
return execution;
}
/**
* Store a successful execution in the DB and assign it to a workflow.
*/
export async function createSuccessfulExecution(workflow: WorkflowEntity) {
return createExecution({ finished: true, status: 'success' }, workflow);
}
/**
* Store an error execution in the DB and assign it to a workflow.
*/
export async function createErrorExecution(workflow: WorkflowEntity) {
return createExecution({ finished: false, stoppedAt: new Date(), status: 'failed' }, workflow);
}
/**
* Store a waiting execution in the DB and assign it to a workflow.
*/
export async function createWaitingExecution(workflow: WorkflowEntity) {
return createExecution({ finished: false, waitTill: new Date(), status: 'waiting' }, workflow);
}
// ----------------------------------
// Tags
// ----------------------------------
export async function createTag(attributes: Partial<TagEntity> = {}, workflow?: WorkflowEntity) {
const { name } = attributes;
const tag = await Container.get(TagRepository).save({
id: generateNanoId(),
name: name ?? randomName(),
...attributes,
});
if (workflow) {
const mappingRepository = Container.get(WorkflowTagMappingRepository);
const mapping = mappingRepository.create({ tagId: tag.id, workflowId: workflow.id });
await mappingRepository.save(mapping);
}
return tag;
}
// ----------------------------------
// Workflow helpers
// ----------------------------------
export async function createManyWorkflows(
amount: number,
attributes: Partial<WorkflowEntity> = {},
user?: User,
) {
const workflowRequests = [...Array(amount)].map(async (_) => createWorkflow(attributes, user));
return Promise.all(workflowRequests);
}
/**
* Store a workflow in the DB (without a trigger) and optionally assign it to a user.
* @param attributes workflow attributes
* @param user user to assign the workflow to
*/
export async function createWorkflow(attributes: Partial<WorkflowEntity> = {}, user?: User) {
const { active, name, nodes, connections, versionId } = attributes;
const workflowEntity = Db.collections.Workflow.create({
active: active ?? false,
name: name ?? 'test workflow',
nodes: nodes ?? [
{
id: 'uuid-1234',
name: 'Schedule Trigger',
parameters: {},
position: [-20, 260],
type: 'n8n-nodes-base.scheduleTrigger',
typeVersion: 1,
},
],
connections: connections ?? {},
versionId: versionId ?? uuid(),
...attributes,
});
const workflow = await Db.collections.Workflow.save(workflowEntity);
if (user) {
await Db.collections.SharedWorkflow.save({
user,
workflow,
role: await getWorkflowOwnerRole(),
});
}
return workflow;
}
export async function shareWorkflowWithUsers(workflow: WorkflowEntity, users: User[]) {
const role = await getWorkflowEditorRole();
const sharedWorkflows = users.map((user) => ({
user,
workflow,
role,
}));
return Db.collections.SharedWorkflow.save(sharedWorkflows);
}
/**
* Store a workflow in the DB (with a trigger) and optionally assign it to a user.
* @param user user to assign the workflow to
*/
export async function createWorkflowWithTrigger(
attributes: Partial<WorkflowEntity> = {},
user?: User,
) {
const workflow = await createWorkflow(
{
nodes: [
{
id: 'uuid-1',
parameters: {},
name: 'Start',
type: 'n8n-nodes-base.start',
typeVersion: 1,
position: [240, 300],
},
{
id: 'uuid-2',
parameters: { triggerTimes: { item: [{ mode: 'everyMinute' }] } },
name: 'Cron',
type: 'n8n-nodes-base.cron',
typeVersion: 1,
position: [500, 300],
},
{
id: 'uuid-3',
parameters: { options: {} },
name: 'Set',
type: 'n8n-nodes-base.set',
typeVersion: 1,
position: [780, 300],
},
],
connections: { Cron: { main: [[{ node: 'Set', type: 'main', index: 0 }]] } },
...attributes,
},
user,
);
return workflow;
}
export async function getAllWorkflows() {
return Db.collections.Workflow.find();
}
export async function getAllExecutions() {
return Db.collections.Execution.find();
}
// ----------------------------------
// workflow sharing
// ----------------------------------
export async function getWorkflowSharing(workflow: WorkflowEntity) {
return Db.collections.SharedWorkflow.findBy({
workflowId: workflow.id,
});
}
// ----------------------------------
// variables
// ----------------------------------
export async function createVariable(key: string, value: string) {
const result = await Db.collections.Variables.save({
id: generateNanoId(),
key,
value,
});
await Container.get(VariablesService).updateCache();
return result;
}
export async function getVariableByKey(key: string) {
return Db.collections.Variables.findOne({
where: {
key,
},
});
}
export async function getVariableById(id: string) {
return Db.collections.Variables.findOne({
where: {
id,
},
});
}
// ----------------------------------
// workflow history
// ----------------------------------
export async function createWorkflowHistoryItem(
workflowId: string,
data?: Partial<WorkflowHistory>,
) {
return Container.get(WorkflowHistoryRepository).save({
authors: 'John Smith',
connections: {},
nodes: [
{
id: 'uuid-1234',
name: 'Start',
parameters: {},
position: [-20, 260],
type: 'n8n-nodes-base.start',
typeVersion: 1,
},
],
versionId: uuid(),
...(data ?? {}),
workflowId,
});
}
export async function createManyWorkflowHistoryItems(
workflowId: string,
count: number,
time?: Date,
) {
const baseTime = (time ?? new Date()).valueOf();
return Promise.all(
[...Array(count)].map(async (_, i) =>
createWorkflowHistoryItem(workflowId, {
createdAt: new Date(baseTime + i),
updatedAt: new Date(baseTime + i),
}),
),
);
}
// ----------------------------------
// connection options
// ----------------------------------
@ -683,16 +163,3 @@ const getDBOptions = (type: TestDBType, name: string) => ({
synchronize: false,
logging: false,
});
// ----------------------------------
// encryption
// ----------------------------------
async function encryptCredentialData(credential: CredentialsEntity) {
const coreCredential = createCredentialsFromCredentialsEntity(credential, true);
// @ts-ignore
coreCredential.setData(credential.data);
return coreCredential.getDataToSave() as ICredentialsDb;
}

View file

@ -12,7 +12,7 @@ export type CollectionName =
| keyof IDatabaseCollections
| { new (dataSource: DataSource): Repository<any> };
export type EndpointGroup =
type EndpointGroup =
| 'me'
| 'users'
| 'auth'

View file

@ -12,7 +12,6 @@ import { v4 as uuid } from 'uuid';
import config from '@/config';
import * as Db from '@/Db';
import { WorkflowEntity } from '@db/entities/WorkflowEntity';
import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
import { AUTH_COOKIE_NAME } from '@/constants';
import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials';
@ -29,7 +28,8 @@ export { setupTestServer } from './testServer';
/**
* Initialize node types.
*/
export async function initActiveWorkflowRunner(): Promise<ActiveWorkflowRunner> {
export async function initActiveWorkflowRunner() {
const { ActiveWorkflowRunner } = await import('@/ActiveWorkflowRunner');
const workflowRunner = Container.get(ActiveWorkflowRunner);
await workflowRunner.init();
return workflowRunner;

View file

@ -6,61 +6,27 @@ import request from 'supertest';
import { URL } from 'url';
import config from '@/config';
import { ExternalHooks } from '@/ExternalHooks';
import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
import { workflowsController } from '@/workflows/workflows.controller';
import { AUTH_COOKIE_NAME } from '@/constants';
import { credentialsController } from '@/credentials/credentials.controller';
import type { User } from '@db/entities/User';
import { loadPublicApiVersions } from '@/PublicApi/';
import { issueJWT } from '@/auth/jwt';
import { UserManagementMailer } from '@/UserManagement/email/UserManagementMailer';
import { licenseController } from '@/license/license.controller';
import { registerController } from '@/decorators';
import {
AuthController,
LdapController,
MFAController,
MeController,
OwnerController,
PasswordResetController,
TagsController,
UsersController,
} from '@/controllers';
import { rawBodyReader, bodyParser, setupAuthMiddlewares } from '@/middlewares';
import { InternalHooks } from '@/InternalHooks';
import { PostHogClient } from '@/posthog';
import { variablesController } from '@/environments/variables/variables.controller';
import { LdapManager } from '@/Ldap/LdapManager.ee';
import { handleLdapInit } from '@/Ldap/helpers';
import { setSamlLoginEnabled } from '@/sso/saml/samlHelpers';
import { SamlController } from '@/sso/saml/routes/saml.controller.ee';
import { EventBusController } from '@/eventbus/eventBus.controller';
import { EventBusControllerEE } from '@/eventbus/eventBus.controller.ee';
import { License } from '@/License';
import { SourceControlController } from '@/environments/sourceControl/sourceControl.controller.ee';
import * as testDb from '../../shared/testDb';
import { AUTHLESS_ENDPOINTS, PUBLIC_API_REST_PATH_SEGMENT, REST_PATH_SEGMENT } from '../constants';
import type { EndpointGroup, SetupProps, TestServer } from '../types';
import { mockInstance } from './mocking';
import { ExternalSecretsController } from '@/ExternalSecrets/ExternalSecrets.controller.ee';
import { MfaService } from '@/Mfa/mfa.service';
import { MetricsService } from '@/services/metrics.service';
import {
SettingsRepository,
SharedCredentialsRepository,
SharedWorkflowRepository,
} from '@/databases/repositories';
import { JwtService } from '@/services/jwt.service';
import { RoleService } from '@/services/role.service';
import { UserService } from '@/services/user.service';
import { executionsController } from '@/executions/executions.controller';
import { WorkflowHistoryController } from '@/workflows/workflowHistory/workflowHistory.controller.ee';
import { BinaryDataController } from '@/controllers/binaryData.controller';
import { Logger } from '@/Logger';
import * as testDb from '../../shared/testDb';
import { AUTHLESS_ENDPOINTS, PUBLIC_API_REST_PATH_SEGMENT, REST_PATH_SEGMENT } from '../constants';
import type { SetupProps, TestServer } from '../types';
import { mockInstance } from './mocking';
/**
* Plugin to prefix a path segment into a request URL pathname.
*
@ -81,30 +47,6 @@ function prefix(pathSegment: string) {
};
}
/**
* Classify endpoint groups into `routerEndpoints` (newest, using `express.Router`),
* and `functionEndpoints` (legacy, namespaced inside a function).
*/
const classifyEndpointGroups = (endpointGroups: EndpointGroup[]) => {
const routerEndpoints: EndpointGroup[] = [];
const functionEndpoints: EndpointGroup[] = [];
const ROUTER_GROUP = [
'credentials',
'workflows',
'publicApi',
'license',
'variables',
'executions',
];
endpointGroups.forEach((group) =>
(ROUTER_GROUP.includes(group) ? routerEndpoints : functionEndpoints).push(group),
);
return [routerEndpoints, functionEndpoints];
};
function createAgent(app: express.Application, options?: { auth: boolean; user: User }) {
const agent = request.agent(app);
void agent.use(prefix(REST_PATH_SEGMENT));
@ -168,118 +110,177 @@ export const setupTestServer = ({
app.use(bodyParser);
const [routerEndpoints, functionEndpoints] = classifyEndpointGroups(endpointGroups);
if (routerEndpoints.length) {
const map: Record<string, express.Router | express.Router[] | any> = {
credentials: { controller: credentialsController, path: 'credentials' },
workflows: { controller: workflowsController, path: 'workflows' },
license: { controller: licenseController, path: 'license' },
variables: { controller: variablesController, path: 'variables' },
executions: { controller: executionsController, path: 'executions' },
};
if (enablePublicAPI) {
const { apiRouters } = await loadPublicApiVersions(PUBLIC_API_REST_PATH_SEGMENT);
map.publicApi = apiRouters;
}
for (const group of routerEndpoints) {
if (group === 'publicApi') {
app.use(...(map[group] as express.Router[]));
} else {
app.use(`/${REST_PATH_SEGMENT}/${map[group].path}`, map[group].controller);
}
}
if (enablePublicAPI) {
const { loadPublicApiVersions } = await import('@/PublicApi');
const { apiRouters } = await loadPublicApiVersions(PUBLIC_API_REST_PATH_SEGMENT);
app.use(...apiRouters);
}
if (functionEndpoints.length) {
const externalHooks = Container.get(ExternalHooks);
const internalHooks = Container.get(InternalHooks);
const mailer = Container.get(UserManagementMailer);
const mfaService = Container.get(MfaService);
const userService = Container.get(UserService);
for (const group of functionEndpoints) {
if (endpointGroups.length) {
for (const group of endpointGroups) {
switch (group) {
case 'credentials':
const { credentialsController } = await import('@/credentials/credentials.controller');
app.use(`/${REST_PATH_SEGMENT}/credentials`, credentialsController);
break;
case 'workflows':
const { workflowsController } = await import('@/workflows/workflows.controller');
app.use(`/${REST_PATH_SEGMENT}/workflows`, workflowsController);
break;
case 'executions':
const { executionsController } = await import('@/executions/executions.controller');
app.use(`/${REST_PATH_SEGMENT}/executions`, executionsController);
break;
case 'variables':
const { variablesController } = await import(
'@/environments/variables/variables.controller'
);
app.use(`/${REST_PATH_SEGMENT}/variables`, variablesController);
break;
case 'license':
const { licenseController } = await import('@/license/license.controller');
app.use(`/${REST_PATH_SEGMENT}/license`, licenseController);
break;
case 'metrics':
const { MetricsService } = await import('@/services/metrics.service');
await Container.get(MetricsService).configureMetrics(app);
break;
case 'eventBus':
const { EventBusController } = await import('@/eventbus/eventBus.controller');
const { EventBusControllerEE } = await import('@/eventbus/eventBus.controller.ee');
registerController(app, config, new EventBusController());
registerController(app, config, new EventBusControllerEE());
break;
case 'auth':
const { AuthController } = await import('@/controllers/auth.controller');
registerController(app, config, Container.get(AuthController));
break;
case 'mfa':
registerController(app, config, new MFAController(mfaService));
const { MFAController } = await import('@/controllers/mfa.controller');
registerController(app, config, Container.get(MFAController));
break;
case 'ldap':
const { LdapManager } = await import('@/Ldap/LdapManager.ee');
const { handleLdapInit } = await import('@/Ldap/helpers');
const { LdapController } = await import('@/controllers/ldap.controller');
Container.get(License).isLdapEnabled = () => true;
await handleLdapInit();
const { service, sync } = LdapManager.getInstance();
registerController(app, config, new LdapController(service, sync, internalHooks));
registerController(
app,
config,
new LdapController(service, sync, Container.get(InternalHooks)),
);
break;
case 'saml':
const { setSamlLoginEnabled } = await import('@/sso/saml/samlHelpers');
const { SamlController } = await import('@/sso/saml/routes/saml.controller.ee');
await setSamlLoginEnabled(true);
registerController(app, config, Container.get(SamlController));
break;
case 'sourceControl':
const { SourceControlController } = await import(
'@/environments/sourceControl/sourceControl.controller.ee'
);
registerController(app, config, Container.get(SourceControlController));
break;
case 'community-packages':
const { CommunityPackagesController } = await import(
'@/controllers/communityPackages.controller'
);
registerController(app, config, Container.get(CommunityPackagesController));
break;
case 'me':
const { MeController } = await import('@/controllers/me.controller');
registerController(app, config, Container.get(MeController));
break;
case 'passwordReset':
const { PasswordResetController } = await import(
'@/controllers/passwordReset.controller'
);
registerController(app, config, Container.get(PasswordResetController));
break;
case 'owner':
const { UserService } = await import('@/services/user.service');
const { OwnerController } = await import('@/controllers/owner.controller');
registerController(
app,
config,
new OwnerController(
config,
logger,
internalHooks,
Container.get(InternalHooks),
Container.get(SettingsRepository),
userService,
Container.get(UserService),
),
);
break;
case 'users':
const { ActiveWorkflowRunner } = await import('@/ActiveWorkflowRunner');
const { ExternalHooks } = await import('@/ExternalHooks');
const { JwtService } = await import('@/services/jwt.service');
const { RoleService } = await import('@/services/role.service');
const { UserService: US } = await import('@/services/user.service');
const { UserManagementMailer } = await import(
'@/UserManagement/email/UserManagementMailer'
);
const { UsersController } = await import('@/controllers/users.controller');
registerController(
app,
config,
new UsersController(
config,
logger,
externalHooks,
internalHooks,
Container.get(ExternalHooks),
Container.get(InternalHooks),
Container.get(SharedCredentialsRepository),
Container.get(SharedWorkflowRepository),
Container.get(ActiveWorkflowRunner),
mailer,
Container.get(UserManagementMailer),
Container.get(JwtService),
Container.get(RoleService),
userService,
Container.get(US),
),
);
break;
case 'tags':
const { TagsController } = await import('@/controllers/tags.controller');
registerController(app, config, Container.get(TagsController));
break;
case 'externalSecrets':
const { ExternalSecretsController } = await import(
'@/ExternalSecrets/ExternalSecrets.controller.ee'
);
registerController(app, config, Container.get(ExternalSecretsController));
break;
case 'workflowHistory':
const { WorkflowHistoryController } = await import(
'@/workflows/workflowHistory/workflowHistory.controller.ee'
);
registerController(app, config, Container.get(WorkflowHistoryController));
break;
case 'binaryData':
const { BinaryDataController } = await import('@/controllers/binaryData.controller');
registerController(app, config, Container.get(BinaryDataController));
break;
}

View file

@ -3,13 +3,15 @@ import * as testDb from './shared/testDb';
import type { SuperAgentTest } from 'supertest';
import { TagRepository } from '@/databases/repositories';
import Container from 'typedi';
import { getGlobalOwnerRole } from './shared/db/roles';
import { createUserShell } from './shared/db/users';
let authOwnerAgent: SuperAgentTest;
const testServer = utils.setupTestServer({ endpointGroups: ['tags'] });
beforeAll(async () => {
const globalOwnerRole = await testDb.getGlobalOwnerRole();
const ownerShell = await testDb.createUserShell(globalOwnerRole);
const globalOwnerRole = await getGlobalOwnerRole();
const ownerShell = await createUserShell(globalOwnerRole);
authOwnerAgent = testServer.authAgentFor(ownerShell);
});

View file

@ -20,6 +20,10 @@ import {
} from './shared/random';
import * as testDb from './shared/testDb';
import * as utils from './shared/utils/';
import { saveCredential } from './shared/db/credentials';
import { getAllRoles } from './shared/db/roles';
import { createUser, createUserShell } from './shared/db/users';
import { createWorkflow } from './shared/db/workflows';
let globalMemberRole: Role;
let workflowOwnerRole: Role;
@ -37,13 +41,13 @@ beforeAll(async () => {
fetchedGlobalMemberRole,
fetchedWorkflowOwnerRole,
fetchedCredentialOwnerRole,
] = await testDb.getAllRoles();
] = await getAllRoles();
globalMemberRole = fetchedGlobalMemberRole;
workflowOwnerRole = fetchedWorkflowOwnerRole;
credentialOwnerRole = fetchedCredentialOwnerRole;
owner = await testDb.createUser({ globalRole: globalOwnerRole });
owner = await createUser({ globalRole: globalOwnerRole });
authOwnerAgent = testServer.authAgentFor(owner);
});
@ -57,7 +61,7 @@ beforeEach(async () => {
describe('DELETE /users/:id', () => {
test('should delete the user', async () => {
const userToDelete = await testDb.createUser({ globalRole: globalMemberRole });
const userToDelete = await createUser({ globalRole: globalMemberRole });
const newWorkflow = new WorkflowEntity();
@ -132,7 +136,7 @@ describe('DELETE /users/:id', () => {
});
test('should fail if user to delete is transferee', async () => {
const { id: idToDelete } = await testDb.createUser({ globalRole: globalMemberRole });
const { id: idToDelete } = await createUser({ globalRole: globalMemberRole });
const response = await authOwnerAgent.delete(`/users/${idToDelete}`).query({
transferId: idToDelete,
@ -145,11 +149,11 @@ describe('DELETE /users/:id', () => {
});
test('with transferId should perform transfer', async () => {
const userToDelete = await testDb.createUser({ globalRole: globalMemberRole });
const userToDelete = await createUser({ globalRole: globalMemberRole });
const savedWorkflow = await testDb.createWorkflow(undefined, userToDelete);
const savedWorkflow = await createWorkflow(undefined, userToDelete);
const savedCredential = await testDb.saveCredential(randomCredentialPayload(), {
const savedCredential = await saveCredential(randomCredentialPayload(), {
user: userToDelete,
role: credentialOwnerRole,
});
@ -184,7 +188,7 @@ describe('DELETE /users/:id', () => {
describe('POST /users/:id', () => {
test('should fill out a user shell', async () => {
const memberShell = await testDb.createUserShell(globalMemberRole);
const memberShell = await createUserShell(globalMemberRole);
const memberData = {
inviterId: owner.id,
@ -282,7 +286,7 @@ describe('POST /users/:id', () => {
});
test('should fail with already accepted invite', async () => {
const member = await testDb.createUser({ globalRole: globalMemberRole });
const member = await createUser({ globalRole: globalMemberRole });
const newMemberData = {
inviterId: owner.id,
@ -317,8 +321,8 @@ describe('POST /users', () => {
});
test('should email invites and create user shells but ignore existing', async () => {
const member = await testDb.createUser({ globalRole: globalMemberRole });
const memberShell = await testDb.createUserShell(globalMemberRole);
const member = await createUser({ globalRole: globalMemberRole });
const memberShell = await createUserShell(globalMemberRole);
const testEmails = [
randomEmail(),
@ -407,7 +411,7 @@ describe('POST /users/:id/reinvite', () => {
expect(reinviteResponse.statusCode).toBe(200);
const member = await testDb.createUser({ globalRole: globalMemberRole });
const member = await createUser({ globalRole: globalMemberRole });
const reinviteMemberResponse = await authOwnerAgent.post(`/users/${member.id}/reinvite`);
expect(reinviteMemberResponse.statusCode).toBe(400);

View file

@ -1,3 +1,4 @@
import { createMember, createOwner } from './shared/db/users';
import * as testDb from './shared/testDb';
import { setupTestServer } from './shared/utils/';
import type { User } from '@/databases/entities/User';
@ -12,8 +13,8 @@ let member: User;
beforeEach(async () => {
await testDb.truncate(['User']);
owner = await testDb.createOwner();
member = await testDb.createMember();
owner = await createOwner();
member = await createMember();
});
const validatePublicUser = (user: PublicUser) => {
@ -47,7 +48,7 @@ describe('GET /users', () => {
describe('filter', () => {
test('should filter users by field: email', async () => {
const secondMember = await testDb.createMember();
const secondMember = await createMember();
const response = await testServer
.authAgentFor(owner)
@ -71,7 +72,7 @@ describe('GET /users', () => {
});
test('should filter users by field: firstName', async () => {
const secondMember = await testDb.createMember();
const secondMember = await createMember();
const response = await testServer
.authAgentFor(owner)
@ -95,7 +96,7 @@ describe('GET /users', () => {
});
test('should filter users by field: lastName', async () => {
const secondMember = await testDb.createMember();
const secondMember = await createMember();
const response = await testServer
.authAgentFor(owner)

View file

@ -1,8 +1,13 @@
import Container from 'typedi';
import type { SuperAgentTest } from 'supertest';
import type { Variables } from '@db/entities/Variables';
import { VariablesRepository } from '@db/repositories';
import { generateNanoId } from '@db/utils/generators';
import { License } from '@/License';
import { VariablesService } from '@/environments/variables/variables.service';
import * as testDb from './shared/testDb';
import * as utils from './shared/utils/';
import { createOwner, createUser } from './shared/db/users';
let authOwnerAgent: SuperAgentTest;
let authMemberAgent: SuperAgentTest;
@ -15,12 +20,38 @@ const licenseLike = {
const testServer = utils.setupTestServer({ endpointGroups: ['variables'] });
async function createVariable(key: string, value: string) {
const result = await Container.get(VariablesRepository).save({
id: generateNanoId(),
key,
value,
});
await Container.get(VariablesService).updateCache();
return result;
}
async function getVariableByKey(key: string) {
return Container.get(VariablesRepository).findOne({
where: {
key,
},
});
}
async function getVariableById(id: string) {
return Container.get(VariablesRepository).findOne({
where: {
id,
},
});
}
beforeAll(async () => {
utils.mockInstance(License, licenseLike);
const owner = await testDb.createOwner();
const owner = await createOwner();
authOwnerAgent = testServer.authAgentFor(owner);
const member = await testDb.createUser();
const member = await createUser();
authMemberAgent = testServer.authAgentFor(member);
});
@ -35,10 +66,7 @@ beforeEach(async () => {
// ----------------------------------------
describe('GET /variables', () => {
beforeEach(async () => {
await Promise.all([
testDb.createVariable('test1', 'value1'),
testDb.createVariable('test2', 'value2'),
]);
await Promise.all([createVariable('test1', 'value1'), createVariable('test2', 'value2')]);
});
test('should return all variables for an owner', async () => {
@ -61,8 +89,8 @@ describe('GET /variables/:id', () => {
let var1: Variables, var2: Variables;
beforeEach(async () => {
[var1, var2] = await Promise.all([
testDb.createVariable('test1', 'value1'),
testDb.createVariable('test2', 'value2'),
createVariable('test1', 'value1'),
createVariable('test2', 'value2'),
]);
});
@ -104,8 +132,8 @@ describe('POST /variables', () => {
expect(response.body.data.value).toBe(toCreate.value);
const [byId, byKey] = await Promise.all([
testDb.getVariableById(response.body.data.id),
testDb.getVariableByKey(toCreate.key),
getVariableById(response.body.data.id),
getVariableByKey(toCreate.key),
]);
expect(byId).not.toBeNull();
@ -123,7 +151,7 @@ describe('POST /variables', () => {
expect(response.body.data?.key).not.toBe(toCreate.key);
expect(response.body.data?.value).not.toBe(toCreate.value);
const byKey = await testDb.getVariableByKey(toCreate.key);
const byKey = await getVariableByKey(toCreate.key);
expect(byKey).toBeNull();
});
@ -134,12 +162,12 @@ describe('POST /variables', () => {
expect(response.body.data?.key).not.toBe(toCreate.key);
expect(response.body.data?.value).not.toBe(toCreate.value);
const byKey = await testDb.getVariableByKey(toCreate.key);
const byKey = await getVariableByKey(toCreate.key);
expect(byKey).toBeNull();
});
test('should fail to create a new variable and if one with the same key exists', async () => {
await testDb.createVariable(toCreate.key, toCreate.value);
await createVariable(toCreate.key, toCreate.value);
const response = await authOwnerAgent.post('/variables').send(toCreate);
expect(response.statusCode).toBe(500);
expect(response.body.data?.key).not.toBe(toCreate.key);
@ -151,7 +179,7 @@ describe('POST /variables', () => {
let i = 1;
let toCreate = generatePayload(i);
while (i < 3) {
await testDb.createVariable(toCreate.key, toCreate.value);
await createVariable(toCreate.key, toCreate.value);
i++;
toCreate = generatePayload(i);
}
@ -166,7 +194,7 @@ describe('POST /variables', () => {
let i = 1;
let toCreate = generatePayload(i);
while (i < 6) {
await testDb.createVariable(toCreate.key, toCreate.value);
await createVariable(toCreate.key, toCreate.value);
i++;
toCreate = generatePayload(i);
}
@ -224,15 +252,15 @@ describe('PATCH /variables/:id', () => {
};
test('should modify existing variable if use is an owner', async () => {
const variable = await testDb.createVariable('test1', 'value1');
const variable = await createVariable('test1', 'value1');
const response = await authOwnerAgent.patch(`/variables/${variable.id}`).send(toModify);
expect(response.statusCode).toBe(200);
expect(response.body.data.key).toBe(toModify.key);
expect(response.body.data.value).toBe(toModify.value);
const [byId, byKey] = await Promise.all([
testDb.getVariableById(response.body.data.id),
testDb.getVariableByKey(toModify.key),
getVariableById(response.body.data.id),
getVariableByKey(toModify.key),
]);
expect(byId).not.toBeNull();
@ -245,15 +273,15 @@ describe('PATCH /variables/:id', () => {
});
test('should modify existing variable if use is an owner', async () => {
const variable = await testDb.createVariable('test1', 'value1');
const variable = await createVariable('test1', 'value1');
const response = await authOwnerAgent.patch(`/variables/${variable.id}`).send(toModify);
expect(response.statusCode).toBe(200);
expect(response.body.data.key).toBe(toModify.key);
expect(response.body.data.value).toBe(toModify.value);
const [byId, byKey] = await Promise.all([
testDb.getVariableById(response.body.data.id),
testDb.getVariableByKey(toModify.key),
getVariableById(response.body.data.id),
getVariableByKey(toModify.key),
]);
expect(byId).not.toBeNull();
@ -266,13 +294,13 @@ describe('PATCH /variables/:id', () => {
});
test('should not modify existing variable if use is a member', async () => {
const variable = await testDb.createVariable('test1', 'value1');
const variable = await createVariable('test1', 'value1');
const response = await authMemberAgent.patch(`/variables/${variable.id}`).send(toModify);
expect(response.statusCode).toBe(401);
expect(response.body.data?.key).not.toBe(toModify.key);
expect(response.body.data?.value).not.toBe(toModify.value);
const byId = await testDb.getVariableById(variable.id);
const byId = await getVariableById(variable.id);
expect(byId).not.toBeNull();
expect(byId!.key).not.toBe(toModify.key);
expect(byId!.value).not.toBe(toModify.value);
@ -280,15 +308,15 @@ describe('PATCH /variables/:id', () => {
test('should not modify existing variable if one with the same key exists', async () => {
const [var1, var2] = await Promise.all([
testDb.createVariable('test1', 'value1'),
testDb.createVariable(toModify.key, toModify.value),
createVariable('test1', 'value1'),
createVariable(toModify.key, toModify.value),
]);
const response = await authOwnerAgent.patch(`/variables/${var1.id}`).send(toModify);
expect(response.statusCode).toBe(500);
expect(response.body.data?.key).not.toBe(toModify.key);
expect(response.body.data?.value).not.toBe(toModify.value);
const byId = await testDb.getVariableById(var1.id);
const byId = await getVariableById(var1.id);
expect(byId).not.toBeNull();
expect(byId!.key).toBe(var1.key);
expect(byId!.value).toBe(var1.value);
@ -301,15 +329,15 @@ describe('PATCH /variables/:id', () => {
describe('DELETE /variables/:id', () => {
test('should delete a single variable for an owner', async () => {
const [var1, var2, var3] = await Promise.all([
testDb.createVariable('test1', 'value1'),
testDb.createVariable('test2', 'value2'),
testDb.createVariable('test3', 'value3'),
createVariable('test1', 'value1'),
createVariable('test2', 'value2'),
createVariable('test3', 'value3'),
]);
const delResponse = await authOwnerAgent.delete(`/variables/${var1.id}`);
expect(delResponse.statusCode).toBe(200);
const byId = await testDb.getVariableById(var1.id);
const byId = await getVariableById(var1.id);
expect(byId).toBeNull();
const getResponse = await authOwnerAgent.get('/variables');
@ -318,15 +346,15 @@ describe('DELETE /variables/:id', () => {
test('should not delete a single variable for a member', async () => {
const [var1, var2, var3] = await Promise.all([
testDb.createVariable('test1', 'value1'),
testDb.createVariable('test2', 'value2'),
testDb.createVariable('test3', 'value3'),
createVariable('test1', 'value1'),
createVariable('test2', 'value2'),
createVariable('test3', 'value3'),
]);
const delResponse = await authMemberAgent.delete(`/variables/${var1.id}`);
expect(delResponse.statusCode).toBe(401);
const byId = await testDb.getVariableById(var1.id);
const byId = await getVariableById(var1.id);
expect(byId).not.toBeNull();
const getResponse = await authMemberAgent.get('/variables');

View file

@ -12,6 +12,8 @@ import type { WorkflowEntity } from '@db/entities/WorkflowEntity';
import { mockInstance, initActiveWorkflowRunner } from './shared/utils';
import * as testDb from './shared/testDb';
import { createUser } from './shared/db/users';
import { createWorkflow } from './shared/db/workflows';
describe('Webhook API', () => {
mockInstance(ExternalHooks);
@ -31,8 +33,8 @@ describe('Webhook API', () => {
describe('Content-Type support', () => {
beforeAll(async () => {
const node = new WebhookTestingNode();
const user = await testDb.createUser();
await testDb.createWorkflow(createWebhookWorkflow(node), user);
const user = await createUser();
await createWorkflow(createWebhookWorkflow(node), user);
const nodeTypes = mockInstance(NodeTypes);
nodeTypes.getByName.mockReturnValue(node);
@ -134,8 +136,8 @@ describe('Webhook API', () => {
describe('Params support', () => {
beforeAll(async () => {
const node = new WebhookTestingNode();
const user = await testDb.createUser();
await testDb.createWorkflow(createWebhookWorkflow(node, ':variable', 'PATCH'), user);
const user = await createUser();
await createWorkflow(createWebhookWorkflow(node, ':variable', 'PATCH'), user);
const nodeTypes = mockInstance(NodeTypes);
nodeTypes.getByName.mockReturnValue(node);

View file

@ -4,6 +4,9 @@ import * as testDb from './shared/testDb';
import * as utils from './shared/utils/';
import type { User } from '@/databases/entities/User';
import { WorkflowHistoryRepository } from '@/databases/repositories';
import { createOwner, createUser } from './shared/db/users';
import { createWorkflow } from './shared/db/workflows';
import { createWorkflowHistoryItem } from './shared/db/workflowHistory';
let owner: User;
let authOwnerAgent: SuperAgentTest;
@ -18,9 +21,9 @@ const licenseLike = utils.mockInstance(License, {
const testServer = utils.setupTestServer({ endpointGroups: ['workflowHistory'] });
beforeAll(async () => {
owner = await testDb.createOwner();
owner = await createOwner();
authOwnerAgent = testServer.authAgentFor(owner);
member = await testDb.createUser();
member = await createUser();
authMemberAgent = testServer.authAgentFor(member);
});
@ -41,31 +44,31 @@ describe('GET /workflow-history/:workflowId', () => {
});
test('should not return anything on an invalid workflow ID', async () => {
await testDb.createWorkflow(undefined, owner);
await createWorkflow(undefined, owner);
const resp = await authOwnerAgent.get('/workflow-history/workflow/badid');
expect(resp.status).toBe(404);
});
test('should not return anything if not shared with user', async () => {
const workflow = await testDb.createWorkflow(undefined, owner);
const workflow = await createWorkflow(undefined, owner);
const resp = await authMemberAgent.get('/workflow-history/workflow/' + workflow.id);
expect(resp.status).toBe(404);
});
test('should return any empty list if no versions', async () => {
const workflow = await testDb.createWorkflow(undefined, owner);
const workflow = await createWorkflow(undefined, owner);
const resp = await authOwnerAgent.get('/workflow-history/workflow/' + workflow.id);
expect(resp.status).toBe(200);
expect(resp.body).toEqual({ data: [] });
});
test('should return versions for workflow', async () => {
const workflow = await testDb.createWorkflow(undefined, owner);
const workflow = await createWorkflow(undefined, owner);
const versions = await Promise.all(
new Array(10)
.fill(undefined)
.map(async (_, i) =>
testDb.createWorkflowHistoryItem(workflow.id, { createdAt: new Date(Date.now() + i) }),
createWorkflowHistoryItem(workflow.id, { createdAt: new Date(Date.now() + i) }),
),
);
@ -84,20 +87,18 @@ describe('GET /workflow-history/:workflowId', () => {
});
test('should return versions only for workflow id provided', async () => {
const workflow = await testDb.createWorkflow(undefined, owner);
const workflow2 = await testDb.createWorkflow(undefined, owner);
const workflow = await createWorkflow(undefined, owner);
const workflow2 = await createWorkflow(undefined, owner);
const versions = await Promise.all(
new Array(10)
.fill(undefined)
.map(async (_, i) =>
testDb.createWorkflowHistoryItem(workflow.id, { createdAt: new Date(Date.now() + i) }),
createWorkflowHistoryItem(workflow.id, { createdAt: new Date(Date.now() + i) }),
),
);
const versions2 = await Promise.all(
new Array(10)
.fill(undefined)
.map(async (_) => testDb.createWorkflowHistoryItem(workflow2.id)),
new Array(10).fill(undefined).map(async (_) => createWorkflowHistoryItem(workflow2.id)),
);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@ -115,12 +116,12 @@ describe('GET /workflow-history/:workflowId', () => {
});
test('should work with take parameter', async () => {
const workflow = await testDb.createWorkflow(undefined, owner);
const workflow = await createWorkflow(undefined, owner);
const versions = await Promise.all(
new Array(10)
.fill(undefined)
.map(async (_, i) =>
testDb.createWorkflowHistoryItem(workflow.id, { createdAt: new Date(Date.now() + i) }),
createWorkflowHistoryItem(workflow.id, { createdAt: new Date(Date.now() + i) }),
),
);
@ -139,12 +140,12 @@ describe('GET /workflow-history/:workflowId', () => {
});
test('should work with skip parameter', async () => {
const workflow = await testDb.createWorkflow(undefined, owner);
const workflow = await createWorkflow(undefined, owner);
const versions = await Promise.all(
new Array(10)
.fill(undefined)
.map(async (_, i) =>
testDb.createWorkflowHistoryItem(workflow.id, { createdAt: new Date(Date.now() + i) }),
createWorkflowHistoryItem(workflow.id, { createdAt: new Date(Date.now() + i) }),
),
);
@ -174,8 +175,8 @@ describe('GET /workflow-history/workflow/:workflowId/version/:versionId', () =>
});
test('should not return anything on an invalid workflow ID', async () => {
const workflow = await testDb.createWorkflow(undefined, owner);
const version = await testDb.createWorkflowHistoryItem(workflow.id);
const workflow = await createWorkflow(undefined, owner);
const version = await createWorkflowHistoryItem(workflow.id);
const resp = await authOwnerAgent.get(
`/workflow-history/workflow/badid/version/${version.versionId}`,
);
@ -183,8 +184,8 @@ describe('GET /workflow-history/workflow/:workflowId/version/:versionId', () =>
});
test('should not return anything on an invalid version ID', async () => {
const workflow = await testDb.createWorkflow(undefined, owner);
await testDb.createWorkflowHistoryItem(workflow.id);
const workflow = await createWorkflow(undefined, owner);
await createWorkflowHistoryItem(workflow.id);
const resp = await authOwnerAgent.get(
`/workflow-history/workflow/${workflow.id}/version/badid`,
);
@ -192,8 +193,8 @@ describe('GET /workflow-history/workflow/:workflowId/version/:versionId', () =>
});
test('should return version', async () => {
const workflow = await testDb.createWorkflow(undefined, owner);
const version = await testDb.createWorkflowHistoryItem(workflow.id);
const workflow = await createWorkflow(undefined, owner);
const version = await createWorkflowHistoryItem(workflow.id);
const resp = await authOwnerAgent.get(
`/workflow-history/workflow/${workflow.id}/version/${version.versionId}`,
);
@ -206,8 +207,8 @@ describe('GET /workflow-history/workflow/:workflowId/version/:versionId', () =>
});
test('should not return anything if not shared with user', async () => {
const workflow = await testDb.createWorkflow(undefined, owner);
const version = await testDb.createWorkflowHistoryItem(workflow.id);
const workflow = await createWorkflow(undefined, owner);
const version = await createWorkflowHistoryItem(workflow.id);
const resp = await authMemberAgent.get(
`/workflow-history/workflow/${workflow.id}/version/${version.versionId}`,
);
@ -215,9 +216,9 @@ describe('GET /workflow-history/workflow/:workflowId/version/:versionId', () =>
});
test('should not return anything if not shared with user and using workflow owned by unshared user', async () => {
const workflow = await testDb.createWorkflow(undefined, owner);
const workflowMember = await testDb.createWorkflow(undefined, member);
const version = await testDb.createWorkflowHistoryItem(workflow.id);
const workflow = await createWorkflow(undefined, owner);
const workflowMember = await createWorkflow(undefined, member);
const version = await createWorkflowHistoryItem(workflow.id);
const resp = await authMemberAgent.get(
`/workflow-history/workflow/${workflowMember.id}/version/${version.versionId}`,
);

View file

@ -9,6 +9,8 @@ import { WorkflowHistoryManager } from '@/workflows/workflowHistory/workflowHist
import * as testDb from './shared/testDb';
import { mockInstance } from './shared/utils';
import { createWorkflow } from './shared/db/workflows';
import { createManyWorkflowHistoryItems } from './shared/db/workflowHistory';
describe('Workflow History Manager', () => {
const license = mockInstance(License);
@ -98,9 +100,9 @@ describe('Workflow History Manager', () => {
});
const createWorkflowHistory = async (ageInDays = 2) => {
const workflow = await testDb.createWorkflow();
const workflow = await createWorkflow();
const time = DateTime.now().minus({ days: ageInDays }).toJSDate();
return testDb.createManyWorkflowHistoryItems(workflow.id, 10, time);
return createManyWorkflowHistoryItems(workflow.id, 10, time);
};
const pruneAndAssertCount = async (finalCount = 10, initialCount = 10) => {

View file

@ -1,3 +1,4 @@
import Container from 'typedi';
import type { SuperAgentTest } from 'supertest';
import { v4 as uuid } from 'uuid';
import type { INode } from 'n8n-workflow';
@ -5,17 +6,19 @@ import type { INode } from 'n8n-workflow';
import * as UserManagementHelpers from '@/UserManagement/UserManagementHelper';
import type { User } from '@db/entities/User';
import { getSharedWorkflowIds } from '@/WorkflowHelpers';
import { License } from '@/License';
import { WorkflowHistoryRepository } from '@/databases/repositories';
import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
import * as utils from './shared/utils/';
import * as testDb from './shared/testDb';
import { createWorkflow, getGlobalMemberRole, getGlobalOwnerRole } from './shared/testDb';
import type { SaveCredentialFunction } from './shared/types';
import { makeWorkflow } from './shared/utils/';
import { randomCredentialPayload } from './shared/random';
import { License } from '@/License';
import { WorkflowHistoryRepository } from '@/databases/repositories';
import Container from 'typedi';
import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
import { affixRoleToSaveCredential, shareCredentialWithUsers } from './shared/db/credentials';
import { getCredentialOwnerRole, getGlobalMemberRole, getGlobalOwnerRole } from './shared/db/roles';
import { createUser } from './shared/db/users';
import { createWorkflow, getWorkflowSharing, shareWorkflowWithUsers } from './shared/db/workflows';
let owner: User;
let member: User;
@ -38,19 +41,19 @@ const testServer = utils.setupTestServer({
});
beforeAll(async () => {
const globalOwnerRole = await testDb.getGlobalOwnerRole();
const globalMemberRole = await testDb.getGlobalMemberRole();
const credentialOwnerRole = await testDb.getCredentialOwnerRole();
const globalOwnerRole = await getGlobalOwnerRole();
const globalMemberRole = await getGlobalMemberRole();
const credentialOwnerRole = await getCredentialOwnerRole();
owner = await testDb.createUser({ globalRole: globalOwnerRole });
member = await testDb.createUser({ globalRole: globalMemberRole });
anotherMember = await testDb.createUser({ globalRole: globalMemberRole });
owner = await createUser({ globalRole: globalOwnerRole });
member = await createUser({ globalRole: globalMemberRole });
anotherMember = await createUser({ globalRole: globalMemberRole });
authOwnerAgent = testServer.authAgentFor(owner);
authMemberAgent = testServer.authAgentFor(member);
authAnotherMemberAgent = testServer.authAgentFor(anotherMember);
saveCredential = testDb.affixRoleToSaveCredential(credentialOwnerRole);
saveCredential = affixRoleToSaveCredential(credentialOwnerRole);
await utils.initNodeTypes();
});
@ -98,7 +101,7 @@ describe('PUT /workflows/:id', () => {
expect(response.statusCode).toBe(200);
const sharedWorkflows = await testDb.getWorkflowSharing(workflow);
const sharedWorkflows = await getWorkflowSharing(workflow);
expect(sharedWorkflows).toHaveLength(2);
});
@ -111,7 +114,7 @@ describe('PUT /workflows/:id', () => {
expect(response.statusCode).toBe(200);
const sharedWorkflows = await testDb.getWorkflowSharing(workflow);
const sharedWorkflows = await getWorkflowSharing(workflow);
expect(sharedWorkflows).toHaveLength(1);
});
@ -124,7 +127,7 @@ describe('PUT /workflows/:id', () => {
expect(response.statusCode).toBe(200);
const sharedWorkflows = await testDb.getWorkflowSharing(workflow);
const sharedWorkflows = await getWorkflowSharing(workflow);
expect(sharedWorkflows).toHaveLength(3);
});
@ -137,7 +140,7 @@ describe('PUT /workflows/:id', () => {
expect(response.statusCode).toBe(200);
const sharedWorkflows = await testDb.getWorkflowSharing(workflow);
const sharedWorkflows = await getWorkflowSharing(workflow);
expect(sharedWorkflows).toHaveLength(3);
const secondResponse = await authOwnerAgent
@ -145,7 +148,7 @@ describe('PUT /workflows/:id', () => {
.send({ shareWithIds: [member.id] });
expect(secondResponse.statusCode).toBe(200);
const secondSharedWorkflows = await testDb.getWorkflowSharing(workflow);
const secondSharedWorkflows = await getWorkflowSharing(workflow);
expect(secondSharedWorkflows).toHaveLength(2);
});
});
@ -198,7 +201,7 @@ describe('GET /workflows/:id', () => {
test('GET should return shared workflow with user data', async () => {
const workflow = await createWorkflow({}, owner);
await testDb.shareWorkflowWithUsers(workflow, [member]);
await shareWorkflowWithUsers(workflow, [member]);
const response = await authOwnerAgent.get(`/workflows/${workflow.id}`);
@ -221,7 +224,7 @@ describe('GET /workflows/:id', () => {
test('GET should return all sharees', async () => {
const workflow = await createWorkflow({}, owner);
await testDb.shareWorkflowWithUsers(workflow, [member, anotherMember]);
await shareWorkflowWithUsers(workflow, [member, anotherMember]);
const response = await authOwnerAgent.get(`/workflows/${workflow.id}`);
@ -290,7 +293,7 @@ describe('GET /workflows/:id', () => {
withCredential: { id: savedCredential.id, name: savedCredential.name },
});
const workflow = await createWorkflow(workflowPayload, member);
await testDb.shareWorkflowWithUsers(workflow, [anotherMember]);
await shareWorkflowWithUsers(workflow, [anotherMember]);
const responseMember1 = await authMemberAgent.get(`/workflows/${workflow.id}`);
expect(responseMember1.statusCode).toBe(200);
@ -318,14 +321,14 @@ describe('GET /workflows/:id', () => {
test('GET should return workflow with credentials for all users with access', async () => {
const savedCredential = await saveCredential(randomCredentialPayload(), { user: member });
// Both users have access to the credential (none is owner)
await testDb.shareCredentialWithUsers(savedCredential, [anotherMember]);
await shareCredentialWithUsers(savedCredential, [anotherMember]);
const workflowPayload = makeWorkflow({
withPinData: false,
withCredential: { id: savedCredential.id, name: savedCredential.name },
});
const workflow = await createWorkflow(workflowPayload, member);
await testDb.shareWorkflowWithUsers(workflow, [anotherMember]);
await shareWorkflowWithUsers(workflow, [anotherMember]);
const responseMember1 = await authMemberAgent.get(`/workflows/${workflow.id}`);
expect(responseMember1.statusCode).toBe(200);
@ -403,7 +406,7 @@ describe('POST /workflows', () => {
it('Should allow saving a workflow using a credential owned by others and shared with you', async () => {
const savedCredential = await saveCredential(randomCredentialPayload(), { user: member });
await testDb.shareCredentialWithUsers(savedCredential, [anotherMember]);
await shareCredentialWithUsers(savedCredential, [anotherMember]);
const workflow = makeWorkflow({
withPinData: false,
@ -927,8 +930,8 @@ describe('getSharedWorkflowIds', () => {
const workflow1 = await createWorkflow({}, anotherMember);
const workflow2 = await createWorkflow({}, anotherMember);
const workflow3 = await createWorkflow({}, anotherMember);
await testDb.shareWorkflowWithUsers(workflow1, [member]);
await testDb.shareWorkflowWithUsers(workflow3, [member]);
await shareWorkflowWithUsers(workflow1, [member]);
await shareWorkflowWithUsers(workflow3, [member]);
const sharedWorkflowIds = await getSharedWorkflowIds(member);
expect(sharedWorkflowIds).toHaveLength(2);
expect(sharedWorkflowIds).toContain(workflow1.id);
@ -939,7 +942,7 @@ describe('getSharedWorkflowIds', () => {
describe('PATCH /workflows/:id - workflow history', () => {
test('Should create workflow history version when licensed', async () => {
licenseLike.isWorkflowHistoryLicensed.mockReturnValue(true);
const workflow = await testDb.createWorkflow({}, owner);
const workflow = await createWorkflow({}, owner);
const payload = {
name: 'name updated',
versionId: workflow.versionId,
@ -997,7 +1000,7 @@ describe('PATCH /workflows/:id - workflow history', () => {
test('Should not create workflow history version when not licensed', async () => {
licenseLike.isWorkflowHistoryLicensed.mockReturnValue(false);
const workflow = await testDb.createWorkflow({}, owner);
const workflow = await createWorkflow({}, owner);
const payload = {
name: 'name updated',
versionId: workflow.versionId,
@ -1049,7 +1052,7 @@ describe('PATCH /workflows/:id - workflow history', () => {
describe('PATCH /workflows/:id - activate workflow', () => {
test('should activate workflow without changing version ID', async () => {
licenseLike.isWorkflowHistoryLicensed.mockReturnValue(false);
const workflow = await testDb.createWorkflow({}, owner);
const workflow = await createWorkflow({}, owner);
const payload = {
versionId: workflow.versionId,
active: true,
@ -1071,7 +1074,7 @@ describe('PATCH /workflows/:id - activate workflow', () => {
test('should deactivate workflow without changing version ID', async () => {
licenseLike.isWorkflowHistoryLicensed.mockReturnValue(false);
const workflow = await testDb.createWorkflow({ active: true }, owner);
const workflow = await createWorkflow({ active: true }, owner);
const payload = {
versionId: workflow.versionId,
active: false,

View file

@ -1,12 +1,7 @@
import type { SuperAgentTest } from 'supertest';
import type { INode, IPinData } from 'n8n-workflow';
import * as UserManagementHelpers from '@/UserManagement/UserManagementHelper';
import * as utils from './shared/utils/';
import * as testDb from './shared/testDb';
import { makeWorkflow, MOCK_PINDATA } from './shared/utils/';
import type { User } from '@/databases/entities/User';
import { randomCredentialPayload } from './shared/random';
import { v4 as uuid } from 'uuid';
import { RoleService } from '@/services/role.service';
import Container from 'typedi';
@ -15,6 +10,15 @@ import { License } from '@/License';
import { WorkflowHistoryRepository } from '@/databases/repositories';
import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
import * as utils from './shared/utils/';
import * as testDb from './shared/testDb';
import { makeWorkflow, MOCK_PINDATA } from './shared/utils/';
import { randomCredentialPayload } from './shared/random';
import { saveCredential } from './shared/db/credentials';
import { createOwner } from './shared/db/users';
import { createWorkflow } from './shared/db/workflows';
import { createTag } from './shared/db/tags';
let owner: User;
let authOwnerAgent: SuperAgentTest;
@ -31,7 +35,7 @@ const licenseLike = utils.mockInstance(License, {
const activeWorkflowRunnerLike = utils.mockInstance(ActiveWorkflowRunner);
beforeAll(async () => {
owner = await testDb.createOwner();
owner = await createOwner();
authOwnerAgent = testServer.authAgentFor(owner);
});
@ -172,7 +176,7 @@ describe('GET /workflows', () => {
});
test('should return workflows', async () => {
const credential = await testDb.saveCredential(randomCredentialPayload(), {
const credential = await saveCredential(randomCredentialPayload(), {
user: owner,
role: await Container.get(RoleService).findCredentialOwnerRole(),
});
@ -194,10 +198,10 @@ describe('GET /workflows', () => {
},
];
const tag = await testDb.createTag({ name: 'A' });
const tag = await createTag({ name: 'A' });
await testDb.createWorkflow({ name: 'First', nodes, tags: [tag] }, owner);
await testDb.createWorkflow({ name: 'Second' }, owner);
await createWorkflow({ name: 'First', nodes, tags: [tag] }, owner);
await createWorkflow({ name: 'Second' }, owner);
const response = await authOwnerAgent.get('/workflows').expect(200);
@ -238,8 +242,8 @@ describe('GET /workflows', () => {
describe('filter', () => {
test('should filter workflows by field: name', async () => {
await testDb.createWorkflow({ name: 'First' }, owner);
await testDb.createWorkflow({ name: 'Second' }, owner);
await createWorkflow({ name: 'First' }, owner);
await createWorkflow({ name: 'Second' }, owner);
const response = await authOwnerAgent
.get('/workflows')
@ -253,8 +257,8 @@ describe('GET /workflows', () => {
});
test('should filter workflows by field: active', async () => {
await testDb.createWorkflow({ active: true }, owner);
await testDb.createWorkflow({ active: false }, owner);
await createWorkflow({ active: true }, owner);
await createWorkflow({ active: false }, owner);
const response = await authOwnerAgent
.get('/workflows')
@ -268,10 +272,10 @@ describe('GET /workflows', () => {
});
test('should filter workflows by field: tags', async () => {
const workflow = await testDb.createWorkflow({ name: 'First' }, owner);
const workflow = await createWorkflow({ name: 'First' }, owner);
await testDb.createTag({ name: 'A' }, workflow);
await testDb.createTag({ name: 'B' }, workflow);
await createTag({ name: 'A' }, workflow);
await createTag({ name: 'B' }, workflow);
const response = await authOwnerAgent
.get('/workflows')
@ -287,8 +291,8 @@ describe('GET /workflows', () => {
describe('select', () => {
test('should select workflow field: name', async () => {
await testDb.createWorkflow({ name: 'First' }, owner);
await testDb.createWorkflow({ name: 'Second' }, owner);
await createWorkflow({ name: 'First' }, owner);
await createWorkflow({ name: 'Second' }, owner);
const response = await authOwnerAgent.get('/workflows').query('select=["name"]').expect(200);
@ -302,8 +306,8 @@ describe('GET /workflows', () => {
});
test('should select workflow field: active', async () => {
await testDb.createWorkflow({ active: true }, owner);
await testDb.createWorkflow({ active: false }, owner);
await createWorkflow({ active: true }, owner);
await createWorkflow({ active: false }, owner);
const response = await authOwnerAgent
.get('/workflows')
@ -320,11 +324,11 @@ describe('GET /workflows', () => {
});
test('should select workflow field: tags', async () => {
const firstWorkflow = await testDb.createWorkflow({ name: 'First' }, owner);
const secondWorkflow = await testDb.createWorkflow({ name: 'Second' }, owner);
const firstWorkflow = await createWorkflow({ name: 'First' }, owner);
const secondWorkflow = await createWorkflow({ name: 'Second' }, owner);
await testDb.createTag({ name: 'A' }, firstWorkflow);
await testDb.createTag({ name: 'B' }, secondWorkflow);
await createTag({ name: 'A' }, firstWorkflow);
await createTag({ name: 'B' }, secondWorkflow);
const response = await authOwnerAgent.get('/workflows').query('select=["tags"]').expect(200);
@ -343,14 +347,14 @@ describe('GET /workflows', () => {
const secondWorkflowCreatedAt = '2023-07-07T09:31:25.000Z';
const secondWorkflowUpdatedAt = '2023-07-07T09:31:40.000Z';
await testDb.createWorkflow(
await createWorkflow(
{
createdAt: new Date(firstWorkflowCreatedAt),
updatedAt: new Date(firstWorkflowUpdatedAt),
},
owner,
);
await testDb.createWorkflow(
await createWorkflow(
{
createdAt: new Date(secondWorkflowCreatedAt),
updatedAt: new Date(secondWorkflowUpdatedAt),
@ -384,8 +388,8 @@ describe('GET /workflows', () => {
const firstWorkflowVersionId = 'e95ccdde-2b4e-4fd0-8834-220a2b5b4353';
const secondWorkflowVersionId = 'd099b8dc-b1d8-4b2d-9b02-26f32c0ee785';
await testDb.createWorkflow({ versionId: firstWorkflowVersionId }, owner);
await testDb.createWorkflow({ versionId: secondWorkflowVersionId }, owner);
await createWorkflow({ versionId: firstWorkflowVersionId }, owner);
await createWorkflow({ versionId: secondWorkflowVersionId }, owner);
const response = await authOwnerAgent
.get('/workflows')
@ -402,8 +406,8 @@ describe('GET /workflows', () => {
});
test('should select workflow field: ownedBy', async () => {
await testDb.createWorkflow({}, owner);
await testDb.createWorkflow({}, owner);
await createWorkflow({}, owner);
await createWorkflow({}, owner);
const response = await authOwnerAgent
.get('/workflows')
@ -424,7 +428,7 @@ describe('GET /workflows', () => {
describe('PATCH /workflows/:id', () => {
test('should create workflow history version when licensed', async () => {
licenseLike.isWorkflowHistoryLicensed.mockReturnValue(true);
const workflow = await testDb.createWorkflow({}, owner);
const workflow = await createWorkflow({}, owner);
const payload = {
name: 'name updated',
versionId: workflow.versionId,
@ -482,7 +486,7 @@ describe('PATCH /workflows/:id', () => {
test('should not create workflow history version when not licensed', async () => {
licenseLike.isWorkflowHistoryLicensed.mockReturnValue(false);
const workflow = await testDb.createWorkflow({}, owner);
const workflow = await createWorkflow({}, owner);
const payload = {
name: 'name updated',
versionId: workflow.versionId,
@ -532,7 +536,7 @@ describe('PATCH /workflows/:id', () => {
test('should activate workflow without changing version ID', async () => {
licenseLike.isWorkflowHistoryLicensed.mockReturnValue(false);
const workflow = await testDb.createWorkflow({}, owner);
const workflow = await createWorkflow({}, owner);
const payload = {
versionId: workflow.versionId,
active: true,
@ -554,7 +558,7 @@ describe('PATCH /workflows/:id', () => {
test('should deactivate workflow without changing version ID', async () => {
licenseLike.isWorkflowHistoryLicensed.mockReturnValue(false);
const workflow = await testDb.createWorkflow({ active: true }, owner);
const workflow = await createWorkflow({ active: true }, owner);
const payload = {
versionId: workflow.versionId,
active: false,

View file

@ -15,8 +15,6 @@ import { mockInstance } from '../integration/shared/utils';
import Container from 'typedi';
describe('CredentialsHelper', () => {
const TEST_ENCRYPTION_KEY = 'test';
const mockNodesAndCredentials = mockInstance(LoadNodesAndCredentials, {
loadedNodes: {
'test.set': {

View file

@ -12,7 +12,7 @@ let telemetry: Telemetry;
describe('InternalHooks', () => {
beforeAll(() => {
telemetry = mockInstance(Telemetry);
internalHooks = new InternalHooks(telemetry, mock(), mock(), mock(), mock(), mock());
internalHooks = new InternalHooks(telemetry, mock(), mock(), mock(), mock());
});
it('Should be defined', () => {

View file

@ -22,6 +22,9 @@ import {
import * as testDb from '../integration/shared/testDb';
import type { SaveCredentialFunction } from '../integration/shared/types';
import { mockNodeTypesData } from './Helpers';
import { affixRoleToSaveCredential } from '../integration/shared/db/credentials';
import { getCredentialOwnerRole, getWorkflowOwnerRole } from '../integration/shared/db/roles';
import { createOwner, createUser } from '../integration/shared/db/users';
let mockNodeTypes: INodeTypes;
let credentialOwnerRole: Role;
@ -37,10 +40,10 @@ beforeAll(async () => {
mockNodeTypes = Container.get(NodeTypes);
credentialOwnerRole = await testDb.getCredentialOwnerRole();
workflowOwnerRole = await testDb.getWorkflowOwnerRole();
credentialOwnerRole = await getCredentialOwnerRole();
workflowOwnerRole = await getWorkflowOwnerRole();
saveCredential = testDb.affixRoleToSaveCredential(credentialOwnerRole);
saveCredential = affixRoleToSaveCredential(credentialOwnerRole);
});
beforeEach(async () => {
@ -77,7 +80,7 @@ describe('PermissionChecker.check()', () => {
});
test('should allow if requesting user is instance owner', async () => {
const owner = await testDb.createOwner();
const owner = await createOwner();
const workflow = new Workflow({
id: randomPositiveDigit().toString(),
@ -107,7 +110,7 @@ describe('PermissionChecker.check()', () => {
});
test('should allow if workflow creds are valid subset', async () => {
const [owner, member] = await Promise.all([testDb.createOwner(), testDb.createUser()]);
const [owner, member] = await Promise.all([createOwner(), createUser()]);
const ownerCred = await saveCredential(randomCred(), { user: owner });
const memberCred = await saveCredential(randomCred(), { user: member });
@ -154,7 +157,7 @@ describe('PermissionChecker.check()', () => {
});
test('should deny if workflow creds are not valid subset', async () => {
const member = await testDb.createUser();
const member = await createUser();
const memberCred = await saveCredential(randomCred(), { user: member });

View file

@ -1,13 +1,16 @@
import type { User } from '@db/entities/User';
import * as testDb from '../integration/shared/testDb';
import * as utils from '../integration/shared/utils/';
import { createWorkflow, createExecution } from '../integration/shared/testDb';
import { WorkflowRunner } from '@/WorkflowRunner';
import { WorkflowHooks, type ExecutionError, type IWorkflowExecuteHooks } from 'n8n-workflow';
import { Push } from '@/push';
import { mockInstance } from '../integration/shared/utils';
import Container from 'typedi';
import config from '@/config';
import { getGlobalOwnerRole } from '../integration/shared/db/roles';
import { createUser } from '../integration/shared/db/users';
import { createWorkflow } from '../integration/shared/db/workflows';
import { createExecution } from '../integration/shared/db/executions';
let owner: User;
let runner: WorkflowRunner;
@ -21,8 +24,8 @@ const watchers = new Watchers();
const watchedWorkflowExecuteAfter = jest.spyOn(watchers, 'workflowExecuteAfter');
beforeAll(async () => {
const globalOwnerRole = await testDb.getGlobalOwnerRole();
owner = await testDb.createUser({ globalRole: globalOwnerRole });
const globalOwnerRole = await getGlobalOwnerRole();
owner = await createUser({ globalRole: globalOwnerRole });
mockInstance(Push);
Container.set(Push, new Push());

View file

@ -4,7 +4,7 @@ import jwt from 'jsonwebtoken';
import { mock, anyObject, captor } from 'jest-mock-extended';
import type { PublicUser } from '@/Interfaces';
import type { User } from '@db/entities/User';
import { MeController } from '@/controllers';
import { MeController } from '@/controllers/me.controller';
import { AUTH_COOKIE_NAME } from '@/constants';
import { BadRequestError } from '@/ResponseHelper';
import type { AuthenticatedRequest, MeRequest } from '@/requests';

View file

@ -7,7 +7,7 @@ import type { SettingsRepository } from '@db/repositories';
import type { Config } from '@/config';
import { BadRequestError } from '@/ResponseHelper';
import type { OwnerRequest } from '@/requests';
import { OwnerController } from '@/controllers';
import { OwnerController } from '@/controllers/owner.controller';
import { badPasswords } from '../shared/testData';
import { AUTH_COOKIE_NAME } from '@/constants';
import { UserService } from '@/services/user.service';