test(tags): add end-to-end tests for tag CRUD operations and relationships with persons and projects
This commit is contained in:
parent
bdca6511bd
commit
634c2d046e
416
backend/test/tags.e2e-spec.ts
Normal file
416
backend/test/tags.e2e-spec.ts
Normal file
@ -0,0 +1,416 @@
|
|||||||
|
import { INestApplication } from '@nestjs/common';
|
||||||
|
import * as request from 'supertest';
|
||||||
|
import { createTestApp, createTestUser, generateTokensForUser, cleanupTestData } from './test-utils';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
import { DRIZZLE } from '../src/database/database.module';
|
||||||
|
import * as schema from '../src/database/schema';
|
||||||
|
import { eq, and } from 'drizzle-orm';
|
||||||
|
|
||||||
|
describe('TagsController (e2e)', () => {
|
||||||
|
let app: INestApplication;
|
||||||
|
let accessToken: string;
|
||||||
|
let testUser: any;
|
||||||
|
let testUserId: string;
|
||||||
|
let db: any;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
app = await createTestApp();
|
||||||
|
|
||||||
|
// Get the DrizzleORM instance
|
||||||
|
db = app.get(DRIZZLE);
|
||||||
|
|
||||||
|
// 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('Tag CRUD operations', () => {
|
||||||
|
let createdTag: any;
|
||||||
|
const testTagData = {
|
||||||
|
name: `Test Tag ${uuidv4().substring(0, 8)}`,
|
||||||
|
color: '#FF5733',
|
||||||
|
type: 'PERSON'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Clean up any test tags after tests
|
||||||
|
afterAll(async () => {
|
||||||
|
if (createdTag?.id) {
|
||||||
|
try {
|
||||||
|
await db.delete(schema.tags).where(eq(schema.tags.id, createdTag.id));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to clean up test tag:', error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create a new tag', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.post('/api/tags')
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.send(testTagData)
|
||||||
|
.expect(201)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body).toHaveProperty('id');
|
||||||
|
expect(res.body.name).toBe(testTagData.name);
|
||||||
|
expect(res.body.color).toBe(testTagData.color);
|
||||||
|
expect(res.body.type).toBe(testTagData.type);
|
||||||
|
createdTag = res.body;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get all tags', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.get('/api/tags')
|
||||||
|
.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(tag => tag.id === createdTag.id)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get tags by type', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.get('/api/tags?type=PERSON')
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(Array.isArray(res.body)).toBe(true);
|
||||||
|
expect(res.body.every(tag => tag.type === 'PERSON')).toBe(true);
|
||||||
|
expect(res.body.some(tag => tag.id === createdTag.id)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get a tag by ID', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.get(`/api/tags/${createdTag.id}`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body).toHaveProperty('id', createdTag.id);
|
||||||
|
expect(res.body.name).toBe(createdTag.name);
|
||||||
|
expect(res.body.color).toBe(createdTag.color);
|
||||||
|
expect(res.body.type).toBe(createdTag.type);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update a tag', () => {
|
||||||
|
const updateData = {
|
||||||
|
name: `Updated Tag ${uuidv4().substring(0, 8)}`,
|
||||||
|
color: '#33FF57'
|
||||||
|
};
|
||||||
|
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.put(`/api/tags/${createdTag.id}`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.send(updateData)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body).toHaveProperty('id', createdTag.id);
|
||||||
|
expect(res.body.name).toBe(updateData.name);
|
||||||
|
expect(res.body.color).toBe(updateData.color);
|
||||||
|
expect(res.body.type).toBe(createdTag.type); // Type should remain unchanged
|
||||||
|
|
||||||
|
// Update the createdTag reference for subsequent tests
|
||||||
|
createdTag = res.body;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return 404 when getting a non-existent tag', () => {
|
||||||
|
const nonExistentId = uuidv4();
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.get(`/api/tags/${nonExistentId}`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.expect(404);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return 404 when updating a non-existent tag', () => {
|
||||||
|
const nonExistentId = uuidv4();
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.put(`/api/tags/${nonExistentId}`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.send({ name: 'Updated Tag' })
|
||||||
|
.expect(404);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Tag relations with persons', () => {
|
||||||
|
let personTag: any;
|
||||||
|
let testPerson: any;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
// Create a test tag for persons
|
||||||
|
const [tag] = await db
|
||||||
|
.insert(schema.tags)
|
||||||
|
.values({
|
||||||
|
name: `Person Tag ${uuidv4().substring(0, 8)}`,
|
||||||
|
color: '#3366FF',
|
||||||
|
type: 'PERSON'
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
|
personTag = tag;
|
||||||
|
|
||||||
|
// Create a test project first (needed for person)
|
||||||
|
const [project] = await db
|
||||||
|
.insert(schema.projects)
|
||||||
|
.values({
|
||||||
|
name: `Test Project ${uuidv4().substring(0, 8)}`,
|
||||||
|
description: 'A test project for e2e tests',
|
||||||
|
ownerId: testUserId
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
// Create a test person
|
||||||
|
const [person] = await db
|
||||||
|
.insert(schema.persons)
|
||||||
|
.values({
|
||||||
|
firstName: `Test ${uuidv4().substring(0, 8)}`,
|
||||||
|
lastName: `Person ${uuidv4().substring(0, 8)}`,
|
||||||
|
gender: 'MALE',
|
||||||
|
technicalLevel: 3,
|
||||||
|
hasTechnicalTraining: true,
|
||||||
|
frenchSpeakingLevel: 4,
|
||||||
|
oralEaseLevel: 'COMFORTABLE',
|
||||||
|
projectId: project.id
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
|
testPerson = person;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
// Clean up test data
|
||||||
|
if (personTag?.id) {
|
||||||
|
try {
|
||||||
|
await db.delete(schema.tags).where(eq(schema.tags.id, personTag.id));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to clean up test tag:', error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (testPerson?.id) {
|
||||||
|
try {
|
||||||
|
await db.delete(schema.persons).where(eq(schema.persons.id, testPerson.id));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to clean up test person:', error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add a tag to a person', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.post(`/api/tags/persons/${testPerson.id}/tags/${personTag.id}`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.expect(201)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body).toHaveProperty('personId', testPerson.id);
|
||||||
|
expect(res.body).toHaveProperty('tagId', personTag.id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get all tags for a person', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.get(`/api/tags/persons/${testPerson.id}/tags`)
|
||||||
|
.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(item => item.tag.id === personTag.id)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove a tag from a person', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.delete(`/api/tags/persons/${testPerson.id}/tags/${personTag.id}`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body).toHaveProperty('personId', testPerson.id);
|
||||||
|
expect(res.body).toHaveProperty('tagId', personTag.id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return 404 when adding a tag to a non-existent person', () => {
|
||||||
|
const nonExistentId = uuidv4();
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.post(`/api/tags/persons/${nonExistentId}/tags/${personTag.id}`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.expect(404);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return 400 when adding a project tag to a person', async () => {
|
||||||
|
// Create a project tag
|
||||||
|
const [projectTag] = await db
|
||||||
|
.insert(schema.tags)
|
||||||
|
.values({
|
||||||
|
name: `Project Tag ${uuidv4().substring(0, 8)}`,
|
||||||
|
color: '#FF3366',
|
||||||
|
type: 'PROJECT'
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
const response = await request(app.getHttpServer())
|
||||||
|
.post(`/api/tags/persons/${testPerson.id}/tags/${projectTag.id}`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.expect(400);
|
||||||
|
|
||||||
|
// Clean up the project tag
|
||||||
|
await db.delete(schema.tags).where(eq(schema.tags.id, projectTag.id));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Tag relations with projects', () => {
|
||||||
|
let projectTag: any;
|
||||||
|
let testProject: any;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
// Create a test tag for projects
|
||||||
|
const [tag] = await db
|
||||||
|
.insert(schema.tags)
|
||||||
|
.values({
|
||||||
|
name: `Project Tag ${uuidv4().substring(0, 8)}`,
|
||||||
|
color: '#33FFCC',
|
||||||
|
type: 'PROJECT'
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
|
projectTag = tag;
|
||||||
|
|
||||||
|
// Create a test project
|
||||||
|
const [project] = await db
|
||||||
|
.insert(schema.projects)
|
||||||
|
.values({
|
||||||
|
name: `Test Project ${uuidv4().substring(0, 8)}`,
|
||||||
|
description: 'A test project for e2e tests',
|
||||||
|
ownerId: testUserId
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
|
testProject = project;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
// Clean up test data
|
||||||
|
if (projectTag?.id) {
|
||||||
|
try {
|
||||||
|
await db.delete(schema.tags).where(eq(schema.tags.id, projectTag.id));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to clean up test tag:', error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (testProject?.id) {
|
||||||
|
try {
|
||||||
|
await db.delete(schema.projects).where(eq(schema.projects.id, testProject.id));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to clean up test project:', error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add a tag to a project', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.post(`/api/tags/projects/${testProject.id}/tags/${projectTag.id}`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.expect(201)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body).toHaveProperty('projectId', testProject.id);
|
||||||
|
expect(res.body).toHaveProperty('tagId', projectTag.id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get all tags for a project', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.get(`/api/tags/projects/${testProject.id}/tags`)
|
||||||
|
.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(item => item.tag.id === projectTag.id)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove a tag from a project', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.delete(`/api/tags/projects/${testProject.id}/tags/${projectTag.id}`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body).toHaveProperty('projectId', testProject.id);
|
||||||
|
expect(res.body).toHaveProperty('tagId', projectTag.id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return 404 when adding a tag to a non-existent project', () => {
|
||||||
|
const nonExistentId = uuidv4();
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.post(`/api/tags/projects/${nonExistentId}/tags/${projectTag.id}`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.expect(404);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return 400 when adding a person tag to a project', async () => {
|
||||||
|
// Create a person tag
|
||||||
|
const [personTag] = await db
|
||||||
|
.insert(schema.tags)
|
||||||
|
.values({
|
||||||
|
name: `Person Tag ${uuidv4().substring(0, 8)}`,
|
||||||
|
color: '#CCFF33',
|
||||||
|
type: 'PERSON'
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
const response = await request(app.getHttpServer())
|
||||||
|
.post(`/api/tags/projects/${testProject.id}/tags/${personTag.id}`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.expect(400);
|
||||||
|
|
||||||
|
// Clean up the person tag
|
||||||
|
await db.delete(schema.tags).where(eq(schema.tags.id, personTag.id));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Tag deletion', () => {
|
||||||
|
let tagToDelete: any;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
// Create a new tag to delete
|
||||||
|
const [tag] = await db
|
||||||
|
.insert(schema.tags)
|
||||||
|
.values({
|
||||||
|
name: `Tag to Delete ${uuidv4().substring(0, 8)}`,
|
||||||
|
color: '#FF99CC',
|
||||||
|
type: 'PERSON'
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
|
tagToDelete = tag;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should delete a tag', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.delete(`/api/tags/${tagToDelete.id}`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.expect(200)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body).toHaveProperty('id', tagToDelete.id);
|
||||||
|
expect(res.body.name).toBe(tagToDelete.name);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return 404 when deleting a non-existent tag', () => {
|
||||||
|
const nonExistentId = uuidv4();
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.delete(`/api/tags/${nonExistentId}`)
|
||||||
|
.set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
.expect(404);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user