| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913 |
- import hashlib
- from io import BytesIO
- from unittest.mock import patch
- import pytest
- from faker import Faker
- from werkzeug.exceptions import NotFound
- from configs import dify_config
- from models.account import Account, Tenant
- from models.enums import CreatorUserRole
- from models.model import EndUser, UploadFile
- from services.errors.file import FileTooLargeError, UnsupportedFileTypeError
- from services.file_service import FileService
- class TestFileService:
- """Integration tests for FileService using testcontainers."""
- @pytest.fixture
- def mock_external_service_dependencies(self):
- """Mock setup for external service dependencies."""
- with (
- patch("services.file_service.storage") as mock_storage,
- patch("services.file_service.file_helpers") as mock_file_helpers,
- patch("services.file_service.ExtractProcessor") as mock_extract_processor,
- ):
- # Setup default mock returns
- mock_storage.save.return_value = None
- mock_storage.load.return_value = BytesIO(b"mock file content")
- mock_file_helpers.get_signed_file_url.return_value = "https://example.com/signed-url"
- mock_file_helpers.verify_image_signature.return_value = True
- mock_file_helpers.verify_file_signature.return_value = True
- mock_extract_processor.load_from_upload_file.return_value = "extracted text content"
- yield {
- "storage": mock_storage,
- "file_helpers": mock_file_helpers,
- "extract_processor": mock_extract_processor,
- }
- def _create_test_account(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Helper method to create a test account for testing.
- Args:
- db_session_with_containers: Database session from testcontainers infrastructure
- mock_external_service_dependencies: Mock dependencies
- Returns:
- Account: Created account instance
- """
- fake = Faker()
- # Create account
- account = Account(
- email=fake.email(),
- name=fake.name(),
- interface_language="en-US",
- status="active",
- )
- from extensions.ext_database import db
- db.session.add(account)
- db.session.commit()
- # Create tenant for the account
- tenant = Tenant(
- name=fake.company(),
- status="normal",
- )
- db.session.add(tenant)
- db.session.commit()
- # Create tenant-account join
- from models.account import TenantAccountJoin, TenantAccountRole
- join = TenantAccountJoin(
- tenant_id=tenant.id,
- account_id=account.id,
- role=TenantAccountRole.OWNER.value,
- current=True,
- )
- db.session.add(join)
- db.session.commit()
- # Set current tenant for account
- account.current_tenant = tenant
- return account
- def _create_test_end_user(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Helper method to create a test end user for testing.
- Args:
- db_session_with_containers: Database session from testcontainers infrastructure
- mock_external_service_dependencies: Mock dependencies
- Returns:
- EndUser: Created end user instance
- """
- fake = Faker()
- end_user = EndUser(
- tenant_id=str(fake.uuid4()),
- type="web",
- name=fake.name(),
- is_anonymous=False,
- session_id=fake.uuid4(),
- )
- from extensions.ext_database import db
- db.session.add(end_user)
- db.session.commit()
- return end_user
- def _create_test_upload_file(self, db_session_with_containers, mock_external_service_dependencies, account):
- """
- Helper method to create a test upload file for testing.
- Args:
- db_session_with_containers: Database session from testcontainers infrastructure
- mock_external_service_dependencies: Mock dependencies
- account: Account instance
- Returns:
- UploadFile: Created upload file instance
- """
- fake = Faker()
- upload_file = UploadFile(
- tenant_id=account.current_tenant_id if hasattr(account, "current_tenant_id") else str(fake.uuid4()),
- storage_type="local",
- key=f"upload_files/test/{fake.uuid4()}.txt",
- name="test_file.txt",
- size=1024,
- extension="txt",
- mime_type="text/plain",
- created_by_role=CreatorUserRole.ACCOUNT,
- created_by=account.id,
- created_at=fake.date_time(),
- used=False,
- hash=hashlib.sha3_256(b"test content").hexdigest(),
- source_url="",
- )
- from extensions.ext_database import db
- db.session.add(upload_file)
- db.session.commit()
- return upload_file
- # Test upload_file method
- def test_upload_file_success(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test successful file upload with valid parameters.
- """
- fake = Faker()
- account = self._create_test_account(db_session_with_containers, mock_external_service_dependencies)
- filename = "test_document.pdf"
- content = b"test file content"
- mimetype = "application/pdf"
- upload_file = FileService.upload_file(
- filename=filename,
- content=content,
- mimetype=mimetype,
- user=account,
- )
- assert upload_file is not None
- assert upload_file.name == filename
- assert upload_file.size == len(content)
- assert upload_file.extension == "pdf"
- assert upload_file.mime_type == mimetype
- assert upload_file.created_by == account.id
- assert upload_file.created_by_role == CreatorUserRole.ACCOUNT.value
- assert upload_file.used is False
- assert upload_file.hash == hashlib.sha3_256(content).hexdigest()
- # Verify storage was called
- mock_external_service_dependencies["storage"].save.assert_called_once()
- # Verify database state
- from extensions.ext_database import db
- db.session.refresh(upload_file)
- assert upload_file.id is not None
- def test_upload_file_with_end_user(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test file upload with end user instead of account.
- """
- fake = Faker()
- end_user = self._create_test_end_user(db_session_with_containers, mock_external_service_dependencies)
- filename = "test_image.jpg"
- content = b"test image content"
- mimetype = "image/jpeg"
- upload_file = FileService.upload_file(
- filename=filename,
- content=content,
- mimetype=mimetype,
- user=end_user,
- )
- assert upload_file is not None
- assert upload_file.created_by == end_user.id
- assert upload_file.created_by_role == CreatorUserRole.END_USER.value
- def test_upload_file_with_datasets_source(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test file upload with datasets source parameter.
- """
- fake = Faker()
- account = self._create_test_account(db_session_with_containers, mock_external_service_dependencies)
- filename = "test_document.pdf"
- content = b"test file content"
- mimetype = "application/pdf"
- upload_file = FileService.upload_file(
- filename=filename,
- content=content,
- mimetype=mimetype,
- user=account,
- source="datasets",
- source_url="https://example.com/source",
- )
- assert upload_file is not None
- assert upload_file.source_url == "https://example.com/source"
- def test_upload_file_invalid_filename_characters(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test file upload with invalid filename characters.
- """
- fake = Faker()
- account = self._create_test_account(db_session_with_containers, mock_external_service_dependencies)
- filename = "test/file<name>.txt"
- content = b"test content"
- mimetype = "text/plain"
- with pytest.raises(ValueError, match="Filename contains invalid characters"):
- FileService.upload_file(
- filename=filename,
- content=content,
- mimetype=mimetype,
- user=account,
- )
- def test_upload_file_filename_too_long(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test file upload with filename that exceeds length limit.
- """
- fake = Faker()
- account = self._create_test_account(db_session_with_containers, mock_external_service_dependencies)
- # Create a filename longer than 200 characters
- long_name = "a" * 250
- filename = f"{long_name}.txt"
- content = b"test content"
- mimetype = "text/plain"
- upload_file = FileService.upload_file(
- filename=filename,
- content=content,
- mimetype=mimetype,
- user=account,
- )
- # Verify filename was truncated (the logic truncates the base name to 200 chars + extension)
- # So the total length should be <= 200 + len(extension) + 1 (for the dot)
- assert len(upload_file.name) <= 200 + len(upload_file.extension) + 1
- assert upload_file.name.endswith(".txt")
- # Verify the base name was truncated
- base_name = upload_file.name[:-4] # Remove .txt
- assert len(base_name) <= 200
- def test_upload_file_datasets_unsupported_type(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test file upload for datasets with unsupported file type.
- """
- fake = Faker()
- account = self._create_test_account(db_session_with_containers, mock_external_service_dependencies)
- filename = "test_image.jpg"
- content = b"test content"
- mimetype = "image/jpeg"
- with pytest.raises(UnsupportedFileTypeError):
- FileService.upload_file(
- filename=filename,
- content=content,
- mimetype=mimetype,
- user=account,
- source="datasets",
- )
- def test_upload_file_too_large(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test file upload with file size exceeding limit.
- """
- fake = Faker()
- account = self._create_test_account(db_session_with_containers, mock_external_service_dependencies)
- filename = "large_image.jpg"
- # Create content larger than the limit
- content = b"x" * (dify_config.UPLOAD_IMAGE_FILE_SIZE_LIMIT * 1024 * 1024 + 1)
- mimetype = "image/jpeg"
- with pytest.raises(FileTooLargeError):
- FileService.upload_file(
- filename=filename,
- content=content,
- mimetype=mimetype,
- user=account,
- )
- # Test is_file_size_within_limit method
- def test_is_file_size_within_limit_image_success(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test file size check for image files within limit.
- """
- extension = "jpg"
- file_size = dify_config.UPLOAD_IMAGE_FILE_SIZE_LIMIT * 1024 * 1024 # Exactly at limit
- result = FileService.is_file_size_within_limit(extension=extension, file_size=file_size)
- assert result is True
- def test_is_file_size_within_limit_video_success(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test file size check for video files within limit.
- """
- extension = "mp4"
- file_size = dify_config.UPLOAD_VIDEO_FILE_SIZE_LIMIT * 1024 * 1024 # Exactly at limit
- result = FileService.is_file_size_within_limit(extension=extension, file_size=file_size)
- assert result is True
- def test_is_file_size_within_limit_audio_success(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test file size check for audio files within limit.
- """
- extension = "mp3"
- file_size = dify_config.UPLOAD_AUDIO_FILE_SIZE_LIMIT * 1024 * 1024 # Exactly at limit
- result = FileService.is_file_size_within_limit(extension=extension, file_size=file_size)
- assert result is True
- def test_is_file_size_within_limit_document_success(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test file size check for document files within limit.
- """
- extension = "pdf"
- file_size = dify_config.UPLOAD_FILE_SIZE_LIMIT * 1024 * 1024 # Exactly at limit
- result = FileService.is_file_size_within_limit(extension=extension, file_size=file_size)
- assert result is True
- def test_is_file_size_within_limit_image_exceeded(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test file size check for image files exceeding limit.
- """
- extension = "jpg"
- file_size = dify_config.UPLOAD_IMAGE_FILE_SIZE_LIMIT * 1024 * 1024 + 1 # Exceeds limit
- result = FileService.is_file_size_within_limit(extension=extension, file_size=file_size)
- assert result is False
- def test_is_file_size_within_limit_unknown_extension(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test file size check for unknown file extension.
- """
- extension = "xyz"
- file_size = dify_config.UPLOAD_FILE_SIZE_LIMIT * 1024 * 1024 # Uses default limit
- result = FileService.is_file_size_within_limit(extension=extension, file_size=file_size)
- assert result is True
- # Test upload_text method
- def test_upload_text_success(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test successful text upload.
- """
- fake = Faker()
- text = "This is a test text content"
- text_name = "test_text.txt"
- # Mock current_user
- with patch("services.file_service.current_user") as mock_current_user:
- mock_current_user.current_tenant_id = str(fake.uuid4())
- mock_current_user.id = str(fake.uuid4())
- upload_file = FileService.upload_text(text=text, text_name=text_name)
- assert upload_file is not None
- assert upload_file.name == text_name
- assert upload_file.size == len(text)
- assert upload_file.extension == "txt"
- assert upload_file.mime_type == "text/plain"
- assert upload_file.used is True
- assert upload_file.used_by == mock_current_user.id
- # Verify storage was called
- mock_external_service_dependencies["storage"].save.assert_called_once()
- def test_upload_text_name_too_long(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test text upload with name that exceeds length limit.
- """
- fake = Faker()
- text = "test content"
- long_name = "a" * 250 # Longer than 200 characters
- # Mock current_user
- with patch("services.file_service.current_user") as mock_current_user:
- mock_current_user.current_tenant_id = str(fake.uuid4())
- mock_current_user.id = str(fake.uuid4())
- upload_file = FileService.upload_text(text=text, text_name=long_name)
- # Verify name was truncated
- assert len(upload_file.name) <= 200
- assert upload_file.name == "a" * 200
- # Test get_file_preview method
- def test_get_file_preview_success(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test successful file preview generation.
- """
- fake = Faker()
- account = self._create_test_account(db_session_with_containers, mock_external_service_dependencies)
- upload_file = self._create_test_upload_file(
- db_session_with_containers, mock_external_service_dependencies, account
- )
- # Update file to have document extension
- upload_file.extension = "pdf"
- from extensions.ext_database import db
- db.session.commit()
- result = FileService.get_file_preview(file_id=upload_file.id)
- assert result == "extracted text content"
- mock_external_service_dependencies["extract_processor"].load_from_upload_file.assert_called_once()
- def test_get_file_preview_file_not_found(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test file preview with non-existent file.
- """
- fake = Faker()
- non_existent_id = str(fake.uuid4())
- with pytest.raises(NotFound, match="File not found"):
- FileService.get_file_preview(file_id=non_existent_id)
- def test_get_file_preview_unsupported_file_type(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test file preview with unsupported file type.
- """
- fake = Faker()
- account = self._create_test_account(db_session_with_containers, mock_external_service_dependencies)
- upload_file = self._create_test_upload_file(
- db_session_with_containers, mock_external_service_dependencies, account
- )
- # Update file to have non-document extension
- upload_file.extension = "jpg"
- from extensions.ext_database import db
- db.session.commit()
- with pytest.raises(UnsupportedFileTypeError):
- FileService.get_file_preview(file_id=upload_file.id)
- def test_get_file_preview_text_truncation(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test file preview with text that exceeds preview limit.
- """
- fake = Faker()
- account = self._create_test_account(db_session_with_containers, mock_external_service_dependencies)
- upload_file = self._create_test_upload_file(
- db_session_with_containers, mock_external_service_dependencies, account
- )
- # Update file to have document extension
- upload_file.extension = "pdf"
- from extensions.ext_database import db
- db.session.commit()
- # Mock long text content
- long_text = "x" * 5000 # Longer than PREVIEW_WORDS_LIMIT
- mock_external_service_dependencies["extract_processor"].load_from_upload_file.return_value = long_text
- result = FileService.get_file_preview(file_id=upload_file.id)
- assert len(result) == 3000 # PREVIEW_WORDS_LIMIT
- assert result == "x" * 3000
- # Test get_image_preview method
- def test_get_image_preview_success(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test successful image preview generation.
- """
- fake = Faker()
- account = self._create_test_account(db_session_with_containers, mock_external_service_dependencies)
- upload_file = self._create_test_upload_file(
- db_session_with_containers, mock_external_service_dependencies, account
- )
- # Update file to have image extension
- upload_file.extension = "jpg"
- from extensions.ext_database import db
- db.session.commit()
- timestamp = "1234567890"
- nonce = "test_nonce"
- sign = "test_signature"
- generator, mime_type = FileService.get_image_preview(
- file_id=upload_file.id,
- timestamp=timestamp,
- nonce=nonce,
- sign=sign,
- )
- assert generator is not None
- assert mime_type == upload_file.mime_type
- mock_external_service_dependencies["file_helpers"].verify_image_signature.assert_called_once()
- def test_get_image_preview_invalid_signature(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test image preview with invalid signature.
- """
- fake = Faker()
- account = self._create_test_account(db_session_with_containers, mock_external_service_dependencies)
- upload_file = self._create_test_upload_file(
- db_session_with_containers, mock_external_service_dependencies, account
- )
- # Mock invalid signature
- mock_external_service_dependencies["file_helpers"].verify_image_signature.return_value = False
- timestamp = "1234567890"
- nonce = "test_nonce"
- sign = "invalid_signature"
- with pytest.raises(NotFound, match="File not found or signature is invalid"):
- FileService.get_image_preview(
- file_id=upload_file.id,
- timestamp=timestamp,
- nonce=nonce,
- sign=sign,
- )
- def test_get_image_preview_file_not_found(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test image preview with non-existent file.
- """
- fake = Faker()
- non_existent_id = str(fake.uuid4())
- timestamp = "1234567890"
- nonce = "test_nonce"
- sign = "test_signature"
- with pytest.raises(NotFound, match="File not found or signature is invalid"):
- FileService.get_image_preview(
- file_id=non_existent_id,
- timestamp=timestamp,
- nonce=nonce,
- sign=sign,
- )
- def test_get_image_preview_unsupported_file_type(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test image preview with non-image file type.
- """
- fake = Faker()
- account = self._create_test_account(db_session_with_containers, mock_external_service_dependencies)
- upload_file = self._create_test_upload_file(
- db_session_with_containers, mock_external_service_dependencies, account
- )
- # Update file to have non-image extension
- upload_file.extension = "pdf"
- from extensions.ext_database import db
- db.session.commit()
- timestamp = "1234567890"
- nonce = "test_nonce"
- sign = "test_signature"
- with pytest.raises(UnsupportedFileTypeError):
- FileService.get_image_preview(
- file_id=upload_file.id,
- timestamp=timestamp,
- nonce=nonce,
- sign=sign,
- )
- # Test get_file_generator_by_file_id method
- def test_get_file_generator_by_file_id_success(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test successful file generator retrieval.
- """
- fake = Faker()
- account = self._create_test_account(db_session_with_containers, mock_external_service_dependencies)
- upload_file = self._create_test_upload_file(
- db_session_with_containers, mock_external_service_dependencies, account
- )
- timestamp = "1234567890"
- nonce = "test_nonce"
- sign = "test_signature"
- generator, file_obj = FileService.get_file_generator_by_file_id(
- file_id=upload_file.id,
- timestamp=timestamp,
- nonce=nonce,
- sign=sign,
- )
- assert generator is not None
- assert file_obj == upload_file
- mock_external_service_dependencies["file_helpers"].verify_file_signature.assert_called_once()
- def test_get_file_generator_by_file_id_invalid_signature(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test file generator retrieval with invalid signature.
- """
- fake = Faker()
- account = self._create_test_account(db_session_with_containers, mock_external_service_dependencies)
- upload_file = self._create_test_upload_file(
- db_session_with_containers, mock_external_service_dependencies, account
- )
- # Mock invalid signature
- mock_external_service_dependencies["file_helpers"].verify_file_signature.return_value = False
- timestamp = "1234567890"
- nonce = "test_nonce"
- sign = "invalid_signature"
- with pytest.raises(NotFound, match="File not found or signature is invalid"):
- FileService.get_file_generator_by_file_id(
- file_id=upload_file.id,
- timestamp=timestamp,
- nonce=nonce,
- sign=sign,
- )
- def test_get_file_generator_by_file_id_file_not_found(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test file generator retrieval with non-existent file.
- """
- fake = Faker()
- non_existent_id = str(fake.uuid4())
- timestamp = "1234567890"
- nonce = "test_nonce"
- sign = "test_signature"
- with pytest.raises(NotFound, match="File not found or signature is invalid"):
- FileService.get_file_generator_by_file_id(
- file_id=non_existent_id,
- timestamp=timestamp,
- nonce=nonce,
- sign=sign,
- )
- # Test get_public_image_preview method
- def test_get_public_image_preview_success(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test successful public image preview generation.
- """
- fake = Faker()
- account = self._create_test_account(db_session_with_containers, mock_external_service_dependencies)
- upload_file = self._create_test_upload_file(
- db_session_with_containers, mock_external_service_dependencies, account
- )
- # Update file to have image extension
- upload_file.extension = "jpg"
- from extensions.ext_database import db
- db.session.commit()
- generator, mime_type = FileService.get_public_image_preview(file_id=upload_file.id)
- assert generator is not None
- assert mime_type == upload_file.mime_type
- mock_external_service_dependencies["storage"].load.assert_called_once()
- def test_get_public_image_preview_file_not_found(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test public image preview with non-existent file.
- """
- fake = Faker()
- non_existent_id = str(fake.uuid4())
- with pytest.raises(NotFound, match="File not found or signature is invalid"):
- FileService.get_public_image_preview(file_id=non_existent_id)
- def test_get_public_image_preview_unsupported_file_type(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test public image preview with non-image file type.
- """
- fake = Faker()
- account = self._create_test_account(db_session_with_containers, mock_external_service_dependencies)
- upload_file = self._create_test_upload_file(
- db_session_with_containers, mock_external_service_dependencies, account
- )
- # Update file to have non-image extension
- upload_file.extension = "pdf"
- from extensions.ext_database import db
- db.session.commit()
- with pytest.raises(UnsupportedFileTypeError):
- FileService.get_public_image_preview(file_id=upload_file.id)
- # Test edge cases and boundary conditions
- def test_upload_file_empty_content(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test file upload with empty content.
- """
- fake = Faker()
- account = self._create_test_account(db_session_with_containers, mock_external_service_dependencies)
- filename = "empty.txt"
- content = b""
- mimetype = "text/plain"
- upload_file = FileService.upload_file(
- filename=filename,
- content=content,
- mimetype=mimetype,
- user=account,
- )
- assert upload_file is not None
- assert upload_file.size == 0
- def test_upload_file_special_characters_in_name(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test file upload with special characters in filename (but valid ones).
- """
- fake = Faker()
- account = self._create_test_account(db_session_with_containers, mock_external_service_dependencies)
- filename = "test-file_with_underscores_and.dots.txt"
- content = b"test content"
- mimetype = "text/plain"
- upload_file = FileService.upload_file(
- filename=filename,
- content=content,
- mimetype=mimetype,
- user=account,
- )
- assert upload_file is not None
- assert upload_file.name == filename
- def test_upload_file_different_case_extensions(
- self, db_session_with_containers, mock_external_service_dependencies
- ):
- """
- Test file upload with different case extensions.
- """
- fake = Faker()
- account = self._create_test_account(db_session_with_containers, mock_external_service_dependencies)
- filename = "test.PDF"
- content = b"test content"
- mimetype = "application/pdf"
- upload_file = FileService.upload_file(
- filename=filename,
- content=content,
- mimetype=mimetype,
- user=account,
- )
- assert upload_file is not None
- assert upload_file.extension == "pdf" # Should be converted to lowercase
- def test_upload_text_empty_text(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test text upload with empty text.
- """
- fake = Faker()
- text = ""
- text_name = "empty.txt"
- # Mock current_user
- with patch("services.file_service.current_user") as mock_current_user:
- mock_current_user.current_tenant_id = str(fake.uuid4())
- mock_current_user.id = str(fake.uuid4())
- upload_file = FileService.upload_text(text=text, text_name=text_name)
- assert upload_file is not None
- assert upload_file.size == 0
- def test_file_size_limits_edge_cases(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test file size limits with edge case values.
- """
- # Test exactly at limit
- for extension, limit_config in [
- ("jpg", dify_config.UPLOAD_IMAGE_FILE_SIZE_LIMIT),
- ("mp4", dify_config.UPLOAD_VIDEO_FILE_SIZE_LIMIT),
- ("mp3", dify_config.UPLOAD_AUDIO_FILE_SIZE_LIMIT),
- ("pdf", dify_config.UPLOAD_FILE_SIZE_LIMIT),
- ]:
- file_size = limit_config * 1024 * 1024
- result = FileService.is_file_size_within_limit(extension=extension, file_size=file_size)
- assert result is True
- # Test one byte over limit
- file_size = limit_config * 1024 * 1024 + 1
- result = FileService.is_file_size_within_limit(extension=extension, file_size=file_size)
- assert result is False
- def test_upload_file_with_source_url(self, db_session_with_containers, mock_external_service_dependencies):
- """
- Test file upload with source URL that gets overridden by signed URL.
- """
- fake = Faker()
- account = self._create_test_account(db_session_with_containers, mock_external_service_dependencies)
- filename = "test.pdf"
- content = b"test content"
- mimetype = "application/pdf"
- source_url = "https://original-source.com/file.pdf"
- upload_file = FileService.upload_file(
- filename=filename,
- content=content,
- mimetype=mimetype,
- user=account,
- source_url=source_url,
- )
- # When source_url is provided, it should be preserved
- assert upload_file.source_url == source_url
- # The signed URL should only be set when source_url is empty
- # Let's test that scenario
- upload_file2 = FileService.upload_file(
- filename="test2.pdf",
- content=b"test content 2",
- mimetype="application/pdf",
- user=account,
- source_url="", # Empty source_url
- )
- # Should have the signed URL when source_url is empty
- assert upload_file2.source_url == "https://example.com/signed-url"
|