# retoor from tests.conftest import run_async from devplacepy.services.community.ai import AiAssistant, outline, parse_json, to_html from devplacepy.services.community.extract import parse_html, select_nodes from tests.unit.services.community.fakes import HTML_ITEM class Canned(AiAssistant): def __init__(self, answer): self.answer = answer self.asked = [] def enabled(self): return True async def ask(self, system, user): self.asked.append((system, user)) return self.answer def nodes(): root = parse_html("
" + "".join(HTML_ITEM.format(n=n) for n in range(1, 5)) + "
") return root, select_nodes(root, "article.post") def test_json_is_extracted_from_fenced_or_chatty_replies(): assert parse_json('```json\n{"a": 1}\n```') == {"a": 1} assert parse_json('Sure! Here you go: {"a": {"b": 2}} hope it helps') == {"a": {"b": 2}} assert parse_json("no json") is None def test_only_proposals_that_match_the_sample_are_accepted(): _, items = nodes() assistant = Canned({"title": "h2", "link": "h2 a::attr(href)", "author": ".nonexistent", "created_at": "time::attr(datetime)"}) accepted = run_async(assistant.post_fields(items)) assert accepted == {"title": "h2", "link": "h2 a::attr(href)", "created_at": "time::attr(datetime)"} def test_item_selector_must_match_at_least_three_elements(): root, _ = nodes() assert run_async(Canned({"item_selector": "article.post"}).item_selector(root)) == "article.post" assert run_async(Canned({"item_selector": "article.missing"}).item_selector(root)) is None assert run_async(Canned({"item_selector": "!!"}).item_selector(root)) is None def test_comment_structure_needs_content(): root = parse_html('

hello there

ann

second text

bob
') good = run_async(Canned({"item_selector": "div.c", "id": "::attr(id)", "content": "p", "author": "b"}).comment_structure(root)) assert good == {"item_selector": "div.c", "id": "::attr(id)", "content": "p", "author": "b"} assert run_async(Canned({"item_selector": "div.c", "content": "span"}).comment_structure(root)) is None def test_disabled_assistant_never_asks(): class Off(AiAssistant): def enabled(self): return False assert run_async(Off().ask("s", "u")) is None def test_outline_and_html_are_bounded(): root, items = nodes() assert "article.post" in outline(root) assert len(outline(root, 3).splitlines()) <= 3 assert len(to_html(items[0], 100)) <= 100