Zum Inhalt

Immohai Validierungs- und Teststrategie

Status: verbindlich
Spezifikationsversion: 1.1.1
Modellbezug: 0.6.2
Implementierungsbezug: frontend-mvp-0.2.5
Stand: 2026-07-05

1. Zweck

Die Validierung schützt die fachliche Wahrheit und die verbindlichen Datenverträge.

Sie stellt sicher:

Formeln rechnen reproduzierbar.
Schemas, Konfiguration und Frontend-Kopien stimmen überein.
Einzel- und Batch-Vollanalyse verwenden dieselbe Engine.
Szenario-Slider werden vor der Berechnung im PropertyInput gespeichert.
Annahmen, Herkunft und Szenario-Policies bleiben sichtbar.
Datenqualität wird reproduzierbar bewertet.
Case Ratings und Investment Rating bleiben getrennt.
Snapshots werden nicht still neu berechnet.
Reports verwenden ausschließlich berechnete oder gespeicherte Snapshots.
Änderungen brechen Regeln nicht unbemerkt.

2. Testebenen

1. vollständige JSON-Schema-Validierung der verwendeten Schemafunktionen
2. Konfigurations- und Versionssynchronisation
3. Semantikvalidierung von PropertyInput
4. Einheiten- und Formeltests
5. Szenario- und Engine-Regression
6. AnalysisResult- und Datenqualitätstest
7. Case-Rating-, Investment-Rating- und UNRATED-Test
8. PropertyRecord- und Snapshot-Test
9. RentEstimate- und Marktmiettest
10. Batch-Konsistenztest
11. Frontend-Controller- und Browserpfadtest
12. Dokumentations- und Spezifikationskonsistenztest
13. Docker-Compose-Validierung
14. MkDocs-Strict-Build
15. Browserabnahme

3. Aktuelle Prüfskripte

scripts/check-active-frontend-syntax.mjs
scripts/check-frontend-config-sync.sh
scripts/check-engine-version-sync.mjs
scripts/validate-property-input.mjs
scripts/check-baseline.mjs
scripts/check-analysis-contract.mjs
scripts/check-property-record-store.mjs
scripts/check-property-record-adapter.mjs
scripts/check-single-analysis-controller.mjs
scripts/check-market-rent-aggregation.mjs
scripts/check-specification-consistency.mjs
scripts/check-frontend-baseline.sh
scripts/deploy-and-verify.sh
scripts/lib/json-schema-validator.mjs

Die Pull-Request-CI führt die Einzelprüfungen in getrennten, klar benannten Schritten aus. Dadurch ist ein Fehler unmittelbar einem Vertrag oder einer Testgruppe zuordenbar.

4. Kanonische Konfiguration und Schemas

model/config/model-manifest.json
model/config/default-assumptions.json
model/config/scenario-presets.json
model/config/object-types.json

model/schema/property-input.schema.json
model/schema/analysis-result.schema.json
model/schema/property-record.schema.json
model/schema/rent-estimate.schema.json
model/schema/market-rent-import.schema.json
model/schema/analysis-data-quality.schema.json

Frontend-Kopien unter frontend/assets/config/ müssen bytegleich sein. Die Synchronisation wird vor den fachlichen Regressionen geprüft.

5. JSON-Schema-Validierung

Der gemeinsame Validator prüft die im Repository verwendeten JSON-Schema-Funktionen:

type
required
properties
additionalProperties
const
enum
minLength und maxLength
pattern
minimum und maximum
exclusiveMinimum und exclusiveMaximum
minItems, maxItems und uniqueItems
format date, date-time und uri
allOf
if, then und else
lokale und externe $ref

Zu validieren sind mindestens:

PropertyInput gegen property-input.schema.json
serialisiertes AnalysisResult gegen analysis-result.schema.json
neuer PropertyRecord gegen property-record.schema.json

Nicht serialisierte Runtime- oder Legacy-Lesealiase dürfen die Schemavalidierung nicht beeinflussen.

6. PropertyInput 0.5.1

Zu prüfen:

Pflichtgruppen und Pflichtfelder
positive Kaufpreis- und Flächenwerte
input_mode quick, expert oder batch
analysis_origin und input_channel getrennt
monthly_cold_rent kanonisch
rent_basis vorhanden
interest_basis und interest_reset_basis vorhanden
origin missing mit value null
value null nur mit origin missing
value_status final nur mit scenario_policy fixed
created_at und Versionen
scenario_order exakt Best, Base, Stress
Base als einziges Basisszenario
keine Base-Overrides
ETF-Rendite nicht als Immobilien-Szenario-Override
CAPEX-Methode und aktives Wertfeld konsistent
special_capex vollständig
custom_sliders enthält alle sieben erforderlichen Steuerwerte
ListingSourceReference vollständig und reference_only
RentEstimateReference verwendet kanonische Felder
market_rent_estimate enthält Dataset-ID, Version, Scope, Gruppierungsfelder, P25, Median, P75 und Samplegröße

Legacy-Aliase dürfen nur über explizite Leseadapter wirken und werden von neuen PropertyInputs nicht geschrieben.

7. Szenarioauflösung

Zu prüfen:

Szenarioresolver greift nicht auf das DOM zu.
Sliderwerte werden im PropertyInput gespeichert.
Base bleibt bei Presets und eigenen Slidern unverändert.
Best und Stress werden reproduzierbar aus demselben Snapshot erzeugt.
Finale Werte bleiben unverändert.
Objektprofil-Anpassungen wirken nur auf Profil- oder Defaultwerte.
derived, estimated, imported und scraped werden nicht objektprofilverändert.
fixed bleibt unverändert.
scenario_adjustable erhält nur zulässige Deltas.
sensitivity_only wird separat untersucht.
ETF-Rendite bleibt in Best, Base und Stress identisch.
rent_basis und interest_basis bestimmen die Standard-Policy.

8. Erwerb und Finanzierung

closing_costs = purchase_price × closing_costs_pct
total_acquisition_cost = purchase_price + closing_costs
total_initial_investment enthält initial_renovation_costs
equity_contributed ist auf total_initial_investment begrenzt
loan_amount enthält die gesamte Finanzierungslücke
non_purchase_costs_financed_amount ist korrekt
cash_reserve_after_closing verändert das Darlehen nicht

9. Darlehensverlauf

Initialzins bis einschließlich interest_reset_year
Reset-Zins ab dem Folgejahr
Reset-Kapitaldienst aus offener Restschuld
keine negative Tilgung
keine negative Restschuld
keine Überzahlung
kein Darlehen -> DSCR null und not_applicable
Darlehen ohne Kapitaldienst -> kritischer Befund und RED

10. Kosten und Cashflow

NOI enthält Leerstand und nicht umlagefähige Betriebskosten
Reserve und CAPEX liegen außerhalb des NOI
Cashflow zieht Reserve, CAPEX, Sonder-CAPEX und Kapitaldienst ab
Hausgeld wird nicht doppelt abgezogen
recurring_capex_method steuert genau eine Methode
Fix-CAPEX wird inflationsfortgeschrieben
Sonder-CAPEX bleibt nominal im Ereignisjahr

11. KPIs, Eigenkapital und ETF

initial_ltv = loan_amount / purchase_price
current_ltv_y = debt_y / property_value_y
DSCR = (NOI - Reserve) / Kapitaldienst
noi_yield_y1 = noi_y1 / total_initial_investment
gross_rent_y1 ist der kanonische Jahresmiet-KPI
annual_gross_rent_y1 wird nicht neu serialisiert
ETF startet mit equity_contributed
Jahrescashflows werden am Jahresende angesetzt
Verkaufskosten und Restschuld werden im terminalen Vergleich abgezogen
terminal_net_vs_etf_after_exit ist Hauptendkennzahl

Für equity_contributed = 0 sind ausdrücklich zu testen:

kein fiktiver 1-EUR-Nenner
jeder positive kumulative Zuschussbedarf ist kritisch
terminaler ETF-Vergleich > 0 ist gut
terminaler ETF-Vergleich = 0 ist grenzwertig
terminaler ETF-Vergleich < 0 ist kritisch

12. AnalysisDataQualityAssessment 0.2.0

Verbindliche Methode:

docs/specification/data-quality-and-validation/data-quality-assessment.md
analysis-dq-0.2.0

Zu prüfen:

Komponentenscores entsprechen den dokumentierten Tabellen
Rundung entspricht der Spezifikation
Gesamtscore liegt zwischen 0 und 100
Methodenversion wird gespeichert
identischer PropertyInput erzeugt identischen Score
confidence und confidence_level bleiben getrennt
numerische confidence wird deterministisch kategorisiert
Score unter 50 oder fehlender Score führt zu UNRATED
Datenqualität verändert keine Finanzformel

Listing-Datenqualität und RentEstimate-Confidence bleiben getrennt.

13. ScenarioResult und AnalysisResult 0.1.1

Jeder ScenarioResult enthält:

scenario_name
projection
kpis
kpi_assessments
scenario_assessment
scenario_rating
plausibility_checks
warnings

Jeder vollständige Rechenlauf erzeugt ein AnalysisResult mit mindestens:

analysis_result_id
property_input_id
property_input
Modell-, Engine-, Schema-, Konfigurations- und Methodenversionen
created_at
scenario_results
base_summary_kpis
investment_rating
data_quality_assessment
warnings
calculation_status

Zu prüfen:

Best, Base und Stress sind enthalten.
Jeder Case besitzt ein ScenarioRating.
Jede Projektionszeile erfüllt den vollständigen Feldvertrag.
ScenarioResults serialisieren kein Investment Rating.
Nur AnalysisResult besitzt das Investment Rating.
Base-KPIs stimmen mit base_summary_kpis überein.
Stress bestimmt die Robustness-Komponente.
Best beeinflusst das Investment Rating nicht.
property_input_id stimmt mit property_input.meta.property_input_id überein.
property_input ist gegen spätere Formularänderung isoliert.
Runtime-, Snapshot- und Kompatibilitätsaliase werden nicht serialisiert.
Das serialisierte Ergebnis erfüllt analysis-result.schema.json vollständig.

14. Rating

fehlender oder unzureichender DQ-Score ergibt UNRATED
Darlehen ohne Kapitaldienst ergibt RED
Base-DSCR unter 1 bei Darlehen ergibt RED
initial_ltv über 1 ergibt RED
kritischer ETF-Nachteil, Liquidität oder Stress ergibt RED
GREEN erfordert gute Komponenten und ausreichende Datenqualität
keine zusätzliche RED-Regel außerhalb der kanonischen harten Befunde
Case Ratings haben keine Rückwirkung auf Formeln oder Investment Rating

15. PropertyRecord 0.1.1 und Snapshot

Berechnen allein speichert nicht.
Bewusstes Speichern erzeugt einen schema-validen PropertyRecord 0.1.1.
AnalysisResult-ID und alle kanonischen Versionen werden gespeichert.
PropertyInput und ScenarioResults werden unverändert gespeichert.
Case Ratings bleiben im Snapshot erhalten.
Investment Rating wird separat gespeichert.
Datenqualität und Methodenversion werden gespeichert.
object_id wird nicht in den berechneten PropertyInput geschrieben.
Öffnen zeigt den gespeicherten Snapshot.
Öffnen startet keinen Rechenlauf.
Alle im Formular repräsentierbaren Eingaben werden wiederhergestellt.
Szenario-Steuerungsmodus und alle sieben Sliderwerte werden wiederhergestellt.
Nicht sichtbare Annahmen bleiben für eine bewusste Neuberechnung erhalten.
Eine bewusste Neuberechnung verändert den alten Snapshot nicht.
Eine geänderte manuelle Miete entfernt eine veraltete RentEstimateReference.
Formularänderungen können nicht mit altem Ergebnis gespeichert werden.
CSV verwendet gespeicherte Werte.
Legacy-Aliase werden nicht neu geschrieben.
Legacy-Quellenlinks werden in den kanonischen Referenzvertrag überführt.

16. RentEstimate und Marktmietdaten

nur CSV ist aktueller Dateiimport
nur Nettokaltmieten automatisch verwenden
Marktarten nicht vermischen
Wohnung, Zimmer und Gewerbe nicht vermischen
Möblierung trennen
Dubletten und Ausreißer markieren
draft und archived nicht im Lookup verwenden
published nur innerhalb seines Scope verwenden
pro scope_id genau ein published Dataset
neue Veröffentlichung archiviert nur den vorherigen Stand desselben Scope
Dataset-ID, Scope und Version speichern
RentEstimateReference verwendet kanonische Dataset-Namen
RentEstimateReference enthält alle für DQ und Reporting benötigten Felder
Inseratslink wird ausschließlich als Quellenreferenz gespeichert.
Es erfolgt keine automatische Portalabfrage.
Es erfolgt keine automatische Feldübernahme.
Der Link wird nicht direkt berechnet.
PropertyInput und PropertyRecord verwenden den strukturierten ListingSourceReference-Vertrag.
PropertyRecord verwendet listing_import_reference.

18. Batch-Konsistenz

identisches PropertyInput
-> identische ScenarioInputs
-> identische Projektionen
-> identische KPIs
-> identische Case Ratings
-> identisches Investment Rating

Pre-Screening darf keine finanzierungsabhängigen KPIs erzeugen. NOI-basierte Kennzahlen sind nur zulässig, wenn alle benötigten Kosten- und Erwerbsdaten vorhanden oder transparent defaulted sind.

19. Reporting

Reportvorschau verwendet AnalysisResult-Snapshot.
Objektbericht verwendet PropertyRecord-Snapshot.
Vergleich verwendet PropertyRecord-Snapshots.
Unberechnete Formularwerte verändern keinen Report.
Reports erzeugen keine Formeln, Schwellen oder Ratings.
ScenarioRatings und Investment Rating werden getrennt dargestellt.
Sliderwerte stammen aus dem PropertyInput-Snapshot.
Kanonische Metadaten- und Datasetfelder werden verwendet.

Die bekannten Architekturabweichungen der aktuellen Reporting-Implementierung werden separat über die bestehenden Architektur-Proposals behandelt. Sie sind nicht Teil dieses Korrekturblocks.

20. Spezifikationskonsistenz

Automatisch zu prüfen:

Manifest- und Schemaversionen stimmen überein.
Frontend-Konfiguration ist bytegleich zur Modellkonfiguration.
Dokumentierte Vertragsversionen stimmen mit dem Manifest überein.
Keine Entwurfsdatei ist als verbindliche Wahrheit navigiert.
Keine abweichenden Report-Metadatenfelder.
Keine zweite RentEstimate-Struktur.
Keine veralteten AnalysisResult-Feldlisten.
Keine neuen Legacy-Felder in kanonischen Beispielen.
Szenarioresolver enthält keinen DOM-Zugriff.
Aktive Dateien enthalten keine veraltete Engine-Version.
Snapshot-Adapter erhalten den vollständigen geladenen Eingabesnapshot.
PropertyRecord-Adapter stellt die Szenario-Steuerung wieder her.

Die strukturelle Konsistenzprüfung bleibt bewusst schnell und ersetzt nicht die fachlichen Regressionen.

21. CI, Docker und Dokumentation

Pull-Request-CI:

Konfigurations-Sync
Engine-Version-Sync
Frontend-Syntax
PropertyInput-Validierung
Baseline-Regressionen
AnalysisResult-Vertrag
PropertyRecord-Store und Adapter
SingleAnalysisController
Marktmietaggregation
Spezifikationskonsistenz
Docker-Compose-Konfiguration
MkDocs build --strict

Der MkDocs-Build muss Navigation und interne Links ohne Fehler verarbeiten.

22. Browserabnahme

Bei UI-Änderungen mindestens prüfen:

Quick-Analyse
Best/Base/Stress mit Presets
Best/Base/Stress mit eigenen Slidern
Base bleibt bei Slidern unverändert
Case Ratings
Investment Rating
UNRATED
Datenqualität
Reportvorschauen
Speichern zu Meine Objekte
Snapshot öffnen ohne automatische Neuberechnung
vollständige Wiederherstellung sichtbarer Eingaben und Szenario-Slider
bewusste Neuberechnung ohne Verlust gespeicherter Annahmen
Inseratslink als Quellenreferenz
Marktmiet-Scope
Browser-Konsole
responsive Darstellung

23. Abnahmekriterium

Ein Block ist erst abgeschlossen, wenn:

führende Spezifikation aktualisiert
betroffene Querverweise synchronisiert
Schema und Konfiguration synchronisiert
Code angepasst
Regressionstests erfolgreich
Frontend-Baseline erfolgreich
Spezifikationskonsistenz erfolgreich
Docker-Compose-Konfiguration gültig
Dokumentation streng gebaut
Roadmap und Arbeitsstand aktualisiert
Browserabnahme erfolgt, soweit UI betroffen
Review abgeschlossen