aboutsummaryrefslogtreecommitdiff
path: root/docs/design/2026-06-21-anki-titlefix-test.py
diff options
context:
space:
mode:
Diffstat (limited to 'docs/design/2026-06-21-anki-titlefix-test.py')
-rw-r--r--docs/design/2026-06-21-anki-titlefix-test.py190
1 files changed, 0 insertions, 190 deletions
diff --git a/docs/design/2026-06-21-anki-titlefix-test.py b/docs/design/2026-06-21-anki-titlefix-test.py
deleted file mode 100644
index 87008a8..0000000
--- a/docs/design/2026-06-21-anki-titlefix-test.py
+++ /dev/null
@@ -1,190 +0,0 @@
-"""Tests for flashcard-to-anki.py default-path and deck-name helpers.
-
-The script is a PEP 723 uv-run script that imports genanki, which uv resolves
-at runtime but isn't installed in the test environment. The fixture stubs
-genanki in sys.modules so the module loads; the pure helpers under test never
-call into it.
-"""
-from __future__ import annotations
-
-import importlib.util
-import sys
-import types
-from pathlib import Path
-
-import pytest
-
-SCRIPT = Path(__file__).resolve().parents[1] / "flashcard-to-anki.py"
-
-
-@pytest.fixture(scope="module")
-def drill():
- # Only stub when genanki is genuinely absent, so a real install isn't shadowed.
- sys.modules.setdefault("genanki", types.ModuleType("genanki"))
- spec = importlib.util.spec_from_file_location("flashcard_to_anki", SCRIPT)
- assert spec and spec.loader
- module = importlib.util.module_from_spec(spec)
- spec.loader.exec_module(module)
- return module
-
-
-def test_default_output_path_targets_phone_anki_dir(drill):
- """The .apkg is a phone artifact, so it defaults under sync/phone/anki/."""
- result = drill.default_output_path(Path("/home/x/projects/health/health-drill.org"))
- assert result == Path.home() / "sync" / "phone" / "anki" / "health-drill.apkg"
-
-
-def test_default_deck_name_uses_org_title(drill):
- """The #+TITLE drives the Anki deck name, not the filename slug."""
- org = "#+TITLE: Refutations\n* Section\n** Q? :drill:\na\n"
- assert drill.default_deck_name(Path("/x/refutation-drill.org"), org) == "Refutations"
-
-
-def test_default_deck_name_title_is_trimmed(drill):
- """Surrounding whitespace on the #+TITLE value is stripped."""
- org = "#+TITLE: DeepSat Flashcards \n"
- assert drill.default_deck_name(Path("/x/deepsat.org"), org) == "DeepSat Flashcards"
-
-
-def test_default_deck_name_title_match_is_case_insensitive(drill):
- """A lowercase #+title: keyword is still recognized."""
- org = "#+title: Health Flashcards\n"
- assert drill.default_deck_name(Path("/x/health-drill.org"), org) == "Health Flashcards"
-
-
-def test_default_deck_name_falls_back_to_basename_without_title(drill):
- """No #+TITLE line falls back to the input basename, case preserved."""
- org = "* Section\n** Q? :drill:\na\n"
- assert drill.default_deck_name(Path("/x/deepsat.org"), org) == "deepsat"
-
-
-def test_default_deck_name_blank_title_falls_back_to_basename(drill):
- """An empty #+TITLE value is ignored in favour of the basename."""
- assert drill.default_deck_name(Path("/x/health-drill.org"), "#+TITLE: \n") == "health-drill"
-
-
-# --- section_to_tag (pure) ---
-
-def test_section_to_tag_slugifies_words(drill):
- assert drill.section_to_tag("Orbital Regimes") == "orbital-regimes"
-
-
-def test_section_to_tag_strips_leading_and_trailing_nonalnum(drill):
- assert drill.section_to_tag(" People & Roles! ") == "people-roles"
-
-
-def test_section_to_tag_empty_string(drill):
- assert drill.section_to_tag("") == ""
-
-
-# --- escape_html (pure) ---
-
-def test_escape_html_escapes_amp_lt_gt(drill):
- assert drill.escape_html("a & b < c > d") == "a &amp; b &lt; c &gt; d"
-
-
-def test_escape_html_plain_text_unchanged(drill):
- assert drill.escape_html("plain text") == "plain text"
-
-
-def test_escape_html_escapes_amp_first_so_existing_entity_is_literal(drill):
- # & is replaced before < / >, so a literal "&lt;" becomes "&amp;lt;",
- # not silently treated as an already-escaped entity.
- assert drill.escape_html("&lt;") == "&amp;lt;"
-
-
-def test_escape_html_empty_string(drill):
- assert drill.escape_html("") == ""
-
-
-# --- stable_id (pure) ---
-
-def test_stable_id_is_deterministic(drill):
- assert drill.stable_id("DeepSat", "deck") == drill.stable_id("DeepSat", "deck")
-
-
-def test_stable_id_salt_changes_the_result(drill):
- assert drill.stable_id("DeepSat", "deck") != drill.stable_id("DeepSat", "model")
-
-
-def test_stable_id_stays_within_the_reserved_range(drill):
- value = drill.stable_id("anything", "deck")
- assert drill.ID_BASE <= value < drill.ID_BASE + drill.ID_RANGE
-
-
-# --- strip_org_metadata (pure) ---
-
-def test_strip_org_metadata_drops_properties_drawer(drill):
- body = [":PROPERTIES:", ":ID: x", ":END:", "real content"]
- assert drill.strip_org_metadata(body) == ["real content"]
-
-
-def test_strip_org_metadata_drops_planning_lines(drill):
- body = ["SCHEDULED: <2026-05-30>", "DEADLINE: <2026-06-01>",
- "CLOSED: [2026-05-29]", "body"]
- assert drill.strip_org_metadata(body) == ["body"]
-
-
-def test_strip_org_metadata_leaves_plain_body_unchanged(drill):
- body = ["line one", "line two"]
- assert drill.strip_org_metadata(body) == ["line one", "line two"]
-
-
-def test_strip_org_metadata_empty_list(drill):
- assert drill.strip_org_metadata([]) == []
-
-
-def test_strip_org_metadata_unclosed_drawer_swallows_the_rest(drill):
- # An unterminated :PROPERTIES: drawer consumes everything after it.
- body = [":PROPERTIES:", ":ID: x", "still in drawer"]
- assert drill.strip_org_metadata(body) == []
-
-
-def test_strip_org_metadata_drops_created_date_line(drill):
- # A created/added date never belongs on a card back.
- assert drill.strip_org_metadata(["Created: 2026-05-30", "real answer"]) == ["real answer"]
-
-
-# --- parse (pure, core parser) ---
-
-SECTIONED = """* Orbital Regimes
-** What is LEO? :drill:
-Low Earth Orbit.
-** What is GEO? :drill:
-Geostationary Earth Orbit.
-"""
-
-
-def test_parse_returns_front_back_tag_per_card(drill):
- cards = drill.parse(SECTIONED)
- assert len(cards) == 2
- assert cards[0] == ("What is LEO?", "Low Earth Orbit.", "orbital-regimes")
- assert cards[1][0] == "What is GEO?"
-
-
-def test_parse_card_without_a_section_gets_the_drill_tag(drill):
- assert drill.parse("** Lone card? :drill:\nbody\n") == [("Lone card?", "body", "drill")]
-
-
-def test_parse_strips_properties_drawer_from_back(drill):
- text = "** Q? :drill:\n:PROPERTIES:\n:ID: abc\n:END:\nThe answer.\n"
- assert drill.parse(text) == [("Q?", "The answer.", "drill")]
-
-
-def test_parse_trims_leading_and_trailing_blank_body_lines(drill):
- cards = drill.parse("** Q? :drill:\n\n\nanswer\n\n\n")
- assert cards[0][1] == "answer"
-
-
-def test_parse_card_with_only_a_drawer_has_empty_back(drill):
- text = "** Q? :drill:\n:PROPERTIES:\n:ID: x\n:END:\n"
- assert drill.parse(text) == [("Q?", "", "drill")]
-
-
-def test_parse_joins_multiline_body_with_br(drill):
- cards = drill.parse("** Q? :drill:\nline one\nline two\n")
- assert cards[0][1] == "line one<br>line two"
-
-
-def test_parse_no_drill_cards_returns_empty(drill):
- assert drill.parse("* Section\nno drill cards here\n") == []