from gevent import monkey
monkey.patch_all()
import os
import sys
import pytest
import tempfile
import logging
PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if PROJECT_ROOT not in sys.path:
sys.path.insert(0, PROJECT_ROOT)
SECRET_KEY = "test-secret-key-for-unit-testing-only"
VALID_PASSWORD = "Test@1234"
g_logger = logging.getLogger("virtcca_deploy_test")
class TestConfig:
"""测试专用的配置类,使用临时文件和固定密钥"""
def __init__(self):
self.ssl_cert = None
self.vlan_pool_manager = None
self._temp_dir = tempfile.mkdtemp(prefix="virtcca_test_")
self.jwt_secret_key = None
self.jwt_expiration_minutes = 30
self.max_login_attempts = 5
self.lockout_duration_minutes = 15
self.logger = None
class MockConfigParser:
def __init__(self):
self.config = {
'DEFAULT': {
'manager': "localhost",
'ca_cert': '/etc/virtcca_deploy/cert/ca.crt',
'cvm_image_path': '/var/lib/virtcca_deploy/qcow2'
},
'PCI': {
'pf_whitelist': '[]',
'vf_whitelist': '[]'
},
'NET': {
'base_ip': '192.168.0.0',
'prefix': '24'
},
'AUTH': {
'jwt_expiration_minutes': '30',
'max_login_attempts': '5',
'lockout_duration_minutes': '15'
}
}
def get(self, section, option):
"""模拟ConfigParser的get方法"""
if section in self.config and option in self.config[section]:
return self.config[section][option]
raise KeyError(f"Option {option} not found in section {section}")
def getint(self, section, option):
"""模拟ConfigParser的getint方法"""
return int(self.get(section, option))
self.config = MockConfigParser()
def __del__(self):
"""析构函数,清理临时文件"""
try:
import shutil
if os.path.exists(self._temp_dir):
shutil.rmtree(self._temp_dir)
except:
pass
def configure_log(self, log_name):
"""配置日志"""
if self.logger is not None:
return self.logger
log_dir = os.path.join(self._temp_dir, "logs")
os.makedirs(log_dir, exist_ok=True)
log_file = os.path.join(log_dir, log_name)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[
logging.FileHandler(log_file),
logging.StreamHandler()
]
)
self.logger = g_logger
return self.logger
def configure_ssl(self):
"""配置SSL"""
pass
def configure_auth(self):
"""配置认证 - 使用临时文件和固定密钥"""
jwt_secret_key_file = os.path.join(self._temp_dir, "jwt_secret.key")
self.jwt_secret_key = "test-jwt-secret-key-for-unit-testing-only-very-long-string-to-match-requirement"
try:
os.makedirs(os.path.dirname(jwt_secret_key_file), exist_ok=True)
with open(jwt_secret_key_file, 'w') as f:
f.write(self.jwt_secret_key)
import stat
os.chmod(jwt_secret_key_file, stat.S_IRUSR | stat.S_IWUSR)
except Exception:
pass
def configure_device(self):
class MockDeviceAllocator:
def __init__(self):
self._devices = []
def allocate(self, request):
from virtcca_deploy.common.data_model import DeviceAllocResp
return DeviceAllocResp(success=True, device_dict={})
def release(self, request):
from virtcca_deploy.common.data_model import DeviceReleaseResp
return DeviceReleaseResp(success=True)
def discover_hi1822_devices(self):
return []
def sync_discovered_to_db(self):
pass
self.device_allocator = MockDeviceAllocator()
@pytest.fixture(scope='function')
def app():
"""创建测试用 Flask 应用(内存 SQLite),并注册所有路由"""
import tempfile
from unittest import mock
from virtcca_deploy.services.db_service import db
from virtcca_deploy.manager import manager
main_temp_dir = tempfile.mkdtemp(prefix="virtcca_main_test_")
with mock.patch.multiple('virtcca_deploy.common.constants',
DEFAULT_CONFIG_PATH=main_temp_dir,
MANAGER_DB_PATH=main_temp_dir,
MANAGER_DB='sqlite:///:memory:',
CVM_COLLECT_LOG_PATH=os.path.join(main_temp_dir, "logs"),
CVM_MANAGER_SOFTWARE_PATH=os.path.join(main_temp_dir, "software")):
test_config = TestConfig()
with mock.patch('virtcca_deploy.common.config.Config', return_value=test_config):
app = manager.create_app()
app.config.update({
'SQLALCHEMY_DATABASE_URI': 'sqlite:///:memory:',
'SQLALCHEMY_TRACK_MODIFICATIONS': False,
'TESTING': True,
})
with app.app_context():
db.create_all()
from virtcca_deploy.manager.auth.auth_models import User
if not User.query.filter_by(username="root").first():
root = User(username="root", password_hash='', salt='')
db.session.add(root)
db.session.commit()
yield app
try:
import shutil
if os.path.exists(main_temp_dir):
shutil.rmtree(main_temp_dir)
except:
pass
@pytest.fixture(scope='function')
def compute_app():
"""创建测试用 Compute Flask 应用"""
import tempfile
from unittest import mock
main_temp_dir = tempfile.mkdtemp(prefix="virtcca_compute_test_")
with mock.patch.multiple('virtcca_deploy.common.constants',
DEFAULT_CONFIG_PATH=main_temp_dir,
COMPUTE_LOG_NAME="compute_test.log",
MANAGER_PORT=5001):
test_config = TestConfig()
with mock.patch('virtcca_deploy.common.config.Config', return_value=test_config):
with mock.patch('virtcca_deploy.services.network_service.NetworkService') as mock_network_service:
mock_manager_link = mock.MagicMock()
mock_manager_link.node_register.return_value = None
mock_network_service.return_value = mock_manager_link
with mock.patch('virtcca_deploy.compute.compute.resource_allocator.DeviceManagerAllocator') as MockAllocator:
mock_allocator_instance = mock.MagicMock()
mock_allocator_instance.discover_hi1822_devices.return_value = []
mock_allocator_instance.sync_discovered_to_db.return_value = None
MockAllocator.return_value = mock_allocator_instance
from virtcca_deploy.compute import compute
app = compute.create_app()
app.config.update({
'TESTING': True,
})
yield app
try:
import shutil
if os.path.exists(main_temp_dir):
shutil.rmtree(main_temp_dir)
except:
pass
@pytest.fixture
def client(app):
"""Flask 测试客户端"""
return app.test_client()
@pytest.fixture
def compute_client(compute_app):
"""Compute Flask 测试客户端"""
return compute_app.test_client()
@pytest.fixture
def authenticated_client(app):
"""已认证的测试客户端"""
from virtcca_deploy.manager.auth.auth_service import AuthService
from virtcca_deploy.manager.auth.auth_models import User
from virtcca_deploy.services.db_service import db
with app.app_context():
hashed, salt = AuthService.hash_password("Test@1234")
user = User.query.filter_by(username="root").first()
user.password_hash = hashed
user.salt = salt
db.session.commit()
secret_key = app.config['JWT_SECRET_KEY']
expiration_minutes = app.config['JWT_EXPIRATION_MINUTES']
if not isinstance(expiration_minutes, int):
try:
expiration_minutes = int(expiration_minutes)
except (TypeError, ValueError):
expiration_minutes = 30
token = AuthService.generate_token(
user_id=user.id,
username="root",
device_id="test-device-1",
secret_key=secret_key,
expiration_minutes=expiration_minutes
)
AuthService.set_active_session(
user_id=user.id,
username="root",
device_id="test-device-1",
token=token,
ip_address="127.0.0.1"
)
client = app.test_client()
client.environ_base['HTTP_AUTHORIZATION'] = f'Bearer {token}'
return client
@pytest.fixture
def db_session(app):
"""提供数据库会话"""
from virtcca_deploy.services.db_service import db
with app.app_context():
yield db.session