308 lines
10 KiB
Python
308 lines
10 KiB
Python
#!/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 |