Files
hate_crack/tests/test_proxy.py
Justin Bollinger 978a24a7c2 test: add 150 tests for attacks, wrappers, utils, api, and proxy
- tests/test_attacks_behavior.py: 29 tests for attack handler logic
  (loopback, extensive, top_mask, combinator, hybrid, ollama, simple pass-throughs)
- tests/test_hashcat_wrappers.py: 33 tests for hashcat subprocess wrappers
  (brute force, quick dict, combination, hybrid, prince, recycle, good measure, etc.)
- tests/test_main_utils.py: 44 tests for utility functions
  (_append_potfile_arg, generate_session_id, _ensure_hashfile_in_cwd,
  _run_hashcat_show, _dedup_netntlm_by_username, path resolution, cleanup)
- tests/test_api_downloads.py: 25 tests for api.py functions
  (sanitize_filename, check_7z, potfile config, hashmob key, extract_with_7z, download)
- tests/test_proxy.py: 18 tests for root module proxy mechanism
  (__getattr__, _sync_globals_to_main, _sync_callables_to_main, symbol re-export)

Also fix combinator_crack to abort gracefully when hcatCombinationWordlist is
a single string (only 1 wordlist configured) instead of crashing with IndexError.
2026-03-02 17:16:54 -05:00

126 lines
4.6 KiB
Python

import pytest
class TestGetAttrProxy:
def test_known_attribute_proxied(self, hc_module):
"""Accessing a known main.py global via hc_module returns main's value."""
val = hc_module._main.debug_mode
assert hc_module.debug_mode == val
def test_nonexistent_attribute_raises(self, hc_module):
"""Accessing a nonexistent attribute raises AttributeError."""
with pytest.raises(AttributeError):
_ = hc_module.this_attribute_does_not_exist_xyz
def test_getattr_returns_main_function(self, hc_module):
"""Functions defined in main.py are accessible via the proxy."""
assert callable(hc_module.generate_session_id)
class TestSyncGlobalsToMain:
@pytest.mark.parametrize(
"name,value",
[
("hcatHashType", "9999"),
("hcatHashFile", "/tmp/synced.txt"),
("hcatHashFileOrig", "/tmp/orig.txt"),
("pipalPath", "/tmp/pipal"),
("pipal_count", 42),
("debug_mode", True),
],
)
def test_syncs_global(self, hc_module, name, value):
"""Setting a synced name in hc_module.__dict__ and calling sync propagates to main."""
hc_module.__dict__[name] = value
hc_module._sync_globals_to_main()
assert getattr(hc_module._main, name) == value
def test_only_syncs_listed_names(self, hc_module):
"""Names not in the sync list are not pushed to main."""
hc_module.__dict__["_random_unlisted_var"] = "should_not_sync"
hc_module._sync_globals_to_main()
assert getattr(hc_module._main, "_random_unlisted_var", None) != "should_not_sync"
def test_absent_name_skipped(self, hc_module):
"""If a synced name is absent from hc_module globals, main is not modified."""
hc_module.__dict__.pop("pipal_count", None)
original = getattr(hc_module._main, "pipal_count", None)
hc_module._sync_globals_to_main()
assert getattr(hc_module._main, "pipal_count", None) == original
class TestSyncCallablesToMain:
def test_syncs_callable_to_main(self, hc_module):
"""A callable set in hc_module.__dict__ is pushed to main."""
def fake_fn():
return "fake"
hc_module.__dict__["quit_hc"] = fake_fn
hc_module._sync_callables_to_main()
assert hc_module._main.quit_hc is fake_fn
def test_syncs_show_results(self, hc_module):
"""show_results callable is pushed to main when present."""
def fake_fn():
return "results"
hc_module.__dict__["show_results"] = fake_fn
hc_module._sync_callables_to_main()
assert hc_module._main.show_results is fake_fn
def test_skips_when_not_in_globals(self, hc_module):
"""If a callable name is absent from hc_module globals, main is unchanged."""
original = getattr(hc_module._main, "show_readme", None)
hc_module.__dict__.pop("show_readme", None)
hc_module._sync_callables_to_main()
assert getattr(hc_module._main, "show_readme", None) == original
def test_all_callable_names_sync(self, hc_module):
"""All callable names in the sync list are pushed when present."""
names = [
"weakpass_wordlist_menu",
"download_hashmob_wordlists",
"download_hashmob_rules",
"hashview_api",
"export_excel",
"show_results",
"show_readme",
"quit_hc",
]
def make_fn(n: str):
def fn() -> str:
return n
return fn
fakes = {name: make_fn(name) for name in names}
for name, fn in fakes.items():
hc_module.__dict__[name] = fn
hc_module._sync_callables_to_main()
for name, fn in fakes.items():
assert getattr(hc_module._main, name) is fn
class TestSymbolReexport:
def test_main_function_accessible(self, hc_module):
"""Functions from main.py should be accessible on hc_module."""
assert callable(getattr(hc_module, "hcatBruteForce", None))
def test_hc_module_has_main_ref(self, hc_module):
"""hc_module._main should be the hate_crack.main module."""
import hate_crack.main as main_mod
assert hc_module._main is main_mod
def test_debug_mode_reexported(self, hc_module):
"""Module-level globals from main.py appear on hc_module at load time."""
assert hasattr(hc_module, "debug_mode")
def test_generate_session_id_reexported(self, hc_module):
"""generate_session_id from main.py is accessible directly on hc_module."""
fn = getattr(hc_module, "generate_session_id", None)
assert callable(fn)