This commit is contained in:
beabigegg
2025-09-02 13:11:48 +08:00
parent a60d965317
commit b11a8272c4
76 changed files with 15321 additions and 200 deletions

308
tests/test_models.py Normal file
View File

@@ -0,0 +1,308 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
資料模型測試
Author: PANJIT IT Team
Created: 2024-01-28
Modified: 2024-01-28
"""
import pytest
from datetime import datetime, timedelta
from app.models.user import User
from app.models.job import TranslationJob, JobFile
from app.models.cache import TranslationCache
from app.models.stats import APIUsageStats
from app.models.log import SystemLog
from app import db
class TestUserModel:
"""使用者模型測試"""
def test_create_user(self, app):
"""測試建立使用者"""
with app.app_context():
user = User(
username='testuser',
display_name='Test User',
email='test@example.com',
department='IT',
is_admin=False
)
db.session.add(user)
db.session.commit()
assert user.id is not None
assert user.username == 'testuser'
assert user.is_admin is False
def test_user_to_dict(self, app, auth_user):
"""測試使用者轉字典"""
with app.app_context():
user_dict = auth_user.to_dict()
assert 'id' in user_dict
assert 'username' in user_dict
assert 'display_name' in user_dict
assert 'email' in user_dict
assert user_dict['username'] == auth_user.username
def test_user_get_or_create_existing(self, app, auth_user):
"""測試取得已存在的使用者"""
with app.app_context():
user = User.get_or_create(
username=auth_user.username,
display_name='Updated Name',
email=auth_user.email
)
assert user.id == auth_user.id
assert user.display_name == 'Updated Name' # 應該更新
def test_user_get_or_create_new(self, app):
"""測試建立新使用者"""
with app.app_context():
user = User.get_or_create(
username='newuser',
display_name='New User',
email='new@example.com'
)
assert user.id is not None
assert user.username == 'newuser'
def test_update_last_login(self, app, auth_user):
"""測試更新最後登入時間"""
with app.app_context():
old_login_time = auth_user.last_login
auth_user.update_last_login()
assert auth_user.last_login is not None
if old_login_time:
assert auth_user.last_login > old_login_time
class TestTranslationJobModel:
"""翻譯任務模型測試"""
def test_create_translation_job(self, app, auth_user):
"""測試建立翻譯任務"""
with app.app_context():
job = TranslationJob(
user_id=auth_user.id,
original_filename='test.docx',
file_extension='.docx',
file_size=1024,
file_path='/tmp/test.docx',
source_language='auto',
target_languages=['en', 'vi'],
status='PENDING'
)
db.session.add(job)
db.session.commit()
assert job.id is not None
assert job.job_uuid is not None
assert len(job.job_uuid) == 36 # UUID 格式
def test_job_to_dict(self, app, sample_job):
"""測試任務轉字典"""
with app.app_context():
job_dict = sample_job.to_dict()
assert 'id' in job_dict
assert 'job_uuid' in job_dict
assert 'original_filename' in job_dict
assert 'target_languages' in job_dict
assert job_dict['job_uuid'] == sample_job.job_uuid
def test_update_status(self, app, sample_job):
"""測試更新任務狀態"""
with app.app_context():
old_updated_at = sample_job.updated_at
sample_job.update_status('PROCESSING', progress=50.0)
assert sample_job.status == 'PROCESSING'
assert sample_job.progress == 50.0
assert sample_job.processing_started_at is not None
assert sample_job.updated_at > old_updated_at
def test_add_original_file(self, app, sample_job):
"""測試新增原始檔案記錄"""
with app.app_context():
file_record = sample_job.add_original_file(
filename='test.docx',
file_path='/tmp/test.docx',
file_size=1024
)
assert file_record.id is not None
assert file_record.file_type == 'ORIGINAL'
assert file_record.filename == 'test.docx'
def test_add_translated_file(self, app, sample_job):
"""測試新增翻譯檔案記錄"""
with app.app_context():
file_record = sample_job.add_translated_file(
language_code='en',
filename='test_en.docx',
file_path='/tmp/test_en.docx',
file_size=1200
)
assert file_record.id is not None
assert file_record.file_type == 'TRANSLATED'
assert file_record.language_code == 'en'
def test_can_retry(self, app, sample_job):
"""測試是否可以重試"""
with app.app_context():
# PENDING 狀態不能重試
assert not sample_job.can_retry()
# FAILED 狀態且重試次數 < 3 可以重試
sample_job.update_status('FAILED')
sample_job.retry_count = 2
assert sample_job.can_retry()
# 重試次數達到上限不能重試
sample_job.retry_count = 3
assert not sample_job.can_retry()
class TestTranslationCacheModel:
"""翻譯快取模型測試"""
def test_save_and_get_translation(self, app):
"""測試儲存和取得翻譯快取"""
with app.app_context():
source_text = "Hello, world!"
translated_text = "你好,世界!"
# 儲存翻譯
result = TranslationCache.save_translation(
source_text=source_text,
source_language='en',
target_language='zh-TW',
translated_text=translated_text
)
assert result is True
# 取得翻譯
cached_translation = TranslationCache.get_translation(
source_text=source_text,
source_language='en',
target_language='zh-TW'
)
assert cached_translation == translated_text
def test_get_nonexistent_translation(self, app):
"""測試取得不存在的翻譯"""
with app.app_context():
cached_translation = TranslationCache.get_translation(
source_text="Nonexistent text",
source_language='en',
target_language='zh-TW'
)
assert cached_translation is None
def test_generate_hash(self):
"""測試生成文字雜湊"""
text = "Hello, world!"
hash1 = TranslationCache.generate_hash(text)
hash2 = TranslationCache.generate_hash(text)
assert hash1 == hash2
assert len(hash1) == 64 # SHA256 雜湊長度
class TestAPIUsageStatsModel:
"""API 使用統計模型測試"""
def test_record_api_call(self, app, auth_user, sample_job):
"""測試記錄 API 呼叫"""
with app.app_context():
metadata = {
'usage': {
'prompt_tokens': 10,
'completion_tokens': 5,
'total_tokens': 15,
'prompt_unit_price': 0.0001,
'prompt_price_unit': 'USD'
}
}
stats = APIUsageStats.record_api_call(
user_id=auth_user.id,
job_id=sample_job.id,
api_endpoint='/chat-messages',
metadata=metadata,
response_time_ms=1000
)
assert stats.id is not None
assert stats.prompt_tokens == 10
assert stats.total_tokens == 15
assert stats.cost == 10 * 0.0001 # prompt_tokens * prompt_unit_price
def test_get_user_statistics(self, app, auth_user):
"""測試取得使用者統計"""
with app.app_context():
stats = APIUsageStats.get_user_statistics(auth_user.id)
assert 'total_calls' in stats
assert 'successful_calls' in stats
assert 'total_cost' in stats
class TestSystemLogModel:
"""系統日誌模型測試"""
def test_create_log_entry(self, app, auth_user):
"""測試建立日誌項目"""
with app.app_context():
log = SystemLog.log(
level='INFO',
module='test_module',
message='Test message',
user_id=auth_user.id
)
assert log.id is not None
assert log.level == 'INFO'
assert log.module == 'test_module'
assert log.message == 'Test message'
def test_log_convenience_methods(self, app):
"""測試日誌便利方法"""
with app.app_context():
# 測試不同等級的日誌方法
info_log = SystemLog.info('test', 'Info message')
warning_log = SystemLog.warning('test', 'Warning message')
error_log = SystemLog.error('test', 'Error message')
assert info_log.level == 'INFO'
assert warning_log.level == 'WARNING'
assert error_log.level == 'ERROR'
def test_get_logs_with_filters(self, app):
"""測試帶篩選條件的日誌查詢"""
with app.app_context():
# 建立測試日誌
SystemLog.info('module1', 'Test message 1')
SystemLog.error('module2', 'Test message 2')
# 按等級篩選
info_logs = SystemLog.get_logs(level='INFO', limit=10)
assert len([log for log in info_logs if log.level == 'INFO']) > 0
# 按模組篩選
module1_logs = SystemLog.get_logs(module='module1', limit=10)
assert len([log for log in module1_logs if 'module1' in log.module]) > 0