Files

203 lines
6.1 KiB
TypeScript
Raw Permalink Normal View History

2026-07-26 22:47:50 +02:00
/**
* E2E Workflow Test: Login → Create Project → Create Drawing → Create Layer → Create Element → Update → Delete
*/
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import type { FastifyInstance } from 'fastify';
import { SqliteAdapter } from '../src/database/SqliteAdapter.js';
import { createServer } from '../src/server.js';
describe('E2E: Full CAD Workflow', () => {
let app: FastifyInstance;
let db: SqliteAdapter;
let authToken: string;
let projectId: string;
let drawingId: string;
let layerId: string;
let elementId: string;
beforeAll(async () => {
db = new SqliteAdapter(':memory:');
await db.init();
app = await createServer({ db, port: 0 });
await app.ready();
});
afterAll(async () => {
await app.close();
db.close();
});
it('Step 1: Register a new user', async () => {
const res = await app.inject({
method: 'POST',
url: '/api/auth/register',
payload: { email: 'e2e@example.com', password: 'Password123!', name: 'E2E Tester' },
});
expect(res.statusCode).toBe(201);
const body = JSON.parse(res.body);
expect(body.session.token).toBeDefined();
authToken = body.session.token;
});
it('Step 2: Get user profile', async () => {
const res = await app.inject({
method: 'GET',
url: '/api/auth/me',
headers: { authorization: `Bearer ${authToken}` },
});
expect(res.statusCode).toBe(200);
const body = JSON.parse(res.body);
expect(body.email).toBe('e2e@example.com');
});
it('Step 3: Create a project', async () => {
const res = await app.inject({
method: 'POST',
url: '/api/projects',
headers: { authorization: `Bearer ${authToken}` },
payload: { name: 'E2E Test Project', description: 'Full workflow test' },
});
expect(res.statusCode).toBe(201);
const body = JSON.parse(res.body);
expect(body.name).toBe('E2E Test Project');
projectId = body.id;
});
it('Step 4: Create a drawing in the project', async () => {
const res = await app.inject({
method: 'POST',
url: `/api/projects/${projectId}/drawings`,
headers: { authorization: `Bearer ${authToken}` },
payload: { name: 'Test Drawing' },
});
expect(res.statusCode).toBe(201);
const body = JSON.parse(res.body);
expect(body.name).toBe('Test Drawing');
drawingId = body.id;
});
it('Step 5: Create a layer in the drawing', async () => {
const res = await app.inject({
method: 'POST',
url: `/api/drawings/${drawingId}/layers`,
headers: { authorization: `Bearer ${authToken}` },
payload: { name: 'Test Layer', color: '#ff0000' },
});
expect(res.statusCode).toBe(201);
const body = JSON.parse(res.body);
expect(body.name).toBe('Test Layer');
layerId = body.id;
});
it('Step 6: Create an element on the layer', async () => {
const res = await app.inject({
method: 'POST',
url: `/api/drawings/${drawingId}/elements`,
headers: { authorization: `Bearer ${authToken}` },
payload: {
layer_id: layerId,
type: 'rect',
x: 10, y: 20, width: 100, height: 50,
properties_json: JSON.stringify({ stroke: '#0000ff', fill: '#cccccc' }),
},
});
expect(res.statusCode).toBe(201);
const body = JSON.parse(res.body);
expect(body.type).toBe('rect');
elementId = body.id;
});
it('Step 7: List elements in the drawing', async () => {
const res = await app.inject({
method: 'GET',
url: `/api/drawings/${drawingId}/elements`,
headers: { authorization: `Bearer ${authToken}` },
});
expect(res.statusCode).toBe(200);
const body = JSON.parse(res.body);
expect(body.length).toBe(1);
expect(body[0].id).toBe(elementId);
});
it('Step 8: Update the element', async () => {
const res = await app.inject({
method: 'PATCH',
url: `/api/elements/${elementId}`,
headers: { authorization: `Bearer ${authToken}` },
payload: { x: 50, y: 60, width: 200, height: 100 },
});
expect(res.statusCode).toBe(200);
const body = JSON.parse(res.body);
expect(body.x).toBe(50);
expect(body.width).toBe(200);
});
it('Step 9: Update the layer visibility', async () => {
const res = await app.inject({
method: 'PATCH',
url: `/api/layers/${layerId}`,
headers: { authorization: `Bearer ${authToken}` },
payload: { visible: 0 },
});
expect(res.statusCode).toBe(200);
const body = JSON.parse(res.body);
expect(body.visible).toBe(0);
});
it('Step 10: Delete the element', async () => {
const res = await app.inject({
method: 'DELETE',
url: `/api/elements/${elementId}`,
headers: { authorization: `Bearer ${authToken}` },
});
expect(res.statusCode).toBe(204);
});
it('Step 11: Verify element is deleted', async () => {
const res = await app.inject({
method: 'GET',
url: `/api/drawings/${drawingId}/elements`,
headers: { authorization: `Bearer ${authToken}` },
});
expect(res.statusCode).toBe(200);
const body = JSON.parse(res.body);
expect(body.length).toBe(0);
});
it('Step 12: Delete the drawing', async () => {
const res = await app.inject({
method: 'DELETE',
url: `/api/drawings/${drawingId}`,
headers: { authorization: `Bearer ${authToken}` },
});
expect(res.statusCode).toBe(204);
});
it('Step 13: Delete the project', async () => {
const res = await app.inject({
method: 'DELETE',
url: `/api/projects/${projectId}`,
headers: { authorization: `Bearer ${authToken}` },
});
expect(res.statusCode).toBe(204);
});
it('Step 14: Logout', async () => {
const res = await app.inject({
method: 'POST',
url: '/api/auth/logout',
headers: { authorization: `Bearer ${authToken}` },
});
expect(res.statusCode).toBe(204);
});
it('Step 15: Verify token is invalid after logout', async () => {
const res = await app.inject({
method: 'GET',
url: '/api/auth/me',
headers: { authorization: `Bearer ${authToken}` },
});
expect(res.statusCode).toBe(401);
});
});