From dc1ca8ed9a7d61268ae86cf59a3c00bd7514672c Mon Sep 17 00:00:00 2001 From: Swati Panchal Date: Wed, 14 May 2025 16:45:48 +0530 Subject: [PATCH 1/2] chore: ADDON-80802 Updated the search query for test_requirements_fields --- pytest_splunk_addon/event_ingestors/hec_event_ingestor.py | 1 + pytest_splunk_addon/fields_tests/test_generator.py | 1 + pytest_splunk_addon/fields_tests/test_templates.py | 7 ++++++- pytest_splunk_addon/sample_generation/sample_event.py | 2 ++ .../sample_generation/sample_xdist_generator.py | 2 ++ 5 files changed, 12 insertions(+), 1 deletion(-) diff --git a/pytest_splunk_addon/event_ingestors/hec_event_ingestor.py b/pytest_splunk_addon/event_ingestors/hec_event_ingestor.py index a292492c..cc2d837d 100644 --- a/pytest_splunk_addon/event_ingestors/hec_event_ingestor.py +++ b/pytest_splunk_addon/event_ingestors/hec_event_ingestor.py @@ -92,6 +92,7 @@ def ingest(self, events, thread_count): "source": event.metadata.get("source", "pytest_splunk_addon:hec:event"), "event": event.event, "index": event.metadata.get("index", "main"), + "fields": {"unique_identifier": event.unique_identifier} } if event.metadata.get("host_type") in ("plugin", None): diff --git a/pytest_splunk_addon/fields_tests/test_generator.py b/pytest_splunk_addon/fields_tests/test_generator.py index 273ae192..c38af289 100644 --- a/pytest_splunk_addon/fields_tests/test_generator.py +++ b/pytest_splunk_addon/fields_tests/test_generator.py @@ -266,6 +266,7 @@ def generate_requirements_tests(self): "escaped_event": escaped_event, "fields": requirement_fields, "modinput_params": modinput_params, + "unique_identifier": event.unique_identifier, }, id=f"sample_name::{event.sample_name}::host::{event.metadata.get('host')}", ) diff --git a/pytest_splunk_addon/fields_tests/test_templates.py b/pytest_splunk_addon/fields_tests/test_templates.py index fde16489..1c4fdf6c 100644 --- a/pytest_splunk_addon/fields_tests/test_templates.py +++ b/pytest_splunk_addon/fields_tests/test_templates.py @@ -165,12 +165,16 @@ def test_requirements_fields( record_property( "stanza_name", splunk_searchtime_fields_requirements["escaped_event"] ) + record_property( + "stanza_name", splunk_searchtime_fields_requirements["unique_identifier"] + ) record_property("fields", splunk_searchtime_fields_requirements["fields"]) record_property( "modinput_params", splunk_searchtime_fields_requirements["modinput_params"] ) escaped_event = splunk_searchtime_fields_requirements["escaped_event"] + unique_identifier = splunk_searchtime_fields_requirements["unique_identifier"] fields = splunk_searchtime_fields_requirements["fields"] modinput_params = splunk_searchtime_fields_requirements["modinput_params"] @@ -185,7 +189,7 @@ def test_requirements_fields( if param_value is not None: basic_search += f" {param}={param_value}" - search = f"search {index_list} {basic_search} {escaped_event} | fields *" + search = f"search {index_list} {basic_search} unique_identifier=\"{unique_identifier}\" | fields *" self.logger.info(f"Executing the search query: {search}") @@ -225,6 +229,7 @@ def test_requirements_fields( assert wrong_value_fields == {}, ( f"\nNot all required fields have correct values or some fields are missing in Splunk. Wrong field values:\n{wrong_values_table}" f"{format_search_query_log(search)}" + f"Test failed for event: {escaped_event}\n" ) @pytest.mark.splunk_searchtime_fields diff --git a/pytest_splunk_addon/sample_generation/sample_event.py b/pytest_splunk_addon/sample_generation/sample_event.py index 4eadb50e..59fafe8b 100644 --- a/pytest_splunk_addon/sample_generation/sample_event.py +++ b/pytest_splunk_addon/sample_generation/sample_event.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # +import uuid import re import logging from ..index_tests import key_fields @@ -68,6 +69,7 @@ def __init__(self, event_string, metadata, sample_name, requirement_test_data=No self.metadata = metadata self.sample_name = sample_name self.host_count = 0 + self.unique_identifier = str(uuid.uuid4()) self.requirement_test_data = requirement_test_data def update(self, new_event): diff --git a/pytest_splunk_addon/sample_generation/sample_xdist_generator.py b/pytest_splunk_addon/sample_generation/sample_xdist_generator.py index 6d1dd1e7..453610c3 100644 --- a/pytest_splunk_addon/sample_generation/sample_xdist_generator.py +++ b/pytest_splunk_addon/sample_generation/sample_xdist_generator.py @@ -131,6 +131,7 @@ def store_events(self, tokenized_events): "events": [ { "event": each_event.event, + "unique_identifier": each_event.unique_identifier, "key_fields": each_event.key_fields, "time_values": each_event.time_values, "requirement_test_data": each_event.requirement_test_data, @@ -141,6 +142,7 @@ def store_events(self, tokenized_events): tokenized_samples_dict[each_event.sample_name]["events"].append( { "event": each_event.event, + "unique_identifier": each_event.unique_identifier, "key_fields": each_event.key_fields, "time_values": each_event.time_values, "requirement_test_data": each_event.requirement_test_data, From 26c0b3665257d579cf9a210bbfed9ff91a0ed354 Mon Sep 17 00:00:00 2001 From: Swati Panchal Date: Tue, 27 May 2025 17:22:16 +0530 Subject: [PATCH 2/2] chore: ADDON-80802 Fixed the unit test case --- .../event_ingestors/hec_event_ingestor.py | 11 +- .../fields_tests/test_templates.py | 2 +- .../test_event_ingestors/conftest.py | 13 ++ .../test_fields_tests/test_test_generator.py | 210 +++++++++--------- .../test_sample_xdist_generator.py | 8 +- 5 files changed, 134 insertions(+), 110 deletions(-) diff --git a/pytest_splunk_addon/event_ingestors/hec_event_ingestor.py b/pytest_splunk_addon/event_ingestors/hec_event_ingestor.py index cc2d837d..96127087 100644 --- a/pytest_splunk_addon/event_ingestors/hec_event_ingestor.py +++ b/pytest_splunk_addon/event_ingestors/hec_event_ingestor.py @@ -60,7 +60,8 @@ def ingest(self, events, thread_count): "sourcetype": "sample_HEC", "source": "sample_source", "host": "sample_host", - "event": "event_str" + "event": "event_str", + "fields": {"unique_identifier": "uuid"} } The format of dictionary for ingesting a batch of events:: @@ -70,13 +71,15 @@ def ingest(self, events, thread_count): "sourcetype": "sample_HEC", "source": "sample_source", "host": "sample_host", - "event": "event_str1" + "event": "event_str1", + "fields": {"unique_identifier": "uuid"} }, { "sourcetype": "sample_HEC", "source": "sample_source", "host": "sample_host", - "event": "event_str2" + "event": "event_str2", + "fields": {"unique_identifier": "uuid"} }, ] @@ -92,7 +95,7 @@ def ingest(self, events, thread_count): "source": event.metadata.get("source", "pytest_splunk_addon:hec:event"), "event": event.event, "index": event.metadata.get("index", "main"), - "fields": {"unique_identifier": event.unique_identifier} + "fields": {"unique_identifier": event.unique_identifier}, } if event.metadata.get("host_type") in ("plugin", None): diff --git a/pytest_splunk_addon/fields_tests/test_templates.py b/pytest_splunk_addon/fields_tests/test_templates.py index 1c4fdf6c..de86a5bb 100644 --- a/pytest_splunk_addon/fields_tests/test_templates.py +++ b/pytest_splunk_addon/fields_tests/test_templates.py @@ -189,7 +189,7 @@ def test_requirements_fields( if param_value is not None: basic_search += f" {param}={param_value}" - search = f"search {index_list} {basic_search} unique_identifier=\"{unique_identifier}\" | fields *" + search = f'search {index_list} {basic_search} unique_identifier="{unique_identifier}" | fields *' self.logger.info(f"Executing the search query: {search}") diff --git a/tests/unit/tests_standard_lib/test_event_ingestors/conftest.py b/tests/unit/tests_standard_lib/test_event_ingestors/conftest.py index cb249019..1c3ba5d9 100644 --- a/tests/unit/tests_standard_lib/test_event_ingestors/conftest.py +++ b/tests/unit/tests_standard_lib/test_event_ingestors/conftest.py @@ -6,6 +6,7 @@ @dataclass() class SampleEvent: event: str + unique_identifier: str metadata: dict sample_name: str key_fields: dict = None @@ -17,6 +18,7 @@ def modinput_events(): return [ SampleEvent( event="test_modinput_1 host=modinput_host_event_time_plugin.samples_1", + unique_identifier="uuid", key_fields={"host": ["modinput_host_event_time_plugin.samples_1"]}, metadata={ "sourcetype": "test:indextime:sourcetype:modinput_host_event_time_plugin", @@ -33,6 +35,7 @@ def modinput_events(): ), SampleEvent( event="test_modinput_2 host=modinput_host_event_time_plugin.samples_2", + unique_identifier="uuid", key_fields={"host": ["modinput_host_event_time_plugin.samples_2"]}, metadata={ "sourcetype": "test:indextime:sourcetype:modinput_host_event_time_plugin", @@ -49,6 +52,7 @@ def modinput_events(): ), SampleEvent( event="fake event nothing happened", + unique_identifier="uuid", key_fields={}, metadata={ "host_type": "plugin", @@ -73,18 +77,21 @@ def modinput_posts_sent(): '"source": "pytest-splunk-addon:modinput", ' '"event": "test_modinput_1 host=modinput_host_event_time_plugin.samples_1", ' '"index": "main", ' + '"fields": {"unique_identifier": "uuid"}, ' '"host": "modinput_host_event_time_plugin.samples_1"' "}, {" '"sourcetype": "test:indextime:sourcetype:modinput_host_event_time_plugin", ' '"source": "pytest-splunk-addon:modinput", ' '"event": "test_modinput_2 host=modinput_host_event_time_plugin.samples_2", ' '"index": "main", ' + '"fields": {"unique_identifier": "uuid"}, ' '"host": "modinput_host_event_time_plugin.samples_2"' "}, {" '"sourcetype": "pytest_splunk_addon", ' '"source": "pytest_splunk_addon:hec:event", ' '"event": "fake event nothing happened", ' '"index": "fake_index", ' + '"fields": {"unique_identifier": "uuid"}, ' '"host": "fake host", ' '"time": 1234.5678' "}]", @@ -98,6 +105,7 @@ def file_monitor_events(): SampleEvent( event="host=test-host-file_monitor_host_prefix.sample-2 Test for host_prefix file_monitor" "host=test-host-file_monitor_host_prefix.sample-4 Test for host_prefix file_monitor", + unique_identifier="uuid", metadata={ "interval": "60", "earliest": "-60s", @@ -118,6 +126,7 @@ def file_monitor_events(): SampleEvent( event="test_failing_1 src=10.1.0.81 dest_ip=10.100.0.91 src_port=4889 dest_port=21 " "dvc=172.16.22.73 user=user297 test_list_all=a test_email=user297@email.com", + unique_identifier="uuid", metadata={ "sourcetype": "test:indextime:failing", "host_type": "plugin", @@ -135,6 +144,7 @@ def file_monitor_events(): ), SampleEvent( event="fake event nothing happened src=0.0.0.0 src_port=5050 dest=10.0.0.1 dest_port=6060", + unique_identifier="uuid", metadata={ "input_type": "file_monitor", "index": "fake_index", @@ -223,6 +233,7 @@ def requirement_events(): return [ SampleEvent( event="requirement event", + unique_identifier="uuid", metadata={ "source": "requirement source", "sourcetype": "requirement source type", @@ -245,6 +256,7 @@ def sc4s_events(): return [ SampleEvent( event='sc4s-host-plugin-time-sample-31 EPOEvents - EventFwd [agentInfo@3401 tenantId="1" bpsId="1" tenantGUID="50486da4-b851-47eb-9e27-a3337f14522f', + unique_identifier="uuid", metadata={ "timestamp_type": "event", "sourcetype": "mcafee:epo:syslog", @@ -261,6 +273,7 @@ def sc4s_events(): ), SampleEvent( event='sc4s-host-plugin-time-sample-32 EPOEvents - EventFwd [agentInfo@3401 tenantId="1" bpsId="1" tenantGUID="523efa00-cb66-4682-8ad7-c8b800adabd1"', + unique_identifier="uuid", metadata={ "timestamp_type": "event", "sourcetype": "mcafee:epo:syslog", diff --git a/tests/unit/tests_standard_lib/test_fields_tests/test_test_generator.py b/tests/unit/tests_standard_lib/test_fields_tests/test_test_generator.py index e57daefa..6716f04e 100644 --- a/tests/unit/tests_standard_lib/test_fields_tests/test_test_generator.py +++ b/tests/unit/tests_standard_lib/test_fields_tests/test_test_generator.py @@ -411,115 +411,119 @@ def test_generate_field_tests( assert param_mock.call_count == len(expected_output) -@pytest.mark.parametrize( - "tokenised_events, expected_output", - [ - ( - [ - SampleEvent( - event_string="escaped_event", - metadata={ - "input_type": "modinput", - "sourcetype_to_search": "dummy_sourcetype", - "host": "dummy_host", - }, - sample_name="file1.xml", - requirement_test_data={ - "cim_fields": { - "dest": "192.168.0.1", - "severity": "low", - "signature_id": "405001", - "src": "192.168.0.1", - "type": "event", +with patch("uuid.uuid4", return_value="uuid"): + + @pytest.mark.parametrize( + "tokenised_events, expected_output", + [ + ( + [ + SampleEvent( + event_string="escaped_event", + metadata={ + "input_type": "modinput", + "sourcetype_to_search": "dummy_sourcetype", + "host": "dummy_host", }, - "exceptions": {"mane_1": "value_1", "dest": "192.168.0.1"}, - "other_fields": { - "vendor_product": "Pytest Splunk Addon", - "target_users": "dummy.user@splunk.com", + sample_name="file1.xml", + requirement_test_data={ + "cim_fields": { + "dest": "192.168.0.1", + "severity": "low", + "signature_id": "405001", + "src": "192.168.0.1", + "type": "event", + }, + "exceptions": {"mane_1": "value_1", "dest": "192.168.0.1"}, + "other_fields": { + "vendor_product": "Pytest Splunk Addon", + "target_users": "dummy.user@splunk.com", + }, }, - }, - ), - SampleEvent( - event_string="escaped_event", - metadata={ - "input_type": "syslog_tcp", - "sourcetype_to_search": "dummy_sourcetype", - "host": "dummy_host_syslog", - }, - sample_name="file1.xml", - requirement_test_data={}, - ), - SampleEvent( - event_string="escaped_event", - metadata={ - "input_type": "syslog_tcp", - "sourcetype_to_search": "dummy_sourcetype", - "host": "dummy_host_syslog", - }, - sample_name="file1.xml", - requirement_test_data={ - "cim_fields": { - "src": "192.168.0.1", - "type": "event", + ), + SampleEvent( + event_string="escaped_event", + metadata={ + "input_type": "syslog_tcp", + "sourcetype_to_search": "dummy_sourcetype", + "host": "dummy_host_syslog", }, - "exceptions": {}, - "other_fields": { - "vendor_product": "Pytest Splunk Addon", - "target_users": "dummy.user@splunk.com", + sample_name="file1.xml", + requirement_test_data={}, + ), + SampleEvent( + event_string="escaped_event", + metadata={ + "input_type": "syslog_tcp", + "sourcetype_to_search": "dummy_sourcetype", + "host": "dummy_host_syslog", }, - }, - ), - ], - [ - ( - { - "escaped_event": "escaped_event", - "fields": { - "severity": "low", - "signature_id": "405001", - "src": "192.168.0.1", - "type": "event", - "vendor_product": "Pytest Splunk Addon", - "target_users": "dummy.user@splunk.com", + sample_name="file1.xml", + requirement_test_data={ + "cim_fields": { + "src": "192.168.0.1", + "type": "event", + }, + "exceptions": {}, + "other_fields": { + "vendor_product": "Pytest Splunk Addon", + "target_users": "dummy.user@splunk.com", + }, }, - "modinput_params": {"sourcetype": "dummy_sourcetype"}, - }, - "sample_name::file1.xml::host::dummy_host", - ), - ( - { - "escaped_event": "escaped_event", - "fields": { - "src": "192.168.0.1", - "type": "event", - "vendor_product": "Pytest Splunk Addon", - "target_users": "dummy.user@splunk.com", + ), + ], + [ + ( + { + "escaped_event": "escaped_event", + "fields": { + "severity": "low", + "signature_id": "405001", + "src": "192.168.0.1", + "type": "event", + "vendor_product": "Pytest Splunk Addon", + "target_users": "dummy.user@splunk.com", + }, + "modinput_params": {"sourcetype": "dummy_sourcetype"}, + "unique_identifier": "uuid", }, - "modinput_params": {"sourcetype": "dummy_sourcetype"}, - }, - "sample_name::file1.xml::host::dummy_host_syslog", - ), - ], - ), - ], -) -def test_generate_requirement_tests(tokenised_events, expected_output): - with patch.object( - xml_event_parser, "strip_syslog_header", return_value="escaped_event" - ), patch.object( - xml_event_parser, "escape_char_event", return_value="escaped_event" - ), patch.object( - pytest, "param", side_effect=lambda x, id: (x, id) - ) as param_mock: - out = list( - FieldTestGenerator( - "app_path", - tokenised_events, - "field_bank", - ).generate_requirements_tests() - ) - assert out == expected_output - assert param_mock.call_count == len(expected_output) + "sample_name::file1.xml::host::dummy_host", + ), + ( + { + "escaped_event": "escaped_event", + "fields": { + "src": "192.168.0.1", + "type": "event", + "vendor_product": "Pytest Splunk Addon", + "target_users": "dummy.user@splunk.com", + }, + "modinput_params": {"sourcetype": "dummy_sourcetype"}, + "unique_identifier": "uuid", + }, + "sample_name::file1.xml::host::dummy_host_syslog", + ), + ], + ), + ], + ) + def test_generate_requirement_tests(tokenised_events, expected_output): + with patch.object( + xml_event_parser, "strip_syslog_header", return_value="escaped_event" + ), patch.object( + xml_event_parser, "escape_char_event", return_value="escaped_event" + ), patch.object( + pytest, "param", side_effect=lambda x, id: (x, id) + ) as param_mock: + out = list( + FieldTestGenerator( + "app_path", + tokenised_events, + "field_bank", + ).generate_requirements_tests() + ) + assert out == expected_output + assert param_mock.call_count == len(expected_output) @pytest.mark.parametrize( diff --git a/tests/unit/tests_standard_lib/tests_sample_generation/test_sample_xdist_generator.py b/tests/unit/tests_standard_lib/tests_sample_generation/test_sample_xdist_generator.py index 0b7f5236..e8321a05 100644 --- a/tests/unit/tests_standard_lib/tests_sample_generation/test_sample_xdist_generator.py +++ b/tests/unit/tests_standard_lib/tests_sample_generation/test_sample_xdist_generator.py @@ -12,6 +12,7 @@ "sample_name", "metadata", "event", + "unique_identifier", "key_fields", "time_values", "requirement_test_data", @@ -30,6 +31,7 @@ "input_type": "modinput", }, "event_field", + "uuid", "key_fields_field", "time_values_field", "requirement_test_data", @@ -46,6 +48,7 @@ "sample_count": 4, }, "event_field", + "uuid", "key_fields_field", "time_values_field", "requirement_test_data", @@ -62,6 +65,7 @@ "sample_count": 4, }, "event_field", + "uuid", "key_fields_field_3", "time_values_field_3", "requirement_test_data", @@ -140,10 +144,10 @@ def test_store_events(self, exists_value, makedirs_calls): open_mock().write.assert_has_calls( [ call( - '{\n\t"sample_name_1": {\n\t\t"metadata": {\n\t\t\t"host": "host_1",\n\t\t\t"source": "source_1",\n\t\t\t"sourcetype": "sourcetype_1",\n\t\t\t"timestamp_type": "timestamp_type_1",\n\t\t\t"input_type": "modinput",\n\t\t\t"expected_event_count": 1,\n\t\t\t"index": "main"\n\t\t},\n\t\t"events": [\n\t\t\t{\n\t\t\t\t"event": "event_field",\n\t\t\t\t"key_fields": "key_fields_field",\n\t\t\t\t"time_values": "time_values_field",\n\t\t\t\t"requirement_test_data": "requirement_test_data"\n\t\t\t},\n\t\t\t{\n\t\t\t\t"event": "event_field",\n\t\t\t\t"key_fields": "key_fields_field_3",\n\t\t\t\t"time_values": "time_values_field_3",\n\t\t\t\t"requirement_test_data": "requirement_test_data"\n\t\t\t}\n\t\t]\n\t}\n}' + '{\n\t"sample_name_1": {\n\t\t"metadata": {\n\t\t\t"host": "host_1",\n\t\t\t"source": "source_1",\n\t\t\t"sourcetype": "sourcetype_1",\n\t\t\t"timestamp_type": "timestamp_type_1",\n\t\t\t"input_type": "modinput",\n\t\t\t"expected_event_count": 1,\n\t\t\t"index": "main"\n\t\t},\n\t\t"events": [\n\t\t\t{\n\t\t\t\t"event": "event_field",\n\t\t\t\t"unique_identifier": "uuid",\n\t\t\t\t"key_fields": "key_fields_field",\n\t\t\t\t"time_values": "time_values_field",\n\t\t\t\t"requirement_test_data": "requirement_test_data"\n\t\t\t},\n\t\t\t{\n\t\t\t\t"event": "event_field",\n\t\t\t\t"unique_identifier": "uuid",\n\t\t\t\t"key_fields": "key_fields_field_3",\n\t\t\t\t"time_values": "time_values_field_3",\n\t\t\t\t"requirement_test_data": "requirement_test_data"\n\t\t\t}\n\t\t]\n\t}\n}' ), call( - '{\n\t"sample_name_2": {\n\t\t"metadata": {\n\t\t\t"host": "host_2",\n\t\t\t"source": "source_2",\n\t\t\t"sourcetype": "sourcetype_2",\n\t\t\t"timestamp_type": "timestamp_type_2",\n\t\t\t"input_type": "input_else",\n\t\t\t"expected_event_count": 8,\n\t\t\t"index": "main"\n\t\t},\n\t\t"events": [\n\t\t\t{\n\t\t\t\t"event": "event_field",\n\t\t\t\t"key_fields": "key_fields_field",\n\t\t\t\t"time_values": "time_values_field",\n\t\t\t\t"requirement_test_data": "requirement_test_data"\n\t\t\t}\n\t\t]\n\t}\n}' + '{\n\t"sample_name_2": {\n\t\t"metadata": {\n\t\t\t"host": "host_2",\n\t\t\t"source": "source_2",\n\t\t\t"sourcetype": "sourcetype_2",\n\t\t\t"timestamp_type": "timestamp_type_2",\n\t\t\t"input_type": "input_else",\n\t\t\t"expected_event_count": 8,\n\t\t\t"index": "main"\n\t\t},\n\t\t"events": [\n\t\t\t{\n\t\t\t\t"event": "event_field",\n\t\t\t\t"unique_identifier": "uuid",\n\t\t\t\t"key_fields": "key_fields_field",\n\t\t\t\t"time_values": "time_values_field",\n\t\t\t\t"requirement_test_data": "requirement_test_data"\n\t\t\t}\n\t\t]\n\t}\n}' ), ] )