"""Tests for drill-deck-stats.py: prompt-form heuristic + CLI inventory/gate. Plain python3 script (no third-party deps), so the pure helper imports directly; the inventory/gate behavior is exercised through the CLI. """ from __future__ import annotations import importlib.util import subprocess import sys from pathlib import Path import pytest SCRIPT = Path(__file__).resolve().parents[1] / "drill-deck-stats.py" @pytest.fixture(scope="module") def stats(): spec = importlib.util.spec_from_file_location("drill_deck_stats", SCRIPT) assert spec and spec.loader module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) return module # --- is_prompt_form (pure) --- def test_is_prompt_form_question_mark(stats): assert stats.is_prompt_form("What is DeepSat?") is True def test_is_prompt_form_imperative_verb(stats): assert stats.is_prompt_form("Spell out the orbital regimes") is True def test_is_prompt_form_imperative_is_case_insensitive(stats): assert stats.is_prompt_form("introduce yourself") is True def test_is_prompt_form_topic_heading_is_not_a_prompt(stats): assert stats.is_prompt_form("DeepSat") is False def test_is_prompt_form_strips_trailing_punctuation_off_first_word(stats): assert stats.is_prompt_form("List: the founders") is True # --- CLI inventory + gate (integration) --- CLEAN_DECK = """#+TITLE: DeepSat Flashcards * Section ** What is DeepSat? :drill: :PROPERTIES: :ID: card-1 :END: A satellite company. """ DIRTY_DECK = """#+TITLE: DeepSat Org-Drill Flashcards * Section ** DeepSat :drill: *** Answer A satellite company. """ def _run(path): return subprocess.run( [sys.executable, str(SCRIPT), str(path)], capture_output=True, text=True, ) def test_cli_clean_deck_exits_zero(tmp_path): f = tmp_path / "clean.org" f.write_text(CLEAN_DECK) r = _run(f) assert r.returncode == 0 assert "clean" in r.stdout def test_cli_dirty_deck_warns_and_exits_one(tmp_path): f = tmp_path / "dirty.org" f.write_text(DIRTY_DECK) r = _run(f) assert r.returncode == 1 assert "WARN" in r.stdout assert "org-drill" in r.stdout.lower() # title-jargon audit fired def test_cli_missing_file_exits_two(tmp_path): r = _run(tmp_path / "nope.org") assert r.returncode == 2 NO_TITLE_DECK = """* Section ** What is DeepSat? :drill: :PROPERTIES: :ID: card-1 :END: A satellite company. """ # Two cards, only one PROPERTIES drawer. PROP_MISMATCH_DECK = """#+TITLE: DeepSat Flashcards * Section ** What is DeepSat? :drill: A satellite company. ** Who founded it? :drill: :PROPERTIES: :ID: card-2 :END: The team. """ def test_cli_missing_title_warns_and_exits_one(tmp_path): f = tmp_path / "notitle.org" f.write_text(NO_TITLE_DECK) r = _run(f) assert r.returncode == 1 assert "no #+TITLE" in r.stdout def test_cli_properties_count_mismatch_warns_and_exits_one(tmp_path): f = tmp_path / "mismatch.org" f.write_text(PROP_MISMATCH_DECK) r = _run(f) assert r.returncode == 1 assert "does not match card count" in r.stdout