Code Cleanup: Duplikate + Extraktionen
Prioritaet 1: Wird durch geplante Features geloest
Diese Duplikate verschwinden automatisch wenn die geplanten Features gebaut werden:
| Duplikat | Wie oft | Geloest durch |
|---|---|---|
ctx["db"] as DbConnection | 17x | Tenant DB Context — ctx.db typisiert |
| Test Setup (createTestDb + createTestRedis + cleanup) | 6+ Dateien | Testing — createTestApp() |
| Test SessionUser Mocks | 9+ Dateien | Testing — app.asUser() + Factories |
| Handler Registration Boilerplate | Alle Features | Feature-Struktur — defineWriteHandler Objekte |
Prioritaet 2: Jetzt extrahieren (vor neuen Features)
2a: Test Request Helper → shared Utility
Aktuell 5 verschiedene Varianten von req():
// VORHER: in jeder Testdatei kopiertasync function req(method: string, path: string, user: SessionUser, body?: unknown) { const token = await jwt.sign(user); const init: RequestInit = { method, headers: { ... } }; if (body) init.body = JSON.stringify(body); return app.request(path, init);}// NACHHER: shared utilityexport function createRequestHelper(app: HonoApp, jwt: JwtHelper) { return { write: (type: string, payload: unknown, user: SessionUser) => ..., query: (type: string, payload: unknown, user: SessionUser) => ..., command: (type: string, payload: unknown, user: SessionUser) => ..., };}Dateien: full-stack.integration.ts, field-access.integration.ts, jobs-feature.integration.ts, multi-tenant.integration.ts, files.integration.ts
2b: Test Assertions → shared Helper
if (!result.isSuccess) 25x in Tests:
// VORHER:const result = await write("user.create", payload);if (!result.isSuccess) throw new Error("Setup failed");
// NACHHER:// packages/framework/src/testing/assertions.tsexport function expectSuccess<T>(result: WriteResult<T>): asserts result is { isSuccess: true; data: T } { if (!result.isSuccess) { throw new Error(`Expected success but got error: ${result.error}`); }}
// Nutzung:const result = await write("user.create", payload);expectSuccess(result);// result.data ist jetzt typisiert2c: sleep() → shared Utility
// VORHER: 3x kopiertconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
// NACHHER:// packages/framework/src/testing/utils.tsexport const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));2d: SessionUser Fixtures
// VORHER: 9x kopiertconst adminUser: SessionUser = { id: 1, tenantId: 1, roles: ["Admin"] };const normalUser: SessionUser = { id: 2, tenantId: 1, roles: ["User"] };
// NACHHER:// packages/framework/src/testing/fixtures.tsexport const TestUsers = { admin: { id: 1, tenantId: 1, roles: ["Admin"] }, systemAdmin: { id: 1, tenantId: 1, roles: ["SystemAdmin"] }, user: { id: 2, tenantId: 1, roles: ["User"] }, driver: { id: 3, tenantId: 1, roles: ["Driver"] }, otherTenant: { id: 10, tenantId: 2, roles: ["Admin"] },} as const satisfies Record<string, SessionUser>;2e: Standard Table SQL → Table Builder nutzen
Aktuell hardcoded SQL in 4+ Test-Dateien:
-- VORHER: manuell in jeder TestdateiCREATE TABLE "test_Users" ( "id" SERIAL PRIMARY KEY, "tenant_id" INTEGER NOT NULL, "version" INTEGER NOT NULL DEFAULT 1, "inserted_at" TIMESTAMP NOT NULL DEFAULT NOW(), ...);// NACHHER: Table Builder generiertconst table = buildTable("test_Users", entityDef);await db.execute(table.createSQL());2f: Buffer/Encoding → shared Utility
// VORHER: verstreut in cursor.ts, encryption.ts, filesBuffer.from(value).toString("base64")Buffer.from(value, "base64").toString("utf-8")
// NACHHER:// packages/framework/src/utils/encoding.tsexport const encode = { toBase64: (value: string) => Buffer.from(value).toString("base64"), fromBase64: (value: string) => Buffer.from(value, "base64").toString("utf-8"), toJson: <T>(value: T) => JSON.stringify(value), fromJson: <T>(value: string) => JSON.parse(value) as T,};2g: JSON Roles Parsing → shared Helper
// VORHER: kopiert in tenant + testsroles: JSON.parse(row["roles"] as string) as string[]
// NACHHER:// packages/framework/src/utils/serialization.tsexport function parseRoles(raw: unknown): string[] { if (typeof raw === "string") return JSON.parse(raw); if (Array.isArray(raw)) return raw; return [];}Zusammenfassung
| Step | Was | Dateien betroffen |
|---|---|---|
| 2a | Test Request Helper | 5 Testdateien |
| 2b | Test Assertions (expectSuccess) | 25+ Stellen |
| 2c | sleep() | 3 Testdateien |
| 2d | SessionUser Fixtures | 9 Testdateien |
| 2e | Table SQL → Builder | 4 Testdateien |
| 2f | Buffer/Encoding | 4 Dateien |
| 2g | JSON Roles Parsing | 3 Dateien |
Reihenfolge: 2a → 2d → 2b → 2c (Test Utilities zuerst, dann nutzt jeder Test sie). 2e-2g koennen parallel.
Neue Dateistruktur Testing
packages/framework/src/testing/ index.ts ← Re-exports create-test-app.ts ← createTestApp() (spaeter, aus Testing Plan) create-test-db.ts ← Schon da create-test-redis.ts ← Schon da request-helper.ts ← NEU: write(), query(), command() assertions.ts ← NEU: expectSuccess(), expectError() fixtures.ts ← NEU: TestUsers, TestTenants utils.ts ← NEU: sleep() factories.ts ← Spaeter: Entity Factories