| | |
| | import pytest |
| | from pydantic import ValidationError |
| |
|
| | from ankigen_core.models import ( |
| | Step, |
| | Subtopics, |
| | Topics, |
| | CardFront, |
| | CardBack, |
| | Card, |
| | CardList, |
| | ConceptBreakdown, |
| | CardGeneration, |
| | LearningSequence, |
| | CrawledPage, |
| | AnkiCardData, |
| | ) |
| |
|
| |
|
| | |
| | def test_step_creation(): |
| | step = Step(explanation="Test explanation", output="Test output") |
| | assert step.explanation == "Test explanation" |
| | assert step.output == "Test output" |
| |
|
| |
|
| | def test_step_missing_fields(): |
| | with pytest.raises(ValidationError): |
| | Step(output="Test output") |
| | with pytest.raises(ValidationError): |
| | Step(explanation="Test explanation") |
| |
|
| |
|
| | |
| | def test_subtopics_creation(): |
| | step1 = Step(explanation="Expl1", output="Out1") |
| | step2 = Step(explanation="Expl2", output="Out2") |
| | subtopics = Subtopics(steps=[step1, step2], result=["Res1", "Res2"]) |
| | assert len(subtopics.steps) == 2 |
| | assert subtopics.steps[0].explanation == "Expl1" |
| | assert subtopics.result == ["Res1", "Res2"] |
| |
|
| |
|
| | def test_subtopics_missing_fields(): |
| | with pytest.raises(ValidationError): |
| | Subtopics(result=["Res1"]) |
| | with pytest.raises(ValidationError): |
| | Subtopics(steps=[Step(explanation="e", output="o")]) |
| |
|
| |
|
| | def test_subtopics_incorrect_types(): |
| | with pytest.raises(ValidationError): |
| | Subtopics(steps="not a list", result=["Res1"]) |
| | with pytest.raises(ValidationError): |
| | Subtopics(steps=[Step(explanation="e", output="o")], result="not a list") |
| | with pytest.raises(ValidationError): |
| | Subtopics(steps=["not a step"], result=["Res1"]) |
| |
|
| |
|
| | |
| | def test_topics_creation(): |
| | step = Step(explanation="e", output="o") |
| | subtopic1 = Subtopics(steps=[step], result=["R1"]) |
| | topics = Topics(result=[subtopic1]) |
| | assert len(topics.result) == 1 |
| | assert topics.result[0].steps[0].explanation == "e" |
| |
|
| |
|
| | def test_topics_missing_fields(): |
| | with pytest.raises(ValidationError): |
| | Topics() |
| |
|
| |
|
| | def test_topics_incorrect_types(): |
| | with pytest.raises(ValidationError): |
| | Topics(result="not a list") |
| | with pytest.raises(ValidationError): |
| | Topics(result=["not a subtopic"]) |
| |
|
| |
|
| | |
| | def test_card_front_creation(): |
| | card_front = CardFront(question="What is Pydantic?") |
| | assert card_front.question == "What is Pydantic?" |
| | card_front_none = CardFront() |
| | assert card_front_none.question is None |
| |
|
| |
|
| | |
| | def test_card_back_creation(): |
| | card_back = CardBack( |
| | answer="A data validation library", |
| | explanation="It uses Python type hints", |
| | example="class Model(BaseModel): ...", |
| | ) |
| | assert card_back.answer == "A data validation library" |
| | assert card_back.explanation == "It uses Python type hints" |
| | assert card_back.example == "class Model(BaseModel): ..." |
| |
|
| | |
| | card_back_no_answer = CardBack( |
| | explanation="Explanation only", example="Example only" |
| | ) |
| | assert card_back_no_answer.answer is None |
| | assert card_back_no_answer.explanation == "Explanation only" |
| | assert card_back_no_answer.example == "Example only" |
| |
|
| |
|
| | def test_card_back_missing_fields(): |
| | with pytest.raises(ValidationError): |
| | CardBack(answer="A", explanation="B") |
| | with pytest.raises(ValidationError): |
| | CardBack(answer="A", example="C") |
| | |
| | |
| |
|
| |
|
| | |
| | def test_card_creation(): |
| | front = CardFront(question="Q") |
| | back = CardBack(answer="A", explanation="E", example="Ex") |
| | card = Card(front=front, back=back, metadata={"source": "test"}, card_type="basic") |
| | assert card.front.question == "Q" |
| | assert card.back.answer == "A" |
| | assert card.metadata == {"source": "test"} |
| | assert card.card_type == "basic" |
| |
|
| | card_default_type = Card(front=front, back=back) |
| | assert card_default_type.card_type == "basic" |
| |
|
| |
|
| | def test_card_missing_fields(): |
| | front = CardFront(question="Q") |
| | back = CardBack(answer="A", explanation="E", example="Ex") |
| | with pytest.raises(ValidationError): |
| | Card(front=front) |
| | with pytest.raises(ValidationError): |
| | Card(back=back) |
| |
|
| |
|
| | def test_card_incorrect_types(): |
| | back = CardBack(answer="A", explanation="E", example="Ex") |
| | with pytest.raises(ValidationError): |
| | Card(front="not a CardFront", back=back) |
| |
|
| |
|
| | |
| | def test_card_list_creation(): |
| | front = CardFront(question="Q") |
| | back = CardBack(answer="A", explanation="E", example="Ex") |
| | card1 = Card(front=front, back=back) |
| | card_list = CardList(topic="Python Basics", cards=[card1]) |
| | assert card_list.topic == "Python Basics" |
| | assert len(card_list.cards) == 1 |
| | assert card_list.cards[0].front.question == "Q" |
| |
|
| |
|
| | def test_card_list_missing_fields(): |
| | with pytest.raises(ValidationError): |
| | CardList(cards=[]) |
| | with pytest.raises(ValidationError): |
| | CardList(topic="Topic") |
| |
|
| |
|
| | def test_card_list_incorrect_types(): |
| | with pytest.raises(ValidationError): |
| | CardList(topic=123, cards=[]) |
| | with pytest.raises(ValidationError): |
| | CardList(topic="Topic", cards="not a list") |
| | with pytest.raises(ValidationError): |
| | CardList(topic="Topic", cards=["not a card"]) |
| |
|
| |
|
| | |
| | def test_concept_breakdown_creation(): |
| | cb = ConceptBreakdown( |
| | main_concept="Loops", |
| | prerequisites=["Variables"], |
| | learning_outcomes=["Understand for/while loops"], |
| | common_misconceptions=["Off-by-one errors"], |
| | difficulty_level="beginner", |
| | ) |
| | assert cb.main_concept == "Loops" |
| | assert cb.prerequisites == ["Variables"] |
| | assert cb.learning_outcomes == ["Understand for/while loops"] |
| | assert cb.common_misconceptions == ["Off-by-one errors"] |
| | assert cb.difficulty_level == "beginner" |
| |
|
| |
|
| | def test_concept_breakdown_missing_fields(): |
| | with pytest.raises(ValidationError): |
| | ConceptBreakdown( |
| | prerequisites=[] |
| | ) |
| | with pytest.raises(ValidationError): |
| | ConceptBreakdown(main_concept="Test") |
| |
|
| |
|
| | |
| | def test_card_generation_creation(): |
| | front = CardFront(question="What is a for loop?") |
| | back = CardBack(answer="A control flow statement", explanation="...", example="...") |
| | card = Card(front=front, back=back) |
| | cg = CardGeneration( |
| | concept="For Loops", |
| | thought_process="Break down the concept...", |
| | verification_steps=["Check for clarity"], |
| | card=card, |
| | ) |
| | assert cg.concept == "For Loops" |
| | assert cg.thought_process == "Break down the concept..." |
| | assert cg.verification_steps == ["Check for clarity"] |
| | assert cg.card.front.question == "What is a for loop?" |
| |
|
| |
|
| | def test_card_generation_missing_fields(): |
| | front = CardFront(question="Q") |
| | back = CardBack(answer="A", explanation="E", example="Ex") |
| | card = Card(front=front, back=back) |
| | with pytest.raises(ValidationError): |
| | CardGeneration( |
| | concept="Test", thought_process="Test", verification_steps=[] |
| | ) |
| | with pytest.raises(ValidationError): |
| | CardGeneration( |
| | concept="Test", thought_process="Test", card=card |
| | ) |
| |
|
| |
|
| | |
| | def test_learning_sequence_creation(): |
| | concept = ConceptBreakdown( |
| | main_concept="C", |
| | prerequisites=["P"], |
| | learning_outcomes=["L"], |
| | common_misconceptions=["M"], |
| | difficulty_level="D", |
| | ) |
| | front = CardFront(question="Q") |
| | back = CardBack(answer="A", explanation="E", example="Ex") |
| | card_obj = Card(front=front, back=back) |
| | card_gen = CardGeneration( |
| | concept="C", thought_process="T", verification_steps=["V"], card=card_obj |
| | ) |
| | ls = LearningSequence( |
| | topic="Advanced Python", |
| | concepts=[concept], |
| | cards=[card_gen], |
| | suggested_study_order=["C"], |
| | review_recommendations=["Review daily"], |
| | ) |
| | assert ls.topic == "Advanced Python" |
| | assert len(ls.concepts) == 1 |
| | assert ls.concepts[0].main_concept == "C" |
| | assert len(ls.cards) == 1 |
| | assert ls.cards[0].concept == "C" |
| | assert ls.suggested_study_order == ["C"] |
| | assert ls.review_recommendations == ["Review daily"] |
| |
|
| |
|
| | def test_learning_sequence_missing_fields(): |
| | with pytest.raises(ValidationError): |
| | LearningSequence(topic="Test") |
| |
|
| |
|
| | |
| | def test_crawled_page_creation(): |
| | page_data = { |
| | "url": "http://example.com/page1", |
| | "html_content": "<html><body><h1>Title</h1><p>Content</p></body></html>", |
| | "text_content": "Title Content", |
| | "title": "Example Title", |
| | "crawl_depth": 1, |
| | "parent_url": "http://example.com", |
| | } |
| | page = CrawledPage(**page_data) |
| | assert page.url == page_data["url"] |
| | assert page.html_content == page_data["html_content"] |
| | assert page.text_content == page_data["text_content"] |
| | assert page.title == page_data["title"] |
| | assert page.crawl_depth == page_data["crawl_depth"] |
| | assert page.parent_url == page_data["parent_url"] |
| |
|
| |
|
| | def test_crawled_page_defaults(): |
| | page_data = { |
| | "url": "http://example.com/page2", |
| | "html_content": "<html></html>", |
| | "text_content": "", |
| | } |
| | page = CrawledPage(**page_data) |
| | assert page.title is None |
| | assert page.crawl_depth == 0 |
| | assert page.parent_url is None |
| |
|
| |
|
| | def test_crawled_page_missing_required_fields(): |
| | with pytest.raises(ValidationError): |
| | CrawledPage(html_content="<html></html>", text_content="") |
| | with pytest.raises(ValidationError): |
| | CrawledPage(url="http://example.com", text_content="") |
| | with pytest.raises(ValidationError): |
| | CrawledPage( |
| | url="http://example.com", html_content="<html></html>" |
| | ) |
| |
|
| |
|
| | def test_crawled_page_serialization(): |
| | page_data = { |
| | "url": "http://example.com/page1", |
| | "html_content": "<html><body><h1>Title</h1><p>Content</p></body></html>", |
| | "text_content": "Title Content", |
| | "title": "Example Title", |
| | "crawl_depth": 1, |
| | "parent_url": "http://example.com", |
| | } |
| | page = CrawledPage(**page_data) |
| |
|
| | |
| | expected_data_for_dump = page_data.copy() |
| |
|
| | |
| | expected_data_for_dump.setdefault("meta_description", None) |
| | expected_data_for_dump.setdefault("meta_keywords", []) |
| |
|
| | |
| | dumped_model = page.model_dump() |
| |
|
| | |
| | |
| | if "last_crawled_at" in dumped_model: |
| | actual_last_crawled_at = dumped_model["last_crawled_at"] |
| | expected_data_for_dump["last_crawled_at"] = actual_last_crawled_at |
| | else: |
| | expected_data_for_dump.pop("last_crawled_at", None) |
| |
|
| | assert dumped_model == expected_data_for_dump |
| |
|
| |
|
| | def test_crawled_page_with_metadata(): |
| | page_data = { |
| | "url": "http://example.com/metadata_page", |
| | "html_content": "<html><body>Meta content</body></html>", |
| | "text_content": "Meta content", |
| | "title": "Metadata Test Page", |
| | "meta_description": "This is a test description.", |
| | "meta_keywords": ["test", "metadata", "example"], |
| | "crawl_depth": 0, |
| | } |
| | page = CrawledPage(**page_data) |
| | assert page.url == "http://example.com/metadata_page" |
| | assert page.title == "Metadata Test Page" |
| | assert page.meta_description == "This is a test description." |
| | assert page.meta_keywords == ["test", "metadata", "example"] |
| | assert page.crawl_depth == 0 |
| | assert page.parent_url is None |
| |
|
| |
|
| | |
| | def test_anki_card_data_creation(): |
| | card_data_dict = { |
| | "front": "What is PydanticAI?", |
| | "back": "An agent framework.", |
| | "tags": ["python", "ai"], |
| | "source_url": "http://example.com/pydantic-ai", |
| | "note_type": "Q&A", |
| | } |
| | card = AnkiCardData(**card_data_dict) |
| | assert card.front == card_data_dict["front"] |
| | assert card.back == card_data_dict["back"] |
| | assert card.tags == card_data_dict["tags"] |
| | assert card.source_url == card_data_dict["source_url"] |
| | assert card.note_type == card_data_dict["note_type"] |
| |
|
| |
|
| | def test_anki_card_data_defaults(): |
| | card_data_dict = {"front": "Question?", "back": "Answer."} |
| | card = AnkiCardData(**card_data_dict) |
| | assert card.tags == [] |
| | assert card.source_url is None |
| | assert card.note_type == "Basic" |
| |
|
| |
|
| | def test_anki_card_data_missing_required_fields(): |
| | with pytest.raises(ValidationError): |
| | AnkiCardData(back="Answer") |
| | with pytest.raises(ValidationError): |
| | AnkiCardData(front="Question") |
| |
|
| |
|
| | def test_anki_card_data_serialization(): |
| | card_data_dict = { |
| | "front": "What is PydanticAI?", |
| | "back": "An agent framework.", |
| | "tags": ["python", "ai"], |
| | "source_url": "http://example.com/pydantic-ai", |
| | "note_type": "Q&A", |
| | } |
| | card = AnkiCardData(**card_data_dict) |
| | |
| | |
| | expected_dump = card_data_dict.copy() |
| | if not expected_dump.get("tags"): |
| | expected_dump[ |
| | "tags" |
| | ] = [] |
| | assert card.model_dump() == expected_dump |
| |
|