Skip to content

Commit b65ea4b

Browse files
Feature/unit tests/infra 30094/standard lib (#296)
* test_test_generator * fix for ut internal error * test_file_monitor_ingestor * too long line splitted * test_savedsearches_parser * test optimization * savedsearches tests in addon_parser.__init__ * mock_object optimization * test_app_test_generator.py * os.path.join mock fix * removing unreachable code * exclude addon_basic.py from coverage calculation
1 parent 4a04cc7 commit b65ea4b

File tree

11 files changed

+843
-60
lines changed

11 files changed

+843
-60
lines changed

.coveragerc

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
# .coveragerc to control coverage.py
22
[run]
33
omit =
4-
*/test_templates.py
4+
pytest_splunk_addon/standard_lib/*/test_templates.py
55
pytest_splunk_addon/standard_lib/cim_compliance/base_report.py
66
pytest_splunk_addon/standard_lib/cim_compliance/base_table.py
77
pytest_splunk_addon/standard_lib/cim_tests/base_schema.py
88
pytest_splunk_addon/standard_lib/event_ingestors/base_event_ingestor.py
9+
pytest_splunk_addon/standard_lib/addon_basic.py
910

1011
[report]
1112
exclude_lines =

pytest_splunk_addon/standard_lib/app_test_generator.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,8 @@ def generate_tests(self, fixture):
120120
test_type="line_breaker"
121121
)
122122
)
123-
if isinstance(pytest_params, str):
124-
LOGGER.warning(pytest_params)
125123

126-
elif pytest_params:
127-
yield from sorted(pytest_params, key=lambda param: param.id)
124+
yield from sorted(pytest_params, key=lambda param: param.id)
128125

129126
def dedup_tests(self, test_list, fixture):
130127
"""

tests/unit/tests_standard_lib/conftest.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55

66
@pytest.fixture()
77
def mock_object(monkeypatch):
8-
def create_mock_object(object_path):
9-
mo = MagicMock()
8+
def create_mock_object(object_path, **kwargs):
9+
mo = MagicMock(**kwargs)
1010
monkeypatch.setattr(object_path, mo)
1111
return mo
1212

@@ -20,6 +20,13 @@ def open_mock(monkeypatch):
2020
return open_mock
2121

2222

23+
@pytest.fixture()
24+
def os_path_join_file_mock(mock_object):
25+
os = mock_object("os.path.join")
26+
os.side_effect = lambda *x: "/".join(x)
27+
return os
28+
29+
2330
@pytest.fixture()
2431
def json_load_mock(mock_object):
2532
return mock_object("json.load")

tests/unit/tests_standard_lib/test_addon_parser/test_pytest_addon_init.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
PROPS_RETURN_VALUE = "Props_return_value"
88
TAGS_RETURN_VALUE = "Tags_return_value"
99
EVENTTYPE_RETURN_VALUE = "Eventtype_return_value"
10+
SAVEDSEARCH_RETURN_VALUE = "Savedsearch_return_value"
1011
TEST_VALUE = "Test_value"
1112
ADDON_PARSER_PATH = "pytest_splunk_addon.standard_lib.addon_parser"
1213

@@ -19,11 +20,14 @@ def addonparser():
1920
f"{ADDON_PARSER_PATH}.tags_parser.TagsParser"
2021
) as tags_mock, patch(
2122
f"{ADDON_PARSER_PATH}.eventtype_parser.EventTypeParser"
22-
) as eventtype_mock:
23+
) as eventtype_mock, patch(
24+
f"{ADDON_PARSER_PATH}.savedsearches_parser.SavedSearchParser"
25+
) as savedsearch_mock:
2326
app_mock.return_value = APP_RETURN_VALUE
2427
props_mock.return_value = PROPS_RETURN_VALUE
2528
tags_mock.return_value = TAGS_RETURN_VALUE
2629
eventtype_mock.return_value = EVENTTYPE_RETURN_VALUE
30+
savedsearch_mock.return_value = SAVEDSEARCH_RETURN_VALUE
2731
import pytest_splunk_addon.standard_lib.addon_parser
2832

2933
importlib.reload(pytest_splunk_addon.standard_lib.addon_parser)
@@ -37,6 +41,7 @@ def test_addonparser_init(addonparser):
3741
assert ap.props_parser == PROPS_RETURN_VALUE
3842
assert ap.tags_parser == TAGS_RETURN_VALUE
3943
assert ap.eventtype_parser == EVENTTYPE_RETURN_VALUE
44+
assert ap.savedsearch_parser == SAVEDSEARCH_RETURN_VALUE
4045

4146

4247
@pytest.mark.parametrize(
@@ -45,6 +50,7 @@ def test_addonparser_init(addonparser):
4550
("get_tags", "tags_parser"),
4651
("get_props_fields", "props_parser"),
4752
("get_eventtypes", "eventtype_parser"),
53+
("get_savedsearches", "savedsearch_parser"),
4854
],
4955
)
5056
def test_get_methods(addonparser, monkeypatch, function, obj_to_mock):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import pytest
2+
from unittest.mock import patch, PropertyMock
3+
from pytest_splunk_addon.standard_lib.addon_parser.savedsearches_parser import (
4+
SavedSearchParser,
5+
)
6+
7+
output_to_build = {
8+
"basic_search": {
9+
"search": "_internal | stats count by sourcetype",
10+
},
11+
"search_earliest_time": {
12+
"search": "index = _internal | stats count by sourcetype | outputlookup saved_search_data.csv",
13+
"dispatch.earliest_time": "-4d",
14+
},
15+
"empty_search_latest_time": {
16+
"search": "",
17+
"dispatch.latest_time": "-1s",
18+
},
19+
}
20+
21+
22+
@pytest.fixture(scope="module")
23+
def parsed_output(build_parsed_output):
24+
return build_parsed_output(output_to_build)
25+
26+
27+
@pytest.fixture()
28+
def parser_instance(parsed_output, parser):
29+
return parser(SavedSearchParser, "get_config", parsed_output)
30+
31+
32+
def test_savedsearches(parser_instance):
33+
assert list(parser_instance.savedsearches.sects.keys()) == [
34+
"basic_search",
35+
"search_earliest_time",
36+
"empty_search_latest_time",
37+
]
38+
parser_instance.app.get_config.assert_called_once_with("savedsearches.conf")
39+
40+
41+
def test_no_savedsearches_config_file(parser_instance):
42+
parser_instance.app.get_config.side_effect = OSError
43+
assert parser_instance.savedsearches is None
44+
45+
46+
def test_get_savedsearches(parser_instance):
47+
out = list(parser_instance.get_savedsearches())
48+
assert out == [
49+
{
50+
"stanza": "basic_search",
51+
"search": "_internal | stats count by sourcetype",
52+
"dispatch.earliest_time": "0",
53+
"dispatch.latest_time": "now",
54+
},
55+
{
56+
"stanza": "search_earliest_time",
57+
"search": "index = _internal | stats count by sourcetype | outputlookup saved_search_data.csv",
58+
"dispatch.earliest_time": "-4d",
59+
"dispatch.latest_time": "now",
60+
},
61+
{
62+
"stanza": "empty_search_latest_time",
63+
"search": 'index = "main"',
64+
"dispatch.earliest_time": "0",
65+
"dispatch.latest_time": "-1s",
66+
},
67+
]
68+
69+
70+
def test_get_savedsearches_without_config_file(parser):
71+
with patch.object(
72+
SavedSearchParser, "savedsearches", new_callable=PropertyMock
73+
) as savedsearches_mock:
74+
savedsearches_mock.return_value = None
75+
parser_instance = parser(SavedSearchParser, "get_config", {})
76+
output = [search for search in parser_instance.get_savedsearches() if search]
77+
assert output == [], "savedsearches returned when no config file exists"

tests/unit/tests_standard_lib/test_addon_parser/test_tags_parser.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,7 @@ def test_tags_can_be_parsed_and_returned(parser_instance):
5656
def test_get_tags_calls_app_get_config(parser_instance):
5757
for _ in parser_instance.get_tags():
5858
pass
59-
parser_instance.app.get_config.assert_called_once()
60-
parser_instance.app.get_config.assert_called_with("tags.conf")
59+
parser_instance.app.get_config.assert_called_once_with("tags.conf")
6160

6261

6362
def test_no_tags_config_file(parser_instance):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
import pytest
2+
from unittest.mock import patch
3+
from collections import namedtuple
4+
from pytest_splunk_addon.standard_lib.app_test_generator import AppTestGenerator
5+
6+
module = "pytest_splunk_addon.standard_lib.app_test_generator"
7+
config = {
8+
"splunk_app": "fake_app",
9+
"field_bank": "fake_field_bank",
10+
"splunk_dm_path": "fake_path",
11+
"store_events": True,
12+
"splunk_data_generator": "psa.conf",
13+
}
14+
pytest_config = namedtuple("Config", ["getoption"])
15+
test_config = pytest_config(getoption=lambda x, *y: config[x])
16+
test_config_without_dm_path = pytest_config(
17+
getoption=lambda x, *y: config[x] if x != "splunk_dm_path" else None
18+
)
19+
params = namedtuple("ParameterSet", ["values", "id"])
20+
21+
22+
@pytest.fixture()
23+
def app_test_generator(mock_object):
24+
fieldtest_generator = mock_object(f"{module}.FieldTestGenerator")
25+
cim_test_generator = mock_object(f"{module}.CIMTestGenerator")
26+
indextime_test_generator = mock_object(f"{module}.IndexTimeTestGenerator")
27+
requirement_test_generator = mock_object(f"{module}.ReqsTestGenerator")
28+
for mock_element in [
29+
fieldtest_generator,
30+
cim_test_generator,
31+
indextime_test_generator,
32+
requirement_test_generator,
33+
]:
34+
setattr(mock_element, "return_value", mock_element)
35+
36+
37+
@pytest.mark.parametrize(
38+
"simple_config, path",
39+
[
40+
(test_config, "fake_path"),
41+
(test_config_without_dm_path, "/fake_dir/data_models"),
42+
],
43+
)
44+
def test_app_test_generator_instantiation(
45+
mock_object, os_path_join_file_mock, app_test_generator, simple_config, path
46+
):
47+
os_path_dirname_mock = mock_object("os.path.dirname")
48+
os_path_dirname_mock.return_value = "/fake_dir"
49+
atg = AppTestGenerator(simple_config)
50+
atg.fieldtest_generator.assert_called_once_with(
51+
config["splunk_app"], field_bank=config["field_bank"]
52+
)
53+
atg.cim_test_generator.assert_called_once_with(config["splunk_app"], path)
54+
atg.requirement_test_generator.assert_called_once_with(config["splunk_app"])
55+
atg.indextime_test_generator.assert_called_once_with()
56+
57+
58+
@pytest.mark.parametrize(
59+
"fixture, called_function, test_generator, generator_args, generator_kwargs, expected_tests, dedup_call_count",
60+
[
61+
(
62+
"splunk_searchtime_fields",
63+
"fieldtest_generator",
64+
lambda fixture: (f"{fixture}_test_{i + 1}" for i in range(3)),
65+
["splunk_searchtime_fields"],
66+
{},
67+
[
68+
"splunk_searchtime_fields_test_1",
69+
"splunk_searchtime_fields_test_2",
70+
"splunk_searchtime_fields_test_3",
71+
],
72+
1,
73+
),
74+
(
75+
"splunk_searchtime_cim",
76+
"cim_test_generator",
77+
lambda fixture: (f"{fixture}_test_{i + 1}" for i in range(3)),
78+
["splunk_searchtime_cim"],
79+
{},
80+
[
81+
"splunk_searchtime_cim_test_1",
82+
"splunk_searchtime_cim_test_2",
83+
"splunk_searchtime_cim_test_3",
84+
],
85+
1,
86+
),
87+
(
88+
"splunk_searchtime_requirement",
89+
"requirement_test_generator",
90+
lambda fixture: (f"{fixture}_test_{i + 1}" for i in range(3)),
91+
["splunk_searchtime_requirement"],
92+
{},
93+
[
94+
"splunk_searchtime_requirement_test_1",
95+
"splunk_searchtime_requirement_test_2",
96+
"splunk_searchtime_requirement_test_3",
97+
],
98+
1,
99+
),
100+
(
101+
"splunk_indextime_key_fields",
102+
"indextime_test_generator",
103+
lambda x, app_path, config_path, test_type: (
104+
params(values=f"splunk_indextime_{test_type}_test_{3 - i}", id=3 - i)
105+
for i in range(3)
106+
),
107+
[True],
108+
{
109+
"app_path": "fake_app",
110+
"config_path": "psa.conf",
111+
"test_type": "key_fields",
112+
},
113+
[
114+
params(values=f"splunk_indextime_key_fields_test_1", id=1),
115+
params(values=f"splunk_indextime_key_fields_test_2", id=2),
116+
params(values=f"splunk_indextime_key_fields_test_3", id=3),
117+
],
118+
0,
119+
),
120+
(
121+
"splunk_indextime_time",
122+
"indextime_test_generator",
123+
lambda x, app_path, config_path, test_type: (
124+
params(values=f"splunk_indextime_{test_type}_test_{3 - i}", id=3 - i)
125+
for i in range(3)
126+
),
127+
[True],
128+
{"app_path": "fake_app", "config_path": "psa.conf", "test_type": "_time"},
129+
[
130+
params(values=f"splunk_indextime__time_test_1", id=1),
131+
params(values=f"splunk_indextime__time_test_2", id=2),
132+
params(values=f"splunk_indextime__time_test_3", id=3),
133+
],
134+
0,
135+
),
136+
(
137+
"splunk_indextime_line_breaker",
138+
"indextime_test_generator",
139+
lambda x, app_path, config_path, test_type: (
140+
params(values=f"splunk_indextime_{test_type}_test_{3 - i}", id=3 - i)
141+
for i in range(3)
142+
),
143+
[True],
144+
{
145+
"app_path": "fake_app",
146+
"config_path": "psa.conf",
147+
"test_type": "line_breaker",
148+
},
149+
[
150+
params(values=f"splunk_indextime_line_breaker_test_1", id=1),
151+
params(values=f"splunk_indextime_line_breaker_test_2", id=2),
152+
params(values=f"splunk_indextime_line_breaker_test_3", id=3),
153+
],
154+
0,
155+
),
156+
],
157+
)
158+
def test_generate_tests(
159+
app_test_generator,
160+
fixture,
161+
called_function,
162+
test_generator,
163+
generator_args,
164+
generator_kwargs,
165+
expected_tests,
166+
dedup_call_count,
167+
):
168+
atg = AppTestGenerator(test_config)
169+
setattr(getattr(atg, called_function).generate_tests, "side_effect", test_generator)
170+
with patch.object(
171+
AppTestGenerator, "dedup_tests", side_effect=lambda x, y: x
172+
) as dedup_mock:
173+
out = list(atg.generate_tests(fixture))
174+
assert out == expected_tests
175+
getattr(atg, called_function).generate_tests.assert_called_once_with(
176+
*generator_args, **generator_kwargs
177+
)
178+
assert dedup_mock.call_count == dedup_call_count
179+
180+
181+
def test_dedup_tests(app_test_generator):
182+
parameter_list = [params(values=f"val{x}", id=x) for x in range(7)]
183+
atg = AppTestGenerator(test_config)
184+
out = []
185+
for parameters in [parameter_list[:3], parameter_list[2:5]]:
186+
out.extend(atg.dedup_tests(parameters, "splunk_searchtime"))
187+
assert out == parameter_list[:5]
188+
assert atg.seen_tests == {("splunk_searchtime", x) for x in range(5)}

0 commit comments

Comments
 (0)