452 lines
18 KiB
Python
452 lines
18 KiB
Python
"""Tests for the CLI module."""
|
|
|
|
import pytest
|
|
from unittest.mock import patch, AsyncMock, MagicMock
|
|
from click.testing import CliRunner
|
|
from vultr_dns_mcp.cli import cli, main
|
|
|
|
|
|
@pytest.fixture
|
|
def cli_runner():
|
|
"""Create a CLI test runner."""
|
|
return CliRunner()
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_client_for_cli():
|
|
"""Create a mock VultrDNSClient for CLI tests."""
|
|
mock_client = AsyncMock()
|
|
|
|
# Configure mock responses
|
|
mock_client.domains.return_value = [
|
|
{"domain": "example.com", "date_created": "2024-01-01"},
|
|
{"domain": "test.com", "date_created": "2024-01-02"}
|
|
]
|
|
|
|
mock_client.get_domain_summary.return_value = {
|
|
"domain": "example.com",
|
|
"total_records": 5,
|
|
"record_types": {"A": 2, "MX": 1, "TXT": 2},
|
|
"configuration": {
|
|
"has_root_record": True,
|
|
"has_www_subdomain": True,
|
|
"has_email_setup": True
|
|
}
|
|
}
|
|
|
|
mock_client.records.return_value = [
|
|
{"id": "rec1", "type": "A", "name": "@", "data": "192.168.1.100", "ttl": 300},
|
|
{"id": "rec2", "type": "A", "name": "www", "data": "192.168.1.100", "ttl": 300}
|
|
]
|
|
|
|
mock_client.add_domain.return_value = {"domain": "newdomain.com"}
|
|
mock_client.add_record.return_value = {"id": "new-rec", "type": "A", "name": "www", "data": "192.168.1.100"}
|
|
mock_client.remove_record.return_value = True
|
|
|
|
mock_client.setup_basic_website.return_value = {
|
|
"domain": "example.com",
|
|
"created_records": ["A record for root domain", "A record for www subdomain"],
|
|
"errors": []
|
|
}
|
|
|
|
mock_client.setup_email.return_value = {
|
|
"domain": "example.com",
|
|
"created_records": ["MX record for mail.example.com"],
|
|
"errors": []
|
|
}
|
|
|
|
return mock_client
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestCLIBasics:
|
|
"""Test basic CLI functionality."""
|
|
|
|
def test_cli_help(self, cli_runner):
|
|
"""Test CLI help output."""
|
|
result = cli_runner.invoke(cli, ['--help'])
|
|
assert result.exit_code == 0
|
|
assert "Vultr DNS MCP" in result.output
|
|
|
|
def test_cli_version(self, cli_runner):
|
|
"""Test CLI version output."""
|
|
result = cli_runner.invoke(cli, ['--version'])
|
|
assert result.exit_code == 0
|
|
|
|
def test_cli_without_api_key(self, cli_runner):
|
|
"""Test CLI behavior without API key."""
|
|
with patch.dict('os.environ', {}, clear=True):
|
|
result = cli_runner.invoke(cli, ['domains', 'list'])
|
|
assert result.exit_code == 1
|
|
assert "VULTR_API_KEY is required" in result.output
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestServerCommand:
|
|
"""Test the server command."""
|
|
|
|
def test_server_command_without_api_key(self, cli_runner):
|
|
"""Test server command without API key."""
|
|
with patch.dict('os.environ', {}, clear=True):
|
|
result = cli_runner.invoke(cli, ['server'])
|
|
assert result.exit_code == 1
|
|
assert "VULTR_API_KEY is required" in result.output
|
|
|
|
@patch('vultr_dns_mcp.cli.run_server')
|
|
def test_server_command_with_api_key(self, mock_run_server, cli_runner):
|
|
"""Test server command with API key."""
|
|
with patch.dict('os.environ', {'VULTR_API_KEY': 'test-key'}):
|
|
# Mock run_server to avoid actually starting the server
|
|
mock_run_server.side_effect = KeyboardInterrupt()
|
|
|
|
result = cli_runner.invoke(cli, ['server'])
|
|
assert "Starting Vultr DNS MCP Server" in result.output
|
|
mock_run_server.assert_called_once_with('test-key')
|
|
|
|
@patch('vultr_dns_mcp.cli.run_server')
|
|
def test_server_command_with_error(self, mock_run_server, cli_runner):
|
|
"""Test server command with error."""
|
|
with patch.dict('os.environ', {'VULTR_API_KEY': 'test-key'}):
|
|
mock_run_server.side_effect = Exception("Server error")
|
|
|
|
result = cli_runner.invoke(cli, ['server'])
|
|
assert result.exit_code == 1
|
|
assert "Server error" in result.output
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestDomainsCommands:
|
|
"""Test domain management commands."""
|
|
|
|
@patch('vultr_dns_mcp.cli.VultrDNSClient')
|
|
def test_list_domains(self, mock_client_class, cli_runner, mock_client_for_cli):
|
|
"""Test domains list command."""
|
|
mock_client_class.return_value = mock_client_for_cli
|
|
|
|
with patch.dict('os.environ', {'VULTR_API_KEY': 'test-key'}):
|
|
result = cli_runner.invoke(cli, ['domains', 'list'])
|
|
|
|
assert result.exit_code == 0
|
|
assert "example.com" in result.output
|
|
assert "test.com" in result.output
|
|
mock_client_for_cli.domains.assert_called_once()
|
|
|
|
@patch('vultr_dns_mcp.cli.VultrDNSClient')
|
|
def test_list_domains_empty(self, mock_client_class, cli_runner):
|
|
"""Test domains list command with no domains."""
|
|
mock_client = AsyncMock()
|
|
mock_client.domains.return_value = []
|
|
mock_client_class.return_value = mock_client
|
|
|
|
with patch.dict('os.environ', {'VULTR_API_KEY': 'test-key'}):
|
|
result = cli_runner.invoke(cli, ['domains', 'list'])
|
|
|
|
assert result.exit_code == 0
|
|
assert "No domains found" in result.output
|
|
|
|
@patch('vultr_dns_mcp.cli.VultrDNSClient')
|
|
def test_domain_info(self, mock_client_class, cli_runner, mock_client_for_cli):
|
|
"""Test domains info command."""
|
|
mock_client_class.return_value = mock_client_for_cli
|
|
|
|
with patch.dict('os.environ', {'VULTR_API_KEY': 'test-key'}):
|
|
result = cli_runner.invoke(cli, ['domains', 'info', 'example.com'])
|
|
|
|
assert result.exit_code == 0
|
|
assert "example.com" in result.output
|
|
assert "Total Records: 5" in result.output
|
|
mock_client_for_cli.get_domain_summary.assert_called_once_with('example.com')
|
|
|
|
@patch('vultr_dns_mcp.cli.VultrDNSClient')
|
|
def test_domain_info_error(self, mock_client_class, cli_runner):
|
|
"""Test domains info command with error."""
|
|
mock_client = AsyncMock()
|
|
mock_client.get_domain_summary.return_value = {"error": "Domain not found"}
|
|
mock_client_class.return_value = mock_client
|
|
|
|
with patch.dict('os.environ', {'VULTR_API_KEY': 'test-key'}):
|
|
result = cli_runner.invoke(cli, ['domains', 'info', 'nonexistent.com'])
|
|
|
|
assert result.exit_code == 1
|
|
assert "Domain not found" in result.output
|
|
|
|
@patch('vultr_dns_mcp.cli.VultrDNSClient')
|
|
def test_create_domain(self, mock_client_class, cli_runner, mock_client_for_cli):
|
|
"""Test domains create command."""
|
|
mock_client_class.return_value = mock_client_for_cli
|
|
|
|
with patch.dict('os.environ', {'VULTR_API_KEY': 'test-key'}):
|
|
result = cli_runner.invoke(cli, ['domains', 'create', 'newdomain.com', '192.168.1.100'])
|
|
|
|
assert result.exit_code == 0
|
|
assert "Created domain newdomain.com" in result.output
|
|
mock_client_for_cli.add_domain.assert_called_once_with('newdomain.com', '192.168.1.100')
|
|
|
|
@patch('vultr_dns_mcp.cli.VultrDNSClient')
|
|
def test_create_domain_error(self, mock_client_class, cli_runner):
|
|
"""Test domains create command with error."""
|
|
mock_client = AsyncMock()
|
|
mock_client.add_domain.return_value = {"error": "Domain already exists"}
|
|
mock_client_class.return_value = mock_client
|
|
|
|
with patch.dict('os.environ', {'VULTR_API_KEY': 'test-key'}):
|
|
result = cli_runner.invoke(cli, ['domains', 'create', 'existing.com', '192.168.1.100'])
|
|
|
|
assert result.exit_code == 1
|
|
assert "Domain already exists" in result.output
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestRecordsCommands:
|
|
"""Test DNS records commands."""
|
|
|
|
@patch('vultr_dns_mcp.cli.VultrDNSClient')
|
|
def test_list_records(self, mock_client_class, cli_runner, mock_client_for_cli):
|
|
"""Test records list command."""
|
|
mock_client_class.return_value = mock_client_for_cli
|
|
|
|
with patch.dict('os.environ', {'VULTR_API_KEY': 'test-key'}):
|
|
result = cli_runner.invoke(cli, ['records', 'list', 'example.com'])
|
|
|
|
assert result.exit_code == 0
|
|
assert "example.com" in result.output
|
|
assert "rec1" in result.output
|
|
mock_client_for_cli.records.assert_called_once_with('example.com')
|
|
|
|
@patch('vultr_dns_mcp.cli.VultrDNSClient')
|
|
def test_list_records_filtered(self, mock_client_class, cli_runner, mock_client_for_cli):
|
|
"""Test records list command with type filter."""
|
|
mock_client_class.return_value = mock_client_for_cli
|
|
|
|
with patch.dict('os.environ', {'VULTR_API_KEY': 'test-key'}):
|
|
result = cli_runner.invoke(cli, ['records', 'list', 'example.com', '--type', 'A'])
|
|
|
|
assert result.exit_code == 0
|
|
mock_client_for_cli.find_records_by_type.assert_called_once_with('example.com', 'A')
|
|
|
|
@patch('vultr_dns_mcp.cli.VultrDNSClient')
|
|
def test_list_records_empty(self, mock_client_class, cli_runner):
|
|
"""Test records list command with no records."""
|
|
mock_client = AsyncMock()
|
|
mock_client.records.return_value = []
|
|
mock_client_class.return_value = mock_client
|
|
|
|
with patch.dict('os.environ', {'VULTR_API_KEY': 'test-key'}):
|
|
result = cli_runner.invoke(cli, ['records', 'list', 'example.com'])
|
|
|
|
assert result.exit_code == 0
|
|
assert "No records found" in result.output
|
|
|
|
@patch('vultr_dns_mcp.cli.VultrDNSClient')
|
|
def test_add_record(self, mock_client_class, cli_runner, mock_client_for_cli):
|
|
"""Test records add command."""
|
|
mock_client_class.return_value = mock_client_for_cli
|
|
|
|
with patch.dict('os.environ', {'VULTR_API_KEY': 'test-key'}):
|
|
result = cli_runner.invoke(cli, [
|
|
'records', 'add', 'example.com', 'A', 'www', '192.168.1.100'
|
|
])
|
|
|
|
assert result.exit_code == 0
|
|
assert "Created A record" in result.output
|
|
mock_client_for_cli.add_record.assert_called_once_with(
|
|
'example.com', 'A', 'www', '192.168.1.100', None, None
|
|
)
|
|
|
|
@patch('vultr_dns_mcp.cli.VultrDNSClient')
|
|
def test_add_record_with_ttl_and_priority(self, mock_client_class, cli_runner, mock_client_for_cli):
|
|
"""Test records add command with TTL and priority."""
|
|
mock_client_class.return_value = mock_client_for_cli
|
|
|
|
with patch.dict('os.environ', {'VULTR_API_KEY': 'test-key'}):
|
|
result = cli_runner.invoke(cli, [
|
|
'records', 'add', 'example.com', 'MX', '@', 'mail.example.com',
|
|
'--ttl', '600', '--priority', '10'
|
|
])
|
|
|
|
assert result.exit_code == 0
|
|
mock_client_for_cli.add_record.assert_called_once_with(
|
|
'example.com', 'MX', '@', 'mail.example.com', 600, 10
|
|
)
|
|
|
|
@patch('vultr_dns_mcp.cli.VultrDNSClient')
|
|
def test_add_record_error(self, mock_client_class, cli_runner):
|
|
"""Test records add command with error."""
|
|
mock_client = AsyncMock()
|
|
mock_client.add_record.return_value = {"error": "Invalid record"}
|
|
mock_client_class.return_value = mock_client
|
|
|
|
with patch.dict('os.environ', {'VULTR_API_KEY': 'test-key'}):
|
|
result = cli_runner.invoke(cli, [
|
|
'records', 'add', 'example.com', 'A', 'www', 'invalid-ip'
|
|
])
|
|
|
|
assert result.exit_code == 1
|
|
assert "Invalid record" in result.output
|
|
|
|
@patch('vultr_dns_mcp.cli.VultrDNSClient')
|
|
def test_delete_record(self, mock_client_class, cli_runner, mock_client_for_cli):
|
|
"""Test records delete command."""
|
|
mock_client_class.return_value = mock_client_for_cli
|
|
|
|
with patch.dict('os.environ', {'VULTR_API_KEY': 'test-key'}):
|
|
result = cli_runner.invoke(cli, [
|
|
'records', 'delete', 'example.com', 'record-123'
|
|
], input='y\n') # Confirm deletion
|
|
|
|
assert result.exit_code == 0
|
|
assert "Deleted record record-123" in result.output
|
|
mock_client_for_cli.remove_record.assert_called_once_with('example.com', 'record-123')
|
|
|
|
@patch('vultr_dns_mcp.cli.VultrDNSClient')
|
|
def test_delete_record_failure(self, mock_client_class, cli_runner):
|
|
"""Test records delete command failure."""
|
|
mock_client = AsyncMock()
|
|
mock_client.remove_record.return_value = False
|
|
mock_client_class.return_value = mock_client
|
|
|
|
with patch.dict('os.environ', {'VULTR_API_KEY': 'test-key'}):
|
|
result = cli_runner.invoke(cli, [
|
|
'records', 'delete', 'example.com', 'record-123'
|
|
], input='y\n')
|
|
|
|
assert result.exit_code == 1
|
|
assert "Failed to delete" in result.output
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestSetupCommands:
|
|
"""Test setup utility commands."""
|
|
|
|
@patch('vultr_dns_mcp.cli.VultrDNSClient')
|
|
def test_setup_website(self, mock_client_class, cli_runner, mock_client_for_cli):
|
|
"""Test setup-website command."""
|
|
mock_client_class.return_value = mock_client_for_cli
|
|
|
|
with patch.dict('os.environ', {'VULTR_API_KEY': 'test-key'}):
|
|
result = cli_runner.invoke(cli, [
|
|
'setup-website', 'example.com', '192.168.1.100'
|
|
])
|
|
|
|
assert result.exit_code == 0
|
|
assert "Setting up website" in result.output
|
|
assert "Website setup complete" in result.output
|
|
mock_client_for_cli.setup_basic_website.assert_called_once_with(
|
|
'example.com', '192.168.1.100', True, None
|
|
)
|
|
|
|
@patch('vultr_dns_mcp.cli.VultrDNSClient')
|
|
def test_setup_website_no_www(self, mock_client_class, cli_runner, mock_client_for_cli):
|
|
"""Test setup-website command without www."""
|
|
mock_client_class.return_value = mock_client_for_cli
|
|
|
|
with patch.dict('os.environ', {'VULTR_API_KEY': 'test-key'}):
|
|
result = cli_runner.invoke(cli, [
|
|
'setup-website', 'example.com', '192.168.1.100', '--no-www'
|
|
])
|
|
|
|
assert result.exit_code == 0
|
|
mock_client_for_cli.setup_basic_website.assert_called_once_with(
|
|
'example.com', '192.168.1.100', False, None
|
|
)
|
|
|
|
@patch('vultr_dns_mcp.cli.VultrDNSClient')
|
|
def test_setup_website_with_ttl(self, mock_client_class, cli_runner, mock_client_for_cli):
|
|
"""Test setup-website command with custom TTL."""
|
|
mock_client_class.return_value = mock_client_for_cli
|
|
|
|
with patch.dict('os.environ', {'VULTR_API_KEY': 'test-key'}):
|
|
result = cli_runner.invoke(cli, [
|
|
'setup-website', 'example.com', '192.168.1.100', '--ttl', '600'
|
|
])
|
|
|
|
assert result.exit_code == 0
|
|
mock_client_for_cli.setup_basic_website.assert_called_once_with(
|
|
'example.com', '192.168.1.100', True, 600
|
|
)
|
|
|
|
@patch('vultr_dns_mcp.cli.VultrDNSClient')
|
|
def test_setup_website_with_errors(self, mock_client_class, cli_runner):
|
|
"""Test setup-website command with errors."""
|
|
mock_client = AsyncMock()
|
|
mock_client.setup_basic_website.return_value = {
|
|
"domain": "example.com",
|
|
"created_records": ["A record for root domain"],
|
|
"errors": ["Failed to create www record"]
|
|
}
|
|
mock_client_class.return_value = mock_client
|
|
|
|
with patch.dict('os.environ', {'VULTR_API_KEY': 'test-key'}):
|
|
result = cli_runner.invoke(cli, [
|
|
'setup-website', 'example.com', '192.168.1.100'
|
|
])
|
|
|
|
assert result.exit_code == 0
|
|
assert "Setup completed with some errors" in result.output
|
|
|
|
@patch('vultr_dns_mcp.cli.VultrDNSClient')
|
|
def test_setup_email(self, mock_client_class, cli_runner, mock_client_for_cli):
|
|
"""Test setup-email command."""
|
|
mock_client_class.return_value = mock_client_for_cli
|
|
|
|
with patch.dict('os.environ', {'VULTR_API_KEY': 'test-key'}):
|
|
result = cli_runner.invoke(cli, [
|
|
'setup-email', 'example.com', 'mail.example.com'
|
|
])
|
|
|
|
assert result.exit_code == 0
|
|
assert "Setting up email" in result.output
|
|
assert "Email setup complete" in result.output
|
|
mock_client_for_cli.setup_email.assert_called_once_with(
|
|
'example.com', 'mail.example.com', 10, None
|
|
)
|
|
|
|
@patch('vultr_dns_mcp.cli.VultrDNSClient')
|
|
def test_setup_email_custom_priority(self, mock_client_class, cli_runner, mock_client_for_cli):
|
|
"""Test setup-email command with custom priority."""
|
|
mock_client_class.return_value = mock_client_for_cli
|
|
|
|
with patch.dict('os.environ', {'VULTR_API_KEY': 'test-key'}):
|
|
result = cli_runner.invoke(cli, [
|
|
'setup-email', 'example.com', 'mail.example.com', '--priority', '5'
|
|
])
|
|
|
|
assert result.exit_code == 0
|
|
mock_client_for_cli.setup_email.assert_called_once_with(
|
|
'example.com', 'mail.example.com', 5, None
|
|
)
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestCLIErrorHandling:
|
|
"""Test CLI error handling."""
|
|
|
|
@patch('vultr_dns_mcp.cli.VultrDNSClient')
|
|
def test_api_exception_handling(self, mock_client_class, cli_runner):
|
|
"""Test CLI handling of API exceptions."""
|
|
mock_client = AsyncMock()
|
|
mock_client.domains.side_effect = Exception("Network error")
|
|
mock_client_class.return_value = mock_client
|
|
|
|
with patch.dict('os.environ', {'VULTR_API_KEY': 'test-key'}):
|
|
result = cli_runner.invoke(cli, ['domains', 'list'])
|
|
|
|
assert result.exit_code == 1
|
|
assert "Network error" in result.output
|
|
|
|
def test_missing_arguments(self, cli_runner):
|
|
"""Test CLI behavior with missing arguments."""
|
|
with patch.dict('os.environ', {'VULTR_API_KEY': 'test-key'}):
|
|
result = cli_runner.invoke(cli, ['domains', 'info'])
|
|
|
|
assert result.exit_code == 2 # Click argument error
|
|
|
|
def test_invalid_command(self, cli_runner):
|
|
"""Test CLI behavior with invalid command."""
|
|
result = cli_runner.invoke(cli, ['invalid-command'])
|
|
assert result.exit_code == 2
|
|
|
|
|
|
if __name__ == "__main__":
|
|
pytest.main([__file__])
|