Files

113 lines
4.2 KiB
Python

"""Tests for the sync hashing boundary."""
from __future__ import annotations
from sync_engine_test_support import * # noqa: F401,F403
def test_resolve_model_path_preserves_huggingface_cache_symlink(
sync_engine_module: Any,
tmp_path: Path,
) -> None:
"""Model resolution must retain a cache symlink long enough for provenance discovery."""
cached_file = tmp_path / "cache" / "model.safetensors"
cached_file.parent.mkdir()
cached_file.write_bytes(b"cached")
linked_file = tmp_path / "models" / "model.safetensors"
linked_file.parent.mkdir()
linked_file.symlink_to(cached_file)
resolved = sync_engine_module.resolve_model_path(str(linked_file))
assert resolved == linked_file
assert resolved.resolve() == cached_file
def test_hash_directory_ignores_virtualenv_and_bytecode_artifacts(
settings_module: Any,
sync_engine_module: Any,
monkeypatch: Any,
tmp_path: Path,
) -> None:
"""Ignored directories and file suffixes should not affect custom_nodes hashing."""
monkeypatch.setattr(sync_engine_module._sync_backends, "modal", None)
custom_nodes_dir = tmp_path / "custom_nodes"
package_dir = custom_nodes_dir / "example"
package_dir.mkdir(parents=True)
(package_dir / "__init__.py").write_text("NODE_CLASS_MAPPINGS = {}\n", encoding="utf-8")
settings = settings_module.ModalSyncSettings(
app_name="app",
auto_deploy=True,
allow_ephemeral_fallback=False,
enable_memory_snapshot=True,
enable_gpu_memory_snapshot=False,
execution_mode="remote",
sync_custom_nodes=True,
volume_name="volume",
route_path="/modal/queue_prompt",
marker_property="is_modal_remote",
local_storage_root=tmp_path / "storage",
remote_storage_root="/storage",
custom_nodes_archive_name="custom_nodes_bundle.zip",
comfyui_root=None,
custom_nodes_dir=custom_nodes_dir,
)
engine = sync_engine_module.ModalAssetSyncEngine.from_environment(settings)
baseline_hash = engine._hash_directory(custom_nodes_dir)
ignored_venv_dir = custom_nodes_dir / ".venv" / "lib"
ignored_venv_dir.mkdir(parents=True)
(ignored_venv_dir / "ignored.py").write_text("print('ignored')\n", encoding="utf-8")
ignored_cache_dir = package_dir / "__pycache__"
ignored_cache_dir.mkdir()
(ignored_cache_dir / "example.cpython-312.pyc").write_bytes(b"bytecode")
(package_dir / "ignored.pyc").write_bytes(b"bytecode")
(package_dir / "ignored.log").write_text("log noise\n", encoding="utf-8")
ignored_hash = engine._hash_directory(custom_nodes_dir)
assert ignored_hash == baseline_hash
def test_sync_file_reuses_cached_hash_for_unchanged_file(
settings_module: Any,
sync_engine_module: Any,
monkeypatch: Any,
tmp_path: Path,
) -> None:
"""Unchanged files should reuse the persisted file digest instead of re-reading the payload."""
monkeypatch.setattr(sync_engine_module._sync_backends, "modal", None)
asset_path = tmp_path / "model.safetensors"
asset_path.write_bytes(b"model-bytes")
settings = settings_module.ModalSyncSettings(
app_name="app",
auto_deploy=True,
allow_ephemeral_fallback=False,
enable_memory_snapshot=True,
enable_gpu_memory_snapshot=False,
execution_mode="local",
sync_custom_nodes=False,
volume_name="volume",
route_path="/modal/queue_prompt",
marker_property="is_modal_remote",
local_storage_root=tmp_path / "storage",
remote_storage_root="/storage",
custom_nodes_archive_name="custom_nodes_bundle.zip",
comfyui_root=None,
custom_nodes_dir=None,
)
first_engine = sync_engine_module.ModalAssetSyncEngine.from_environment(settings)
first_sha = first_engine._hash_file(asset_path)
second_engine = sync_engine_module.ModalAssetSyncEngine.from_environment(settings)
def fail_open(*args: Any, **kwargs: Any) -> Any:
raise AssertionError("Expected cached hash lookup to avoid reopening the unchanged file.")
monkeypatch.setattr(Path, "open", fail_open)
second_sha = second_engine._hash_file(asset_path)
assert second_sha == first_sha