mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-09-13 08:29:28 +00:00
Phase 1 of deprecating the format name sloppak -> feedpak. Purely additive and fully back-compat: nothing sloppak stops working, and no on-disk/runtime artifacts (cache dir, config, SLOPSMITH_* env) are touched. - lib/sloppak.py: detection accepts both `.feedpak` and `.sloppak` (new PACK_SUFFIXES / is_pack / is_feedpak; is_sloppak kept as a deprecated alias that now matches both). Read the optional top-level `feedpak_version` manifest key onto LoadedSloppak and into extract_meta(). Add LoadedFeedpak class alias and a deprecation note in the module docstring. - lib/feedpak.py (new): canonical module name; re-exports the sloppak public API unchanged so new code can `import feedpak`. - server.py: accept `.feedpak` uploads (_ALLOWED_SONG_EXTS + message); add a `get_feedpak_cache_dir` plugin-context key alongside the legacy `get_sloppak_cache_dir` (same dir); tolerate a `feedpak` value in the library format filter. - static/: accept `.feedpak` in the upload picker (index.html accept=, app.js client-side filter). - tests/test_feedpak_alias.py: both extensions detected + loaded via both module names, feedpak_version read, legacy aliases intact. The emitted `format` library tag stays "sloppak" for both extensions this phase (flipping it to "feedpak" with badge/gating updates is a deliberate frontend-touching follow-up). Format spec: got-feedback/feedback-feedpak-spec. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Kris Anderson <topkoa@gmail.com>
108 lines
4.3 KiB
Python
108 lines
4.3 KiB
Python
"""Coverage for the additive sloppak → feedpak rename (back-compat foundation).
|
|
|
|
The format was renamed `sloppak` → `feedpak`. This phase is purely additive: both
|
|
the `.feedpak` and legacy `.sloppak` extensions must load, the new `feedpak`
|
|
module must re-export the loader, the `feedpak_version` manifest key must be read,
|
|
and every legacy `sloppak`-named symbol must keep working. Nothing sloppak breaks.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
import yaml
|
|
|
|
import sloppak as sloppak_mod
|
|
import feedpak as feedpak_mod
|
|
|
|
|
|
def _build(root: Path, suffix: str, manifest_extras: dict | None = None) -> Path:
|
|
"""Build a minimal directory-form pack with the given extension."""
|
|
pak = root / f"{root.name}{suffix}"
|
|
pak.mkdir()
|
|
arr_dir = pak / "arrangements"
|
|
arr_dir.mkdir()
|
|
(arr_dir / "lead.json").write_text(json.dumps({
|
|
"name": "Lead", "tuning": [0, 0, 0, 0, 0, 0], "capo": 0,
|
|
"notes": [], "chords": [], "anchors": [], "handshapes": [], "templates": [],
|
|
}))
|
|
manifest = {
|
|
"title": "Test", "artist": "Tester", "album": "", "year": 2026,
|
|
"duration": 10.0,
|
|
"arrangements": [{"id": "lead", "name": "Lead", "file": "arrangements/lead.json"}],
|
|
"stems": [{"id": "full", "file": "stems/full.ogg", "default": True}],
|
|
}
|
|
manifest.update(manifest_extras or {})
|
|
(pak / "manifest.yaml").write_text(yaml.safe_dump(manifest, sort_keys=False))
|
|
return pak
|
|
|
|
|
|
def _load(mod, pak: Path, tmp_path: Path):
|
|
cache = tmp_path / "cache"
|
|
cache.mkdir()
|
|
return mod.load_song(pak.name, pak.parent, cache)
|
|
|
|
|
|
# ── Both extensions are detected ──────────────────────────────────────────────
|
|
|
|
@pytest.mark.parametrize("suffix", [".feedpak", ".sloppak"])
|
|
def test_detection_accepts_both_extensions(tmp_path: Path, suffix: str):
|
|
pak = tmp_path / f"song{suffix}"
|
|
assert sloppak_mod.is_pack(pak)
|
|
assert sloppak_mod.is_feedpak(pak)
|
|
assert sloppak_mod.is_sloppak(pak) # deprecated alias still accepts .feedpak
|
|
|
|
|
|
def test_detection_rejects_other_extensions(tmp_path: Path):
|
|
assert not sloppak_mod.is_pack(tmp_path / "song.zip")
|
|
assert not sloppak_mod.is_pack(tmp_path / "song.mp3")
|
|
|
|
|
|
# ── Both extensions load, via both module names ───────────────────────────────
|
|
|
|
@pytest.mark.parametrize("suffix", [".feedpak", ".sloppak"])
|
|
def test_load_via_sloppak_module(tmp_path: Path, suffix: str):
|
|
pak = _build(tmp_path, suffix)
|
|
loaded = _load(sloppak_mod, pak, tmp_path)
|
|
assert loaded.song.title == "Test"
|
|
assert loaded.stems[0]["id"] == "full"
|
|
|
|
|
|
@pytest.mark.parametrize("suffix", [".feedpak", ".sloppak"])
|
|
def test_load_via_feedpak_module(tmp_path: Path, suffix: str):
|
|
"""The canonical `feedpak` module re-exports the loader and loads both forms."""
|
|
pak = _build(tmp_path, suffix)
|
|
loaded = _load(feedpak_mod, pak, tmp_path)
|
|
assert loaded.song.title == "Test"
|
|
|
|
|
|
def test_feedpak_module_reexports_match_sloppak():
|
|
for name in ("load_song", "load_manifest", "extract_meta", "resolve_source_dir",
|
|
"is_pack", "is_feedpak", "is_sloppak", "read_feedpak_version"):
|
|
assert getattr(feedpak_mod, name) is getattr(sloppak_mod, name)
|
|
assert feedpak_mod.LoadedFeedpak is sloppak_mod.LoadedSloppak
|
|
|
|
|
|
# ── feedpak_version is read ───────────────────────────────────────────────────
|
|
|
|
def test_feedpak_version_read_when_present(tmp_path: Path):
|
|
pak = _build(tmp_path, ".feedpak", {"feedpak_version": "1.0.0"})
|
|
loaded = _load(feedpak_mod, pak, tmp_path)
|
|
assert loaded.feedpak_version == "1.0.0"
|
|
assert feedpak_mod.extract_meta(pak)["feedpak_version"] == "1.0.0"
|
|
|
|
|
|
def test_feedpak_version_none_when_absent(tmp_path: Path):
|
|
pak = _build(tmp_path, ".feedpak")
|
|
loaded = _load(feedpak_mod, pak, tmp_path)
|
|
assert loaded.feedpak_version is None
|
|
assert feedpak_mod.extract_meta(pak)["feedpak_version"] is None
|
|
|
|
|
|
def test_feedpak_version_ignores_non_string(tmp_path: Path):
|
|
pak = _build(tmp_path, ".feedpak", {"feedpak_version": 1})
|
|
loaded = _load(feedpak_mod, pak, tmp_path)
|
|
assert loaded.feedpak_version is None
|