Spaces:
Running on Zero
Running on Zero
| """ | |
| 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"</?span[^>]*>", "", 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 | |
| "καλὰ μὲν ἠέξευ, καλὰ δ᾽ ἔτραφες, οὐράνιε Ζεῦ", | |
| ] | |
| 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 = ( | |
| 'νε' | |
| '<span class="vowel long">α</span>' | |
| 'ν<span class="vowel short">ί</span>' | |
| 'ας ' | |
| '<span class="vowel short">ἀ</span>' | |
| '<span class="vowel long">ά</span>' | |
| '<span class="vowel short">α</span>' | |
| 'τός ' | |
| 'ἐστ<span class="vowel short">ι</span>ν ' | |
| 'καὶ ' | |
| 'κ<span class="vowel short">α</span>λός. ' | |
| 'τ<span class="vowel short">ὰ</span> ' | |
| 'παῖδες ' | |
| 'τ<span class="vowel short">ὰ</span> ' | |
| 'κ<span class="vowel long">α</span>λ<span class="vowel long">ά</span>' | |
| ) | |
| assert styled_output == expected | |
| assert "vowel clear" not in styled_output | |
| 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 '<div class="source">' not in html_output | |
| assert '<div class="line-output">alpha</div>' 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" | |
| 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"]) | |