412 lines
15 KiB
Python
412 lines
15 KiB
Python
"""
|
|
Unit tests for deployment CLI commands
|
|
"""
|
|
|
|
import pytest
|
|
from click.testing import CliRunner
|
|
from unittest.mock import Mock, patch, MagicMock
|
|
from src.cli.commands import app
|
|
from src.database.models import User, SiteDeployment
|
|
from src.deployment.bunnynet import (
|
|
StorageZoneResult,
|
|
PullZoneResult,
|
|
BunnyNetAuthError,
|
|
BunnyNetResourceConflictError,
|
|
BunnyNetAPIError
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def cli_runner():
|
|
"""Fixture to create a Click CLI test runner"""
|
|
return CliRunner()
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_admin_user():
|
|
"""Fixture for a mock admin user"""
|
|
user = Mock(spec=User)
|
|
user.id = 1
|
|
user.username = "admin"
|
|
user.role = "Admin"
|
|
user.is_admin.return_value = True
|
|
return user
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_site_deployment():
|
|
"""Fixture for a mock site deployment"""
|
|
site = Mock(spec=SiteDeployment)
|
|
site.id = 1
|
|
site.site_name = "Test Site"
|
|
site.custom_hostname = "www.example.com"
|
|
site.storage_zone_id = 12345
|
|
site.storage_zone_name = "test-storage"
|
|
site.storage_zone_password = "test-pass"
|
|
site.storage_zone_region = "DE"
|
|
site.pull_zone_id = 67890
|
|
site.pull_zone_bcdn_hostname = "test-cdn.b-cdn.net"
|
|
site.created_at = Mock()
|
|
site.created_at.strftime.return_value = "2024-01-01 00:00:00"
|
|
site.updated_at = Mock()
|
|
site.updated_at.strftime.return_value = "2024-01-01 00:00:00"
|
|
return site
|
|
|
|
|
|
class TestProvisionSiteCommand:
|
|
"""Tests for provision-site CLI command"""
|
|
|
|
@patch('src.cli.commands.authenticate_admin')
|
|
@patch('src.cli.commands.get_bunny_account_api_key')
|
|
@patch('src.cli.commands.BunnyNetClient')
|
|
@patch('src.cli.commands.db_manager')
|
|
def test_provision_site_success(
|
|
self, mock_db, mock_client_class, mock_get_key, mock_auth, cli_runner, mock_admin_user
|
|
):
|
|
"""Test successful site provisioning"""
|
|
mock_auth.return_value = mock_admin_user
|
|
mock_get_key.return_value = "test_api_key"
|
|
|
|
mock_client = Mock()
|
|
mock_client_class.return_value = mock_client
|
|
|
|
mock_client.create_storage_zone.return_value = StorageZoneResult(
|
|
id=12345, name="test-storage", password="test-pass", region="DE"
|
|
)
|
|
mock_client.create_pull_zone.return_value = PullZoneResult(
|
|
id=67890, name="test-cdn", hostname="test-cdn.b-cdn.net"
|
|
)
|
|
mock_client.add_custom_hostname.return_value = True
|
|
|
|
mock_session = Mock()
|
|
mock_db.get_session.return_value = mock_session
|
|
|
|
mock_repo = Mock()
|
|
mock_repo.exists.return_value = False
|
|
mock_repo.create.return_value = Mock()
|
|
|
|
with patch('src.cli.commands.SiteDeploymentRepository', return_value=mock_repo):
|
|
result = cli_runner.invoke(app, [
|
|
'provision-site',
|
|
'--name', 'Test Site',
|
|
'--domain', 'www.example.com',
|
|
'--storage-name', 'test-storage',
|
|
'--region', 'DE',
|
|
'--admin-user', 'admin',
|
|
'--admin-password', 'password'
|
|
])
|
|
|
|
assert result.exit_code == 0
|
|
assert "Site provisioned successfully!" in result.output
|
|
assert "MANUAL DNS CONFIGURATION REQUIRED" in result.output
|
|
mock_client.create_storage_zone.assert_called_once_with("test-storage", "DE")
|
|
mock_client.create_pull_zone.assert_called_once()
|
|
mock_client.add_custom_hostname.assert_called_once()
|
|
|
|
@patch('src.cli.commands.authenticate_admin')
|
|
def test_provision_site_auth_failure(self, mock_auth, cli_runner):
|
|
"""Test provision-site with authentication failure"""
|
|
mock_auth.return_value = None
|
|
|
|
result = cli_runner.invoke(app, [
|
|
'provision-site',
|
|
'--name', 'Test Site',
|
|
'--domain', 'www.example.com',
|
|
'--storage-name', 'test-storage',
|
|
'--region', 'DE',
|
|
'--admin-user', 'admin',
|
|
'--admin-password', 'wrong'
|
|
])
|
|
|
|
assert result.exit_code != 0
|
|
assert "Authentication failed" in result.output
|
|
|
|
@patch('src.cli.commands.authenticate_admin')
|
|
@patch('src.cli.commands.get_bunny_account_api_key')
|
|
def test_provision_site_missing_api_key(self, mock_get_key, mock_auth, cli_runner, mock_admin_user):
|
|
"""Test provision-site with missing API key"""
|
|
mock_auth.return_value = mock_admin_user
|
|
mock_get_key.side_effect = ValueError("BUNNY_ACCOUNT_API_KEY environment variable is required")
|
|
|
|
result = cli_runner.invoke(app, [
|
|
'provision-site',
|
|
'--name', 'Test Site',
|
|
'--domain', 'www.example.com',
|
|
'--storage-name', 'test-storage',
|
|
'--region', 'DE',
|
|
'--admin-user', 'admin',
|
|
'--admin-password', 'password'
|
|
])
|
|
|
|
assert result.exit_code != 0
|
|
assert "BUNNY_ACCOUNT_API_KEY" in result.output
|
|
|
|
@patch('src.cli.commands.authenticate_admin')
|
|
@patch('src.cli.commands.get_bunny_account_api_key')
|
|
@patch('src.cli.commands.BunnyNetClient')
|
|
@patch('src.cli.commands.db_manager')
|
|
def test_provision_site_domain_exists(
|
|
self, mock_db, mock_client_class, mock_get_key, mock_auth, cli_runner, mock_admin_user
|
|
):
|
|
"""Test provision-site with existing domain"""
|
|
mock_auth.return_value = mock_admin_user
|
|
mock_get_key.return_value = "test_api_key"
|
|
|
|
mock_session = Mock()
|
|
mock_db.get_session.return_value = mock_session
|
|
|
|
mock_repo = Mock()
|
|
mock_repo.exists.return_value = True
|
|
|
|
with patch('src.cli.commands.SiteDeploymentRepository', return_value=mock_repo):
|
|
result = cli_runner.invoke(app, [
|
|
'provision-site',
|
|
'--name', 'Test Site',
|
|
'--domain', 'www.example.com',
|
|
'--storage-name', 'test-storage',
|
|
'--region', 'DE',
|
|
'--admin-user', 'admin',
|
|
'--admin-password', 'password'
|
|
])
|
|
|
|
assert result.exit_code != 0
|
|
assert "already exists" in result.output
|
|
|
|
|
|
class TestAttachDomainCommand:
|
|
"""Tests for attach-domain CLI command"""
|
|
|
|
@patch('src.cli.commands.authenticate_admin')
|
|
@patch('src.cli.commands.get_bunny_account_api_key')
|
|
@patch('src.cli.commands.BunnyNetClient')
|
|
@patch('src.cli.commands.db_manager')
|
|
def test_attach_domain_success(
|
|
self, mock_db, mock_client_class, mock_get_key, mock_auth, cli_runner, mock_admin_user
|
|
):
|
|
"""Test successful domain attachment"""
|
|
mock_auth.return_value = mock_admin_user
|
|
mock_get_key.return_value = "test_api_key"
|
|
|
|
mock_client = Mock()
|
|
mock_client_class.return_value = mock_client
|
|
|
|
mock_client.find_storage_zone_by_name.return_value = StorageZoneResult(
|
|
id=12345, name="existing-storage", password="test-pass", region="DE"
|
|
)
|
|
mock_client.create_pull_zone.return_value = PullZoneResult(
|
|
id=67890, name="test-cdn", hostname="test-cdn.b-cdn.net"
|
|
)
|
|
mock_client.add_custom_hostname.return_value = True
|
|
|
|
mock_session = Mock()
|
|
mock_db.get_session.return_value = mock_session
|
|
|
|
mock_repo = Mock()
|
|
mock_repo.exists.return_value = False
|
|
mock_repo.create.return_value = Mock()
|
|
|
|
with patch('src.cli.commands.SiteDeploymentRepository', return_value=mock_repo):
|
|
result = cli_runner.invoke(app, [
|
|
'attach-domain',
|
|
'--name', 'Test Site',
|
|
'--domain', 'www.example.com',
|
|
'--storage-name', 'existing-storage',
|
|
'--admin-user', 'admin',
|
|
'--admin-password', 'password'
|
|
])
|
|
|
|
assert result.exit_code == 0
|
|
assert "Domain attached successfully!" in result.output
|
|
assert "MANUAL DNS CONFIGURATION REQUIRED" in result.output
|
|
mock_client.find_storage_zone_by_name.assert_called_once_with("existing-storage")
|
|
|
|
@patch('src.cli.commands.authenticate_admin')
|
|
@patch('src.cli.commands.get_bunny_account_api_key')
|
|
@patch('src.cli.commands.BunnyNetClient')
|
|
@patch('src.cli.commands.db_manager')
|
|
def test_attach_domain_storage_not_found(
|
|
self, mock_db, mock_client_class, mock_get_key, mock_auth, cli_runner, mock_admin_user
|
|
):
|
|
"""Test attach-domain with non-existent storage zone"""
|
|
mock_auth.return_value = mock_admin_user
|
|
mock_get_key.return_value = "test_api_key"
|
|
|
|
mock_client = Mock()
|
|
mock_client_class.return_value = mock_client
|
|
mock_client.find_storage_zone_by_name.return_value = None
|
|
|
|
mock_session = Mock()
|
|
mock_db.get_session.return_value = mock_session
|
|
|
|
mock_repo = Mock()
|
|
mock_repo.exists.return_value = False
|
|
|
|
with patch('src.cli.commands.SiteDeploymentRepository', return_value=mock_repo):
|
|
result = cli_runner.invoke(app, [
|
|
'attach-domain',
|
|
'--name', 'Test Site',
|
|
'--domain', 'www.example.com',
|
|
'--storage-name', 'nonexistent',
|
|
'--admin-user', 'admin',
|
|
'--admin-password', 'password'
|
|
])
|
|
|
|
assert result.exit_code != 0
|
|
assert "not found" in result.output
|
|
|
|
|
|
class TestListSitesCommand:
|
|
"""Tests for list-sites CLI command"""
|
|
|
|
@patch('src.cli.commands.authenticate_admin')
|
|
@patch('src.cli.commands.db_manager')
|
|
def test_list_sites_success(self, mock_db, mock_auth, cli_runner, mock_admin_user, mock_site_deployment):
|
|
"""Test successful sites listing"""
|
|
mock_auth.return_value = mock_admin_user
|
|
|
|
mock_session = Mock()
|
|
mock_db.get_session.return_value = mock_session
|
|
|
|
mock_repo = Mock()
|
|
mock_repo.get_all.return_value = [mock_site_deployment]
|
|
|
|
with patch('src.cli.commands.SiteDeploymentRepository', return_value=mock_repo):
|
|
result = cli_runner.invoke(app, [
|
|
'list-sites',
|
|
'--admin-user', 'admin',
|
|
'--admin-password', 'password'
|
|
])
|
|
|
|
assert result.exit_code == 0
|
|
assert "Total sites: 1" in result.output
|
|
assert "www.example.com" in result.output
|
|
|
|
@patch('src.cli.commands.authenticate_admin')
|
|
@patch('src.cli.commands.db_manager')
|
|
def test_list_sites_empty(self, mock_db, mock_auth, cli_runner, mock_admin_user):
|
|
"""Test listing sites when none exist"""
|
|
mock_auth.return_value = mock_admin_user
|
|
|
|
mock_session = Mock()
|
|
mock_db.get_session.return_value = mock_session
|
|
|
|
mock_repo = Mock()
|
|
mock_repo.get_all.return_value = []
|
|
|
|
with patch('src.cli.commands.SiteDeploymentRepository', return_value=mock_repo):
|
|
result = cli_runner.invoke(app, [
|
|
'list-sites',
|
|
'--admin-user', 'admin',
|
|
'--admin-password', 'password'
|
|
])
|
|
|
|
assert result.exit_code == 0
|
|
assert "No site deployments found" in result.output
|
|
|
|
|
|
class TestGetSiteCommand:
|
|
"""Tests for get-site CLI command"""
|
|
|
|
@patch('src.cli.commands.authenticate_admin')
|
|
@patch('src.cli.commands.db_manager')
|
|
def test_get_site_success(self, mock_db, mock_auth, cli_runner, mock_admin_user, mock_site_deployment):
|
|
"""Test successful site details retrieval"""
|
|
mock_auth.return_value = mock_admin_user
|
|
|
|
mock_session = Mock()
|
|
mock_db.get_session.return_value = mock_session
|
|
|
|
mock_repo = Mock()
|
|
mock_repo.get_by_hostname.return_value = mock_site_deployment
|
|
|
|
with patch('src.cli.commands.SiteDeploymentRepository', return_value=mock_repo):
|
|
result = cli_runner.invoke(app, [
|
|
'get-site',
|
|
'--domain', 'www.example.com',
|
|
'--admin-user', 'admin',
|
|
'--admin-password', 'password'
|
|
])
|
|
|
|
assert result.exit_code == 0
|
|
assert "Site Deployment Details" in result.output
|
|
assert "www.example.com" in result.output
|
|
assert "test-storage" in result.output
|
|
|
|
@patch('src.cli.commands.authenticate_admin')
|
|
@patch('src.cli.commands.db_manager')
|
|
def test_get_site_not_found(self, mock_db, mock_auth, cli_runner, mock_admin_user):
|
|
"""Test get-site with non-existent domain"""
|
|
mock_auth.return_value = mock_admin_user
|
|
|
|
mock_session = Mock()
|
|
mock_db.get_session.return_value = mock_session
|
|
|
|
mock_repo = Mock()
|
|
mock_repo.get_by_hostname.return_value = None
|
|
|
|
with patch('src.cli.commands.SiteDeploymentRepository', return_value=mock_repo):
|
|
result = cli_runner.invoke(app, [
|
|
'get-site',
|
|
'--domain', 'nonexistent.com',
|
|
'--admin-user', 'admin',
|
|
'--admin-password', 'password'
|
|
])
|
|
|
|
assert result.exit_code != 0
|
|
assert "not found" in result.output
|
|
|
|
|
|
class TestRemoveSiteCommand:
|
|
"""Tests for remove-site CLI command"""
|
|
|
|
@patch('src.cli.commands.authenticate_admin')
|
|
@patch('src.cli.commands.db_manager')
|
|
def test_remove_site_success(self, mock_db, mock_auth, cli_runner, mock_admin_user, mock_site_deployment):
|
|
"""Test successful site removal"""
|
|
mock_auth.return_value = mock_admin_user
|
|
|
|
mock_session = Mock()
|
|
mock_db.get_session.return_value = mock_session
|
|
|
|
mock_repo = Mock()
|
|
mock_repo.get_by_hostname.return_value = mock_site_deployment
|
|
mock_repo.delete.return_value = True
|
|
|
|
with patch('src.cli.commands.SiteDeploymentRepository', return_value=mock_repo):
|
|
result = cli_runner.invoke(app, [
|
|
'remove-site',
|
|
'--domain', 'www.example.com',
|
|
'--admin-user', 'admin',
|
|
'--admin-password', 'password',
|
|
'--yes'
|
|
])
|
|
|
|
assert result.exit_code == 0
|
|
assert "has been removed" in result.output
|
|
assert "does NOT delete resources from bunny.net" in result.output
|
|
|
|
@patch('src.cli.commands.authenticate_admin')
|
|
@patch('src.cli.commands.db_manager')
|
|
def test_remove_site_not_found(self, mock_db, mock_auth, cli_runner, mock_admin_user):
|
|
"""Test remove-site with non-existent domain"""
|
|
mock_auth.return_value = mock_admin_user
|
|
|
|
mock_session = Mock()
|
|
mock_db.get_session.return_value = mock_session
|
|
|
|
mock_repo = Mock()
|
|
mock_repo.get_by_hostname.return_value = None
|
|
|
|
with patch('src.cli.commands.SiteDeploymentRepository', return_value=mock_repo):
|
|
result = cli_runner.invoke(app, [
|
|
'remove-site',
|
|
'--domain', 'nonexistent.com',
|
|
'--admin-user', 'admin',
|
|
'--admin-password', 'password',
|
|
'--yes'
|
|
])
|
|
|
|
assert result.exit_code != 0
|
|
assert "not found" in result.output
|
|
|