import Container from 'typedi'; import { mock } from 'jest-mock-extended'; import { v4 as uuid } from 'uuid'; import type { INode } from 'n8n-workflow'; import { CredentialsRepository } from '@/databases/repositories/credentials.repository'; import { TagRepository } from '@/databases/repositories/tag.repository'; import { ImportService } from '@/services/import.service'; import { TagEntity } from '@/databases/entities/TagEntity'; import { WorkflowRepository } from '@/databases/repositories/workflow.repository'; import { SharedWorkflowRepository } from '@/databases/repositories/sharedWorkflow.repository'; import * as testDb from './shared/testDb'; import { mockInstance } from '../shared/mocking'; import { createMember, createOwner } from './shared/db/users'; import { createWorkflow, getAllSharedWorkflows, getWorkflowById, newWorkflow, } from './shared/db/workflows'; import type { User } from '@db/entities/User'; describe('ImportService', () => { let importService: ImportService; let tagRepository: TagRepository; let owner: User; beforeAll(async () => { await testDb.init(); owner = await createOwner(); tagRepository = Container.get(TagRepository); const credentialsRepository = mockInstance(CredentialsRepository); credentialsRepository.find.mockResolvedValue([]); importService = new ImportService(mock(), credentialsRepository, tagRepository); }); afterEach(async () => { await testDb.truncate(['Workflow', 'SharedWorkflow', 'Tag', 'WorkflowTagMapping']); }); afterAll(async () => { await testDb.terminate(); }); test('should import credless and tagless workflow', async () => { const workflowToImport = await createWorkflow(); await importService.importWorkflows([workflowToImport], owner.id); const dbWorkflow = await getWorkflowById(workflowToImport.id); if (!dbWorkflow) fail('Expected to find workflow'); expect(dbWorkflow.id).toBe(workflowToImport.id); }); test('should make user owner of imported workflow', async () => { const workflowToImport = newWorkflow(); await importService.importWorkflows([workflowToImport], owner.id); const dbSharing = await Container.get(SharedWorkflowRepository).findOneOrFail({ where: { workflowId: workflowToImport.id, userId: owner.id, role: 'workflow:owner' }, }); expect(dbSharing.userId).toBe(owner.id); }); test('should not change the owner if it already exists', async () => { const member = await createMember(); const workflowToImport = await createWorkflow(undefined, owner); await importService.importWorkflows([workflowToImport], member.id); const sharings = await getAllSharedWorkflows(); expect(sharings).toMatchObject([ expect.objectContaining({ workflowId: workflowToImport.id, userId: owner.id, role: 'workflow:owner', }), ]); }); test('should deactivate imported workflow if active', async () => { const workflowToImport = await createWorkflow({ active: true }); await importService.importWorkflows([workflowToImport], owner.id); const dbWorkflow = await getWorkflowById(workflowToImport.id); if (!dbWorkflow) fail('Expected to find workflow'); expect(dbWorkflow.active).toBe(false); }); test('should leave intact new-format credentials', async () => { const credential = { n8nApi: { id: '123', name: 'n8n API' }, }; const nodes: INode[] = [ { id: uuid(), name: 'n8n', parameters: {}, position: [0, 0], type: 'n8n-nodes-base.n8n', typeVersion: 1, credentials: credential, }, ]; const workflowToImport = await createWorkflow({ nodes }); await importService.importWorkflows([workflowToImport], owner.id); const dbWorkflow = await getWorkflowById(workflowToImport.id); if (!dbWorkflow) fail('Expected to find workflow'); expect(dbWorkflow.nodes.at(0)?.credentials).toMatchObject(credential); }); test('should set tag by identical match', async () => { const tag = Object.assign(new TagEntity(), { id: '123', createdAt: new Date(), name: 'Test', }); await tagRepository.save(tag); // tag stored const workflowToImport = await createWorkflow({ tags: [tag] }); await importService.importWorkflows([workflowToImport], owner.id); const dbWorkflow = await Container.get(WorkflowRepository).findOneOrFail({ where: { id: workflowToImport.id }, relations: ['tags'], }); expect(dbWorkflow.tags).toStrictEqual([tag]); // workflow tagged const dbTags = await tagRepository.find(); expect(dbTags).toStrictEqual([tag]); // tag matched }); test('should set tag by name match', async () => { const tag = Object.assign(new TagEntity(), { name: 'Test' }); await tagRepository.save(tag); // tag stored const workflowToImport = await createWorkflow({ tags: [tag] }); await importService.importWorkflows([workflowToImport], owner.id); const dbWorkflow = await Container.get(WorkflowRepository).findOneOrFail({ where: { id: workflowToImport.id }, relations: ['tags'], }); expect(dbWorkflow.tags).toStrictEqual([tag]); // workflow tagged const dbTags = await tagRepository.find(); expect(dbTags).toStrictEqual([tag]); // tag matched }); test('should set tag by creating if no match', async () => { const tag = Object.assign(new TagEntity(), { name: 'Test' }); // tag not stored const workflowToImport = await createWorkflow({ tags: [tag] }); await importService.importWorkflows([workflowToImport], owner.id); const dbWorkflow = await Container.get(WorkflowRepository).findOneOrFail({ where: { id: workflowToImport.id }, relations: ['tags'], }); if (!dbWorkflow.tags) fail('No tags found on workflow'); expect(dbWorkflow.tags.at(0)?.name).toBe(tag.name); // workflow tagged const dbTag = await tagRepository.findOneOrFail({ where: { name: tag.name } }); expect(dbTag.name).toBe(tag.name); // tag created }); });