Files
driver-standard/tests/test_cli.py
T
christianmanivong db6b2e4ea7
CI / test (3.11) (push) Successful in 34s
CI / test (3.12) (push) Successful in 30s
CI / test (3.13) (push) Successful in 37s
feat: standards, shared CI and release tooling for NAPALM drivers
STANDARDS.md binds every repository in the NAPALM org: one default branch,
Conventional Commits, the device-types contract, test-driven development,
clean-code limits, a quality ratchet for existing code, packaging rules and
releases by tag to PyPI.

napalm-standard holds what a tool can check: the conformance check (with
release rules on a tag), the quality ratchet (ported from netOrk's
quality_ratchet.py), and the Gitea release from the CHANGELOG section.
.gitea/workflows/driver.yml is the CI every driver calls; template/ is a new
driver's starting point and passes the check as it is.
2026-10-22 19:01:14 +02:00

138 lines
4.4 KiB
Python

"""The command line the shared workflow calls."""
from __future__ import annotations
import pathlib
import pytest
from napalm_standard import cli
from napalm_standard.check import Finding
from tests.conftest import RepoFactory
def test_check_passes_a_conforming_repo(
make_repo: RepoFactory, capsys: pytest.CaptureFixture[str]
) -> None:
assert cli.main(["check", str(make_repo())]) == 0
assert "meets the standard" in capsys.readouterr().out
def test_check_lists_every_finding_and_fails(
make_repo: RepoFactory, capsys: pytest.CaptureFixture[str]
) -> None:
repo = make_repo({"LICENSE": None, "napalm_example/py.typed": None})
assert cli.main(["check", str(repo)]) == 1
out = capsys.readouterr().out
assert "[files] LICENSE is missing or empty" in out
assert "[py.typed]" in out
def test_check_with_a_tag_applies_the_release_rules(
make_repo: RepoFactory, capsys: pytest.CaptureFixture[str]
) -> None:
assert cli.main(["check", "--tag", "v9.0.0", str(make_repo())]) == 1
assert "[release]" in capsys.readouterr().out
def test_ratchet_writes_then_holds(
make_repo: RepoFactory, capsys: pytest.CaptureFixture[str]
) -> None:
repo = make_repo({"napalm_example/mod.py": "def raw(a):\n pass\n"})
assert cli.main(["ratchet", str(repo)]) == 1
assert cli.main(["ratchet", "--write", str(repo)]) == 0
assert cli.main(["ratchet", str(repo)]) == 0
assert "within the baseline" in capsys.readouterr().out
def test_notes_prints_the_section(
make_repo: RepoFactory, capsys: pytest.CaptureFixture[str]
) -> None:
assert cli.main(["notes", "v1.2.0", str(make_repo())]) == 0
assert capsys.readouterr().out.strip() == "### Added\n- Interfaces."
def test_notes_fails_for_a_version_without_a_section(make_repo: RepoFactory) -> None:
assert cli.main(["notes", "v9.9.9", str(make_repo())]) == 1
def test_release_publishes_with_the_environment(
make_repo: RepoFactory,
monkeypatch: pytest.MonkeyPatch,
capsys: pytest.CaptureFixture[str],
) -> None:
seen: dict[str, object] = {}
def fake_api(base_url: str, repository: str, token: str) -> object:
seen["client"] = (base_url, repository, token)
return "api"
def fake_publish(tag: str, package: str, changelog: str, api: object) -> dict[str, int]:
seen["publish"] = (tag, package, api)
return {"id": 5}
monkeypatch.setenv("GITEA_URL", "https://git.example")
monkeypatch.setenv("GITEA_REPOSITORY", "NAPALM/napalm-example")
monkeypatch.setenv("GITEA_TOKEN", "t0ken")
monkeypatch.setattr(cli, "gitea_api", fake_api)
monkeypatch.setattr(cli, "publish", fake_publish)
assert cli.main(["release", "v1.2.0", str(make_repo())]) == 0
assert seen["client"] == ("https://git.example", "NAPALM/napalm-example", "t0ken")
assert seen["publish"] == ("v1.2.0", "napalm-example", "api")
assert "release 5 for v1.2.0" in capsys.readouterr().out
def test_release_without_its_environment_fails(
make_repo: RepoFactory, monkeypatch: pytest.MonkeyPatch
) -> None:
for name in ("GITEA_URL", "GITEA_REPOSITORY", "GITEA_TOKEN"):
monkeypatch.delenv(name, raising=False)
assert cli.main(["release", "v1.2.0", str(make_repo())]) == 2
def test_the_path_defaults_to_the_working_directory(
make_repo: RepoFactory, monkeypatch: pytest.MonkeyPatch
) -> None:
monkeypatch.chdir(make_repo())
assert cli.main(["check"]) == 0
def test_a_finding_reads_as_rule_and_message() -> None:
assert str(Finding("files", "LICENSE is missing")) == "[files] LICENSE is missing"
def test_an_unparsable_dependency_is_left_to_the_build(make_repo: RepoFactory) -> None:
from tests.conftest import PYPROJECT
pyproject = PYPROJECT.replace('"napalm>=5.0",', '"napalm >= = 5",')
assert cli.main(["check", str(make_repo({"pyproject.toml": pyproject}))]) == 0
def test_the_module_runs_as_a_script(tmp_path: pathlib.Path) -> None:
import subprocess
import sys
result = subprocess.run(
[sys.executable, "-m", "napalm_standard", "check", str(tmp_path)],
capture_output=True,
text=True,
check=False,
)
assert result.returncode == 1
assert "[pyproject]" in result.stdout
def test_package_prints_the_import_package(
make_repo: RepoFactory, capsys: pytest.CaptureFixture[str]
) -> None:
assert cli.main(["package", str(make_repo())]) == 0
assert capsys.readouterr().out == "napalm_example\n"