Skip to content

Commit d3338d5

Browse files
Feature/unit tests/infra 30041/sample generation (#266)
* First part of unit tests for sample_generation
1 parent a832557 commit d3338d5

File tree

5 files changed

+516
-42
lines changed

5 files changed

+516
-42
lines changed

.github/workflows/test_matrix.yml

Lines changed: 39 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,39 @@
1-
#name: Test Matrix
2-
#
3-
#on: [push]
4-
#
5-
#jobs:
6-
# build:
7-
# runs-on: ubuntu-latest
8-
# strategy:
9-
# fail-fast: false
10-
# matrix:
11-
# os: [ubuntu-latest, macos-latest, windows-latest]
12-
# python-version: [3.7]
13-
# splunk-version: [7.2, 7.3, 8.0]
14-
#
15-
# steps:
16-
# - uses: actions/checkout@v2
17-
# with:
18-
# submodules: true
19-
# - name: Set up OS=${{ matrix.os }}::Python=${{ matrix.python-version }}::Splunk=${{ matrix.splunk-version }}
20-
# uses: actions/setup-python@v1
21-
# with:
22-
# python-version: ${{ matrix.python-version }}
23-
# - name: Install dependencies
24-
# run: |
25-
# sudo apt-get install -y build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev libffi-dev liblzma-dev python-openssl git
26-
# curl https://pyenv.run | bash
27-
# export PATH="~/.pyenv/bin:$PATH"
28-
# eval "$(pyenv init -)"
29-
# pyenv install 3.7.8
30-
# pyenv local 3.7.8
31-
# curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python
32-
# source ~/.poetry/env
33-
# - name: Test with pytest
34-
# run: |
35-
# export PATH="~/.pyenv/bin:$PATH"
36-
# eval "$(pyenv init -)"
37-
# source ~/.poetry/env
38-
# poetry install -E docker
39-
# poetry run pytest -v --splunk-version=${{ matrix.splunk-version }} -m docker
1+
name: Test Matrix
2+
3+
on: [push]
4+
5+
jobs:
6+
build:
7+
runs-on: ubuntu-latest
8+
strategy:
9+
fail-fast: false
10+
matrix:
11+
os: [ubuntu-latest, macos-latest, windows-latest]
12+
python-version: [3.7]
13+
splunk-version: [7.2, 7.3, 8.0]
14+
15+
steps:
16+
- uses: actions/checkout@v2
17+
with:
18+
submodules: true
19+
- name: Set up OS=${{ matrix.os }}::Python=${{ matrix.python-version }}::Splunk=${{ matrix.splunk-version }}
20+
uses: actions/setup-python@v1
21+
with:
22+
python-version: ${{ matrix.python-version }}
23+
- name: Install dependencies
24+
run: |
25+
sudo apt-get install -y build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev libffi-dev liblzma-dev python-openssl git
26+
curl https://pyenv.run | bash
27+
export PATH="~/.pyenv/bin:$PATH"
28+
eval "$(pyenv init -)"
29+
pyenv install 3.7.8
30+
pyenv local 3.7.8
31+
curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python
32+
source ~/.poetry/env
33+
- name: Test with pytest
34+
run: |
35+
export PATH="~/.pyenv/bin:$PATH"
36+
eval "$(pyenv init -)"
37+
source ~/.poetry/env
38+
poetry install -E docker
39+
poetry run pytest -v --splunk-version=${{ matrix.splunk-version }} -m docker

.github/workflows/unit_tests.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ jobs:
3232
eval "$(pyenv init -)"
3333
source ~/.poetry/env
3434
poetry install
35-
poetry run pytest -v --cov=pytest_splunk_addon --cov-report=html tests/unit
35+
poetry run coverage run --source=./pytest_splunk_addon/standard_lib -m pytest -v tests/unit
36+
poetry run coverage html
3637
- name: Archive test coverage results
3738
uses: actions/upload-artifact@v2
3839
with:

pytest_splunk_addon/standard_lib/sample_generation/sample_event.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,6 @@ def update_metadata(self, event, metadata, key_fields):
301301

302302
return event, metadata, key_fields
303303

304-
except IndexError as error:
304+
except KeyError as error:
305305
LOGGER.error(f"Unexpected data found. Error: {error}")
306-
raise Exception(error)
306+
raise error
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
from collections import namedtuple
2+
from unittest.mock import MagicMock, ANY
3+
4+
import pytest
5+
6+
import pytest_splunk_addon.standard_lib.sample_generation.rule
7+
8+
TOKEN_DATA = "token_data"
9+
FIELD = "Field"
10+
EVENTGEN_PARAMS = {"eventgen_params": "eventgen_params_value"}
11+
SAMPLE_PATH = "sample_path"
12+
SAMPLE_NAME = "Sample_name"
13+
RETURN_VALUE = "Return_value"
14+
IPV4 = "IPv4"
15+
IPV6 = "IPv4"
16+
FQDN = "fqdn"
17+
REPL = "repl"
18+
19+
TokenValue = namedtuple("TokenValue", ["value"])
20+
21+
22+
def token(replacement=REPL, replacement_type="static"):
23+
return {
24+
"token": TOKEN_DATA,
25+
"replacement": replacement,
26+
"replacementType": replacement_type,
27+
"field": FIELD,
28+
}
29+
30+
31+
def test_raise_warning(caplog):
32+
warning_message = "Warning_message"
33+
pytest_splunk_addon.standard_lib.sample_generation.rule.raise_warning(
34+
warning_message
35+
)
36+
assert caplog.messages == [warning_message]
37+
38+
39+
@pytest.fixture
40+
def event():
41+
def func(token_count=1):
42+
eve = MagicMock()
43+
eve.sample_name = SAMPLE_NAME
44+
eve.get_token_count.return_value = token_count
45+
eve.replacement_map = {}
46+
return eve
47+
48+
return func
49+
50+
51+
class TestRule:
52+
@pytest.fixture
53+
def rule(self):
54+
return pytest_splunk_addon.standard_lib.sample_generation.rule.Rule(token())
55+
56+
@pytest.fixture
57+
def mock_class(self, monkeypatch):
58+
def func(class_to_mock):
59+
class_mock = MagicMock()
60+
class_mock.return_value = RETURN_VALUE
61+
monkeypatch.setattr(
62+
f"pytest_splunk_addon.standard_lib.sample_generation.rule.{class_to_mock}",
63+
class_mock,
64+
)
65+
return class_mock
66+
67+
return func
68+
69+
@pytest.mark.parametrize(
70+
"rule_name, _token, params, params_dict",
71+
[
72+
("StaticRule", token(), [token()], {}),
73+
(
74+
"TimeRule",
75+
token(replacement_type="timestamp"),
76+
[token(replacement_type="timestamp"), EVENTGEN_PARAMS],
77+
{},
78+
),
79+
(
80+
"FileRule",
81+
token(replacement_type="file"),
82+
[token(replacement_type="file")],
83+
{"sample_path": SAMPLE_PATH},
84+
),
85+
(
86+
"FileRule",
87+
token(replacement_type="mvfile"),
88+
[token(replacement_type="mvfile")],
89+
{"sample_path": SAMPLE_PATH},
90+
),
91+
(
92+
"HexRule",
93+
token(replacement_type="random", replacement="hex"),
94+
[token(replacement_type="random", replacement="hex")],
95+
{"sample_path": SAMPLE_PATH},
96+
),
97+
(
98+
"ListRule",
99+
token(replacement_type="all", replacement="list_repl"),
100+
[token(replacement_type="all", replacement="list_repl")],
101+
{"sample_path": SAMPLE_PATH},
102+
),
103+
(
104+
"Ipv4Rule",
105+
token(replacement_type="all", replacement="ipv4"),
106+
[token(replacement_type="random", replacement="ipv4")],
107+
{"sample_path": SAMPLE_PATH},
108+
),
109+
],
110+
)
111+
def test_parse_rule(self, rule, mock_class, rule_name, _token, params, params_dict):
112+
static_mock = mock_class(rule_name)
113+
assert rule.parse_rule(_token, EVENTGEN_PARAMS, SAMPLE_PATH) == RETURN_VALUE
114+
static_mock.assert_called_once_with(*params, **params_dict)
115+
116+
def test_parse_rule_other_repl_type(self, rule):
117+
assert (
118+
rule.parse_rule(
119+
token(replacement_type="other", replacement="dest"),
120+
EVENTGEN_PARAMS,
121+
SAMPLE_PATH,
122+
)
123+
is None
124+
)
125+
126+
def test_apply_replacement_type_all(self, mock_class, event):
127+
sample_event_mock = mock_class("SampleEvent")
128+
return_event_1 = MagicMock()
129+
return_event_2 = MagicMock()
130+
sample_event_mock.copy.side_effect = [return_event_1, return_event_2]
131+
replace_mock = MagicMock()
132+
token_values = [[TokenValue(1)], [TokenValue(2)]]
133+
replace_mock.side_effect = token_values
134+
rule = pytest_splunk_addon.standard_lib.sample_generation.rule.Rule(
135+
token(replacement_type="all")
136+
)
137+
rule.replace = replace_mock
138+
event1 = event()
139+
event2 = event()
140+
events = [event1, event2]
141+
assert rule.apply(events) == [return_event_1, return_event_2]
142+
assert (
143+
pytest_splunk_addon.standard_lib.sample_generation.rule.event_host_count
144+
== 2
145+
)
146+
for e, tv in zip(
147+
[return_event_1, return_event_2], [TokenValue(1), TokenValue(2)]
148+
):
149+
e.replace_token.assert_called_once_with(TOKEN_DATA, tv.value)
150+
e.register_field_value.assert_called_once_with(FIELD, tv)
151+
152+
def test_apply_replacement_type_not_all(self, event):
153+
replace_mock = MagicMock()
154+
token_values = [[TokenValue(1)], [TokenValue(2)], [TokenValue(3)]]
155+
replace_mock.side_effect = token_values
156+
rule = pytest_splunk_addon.standard_lib.sample_generation.rule.Rule(
157+
token(replacement_type="random")
158+
)
159+
rule.replace = replace_mock
160+
event1 = event()
161+
event2 = event()
162+
event3 = event(token_count=0)
163+
events = [event1, event2, event3]
164+
assert rule.apply(events) == events
165+
for e, tv in zip(events[:2], token_values[:2]):
166+
e.replace_token.assert_called_once_with(TOKEN_DATA, tv)
167+
e.register_field_value.assert_called_once_with(FIELD, tv)
168+
assert not event3.replace_token.called
169+
assert not event3.register_field_value.called
170+
171+
def test_get_lookup_value(self, rule, event):
172+
eve = event()
173+
test_key = "test_key"
174+
header_2 = "header2"
175+
headers = ["header1", header_2]
176+
one = "one"
177+
csv_template = [
178+
"user{}",
179+
"user{}@email.com",
180+
r"sample_domain.com\user{}",
181+
"CN=user{}",
182+
]
183+
184+
def create_csv(value):
185+
return [e.format(value) for e in csv_template]
186+
187+
def validate(value_list, index_list, csv, email_count, result_csv):
188+
assert rule.get_lookup_value(eve, test_key, headers, value_list) == (
189+
index_list,
190+
csv,
191+
)
192+
pytest_splunk_addon.standard_lib.sample_generation.rule.user_email_count = (
193+
email_count
194+
)
195+
assert eve.replacement_map == {test_key: result_csv}
196+
197+
csv_row_1 = create_csv("1")
198+
validate([one, header_2], [1], csv_row_1, 1, [csv_row_1])
199+
csv_row_2 = create_csv("2")
200+
validate([one] + headers, [0, 1], csv_row_2, 2, [csv_row_1, csv_row_2])
201+
202+
@pytest.mark.parametrize(
203+
"value_list, expected",
204+
[
205+
(["host", "more"], [FIELD]),
206+
(["host1", "ipv4", "ipv6"], [IPV4, IPV6]),
207+
([FQDN], [FQDN]),
208+
(["one", "two", "three"], []),
209+
],
210+
)
211+
def test_get_rule_replacement_values(self, rule, value_list, expected):
212+
sample = MagicMock()
213+
sample.get_field_host.side_effect = [FIELD]
214+
sample.get_ipv4.side_effect = [IPV4]
215+
sample.get_ipv6.side_effect = [IPV6]
216+
sample.get_field_fqdn.side_effect = [FQDN]
217+
assert rule.get_rule_replacement_values(sample, value_list, ANY) == expected
218+
219+
def test_clean_rules(self, rule):
220+
pytest_splunk_addon.standard_lib.sample_generation.rule.event_host_count = 25
221+
assert (
222+
pytest_splunk_addon.standard_lib.sample_generation.rule.event_host_count
223+
== 25
224+
)
225+
rule.clean_rules()
226+
assert (
227+
pytest_splunk_addon.standard_lib.sample_generation.rule.event_host_count
228+
== 0
229+
)
230+
231+
232+
@pytest.mark.parametrize(
233+
"repl_type, repl, expected",
234+
[
235+
("random", "integer[30:212]", ([44, 44], [44, 44])),
236+
("all", "Integer[4:7]", (["4", "4"], ["5", "5"], ["6", "6"])),
237+
],
238+
)
239+
def test_int_rule(event, monkeypatch, repl_type, repl, expected):
240+
rule = pytest_splunk_addon.standard_lib.sample_generation.rule.IntRule(
241+
token(replacement=repl, replacement_type=repl_type)
242+
)
243+
eve = event()
244+
monkeypatch.setattr(
245+
pytest_splunk_addon.standard_lib.sample_generation.rule,
246+
"randint",
247+
MagicMock(return_value=44),
248+
)
249+
assert list(rule.replace(eve, 2)) == [rule.token_value(i, j) for i, j in expected]
250+
251+
252+
def test_int_rule_no_match(event, caplog):
253+
rule = pytest_splunk_addon.standard_lib.sample_generation.rule.IntRule(token())
254+
eve = event()
255+
assert list(rule.replace(eve, 40)) == []
256+
assert caplog.messages == [
257+
f"Non-supported format: '{REPL}' in stanza '{SAMPLE_NAME}'.\n Try integer[0:10]"
258+
]

0 commit comments

Comments
 (0)