- Full i18n (DE/EN): UI, rules engine, content pack and generated documents; language switcher, browser-language default, architecture extensible to further EU languages via localized source texts - National country modules (opt-in per company profile): DE (KI-MIG/ BNetzA supervision, works council co-determination Sec. 87 BetrVG, GDPR/BDSG), AT (RTR AI service desk), IT (Law 132/2025: employer info duty, deepfake criminal liability, sector decrees), ES (AESIA, sandbox RD 817/2023), FR (CNIL guidance), NL (algorithm register); merged into obligation derivation, checklists and documents - New EU obligations: corrective actions (Art. 20/21), authorised representative (Art. 22), EU database registration for public-body deployers (Art. 49(3)), post-market monitoring (Art. 72), serious incident reporting (Art. 73), GDPR DPIA (Art. 35 GDPR / 26(9)) - New documents: AI procurement requirements specification (MUST/SHOULD matrix for vendors) and EU declaration of conformity draft (Annex V) - Cross-system checkable task list (due now / upcoming / done) with progress bar, one-tap toggling, per-system links - Legal safety: first-run acknowledgement gate (working aid, no legal services), legal notice panel in settings, result-screen disclaimer, disclaimers on every document - Responsive layout for phone/tablet (top-bar nav, scrollable tables, larger tap targets) - 12 new tests (44 total): EN pack parity, national merge, checklist grouping, new document generators Claude-Session: https://claude.ai/code/session_017YjohVNx1ZGNM4MX7tHpfv
91 lines
3.2 KiB
TypeScript
91 lines
3.2 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { CONTENT_PACK } from '../content/pack';
|
|
import { classify } from './classify';
|
|
import { actionPlan, annexIVSkeleton, inventoryReport, riskReport } from './documents';
|
|
import { deriveObligations } from './obligations';
|
|
import type { AISystem, AssessmentAnswers, CompanyProfile } from './types';
|
|
|
|
const answers: AssessmentAnswers = {
|
|
isAISystem: true,
|
|
role: 'provider',
|
|
isGPAIModel: false,
|
|
prohibitedPractices: [],
|
|
annexIIICategories: ['a3-employment'],
|
|
art63Exception: false,
|
|
involvesProfiling: true,
|
|
isAnnexISafetyComponent: false,
|
|
transparencyTriggers: ['tr-chatbot'],
|
|
};
|
|
|
|
const system: AISystem = {
|
|
id: 'sys-1',
|
|
name: 'HR-Screening',
|
|
purpose: 'Vorauswahl von Bewerbungen',
|
|
vendor: 'Beispiel GmbH',
|
|
owner: 'Jane Doe',
|
|
createdAt: '2026-07-01T00:00:00.000Z',
|
|
updatedAt: '2026-07-01T00:00:00.000Z',
|
|
answers,
|
|
classification: classify(answers, CONTENT_PACK),
|
|
obligationStatus: { 'ob-risk-mgmt': 'in-progress' },
|
|
};
|
|
|
|
const TODAY = '2026-07-04';
|
|
|
|
describe('documents', () => {
|
|
it('riskReport enthält Klassifizierung, Begründung und Disclaimer', () => {
|
|
const md = riskReport(system, CONTENT_PACK, TODAY);
|
|
expect(md).toContain('# Risikoklassifizierung');
|
|
expect(md).toContain('Hochrisiko');
|
|
expect(md).toContain('Annex III');
|
|
expect(md).toContain('keine Rechtsberatung');
|
|
expect(md).toContain(CONTENT_PACK.version);
|
|
});
|
|
|
|
it('actionPlan listet Pflichten mit Status und Frist', () => {
|
|
const obligations = deriveObligations(system.classification, answers, CONTENT_PACK);
|
|
const md = actionPlan(system, obligations, CONTENT_PACK, TODAY);
|
|
expect(md).toContain('Risikomanagementsystem');
|
|
expect(md).toContain('In Umsetzung');
|
|
expect(md).toContain('02.12.2027');
|
|
});
|
|
|
|
it('annexIVSkeleton befüllt bekannte Felder und markiert offene Punkte', () => {
|
|
const md = annexIVSkeleton(system, CONTENT_PACK, TODAY);
|
|
expect(md).toContain('Vorauswahl von Bewerbungen');
|
|
expect(md).toContain('_[AUSFÜLLEN]_');
|
|
expect(md).toContain('Annex IV');
|
|
});
|
|
|
|
it('inventoryReport listet alle Systeme tabellarisch', () => {
|
|
const md = inventoryReport([system], CONTENT_PACK, TODAY);
|
|
expect(md).toContain('HR-Screening');
|
|
expect(md).toContain('| System |');
|
|
expect(md).toContain('Registrierte KI-Systeme: **1**');
|
|
});
|
|
|
|
it('übernimmt das Firmenprofil in den Berichtskopf', () => {
|
|
const profile: CompanyProfile = {
|
|
name: 'Muster GmbH',
|
|
street: 'Musterstraße 1',
|
|
zipCity: '60311 Frankfurt',
|
|
contactPerson: 'Max Mustermann',
|
|
email: 'compliance@muster.de',
|
|
phone: '+49 69 000000',
|
|
website: 'www.muster.de',
|
|
logoDataUrl: '',
|
|
countries: [],
|
|
};
|
|
const md = riskReport(system, CONTENT_PACK, TODAY, profile);
|
|
expect(md).toContain('| **Unternehmen** | Muster GmbH |');
|
|
expect(md).toContain('compliance@muster.de');
|
|
expect(md).toContain('| **Ansprechpartner** | Max Mustermann |');
|
|
expect(inventoryReport([system], CONTENT_PACK, TODAY, profile)).toContain('Muster GmbH · Stand');
|
|
});
|
|
|
|
it('lässt den Berichtskopf ohne Profil unverändert', () => {
|
|
const md = riskReport(system, CONTENT_PACK, TODAY);
|
|
expect(md).not.toContain('**Unternehmen**');
|
|
});
|
|
});
|