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.
138 lines
4.4 KiB
Python
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"
|