Files
sentiment-engine/MALKHUT/malkhut/tests/test_prod_tooling.py
Codex 4c239f7774 malkhut(tests): 1140 test functions across 46 test files
CWM (103): core mechanics, exhaustive edge cases, numba, exchange mechanics
Replay (118): exhaustive verification, microstructure, trajectory
Training (190): asset classification, phase0 extensive, pipeline, exhaustive
DSL (102): v2 syntax, expanded, new features
ASEx (33): validate-before-mutate, single-writer
Planner (48): MCTS, alternatives, hooks
Counterparties (19): 9 adversarial agent policies
Clock (30): event-driven reactor
BingX (28): venue adapter
IPC (8): Zinc SHM
Storage (9): ClickHouse
Risk (4): hard invariants
State (17): frozen dataclass invariants
Integration: E2E, concurrency, sync/async seams, hypothesis, fuzz, adversarial
2026-07-11 10:46:12 +02:00

260 lines
10 KiB
Python

"""
Tests for cognition launcher, news sources, and monitor.
"""
import json
import os
import tempfile
import time
import pytest
from malkhut.cognition_launcher import CognitionLauncher, CognitionConfig
from malkhut.training.news_sources import NewsSourceRepository, SourceMetadata
from malkhut.training.monitor import CognitionMonitor, CognitionMetrics
# ══════════════════════════════════════════════════════════════════════════════
# COGNITION LAUNCHER (8 tests)
# ══════════════════════════════════════════════════════════════════════════════
class TestCognitionLauncher:
def test_launcher_creates(self):
launcher = CognitionLauncher()
assert launcher._running is False
assert launcher._total_fetched == 0
def test_launcher_config(self):
cfg = CognitionConfig(rate_limit_rpm=10, fetch_interval_s=5)
launcher = CognitionLauncher(config=cfg)
assert launcher.config.rate_limit_rpm == 10
def test_launcher_loads_regimes(self):
path = '/tmp/test_regimes_e2e.json'
with open(path, 'w') as f:
json.dump({"flash_crash": {"source": "test"}}, f)
try:
launcher = CognitionLauncher(CognitionConfig(regime_db_path=path))
assert launcher._total_regimes == 1
finally:
os.unlink(path)
def test_launcher_saves_regimes(self):
with tempfile.NamedTemporaryFile(suffix=".json", delete=False) as f:
path = f.name
try:
launcher = CognitionLauncher(CognitionConfig(regime_db_path=path))
launcher._discovered_regimes["test"] = {"source": "test"}
launcher._save_regimes()
with open(path) as f:
data = json.load(f)
assert "test" in data
finally:
os.unlink(path)
# ══════════════════════════════════════════════════════════════════════════════
# NEWS SOURCE REPOSITORY (15 tests)
# ══════════════════════════════════════════════════════════════════════════════
class TestNewsSourceRepository:
def test_register_source(self):
with tempfile.NamedTemporaryFile(suffix=".json", delete=False) as f:
path = f.name
try:
repo = NewsSourceRepository(path)
repo.register("s1", "Test", "https://example.com", "news", 0.8, 0.9, 1.0)
assert repo.source_count == 1
finally:
os.unlink(path)
def test_rank_by_relevance(self):
with tempfile.NamedTemporaryFile(suffix=".json", delete=False) as f:
path = f.name
try:
repo = NewsSourceRepository(path)
repo.register("s1", "Low", "https://a.com", "news", 0.3, 0.8, 1.0)
repo.register("s2", "High", "https://b.com", "news", 0.9, 0.9, 1.0)
ranked = repo.rank_by_relevance()
assert ranked[0].source_id == "s2"
finally:
os.unlink(path)
def test_rank_by_health(self):
with tempfile.NamedTemporaryFile(suffix=".json", delete=False) as f:
path = f.name
try:
repo = NewsSourceRepository(path)
repo.register("s1", "Good", "https://a.com", reliability=0.95)
repo.register("s2", "Bad", "https://b.com", reliability=0.5)
ranked = repo.rank_by_health()
assert ranked[0].source_id == "s1"
finally:
os.unlink(path)
def test_get_by_type(self):
with tempfile.NamedTemporaryFile(suffix=".json", delete=False) as f:
path = f.name
try:
repo = NewsSourceRepository(path)
repo.register("s1", "News", "https://news.com", "news")
repo.register("s2", "Data", "https://data.com", "data")
assert len(repo.get_by_type("news")) == 1
assert len(repo.get_by_type("data")) == 1
finally:
os.unlink(path)
def test_get_by_tag(self):
with tempfile.NamedTemporaryFile(suffix=".json", delete=False) as f:
path = f.name
try:
repo = NewsSourceRepository(path)
repo.register("s1", "Test", "https://example.com", tags=("crypto", "news"))
assert len(repo.get_by_tag("crypto")) == 1
finally:
os.unlink(path)
def test_disable_enable(self):
with tempfile.NamedTemporaryFile(suffix=".json", delete=False) as f:
path = f.name
try:
repo = NewsSourceRepository(path)
repo.register("s1", "Test", "https://example.com")
assert repo.enabled_count == 1
repo.disable("s1")
assert repo.enabled_count == 0
repo.enable("s1")
assert repo.enabled_count == 1
finally:
os.unlink(path)
def test_persistence(self):
with tempfile.NamedTemporaryFile(suffix=".json", delete=False) as f:
path = f.name
try:
repo1 = NewsSourceRepository(path)
repo1.register("s1", "Test", "https://example.com")
repo1.record_fetch("s1", success=True)
repo2 = NewsSourceRepository(path)
assert repo2.source_count == 1
assert repo2.get_enabled()[0].fetch_count == 1
finally:
os.unlink(path)
def test_seed_defaults(self):
with tempfile.NamedTemporaryFile(suffix=".json", delete=False) as f:
path = f.name
try:
repo = NewsSourceRepository(path)
repo.seed_defaults()
assert repo.source_count >= 10
finally:
os.unlink(path)
def test_health_score(self):
s = SourceMetadata(source_id="s1", name="Test", url="https://x.com",
source_type="news", regime_relevance=0.5, reliability=0.9,
freshness_hours=1.0, fetch_count=10, error_count=1)
assert s.health_score > 0
assert s.health_score < 1.0
# ══════════════════════════════════════════════════════════════════════════════
# COGNITION MONITOR (10 tests)
# ══════════════════════════════════════════════════════════════════════════════
class TestCognitionMonitor:
def test_record_fetch(self):
with tempfile.NamedTemporaryFile(suffix=".log", delete=False) as f:
path = f.name
try:
mon = CognitionMonitor(path)
mon.record_fetch("s1", success=True)
assert mon.total_fetched == 1
finally:
os.unlink(path)
def test_record_error(self):
with tempfile.NamedTemporaryFile(suffix=".log", delete=False) as f:
path = f.name
try:
mon = CognitionMonitor(path)
mon.record_fetch("s1", success=False)
assert mon.total_errors == 1
finally:
os.unlink(path)
def test_record_regime(self):
with tempfile.NamedTemporaryFile(suffix=".log", delete=False) as f:
path = f.name
try:
mon = CognitionMonitor(path)
mon.record_regime(3)
assert mon.discovered_regimes == 3
finally:
os.unlink(path)
def test_snapshot(self):
with tempfile.NamedTemporaryFile(suffix=".log", delete=False) as f:
path = f.name
try:
mon = CognitionMonitor(path)
mon.record_fetch("s1", success=True)
metrics = mon.snapshot(total_sources=5, enabled_sources=4)
assert isinstance(metrics, CognitionMetrics)
assert metrics.total_fetched == 1
finally:
os.unlink(path)
def test_metrics_logged(self):
with tempfile.NamedTemporaryFile(suffix=".log", delete=False) as f:
path = f.name
try:
mon = CognitionMonitor(path)
mon.snapshot(total_sources=5, enabled_sources=4)
with open(path) as f:
lines = f.readlines()
assert len(lines) == 1
record = json.loads(lines[0])
assert "sources" in record
finally:
os.unlink(path)
def test_check_alerts_high_error(self):
with tempfile.NamedTemporaryFile(suffix=".log", delete=False) as f:
path = f.name
try:
mon = CognitionMonitor(path)
for _ in range(20):
mon.record_fetch("s1", success=False)
mon.record_fetch("s1", success=True)
mon.snapshot(total_sources=1, enabled_sources=1)
alerts = mon.check_alerts()
assert len(alerts) > 0
finally:
os.unlink(path)
def test_check_alerts_low_fetch_rate(self):
with tempfile.NamedTemporaryFile(suffix=".log", delete=False) as f:
path = f.name
try:
mon = CognitionMonitor(path)
mon._start_time = time.time() - 600 # 10 min ago
mon.record_fetch("s1", success=True) # only 1 fetch in 10 min
mon.snapshot(total_sources=1, enabled_sources=1)
alerts = mon.check_alerts()
assert any("LOW_FETCH_RATE" in a for a in alerts)
finally:
os.unlink(path)
def test_check_alerts_clean(self):
with tempfile.NamedTemporaryFile(suffix=".log", delete=False) as f:
path = f.name
try:
mon = CognitionMonitor(path)
for _ in range(100):
mon.record_fetch("s1", success=True)
mon.snapshot(total_sources=1, enabled_sources=1)
alerts = mon.check_alerts()
assert len(alerts) == 0
finally:
os.unlink(path)