Scholar-Lens / tests /test_app_core.py
Mr-Gondal
Codexx polish constellation interactions
ee4fab6
Raw
History Blame Contribute Delete
13.4 kB
import unittest
from unittest.mock import Mock, patch
from pathlib import Path
import app
def paper(
title: str,
*,
year: str = "2024",
abstract: str = "",
citations: str = "0",
) -> app.PaperResult:
return app.PaperResult(
title=title,
year=year,
source="OpenAlex",
authors="A. Author",
citations=citations,
url="https://example.com",
abstract=abstract,
)
class AppCoreTests(unittest.TestCase):
def test_extract_search_query_removes_question_filler(self):
query = app._extract_search_query(
"What are the main approaches to early cancer detection using MRI?"
)
self.assertEqual(query, "approaches early cancer detection mri")
def test_rank_results_prefers_relevance_before_year(self):
older_relevant = paper(
"Cancer detection with MRI",
year="2020",
abstract="MRI cancer detection screening model",
citations="50",
)
newer_irrelevant = paper(
"Unrelated particle physics survey",
year="2025",
abstract="Collider measurements",
citations="500",
)
ranked = app._rank_results(
[newer_irrelevant, older_relevant],
"cancer detection MRI",
)
self.assertEqual(ranked[0], older_relevant)
def test_split_search_queries_accepts_multiple_keywords(self):
queries = app._split_search_queries("aerosols, cloud feedback\nsatellite rainfall; aerosols")
self.assertEqual(queries, ["aerosols", "cloud feedback", "satellite rainfall"])
def test_collect_results_merges_multiple_keyword_searches(self):
first = paper("Aerosol cloud interactions", abstract="aerosol cloud")
second = paper("Satellite rainfall retrieval", abstract="satellite rainfall")
with patch(
"app._collect_single_query_results",
side_effect=[([first], []), ([second], [])],
) as mocked:
results, warnings = app._collect_results("aerosols, satellite rainfall")
self.assertEqual(mocked.call_count, 2)
self.assertEqual({result.title for result in results}, {first.title, second.title})
self.assertEqual(warnings, [])
def test_search_all_sources_reports_multiple_keyword_searches(self):
with patch(
"app._collect_results",
return_value=([paper("Aerosol cloud interactions")], []),
):
status, *_ = app.search_all_sources("aerosols, cloud feedback")
self.assertIn("2", status)
self.assertIn("keyword searches", status)
def test_render_result_insights_handles_results(self):
panel = app._render_result_insights([paper("Aerosol cloud interactions")])
self.assertIn("Papers", panel)
self.assertIn("Top Source", panel)
def test_context_builder_respects_budget(self):
long_abstract = "cancer detection " * 1000
papers = [paper(f"Paper {index}", abstract=long_abstract) for index in range(20)]
context = app._build_synthesis_context(papers)
self.assertLessEqual(len(context), app.SYNTHESIS_CONTEXT_CHAR_LIMIT)
self.assertLessEqual(
app._rough_token_count(context),
app.SYNTHESIS_CONTEXT_TOKEN_LIMIT,
)
def test_clear_search_returns_all_reset_outputs(self):
result = app.clear_search()
self.assertEqual(len(result), 28)
self.assertEqual(result[0], "Enter a research topic to begin.")
self.assertIsNone(result[9])
self.assertEqual(result[10], "")
self.assertEqual(result[11], "")
self.assertEqual(result[12], "")
self.assertEqual(result[14], app.DEFAULT_ASK_ANSWER)
self.assertEqual(result[16], app.DEFAULT_LOAD_STATUS)
self.assertIsNone(result[18])
self.assertEqual(result[21], app.DEFAULT_PAPER_CHAT_ANSWER)
self.assertEqual(result[25], app.DEFAULT_COMPARE_ANSWER)
self.assertIn("Literature Constellation", result[26])
def test_pagination_updates_disable_edges(self):
papers = [paper(f"Paper {index}") for index in range(app.RESULTS_PER_PAGE + 1)]
first_prev, first_next = app._pagination_updates(papers, 0)
second_prev, second_next = app._pagination_updates(papers, 1)
self.assertFalse(first_prev["interactive"])
self.assertTrue(first_next["interactive"])
self.assertTrue(second_prev["interactive"])
self.assertFalse(second_next["interactive"])
def test_compare_selector_updates_use_display_choices(self):
papers = [paper("Paper A"), paper("Paper B")]
left, right = app._compare_selector_updates(papers)
self.assertEqual(left["value"], "1. Paper A")
self.assertEqual(right["value"], "2. Paper B")
def test_reconstruct_abstract_orders_openalex_index(self):
abstract = app._reconstruct_abstract({"world": [1], "hello": [0]})
self.assertEqual(abstract, "hello world")
def test_normalize_doi_strips_doi_url(self):
self.assertEqual(
app._normalize_doi("https://doi.org/10.1234/ABC"),
"10.1234/abc",
)
def test_dedupe_prefers_duplicate_with_abstract(self):
weak = paper("Deep Learning for Cancer Detection", abstract="")
strong = paper(
"Deep Learning for Cancer Detection!",
abstract="Detailed abstract",
citations="3",
)
deduped = app._dedupe_results([weak, strong])
self.assertEqual(len(deduped), 1)
self.assertEqual(deduped[0].abstract, "Detailed abstract")
def test_source_specific_queries(self):
self.assertIn("ti:cancer", app._arxiv_search_query("cancer detection with MRI"))
self.assertIn(
"cancer[Title/Abstract]",
app._pubmed_search_query("cancer detection with MRI"),
)
def test_rubric_proof_panel_mentions_judging_evidence(self):
panel = app._render_rubric_proof()
self.assertIn("Backyard AI proof points", panel)
self.assertIn("Real professor workflow", panel)
self.assertIn("NVIDIA Nemotron fit", panel)
self.assertIn(app.MODEL_DISPLAY_NAME, panel)
def test_public_model_story_is_nvidia_nemotron(self):
self.assertEqual(
app.MODEL_ID,
"nvidia/Llama-3.1-Nemotron-Nano-8B-v1",
)
self.assertEqual(app.MODEL_PROVIDER_BADGE, "Powered by NVIDIA Nemotron on Modal")
self.assertIn("Nemotron", app.MODEL_DISPLAY_NAME)
def test_modal_default_model_is_nemotron_nano(self):
modal_source = Path(app.__file__).with_name("modal_inference.py").read_text(
encoding="utf-8",
)
self.assertIn(
'"nvidia/Llama-3.1-Nemotron-Nano-8B-v1"',
modal_source,
)
self.assertIn("trust_remote_code=True", modal_source)
self.assertIn("enforce_eager=True", modal_source)
self.assertIn("Use only the supplied context", modal_source)
def test_load_selected_paper_returns_context(self):
item = paper("Useful Paper", abstract="A clear abstract about useful results.")
paper_text, status, summary, tab_update = app.load_selected_paper(0, [item])
self.assertIn("Useful Paper", paper_text)
self.assertIn("Loaded", status)
self.assertEqual(summary, "")
self.assertEqual(tab_update["selected"], "summarize")
def test_summarize_now_loads_tab_without_modal_call(self):
item = paper("Useful Paper", abstract="A clear abstract about useful results.")
with patch("app.summarize_with_modal") as mocked:
paper_text, status, summary, tab_update, *_ = app.load_selected_paper_reset_chat(
0,
[item],
)
mocked.assert_not_called()
self.assertIn("Useful Paper", paper_text)
self.assertIn("Loaded", status)
self.assertIn("Click Summarize with AI", summary)
self.assertEqual(tab_update["selected"], "summarize")
def test_row_selection_loads_without_modal_call(self):
item = paper("Useful Paper", abstract="A clear abstract about useful results.")
with patch("app.summarize_with_modal") as mocked:
paper_text, status, summary, tab_update, *_ = app.summarize_row_selection(
"0",
[item],
)
mocked.assert_not_called()
self.assertIn("Useful Paper", paper_text)
self.assertIn("Loaded", status)
self.assertIn("Click Summarize with AI", summary)
self.assertEqual(tab_update["selected"], "summarize")
def test_export_results_csv_creates_file(self):
path = app.export_results_csv([paper("Exportable Paper")])
self.assertIsNotNone(path)
with open(path, encoding="utf-8") as handle:
content = handle.read()
self.assertIn("Exportable Paper", content)
def test_combine_paper_context_includes_results_section(self):
context = app._combine_paper_context("Abstract text", "Result text")
self.assertIn("Abstract text", context)
self.assertIn("Results / Findings", context)
self.assertIn("Result text", context)
def test_export_summary_markdown_includes_results(self):
path = app.export_summary_markdown("Abstract text", "Result text", "Summary text")
self.assertIsNotNone(path)
with open(path, encoding="utf-8") as handle:
content = handle.read()
self.assertIn("Results / Findings", content)
self.assertIn("Result text", content)
def test_modal_request_error_uses_response_detail(self):
response = Mock()
response.json.return_value = {"detail": "Bad input"}
exc = app.requests.HTTPError(response=response)
self.assertEqual(
app._modal_request_error_message(exc, "Modal"),
"Modal: Bad input",
)
def test_get_first_author_accepts_string_or_list(self):
self.assertEqual(app.get_first_author("Ada Lovelace, Alan Turing"), "Ada Lovelace")
self.assertEqual(app.get_first_author(["Grace Hopper", "Katherine Johnson"]), "Grace Hopper")
def test_search_result_constellation_marks_keyword_fallback(self):
graph = app.build_constellation_from_papers(
"connectome",
[
paper("Functional connectome graph theory", abstract="modularity graph network"),
paper("Resting state connectome modularity", abstract="resting fmri modularity"),
],
)
self.assertTrue(graph["data_completeness"]["keyword_fallback_used"])
self.assertEqual(graph["data_completeness"]["paper_count"], 2)
self.assertIn("nodes", graph)
def test_constellation_community_ids_match_nodes(self):
graph = app.build_constellation_from_papers(
"mixed methods",
[
paper("Functional graph modularity", abstract="graph modularity community"),
paper("Clinical disease cohort", abstract="clinical disease disorder"),
paper("Diffusion tractography", abstract="structural diffusion tractography"),
],
)
community_ids = {community["id"] for community in graph["communities"]}
self.assertTrue({node["community"] for node in graph["nodes"]}.issubset(community_ids))
def test_constellation_render_has_nonblank_fallback(self):
graph = app.build_constellation_from_papers(
"connectome",
[paper("Functional connectome graph theory", abstract="modularity graph network")],
)
html = app._render_constellation_html(graph)
self.assertIn("Literature Constellation", html)
self.assertIn("CONNECTED LITERATURE MAP", html)
self.assertIn("canvas", html)
self.assertIn("function showDetail", html)
self.assertIn("if (node) showDetail(node)", html)
self.assertNotIn("const labeled", html)
self.assertNotIn("fillText(label", html)
self.assertNotIn("Connectome Constellation", html)
def test_compare_prompt_names_nemotron(self):
results = [
paper("Paper A", abstract="A studies rainfall with satellite data."),
paper("Paper B", abstract="B studies rainfall with station data."),
]
with patch("app.synthesize_with_modal", return_value="comparison") as mocked:
result = app.compare_papers_with_ai(0, 1, results)
self.assertEqual(result, "comparison")
prompt = mocked.call_args.args[0]
self.assertIn(app.MODEL_DISPLAY_NAME, prompt)
self.assertIn("Use only the provided metadata", prompt)
def test_export_corpus_zip_includes_graph_json(self):
graph = app.build_constellation_from_papers(
"connectome",
[paper("Functional connectome graph theory", abstract="modularity graph network")],
)
path = app.export_corpus_zip(graph)
self.assertIsNotNone(path)
with app.zipfile.ZipFile(path) as archive:
self.assertIn("graph.json", archive.namelist())
self.assertIn("data-completeness.json", archive.namelist())
if __name__ == "__main__":
unittest.main()