fix(quality): Ruff/CVE/TS-Fixes, 104 neue Tests, Complexity-Refactoring
Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Failing after 30s
CI / test-python-backend-compliance (push) Successful in 30s
CI / test-python-document-crawler (push) Successful in 21s
CI / test-python-dsms-gateway (push) Successful in 17s

- Ruff: 144 auto-fixes (unused imports, == None → is None), F821/F811/F841 manuell
- CVEs: python-multipart>=0.0.22, weasyprint>=68.0, pillow>=12.1.1, npm audit fix (0 vulns)
- TS: 5 tote Drafting-Engine-Dateien entfernt, allowed-facts/sanitizer/StepHeader/context fixes
- Tests: +104 (ISMS 58, Evidence 18, VVT 14, Generation 14) → 1449 passed
- Refactoring: collect_ci_evidence (F→A), row_to_response (E→A), extract_requirements (E→A)
- Dead Code: pca-platform, 7 Go-Handler, dsr_api.py, duplicate Schemas entfernt

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-03-07 19:00:33 +01:00
parent 6509e64dd9
commit 95fcba34cd
124 changed files with 2533 additions and 15709 deletions

View File

@@ -1,153 +0,0 @@
import { IntentClassifier } from '../intent-classifier'
describe('IntentClassifier', () => {
const classifier = new IntentClassifier()
describe('classify - Draft mode', () => {
it.each([
['Erstelle ein VVT fuer unseren Hauptprozess', 'draft'],
['Generiere eine TOM-Dokumentation', 'draft'],
['Schreibe eine Datenschutzerklaerung', 'draft'],
['Verfasse einen Entwurf fuer das Loeschkonzept', 'draft'],
['Create a DSFA document', 'draft'],
['Draft a privacy policy for us', 'draft'],
['Neues VVT anlegen', 'draft'],
])('"%s" should classify as %s', (input, expectedMode) => {
const result = classifier.classify(input)
expect(result.mode).toBe(expectedMode)
expect(result.confidence).toBeGreaterThan(0.7)
})
})
describe('classify - Validate mode', () => {
it.each([
['Pruefe die Konsistenz meiner Dokumente', 'validate'],
['Ist mein VVT korrekt?', 'validate'],
['Validiere die TOM gegen das VVT', 'validate'],
['Check die Vollstaendigkeit', 'validate'],
['Stimmt das mit der DSFA ueberein?', 'validate'],
['Cross-Check VVT und TOM', 'validate'],
])('"%s" should classify as %s', (input, expectedMode) => {
const result = classifier.classify(input)
expect(result.mode).toBe(expectedMode)
expect(result.confidence).toBeGreaterThan(0.7)
})
})
describe('classify - Ask mode', () => {
it.each([
['Was fehlt noch in meinem Profil?', 'ask'],
['Zeige mir die Luecken', 'ask'],
['Welche Dokumente fehlen noch?', 'ask'],
['Was ist der naechste Schritt?', 'ask'],
['Welche Informationen brauche ich noch?', 'ask'],
])('"%s" should classify as %s', (input, expectedMode) => {
const result = classifier.classify(input)
expect(result.mode).toBe(expectedMode)
expect(result.confidence).toBeGreaterThan(0.6)
})
})
describe('classify - Explain mode (fallback)', () => {
it.each([
['Was ist DSGVO?', 'explain'],
['Erklaere mir Art. 30', 'explain'],
['Hallo', 'explain'],
['Danke fuer die Hilfe', 'explain'],
])('"%s" should classify as %s (fallback)', (input, expectedMode) => {
const result = classifier.classify(input)
expect(result.mode).toBe(expectedMode)
})
})
describe('classify - confidence thresholds', () => {
it('should have high confidence for clear draft intents', () => {
const result = classifier.classify('Erstelle ein neues VVT')
expect(result.confidence).toBeGreaterThanOrEqual(0.85)
})
it('should have lower confidence for ambiguous inputs', () => {
const result = classifier.classify('Hallo')
expect(result.confidence).toBeLessThan(0.6)
})
it('should boost confidence with document type detection', () => {
const withDoc = classifier.classify('Erstelle VVT')
const withoutDoc = classifier.classify('Erstelle etwas')
expect(withDoc.confidence).toBeGreaterThanOrEqual(withoutDoc.confidence)
})
it('should boost confidence with multiple pattern matches', () => {
const single = classifier.classify('Erstelle Dokument')
const multi = classifier.classify('Erstelle und generiere ein neues Dokument')
expect(multi.confidence).toBeGreaterThanOrEqual(single.confidence)
})
})
describe('detectDocumentType', () => {
it.each([
['VVT erstellen', 'vvt'],
['Verarbeitungsverzeichnis', 'vvt'],
['Art. 30 Dokumentation', 'vvt'],
['TOM definieren', 'tom'],
['technisch organisatorische Massnahmen', 'tom'],
['Art. 32 Massnahmen', 'tom'],
['DSFA durchfuehren', 'dsfa'],
['Datenschutz-Folgenabschaetzung', 'dsfa'],
['Art. 35 Pruefung', 'dsfa'],
['DPIA erstellen', 'dsfa'],
['Datenschutzerklaerung', 'dsi'],
['Privacy Policy', 'dsi'],
['Art. 13 Information', 'dsi'],
['Loeschfristen definieren', 'lf'],
['Loeschkonzept erstellen', 'lf'],
['Retention Policy', 'lf'],
['Auftragsverarbeitung', 'av_vertrag'],
['AVV erstellen', 'av_vertrag'],
['Art. 28 Vertrag', 'av_vertrag'],
['Einwilligung einholen', 'einwilligung'],
['Consent Management', 'einwilligung'],
['Cookie Banner', 'einwilligung'],
])('"%s" should detect document type %s', (input, expectedType) => {
const result = classifier.detectDocumentType(input)
expect(result).toBe(expectedType)
})
it('should return undefined for unrecognized types', () => {
expect(classifier.detectDocumentType('Hallo Welt')).toBeUndefined()
expect(classifier.detectDocumentType('Was kostet das?')).toBeUndefined()
})
})
describe('classify - Umlaut handling', () => {
it('should handle German umlauts correctly', () => {
// With actual umlauts (ä, ö, ü)
const result1 = classifier.classify('Prüfe die Vollständigkeit')
expect(result1.mode).toBe('validate')
// With ae/oe/ue substitution
const result2 = classifier.classify('Pruefe die Vollstaendigkeit')
expect(result2.mode).toBe('validate')
})
it('should handle ß correctly', () => {
const result = classifier.classify('Schließe Lücken')
// Should still detect via normalized patterns
expect(result).toBeDefined()
})
})
describe('classify - combined mode + document type', () => {
it('should detect both mode and document type', () => {
const result = classifier.classify('Erstelle ein VVT fuer unsere Firma')
expect(result.mode).toBe('draft')
expect(result.detectedDocumentType).toBe('vvt')
})
it('should detect validate + document type', () => {
const result = classifier.classify('Pruefe mein TOM auf Konsistenz')
expect(result.mode).toBe('validate')
expect(result.detectedDocumentType).toBe('tom')
})
})
})

View File

@@ -1,312 +0,0 @@
import { StateProjector } from '../state-projector'
import type { SDKState } from '../../types'
describe('StateProjector', () => {
const projector = new StateProjector()
// Helper: minimal SDKState
function makeState(overrides: Partial<SDKState> = {}): SDKState {
return {
version: '1.0.0',
lastModified: new Date(),
tenantId: 'test',
userId: 'user1',
subscription: 'PROFESSIONAL',
customerType: null,
companyProfile: null,
complianceScope: null,
sourcePolicy: null,
currentPhase: 1,
currentStep: 'company-profile',
completedSteps: [],
checkpoints: {},
importedDocuments: [],
gapAnalysis: null,
useCases: [],
activeUseCase: null,
screening: null,
modules: [],
requirements: [],
controls: [],
evidence: [],
checklist: [],
risks: [],
aiActClassification: null,
obligations: [],
dsfa: null,
toms: [],
retentionPolicies: [],
vvt: [],
documents: [],
cookieBanner: null,
consents: [],
dsrConfig: null,
escalationWorkflows: [],
preferences: {
language: 'de',
theme: 'light',
compactMode: false,
showHints: true,
autoSave: true,
autoValidate: true,
allowParallelWork: true,
},
...overrides,
} as SDKState
}
function makeDecisionState(level: string = 'L2'): SDKState {
return makeState({
companyProfile: {
companyName: 'Test GmbH',
industry: 'IT-Dienstleistung',
employeeCount: 50,
businessModel: 'SaaS',
isPublicSector: false,
} as any,
complianceScope: {
decision: {
id: 'dec-1',
determinedLevel: level,
scores: { risk_score: 60, complexity_score: 50, assurance_need: 55, composite_score: 55 },
triggeredHardTriggers: [],
requiredDocuments: [
{ documentType: 'vvt', label: 'VVT', required: true, depth: 'Standard', detailItems: ['Bezeichnung', 'Zweck'], estimatedEffort: '2h', triggeredBy: [] },
{ documentType: 'tom', label: 'TOM', required: true, depth: 'Standard', detailItems: ['Verschluesselung'], estimatedEffort: '3h', triggeredBy: [] },
{ documentType: 'lf', label: 'LF', required: true, depth: 'Basis', detailItems: [], estimatedEffort: '1h', triggeredBy: [] },
],
riskFlags: [
{ id: 'rf-1', severity: 'MEDIUM', title: 'Cloud-Nutzung', description: '', recommendation: 'AVV pruefen' },
],
gaps: [
{ id: 'gap-1', severity: 'high', title: 'TOM fehlt', description: 'Keine TOM definiert', relatedDocuments: ['tom'] },
],
nextActions: [],
reasoning: [],
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
},
answers: [],
} as any,
vvt: [{ id: 'vvt-1', name: 'Kundenverwaltung' }] as any[],
toms: [],
retentionPolicies: [],
})
}
describe('projectForDraft', () => {
it('should return a DraftContext with correct structure', () => {
const state = makeDecisionState()
const result = projector.projectForDraft(state, 'vvt')
expect(result).toHaveProperty('decisions')
expect(result).toHaveProperty('companyProfile')
expect(result).toHaveProperty('constraints')
expect(result.decisions.level).toBe('L2')
})
it('should project company profile', () => {
const state = makeDecisionState()
const result = projector.projectForDraft(state, 'vvt')
expect(result.companyProfile.name).toBe('Test GmbH')
expect(result.companyProfile.industry).toBe('IT-Dienstleistung')
expect(result.companyProfile.employeeCount).toBe(50)
})
it('should provide defaults when no company profile', () => {
const state = makeState()
const result = projector.projectForDraft(state, 'vvt')
expect(result.companyProfile.name).toBe('Unbekannt')
expect(result.companyProfile.industry).toBe('Unbekannt')
expect(result.companyProfile.employeeCount).toBe(0)
})
it('should extract constraints and depth requirements', () => {
const state = makeDecisionState()
const result = projector.projectForDraft(state, 'vvt')
expect(result.constraints.depthRequirements).toBeDefined()
expect(result.constraints.boundaries.length).toBeGreaterThan(0)
})
it('should extract risk flags', () => {
const state = makeDecisionState()
const result = projector.projectForDraft(state, 'vvt')
expect(result.constraints.riskFlags.length).toBe(1)
expect(result.constraints.riskFlags[0].title).toBe('Cloud-Nutzung')
})
it('should include existing document data when available', () => {
const state = makeDecisionState()
const result = projector.projectForDraft(state, 'vvt')
expect(result.existingDocumentData).toBeDefined()
expect((result.existingDocumentData as any).totalCount).toBe(1)
})
it('should return undefined existingDocumentData when none exists', () => {
const state = makeDecisionState()
const result = projector.projectForDraft(state, 'tom')
expect(result.existingDocumentData).toBeUndefined()
})
it('should filter required documents', () => {
const state = makeDecisionState()
const result = projector.projectForDraft(state, 'vvt')
expect(result.decisions.requiredDocuments.length).toBe(3)
expect(result.decisions.requiredDocuments.every(d => d.documentType)).toBe(true)
})
it('should handle empty state gracefully', () => {
const state = makeState()
const result = projector.projectForDraft(state, 'vvt')
expect(result.decisions.level).toBe('L1')
expect(result.decisions.hardTriggers).toEqual([])
expect(result.decisions.requiredDocuments).toEqual([])
})
})
describe('projectForAsk', () => {
it('should return a GapContext with correct structure', () => {
const state = makeDecisionState()
const result = projector.projectForAsk(state)
expect(result).toHaveProperty('unansweredQuestions')
expect(result).toHaveProperty('gaps')
expect(result).toHaveProperty('missingDocuments')
})
it('should identify missing documents', () => {
const state = makeDecisionState()
// vvt exists, tom and lf are missing
const result = projector.projectForAsk(state)
expect(result.missingDocuments.some(d => d.documentType === 'tom')).toBe(true)
expect(result.missingDocuments.some(d => d.documentType === 'lf')).toBe(true)
})
it('should not list existing documents as missing', () => {
const state = makeDecisionState()
const result = projector.projectForAsk(state)
// vvt exists in state
expect(result.missingDocuments.some(d => d.documentType === 'vvt')).toBe(false)
})
it('should include gaps from scope decision', () => {
const state = makeDecisionState()
const result = projector.projectForAsk(state)
expect(result.gaps.length).toBe(1)
expect(result.gaps[0].title).toBe('TOM fehlt')
})
it('should handle empty state', () => {
const state = makeState()
const result = projector.projectForAsk(state)
expect(result.gaps).toEqual([])
expect(result.missingDocuments).toEqual([])
})
})
describe('projectForValidate', () => {
it('should return a ValidationContext with correct structure', () => {
const state = makeDecisionState()
const result = projector.projectForValidate(state, ['vvt', 'tom', 'lf'])
expect(result).toHaveProperty('documents')
expect(result).toHaveProperty('crossReferences')
expect(result).toHaveProperty('scopeLevel')
expect(result).toHaveProperty('depthRequirements')
})
it('should include all requested document types', () => {
const state = makeDecisionState()
const result = projector.projectForValidate(state, ['vvt', 'tom'])
expect(result.documents.length).toBe(2)
expect(result.documents.map(d => d.type)).toContain('vvt')
expect(result.documents.map(d => d.type)).toContain('tom')
})
it('should include cross-references', () => {
const state = makeDecisionState()
const result = projector.projectForValidate(state, ['vvt', 'tom', 'lf'])
expect(result.crossReferences).toHaveProperty('vvtCategories')
expect(result.crossReferences).toHaveProperty('tomControls')
expect(result.crossReferences).toHaveProperty('retentionCategories')
expect(result.crossReferences.vvtCategories.length).toBe(1)
expect(result.crossReferences.vvtCategories[0]).toBe('Kundenverwaltung')
})
it('should include scope level', () => {
const state = makeDecisionState('L3')
const result = projector.projectForValidate(state, ['vvt'])
expect(result.scopeLevel).toBe('L3')
})
it('should include depth requirements per document type', () => {
const state = makeDecisionState()
const result = projector.projectForValidate(state, ['vvt', 'tom'])
expect(result.depthRequirements).toHaveProperty('vvt')
expect(result.depthRequirements).toHaveProperty('tom')
})
it('should summarize documents', () => {
const state = makeDecisionState()
const result = projector.projectForValidate(state, ['vvt', 'tom'])
expect(result.documents[0].contentSummary).toContain('1')
expect(result.documents[1].contentSummary).toContain('Keine TOM')
})
it('should handle empty state', () => {
const state = makeState()
const result = projector.projectForValidate(state, ['vvt', 'tom', 'lf'])
expect(result.scopeLevel).toBe('L1')
expect(result.crossReferences.vvtCategories).toEqual([])
expect(result.crossReferences.tomControls).toEqual([])
})
})
describe('token budget estimation', () => {
it('projectForDraft should produce compact output', () => {
const state = makeDecisionState()
const result = projector.projectForDraft(state, 'vvt')
const json = JSON.stringify(result)
// Rough token estimation: ~4 chars per token
const estimatedTokens = json.length / 4
expect(estimatedTokens).toBeLessThan(2000) // Budget is ~1500
})
it('projectForAsk should produce very compact output', () => {
const state = makeDecisionState()
const result = projector.projectForAsk(state)
const json = JSON.stringify(result)
const estimatedTokens = json.length / 4
expect(estimatedTokens).toBeLessThan(1000) // Budget is ~600
})
it('projectForValidate should stay within budget', () => {
const state = makeDecisionState()
const result = projector.projectForValidate(state, ['vvt', 'tom', 'lf'])
const json = JSON.stringify(result)
const estimatedTokens = json.length / 4
expect(estimatedTokens).toBeLessThan(3000) // Budget is ~2000
})
})
})