test_package_config.py (5590B)
1 #!/usr/bin/env python3 2 3 # This file is in the public domain. 4 5 import sys 6 import tempfile 7 import tomllib 8 import unittest 9 import stat 10 from dataclasses import replace 11 from pathlib import Path 12 13 BUILDSCRIPTS = Path(__file__).parents[1] / "buildscripts" 14 sys.path.insert(0, str(BUILDSCRIPTS)) 15 16 from package_config import ( # noqa: E402 17 ConfigError, 18 load_config, 19 parse_config, 20 serialize_config, 21 write_config, 22 ) 23 24 25 VALID_CONFIG = { 26 "repositories": { 27 "workspace": { 28 "url": "git://example.test/workspace.git", 29 "builder": "pnpm-workspace", 30 } 31 }, 32 "packages": { 33 "application": { 34 "repository": "workspace", 35 "tag": "v1.2.3", 36 "debian_path": "packages/application", 37 "dependencies": ["library"], 38 "enabled": True, 39 }, 40 "library": { 41 "repository": "workspace", 42 "tag": "v1.2.2", 43 }, 44 }, 45 } 46 47 48 class PackageConfigTests(unittest.TestCase): 49 def test_defaults_and_builder_inheritance(self): 50 config = parse_config(VALID_CONFIG) 51 52 library = config.packages["library"] 53 self.assertEqual("", library.debian_path) 54 self.assertEqual((), library.dependencies) 55 self.assertTrue(library.enabled) 56 self.assertTrue(library.auto_upgrade) 57 self.assertEqual("pnpm-workspace", config.builder_for(library)) 58 59 def test_auto_upgrade_can_be_disabled(self): 60 data = { 61 **VALID_CONFIG, 62 "packages": { 63 **VALID_CONFIG["packages"], 64 "library": { 65 **VALID_CONFIG["packages"]["library"], 66 "auto_upgrade": False, 67 }, 68 }, 69 } 70 71 config = parse_config(data) 72 73 self.assertFalse(config.packages["library"].auto_upgrade) 74 self.assertIn("auto_upgrade = false", serialize_config(config)) 75 76 def test_rejects_non_boolean_auto_upgrade(self): 77 data = { 78 **VALID_CONFIG, 79 "packages": { 80 **VALID_CONFIG["packages"], 81 "library": { 82 **VALID_CONFIG["packages"]["library"], 83 "auto_upgrade": "no", 84 }, 85 }, 86 } 87 88 with self.assertRaisesRegex(ConfigError, "auto_upgrade must be a boolean"): 89 parse_config(data) 90 91 def test_package_builder_override(self): 92 data = { 93 **VALID_CONFIG, 94 "packages": { 95 **VALID_CONFIG["packages"], 96 "library": { 97 **VALID_CONFIG["packages"]["library"], 98 "builder": "generic", 99 }, 100 }, 101 } 102 config = parse_config(data) 103 104 self.assertEqual("generic", config.builder_for(config.packages["library"])) 105 106 def test_rejects_unknown_builder(self): 107 data = { 108 **VALID_CONFIG, 109 "repositories": { 110 "workspace": { 111 **VALID_CONFIG["repositories"]["workspace"], 112 "builder": "unknown", 113 } 114 }, 115 } 116 117 with self.assertRaisesRegex(ConfigError, "builder is not supported"): 118 parse_config(data) 119 120 def test_rejects_unknown_fields_and_references(self): 121 with self.assertRaisesRegex(ConfigError, "unknown field"): 122 parse_config({**VALID_CONFIG, "surprise": {}}) 123 124 data = { 125 **VALID_CONFIG, 126 "packages": { 127 **VALID_CONFIG["packages"], 128 "library": { 129 **VALID_CONFIG["packages"]["library"], 130 "repository": "missing", 131 }, 132 }, 133 } 134 with self.assertRaisesRegex(ConfigError, "unknown repository"): 135 parse_config(data) 136 137 def test_rejects_dependency_cycles(self): 138 data = { 139 **VALID_CONFIG, 140 "packages": { 141 **VALID_CONFIG["packages"], 142 "library": { 143 **VALID_CONFIG["packages"]["library"], 144 "dependencies": ["application"], 145 }, 146 }, 147 } 148 149 with self.assertRaisesRegex(ConfigError, "application -> library -> application"): 150 parse_config(data) 151 152 def test_serializer_is_canonical_and_round_trips(self): 153 config = parse_config(VALID_CONFIG) 154 serialized = serialize_config(config) 155 156 self.assertEqual(config, parse_config(tomllib.loads(serialized))) 157 self.assertLess( 158 serialized.index('[packages."application"]'), 159 serialized.index('[packages."library"]'), 160 ) 161 self.assertEqual(serialized, serialize_config(parse_config(tomllib.loads(serialized)))) 162 163 def test_write_config_replaces_complete_document(self): 164 config = parse_config(VALID_CONFIG) 165 updated_packages = dict(config.packages) 166 updated_packages["library"] = replace( 167 updated_packages["library"], tag="v1.2.4" 168 ) 169 updated = replace(config, packages=updated_packages) 170 171 with tempfile.TemporaryDirectory() as temporary_directory: 172 path = Path(temporary_directory) / "packages.toml" 173 path.write_text("old contents\n") 174 path.chmod(0o640) 175 write_config(path, updated) 176 177 self.assertEqual(updated, load_config(path)) 178 self.assertEqual(serialize_config(updated), path.read_text()) 179 self.assertEqual(0o640, stat.S_IMODE(path.stat().st_mode)) 180 181 182 if __name__ == "__main__": 183 unittest.main()