#!/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