Initial project import
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
"""Pure regression fixtures for the proven ConfigInfo file-SHA1 map rewrite."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import hashlib
|
||||
import sys
|
||||
import zlib
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
CONNECTOR = ROOT / "plugins" / "1c" / "connector"
|
||||
PARSER = ROOT / "plugins" / "1c"
|
||||
for path in (CONNECTOR, PARSER):
|
||||
if str(path) not in sys.path:
|
||||
sys.path.insert(0, str(path))
|
||||
|
||||
import adapter_1c_server as adapter # noqa: E402
|
||||
from parser.payload import decode_payload_lossless # noqa: E402
|
||||
|
||||
|
||||
LOGICAL = "77494708-43ea-4956-ac3c-199cfb035ad2.0"
|
||||
|
||||
|
||||
def raw_deflate(text: str) -> bytes:
|
||||
encoder = zlib.compressobj(wbits=-zlib.MAX_WBITS)
|
||||
return encoder.compress(text.encode("utf-8")) + encoder.flush()
|
||||
|
||||
|
||||
def test_rewrites_only_named_file_sha1_reference() -> None:
|
||||
original_file = b"old module bytes"
|
||||
replacement_file = b"new module bytes"
|
||||
old_ref = base64.b64encode(hashlib.sha1(original_file).digest()).decode("ascii")
|
||||
service_atom = base64.b64encode(b"S" * 32).decode("ascii")
|
||||
source_text = f'{{2,extension,{service_atom}}},{{7,"{LOGICAL}",{old_ref},"other.0",{old_ref}}}'
|
||||
result = adapter.rewrite_extension_configinfo_file_sha1_reference(
|
||||
raw_deflate(source_text), logical_file_name=LOGICAL, replacement_file_data=replacement_file
|
||||
)
|
||||
assert result["status"] == "ok"
|
||||
assert result["replacement_file_sha1"] == hashlib.sha1(replacement_file).hexdigest()
|
||||
decoded = str(decode_payload_lossless(result["payload"]).get("text") or "")
|
||||
assert service_atom in decoded
|
||||
assert result["replacement_reference_base64"] in decoded
|
||||
assert decoded.count(old_ref) == 1 # unrelated map entry was preserved
|
||||
|
||||
|
||||
def test_rejects_ambiguous_logical_reference() -> None:
|
||||
ref = base64.b64encode(hashlib.sha1(b"old").digest()).decode("ascii")
|
||||
source_text = f'{{7,"{LOGICAL}",{ref},"{LOGICAL}",{ref}}}'
|
||||
result = adapter.rewrite_extension_configinfo_file_sha1_reference(
|
||||
raw_deflate(source_text), logical_file_name=LOGICAL, replacement_file_data=b"new"
|
||||
)
|
||||
assert result["status"] == "reference_not_unique"
|
||||
assert result["occurrences"] == 2
|
||||
|
||||
|
||||
def test_builds_paired_plan_only_when_map_matches_current_stream() -> None:
|
||||
current = b"current module"
|
||||
replacement = b"replacement module"
|
||||
current_ref = base64.b64encode(hashlib.sha1(current).digest()).decode("ascii")
|
||||
configinfo = raw_deflate(f'{{7,"{LOGICAL}",{current_ref}}}')
|
||||
result = adapter.build_extension_saved_state_pair_plan(
|
||||
logical_file_name=LOGICAL,
|
||||
current_file_data=current,
|
||||
replacement_file_data=replacement,
|
||||
current_configinfo_data=configinfo,
|
||||
)
|
||||
assert result["status"] == "ok"
|
||||
assert result["module"]["replacement_sha1"] == hashlib.sha1(replacement).hexdigest()
|
||||
assert result["configinfo"]["service_atoms"] == "preserved_unmodified"
|
||||
|
||||
|
||||
def test_rejects_pair_when_map_points_to_another_stream() -> None:
|
||||
mapped = b"other module"
|
||||
current = b"current module"
|
||||
mapped_ref = base64.b64encode(hashlib.sha1(mapped).digest()).decode("ascii")
|
||||
configinfo = raw_deflate(f'{{7,"{LOGICAL}",{mapped_ref}}}')
|
||||
result = adapter.build_extension_saved_state_pair_plan(
|
||||
logical_file_name=LOGICAL,
|
||||
current_file_data=current,
|
||||
replacement_file_data=b"replacement",
|
||||
current_configinfo_data=configinfo,
|
||||
)
|
||||
assert result["status"] == "configinfo_reference_mismatch"
|
||||
|
||||
|
||||
class FakeCursor:
|
||||
def __init__(self, state: dict[str, bytes]) -> None:
|
||||
self.state = state
|
||||
self.rows: list[dict[str, object]] = []
|
||||
|
||||
def execute(self, sql: str, params: tuple[object, ...] | None = None) -> None:
|
||||
if sql.startswith("SET LOCK_TIMEOUT"):
|
||||
return
|
||||
if sql.startswith("SELECT FileName, PartNo, BinaryData"):
|
||||
assert params is not None
|
||||
self.rows = [
|
||||
{"FileName": name, "PartNo": 0, "BinaryData": self.state[name]}
|
||||
for name in params
|
||||
if name in self.state
|
||||
]
|
||||
return
|
||||
if sql.startswith("UPDATE dbo.ConfigCASSave"):
|
||||
assert params is not None
|
||||
data, _size, name, _part = params
|
||||
self.state[str(name)] = bytes(data)
|
||||
return
|
||||
raise AssertionError(sql)
|
||||
|
||||
def fetchall(self) -> list[dict[str, object]]:
|
||||
return self.rows
|
||||
|
||||
|
||||
class FakeConnection:
|
||||
def __init__(self, state: dict[str, bytes]) -> None:
|
||||
self.cursor_instance = FakeCursor(state)
|
||||
self.committed = False
|
||||
self.rolled_back = False
|
||||
self.closed = False
|
||||
|
||||
def cursor(self, **_kwargs: object) -> FakeCursor:
|
||||
return self.cursor_instance
|
||||
|
||||
def commit(self) -> None:
|
||||
self.committed = True
|
||||
|
||||
def rollback(self) -> None:
|
||||
self.rolled_back = True
|
||||
|
||||
def close(self) -> None:
|
||||
self.closed = True
|
||||
|
||||
|
||||
def install_fake_pair_sql(monkeypatch: object, state: dict[str, bytes], connection: FakeConnection) -> None:
|
||||
monkeypatch.setattr(adapter, "connect_live_sql", lambda *_args, **_kwargs: (connection, {"database": "test"}, None))
|
||||
monkeypatch.setattr(
|
||||
adapter,
|
||||
"read_storage_file_bytes",
|
||||
lambda _base, _table, name, **_kwargs: (state.get(name), {"database": "test"}, None if name in state else {"status": "missing"}),
|
||||
)
|
||||
monkeypatch.setattr(adapter, "write_storage_apply_backup", lambda **_kwargs: {"backup_id": "test"})
|
||||
monkeypatch.setattr(adapter, "invalidate_adapter_caches_after_saved_state_change", lambda *_args, **_kwargs: {"invalidated": True})
|
||||
|
||||
|
||||
def test_atomically_updates_module_and_configinfo(monkeypatch: object) -> None:
|
||||
extension = "fb26cf42-7609-11f1-828f-005056b0d483"
|
||||
module_name = f"{extension}__{LOGICAL}"
|
||||
configinfo_name = f"{extension}__configinfo"
|
||||
current = b"current module"
|
||||
current_ref = base64.b64encode(hashlib.sha1(current).digest()).decode("ascii")
|
||||
state = {module_name: current, configinfo_name: raw_deflate(f'{{7,"{LOGICAL}",{current_ref}}}')}
|
||||
connection = FakeConnection(state)
|
||||
install_fake_pair_sql(monkeypatch, state, connection)
|
||||
|
||||
result = adapter.apply_extension_saved_state_pair(
|
||||
"upo_test",
|
||||
module_file_name=module_name,
|
||||
replacement_module_data=b"new module",
|
||||
expected_module_sha1=hashlib.sha1(current).hexdigest(),
|
||||
proposal={"method": "test"},
|
||||
)
|
||||
|
||||
assert result["status"] == "applied"
|
||||
assert result["applied"] is True
|
||||
assert connection.committed is True
|
||||
assert result["configurator_refresh"]["action"] == "close_reopen_object"
|
||||
map_text = str(decode_payload_lossless(state[configinfo_name]).get("text") or "")
|
||||
assert base64.b64encode(hashlib.sha1(b"new module").digest()).decode("ascii") in map_text
|
||||
|
||||
|
||||
def test_atomic_writer_rolls_back_when_configinfo_map_diverges(monkeypatch: object) -> None:
|
||||
extension = "fb26cf42-7609-11f1-828f-005056b0d483"
|
||||
module_name = f"{extension}__{LOGICAL}"
|
||||
configinfo_name = f"{extension}__configinfo"
|
||||
current = b"current module"
|
||||
wrong_ref = base64.b64encode(hashlib.sha1(b"other module").digest()).decode("ascii")
|
||||
state = {module_name: current, configinfo_name: raw_deflate(f'{{7,"{LOGICAL}",{wrong_ref}}}')}
|
||||
connection = FakeConnection(state)
|
||||
install_fake_pair_sql(monkeypatch, state, connection)
|
||||
|
||||
result = adapter.apply_extension_saved_state_pair(
|
||||
"upo_test",
|
||||
module_file_name=module_name,
|
||||
replacement_module_data=b"new module",
|
||||
expected_module_sha1=hashlib.sha1(current).hexdigest(),
|
||||
proposal={"method": "test"},
|
||||
)
|
||||
|
||||
assert result["status"] == "configinfo_reference_mismatch"
|
||||
assert result["applied"] is False
|
||||
assert connection.rolled_back is True
|
||||
assert state[module_name] == current
|
||||
Reference in New Issue
Block a user