""" Test that plain output preserves input exactly except for markup. Output should equal: input.replace("^", "").replace("_", "") """ import pytest import sys import html import re from pathlib import Path # Add parent directory to path to import app module sys.path.insert(0, str(Path(__file__).parent.parent)) from app import ( DEFAULT_MODEL_ID, DEFAULT_MODEL_LABEL, MODEL_OPTIONS, _resolve_model_id, _render_plain_line_per_word, _render_styled_line_per_word, macronize, macronize_ui, preprocess_and_syllabify, ) def strip_markup(text: str) -> str: """Remove markup characters from text.""" return text.replace("^", "").replace("_", "") def strip_color(text: str) -> str: """Remove pretty-output color markup.""" return html.unescape(re.sub(r"]*>", "", text)) # Test cases with Greek lines TEST_CASES = [ # Basic simple word "νεανίας", # Multiple words with spaces "νεανίας ἀάατός ἐστιν", # With final sigma "καὶ καλός", # Multiple spaces "καλὰ μὲν", # Single letter "ἢ", # Word with punctuation preserved "τυφλὸς ἤ", # Multi-word with accents "Ἀτρεΐδαι τε καὶ ἄλλοι", # Longer passage "νεανίας ἀάατός ἐστιν καὶ καλός", # Expanded ξ and trailing comma must be restored exactly "καλὰ μὲν ἠέξευ, καλὰ δ᾽ ἔτραφες, οὐράνιε Ζεῦ", ] @pytest.mark.parametrize("input_line", TEST_CASES) def test_plain_output_preserves_input_without_markup(input_line): """ Test that the plain output is identical to input after removing markup. The output should be: input.replace("^", "").replace("_", "") This ensures: - All original characters are preserved - Spaces are preserved exactly - Final sigmas are preserved - Only the markup (^ and _) are added """ # Get the rendered plain line plain_output = _render_plain_line_per_word(input_line, DEFAULT_MODEL_ID) # Strip markup from the output to get back the base text output_without_markup = strip_markup(plain_output) # The expected result: input with no markup # (We don't normalize final sigma - we preserve exactly what was in the input) input_expected = input_line print(f"\nInput: {repr(input_line)}") print(f"Output: {repr(plain_output)}") print(f"Output without markup: {repr(output_without_markup)}") print(f"Expected: {repr(input_expected)}") # The core assertion: output without markup should match input assert output_without_markup == input_expected, ( f"Output without markup doesn't match input.\n" f"Expected: {repr(input_expected)}\n" f"Got: {repr(output_without_markup)}" ) def test_styled_output_uses_plain_formatting_with_colored_vowels(monkeypatch): input_line = "νεανίας ἀάατός ἐστιν καὶ καλός. τὰ παῖδες τὰ καλά" labels_by_word = { "νεανίασ": [("νε", 0), ("α", 1), ("νί", 2), ("ασ", 0)], "ἀάατόσ": [("ἀ", 2), ("ά", 1), ("α", 2), ("τόσ", 0)], "ἐστιν": [("ἐσ", 0), ("τιν", 2)], "καὶ": [("καὶ", 0)], "καλόσ.": [("κα", 2), ("λό", 0), ("σ.", 0)], "τὰ": [("τὰ", 2)], "παῖδεσ": [("παῖ", 0), ("δεσ", 0)], "καλά": [("κα", 1), ("λά", 1)], } def fake_classify_line(word, model_id): return labels_by_word[word] monkeypatch.setattr("app.classify_line", fake_classify_line) styled_output = _render_styled_line_per_word(input_line, DEFAULT_MODEL_ID) expected = ( 'νε' 'α' 'νί' 'ας ' '' 'ά' 'α' 'τός ' 'ἐστιν ' 'καὶ ' 'καλός. ' 'τ ' 'παῖδες ' 'τ ' 'καλά' ) assert styled_output == expected assert "vowel clear" not in styled_output @pytest.mark.parametrize("input_line", TEST_CASES) def test_styled_output_preserves_input_without_color(monkeypatch, input_line): def fake_classify_line(word, model_id): return [ (syllable, 1 if idx % 2 == 0 else 2) for idx, syllable in enumerate(preprocess_and_syllabify(word)) ] monkeypatch.setattr("app.classify_line", fake_classify_line) styled_output = _render_styled_line_per_word(input_line, DEFAULT_MODEL_ID) assert strip_color(styled_output) == input_line def test_plain_output_box_contains_only_output_lines(monkeypatch): monkeypatch.setattr("app._render_plain_line_per_word", lambda line, model_id: f"marked {line}") monkeypatch.setattr("app._render_styled_line_per_word", lambda line, model_id: line) html_output, plain_output = macronize_ui("alpha\nbeta", DEFAULT_MODEL_LABEL) assert plain_output == "marked alpha\nmarked beta" assert '
' not in html_output assert '
alpha
' in html_output def test_api_returns_only_plain_output(monkeypatch): monkeypatch.setattr("app._render_plain_line_per_word", lambda line, model_id: f"marked {line}") assert macronize("alpha\nbeta") == "marked alpha\nmarked beta" @pytest.mark.parametrize( ("model_label", "expected_model_id"), [ ("modernbert", MODEL_OPTIONS["Pretrained ModernBERT"]), ("syllamobert", MODEL_OPTIONS["Pretrained ModernBERT"]), ("roberta", MODEL_OPTIONS["Fine-tuned RoBERTa"]), ("Pretrained ModernBERT", MODEL_OPTIONS["Pretrained ModernBERT"]), ("Fine-tuned RoBERTa", MODEL_OPTIONS["Fine-tuned RoBERTa"]), ], ) def test_model_label_aliases(model_label, expected_model_id): assert _resolve_model_id(model_label) == expected_model_id def test_api_model_label_alias_is_used(monkeypatch): model_ids = [] def fake_render_line(line, model_id): model_ids.append(model_id) return f"marked {line}" monkeypatch.setattr("app._render_plain_line_per_word", fake_render_line) assert macronize("alpha", model="roberta") == "marked alpha" assert model_ids == [MODEL_OPTIONS["Fine-tuned RoBERTa"]] def test_public_api_endpoint_is_plain_output_only(): import app public_deps = [ dep for dep in app.demo.config.get("dependencies", []) if dep.get("api_name") == "macronize" and dep.get("api_visibility") == "public" ] assert len(public_deps) == 1 assert len(public_deps[0].get("inputs", [])) == 2 assert len(public_deps[0].get("outputs", [])) == 1 public_callable_deps = [ dep for dep in app.demo.config.get("dependencies", []) if dep.get("api_visibility") == "public" and dep.get("api_name") not in (None, False) ] assert [dep.get("api_name") for dep in public_callable_deps] == ["macronize"] def test_mcp_tool_surface_exposes_only_plain_endpoint(): import app assert set(app.demo.get_api_info()["named_endpoints"]) == {"/macronize"} def test_public_api_text_example_is_greek(): import app api_text_components = [ component for component in app.demo.config.get("components", []) if component.get("props", {}).get("label") == "text" ] assert len(api_text_components) == 1 assert api_text_components[0]["props"]["value"] == "μῆνιν ἄειδε θεὰ Πηληϊάδεω Ἀχιλῆος" def test_try_examples_preserve_visible_line_breaks(): import app assert any("\n" in example for example in app.examples) assert "#try-examples .examples" in app.CSS assert "#try-examples .example-text" in app.CSS assert "white-space: pre-wrap" in app.CSS assert "overflow: visible" in app.CSS assert "text-overflow: clip" in app.CSS def test_python_api_instructions_omit_api_name(): import app markdown_values = [ component.get("props", {}).get("value", "") for component in app.demo.config.get("components", []) if component.get("type") == "markdown" ] api_markdown = next(value for value in markdown_values if "Python API" in value) assert 'Client("Urdatorn/macronizer")' in api_markdown assert 'model="roberta"' in api_markdown assert 'api_name="/macronize"' in api_markdown if __name__ == "__main__": pytest.main([__file__, "-v", "-s"])