"""Unit tests for FileUploadConfig model.""" from datetime import datetime, timezone from abacus_common_logic.connectors.database import db from abacus_file_upload.constants import UPLOAD_TYPES from abacus_file_upload.models import FileUploadConfig from abacus_file_upload.tests.utils.factories import ( FileUploadConfigFactory, FileUploadFactory, ) class TestFileUploadConfigModel: """Tests for FileUploadConfig model.""" def test_create_file_upload_config_with_all_fields(self): """Test creating a FileUploadConfig with all fields.""" config = FileUploadConfigFactory( upload_type=UPLOAD_TYPES.ADJUSTMENTS, s3_key_template='{year}/{month}/{file_key}.{ext}', allowed_file_types=['csv', 'xlsx', 'pdf'], max_file_size_bytes=20971520, # 20MB multipart_threshold_bytes=209715200, # 200MB min_multipart_chunk_size_bytes=20971520, # 20MB description='Test adjustment file uploads', ) assert config.file_upload_config_id is not None assert config.upload_type == UPLOAD_TYPES.ADJUSTMENTS assert config.s3_key_template == '{year}/{month}/{file_key}.{ext}' assert config.allowed_file_types == ['csv', 'xlsx', 'pdf'] assert config.max_file_size_bytes == 20971520 assert config.multipart_threshold_bytes == 209715200 assert config.min_multipart_chunk_size_bytes == 20971520 assert config.description == 'Test adjustment file uploads' assert config.event_name is None assert config.created_at is not None assert config.created_by is not None assert config.deleted_at is None def test_create_file_upload_config_with_minimal_fields(self): """Test creating a FileUploadConfig with only required fields.""" config = FileUploadConfigFactory( upload_type=UPLOAD_TYPES.FLOWTHROUGH, allowed_file_types=None, description=None, ) assert config.file_upload_config_id is not None assert config.upload_type == UPLOAD_TYPES.FLOWTHROUGH assert config.s3_key_template == '{year}/{month}/{file_key}.{ext}' assert config.allowed_file_types is None assert config.max_file_size_bytes == 10485760 # default 10MB assert config.multipart_threshold_bytes == 104857600 # default 100MB assert config.min_multipart_chunk_size_bytes == 10485760 # default 10MB assert config.description is None def test_create_file_upload_config_with_null_allowed_file_types(self): """Test that NULL allowed_file_types means allow all extensions.""" config = FileUploadConfigFactory( upload_type=UPLOAD_TYPES.ADJUSTMENTS, allowed_file_types=None ) assert config.allowed_file_types is None # NULL means allow all def test_file_upload_config_defaults(self): """Test that default values are properly set.""" config = FileUploadConfigFactory() # Check defaults assert config.s3_key_template == '{year}/{month}/{file_key}.{ext}' assert config.max_file_size_bytes == 10485760 assert config.multipart_threshold_bytes == 104857600 assert config.min_multipart_chunk_size_bytes == 10485760 def test_find_by_upload_type_active_config(self): """Test finding active config by upload_type.""" config = FileUploadConfigFactory(upload_type=UPLOAD_TYPES.ADJUSTMENTS) found_config = FileUploadConfig.find_by_upload_type(UPLOAD_TYPES.ADJUSTMENTS) assert found_config is not None assert found_config.file_upload_config_id == config.file_upload_config_id assert found_config.upload_type == UPLOAD_TYPES.ADJUSTMENTS def test_find_by_upload_type_not_found(self): """Test finding config by upload_type when it doesn't exist.""" found_config = FileUploadConfig.find_by_upload_type('nonexistent_type') assert found_config is None def test_find_by_upload_type_excludes_soft_deleted(self): """Test that soft-deleted configs are excluded from find_by_upload_type.""" config = FileUploadConfigFactory(upload_type=UPLOAD_TYPES.ADJUSTMENTS) # Soft delete the config config.deleted_at = datetime.now(timezone.utc) config.deleted_by = 'test_user@example.com' db.session.commit() # Should not find the soft-deleted config found_config = FileUploadConfig.find_by_upload_type(UPLOAD_TYPES.ADJUSTMENTS) assert found_config is None def test_find_by_upload_type_with_multiple_configs(self): """Test that find_by_upload_type returns the correct config when multiple exist.""" config1 = FileUploadConfigFactory(upload_type=UPLOAD_TYPES.ADJUSTMENTS) config2 = FileUploadConfigFactory(upload_type=UPLOAD_TYPES.FLOWTHROUGH) found_config = FileUploadConfig.find_by_upload_type(UPLOAD_TYPES.FLOWTHROUGH) assert found_config is not None assert found_config.file_upload_config_id == config2.file_upload_config_id assert found_config.upload_type == UPLOAD_TYPES.FLOWTHROUGH def test_default_order(self): """Test that default ordering is by upload_type.""" config1 = FileUploadConfigFactory(upload_type=UPLOAD_TYPES.FLOWTHROUGH) config2 = FileUploadConfigFactory(upload_type=UPLOAD_TYPES.ADJUSTMENTS) configs = FileUploadConfig.query.order_by( FileUploadConfig.default_order() ).all() # Should be ordered alphabetically by upload_type (adjustments comes before flowthrough) assert configs[0].upload_type == UPLOAD_TYPES.ADJUSTMENTS assert configs[1].upload_type == UPLOAD_TYPES.FLOWTHROUGH def test_file_upload_config_relationship(self): """Test relationship between FileUploadConfig and FileUpload.""" # FileUploadFactory creates both config and upload upload = FileUploadFactory() config = upload.file_upload_config assert config is not None assert config.file_upload_config_id == upload.file_upload_config_id # Test reverse relationship file_uploads = config.file_uploads.all() assert len(file_uploads) == 1 assert file_uploads[0].file_upload_id == upload.file_upload_id def test_file_upload_config_json_field(self): """Test that allowed_file_types JSON field works correctly.""" file_types = ['csv', 'xlsx', 'pdf', 'txt'] config = FileUploadConfigFactory(allowed_file_types=file_types) assert config.allowed_file_types == file_types assert isinstance(config.allowed_file_types, list) assert len(config.allowed_file_types) == 4 assert 'csv' in config.allowed_file_types def test_soft_delete_file_upload_config(self): """Test soft deleting a FileUploadConfig.""" config = FileUploadConfigFactory(upload_type=UPLOAD_TYPES.ADJUSTMENTS) # Soft delete config.deleted_at = datetime.now(timezone.utc) config.deleted_by = 'test_user@example.com' db.session.commit() # Should still exist in database but have deleted_at set found_config = FileUploadConfig.query.filter_by( file_upload_config_id=config.file_upload_config_id ).first() assert found_config is not None assert found_config.deleted_at is not None assert found_config.deleted_by == 'test_user@example.com' def test_update_file_upload_config(self): """Test updating a FileUploadConfig.""" config = FileUploadConfigFactory( upload_type=UPLOAD_TYPES.ADJUSTMENTS, max_file_size_bytes=10485760 ) # Update max file size config.max_file_size_bytes = 52428800 # 50MB config.description = 'Updated description' db.session.commit() # Verify updates updated_config = FileUploadConfig.query.get(config.file_upload_config_id) assert updated_config.max_file_size_bytes == 52428800 assert updated_config.description == 'Updated description' def test_file_upload_config_timestamps(self): """Test that timestamp fields are properly set.""" config = FileUploadConfigFactory() assert config.created_at is not None assert isinstance(config.created_at, datetime) assert config.created_by is not None class TestFileUploadConfigValidation: """Tests for FileUploadConfig validation.""" def test_large_file_size_limits(self): """Test creating config with large file size limits.""" # S3 maximum is 5TB five_tb = 5 * 1024 * 1024 * 1024 * 1024 config = FileUploadConfigFactory( max_file_size_bytes=five_tb, multipart_threshold_bytes=five_tb, min_multipart_chunk_size_bytes=5 * 1024 * 1024 * 1024, # 5GB ) assert config.max_file_size_bytes == five_tb assert config.multipart_threshold_bytes == five_tb def test_s3_key_template_variations(self): """Test different S3 key template patterns.""" templates = [ ('{file_key}.{ext}', UPLOAD_TYPES.ADJUSTMENTS), ('{year}/{month}/{file_key}.{ext}', UPLOAD_TYPES.FLOWTHROUGH), ] for template, upload_type in templates: config = FileUploadConfigFactory( s3_key_template=template, upload_type=upload_type ) assert config.s3_key_template == template