Files
last30days-skill/tests/test_skill_meta.py
Trevin Chow 73dc6b9996 refactor: consolidate SKILL.md version regex into lib/skill_meta.py
The same `^version:\s*"([^"]+)"\s*$` regex (or a slight variant) was
duplicated across three files: render.py inline, test_plugin_contract.py
local helper, test_version_consistency.py local helper. A future change
to the SKILL.md frontmatter version format would have needed to update
three places without any compile-time pressure to keep them in sync.

New skills/last30days/scripts/lib/skill_meta.py provides:
- `_VERSION_RE` private compiled pattern (accepts double-quoted,
  single-quoted, or unquoted YAML version scalars per the widening
  landed in 997708a)
- `read_skill_version(skill_md_path: Path) -> str | None` helper that
  catches OSError + UnicodeDecodeError and returns None on miss

Callers updated:
- render.py::_skill_version now calls skill_meta.read_skill_version
  inside the SKILL.md fallback loop, returning `read_skill_version(...) or "?"`.
  Semantically equivalent to the old break-after-first-SKILL.md logic.
- test_plugin_contract.py and test_version_consistency.py import the
  helper instead of defining the regex inline. Both files use the
  established sys.path.insert pattern.

Added tests/test_skill_meta.py with 6 direct unit tests covering the
helper's full contract: missing file, undecodable bytes, no-version-line,
and all three quoting styles (double, single, unquoted). Previously the
helper was only exercised transitively through render._skill_version().

Added test_skill_md_uses_double_quoted_version to
test_version_consistency.py — the old per-test regex incidentally
asserted "this repo's SKILL.md uses double-quotes" by being strict;
the shared helper accepts all three styles, so the assertion is now
explicit instead of implicit.

Code-reviewed by ce-code-review (8 reviewers); safe_auto fixes applied
inline (rename to _VERSION_RE, group or-chain instead of generator,
docstring tightened, dropped unnecessary `from __future__ import
annotations`, tightened signature to Path-only).

Conftest.py refactor for the sys.path.insert duplication across ~20 test
files filed as issue #411 — out of scope for this PR (touches many
files, separate concern).

Test results: 23 passed in the affected test set (16 prior + 6 new
test_skill_meta tests + 1 new double-quote assertion). Full suite shows
same 13 pre-existing failures as main; zero new failures.
2026-05-16 18:17:33 -07:00

62 lines
2.2 KiB
Python

"""Direct unit tests for skill_meta.read_skill_version.
Covers the helper's own contract independent of render._skill_version which
exercises it transitively. Without these, regressions in error handling or
regex coverage inside the helper could pass CI because render.py's fallback
to "?" swallows the signal.
"""
import sys
import tempfile
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "skills" / "last30days" / "scripts"))
from lib.skill_meta import read_skill_version # noqa: E402
class ReadSkillVersionTests(unittest.TestCase):
def setUp(self) -> None:
self._tmp = tempfile.TemporaryDirectory()
self.tmp_path = Path(self._tmp.name)
def tearDown(self) -> None:
self._tmp.cleanup()
def _write_skill_md(self, body: str) -> Path:
path = self.tmp_path / "SKILL.md"
path.write_text(body)
return path
def test_double_quoted_version(self) -> None:
path = self._write_skill_md('---\nname: x\nversion: "9.9.9"\n---\n')
self.assertEqual("9.9.9", read_skill_version(path))
def test_single_quoted_version(self) -> None:
path = self._write_skill_md("---\nname: x\nversion: '8.8.8'\n---\n")
self.assertEqual("8.8.8", read_skill_version(path))
def test_unquoted_version(self) -> None:
path = self._write_skill_md("---\nname: x\nversion: 7.7.7\n---\n")
self.assertEqual("7.7.7", read_skill_version(path))
def test_missing_file_returns_none(self) -> None:
self.assertIsNone(read_skill_version(self.tmp_path / "does-not-exist.md"))
def test_no_version_line_returns_none(self) -> None:
path = self._write_skill_md("---\nname: x\n---\n# body without version\n")
self.assertIsNone(read_skill_version(path))
def test_undecodable_bytes_returns_none(self) -> None:
# Bytes 128-255 don't form valid UTF-8 sequences; read_text() raises
# UnicodeDecodeError which the helper must catch.
path = self.tmp_path / "SKILL.md"
path.write_bytes(bytes(range(128, 256)))
self.assertIsNone(read_skill_version(path))
if __name__ == "__main__":
unittest.main()