Files
web-cad/backend/tests/layers.test.ts
T
Leopoldadmin 1bcfaffdd4 fix: replace hardcoded localhost:3001 with relative API URL + add canvas mock and integration test
- Fix frontend Network error: API_BASE now defaults to empty string (same-origin)
- Fix 3 files: api.ts, AuthContext.tsx, Dashboard.tsx
- Add Canvas 2D context mock in tests/setup.ts for jsdom
- Fix -0 vs +0 in ZoomPanController.getViewport()
- Add IntegrationWorkflow.test.ts (35 tests, full CAD workflow)
2026-06-26 17:48:24 +02:00

200 lines
6.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Layers API Tests CRUD (List / Create / 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('Layers API', () => {
let app: FastifyInstance;
let db: SqliteAdapter;
let drawingId: string;
let createdLayerId: string;
beforeAll(async () => {
db = new SqliteAdapter(':memory:');
await db.init();
app = await createServer({ db, port: 0 });
await app.ready();
// Create project → drawing hierarchy
const projRes = await app.inject({
method: 'POST',
url: '/api/projects',
payload: { name: 'Layers Test Project' },
});
const projectId = JSON.parse(projRes.body).id;
const drawRes = await app.inject({
method: 'POST',
url: `/api/projects/${projectId}/drawings`,
payload: { name: 'Layers Test Drawing' },
});
drawingId = JSON.parse(drawRes.body).id;
});
afterAll(async () => {
await app.close();
db.close();
});
// ─── Create ─────────────────────────────────────────
describe('POST /api/drawings/:drawingId/layers', () => {
it('should create a layer with 201', async () => {
const response = await app.inject({
method: 'POST',
url: `/api/drawings/${drawingId}/layers`,
payload: { name: 'Background', color: '#ff0000' },
});
expect(response.statusCode).toBe(201);
const body = JSON.parse(response.body);
expect(body.id).toBeDefined();
expect(body.name).toBe('Background');
expect(body.color).toBe('#ff0000');
expect(body.drawing_id).toBe(drawingId);
createdLayerId = body.id;
});
it('should create a layer with default values', async () => {
const response = await app.inject({
method: 'POST',
url: `/api/drawings/${drawingId}/layers`,
payload: {},
});
expect(response.statusCode).toBe(201);
const body = JSON.parse(response.body);
expect(body.name).toBe('Layer');
expect(body.visible).toBe(1);
expect(body.locked).toBe(0);
expect(body.color).toBe('#ffffff');
expect(body.line_type).toBe('solid');
});
});
// ─── List ───────────────────────────────────────────
describe('GET /api/drawings/:drawingId/layers', () => {
it('should list layers for a drawing', async () => {
const response = await app.inject({
method: 'GET',
url: `/api/drawings/${drawingId}/layers`,
});
expect(response.statusCode).toBe(200);
const body = JSON.parse(response.body);
expect(Array.isArray(body)).toBe(true);
expect(body.length).toBeGreaterThanOrEqual(2);
});
it('should return empty array for drawing with no layers', async () => {
// Create a new drawing with no layers
const projRes = await app.inject({
method: 'POST',
url: '/api/projects',
payload: { name: 'Empty Layers Project' },
});
const projId = JSON.parse(projRes.body).id;
const drawRes = await app.inject({
method: 'POST',
url: `/api/projects/${projId}/drawings`,
payload: { name: 'Empty Drawing' },
});
const emptyDrawId = JSON.parse(drawRes.body).id;
const response = await app.inject({
method: 'GET',
url: `/api/drawings/${emptyDrawId}/layers`,
});
expect(response.statusCode).toBe(200);
const body = JSON.parse(response.body);
expect(Array.isArray(body)).toBe(true);
expect(body.length).toBe(0);
});
});
// ─── Update ─────────────────────────────────────────
describe('PATCH /api/layers/:id', () => {
it('should update layer name', async () => {
const response = await app.inject({
method: 'PATCH',
url: `/api/layers/${createdLayerId}`,
payload: { name: 'Updated Layer Name' },
});
expect(response.statusCode).toBe(200);
const body = JSON.parse(response.body);
expect(body.name).toBe('Updated Layer Name');
});
it('should update layer visibility', async () => {
const response = await app.inject({
method: 'PATCH',
url: `/api/layers/${createdLayerId}`,
payload: { visible: 0, locked: 1 },
});
expect(response.statusCode).toBe(200);
const body = JSON.parse(response.body);
expect(body.visible).toBe(0);
expect(body.locked).toBe(1);
});
it('should update layer color and line_type', async () => {
const response = await app.inject({
method: 'PATCH',
url: `/api/layers/${createdLayerId}`,
payload: { color: '#00ff00', line_type: 'dashed' },
});
expect(response.statusCode).toBe(200);
const body = JSON.parse(response.body);
expect(body.color).toBe('#00ff00');
expect(body.line_type).toBe('dashed');
});
it('should return 404 for non-existent layer update', async () => {
const response = await app.inject({
method: 'PATCH',
url: '/api/layers/non-existent-id',
payload: { name: 'New Name' },
});
expect(response.statusCode).toBe(404);
});
});
// ─── Delete ─────────────────────────────────────────
describe('DELETE /api/layers/:id', () => {
it('should delete a layer', async () => {
// Create a layer to delete
const createRes = await app.inject({
method: 'POST',
url: `/api/drawings/${drawingId}/layers`,
payload: { name: 'To Be Deleted' },
});
const layerId = JSON.parse(createRes.body).id;
const response = await app.inject({
method: 'DELETE',
url: `/api/layers/${layerId}`,
});
expect(response.statusCode).toBe(204);
// Verify it's gone via list
const listRes = await app.inject({
method: 'GET',
url: `/api/drawings/${drawingId}/layers`,
});
const layers = JSON.parse(listRes.body);
expect(layers.find((l: any) => l.id === layerId)).toBeUndefined();
});
it('should return 404 for non-existent layer delete', async () => {
const response = await app.inject({
method: 'DELETE',
url: '/api/layers/non-existent-id',
});
expect(response.statusCode).toBe(404);
});
});
});