"""Tests for B17 Audit-Walk-Wiring (Stufe 1).""" import asyncio from unittest.mock import patch, MagicMock, AsyncMock import pytest from compliance.api.agent_check._b17_wiring import _render, run_b17 _FAKE_WALK = { "walk_id": "abc123def456", "url": "https://example.com/", "started_at": "2026-06-07T10:00:00+00:00", "completed_at": "2026-06-07T10:00:30+00:00", "engine": "playwright/webkit", "viewport": "1280x800", "actions": [ {"timestamp": "2026-06-07T10:00:00+00:00", "action": "goto", "url": "https://example.com/", "status": 200}, {"timestamp": "2026-06-07T10:00:02+00:00", "action": "accept_banner", "result": "clicked", "phrase": "alle akzeptieren"}, {"timestamp": "2026-06-07T10:00:04+00:00", "action": "discover_footer_links", "count": 3, "links": []}, {"timestamp": "2026-06-07T10:00:06+00:00", "action": "navigate", "url": "https://example.com/datenschutz", "anchor_text": "Datenschutz", "status": 200, "title": "Datenschutzerklärung"}, {"timestamp": "2026-06-07T10:00:12+00:00", "action": "expand_accordions", "expanded": 5, "max": 25}, ], "video": { "filename": "video.webm", "size_bytes": 512000, "sha256": "a1b2c3d4e5f67890fedcba0987654321ffffeeeeddddccccbbbbaaaa00001111", }, } class TestRender: def test_renders_walk_id_and_link(self): html = _render(_FAKE_WALK) assert "abc123def456" in html assert "video.webm" in html assert "walk.json" in html def test_includes_sha_prefix(self): html = _render(_FAKE_WALK) # First 12 chars of sha assert "a1b2c3d4e5f6" in html def test_action_table_lists_all_actions(self): html = _render(_FAKE_WALK) # All five actions + header appear as assert html.count("") >= 5 def test_nav_count_reflects_navigate_actions(self): html = _render(_FAKE_WALK) # 1 navigate in the fixture assert "1 Compliance-Seiten" in html def test_renders_accordion_expansion_count(self): html = _render(_FAKE_WALK) assert "5 Akkordeon" in html def test_accordion_zero_renders_neutral_text(self): walk = dict(_FAKE_WALK) walk["actions"] = [ {"timestamp": "...", "action": "expand_accordions", "expanded": 0, "max": 25}, ] html = _render(walk) assert "Keine Akkordeons gefunden" in html def test_dsms_anchor_rendered_when_cid_present(self): walk = dict(_FAKE_WALK) walk["video"] = dict(walk["video"]) walk["video"]["dsms"] = { "cid": "QmTestCidVideoXX1234567890ABCDEFGHJKLMN", "gateway_url": "http://dsms-node:8080/ipfs/QmTestCidVideo", } walk["walk_json_dsms"] = { "cid": "QmTestCidMetaXX1234567890ABCDEFGHJKLMN", "gateway_url": "http://dsms-node:8080/ipfs/QmTestCidMeta", } html = _render(walk) assert "DSMS-Anchor" in html assert "QmTestCidVideoXX1234" in html # Internal gateway-host must be rewritten to public for the mail assert "dsms-node:8080" not in html def test_no_dsms_block_when_cid_absent(self): walk = dict(_FAKE_WALK) walk["video"] = dict(walk["video"]) walk["video"].pop("dsms", None) walk.pop("walk_json_dsms", None) html = _render(walk) assert "DSMS-Anchor" not in html class TestRunB17: def test_no_request_skipped(self): state = {} asyncio.run(run_b17(state)) assert "audit_walk" not in state def test_no_url_skipped(self): state = {"req": MagicMock(documents=[MagicMock(url="")])} asyncio.run(run_b17(state)) assert "audit_walk" not in state def test_consent_tester_failure_skipped(self): req = MagicMock(documents=[MagicMock(url="https://example.com/dse")]) state = {"req": req} with patch( "compliance.api.agent_check._b17_wiring.httpx.AsyncClient" ) as mock_client: instance = mock_client.return_value.__aenter__.return_value instance.post = AsyncMock(side_effect=Exception("nope")) asyncio.run(run_b17(state)) assert "audit_walk" not in state def test_success_populates_state(self): req = MagicMock(documents=[MagicMock(url="https://example.com/dse")]) state = {"req": req} resp = MagicMock(status_code=200) resp.json = MagicMock(return_value=_FAKE_WALK) with patch( "compliance.api.agent_check._b17_wiring.httpx.AsyncClient" ) as mock_client: instance = mock_client.return_value.__aenter__.return_value instance.post = AsyncMock(return_value=resp) asyncio.run(run_b17(state)) assert state["audit_walk"]["walk_id"] == "abc123def456" assert "video.webm" in state["audit_walk_html"]