Merge commit '265ce30f81b1302ca374330f7129f02d6fde4697' into HEAD
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
extends SceneTree
|
||||
|
||||
const Model = preload("res://scripts/relationship/original_relationship_state.gd")
|
||||
const Service = preload("res://scripts/relationship/relationship_service.gd")
|
||||
var _checks := 0
|
||||
var _failures := 0
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
call_deferred("_run")
|
||||
|
||||
|
||||
func _expect(value: bool, message: String) -> void:
|
||||
_checks += 1
|
||||
if not value:
|
||||
_failures += 1
|
||||
push_error(message)
|
||||
|
||||
|
||||
static func fixture() -> Dictionary:
|
||||
# Validation fixtures, never approved initialization or range limits.
|
||||
return {"subject_id": "heroine_anri", "protagonist_id": "player", "rival_id": "rival_rai",
|
||||
"affection": 142.5, "lust": 23.25, "netorare": 11, "shield_current": 90.5, "shield_max": 142.5}
|
||||
|
||||
|
||||
func _service() -> RelationshipService:
|
||||
return Service.new().configure_original_identity_validator(
|
||||
func(p: Dictionary) -> bool:
|
||||
return p["subject_id"] == "heroine_anri" and p["protagonist_id"] == "player" and p["rival_id"] == "rival_rai"
|
||||
)
|
||||
|
||||
|
||||
static func legacy_fixture() -> Dictionary:
|
||||
return {"version": 1, "relationships": [
|
||||
{"relationship_id": "aliases", "actor_a_id": "heroine_anri", "actor_b_id": "player",
|
||||
"metrics": {"affinity": 41, "obligation": 19, "extension": {"opaque": [3, "keep"]}},
|
||||
"extra": ["raw", 2.5]},
|
||||
{"relationship_id": "second", "actor_a_id": "departed", "actor_b_id": "player",
|
||||
"metrics": {"trust": 7}}],
|
||||
"threshold_candidates": [], "next_candidate_sequence": 1, "extension": ["ordered", 7]}
|
||||
|
||||
|
||||
func _alias_mutations() -> void:
|
||||
var service := _service()
|
||||
var legacy := legacy_fixture()
|
||||
var input := var_to_bytes(legacy)
|
||||
_expect(service.restore_snapshot(legacy), "int v1 aliases accepted")
|
||||
_expect(service.apply_value_change(&"aliases", &"affection", 2, 0).is_success(), "affection mutation through real service")
|
||||
_expect(service.apply_value_change(&"aliases", &"dependence", 3, 0).is_success(), "dependence mutation through real service")
|
||||
var saved := service.create_snapshot()
|
||||
var raw: Dictionary = saved[&"legacy"]["relationships"][0]
|
||||
_expect(raw["metrics"]["affection"] == 43 and raw["metrics"]["dependence"] == 22, "canonical changes merged")
|
||||
_expect(raw["metrics"]["affinity"] == 41 and raw["metrics"]["obligation"] == 19, "aliases remain old facts")
|
||||
_expect(raw["metrics"]["extension"] == legacy["relationships"][0]["metrics"]["extension"] and raw["extra"] == ["raw", 2.5], "unknown facts survive mutation")
|
||||
_expect(saved[&"legacy"]["relationships"][1] == legacy["relationships"][1], "record order preserved")
|
||||
var restored := _service()
|
||||
_expect(restored.restore_snapshot(saved), "mutated v2 restores")
|
||||
_expect(var_to_bytes(restored.create_snapshot()) == var_to_bytes(saved), "mutated v2 raw order and bytes preserved")
|
||||
_expect(restored.get_relationship(&"aliases").get_metric(&"affection") == 43 and restored.get_relationship(&"aliases").get_metric(&"dependence") == 22, "canonical values take precedence over raw aliases")
|
||||
for key: StringName in [&"affection", &"lust", &"netorare"]:
|
||||
_expect(restored.get_original_relationship(&"heroine_anri")[key] == null, "old axes never configure original " + String(key))
|
||||
_expect(var_to_bytes(legacy) == input, "mutation never changes input bytes")
|
||||
for value: Variant in ["50", null, true, 50.0, 1.5, INF, NAN]:
|
||||
var bad := legacy_fixture()
|
||||
bad["relationships"][0]["metrics"]["affinity"] = value
|
||||
_rejected(restored, bad)
|
||||
var bad := legacy_fixture()
|
||||
bad["relationships"][0]["metrics"] = []
|
||||
_rejected(restored, bad)
|
||||
bad = saved.duplicate(true)
|
||||
bad[&"relationships"] = []
|
||||
_rejected(restored, bad)
|
||||
|
||||
|
||||
func _run() -> void:
|
||||
_alias_mutations()
|
||||
var service := _service()
|
||||
var before := var_to_bytes(service.create_snapshot())
|
||||
var unset := service.get_original_relationship(&"heroine_anri")
|
||||
_expect(not unset[&"configured"] and unset[&"affection"] == null, "missing record is unset")
|
||||
unset[&"affection"] = 999
|
||||
_expect(var_to_bytes(service.create_snapshot()) == before, "reads own their dictionaries")
|
||||
var payload := fixture()
|
||||
var payload_bytes := var_to_bytes(payload)
|
||||
_expect(service.register_original_relationship(payload).is_success(), "complete directional fixture registers")
|
||||
payload["affection"] = 0
|
||||
_expect(service.get_original_relationship(&"heroine_anri")["affection"] == 142.5, "caller cannot mutate stored facts")
|
||||
_expect(not service.register_original_relationship(fixture()).is_success(), "duplicate subject rejected")
|
||||
var saved := service.create_snapshot()
|
||||
var restored := _service()
|
||||
_expect(restored.restore_snapshot(saved), "new-only v2 restores")
|
||||
_expect(var_to_bytes(restored.create_snapshot()) == var_to_bytes(saved), "new-only exact bytes")
|
||||
_expect(var_to_bytes(fixture()) == payload_bytes, "fixture is explicit and stable")
|
||||
for field: String in Model.FIELDS:
|
||||
var missing := fixture()
|
||||
missing.erase(field)
|
||||
_expect(Model.from_snapshot(missing) == null, "missing required field " + field)
|
||||
for value: Variant in [null, true, "12", INF, NAN, {}, []]:
|
||||
var wrong := fixture()
|
||||
wrong["lust"] = value
|
||||
_expect(Model.from_snapshot(wrong) == null, "wrong/nonfinite numeric value rejected")
|
||||
for patch: Dictionary in [{"shield_max": 1}, {"shield_current": -1}, {"shield_current": 143}, {"rival_id": "player"}, {"subject_id": "player"}]:
|
||||
var wrong := fixture()
|
||||
wrong.merge(patch, true)
|
||||
_expect(Model.from_snapshot(wrong) == null, "value/identity invariant rejected")
|
||||
var zero := fixture()
|
||||
for field: String in ["affection", "lust", "netorare", "shield_current", "shield_max"]:
|
||||
zero[field] = 0
|
||||
_expect(Model.from_snapshot(zero) != null, "explicit zero is configured")
|
||||
var legacy := {"version": 1, "relationships": [{"relationship_id": "old", "actor_a_id": "player", "actor_b_id": "heroine_anri",
|
||||
"metrics": {"affinity": 117, "dependence": 29, "extension": {"opaque": [3, "keep"]}}, "extra": ["raw", 2.5]}],
|
||||
"threshold_candidates": [], "next_candidate_sequence": 1, "extension": {"version": 70}}
|
||||
var legacy_bytes := var_to_bytes(legacy)
|
||||
var migrated := _service()
|
||||
_expect(migrated.restore_snapshot(legacy), "legacy aliases still usable")
|
||||
_expect(migrated.get_relationship(&"old").get_metric(&"affection") == 100, "legacy normalization behavior retained")
|
||||
_expect(not migrated.get_original_relationship(&"heroine_anri")[&"configured"], "legacy never configures original facts")
|
||||
_expect(var_to_bytes(migrated.create_snapshot()[&"legacy"]) == legacy_bytes, "all raw legacy values and extensions preserved")
|
||||
_expect(var_to_bytes(legacy) == legacy_bytes, "legacy input bytes untouched")
|
||||
_expect(migrated.register_original_relationship(fixture()).is_success(), "mixed state registers independently")
|
||||
var mixed := migrated.create_snapshot()
|
||||
_expect(restored.restore_snapshot(mixed), "mixed snapshot restores")
|
||||
_expect(var_to_bytes(restored.create_snapshot()) == var_to_bytes(mixed), "mixed exact bytes")
|
||||
_expect(migrated.apply_value_change(&"old", &"trust", 3, 0).is_success(), "legacy changes still apply")
|
||||
var changed: Dictionary = migrated.create_snapshot()[&"legacy"]
|
||||
_expect(changed["relationships"][0]["metrics"]["affinity"] == 117 and changed["relationships"][0]["metrics"]["trust"] == 3, "changed metric preserves raw alias")
|
||||
for version: Variant in [0, 3, "2", 2.0, true]:
|
||||
var bad := mixed.duplicate(true)
|
||||
bad[&"version"] = version
|
||||
_rejected(restored, bad)
|
||||
var duplicate := mixed.duplicate(true)
|
||||
duplicate[&"original_relationships"].append(fixture())
|
||||
_rejected(restored, duplicate)
|
||||
for patch: Dictionary in [{"rival_id": "unknown"}, {"protagonist_id": "unknown"}, {"lust": false}, {"shield_max": 10}]:
|
||||
var bad := mixed.duplicate(true)
|
||||
bad[&"original_relationships"][0].merge(patch, true)
|
||||
_rejected(restored, bad)
|
||||
var bad_legacy := legacy.duplicate(true)
|
||||
bad_legacy["relationships"][0]["metrics"]["affinity"] = "117"
|
||||
_rejected(restored, bad_legacy)
|
||||
bad_legacy = legacy.duplicate(true)
|
||||
bad_legacy["relationships"].append(bad_legacy["relationships"][0].duplicate(true))
|
||||
_rejected(restored, bad_legacy)
|
||||
print("Original relationship model: %d checks, %d failures" % [_checks, _failures])
|
||||
quit(_failures)
|
||||
|
||||
|
||||
func _rejected(service: RelationshipService, payload: Dictionary) -> void:
|
||||
var before := var_to_bytes(service.create_snapshot())
|
||||
var input := var_to_bytes(payload)
|
||||
_expect(not service.restore_snapshot(payload), "corrupt snapshot rejected")
|
||||
_expect(var_to_bytes(service.create_snapshot()) == before, "rejection preserves prior state")
|
||||
_expect(var_to_bytes(payload) == input, "rejection preserves input bytes")
|
||||
@@ -0,0 +1 @@
|
||||
uid://cqxwpbkgjtnsb
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
uid://cqxwpbkgjtnsc
|
||||
Reference in New Issue
Block a user