Files
richardtekula f78c2199e1 Fix: Rename directory to remove & character causing shell issues
Renamed ebook_backend&admin_panel to ebook_backend_admin_panel
  The & character was being interpreted by shell as background
  process operator, causing 'Dockerfile not found' errors in Coolify.
2025-11-11 17:06:39 +01:00

373 lines
17 KiB
Python

import pytest
import os
import tempfile
from unittest.mock import patch, MagicMock, mock_open
from fastapi import HTTPException
from fastapi.testclient import TestClient
class TestTranslationRoutes:
"""Test cases for translation file management routes"""
def test_upload_translation_unauthorized(self, client):
"""Test uploading translation file without authentication"""
# Create a mock file
mock_file = MagicMock()
mock_file.filename = "test.xlsx"
mock_file.read.return_value = b"test content"
response = client.post("/upload-translations", files={"file": ("test.xlsx", b"test content", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")})
assert response.status_code == 401
data = response.json()
assert data["detail"] == "Unauthorized"
@patch('routes.auth.os.path.exists')
@patch('routes.auth.os.makedirs')
@patch('builtins.open', new_callable=mock_open)
def test_upload_translation_success(self, mock_file, mock_makedirs, mock_exists, client, auth_headers, temp_translation_dir):
"""Test successful translation file upload"""
# Mock that file doesn't exist initially
mock_exists.return_value = False
# Create a mock file content
file_content = b"test excel content"
response = client.post(
"/upload-translations",
files={"file": ("test_translation.xlsx", file_content, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")},
headers=auth_headers
)
assert response.status_code == 200
data = response.json()
assert data["message"] == "Translation file uploaded successfully"
assert data["filename"] == "test_translation.xlsx"
# Verify directory creation was attempted
mock_makedirs.assert_called_once()
@patch('routes.auth.os.path.exists')
def test_upload_translation_file_already_exists(self, mock_exists, client, auth_headers):
"""Test uploading translation file when one already exists"""
# Mock that file already exists
mock_exists.return_value = True
file_content = b"test excel content"
response = client.post(
"/upload-translations",
files={"file": ("test_translation.xlsx", file_content, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")},
headers=auth_headers
)
assert response.status_code == 400
data = response.json()
assert data["detail"] == "A translation file already exists. Please delete it first."
@patch('routes.auth.os.path.exists')
@patch('routes.auth.os.makedirs')
@patch('builtins.open', side_effect=Exception("File write error"))
def test_upload_translation_write_error(self, mock_file, mock_makedirs, mock_exists, client, auth_headers):
"""Test translation upload with file write error"""
mock_exists.return_value = False
file_content = b"test excel content"
response = client.post(
"/upload-translations",
files={"file": ("test_translation.xlsx", file_content, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")},
headers=auth_headers
)
assert response.status_code == 500
data = response.json()
assert "Upload failed" in data["detail"]
@patch('routes.auth.os.path.exists')
@patch('routes.auth.os.makedirs')
@patch('builtins.open', new_callable=mock_open)
@patch('routes.auth.os.remove')
def test_upload_translation_cleanup_on_error(self, mock_remove, mock_file, mock_makedirs, mock_exists, client, auth_headers):
"""Test cleanup when translation upload fails"""
# Mock that files don't exist initially
mock_exists.return_value = False
# Mock file write to succeed but metadata write to fail
mock_file.side_effect = [
MagicMock(), # Translation file write succeeds
Exception("Metadata write error") # Metadata write fails
]
file_content = b"test excel content"
response = client.post(
"/upload-translations",
files={"file": ("test_translation.xlsx", file_content, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")},
headers=auth_headers
)
assert response.status_code == 500
# The cleanup should happen in the exception handler, but since we're mocking os.path.exists
# to return False, the cleanup won't be called. This test verifies the error handling works.
def test_delete_translation_unauthorized(self, client):
"""Test deleting translation file without authentication"""
response = client.delete("/delete-translation")
assert response.status_code == 401
data = response.json()
assert data["detail"] == "Unauthorized"
@patch('routes.auth.os.path.exists')
@patch('routes.auth.os.remove')
@patch('routes.auth.os.listdir')
@patch('routes.auth.os.rmdir')
def test_delete_translation_success(self, mock_rmdir, mock_listdir, mock_remove, mock_exists, client, auth_headers):
"""Test successful translation file deletion"""
# Mock that files exist
mock_exists.side_effect = lambda path: "translation.xlsx" in path or "metadata.txt" in path
# Mock empty directory after deletion
mock_listdir.return_value = []
response = client.delete("/delete-translation", headers=auth_headers)
assert response.status_code == 200
data = response.json()
assert data["message"] == "Translation file deleted successfully"
# Verify files were deleted
assert mock_remove.call_count == 2 # Translation file and metadata
@patch('routes.auth.os.path.exists')
def test_delete_translation_not_found(self, mock_exists, client, auth_headers):
"""Test deleting translation file when none exists"""
# Mock that no files exist
mock_exists.return_value = False
response = client.delete("/delete-translation", headers=auth_headers)
assert response.status_code == 404
data = response.json()
assert data["detail"] == "No translation file found"
@patch('routes.auth.os.path.exists')
@patch('routes.auth.os.remove')
@patch('routes.auth.os.listdir')
def test_delete_translation_directory_not_empty(self, mock_listdir, mock_remove, mock_exists, client, auth_headers):
"""Test deletion when directory is not empty after file removal"""
# Mock that files exist
mock_exists.side_effect = lambda path: "translation.xlsx" in path or "metadata.txt" in path
# Mock non-empty directory after deletion
mock_listdir.return_value = ["other_file.txt"]
response = client.delete("/delete-translation", headers=auth_headers)
assert response.status_code == 200
data = response.json()
assert data["message"] == "Translation file deleted successfully"
# Directory should not be removed since it's not empty
assert mock_remove.call_count == 2 # Only files, not directory
def test_download_translation_unauthorized(self, client):
"""Test downloading translation file without authentication"""
response = client.get("/download-translation")
assert response.status_code == 401
data = response.json()
assert data["detail"] == "Unauthorized"
@patch('routes.auth.os.path.exists')
@patch('builtins.open', new_callable=mock_open, read_data=b"test content")
def test_download_translation_success(self, mock_file, mock_exists, client, auth_headers):
"""Test successful translation file download"""
# Mock that file exists
mock_exists.return_value = True
response = client.get("/download-translation", headers=auth_headers)
assert response.status_code == 200
# Check response headers
assert response.headers["content-type"] == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
assert "attachment" in response.headers["content-disposition"]
# The filename should be in the content disposition header
content_disposition = response.headers["content-disposition"]
assert "filename" in content_disposition
@patch('routes.auth.os.path.exists')
@patch('builtins.open', new_callable=mock_open, read_data=b"test content")
def test_download_translation_with_metadata(self, mock_file, mock_exists, client, auth_headers):
"""Test translation download with metadata filename"""
# Mock that files exist
mock_exists.side_effect = lambda path: True
response = client.get("/download-translation", headers=auth_headers)
assert response.status_code == 200
# Check that we get a valid response with proper headers
assert response.headers["content-type"] == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
assert "attachment" in response.headers["content-disposition"]
assert "filename" in response.headers["content-disposition"]
@patch('routes.auth.os.path.exists')
def test_download_translation_not_found(self, mock_exists, client, auth_headers):
"""Test downloading translation file when none exists"""
# Mock that file doesn't exist
mock_exists.return_value = False
response = client.get("/download-translation", headers=auth_headers)
assert response.status_code == 404
data = response.json()
assert data["detail"] == "No translation file found"
@patch('routes.auth.os.path.exists')
@patch('builtins.open', side_effect=Exception("File read error"))
def test_download_translation_read_error(self, mock_file, mock_exists, client, auth_headers):
"""Test translation download with file read error"""
mock_exists.return_value = True
# Should raise an exception when file read fails
with pytest.raises(Exception, match="File read error"):
client.get("/download-translation", headers=auth_headers)
def test_check_translation_status_no_file(self, client):
"""Test translation status check when no file exists"""
with patch('routes.auth.os.path.exists') as mock_exists:
mock_exists.return_value = False
response = client.get("/translations/status")
assert response.status_code == 200
data = response.json()
assert data["file_exists"] is False
assert data["file_name"] is None
@patch('routes.auth.os.path.exists')
@patch('builtins.open', new_callable=mock_open, read_data=b"custom_filename.xlsx")
def test_check_translation_status_with_file(self, mock_file, mock_exists, client):
"""Test translation status check when file exists"""
# Mock that files exist
mock_exists.side_effect = lambda path: True
response = client.get("/translations/status")
assert response.status_code == 200
data = response.json()
assert data["file_exists"] is True
assert data["file_name"] == "custom_filename.xlsx"
@patch('routes.auth.os.path.exists')
@patch('builtins.open', side_effect=Exception("Metadata read error"))
def test_check_translation_status_metadata_error(self, mock_file, mock_exists, client):
"""Test translation status check with metadata read error"""
# Mock that files exist
mock_exists.side_effect = lambda path: True
response = client.get("/translations/status")
assert response.status_code == 200
data = response.json()
# Should fall back to default filename
assert data["file_exists"] is True
assert data["file_name"] == "translation.xlsx"
def test_get_latest_translation_no_file(self, client):
"""Test latest translation endpoint when no file exists"""
with patch('routes.auth.os.path.exists') as mock_exists:
mock_exists.return_value = False
response = client.get("/translations/latest")
assert response.status_code == 404
data = response.json()
assert data["detail"] == "No translation file found"
@patch('routes.auth.os.path.exists')
@patch('builtins.open', new_callable=mock_open, read_data=b"test content")
def test_get_latest_translation_success(self, mock_file, mock_exists, client):
"""Test successful latest translation download"""
# Mock that files exist
mock_exists.side_effect = lambda path: True
response = client.get("/translations/latest")
assert response.status_code == 200
# Check response headers
assert response.headers["content-type"] == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
assert "attachment" in response.headers["content-disposition"]
@patch('routes.auth.os.path.exists')
@patch('builtins.open', new_callable=mock_open, read_data=b"test content")
def test_get_latest_translation_with_metadata(self, mock_file, mock_exists, client):
"""Test latest translation download with metadata filename"""
# Mock that files exist
mock_exists.side_effect = lambda path: True
response = client.get("/translations/latest")
assert response.status_code == 200
# Check that we get a valid response with proper headers
assert response.headers["content-type"] == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
assert "attachment" in response.headers["content-disposition"]
assert "filename" in response.headers["content-disposition"]
def test_upload_translation_invalid_file_type(self, client, auth_headers):
"""Test uploading non-Excel file"""
file_content = b"not an excel file"
response = client.post(
"/upload-translations",
files={"file": ("test.txt", file_content, "text/plain")},
headers=auth_headers
)
# Should still accept the file since validation is not strict
assert response.status_code in [200, 400] # Depends on implementation
def test_upload_translation_empty_file(self, client, auth_headers):
"""Test uploading empty file"""
with patch('routes.auth.os.path.exists') as mock_exists:
mock_exists.return_value = False
response = client.post(
"/upload-translations",
files={"file": ("empty.xlsx", b"", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")},
headers=auth_headers
)
assert response.status_code == 200
data = response.json()
assert data["message"] == "Translation file uploaded successfully"
def test_upload_translation_large_file(self, client, auth_headers):
"""Test uploading large file"""
with patch('routes.auth.os.path.exists') as mock_exists:
mock_exists.return_value = False
# Create a large file content (1MB)
large_content = b"x" * (1024 * 1024)
response = client.post(
"/upload-translations",
files={"file": ("large.xlsx", large_content, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")},
headers=auth_headers
)
assert response.status_code == 200
data = response.json()
assert data["message"] == "Translation file uploaded successfully"
@patch('routes.auth.os.path.exists')
@patch('routes.auth.os.makedirs')
@patch('builtins.open', new_callable=mock_open)
def test_upload_translation_no_filename(self, mock_file, mock_makedirs, mock_exists, client, auth_headers):
"""Test uploading file with minimal filename"""
mock_exists.return_value = False
file_content = b"test content"
response = client.post(
"/upload-translations",
files={"file": ("test.xlsx", file_content, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")},
headers=auth_headers
)
# Should handle the upload successfully
assert response.status_code == 200
data = response.json()
assert data["filename"] == "test.xlsx"