diff --git a/api/messages/messages_service.py b/api/messages/messages_service.py index 3b96abd..d7936c1 100644 --- a/api/messages/messages_service.py +++ b/api/messages/messages_service.py @@ -226,6 +226,8 @@ def add_nonprofit_to_hackathon(json): # Update the hackathon document hackathon_doc.set(hackathon_dict, merge=True) + # Clear cache to ensure fresh data is served + clear_cache() return { "message": "Nonprofit added to hackathon" @@ -280,6 +282,9 @@ def remove_nonprofit_from_hackathon(json): hackathon_data["nonprofits"] = updated_nonprofits hackathon_doc.set(hackathon_data, merge=True) + # Clear cache to ensure fresh data is served + clear_cache() + logger.info(f"Remove Nonprofit from Hackathon End (nonprofit removed)") return { "message": "Nonprofit removed from hackathon" @@ -1233,6 +1238,7 @@ def clear_cache(): doc_to_json.cache_clear() get_single_hackathon_event.cache_clear() get_single_hackathon_id.cache_clear() + get_hackathon_list.cache_clear() @limits(calls=100, period=ONE_MINUTE) @@ -1707,11 +1713,8 @@ def update_hackathon(transaction): transaction = db.transaction() update_hackathon(transaction) - # Clear cache for get_single_hackathon_event - get_single_hackathon_event.cache_clear() - - # Clear cache for get_hackathon_list - doc_to_json.cache_clear() + # Clear all hackathon-related caches + clear_cache() logger.info(f"Hackathon {'updated' if is_update else 'created'} successfully. ID: {doc_id}") diff --git a/api/messages/tests/__init__.py b/api/messages/tests/__init__.py new file mode 100644 index 0000000..31d41f8 --- /dev/null +++ b/api/messages/tests/__init__.py @@ -0,0 +1 @@ +# Tests for messages service diff --git a/api/messages/tests/test_cache_invalidation.py b/api/messages/tests/test_cache_invalidation.py new file mode 100644 index 0000000..d7dd251 --- /dev/null +++ b/api/messages/tests/test_cache_invalidation.py @@ -0,0 +1,143 @@ +""" +Test cases for cache invalidation in messages_service. + +These tests verify that cache is properly invalidated when hackathon data is modified. +""" +import pytest +from unittest.mock import patch, MagicMock, call +from api.messages.messages_service import ( + add_nonprofit_to_hackathon, + remove_nonprofit_from_hackathon, + save_hackathon, + clear_cache +) + + +class TestCacheInvalidation: + """Test cases for cache invalidation in hackathon operations.""" + + @patch('api.messages.messages_service.clear_cache') + @patch('api.messages.messages_service.get_db') + def test_add_nonprofit_to_hackathon_clears_cache(self, mock_db, mock_clear_cache): + """Test that adding a nonprofit to a hackathon clears the cache.""" + # Setup + mock_hackathon_doc = MagicMock() + mock_hackathon_data = MagicMock() + mock_hackathon_data.exists = True + mock_hackathon_data.to_dict.return_value = {"nonprofits": []} + mock_hackathon_doc.get.return_value = mock_hackathon_data + + mock_nonprofit_doc = MagicMock() + mock_nonprofit_data = MagicMock() + mock_nonprofit_data.exists = True + mock_nonprofit_doc.get.return_value = mock_nonprofit_data + + mock_collection = MagicMock() + mock_collection.document.side_effect = lambda doc_id: ( + mock_hackathon_doc if doc_id == "hackathon123" else mock_nonprofit_doc + ) + mock_db.return_value.collection.return_value = mock_collection + + json_data = { + "hackathonId": "hackathon123", + "nonprofitId": "nonprofit456" + } + + # Execute + result = add_nonprofit_to_hackathon(json_data) + + # Assert + assert result["message"] == "Nonprofit added to hackathon" + mock_clear_cache.assert_called_once() + + @patch('api.messages.messages_service.clear_cache') + @patch('api.messages.messages_service.get_db') + def test_remove_nonprofit_from_hackathon_clears_cache(self, mock_db, mock_clear_cache): + """Test that removing a nonprofit from a hackathon clears the cache.""" + # Setup + mock_nonprofit_ref = MagicMock() + mock_nonprofit_ref.id = "nonprofit456" + + mock_hackathon_doc = MagicMock() + mock_hackathon_data = MagicMock() + mock_hackathon_data.to_dict.return_value = {"nonprofits": [mock_nonprofit_ref]} + mock_hackathon_doc.get.return_value = mock_hackathon_data + + mock_nonprofit_doc = MagicMock() + + mock_collection = MagicMock() + mock_collection.document.side_effect = lambda doc_id: ( + mock_hackathon_doc if doc_id == "hackathon123" else mock_nonprofit_doc + ) + mock_db.return_value.collection.return_value = mock_collection + + json_data = { + "hackathonId": "hackathon123", + "nonprofitId": "nonprofit456" + } + + # Execute + result = remove_nonprofit_from_hackathon(json_data) + + # Assert + assert result["message"] == "Nonprofit removed from hackathon" + mock_clear_cache.assert_called_once() + + @patch('api.messages.messages_service.clear_cache') + @patch('api.messages.messages_service.get_db') + @patch('api.messages.messages_service.validate_hackathon_data') + def test_save_hackathon_clears_cache(self, mock_validate, mock_db, mock_clear_cache): + """Test that saving a hackathon clears the cache.""" + # Setup + mock_db_instance = MagicMock() + mock_db.return_value = mock_db_instance + mock_validate.return_value = None + + # Mock transaction + mock_transaction = MagicMock() + mock_db_instance.transaction.return_value = mock_transaction + + # Mock collection and document + mock_hackathon_ref = MagicMock() + mock_collection = MagicMock() + mock_collection.document.return_value = mock_hackathon_ref + mock_db_instance.collection.return_value = mock_collection + + json_data = { + "title": "Test Hackathon", + "description": "Test Description", + "location": "Test Location", + "start_date": "2024-01-01", + "end_date": "2024-01-02", + "type": "virtual", + "image_url": "https://example.com/image.png", + "event_id": "event123" + } + + # Execute + result = save_hackathon(json_data, "user123") + + # Assert + assert result.text == "Saved Hackathon" + mock_clear_cache.assert_called_once() + + @patch('api.messages.messages_service.doc_to_json') + @patch('api.messages.messages_service.get_single_hackathon_event') + @patch('api.messages.messages_service.get_single_hackathon_id') + @patch('api.messages.messages_service.get_hackathon_list') + def test_clear_cache_clears_all_caches( + self, + mock_get_hackathon_list, + mock_get_single_hackathon_id, + mock_get_single_hackathon_event, + mock_doc_to_json + ): + """Test that clear_cache clears all hackathon-related caches.""" + # Execute + clear_cache() + + # Assert - verify that cache_clear was called on all cached functions + mock_doc_to_json.cache_clear.assert_called_once() + mock_get_single_hackathon_event.cache_clear.assert_called_once() + mock_get_single_hackathon_id.cache_clear.assert_called_once() + mock_get_hackathon_list.cache_clear.assert_called_once() diff --git a/services/problem_statements_service.py b/services/problem_statements_service.py index a9a46d7..ee5c2f2 100644 --- a/services/problem_statements_service.py +++ b/services/problem_statements_service.py @@ -105,7 +105,12 @@ def update_problem_statement_fields(d): if problem_statement is not None: problem_statement.update(d) problem_statement.id = d['id'] - return update_problem_statement(problem_statement) + result = update_problem_statement(problem_statement) + + # Clear cache after updating problem statement + get_problem_statement.cache_clear() + + return result else: return None