Skip to content

Commit

Permalink
Merge pull request #54 from cisagov/issue-27/input-and-output-file-di…
Browse files Browse the repository at this point in the history
…rectories-change

Convert OUTPUT_DIRECTORY to optional argument with default value
  • Loading branch information
nickviola committed Oct 2, 2023
2 parents 73b9b59 + 52117a3 commit aa12450
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 26 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,21 @@ The package collects raw data and creates an encrypted PDF.

```console
Usage:
tpt-reports ASSESSMENT_ID ELECTION_NAME DOMAIN_TESTED JSON_FILE_PATH OUTPUT_DIRECTORY [--log-level=LEVEL]
tpt-reports ASSESSMENT_ID ELECTION_NAME DOMAIN_TESTED JSON_FILE_PATH [--log-level=LEVEL] [--output-dir=OUTPUT_DIRECTORY]

Options:
-h --help Show this message.
-l --log-level=LEVEL If specified, then the log level will be set to
the specified value. Valid values are "debug", "info",
"warning", "error", and "critical". [default: info]
-o --output-dir=OUTPUT_DIRECTORY The directory where the final PDF reports
should be saved. [default: ~/]

Arguments:
ASSESSMENT_ID The assessment identifier.
ELECTION_NAME The name of the election being reported on.
DOMAIN_TESTED The email domain used in the testing.
JSON_FILE_PATH Path to the JSON file to act as a data source.
OUTPUT_DIRECTORY The directory where the final PDF
reports should be saved.
```

## Contributing ##
Expand Down
27 changes: 13 additions & 14 deletions src/tpt_reports/tpt_reports.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
"""cisagov/tpt-reports: A tool for creating Technical Phishing Test (TPT) reports.
Usage:
tpt-reports ASSESSMENT_ID ELECTION_NAME DOMAIN_TESTED JSON_FILE_PATH OUTPUT_DIRECTORY [--log-level=LEVEL]
tpt-reports ASSESSMENT_ID ELECTION_NAME DOMAIN_TESTED JSON_FILE_PATH [--log-level=LEVEL] [--output-dir=OUTPUT_DIRECTORY]
Options:
-h --help Show this message.
-l --log-level=LEVEL If specified, then the log level will be set to
the specified value. Valid values are "debug", "info",
"warning", "error", and "critical". [default: info]
-o --output-dir=OUTPUT_DIRECTORY The directory where the final PDF reports
should be saved. [default: ~/]
Arguments:
ASSESSMENT_ID The assessment identifier.
ELECTION_NAME The name of the election being reported on.
DOMAIN_TESTED The email domain used in the testing.
JSON_FILE_PATH Path to the JSON file to act as a data source.
OUTPUT_DIRECTORY The directory where the final PDF
reports should be saved.
"""

Expand Down Expand Up @@ -141,13 +142,13 @@ def main() -> None:
error="Possible values for --log-level are "
+ "debug, info, warning, error, and critical.",
),
"--output-dir": Use(str, error="--output-dir must be a string."),
"ASSESSMENT_ID": Use(str, error="ASSESSMENT_ID must be a string."),
# Issue #36 - Validate DOMAIN_TESTED argument inputs
# TODO: Provide input validation for DOMAIN_TESTED.
"ASSESSMENT_ID": Use(str, error="ASSESSMENT_ID must be a string."),
"ELECTION_NAME": Use(str, error="ELECTION_NAME must be a string."),
"DOMAIN_TESTED": Use(str, error="DOMAIN_TESTED must be a string."),
"ELECTION_NAME": Use(str, error="ELECTION_NAME must be a string."),
"JSON_FILE_PATH": Use(str, error="JSON_FILE_PATH must be a string."),
"OUTPUT_DIRECTORY": Use(str, error="OUTPUT_DIRECTORY must be a string."),
}
)

Expand All @@ -165,12 +166,12 @@ def main() -> None:
sys.exit(2)

# Assign validated arguments to variables
log_level: str = validated_args["--log-level"]
assessment_id: str = validated_args["ASSESSMENT_ID"]
election_name: str = validated_args["ELECTION_NAME"]
domain_tested: str = validated_args["DOMAIN_TESTED"]
output_directory: str = validated_args["OUTPUT_DIRECTORY"]
election_name: str = validated_args["ELECTION_NAME"]
json_file_path: str = validated_args["JSON_FILE_PATH"]
log_level: str = validated_args["--log-level"]
output_directory: str = validated_args["--output-dir"]

# Set up logging
logging.basicConfig(
Expand All @@ -183,11 +184,9 @@ def main() -> None:

LOGGER.info("Loading TPT Report, Version : %s", __version__)

# Issue #27 - Input and output file directories change
# TODO: Validate that the output_directory is not in the repo.
# Create output directory
if not os.path.exists(validated_args["OUTPUT_DIRECTORY"]):
os.mkdir(validated_args["OUTPUT_DIRECTORY"])
# Check if output directory exists and create if needed
if not os.path.exists(output_directory):
os.mkdir(output_directory)

if generate_reports(
assessment_id, election_name, domain_tested, output_directory, json_file_path
Expand Down
84 changes: 75 additions & 9 deletions tests/test_tpt_reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@
RELEASE_TAG = os.getenv("RELEASE_TAG")
PROJECT_VERSION = tpt_reports.__version__
TEST_JSON_FILE = "tests/data/test.json"
DEFAULT_OUTPUT_DIRECTORY = "~/"


def test_stdout_version(capsys):
@patch("tpt_reports.tpt_reports.generate_reports")
def test_stdout_version(mock_generate_reports, capsys):
"""Verify that version string sent to stdout agrees with the module version."""
with pytest.raises(SystemExit):
with patch.object(sys, "argv", ["bogus", "--version"]):
Expand Down Expand Up @@ -66,7 +68,8 @@ def test_release_version():


@pytest.mark.parametrize("level", log_levels)
def test_log_levels(level):
@patch("tpt_reports.tpt_reports.generate_reports")
def test_log_levels(mock_generate_reports, level):
"""Validate commandline log-level arguments."""
with patch.object(
sys,
Expand All @@ -77,8 +80,8 @@ def test_log_levels(level):
"test",
"test",
"cisa.gov",
"test.json",
"./test_output",
TEST_JSON_FILE,
"--output-dir=./test_output",
],
):
with patch.object(logging.root, "handlers", []):
Expand All @@ -100,12 +103,13 @@ def test_log_levels(level):
assert return_code is None, "main() should return success"


def test_bad_log_level():
@patch("tpt_reports.tpt_reports.generate_reports")
def test_bad_log_level(mock_generate_reports):
"""Validate bad log-level argument returns error."""
with patch.object(
sys,
"argv",
["bogus", "--log-level=emergency", "test", "test", "test", "test", "test"],
["bogus", "--log-level=emergency", "test", "test", "test", TEST_JSON_FILE],
):
return_code = None
try:
Expand All @@ -115,7 +119,8 @@ def test_bad_log_level():
assert return_code == 1, "main() should exit with error return code 1"


def test_domain_validation():
@patch("tpt_reports.tpt_reports.generate_reports")
def test_domain_validation(mock_generate_reports):
"""Validate invalid domain arguments."""
with patch.object(
sys,
Expand All @@ -126,8 +131,8 @@ def test_domain_validation():
"test",
"test",
"cisa",
"test.json",
"./test_output",
TEST_JSON_FILE,
"--output-dir=./test_output",
],
):
return_code = None
Expand Down Expand Up @@ -170,3 +175,64 @@ def test_parse_json():
assert payloads_meta["num_payloads"] == 1
assert payloads_meta["payloads_blocked"] == 1
assert payloads_meta["payloads_not_blocked"] == 1


@patch("tpt_reports.tpt_reports.generate_reports")
def test_no_output_directory(mock_generate_reports):
"""Validate that no output directory argument uses default."""
patched_args = [
"bogus",
"--log-level=info",
"test",
"test",
"test",
TEST_JSON_FILE,
]

# Patch usage arguments
with patch.object(sys, "argv", patched_args):
# Set mock return value
mock_generate_reports.return_value = True

# Call tpt_reports.main entry point function
result = tpt_reports.tpt_reports.main()
assert result is None

# Confirm generate_reports was called once with expected arguments
expected_call_args = (
"test",
"test",
"test",
DEFAULT_OUTPUT_DIRECTORY,
TEST_JSON_FILE,
)
assert mock_generate_reports.call_count == 1
assert mock_generate_reports.call_args[0] == expected_call_args


@patch("tpt_reports.tpt_reports.generate_reports")
def test_with_output_directory(mock_generate_reports):
"""Validate that when output directory is passed it overrides default."""
patched_args = [
"bogus",
"--log-level=info",
"test",
"test",
"test",
TEST_JSON_FILE,
"--output-dir=./",
]

# Patch usage arguments
with patch.object(sys, "argv", patched_args):
# Set mock return value
mock_generate_reports.return_value = True

# Call tpt_reports.main entry poifunction
result = tpt_reports.tpt_reports.main()
assert result is None

# Confirm generate_reports was called with expected parameters
expected_call_args = ("test", "test", "test", "./", TEST_JSON_FILE)
assert mock_generate_reports.call_count == 1
assert mock_generate_reports.call_args[0] == expected_call_args

0 comments on commit aa12450

Please sign in to comment.