Skip to content

Commit 4fae451

Browse files
committed
#34 - Experiment with analyzer integration tests
1 parent fcfd40f commit 4fae451

1 file changed

Lines changed: 191 additions & 0 deletions

File tree

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
import json
2+
import tempfile
3+
from contextlib import contextmanager
4+
from pathlib import Path
5+
from typing import Generator
6+
7+
import pytest
8+
9+
from cortexutils.analyzer import Analyzer
10+
11+
DEFAULT_INPUT = {
12+
"dataType": "ip",
13+
"data": "1.2.3.4",
14+
}
15+
16+
DEFAULT_OUTPUT = {
17+
"success": True,
18+
"full": {},
19+
"summary": {},
20+
"artifacts": [],
21+
"operations": [],
22+
}
23+
24+
25+
@contextmanager
26+
def init_job_directory(input_obj: dict | None = None) -> Generator[Path]:
27+
"""Context manager that yields a temporary job directory path, then cleans up."""
28+
temp_dir = tempfile.TemporaryDirectory()
29+
job_dir = Path(temp_dir.name)
30+
31+
try:
32+
input_dir = job_dir / "input"
33+
input_dir.mkdir()
34+
35+
if input_obj is None:
36+
input_obj = DEFAULT_INPUT
37+
38+
with open(input_dir / "input.json", "w") as f:
39+
json.dump(input_obj, f)
40+
41+
output_dir = job_dir / "output"
42+
output_dir.mkdir()
43+
44+
yield job_dir
45+
46+
finally:
47+
temp_dir.cleanup()
48+
49+
50+
def load_output(job_directory: Path) -> dict:
51+
with open(job_directory / "output" / "output.json") as output_file:
52+
output = json.load(output_file)
53+
return output
54+
55+
56+
def test_simple_analyzer_success():
57+
with init_job_directory() as job_directory:
58+
Analyzer(job_directory).report({})
59+
output = load_output(job_directory)
60+
assert output == DEFAULT_OUTPUT
61+
62+
63+
def test_simple_analyzer_error():
64+
65+
error_msg = "test analyzer error"
66+
with init_job_directory() as job_directory:
67+
with pytest.raises(SystemExit):
68+
Analyzer(job_directory).error(error_msg)
69+
output = load_output(job_directory)
70+
71+
assert output == {
72+
"success": False,
73+
"input": DEFAULT_INPUT,
74+
"errorMessage": error_msg,
75+
}
76+
77+
78+
def test_analyzer_report_with_summary_and_taxonomies():
79+
class TestAnalyzer(Analyzer):
80+
def summary(self, raw):
81+
taxonomies = []
82+
for taxonomy_level in ["info", "safe", "suspicious", "malicious", "n/a"]:
83+
taxonomies.append(
84+
self.build_taxonomy(
85+
level=taxonomy_level,
86+
namespace="cortexutils",
87+
predicate="integration-tests",
88+
value="analyzer",
89+
)
90+
)
91+
return {"taxonomies": taxonomies, "raw": raw}
92+
93+
with init_job_directory() as job_directory:
94+
full_report = {}
95+
TestAnalyzer(job_directory).report(full_report)
96+
output = load_output(job_directory)
97+
98+
assert output == {
99+
**DEFAULT_OUTPUT,
100+
"summary": {
101+
"raw": full_report,
102+
"taxonomies": [
103+
{
104+
"level": level,
105+
"namespace": "cortexutils",
106+
"predicate": "integration-tests",
107+
"value": "analyzer",
108+
}
109+
for level in ["info", "safe", "suspicious", "malicious", "info"]
110+
],
111+
},
112+
}
113+
114+
115+
def test_analyzer_report_with_operations():
116+
class TestAnalyzer(Analyzer):
117+
def operations(self, raw):
118+
return [self.build_operation(op_type="DummyOperation", dummy="parameter")]
119+
120+
with init_job_directory() as job_directory:
121+
TestAnalyzer(job_directory).report({})
122+
output = load_output(job_directory)
123+
124+
assert output == {
125+
**DEFAULT_OUTPUT,
126+
"operations": [{"type": "DummyOperation", "dummy": "parameter"}],
127+
}
128+
129+
130+
def test_analyzer_report_with_extractable_artifacts():
131+
string_ip_artifact = "11.22.33.44"
132+
list_ip_artifacts = ["10.20.30.40", "20.30.40.50"]
133+
dict_item_ip_artifact = "100.100.100.100"
134+
135+
report = {
136+
"simple-ip": string_ip_artifact,
137+
"list-of-ips": list_ip_artifacts,
138+
"dict-with-ip": {"just-an-ip": dict_item_ip_artifact},
139+
}
140+
141+
with init_job_directory() as job_directory:
142+
Analyzer(job_directory).report(report)
143+
output = load_output(job_directory)
144+
145+
assert output == {
146+
**DEFAULT_OUTPUT,
147+
"artifacts": [
148+
{"data": ip, "dataType": "ip"}
149+
for ip in [string_ip_artifact, *list_ip_artifacts, dict_item_ip_artifact]
150+
],
151+
"full": report,
152+
}
153+
154+
155+
def test_analyzer_error_for_invalid_input():
156+
157+
empty_input = {}
158+
with init_job_directory(empty_input) as job_directory:
159+
with pytest.raises(SystemExit):
160+
Analyzer(job_directory)
161+
output = load_output(job_directory)
162+
163+
assert output == {
164+
"success": False,
165+
"input": empty_input,
166+
"errorMessage": "Missing dataType field",
167+
}
168+
169+
generic_input_without_data = {"dataType": "ip"}
170+
with init_job_directory(generic_input_without_data) as job_directory:
171+
with pytest.raises(SystemExit):
172+
Analyzer(job_directory).report({})
173+
output = load_output(job_directory)
174+
175+
assert output == {
176+
"success": False,
177+
"input": generic_input_without_data,
178+
"errorMessage": "Missing data field",
179+
}
180+
181+
file_input_without_filename = {"dataType": "file"}
182+
with init_job_directory(file_input_without_filename) as job_directory:
183+
with pytest.raises(SystemExit):
184+
Analyzer(job_directory).report({})
185+
output = load_output(job_directory)
186+
187+
assert output == {
188+
"success": False,
189+
"input": file_input_without_filename,
190+
"errorMessage": "Missing filename.",
191+
}

0 commit comments

Comments
 (0)