Files
beaver_project/app-instance/backend/tests/unit/test_plugin_hashing.py

84 lines
2.9 KiB
Python

from __future__ import annotations
import os
from pathlib import Path
import pytest
from beaver.plugins.hashing import hash_plugin_skill_tree
def test_skill_tree_hash_changes_when_supporting_file_changes(tmp_path: Path) -> None:
root = tmp_path / "skill"
root.mkdir()
(root / "SKILL.md").write_text("# Skill\n", encoding="utf-8")
(root / "templates").mkdir()
template = root / "templates" / "report.md"
template.write_text("v1", encoding="utf-8")
first = hash_plugin_skill_tree(root)
template.write_text("v2", encoding="utf-8")
second = hash_plugin_skill_tree(root)
assert first.skill_content_hash == second.skill_content_hash
assert first.skill_tree_hash != second.skill_tree_hash
def test_skill_tree_hash_changes_when_path_changes(tmp_path: Path) -> None:
root = tmp_path / "skill"
root.mkdir()
(root / "SKILL.md").write_text("# Skill\n", encoding="utf-8")
(root / "a.txt").write_text("same", encoding="utf-8")
first = hash_plugin_skill_tree(root)
(root / "b.txt").write_text((root / "a.txt").read_text(encoding="utf-8"), encoding="utf-8")
(root / "a.txt").unlink()
second = hash_plugin_skill_tree(root)
assert first.skill_tree_hash != second.skill_tree_hash
def test_skill_tree_hash_tracks_executable_bit_but_not_other_mode_bits(tmp_path: Path) -> None:
root = tmp_path / "skill"
root.mkdir()
script = root / "script.sh"
(root / "SKILL.md").write_text("# Skill\n", encoding="utf-8")
script.write_text("#!/bin/sh\n", encoding="utf-8")
script.chmod(0o644)
first = hash_plugin_skill_tree(root)
script.chmod(0o600)
non_exec_changed = hash_plugin_skill_tree(root)
script.chmod(0o700)
exec_changed = hash_plugin_skill_tree(root)
assert first.skill_tree_hash == non_exec_changed.skill_tree_hash
assert first.skill_tree_hash != exec_changed.skill_tree_hash
def test_skill_tree_hash_ignores_mtime_and_beaver_metadata(tmp_path: Path) -> None:
root = tmp_path / "skill"
root.mkdir()
skill = root / "SKILL.md"
skill.write_text("# Skill\n", encoding="utf-8")
(root / "version.json").write_text('{"ignored": true}', encoding="utf-8")
(root / "upstream.json").write_text('{"ignored": true}', encoding="utf-8")
first = hash_plugin_skill_tree(root)
os.utime(skill, (skill.stat().st_atime + 20, skill.stat().st_mtime + 20))
(root / "version.json").write_text('{"ignored": false}', encoding="utf-8")
(root / "upstream.json").write_text('{"ignored": false}', encoding="utf-8")
second = hash_plugin_skill_tree(root)
assert first.skill_tree_hash == second.skill_tree_hash
def test_skill_tree_hash_rejects_symlinks(tmp_path: Path) -> None:
root = tmp_path / "skill"
root.mkdir()
(root / "SKILL.md").write_text("# Skill\n", encoding="utf-8")
(root / "linked").symlink_to(root / "SKILL.md")
with pytest.raises(ValueError, match="symlink"):
hash_plugin_skill_tree(root)