| import copy |
|
|
| import pytest |
| from pydantic import ValidationError |
|
|
| from app.schemas.requests import GenerateRoadmapRequest |
| from app.schemas.roadmap import Roadmap |
| from app.schemas.roadmap import ResourceType |
| from app.services.id_generator import assign_backend_ids |
|
|
|
|
| def valid_payload(): |
| resource_a = { |
| "title": "Python for Beginners", |
| "url": "https://www.youtube.com/watch?v=kqtD5dpn9C8", |
| "type": "youtube_video", |
| "source": "YouTube", |
| "is_free": True, |
| } |
| resource_b = { |
| "title": "Python Tutorial", |
| "url": "https://www.w3schools.com/python/", |
| "type": "article", |
| "source": "Tavily", |
| "is_free": True, |
| } |
| phases = [] |
| for phase_index in range(1, 4): |
| nodes = [] |
| for node_index in range(1, 4): |
| nodes.append( |
| { |
| "title": f"Node {node_index}", |
| "description": "Learn the core idea with simple examples and short practice tasks.", |
| "estimated_completion_time": "2-3 hours", |
| "resources": [resource_a, resource_b], |
| } |
| ) |
| phases.append( |
| { |
| "title": f"Phase {phase_index}", |
| "level": "beginner", |
| "goal": "Build practical beginner confidence.", |
| "estimated_duration": "2 weeks", |
| "nodes": nodes, |
| "project": { |
| "title": f"Project {phase_index}", |
| "brief": "Build a small practical project that demonstrates the phase skills.", |
| "tools_needed": ["Browser"], |
| "resources": [resource_a, resource_b], |
| }, |
| } |
| ) |
| payload = { |
| "skill": "Python for Beginners", |
| "overview": "A beginner roadmap with practical examples and projects for learning step by step.", |
| "estimated_total_duration": "6-8 weeks", |
| "phases": phases, |
| "metadata": {"model_used": "test", "resource_sources": ["test"], "cached": False}, |
| } |
| return assign_backend_ids(payload, "python") |
|
|
|
|
| def test_valid_roadmap_passes(): |
| roadmap = Roadmap.model_validate(valid_payload()) |
| assert len(roadmap.phases) == 3 |
| assert len(roadmap.phases[0].nodes) == 3 |
|
|
|
|
| def test_fewer_than_three_phases_fails(): |
| payload = valid_payload() |
| payload["phases"] = payload["phases"][:2] |
| payload["projects"] = payload["projects"][:2] |
| with pytest.raises(ValidationError): |
| Roadmap.model_validate(payload) |
|
|
|
|
| def test_node_with_fewer_than_two_resources_fails(): |
| payload = valid_payload() |
| payload["phases"][0]["nodes"][0]["resources"] = payload["phases"][0]["nodes"][0]["resources"][:1] |
| with pytest.raises(ValidationError): |
| Roadmap.model_validate(payload) |
|
|
|
|
| def test_paid_resource_with_is_free_false_fails(): |
| payload = valid_payload() |
| payload["phases"][0]["nodes"][0]["resources"][0]["is_free"] = False |
| with pytest.raises(ValidationError): |
| Roadmap.model_validate(payload) |
|
|
|
|
| def test_invalid_resource_type_fails(): |
| payload = copy.deepcopy(valid_payload()) |
| payload["phases"][0]["nodes"][0]["resources"][0]["type"] = "paid_course" |
| with pytest.raises(ValidationError): |
| Roadmap.model_validate(payload) |
|
|
|
|
| def test_request_defaults_include_all_resource_types(): |
| request = GenerateRoadmapRequest(skill="Learn Python") |
| assert request.preferred_resource_types == list(ResourceType) |
|
|