"""Tests for abacus_file_upload.blueprints.file_upload module.""" from unittest.mock import patch from owsresponse import response from abacus_file_upload.constants import UPLOAD_STATUSES class TestFileUploadInitiateView: """Tests for POST /file-upload endpoint.""" @patch('abacus_file_upload.blueprints.file_upload.flask_request') def test_initiate_upload_unauthorized(self, mock_flask_request, fixture_client): """Test initiate upload returns 401 when unauthorized.""" mock_flask_request.verify_rules_access_standalone.return_value = False response = fixture_client.post( '/file-upload', json={ 'upload_type': 'adjustments', 'filename': 'test.csv', 'file_size_bytes': 1024000, 'md5sum': '5d41402abc4b2a76b9719d911017c592', }, ) assert response.status_code == 401 assert response.json['message'] == 'Unauthorized' @patch('abacus_file_upload.blueprints.file_upload.logic.initiate_upload') @patch('abacus_file_upload.blueprints.file_upload.flask_request') def test_initiate_upload_success( self, mock_flask_request, mock_initiate_upload, fixture_client ): """Test successful upload initiation.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_initiate_upload.return_value = response.Response( message={ 'file_key': '550e8400-e29b-41d4-a716-446655440000', 'is_multipart': False, 'expires_at': '2025-01-16T11:00:00+00:00', 'upload_url': 'https://s3.amazonaws.com/...', 'required_headers': {'Content-MD5': 'test'}, }, status=201, ) resp = fixture_client.post( '/file-upload', json={ 'upload_type': 'adjustments', 'filename': 'test.csv', 'file_size_bytes': 1024000, 'md5sum': '5d41402abc4b2a76b9719d911017c592', }, ) assert resp.status_code == 201 assert 'file_key' in resp.json assert 'upload_url' in resp.json mock_initiate_upload.assert_called_once() @patch('abacus_file_upload.blueprints.file_upload.logic.initiate_upload') @patch('abacus_file_upload.blueprints.file_upload.flask_request') def test_initiate_upload_validation_error( self, mock_flask_request, mock_initiate_upload, fixture_client ): """Test upload initiation with validation error.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_initiate_upload.return_value = response.Response( message='File too large', status=400 ) resp = fixture_client.post( '/file-upload', json={ 'upload_type': 'adjustments', 'filename': 'test.csv', 'file_size_bytes': 999999999999, 'md5sum': '5d41402abc4b2a76b9719d911017c592', }, ) assert resp.status_code == 400 class TestGetFileUpload: """Tests for GET /file-upload/ endpoint.""" @patch('abacus_file_upload.blueprints.file_upload.flask_request') def test_get_file_upload_unauthorized(self, mock_flask_request, fixture_client): """Test get file upload returns 401 when unauthorized.""" mock_flask_request.verify_rules_access_standalone.return_value = False resp = fixture_client.get('/file-upload/550e8400-e29b-41d4-a716-446655440000') assert resp.status_code == 401 assert resp.json['message'] == 'Unauthorized' @patch('abacus_file_upload.blueprints.file_upload.logic.get_file_upload') @patch('abacus_file_upload.blueprints.file_upload.flask_request') def test_get_file_upload_success( self, mock_flask_request, mock_get_file_upload, fixture_client ): """Test successful file upload retrieval.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_get_file_upload.return_value = response.Response( message={ 'file_key': '550e8400-e29b-41d4-a716-446655440000', 'original_file_name': 'test.csv', 'file_size_bytes': 1024000, 'upload_status': UPLOAD_STATUSES.INIT, }, status=200, ) resp = fixture_client.get('/file-upload/550e8400-e29b-41d4-a716-446655440000') assert resp.status_code == 200 assert resp.json['file_key'] == '550e8400-e29b-41d4-a716-446655440000' mock_get_file_upload.assert_called_once_with( '550e8400-e29b-41d4-a716-446655440000', verbose=False ) @patch('abacus_file_upload.blueprints.file_upload.logic.get_file_upload') @patch('abacus_file_upload.blueprints.file_upload.flask_request') def test_get_file_upload_verbose( self, mock_flask_request, mock_get_file_upload, fixture_client ): """Test file upload retrieval with verbose parameter.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_get_file_upload.return_value = response.Response( message={ 'file_key': '550e8400-e29b-41d4-a716-446655440000', 'original_file_name': 'test.csv', 'file_size_bytes': 1024000, 'upload_status': UPLOAD_STATUSES.INIT, 'created_at': '2025-01-16T11:00:00+00:00', 'created_by': 'test_user', }, status=200, ) resp = fixture_client.get( '/file-upload/550e8400-e29b-41d4-a716-446655440000?verbose=true' ) assert resp.status_code == 200 mock_get_file_upload.assert_called_once_with( '550e8400-e29b-41d4-a716-446655440000', verbose=True ) @patch('abacus_file_upload.blueprints.file_upload.logic.get_file_upload') @patch('abacus_file_upload.blueprints.file_upload.flask_request') def test_get_file_upload_verbose_variations( self, mock_flask_request, mock_get_file_upload, fixture_client ): """Test verbose parameter accepts multiple boolean formats.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_get_file_upload.return_value = response.Response( message={'file_key': '550e8400-e29b-41d4-a716-446655440000'}, status=200 ) # Test various boolean formats for verbose_val in ['yes', '1', 'on', 't', 'y', 'TRUE']: fixture_client.get( f'/file-upload/550e8400-e29b-41d4-a716-446655440000?verbose={verbose_val}' ) # Last call should have verbose=True assert mock_get_file_upload.call_args[1]['verbose'] is True # Test false values for verbose_val in ['false', '0', 'off', 'f', 'n', 'no']: fixture_client.get( f'/file-upload/550e8400-e29b-41d4-a716-446655440000?verbose={verbose_val}' ) # Last call should have verbose=False assert mock_get_file_upload.call_args[1]['verbose'] is False @patch('abacus_file_upload.blueprints.file_upload.logic.get_file_upload') @patch('abacus_file_upload.blueprints.file_upload.flask_request') def test_get_file_upload_not_found( self, mock_flask_request, mock_get_file_upload, fixture_client ): """Test get file upload with non-existent file key.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_get_file_upload.return_value = response.Response( message="Upload not found: 'invalid-key'", status=404 ) resp = fixture_client.get('/file-upload/invalid-key') assert resp.status_code == 404 class TestCompleteUpload: """Tests for POST /file-upload//complete endpoint.""" @patch('abacus_file_upload.blueprints.file_upload.flask_request') def test_complete_upload_unauthorized(self, mock_flask_request, fixture_client): """Test complete upload returns 401 when unauthorized.""" mock_flask_request.verify_rules_access_standalone.return_value = False resp = fixture_client.post( '/file-upload/550e8400-e29b-41d4-a716-446655440000/complete' ) assert resp.status_code == 401 assert resp.json['message'] == 'Unauthorized' @patch('abacus_file_upload.blueprints.file_upload.logic.complete_upload') @patch('abacus_file_upload.blueprints.file_upload.flask_request') def test_complete_upload_success( self, mock_flask_request, mock_complete_upload, fixture_client ): """Test successful upload completion.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_complete_upload.return_value = response.Response( message={ 'file_key': '550e8400-e29b-41d4-a716-446655440000', 'upload_status': UPLOAD_STATUSES.COMPLETE, 'completed_at': '2025-01-16T11:00:00+00:00', }, status=200, ) resp = fixture_client.post( '/file-upload/550e8400-e29b-41d4-a716-446655440000/complete' ) assert resp.status_code == 200 assert resp.json['upload_status'] == UPLOAD_STATUSES.COMPLETE mock_complete_upload.assert_called_once_with( '550e8400-e29b-41d4-a716-446655440000' ) @patch('abacus_file_upload.blueprints.file_upload.logic.complete_upload') @patch('abacus_file_upload.blueprints.file_upload.flask_request') def test_complete_upload_not_found( self, mock_flask_request, mock_complete_upload, fixture_client ): """Test complete upload with non-existent file key.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_complete_upload.return_value = response.Response( message="Upload not found: 'invalid-key'", status=404 ) resp = fixture_client.post('/file-upload/invalid-key/complete') assert resp.status_code == 404 @patch('abacus_file_upload.blueprints.file_upload.logic.complete_upload') @patch('abacus_file_upload.blueprints.file_upload.flask_request') def test_complete_upload_validation_error( self, mock_flask_request, mock_complete_upload, fixture_client ): """Test completing a cancelled upload.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_complete_upload.return_value = response.Response( message='Cannot complete a cancelled upload', status=400 ) resp = fixture_client.post( '/file-upload/550e8400-e29b-41d4-a716-446655440000/complete' ) assert resp.status_code == 400 class TestCancelUpload: """Tests for POST /file-upload//cancel endpoint.""" @patch('abacus_file_upload.blueprints.file_upload.flask_request') def test_cancel_upload_unauthorized(self, mock_flask_request, fixture_client): """Test cancel upload returns 401 when unauthorized.""" mock_flask_request.verify_rules_access_standalone.return_value = False resp = fixture_client.post( '/file-upload/550e8400-e29b-41d4-a716-446655440000/cancel' ) assert resp.status_code == 401 assert resp.json['message'] == 'Unauthorized' @patch('abacus_file_upload.blueprints.file_upload.logic.cancel_upload') @patch('abacus_file_upload.blueprints.file_upload.flask_request') def test_cancel_upload_success( self, mock_flask_request, mock_cancel_upload, fixture_client ): """Test successful upload cancellation.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_cancel_upload.return_value = response.Response( message={ 'file_key': '550e8400-e29b-41d4-a716-446655440000', 'upload_status': UPLOAD_STATUSES.CANCELLED, }, status=200, ) resp = fixture_client.post( '/file-upload/550e8400-e29b-41d4-a716-446655440000/cancel' ) assert resp.status_code == 200 assert resp.json['upload_status'] == UPLOAD_STATUSES.CANCELLED mock_cancel_upload.assert_called_once_with( '550e8400-e29b-41d4-a716-446655440000' ) @patch('abacus_file_upload.blueprints.file_upload.logic.cancel_upload') @patch('abacus_file_upload.blueprints.file_upload.flask_request') def test_cancel_upload_not_found( self, mock_flask_request, mock_cancel_upload, fixture_client ): """Test cancel upload with non-existent file key.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_cancel_upload.return_value = response.Response( message="Upload not found: 'invalid-key'", status=404 ) resp = fixture_client.post('/file-upload/invalid-key/cancel') assert resp.status_code == 404 @patch('abacus_file_upload.blueprints.file_upload.logic.cancel_upload') @patch('abacus_file_upload.blueprints.file_upload.flask_request') def test_cancel_upload_validation_error( self, mock_flask_request, mock_cancel_upload, fixture_client ): """Test cancelling a completed upload.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_cancel_upload.return_value = response.Response( message='Cannot cancel a completed upload', status=400 ) resp = fixture_client.post( '/file-upload/550e8400-e29b-41d4-a716-446655440000/cancel' ) assert resp.status_code == 400 class TestDownloadFile: """Tests for GET /file-upload//download endpoint.""" @patch('abacus_file_upload.blueprints.file_upload.flask_request') def test_download_file_unauthorized(self, mock_flask_request, fixture_client): """Test download file returns 401 when unauthorized.""" mock_flask_request.verify_rules_access_standalone.return_value = False resp = fixture_client.get( '/file-upload/550e8400-e29b-41d4-a716-446655440000/download' ) assert resp.status_code == 401 assert resp.json['message'] == 'Unauthorized' @patch('abacus_file_upload.blueprints.file_upload.logic.download_file') @patch('abacus_file_upload.blueprints.file_upload.flask_request') def test_download_file_success( self, mock_flask_request, mock_download_file, fixture_client ): """Test successful download URL generation.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_download_file.return_value = response.Response( message={ 'download_url': 'https://s3.amazonaws.com/...', 'expires_in': 3600, 'file_name': 'test.csv', 'file_size_bytes': 1024000, }, status=200, ) resp = fixture_client.get( '/file-upload/550e8400-e29b-41d4-a716-446655440000/download' ) assert resp.status_code == 200 assert 'download_url' in resp.json assert resp.json['expires_in'] == 3600 mock_download_file.assert_called_once_with( '550e8400-e29b-41d4-a716-446655440000' ) @patch('abacus_file_upload.blueprints.file_upload.logic.download_file') @patch('abacus_file_upload.blueprints.file_upload.flask_request') def test_download_file_not_found( self, mock_flask_request, mock_download_file, fixture_client ): """Test download with non-existent file key.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_download_file.return_value = response.Response( message="Upload not found: 'invalid-key'", status=404 ) resp = fixture_client.get('/file-upload/invalid-key/download') assert resp.status_code == 404 @patch('abacus_file_upload.blueprints.file_upload.logic.download_file') @patch('abacus_file_upload.blueprints.file_upload.flask_request') def test_download_file_not_complete( self, mock_flask_request, mock_download_file, fixture_client ): """Test download when upload is not complete.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_download_file.return_value = response.Response( message="File not available for download. Upload status: 'init'", status=400 ) resp = fixture_client.get( '/file-upload/550e8400-e29b-41d4-a716-446655440000/download' ) assert resp.status_code == 400 class TestQuarantineInfectedUpload: """Tests for POST /file-upload//quarantine endpoint.""" @patch('abacus_file_upload.blueprints.file_upload.flask_request') def test_quarantine_infected_upload_unauthorized( self, mock_flask_request, fixture_client ): """Test quarantine upload returns 401 when unauthorized.""" mock_flask_request.verify_rules_access_standalone.return_value = False resp = fixture_client.post( '/file-upload/550e8400-e29b-41d4-a716-446655440000/quarantine' ) assert resp.status_code == 401 assert resp.json['message'] == 'Unauthorized' @patch('abacus_file_upload.blueprints.file_upload.logic.quarantine_infected_upload') @patch('abacus_file_upload.blueprints.file_upload.flask_request') def test_quarantine_infected_upload_success( self, mock_flask_request, mock_quarantine_infected_upload, fixture_client ): """Test successful quarantine upload.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_quarantine_infected_upload.return_value = response.Response( message={ 'file_key': '550e8400-e29b-41d4-a716-446655440000', 'upload_status': UPLOAD_STATUSES.QUARANTINED, }, status=200, ) resp = fixture_client.post( '/file-upload/550e8400-e29b-41d4-a716-446655440000/quarantine' ) assert resp.status_code == 200 assert resp.json['upload_status'] == UPLOAD_STATUSES.QUARANTINED mock_quarantine_infected_upload.assert_called_once_with( '550e8400-e29b-41d4-a716-446655440000' ) @patch('abacus_file_upload.blueprints.file_upload.logic.quarantine_infected_upload') @patch('abacus_file_upload.blueprints.file_upload.flask_request') def test_quarantine_infected_upload_not_found( self, mock_flask_request, mock_quarantine_infected_upload, fixture_client ): """Test quarantine_infected_upload with non-existent file key.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_quarantine_infected_upload.return_value = response.Response( message="Upload not found: 'invalid-key'", status=404 ) resp = fixture_client.post('/file-upload/invalid-key/quarantine') assert resp.status_code == 404 @patch('abacus_file_upload.blueprints.file_upload.logic.quarantine_infected_upload') @patch('abacus_file_upload.blueprints.file_upload.flask_request') def test_quarantine_infected_upload_validation_error( self, mock_flask_request, mock_quarantine_infected_upload, fixture_client ): """Test returns an error for quarantine upload.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_quarantine_infected_upload.return_value = response.Response( message='Error for quarantine upload', status=400 ) resp = fixture_client.post( '/file-upload/550e8400-e29b-41d4-a716-446655440000/quarantine' ) assert resp.status_code == 400 class TestUpdateFileUploadStatus: """Tests for PUT /file-upload//status endpoint.""" @patch('abacus_file_upload.blueprints.file_upload.flask_request') def test_update_file_upload_status_unauthorized( self, mock_flask_request, fixture_client ): """Test updating status returns 401 when unauthorized.""" mock_flask_request.verify_rules_access_standalone.return_value = False resp = fixture_client.put( '/file-upload/550e8400-e29b-41d4-a716-446655440000/status' ) assert resp.status_code == 401 assert resp.json['message'] == 'Unauthorized' @patch('abacus_file_upload.blueprints.file_upload.logic.update_file_upload_status') @patch('abacus_file_upload.blueprints.file_upload.flask_request') def test_updating_upload_status_success( self, mock_flask_request, mock_update_file_upload_status, fixture_client ): """Test successful updating file_upload status.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_update_file_upload_status.return_value = response.Response( message={ 'file_key': '550e8400-e29b-41d4-a716-446655440000', 'upload_status': UPLOAD_STATUSES.QUARANTINED, }, status=200, ) mock_put_request_body = {'upload_status': UPLOAD_STATUSES.QUARANTINED} resp = fixture_client.put( '/file-upload/550e8400-e29b-41d4-a716-446655440000/status', json=mock_put_request_body, ) assert resp.status_code == 200 assert resp.json['upload_status'] == UPLOAD_STATUSES.QUARANTINED mock_update_file_upload_status.assert_called_once_with( '550e8400-e29b-41d4-a716-446655440000', {'upload_status': UPLOAD_STATUSES.QUARANTINED}, ) @patch('abacus_file_upload.blueprints.file_upload.logic.update_file_upload_status') @patch('abacus_file_upload.blueprints.file_upload.flask_request') def test_file_not_found( self, mock_flask_request, mock_update_file_upload_status, fixture_client ): """Test updating upload_status for non-existent file key.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_update_file_upload_status.return_value = response.Response( message="Upload not found: 'invalid-key'", status=404 ) mock_put_request_body = {'upload_status': UPLOAD_STATUSES.COMPLETE} resp = fixture_client.put( '/file-upload/invalid-key/status', json=mock_put_request_body ) assert resp.status_code == 404 assert resp.data.decode('utf-8') == "Upload not found: 'invalid-key'" @patch('abacus_file_upload.blueprints.file_upload.logic.update_file_upload_status') @patch('abacus_file_upload.blueprints.file_upload.flask_request') def test_invalid_upload_status_transition_error( self, mock_flask_request, mock_quarantine_infected_upload, fixture_client ): """Test returns an error for invalid upload status transition.""" mock_flask_request.verify_rules_access_standalone.return_value = True mock_quarantine_infected_upload.return_value = response.Response( message='invalid batch status transition', status=400 ) mock_put_request_body = {'upload_status': UPLOAD_STATUSES.INIT} resp = fixture_client.put( '/file-upload/550e8400-e29b-41d4-a716-446655440000/status', json=mock_put_request_body, ) assert resp.status_code == 400 assert resp.data.decode('utf-8') == 'invalid batch status transition'