#!/usr/bin/env python3 """ Additional tests for cell_cli.py covering the functions NOT in test_cli_tool.py: - list_peers (error path) - list_nat_rules / add_nat_rule / delete_nat_rule - list_peer_routes / add_peer_route / delete_peer_route - list_firewall_rules / add_firewall_rule / delete_firewall_rule - show_services_status - list_wireguard_peers - show_network_info / show_dns_status / show_ntp_status - main() command routing """ import sys import unittest from pathlib import Path from unittest.mock import patch, MagicMock api_dir = Path(__file__).parent.parent / 'api' sys.path.insert(0, str(api_dir)) from cell_cli import ( list_peers, add_peer, remove_peer, show_config, update_config, list_nat_rules, add_nat_rule, delete_nat_rule, list_peer_routes, add_peer_route, delete_peer_route, list_firewall_rules, add_firewall_rule, delete_firewall_rule, show_services_status, list_wireguard_peers, show_network_info, show_dns_status, show_ntp_status, ) class TestListPeersErrorPath(unittest.TestCase): @patch('builtins.print') @patch('cell_cli.api_request', return_value=None) def test_list_peers_failure_prints_error(self, mock_req, mock_print): list_peers() mock_print.assert_any_call('Failed to fetch peers.') @patch('builtins.print') @patch('cell_cli.api_request', return_value=[]) def test_list_peers_empty_list(self, mock_req, mock_print): list_peers() mock_print.assert_any_call('No peers configured.') @patch('builtins.print') @patch('cell_cli.api_request', return_value=[ {'name': 'alice', 'ip': '10.0.0.2', 'public_key': 'abcdefghijklmnopqrstuvwxyz', 'added_at': '2026-01-01'} ]) def test_list_peers_shows_peer_info(self, mock_req, mock_print): list_peers() self.assertTrue(any('alice' in str(c) for c in mock_print.call_args_list)) class TestNatRules(unittest.TestCase): @patch('builtins.print') @patch('cell_cli.api_request', return_value={'nat_rules': []}) def test_list_nat_rules_empty(self, mock_req, mock_print): list_nat_rules() mock_print.assert_any_call('No NAT rules configured.') @patch('builtins.print') @patch('cell_cli.api_request', return_value={'nat_rules': [ {'id': 1, 'source_network': '10.0.0.0/24', 'target_interface': 'eth0', 'masquerade': True, 'nat_type': 'MASQUERADE', 'protocol': 'ALL', 'external_port': '', 'internal_ip': '', 'internal_port': ''} ]}) def test_list_nat_rules_shows_rules(self, mock_req, mock_print): list_nat_rules() self.assertTrue(any('eth0' in str(c) for c in mock_print.call_args_list)) @patch('builtins.print') @patch('cell_cli.api_request', return_value=None) def test_list_nat_rules_failure(self, mock_req, mock_print): list_nat_rules() mock_print.assert_any_call('Failed to fetch NAT rules.') @patch('builtins.print') @patch('cell_cli.api_request', return_value={'id': 1}) def test_add_nat_rule_success(self, mock_req, mock_print): add_nat_rule('10.0.0.0/24', 'eth0', True, 'MASQUERADE', 'ALL', '', '', '') mock_print.assert_any_call('✅ NAT rule added.') @patch('builtins.print') @patch('cell_cli.api_request', return_value=None) def test_add_nat_rule_failure(self, mock_req, mock_print): add_nat_rule('10.0.0.0/24', 'eth0', False, 'DNAT', 'TCP', '80', '10.0.0.5', '8080') mock_print.assert_any_call('❌ Failed to add NAT rule.') @patch('builtins.print') @patch('cell_cli.api_request', return_value={'ok': True}) def test_delete_nat_rule_success(self, mock_req, mock_print): delete_nat_rule(1) mock_print.assert_any_call('✅ NAT rule deleted.') @patch('builtins.print') @patch('cell_cli.api_request', return_value=None) def test_delete_nat_rule_failure(self, mock_req, mock_print): delete_nat_rule(99) mock_print.assert_any_call('❌ Failed to delete NAT rule.') class TestPeerRoutes(unittest.TestCase): @patch('builtins.print') @patch('cell_cli.api_request', return_value={'peer_routes': []}) def test_list_peer_routes_empty(self, mock_req, mock_print): list_peer_routes() mock_print.assert_any_call('No peer routes configured.') @patch('builtins.print') @patch('cell_cli.api_request', return_value={'peer_routes': [ {'peer_name': 'alice', 'peer_ip': '10.0.0.2', 'allowed_networks': ['192.168.1.0/24'], 'route_type': 'split'} ]}) def test_list_peer_routes_shows_routes(self, mock_req, mock_print): list_peer_routes() self.assertTrue(any('alice' in str(c) for c in mock_print.call_args_list)) @patch('builtins.print') @patch('cell_cli.api_request', return_value=None) def test_list_peer_routes_failure(self, mock_req, mock_print): list_peer_routes() mock_print.assert_any_call('Failed to fetch peer routes.') @patch('builtins.print') @patch('cell_cli.api_request', return_value={'ok': True}) def test_add_peer_route_success(self, mock_req, mock_print): add_peer_route('alice', '10.0.0.2', '192.168.1.0/24', 'split') mock_print.assert_any_call('✅ Peer route added.') @patch('builtins.print') @patch('cell_cli.api_request', return_value=None) def test_add_peer_route_failure(self, mock_req, mock_print): add_peer_route('alice', '10.0.0.2', '192.168.1.0/24', 'split') mock_print.assert_any_call('❌ Failed to add peer route.') @patch('builtins.print') @patch('cell_cli.api_request', return_value={'ok': True}) def test_delete_peer_route_success(self, mock_req, mock_print): delete_peer_route('alice') mock_print.assert_any_call('✅ Peer route deleted.') @patch('builtins.print') @patch('cell_cli.api_request', return_value=None) def test_delete_peer_route_failure(self, mock_req, mock_print): delete_peer_route('alice') mock_print.assert_any_call('❌ Failed to delete peer route.') class TestFirewallRules(unittest.TestCase): @patch('builtins.print') @patch('cell_cli.api_request', return_value={'firewall_rules': []}) def test_list_firewall_rules_empty(self, mock_req, mock_print): list_firewall_rules() mock_print.assert_any_call('No firewall rules configured.') @patch('builtins.print') @patch('cell_cli.api_request', return_value={'firewall_rules': [ {'id': 1, 'rule_type': 'ACCEPT', 'source': '10.0.0.0/24', 'destination': 'any', 'protocol': 'TCP', 'port_range': '80', 'action': 'ACCEPT'} ]}) def test_list_firewall_rules_shows_rules(self, mock_req, mock_print): list_firewall_rules() self.assertTrue(any('ACCEPT' in str(c) for c in mock_print.call_args_list)) @patch('builtins.print') @patch('cell_cli.api_request', return_value=None) def test_list_firewall_rules_failure(self, mock_req, mock_print): list_firewall_rules() mock_print.assert_any_call('Failed to fetch firewall rules.') @patch('builtins.print') @patch('cell_cli.api_request', return_value={'id': 1}) def test_add_firewall_rule_success(self, mock_req, mock_print): add_firewall_rule('ACCEPT', '10.0.0.0/24', 'any', 'ACCEPT', 'TCP', '80') mock_print.assert_any_call('✅ Firewall rule added.') @patch('builtins.print') @patch('cell_cli.api_request', return_value=None) def test_add_firewall_rule_failure(self, mock_req, mock_print): add_firewall_rule('DROP', 'any', 'any', 'DROP', 'ALL', '') mock_print.assert_any_call('❌ Failed to add firewall rule.') @patch('builtins.print') @patch('cell_cli.api_request', return_value={'ok': True}) def test_delete_firewall_rule_success(self, mock_req, mock_print): delete_firewall_rule(1) mock_print.assert_any_call('✅ Firewall rule deleted.') @patch('builtins.print') @patch('cell_cli.api_request', return_value=None) def test_delete_firewall_rule_failure(self, mock_req, mock_print): delete_firewall_rule(99) mock_print.assert_any_call('❌ Failed to delete firewall rule.') class TestShowServicesStatus(unittest.TestCase): @patch('builtins.print') @patch('cell_cli.api_request', return_value={ 'email': {'status': 'online', 'running': True}, 'dns': True }) def test_show_services_status_with_dict_and_bool(self, mock_req, mock_print): show_services_status() self.assertTrue(any('email' in str(c) for c in mock_print.call_args_list)) self.assertTrue(any('dns' in str(c) for c in mock_print.call_args_list)) @patch('builtins.print') @patch('cell_cli.api_request', return_value=None) def test_show_services_status_failure(self, mock_req, mock_print): show_services_status() mock_print.assert_any_call('Failed to fetch service status.') class TestListWireguardPeers(unittest.TestCase): @patch('builtins.print') @patch('cell_cli.api_request', return_value=[ {'name': 'alice', 'public_key': 'pk1', 'ip': '10.0.0.2', 'status': 'active'} ]) def test_list_wireguard_peers_shows_peers(self, mock_req, mock_print): list_wireguard_peers() self.assertTrue(any('alice' in str(c) for c in mock_print.call_args_list)) @patch('builtins.print') @patch('cell_cli.api_request', return_value=None) def test_list_wireguard_peers_failure(self, mock_req, mock_print): list_wireguard_peers() mock_print.assert_any_call('Failed to fetch WireGuard peers.') class TestNetworkDnsNtpStatus(unittest.TestCase): @patch('builtins.print') @patch('cell_cli.api_request', return_value={'gateway': '192.168.1.1', 'subnet': '10.0.0.0/24'}) def test_show_network_info_success(self, mock_req, mock_print): show_network_info() self.assertTrue(any('gateway' in str(c) for c in mock_print.call_args_list)) @patch('builtins.print') @patch('cell_cli.api_request', return_value=None) def test_show_network_info_failure(self, mock_req, mock_print): show_network_info() mock_print.assert_any_call('Failed to fetch network info.') @patch('builtins.print') @patch('cell_cli.api_request', return_value={'running': True, 'port': 53}) def test_show_dns_status_success(self, mock_req, mock_print): show_dns_status() self.assertTrue(any('running' in str(c) for c in mock_print.call_args_list)) @patch('builtins.print') @patch('cell_cli.api_request', return_value=None) def test_show_dns_status_failure(self, mock_req, mock_print): show_dns_status() mock_print.assert_any_call('Failed to fetch DNS status.') @patch('builtins.print') @patch('cell_cli.api_request', return_value={'synced': True, 'server': 'pool.ntp.org'}) def test_show_ntp_status_success(self, mock_req, mock_print): show_ntp_status() self.assertTrue(any('synced' in str(c) for c in mock_print.call_args_list)) @patch('builtins.print') @patch('cell_cli.api_request', return_value=None) def test_show_ntp_status_failure(self, mock_req, mock_print): show_ntp_status() mock_print.assert_any_call('Failed to fetch NTP status.') class TestMainFunction(unittest.TestCase): """Cover main() by patching individual functions and simulating command dispatch.""" def _run_main(self, args): import sys as _sys from cell_cli import main old_argv = _sys.argv _sys.argv = ['cell_cli'] + args try: with patch('builtins.print'): try: main() except SystemExit: pass finally: _sys.argv = old_argv def test_main_status_command(self): with patch('cell_cli.show_status') as mock_fn: self._run_main(['status']) mock_fn.assert_called_once() def test_main_peers_list_command(self): with patch('cell_cli.list_peers') as mock_fn: self._run_main(['peers', 'list']) mock_fn.assert_called_once() def test_main_peers_add_command(self): with patch('cell_cli.add_peer') as mock_fn: self._run_main(['peers', 'add', 'alice', '10.0.0.2', 'pubkey']) mock_fn.assert_called_once_with('alice', '10.0.0.2', 'pubkey') def test_main_peers_remove_command(self): with patch('cell_cli.remove_peer') as mock_fn: self._run_main(['peers', 'remove', 'alice']) mock_fn.assert_called_once_with('alice') def test_main_config_show_command(self): with patch('cell_cli.show_config') as mock_fn: self._run_main(['config', 'show']) mock_fn.assert_called_once() def test_main_config_update_command(self): with patch('cell_cli.update_config') as mock_fn: self._run_main(['config', 'update', 'cell_name', 'mycell']) mock_fn.assert_called_once_with('cell_name', 'mycell') def test_main_routing_nat_list(self): with patch('cell_cli.list_nat_rules') as mock_fn: self._run_main(['routing', 'nat', 'list']) mock_fn.assert_called_once() def test_main_routing_nat_add(self): with patch('cell_cli.add_nat_rule') as mock_fn: self._run_main(['routing', 'nat', 'add', '10.0.0.0/24', 'eth0']) mock_fn.assert_called_once() def test_main_routing_nat_delete(self): with patch('cell_cli.delete_nat_rule') as mock_fn: self._run_main(['routing', 'nat', 'delete', '1']) mock_fn.assert_called_once_with('1') # argparse passes as string def test_main_routing_peers_list(self): with patch('cell_cli.list_peer_routes') as mock_fn: self._run_main(['routing', 'peers', 'list']) mock_fn.assert_called_once() def test_main_routing_peers_add(self): with patch('cell_cli.add_peer_route') as mock_fn: self._run_main(['routing', 'peers', 'add', 'alice', '10.0.0.2', '192.168.1.0/24']) mock_fn.assert_called_once() def test_main_routing_peers_delete(self): with patch('cell_cli.delete_peer_route') as mock_fn: self._run_main(['routing', 'peers', 'delete', 'alice']) mock_fn.assert_called_once_with('alice') def test_main_routing_firewall_list(self): with patch('cell_cli.list_firewall_rules') as mock_fn: self._run_main(['routing', 'firewall', 'list']) mock_fn.assert_called_once() def test_main_routing_firewall_add(self): with patch('cell_cli.add_firewall_rule') as mock_fn: self._run_main(['routing', 'firewall', 'add', 'ACCEPT', '10.0.0.0/24', 'any', 'ACCEPT']) mock_fn.assert_called_once() def test_main_routing_firewall_delete(self): with patch('cell_cli.delete_firewall_rule') as mock_fn: self._run_main(['routing', 'firewall', 'delete', '1']) mock_fn.assert_called_once_with('1') def test_main_services_status_command(self): with patch('cell_cli.show_services_status') as mock_fn: self._run_main(['services-status']) mock_fn.assert_called_once() def test_main_wireguard_list_command(self): with patch('cell_cli.list_wireguard_peers') as mock_fn: self._run_main(['wireguard-peers']) mock_fn.assert_called_once() def test_main_network_info_command(self): with patch('cell_cli.show_network_info') as mock_fn: self._run_main(['network-info']) mock_fn.assert_called_once() def test_main_dns_status_command(self): with patch('cell_cli.show_dns_status') as mock_fn: self._run_main(['dns-status']) mock_fn.assert_called_once() def test_main_ntp_status_command(self): with patch('cell_cli.show_ntp_status') as mock_fn: self._run_main(['ntp-status']) mock_fn.assert_called_once() if __name__ == '__main__': unittest.main()