Test-Fix (t_03d0f442): test_library-Katalog-Check datengetrieben statt harte 54

Die harte Erwartung lib.size() == 54 (15 Basis + 39 Natur-Update) brach bei
JEDER Kategorie-Karte: schon der A6-Stand hatte 56 (2 Beispiel-Objekte), nach
D1-D5 sind es 317. Der Check ist jetzt datengetrieben:

- Basis-Bestand bleibt garantiert: lib.size() >= 54 (Merge-Fundament A6) —
  der Katalog darf nur wachsen, schrumpfen failt weiterhin.
- Neu: Registry <-> Thumbnails deckungsgleich (je OID ein 128px-Thumb, keine
  verwaisten Thumbs) — fängt vergessene/abgebrochene sync_objects-Läufe.
- Neu: OID-Vergleich gegen die gemergte Match-Server-Registry
  (../scopedvr-match-server, Quelle der Wahrheit aus objects.d/*), wenn das
  Sibling-Repo liegt; ohne Sibling verkürzt sich der Test statt zu failen.
- Testgruppe Registry-Fußpunkt: "alle 54 Objekte" -> dynamische Zahl im Titel.

Suite grün auf main: 95/95 checks (ist 317). Beweis Drift-Erkennung: eine
injizierte Fremd-OID in der MS-Registry failt den neuen Vergleich gezielt.
This commit is contained in:
scopedvr-dev 2026-10-19 18:23:56 +00:00
parent 6572f1e10c
commit 8f598dc67d

View file

@ -58,12 +58,68 @@ func _load_manifest() -> Dictionary:
return parsed if parsed is Dictionary else {}
## Gemergte Registry aus dem Match-Server-Sibling (Quelle der Wahrheit —
## library/objects.json entsteht dort aus den Fragmenten objects.d/*.json,
## tools/sync_objects.sh zieht die Kopie hierher). Leer, wenn das Sibling
## fehlt (CI/Klon ohne Nachbar-Repo) — der Test verkürzt sich dann statt zu failen.
func _match_server_registry() -> Dictionary:
var base := ProjectSettings.globalize_path("res://")
for k in [base + "../scopedvr-match-server", base + "../../scopedvr-match-server"]:
var path: String = k + "/library/objects.json"
if FileAccess.file_exists(path):
var parsed: Variant = JSON.parse_string(FileAccess.get_file_as_string(path))
return parsed if parsed is Dictionary else {}
return {}
func _test_manifest_load() -> void:
print("Testgruppe: Bibliothek laden (library/objects.json)")
var manifest := _load_manifest()
_check(not manifest.is_empty(), "Manifest lädt und ist ein Objekt")
var lib: Dictionary = manifest.get("objects", {})
_check(lib.size() == 54, "Katalog hat die 54 Objekte (15 Basis + 39 Natur-Update, ist %d)" % lib.size())
# Datengetrieben statt harte Zahl: Der Basis-Bestand (54 Objekte aus dem
# Merge-Fundament A6) muss IMMER erhalten bleiben — der Katalog darf nur
# wachsen (A6-Beispiele, D1 Wände, D2 Absperrungen, D3 Gebäude, D4 Props,
# D5 Natur, ...). Eine exakte Zahl hier bricht bei jeder Kategorie-Karte.
_check(lib.size() >= 54, "Katalog enthält mindestens die 54 Basis-Objekte (ist %d)" % lib.size())
# Registry ↔ Thumbnails deckungsgleich: sync_objects.sh zieht objects.json
# UND rendert je Objekt einen Thumb. Drift (Objekt ohne Thumb oder verwaister
# Thumb) zeigt einen nicht gelaufenen/abgebrochenen Sync.
var fehlen_thumb: Array = []
var verwaist_thumb: Array = []
var tdir := DirAccess.open("res://library/thumbs")
if tdir != null:
var thumbs: Dictionary = {}
tdir.list_dir_begin()
var f := tdir.get_next()
while not f.is_empty():
if f.ends_with(".png"):
thumbs[f.get_basename()] = true
f = tdir.get_next()
tdir.list_dir_end()
for oid in lib.keys():
if not thumbs.has(str(oid)):
fehlen_thumb.append(str(oid))
for t in thumbs.keys():
if not lib.has(t):
verwaist_thumb.append(str(t))
_check(fehlen_thumb.is_empty() and verwaist_thumb.is_empty(),
"Registry und Thumbnails deckungsgleich (fehlen %d, verwaist %d)" % [fehlen_thumb.size(), verwaist_thumb.size()])
# Alle OIDs der Match-Server-Fragmente sind in der Editor-Kopie (wenn das
# Sibling-Repo neben dem Editor liegt, wie in tools/sync_objects.sh).
var ms := _match_server_registry()
if not ms.is_empty():
var ms_ids: Dictionary = ms.get("objects", {})
var fehlt_editor: Array = []
var fehlt_ms: Array = []
for oid in ms_ids.keys():
if not lib.has(str(oid)):
fehlt_editor.append(str(oid))
for oid in lib.keys():
if not ms_ids.has(str(oid)):
fehlt_ms.append(str(oid))
_check(fehlt_editor.is_empty() and fehlt_ms.is_empty(),
"Editor-Registry deckt alle OIDs des Match-Server-Merges ab (fehlen Editor %d / MS %d)" % [fehlt_editor.size(), fehlt_ms.size()])
var all_ok := true
for oid in lib.keys():
var entry: Dictionary = lib[oid]
@ -401,7 +457,7 @@ func _test_placement_foot_convention() -> void:
## Ghost UND Viewport-Bauer nutzen, kommt aus derselben Registry-Kollision
## (MeshShapes.half_height).
func _test_mesh_foot_point_registry() -> void:
print("Testgruppe: Registry-Fußpunkt — Mesh-Unterseite == pos (alle 54 Objekte)")
print("Testgruppe: Registry-Fußpunkt — Mesh-Unterseite == pos (alle Katalog-Objekte, ist %d)" % _load_manifest().get("objects", {}).size())
var manifest := _load_manifest()
var lib: Dictionary = manifest.get("objects", {})
var bad_primitiv: Array = []