|
40 | 40 | _JavaActivitySubprocess, |
41 | 41 | _ResourceTracker, |
42 | 42 | _start_server, |
| 43 | + _walk_jars, |
43 | 44 | ) |
44 | 45 | from airflow.sdk.execution_time.coordinator import BaseCoordinator |
45 | 46 | from airflow.sdk.execution_time.supervisor import ActivitySubprocess |
@@ -214,6 +215,62 @@ def test_find_by_explicit_main_class_not_present_raises(self, tmp_path): |
214 | 215 | with pytest.raises(FileNotFoundError, match="com.example.Missing"): |
215 | 216 | _JarInfo.find([tmp_path], "com.example.Missing") |
216 | 217 |
|
| 218 | + def test_symlink_cycle_does_not_infinite_recurse(self, tmp_path): |
| 219 | + nested = tmp_path / "inner" |
| 220 | + nested.mkdir() |
| 221 | + _make_jar(nested / "app.jar", main_class="com.example.Loop", schema_version="2026-06-16") |
| 222 | + loop = nested / "loop" |
| 223 | + try: |
| 224 | + loop.symlink_to(tmp_path) |
| 225 | + except (OSError, NotImplementedError): |
| 226 | + pytest.skip("symlinks not supported on this platform") |
| 227 | + |
| 228 | + result = _JarInfo.find([tmp_path], "com.example.Loop") |
| 229 | + assert result == _JarInfo("com.example.Loop", "2026-06-16") |
| 230 | + |
| 231 | + |
| 232 | +class TestWalkJars: |
| 233 | + def test_skips_directory_whose_key_is_already_in_seen_dirs(self, tmp_path): |
| 234 | + """A directory whose (st_dev, st_ino) is already in seen_dirs is skipped.""" |
| 235 | + _make_jar(tmp_path / "app.jar", main_class="com.example.Main", schema_version="2026-06-16") |
| 236 | + st = tmp_path.stat() |
| 237 | + seen_dirs: set[tuple[int, int]] = {(st.st_dev, st.st_ino)} |
| 238 | + assert list(_walk_jars([tmp_path], seen_dirs)) == [] |
| 239 | + |
| 240 | + def test_records_visited_directories_in_seen_dirs(self, tmp_path): |
| 241 | + """Every directory descended into is added to seen_dirs.""" |
| 242 | + sub = tmp_path / "sub" |
| 243 | + sub.mkdir() |
| 244 | + _make_jar(sub / "app.jar", main_class="com.example.Main", schema_version="2026-06-16") |
| 245 | + seen_dirs: set[tuple[int, int]] = set() |
| 246 | + list(_walk_jars([tmp_path], seen_dirs)) |
| 247 | + assert (tmp_path.stat().st_dev, tmp_path.stat().st_ino) in seen_dirs |
| 248 | + assert (sub.stat().st_dev, sub.stat().st_ino) in seen_dirs |
| 249 | + |
| 250 | + def test_symlink_cycle_yields_each_jar_once(self, tmp_path): |
| 251 | + """A symlink that loops back to an ancestor must not yield the same JAR twice.""" |
| 252 | + nested = tmp_path / "inner" |
| 253 | + nested.mkdir() |
| 254 | + jar = _make_jar(nested / "app.jar", main_class="com.example.Loop", schema_version="2026-06-16") |
| 255 | + loop = nested / "loop" |
| 256 | + try: |
| 257 | + loop.symlink_to(tmp_path) |
| 258 | + except (OSError, NotImplementedError): |
| 259 | + pytest.skip("symlinks not supported on this platform") |
| 260 | + |
| 261 | + seen_dirs: set[tuple[int, int]] = set() |
| 262 | + yielded = list(_walk_jars([tmp_path], seen_dirs)) |
| 263 | + assert [p.resolve() for p in yielded] == [jar.resolve()] |
| 264 | + |
| 265 | + def test_skip_logged_when_directory_revisited(self, tmp_path): |
| 266 | + """A revisited directory triggers the 'Skipping already-visited directory' debug log.""" |
| 267 | + sub = tmp_path / "sub" |
| 268 | + sub.mkdir() |
| 269 | + seen_dirs: set[tuple[int, int]] = {(sub.stat().st_dev, sub.stat().st_ino)} |
| 270 | + with patch("airflow.sdk.coordinators.java.coordinator.log") as mock_log: |
| 271 | + list(_walk_jars([sub], seen_dirs)) |
| 272 | + mock_log.debug.assert_any_call("Skipping already-visited directory", path=str(sub)) |
| 273 | + |
217 | 274 |
|
218 | 275 | class TestAcceptConnections: |
219 | 276 | def _connect_after_delay(self, addr: tuple[str, int], delay: float = 0.0) -> None: |
|
0 commit comments