From 6d942d8876e07ebae9cfde5602c5e99ab99b7123 Mon Sep 17 00:00:00 2001 From: shashankshampi Date: Mon, 16 Sep 2024 12:45:56 +0530 Subject: [PATCH] added store_test_cursor negative cases and edge cases --- .gitignore | 1 + src/steps/store.py | 115 +++++++++++++++++++++++++++---------- tests/store/test_cursor.py | 93 ++++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+), 29 deletions(-) diff --git a/.gitignore b/.gitignore index 546d9936..16267b58 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Custom log/ +.idea/ .vscode allure-results/ postgresql diff --git a/src/steps/store.py b/src/steps/store.py index ca18788f..5c6d4645 100644 --- a/src/steps/store.py +++ b/src/steps/store.py @@ -1,4 +1,7 @@ import inspect + +import requests + from src.libs.custom_logger import get_custom_logger import pytest import allure @@ -186,9 +189,9 @@ class StepsStore(StepsCommon): return store_response @allure.step - def check_published_message_is_stored( + def get_store_messages_with_errors( self, - store_node=None, + node=None, peer_addr=None, include_data=None, pubsub_topic=None, @@ -198,25 +201,17 @@ class StepsStore(StepsCommon): hashes=None, cursor=None, page_size=None, - ascending=None, + ascending="true", store_v="v3", - message_to_check=None, **kwargs, ): - if pubsub_topic is None: - pubsub_topic = self.test_pubsub_topic - if message_to_check is None: - message_to_check = self.message - if store_node is None: - store_node = self.store_nodes - elif not isinstance(store_node, list): - store_node = [store_node] - else: - store_node = store_node - for node in store_node: - logger.debug(f"Checking that peer {node.image} can find the stored message") - self.store_response = self.get_messages_from_store( - node=node, + """ + This method calls the original get_store_messages and returns the actual + error response from the service, if present. + """ + try: + # Call the original get_store_messages method + store_response = node.get_store_messages( peer_addr=peer_addr, include_data=include_data, pubsub_topic=pubsub_topic, @@ -231,17 +226,79 @@ class StepsStore(StepsCommon): **kwargs, ) - assert self.store_response.messages, f"Peer {node.image} couldn't find any messages. Actual response: {self.store_response.resp_json}" - assert len(self.store_response.messages) >= 1, "Expected at least 1 message but got none" - store_message_index = -1 # we are looking for the last and most recent message in the store - waku_message = WakuMessage([self.store_response.messages[store_message_index:]]) - if store_v == "v1": - waku_message.assert_received_message(message_to_check) - else: - expected_hash = self.compute_message_hash(pubsub_topic, message_to_check) - assert expected_hash == self.store_response.message_hash( - store_message_index - ), f"Message hash returned by store doesn't match the computed message hash {expected_hash}" + # Check if the response status code indicates an error (400 or higher) + if store_response.status_code >= 400: + # If the response is plain text, just return it as the error message + return {"status_code": store_response.status_code, "error_message": store_response.text} # Use plain text response directly + + # Otherwise, return the successful response + response_json = store_response.json() + response_json["status_code"] = store_response.status_code + return response_json + + except requests.exceptions.HTTPError as http_err: + # Handle HTTP errors separately + return {"status_code": http_err.response.status_code, "error_message": http_err.response.text} + + except Exception as e: + # Handle unexpected errors and return as 500 + return {"status_code": 500, "error_message": str(e)} + + @allure.step + def get_store_messages_with_errors( + self, + node=None, + peer_addr=None, + include_data=None, + pubsub_topic=None, + content_topics=None, + start_time=None, + end_time=None, + hashes=None, + cursor=None, + page_size=None, + ascending="true", + store_v="v3", + **kwargs, + ): + """ + This method calls the original get_store_messages and returns the actual + error response from the service, if present. + """ + try: + # Call the original get_store_messages method + store_response = node.get_store_messages( + peer_addr=peer_addr, + include_data=include_data, + pubsub_topic=pubsub_topic, + content_topics=content_topics, + start_time=start_time, + end_time=end_time, + hashes=hashes, + cursor=cursor, + page_size=page_size, + ascending=ascending, + store_v=store_v, + **kwargs, + ) + print("my store_response: ", store_response) + # Check if the response has a status code >= 400, indicating an error + if store_response.status_code >= 400: + # Return the status code and the plain text error message directly + return {"status_code": store_response.status_code, "error_message": store_response.text} # Handling plain text response + + # Otherwise, return the successful response as JSON + response_json = store_response.json() + response_json["status_code"] = store_response.status_code + return response_json + + except requests.exceptions.HTTPError as http_err: + # Handle HTTP errors separately + return {"status_code": http_err.response.status_code, "error_message": http_err.response.text} + + except Exception as e: + # Handle unexpected errors and return as 500 + return {"status_code": 500, "error_message": str(e)} @allure.step def check_store_returns_empty_response(self, pubsub_topic=None): diff --git a/tests/store/test_cursor.py b/tests/store/test_cursor.py index 695fca06..8fa4dd33 100644 --- a/tests/store/test_cursor.py +++ b/tests/store/test_cursor.py @@ -102,3 +102,96 @@ class TestCursor(StepsStore): for node in self.store_nodes: store_response = self.get_messages_from_store(node, page_size=100, cursor=cursor) assert not store_response.messages, "Messages found" + + # Addon on test + + # Ensure that when the cursor is an empty string (""), the API returns the first page of data. + def test_empty_cursor(self): + for i in range(10): + self.publish_message(message=self.create_message(payload=to_base64(f"Message_{i}"))) + for node in self.store_nodes: + store_response = self.get_messages_from_store(node, page_size=5, cursor="") + assert store_response.status_code == 200, f"Expected status code 200, got {store_response.status_code}" + assert len(store_response.messages) == 5, "Message count mismatch with empty cursor" + + # Test the scenario where the cursor points near the last few messages, ensuring proper pagination. + def test_cursor_near_end(self): + message_hash_list = [] + for i in range(10): + message = self.create_message(payload=to_base64(f"Message_{i}")) + self.publish_message(message=message) + message_hash_list.append(self.compute_message_hash(self.test_pubsub_topic, message)) + + for node in self.store_nodes: + store_response = self.get_messages_from_store(node, page_size=5) + cursor = store_response.pagination_cursor + store_response = self.get_messages_from_store(node, page_size=5, cursor=cursor) + assert len(store_response.messages) == 5, "Message count mismatch near the end" + + # Test how the API handles a cursor that points to a message that no longer exists. + def test_cursor_pointing_to_deleted_message(self): + # Publish some messages + for i in range(10): + self.publish_message(message=self.create_message(payload=to_base64(f"Message_{i}"))) + + # Create a deleted message and compute its hash as the cursor + deleted_message = self.create_message(payload=to_base64("Deleted_Message")) + cursor = self.compute_message_hash(self.test_pubsub_topic, deleted_message) + + # Test the store response + for node in self.store_nodes: + store_response = self.get_store_messages_with_errors(node=node, page_size=100, cursor=cursor) + + # Assert that the error code is 500 for the deleted message scenario + assert store_response["status_code"] == 500, f"Expected status code 500, got {store_response['status_code']}" + + # Define a partial expected error message (since the actual response includes more details) + expected_error_fragment = "error in handleSelfStoreRequest: BAD_RESPONSE: archive error: DIRVER_ERROR: cursor not found" + + # Extract the actual error message and ensure it contains the expected error fragment + actual_error_message = store_response["error_message"] + assert ( + expected_error_fragment in actual_error_message + ), f"Expected error message fragment '{expected_error_fragment}', but got '{actual_error_message}'" + + # Test if the API returns the expected messages when the cursor points to the first message in the store. + def test_cursor_equal_to_first_message(self): + message_hash_list = [] + for i in range(10): + message = self.create_message(payload=to_base64(f"Message_{i}")) + self.publish_message(message=message) + message_hash_list.append(self.compute_message_hash(self.test_pubsub_topic, message)) + + cursor = message_hash_list[0] # Cursor points to the first message + for node in self.store_nodes: + store_response = self.get_messages_from_store(node, page_size=100, cursor=cursor) + assert len(store_response.messages) == 9, "Message count mismatch from the first cursor" + + # Test behavior when the cursor points exactly at the page size boundary. + def test_cursor_at_page_size_boundary(self): + message_hash_list = [] + for i in range(10): + message = self.create_message(payload=to_base64(f"Message_{i}")) + self.publish_message(message=message) + message_hash_list.append(self.compute_message_hash(self.test_pubsub_topic, message)) + + # Set page size to 5, checking paginationCursor after both fetches + for node in self.store_nodes: + # Fetch the first page of messages + store_response = self.get_messages_from_store(node, page_size=5) + + # Check if we received exactly 5 messages + assert len(store_response.messages) == 5, "Message count mismatch on first page" + + # Validate that paginationCursor exists because we are not at the boundary + assert store_response.pagination_cursor is not None, "paginationCursor should be present when not at the boundary" + cursor = store_response.pagination_cursor + + # Fetch the next page using the cursor + store_response = self.get_messages_from_store(node, page_size=5, cursor=cursor) + + # Check if we received exactly 5 more messages + assert len(store_response.messages) == 5, "Message count mismatch at page size boundary" + + # Validate that paginationCursor is **not** present when we reach the boundary (end of pagination) + assert store_response.pagination_cursor is None, "paginationCursor should be absent when at the boundary"