import { INestApplication } from '@nestjs/common'; import * as request from 'supertest'; import { createTestApp, createTestUser, generateTokensForUser, cleanupTestData } from './test-utils'; import { v4 as uuidv4 } from 'uuid'; describe('ProjectsController (e2e)', () => { let app: INestApplication; let accessToken: string; let testUser: any; let testUserId: string; let testProjectId: string; let collaboratorUser: any; let collaboratorUserId: string; let collaboratorAccessToken: string; beforeAll(async () => { app = await createTestApp(); // Create a test user and generate tokens testUser = await createTestUser(app); testUserId = testUser.id; const tokens = await generateTokensForUser(app, testUserId); accessToken = tokens.accessToken; // Create a collaborator user collaboratorUser = await createTestUser(app); collaboratorUserId = collaboratorUser.id; const collaboratorTokens = await generateTokensForUser(app, collaboratorUserId); collaboratorAccessToken = collaboratorTokens.accessToken; }); afterAll(async () => { // 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 app.close(); }); describe('POST /api/projects', () => { it('should create a new project', async () => { const createProjectDto = { name: `Test Project ${uuidv4().substring(0, 8)}`, description: 'Test project for e2e tests', ownerId: testUserId }; const response = await request(app.getHttpServer()) .post('/api/projects') .set('Authorization', `Bearer ${accessToken}`) .send(createProjectDto) .expect(201); expect(response.body).toHaveProperty('id'); expect(response.body.name).toBe(createProjectDto.name); expect(response.body.description).toBe(createProjectDto.description); expect(response.body.ownerId).toBe(createProjectDto.ownerId); testProjectId = response.body.id; }); it('should return 401 when not authenticated', () => { return request(app.getHttpServer()) .post('/api/projects') .send({ name: 'Unauthorized Project', ownerId: testUserId }) .expect(401); }); }); describe('GET /api/projects', () => { it('should return all projects', () => { return request(app.getHttpServer()) .get('/api/projects') .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(project => project.id === testProjectId)).toBe(true); }); }); it('should filter projects by owner ID', () => { return request(app.getHttpServer()) .get(`/api/projects?ownerId=${testUserId}`) .set('Authorization', `Bearer ${accessToken}`) .expect(200) .expect((res) => { expect(Array.isArray(res.body)).toBe(true); expect(res.body.length).toBeGreaterThan(0); expect(res.body.every(project => project.ownerId === testUserId)).toBe(true); }); }); it('should return 401 when not authenticated', () => { return request(app.getHttpServer()) .get('/api/projects') .expect(401); }); }); describe('GET /api/projects/:id', () => { it('should return a project by ID', () => { return request(app.getHttpServer()) .get(`/api/projects/${testProjectId}`) .set('Authorization', `Bearer ${accessToken}`) .expect(200) .expect((res) => { expect(res.body).toHaveProperty('id', testProjectId); expect(res.body).toHaveProperty('ownerId', testUserId); }); }); 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(); return request(app.getHttpServer()) .get(`/api/projects/${nonExistentId}`) .set('Authorization', `Bearer ${accessToken}`) .expect(404); }); }); describe('PATCH /api/projects/:id', () => { it('should update a project', () => { const updateData = { name: `Updated Project ${uuidv4().substring(0, 8)}`, description: 'Updated description' }; return request(app.getHttpServer()) .patch(`/api/projects/${testProjectId}`) .set('Authorization', `Bearer ${accessToken}`) .send(updateData) .expect(200) .expect((res) => { expect(res.body).toHaveProperty('id', testProjectId); expect(res.body.name).toBe(updateData.name); expect(res.body.description).toBe(updateData.description); }); }); it('should return 401 when not authenticated', () => { return request(app.getHttpServer()) .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}`) .expect(201); }); 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', () => { it('should check if owner has access to a project', () => { return request(app.getHttpServer()) .get(`/api/projects/${testProjectId}/check-access/${testUserId}`) .set('Authorization', `Bearer ${accessToken}`) .expect(200) .expect((res) => { expect(res.body).toBe(true); }); }); it('should check if collaborator has access to a project', () => { return request(app.getHttpServer()) .get(`/api/projects/${testProjectId}/check-access/${collaboratorUserId}`) .set('Authorization', `Bearer ${accessToken}`) .expect(200) .expect((res) => { 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', () => { it('should remove a collaborator from a project', () => { return request(app.getHttpServer()) .delete(`/api/projects/${testProjectId}/collaborators/${collaboratorUserId}`) .set('Authorization', `Bearer ${accessToken}`) .expect(204); }); it('should return 401 when not authenticated', () => { return request(app.getHttpServer()) .delete(`/api/projects/${testProjectId}/collaborators/${collaboratorUserId}`) .expect(401); }); }); // Note: We're not testing the DELETE /api/projects/:id endpoint here to avoid complications with test cleanup });