100% Coverage

This commit is contained in:
Renn F
2026-05-06 21:02:31 +02:00
parent 64c48356d0
commit 9aa30fb945
106 changed files with 28143 additions and 423 deletions
+128 -3
View File
@@ -2,9 +2,12 @@
from __future__ import annotations
from typing import ClassVar
from uuid import uuid4
import anthropic as anthropic_mod
import pytest
from roboco.llm import ToonAdapter
from roboco.models import MessageType
from roboco.models.extraction import (
ExtractionConfig,
@@ -123,7 +126,8 @@ async def test_extract_splits_on_double_newlines(
) -> None:
content = "First paragraph here.\n\nSecond paragraph here."
result = await svc.extract(_ctx(content))
assert len(result.messages) == 2
_PARAS = 2
assert len(result.messages) == _PARAS
@pytest.mark.asyncio
@@ -145,8 +149,9 @@ async def test_extract_keeps_code_blocks_intact(svc: ExtractionService) -> None:
async def test_extract_respects_max_segments() -> None:
svc = ExtractionService(ExtractionConfig(max_segments_per_buffer=2))
content = "First.\n\nSecond.\n\nThird.\n\nFourth.\n\nFifth."
_PARAS = 2
result = await svc.extract(_ctx(content))
assert len(result.messages) <= 2
assert len(result.messages) <= _PARAS
@pytest.mark.asyncio
@@ -205,10 +210,130 @@ async def test_pipeline_swallows_callback_errors() -> None:
"""Callback failure should not abort the pipeline."""
pipeline = ExtractionPipeline()
async def bad_callback(msg) -> None:
async def bad_callback(_msg) -> None:
raise RuntimeError("boom")
pipeline.on_message(bad_callback)
# Should complete without raising despite callback error.
result = await pipeline.process_buffer(_ctx("Hello there.\n\nGoodbye."))
assert result is not None
# ---------------------------------------------------------------------------
# Mentions extraction (line 209)
# ---------------------------------------------------------------------------
@pytest.mark.asyncio
async def test_extract_logs_mentions_found(svc: ExtractionService) -> None:
"""When @mentions are present, the debug branch fires (line 209)."""
content = "@be-dev-1 can you take a look at this code please?"
result = await svc.extract(_ctx(content))
assert len(result.messages) >= 1
@pytest.mark.asyncio
async def test_extract_skips_empty_segments_from_segmenter(
svc: ExtractionService, monkeypatch: pytest.MonkeyPatch
) -> None:
"""If _segment_content yields a whitespace-only segment, it's skipped.
Triggers the defensive `if not segment.strip(): continue` (line 193).
"""
monkeypatch.setattr(
svc, "_segment_content", lambda _content: [" ", "Real content here."]
)
result = await svc.extract(_ctx("Long enough content to bypass min length."))
# Only the non-empty segment becomes a message.
assert len(result.messages) == 1
# ---------------------------------------------------------------------------
# extract_with_llm (lines 323-404)
# ---------------------------------------------------------------------------
@pytest.mark.asyncio
async def test_extract_with_llm_falls_back_on_error(
svc: ExtractionService, monkeypatch: pytest.MonkeyPatch
) -> None:
"""If the Anthropic call fails, falls back to pattern-based extract."""
class _BadClient:
def __init__(self, **_kwargs: object) -> None:
self.messages = self
async def create(self, **_kwargs: object) -> object:
raise RuntimeError("API down")
monkeypatch.setattr(anthropic_mod, "AsyncAnthropic", _BadClient)
result = await svc.extract_with_llm(_ctx("I'm thinking about this."))
# Pattern fallback path executes; messages list is not empty.
assert result is not None
assert len(result.messages) >= 1
@pytest.mark.asyncio
async def test_extract_with_llm_parses_response(
svc: ExtractionService, monkeypatch: pytest.MonkeyPatch
) -> None:
"""Mock a successful LLM response and verify it gets parsed."""
class _Block:
text = "json-or-toon"
class _Resp:
content: ClassVar = [_Block()]
class _OkClient:
def __init__(self, **_kwargs: object) -> None:
self.messages = self
async def create(self, **_kwargs: object) -> _Resp:
return _Resp()
def _decode_dicts(_self: ToonAdapter, _text: str) -> list[dict[str, object]]:
return [
{"type": "reasoning", "content": "thinking", "confidence": 0.9},
{"type": "action", "content": "doing", "confidence": 0.95},
]
monkeypatch.setattr(anthropic_mod, "AsyncAnthropic", _OkClient)
monkeypatch.setattr(ToonAdapter, "decode", _decode_dicts)
result = await svc.extract_with_llm(_ctx("Some content for the LLM."))
assert result is not None
_COUNT = 2
assert len(result.messages) == _COUNT
@pytest.mark.asyncio
async def test_extract_with_llm_handles_non_dict_segments(
svc: ExtractionService, monkeypatch: pytest.MonkeyPatch
) -> None:
"""When the toon decoder returns non-dict segments, fallback type."""
class _Block:
text = "raw response"
class _Resp:
content: ClassVar = [_Block()]
class _OkClient:
def __init__(self, **_kwargs: object) -> None:
self.messages = self
async def create(self, **_kwargs: object) -> _Resp:
return _Resp()
# Patch toon to yield strings instead of dicts.
def _decode_strings(_self: ToonAdapter, _text: str) -> list[str]:
return ["plain string segment", "another"]
monkeypatch.setattr(anthropic_mod, "AsyncAnthropic", _OkClient)
monkeypatch.setattr(ToonAdapter, "decode", _decode_strings)
result = await svc.extract_with_llm(_ctx("Some content for the LLM."))
assert result is not None
_COUNT = 2
assert len(result.messages) == _COUNT