| from __future__ import annotations |
|
|
| import koolook_routes |
| from koolook_setups import DEFAULT_SETUPS_FILENAME, DEFAULT_SETUPS_SUBDIR |
|
|
|
|
| def test_published_setups_path_sits_beside_snapshot_library(monkeypatch, tmp_path) -> None: |
| """Published setups live as a sibling of the configured snapshot library |
| (so they follow libraryPath / KFORGELABS_PRESETS) instead of a fixed |
| user-dir folder (issue #227).""" |
| library = tmp_path / "kforge" / "koolook-presets" |
| monkeypatch.setattr(koolook_routes, "_configured_dir", lambda: (library, "settings")) |
|
|
| assert koolook_routes._published_setups_path() == ( |
| tmp_path / "kforge" / DEFAULT_SETUPS_SUBDIR / DEFAULT_SETUPS_FILENAME |
| ) |
|
|
|
|
| def test_migrate_legacy_published_setups_copies_when_target_absent(tmp_path) -> None: |
| """A pre-relocation registry at the old user-dir path is copied to the new |
| location when the new one has none yet — non-destructively (old file kept).""" |
| legacy = tmp_path / "user" / "koolook-published-setups" / "setups.json" |
| legacy.parent.mkdir(parents=True) |
| legacy.write_text('{"setups": [{"id": "carried-over"}]}', encoding="utf-8") |
| target = tmp_path / "kforge" / "koolook-published-setups" / "setups.json" |
|
|
| koolook_routes._migrate_legacy_published_setups(legacy, target) |
|
|
| assert target.is_file() |
| assert target.read_text(encoding="utf-8") == legacy.read_text(encoding="utf-8") |
| assert legacy.is_file() |
|
|
|
|
| def test_migrate_legacy_published_setups_noop_when_target_present(tmp_path) -> None: |
| """Migration never overwrites an existing registry at the new location.""" |
| legacy = tmp_path / "user" / "koolook-published-setups" / "setups.json" |
| legacy.parent.mkdir(parents=True) |
| legacy.write_text('{"setups": [{"id": "old"}]}', encoding="utf-8") |
| target = tmp_path / "kforge" / "koolook-published-setups" / "setups.json" |
| target.parent.mkdir(parents=True) |
| target.write_text('{"setups": [{"id": "current"}]}', encoding="utf-8") |
|
|
| koolook_routes._migrate_legacy_published_setups(legacy, target) |
|
|
| assert '"current"' in target.read_text(encoding="utf-8") |
|
|
|
|
| def test_migrate_legacy_published_setups_noop_when_no_legacy(tmp_path) -> None: |
| """Fresh install (no old file) migrates nothing and does not create the |
| target — the registry's own fallback handles an empty location.""" |
| legacy = tmp_path / "user" / "koolook-published-setups" / "setups.json" |
| target = tmp_path / "kforge" / "koolook-published-setups" / "setups.json" |
|
|
| koolook_routes._migrate_legacy_published_setups(legacy, target) |
|
|
| assert not target.exists() |
|
|
|
|
| def _point_registry(monkeypatch, tmp_path): |
| """Wire the default registry factory at tmp paths and return (legacy, target).""" |
| library = tmp_path / "lib" / "koolook-presets" |
| monkeypatch.setattr(koolook_routes, "_configured_dir", lambda: (library, "settings")) |
| legacy = tmp_path / "user" / "koolook-published-setups" / "setups.json" |
| legacy.parent.mkdir(parents=True) |
| legacy.write_text('{"setups": []}', encoding="utf-8") |
| monkeypatch.setattr(koolook_routes, "default_storage_path", lambda: legacy) |
| target = tmp_path / "lib" / "koolook-published-setups" / "setups.json" |
| return legacy, target |
|
|
|
|
| def test_default_registry_keeps_legacy_data_when_migration_fails(monkeypatch, tmp_path) -> None: |
| """If the legacy->new copy fails, the registry keeps reading the real legacy |
| file rather than masking it with the bundled sample (PR #239 review, HIGH).""" |
| legacy, target = _point_registry(monkeypatch, tmp_path) |
|
|
| def boom(*_a, **_k): |
| raise OSError("disk full") |
|
|
| monkeypatch.setattr(koolook_routes.shutil, "copy2", boom) |
|
|
| registry = koolook_routes._default_published_setup_registry() |
|
|
| |
| |
| assert not target.exists() |
| assert registry.storage_path == legacy |
|
|
|
|
| def test_default_registry_uses_new_path_after_successful_migration(monkeypatch, tmp_path) -> None: |
| """On a clean migration the registry uses the relocated path.""" |
| legacy, target = _point_registry(monkeypatch, tmp_path) |
|
|
| registry = koolook_routes._default_published_setup_registry() |
|
|
| assert target.is_file() |
| assert registry.storage_path == target |
|
|
|
|
| def test_default_registry_prefers_intact_legacy_over_corrupt_target(monkeypatch, tmp_path) -> None: |
| """A partial/corrupt relocated file (e.g. an interrupted copy) must not |
| mask intact legacy data: existence alone is not proof migration succeeded |
| (PR #239 re-review, HIGH). The corrupt file is left in place untouched.""" |
| legacy, target = _point_registry(monkeypatch, tmp_path) |
| legacy.write_text('{"setups": [{"id": "real-data"}]}', encoding="utf-8") |
| target.parent.mkdir(parents=True) |
| target.write_text('{"setups": [{"id": "trunc', encoding="utf-8") |
|
|
| registry = koolook_routes._default_published_setup_registry() |
|
|
| assert registry.storage_path == legacy |
| |
| assert target.read_text(encoding="utf-8") == '{"setups": [{"id": "trunc' |
|
|
|
|
| def test_default_registry_keeps_valid_target_authoritative(monkeypatch, tmp_path) -> None: |
| """A readable relocated registry stays primary even while the legacy file |
| still exists — preferring legacy is strictly a corruption fallback.""" |
| legacy, target = _point_registry(monkeypatch, tmp_path) |
| target.parent.mkdir(parents=True) |
| target.write_text('{"setups": [{"id": "migrated"}]}', encoding="utf-8") |
|
|
| registry = koolook_routes._default_published_setup_registry() |
|
|
| assert registry.storage_path == target |
|
|
|
|
| def test_default_registry_keeps_target_when_both_files_corrupt(monkeypatch, tmp_path) -> None: |
| """If legacy is corrupt too there is nothing intact to protect; stay on the |
| relocated path so storage diagnostics point at the current location.""" |
| legacy, target = _point_registry(monkeypatch, tmp_path) |
| legacy.write_text("not json either", encoding="utf-8") |
| target.parent.mkdir(parents=True) |
| target.write_text("not json", encoding="utf-8") |
|
|
| registry = koolook_routes._default_published_setup_registry() |
|
|
| assert registry.storage_path == target |
|
|
|
|
| def test_migration_interrupted_copy_leaves_no_partial_target(monkeypatch, tmp_path) -> None: |
| """An interrupted copy must never leave a partial setups.json at the new |
| location (it would shadow the legacy registry on every later boot). The |
| migration writes via temp file + atomic replace and cleans up after a |
| failure.""" |
| legacy, target = _point_registry(monkeypatch, tmp_path) |
|
|
| real_open = open |
|
|
| def partial_copy(src, dst, *args, **kwargs): |
| with real_open(dst, "w", encoding="utf-8") as f: |
| f.write('{"setups": [{"id"') |
| raise OSError("interrupted") |
|
|
| monkeypatch.setattr(koolook_routes.shutil, "copy2", partial_copy) |
|
|
| registry = koolook_routes._default_published_setup_registry() |
|
|
| assert not target.exists() |
| assert list(target.parent.glob("*.tmp")) == [] |
| assert registry.storage_path == legacy |
|
|