"""Regression tests for Client.Python-037 and Client.Python-038. Client.Python-037: ``pyproject.toml`` description must not contain "scaffold". Client.Python-038: ``advise-supervisory`` CLI subcommand must have coverage (registration smoke test + happy-path command-shape test). Tests are TDD-first — written before the fix and expected to pass once the source change lands. """ from __future__ import annotations import json from pathlib import Path from typing import Any from click.testing import CliRunner from zb_mom_ww_mxgateway import ClientOptions, GatewayClient from zb_mom_ww_mxgateway.generated import mxaccess_gateway_pb2 as pb from zb_mom_ww_mxgateway_cli.commands import main # --------------------------------------------------------------------------- # Client.Python-037 — pyproject.toml description must not contain "scaffold". # --------------------------------------------------------------------------- def test_pyproject_description_does_not_contain_scaffold() -> None: """The ``description`` field in ``pyproject.toml`` must not include the word "scaffold" — a regression of Client.Python-001 that re-entered the file at the package-rename commit. """ pyproject = ( Path(__file__).resolve().parent.parent / "pyproject.toml" ).read_text(encoding="utf-8") # Find the description line and assert "scaffold" is absent. for line in pyproject.splitlines(): stripped = line.strip() if stripped.startswith("description"): assert "scaffold" not in stripped.lower(), ( f"pyproject.toml description must not contain 'scaffold': {stripped!r}" ) return raise AssertionError("pyproject.toml has no 'description' line") # --------------------------------------------------------------------------- # Client.Python-038 — advise-supervisory must be registered + have a happy path. # --------------------------------------------------------------------------- def test_advise_supervisory_is_registered() -> None: """``advise-supervisory`` must be a registered subcommand of ``main``. A ``--help`` invocation must exit 0 and the help text must include the required options (--server-handle and --item-handle). """ runner = CliRunner() result = runner.invoke(main, ["advise-supervisory", "--help"]) assert result.exit_code == 0, result.output assert "--server-handle" in result.output assert "--item-handle" in result.output # --------------- fake-stub infrastructure (mirrors test_review_findings_022_to_026) ---- class _FakeUnary: def __init__(self, replies: list[Any]) -> None: self.replies = replies self.requests: list[Any] = [] self.metadata: tuple[tuple[str, str], ...] | None = None async def __call__(self, request: Any, *, metadata: tuple[tuple[str, str], ...]) -> Any: self.requests.append(request) self.metadata = metadata return self.replies.pop(0) class _FakeStub: def __init__(self) -> None: self.open_session = _FakeUnary( [ pb.OpenSessionReply( session_id="session-1", protocol_status=pb.ProtocolStatus(code=pb.PROTOCOL_STATUS_CODE_OK), ), ], ) self.invoke = _FakeUnary([]) self.OpenSession = self.open_session self.Invoke = self.invoke def set_invoke_replies(self, replies: list[Any]) -> None: self.invoke.replies = replies def _install_fake_connect(monkeypatch: Any, stub: _FakeStub) -> None: """Patch ``GatewayClient.connect`` so the CLI uses the supplied fake stub.""" real_connect = GatewayClient.connect @classmethod # type: ignore[misc] async def _spy_connect(cls: Any, options: ClientOptions, **kwargs: Any) -> GatewayClient: return await real_connect(options, stub=stub) monkeypatch.setattr(GatewayClient, "connect", _spy_connect) def test_cli_advise_supervisory_happy_path(monkeypatch: Any) -> None: """``advise-supervisory`` must forward server_handle and item_handle in an ``MX_COMMAND_KIND_ADVISE_SUPERVISORY`` ``MxCommand``. Pattern mirrors ``test_cli_acknowledge_alarm_happy_path`` in ``test_review_findings_022_to_026.py``. """ stub = _FakeStub() stub.set_invoke_replies( [ pb.MxCommandReply( session_id="session-1", kind=pb.MX_COMMAND_KIND_ADVISE_SUPERVISORY, protocol_status=pb.ProtocolStatus(code=pb.PROTOCOL_STATUS_CODE_OK), ), ], ) _install_fake_connect(monkeypatch, stub) runner = CliRunner() result = runner.invoke( main, [ "advise-supervisory", "--endpoint", "localhost:5000", "--plaintext", "--session-id", "session-1", "--server-handle", "7", "--item-handle", "42", "--json", ], ) assert result.exit_code == 0, result.output payload = json.loads(result.output) assert payload["ok"] is True # Verify the MxCommand shape forwarded to the gateway. assert len(stub.invoke.requests) == 1 cmd = stub.invoke.requests[0].command assert cmd.kind == pb.MX_COMMAND_KIND_ADVISE_SUPERVISORY assert cmd.advise_supervisory.server_handle == 7 assert cmd.advise_supervisory.item_handle == 42