diff --git a/puzzle_generator/create_puzzle.py b/puzzle_generator/create_puzzle.py index 64ccfb5..f8b5bf4 100644 --- a/puzzle_generator/create_puzzle.py +++ b/puzzle_generator/create_puzzle.py @@ -12,16 +12,16 @@ from . import bytestr_utils as bu -def _run_puzzle(in_puzzle, in_decrypt): +def run_puzzle(in_puzzle, in_decrypt, get_answer): print(in_puzzle["str"]) if "rest" in in_puzzle: - this_pass = input() + this_pass = get_answer() new_puzzle = decrypt_data(in_puzzle["rest"], this_pass, in_decrypt) if new_puzzle is None: print("This is a wrong answer. Try again!") sys.exit(1) else: - _run_puzzle(new_puzzle, in_decrypt) + run_puzzle(new_puzzle, in_decrypt, get_answer) def _create_str(in_modules, in_objects, in_encrypted_puzzle, constants_str: str) -> str: @@ -33,7 +33,7 @@ def _create_str(in_modules, in_objects, in_encrypted_puzzle, constants_str: str) modules_str = "\n".join("import " + _ for _ in in_modules) + "\n" objects_str = "\n".join(inspect.getsource(_) for _ in in_objects) puzzle_data_str = f"_PUZZLE = {in_encrypted_puzzle}" - call_str = "_run_puzzle(_PUZZLE, _DECRYPT)" + call_str = "run_puzzle(_PUZZLE, _DECRYPT, input)" return ( "\n".join( @@ -78,7 +78,7 @@ def get_needed_objects(self): bu.bytestr_to_bytes, se.get_decrypt, decrypt_data, - _run_puzzle, + run_puzzle, ] def get_constants_str( @@ -124,7 +124,7 @@ def get_needed_objects(self): bu.bytestr_to_bytes, sse.get_decrypt, decrypt_data, - _run_puzzle, + run_puzzle, ] def get_constants_str( diff --git a/pyproject.toml b/pyproject.toml index dcc6735..73022ea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "puzzle-generator" -version = "0.4.0" +version = "0.4.1" description = "Generates python code representing a puzzle" authors = ["piotr.idzik "] readme = "./puzzle_generator/README.md" diff --git a/tests/test_create_puzzle.py b/tests/test_create_puzzle.py index 51e45a2..90b36ee 100644 --- a/tests/test_create_puzzle.py +++ b/tests/test_create_puzzle.py @@ -1,14 +1,30 @@ import pathlib import hashlib import subprocess +import typing +import itertools +import collections import pytest +import utils import puzzle_generator.create_puzzle as cp +import puzzle_generator.puzzle_data_encryption as pde +_InputOutput = collections.namedtuple("_InputOutput", ["input", "output"]) -@pytest.fixture(name="puzzle") -def fixture_puzzle(): - return { +_PuzzleTestCase = collections.namedtuple( + "_PuzzleTestCase", + [ + "puzzle", + "correct", + "wrong", + ], +) + + +@pytest.fixture(name="puzzle_tc") +def fixture_puzzle_tc(): + puzzle = { "str": "Question 1?", "pass": "Answer 1", "rest": { @@ -17,6 +33,23 @@ def fixture_puzzle(): "rest": {"str": "Congratulations!"}, }, } + return _PuzzleTestCase( + puzzle=puzzle, + correct=_InputOutput( + input=["Answer 1", "Is this the final answer?"], + output="Question 1?\nQuestion 2?\nCongratulations!\n", + ), + wrong=[ + _InputOutput( + input=["Answer 1", "This is a wrong answer"], + output="Question 1?\nQuestion 2?\nThis is a wrong answer. Try again!\n", + ), + _InputOutput( + input=["This is a wrong answer"], + output="Question 1?\nThis is a wrong answer. Try again!\n", + ), + ], + ) @pytest.fixture(name="puzzle_path") @@ -63,30 +96,95 @@ def _run_puzzle_file( @pytest.mark.parametrize("configuration", _CONFIGURATIONS) -def test_all_good_answers(puzzle, puzzle_path: pathlib.Path, configuration) -> None: - cp.create(puzzle, puzzle_path, **configuration) - res = _run_puzzle_file(puzzle_path, ["Answer 1", "Is this the final answer?"]) +def test_all_good_answers( + puzzle_tc, + puzzle_path: pathlib.Path, + configuration, +) -> None: + cp.create(puzzle_tc.puzzle, puzzle_path, **configuration) + res = _run_puzzle_file(puzzle_path, puzzle_tc.correct.input) assert res.returncode == 0 - assert res.stdout == "Question 1?\nQuestion 2?\nCongratulations!\n" + assert res.stdout == puzzle_tc.correct.output assert not res.stderr @pytest.mark.parametrize("configuration", _CONFIGURATIONS) -def test_second_answer_wrong(puzzle, puzzle_path: pathlib.Path, configuration) -> None: - cp.create(puzzle, puzzle_path, **configuration) - res = _run_puzzle_file(puzzle_path, ["Answer 1", "This is a wrong answer"]) - assert res.returncode == 1 - assert ( - res.stdout == "Question 1?\nQuestion 2?\nThis is a wrong answer. Try again!\n" - ) - assert not res.stderr +def test_wrong_answers( + puzzle_tc, + puzzle_path: pathlib.Path, + configuration, +) -> None: + for cur_wrong in puzzle_tc.wrong: + cp.create(puzzle_tc.puzzle, puzzle_path, **configuration) + res = _run_puzzle_file(puzzle_path, cur_wrong.input) + assert res.returncode == 1 + assert res.stdout == cur_wrong.output + assert not res.stderr + + +def get_input_simulator(answers: typing.List[str]) -> typing.Callable[[], str]: + cur_input = 0 + + def _input_simulator() -> str: + nonlocal cur_input + res = answers[cur_input] + cur_input += 1 + return res + + return _input_simulator + + +_SOME_HASHES = [ + hashlib.sha3_384, + hashlib.sha3_256, +] +_PROC_SPICES = [b"11", b"22"] +_SIGNATURE_SPICES = [b"27", b"07", b"2024"] -@pytest.mark.parametrize("configuration", _CONFIGURATIONS) -def test_first_answer_wrong(puzzle, puzzle_path: pathlib.Path, configuration) -> None: - cp.create(puzzle, puzzle_path, **configuration) - res = _run_puzzle_file(puzzle_path, ["This is a wrong answer."]) - assert res.returncode == 1 - assert res.stdout == "Question 1?\nThis is a wrong answer. Try again!\n" - assert not res.stderr +_ENCRYPT_DECRYPT_PAIRS = [ + utils.get_simple_encrypt_decrypt_pair(*_) + for _ in itertools.product(_SOME_HASHES, repeat=2) +] + [ + utils.get_spiced_simple_encrypt_decrypt_pair(*_, _PROC_SPICES, _SIGNATURE_SPICES) + for _ in itertools.product(_SOME_HASHES, repeat=2) +] + + +def _get_input_simulator(answers: typing.List[str]) -> typing.Callable[[], str]: + cur_input = 0 + + def _input_simulator() -> str: + nonlocal cur_input + res = answers[cur_input] + cur_input += 1 + return res + + return _input_simulator + + +@pytest.mark.parametrize(("encrypt", "decrypt"), _ENCRYPT_DECRYPT_PAIRS) +def test_run_puzzle_all_good_answers(capsys, puzzle_tc, encrypt, decrypt) -> None: + encrypted_puzzle = pde.encrypt_data(puzzle_tc.puzzle, encrypt) + cp.run_puzzle( + encrypted_puzzle, decrypt, _get_input_simulator(puzzle_tc.correct.input) + ) + captured = capsys.readouterr() + assert captured.out == puzzle_tc.correct.output + + +@pytest.mark.parametrize(("encrypt", "decrypt"), _ENCRYPT_DECRYPT_PAIRS) +def test_run_puzzle_wrong_answers(capsys, puzzle_tc, encrypt, decrypt) -> None: + for cur_wrong in puzzle_tc.wrong: + encrypted_puzzle = pde.encrypt_data(puzzle_tc.puzzle, encrypt) + with pytest.raises(SystemExit) as exc_info: + cp.run_puzzle( + encrypted_puzzle, + decrypt, + _get_input_simulator(cur_wrong.input), + ) + captured = capsys.readouterr() + assert captured.out == cur_wrong.output + assert exc_info.type is SystemExit + assert exc_info.value.code == 1