test: update existing test suites for refactored codebase

- test_api_attackers.py: update for BaseRepository interface
- test_attacker_worker.py: full test suite for worker logic (formerly in module)
- test_base_repo.py: repository interface conformance tests
- test_cli.py: CLI enhancements (randomize-services, selective deployment)
- test_service_isolation.py: isolation validation tests
- api/conftest.py: fixture updates for RBAC-gated endpoints
- live/test_service_isolation_live.py: live integration tests
This commit is contained in:
2026-04-15 12:51:26 -04:00
parent 7d10b78d50
commit dd4e2aad91
7 changed files with 108 additions and 42 deletions

View File

@@ -23,6 +23,9 @@ from decnet.web.auth import get_password_hash
from decnet.env import DECNET_ADMIN_USER, DECNET_ADMIN_PASSWORD from decnet.env import DECNET_ADMIN_USER, DECNET_ADMIN_PASSWORD
import decnet.config import decnet.config
VIEWER_USERNAME = "testviewer"
VIEWER_PASSWORD = "viewer-pass-123"
@pytest.fixture(scope="function", autouse=True) @pytest.fixture(scope="function", autouse=True)
async def setup_db(monkeypatch) -> AsyncGenerator[None, None]: async def setup_db(monkeypatch) -> AsyncGenerator[None, None]:
@@ -76,6 +79,30 @@ async def auth_token(client: httpx.AsyncClient) -> str:
resp2 = await client.post("/api/v1/auth/login", json={"username": DECNET_ADMIN_USER, "password": DECNET_ADMIN_PASSWORD}) resp2 = await client.post("/api/v1/auth/login", json={"username": DECNET_ADMIN_USER, "password": DECNET_ADMIN_PASSWORD})
return resp2.json()["access_token"] return resp2.json()["access_token"]
@pytest.fixture
async def viewer_token(client, setup_db):
"""Seed a viewer user and return their auth token."""
async with repo.session_factory() as session:
result = await session.execute(
select(User).where(User.username == VIEWER_USERNAME)
)
if not result.scalar_one_or_none():
session.add(User(
uuid=str(_uuid.uuid4()),
username=VIEWER_USERNAME,
password_hash=get_password_hash(VIEWER_PASSWORD),
role="viewer",
must_change_password=False,
))
await session.commit()
resp = await client.post("/api/v1/auth/login", json={
"username": VIEWER_USERNAME,
"password": VIEWER_PASSWORD,
})
return resp.json()["access_token"]
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def patch_state_file(monkeypatch, tmp_path) -> Path: def patch_state_file(monkeypatch, tmp_path) -> Path:
state_file = tmp_path / "decnet-state.json" state_file = tmp_path / "decnet-state.json"

View File

@@ -36,7 +36,7 @@ from decnet.collector.worker import ( # noqa: E402
is_service_container, is_service_container,
) )
from decnet.web.ingester import log_ingestion_worker # noqa: E402 from decnet.web.ingester import log_ingestion_worker # noqa: E402
from decnet.web.attacker_worker import ( # noqa: E402 from decnet.profiler.worker import ( # noqa: E402
attacker_profile_worker, attacker_profile_worker,
_WorkerState, _WorkerState,
_incremental_update, _incremental_update,

View File

@@ -58,10 +58,11 @@ class TestGetAttackers:
with patch("decnet.web.router.attackers.api_get_attackers.repo") as mock_repo: with patch("decnet.web.router.attackers.api_get_attackers.repo") as mock_repo:
mock_repo.get_attackers = AsyncMock(return_value=[sample]) mock_repo.get_attackers = AsyncMock(return_value=[sample])
mock_repo.get_total_attackers = AsyncMock(return_value=1) mock_repo.get_total_attackers = AsyncMock(return_value=1)
mock_repo.get_behaviors_for_ips = AsyncMock(return_value={})
result = await get_attackers( result = await get_attackers(
limit=50, offset=0, search=None, sort_by="recent", limit=50, offset=0, search=None, sort_by="recent",
current_user="test-user", user={"uuid": "test-user", "role": "viewer"},
) )
assert result["total"] == 1 assert result["total"] == 1
@@ -77,10 +78,11 @@ class TestGetAttackers:
with patch("decnet.web.router.attackers.api_get_attackers.repo") as mock_repo: with patch("decnet.web.router.attackers.api_get_attackers.repo") as mock_repo:
mock_repo.get_attackers = AsyncMock(return_value=[]) mock_repo.get_attackers = AsyncMock(return_value=[])
mock_repo.get_total_attackers = AsyncMock(return_value=0) mock_repo.get_total_attackers = AsyncMock(return_value=0)
mock_repo.get_behaviors_for_ips = AsyncMock(return_value={})
await get_attackers( await get_attackers(
limit=50, offset=0, search="192.168", sort_by="recent", limit=50, offset=0, search="192.168", sort_by="recent",
current_user="test-user", user={"uuid": "test-user", "role": "viewer"},
) )
mock_repo.get_attackers.assert_awaited_once_with( mock_repo.get_attackers.assert_awaited_once_with(
@@ -95,10 +97,11 @@ class TestGetAttackers:
with patch("decnet.web.router.attackers.api_get_attackers.repo") as mock_repo: with patch("decnet.web.router.attackers.api_get_attackers.repo") as mock_repo:
mock_repo.get_attackers = AsyncMock(return_value=[]) mock_repo.get_attackers = AsyncMock(return_value=[])
mock_repo.get_total_attackers = AsyncMock(return_value=0) mock_repo.get_total_attackers = AsyncMock(return_value=0)
mock_repo.get_behaviors_for_ips = AsyncMock(return_value={})
await get_attackers( await get_attackers(
limit=50, offset=0, search="null", sort_by="recent", limit=50, offset=0, search="null", sort_by="recent",
current_user="test-user", user={"uuid": "test-user", "role": "viewer"},
) )
mock_repo.get_attackers.assert_awaited_once_with( mock_repo.get_attackers.assert_awaited_once_with(
@@ -112,10 +115,11 @@ class TestGetAttackers:
with patch("decnet.web.router.attackers.api_get_attackers.repo") as mock_repo: with patch("decnet.web.router.attackers.api_get_attackers.repo") as mock_repo:
mock_repo.get_attackers = AsyncMock(return_value=[]) mock_repo.get_attackers = AsyncMock(return_value=[])
mock_repo.get_total_attackers = AsyncMock(return_value=0) mock_repo.get_total_attackers = AsyncMock(return_value=0)
mock_repo.get_behaviors_for_ips = AsyncMock(return_value={})
await get_attackers( await get_attackers(
limit=50, offset=0, search=None, sort_by="active", limit=50, offset=0, search=None, sort_by="active",
current_user="test-user", user={"uuid": "test-user", "role": "viewer"},
) )
mock_repo.get_attackers.assert_awaited_once_with( mock_repo.get_attackers.assert_awaited_once_with(
@@ -129,10 +133,11 @@ class TestGetAttackers:
with patch("decnet.web.router.attackers.api_get_attackers.repo") as mock_repo: with patch("decnet.web.router.attackers.api_get_attackers.repo") as mock_repo:
mock_repo.get_attackers = AsyncMock(return_value=[]) mock_repo.get_attackers = AsyncMock(return_value=[])
mock_repo.get_total_attackers = AsyncMock(return_value=0) mock_repo.get_total_attackers = AsyncMock(return_value=0)
mock_repo.get_behaviors_for_ips = AsyncMock(return_value={})
await get_attackers( await get_attackers(
limit=50, offset=0, search="", sort_by="recent", limit=50, offset=0, search="", sort_by="recent",
current_user="test-user", user={"uuid": "test-user", "role": "viewer"},
) )
mock_repo.get_attackers.assert_awaited_once_with( mock_repo.get_attackers.assert_awaited_once_with(
@@ -146,10 +151,11 @@ class TestGetAttackers:
with patch("decnet.web.router.attackers.api_get_attackers.repo") as mock_repo: with patch("decnet.web.router.attackers.api_get_attackers.repo") as mock_repo:
mock_repo.get_attackers = AsyncMock(return_value=[]) mock_repo.get_attackers = AsyncMock(return_value=[])
mock_repo.get_total_attackers = AsyncMock(return_value=0) mock_repo.get_total_attackers = AsyncMock(return_value=0)
mock_repo.get_behaviors_for_ips = AsyncMock(return_value={})
await get_attackers( await get_attackers(
limit=50, offset=0, search=None, sort_by="recent", limit=50, offset=0, search=None, sort_by="recent",
service="https", current_user="test-user", service="https", user={"uuid": "test-user", "role": "viewer"},
) )
mock_repo.get_attackers.assert_awaited_once_with( mock_repo.get_attackers.assert_awaited_once_with(
@@ -168,8 +174,9 @@ class TestGetAttackerDetail:
sample = _sample_attacker() sample = _sample_attacker()
with patch("decnet.web.router.attackers.api_get_attacker_detail.repo") as mock_repo: with patch("decnet.web.router.attackers.api_get_attacker_detail.repo") as mock_repo:
mock_repo.get_attacker_by_uuid = AsyncMock(return_value=sample) mock_repo.get_attacker_by_uuid = AsyncMock(return_value=sample)
mock_repo.get_attacker_behavior = AsyncMock(return_value=None)
result = await get_attacker_detail(uuid="att-uuid-1", current_user="test-user") result = await get_attacker_detail(uuid="att-uuid-1", user={"uuid": "test-user", "role": "viewer"})
assert result["uuid"] == "att-uuid-1" assert result["uuid"] == "att-uuid-1"
assert result["ip"] == "1.2.3.4" assert result["ip"] == "1.2.3.4"
@@ -184,7 +191,7 @@ class TestGetAttackerDetail:
mock_repo.get_attacker_by_uuid = AsyncMock(return_value=None) mock_repo.get_attacker_by_uuid = AsyncMock(return_value=None)
with pytest.raises(HTTPException) as exc_info: with pytest.raises(HTTPException) as exc_info:
await get_attacker_detail(uuid="nonexistent", current_user="test-user") await get_attacker_detail(uuid="nonexistent", user={"uuid": "test-user", "role": "viewer"})
assert exc_info.value.status_code == 404 assert exc_info.value.status_code == 404
@@ -195,8 +202,9 @@ class TestGetAttackerDetail:
sample = _sample_attacker() sample = _sample_attacker()
with patch("decnet.web.router.attackers.api_get_attacker_detail.repo") as mock_repo: with patch("decnet.web.router.attackers.api_get_attacker_detail.repo") as mock_repo:
mock_repo.get_attacker_by_uuid = AsyncMock(return_value=sample) mock_repo.get_attacker_by_uuid = AsyncMock(return_value=sample)
mock_repo.get_attacker_behavior = AsyncMock(return_value=None)
result = await get_attacker_detail(uuid="att-uuid-1", current_user="test-user") result = await get_attacker_detail(uuid="att-uuid-1", user={"uuid": "test-user", "role": "viewer"})
assert isinstance(result["services"], list) assert isinstance(result["services"], list)
assert isinstance(result["deckies"], list) assert isinstance(result["deckies"], list)
@@ -222,7 +230,7 @@ class TestGetAttackerCommands:
result = await get_attacker_commands( result = await get_attacker_commands(
uuid="att-uuid-1", limit=50, offset=0, service=None, uuid="att-uuid-1", limit=50, offset=0, service=None,
current_user="test-user", user={"uuid": "test-user", "role": "viewer"},
) )
assert result["total"] == 2 assert result["total"] == 2
@@ -241,7 +249,7 @@ class TestGetAttackerCommands:
await get_attacker_commands( await get_attacker_commands(
uuid="att-uuid-1", limit=50, offset=0, service="ssh", uuid="att-uuid-1", limit=50, offset=0, service="ssh",
current_user="test-user", user={"uuid": "test-user", "role": "viewer"},
) )
mock_repo.get_attacker_commands.assert_awaited_once_with( mock_repo.get_attacker_commands.assert_awaited_once_with(
@@ -258,7 +266,7 @@ class TestGetAttackerCommands:
with pytest.raises(HTTPException) as exc_info: with pytest.raises(HTTPException) as exc_info:
await get_attacker_commands( await get_attacker_commands(
uuid="nonexistent", limit=50, offset=0, service=None, uuid="nonexistent", limit=50, offset=0, service=None,
current_user="test-user", user={"uuid": "test-user", "role": "viewer"},
) )
assert exc_info.value.status_code == 404 assert exc_info.value.status_code == 404

View File

@@ -1,5 +1,5 @@
""" """
Tests for decnet/web/attacker_worker.py Tests for decnet/attacker/worker.py
Covers: Covers:
- _cold_start(): full build on first run, cursor persistence - _cold_start(): full build on first run, cursor persistence
@@ -22,7 +22,7 @@ import pytest
from decnet.correlation.parser import LogEvent from decnet.correlation.parser import LogEvent
from decnet.logging.syslog_formatter import SEVERITY_INFO, format_rfc5424 from decnet.logging.syslog_formatter import SEVERITY_INFO, format_rfc5424
from decnet.web.attacker_worker import ( from decnet.profiler.worker import (
_BATCH_SIZE, _BATCH_SIZE,
_STATE_KEY, _STATE_KEY,
_WorkerState, _WorkerState,
@@ -104,7 +104,8 @@ def _make_repo(logs=None, bounties=None, bounties_for_ips=None, max_log_id=0, sa
repo.get_logs_after_id = AsyncMock(return_value=[]) repo.get_logs_after_id = AsyncMock(return_value=[])
repo.get_state = AsyncMock(return_value=saved_state) repo.get_state = AsyncMock(return_value=saved_state)
repo.set_state = AsyncMock() repo.set_state = AsyncMock()
repo.upsert_attacker = AsyncMock() repo.upsert_attacker = AsyncMock(return_value="mock-uuid")
repo.upsert_attacker_behavior = AsyncMock()
return repo return repo
@@ -584,8 +585,8 @@ class TestAttackerProfileWorker:
async def bad_update(_repo, _state): async def bad_update(_repo, _state):
raise RuntimeError("DB exploded") raise RuntimeError("DB exploded")
with patch("decnet.web.attacker_worker.asyncio.sleep", side_effect=fake_sleep): with patch("decnet.profiler.worker.asyncio.sleep", side_effect=fake_sleep):
with patch("decnet.web.attacker_worker._incremental_update", side_effect=bad_update): with patch("decnet.profiler.worker._incremental_update", side_effect=bad_update):
with pytest.raises(asyncio.CancelledError): with pytest.raises(asyncio.CancelledError):
await attacker_profile_worker(repo) await attacker_profile_worker(repo)
@@ -605,8 +606,8 @@ class TestAttackerProfileWorker:
async def mock_update(_repo, _state): async def mock_update(_repo, _state):
update_calls.append(True) update_calls.append(True)
with patch("decnet.web.attacker_worker.asyncio.sleep", side_effect=fake_sleep): with patch("decnet.profiler.worker.asyncio.sleep", side_effect=fake_sleep):
with patch("decnet.web.attacker_worker._incremental_update", side_effect=mock_update): with patch("decnet.profiler.worker._incremental_update", side_effect=mock_update):
with pytest.raises(asyncio.CancelledError): with pytest.raises(asyncio.CancelledError):
await attacker_profile_worker(repo) await attacker_profile_worker(repo)

View File

@@ -26,11 +26,18 @@ class DummyRepo(BaseRepository):
async def get_logs_after_id(self, last_id, limit=500): await super().get_logs_after_id(last_id, limit) async def get_logs_after_id(self, last_id, limit=500): await super().get_logs_after_id(last_id, limit)
async def get_all_bounties_by_ip(self): await super().get_all_bounties_by_ip() async def get_all_bounties_by_ip(self): await super().get_all_bounties_by_ip()
async def get_bounties_for_ips(self, ips): await super().get_bounties_for_ips(ips) async def get_bounties_for_ips(self, ips): await super().get_bounties_for_ips(ips)
async def upsert_attacker(self, d): await super().upsert_attacker(d) async def upsert_attacker(self, d): await super().upsert_attacker(d); return ""
async def upsert_attacker_behavior(self, u, d): await super().upsert_attacker_behavior(u, d)
async def get_attacker_behavior(self, u): await super().get_attacker_behavior(u)
async def get_behaviors_for_ips(self, ips): await super().get_behaviors_for_ips(ips)
async def get_attacker_by_uuid(self, u): await super().get_attacker_by_uuid(u) async def get_attacker_by_uuid(self, u): await super().get_attacker_by_uuid(u)
async def get_attackers(self, **kw): await super().get_attackers(**kw) async def get_attackers(self, **kw): await super().get_attackers(**kw)
async def get_total_attackers(self, **kw): await super().get_total_attackers(**kw) async def get_total_attackers(self, **kw): await super().get_total_attackers(**kw)
async def get_attacker_commands(self, **kw): await super().get_attacker_commands(**kw) async def get_attacker_commands(self, **kw): await super().get_attacker_commands(**kw)
async def list_users(self): await super().list_users()
async def delete_user(self, u): await super().delete_user(u)
async def update_user_role(self, u, r): await super().update_user_role(u, r)
async def purge_logs_and_bounties(self): await super().purge_logs_and_bounties()
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_base_repo_coverage(): async def test_base_repo_coverage():
@@ -57,7 +64,14 @@ async def test_base_repo_coverage():
await dr.get_all_bounties_by_ip() await dr.get_all_bounties_by_ip()
await dr.get_bounties_for_ips({"1.1.1.1"}) await dr.get_bounties_for_ips({"1.1.1.1"})
await dr.upsert_attacker({}) await dr.upsert_attacker({})
await dr.upsert_attacker_behavior("a", {})
await dr.get_attacker_behavior("a")
await dr.get_behaviors_for_ips({"1.1.1.1"})
await dr.get_attacker_by_uuid("a") await dr.get_attacker_by_uuid("a")
await dr.get_attackers() await dr.get_attackers()
await dr.get_total_attackers() await dr.get_total_attackers()
await dr.get_attacker_commands(uuid="a") await dr.get_attacker_commands(uuid="a")
await dr.list_users()
await dr.delete_user("a")
await dr.update_user_role("a", "admin")
await dr.purge_logs_and_bounties()

View File

@@ -181,7 +181,7 @@ class TestTeardownCommand:
result = runner.invoke(app, ["teardown"]) result = runner.invoke(app, ["teardown"])
assert result.exit_code == 1 assert result.exit_code == 1
@patch("decnet.cli._kill_api") @patch("decnet.cli._kill_all_services")
@patch("decnet.engine.teardown") @patch("decnet.engine.teardown")
def test_teardown_all(self, mock_teardown, mock_kill): def test_teardown_all(self, mock_teardown, mock_kill):
result = runner.invoke(app, ["teardown", "--all"]) result = runner.invoke(app, ["teardown", "--all"])
@@ -275,13 +275,29 @@ class TestWebCommand:
assert result.exit_code == 1 assert result.exit_code == 1
assert "Frontend build not found" in result.stdout assert "Frontend build not found" in result.stdout
@patch("socketserver.TCPServer") def test_web_success(self):
@patch("os.chdir") with (
@patch("pathlib.Path.exists", return_value=True) patch("pathlib.Path.exists", return_value=True),
def test_web_success(self, mock_exists, mock_chdir, mock_server): patch("os.chdir"),
# We need to simulate a KeyboardInterrupt to stop serve_forever patch(
mock_server.return_value.__enter__.return_value.serve_forever.side_effect = KeyboardInterrupt "socketserver.TCPServer.__init__",
result = runner.invoke(app, ["web"]) lambda self, *a, **kw: None,
),
patch(
"socketserver.TCPServer.__enter__",
lambda self: self,
),
patch(
"socketserver.TCPServer.__exit__",
lambda self, *a: None,
),
patch(
"socketserver.TCPServer.serve_forever",
side_effect=KeyboardInterrupt,
),
):
result = runner.invoke(app, ["web"])
assert result.exit_code == 0 assert result.exit_code == 0
assert "Serving DECNET Web Dashboard" in result.stdout assert "Serving DECNET Web Dashboard" in result.stdout
@@ -320,13 +336,13 @@ class TestApiCommand:
assert result.exit_code == 0 assert result.exit_code == 0
# ── _kill_api ───────────────────────────────────────────────────────────────── # ── _kill_all_services ────────────────────────────────────────────────────────
class TestKillApi: class TestKillAllServices:
@patch("os.kill") @patch("os.kill")
@patch("psutil.process_iter") @patch("psutil.process_iter")
def test_kills_matching_processes(self, mock_iter, mock_kill): def test_kills_matching_processes(self, mock_iter, mock_kill):
from decnet.cli import _kill_api from decnet.cli import _kill_all_services
mock_uvicorn = MagicMock() mock_uvicorn = MagicMock()
mock_uvicorn.info = { mock_uvicorn.info = {
"pid": 111, "name": "python", "pid": 111, "name": "python",
@@ -343,21 +359,21 @@ class TestKillApi:
"cmdline": ["python", "-m", "decnet.cli", "collect", "--log-file", "/tmp/decnet.log"], "cmdline": ["python", "-m", "decnet.cli", "collect", "--log-file", "/tmp/decnet.log"],
} }
mock_iter.return_value = [mock_uvicorn, mock_mutate, mock_collector] mock_iter.return_value = [mock_uvicorn, mock_mutate, mock_collector]
_kill_api() _kill_all_services()
assert mock_kill.call_count == 3 assert mock_kill.call_count == 3
@patch("psutil.process_iter") @patch("psutil.process_iter")
def test_no_matching_processes(self, mock_iter): def test_no_matching_processes(self, mock_iter):
from decnet.cli import _kill_api from decnet.cli import _kill_all_services
mock_proc = MagicMock() mock_proc = MagicMock()
mock_proc.info = {"pid": 1, "name": "bash", "cmdline": ["bash"]} mock_proc.info = {"pid": 1, "name": "bash", "cmdline": ["bash"]}
mock_iter.return_value = [mock_proc] mock_iter.return_value = [mock_proc]
_kill_api() _kill_all_services()
@patch("psutil.process_iter") @patch("psutil.process_iter")
def test_handles_empty_cmdline(self, mock_iter): def test_handles_empty_cmdline(self, mock_iter):
from decnet.cli import _kill_api from decnet.cli import _kill_all_services
mock_proc = MagicMock() mock_proc = MagicMock()
mock_proc.info = {"pid": 1, "name": "bash", "cmdline": None} mock_proc.info = {"pid": 1, "name": "bash", "cmdline": None}
mock_iter.return_value = [mock_proc] mock_iter.return_value = [mock_proc]
_kill_api() _kill_all_services()

View File

@@ -184,7 +184,7 @@ class TestAttackerWorkerIsolation:
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_attacker_worker_survives_db_error(self): async def test_attacker_worker_survives_db_error(self):
"""Attacker worker must catch DB errors and continue looping.""" """Attacker worker must catch DB errors and continue looping."""
from decnet.web.attacker_worker import attacker_profile_worker from decnet.profiler import attacker_profile_worker
mock_repo = MagicMock() mock_repo = MagicMock()
mock_repo.get_all_logs_raw = AsyncMock(side_effect=Exception("DB is locked")) mock_repo.get_all_logs_raw = AsyncMock(side_effect=Exception("DB is locked"))
@@ -199,7 +199,7 @@ class TestAttackerWorkerIsolation:
if iterations >= 3: if iterations >= 3:
raise asyncio.CancelledError() raise asyncio.CancelledError()
with patch("decnet.web.attacker_worker.asyncio.sleep", side_effect=_controlled_sleep): with patch("decnet.profiler.worker.asyncio.sleep", side_effect=_controlled_sleep):
task = asyncio.create_task(attacker_profile_worker(mock_repo)) task = asyncio.create_task(attacker_profile_worker(mock_repo))
with pytest.raises(asyncio.CancelledError): with pytest.raises(asyncio.CancelledError):
await task await task
@@ -209,7 +209,7 @@ class TestAttackerWorkerIsolation:
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_attacker_worker_survives_empty_db(self): async def test_attacker_worker_survives_empty_db(self):
"""Attacker worker must handle an empty database gracefully.""" """Attacker worker must handle an empty database gracefully."""
from decnet.web.attacker_worker import _WorkerState, _incremental_update from decnet.profiler.worker import _WorkerState, _incremental_update
mock_repo = MagicMock() mock_repo = MagicMock()
mock_repo.get_all_logs_raw = AsyncMock(return_value=[]) mock_repo.get_all_logs_raw = AsyncMock(return_value=[])
@@ -433,7 +433,7 @@ class TestCascadeIsolation:
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_ingester_failure_does_not_kill_attacker(self): async def test_ingester_failure_does_not_kill_attacker(self):
"""When ingester dies, attacker worker must keep running independently.""" """When ingester dies, attacker worker must keep running independently."""
from decnet.web.attacker_worker import attacker_profile_worker from decnet.profiler import attacker_profile_worker
mock_repo = MagicMock() mock_repo = MagicMock()
mock_repo.get_all_logs_raw = AsyncMock(return_value=[]) mock_repo.get_all_logs_raw = AsyncMock(return_value=[])
@@ -449,7 +449,7 @@ class TestCascadeIsolation:
if iterations >= 3: if iterations >= 3:
raise asyncio.CancelledError() raise asyncio.CancelledError()
with patch("decnet.web.attacker_worker.asyncio.sleep", side_effect=_controlled_sleep): with patch("decnet.profiler.worker.asyncio.sleep", side_effect=_controlled_sleep):
task = asyncio.create_task(attacker_profile_worker(mock_repo)) task = asyncio.create_task(attacker_profile_worker(mock_repo))
with pytest.raises(asyncio.CancelledError): with pytest.raises(asyncio.CancelledError):
await task await task