|
40 | 40 | _highest_upstream_pin, |
41 | 41 | _select_cooldown_pin, |
42 | 42 | _update_tool_config, |
| 43 | + adopted_ongoing_configs, |
43 | 44 | default_version_pin, |
44 | 45 | export_content, |
45 | 46 | get_data_content, |
@@ -515,6 +516,62 @@ def test_init_config_lychee_preserves_other_sections(toml_file) -> None: |
515 | 516 | assert "lychee" in parsed["tool"] |
516 | 517 |
|
517 | 518 |
|
| 519 | +@pytest.mark.parametrize( |
| 520 | + ("content", "expected"), |
| 521 | + ( |
| 522 | + pytest.param("", set(), id="no-tool-table"), |
| 523 | + pytest.param('[project]\nname = "papaya"\n', set(), id="project-only"), |
| 524 | + pytest.param("[tool.typos]\n", {"typos"}, id="ongoing-adopted"), |
| 525 | + pytest.param("[tool.ruff]\n", set(), id="bootstrap-stays-out"), |
| 526 | + pytest.param("[tool.gitleaks]\n", set(), id="unmanaged-section"), |
| 527 | + pytest.param( |
| 528 | + "[tool.typos]\n[tool.uv]\n[tool.bumpversion]\n[tool.mypy]\n", |
| 529 | + {"typos", "uv", "bumpversion"}, |
| 530 | + id="every-ongoing-config", |
| 531 | + ), |
| 532 | + ), |
| 533 | +) |
| 534 | +def test_adopted_ongoing_configs( |
| 535 | + tmp_path: Path, content: str, expected: set[str] |
| 536 | +) -> None: |
| 537 | + """A section already on disk re-enters the bare-init set, if it is ONGOING. |
| 538 | +
|
| 539 | + `EXPLICIT` keeps `init` from pushing a tool config onto a repository that |
| 540 | + never asked for one; it must not also stop the ongoing sync of a section the |
| 541 | + repository already carries. `BOOTSTRAP` templates stay out either way: the |
| 542 | + repository owns them outright after the first write. |
| 543 | + """ |
| 544 | + (tmp_path / "pyproject.toml").write_text(content, encoding="UTF-8") |
| 545 | + assert adopted_ongoing_configs(tmp_path) == expected |
| 546 | + |
| 547 | + |
| 548 | +def test_adopted_ongoing_configs_without_pyproject(tmp_path: Path) -> None: |
| 549 | + """A repository with no pyproject.toml adopts nothing.""" |
| 550 | + assert adopted_ongoing_configs(tmp_path) == set() |
| 551 | + |
| 552 | + |
| 553 | +def test_adopted_ongoing_configs_are_explicit_and_ongoing(tmp_path: Path) -> None: |
| 554 | + """Whatever the helper returns is an EXPLICIT, ONGOING tool config. |
| 555 | +
|
| 556 | + Guards the invariant against a registry edit that flips a component's |
| 557 | + `init_default` or `sync_mode` without revisiting this selection path. |
| 558 | + """ |
| 559 | + sections = "\n".join( |
| 560 | + f"[{comp.tool_section}]" |
| 561 | + for comp in COMPONENTS |
| 562 | + if isinstance(comp, ToolConfigComponent) |
| 563 | + ) |
| 564 | + (tmp_path / "pyproject.toml").write_text(sections + "\n", encoding="UTF-8") |
| 565 | + |
| 566 | + adopted = adopted_ongoing_configs(tmp_path) |
| 567 | + assert adopted |
| 568 | + for name in adopted: |
| 569 | + comp = COMPONENTS_BY_NAME[name] |
| 570 | + assert isinstance(comp, ToolConfigComponent) |
| 571 | + assert comp.init_default is InitDefault.EXPLICIT |
| 572 | + assert comp.sync_mode is SyncMode.ONGOING |
| 573 | + |
| 574 | + |
518 | 575 | def test_uv_component_uses_overlay_ongoing() -> None: |
519 | 576 | """The uv tool config is an ongoing overlay, not a full-section rebuild.""" |
520 | 577 | comp = COMPONENTS_BY_NAME["uv"] |
|
0 commit comments