diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..06f64e0 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,27 @@ +# tests/conftest.py +import pytest +import toml +from pathlib import Path + +@pytest.fixture(autouse=True) +def setup_test_config(tmp_path, monkeypatch): + """Autouse fixture to isolate test runs from the real config.toml.""" + test_config = tmp_path / "config.toml" + default_data = { + "Competition": { + "competition": "picoctf", + "catagory": "textCat", + "challenge": "testChal", + "flag_format": r"picoCTF\{.*\}", + "last_flag": "flag{my_flag_here}" + }, + "Enviroment": { + "ctf_dir": "/home/venus/ctf" + } + } + test_config.parent.mkdir(parents=True, exist_ok=True) + with open(test_config, "w") as f: + toml.dump(default_data, f) + + monkeypatch.setenv("CTF_CONFIG_PATH", str(test_config)) + return test_config diff --git a/tests/test_cli_helpers.py b/tests/test_cli_helpers.py index 8cad7a9..58a7e9b 100644 --- a/tests/test_cli_helpers.py +++ b/tests/test_cli_helpers.py @@ -4,7 +4,7 @@ import pytest from pathlib import Path from click.testing import CliRunner from unittest.mock import patch -from ctf.config import load_config, write_config +from ctf.config import Config # {{{ test_flag_cmd_cli def test_flag_cmd_cli(): @@ -13,30 +13,21 @@ def test_flag_cmd_cli(): """ from ctf.main import cli - config_file = Path("/home/venus/code/ctf/config.toml") - original_config = load_config(str(config_file)) + cfg = Config() + cfg.data["Competition"]["last_flag"] = "flag{test_cli_flag}" + cfg.save(cfg.data) - try: - # Pre-set last_flag in config - temp_config = load_config(str(config_file)) - if "Competition" not in temp_config: - temp_config["Competition"] = {} - temp_config["Competition"]["last_flag"] = "flag{test_cli_flag}" - write_config(temp_config, str(config_file)) - - runner = CliRunner() - - # Test standard flag command output - result_std = runner.invoke(cli, ["flag"]) - assert result_std.exit_code == 0 - assert "Last detected flag: flag{test_cli_flag}" in result_std.output - - # Test plain flag command output - result_plain = runner.invoke(cli, ["flag", "--plain"]) - assert result_plain.exit_code == 0 - assert result_plain.output.strip() == "flag{test_cli_flag}" - finally: - write_config(original_config, str(config_file)) + runner = CliRunner() + + # Test standard flag command output + result_std = runner.invoke(cli, ["flag"]) + assert result_std.exit_code == 0 + assert "Last detected flag: flag{test_cli_flag}" in result_std.output + + # Test plain flag command output + result_plain = runner.invoke(cli, ["flag", "--plain"]) + assert result_plain.exit_code == 0 + assert result_plain.output.strip() == "flag{test_cli_flag}" # }}} # {{{ test_flag_cmd_exemption @@ -46,29 +37,21 @@ def test_flag_cmd_exemption(capsys): """ from ctf.main import main - config_file = Path("/home/venus/code/ctf/config.toml") - original_config = load_config(str(config_file)) + cfg = Config() + cfg.data["Competition"]["flag_format"] = r"flag\{[a-z_]+\}" + cfg.data["Competition"]["last_flag"] = "flag{test_exempt_flag}" + cfg.save(cfg.data) - try: - temp_config = load_config(str(config_file)) - if "Competition" not in temp_config: - temp_config["Competition"] = {} - temp_config["Competition"]["flag_format"] = r"flag\{[a-z_]+\}" - temp_config["Competition"]["last_flag"] = "flag{test_exempt_flag}" - write_config(temp_config, str(config_file)) + with patch.object(sys, "argv", ["ctf", "flag"]): + with pytest.raises(SystemExit) as exc_info: + main() + assert exc_info.value.code == 0 - with patch.object(sys, "argv", ["ctf", "flag"]): - with pytest.raises(SystemExit) as exc_info: - main() - assert exc_info.value.code == 0 - - captured = capsys.readouterr() - # Verify that the warning box is NOT in stdout - assert "Potential flag(s) detected in command output" not in captured.out - # Verify that the actual flag info IS printed - assert "Last detected flag: flag{test_exempt_flag}" in captured.out - finally: - write_config(original_config, str(config_file)) + captured = capsys.readouterr() + # Verify that the warning box is NOT in stdout + assert "Potential flag(s) detected in command output" not in captured.out + # Verify that the actual flag info IS printed + assert "Last detected flag: flag{test_exempt_flag}" in captured.out # }}} # {{{ test_flag_cmd_list_format @@ -78,29 +61,21 @@ def test_flag_cmd_list_format(): """ from ctf.main import cli - config_file = Path("/home/venus/code/ctf/config.toml") - original_config = load_config(str(config_file)) + cfg = Config() + cfg.data["Competition"]["flag_format"] = "TEST_FORMAT{[a-z]+}" + cfg.save(cfg.data) - try: - temp_config = load_config(str(config_file)) - if "Competition" not in temp_config: - temp_config["Competition"] = {} - temp_config["Competition"]["flag_format"] = "TEST_FORMAT{[a-z]+}" - write_config(temp_config, str(config_file)) - - runner = CliRunner() - - # Test standard output - result = runner.invoke(cli, ["flag", "-l"]) - assert result.exit_code == 0 - assert "Current flag format: TEST_FORMAT{[a-z]+}" in result.output - - # Test plain output - result_plain = runner.invoke(cli, ["flag", "-l", "-p"]) - assert result_plain.exit_code == 0 - assert result_plain.output.strip() == "TEST_FORMAT{[a-z]+}" - finally: - write_config(original_config, str(config_file)) + runner = CliRunner() + + # Test standard output + result = runner.invoke(cli, ["flag", "-l"]) + assert result.exit_code == 0 + assert "Current flag format: TEST_FORMAT{[a-z]+}" in result.output + + # Test plain output + result_plain = runner.invoke(cli, ["flag", "-l", "-p"]) + assert result_plain.exit_code == 0 + assert result_plain.output.strip() == "TEST_FORMAT{[a-z]+}" # }}} # {{{ test_flag_cmd_set_format @@ -110,18 +85,11 @@ def test_flag_cmd_set_format(): """ from ctf.main import cli - config_file = Path("/home/venus/code/ctf/config.toml") - original_config = load_config(str(config_file)) + runner = CliRunner() + result = runner.invoke(cli, ["flag", "-s", "NEW_FLAG_FORMAT{[0-9]+}"]) + assert result.exit_code == 0 + assert "Flag format set to: NEW_FLAG_FORMAT{[0-9]+}" in result.output - try: - runner = CliRunner() - result = runner.invoke(cli, ["flag", "-s", "NEW_FLAG_FORMAT{[0-9]+}"]) - assert result.exit_code == 0 - assert "Flag format set to: NEW_FLAG_FORMAT{[0-9]+}" in result.output - - updated_config = load_config(str(config_file)) - assert updated_config["Competition"]["flag_format"] == "NEW_FLAG_FORMAT{[0-9]+}" - finally: - write_config(original_config, str(config_file)) + updated_cfg = Config() + assert updated_cfg.data["Competition"]["flag_format"] == "NEW_FLAG_FORMAT{[0-9]+}" # }}} - diff --git a/tests/test_commands.py b/tests/test_commands.py index 3f45ba0..06e2590 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -1,10 +1,10 @@ +# tests/test_commands.py from pathlib import Path -import toml from click.testing import CliRunner from ctf.commands import basic_group +from ctf.config import Config -TEST_ENV = Path("tests/env") - +# {{{ test_basic_test_cli def test_basic_test_cli(): """ Verifies that 'basic test' runs successfully and outputs 'hello from test'. @@ -13,7 +13,9 @@ def test_basic_test_cli(): result = runner.invoke(basic_group, ["test"]) assert result.exit_code == 0 assert "hello from test" in result.output +# }}} +# {{{ test_basic_greet_cli def test_basic_greet_cli(): """ Verifies that 'basic greet' runs successfully and greets the name argument. @@ -22,65 +24,44 @@ def test_basic_greet_cli(): result = runner.invoke(basic_group, ["greet", "Alice"]) assert result.exit_code == 0 assert "hello Alice" in result.output +# }}} +# {{{ test_basic_set_flag_format_cli def test_basic_set_flag_format_cli(): """ Verifies that 'basic set-flag-format' CLI command writes the pattern to config.toml. """ - from ctf.config import load_config - runner = CliRunner() - config_file = Path("/home/venus/code/ctf/config.toml") - # Save the original config to restore later - original_config = load_config(str(config_file)) + cfg = Config() + assert cfg.data["Competition"]["flag_format"] == r"picoCTF\{.*\}" - try: - result = runner.invoke(basic_group, ["set-flag-format", "TEST_FLAG{[a-z]+}"]) - assert result.exit_code == 0 - assert "Flag format set to" in result.output - - # Verify it was written to config.toml - updated_config = load_config(str(config_file)) - assert updated_config["Competition"]["flag_format"] == "TEST_FLAG{[a-z]+}" - finally: - # Restore original config - from ctf.config import write_config - write_config(original_config, str(config_file)) + result = runner.invoke(basic_group, ["set-flag-format", "TEST_FLAG{[a-z]+}"]) + assert result.exit_code == 0 + assert "Flag format set to" in result.output + + updated_cfg = Config() + assert updated_cfg.data["Competition"]["flag_format"] == "TEST_FLAG{[a-z]+}" +# }}} +# {{{ test_basic_set_flag_format_original_option_cli def test_basic_set_flag_format_original_option_cli(): """ Verifies that 'basic set-flag-format' with --original/-o option prompts for selection and writes it. """ - from ctf.config import load_config - runner = CliRunner() - config_file = Path("/home/venus/code/ctf/config.toml") - # Save the original config to restore later - original_config = load_config(str(config_file)) + result = runner.invoke(basic_group, ["set-flag-format", "-o", "SKY-1111-000"], input="2\n") + assert result.exit_code == 0 + assert "Select a pattern index" in result.output + assert "Flag format set to" in result.output - try: - # We pass -o SKY-1111-000 and input "2" to choose the second suggested pattern - result = runner.invoke(basic_group, ["set-flag-format", "-o", "SKY-1111-000"], input="2\n") - assert result.exit_code == 0 - comp_name = original_config.get("Competition", {}).get("competition", "") - if comp_name: - assert f"Suggested regex patterns for competition {comp_name}:" in result.output - else: - assert "Suggested regex patterns:" in result.output - assert "Select a pattern index" in result.output - assert "Flag format set to" in result.output - - # Verify it was written to config.toml - updated_config = load_config(str(config_file)) - selected_pattern = updated_config["Competition"]["flag_format"] - assert len(selected_pattern) > 0 - finally: - # Restore original config - from ctf.config import write_config - write_config(original_config, str(config_file)) + updated_cfg = Config() + selected_pattern = updated_cfg.data["Competition"]["flag_format"] + assert len(selected_pattern) > 0 +# }}} +# {{{ test_basic_set_flag_format_validation_cli def test_basic_set_flag_format_validation_cli(): """ Verifies validation rules: @@ -104,53 +85,36 @@ def test_basic_set_flag_format_validation_cli(): result3 = runner.invoke(basic_group, ["set-flag-format", "-o", " "]) assert result3.exit_code != 0 assert "Original flag cannot be empty or whitespace only." in result3.output +# }}} +# {{{ test_basic_set_competition_cli def test_basic_set_competition_cli(): """ Verifies that 'basic set-competition' updates the competition name in config.toml. """ - from ctf.config import load_config - runner = CliRunner() - config_file = Path("/home/venus/code/ctf/config.toml") - original_config = load_config(str(config_file)) + result = runner.invoke(basic_group, ["set-competition", "CyberCTF2026"]) + assert result.exit_code == 0 + assert "Competition name set to: CyberCTF2026" in result.output - try: - result = runner.invoke(basic_group, ["set-competition", "CyberCTF2026"]) - assert result.exit_code == 0 - assert "Competition name set to: CyberCTF2026" in result.output - - updated_config = load_config(str(config_file)) - assert updated_config["Competition"]["competition"] == "CyberCTF2026" - finally: - from ctf.config import write_config - write_config(original_config, str(config_file)) + updated_cfg = Config() + assert updated_cfg.data["Competition"]["competition"] == "CyberCTF2026" +# }}} +# {{{ test_basic_set_flag_format_warning_cli def test_basic_set_flag_format_warning_cli(): """ Verifies that if competition name is set and not present in the example flag, set-flag-format outputs a warning. """ - from ctf.config import load_config, write_config - runner = CliRunner() - config_file = Path("/home/venus/code/ctf/config.toml") - original_config = load_config(str(config_file)) - try: - # Set competition name first - temp_config = load_config(str(config_file)) - if "Competition" not in temp_config: - temp_config["Competition"] = {} - temp_config["Competition"]["competition"] = "SECURE" - write_config(temp_config, str(config_file)) - - # Now run set-flag-format with an example flag that has no "SECURE" substring - # We also pass input "1" to satisfy the choice prompt - result = runner.invoke(basic_group, ["set-flag-format", "-o", "CTF{easy_flag_123}"], input="1\n") - assert result.exit_code == 0 - assert "Warning: Current competition name 'SECURE' was not found in the example flag." in result.output - finally: - write_config(original_config, str(config_file)) - - + # Set competition name first + cfg = Config() + cfg.data["Competition"]["competition"] = "SECURE" + cfg.save(cfg.data) + + result = runner.invoke(basic_group, ["set-flag-format", "-o", "CTF{easy_flag_123}"], input="1\n") + assert result.exit_code == 0 + assert "Warning: Current competition name 'SECURE' was not found in the example flag." in result.output +# }}} diff --git a/tests/test_config.py b/tests/test_config.py index ee76691..86e4d63 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,13 +1,13 @@ # tests/test_config.py import toml from pathlib import Path -from ctf.config import load_config, write_config +from ctf.config import Config TEST_ENV = Path("tests/env") # {{{ test_load_config_static def test_load_config_static(): - """Verifies load_config using the persistent test file.""" + """Verifies Config loading using a persistent test file.""" config_file = TEST_ENV / "config.toml" test_data = { "Competition": {"name": "PersistentComp"}, @@ -17,17 +17,19 @@ def test_load_config_static(): with open(config_file, "w") as f: toml.dump(test_data, f) - loaded_data = load_config(str(config_file)) - assert loaded_data["Competition"]["name"] == "PersistentComp" + cfg = Config(config_file) + assert cfg.data["Competition"]["name"] == "PersistentComp" # }}} # {{{ test_write_config_static def test_write_config_static(): - """Verifies write_config by writing and reloading.""" + """Verifies Config writing by saving and reloading.""" config_file = TEST_ENV / "test_write_config.toml" test_data = {"TestKey": "TestVal"} - write_config(test_data, str(config_file)) - loaded = load_config(str(config_file)) - assert loaded == test_data + cfg = Config(config_file) + cfg.save(test_data) + + reloaded = Config(config_file) + assert reloaded.data == test_data # }}} diff --git a/tests/test_forensics.py b/tests/test_forensics.py index 27065de..e79dbb8 100644 --- a/tests/test_forensics.py +++ b/tests/test_forensics.py @@ -4,7 +4,6 @@ import os import stat import sys from click.testing import CliRunner -from ctf.config import load_config from ctf.utils import active_categories from ctf.forensics import get_metadata from ctf.cli_forensics import forensics_group, metadata