MIL-003: Step 2 - New flash cards

Review record RC-025 (Go, autonomous mode, not independent).

Task: MIL-003#1
Task: MIL-003#2
Task: MIL-003#3
Task: MIL-003#4
Task: MIL-003#5
This commit is contained in:
2026-10-14 21:59:29 +08:00
parent c6c737ce40
commit 8d66c43eea
36 changed files with 1167 additions and 102 deletions
+38
View File
@@ -0,0 +1,38 @@
"""Tests of the Card, LanguagePair and Vocabulary value types."""
import dataclasses
import pytest
from flash_card.domain.entities.card import Card
from flash_card.domain.entities.vocabulary import Vocabulary
from flash_card.domain.value_objects.language_pair import LanguagePair
def test_card_equals_card_with_same_texts() -> None:
assert Card("aku", "mig") == Card("aku", "mig")
def test_card_is_frozen() -> None:
card = Card("aku", "mig")
with pytest.raises(dataclasses.FrozenInstanceError):
card.front = "kau" # type: ignore[misc]
def test_language_pair_holds_codes() -> None:
pair = LanguagePair("ms", "da")
assert (pair.front_code, pair.back_code) == ("ms", "da")
def test_language_pair_is_frozen() -> None:
pair = LanguagePair("ms", "da")
with pytest.raises(dataclasses.FrozenInstanceError):
pair.front_code = "en" # type: ignore[misc]
def test_vocabulary_holds_pair_and_cards() -> None:
pair = LanguagePair("ms", "da")
cards = (Card("aku", "mig"),)
vocabulary = Vocabulary(pair, cards)
assert vocabulary.language_pair == pair
assert vocabulary.cards == cards
+73
View File
@@ -0,0 +1,73 @@
"""Tests of the Deck entity."""
from collections.abc import Sequence
import pytest
from flash_card.domain.entities.card import Card
from flash_card.domain.entities.deck import Deck
from flash_card.domain.entities.vocabulary import Vocabulary
from flash_card.domain.errors.vocabulary_errors import EmptyDeckError
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]
def test_from_vocabulary_holds_all_cards() -> None:
deck = Deck.from_vocabulary(Vocabulary(PAIR, (A, B)))
assert deck.cards == (A, B)
assert deck.language_pair == PAIR
assert len(deck) == 2
def test_cards_returns_copy_when_deck_changes_later() -> None:
deck = Deck(PAIR, [A])
snapshot = deck.cards
deck.remove(A)
assert snapshot == (A,)
assert deck.is_empty
def test_draw_returns_chosen_card() -> None:
assert Deck(PAIR, [A, B]).draw(_first) == A
def test_draw_avoids_excluded_card_when_another_exists() -> None:
assert Deck(PAIR, [A, B]).draw(_first, exclude=A) == B
def test_draw_returns_excluded_card_when_it_is_the_only_one() -> None:
assert Deck(PAIR, [A]).draw(_first, exclude=A) == A
def test_draw_keeps_card_in_deck() -> None:
deck = Deck(PAIR, [A, B])
deck.draw(_first)
assert len(deck) == 2
def test_draw_raises_when_deck_is_empty() -> None:
with pytest.raises(EmptyDeckError):
Deck(PAIR, []).draw(_first)
def test_remove_deletes_first_equal_card() -> None:
deck = Deck(PAIR, [A, B, A])
deck.remove(A)
assert deck.cards == (B, A)
def test_remove_raises_when_card_is_absent() -> None:
with pytest.raises(ValueError):
Deck(PAIR, [A]).remove(B)
def test_is_empty_reflects_cards() -> None:
assert Deck(PAIR, []).is_empty
assert not Deck(PAIR, [A]).is_empty
@@ -0,0 +1,21 @@
"""Tests of the vocabulary error hierarchy."""
import pytest
from flash_card.domain.errors.vocabulary_errors import (
EmptyDeckError,
VocabularyError,
VocabularyFormatError,
VocabularyNotFoundError,
)
@pytest.mark.parametrize(
"error", [VocabularyFormatError, VocabularyNotFoundError, EmptyDeckError]
)
def test_error_is_a_vocabulary_error(error: type[VocabularyError]) -> None:
assert issubclass(error, VocabularyError)
def test_vocabulary_error_is_an_exception() -> None:
assert issubclass(VocabularyError, Exception)