From 276f9834e5101c6243afb7ebe4e6375eab0079f6 Mon Sep 17 00:00:00 2001 From: Richard Kroegel <42204099+rikroe@users.noreply.github.com> Date: Mon, 1 Jul 2024 19:32:55 +0200 Subject: [PATCH] Add basic CLI tests (#624) * Add basic CLI tests * Use stdlib only --- bimmer_connected/cli.py | 8 +- bimmer_connected/tests/test_cli.py | 138 +++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 2 deletions(-) create mode 100644 bimmer_connected/tests/test_cli.py diff --git a/bimmer_connected/cli.py b/bimmer_connected/cli.py index 9ad7a03e..f4bda1f8 100644 --- a/bimmer_connected/cli.py +++ b/bimmer_connected/cli.py @@ -138,7 +138,7 @@ async def get_status(args) -> None: if args.json: if args.vin: - print(json.dumps(account.get_vehicle(args.vin), cls=MyBMWJSONEncoder)) + print(json.dumps(get_vehicle_or_return(account, args.vin), cls=MyBMWJSONEncoder)) else: print(json.dumps(account.vehicles, cls=MyBMWJSONEncoder)) else: @@ -338,7 +338,11 @@ def main(): logging.getLogger("asyncio").setLevel(logging.WARNING) loop = asyncio.get_event_loop() - loop.run_until_complete(args.func(args)) + try: + loop.run_until_complete(args.func(args)) + except Exception as ex: # pylint: disable=broad-except + sys.stderr.write(f"{type(ex).__name__}: {ex}\n") + sys.exit(1) if __name__ == "__main__": diff --git a/bimmer_connected/tests/test_cli.py b/bimmer_connected/tests/test_cli.py new file mode 100644 index 00000000..6f5eec83 --- /dev/null +++ b/bimmer_connected/tests/test_cli.py @@ -0,0 +1,138 @@ +import json +import subprocess +import sys +import tempfile +from pathlib import Path + +import pytest + +import bimmer_connected.cli + +from . import get_fingerprint_count + +ARGS_USER_PW_REGION = ["myuser", "mypassword", "rest_of_world"] +FIXTURE_CLI_HELP = "A simple executable to use and test the library." + + +def test_run_entrypoint(): + """Test if the entrypoint is installed correctly.""" + result = subprocess.run(["bimmerconnected", "--help"], capture_output=True, text=True) + + assert FIXTURE_CLI_HELP in result.stdout + assert result.returncode == 0 + + +def test_run_module(): + """Test if the module can be run as a python module.""" + result = subprocess.run(["python", "-m", "bimmer_connected.cli", "--help"], capture_output=True, text=True) + + assert FIXTURE_CLI_HELP in result.stdout + assert result.returncode == 0 + + +@pytest.mark.usefixtures("bmw_fixture") +@pytest.mark.parametrize( + ("vin", "expected_count"), + [ + ("WBA00000000000F31", 1), + ("WBA00000000000F31,WBA00000000DEMO03", 0), + ("WBA00000000000Z99", 0), + ], +) +def test_status_json_filtered(capsys: pytest.CaptureFixture, vin, expected_count): + """Test the status command JSON output filtered by VIN.""" + + sys.argv = ["bimmerconnected", "status", "-j", "-v", vin, *ARGS_USER_PW_REGION] + try: + bimmer_connected.cli.main() + except SystemExit: + pass + result = capsys.readouterr() + + if expected_count == 1: + result_json = json.loads(result.out) + assert isinstance(result_json, dict) + assert result_json["vin"] == vin + else: + assert "Error: Could not find vehicle" in result.err + + +@pytest.mark.usefixtures("bmw_fixture") +def test_status_json_unfiltered(capsys: pytest.CaptureFixture): + """Test the status command JSON output filtered by VIN.""" + + sys.argv = ["bimmerconnected", "status", "-j", *ARGS_USER_PW_REGION] + bimmer_connected.cli.main() + result = capsys.readouterr() + + result_json = json.loads(result.out) + assert isinstance(result_json, list) + assert len(result_json) == get_fingerprint_count("states") + + +@pytest.mark.usefixtures("bmw_fixture") +@pytest.mark.parametrize( + ("vin", "expected_count"), + [ + ("WBA00000000000F31", 1), + ("WBA00000000000F31,WBA00000000DEMO03", 0), + ("WBA00000000000Z99", 0), + ], +) +def test_status_filtered(capsys: pytest.CaptureFixture, vin, expected_count): + """Test the status command text output filtered by VIN.""" + + sys.argv = ["bimmerconnected", "status", "-v", vin, *ARGS_USER_PW_REGION] + try: + bimmer_connected.cli.main() + except SystemExit: + pass + result = capsys.readouterr() + + assert f"Found {get_fingerprint_count('states')} vehicles" in result.out + + if expected_count == 1: + assert f"VIN: {vin}" in result.out + assert result.out.count("VIN: ") == expected_count + else: + assert result.out.count("VIN: ") == expected_count + + +@pytest.mark.usefixtures("bmw_fixture") +def test_status_unfiltered(capsys: pytest.CaptureFixture): + """Test the status command text output filtered by VIN.""" + + sys.argv = ["bimmerconnected", "status", *ARGS_USER_PW_REGION] + bimmer_connected.cli.main() + result = capsys.readouterr() + + assert f"Found {get_fingerprint_count('states')} vehicles" in result.out + assert result.out.count("VIN: ") == get_fingerprint_count("states") + + +@pytest.mark.usefixtures("bmw_fixture") +@pytest.mark.usefixtures("bmw_log_all_responses") +def test_fingerprint(capsys: pytest.CaptureFixture, monkeypatch: pytest.MonkeyPatch): + """Test the fingerprint command.""" + + with tempfile.TemporaryDirectory() as tmpdirname: + tmp_path = Path(tmpdirname) + monkeypatch.setattr("pathlib.Path.home", lambda: tmp_path) + + sys.argv = ["bimmerconnected", "fingerprint", *ARGS_USER_PW_REGION] + bimmer_connected.cli.main() + result = capsys.readouterr() + + assert "fingerprint of the vehicles written to" in result.out + + files = list(tmp_path.rglob("*")) + json_files = [f for f in files if f.suffix == ".json"] + txt_files = [f for f in files if f.suffix == ".txt"] + + assert len(json_files) == ( + get_fingerprint_count("vehicles") + + get_fingerprint_count("profiles") + + get_fingerprint_count("states") + + get_fingerprint_count("charging_settings") + ) + assert len(txt_files) == 0