Rename to stealergram, add pyproject.toml, purge em-dashes
- Rename project to stealergram throughout - Add pyproject.toml (replaces requirements.txt split, folds pytest.ini) - Replace all em-dashes with hyphens across all source files Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,7 @@ os.environ.setdefault("API_HASH", "dummy_hash_for_tests")
|
||||
os.environ.setdefault("BOT_TOKEN", "0:dummy_bot_token")
|
||||
os.environ.setdefault("NOTIFY_CHAT_ID", "99999")
|
||||
|
||||
# Web frontend test defaults — set once here so all web test files see the same values.
|
||||
# Web frontend test defaults - set once here so all web test files see the same values.
|
||||
os.environ.setdefault("WEB_SECRET_KEY", "test-secret-key-for-pytest")
|
||||
os.environ.setdefault("WEB_ADMIN_USER", "superadmin")
|
||||
os.environ.setdefault("WEB_ADMIN_PASS", "superpass")
|
||||
@@ -17,8 +17,8 @@ import config
|
||||
import utils.scorer as scorer
|
||||
|
||||
# Two test keywords:
|
||||
# @testcorp\.com — employee email domain (triggers CRITICAL)
|
||||
# testcorp\.com — plain domain match (triggers LOW baseline)
|
||||
# @testcorp\.com - employee email domain (triggers CRITICAL)
|
||||
# testcorp\.com - plain domain match (triggers LOW baseline)
|
||||
TEST_KEYWORDS = [r"@testcorp\.com", r"testcorp\.com"]
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ def patched_keywords(monkeypatch):
|
||||
scorer's module-level globals so scoring logic uses known test patterns.
|
||||
|
||||
scorer.py now reads _config.TARGET_KEYWORDS at call time via `import config as _config`,
|
||||
so patching config.TARGET_KEYWORDS is sufficient — no direct scorer patch needed.
|
||||
so patching config.TARGET_KEYWORDS is sufficient - no direct scorer patch needed.
|
||||
"""
|
||||
monkeypatch.setattr(config, "TARGET_KEYWORDS", TEST_KEYWORDS)
|
||||
monkeypatch.setattr(scorer, "EMPLOYEE_DOMAINS", scorer._build_employee_domains())
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Tests for utils/cache.py — file-ID deduplication cache.
|
||||
Tests for utils/cache.py - file-ID deduplication cache.
|
||||
|
||||
Each test gets an isolated cache file via the `isolated_cache` fixture
|
||||
so tests never touch data/cache.json.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Tests for utils/database.py — SQLite persistence layer.
|
||||
Tests for utils/database.py - SQLite persistence layer.
|
||||
|
||||
Each test gets an isolated in-memory-equivalent DB via the `isolated_db`
|
||||
fixture so tests never touch data/hits.db.
|
||||
@@ -112,7 +112,7 @@ def test_by_severity_returns_correct_severity():
|
||||
|
||||
|
||||
def test_by_severity_excludes_duplicates():
|
||||
"""seen_before=1 rows must be invisible to by_severity — they are stored for stats only."""
|
||||
"""seen_before=1 rows must be invisible to by_severity - they are stored for stats only."""
|
||||
hit = make_hit(severity=HIGH, url="intranet.testcorp.com")
|
||||
db_module.insert_hits([hit], source="c", filename="f.txt", seen_before=True)
|
||||
assert db_module.by_severity(HIGH) == []
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Tests for tui/events.py — subscribe/unsubscribe broadcast, signal_channel_changed.
|
||||
Tests for tui/events.py - subscribe/unsubscribe broadcast, signal_channel_changed.
|
||||
"""
|
||||
|
||||
import queue
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Tests for core/processor.py — archive extraction and line-by-line search.
|
||||
Tests for core/processor.py - archive extraction and line-by-line search.
|
||||
|
||||
No Telegram deps, no async. Tests create real archive fixtures in tmp_path
|
||||
so process_file's cleanup guarantee can be verified against actual disk state.
|
||||
@@ -60,7 +60,7 @@ class TestSearchFile:
|
||||
assert search_file(f, patterns) == ["testcorp.com|user|pass"]
|
||||
|
||||
def test_handles_encoding_errors_gracefully(self, tmp_path, patterns):
|
||||
"""Combo files are often messy — invalid bytes must not crash the search."""
|
||||
"""Combo files are often messy - invalid bytes must not crash the search."""
|
||||
f = tmp_path / "combo.txt"
|
||||
f.write_bytes(
|
||||
b"testcorp.com|user1|pass\n"
|
||||
@@ -81,7 +81,7 @@ class TestSearchFile:
|
||||
assert len(hits) == 2
|
||||
|
||||
|
||||
# ─── process_file — plain .txt ────────────────────────────────────────────────
|
||||
# ─── process_file - plain .txt ────────────────────────────────────────────────
|
||||
|
||||
class TestProcessFilePlainText:
|
||||
def test_returns_hits(self, tmp_path, patterns):
|
||||
@@ -104,7 +104,7 @@ class TestProcessFilePlainText:
|
||||
assert not f.exists()
|
||||
|
||||
|
||||
# ─── process_file — .zip extraction ──────────────────────────────────────────
|
||||
# ─── process_file - .zip extraction ──────────────────────────────────────────
|
||||
|
||||
class TestProcessFileZip:
|
||||
def _make_zip(self, tmp_path: Path, content: str, filename="content.txt") -> Path:
|
||||
@@ -155,7 +155,7 @@ class TestProcessFileZip:
|
||||
assert len(hits) == 2
|
||||
|
||||
|
||||
# ─── process_file — nested archives ──────────────────────────────────────────
|
||||
# ─── process_file - nested archives ──────────────────────────────────────────
|
||||
|
||||
class TestProcessFileNested:
|
||||
def test_nested_zip_is_recursed(self, tmp_path, patterns):
|
||||
@@ -177,7 +177,7 @@ class TestProcessFileNested:
|
||||
assert not (tmp_path / "outer").exists()
|
||||
|
||||
|
||||
# ─── process_file — password-protected .7z ───────────────────────────────────
|
||||
# ─── process_file - password-protected .7z ───────────────────────────────────
|
||||
|
||||
class TestProcessFile7zPassword:
|
||||
def test_unlocks_with_correct_password(self, tmp_path, patterns, monkeypatch):
|
||||
@@ -218,6 +218,6 @@ class TestProcessFile7zPassword:
|
||||
z.write(txt, "content.txt")
|
||||
txt.unlink()
|
||||
|
||||
# No hits — archive could not be opened
|
||||
# No hits - archive could not be opened
|
||||
hits = process_file(szf, patterns)
|
||||
assert hits == []
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"""
|
||||
Tests for utils/scorer.py — severity scoring and ULP line parsing.
|
||||
Tests for utils/scorer.py - severity scoring and ULP line parsing.
|
||||
|
||||
All tests use the `patched_keywords` fixture (see conftest.py) which
|
||||
replaces TARGET_KEYWORDS with two entries:
|
||||
@testcorp.com — employee email domain (CRITICAL trigger)
|
||||
testcorp.com — plain domain match (LOW baseline)
|
||||
@testcorp.com - employee email domain (CRITICAL trigger)
|
||||
testcorp.com - plain domain match (LOW baseline)
|
||||
"""
|
||||
|
||||
import pytest
|
||||
@@ -50,7 +50,7 @@ class TestULPParsingRealWorld:
|
||||
|
||||
@pytest.mark.parametrize("line,exp_url,exp_user,exp_pass", [
|
||||
# ── Protocol + port + path, colon separator ──────────────────────────
|
||||
# Port is digits followed by '/' — must be consumed as part of the URL.
|
||||
# Port is digits followed by '/' - must be consumed as part of the URL.
|
||||
(
|
||||
"http://portal.fakehosp.example.com:88/:55512309-1:hunter2",
|
||||
"http://portal.fakehosp.example.com:88/", "55512309-1", "hunter2",
|
||||
@@ -91,7 +91,7 @@ class TestULPParsingRealWorld:
|
||||
"jdoe@fakehosp.example.com", "Passw0rd!",
|
||||
),
|
||||
|
||||
# ── Pipe separator (unambiguous — port stays in URL) ──────────────────
|
||||
# ── Pipe separator (unambiguous - port stays in URL) ──────────────────
|
||||
(
|
||||
"http://portal.fakehosp.example.com:88/|22.987.654-3|florida88",
|
||||
"http://portal.fakehosp.example.com:88/", "22.987.654-3", "florida88",
|
||||
@@ -113,7 +113,7 @@ class TestULPParsingRealWorld:
|
||||
"portal.fakehosp.example.com:88/", "22.987.654-3", "florida88",
|
||||
),
|
||||
|
||||
# ── No protocol, no port — plain colon separators ────────────────────
|
||||
# ── No protocol, no port - plain colon separators ────────────────────
|
||||
(
|
||||
"booking.fakehosp.example.com:66778899-7:correcthorse",
|
||||
"booking.fakehosp.example.com", "66778899-7", "correcthorse",
|
||||
@@ -234,7 +234,7 @@ class TestWeakPasswordFlags:
|
||||
assert any("Common password" in r for r in hit.reasons)
|
||||
|
||||
def test_weak_password_does_not_escalate_severity(self, patched_keywords):
|
||||
"""Weak password flags are informational — they must not change severity."""
|
||||
"""Weak password flags are informational - they must not change severity."""
|
||||
hit = score_hit("testcorp.com|user|abc")
|
||||
assert hit.severity == LOW
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Tests for web/auth.py — JWT token lifecycle, bcrypt helpers.
|
||||
Tests for web/auth.py - JWT token lifecycle, bcrypt helpers.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Tests for web/db.py — user store and refresh token management.
|
||||
Tests for web/db.py - user store and refresh token management.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
Reference in New Issue
Block a user