feedBack/tests/test_sloppak_feedpak_version.py
Byron Gamatos b8382139ca
feat(core): adopt feedpak_version — read on load + stamp on manifest writes (spec §4) (#530)
Core never read or emitted the manifest `feedpak_version` field. Adopt it:

- sloppak.py: `FEEDPAK_VERSION = "1.2.0"` constant (the format version this build
  targets); `LoadedSloppak.feedpak_version` read from the manifest on load
  (string, else None for legacy/absent).
- Stamp the version on the two core manifest-rewrite paths, without downgrading
  an existing (possibly higher) declared version:
  - gp2notation: `setdefault` before its notation-add rewrite.
  - songmeta: opportunistically when a metadata field is supplied (gated on the
    existing `dirty` flag, so never a standalone rewrite).

Core has no create-from-scratch path (RS-free repo) — the editor plugin's
create-mode save stamping FEEDPAK_VERSION is a follow-up in that repo. Internal
"sloppak" naming is intentionally left as-is (a rename is out of scope / risky).

Codex-reviewed: no P1/P2. +6 tests (read present/absent/non-string; metadata-write
stamp-when-absent / preserve-existing / no-op-no-stamp) + updated the gp2notation
key-order test for the appended version. 197 sloppak/songmeta/gp2notation tests pass.

Closes #527. Part of got-feedback/feedback#334.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 21:59:04 +02:00

87 lines
3.3 KiB
Python

"""feedpak_version (spec §4): read on load + opportunistic stamp on a metadata
write. Core has no create-from-scratch path (RS-free repo); the editor plugin's
create-mode save stamping the version is a separate follow-up."""
from __future__ import annotations
import json
from pathlib import Path
import yaml
import sloppak as sloppak_mod
from sloppak import FEEDPAK_VERSION
from songmeta import write_sloppak_metadata
def _write_dir_sloppak(root: Path, manifest_extras: dict) -> Path:
pak = root / f"{root.name}.sloppak"
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": [], "beats": [], "sections": [],
}))
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)
(pak / "manifest.yaml").write_text(yaml.safe_dump(manifest, sort_keys=False))
return pak
def _load(pak: Path, tmp_path: Path):
cache = tmp_path / "cache"
cache.mkdir()
return sloppak_mod.load_song(pak.name, pak.parent, cache)
def _manifest(pak: Path) -> dict:
return yaml.safe_load((pak / "manifest.yaml").read_text(encoding="utf-8"))
# ── read ─────────────────────────────────────────────────────────────────────
def test_feedpak_version_read_from_manifest(tmp_path: Path):
pak = _write_dir_sloppak(tmp_path, {"feedpak_version": "1.2.0"})
assert _load(pak, tmp_path).feedpak_version == "1.2.0"
def test_feedpak_version_none_when_absent(tmp_path: Path):
pak = _write_dir_sloppak(tmp_path, {})
assert _load(pak, tmp_path).feedpak_version is None
def test_feedpak_version_none_when_not_a_string(tmp_path: Path):
pak = _write_dir_sloppak(tmp_path, {"feedpak_version": 12})
assert _load(pak, tmp_path).feedpak_version is None
# ── opportunistic stamp on a metadata write ──────────────────────────────────
def test_metadata_write_stamps_version_when_absent(tmp_path: Path):
pak = _write_dir_sloppak(tmp_path, {})
assert "feedpak_version" not in _manifest(pak)
assert write_sloppak_metadata(pak, {"title": "New"}) is True
m = _manifest(pak)
assert m["title"] == "New"
assert m["feedpak_version"] == FEEDPAK_VERSION
def test_metadata_write_preserves_existing_version(tmp_path: Path):
pak = _write_dir_sloppak(tmp_path, {"feedpak_version": "9.9.9"})
write_sloppak_metadata(pak, {"artist": "X"})
assert _manifest(pak)["feedpak_version"] == "9.9.9" # not downgraded
def test_metadata_no_change_does_not_add_version(tmp_path: Path):
# A no-op metadata write must NOT stamp a version (no rewrite happens).
pak = _write_dir_sloppak(tmp_path, {})
assert write_sloppak_metadata(pak, {}) is False
assert "feedpak_version" not in _manifest(pak)