From ab4116362d90dc5806817cd5cd43646280312b76 Mon Sep 17 00:00:00 2001 From: Danny Martini Date: Tue, 20 Feb 2024 12:29:57 +0100 Subject: [PATCH 1/2] fix ProjectRepository.getPersonalProjectForUser[OrFail] It was not taking the type of the project and the role the user has to the project into account. --- .../repositories/project.repository.ts | 8 +- .../repositories/project.repository.test.ts | 143 ++++++++++++++++++ .../cli/test/integration/shared/testDb.ts | 3 +- 3 files changed, 151 insertions(+), 3 deletions(-) create mode 100644 packages/cli/test/integration/database/repositories/project.repository.test.ts diff --git a/packages/cli/src/databases/repositories/project.repository.ts b/packages/cli/src/databases/repositories/project.repository.ts index 93fa393923664..c02535dc69665 100644 --- a/packages/cli/src/databases/repositories/project.repository.ts +++ b/packages/cli/src/databases/repositories/project.repository.ts @@ -9,10 +9,14 @@ export class ProjectRepository extends Repository { } async getPersonalProjectForUser(userId: string) { - return await this.findOne({ where: { projectRelations: { userId } } }); + return await this.findOne({ + where: { type: 'personal', projectRelations: { userId, role: 'project:personalOwner' } }, + }); } async getPersonalProjectForUserOrFail(userId: string) { - return await this.findOneOrFail({ where: { projectRelations: { userId } } }); + return await this.findOneOrFail({ + where: { type: 'personal', projectRelations: { userId, role: 'project:personalOwner' } }, + }); } } diff --git a/packages/cli/test/integration/database/repositories/project.repository.test.ts b/packages/cli/test/integration/database/repositories/project.repository.test.ts new file mode 100644 index 0000000000000..eebb81377a8c9 --- /dev/null +++ b/packages/cli/test/integration/database/repositories/project.repository.test.ts @@ -0,0 +1,143 @@ +import Container from 'typedi'; +import { createOwner } from '../../shared/db/users'; +import * as testDb from '../../shared/testDb'; +import { ProjectRepository } from '@/databases/repositories/project.repository'; +import type { DeepPartial } from 'ts-essentials'; +import type { Project } from '@/databases/entities/Project'; +import { ProjectRelationRepository } from '@/databases/repositories/projectRelation.repository'; +import { User } from '@/databases/entities/User'; +import { createPromptModule } from 'inquirer'; +import { ApplicationError } from 'n8n-workflow'; +import { EntityNotFoundError } from '@n8n/typeorm'; + +describe('ProjectRepository', () => { + beforeAll(async () => { + await testDb.init(); + }); + + beforeEach(async () => { + await testDb.truncate(['User', 'Workflow', 'Project']); + }); + + afterAll(async () => { + await testDb.terminate(); + }); + + async function createProject(user: User, project?: DeepPartial) { + const projectRepository = Container.get(ProjectRepository); + const projectRelationRepository = Container.get(ProjectRelationRepository); + + const savedProject = await projectRepository.save( + projectRepository.create({ + name: 'project name', + type: 'team', + ...project, + }), + ); + await projectRelationRepository.save( + projectRelationRepository.create({ + userId: user.id, + projectId: savedProject.id, + role: 'project:personalOwner', + }), + ); + + return savedProject; + } + + describe('getPersonalProjectForUser', () => { + it('returns the personal project', async () => { + // + // ARRANGE + // + const owner = await createOwner(); + const ownerPersonalProject = await Container.get(ProjectRepository).findOneByOrFail({ + projectRelations: { userId: owner.id }, + }); + + // + // ACT + // + const personalProject = await Container.get(ProjectRepository).getPersonalProjectForUser( + owner.id, + ); + + // + // ASSERT + // + if (!personalProject) { + fail('Expected personalProject to be defined.'); + } + expect(personalProject).toBeDefined(); + expect(personalProject.id).toBe(ownerPersonalProject.id); + }); + + it('does not return non personal projects', async () => { + // + // ARRANGE + // + const owner = await createOwner(); + await Container.get(ProjectRepository).delete({}); + await createProject(owner); + + // + // ACT + // + const personalProject = await Container.get(ProjectRepository).getPersonalProjectForUser( + owner.id, + ); + + // + // ASSERT + // + expect(personalProject).toBeNull(); + }); + }); + + describe('getPersonalProjectForUserOrFail', () => { + it('returns the personal project', async () => { + // + // ARRANGE + // + const owner = await createOwner(); + const ownerPersonalProject = await Container.get(ProjectRepository).findOneByOrFail({ + projectRelations: { userId: owner.id }, + }); + + // + // ACT + // + const personalProject = await Container.get( + ProjectRepository, + ).getPersonalProjectForUserOrFail(owner.id); + + // + // ASSERT + // + if (!personalProject) { + fail('Expected personalProject to be defined.'); + } + expect(personalProject).toBeDefined(); + expect(personalProject.id).toBe(ownerPersonalProject.id); + }); + + it('does not return non personal projects', async () => { + // + // ARRANGE + // + const owner = await createOwner(); + await Container.get(ProjectRepository).delete({}); + await createProject(owner); + + // + // ACT + // + const promise = Container.get(ProjectRepository).getPersonalProjectForUserOrFail(owner.id); + + // + // ASSERT + // + await expect(promise).rejects.toThrowError(EntityNotFoundError); + }); + }); +}); diff --git a/packages/cli/test/integration/shared/testDb.ts b/packages/cli/test/integration/shared/testDb.ts index 4158daccfe5a2..45f4873074a54 100644 --- a/packages/cli/test/integration/shared/testDb.ts +++ b/packages/cli/test/integration/shared/testDb.ts @@ -93,11 +93,12 @@ const repositories = [ 'AuthProviderSyncHistory', 'Credentials', 'EventDestinations', + 'Execution', 'ExecutionData', 'ExecutionMetadata', - 'Execution', 'InstalledNodes', 'InstalledPackages', + 'Project', 'Role', 'Settings', 'SharedCredentials', From a41aba231159f98701c214f164c56376b9a18637 Mon Sep 17 00:00:00 2001 From: Danny Martini Date: Tue, 20 Feb 2024 12:37:27 +0100 Subject: [PATCH 2/2] extract project creation test helper --- .../repositories/project.repository.test.ts | 29 +------------------ .../test/integration/shared/db/projects.ts | 28 ++++++++++++++++++ 2 files changed, 29 insertions(+), 28 deletions(-) create mode 100644 packages/cli/test/integration/shared/db/projects.ts diff --git a/packages/cli/test/integration/database/repositories/project.repository.test.ts b/packages/cli/test/integration/database/repositories/project.repository.test.ts index eebb81377a8c9..bf6e779db62f3 100644 --- a/packages/cli/test/integration/database/repositories/project.repository.test.ts +++ b/packages/cli/test/integration/database/repositories/project.repository.test.ts @@ -2,13 +2,8 @@ import Container from 'typedi'; import { createOwner } from '../../shared/db/users'; import * as testDb from '../../shared/testDb'; import { ProjectRepository } from '@/databases/repositories/project.repository'; -import type { DeepPartial } from 'ts-essentials'; -import type { Project } from '@/databases/entities/Project'; -import { ProjectRelationRepository } from '@/databases/repositories/projectRelation.repository'; -import { User } from '@/databases/entities/User'; -import { createPromptModule } from 'inquirer'; -import { ApplicationError } from 'n8n-workflow'; import { EntityNotFoundError } from '@n8n/typeorm'; +import { createProject } from '../../shared/db/projects'; describe('ProjectRepository', () => { beforeAll(async () => { @@ -23,28 +18,6 @@ describe('ProjectRepository', () => { await testDb.terminate(); }); - async function createProject(user: User, project?: DeepPartial) { - const projectRepository = Container.get(ProjectRepository); - const projectRelationRepository = Container.get(ProjectRelationRepository); - - const savedProject = await projectRepository.save( - projectRepository.create({ - name: 'project name', - type: 'team', - ...project, - }), - ); - await projectRelationRepository.save( - projectRelationRepository.create({ - userId: user.id, - projectId: savedProject.id, - role: 'project:personalOwner', - }), - ); - - return savedProject; - } - describe('getPersonalProjectForUser', () => { it('returns the personal project', async () => { // diff --git a/packages/cli/test/integration/shared/db/projects.ts b/packages/cli/test/integration/shared/db/projects.ts new file mode 100644 index 0000000000000..d8f92810566ed --- /dev/null +++ b/packages/cli/test/integration/shared/db/projects.ts @@ -0,0 +1,28 @@ +import Container from 'typedi'; +import { ProjectRepository } from '@/databases/repositories/project.repository'; +import type { DeepPartial } from 'ts-essentials'; +import type { Project } from '@/databases/entities/Project'; +import { ProjectRelationRepository } from '@/databases/repositories/projectRelation.repository'; +import type { User } from '@/databases/entities/User'; + +export async function createProject(user: User, project?: DeepPartial) { + const projectRepository = Container.get(ProjectRepository); + const projectRelationRepository = Container.get(ProjectRelationRepository); + + const savedProject = await projectRepository.save( + projectRepository.create({ + name: 'project name', + type: 'team', + ...project, + }), + ); + await projectRelationRepository.save( + projectRelationRepository.create({ + userId: user.id, + projectId: savedProject.id, + role: 'project:personalOwner', + }), + ); + + return savedProject; +}