2026-08-22 07:58:39 +00:00
|
|
|
|
# Proteus-Testdaten-Pipeline
|
|
|
|
|
|
|
|
|
|
|
|
Ergänzender Ansatz zur [Codeanalyse](UPDATEANALYSE.md): Statt den
|
|
|
|
|
|
`__register__`-Code zu lesen, wird ein deklarativ definierter Testdatensatz
|
|
|
|
|
|
über Proteus in zwei frisch installierten Tryton-Instanzen (5.0 und 8.0)
|
|
|
|
|
|
tatsächlich angelegt. Ziel ist, dieselbe Datei gegen beide Versionen laufen
|
|
|
|
|
|
zu lassen, damit sowohl die Skript-Ausgabe (`compare_snapshots.py`) als
|
|
|
|
|
|
auch anschließend die SQL-Dumps beider Datenbanken vergleichbar sind -
|
|
|
|
|
|
dafür müssen inhaltlich identische Daten in derselben Reihenfolge
|
|
|
|
|
|
eingespielt werden.
|
|
|
|
|
|
|
|
|
|
|
|
## Workflow
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
tests/scenario_*.rst (Tryton-Repo, pro Modul)
|
|
|
|
|
|
│ rst_to_dataset.py
|
|
|
|
|
|
▼
|
|
|
|
|
|
rsts/<version>e/*.yaml (ein YAML-Record pro angelegtem Proteus-Objekt)
|
|
|
|
|
|
│ rsts_merge_dedup.py
|
|
|
|
|
|
▼
|
|
|
|
|
|
reports/<version>_testdata_merged.yaml (dedupliziert, abhängigkeitskorrekt sortiert)
|
|
|
|
|
|
│ create_dataset.py --database ...
|
|
|
|
|
|
▼
|
|
|
|
|
|
snapshot_<version>.json
|
|
|
|
|
|
│ compare_snapshots.py snapshot_a.json snapshot_b.json
|
|
|
|
|
|
▼
|
|
|
|
|
|
Fund-Liste: status_changed / value_changed / missing_record
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 1. `rst_to_dataset.py` – Testdaten aus den Modul-eigenen Tests extrahieren
|
|
|
|
|
|
|
|
|
|
|
|
Tryton-Module bringen eigene `tests/scenario_*.rst`-Doctests mit (ausführbarer
|
|
|
|
|
|
Proteus-Code in `>>>`-Zeilen). Das Skript parst diese Dateien per AST und
|
|
|
|
|
|
erkennt:
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
>>> Party = Model.get('party.party')
|
|
|
|
|
|
>>> party = Party(name='Testfirma GmbH')
|
|
|
|
|
|
>>> party.vat_number = '123'
|
|
|
|
|
|
>>> party.save()
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
→ wird zu einem YAML-Record mit `key`, `model`, `fields`. Referenzen auf
|
|
|
|
|
|
zuvor im selben File angelegte Records werden zu `{"$ref": "..."}`,
|
2026-08-22 17:45:46 +02:00
|
|
|
|
`Decimal(...)`/`date(...)` zu `{"$decimal": ...}`/`{"$date": ...}`.
|
|
|
|
|
|
|
|
|
|
|
|
Ein weiteres, sehr häufiges Muster wird ebenfalls erkannt: Zugriff auf
|
|
|
|
|
|
bereits vorhandene Stammdaten (Länder, Währungen, Einheiten,
|
|
|
|
|
|
Standard-Lagerorte, ... - i. d. R. per XML-Fixture beim Modul-Aktivieren
|
|
|
|
|
|
angelegt, nicht Teil des eigentlichen Testdatensatzes):
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
>>> ProductUom = Model.get('product.uom')
|
|
|
|
|
|
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
→ wird zu `{"$search": {"model": "product.uom", "domain": [["name", "=", "Unit"]]}}`
|
|
|
|
|
|
- aufgelöst wird das erst live beim Anlegen durch `create_dataset.py`
|
|
|
|
|
|
(Schritt 3), nicht schon hier, da diese Records ja nicht selbst Teil der
|
|
|
|
|
|
YAML sind. Nur literale Domains werden erkannt (keine Variablen darin);
|
|
|
|
|
|
sonst bleibt es wie bisher `$manual`.
|
|
|
|
|
|
|
|
|
|
|
|
Alles sonst nicht automatisch Auflösbare (Wizards, `.click(...)`,
|
|
|
|
|
|
Helper-Funktionsaufrufe) wird als `{"$manual": "<Ausdruck>"}` markiert oder
|
|
|
|
|
|
ganz übersprungen - bewusst sichtbar unvollständig statt still falsch.
|
2026-08-22 07:58:39 +00:00
|
|
|
|
|
|
|
|
|
|
Record-Keys werden mit dem Dateinamen präfixiert, damit beim
|
|
|
|
|
|
Zusammenführen mehrerer Module keine Kollisionen entstehen.
|
|
|
|
|
|
|
|
|
|
|
|
### 2. `rsts_merge_dedup.py` – pro Version zusammenführen
|
|
|
|
|
|
|
|
|
|
|
|
Liest alle `rsts/<version>e/*.yaml` eines Verzeichnisses ein und entfernt
|
|
|
|
|
|
Records mit identischem `model`+`fields` (z. B. dasselbe
|
|
|
|
|
|
Basis-`product.template`, das in vielen Szenarien wiederverwendet wird).
|
|
|
|
|
|
`$ref`-Verweise auf entfernte Duplikate werden auf den behaltenen Record
|
|
|
|
|
|
umgeschrieben. Danach wird per Kahn-Algorithmus so sortiert, dass
|
|
|
|
|
|
`$ref`-Abhängigkeiten immer VOR dem referenzierenden Record stehen, aber
|
|
|
|
|
|
innerhalb der Freiheitsgrade weiterhin nach `model` gruppiert wird.
|
|
|
|
|
|
|
|
|
|
|
|
**Achtung:** Dedupliziert bewusst GLOBAL, auch innerhalb derselben Datei.
|
|
|
|
|
|
Bei Szenarien, die zwei absichtlich gleich aussehende Records anlegen (z. B.
|
|
|
|
|
|
`scenario_party_replace.rst`: zwei Parteien namens "Pam", um eine zu
|
|
|
|
|
|
ersetzen), verändert das den Sinn des Szenarios - für einen groben
|
|
|
|
|
|
Testdatensatz ist das in Ordnung, für szenario-treue Tests nicht.
|
|
|
|
|
|
|
|
|
|
|
|
### 3. `create_dataset.py` – gegen eine echte Instanz anlegen
|
|
|
|
|
|
|
|
|
|
|
|
Legt die Records der Reihe nach per Proteus an, liest jeden Wert danach
|
|
|
|
|
|
FRISCH VOM SERVER zurück (nicht aus dem lokalen Proteus-Cache), damit
|
|
|
|
|
|
automatische Reformatierung sichtbar wird. Ergebnis: ein JSON-Snapshot
|
|
|
|
|
|
(`status`/`error`/`stored` pro Record).
|
|
|
|
|
|
|
|
|
|
|
|
**Feld-Umbenennungen zwischen Versionen** (`FIELD_RENAMES`): Da dieselbe
|
|
|
|
|
|
YAML gegen mehrere Versionen laufen soll, aber Felder umbenannt werden
|
|
|
|
|
|
können (bestätigt: `stock.move.uom` → `stock.move.unit` zwischen 5.0 und
|
|
|
|
|
|
8.0), prüft das Skript pro Modell zuerst per `fields_get()`, ob der
|
|
|
|
|
|
YAML-Feldname auf dem jeweiligen Server existiert, und weicht sonst auf
|
|
|
|
|
|
einen bekannten, bidirektionalen Alias aus. Ein wirklich unbekanntes Feld
|
|
|
|
|
|
bleibt trotzdem ein Fehler - das ist ja gerade das gesuchte Signal für eine
|
|
|
|
|
|
echte Schema-Änderung. Weitere Umbenennungen bei Bedarf in `FIELD_RENAMES`
|
|
|
|
|
|
ergänzen, sobald ein Lauf sie aufdeckt.
|
|
|
|
|
|
|
2026-08-22 17:45:46 +02:00
|
|
|
|
**`$search`-Auflösung:** Werte der Form `{"$search": {"model": ...,
|
|
|
|
|
|
"domain": ...}}` (von `rst_to_dataset.py` erzeugt, siehe Schritt 1) werden
|
|
|
|
|
|
live per `Model.get(model).find(domain)` gegen den Zielserver aufgelöst -
|
|
|
|
|
|
für Stammdaten, die nicht selbst angelegt werden, sondern schon per
|
|
|
|
|
|
XML-Fixture da sind. Ergebnis wird pro Lauf gecacht (dieselbe Suche, z. B.
|
|
|
|
|
|
nach der Einheit "Unit", kommt in vielen Records vor). Kein Treffer ->
|
|
|
|
|
|
Fehler mit Klartext statt eines neu angelegten Records - das ist selbst
|
|
|
|
|
|
ein Signal, dass sich die Stammdaten zwischen den Versionen geändert
|
|
|
|
|
|
haben.
|
|
|
|
|
|
|
2026-08-22 07:58:39 +00:00
|
|
|
|
### 4. `compare_snapshots.py` – zwei Versionen gegenüberstellen
|
|
|
|
|
|
|
|
|
|
|
|
Vergleicht zwei Snapshots (gleiche YAML, zwei Versionen/Datenbanken) und
|
|
|
|
|
|
markiert `status_changed` (ein Record schlägt nur in einer Version fehl -
|
|
|
|
|
|
Kandidat für Format-/Constraint-/Pflichtfeld-Änderung), `value_changed`
|
|
|
|
|
|
(gespeicherter Wert weicht trotz gleichem Input ab - automatische
|
|
|
|
|
|
Reformatierung) und `missing_record`.
|
|
|
|
|
|
|
|
|
|
|
|
## Stand
|
|
|
|
|
|
|
|
|
|
|
|
- `rst_to_dataset.py` und `rsts_merge_dedup.py` sind gegen echte
|
|
|
|
|
|
`scenario_*.rst`-Dateien aus `/mnt/tryton` (5.0- und 8.0-Branch) getestet,
|
|
|
|
|
|
siehe `reports/5_testdata_merged.yaml` / `reports/8_testdata_merged.yaml`
|
|
|
|
|
|
(jeweils NUR aus den Szenarien der jeweiligen Version extrahiert, noch
|
|
|
|
|
|
NICHT zu einer gemeinsamen Datei zusammengeführt).
|
|
|
|
|
|
- `create_dataset.py` und `compare_snapshots.py` sind noch nicht gegen eine
|
|
|
|
|
|
echte Tryton-Instanz gelaufen (nur syntaktisch geprüft).
|
|
|
|
|
|
- Welche Modelle/Felder überhaupt Testdaten brauchen bzw. wo sich 5.0 und
|
|
|
|
|
|
8.0 tatsächlich unterscheiden, siehe [MODELLVERGLEICH.md](MODELLVERGLEICH.md)
|
|
|
|
|
|
- das steuert, wo als Nächstes von Hand YAML-Records ergänzt werden.
|