"""Tests for src/cmos/runtime_validator.py. Runtime validator runs at format time on a single candidate string with no exemplar context. It checks type-independent structural sanity rules so the formatter can self-retry when GPT-5's reasoning drift produces a malformed entry. It is deliberately weaker than the scoring linter (which has access to canonical fields and source type) but it catches the most common nondeterministic failures: missing terminal period, stray Ibid., dropped italic markers, unbalanced quotes/asterisks. """ from cmos.runtime_validator import validate def test_clean_book_entry_passes(): candidate = "Yu, Charles. *Interior Chinatown*. Pantheon Books, 2020." result = validate(candidate) assert result.passed is True assert result.failures == [] def test_clean_journal_entry_passes(): candidate = ( 'Kwon, Hyeyoung. "Inclusion Work: Children of Immigrants Claiming Membership in Everyday Life." ' "*American Journal of Sociology* 127, no. 6 (2022): 1818–59. " "https://doi.org/10.1086/720277." ) assert validate(candidate).passed def test_missing_terminal_period_fails(): result = validate("Yu, Charles. *Interior Chinatown*. Pantheon Books, 2020") assert result.passed is False assert any("period" in f.lower() for f in result.failures) def test_ibid_fails(): result = validate("Ibid., 42.") assert result.passed is False assert any("ibid" in f.lower() for f in result.failures) def test_missing_italics_anywhere_fails(): # The "New Yorker" magazine regression we saw in iter 7 variance: italic # markers around the magazine name disappeared. This rule should catch # that — almost every CMOS bibliography entry italicizes something. candidate = 'Mead, Rebecca. "Terms of Aggrievement." New Yorker, December 18, 2023.' result = validate(candidate) assert result.passed is False assert any("italic" in f.lower() for f in result.failures) def test_unbalanced_italic_markers_fails(): candidate = "Yu, Charles. *Interior Chinatown. Pantheon Books, 2020." result = validate(candidate) assert result.passed is False assert any("italic" in f.lower() or "*" in f for f in result.failures) def test_unbalanced_double_quotes_fails(): candidate = 'Mead, Rebecca. "Terms of Aggrievement. *New Yorker*, December 18, 2023.' result = validate(candidate) assert result.passed is False assert any("quote" in f.lower() for f in result.failures) def test_passed_property_is_negation_of_failures(): # Sanity: passed should be True iff there are no failures. result = validate("Yu, Charles. *Interior Chinatown*. Pantheon Books, 2020.") assert result.passed == (result.failures == [])