refactor(core): Refactor cli command tests (no-changelog) (#9731)

This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™ 2024-06-18 10:50:39 +02:00 committed by GitHub
parent fb73ec3994
commit 2d02c73fbd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 160 additions and 250 deletions

View file

@ -14,7 +14,7 @@ import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData'
import config from '@/config'; import config from '@/config';
import type { Job, JobId, JobResponse, WebhookResponse } from '@/Queue'; import type { Job, JobId, JobResponse, WebhookResponse } from '@/Queue';
import { Queue } from '@/Queue'; import { Queue } from '@/Queue';
import { N8N_VERSION } from '@/constants'; import { N8N_VERSION, inTest } from '@/constants';
import { ExecutionRepository } from '@db/repositories/execution.repository'; import { ExecutionRepository } from '@db/repositories/execution.repository';
import { WorkflowRepository } from '@db/repositories/workflow.repository'; import { WorkflowRepository } from '@db/repositories/workflow.repository';
import type { ICredentialsOverwrite } from '@/Interfaces'; import type { ICredentialsOverwrite } from '@/Interfaces';
@ -498,7 +498,7 @@ export class Worker extends BaseCommand {
} }
// Make sure that the process does not close // Make sure that the process does not close
await new Promise(() => {}); if (!inTest) await new Promise(() => {});
} }
async catch(error: Error) { async catch(error: Error) {

View file

@ -1,39 +1,24 @@
import { Config } from '@oclif/core'; import { nanoid } from 'nanoid';
import { InternalHooks } from '@/InternalHooks'; import { InternalHooks } from '@/InternalHooks';
import { ImportCredentialsCommand } from '@/commands/import/credentials'; import { ImportCredentialsCommand } from '@/commands/import/credentials';
import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials'; import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials';
import { setupTestCommand } from '@test-integration/utils/testCommand';
import { mockInstance } from '../../shared/mocking'; import { mockInstance } from '../../shared/mocking';
import * as testDb from '../shared/testDb'; import * as testDb from '../shared/testDb';
import { getAllCredentials, getAllSharedCredentials } from '../shared/db/credentials'; import { getAllCredentials, getAllSharedCredentials } from '../shared/db/credentials';
import { createMember, createOwner } from '../shared/db/users'; import { createMember, createOwner } from '../shared/db/users';
import { getPersonalProject } from '../shared/db/projects'; import { getPersonalProject } from '../shared/db/projects';
import { nanoid } from 'nanoid';
const oclifConfig = new Config({ root: __dirname }); mockInstance(InternalHooks);
mockInstance(LoadNodesAndCredentials);
async function importCredential(argv: string[]) { const command = setupTestCommand(ImportCredentialsCommand);
const importer = new ImportCredentialsCommand(argv, oclifConfig);
await importer.init();
await importer.run();
}
beforeAll(async () => {
mockInstance(InternalHooks);
mockInstance(LoadNodesAndCredentials);
await testDb.init();
await oclifConfig.load();
});
beforeEach(async () => { beforeEach(async () => {
await testDb.truncate(['Credentials', 'SharedCredentials', 'User']); await testDb.truncate(['Credentials', 'SharedCredentials', 'User']);
}); });
afterAll(async () => {
await testDb.terminate();
});
test('import:credentials should import a credential', async () => { test('import:credentials should import a credential', async () => {
// //
// ARRANGE // ARRANGE
@ -44,9 +29,7 @@ test('import:credentials should import a credential', async () => {
// //
// ACT // ACT
// //
await importCredential([ await command.run(['--input=./test/integration/commands/importCredentials/credentials.json']);
'--input=./test/integration/commands/importCredentials/credentials.json',
]);
// //
// ASSERT // ASSERT
@ -78,7 +61,7 @@ test('import:credentials should import a credential from separated files', async
// ACT // ACT
// //
// import credential the first time, assigning it to the owner // import credential the first time, assigning it to the owner
await importCredential([ await command.run([
'--separate', '--separate',
'--input=./test/integration/commands/importCredentials/separate', '--input=./test/integration/commands/importCredentials/separate',
]); ]);
@ -117,7 +100,7 @@ test('`import:credentials --userId ...` should fail if the credential exists alr
const member = await createMember(); const member = await createMember();
// import credential the first time, assigning it to the owner // import credential the first time, assigning it to the owner
await importCredential([ await command.run([
'--input=./test/integration/commands/importCredentials/credentials.json', '--input=./test/integration/commands/importCredentials/credentials.json',
`--userId=${owner.id}`, `--userId=${owner.id}`,
]); ]);
@ -145,7 +128,7 @@ test('`import:credentials --userId ...` should fail if the credential exists alr
// Import again while updating the name we try to assign the // Import again while updating the name we try to assign the
// credential to another user. // credential to another user.
await expect( await expect(
importCredential([ command.run([
'--input=./test/integration/commands/importCredentials/credentials-updated.json', '--input=./test/integration/commands/importCredentials/credentials-updated.json',
`--userId=${member.id}`, `--userId=${member.id}`,
]), ]),
@ -188,7 +171,7 @@ test("only update credential, don't create or update owner if neither `--userId`
const memberProject = await getPersonalProject(member); const memberProject = await getPersonalProject(member);
// import credential the first time, assigning it to a member // import credential the first time, assigning it to a member
await importCredential([ await command.run([
'--input=./test/integration/commands/importCredentials/credentials.json', '--input=./test/integration/commands/importCredentials/credentials.json',
`--userId=${member.id}`, `--userId=${member.id}`,
]); ]);
@ -213,7 +196,7 @@ test("only update credential, don't create or update owner if neither `--userId`
// ACT // ACT
// //
// Import again only updating the name and omitting `--userId` // Import again only updating the name and omitting `--userId`
await importCredential([ await command.run([
'--input=./test/integration/commands/importCredentials/credentials-updated.json', '--input=./test/integration/commands/importCredentials/credentials-updated.json',
]); ]);
@ -253,7 +236,7 @@ test('`import:credential --projectId ...` should fail if the credential already
const memberProject = await getPersonalProject(member); const memberProject = await getPersonalProject(member);
// import credential the first time, assigning it to the owner // import credential the first time, assigning it to the owner
await importCredential([ await command.run([
'--input=./test/integration/commands/importCredentials/credentials.json', '--input=./test/integration/commands/importCredentials/credentials.json',
`--userId=${owner.id}`, `--userId=${owner.id}`,
]); ]);
@ -281,7 +264,7 @@ test('`import:credential --projectId ...` should fail if the credential already
// Import again while updating the name we try to assign the // Import again while updating the name we try to assign the
// credential to another user. // credential to another user.
await expect( await expect(
importCredential([ command.run([
'--input=./test/integration/commands/importCredentials/credentials-updated.json', '--input=./test/integration/commands/importCredentials/credentials-updated.json',
`--projectId=${memberProject.id}`, `--projectId=${memberProject.id}`,
]), ]),
@ -317,7 +300,7 @@ test('`import:credential --projectId ...` should fail if the credential already
test('`import:credential --projectId ... --userId ...` fails explaining that only one of the options can be used at a time', async () => { test('`import:credential --projectId ... --userId ...` fails explaining that only one of the options can be used at a time', async () => {
await expect( await expect(
importCredential([ command.run([
'--input=./test/integration/commands/importCredentials/credentials-updated.json', '--input=./test/integration/commands/importCredentials/credentials-updated.json',
`--projectId=${nanoid()}`, `--projectId=${nanoid()}`,
`--userId=${nanoid()}`, `--userId=${nanoid()}`,

View file

@ -1,39 +1,24 @@
import { Config } from '@oclif/core'; import { nanoid } from 'nanoid';
import { InternalHooks } from '@/InternalHooks'; import { InternalHooks } from '@/InternalHooks';
import { ImportWorkflowsCommand } from '@/commands/import/workflow'; import { ImportWorkflowsCommand } from '@/commands/import/workflow';
import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials'; import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials';
import { setupTestCommand } from '@test-integration/utils/testCommand';
import { mockInstance } from '../../shared/mocking'; import { mockInstance } from '../../shared/mocking';
import * as testDb from '../shared/testDb'; import * as testDb from '../shared/testDb';
import { getAllSharedWorkflows, getAllWorkflows } from '../shared/db/workflows'; import { getAllSharedWorkflows, getAllWorkflows } from '../shared/db/workflows';
import { createMember, createOwner } from '../shared/db/users'; import { createMember, createOwner } from '../shared/db/users';
import { getPersonalProject } from '../shared/db/projects'; import { getPersonalProject } from '../shared/db/projects';
import { nanoid } from 'nanoid';
const oclifConfig = new Config({ root: __dirname }); mockInstance(InternalHooks);
mockInstance(LoadNodesAndCredentials);
async function importWorkflow(argv: string[]) { const command = setupTestCommand(ImportWorkflowsCommand);
const importer = new ImportWorkflowsCommand(argv, oclifConfig);
await importer.init();
await importer.run();
}
beforeAll(async () => {
mockInstance(InternalHooks);
mockInstance(LoadNodesAndCredentials);
await testDb.init();
await oclifConfig.load();
});
beforeEach(async () => { beforeEach(async () => {
await testDb.truncate(['Workflow', 'SharedWorkflow', 'User']); await testDb.truncate(['Workflow', 'SharedWorkflow', 'User']);
}); });
afterAll(async () => {
await testDb.terminate();
});
test('import:workflow should import active workflow and deactivate it', async () => { test('import:workflow should import active workflow and deactivate it', async () => {
// //
// ARRANGE // ARRANGE
@ -44,10 +29,7 @@ test('import:workflow should import active workflow and deactivate it', async ()
// //
// ACT // ACT
// //
await importWorkflow([ await command.run(['--separate', '--input=./test/integration/commands/importWorkflows/separate']);
'--separate',
'--input=./test/integration/commands/importWorkflows/separate',
]);
// //
// ASSERT // ASSERT
@ -86,9 +68,7 @@ test('import:workflow should import active workflow from combined file and deact
// //
// ACT // ACT
// //
await importWorkflow([ await command.run(['--input=./test/integration/commands/importWorkflows/combined/combined.json']);
'--input=./test/integration/commands/importWorkflows/combined/combined.json',
]);
// //
// ASSERT // ASSERT
@ -126,7 +106,7 @@ test('`import:workflow --userId ...` should fail if the workflow exists already
const member = await createMember(); const member = await createMember();
// Import workflow the first time, assigning it to a member. // Import workflow the first time, assigning it to a member.
await importWorkflow([ await command.run([
'--input=./test/integration/commands/importWorkflows/combined-with-update/original.json', '--input=./test/integration/commands/importWorkflows/combined-with-update/original.json',
`--userId=${owner.id}`, `--userId=${owner.id}`,
]); ]);
@ -153,7 +133,7 @@ test('`import:workflow --userId ...` should fail if the workflow exists already
// Import the same workflow again, with another name but the same ID, and try // Import the same workflow again, with another name but the same ID, and try
// to assign it to the member. // to assign it to the member.
await expect( await expect(
importWorkflow([ command.run([
'--input=./test/integration/commands/importWorkflows/combined-with-update/updated.json', '--input=./test/integration/commands/importWorkflows/combined-with-update/updated.json',
`--userId=${member.id}`, `--userId=${member.id}`,
]), ]),
@ -190,7 +170,7 @@ test("only update the workflow, don't create or update the owner if `--userId` i
const memberProject = await getPersonalProject(member); const memberProject = await getPersonalProject(member);
// Import workflow the first time, assigning it to a member. // Import workflow the first time, assigning it to a member.
await importWorkflow([ await command.run([
'--input=./test/integration/commands/importWorkflows/combined-with-update/original.json', '--input=./test/integration/commands/importWorkflows/combined-with-update/original.json',
`--userId=${member.id}`, `--userId=${member.id}`,
]); ]);
@ -215,7 +195,7 @@ test("only update the workflow, don't create or update the owner if `--userId` i
// ACT // ACT
// //
// Import the same workflow again, with another name but the same ID. // Import the same workflow again, with another name but the same ID.
await importWorkflow([ await command.run([
'--input=./test/integration/commands/importWorkflows/combined-with-update/updated.json', '--input=./test/integration/commands/importWorkflows/combined-with-update/updated.json',
]); ]);
@ -249,7 +229,7 @@ test('`import:workflow --projectId ...` should fail if the credential already ex
const memberProject = await getPersonalProject(member); const memberProject = await getPersonalProject(member);
// Import workflow the first time, assigning it to a member. // Import workflow the first time, assigning it to a member.
await importWorkflow([ await command.run([
'--input=./test/integration/commands/importWorkflows/combined-with-update/original.json', '--input=./test/integration/commands/importWorkflows/combined-with-update/original.json',
`--userId=${owner.id}`, `--userId=${owner.id}`,
]); ]);
@ -276,7 +256,7 @@ test('`import:workflow --projectId ...` should fail if the credential already ex
// Import the same workflow again, with another name but the same ID, and try // Import the same workflow again, with another name but the same ID, and try
// to assign it to the member. // to assign it to the member.
await expect( await expect(
importWorkflow([ command.run([
'--input=./test/integration/commands/importWorkflows/combined-with-update/updated.json', '--input=./test/integration/commands/importWorkflows/combined-with-update/updated.json',
`--projectId=${memberProject.id}`, `--projectId=${memberProject.id}`,
]), ]),
@ -306,7 +286,7 @@ test('`import:workflow --projectId ...` should fail if the credential already ex
test('`import:workflow --projectId ... --userId ...` fails explaining that only one of the options can be used at a time', async () => { test('`import:workflow --projectId ... --userId ...` fails explaining that only one of the options can be used at a time', async () => {
await expect( await expect(
importWorkflow([ command.run([
'--input=./test/integration/commands/importWorkflows/combined-with-update/updated.json', '--input=./test/integration/commands/importWorkflows/combined-with-update/updated.json',
`--userId=${nanoid()}`, `--userId=${nanoid()}`,
`--projectId=${nanoid()}`, `--projectId=${nanoid()}`,

View file

@ -1,57 +1,37 @@
import { Reset } from '@/commands/ldap/reset'; import { Container } from 'typedi';
import { Config } from '@oclif/core'; import { v4 as uuid } from 'uuid';
import { EntityNotFoundError } from '@n8n/typeorm';
import * as testDb from '../../shared/testDb'; import { Reset } from '@/commands/ldap/reset';
import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials'; import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials';
import { mockInstance } from '../../../shared/mocking';
import { InternalHooks } from '@/InternalHooks'; import { InternalHooks } from '@/InternalHooks';
import { WorkflowRepository } from '@db/repositories/workflow.repository';
import { CredentialsRepository } from '@db/repositories/credentials.repository';
import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository';
import { getLdapSynchronizations, saveLdapSynchronization } from '@/Ldap/helpers';
import { LdapService } from '@/Ldap/ldap.service';
import { Push } from '@/push';
import { Telemetry } from '@/telemetry';
import { setupTestCommand } from '@test-integration/utils/testCommand';
import { mockInstance } from '../../../shared/mocking';
import { createLdapUser, createMember, getUserById } from '../../shared/db/users'; import { createLdapUser, createMember, getUserById } from '../../shared/db/users';
import { createWorkflow } from '../../shared/db/workflows'; import { createWorkflow } from '../../shared/db/workflows';
import { randomCredentialPayload } from '../../shared/random'; import { randomCredentialPayload } from '../../shared/random';
import { saveCredential } from '../../shared/db/credentials'; import { saveCredential } from '../../shared/db/credentials';
import Container from 'typedi';
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
import { CredentialsRepository } from '@/databases/repositories/credentials.repository';
import { EntityNotFoundError } from '@n8n/typeorm';
import { Push } from '@/push';
import { SharedWorkflowRepository } from '@/databases/repositories/sharedWorkflow.repository';
import { SharedCredentialsRepository } from '@/databases/repositories/sharedCredentials.repository';
import { createTeamProject, findProject, getPersonalProject } from '../../shared/db/projects';
import { getLdapSynchronizations, saveLdapSynchronization } from '@/Ldap/helpers';
import { createLdapConfig } from '../../shared/ldap'; import { createLdapConfig } from '../../shared/ldap';
import { LdapService } from '@/Ldap/ldap.service'; import { createTeamProject, findProject, getPersonalProject } from '../../shared/db/projects';
import { v4 as uuid } from 'uuid';
import { Telemetry } from '@/telemetry';
mockInstance(Telemetry); mockInstance(Telemetry);
const oclifConfig = new Config({ root: __dirname }); mockInstance(Push);
mockInstance(InternalHooks);
async function resetLDAP(argv: string[]) { mockInstance(LoadNodesAndCredentials);
const cmd = new Reset(argv, oclifConfig); const command = setupTestCommand(Reset);
try {
await cmd.init();
} catch (error) {
console.error(error);
throw error;
}
await cmd.run();
}
beforeAll(async () => {
mockInstance(Push);
mockInstance(InternalHooks);
mockInstance(LoadNodesAndCredentials);
await testDb.init();
await oclifConfig.load();
});
afterAll(async () => {
await testDb.terminate();
});
test('fails if neither `--userId` nor `--projectId` nor `--deleteWorkflowsAndCredentials` is passed', async () => { test('fails if neither `--userId` nor `--projectId` nor `--deleteWorkflowsAndCredentials` is passed', async () => {
await expect(resetLDAP([])).rejects.toThrowError( await expect(command.run()).rejects.toThrowError(
'You must use exactly one of `--userId`, `--projectId` or `--deleteWorkflowsAndCredentials`.', 'You must use exactly one of `--userId`, `--projectId` or `--deleteWorkflowsAndCredentials`.',
); );
}); });
@ -66,7 +46,7 @@ test.each([
])( ])(
'fails if more than one of `--userId`, `--projectId`, `--deleteWorkflowsAndCredentials` are passed', 'fails if more than one of `--userId`, `--projectId`, `--deleteWorkflowsAndCredentials` are passed',
async (...argv) => { async (...argv) => {
await expect(resetLDAP(argv)).rejects.toThrowError( await expect(command.run(argv)).rejects.toThrowError(
'You must use exactly one of `--userId`, `--projectId` or `--deleteWorkflowsAndCredentials`.', 'You must use exactly one of `--userId`, `--projectId` or `--deleteWorkflowsAndCredentials`.',
); );
}, },
@ -95,7 +75,7 @@ describe('--deleteWorkflowsAndCredentials', () => {
// //
// ACT // ACT
// //
await resetLDAP(['--deleteWorkflowsAndCredentials']); await command.run(['--deleteWorkflowsAndCredentials']);
// //
// ASSERT // ASSERT
@ -139,7 +119,7 @@ describe('--deleteWorkflowsAndCredentials', () => {
// //
// ACT // ACT
// //
await resetLDAP(['--deleteWorkflowsAndCredentials']); await command.run(['--deleteWorkflowsAndCredentials']);
// //
// ASSERT // ASSERT
@ -159,7 +139,7 @@ describe('--deleteWorkflowsAndCredentials', () => {
// //
// ACT // ACT
// //
await resetLDAP(['--deleteWorkflowsAndCredentials']); await command.run(['--deleteWorkflowsAndCredentials']);
// //
// ASSERT // ASSERT
@ -173,7 +153,7 @@ describe('--deleteWorkflowsAndCredentials', () => {
describe('--userId', () => { describe('--userId', () => {
test('fails if the user does not exist', async () => { test('fails if the user does not exist', async () => {
const userId = uuid(); const userId = uuid();
await expect(resetLDAP([`--userId=${userId}`])).rejects.toThrowError( await expect(command.run([`--userId=${userId}`])).rejects.toThrowError(
`Could not find the user with the ID ${userId} or their personalProject.`, `Could not find the user with the ID ${userId} or their personalProject.`,
); );
}); });
@ -184,7 +164,7 @@ describe('--userId', () => {
// //
const member = await createLdapUser({ role: 'global:member' }, uuid()); const member = await createLdapUser({ role: 'global:member' }, uuid());
await expect(resetLDAP([`--userId=${member.id}`])).rejects.toThrowError( await expect(command.run([`--userId=${member.id}`])).rejects.toThrowError(
`Can't migrate workflows and credentials to the user with the ID ${member.id}. That user was created via LDAP and will be deleted as well.`, `Can't migrate workflows and credentials to the user with the ID ${member.id}. That user was created via LDAP and will be deleted as well.`,
); );
}); });
@ -212,7 +192,7 @@ describe('--userId', () => {
// //
// ACT // ACT
// //
await resetLDAP([`--userId=${normalMember.id}`]); await command.run([`--userId=${normalMember.id}`]);
// //
// ASSERT // ASSERT
@ -249,7 +229,7 @@ describe('--userId', () => {
describe('--projectId', () => { describe('--projectId', () => {
test('fails if the project does not exist', async () => { test('fails if the project does not exist', async () => {
const projectId = uuid(); const projectId = uuid();
await expect(resetLDAP([`--projectId=${projectId}`])).rejects.toThrowError( await expect(command.run([`--projectId=${projectId}`])).rejects.toThrowError(
`Could not find the project with the ID ${projectId}.`, `Could not find the project with the ID ${projectId}.`,
); );
}); });
@ -261,7 +241,7 @@ describe('--projectId', () => {
const member = await createLdapUser({ role: 'global:member' }, uuid()); const member = await createLdapUser({ role: 'global:member' }, uuid());
const memberProject = await getPersonalProject(member); const memberProject = await getPersonalProject(member);
await expect(resetLDAP([`--projectId=${memberProject.id}`])).rejects.toThrowError( await expect(command.run([`--projectId=${memberProject.id}`])).rejects.toThrowError(
`Can't migrate workflows and credentials to the project with the ID ${memberProject.id}. That project is a personal project belonging to a user that was created via LDAP and will be deleted as well.`, `Can't migrate workflows and credentials to the project with the ID ${memberProject.id}. That project is a personal project belonging to a user that was created via LDAP and will be deleted as well.`,
); );
}); });
@ -289,7 +269,7 @@ describe('--projectId', () => {
// //
// ACT // ACT
// //
await resetLDAP([`--projectId=${normalMemberProject.id}`]); await command.run([`--projectId=${normalMemberProject.id}`]);
// //
// ASSERT // ASSERT
@ -346,7 +326,7 @@ describe('--projectId', () => {
// //
// ACT // ACT
// //
await resetLDAP([`--projectId=${teamProject.id}`]); await command.run([`--projectId=${teamProject.id}`]);
// //
// ASSERT // ASSERT

View file

@ -2,27 +2,18 @@ import { InternalHooks } from '@/InternalHooks';
import { License } from '@/License'; import { License } from '@/License';
import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials'; import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials';
import { ClearLicenseCommand } from '@/commands/license/clear'; import { ClearLicenseCommand } from '@/commands/license/clear';
import { Config } from '@oclif/core';
import { setupTestCommand } from '@test-integration/utils/testCommand';
import { mockInstance } from '../../shared/mocking'; import { mockInstance } from '../../shared/mocking';
const oclifConfig = new Config({ root: __dirname }); mockInstance(InternalHooks);
mockInstance(LoadNodesAndCredentials);
beforeAll(async () => { const license = mockInstance(License);
mockInstance(InternalHooks); const command = setupTestCommand(ClearLicenseCommand);
mockInstance(LoadNodesAndCredentials);
await oclifConfig.load();
});
test('license:clear invokes shutdown() to release any floating entitlements', async () => { test('license:clear invokes shutdown() to release any floating entitlements', async () => {
const cmd = new ClearLicenseCommand([], oclifConfig); await command.run();
await cmd.init();
const license = mockInstance(License);
await cmd.run();
expect(license.init).toHaveBeenCalledTimes(1); expect(license.init).toHaveBeenCalledTimes(1);
expect(license.shutdown).toHaveBeenCalledTimes(1); expect(license.shutdown).toHaveBeenCalledTimes(1);
jest.restoreAllMocks();
}); });

View file

@ -1,38 +1,34 @@
import { Container } from 'typedi';
import { Reset } from '@/commands/user-management/reset'; import { Reset } from '@/commands/user-management/reset';
import { InternalHooks } from '@/InternalHooks'; import { InternalHooks } from '@/InternalHooks';
import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials'; import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials';
import { NodeTypes } from '@/NodeTypes'; import { NodeTypes } from '@/NodeTypes';
import Container from 'typedi'; import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository';
import { CredentialsRepository } from '@db/repositories/credentials.repository';
import { CredentialsEntity } from '@db/entities/CredentialsEntity';
import { SettingsRepository } from '@db/repositories/settings.repository';
import { UserRepository } from '@db/repositories/user.repository'; import { UserRepository } from '@db/repositories/user.repository';
import { setupTestCommand } from '@test-integration/utils/testCommand';
import { mockInstance } from '../../shared/mocking'; import { mockInstance } from '../../shared/mocking';
import * as testDb from '../shared/testDb'; import * as testDb from '../shared/testDb';
import { createMember, createUser } from '../shared/db/users'; import { createMember, createUser } from '../shared/db/users';
import { createWorkflow } from '../shared/db/workflows'; import { createWorkflow } from '../shared/db/workflows';
import { SharedWorkflowRepository } from '@/databases/repositories/sharedWorkflow.repository';
import { getPersonalProject } from '../shared/db/projects'; import { getPersonalProject } from '../shared/db/projects';
import { encryptCredentialData, saveCredential } from '../shared/db/credentials'; import { encryptCredentialData, saveCredential } from '../shared/db/credentials';
import { randomCredentialPayload } from '../shared/random'; import { randomCredentialPayload } from '../shared/random';
import { SharedCredentialsRepository } from '@/databases/repositories/sharedCredentials.repository';
import { CredentialsRepository } from '@/databases/repositories/credentials.repository';
import { CredentialsEntity } from '@/databases/entities/CredentialsEntity';
import { SettingsRepository } from '@/databases/repositories/settings.repository';
beforeAll(async () => { mockInstance(InternalHooks);
mockInstance(InternalHooks); mockInstance(LoadNodesAndCredentials);
mockInstance(LoadNodesAndCredentials); mockInstance(NodeTypes);
mockInstance(NodeTypes); const command = setupTestCommand(Reset);
await testDb.init();
});
beforeEach(async () => { beforeEach(async () => {
await testDb.truncate(['User']); await testDb.truncate(['User']);
}); });
afterAll(async () => {
await testDb.terminate();
});
// eslint-disable-next-line n8n-local-rules/no-skipped-tests // eslint-disable-next-line n8n-local-rules/no-skipped-tests
test('user-management:reset should reset DB to default user state', async () => { test('user-management:reset should reset DB to default user state', async () => {
// //
@ -65,7 +61,7 @@ test('user-management:reset should reset DB to default user state', async () =>
// //
// ACT // ACT
// //
await Reset.run(); await command.run();
// //
// ASSERT // ASSERT

View file

@ -1,29 +1,20 @@
import { Config } from '@oclif/core';
import { InternalHooks } from '@/InternalHooks'; import { InternalHooks } from '@/InternalHooks';
import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials'; import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials';
import { UpdateWorkflowCommand } from '@/commands/update/workflow'; import { UpdateWorkflowCommand } from '@/commands/update/workflow';
import { setupTestCommand } from '@test-integration/utils/testCommand';
import * as testDb from '../../shared/testDb'; import * as testDb from '../../shared/testDb';
import { createWorkflowWithTrigger, getAllWorkflows } from '../../shared/db/workflows'; import { createWorkflowWithTrigger, getAllWorkflows } from '../../shared/db/workflows';
import { mockInstance } from '../../../shared/mocking'; import { mockInstance } from '../../../shared/mocking';
const oclifConfig = new Config({ root: __dirname }); mockInstance(InternalHooks);
mockInstance(LoadNodesAndCredentials);
beforeAll(async () => { const command = setupTestCommand(UpdateWorkflowCommand);
mockInstance(InternalHooks);
mockInstance(LoadNodesAndCredentials);
await testDb.init();
await oclifConfig.load();
});
beforeEach(async () => { beforeEach(async () => {
await testDb.truncate(['Workflow']); await testDb.truncate(['Workflow']);
}); });
afterAll(async () => {
await testDb.terminate();
});
test('update:workflow can activate all workflows', async () => { test('update:workflow can activate all workflows', async () => {
// //
// ARRANGE // ARRANGE
@ -37,9 +28,7 @@ test('update:workflow can activate all workflows', async () => {
// //
// ACT // ACT
// //
const updater = new UpdateWorkflowCommand(['--all', '--active=true'], oclifConfig); await command.run(['--all', '--active=true']);
await updater.init();
await updater.run();
// //
// ASSERT // ASSERT
@ -61,9 +50,7 @@ test('update:workflow can deactivate all workflows', async () => {
// //
// ACT // ACT
// //
const updater = new UpdateWorkflowCommand(['--all', '--active=false'], oclifConfig); await command.run(['--all', '--active=false']);
await updater.init();
await updater.run();
// //
// ASSERT // ASSERT
@ -87,12 +74,7 @@ test('update:workflow can activate a specific workflow', async () => {
// //
// ACT // ACT
// //
const updater = new UpdateWorkflowCommand( await command.run([`--id=${workflows[0].id}`, '--active=true']);
[`--id=${workflows[0].id}`, '--active=true'],
oclifConfig,
);
await updater.init();
await updater.run();
// //
// ASSERT // ASSERT
@ -116,12 +98,7 @@ test('update:workflow can deactivate a specific workflow', async () => {
// //
// ACT // ACT
// //
const updater = new UpdateWorkflowCommand( await command.run([`--id=${workflows[0].id}`, '--active=false']);
[`--id=${workflows[0].id}`, '--active=false'],
oclifConfig,
);
await updater.init();
await updater.run();
// //
// ASSERT // ASSERT

View file

@ -1,85 +1,51 @@
import { Config } from '@oclif/core'; import { BinaryDataService } from 'n8n-core';
import { mock } from 'jest-mock-extended';
import { Worker } from '@/commands/worker'; import { Worker } from '@/commands/worker';
import config from '@/config'; import config from '@/config';
import { Telemetry } from '@/telemetry';
import { ExternalSecretsManager } from '@/ExternalSecrets/ExternalSecretsManager.ee'; import { ExternalSecretsManager } from '@/ExternalSecrets/ExternalSecretsManager.ee';
import { BinaryDataService } from 'n8n-core';
import { CacheService } from '@/services/cache/cache.service';
import { RedisServicePubSubPublisher } from '@/services/redis/RedisServicePubSubPublisher';
import { RedisServicePubSubSubscriber } from '@/services/redis/RedisServicePubSubSubscriber';
import { MessageEventBus } from '@/eventbus/MessageEventBus/MessageEventBus'; import { MessageEventBus } from '@/eventbus/MessageEventBus/MessageEventBus';
import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials'; import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials';
import { CredentialTypes } from '@/CredentialTypes';
import { NodeTypes } from '@/NodeTypes';
import { InternalHooks } from '@/InternalHooks'; import { InternalHooks } from '@/InternalHooks';
import { PostHogClient } from '@/posthog';
import { RedisService } from '@/services/redis.service';
import { OrchestrationHandlerWorkerService } from '@/services/orchestration/worker/orchestration.handler.worker.service'; import { OrchestrationHandlerWorkerService } from '@/services/orchestration/worker/orchestration.handler.worker.service';
import { OrchestrationService } from '@/services/orchestration.service'; import { OrchestrationWorkerService } from '@/services/orchestration/worker/orchestration.worker.service';
import { License } from '@/License';
import { ExternalHooks } from '@/ExternalHooks';
import { type JobQueue, Queue } from '@/Queue';
import * as testDb from '../shared/testDb'; import { setupTestCommand } from '@test-integration/utils/testCommand';
import { mockInstance } from '../../shared/mocking'; import { mockInstance } from '../../shared/mocking';
const oclifConfig = new Config({ root: __dirname }); config.set('executions.mode', 'queue');
config.set('binaryDataManager.availableModes', 'filesystem');
mockInstance(InternalHooks);
mockInstance(LoadNodesAndCredentials);
const binaryDataService = mockInstance(BinaryDataService);
const externalHooks = mockInstance(ExternalHooks);
const externalSecretsManager = mockInstance(ExternalSecretsManager);
const license = mockInstance(License);
const messageEventBus = mockInstance(MessageEventBus);
const orchestrationHandlerWorkerService = mockInstance(OrchestrationHandlerWorkerService);
const queue = mockInstance(Queue);
const orchestrationWorkerService = mockInstance(OrchestrationWorkerService);
const command = setupTestCommand(Worker);
let eventBus: MessageEventBus; queue.getBullObjectInstance.mockReturnValue(mock<JobQueue>({ on: jest.fn() }));
beforeAll(async () => {
config.set('executions.mode', 'queue');
config.set('binaryDataManager.availableModes', 'filesystem');
mockInstance(Telemetry);
mockInstance(PostHogClient);
mockInstance(InternalHooks);
mockInstance(CacheService);
mockInstance(ExternalSecretsManager);
mockInstance(BinaryDataService);
eventBus = mockInstance(MessageEventBus);
mockInstance(LoadNodesAndCredentials);
mockInstance(CredentialTypes);
mockInstance(NodeTypes);
mockInstance(RedisService);
mockInstance(RedisServicePubSubPublisher);
mockInstance(RedisServicePubSubSubscriber);
mockInstance(OrchestrationService);
await testDb.init();
await oclifConfig.load();
});
afterAll(async () => {
await testDb.terminate();
});
test('worker initializes all its components', async () => { test('worker initializes all its components', async () => {
const worker = new Worker([], oclifConfig); const worker = await command.run();
jest.spyOn(worker, 'init');
jest.spyOn(worker, 'initLicense').mockImplementation(async () => {});
jest.spyOn(worker, 'initBinaryDataService').mockImplementation(async () => {});
jest.spyOn(worker, 'initExternalHooks').mockImplementation(async () => {});
jest.spyOn(worker, 'initExternalSecrets').mockImplementation(async () => {});
jest.spyOn(worker, 'initEventBus').mockImplementation(async () => {});
jest.spyOn(worker, 'initOrchestration');
// jest.spyOn(MessageEventBus.prototype, 'send').mockImplementation(async () => {});
jest
.spyOn(OrchestrationHandlerWorkerService.prototype, 'initSubscriber')
.mockImplementation(async () => {});
jest.spyOn(RedisServicePubSubPublisher.prototype, 'init').mockImplementation(async () => {});
jest.spyOn(worker, 'initQueue').mockImplementation(async () => {});
await worker.init();
expect(worker.queueModeId).toBeDefined(); expect(worker.queueModeId).toBeDefined();
expect(worker.queueModeId).toContain('worker'); expect(worker.queueModeId).toContain('worker');
expect(worker.queueModeId.length).toBeGreaterThan(15); expect(worker.queueModeId.length).toBeGreaterThan(15);
expect(worker.initLicense).toHaveBeenCalledTimes(1); expect(license.init).toHaveBeenCalledTimes(1);
expect(worker.initBinaryDataService).toHaveBeenCalledTimes(1); expect(binaryDataService.init).toHaveBeenCalledTimes(1);
expect(worker.initExternalHooks).toHaveBeenCalledTimes(1); expect(externalHooks.init).toHaveBeenCalledTimes(1);
expect(worker.initExternalSecrets).toHaveBeenCalledTimes(1); expect(externalSecretsManager.init).toHaveBeenCalledTimes(1);
expect(worker.initEventBus).toHaveBeenCalledTimes(1); expect(messageEventBus.initialize).toHaveBeenCalledTimes(1);
expect(worker.initOrchestration).toHaveBeenCalledTimes(1); expect(queue.init).toHaveBeenCalledTimes(1);
expect(OrchestrationHandlerWorkerService.prototype.initSubscriber).toHaveBeenCalledTimes(1); expect(queue.process).toHaveBeenCalledTimes(1);
expect(eventBus.send).toHaveBeenCalledTimes(1); expect(orchestrationWorkerService.init).toHaveBeenCalledTimes(1);
expect(worker.initQueue).toHaveBeenCalledTimes(1); expect(orchestrationHandlerWorkerService.initWithOptions).toHaveBeenCalledTimes(1);
expect(messageEventBus.send).toHaveBeenCalledTimes(1);
jest.restoreAllMocks();
}); });

View file

@ -0,0 +1,37 @@
import type { Config } from '@oclif/core';
import type { Class } from 'n8n-core';
import { mock } from 'jest-mock-extended';
import type { BaseCommand } from '@/commands/BaseCommand';
import * as testDb from '../testDb';
export const setupTestCommand = <T extends BaseCommand>(Command: Class<T>) => {
const config = mock<Config>();
config.runHook.mockResolvedValue({ successes: [], failures: [] });
// mock SIGINT/SIGTERM registration
process.once = jest.fn();
beforeAll(async () => {
await testDb.init();
});
beforeEach(() => {
jest.clearAllMocks();
});
afterAll(async () => {
await testDb.terminate();
jest.restoreAllMocks();
});
const run = async (argv: string[] = []) => {
const command = new Command(argv, config);
await command.init();
await command.run();
return command;
};
return { run };
};