fedstylevalidator / tests /test_ui.py
Eric Putnam
Show FSV version in Streamlit footer
61bf96e
Raw History Blame Contribute Delete
7.84 kB
from __future__ import annotations
from pathlib import Path
import pytest
from fedstylevalidator.core.models import VisibilitySettings
from fsv_streamlit import ui
from fsv_streamlit.errors import FileHandlingError, ProviderError, ValidationError
def test_run_app_smoke_path(fake_st: object, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(ui, "_installed_fsv_version", lambda: "0.1.1")
ui.run_app()
assert fake_st.title_calls == ["Federal Style Validator (FSV)"]
assert fake_st.caption_calls[0] == (
"Upload a Word document and review the Federal Style Validator (FSV) "
"findings."
)
assert fake_st.tabs_calls == [
["Document Checker", "Style Guide", "Rules Reference"]
]
assert fake_st.sidebar.selectbox_calls[0] == {
"label": "Document Type",
"options": ["Default", "Advisory Circular", "Order"],
"index": 0,
}
assert any("2026 Eric Putnam" in body for body, _ in fake_st.markdown_calls)
assert any(
"Federal Style Validator v.0.1.1" in body
for body, _ in fake_st.markdown_calls
)
def test_load_style_guide_content_prefers_fsv_generated_reference() -> None:
assert "Checks Reference" in ui._load_style_guide_content()
def test_render_style_guide_tab_outputs_markdown(fake_st: object) -> None:
ui._render_style_guide_tab()
assert fake_st.subheader_calls[-1] == "Style Guide Reference"
assert "Checks Reference" in fake_st.markdown_calls[-1][0]
def test_load_rules_reference_content_prefers_fsv_generated_reference() -> None:
assert "Rules Reference" in ui._load_rules_reference_content()
def test_render_rules_reference_tab_outputs_markdown(fake_st: object) -> None:
ui._render_rules_reference_tab()
assert fake_st.subheader_calls[-1] == "Rules Reference"
assert "Rules Reference" in fake_st.markdown_calls[-1][0]
def test_get_document_types_prioritizes_default_profile() -> None:
assert ui._get_document_types() == ["Default", "Advisory Circular", "Order"]
def test_validate_upload_accepts_supported_docx(
uploaded_file_factory: object,
) -> None:
uploaded_file = uploaded_file_factory(name="valid.docx", data=b"abc", size=3)
ui._validate_upload(uploaded_file)
@pytest.mark.parametrize("file_name", ["invalid.txt", "report.pdf"])
def test_validate_upload_rejects_non_docx_files(
uploaded_file_factory: object,
file_name: str,
) -> None:
uploaded_file = uploaded_file_factory(name=file_name, data=b"abc", size=3)
with pytest.raises(ValidationError, match="Only .docx files are supported."):
ui._validate_upload(uploaded_file)
def test_validate_upload_rejects_oversized_files(
uploaded_file_factory: object,
) -> None:
uploaded_file = uploaded_file_factory(
name="large.docx",
data=b"x",
size=ui.MAX_FILE_BYTES + 1,
)
with pytest.raises(ValidationError, match="File exceeds 20 MB limit."):
ui._validate_upload(uploaded_file)
def test_write_temp_file_persists_uploaded_bytes(
uploaded_file_factory: object,
) -> None:
uploaded_file = uploaded_file_factory(name="sample.docx", data=b"payload")
temp_path = Path(ui._write_temp_file(uploaded_file))
try:
assert temp_path.suffix == ".docx"
assert temp_path.read_bytes() == b"payload"
finally:
temp_path.unlink(missing_ok=True)
def test_write_temp_file_wraps_os_errors(
uploaded_file_factory: object,
monkeypatch: pytest.MonkeyPatch,
) -> None:
uploaded_file = uploaded_file_factory(name="sample.docx", data=b"payload")
def fail_named_temporary_file(*args: object, **kwargs: object) -> object:
raise OSError("no temp dir")
monkeypatch.setattr(ui.tempfile, "NamedTemporaryFile", fail_named_temporary_file)
with pytest.raises(FileHandlingError, match="could not store the uploaded DOCX"):
ui._write_temp_file(uploaded_file)
def test_sync_upload_state_clears_analysis_for_removed_upload(
fake_st: object,
) -> None:
fake_st.session_state.update(
{
ui._UPLOAD_TOKEN_KEY: "sample.docx:12",
ui._RESULT_KEY: {"by_category": {}},
ui._DOC_NAME_KEY: "sample.docx",
ui._DOC_TYPE_KEY: "Order",
ui._TIMESTAMP_KEY: "20260410_120000",
}
)
ui._sync_upload_state(None)
assert fake_st.session_state == {}
def test_sync_upload_state_clears_stale_analysis_when_upload_changes(
fake_st: object,
uploaded_file_factory: object,
) -> None:
fake_st.session_state.update(
{
ui._UPLOAD_TOKEN_KEY: "old.docx:3",
ui._RESULT_KEY: {"by_category": {"analysis": []}},
}
)
ui._sync_upload_state(uploaded_file_factory(name="new.docx", data=b"hello"))
assert fake_st.session_state[ui._UPLOAD_TOKEN_KEY] == "new.docx:5"
assert ui._RESULT_KEY not in fake_st.session_state
def test_apply_visibility_filters_hidden_categories() -> None:
visibility = VisibilitySettings(show_analysis=False, show_headings=True)
result = {
"by_category": {
"headings": [{"id": "H1"}],
"analysis": [{"id": "A1"}],
"custom": [{"id": "C1"}],
},
"findings": [{"id": "ignored"}],
"metadata": {"title": "Doc"},
}
filtered = ui._apply_visibility(result, visibility)
assert filtered == {
"by_category": {
"headings": [{"id": "H1"}],
"custom": [{"id": "C1"}],
},
"metadata": {"title": "Doc"},
}
def test_run_analysis_for_upload_sets_session_state_and_cleans_temp_file(
fake_st: object,
uploaded_file_factory: object,
monkeypatch: pytest.MonkeyPatch,
) -> None:
uploaded_file = uploaded_file_factory(name="sample.docx", data=b"payload")
captured_path: dict[str, Path] = {}
def fake_run_analysis(
*,
file_path: str,
doc_type: str,
visibility: VisibilitySettings,
check_links: bool,
) -> dict[str, object]:
path = Path(file_path)
captured_path["value"] = path
assert path.exists()
assert doc_type == "Order"
assert check_links is True
assert visibility.to_dict()["headings"] is True
return {"by_category": {"headings": []}}
monkeypatch.setattr(ui, "run_analysis", fake_run_analysis)
monkeypatch.setattr(ui, "_timestamp_for_filename", lambda: "20260410_120000")
ui._run_analysis_for_upload(
uploaded_file=uploaded_file,
doc_type="Order",
check_links=True,
)
assert not captured_path["value"].exists()
assert fake_st.session_state[ui._RESULT_KEY] == {"by_category": {"headings": []}}
assert fake_st.session_state[ui._DOC_NAME_KEY] == "sample.docx"
assert fake_st.session_state[ui._DOC_TYPE_KEY] == "Order"
assert fake_st.session_state[ui._TIMESTAMP_KEY] == "20260410_120000"
assert fake_st.session_state[ui._CHECK_LINKS_KEY] is True
def test_run_analysis_for_upload_reports_provider_errors(
fake_st: object,
uploaded_file_factory: object,
monkeypatch: pytest.MonkeyPatch,
) -> None:
uploaded_file = uploaded_file_factory(name="sample.docx", data=b"payload")
captured_path: dict[str, Path] = {}
def fake_run_analysis(
*,
file_path: str,
doc_type: str,
visibility: VisibilitySettings,
check_links: bool,
) -> dict[str, object]:
captured_path["value"] = Path(file_path)
raise ProviderError("provider failed")
monkeypatch.setattr(ui, "run_analysis", fake_run_analysis)
ui._run_analysis_for_upload(
uploaded_file=uploaded_file,
doc_type="Order",
check_links=False,
)
assert fake_st.error_calls == ["provider failed"]
assert not captured_path["value"].exists()