Files
031-flash-card/tests/unit/application/test_study_session.py
Tirsvad b3c5116110 MIL-006: Spoken Malay word
Review record RC-028 (Go, autonomous mode, not independent).

Task: MIL-006#1
Task: MIL-006#2
Task: MIL-006#3
Task: MIL-006#4
Task: MIL-006#5
2026-10-14 22:39:36 +08:00

153 lines
4.3 KiB
Python

"""Tests of the StudySession use case."""
from collections.abc import Sequence
from flash_card.application.dto.card_dto import CardDto
from flash_card.application.use_cases.study_session import StudySession
from flash_card.domain.entities.card import Card
from flash_card.domain.entities.deck import Deck
from flash_card.domain.value_objects.language_pair import LanguagePair
PAIR = LanguagePair("ms", "da")
A = Card("aku", "mig")
B = Card("kau", "du")
def _first(cards: Sequence[Card]) -> Card:
return cards[0]
class _RecordingProgress:
def __init__(self) -> None:
self.saved: list[tuple[Card, ...]] = []
def load(self) -> Deck | None:
return None
def save(self, deck: Deck) -> None:
self.saved.append(deck.cards)
class _RecordingSpeech:
def __init__(self) -> None:
self.spoken: list[tuple[str, str]] = []
def speak(self, text: str, language_code: str) -> None:
self.spoken.append((text, language_code))
def _session(
*cards: Card,
progress: _RecordingProgress | None = None,
speech: _RecordingSpeech | None = None,
) -> StudySession:
store = _RecordingProgress() if progress is None else progress
voice = _RecordingSpeech() if speech is None else speech
return StudySession(Deck(PAIR, cards), _first, store, voice)
def test_current_card_is_none_before_first_draw() -> None:
assert _session(A).current_card is None
def test_next_card_returns_chosen_card_as_dto() -> None:
assert _session(A, B).next_card() == CardDto("aku", "mig", "ms", "da")
def test_next_card_returns_different_card_when_two_or_more() -> None:
session = _session(A, B)
first = session.next_card()
second = session.next_card()
assert first != second
assert second == CardDto("kau", "du", "ms", "da")
def test_next_card_returns_same_card_when_one() -> None:
session = _session(A)
assert session.next_card() == session.next_card()
def test_next_card_returns_none_when_deck_is_empty() -> None:
session = _session()
assert session.next_card() is None
assert session.current_card is None
def test_current_card_follows_last_draw() -> None:
session = _session(A, B)
shown = session.next_card()
assert session.current_card == shown
def test_remaining_counts_cards_in_deck() -> None:
session = _session(A, B)
session.next_card()
assert session.remaining == 2
def test_mark_known_removes_current_card_and_saves_remaining_once() -> None:
progress = _RecordingProgress()
session = _session(A, B, progress=progress)
session.next_card()
session.mark_known()
assert session.remaining == 1
assert session.current_card is None
assert progress.saved == [(B,)]
def test_mark_known_saves_nothing_when_no_current_card() -> None:
progress = _RecordingProgress()
session = _session(A, progress=progress)
session.mark_known()
assert session.remaining == 1
assert progress.saved == []
def test_mark_known_on_last_card_leaves_session_finished() -> None:
progress = _RecordingProgress()
session = _session(A, progress=progress)
session.next_card()
session.mark_known()
assert progress.saved == [()]
assert session.next_card() is None
def test_next_card_never_saves() -> None:
progress = _RecordingProgress()
session = _session(A, B, progress=progress)
session.next_card()
session.next_card()
assert progress.saved == []
def test_next_card_speaks_front_text_once_in_front_language() -> None:
speech = _RecordingSpeech()
session = _session(A, B, speech=speech)
session.next_card()
assert speech.spoken == [("aku", "ms")]
def test_next_card_does_not_speak_when_deck_is_empty() -> None:
speech = _RecordingSpeech()
session = _session(speech=speech)
session.next_card()
assert speech.spoken == []
def test_next_card_speaks_again_on_every_draw() -> None:
speech = _RecordingSpeech()
session = _session(A, B, speech=speech)
session.next_card()
session.next_card()
assert speech.spoken == [("aku", "ms"), ("kau", "ms")]
def test_mark_known_does_not_speak_but_following_draw_does() -> None:
speech = _RecordingSpeech()
session = _session(A, B, speech=speech)
session.next_card()
session.mark_known()
assert speech.spoken == [("aku", "ms")]
session.next_card()
assert speech.spoken == [("aku", "ms"), ("kau", "ms")]