Repository navigation
Contributing
blackbuble edited this page Oct 30, 2026
·
1 revision
git clone <repo>
cd pentestai
# Install dev dependencies
pip install -r requirements-dev.txt
# Run tests in parallel (required on this host; serial runs can stall)
python -m pytest tests/ -n 4 --timeout=60 -q
# With coverage
python -m coverage run -m pytest tests/ -n 4 --timeout=60 -q
coverage reportOffline tests use VCR cassettes (tests/cassettes/) and tool output fixtures
(tests/fixtures/tool_outputs/). Record new cassettes only with your own key:
VCR_RECORD_MODE=all pytest tests/test_llm_vcr.py -m network -qSecrets are filtered automatically by tests/conftest.py. Do not commit cassettes that
still contain real auth headers.
Required for every PR:
-
Assertions must be meaningful — not
assert result is not None# ❌ Wrong result = store.search("sql injection") assert isinstance(result, list) # ✅ Right result = store.search("sql injection") assert len(result) >= 1 assert "sql" in result[0].document.text.lower() assert result[0].score > 0.5
-
Test isolation — use
tmp_pathand unique IDs# ❌ Wrong — hardcoded session ID can collide a = AuditLogger("session1", "test") # ✅ Right import uuid sid = f"test_{uuid.uuid4().hex[:8]}" a = AuditLogger(sid, "test")
-
Verify coverage — if a test claims to cover specific lines
# Verify test hits target line coverage run --include=module.py \ -m pytest tests/test_file.py::TestClass::test_name -q coverage report --include=module.py --show-missing # Make sure the target line does not appear under "Missing"
-
Read the source before writing a test
cat agents/killchain.py | head -100 # Understand exactly what the target line does
tests/
├── test_api.py # REST API endpoints
├── test_core.py # core/ modules
├── test_shell.py # CLI shell + make_shell fixture
├── test_functional.py # functional workflows
├── test_e2e.py # E2E (embedded server)
├── test_phase*.py # Coverage per component
├── test_meaningful.py # High-quality behavior tests
├── test_precise_coverage.py # Precision coverage tests
└── test_gap_fixes.py # Coverage gap fixes
from tests.test_shell import make_shell
def test_something(tmp_path, monkeypatch):
shell = make_shell(tmp_path, monkeypatch=monkeypatch)
shell._do_recon([])
assert len(shell.session.services) >= 0A PR will be rejected if:
- There is a failing test
- Coverage < 80% (current floor; target 85%)
- Trivial test ratio > 25%
- Spot-check tests do not hit target lines
- Edit
core/llm.py, add it toPROVIDER_SPECS - Implement
_init_client_<provider>() - Add the env var in
from_env() - Test:
llm add→ choose the new provider - Write a test in
test_core.py
- Create a class in
tools/integrations.pyinheriting fromBaseTool - Implement
run(target, **kwargs) -> ToolResult - Add a parser in
tools/parsers.py - Register it in
ToolRegistry.__init__() - Test with a real tool output sample
class HydraToolTest(BaseTool):
name = "hydra"
description = "Credential brute-forcing"
def run(self, target: str, **kwargs) -> ToolResult:
cmd = ["hydra", "-L", kwargs.get("userlist", "users.txt"), ...]
result = subprocess.run(cmd, ...)
return ToolResult(
tool="hydra",
target=target,
command=" ".join(cmd),
raw_output=result.stdout,
success=result.returncode == 0,
)