"""Tests for abacus_file_upload.blueprints.file_upload_config module.""" from unittest.mock import patch from owsresponse import response class TestFileUploadConfigCreateView: """Tests for POST /file-upload-config endpoint.""" @patch('abacus_file_upload.blueprints.file_upload_config.flask_request') def test_create_config_unauthorized(self, mock_flask_request, fixture_client): """Test create config returns 401 when unauthorized.""" mock_flask_request.verify_rules_access_standalone.return_value = False resp = fixture_client.post( '/file-upload-config', json={ 'upload_type': 'adjustments', 's3_key_template': '{year}/{month}/{file_key}.{ext}', 'max_file_size_bytes': 10485760, }, ) assert resp.status_code == 401 assert resp.json['message'] == 'Unauthorized' @patch( 'abacus_file_upload.blueprints.file_upload_config.logic.create_file_upload_config' ) @patch('abacus_file_upload.blueprints.file_upload_config.flask_request') def test_create_config_success( self, mock_flask_request, mock_create_config, fixture_client ): """Test successful config creation.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_create_config.return_value = response.Response( message={ 'file_upload_config_id': 1, 'upload_type': 'adjustments', 's3_key_template': '{year}/{month}/{file_key}.{ext}', 'max_file_size_bytes': 10485760, }, status=201, ) resp = fixture_client.post( '/file-upload-config', json={ 'upload_type': 'adjustments', 's3_key_template': '{year}/{month}/{file_key}.{ext}', 'max_file_size_bytes': 10485760, }, ) assert resp.status_code == 201 assert resp.json['upload_type'] == 'adjustments' mock_create_config.assert_called_once() @patch( 'abacus_file_upload.blueprints.file_upload_config.logic.create_file_upload_config' ) @patch('abacus_file_upload.blueprints.file_upload_config.flask_request') def test_create_config_validation_error( self, mock_flask_request, mock_create_config, fixture_client ): """Test config creation with validation error.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_create_config.return_value = response.Response( message='Validation error: upload_type already exists', status=400 ) resp = fixture_client.post( '/file-upload-config', json={ 'upload_type': 'adjustments', 's3_key_template': '{year}/{month}/{file_key}.{ext}', 'max_file_size_bytes': 10485760, }, ) assert resp.status_code == 400 @patch( 'abacus_file_upload.blueprints.file_upload_config.logic.create_file_upload_config' ) @patch('abacus_file_upload.blueprints.file_upload_config.flask_request') def test_create_config_invalid_file_type( self, mock_flask_request, mock_create_config, fixture_client ): """Test config creation with invalid file upload_type.""" mock_flask_request.verify_rules_access_standalone.return_value = True resp = fixture_client.post( '/file-upload-config', json={ 'upload_type': 'test_file', 's3_key_template': '{year}/{month}/{file_key}.{ext}', 'max_file_size_bytes': 10485760, }, ) assert resp.status_code == 400 assert resp.json['message'] == { 'upload_type': ['Must be one of adjustments, flowthrough'] } mock_create_config.assert_not_called() class TestFileUploadConfigListView: """Tests for GET /file-upload-configs endpoint.""" @patch('abacus_file_upload.blueprints.file_upload_config.flask_request') def test_list_configs_unauthorized(self, mock_flask_request, fixture_client): """Test list configs returns 401 when unauthorized.""" mock_flask_request.verify_rules_access_standalone.return_value = False resp = fixture_client.get('/file-upload-configs') assert resp.status_code == 401 assert resp.json['message'] == 'Unauthorized' @patch( 'abacus_file_upload.blueprints.file_upload_config.logic.get_all_file_upload_configs' ) @patch('abacus_file_upload.blueprints.file_upload_config.flask_request') def test_list_configs_success( self, mock_flask_request, mock_get_all_configs, fixture_client ): """Test successful config list retrieval.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_get_all_configs.return_value = response.Response( message=[ { 'file_upload_config_id': 1, 'upload_type': 'adjustments', 's3_key_template': '{year}/{month}/{file_key}.{ext}', 'max_file_size_bytes': 10485760, }, { 'file_upload_config_id': 2, 'upload_type': 'sales', 's3_key_template': '{year}/{month}/{file_key}.{ext}', 'max_file_size_bytes': 20971520, }, ], status=200, ) resp = fixture_client.get('/file-upload-configs') assert resp.status_code == 200 assert len(resp.json) == 2 assert resp.json[0]['upload_type'] == 'adjustments' mock_get_all_configs.assert_called_once_with(verbose=False) @patch( 'abacus_file_upload.blueprints.file_upload_config.logic.get_all_file_upload_configs' ) @patch('abacus_file_upload.blueprints.file_upload_config.flask_request') def test_list_configs_verbose( self, mock_flask_request, mock_get_all_configs, fixture_client ): """Test config list retrieval with verbose parameter.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_get_all_configs.return_value = response.Response( message=[ { 'file_upload_config_id': 1, 'upload_type': 'adjustments', 's3_key_template': '{year}/{month}/{file_key}.{ext}', 'max_file_size_bytes': 10485760, 'created_at': '2025-01-16T11:00:00+00:00', 'created_by': 'test_user', } ], status=200, ) resp = fixture_client.get('/file-upload-configs?verbose=true') assert resp.status_code == 200 mock_get_all_configs.assert_called_once_with(verbose=True) @patch( 'abacus_file_upload.blueprints.file_upload_config.logic.get_all_file_upload_configs' ) @patch('abacus_file_upload.blueprints.file_upload_config.flask_request') def test_list_configs_verbose_variations( self, mock_flask_request, mock_get_all_configs, fixture_client ): """Test verbose parameter accepts multiple boolean formats.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_get_all_configs.return_value = response.Response(message=[], status=200) # Test various boolean formats for verbose_val in ['yes', '1', 'on', 't', 'y']: fixture_client.get(f'/file-upload-configs?verbose={verbose_val}') # Last call should have verbose=True assert mock_get_all_configs.call_args[1]['verbose'] is True # Test false values for verbose_val in ['false', '0', 'off', 'f', 'n']: fixture_client.get(f'/file-upload-configs?verbose={verbose_val}') # Last call should have verbose=False assert mock_get_all_configs.call_args[1]['verbose'] is False @patch( 'abacus_file_upload.blueprints.file_upload_config.logic.get_all_file_upload_configs' ) @patch('abacus_file_upload.blueprints.file_upload_config.flask_request') def test_list_configs_empty( self, mock_flask_request, mock_get_all_configs, fixture_client ): """Test list configs with no results.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_get_all_configs.return_value = response.Response(message=[], status=200) resp = fixture_client.get('/file-upload-configs') assert resp.status_code == 200 assert resp.json == [] class TestFileUploadConfigItemView: """Tests for /file-upload-config/ endpoints (GET, PUT, DELETE).""" @patch('abacus_file_upload.blueprints.file_upload_config.flask_request') def test_get_config_unauthorized(self, mock_flask_request, fixture_client): """Test get config returns 401 when unauthorized.""" mock_flask_request.verify_rules_access_standalone.return_value = False resp = fixture_client.get('/file-upload-config/1') assert resp.status_code == 401 assert resp.json['message'] == 'Unauthorized' @patch( 'abacus_file_upload.blueprints.file_upload_config.logic.get_file_upload_config' ) @patch('abacus_file_upload.blueprints.file_upload_config.flask_request') def test_get_config_success( self, mock_flask_request, mock_get_config, fixture_client ): """Test successful config retrieval.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_get_config.return_value = response.Response( message={ 'file_upload_config_id': 1, 'upload_type': 'adjustments', 's3_key_template': '{year}/{month}/{file_key}.{ext}', 'max_file_size_bytes': 10485760, }, status=200, ) resp = fixture_client.get('/file-upload-config/1') assert resp.status_code == 200 assert resp.json['file_upload_config_id'] == 1 mock_get_config.assert_called_once_with(1, verbose=False) @patch( 'abacus_file_upload.blueprints.file_upload_config.logic.get_file_upload_config' ) @patch('abacus_file_upload.blueprints.file_upload_config.flask_request') def test_get_config_verbose( self, mock_flask_request, mock_get_config, fixture_client ): """Test config retrieval with verbose parameter.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_get_config.return_value = response.Response( message={ 'file_upload_config_id': 1, 'upload_type': 'adjustments', 's3_key_template': '{year}/{month}/{file_key}.{ext}', 'max_file_size_bytes': 10485760, 'created_at': '2025-01-16T11:00:00+00:00', 'created_by': 'test_user', }, status=200, ) resp = fixture_client.get('/file-upload-config/1?verbose=true') assert resp.status_code == 200 mock_get_config.assert_called_once_with(1, verbose=True) @patch( 'abacus_file_upload.blueprints.file_upload_config.logic.get_file_upload_config' ) @patch('abacus_file_upload.blueprints.file_upload_config.flask_request') def test_get_config_not_found( self, mock_flask_request, mock_get_config, fixture_client ): """Test get config with non-existent ID.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_get_config.return_value = response.Response( message="Upload config with ID '999' not found", status=404 ) resp = fixture_client.get('/file-upload-config/999') assert resp.status_code == 404 @patch('abacus_file_upload.blueprints.file_upload_config.flask_request') def test_update_config_unauthorized(self, mock_flask_request, fixture_client): """Test update config returns 401 when unauthorized.""" mock_flask_request.verify_rules_access_standalone.return_value = False resp = fixture_client.put( '/file-upload-config/1', json={'description': 'Updated description'}, ) assert resp.status_code == 401 assert resp.json['message'] == 'Unauthorized' @patch( 'abacus_file_upload.blueprints.file_upload_config.logic.update_file_upload_config' ) @patch( 'abacus_file_upload.blueprints.file_upload_config.FileUploadConfig.get_by_id' ) @patch('abacus_file_upload.blueprints.file_upload_config.flask_request') def test_update_config_success( self, mock_flask_request, mock_get_by_id, mock_update_config, fixture_client, mock_file_upload_config, ): """Test successful config update.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_get_by_id.return_value = mock_file_upload_config mock_update_config.return_value = response.Response( message={ 'file_upload_config_id': 1, 'upload_type': 'adjustments', 's3_key_template': '{year}/{month}/{file_key}.{ext}', 'max_file_size_bytes': 20971520, # Updated 'description': 'Updated description', }, status=200, ) resp = fixture_client.put( '/file-upload-config/1', json={ 'max_file_size_bytes': 20971520, 'description': 'Updated description', }, ) assert resp.status_code == 200 assert resp.json['max_file_size_bytes'] == 20971520 mock_update_config.assert_called_once() @patch( 'abacus_file_upload.blueprints.file_upload_config.FileUploadConfig.get_by_id' ) @patch('abacus_file_upload.blueprints.file_upload_config.flask_request') def test_update_config_not_found( self, mock_flask_request, mock_get_by_id, fixture_client ): """Test update config with non-existent ID.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_get_by_id.return_value = None resp = fixture_client.put( '/file-upload-config/999', json={'description': 'Updated description'}, ) assert resp.status_code == 404 @patch('abacus_file_upload.blueprints.file_upload_config.flask_request') def test_delete_config_unauthorized(self, mock_flask_request, fixture_client): """Test delete config returns 401 when unauthorized.""" mock_flask_request.verify_rules_access_standalone.return_value = False resp = fixture_client.delete('/file-upload-config/1') assert resp.status_code == 401 assert resp.json['message'] == 'Unauthorized' @patch( 'abacus_file_upload.blueprints.file_upload_config.logic.delete_file_upload_config' ) @patch('abacus_file_upload.blueprints.file_upload_config.flask_request') def test_delete_config_success( self, mock_flask_request, mock_delete_config, fixture_client ): """Test successful config deletion (soft delete).""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_delete_config.return_value = response.Response(status=204) resp = fixture_client.delete('/file-upload-config/1') assert resp.status_code == 204 mock_delete_config.assert_called_once_with(1) @patch( 'abacus_file_upload.blueprints.file_upload_config.logic.delete_file_upload_config' ) @patch('abacus_file_upload.blueprints.file_upload_config.flask_request') def test_delete_config_not_found( self, mock_flask_request, mock_delete_config, fixture_client ): """Test delete config with non-existent ID.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_delete_config.return_value = response.Response( message="Upload config with ID '999' not found", status=404 ) resp = fixture_client.delete('/file-upload-config/999') assert resp.status_code == 404 @patch( 'abacus_file_upload.blueprints.file_upload_config.logic.delete_file_upload_config' ) @patch('abacus_file_upload.blueprints.file_upload_config.flask_request') def test_delete_config_with_uploads( self, mock_flask_request, mock_delete_config, fixture_client ): """Test delete config that has associated uploads.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_delete_config.return_value = response.Response( message='Cannot delete config with existing uploads', status=400 ) resp = fixture_client.delete('/file-upload-config/1') assert resp.status_code == 400