93 lines
3.1 KiB
Python
93 lines
3.1 KiB
Python
|
|
"""Validate the autotune reminder hook and its settings registration."""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import json
|
||
|
|
import shutil
|
||
|
|
import subprocess
|
||
|
|
import tempfile
|
||
|
|
import unittest
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||
|
|
SKILL = REPO_ROOT / "skills" / "autotune" / "SKILL.md"
|
||
|
|
HOOK = REPO_ROOT / "skills" / "autotune" / "hooks" / "reminder.sh"
|
||
|
|
PATCHER = REPO_ROOT / "scripts" / "enable-plugins.js"
|
||
|
|
|
||
|
|
|
||
|
|
def _run_hook(stamp: Path, interval: str = "3600") -> subprocess.CompletedProcess:
|
||
|
|
return subprocess.run(
|
||
|
|
["bash", str(HOOK)],
|
||
|
|
capture_output=True,
|
||
|
|
text=True,
|
||
|
|
check=True,
|
||
|
|
env={
|
||
|
|
"PATH": "/usr/bin:/bin",
|
||
|
|
"HOME": str(stamp.parent),
|
||
|
|
"AUTOTUNE_STAMP": str(stamp),
|
||
|
|
"AUTOTUNE_INTERVAL_SECONDS": interval,
|
||
|
|
},
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
class TestAutotuneSkill(unittest.TestCase):
|
||
|
|
def test_skill_is_trigger_only(self):
|
||
|
|
text = SKILL.read_text(encoding="utf-8")
|
||
|
|
self.assertIn("name: autotune", text)
|
||
|
|
self.assertIn("multiSelect: false", text)
|
||
|
|
|
||
|
|
|
||
|
|
class TestAutotuneHook(unittest.TestCase):
|
||
|
|
def setUp(self):
|
||
|
|
self.tmp = tempfile.TemporaryDirectory()
|
||
|
|
self.addCleanup(self.tmp.cleanup)
|
||
|
|
self.stamp = Path(self.tmp.name) / ".claude" / ".autotune-stamp"
|
||
|
|
|
||
|
|
def test_first_run_hints_and_stamps(self):
|
||
|
|
result = _run_hook(self.stamp)
|
||
|
|
self.assertIn("/autotune", result.stdout)
|
||
|
|
self.assertTrue(self.stamp.is_file())
|
||
|
|
|
||
|
|
def test_second_run_is_silent_within_interval(self):
|
||
|
|
_run_hook(self.stamp)
|
||
|
|
self.assertEqual(_run_hook(self.stamp).stdout, "")
|
||
|
|
|
||
|
|
def test_hint_returns_after_the_interval(self):
|
||
|
|
_run_hook(self.stamp)
|
||
|
|
self.assertIn("/autotune", _run_hook(self.stamp, interval="0").stdout)
|
||
|
|
|
||
|
|
def test_corrupt_stamp_does_not_crash(self):
|
||
|
|
self.stamp.parent.mkdir(parents=True, exist_ok=True)
|
||
|
|
self.stamp.write_text("not-a-timestamp\n", encoding="utf-8")
|
||
|
|
self.assertIn("/autotune", _run_hook(self.stamp).stdout)
|
||
|
|
|
||
|
|
|
||
|
|
@unittest.skipUnless(shutil.which("node"), "node not installed")
|
||
|
|
class TestSettingsRegistration(unittest.TestCase):
|
||
|
|
def setUp(self):
|
||
|
|
self.tmp = tempfile.TemporaryDirectory()
|
||
|
|
self.addCleanup(self.tmp.cleanup)
|
||
|
|
self.settings = Path(self.tmp.name) / "settings.json"
|
||
|
|
|
||
|
|
def _patch(self) -> dict:
|
||
|
|
subprocess.run(["node", str(PATCHER), str(self.settings)], check=True, capture_output=True)
|
||
|
|
return json.loads(self.settings.read_text(encoding="utf-8"))
|
||
|
|
|
||
|
|
def test_hook_registered_once(self):
|
||
|
|
self._patch()
|
||
|
|
data = self._patch()
|
||
|
|
entries = json.dumps(data["hooks"]["UserPromptSubmit"])
|
||
|
|
self.assertEqual(entries.count("autotune/hooks/reminder.sh"), 1)
|
||
|
|
|
||
|
|
def test_existing_hooks_preserved(self):
|
||
|
|
self.settings.write_text(
|
||
|
|
json.dumps({"hooks": {"UserPromptSubmit": [{"hooks": [{"type": "command", "command": "true"}]}]}}),
|
||
|
|
encoding="utf-8",
|
||
|
|
)
|
||
|
|
data = self._patch()
|
||
|
|
self.assertEqual(len(data["hooks"]["UserPromptSubmit"]), 2)
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
unittest.main()
|