#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 檔案管理 API 測試 Author: PANJIT IT Team Created: 2024-01-28 Modified: 2024-01-28 """ import pytest import io import json from unittest.mock import patch, MagicMock from app.models.job import TranslationJob class TestFilesAPI: """檔案管理 API 測試類別""" def test_upload_file_success(self, authenticated_client, auth_user): """測試成功上傳檔案""" # 建立測試檔案 file_data = b'Mock DOCX file content' file_obj = (io.BytesIO(file_data), 'test.docx') with patch('app.utils.helpers.save_uploaded_file') as mock_save: mock_save.return_value = { 'success': True, 'filename': 'original_test_12345678.docx', 'file_path': '/tmp/test_job_uuid/original_test_12345678.docx', 'file_size': len(file_data) } response = authenticated_client.post('/api/v1/files/upload', data={ 'file': file_obj, 'source_language': 'auto', 'target_languages': json.dumps(['en', 'vi']) }) assert response.status_code == 200 data = response.get_json() assert data['success'] is True assert 'job_uuid' in data['data'] assert data['data']['original_filename'] == 'test.docx' def test_upload_file_no_file(self, authenticated_client): """測試未選擇檔案""" response = authenticated_client.post('/api/v1/files/upload', data={ 'source_language': 'auto', 'target_languages': json.dumps(['en']) }) assert response.status_code == 400 data = response.get_json() assert data['success'] is False assert data['error'] == 'NO_FILE' def test_upload_file_invalid_type(self, authenticated_client): """測試上傳無效檔案類型""" file_data = b'Mock text file content' file_obj = (io.BytesIO(file_data), 'test.txt') response = authenticated_client.post('/api/v1/files/upload', data={ 'file': file_obj, 'source_language': 'auto', 'target_languages': json.dumps(['en']) }) assert response.status_code == 400 data = response.get_json() assert data['success'] is False assert data['error'] == 'INVALID_FILE_TYPE' def test_upload_file_too_large(self, authenticated_client, app): """測試上傳過大檔案""" # 建立超過限制的檔案(26MB+) large_file_data = b'x' * (26 * 1024 * 1024 + 1) file_obj = (io.BytesIO(large_file_data), 'large.docx') response = authenticated_client.post('/api/v1/files/upload', data={ 'file': file_obj, 'source_language': 'auto', 'target_languages': json.dumps(['en']) }) assert response.status_code == 400 data = response.get_json() assert data['success'] is False assert data['error'] == 'FILE_TOO_LARGE' def test_upload_file_invalid_target_languages(self, authenticated_client): """測試無效的目標語言""" file_data = b'Mock DOCX file content' file_obj = (io.BytesIO(file_data), 'test.docx') response = authenticated_client.post('/api/v1/files/upload', data={ 'file': file_obj, 'source_language': 'auto', 'target_languages': 'invalid_json' }) assert response.status_code == 400 data = response.get_json() assert data['success'] is False assert data['error'] == 'INVALID_TARGET_LANGUAGES' def test_upload_file_empty_target_languages(self, authenticated_client): """測試空的目標語言""" file_data = b'Mock DOCX file content' file_obj = (io.BytesIO(file_data), 'test.docx') response = authenticated_client.post('/api/v1/files/upload', data={ 'file': file_obj, 'source_language': 'auto', 'target_languages': json.dumps([]) }) assert response.status_code == 400 data = response.get_json() assert data['success'] is False assert data['error'] == 'NO_TARGET_LANGUAGES' def test_upload_file_without_auth(self, client): """測試未認證上傳檔案""" file_data = b'Mock DOCX file content' file_obj = (io.BytesIO(file_data), 'test.docx') response = client.post('/api/v1/files/upload', data={ 'file': file_obj, 'source_language': 'auto', 'target_languages': json.dumps(['en']) }) assert response.status_code == 401 data = response.get_json() assert data['success'] is False assert data['error'] == 'AUTHENTICATION_REQUIRED' def test_download_translated_file_success(self, authenticated_client, sample_job, auth_user): """測試成功下載翻譯檔案""" # 設定任務為已完成 sample_job.update_status('COMPLETED') # 添加翻譯檔案記錄 sample_job.add_translated_file( language_code='en', filename='test_en_translated.docx', file_path='/tmp/test_en_translated.docx', file_size=1024 ) with patch('pathlib.Path.exists') as mock_exists, \ patch('flask.send_file') as mock_send_file: mock_exists.return_value = True mock_send_file.return_value = 'file_content' response = authenticated_client.get(f'/api/v1/files/{sample_job.job_uuid}/download/en') # send_file 被呼叫表示成功 mock_send_file.assert_called_once() def test_download_file_not_found(self, authenticated_client, sample_job): """測試下載不存在的檔案""" response = authenticated_client.get(f'/api/v1/files/nonexistent-uuid/download/en') assert response.status_code == 404 data = response.get_json() assert data['success'] is False assert data['error'] == 'JOB_NOT_FOUND' def test_download_file_permission_denied(self, authenticated_client, sample_job, app): """測試下載他人檔案""" # 建立另一個使用者的任務 from app.models.user import User from app import db with app.app_context(): other_user = User( username='otheruser', display_name='Other User', email='other@panjit.com.tw', department='IT', is_admin=False ) db.session.add(other_user) db.session.commit() other_job = TranslationJob( user_id=other_user.id, original_filename='other.docx', file_extension='.docx', file_size=1024, file_path='/tmp/other.docx', source_language='auto', target_languages=['en'], status='COMPLETED' ) db.session.add(other_job) db.session.commit() response = authenticated_client.get(f'/api/v1/files/{other_job.job_uuid}/download/en') assert response.status_code == 403 data = response.get_json() assert data['success'] is False assert data['error'] == 'PERMISSION_DENIED' def test_download_file_not_completed(self, authenticated_client, sample_job): """測試下載未完成任務的檔案""" response = authenticated_client.get(f'/api/v1/files/{sample_job.job_uuid}/download/en') assert response.status_code == 400 data = response.get_json() assert data['success'] is False assert data['error'] == 'JOB_NOT_COMPLETED' def test_download_original_file_success(self, authenticated_client, sample_job): """測試下載原始檔案""" # 添加原始檔案記錄 sample_job.add_original_file( filename='original_test.docx', file_path='/tmp/original_test.docx', file_size=1024 ) with patch('pathlib.Path.exists') as mock_exists, \ patch('flask.send_file') as mock_send_file: mock_exists.return_value = True mock_send_file.return_value = 'file_content' response = authenticated_client.get(f'/api/v1/files/{sample_job.job_uuid}/download/original') mock_send_file.assert_called_once() def test_get_supported_formats(self, client): """測試取得支援的檔案格式""" response = client.get('/api/v1/files/supported-formats') assert response.status_code == 200 data = response.get_json() assert data['success'] is True assert 'supported_formats' in data['data'] assert 'max_file_size' in data['data'] # 檢查是否包含基本格式 formats = data['data']['supported_formats'] assert '.docx' in formats assert '.pdf' in formats def test_get_supported_languages(self, client): """測試取得支援的語言""" response = client.get('/api/v1/files/supported-languages') assert response.status_code == 200 data = response.get_json() assert data['success'] is True assert 'supported_languages' in data['data'] # 檢查是否包含基本語言 languages = data['data']['supported_languages'] assert 'en' in languages assert 'zh-TW' in languages assert 'auto' in languages