Harden 1C saved-state writes and cache consistency
This commit is contained in:
@@ -75,7 +75,7 @@ def declared_attribute_tree(*, include_new: bool = False) -> dict[str, Any]:
|
||||
seq(atom(1), atom(0), atom(new_guid)),
|
||||
string("КодПоставщика"),
|
||||
seq(atom(1), string("ru"), string("Код поставщика")),
|
||||
string("Комментарий реквизита"),
|
||||
string(""),
|
||||
atom("type-settings"),
|
||||
)
|
||||
)
|
||||
@@ -437,9 +437,42 @@ def test_object_member_add_plan_clones_attribute_and_generates_guid(monkeypatch)
|
||||
assert result["requested_member"]["guid"] == new_guid
|
||||
assert result["requested_member"]["ref"] == "Catalog.Номенклатура.Attribute.КодПоставщика"
|
||||
assert result["container"] == {"ref": "Catalog.Номенклатура", "scope": "object"}
|
||||
assert result["requested_member"]["comment"] == ""
|
||||
assert result["clone_validation"]["settings_preserved"] is True
|
||||
assert result["clone_validation"]["stale_references"] == []
|
||||
assert append["parent_path"] == "4"
|
||||
assert cloned_identities[new_guid]["name"] == "КодПоставщика"
|
||||
assert cloned_identities[new_guid]["synonyms"]["ru"] == "Код поставщика"
|
||||
cloned_comment = adapter.config_tree_identity_property_target(
|
||||
append["node"],
|
||||
new_guid,
|
||||
"comment",
|
||||
)
|
||||
assert cloned_comment["current"] == ""
|
||||
|
||||
|
||||
def test_object_member_clone_blocks_template_identity_references_outside_identity_fields() -> None:
|
||||
template = seq(
|
||||
seq(atom(1), atom(0), atom("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee")),
|
||||
string("Артикул"),
|
||||
seq(atom(1), string("ru"), string("Артикул товара")),
|
||||
string("Комментарий реквизита"),
|
||||
seq(atom("type-settings"), string("Артикул")),
|
||||
)
|
||||
|
||||
cloned, validation = adapter.clone_metadata_member_record(
|
||||
template,
|
||||
template_guid="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
|
||||
new_guid="bbbbbbbb-cccc-dddd-eeee-ffffffffffff",
|
||||
new_name="КодПоставщика",
|
||||
new_synonym="Код поставщика",
|
||||
new_comment="",
|
||||
)
|
||||
|
||||
assert cloned is None
|
||||
assert validation["status"] == "blocked"
|
||||
assert validation["error"] == "template_identity_referenced_outside_identity_fields"
|
||||
assert validation["stale_references"] == [{"path": "4.1", "value": "Артикул"}]
|
||||
|
||||
|
||||
def test_object_member_add_scopes_tabular_column_guid_and_ref(monkeypatch) -> None:
|
||||
|
||||
@@ -26043,7 +26043,7 @@ def test_saved_state_apply_blocks_unsafe_form_payload_rewrite() -> None:
|
||||
def test_saved_state_apply_updates_single_part_with_backup(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
|
||||
original = b"old-payload"
|
||||
replacement = b"new-payload"
|
||||
state = {"data": original, "committed": False, "rolled_back": False}
|
||||
state = {"data": original, "committed": False, "rolled_back": False, "sql": []}
|
||||
|
||||
class FakeCursor:
|
||||
rowcount = 0
|
||||
@@ -26052,6 +26052,7 @@ def test_saved_state_apply_updates_single_part_with_backup(monkeypatch: pytest.M
|
||||
self.store = store
|
||||
|
||||
def execute(self, sql: str, params: tuple[Any, ...]) -> None:
|
||||
self.store["sql"].append(sql)
|
||||
if sql.strip().upper().startswith("SELECT"):
|
||||
self.rowcount = 1
|
||||
return
|
||||
@@ -26088,6 +26089,11 @@ def test_saved_state_apply_updates_single_part_with_backup(monkeypatch: pytest.M
|
||||
"read_storage_file_bytes",
|
||||
lambda *args, **kwargs: (state["data"], {"database": "upo_test"}, None),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
adapter_server,
|
||||
"invalidate_adapter_caches_after_saved_state_change",
|
||||
lambda base_id, *, reason: {"status": "ok", "base_id": base_id, "reason": reason},
|
||||
)
|
||||
|
||||
proposal = {
|
||||
"schema": "onec_change_proposal.v1",
|
||||
@@ -26111,11 +26117,73 @@ def test_saved_state_apply_updates_single_part_with_backup(monkeypatch: pytest.M
|
||||
assert state["data"] == replacement
|
||||
assert state["committed"] is True
|
||||
assert state["rolled_back"] is False
|
||||
assert "WITH (UPDLOCK, HOLDLOCK)" in state["sql"][0]
|
||||
assert result["cache_invalidation"]["reason"] == "saved_state_payload_apply"
|
||||
assert Path(result["backup"]["path"]).exists()
|
||||
backup = json.loads(Path(result["backup"]["path"]).read_text(encoding="utf-8"))
|
||||
assert backup["original"]["payload_hex"] == original.hex()
|
||||
|
||||
|
||||
def test_saved_state_cache_invalidation_clears_persistent_and_runtime_views(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
base_id = "upo_test"
|
||||
other_base_id = "other"
|
||||
data_key = adapter_server.data_schema_cache_key(
|
||||
{"base_id": base_id, "ref": "Catalog.Номенклатура"}
|
||||
)
|
||||
other_data_key = adapter_server.data_schema_cache_key(
|
||||
{"base_id": other_base_id, "ref": "Catalog.Номенклатура"}
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
adapter_server,
|
||||
"metadata_cache_invalidate",
|
||||
lambda payload: {
|
||||
"status": "ok",
|
||||
"counts": {"deleted": 7},
|
||||
"base_id": payload["base_id"],
|
||||
},
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
adapter_server,
|
||||
"BASE_ROOT_METADATA_CACHE",
|
||||
{
|
||||
(base_id, "Config"): {"cached_at": 1},
|
||||
(other_base_id, "Config"): {"cached_at": 1},
|
||||
},
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
adapter_server,
|
||||
"DATA_SCHEMA_CACHE",
|
||||
{
|
||||
data_key: {"cached_at": 1},
|
||||
other_data_key: {"cached_at": 1},
|
||||
},
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
adapter_server,
|
||||
"EXTENSION_MANIFEST_CACHE",
|
||||
{
|
||||
(base_id, "root-a"): {"status": "ok"},
|
||||
(other_base_id, "root-b"): {"status": "ok"},
|
||||
},
|
||||
)
|
||||
|
||||
result = adapter_server.invalidate_adapter_caches_after_saved_state_change(
|
||||
base_id,
|
||||
reason="test",
|
||||
)
|
||||
|
||||
assert result["status"] == "ok"
|
||||
assert result["persistent"]["deleted"] == 7
|
||||
assert result["runtime"] == {
|
||||
"base_root_metadata": 1,
|
||||
"data_schema": 1,
|
||||
"extension_manifests": 1,
|
||||
}
|
||||
assert list(adapter_server.BASE_ROOT_METADATA_CACHE) == [(other_base_id, "Config")]
|
||||
assert list(adapter_server.DATA_SCHEMA_CACHE) == [other_data_key]
|
||||
assert list(adapter_server.EXTENSION_MANIFEST_CACHE) == [(other_base_id, "root-b")]
|
||||
|
||||
|
||||
def test_saved_state_apply_runs_form_element_semantic_verification(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
|
||||
original = b"old-payload"
|
||||
replacement = b"new-payload"
|
||||
|
||||
Reference in New Issue
Block a user