blob: 3154d429d020aeec6defd63659c3d32f41edfddf (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
"""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
|