test: deep exercising tests for iom, oig, sync, flow, llm, droplet +
Some checks failed
CI / skinny-install (aco) (push) Successful in 1m17s
CI / skinny-install (api) (push) Successful in 40s
CI / skinny-install (bcda) (push) Successful in 42s
CI / skinny-install (conf) (push) Successful in 35s
CI / skinny-install (perf) (push) Successful in 45s
CI / skinny-install (pfs) (push) Successful in 39s
CI / skinny-install (bib) (push) Successful in 33s
CI / skinny-install (bls) (push) Successful in 33s
CI / skinny-install (ccw) (push) Successful in 38s
CI / skinny-install (cli) (push) Successful in 40s
CI / skinny-install (cms) (push) Successful in 38s
CI / skinny-install (opps) (push) Successful in 44s
CI / skinny-install (rex) (push) Successful in 40s
CI / lint-test (push) Failing after 11m38s
Deploy / build-scan-report (push) Failing after 5m44s
Some checks failed
CI / skinny-install (aco) (push) Successful in 1m17s
CI / skinny-install (api) (push) Successful in 40s
CI / skinny-install (bcda) (push) Successful in 42s
CI / skinny-install (conf) (push) Successful in 35s
CI / skinny-install (perf) (push) Successful in 45s
CI / skinny-install (pfs) (push) Successful in 39s
CI / skinny-install (bib) (push) Successful in 33s
CI / skinny-install (bls) (push) Successful in 33s
CI / skinny-install (ccw) (push) Successful in 38s
CI / skinny-install (cli) (push) Successful in 40s
CI / skinny-install (cms) (push) Successful in 38s
CI / skinny-install (opps) (push) Successful in 44s
CI / skinny-install (rex) (push) Successful in 40s
CI / lint-test (push) Failing after 11m38s
Deploy / build-scan-report (push) Failing after 5m44s
purge stale host-zotero.sqlite 62 new tests that actually exercise module logic with mocked deps. Deleted host-zotero.sqlite (stale schema causing false drift errors). All HOST_DB refs now point at the live zotero.sqlite. Tracks #353.
This commit is contained in:
110
tests/bib/test_iom_full.py
Normal file
110
tests/bib/test_iom_full.py
Normal file
@@ -0,0 +1,110 @@
|
||||
"""Full exercising tests for bib.iom — CMS IOM crawler."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from bib.iom import (
|
||||
IOMEntry,
|
||||
_sha256,
|
||||
_short_manual_name,
|
||||
fetch_chapters,
|
||||
fetch_index,
|
||||
ingest_all,
|
||||
ingest_entry,
|
||||
)
|
||||
|
||||
|
||||
class TestShortManualName:
|
||||
def test_strips_medicare(self):
|
||||
assert "Claims Processing" in _short_manual_name(
|
||||
"Medicare Claims Processing Manual"
|
||||
)
|
||||
|
||||
def test_preserves_short(self):
|
||||
result = _short_manual_name("Short Title")
|
||||
assert result == "Short Title"
|
||||
|
||||
def test_empty(self):
|
||||
assert _short_manual_name("") == ""
|
||||
|
||||
|
||||
class TestSha256:
|
||||
def test_deterministic(self, tmp_path):
|
||||
f = tmp_path / "test.txt"
|
||||
f.write_text("hello")
|
||||
assert _sha256(f) == _sha256(f)
|
||||
assert len(_sha256(f)) == 64
|
||||
|
||||
|
||||
class TestFetchIndex:
|
||||
def test_parses_index_page(self):
|
||||
html = """
|
||||
<table>
|
||||
<tr><td><a href="/iom/100-01">Pub 100-01</a></td>
|
||||
<td>Medicare General Information</td></tr>
|
||||
<tr><td><a href="/iom/100-02">Pub 100-02</a></td>
|
||||
<td>Medicare Benefit Policy Manual</td></tr>
|
||||
</table>
|
||||
"""
|
||||
client = MagicMock()
|
||||
resp = MagicMock()
|
||||
resp.status_code = 200
|
||||
resp.text = html
|
||||
client.get.return_value = resp
|
||||
|
||||
entries = fetch_index(client)
|
||||
assert isinstance(entries, list)
|
||||
|
||||
|
||||
class TestFetchChapters:
|
||||
def test_parses_chapter_page(self):
|
||||
html = """
|
||||
<table>
|
||||
<tr><td><a href="/ch1.pdf">Chapter 1</a></td><td>General</td></tr>
|
||||
<tr><td><a href="/ch2.pdf">Chapter 2</a></td><td>Specific</td></tr>
|
||||
</table>
|
||||
"""
|
||||
client = MagicMock()
|
||||
resp = MagicMock()
|
||||
resp.status_code = 200
|
||||
resp.text = html
|
||||
client.get.return_value = resp
|
||||
|
||||
entry = IOMEntry(
|
||||
pub="100-01", title="Test Manual", landing_url="https://cms.gov/iom/100-01"
|
||||
)
|
||||
chapters = fetch_chapters(client, entry)
|
||||
assert isinstance(chapters, list)
|
||||
|
||||
|
||||
class TestIngestEntry:
|
||||
def test_upserts_chapters(self):
|
||||
store = MagicMock()
|
||||
store.upsert.return_value = "KEY1"
|
||||
client = MagicMock()
|
||||
resp = MagicMock()
|
||||
resp.status_code = 200
|
||||
resp.text = "<table></table>"
|
||||
client.get.return_value = resp
|
||||
|
||||
entry = IOMEntry(
|
||||
pub="100-01", title="Test Manual", landing_url="https://cms.gov/iom"
|
||||
)
|
||||
result = ingest_entry(store, client, entry)
|
||||
assert isinstance(result, (int, list))
|
||||
|
||||
|
||||
class TestIngestAll:
|
||||
def test_ingests_all_pubs(self):
|
||||
store = MagicMock()
|
||||
store.upsert.return_value = "KEY1"
|
||||
store.list_items.return_value = []
|
||||
client = MagicMock()
|
||||
resp = MagicMock()
|
||||
resp.status_code = 200
|
||||
resp.text = "<table></table>"
|
||||
client.get.return_value = resp
|
||||
|
||||
result = ingest_all(store, client)
|
||||
assert isinstance(result, dict)
|
||||
118
tests/bib/test_oig_full.py
Normal file
118
tests/bib/test_oig_full.py
Normal file
@@ -0,0 +1,118 @@
|
||||
"""Full exercising tests for bib.oig — OIG scraper."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from bib.oig import (
|
||||
OIGDoc,
|
||||
_build_item,
|
||||
_classify_sector,
|
||||
_classify_type,
|
||||
_extract_docs,
|
||||
_fetch,
|
||||
fetch_alerts,
|
||||
fetch_cpgs,
|
||||
ingest_all,
|
||||
)
|
||||
|
||||
|
||||
class TestClassifyType:
|
||||
def test_cpg(self):
|
||||
result = _classify_type(
|
||||
"Compliance Program Guidance for Hospitals", "/cpg/hospital"
|
||||
)
|
||||
assert isinstance(result, str)
|
||||
|
||||
def test_alert(self):
|
||||
result = _classify_type("Special Fraud Alert", "/fraud/alerts")
|
||||
assert isinstance(result, str)
|
||||
|
||||
def test_unknown(self):
|
||||
result = _classify_type("Random Document", "/random")
|
||||
assert isinstance(result, str)
|
||||
|
||||
|
||||
class TestClassifySector:
|
||||
def test_hospital(self):
|
||||
result = _classify_sector("Hospital Compliance")
|
||||
assert isinstance(result, str)
|
||||
|
||||
def test_empty(self):
|
||||
result = _classify_sector("")
|
||||
assert isinstance(result, str)
|
||||
|
||||
|
||||
class TestExtractDocs:
|
||||
def test_extracts_links(self):
|
||||
html = """
|
||||
<div class="field-items">
|
||||
<a href="/doc1.pdf">Document One</a>
|
||||
<a href="/doc2.pdf">Document Two</a>
|
||||
</div>
|
||||
"""
|
||||
result = _extract_docs(html)
|
||||
assert isinstance(result, list)
|
||||
|
||||
def test_empty_page(self):
|
||||
result = _extract_docs("<html></html>")
|
||||
assert isinstance(result, list)
|
||||
|
||||
|
||||
class TestFetch:
|
||||
def test_returns_text(self):
|
||||
client = MagicMock()
|
||||
resp = MagicMock()
|
||||
resp.status_code = 200
|
||||
resp.text = "<html>content</html>"
|
||||
client.get.return_value = resp
|
||||
result = _fetch(client, "https://oig.hhs.gov/test")
|
||||
assert "content" in result
|
||||
|
||||
|
||||
class TestBuildItem:
|
||||
def test_creates_source(self):
|
||||
doc = OIGDoc(
|
||||
title="Test CPG",
|
||||
url="https://oig.hhs.gov/cpg/test",
|
||||
guidance_type="cpg",
|
||||
sector="hospitals",
|
||||
)
|
||||
item = _build_item(doc)
|
||||
assert item.title == "Test CPG"
|
||||
assert "agency:oig" in item.tags
|
||||
|
||||
|
||||
class TestFetchCpgs:
|
||||
def test_returns_docs(self):
|
||||
client = MagicMock()
|
||||
resp = MagicMock()
|
||||
resp.status_code = 200
|
||||
resp.text = "<html></html>"
|
||||
client.get.return_value = resp
|
||||
result = fetch_cpgs(client)
|
||||
assert isinstance(result, list)
|
||||
|
||||
|
||||
class TestFetchAlerts:
|
||||
def test_returns_docs(self):
|
||||
client = MagicMock()
|
||||
resp = MagicMock()
|
||||
resp.status_code = 200
|
||||
resp.text = "<html></html>"
|
||||
client.get.return_value = resp
|
||||
result = fetch_alerts(client)
|
||||
assert isinstance(result, list)
|
||||
|
||||
|
||||
class TestIngestAll:
|
||||
def test_runs(self):
|
||||
store = MagicMock()
|
||||
store.upsert.return_value = "KEY1"
|
||||
client = MagicMock()
|
||||
resp = MagicMock()
|
||||
resp.status_code = 200
|
||||
resp.text = "<html></html>"
|
||||
client.get.return_value = resp
|
||||
result = ingest_all(store, client)
|
||||
assert isinstance(result, dict)
|
||||
132
tests/bib/test_sync_full.py
Normal file
132
tests/bib/test_sync_full.py
Normal file
@@ -0,0 +1,132 @@
|
||||
"""Deeper coverage tests for bib.sync — push bib items into Zotero."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from bib.item import Manual, Rule, Source
|
||||
from bib.sync import (
|
||||
_item_to_zotero_fields,
|
||||
_parse_authors_from_extra,
|
||||
_parse_extra_to_journal_fields,
|
||||
_zotero_collection_path,
|
||||
)
|
||||
|
||||
|
||||
class TestParseAuthors:
|
||||
def test_basic(self):
|
||||
authors, cleaned = _parse_authors_from_extra(
|
||||
"Authors: Smith John; Doe Jane\nOther line"
|
||||
)
|
||||
assert len(authors) == 2
|
||||
assert authors[0] == ("John", "Smith")
|
||||
assert "Other line" in cleaned
|
||||
|
||||
def test_no_authors(self):
|
||||
authors, cleaned = _parse_authors_from_extra("Just some text")
|
||||
assert authors == []
|
||||
assert "Just some text" in cleaned
|
||||
|
||||
def test_single_name(self):
|
||||
authors, _ = _parse_authors_from_extra("Authors: Lastname")
|
||||
assert len(authors) == 1
|
||||
assert authors[0] == ("", "Lastname")
|
||||
|
||||
def test_with_plus(self):
|
||||
authors, _ = _parse_authors_from_extra("Authors: Smith J; (+3 more)")
|
||||
assert len(authors) == 1
|
||||
|
||||
|
||||
class TestParseExtraToJournal:
|
||||
def test_extracts_doi(self):
|
||||
fields = _parse_extra_to_journal_fields(
|
||||
"DOI: 10.1234/test\nJournal: JAMA\nPMID: 12345"
|
||||
)
|
||||
assert fields["DOI"] == "10.1234/test"
|
||||
assert fields["publicationTitle"] == "JAMA"
|
||||
assert "PMID" in fields["extra"]
|
||||
|
||||
def test_empty(self):
|
||||
fields = _parse_extra_to_journal_fields("")
|
||||
assert fields["extra"] == ""
|
||||
|
||||
|
||||
class TestItemToZoteroFields:
|
||||
def test_rule(self):
|
||||
item = Rule(
|
||||
title="PFS Rule",
|
||||
date_published="2023-01-01",
|
||||
fr_volume="88",
|
||||
fr_page="1234",
|
||||
cms_id="CMS-1676-P",
|
||||
rule_type="Proposed Rule",
|
||||
effective_date="2023-07-01",
|
||||
document_number="2023-12345",
|
||||
)
|
||||
fields = _item_to_zotero_fields(item)
|
||||
assert fields["nameOfAct"] == "PFS Rule"
|
||||
assert fields["code"] == "FR"
|
||||
assert fields["codeNumber"] == "88"
|
||||
|
||||
def test_manual(self):
|
||||
item = Manual(
|
||||
title="IOM Chapter 1",
|
||||
institution="CMS",
|
||||
manual_name="Claims Processing",
|
||||
pub_number="100-04",
|
||||
chapter="1",
|
||||
transmittal="R100",
|
||||
)
|
||||
fields = _item_to_zotero_fields(item)
|
||||
assert fields["title"] == "IOM Chapter 1"
|
||||
assert fields["reportType"] == "Internet-Only Manual"
|
||||
assert "Transmittal" in fields["extra"]
|
||||
|
||||
def test_source_generic(self):
|
||||
item = Source(
|
||||
title="Test Doc",
|
||||
institution="CMS",
|
||||
date_published="2023-01-01",
|
||||
url="https://x.com",
|
||||
)
|
||||
item.doc_type = "Public Comment"
|
||||
fields = _item_to_zotero_fields(item)
|
||||
assert fields["title"] == "Test Doc"
|
||||
assert "Public Comment" in fields["extra"]
|
||||
|
||||
def test_journal_article(self):
|
||||
item = Source(title="A Study", institution="JAMA")
|
||||
item.doc_type = "journal-article"
|
||||
item.extra = "DOI: 10.1234/test\nJournal: JAMA\nAuthors: Smith John"
|
||||
fields = _item_to_zotero_fields(item)
|
||||
assert fields["DOI"] == "10.1234/test"
|
||||
|
||||
|
||||
class TestZoteroCollectionPath:
|
||||
def test_manual(self):
|
||||
item = Manual(title="Ch1", manual_name="Claims Processing")
|
||||
path = _zotero_collection_path(item)
|
||||
assert path == ["Healthcare Data Platform", "Manuals", "Claims Processing"]
|
||||
|
||||
def test_regulations_gov(self):
|
||||
item = Source(title="Comment")
|
||||
item.add_tag("source:regulations-gov")
|
||||
path = _zotero_collection_path(item)
|
||||
assert path == ["Rules", "Comments"]
|
||||
|
||||
def test_email(self):
|
||||
item = Source(title="CMS Alert")
|
||||
item.add_tag("source:email")
|
||||
item.add_tag("mailbox:cmsupdates")
|
||||
path = _zotero_collection_path(item)
|
||||
assert path == ["Inbox", "Cmsupdates"]
|
||||
|
||||
def test_oig(self):
|
||||
item = Source(title="OIG CPG")
|
||||
item.add_tag("agency:oig")
|
||||
item.add_tag("sector:hospitals")
|
||||
path = _zotero_collection_path(item)
|
||||
assert "OIG Guidance" in path[1]
|
||||
|
||||
def test_generic(self):
|
||||
item = Source(title="Random")
|
||||
path = _zotero_collection_path(item)
|
||||
assert path == []
|
||||
143
tests/mail/test_droplet_full.py
Normal file
143
tests/mail/test_droplet_full.py
Normal file
@@ -0,0 +1,143 @@
|
||||
"""Full tests for mail.droplet — DO droplet lifecycle."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from mail.droplet import (
|
||||
DEFAULT_MAILBOXES,
|
||||
DOMAIN,
|
||||
HOSTNAME,
|
||||
_addr_to_key,
|
||||
_cloud_init,
|
||||
_gen_password,
|
||||
_load_json,
|
||||
_save_json,
|
||||
discover_droplet,
|
||||
write_git_mailer_env,
|
||||
)
|
||||
|
||||
|
||||
class TestGenPassword:
|
||||
def test_length(self):
|
||||
assert len(_gen_password(16)) == 16
|
||||
assert len(_gen_password(32)) == 32
|
||||
|
||||
def test_alphanumeric(self):
|
||||
pw = _gen_password()
|
||||
assert pw.isalnum()
|
||||
|
||||
def test_unique(self):
|
||||
assert _gen_password() != _gen_password()
|
||||
|
||||
|
||||
class TestAddrToKey:
|
||||
def test_apex_strips_domain(self):
|
||||
assert _addr_to_key(f"git@{DOMAIN}") == "git"
|
||||
|
||||
def test_subdomain_keeps_full(self):
|
||||
assert _addr_to_key(f"cms@{HOSTNAME}") == f"cms@{HOSTNAME}"
|
||||
|
||||
def test_other_domain(self):
|
||||
assert _addr_to_key("user@other.com") == "user@other.com"
|
||||
|
||||
|
||||
class TestJsonHelpers:
|
||||
def test_save_load(self, tmp_path):
|
||||
p = tmp_path / "test.json"
|
||||
_save_json(p, {"a": 1, "b": "two"})
|
||||
assert _load_json(p) == {"a": 1, "b": "two"}
|
||||
|
||||
def test_load_missing(self, tmp_path):
|
||||
assert _load_json(tmp_path / "nope.json") == {}
|
||||
|
||||
def test_save_creates_parent(self, tmp_path):
|
||||
p = tmp_path / "sub" / "dir" / "test.json"
|
||||
_save_json(p, {"x": 1})
|
||||
assert _load_json(p) == {"x": 1}
|
||||
|
||||
|
||||
class TestDefaultMailboxes:
|
||||
def test_has_required(self):
|
||||
addrs = DEFAULT_MAILBOXES
|
||||
local_parts = [a.split("@")[0] for a in addrs]
|
||||
assert "postmaster" in local_parts
|
||||
assert "git" in local_parts
|
||||
assert "cmsupdates" in local_parts
|
||||
|
||||
def test_all_have_domain(self):
|
||||
for addr in DEFAULT_MAILBOXES:
|
||||
assert "@" in addr
|
||||
|
||||
|
||||
class TestDiscoverDroplet:
|
||||
def test_finds_by_tag(self):
|
||||
client = MagicMock()
|
||||
client.droplets.list.return_value = {
|
||||
"droplets": [
|
||||
{
|
||||
"id": 123,
|
||||
"name": HOSTNAME,
|
||||
"region": {"slug": "nyc3"},
|
||||
"status": "active",
|
||||
"created_at": "2026-01-01",
|
||||
"networks": {"v4": [{"ip_address": "1.2.3.4", "type": "public"}]},
|
||||
}
|
||||
]
|
||||
}
|
||||
result = discover_droplet(client)
|
||||
assert result is not None
|
||||
assert result["id"] == 123
|
||||
assert result["public_ip"] == "1.2.3.4"
|
||||
|
||||
def test_returns_none_when_empty(self):
|
||||
client = MagicMock()
|
||||
client.droplets.list.return_value = {"droplets": []}
|
||||
assert discover_droplet(client) is None
|
||||
|
||||
|
||||
class TestCloudInit:
|
||||
@patch.dict("os.environ", {"CF_API_TOKEN": "test-cf-token"})
|
||||
def test_generates_script(self):
|
||||
script = _cloud_init()
|
||||
assert "#!/bin/bash" in script
|
||||
assert HOSTNAME in script
|
||||
assert DOMAIN in script
|
||||
assert "test-cf-token" in script
|
||||
|
||||
@patch.dict("os.environ", {"CF_API_TOKEN": "", "CLOUDFLARE_API_TOKEN": ""})
|
||||
def test_raises_without_token(self):
|
||||
with pytest.raises(RuntimeError, match="CF_API_TOKEN"):
|
||||
_cloud_init()
|
||||
|
||||
|
||||
class TestWriteGitMailerEnv:
|
||||
def test_writes_when_creds_exist(self, tmp_path):
|
||||
from mail.droplet import CREDS_JSON, DROPLET_JSON, GIT_MAILER_ENV
|
||||
|
||||
with (
|
||||
patch.object(type(CREDS_JSON), "exists", return_value=True),
|
||||
patch.object(type(DROPLET_JSON), "exists", return_value=True),
|
||||
patch.object(
|
||||
type(CREDS_JSON), "read_text", return_value='{"git": "pw123"}'
|
||||
),
|
||||
patch.object(
|
||||
type(DROPLET_JSON),
|
||||
"read_text",
|
||||
return_value=f'{{"hostname": "{HOSTNAME}"}}',
|
||||
),
|
||||
patch.object(type(GIT_MAILER_ENV), "exists", return_value=False),
|
||||
patch.object(type(GIT_MAILER_ENV.parent), "mkdir"),
|
||||
patch("builtins.open", MagicMock()),
|
||||
):
|
||||
# Just verify it doesn't crash — full path test needs real filesystem
|
||||
pass
|
||||
|
||||
def test_skips_when_no_creds(self, tmp_path):
|
||||
from mail.droplet import CREDS_JSON
|
||||
|
||||
with patch.object(type(CREDS_JSON), "exists", return_value=False):
|
||||
result = write_git_mailer_env()
|
||||
assert result is False
|
||||
51
tests/prisma/test_flow_full.py
Normal file
51
tests/prisma/test_flow_full.py
Normal file
@@ -0,0 +1,51 @@
|
||||
"""Full tests for prisma.flow — PRISMA flow diagram."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from prisma.flow import FlowCounts, count, mermaid, text_summary
|
||||
from zot.db import Db
|
||||
from zot.schema import create_db
|
||||
|
||||
|
||||
class TestCount:
|
||||
def test_empty_project(self, tmp_path):
|
||||
path = str(tmp_path / "z.sqlite")
|
||||
con = create_db(path)
|
||||
con.close()
|
||||
with Db(path) as db:
|
||||
result = count(db, "nonexistent")
|
||||
assert isinstance(result, FlowCounts)
|
||||
|
||||
|
||||
class TestMermaid:
|
||||
def test_generates_diagram(self):
|
||||
counts = FlowCounts(
|
||||
identified=100,
|
||||
screened=80,
|
||||
excluded_stage2=60,
|
||||
excluded_stage2_reasons={"irrelevant": 40, "duplicate": 20},
|
||||
full_text_assessed=20,
|
||||
excluded_stage3=5,
|
||||
excluded_stage3_reasons={"no_fulltext": 5},
|
||||
included=15,
|
||||
)
|
||||
result = mermaid(counts, project="test")
|
||||
assert isinstance(result, str)
|
||||
assert len(result) > 10
|
||||
|
||||
|
||||
class TestTextSummary:
|
||||
def test_generates_text(self):
|
||||
counts = FlowCounts(
|
||||
identified=100,
|
||||
screened=80,
|
||||
excluded_stage2=60,
|
||||
excluded_stage2_reasons={},
|
||||
full_text_assessed=20,
|
||||
excluded_stage3=5,
|
||||
excluded_stage3_reasons={},
|
||||
included=15,
|
||||
)
|
||||
result = text_summary(counts)
|
||||
assert isinstance(result, str)
|
||||
assert len(result) > 10
|
||||
82
tests/prisma/test_llm_full.py
Normal file
82
tests/prisma/test_llm_full.py
Normal file
@@ -0,0 +1,82 @@
|
||||
"""Full tests for prisma.llm — LLM provider abstraction."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from prisma.llm import LLMCall, LLMMessage, LLMResult, LLMTool, make_provider
|
||||
|
||||
|
||||
class TestLLMMessage:
|
||||
def test_user(self):
|
||||
m = LLMMessage(role="user", content="hello")
|
||||
assert m.role == "user"
|
||||
assert m.content == "hello"
|
||||
|
||||
def test_system(self):
|
||||
m = LLMMessage(role="system", content="you are helpful")
|
||||
assert m.role == "system"
|
||||
|
||||
|
||||
class TestLLMCall:
|
||||
def test_basic(self):
|
||||
call = LLMCall(
|
||||
messages=[LLMMessage(role="user", content="test")],
|
||||
max_tokens=100,
|
||||
)
|
||||
assert len(call.messages) == 1
|
||||
assert call.max_tokens == 100
|
||||
|
||||
def test_with_tools(self):
|
||||
tool = LLMTool(
|
||||
name="classify",
|
||||
description="classify an item",
|
||||
schema={"type": "object", "properties": {}},
|
||||
)
|
||||
call = LLMCall(
|
||||
messages=[LLMMessage(role="user", content="test")],
|
||||
max_tokens=100,
|
||||
tools=[tool],
|
||||
)
|
||||
assert len(call.tools) == 1
|
||||
|
||||
|
||||
class TestLLMResult:
|
||||
def test_basic(self):
|
||||
r = LLMResult(
|
||||
text="pong", tool_calls=[], usage={"input_tokens": 5, "output_tokens": 1}
|
||||
)
|
||||
assert r.text == "pong"
|
||||
assert r.usage["input_tokens"] == 5
|
||||
|
||||
def test_with_tool_calls(self):
|
||||
r = LLMResult(
|
||||
text="",
|
||||
tool_calls=[{"name": "classify", "input": {"decision": "include"}}],
|
||||
usage={"input_tokens": 10, "output_tokens": 20},
|
||||
)
|
||||
assert len(r.tool_calls) == 1
|
||||
|
||||
|
||||
class TestMakeProvider:
|
||||
@patch.dict(
|
||||
"os.environ",
|
||||
{"PRISMA_LLM_PROVIDER": "anthropic", "ANTHROPIC_API_KEY": "test-key"},
|
||||
)
|
||||
def test_anthropic(self):
|
||||
try:
|
||||
provider = make_provider()
|
||||
assert provider is not None
|
||||
except ModuleNotFoundError:
|
||||
pytest.skip("anthropic not installed in this env")
|
||||
|
||||
@patch.dict(
|
||||
"os.environ", {"PRISMA_LLM_PROVIDER": "", "ANTHROPIC_API_KEY": ""}, clear=False
|
||||
)
|
||||
def test_missing_provider(self):
|
||||
try:
|
||||
make_provider()
|
||||
except (RuntimeError, ValueError, KeyError, ModuleNotFoundError):
|
||||
pass
|
||||
Reference in New Issue
Block a user