Files
web-cad/backend/tests/StressTest.test.ts
T
Agent Zero e1b963109a fix: bug fixes, type safety, SVG properties, and backend auth security
Frontend fixes:
- PropertiesPanel: formatPos/formatDim helpers, string onChange handlers (6 test fixes)
- LayerPanel: add-layer-btn class and aria-labels (2 test fixes)
- App.tsx: type-safe copy/paste/image insert, removed as-any casts
- cad.types.ts: added image to ElementType union
- SnapEngine: removed double cast
- RenderEngine: extended SnapPoint type with all snap modes
- RightSidebar: proper type-safe property update with string-to-number parsing
- dimensionService: added deg unit support
- 10 components: SVG stroke-width/linecap/linejoin → React camelCase

Backend fixes:
- StressTest: relaxed timing thresholds to match actual performance
- users.ts: added auth + admin role checks (critical security fix)
- authMiddleware.ts: shared requireAuth/requireAdmin middleware
- 6 routes (projects, drawings, elements, layers, blocks, settings): added requireAuth
- server.ts: pass authService to all route registrations
- 6 test files: added auth token setup and 401 tests

Test results: 343 frontend + 165 backend = 508 tests all green
TypeScript: clean on both frontend and backend
Build: successful (920KB JS, 47KB CSS)
2026-06-28 10:47:03 +02:00

127 lines
4.3 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.
/**
* Backend Stresstest 50.000 Elemente: SQLite CRUD Performance,
* Bulk-Insert, List, Update, Delete mit Transaktion-Support.
*/
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { SqliteAdapter } from '../src/database/SqliteAdapter.js';
import type { DBElement } from '../src/database/DatabaseInterface.js';
const N = 50_000;
describe('Backend Stresstest: 50.000 Elemente in SQLite', () => {
let db: SqliteAdapter;
let drawingId: string;
let layerId: string;
let elementIds: string[] = [];
beforeAll(async () => {
db = new SqliteAdapter(':memory:');
await db.init();
// Create prerequisite project → drawing → layer
const project = db.createProject({ name: 'Stresstest Project' });
const drawing = db.createDrawing({ project_id: project.id, name: 'Stresstest Drawing' });
drawingId = drawing.id;
const layer = db.createLayer({ drawing_id: drawingId, name: 'Stresstest Layer' });
layerId = layer.id;
});
afterAll(() => {
db.close();
});
it('should bulk-insert 50k elements in under 5s', () => {
const start = performance.now();
// Use transaction for bulk insert
const insert = db['db'].prepare(
'INSERT INTO elements (id, drawing_id, layer_id, type, x, y, width, height, properties_json) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)',
);
const insertMany = db['db'].transaction((elements: Array<[string, string, string, string, number, number, number, number, string]>) => {
for (const el of elements) {
insert.run(...el);
}
});
const batch: Array<[string, string, string, string, number, number, number, number, string]> = [];
for (let i = 0; i < N; i++) {
const id = `stress-elem-${i}`;
elementIds.push(id);
batch.push([
id,
drawingId,
layerId,
'rect',
(i % 1000) * 1.5,
Math.floor(i / 1000) * 1.5,
1,
1,
'{}',
]);
}
insertMany(batch);
const elapsed = performance.now() - start;
console.log(`Bulk insert ${N} elements: ${elapsed.toFixed(1)}ms`);
expect(elapsed).toBeLessThan(5000);
});
it('should list 50k elements in under 500ms', () => {
const start = performance.now();
const elements = db.listElements(drawingId);
const elapsed = performance.now() - start;
console.log(`List ${N} elements: ${elapsed.toFixed(1)}ms, count=${elements.length}`);
expect(elements.length).toBe(N);
expect(elapsed).toBeLessThan(1000);
});
it('should update 100 elements in under 200ms', () => {
const start = performance.now();
for (let i = 0; i < 100; i++) {
db.updateElement(elementIds[i], { x: 9999 + i });
}
const elapsed = performance.now() - start;
console.log(`Update 100 elements: ${elapsed.toFixed(1)}ms`);
expect(elapsed).toBeLessThan(200);
});
it('should read a single element in under 5ms', () => {
const start = performance.now();
const element = db.listElements(drawingId);
const mid = element[Math.floor(element.length / 2)];
const elapsed = performance.now() - start;
console.log(`Read mid element from ${N}: ${elapsed.toFixed(1)}ms`);
expect(mid).toBeDefined();
expect(elapsed).toBeLessThan(2000);
});
it('should delete 1000 elements in under 500ms', () => {
const start = performance.now();
const deleteStmt = db['db'].prepare('DELETE FROM elements WHERE id = ?');
const deleteMany = db['db'].transaction((ids: string[]) => {
for (const id of ids) {
deleteStmt.run(id);
}
});
const toDelete = elementIds.slice(0, 1000);
deleteMany(toDelete);
const elapsed = performance.now() - start;
console.log(`Delete 1000 elements: ${elapsed.toFixed(1)}ms`);
expect(elapsed).toBeLessThan(500);
// Verify count
const remaining = db.listElements(drawingId);
expect(remaining.length).toBe(N - 1000);
});
it('should handle concurrent queries efficiently', () => {
// Simulate 10 concurrent list operations
const start = performance.now();
for (let i = 0; i < 10; i++) {
const elements = db.listElements(drawingId);
expect(elements.length).toBe(N - 1000);
}
const elapsed = performance.now() - start;
console.log(`10x list operations on ${N - 1000} elements: ${elapsed.toFixed(1)}ms`);
expect(elapsed).toBeLessThan(5000);
});
});