"""Tests for brother_printer.cups_service module - part 2.""" from __future__ import annotations import subprocess from unittest.mock import MagicMock, patch from python_pkg.brother_printer.cups_service import ( _cups_reasons_to_error, _get_cups_economode, _get_printer_info_from_cups, _map_cups_to_status_code, _parse_cups_usb_uri, _port_status_to_status_code, find_cups_printer_name, ) from python_pkg.brother_printer.data_classes import ( USBPortStatus, ) MOD = "python_pkg.brother_printer.cups_service" # ── _get_cups_economode ────────────────────────────────────────────── class TestGetCupsEconomode: """Tests for _get_cups_economode.""" @patch(f"{MOD}.shutil.which", return_value=None) def test_no_lpoptions(self, m: MagicMock) -> None: assert _get_cups_economode("Brother") == "" @patch(f"{MOD}.subprocess.run") @patch(f"{MOD}.shutil.which", return_value="/usr/bin/lpoptions") def test_economode_on(self, w: MagicMock, mock_run: MagicMock) -> None: mock_run.return_value = MagicMock( stdout="BREconomode/Toner Save Mode: *True False\n" ) assert _get_cups_economode("Brother") == "ON" @patch(f"{MOD}.subprocess.run") @patch(f"{MOD}.shutil.which", return_value="/usr/bin/lpoptions") def test_economode_off(self, w: MagicMock, mock_run: MagicMock) -> None: mock_run.return_value = MagicMock( stdout="BREconomode/Toner Save Mode: True *False\n" ) assert _get_cups_economode("Brother") == "OFF" @patch(f"{MOD}.subprocess.run") @patch(f"{MOD}.shutil.which", return_value="/usr/bin/lpoptions") def test_no_economode_line(self, w: MagicMock, mock_run: MagicMock) -> None: mock_run.return_value = MagicMock( stdout="Resolution/Output Resolution: 600dpi *1200dpi\n" ) assert _get_cups_economode("Brother") == "" @patch(f"{MOD}.subprocess.run") @patch(f"{MOD}.shutil.which", return_value="/usr/bin/lpoptions") def test_economode_no_star_match(self, w: MagicMock, mock_run: MagicMock) -> None: mock_run.return_value = MagicMock( stdout="BREconomode/Toner Save Mode: True False\n" ) assert _get_cups_economode("Brother") == "" @patch(f"{MOD}.subprocess.run") @patch(f"{MOD}.shutil.which", return_value="/usr/bin/lpoptions") def test_timeout(self, w: MagicMock, mock_run: MagicMock) -> None: mock_run.side_effect = subprocess.TimeoutExpired("lpoptions", 5) assert _get_cups_economode("Brother") == "" @patch(f"{MOD}.subprocess.run") @patch(f"{MOD}.shutil.which", return_value="/usr/bin/lpoptions") def test_oserror(self, w: MagicMock, mock_run: MagicMock) -> None: mock_run.side_effect = OSError("fail") assert _get_cups_economode("Brother") == "" # ── _map_cups_to_status_code ───────────────────────────────────────── class TestMapCupsToStatusCode: """Tests for _map_cups_to_status_code.""" def test_reason_match(self) -> None: result = _map_cups_to_status_code("idle", "toner-low-report") assert result == "30010" def test_state_match(self) -> None: result = _map_cups_to_status_code("idle", "none") assert result == "10001" def test_processing_state(self) -> None: result = _map_cups_to_status_code("processing", "none") assert result == "10007" def test_stopped_state(self) -> None: result = _map_cups_to_status_code("stopped", "none") assert result == "10023" def test_unknown_state(self) -> None: result = _map_cups_to_status_code("mystery", "none") assert result == "10001" def test_state_with_parenthetical(self) -> None: result = _map_cups_to_status_code("idle (on fire)", "none") assert result == "10001" # ── _cups_reasons_to_error ─────────────────────────────────────────── class TestCupsReasonsToError: """Tests for _cups_reasons_to_error.""" def test_media_jam(self) -> None: code, display = _cups_reasons_to_error("media-jam-report") assert code == "40000" assert display == "Paper Jam" def test_cover_open(self) -> None: code, _ = _cups_reasons_to_error("cover-open") assert code == "41000" def test_door_open(self) -> None: code, _ = _cups_reasons_to_error("door-open") assert code == "41000" def test_toner_empty(self) -> None: code, _ = _cups_reasons_to_error("toner-empty") assert code == "40310" def test_toner_low(self) -> None: code, _ = _cups_reasons_to_error("toner-low") assert code == "30010" def test_unknown_reason(self) -> None: code, display = _cups_reasons_to_error("something-weird") assert code == "42000" assert display == "Printer Error" # ── _port_status_to_status_code ────────────────────────────────────── class TestPortStatusToStatusCode: """Tests for _port_status_to_status_code.""" def test_error_and_paper_empty(self) -> None: ps = USBPortStatus(error=True, paper_empty=True, online=True) code, display = _port_status_to_status_code(ps, "none") assert code == "40302" assert display == "No Paper" def test_error_and_not_online(self) -> None: ps = USBPortStatus(error=True, paper_empty=False, online=False) code, display = _port_status_to_status_code(ps, "none") assert code == "41000" assert display == "Cover Open" def test_error_only(self) -> None: ps = USBPortStatus(error=True, paper_empty=False, online=True) code, _ = _port_status_to_status_code(ps, "media-jam") assert code == "40000" def test_paper_empty_no_error(self) -> None: ps = USBPortStatus(error=False, paper_empty=True, online=True) code, _ = _port_status_to_status_code(ps, "none") assert code == "40302" def test_not_online_no_error(self) -> None: ps = USBPortStatus(error=False, paper_empty=False, online=False) code, display = _port_status_to_status_code(ps, "none") assert code == "10002" assert display == "Offline / Sleep" def test_all_ok(self) -> None: ps = USBPortStatus(error=False, paper_empty=False, online=True) code, display = _port_status_to_status_code(ps, "none") assert code == "" assert display == "" # ── find_cups_printer_name ─────────────────────────────────────────── class TestFindCupsPrinterName: """Tests for find_cups_printer_name.""" @patch(f"{MOD}.shutil.which", return_value=None) def test_no_lpstat(self, m: MagicMock) -> None: assert find_cups_printer_name() == "" @patch(f"{MOD}.subprocess.run") @patch(f"{MOD}.shutil.which", return_value="/usr/bin/lpstat") def test_found(self, w: MagicMock, mock_run: MagicMock) -> None: mock_run.return_value = MagicMock( stdout="device for BrotherHL1110: usb://Brother/HL-1110\n" ) assert find_cups_printer_name() == "BrotherHL1110" @patch(f"{MOD}.subprocess.run") @patch(f"{MOD}.shutil.which", return_value="/usr/bin/lpstat") def test_no_brother(self, w: MagicMock, mock_run: MagicMock) -> None: mock_run.return_value = MagicMock(stdout="device for HP: ipp://hp.local\n") assert find_cups_printer_name() == "" @patch(f"{MOD}.subprocess.run") @patch(f"{MOD}.shutil.which", return_value="/usr/bin/lpstat") def test_brother_no_match(self, w: MagicMock, mock_run: MagicMock) -> None: mock_run.return_value = MagicMock( stdout="brother printer found but format unexpected\n" ) assert find_cups_printer_name() == "" @patch(f"{MOD}.subprocess.run") @patch(f"{MOD}.shutil.which", return_value="/usr/bin/lpstat") def test_timeout(self, w: MagicMock, mock_run: MagicMock) -> None: mock_run.side_effect = subprocess.TimeoutExpired("lpstat", 5) assert find_cups_printer_name() == "" @patch(f"{MOD}.subprocess.run") @patch(f"{MOD}.shutil.which", return_value="/usr/bin/lpstat") def test_oserror(self, w: MagicMock, mock_run: MagicMock) -> None: mock_run.side_effect = OSError("fail") assert find_cups_printer_name() == "" # ── _parse_cups_usb_uri ───────────────────────────────────────────── class TestParseCupsUsbUri: """Tests for _parse_cups_usb_uri.""" def test_full_uri(self) -> None: info: dict[str, str] = {"product": "", "serial": ""} _parse_cups_usb_uri("usb://Brother/HL-1110%20series?serial=ABC123", info) assert info["product"] == "HL-1110 series" assert info["serial"] == "ABC123" def test_no_serial(self) -> None: info: dict[str, str] = {"product": "", "serial": ""} _parse_cups_usb_uri("usb://Brother/HL-1110", info) assert info["product"] == "HL-1110" assert info["serial"] == "" # ── _get_printer_info_from_cups ────────────────────────────────────── class TestGetPrinterInfoFromCups: """Tests for _get_printer_info_from_cups.""" @patch(f"{MOD}.subprocess.run") def test_found(self, mock_run: MagicMock) -> None: mock_run.return_value = MagicMock( stdout="device for B: usb://Brother/HL-1110?serial=XYZ\n" ) result = _get_printer_info_from_cups() assert result["product"] == "HL-1110" assert result["serial"] == "XYZ" @patch(f"{MOD}.subprocess.run") def test_no_brother(self, mock_run: MagicMock) -> None: mock_run.return_value = MagicMock(stdout="device for HP: ipp://hp.local\n") result = _get_printer_info_from_cups() assert result["product"] == "" @patch(f"{MOD}.subprocess.run") def test_brother_no_usb(self, mock_run: MagicMock) -> None: mock_run.return_value = MagicMock(stdout="device for B: ipp://Brother.local\n") result = _get_printer_info_from_cups() assert result["product"] == "" @patch(f"{MOD}.subprocess.run") def test_timeout(self, mock_run: MagicMock) -> None: mock_run.side_effect = subprocess.TimeoutExpired("lpstat", 5) result = _get_printer_info_from_cups() assert result["product"] == "" @patch(f"{MOD}.subprocess.run") def test_oserror(self, mock_run: MagicMock) -> None: mock_run.side_effect = OSError("fail") result = _get_printer_info_from_cups() assert result["product"] == ""