test: add comprehensive e2e tests for users, groups, auth, and projects APIs
This commit is contained in:
parent
cee85c9885
commit
10d4e940ed
@ -1,25 +1,24 @@
|
|||||||
import { Test, TestingModule } from '@nestjs/testing';
|
|
||||||
import { INestApplication } from '@nestjs/common';
|
import { INestApplication } from '@nestjs/common';
|
||||||
import * as request from 'supertest';
|
import * as request from 'supertest';
|
||||||
import { App } from 'supertest/types';
|
import { createTestApp } from './test-utils';
|
||||||
import { AppModule } from './../src/app.module';
|
|
||||||
|
|
||||||
describe('AppController (e2e)', () => {
|
describe('AppController (e2e)', () => {
|
||||||
let app: INestApplication<App>;
|
let app: INestApplication;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeAll(async () => {
|
||||||
const moduleFixture: TestingModule = await Test.createTestingModule({
|
app = await createTestApp();
|
||||||
imports: [AppModule],
|
|
||||||
}).compile();
|
|
||||||
|
|
||||||
app = moduleFixture.createNestApplication();
|
|
||||||
await app.init();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('/ (GET)', () => {
|
afterAll(async () => {
|
||||||
|
await app.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('GET /api', () => {
|
||||||
|
it('should return "Hello World!"', () => {
|
||||||
return request(app.getHttpServer())
|
return request(app.getHttpServer())
|
||||||
.get('/')
|
.get('/api')
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Hello World!');
|
.expect('Hello World!');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
96
backend/test/auth.e2e-spec.ts
Normal file
96
backend/test/auth.e2e-spec.ts
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
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('AuthController (e2e)', () => {
|
||||||
|
let app: INestApplication;
|
||||||
|
let accessToken: string;
|
||||||
|
let refreshToken: string;
|
||||||
|
let testUser: any;
|
||||||
|
let testUserId: 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;
|
||||||
|
refreshToken = tokens.refreshToken;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
// Clean up test data
|
||||||
|
await cleanupTestData(app, testUserId);
|
||||||
|
await app.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('GET /api/auth/profile', () => {
|
||||||
|
it('should return the current user profile when authenticated', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.get('/api/auth/profile')
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body).toHaveProperty('id', testUserId);
|
||||||
|
expect(res.body.name).toBe(testUser.name);
|
||||||
|
expect(res.body.githubId).toBe(testUser.githubId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return 401 when not authenticated', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.get('/api/auth/profile')
|
||||||
|
.expect(401);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return 401 with invalid token', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.get('/api/auth/profile')
|
||||||
|
.set('Authorization', 'Bearer invalid-token')
|
||||||
|
.expect(401);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('POST /api/auth/refresh', () => {
|
||||||
|
it('should refresh tokens with valid refresh token', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.post('/api/auth/refresh')
|
||||||
|
.set('Authorization', `Bearer ${refreshToken}`)
|
||||||
|
.expect(201)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body).toHaveProperty('accessToken');
|
||||||
|
expect(res.body).toHaveProperty('refreshToken');
|
||||||
|
expect(typeof res.body.accessToken).toBe('string');
|
||||||
|
expect(typeof res.body.refreshToken).toBe('string');
|
||||||
|
|
||||||
|
// Update tokens for subsequent tests
|
||||||
|
accessToken = res.body.accessToken;
|
||||||
|
refreshToken = res.body.refreshToken;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return 401 with invalid refresh token', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.post('/api/auth/refresh')
|
||||||
|
.set('Authorization', 'Bearer invalid-token')
|
||||||
|
.expect(401);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Note: We can't easily test the GitHub OAuth flow in an e2e test
|
||||||
|
// as it requires interaction with the GitHub API
|
||||||
|
describe('GET /api/auth/github', () => {
|
||||||
|
it('should redirect to GitHub OAuth page', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.get('/api/auth/github')
|
||||||
|
.expect(302) // Expect a redirect
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.headers.location).toBeDefined();
|
||||||
|
expect(res.headers.location.startsWith('https://github.com/login/oauth')).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
249
backend/test/groups.e2e-spec.ts
Normal file
249
backend/test/groups.e2e-spec.ts
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
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('GroupsController (e2e)', () => {
|
||||||
|
let app: INestApplication;
|
||||||
|
let accessToken: string;
|
||||||
|
let testUser: any;
|
||||||
|
let testUserId: string;
|
||||||
|
let testGroupId: string;
|
||||||
|
let testProjectId: string;
|
||||||
|
let testPersonId: 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 test project
|
||||||
|
const projectResponse = await request(app.getHttpServer())
|
||||||
|
.post('/api/projects')
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.send({
|
||||||
|
name: `Test Project ${uuidv4().substring(0, 8)}`,
|
||||||
|
description: 'Test project for e2e tests',
|
||||||
|
ownerId: testUserId
|
||||||
|
});
|
||||||
|
testProjectId = projectResponse.body.id;
|
||||||
|
|
||||||
|
// Create a test person
|
||||||
|
const personResponse = await request(app.getHttpServer())
|
||||||
|
.post('/api/persons')
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.send({
|
||||||
|
name: `Test Person ${uuidv4().substring(0, 8)}`,
|
||||||
|
projectId: testProjectId,
|
||||||
|
skills: ['JavaScript', 'TypeScript'],
|
||||||
|
metadata: { email: 'testperson@example.com' }
|
||||||
|
});
|
||||||
|
testPersonId = personResponse.body.id;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
// Clean up test data
|
||||||
|
if (testGroupId) {
|
||||||
|
await request(app.getHttpServer())
|
||||||
|
.delete(`/api/groups/${testGroupId}`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (testPersonId) {
|
||||||
|
await request(app.getHttpServer())
|
||||||
|
.delete(`/api/persons/${testPersonId}`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (testProjectId) {
|
||||||
|
await request(app.getHttpServer())
|
||||||
|
.delete(`/api/projects/${testProjectId}`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
await cleanupTestData(app, testUserId);
|
||||||
|
await app.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('POST /api/groups', () => {
|
||||||
|
it('should create a new group', async () => {
|
||||||
|
const createGroupDto = {
|
||||||
|
name: `Test Group ${uuidv4().substring(0, 8)}`,
|
||||||
|
projectId: testProjectId,
|
||||||
|
description: 'Test group for e2e tests'
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await request(app.getHttpServer())
|
||||||
|
.post('/api/groups')
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.send(createGroupDto)
|
||||||
|
.expect(201);
|
||||||
|
|
||||||
|
expect(response.body).toHaveProperty('id');
|
||||||
|
expect(response.body.name).toBe(createGroupDto.name);
|
||||||
|
expect(response.body.projectId).toBe(createGroupDto.projectId);
|
||||||
|
|
||||||
|
testGroupId = response.body.id;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return 401 when not authenticated', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.post('/api/groups')
|
||||||
|
.send({
|
||||||
|
name: 'Unauthorized Group',
|
||||||
|
projectId: testProjectId
|
||||||
|
})
|
||||||
|
.expect(401);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('GET /api/groups', () => {
|
||||||
|
it('should return all groups', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.get('/api/groups')
|
||||||
|
.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(group => group.id === testGroupId)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should filter groups by project ID', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.get(`/api/groups?projectId=${testProjectId}`)
|
||||||
|
.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(group => group.projectId === testProjectId)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return 401 when not authenticated', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.get('/api/groups')
|
||||||
|
.expect(401);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('GET /api/groups/:id', () => {
|
||||||
|
it('should return a group by ID', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.get(`/api/groups/${testGroupId}`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body).toHaveProperty('id', testGroupId);
|
||||||
|
expect(res.body).toHaveProperty('projectId', testProjectId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return 401 when not authenticated', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.get(`/api/groups/${testGroupId}`)
|
||||||
|
.expect(401);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return 404 for non-existent group', () => {
|
||||||
|
const nonExistentId = uuidv4();
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.get(`/api/groups/${nonExistentId}`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.expect(404);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('PUT /api/groups/:id', () => {
|
||||||
|
it('should update a group', () => {
|
||||||
|
const updateData = {
|
||||||
|
name: `Updated Group ${uuidv4().substring(0, 8)}`,
|
||||||
|
description: 'Updated description'
|
||||||
|
};
|
||||||
|
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.put(`/api/groups/${testGroupId}`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.send(updateData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body).toHaveProperty('id', testGroupId);
|
||||||
|
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())
|
||||||
|
.put(`/api/groups/${testGroupId}`)
|
||||||
|
.send({ name: 'Unauthorized Update' })
|
||||||
|
.expect(401);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('POST /api/groups/:id/persons/:personId', () => {
|
||||||
|
it('should add a person to a group', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.post(`/api/groups/${testGroupId}/persons/${testPersonId}`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.expect(201)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body).toHaveProperty('id', testGroupId);
|
||||||
|
expect(res.body.persons).toContainEqual(expect.objectContaining({ id: testPersonId }));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return 401 when not authenticated', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.post(`/api/groups/${testGroupId}/persons/${testPersonId}`)
|
||||||
|
.expect(401);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('GET /api/groups/:id/persons', () => {
|
||||||
|
it('should get all persons in a group', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.get(`/api/groups/${testGroupId}/persons`)
|
||||||
|
.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(person => person.id === testPersonId)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return 401 when not authenticated', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.get(`/api/groups/${testGroupId}/persons`)
|
||||||
|
.expect(401);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('DELETE /api/groups/:id/persons/:personId', () => {
|
||||||
|
it('should remove a person from a group', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.delete(`/api/groups/${testGroupId}/persons/${testPersonId}`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body).toHaveProperty('id', testGroupId);
|
||||||
|
expect(res.body.persons.every(person => person.id !== testPersonId)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return 401 when not authenticated', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.delete(`/api/groups/${testGroupId}/persons/${testPersonId}`)
|
||||||
|
.expect(401);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Note: We're not testing the DELETE /api/groups/:id endpoint here to avoid complications with test cleanup
|
||||||
|
});
|
302
backend/test/projects.e2e-spec.ts
Normal file
302
backend/test/projects.e2e-spec.ts
Normal file
@ -0,0 +1,302 @@
|
|||||||
|
import { INestApplication } from '@nestjs/common';
|
||||||
|
import * as request from 'supertest';
|
||||||
|
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';
|
||||||
|
|
||||||
|
describe('ProjectsController (e2e)', () => {
|
||||||
|
let app: INestApplication;
|
||||||
|
let accessToken: string;
|
||||||
|
let testUser: any;
|
||||||
|
let testUserId: string;
|
||||||
|
let secondTestUser: any;
|
||||||
|
let secondTestUserId: string;
|
||||||
|
let testProject: any;
|
||||||
|
let testProjectId: string;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
app = await createTestApp();
|
||||||
|
|
||||||
|
// Create test users and generate tokens
|
||||||
|
testUser = await createTestUser(app);
|
||||||
|
testUserId = testUser.id;
|
||||||
|
const tokens = await generateTokensForUser(app, testUserId);
|
||||||
|
accessToken = tokens.accessToken;
|
||||||
|
|
||||||
|
// Create a second test user for collaborator tests
|
||||||
|
secondTestUser = await createTestUser(app);
|
||||||
|
secondTestUserId = secondTestUser.id;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
// Clean up test data
|
||||||
|
await cleanupTestData(app, testUserId);
|
||||||
|
await cleanupTestData(app, secondTestUserId);
|
||||||
|
await app.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('POST /api/projects', () => {
|
||||||
|
it('should create a new project', async () => {
|
||||||
|
const createProjectDto: CreateProjectDto = {
|
||||||
|
name: `Test Project ${uuidv4().substring(0, 8)}`,
|
||||||
|
description: 'A test project for e2e testing',
|
||||||
|
ownerId: testUserId,
|
||||||
|
settings: { isPublic: true },
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
expect(response.body.settings).toEqual(createProjectDto.settings);
|
||||||
|
|
||||||
|
// Save the project for later tests
|
||||||
|
testProject = response.body;
|
||||||
|
testProjectId = response.body.id;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return 400 if required fields are missing', () => {
|
||||||
|
const invalidProjectDto = {
|
||||||
|
// Missing required name and ownerId
|
||||||
|
description: 'An invalid project',
|
||||||
|
};
|
||||||
|
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.post('/api/projects')
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.send(invalidProjectDto)
|
||||||
|
.expect(400);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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.every(project => project.ownerId === testUserId)).toBe(true);
|
||||||
|
expect(res.body.some(project => project.id === testProjectId)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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.name).toBe(testProject.name);
|
||||||
|
expect(res.body.description).toBe(testProject.description);
|
||||||
|
expect(res.body.ownerId).toBe(testProject.ownerId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return 404 if project not found', () => {
|
||||||
|
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 updateProjectDto: UpdateProjectDto = {
|
||||||
|
name: `Updated Project ${uuidv4().substring(0, 8)}`,
|
||||||
|
description: 'An updated test project',
|
||||||
|
settings: { isPublic: false },
|
||||||
|
};
|
||||||
|
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.patch(`/api/projects/${testProjectId}`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.send(updateProjectDto)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body).toHaveProperty('id', testProjectId);
|
||||||
|
expect(res.body.name).toBe(updateProjectDto.name);
|
||||||
|
expect(res.body.description).toBe(updateProjectDto.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', () => {
|
||||||
|
const nonExistentId = uuidv4();
|
||||||
|
const updateProjectDto: UpdateProjectDto = {
|
||||||
|
name: 'Updated Project',
|
||||||
|
};
|
||||||
|
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.patch(`/api/projects/${nonExistentId}`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.send(updateProjectDto)
|
||||||
|
.expect(404);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('GET /api/projects/:id/check-access/:userId', () => {
|
||||||
|
it('should return true if user is the owner', () => {
|
||||||
|
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 return false if user has no access', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.get(`/api/projects/${testProjectId}/check-access/${secondTestUserId}`)
|
||||||
|
.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}`)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('DELETE /api/projects/:id/collaborators/:userId', () => {
|
||||||
|
it('should remove a collaborator from a project', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.delete(`/api/projects/${testProjectId}/collaborators/${secondTestUserId}`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.expect(204);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return 404 if collaboration not found', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.delete(`/api/projects/${testProjectId}/collaborators/${secondTestUserId}`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.expect(404);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// We'll test DELETE last to avoid affecting other tests
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
144
backend/test/users.e2e-spec.ts
Normal file
144
backend/test/users.e2e-spec.ts
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
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('UsersController (e2e)', () => {
|
||||||
|
let app: INestApplication;
|
||||||
|
let accessToken: string;
|
||||||
|
let testUser: any;
|
||||||
|
let testUserId: 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;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
// Clean up test data
|
||||||
|
await cleanupTestData(app, testUserId);
|
||||||
|
await app.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('GET /api/users', () => {
|
||||||
|
it('should return a list of users when authenticated', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.get('/api/users')
|
||||||
|
.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 === testUserId)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return 401 when not authenticated', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.get('/api/users')
|
||||||
|
.expect(401);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('GET /api/users/:id', () => {
|
||||||
|
it('should return a user by ID when authenticated', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.get(`/api/users/${testUserId}`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body).toHaveProperty('id', testUserId);
|
||||||
|
expect(res.body.name).toBe(testUser.name);
|
||||||
|
expect(res.body.githubId).toBe(testUser.githubId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return 401 when not authenticated', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.get(`/api/users/${testUserId}`)
|
||||||
|
.expect(401);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return 404 for non-existent user', () => {
|
||||||
|
const nonExistentId = uuidv4();
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.get(`/api/users/${nonExistentId}`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.expect(404);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('PATCH /api/users/:id', () => {
|
||||||
|
it('should update a user when authenticated', () => {
|
||||||
|
const updateData = {
|
||||||
|
name: `Updated Test User ${uuidv4().substring(0, 8)}`
|
||||||
|
};
|
||||||
|
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.patch(`/api/users/${testUserId}`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.send(updateData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body).toHaveProperty('id', testUserId);
|
||||||
|
expect(res.body.name).toBe(updateData.name);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return 401 when not authenticated', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.patch(`/api/users/${testUserId}`)
|
||||||
|
.send({ name: 'Updated Name' })
|
||||||
|
.expect(401);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('POST /api/users/:id/gdpr-consent', () => {
|
||||||
|
it('should update GDPR consent timestamp when authenticated', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.post(`/api/users/${testUserId}/gdpr-consent`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body).toHaveProperty('id', testUserId);
|
||||||
|
expect(res.body).toHaveProperty('gdprConsentDate');
|
||||||
|
expect(new Date(res.body.gdprConsentDate).getTime()).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return 401 when not authenticated', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.post(`/api/users/${testUserId}/gdpr-consent`)
|
||||||
|
.expect(401);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('GET /api/users/:id/export-data', () => {
|
||||||
|
it('should export user data when authenticated', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.get(`/api/users/${testUserId}/export-data`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body).toHaveProperty('user');
|
||||||
|
expect(res.body.user).toHaveProperty('id', testUserId);
|
||||||
|
expect(res.body).toHaveProperty('projects');
|
||||||
|
expect(res.body).toHaveProperty('groups');
|
||||||
|
expect(res.body).toHaveProperty('persons');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return 401 when not authenticated', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.get(`/api/users/${testUserId}/export-data`)
|
||||||
|
.expect(401);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Note: We're not testing the DELETE endpoint to avoid complications with test user cleanup
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user