Skip to content

Commit d7e1354

Browse files
Test/integrations (#91)
* Added initial setup for integration tests * Integration-test expects a profile set with password * Change tox to execute only unit tests * Refactor- use context management for teardown * Refactor- added teardown decorator * Add more alert tests * Added documentation * Use pexpect for prompt * Fix response change in pexpect * Added assert statement to validate non-empty response * Receiving EOF should be considered success * Fix single line response * make cleanup task OS independent for integration test
1 parent c192228 commit d7e1354

File tree

6 files changed

+142
-1
lines changed

6 files changed

+142
-1
lines changed

integration/__init__.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import os
2+
import pexpect
3+
4+
5+
LINE_FEED = b'\r\n'
6+
PASSWORD_PROMPT = b'Password: '
7+
ENCODING_TYPE = 'utf-8'
8+
9+
10+
def encode_response(line, encoding_type=ENCODING_TYPE):
11+
return line.decode(encoding_type)
12+
13+
14+
def run_command(command):
15+
16+
process = pexpect.spawn(command)
17+
response = []
18+
try:
19+
expected = process.expect([PASSWORD_PROMPT, pexpect.EOF])
20+
if expected == 0:
21+
process.sendline(os.environ["C42_PW"])
22+
process.expect(LINE_FEED)
23+
output = process.readlines()
24+
response = [encode_response(line) for line in output]
25+
else:
26+
output = process.before
27+
response = encode_response(output).splitlines()
28+
except pexpect.TIMEOUT:
29+
return 1, response
30+
return 0, response
31+
32+
33+
__all__ = [run_command]

integration/test_alerts.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import pytest
2+
import json
3+
4+
from integration import run_command
5+
from integration.util import cleanup_after_validation
6+
7+
ALERT_COMMAND = "code42 alerts print -b 2020-05-18 -e 2020-05-20"
8+
9+
10+
def _parse_response(response):
11+
return [json.loads(line) for line in response if len(line)]
12+
13+
14+
def _validate_field_value(field, value, response):
15+
parsed_response = _parse_response(response)
16+
assert len(parsed_response) > 0
17+
for record in parsed_response:
18+
assert record[field] == value
19+
20+
21+
@pytest.mark.parametrize(
22+
"command, field, value",
23+
[("{} --state OPEN".format(ALERT_COMMAND), "state", "OPEN"),
24+
("{} --state RESOLVED".format(ALERT_COMMAND), "state", "RESOLVED"),
25+
("{} --actor [email protected]".format(ALERT_COMMAND), "actor", "[email protected]"),
26+
("{} --rule-name 'File Upload Alert'".format(ALERT_COMMAND), "name", "File Upload Alert"),
27+
("{} --rule-id 962a6a1c-54f6-4477-90bd-a08cc74cbf71".format(ALERT_COMMAND), "ruleId",
28+
"962a6a1c-54f6-4477-90bd-a08cc74cbf71"),
29+
("{} --rule-type FedEndpointExfiltration".format(ALERT_COMMAND), "type",
30+
"FED_ENDPOINT_EXFILTRATION"),
31+
("{} --description 'Alert on any file upload'".format(ALERT_COMMAND), "description",
32+
"Alert on any file upload events"),
33+
]
34+
)
35+
def test_alert_prints_to_stdout_and_filters_result_by_given_value(command, field, value):
36+
return_code, response = run_command(command)
37+
assert return_code is 0
38+
_validate_field_value(field, value, response)
39+
40+
41+
def _validate_begin_date(response):
42+
parsed_response = _parse_response(response)
43+
assert len(parsed_response) > 0
44+
for record in parsed_response:
45+
assert record["createdAt"].startswith("2020-05-18")
46+
47+
48+
@pytest.mark.parametrize("command, validate", [
49+
(ALERT_COMMAND, _validate_begin_date),
50+
])
51+
def test_alert_prints_to_stdout_and_filters_result_between_given_date(command, validate):
52+
return_code, response = run_command(command)
53+
assert return_code is 0
54+
validate(response)
55+
56+
57+
def _validate_severity(response):
58+
record = json.loads(response)
59+
assert record["severity"] == "MEDIUM"
60+
61+
62+
@cleanup_after_validation("./integration/alerts")
63+
def test_alert_writes_to_file_and_filters_result_by_severity():
64+
command = "code42 alerts write-to ./integration/alerts -b 2020-05-18 -e 2020-05-20 " \
65+
"--severity MEDIUM"
66+
return_code, response = run_command(command)
67+
return _validate_severity

integration/util.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import os
2+
3+
4+
class cleanup(object):
5+
def __init__(self, filename):
6+
self.filename = filename
7+
8+
def __enter__(self):
9+
return open(self.filename, "r")
10+
11+
def __exit__(self, exc_type, exc_val, exc_tb):
12+
os.remove(self.filename)
13+
14+
15+
def cleanup_after_validation(filename):
16+
"""Decorator to read response from file for `write-to` commands and cleanup the file after test
17+
execution.
18+
19+
The decorated function should return validation function that takes the content of the file
20+
as input. e.g `test_alerts.py::test_alert_writes_to_file_and_filters_result_by_severity`
21+
"""
22+
def wrap(test_function):
23+
def wrapper():
24+
validate = test_function()
25+
with cleanup(filename) as f:
26+
response = f.read()
27+
validate(response)
28+
return wrapper
29+
return wrap

run_integration.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import sys
2+
import os
3+
4+
if __name__ == "__main__":
5+
if sys.argv[1] and sys.argv[2]:
6+
os.environ["C42_USER"] = sys.argv[1]
7+
os.environ["C42_PW"] = sys.argv[2]
8+
rc = os.system("pytest ./integration -v -rsxX -l --tb=short --strict")
9+
sys.exit(rc)
10+
else:
11+
print("username and password were not supplied. Integration tests will be skipped.")

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"keyring==18.0.1",
2626
"keyrings.alt==3.2.0",
2727
"py42>=1.2.0",
28+
"pexpect>=4.8"
2829
],
2930
license="MIT",
3031
include_package_data=True,

tox.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ commands =
1818
# -l: show locals in tracebacks
1919
# --tb=short: short traceback print mode
2020
# --strict: marks not registered in configuration file raise errors
21-
pytest --cov=code42cli --cov-append -v -rsxX -l --tb=short --strict --disable-pytest-warnings
21+
pytest tests --cov=code42cli --cov-append -v -rsxX -l --tb=short --strict --disable-pytest-warnings
2222

2323
depends =
2424
{py35,py36,py37,py38}: clean

0 commit comments

Comments
 (0)