68 lines
2.6 KiB
Python
68 lines
2.6 KiB
Python
# retoor <retoor@molodetz.nl>
|
|
|
|
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("<main>" + "".join(HTML_ITEM.format(n=n) for n in range(1, 5)) + "</main>")
|
|
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('<div><div class="c" id="a1"><p>hello there</p><b>ann</b></div><div class="c" id="a2"><p>second text</p><b>bob</b></div></div>')
|
|
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
|