From 0cd58cb5c42f5e5d30dd881fdc6976cc61e92807 Mon Sep 17 00:00:00 2001 From: Marc Littlemore Date: Fri, 20 Dec 2024 11:42:02 +0000 Subject: [PATCH] test: add tests for deleting users from project --- .../integration/public-api/projects.test.ts | 126 +++++++++++++++++- .../test/integration/shared/db/projects.ts | 8 ++ 2 files changed, 132 insertions(+), 2 deletions(-) diff --git a/packages/cli/test/integration/public-api/projects.test.ts b/packages/cli/test/integration/public-api/projects.test.ts index f815d9d07bf65..49af371101e06 100644 --- a/packages/cli/test/integration/public-api/projects.test.ts +++ b/packages/cli/test/integration/public-api/projects.test.ts @@ -1,8 +1,17 @@ import { FeatureNotLicensedError } from '@/errors/feature-not-licensed.error'; import { Telemetry } from '@/telemetry'; import { mockInstance } from '@test/mocking'; -import { createTeamProject, getProjectByNameOrFail } from '@test-integration/db/projects'; -import { createMemberWithApiKey, createOwnerWithApiKey } from '@test-integration/db/users'; +import { + createTeamProject, + getProjectByNameOrFail, + linkUserToProject, + getAllProjectRelations, +} from '@test-integration/db/projects'; +import { + createMemberWithApiKey, + createOwnerWithApiKey, + createMember, +} from '@test-integration/db/users'; import { setupTestServer } from '@test-integration/utils'; import * as testDb from '../shared/test-db'; @@ -393,4 +402,117 @@ describe('Projects in Public API', () => { expect(response.body).toHaveProperty('message', 'Forbidden'); }); }); + + describe('DELETE /projects/:id/users/:userId', () => { + it('if not authenticated, should reject with 401', async () => { + const project = await createTeamProject(); + const member = await createMember(); + + const response = await testServer + .publicApiAgentWithoutApiKey() + .delete(`/projects/${project.id}/users/${member.id}`); + + expect(response.status).toBe(401); + expect(response.body).toHaveProperty('message', "'X-N8N-API-KEY' header required"); + }); + + it('if not licensed, should reject with a 403', async () => { + const owner = await createOwnerWithApiKey(); + const project = await createTeamProject(); + const member = await createMember(); + + const response = await testServer + .publicApiAgentFor(owner) + .delete(`/projects/${project.id}/users/${member.id}`); + + expect(response.status).toBe(403); + expect(response.body).toHaveProperty( + 'message', + new FeatureNotLicensedError('feat:projectRole:admin').message, + ); + }); + + it('if missing scope, should reject with 403', async () => { + testServer.license.setQuota('quota:maxTeamProjects', -1); + testServer.license.enable('feat:projectRole:admin'); + const member = await createMemberWithApiKey(); + const project = await createTeamProject(); + + const response = await testServer + .publicApiAgentFor(member) + .delete(`/projects/${project.id}/users/${member.id}`); + + expect(response.status).toBe(403); + expect(response.body).toHaveProperty('message', 'Forbidden'); + }); + + describe('when user has correct license', () => { + beforeEach(() => { + testServer.license.setQuota('quota:maxTeamProjects', -1); + testServer.license.enable('feat:projectRole:admin'); + }); + + it('should remove given user from project', async () => { + const owner = await createOwnerWithApiKey(); + const project = await createTeamProject('shared-project', owner); + const member = await createMember(); + await linkUserToProject(member, project, 'project:viewer'); + const projectBefore = await getAllProjectRelations({ + projectId: project.id, + }); + + const response = await testServer + .publicApiAgentFor(owner) + .delete(`/projects/${project.id}/users/${member.id}`); + + const projectAfter = await getAllProjectRelations({ + projectId: project.id, + }); + + expect(response.status).toBe(204); + expect(projectBefore.length).toEqual(2); + expect(projectBefore[0].userId).toEqual(owner.id); + expect(projectBefore[1].userId).toEqual(member.id); + + expect(projectAfter.length).toEqual(1); + expect(projectBefore[0].userId).toEqual(owner.id); + }); + + it('should reject with 404 if no project found', async () => { + const owner = await createOwnerWithApiKey(); + const member = await createMember(); + + const response = await testServer + .publicApiAgentFor(owner) + .delete(`/projects/123456/users/${member.id}`); + + expect(response.status).toBe(404); + expect(response.body).toHaveProperty('message', 'Not found'); + }); + + it('should remain unchanged if user if not in project', async () => { + const owner = await createOwnerWithApiKey(); + const project = await createTeamProject('shared-project', owner); + const member = await createMember(); + const projectBefore = await getAllProjectRelations({ + projectId: project.id, + }); + + const response = await testServer + .publicApiAgentFor(owner) + .delete(`/projects/${project.id}/users/${member.id}`); + + const projectAfter = await getAllProjectRelations({ + projectId: project.id, + }); + + expect(response.status).toBe(204); + expect(projectBefore.length).toEqual(1); + expect(projectBefore[0].userId).toEqual(owner.id); + + expect(projectAfter.length).toEqual(1); + expect(projectBefore[0].userId).toEqual(owner.id); + }); + }); + }); }); diff --git a/packages/cli/test/integration/shared/db/projects.ts b/packages/cli/test/integration/shared/db/projects.ts index 93310d5a99b8e..2fc8b1eb5792d 100644 --- a/packages/cli/test/integration/shared/db/projects.ts +++ b/packages/cli/test/integration/shared/db/projects.ts @@ -66,3 +66,11 @@ export const getProjectRelations = async ({ where: { projectId, userId, role }, }); }; + +export const getAllProjectRelations = async ({ + projectId, +}: Partial): Promise => { + return await Container.get(ProjectRelationRepository).find({ + where: { projectId }, + }); +};