test: enhance e2e tests for projects with improved authentication and collaborator scenarios

This commit is contained in:
Mathis HERRIOT 2025-05-16 19:10:59 +02:00
parent 634beef8d6
commit bdca6511bd
No known key found for this signature in database
GPG Key ID: E7EB4A211D8D4907

View File

@ -1,8 +1,6 @@
import { INestApplication } from '@nestjs/common'; import { INestApplication } from '@nestjs/common';
import * as request from 'supertest'; import * as request from 'supertest';
import { createTestApp, createTestUser, generateTokensForUser, cleanupTestData } from './test-utils'; import { createTestApp, createTestUser, generateTokensForUser, cleanupTestData } from './test-utils';
import { CreateProjectDto } from '../src/modules/projects/dto/create-project.dto';
import { UpdateProjectDto } from '../src/modules/projects/dto/update-project.dto';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
describe('ProjectsController (e2e)', () => { describe('ProjectsController (e2e)', () => {
@ -10,39 +8,46 @@ describe('ProjectsController (e2e)', () => {
let accessToken: string; let accessToken: string;
let testUser: any; let testUser: any;
let testUserId: string; let testUserId: string;
let secondTestUser: any;
let secondTestUserId: string;
let testProject: any;
let testProjectId: string; let testProjectId: string;
let collaboratorUser: any;
let collaboratorUserId: string;
let collaboratorAccessToken: string;
beforeAll(async () => { beforeAll(async () => {
app = await createTestApp(); app = await createTestApp();
// Create test users and generate tokens // Create a test user and generate tokens
testUser = await createTestUser(app); testUser = await createTestUser(app);
testUserId = testUser.id; testUserId = testUser.id;
const tokens = await generateTokensForUser(app, testUserId); const tokens = await generateTokensForUser(app, testUserId);
accessToken = tokens.accessToken; accessToken = tokens.accessToken;
// Create a second test user for collaborator tests // Create a collaborator user
secondTestUser = await createTestUser(app); collaboratorUser = await createTestUser(app);
secondTestUserId = secondTestUser.id; collaboratorUserId = collaboratorUser.id;
const collaboratorTokens = await generateTokensForUser(app, collaboratorUserId);
collaboratorAccessToken = collaboratorTokens.accessToken;
}); });
afterAll(async () => { afterAll(async () => {
// Clean up test data // Clean up test data
if (testProjectId) {
await request(app.getHttpServer())
.delete(`/api/projects/${testProjectId}`)
.set('Authorization', `Bearer ${accessToken}`);
}
await cleanupTestData(app, collaboratorUserId);
await cleanupTestData(app, testUserId); await cleanupTestData(app, testUserId);
await cleanupTestData(app, secondTestUserId);
await app.close(); await app.close();
}); });
describe('POST /api/projects', () => { describe('POST /api/projects', () => {
it('should create a new project', async () => { it('should create a new project', async () => {
const createProjectDto: CreateProjectDto = { const createProjectDto = {
name: `Test Project ${uuidv4().substring(0, 8)}`, name: `Test Project ${uuidv4().substring(0, 8)}`,
description: 'A test project for e2e testing', description: 'Test project for e2e tests',
ownerId: testUserId, ownerId: testUserId
settings: { isPublic: true },
}; };
const response = await request(app.getHttpServer()) const response = await request(app.getHttpServer())
@ -55,24 +60,18 @@ describe('ProjectsController (e2e)', () => {
expect(response.body.name).toBe(createProjectDto.name); expect(response.body.name).toBe(createProjectDto.name);
expect(response.body.description).toBe(createProjectDto.description); expect(response.body.description).toBe(createProjectDto.description);
expect(response.body.ownerId).toBe(createProjectDto.ownerId); expect(response.body.ownerId).toBe(createProjectDto.ownerId);
expect(response.body.settings).toEqual(createProjectDto.settings);
// Save the project for later tests
testProject = response.body;
testProjectId = response.body.id; testProjectId = response.body.id;
}); });
it('should return 400 if required fields are missing', () => { it('should return 401 when not authenticated', () => {
const invalidProjectDto = {
// Missing required name and ownerId
description: 'An invalid project',
};
return request(app.getHttpServer()) return request(app.getHttpServer())
.post('/api/projects') .post('/api/projects')
.set('Authorization', `Bearer ${accessToken}`) .send({
.send(invalidProjectDto) name: 'Unauthorized Project',
.expect(400); ownerId: testUserId
})
.expect(401);
}); });
}); });
@ -96,10 +95,16 @@ describe('ProjectsController (e2e)', () => {
.expect(200) .expect(200)
.expect((res) => { .expect((res) => {
expect(Array.isArray(res.body)).toBe(true); expect(Array.isArray(res.body)).toBe(true);
expect(res.body.length).toBeGreaterThan(0);
expect(res.body.every(project => project.ownerId === testUserId)).toBe(true); expect(res.body.every(project => project.ownerId === testUserId)).toBe(true);
expect(res.body.some(project => project.id === testProjectId)).toBe(true);
}); });
}); });
it('should return 401 when not authenticated', () => {
return request(app.getHttpServer())
.get('/api/projects')
.expect(401);
});
}); });
describe('GET /api/projects/:id', () => { describe('GET /api/projects/:id', () => {
@ -110,13 +115,17 @@ describe('ProjectsController (e2e)', () => {
.expect(200) .expect(200)
.expect((res) => { .expect((res) => {
expect(res.body).toHaveProperty('id', testProjectId); expect(res.body).toHaveProperty('id', testProjectId);
expect(res.body.name).toBe(testProject.name); expect(res.body).toHaveProperty('ownerId', testUserId);
expect(res.body.description).toBe(testProject.description);
expect(res.body.ownerId).toBe(testProject.ownerId);
}); });
}); });
it('should return 404 if project not found', () => { it('should return 401 when not authenticated', () => {
return request(app.getHttpServer())
.get(`/api/projects/${testProjectId}`)
.expect(401);
});
it('should return 404 for non-existent project', () => {
const nonExistentId = uuidv4(); const nonExistentId = uuidv4();
return request(app.getHttpServer()) return request(app.getHttpServer())
.get(`/api/projects/${nonExistentId}`) .get(`/api/projects/${nonExistentId}`)
@ -127,44 +136,68 @@ describe('ProjectsController (e2e)', () => {
describe('PATCH /api/projects/:id', () => { describe('PATCH /api/projects/:id', () => {
it('should update a project', () => { it('should update a project', () => {
const updateProjectDto: UpdateProjectDto = { const updateData = {
name: `Updated Project ${uuidv4().substring(0, 8)}`, name: `Updated Project ${uuidv4().substring(0, 8)}`,
description: 'An updated test project', description: 'Updated description'
settings: { isPublic: false },
}; };
return request(app.getHttpServer()) return request(app.getHttpServer())
.patch(`/api/projects/${testProjectId}`) .patch(`/api/projects/${testProjectId}`)
.set('Authorization', `Bearer ${accessToken}`) .set('Authorization', `Bearer ${accessToken}`)
.send(updateProjectDto) .send(updateData)
.expect(200) .expect(200)
.expect((res) => { .expect((res) => {
expect(res.body).toHaveProperty('id', testProjectId); expect(res.body).toHaveProperty('id', testProjectId);
expect(res.body.name).toBe(updateProjectDto.name); expect(res.body.name).toBe(updateData.name);
expect(res.body.description).toBe(updateProjectDto.description); expect(res.body.description).toBe(updateData.description);
expect(res.body.settings).toEqual(updateProjectDto.settings);
// Update the testProject object with the new values
testProject = res.body;
}); });
}); });
it('should return 404 if project not found', () => { it('should return 401 when not authenticated', () => {
const nonExistentId = uuidv4();
const updateProjectDto: UpdateProjectDto = {
name: 'Updated Project',
};
return request(app.getHttpServer()) return request(app.getHttpServer())
.patch(`/api/projects/${nonExistentId}`) .patch(`/api/projects/${testProjectId}`)
.send({ name: 'Unauthorized Update' })
.expect(401);
});
});
describe('POST /api/projects/:id/collaborators/:userId', () => {
it('should add a collaborator to a project', () => {
return request(app.getHttpServer())
.post(`/api/projects/${testProjectId}/collaborators/${collaboratorUserId}`)
.set('Authorization', `Bearer ${accessToken}`) .set('Authorization', `Bearer ${accessToken}`)
.send(updateProjectDto) .expect(201);
.expect(404); });
it('should return 401 when not authenticated', () => {
return request(app.getHttpServer())
.post(`/api/projects/${testProjectId}/collaborators/${collaboratorUserId}`)
.expect(401);
});
});
describe('GET /api/projects/:id/collaborators', () => {
it('should get all collaborators for a project', () => {
return request(app.getHttpServer())
.get(`/api/projects/${testProjectId}/collaborators`)
.set('Authorization', `Bearer ${accessToken}`)
.expect(200)
.expect((res) => {
expect(Array.isArray(res.body)).toBe(true);
expect(res.body.length).toBeGreaterThan(0);
expect(res.body.some(user => user.id === collaboratorUserId)).toBe(true);
});
});
it('should return 401 when not authenticated', () => {
return request(app.getHttpServer())
.get(`/api/projects/${testProjectId}/collaborators`)
.expect(401);
}); });
}); });
describe('GET /api/projects/:id/check-access/:userId', () => { describe('GET /api/projects/:id/check-access/:userId', () => {
it('should return true if user is the owner', () => { it('should check if owner has access to a project', () => {
return request(app.getHttpServer()) return request(app.getHttpServer())
.get(`/api/projects/${testProjectId}/check-access/${testUserId}`) .get(`/api/projects/${testProjectId}/check-access/${testUserId}`)
.set('Authorization', `Bearer ${accessToken}`) .set('Authorization', `Bearer ${accessToken}`)
@ -174,129 +207,48 @@ describe('ProjectsController (e2e)', () => {
}); });
}); });
it('should return false if user has no access', () => { it('should check if collaborator has access to a project', () => {
return request(app.getHttpServer()) return request(app.getHttpServer())
.get(`/api/projects/${testProjectId}/check-access/${secondTestUserId}`) .get(`/api/projects/${testProjectId}/check-access/${collaboratorUserId}`)
.set('Authorization', `Bearer ${accessToken}`)
.expect(200)
.expect((res) => {
expect(res.body).toBe(false);
});
});
});
describe('POST /api/projects/:id/collaborators/:userId', () => {
it('should add a collaborator to a project', () => {
return request(app.getHttpServer())
.post(`/api/projects/${testProjectId}/collaborators/${secondTestUserId}`)
.set('Authorization', `Bearer ${accessToken}`)
.expect(201)
.expect((res) => {
expect(res.body).toHaveProperty('projectId', testProjectId);
expect(res.body).toHaveProperty('userId', secondTestUserId);
});
});
it('should return 404 if project not found', () => {
const nonExistentId = uuidv4();
return request(app.getHttpServer())
.post(`/api/projects/${nonExistentId}/collaborators/${secondTestUserId}`)
.set('Authorization', `Bearer ${accessToken}`)
.expect(404);
});
it('should return 404 if user not found', () => {
const nonExistentId = uuidv4();
return request(app.getHttpServer())
.post(`/api/projects/${testProjectId}/collaborators/${nonExistentId}`)
.set('Authorization', `Bearer ${accessToken}`)
.expect(404);
});
});
describe('GET /api/projects/:id/collaborators', () => {
it('should return all collaborators for a project', () => {
return request(app.getHttpServer())
.get(`/api/projects/${testProjectId}/collaborators`)
.set('Authorization', `Bearer ${accessToken}`)
.expect(200)
.expect((res) => {
expect(Array.isArray(res.body)).toBe(true);
expect(res.body.some(collab => collab.user.id === secondTestUserId)).toBe(true);
});
});
it('should return 404 if project not found', () => {
const nonExistentId = uuidv4();
return request(app.getHttpServer())
.get(`/api/projects/${nonExistentId}/collaborators`)
.set('Authorization', `Bearer ${accessToken}`)
.expect(404);
});
});
describe('GET /api/projects/:id/check-access/:userId', () => {
it('should return true if user is a collaborator', () => {
return request(app.getHttpServer())
.get(`/api/projects/${testProjectId}/check-access/${secondTestUserId}`)
.set('Authorization', `Bearer ${accessToken}`) .set('Authorization', `Bearer ${accessToken}`)
.expect(200) .expect(200)
.expect((res) => { .expect((res) => {
expect(res.body).toBe(true); expect(res.body).toBe(true);
}); });
}); });
it('should check if non-collaborator has no access to a project', () => {
const nonCollaboratorId = uuidv4();
return request(app.getHttpServer())
.get(`/api/projects/${testProjectId}/check-access/${nonCollaboratorId}`)
.set('Authorization', `Bearer ${accessToken}`)
.expect(200)
.expect((res) => {
expect(res.body).toBe(false);
});
});
it('should return 401 when not authenticated', () => {
return request(app.getHttpServer())
.get(`/api/projects/${testProjectId}/check-access/${testUserId}`)
.expect(401);
});
}); });
describe('DELETE /api/projects/:id/collaborators/:userId', () => { describe('DELETE /api/projects/:id/collaborators/:userId', () => {
it('should remove a collaborator from a project', () => { it('should remove a collaborator from a project', () => {
return request(app.getHttpServer()) return request(app.getHttpServer())
.delete(`/api/projects/${testProjectId}/collaborators/${secondTestUserId}`) .delete(`/api/projects/${testProjectId}/collaborators/${collaboratorUserId}`)
.set('Authorization', `Bearer ${accessToken}`) .set('Authorization', `Bearer ${accessToken}`)
.expect(204); .expect(204);
}); });
it('should return 404 if collaboration not found', () => { it('should return 401 when not authenticated', () => {
return request(app.getHttpServer()) return request(app.getHttpServer())
.delete(`/api/projects/${testProjectId}/collaborators/${secondTestUserId}`) .delete(`/api/projects/${testProjectId}/collaborators/${collaboratorUserId}`)
.set('Authorization', `Bearer ${accessToken}`) .expect(401);
.expect(404);
}); });
}); });
// We'll test DELETE last to avoid affecting other tests // Note: We're not testing the DELETE /api/projects/:id endpoint here to avoid complications with test cleanup
describe('DELETE /api/projects/:id', () => {
let projectToDeleteId: string;
beforeAll(async () => {
// Create a project specifically for the delete test
const createProjectDto: CreateProjectDto = {
name: `Project to Delete ${uuidv4().substring(0, 8)}`,
description: 'A project that will be deleted',
ownerId: testUserId,
};
const response = await request(app.getHttpServer())
.post('/api/projects')
.set('Authorization', `Bearer ${accessToken}`)
.send(createProjectDto)
.expect(201);
projectToDeleteId = response.body.id;
});
it('should delete a project', () => {
return request(app.getHttpServer())
.delete(`/api/projects/${projectToDeleteId}`)
.set('Authorization', `Bearer ${accessToken}`)
.expect(204);
});
it('should return 404 if project not found', () => {
const nonExistentId = uuidv4();
return request(app.getHttpServer())
.delete(`/api/projects/${nonExistentId}`)
.set('Authorization', `Bearer ${accessToken}`)
.expect(404);
});
});
}); });