Big-Link-Man/tests/integration/test_deployment_integration.py

360 lines
13 KiB
Python

"""
Integration tests for deployment workflow
"""
import pytest
from click.testing import CliRunner
from unittest.mock import patch, Mock
from src.cli.commands import app
from src.database.session import db_manager
from src.database.repositories import UserRepository, SiteDeploymentRepository
from src.auth.service import AuthService
from src.deployment.bunnynet import StorageZoneResult, PullZoneResult
@pytest.fixture
def cli_runner():
"""Fixture to create a Click CLI test runner"""
return CliRunner()
@pytest.fixture
def setup_test_admin():
"""Create a test admin user in the database"""
session = db_manager.get_session()
try:
user_repo = UserRepository(session)
auth_service = AuthService(user_repo)
try:
user = auth_service.create_user_with_hashed_password(
username="test_admin",
password="test_password",
role="Admin"
)
yield user
finally:
if user_repo.get_by_username("test_admin"):
test_user = user_repo.get_by_username("test_admin")
if test_user:
user_repo.delete(test_user.id)
finally:
session.close()
@pytest.fixture
def cleanup_test_sites():
"""Clean up all site deployments before and after test"""
def _cleanup():
session = db_manager.get_session()
try:
deployment_repo = SiteDeploymentRepository(session)
sites = deployment_repo.get_all()
for site in sites:
deployment_repo.delete(site.id)
finally:
session.close()
_cleanup()
yield
_cleanup()
class TestProvisionSiteIntegration:
"""Integration tests for provision-site workflow"""
@patch('src.cli.commands.get_bunny_account_api_key')
@patch('src.cli.commands.BunnyNetClient')
def test_full_provisioning_workflow(
self, mock_client_class, mock_get_key, cli_runner, setup_test_admin, cleanup_test_sites
):
"""Test complete site provisioning workflow with database"""
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=99999,
name="test-integration-storage",
password="test-integration-pass",
region="DE"
)
mock_client.create_pull_zone.return_value = PullZoneResult(
id=88888,
name="test-integration-cdn",
hostname="test-integration.b-cdn.net"
)
mock_client.add_custom_hostname.return_value = True
result = cli_runner.invoke(app, [
'provision-site',
'--name', 'Test Integration Site',
'--domain', 'test-integration.example.com',
'--storage-name', 'test-integration-storage',
'--region', 'DE',
'--admin-user', 'test_admin',
'--admin-password', 'test_password'
])
assert result.exit_code == 0
assert "Site provisioned successfully!" in result.output
session = db_manager.get_session()
try:
deployment_repo = SiteDeploymentRepository(session)
site = deployment_repo.get_by_hostname('test-integration.example.com')
assert site is not None
assert site.site_name == 'Test Integration Site'
assert site.storage_zone_id == 99999
assert site.storage_zone_name == 'test-integration-storage'
assert site.storage_zone_password == 'test-integration-pass'
assert site.storage_zone_region == 'DE'
assert site.pull_zone_id == 88888
assert site.pull_zone_bcdn_hostname == 'test-integration.b-cdn.net'
finally:
session.close()
@patch('src.cli.commands.get_bunny_account_api_key')
@patch('src.cli.commands.BunnyNetClient')
def test_provision_then_list_sites(
self, mock_client_class, mock_get_key, cli_runner, setup_test_admin, cleanup_test_sites
):
"""Test provisioning a site and then listing it"""
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=99999, name="test-list-storage", password="test-pass", region="NY"
)
mock_client.create_pull_zone.return_value = PullZoneResult(
id=88888, name="test-list-cdn", hostname="test-list.b-cdn.net"
)
mock_client.add_custom_hostname.return_value = True
provision_result = cli_runner.invoke(app, [
'provision-site',
'--name', 'Test List Site',
'--domain', 'test-list.example.com',
'--storage-name', 'test-list-storage',
'--region', 'NY',
'--admin-user', 'test_admin',
'--admin-password', 'test_password'
])
assert provision_result.exit_code == 0
list_result = cli_runner.invoke(app, [
'list-sites',
'--admin-user', 'test_admin',
'--admin-password', 'test_password'
])
assert list_result.exit_code == 0
assert "test-list.example.com" in list_result.output
assert "Test List Site" in list_result.output
@patch('src.cli.commands.get_bunny_account_api_key')
@patch('src.cli.commands.BunnyNetClient')
def test_provision_get_and_remove_workflow(
self, mock_client_class, mock_get_key, cli_runner, setup_test_admin, cleanup_test_sites
):
"""Test complete workflow: provision, get details, then remove"""
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=77777, name="test-remove-storage", password="test-pass", region="LA"
)
mock_client.create_pull_zone.return_value = PullZoneResult(
id=66666, name="test-remove-cdn", hostname="test-remove.b-cdn.net"
)
mock_client.add_custom_hostname.return_value = True
provision_result = cli_runner.invoke(app, [
'provision-site',
'--name', 'Test Remove Site',
'--domain', 'test-remove.example.com',
'--storage-name', 'test-remove-storage',
'--region', 'LA',
'--admin-user', 'test_admin',
'--admin-password', 'test_password'
])
assert provision_result.exit_code == 0
get_result = cli_runner.invoke(app, [
'get-site',
'--domain', 'test-remove.example.com',
'--admin-user', 'test_admin',
'--admin-password', 'test_password'
])
assert get_result.exit_code == 0
assert "Test Remove Site" in get_result.output
assert "77777" in get_result.output
assert "test-remove-storage" in get_result.output
remove_result = cli_runner.invoke(app, [
'remove-site',
'--domain', 'test-remove.example.com',
'--admin-user', 'test_admin',
'--admin-password', 'test_password',
'--yes'
])
assert remove_result.exit_code == 0
assert "has been removed" in remove_result.output
session = db_manager.get_session()
try:
deployment_repo = SiteDeploymentRepository(session)
site = deployment_repo.get_by_hostname('test-remove.example.com')
assert site is None
finally:
session.close()
class TestAttachDomainIntegration:
"""Integration tests for attach-domain workflow"""
@patch('src.cli.commands.get_bunny_account_api_key')
@patch('src.cli.commands.BunnyNetClient')
def test_attach_domain_to_existing_storage(
self, mock_client_class, mock_get_key, cli_runner, setup_test_admin, cleanup_test_sites
):
"""Test attaching a domain to an existing storage zone"""
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=55555,
name="existing-storage",
password="existing-pass",
region="SG"
)
mock_client.create_pull_zone.return_value = PullZoneResult(
id=44444,
name="attach-test-cdn",
hostname="attach-test.b-cdn.net"
)
mock_client.add_custom_hostname.return_value = True
result = cli_runner.invoke(app, [
'attach-domain',
'--name', 'Test Attach Site',
'--domain', 'test-attach.example.com',
'--storage-name', 'existing-storage',
'--admin-user', 'test_admin',
'--admin-password', 'test_password'
])
assert result.exit_code == 0
assert "Domain attached successfully!" in result.output
session = db_manager.get_session()
try:
deployment_repo = SiteDeploymentRepository(session)
site = deployment_repo.get_by_hostname('test-attach.example.com')
assert site is not None
assert site.site_name == 'Test Attach Site'
assert site.storage_zone_id == 55555
assert site.storage_zone_name == 'existing-storage'
assert site.pull_zone_id == 44444
finally:
session.close()
class TestDeploymentErrorHandling:
"""Integration tests for error handling in deployment workflow"""
def test_provision_without_api_key(self, cli_runner, setup_test_admin):
"""Test provisioning fails gracefully without API key"""
with patch('src.cli.commands.get_bunny_account_api_key', side_effect=ValueError("API key required")):
result = cli_runner.invoke(app, [
'provision-site',
'--name', 'Test Site',
'--domain', 'test.example.com',
'--storage-name', 'test-storage',
'--region', 'DE',
'--admin-user', 'test_admin',
'--admin-password', 'test_password'
])
assert result.exit_code != 0
assert "BUNNY_ACCOUNT_API_KEY" in result.output
@patch('src.cli.commands.get_bunny_account_api_key')
@patch('src.cli.commands.BunnyNetClient')
def test_provision_duplicate_domain(
self, mock_client_class, mock_get_key, cli_runner, setup_test_admin, cleanup_test_sites
):
"""Test provisioning fails when domain already exists"""
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.b-cdn.net"
)
mock_client.add_custom_hostname.return_value = True
first_result = cli_runner.invoke(app, [
'provision-site',
'--name', 'First Site',
'--domain', 'duplicate.example.com',
'--storage-name', 'test-dup-storage-1',
'--region', 'DE',
'--admin-user', 'test_admin',
'--admin-password', 'test_password'
])
assert first_result.exit_code == 0
second_result = cli_runner.invoke(app, [
'provision-site',
'--name', 'Second Site',
'--domain', 'duplicate.example.com',
'--storage-name', 'test-dup-storage-2',
'--region', 'DE',
'--admin-user', 'test_admin',
'--admin-password', 'test_password'
])
assert second_result.exit_code != 0
assert "already exists" in second_result.output
def test_get_nonexistent_site(self, cli_runner, setup_test_admin):
"""Test getting details of non-existent site"""
result = cli_runner.invoke(app, [
'get-site',
'--domain', 'nonexistent.example.com',
'--admin-user', 'test_admin',
'--admin-password', 'test_password'
])
assert result.exit_code != 0
assert "not found" in result.output
def test_remove_nonexistent_site(self, cli_runner, setup_test_admin):
"""Test removing non-existent site"""
result = cli_runner.invoke(app, [
'remove-site',
'--domain', 'nonexistent.example.com',
'--admin-user', 'test_admin',
'--admin-password', 'test_password',
'--yes'
])
assert result.exit_code != 0
assert "not found" in result.output