test_forum_runtime.py 6.31 KB
from __future__ import annotations

from pathlib import Path

import pytest

from apps.web_api.runtime import forum_runtime
from apps.web_api.runtime.forum_runtime import ForumRuntime
from apps.web_api.runtime.process_registry import ProcessRuntimeRegistry


def _build_process_registry() -> ProcessRuntimeRegistry:
    return ProcessRuntimeRegistry(
        {
            "insight": {"process": None, "port": 18501, "status": "stopped", "output": []},
            "media": {"process": None, "port": 18502, "status": "stopped", "output": []},
            "query": {"process": None, "port": 18503, "status": "stopped", "output": []},
            "forum": {"process": None, "port": None, "status": "stopped", "output": []},
        }
    )


def test_forum_runtime_updates_injected_registry_and_emits_status_lines():
    registry = _build_process_registry()
    emitted: list[tuple[str, dict[str, str]]] = []
    calls: list[str] = []
    runtime = ForumRuntime(
        process_registry=registry,
        read_log=lambda *_args, **_kwargs: ["forum-log-line"],
        start_monitoring=lambda: calls.append("start") or True,
        stop_monitoring=lambda: calls.append("stop"),
    )

    assert runtime.start_engine(
        emit_output=lambda event, payload: emitted.append((event, payload))
    ) is True
    assert registry.get_status("forum") == "running"

    output_payload = runtime.get_output(log_dir=Path("."))
    log_payload = runtime.get_log_payload(log_dir=Path("."))
    runtime.stop_engine(
        emit_output=lambda event, payload: emitted.append((event, payload))
    )

    expected_engine_result = {
        "engine_name": "forum",
        "status": "running",
        "success": True,
        "summary": "forum-log-line",
        "artifacts": {
            "parsed_messages": [
                {
                    "timestamp": "",
                    "source": "UNKNOWN",
                    "content": "forum-log-line",
                }
            ],
            "participants": ["UNKNOWN"],
        },
        "metrics": {
            "log_line_count": 1,
            "parsed_message_count": 1,
            "participant_count": 1,
            "host_summary_count": 0,
        },
        "logs_ref": "forum.log",
    }

    assert registry.get_status("forum") == "stopped"
    assert calls == ["start", "stop"]
    assert output_payload == {
        "success": True,
        "output": ["forum-log-line"],
        "total_lines": 1,
        "engine_result": expected_engine_result,
    }
    assert log_payload == {
        "success": True,
        "log_lines": ["forum-log-line"],
        "parsed_messages": [
            {
                "timestamp": "",
                "source": "UNKNOWN",
                "content": "forum-log-line",
            }
        ],
        "total_lines": 1,
        "engine_result": expected_engine_result,
    }
    assert emitted == [
        ("console_output", {"app": "forum", "line": "[SYSTEM] ForumEngine started"}),
        ("console_output", {"app": "forum", "line": "[SYSTEM] ForumEngine stopped"}),
    ]


def test_forum_runtime_marks_registry_error_when_startup_fails():
    registry = _build_process_registry()
    runtime = ForumRuntime(
        process_registry=registry,
        start_monitoring=lambda: False,
    )

    with pytest.raises(RuntimeError, match="ForumEngine failed to start"):
        runtime.start_engine()

    assert registry.get_status("forum") == "error"


def test_forum_runtime_maps_host_summary_to_completed_engine_result():
    registry = _build_process_registry()
    registry.set_status("forum", "running")
    runtime = ForumRuntime(
        process_registry=registry,
        read_log=lambda *_args, **_kwargs: [
            "[2026-04-22T10:50:00Z][Query Agent] Collected venue review snippets",
            "[2026-04-22T10:50:02Z][HOST] Summary: curation and staff guidance stand out across engines",
        ],
        start_monitoring=lambda: True,
        stop_monitoring=lambda: None,
    )

    payload = runtime.get_log_payload(log_dir=Path("."))

    assert payload["parsed_messages"] == [
        {
            "timestamp": "2026-04-22T10:50:00Z",
            "source": "Query Agent",
            "content": "Collected venue review snippets",
        },
        {
            "timestamp": "2026-04-22T10:50:02Z",
            "source": "HOST",
            "content": "Summary: curation and staff guidance stand out across engines",
        },
    ]
    assert payload["parsed_messages"] == payload["engine_result"]["artifacts"]["parsed_messages"]
    assert payload["engine_result"] == {
        "engine_name": "forum",
        "status": "completed",
        "success": True,
        "summary": "Summary: curation and staff guidance stand out across engines",
        "artifacts": {
            "parsed_messages": [
                {
                    "timestamp": "2026-04-22T10:50:00Z",
                    "source": "Query Agent",
                    "content": "Collected venue review snippets",
                },
                {
                    "timestamp": "2026-04-22T10:50:02Z",
                    "source": "HOST",
                    "content": "Summary: curation and staff guidance stand out across engines",
                },
            ],
            "participants": ["HOST", "Query Agent"],
            "host_summary": "Summary: curation and staff guidance stand out across engines",
        },
        "metrics": {
            "log_line_count": 2,
            "parsed_message_count": 2,
            "participant_count": 2,
            "host_summary_count": 1,
        },
        "logs_ref": "forum.log",
    }


def test_forum_runtime_without_injected_registry_uses_runtime_module_registry(monkeypatch):
    registry = _build_process_registry()
    runtime = ForumRuntime(
        start_monitoring=lambda: True,
        stop_monitoring=lambda: None,
    )
    monkeypatch.setattr(forum_runtime, "PROCESS_RUNTIME_REGISTRY", registry)

    assert runtime.start_engine() is True
    assert runtime.process_registry is registry
    assert registry.get_status("forum") == "running"

    runtime.stop_engine()

    assert registry.get_status("forum") == "stopped"


def test_build_forum_runtime_binds_injected_registry():
    registry = _build_process_registry()

    runtime = forum_runtime.build_forum_runtime(process_registry=registry)

    assert isinstance(runtime, ForumRuntime)
    assert runtime.process_registry is registry