126 lines
3.4 KiB
Python
126 lines
3.4 KiB
Python
import json
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from typer.testing import CliRunner
|
|
|
|
from k1link.device_plugins.xgrids_k1.cli import app
|
|
|
|
runner = CliRunner()
|
|
|
|
|
|
def test_help() -> None:
|
|
result = runner.invoke(app, ["--help"])
|
|
assert result.exit_code == 0
|
|
assert "evidence-led" in result.stdout
|
|
|
|
|
|
def test_doctor_json() -> None:
|
|
result = runner.invoke(app, ["doctor", "--json"])
|
|
assert result.exit_code == 0
|
|
payload = json.loads(result.stdout)
|
|
assert payload["k1link_version"] == "0.1.0"
|
|
assert isinstance(payload["tools"], list)
|
|
assert isinstance(payload["network"], dict)
|
|
assert any(item["name"] == "tcpdump" for item in payload["tools"])
|
|
|
|
|
|
def test_authority_provision_requires_explicit_reviewed_value_confirmation() -> None:
|
|
result = runner.invoke(app, ["authority", "provision"])
|
|
|
|
assert result.exit_code == 2
|
|
assert "provisioning not confirmed" in result.stdout
|
|
|
|
|
|
def test_authority_provision_never_accepts_the_secret_as_a_cli_value(monkeypatch: Any) -> None:
|
|
calls = 0
|
|
|
|
class FakeSnapshot:
|
|
service = "fixed-service"
|
|
account = "fixed-account"
|
|
|
|
class FakeProvisioner:
|
|
def provision_interactively(self) -> FakeSnapshot:
|
|
nonlocal calls
|
|
calls += 1
|
|
return FakeSnapshot()
|
|
|
|
monkeypatch.setattr(
|
|
"k1link.device_plugins.xgrids_k1.cli.MacOSKeychainApplicationAuthorityProvisioner",
|
|
FakeProvisioner,
|
|
)
|
|
result = runner.invoke(
|
|
app,
|
|
["authority", "provision", "--confirm-reviewed-authority"],
|
|
)
|
|
|
|
assert result.exit_code == 0
|
|
assert calls == 1
|
|
assert "Keychain item validated" in result.stdout
|
|
assert "No K1 command was sent" in " ".join(result.stdout.split())
|
|
|
|
|
|
def test_mqtt_capture_requires_owned_device_confirmation(tmp_path: Path) -> None:
|
|
result = runner.invoke(
|
|
app,
|
|
[
|
|
"net",
|
|
"mqtt-capture",
|
|
"--host",
|
|
"192.168.1.20",
|
|
"--out",
|
|
str(tmp_path / "capture"),
|
|
],
|
|
)
|
|
assert result.exit_code == 2
|
|
assert "ownership not confirmed" in result.stdout
|
|
assert not (tmp_path / "capture").exists()
|
|
|
|
|
|
def test_mqtt_capture_cli_uses_bounded_read_only_capture(
|
|
monkeypatch: Any,
|
|
tmp_path: Path,
|
|
) -> None:
|
|
captured: dict[str, object] = {}
|
|
|
|
def fake_capture(host: str, out: Path, **kwargs: object) -> dict[str, object]:
|
|
on_ready = kwargs.pop("on_ready")
|
|
assert callable(on_ready)
|
|
on_ready()
|
|
captured.update({"host": host, "out": out, **kwargs})
|
|
return {
|
|
"stop_reason": "duration_elapsed",
|
|
"message_count": 2,
|
|
"payload_bytes": 128,
|
|
}
|
|
|
|
monkeypatch.setattr("k1link.device_plugins.xgrids_k1.cli.capture_mqtt", fake_capture)
|
|
out = tmp_path / "capture"
|
|
result = runner.invoke(
|
|
app,
|
|
[
|
|
"net",
|
|
"mqtt-capture",
|
|
"--host",
|
|
"10.0.0.42",
|
|
"--out",
|
|
str(out),
|
|
"--duration",
|
|
"5",
|
|
"--max-message-bytes",
|
|
"1024",
|
|
"--confirm-owned-device",
|
|
],
|
|
)
|
|
|
|
assert result.exit_code == 0
|
|
assert captured == {
|
|
"host": "10.0.0.42",
|
|
"out": out,
|
|
"port": 1883,
|
|
"duration_seconds": 5.0,
|
|
"max_message_bytes": 1024,
|
|
}
|
|
assert "messages: 2" in result.stdout
|
|
assert "subscriptions active" in result.stdout
|