"""Tests for services.update_service — no GUI dependency.""" import json import shutil import sys import tempfile import unittest from pathlib import Path sys.path.insert(0, str(Path(__file__).parent.parent / "src")) from services.update_service import ( UpdateInfo, check_for_update, is_newer, parse_version, ) class TestVersionCompare(unittest.TestCase): def test_parse_basic(self): self.assertEqual(parse_version("1.2.3"), (1, 2, 3)) def test_parse_pads_missing_parts(self): self.assertEqual(parse_version("1"), (1, 0, 0)) self.assertEqual(parse_version("1.5"), (1, 5, 0)) def test_parse_tolerates_suffix(self): self.assertEqual(parse_version("1.2.3rc1"), (1, 2, 3)) self.assertEqual(parse_version("v"), (0, 0, 0)) def test_is_newer(self): self.assertTrue(is_newer("1.1.0", "1.0.0")) self.assertTrue(is_newer("1.0.1", "1.0.0")) self.assertTrue(is_newer("2.0.0", "1.9.9")) def test_is_not_newer(self): self.assertFalse(is_newer("1.0.0", "1.0.0")) self.assertFalse(is_newer("1.0.0", "1.1.0")) self.assertFalse(is_newer("0.9.9", "1.0.0")) class TestCheckForUpdate(unittest.TestCase): def setUp(self): self.tmp = Path(tempfile.mkdtemp()) def tearDown(self): shutil.rmtree(str(self.tmp), ignore_errors=True) def _write_manifest(self, data): with open(str(self.tmp / "manifest.json"), "w", encoding="utf-8") as f: json.dump(data, f) def test_no_source_returns_none(self): self.assertIsNone(check_for_update("", "1.0.0")) def test_missing_manifest_returns_none(self): # tmp exists but has no manifest.json self.assertIsNone(check_for_update(str(self.tmp), "1.0.0")) def test_unreachable_source_returns_none(self): self.assertIsNone(check_for_update(str(self.tmp / "nope"), "1.0.0")) def test_malformed_manifest_returns_none(self): with open(str(self.tmp / "manifest.json"), "w", encoding="utf-8") as f: f.write("{ not valid json") self.assertIsNone(check_for_update(str(self.tmp), "1.0.0")) def test_non_object_manifest_returns_none(self): self._write_manifest(["1.1.0"]) self.assertIsNone(check_for_update(str(self.tmp), "1.0.0")) def test_newer_version_returns_info(self): self._write_manifest({ "version": "1.1.0", "source": r"\\nas\cmbot\releases\CMBot-1.1.0", "notes": "fix batch export", "mandatory": False, }) info = check_for_update(str(self.tmp), "1.0.0") self.assertIsInstance(info, UpdateInfo) self.assertEqual(info.version, "1.1.0") self.assertEqual(info.source, r"\\nas\cmbot\releases\CMBot-1.1.0") self.assertEqual(info.notes, "fix batch export") self.assertFalse(info.mandatory) def test_same_version_returns_none(self): self._write_manifest({"version": "1.0.0"}) self.assertIsNone(check_for_update(str(self.tmp), "1.0.0")) def test_older_version_returns_none(self): self._write_manifest({"version": "0.9.0"}) self.assertIsNone(check_for_update(str(self.tmp), "1.0.0")) def test_missing_version_field_returns_none(self): self._write_manifest({"notes": "no version here"}) self.assertIsNone(check_for_update(str(self.tmp), "1.0.0")) def test_source_falls_back_to_update_source(self): self._write_manifest({"version": "2.0.0"}) # no "source" field info = check_for_update(str(self.tmp), "1.0.0") self.assertEqual(info.source, str(self.tmp)) if __name__ == "__main__": unittest.main()