Files
DemonGatanjieu-Anomalous_Mo…/tests/test_version_manager.py
T
DemonGatanjieuandClaude Opus 5.5 7b09909e5b test: publish the plugin's tests, one command to run them
tests/ (Python backend tests, .mjs frontend rule tests) is no longer
ignored. tools/run_tests.mjs runs the structure check, every
tests/*.mjs and the Python tests with $PYTHON, the portable
python_embeded or python. docs/guides/testing.md says how to run and
write them; README links it. Developer notes no longer point at 18 test
files that were removed earlier (AGENTS.md table, backend, frontend and
update-guide notes).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 17:47:35 +08:00

148 lines
6.7 KiB
Python

from pathlib import Path
import subprocess
import sys
import tempfile
import unittest
from unittest import mock
PLUGIN = Path(__file__).resolve().parents[1]
sys.path[:0] = [str(PLUGIN), str(PLUGIN.parents[1])]
from api import version_manager as vm
def git(cwd, *args):
return subprocess.run(['git', '-C', str(cwd), *args], check=True, capture_output=True, text=True).stdout.strip()
def commit(repo, name, content, message):
Path(repo, name).parent.mkdir(parents=True, exist_ok=True)
Path(repo, name).write_text(content, encoding='utf-8')
git(repo, 'add', name)
git(repo, '-c', 'user.name=t', '-c', 'user.email=t@t', 'commit', '-q', '-m', message)
class VersionManagerTests(unittest.TestCase):
def setUp(self):
self._tmp = tempfile.TemporaryDirectory()
root = Path(self._tmp.name)
self.upstream = root / 'upstream'
self.upstream.mkdir()
git(self.upstream, 'init', '-q', '-b', 'main')
commit(self.upstream, 'app.txt', 'one', 'one')
git(self.upstream, 'tag', 'v1.0.0')
commit(self.upstream, 'app.txt', 'two', 'two')
git(self.upstream, 'tag', 'v1.55-beta')
commit(self.upstream, 'api/version_manager.py', '# panel', 'add panel')
git(self.upstream, 'tag', 'v1.58.0')
self.plugin = root / 'plugin'
git(root, 'clone', '-q', str(self.upstream), str(self.plugin))
for name, value in (('PLUGIN_DIR', str(self.plugin)), ('STATE_FILE', str(self.plugin / '.anomalous_version.local.json'))):
patcher = mock.patch.object(vm, name, value)
patcher.start()
self.addCleanup(patcher.stop)
github = mock.patch.object(vm, '_fetch_github_releases', side_effect=OSError('offline'))
github.start()
self.addCleanup(github.stop)
vm._releases_cache.update(expires=0, data=None)
def tearDown(self):
self._tmp.cleanup()
def head(self):
return git(self.plugin, 'rev-parse', 'HEAD')
def test_version_keys_order_mixed_tag_styles(self):
tags = ['v1.5.0', 'v1.57-beta', 'v1.4.3', 'v1.58.0', 'v1.58.0-rc1', 'v1.55-beta']
self.assertEqual(sorted(tags, key=vm.version_key, reverse=True),
['v1.58.0', 'v1.58.0-rc1', 'v1.57-beta', 'v1.55-beta', 'v1.5.0', 'v1.4.3'])
def test_state_and_release_list_from_git_fallback(self):
state = vm.current_state()
self.assertEqual((state['branch'], state['tag'], state['dirty']), ('main', 'v1.58.0', False))
listing = vm.list_releases()
self.assertEqual(listing['source'], 'git')
self.assertEqual([r['tag'] for r in listing['releases']], ['v1.58.0', 'v1.55-beta', 'v1.0.0'])
self.assertEqual(listing['latest'], 'v1.58.0')
self.assertEqual([r['direction'] for r in listing['releases']], ['same', 'older', 'older'])
def test_github_prereleases_are_listed_but_never_latest(self):
releases = [
{'tag': 'v1.59.0-rc1', 'name': 'rc', 'published_at': '2026-10-02', 'prerelease': True, 'notes': '', 'url': None},
{'tag': 'v1.58.0', 'name': 'stable', 'published_at': '2026-10-01', 'prerelease': False, 'notes': 'n', 'url': None},
]
with mock.patch.object(vm, '_fetch_github_releases', return_value=releases), \
mock.patch.object(vm, '_github_repo', return_value=('owner', 'repo')):
listing = vm.list_releases(force=True)
self.assertEqual(listing['source'], 'github')
self.assertEqual(listing['latest'], 'v1.58.0')
self.assertEqual(listing['releases'][0]['direction'], 'newer')
def test_rollback_preview_switch_undo_and_return_to_latest(self):
start = self.head()
preview = vm.preview_switch('v1.55-beta')
self.assertEqual((preview['direction'], preview['has_switcher']), ('older', False))
self.assertEqual(self.head(), start, 'preview changes nothing')
self.assertTrue(vm.preview_switch('v1.58.0')['has_switcher'])
vm.switch_to('v1.55-beta')
state = vm.current_state()
self.assertEqual((state['tag'], state['branch']), ('v1.55-beta', None))
self.assertEqual(Path(self.plugin, 'app.txt').read_text(), 'two')
self.assertEqual(state['previous']['branch'], 'main')
vm.undo_last_switch()
self.assertEqual((vm.current_state()['branch'], self.head()), ('main', start))
vm.switch_to('v1.0.0')
commit(self.upstream, 'app.txt', 'three', 'three') # main moves on after the rollback
vm.return_to_latest()
state = vm.current_state()
self.assertEqual(state['branch'], 'main')
self.assertEqual(Path(self.plugin, 'app.txt').read_text(), 'three')
self.assertEqual(state['previous']['label'], 'v1.0.0')
def test_local_changes_block_every_switch(self):
start = self.head()
Path(self.plugin, 'app.txt').write_text('edited', encoding='utf-8')
for action in (lambda: vm.switch_to('v1.0.0'), vm.return_to_latest, lambda: vm.preview_switch('v1.0.0')):
with self.assertRaises(vm.VersionError) as caught:
action()
self.assertEqual(caught.exception.code, 'dirty')
self.assertIn('app.txt', caught.exception.details['files'])
self.assertEqual(self.head(), start)
self.assertEqual(Path(self.plugin, 'app.txt').read_text(), 'edited')
def test_untracked_files_do_not_block(self):
Path(self.plugin, 'notes.local.json').write_text('{}', encoding='utf-8')
vm.switch_to('v1.0.0')
self.assertEqual(vm.current_state()['tag'], 'v1.0.0')
self.assertTrue(Path(self.plugin, 'notes.local.json').exists())
def test_only_published_tags_can_be_checked_out(self):
for tag, code in (('main', 'unknown_tag'), ('HEAD~1', 'bad_tag'), ('--help', 'bad_tag'), ('v9.9.9', 'unknown_tag')):
with self.assertRaises(vm.VersionError) as caught:
vm.switch_to(tag)
self.assertEqual(caught.exception.code, code, tag)
def test_diverged_local_branch_is_not_overwritten(self):
commit(self.plugin, 'local.txt', 'mine', 'local work')
mine = self.head()
commit(self.upstream, 'app.txt', 'three', 'three')
with self.assertRaises(vm.VersionError) as caught:
vm.return_to_latest()
self.assertEqual(caught.exception.code, 'diverged')
self.assertEqual(self.head(), mine)
def test_undo_without_history(self):
with self.assertRaises(vm.VersionError) as caught:
vm.undo_last_switch()
self.assertEqual(caught.exception.code, 'no_previous')
def test_not_a_git_checkout(self):
with tempfile.TemporaryDirectory() as plain, mock.patch.object(vm, 'PLUGIN_DIR', plain):
self.assertEqual(vm.current_state(), {'is_git': False})
if __name__ == '__main__':
unittest.main()