|
11 | 11 | SCRIPT = REPO_ROOT / ".github/scripts/dependency_age.py" |
12 | 12 | FIXTURES = Path(__file__).resolve().parent / "fixtures" |
13 | 13 | NOW = "2026-04-24T12:00:00Z" |
14 | | -OUTPUT_PATTERN = re.compile(r"^(cutoff_at|found|version|published_at|reason)=(.*)$") |
| 14 | +OUTPUT_PATTERN = re.compile( |
| 15 | + r"^(cutoff_at|found|version|published_at|reason|validated_coordinates|reverted_coordinates)=(.*)$" |
| 16 | +) |
15 | 17 |
|
16 | 18 |
|
17 | 19 | class DependencyAgeScriptTest(unittest.TestCase): |
@@ -61,29 +63,6 @@ def test_reports_when_no_eligible_gradle_release_exists(self) -> None: |
61 | 63 | self.assertEqual(outputs["found"], "false") |
62 | 64 | self.assertIn("No eligible stable Gradle release", outputs["reason"]) |
63 | 65 |
|
64 | | - def test_maven_prerelease_filtering_still_excludes_alpha_beta_and_rc(self) -> None: |
65 | | - result = self.run_script( |
66 | | - "select-maven", |
67 | | - "--now", |
68 | | - NOW, |
69 | | - "--group-id", |
70 | | - "org.apache.maven", |
71 | | - "--artifact-id", |
72 | | - "apache-maven", |
73 | | - "--search-response-file", |
74 | | - str(FIXTURES / "maven-prerelease-filter.json"), |
75 | | - "--prerelease-pattern", |
76 | | - "alpha", |
77 | | - "--prerelease-pattern", |
78 | | - "beta", |
79 | | - "--prerelease-pattern", |
80 | | - "rc", |
81 | | - ) |
82 | | - |
83 | | - self.assertEqual(result.returncode, 0, result.stderr) |
84 | | - outputs = self.parse_outputs(result.stdout) |
85 | | - self.assertEqual(outputs["version"], "3.9.9") |
86 | | - |
87 | 66 | def test_selects_previous_maven_release_when_newest_is_too_new(self) -> None: |
88 | 67 | result = self.run_script( |
89 | 68 | "select-maven", |
@@ -129,7 +108,6 @@ def test_exact_48_hour_boundary_is_accepted(self) -> None: |
129 | 108 | self.assertEqual(outputs["version"], "3.5.5") |
130 | 109 | self.assertEqual(outputs["published_at"], "2026-04-22T12:00:00Z") |
131 | 110 |
|
132 | | - |
133 | 111 | def run_validate_lockfiles( |
134 | 112 | self, |
135 | 113 | *, |
@@ -172,6 +150,38 @@ def run_validate_lockfiles( |
172 | 150 | ) |
173 | 151 | return result, current_dir |
174 | 152 |
|
| 153 | + def test_validates_changed_lockfiles_when_all_updates_are_old_enough(self) -> None: |
| 154 | + lockfile = "module/gradle.lockfile" |
| 155 | + baseline_content = "\n".join([ |
| 156 | + "# Gradle lockfile", |
| 157 | + "com.example:lib-a:1.0.0=runtimeClasspath", |
| 158 | + "com.example:lib-b:1.0.0=runtimeClasspath", |
| 159 | + "", |
| 160 | + ]) |
| 161 | + current_content = "\n".join([ |
| 162 | + "# Gradle lockfile", |
| 163 | + "com.example:lib-a:1.1.0=runtimeClasspath", # valid upgrade |
| 164 | + "com.example:lib-b:1.1.0=runtimeClasspath", # valid upgrade |
| 165 | + "", |
| 166 | + ]) |
| 167 | + metadata = { |
| 168 | + "com.example:lib-a:1.1.0": "2026-04-20T12:00:00Z", # old enough |
| 169 | + "com.example:lib-b:1.1.0": "2026-04-20T11:00:00Z", # old enough |
| 170 | + } |
| 171 | + |
| 172 | + result, current_dir = self.run_validate_lockfiles( |
| 173 | + baseline={"module/gradle.lockfile": baseline_content}, |
| 174 | + current={"module/gradle.lockfile": current_content}, |
| 175 | + metadata=metadata, |
| 176 | + ) |
| 177 | + |
| 178 | + self.assertEqual(result.returncode, 0, result.stderr) |
| 179 | + outputs = self.parse_outputs(result.stdout) |
| 180 | + self.assertEqual(outputs["validated_coordinates"], "2") |
| 181 | + self.assertEqual(outputs["reverted_coordinates"], "0") |
| 182 | + final = (current_dir / lockfile).read_text(encoding="utf-8") |
| 183 | + self.assertEqual(final, current_content) |
| 184 | + |
175 | 185 | def test_reverts_too_new_version_upgrade_keeps_valid_changes(self) -> None: |
176 | 186 | lockfile = "module/gradle.lockfile" |
177 | 187 | baseline_content = "\n".join([ |
@@ -236,6 +246,42 @@ def test_reverts_correct_version_when_multiple_versions_coexist(self) -> None: |
236 | 246 | self.assertIn("com.typesafe:config:1.4.4=runtimeClasspath,testRuntimeClasspath", final) |
237 | 247 | self.assertNotIn("com.typesafe:config:1.5.0", final) |
238 | 248 |
|
| 249 | + def test_reverts_each_version_independently_for_same_group_artifact(self) -> None: |
| 250 | + # Both versions of the same group:artifact are upgraded to too-new versions. |
| 251 | + # Each must be reverted to its own predecessor, not both to the same line. |
| 252 | + lockfile = "module/gradle.lockfile" |
| 253 | + baseline_content = "\n".join([ |
| 254 | + "# Gradle lockfile", |
| 255 | + "com.example:lib:1.0.0=compileClasspath", |
| 256 | + "com.example:lib:2.0.0=runtimeClasspath", |
| 257 | + "", |
| 258 | + ]) |
| 259 | + current_content = "\n".join([ |
| 260 | + "# Gradle lockfile", |
| 261 | + "com.example:lib:1.1.0=compileClasspath", # too new, should revert to 1.0.0 |
| 262 | + "com.example:lib:2.1.0=runtimeClasspath", # too new, should revert to 2.0.0 |
| 263 | + "", |
| 264 | + ]) |
| 265 | + metadata = { |
| 266 | + "com.example:lib:1.1.0": "2026-04-24T11:00:00Z", # too new |
| 267 | + "com.example:lib:2.1.0": "2026-04-24T11:00:00Z", # too new |
| 268 | + } |
| 269 | + |
| 270 | + result, current_dir = self.run_validate_lockfiles( |
| 271 | + baseline={"module/gradle.lockfile": baseline_content}, |
| 272 | + current={"module/gradle.lockfile": current_content}, |
| 273 | + metadata=metadata, |
| 274 | + ) |
| 275 | + |
| 276 | + self.assertEqual(result.returncode, 0, result.stderr) |
| 277 | + final = (current_dir / lockfile).read_text(encoding="utf-8") |
| 278 | + self.assertIn("com.example:lib:1.0.0=compileClasspath", final) |
| 279 | + self.assertIn("com.example:lib:2.0.0=runtimeClasspath", final) |
| 280 | + self.assertNotIn("com.example:lib:1.1.0", final) |
| 281 | + self.assertNotIn("com.example:lib:2.1.0", final) |
| 282 | + # Exactly two entries — no duplication |
| 283 | + self.assertEqual(final.count("com.example:lib:"), 2) |
| 284 | + |
239 | 285 | def test_removes_brand_new_dependency_that_is_too_new(self) -> None: |
240 | 286 | lockfile = "module/gradle.lockfile" |
241 | 287 | baseline_content = "\n".join([ |
|
0 commit comments