Files
DumiFlex 54d48f88f5 fix(toggles): show toggle negatives in the Assembler's negative preview (#382)
A toggle's Negative (or Off negative), and the picked toggle's negative in Pick one, now show in the Assembler's negative preview.
2026-11-12 06:32:07 +01:00

208 lines
6.5 KiB
Python

"""Tests for snapshot.py — payload hash, freeze, legacy coercion."""
from engine.modules.snapshot import (
coerce_legacy_module,
freeze_snapshot,
payload_hash,
)
def test_payload_hash_is_stable_across_key_order():
a = {"x": 1, "y": [1, 2]}
b = {"y": [1, 2], "x": 1}
assert payload_hash(a) == payload_hash(b)
def test_payload_hash_changes_with_value():
a = {"x": 1}
b = {"x": 2}
assert payload_hash(a) != payload_hash(b)
def test_payload_hash_returns_64_char_hex_string():
h = payload_hash({"x": 1})
assert isinstance(h, str)
assert len(h) == 64
int(h, 16) # raises ValueError if not hex
def test_payload_hash_handles_non_ascii():
a = {"name": "café"}
h = payload_hash(a)
assert len(h) == 64
def test_freeze_snapshot_includes_required_fields():
library_row = {
"id": "wc_colors_a3f2",
"type": "wildcard",
"name": "colors",
"category_id": "style",
"version": 7,
"payload": {"options": []},
}
snap = freeze_snapshot(library_row)
assert snap["library_id"] == "wc_colors_a3f2"
assert snap["library_version_at_snapshot"] == 7
assert snap["library_snapshot_at"]
assert snap["library_snapshot_at"].endswith("Z")
assert "." in snap["library_snapshot_at"] # ms-precision
assert snap["type"] == "wildcard"
assert snap["name"] == "colors"
assert snap["category_id"] == "style"
assert snap["payload"] == {"options": []}
assert snap["instance"] == {
"variable_binding": None,
"enabled_options": None,
"category_filter": None,
"option_weights": None,
# SP2a multi-select + null toggle — part of the snapshot baseline.
"exclude_null": None,
"pick_min": None,
"pick_max": None,
"pick_separator": None,
"pick_independent": None,
"mode": None,
"pinned_option_id": None,
"locked_seed": None,
"internal": None,
"disabled_rule_ids": None,
"disabled_exception_keys": None,
"disabled_matrix_cells": None,
}
def test_freeze_snapshot_each_call_yields_fresh_instance_dict():
library_row = {
"id": "x", "type": "wildcard", "name": "n",
"category_id": None, "version": 1, "payload": {},
}
a = freeze_snapshot(library_row)
b = freeze_snapshot(library_row)
a["instance"]["variable_binding"] = "$mutated"
assert b["instance"]["variable_binding"] is None
def test_freeze_snapshot_handles_missing_category_id():
library_row = {
"id": "x", "type": "wildcard", "name": "n",
"version": 1, "payload": {},
}
snap = freeze_snapshot(library_row)
assert snap["category_id"] is None
def test_coerce_legacy_module_fills_missing_fields_for_fixed_values():
legacy = {
"id": "fv_x",
"type": "fixed_values",
"entries": [
{"variable_name": "lens", "value": "85mm"},
{"variable_name": "angle", "value": "wide"},
],
}
coerced = coerce_legacy_module(legacy)
assert coerced["library_id"] is None
assert coerced["library_snapshot_at"] is None
assert coerced["library_version_at_snapshot"] is None
assert coerced["type"] == "fixed_values"
assert coerced["payload"]["values"][0] == {
"id": "val_0000", "name": "lens", "value": "85mm",
}
assert coerced["payload"]["values"][1] == {
"id": "val_0001", "name": "angle", "value": "wide",
}
assert coerced["instance"]["variable_binding"] is None
def test_coerce_legacy_module_passes_through_already_coerced():
already = {
"library_id": "fv_x",
"library_snapshot_at": "2026-04-26T00:00:00Z",
"library_version_at_snapshot": 1,
"type": "fixed_values",
"name": "lens",
"category_id": None,
"payload": {"values": []},
"instance": {
"variable_binding": "$lens",
"enabled_options": None,
"category_filter": None,
},
}
coerced = coerce_legacy_module(already)
assert coerced == already
def test_coerce_legacy_module_uses_meta_name_when_present():
legacy = {
"id": "fv_x",
"type": "fixed_values",
"meta": {"name": "from-meta"},
"entries": [],
}
coerced = coerce_legacy_module(legacy)
assert coerced["name"] == "from-meta"
def test_coerce_legacy_module_prefers_top_level_name_over_meta():
legacy = {
"id": "x",
"type": "fixed_values",
"name": "top-level",
"meta": {"name": "from-meta"},
"entries": [],
}
coerced = coerce_legacy_module(legacy)
assert coerced["name"] == "top-level"
def test_coerce_legacy_module_non_fixed_values_uses_payload():
raw = {
"id": "wc_x",
"type": "wildcard",
"name": "x",
"payload": {"options": [{"id": "o1", "value": "red", "weight": 1}]},
}
coerced = coerce_legacy_module(raw)
assert coerced["type"] == "wildcard"
assert coerced["payload"] == {"options": [{"id": "o1", "value": "red", "weight": 1}]}
def test_coerce_legacy_module_preserves_user_instance_overrides():
"""SPA-picked modules carry an `instance` dict but no `library_id`.
Previously the short-circuit guard required both keys, so user
overrides (option enable/disable, weight overrides) got stomped by
`_fresh_instance()`. This test pins the merge behavior.
"""
raw = {
"id": "ab12cd34",
"type": "wildcard",
"name": "outfit",
"payload": {"options": [{"id": "o1", "value": "red", "weight": 1}]},
"instance": {
"enabled_options": ["o1"],
"option_weights": {"o1": 5.0},
},
}
coerced = coerce_legacy_module(raw)
assert coerced["instance"]["enabled_options"] == ["o1"]
assert coerced["instance"]["option_weights"] == {"o1": 5.0}
# Defaults still present for keys the user didn't override.
assert coerced["instance"]["variable_binding"] is None
assert coerced["instance"]["category_filter"] is None
def test_coerce_legacy_entries_keep_their_negative():
"""The Toggles preview step sends each variable's negative on its entry."""
coerced = coerce_legacy_module({
"id": "0de1f0fe",
"type": "fixed_values",
"entries": [
{"variable_name": "rain", "value": "in the rain", "negative": "sunny sky"},
{"variable_name": "fog", "value": "fog", "negative": " "},
],
})
assert coerced["payload"]["values"][0]["negative"] == "sunny sky"
assert "negative" not in coerced["payload"]["values"][1]