# Copyright Kevin Deldycke <[email protected]> and contributors.
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
"""Checks for the `doctor` operation and its dedicated contract.
Health comes from the diagnostic command's exit code alone, the report merges
`<stdout>` and `<stderr>`, and the diagnosis never inflates the end-of-run
error tally. The argv assertions monkeypatch the binary-resolution seams on the
pooled manager singletons, so they run identically on any host.
"""
from __future__ import annotations
from pathlib import Path
import pytest
from meta_package_manager.capabilities import Operations, implements
from meta_package_manager.execution import CLIError
from meta_package_manager.pool import pool
from .fake_manager import FakeManager
from .test_sudo import only_escalator
[docs]
@pytest.mark.parametrize(
("manager_id", "token"),
(
("apt", "check"),
("brew", "doctor"),
("cask", "doctor"),
("composer", "diagnose"),
("dnf", "check"),
("flatpak", "--dry-run"),
("gem", "check"),
("mise", "doctor"),
("npm", "doctor"),
("pacman", "--check"),
("pip", "check"),
("pkg", "--checksums"),
("scoop", "checkup"),
("xbps", "--all"),
),
)
def test_doctor_cli_argv(monkeypatch, manager_id, token):
"""Each manager's diagnostic invocation carries its native verb."""
manager = pool[manager_id]
monkeypatch.setattr(
manager, "cli_path", Path("/fake/bin") / manager.cli_names[0], raising=False
)
monkeypatch.setattr(manager, "which", lambda name: Path("/fake/bin") / name)
monkeypatch.setattr(
manager, "sibling_cli", lambda name, **kwargs: Path("/fake/bin") / name
)
tokens = tuple(str(arg) for arg in manager.doctor_cli())
assert token in tokens
[docs]
@pytest.mark.parametrize(
"manager_id",
("apt", "brew", "cask", "dnf", "macports", "pacman", "pip", "tlmgr", "xbps"),
)
def test_doctor_supported(manager_id):
assert implements(pool[manager_id], Operations.doctor) is True
[docs]
@pytest.mark.parametrize("manager_id", ("cargo", "cave", "mas", "snap", "uv"))
def test_doctor_unsupported(manager_id):
assert implements(pool[manager_id], Operations.doctor) is False
# Base orchestrator contract, driven through a stubbed run().
def _doctor_with_stubbed_run(monkeypatch, *, code, stdout, stderr, completed=True):
"""Run `doctor()` on a manager whose `run` mimics one executor outcome.
Reproduces `run()`'s observable surface: sets `_last_run` on completion
(or leaves it `None` for a spawn that never finished), applies the failure
gate's default tolerance (a non-zero exit only accumulates a `CLIError`
when `<stderr>` is non-empty), and returns `<stdout>`.
"""
manager = pool["mise"]
monkeypatch.setattr(manager, "doctor_cli", lambda: ("doctor",))
monkeypatch.setattr(manager, "cli_errors", [])
def fake_run(*args, **kwargs):
manager._last_run = (code, stdout, stderr) if completed else None
if completed and code and stderr:
manager.cli_errors.append(CLIError(code, stdout, stderr))
elif not completed:
manager.cli_errors.append(CLIError(None, "", "Timed out."))
return stdout
monkeypatch.setattr(manager, "run", fake_run)
return manager
[docs]
@pytest.mark.parametrize(
("code", "stdout", "stderr", "verdict"),
(
# A healthy run merges both streams into the report.
pytest.param(
0,
"All good.",
"Note: chatter.",
(True, "All good.\nNote: chatter."),
id="healthy-merges-streams",
),
# A non-zero exit with a silent `<stderr>`, the shape of `pip check`, passes
# the failure gate but still reads as unhealthy: the exit code is the
# verdict.
pytest.param(
1,
"conflicting dependencies found",
"",
(False, "conflicting dependencies found"),
id="unhealthy-from-stdout-only-exit",
),
# The unhealthy exit is the diagnosis, not a plumbing error: the `CLIError`
# of the gate is reclaimed, so the end-of-run summary is not inflated.
pytest.param(
1,
"",
"Warning: broken linkage.",
(False, "Warning: broken linkage."),
id="reclaims-failure-gate-entry",
),
),
)
def test_doctor_verdict(monkeypatch, code, stdout, stderr, verdict):
"""The exit code decides the health, and no run leaves a `CLIError` behind."""
manager = _doctor_with_stubbed_run(
monkeypatch, code=code, stdout=stdout, stderr=stderr
)
assert manager.doctor() == verdict
assert manager.cli_errors == []
[docs]
def test_doctor_incomplete_run_stays_an_error(monkeypatch):
"""A spawn that never completed is a plumbing error.
Like a timeout or an interrupt: unhealthy, and its `CLIError` stays in the tally.
"""
manager = _doctor_with_stubbed_run(
monkeypatch, code=0, stdout="", stderr="", completed=False
)
assert manager.doctor() == (False, "")
assert len(manager.cli_errors) == 1
[docs]
def test_base_doctor_not_implemented():
"""A manager without a diagnostic verb propagates `NotImplementedError`."""
with pytest.raises(NotImplementedError):
pool["uv"].doctor()
# CLI plumbing, driven through deterministic fakes.
@pytest.fixture(autouse=True)
def _no_host_escalator():
"""Keep the escalation report of `mpm doctor` off the real host's escalator.
Pretends the host carries none, so the report spends no probe.
"""
with only_escalator(None):
yield
[docs]
class HealthyDoctorFakeManager(FakeManager):
"""Fake manager whose diagnosis succeeds with a report."""
[docs]
def doctor_cli(self) -> tuple[str, ...]:
return ("doctor",)
[docs]
def doctor(self) -> tuple[bool, str]:
return True, "Your system is ready."
[docs]
class SickDoctorFakeManager(HealthyDoctorFakeManager):
"""Fake manager whose diagnosis reports problems."""
[docs]
def doctor(self) -> tuple[bool, str]:
return False, "Broken linkage found."
[docs]
class SilentDoctorFakeManager(HealthyDoctorFakeManager):
"""Fake manager whose diagnosis succeeds silently (the apt-get check shape)."""
[docs]
def doctor(self) -> tuple[bool, str]:
return True, ""
[docs]
def test_doctor_healthy_relays_report(invoke, patch_pool_with):
fake = patch_pool_with(HealthyDoctorFakeManager())
result = invoke("doctor")
assert result.exit_code == 0
assert f"{fake.id}:" in result.stdout
assert "Your system is ready." in result.stdout
[docs]
def test_doctor_opens_on_the_escalation_report(invoke, patch_pool_with):
"""The escalation report comes first, and never changes the exit code."""
patch_pool_with(HealthyDoctorFakeManager())
result = invoke("doctor")
assert result.exit_code == 0
assert result.stdout.startswith("Privilege escalation:\nEscalator: none found")
[docs]
def test_doctor_without_diagnostic_verb_still_reports(invoke, monkeypatch):
"""Selected managers that all lack a diagnostic verb are no selection error.
The escalation report is the whole diagnosis, as on SliTaz with `tazpkg`.
"""
fake = FakeManager()
assert not implements(fake, Operations.doctor)
def select(*args, implements_operation=None, **kwargs):
if implements_operation is None or implements(fake, implements_operation):
yield fake
monkeypatch.setattr(pool, "_select_managers", select)
result = invoke("doctor")
assert result.exit_code == 0
assert result.stdout.startswith("Privilege escalation:")
assert "None of the selected managers has a diagnostic verb." in result.stderr
[docs]
def test_doctor_with_no_manager_selected_fails_after_reporting(invoke, monkeypatch):
"""No manager selected at all stays the usual error.
It comes once the escalation report is out.
"""
monkeypatch.setattr(pool, "_select_managers", lambda *args, **kwargs: iter(()))
result = invoke("doctor")
assert result.exit_code == 2
assert result.stdout.startswith("Privilege escalation:")
assert "No manager selected." in result.stderr
[docs]
def test_doctor_unhealthy_exits_nonzero(invoke, patch_pool_with):
fake = patch_pool_with(SickDoctorFakeManager())
result = invoke("doctor")
assert result.exit_code == 1
assert "Broken linkage found." in result.stdout
assert f"1 manager reported problems ({fake.id})." in result.stderr
[docs]
@pytest.mark.parametrize("flag", ("--zero-exit", "-0"))
def test_doctor_zero_exit_opt_out(invoke, patch_pool_with, flag):
"""-0/--zero-exit keeps the exit code at 0.
The critical summary stays as the durable record.
"""
fake = patch_pool_with(SickDoctorFakeManager())
result = invoke(flag, "doctor")
assert result.exit_code == 0
assert f"1 manager reported problems ({fake.id})." in result.stderr
[docs]
def test_doctor_silent_healthy_prints_no_section(invoke, patch_pool_with):
"""A healthy manager with an empty report gets no stdout section.
The trail already says everything.
"""
fake = patch_pool_with(SilentDoctorFakeManager())
result = invoke("doctor")
assert result.exit_code == 0
assert f"{fake.id}:" not in result.stdout