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()