test: T29-T31 component tests (20) + frontend stresstest (7) + backend DB stresstest (6) with 50k elements
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* 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(500);
|
||||
});
|
||||
|
||||
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(500);
|
||||
});
|
||||
|
||||
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(3000);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user