A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.
This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).
Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
195 lines
6.2 KiB
Python
195 lines
6.2 KiB
Python
"""
|
|
Tests for Learning Generator Service
|
|
"""
|
|
import pytest
|
|
from unittest.mock import patch, MagicMock, AsyncMock
|
|
import json
|
|
|
|
import sys
|
|
sys.path.insert(0, '/app')
|
|
|
|
from services.learning_generator import LearningGeneratorService
|
|
from models.learning_node import LearningTheme, NodeType
|
|
|
|
|
|
class TestLearningGeneratorService:
|
|
"""Tests for Learning Generator Service."""
|
|
|
|
@pytest.fixture
|
|
def service(self):
|
|
"""Create service instance."""
|
|
return LearningGeneratorService()
|
|
|
|
def test_build_generation_prompt_topographie(self, service):
|
|
"""Test prompt building for topography theme."""
|
|
aoi_info = {
|
|
"bounds": {"west": 9.18, "south": 47.70, "east": 9.20, "north": 47.72},
|
|
"center": {"latitude": 47.71, "longitude": 9.19},
|
|
"area_km2": 0.5,
|
|
}
|
|
|
|
prompt = service._build_generation_prompt(
|
|
aoi_info=aoi_info,
|
|
theme=LearningTheme.TOPOGRAPHIE,
|
|
difficulty="mittel",
|
|
node_count=5,
|
|
grade_level="7-8",
|
|
language="de",
|
|
)
|
|
|
|
assert "Topographie" in prompt or "topographie" in prompt
|
|
assert "47.71" in prompt # center latitude
|
|
assert "9.19" in prompt # center longitude
|
|
assert "5" in prompt # node count
|
|
assert "mittel" in prompt # difficulty
|
|
assert "7-8" in prompt # grade level
|
|
assert "JSON" in prompt
|
|
|
|
def test_build_generation_prompt_landnutzung(self, service):
|
|
"""Test prompt building for land use theme."""
|
|
aoi_info = {
|
|
"bounds": {"west": 9.18, "south": 47.70, "east": 9.20, "north": 47.72},
|
|
"center": {"latitude": 47.71, "longitude": 9.19},
|
|
"area_km2": 0.5,
|
|
}
|
|
|
|
prompt = service._build_generation_prompt(
|
|
aoi_info=aoi_info,
|
|
theme=LearningTheme.LANDNUTZUNG,
|
|
difficulty="leicht",
|
|
node_count=3,
|
|
grade_level=None,
|
|
language="de",
|
|
)
|
|
|
|
assert "Landnutzung" in prompt or "landnutzung" in prompt
|
|
|
|
def test_parse_llm_response_valid(self, service):
|
|
"""Test parsing valid LLM response."""
|
|
response = """
|
|
Here are the learning nodes:
|
|
[
|
|
{
|
|
"title": "Höhenbestimmung",
|
|
"position": {"latitude": 47.71, "longitude": 9.19},
|
|
"question": "Schätze die Höhe dieses Punktes.",
|
|
"hints": ["Schau auf die Vegetation", "Vergleiche mit dem See"],
|
|
"answer": "Ca. 500m",
|
|
"explanation": "Die Höhe lässt sich...",
|
|
"node_type": "question",
|
|
"points": 10
|
|
}
|
|
]
|
|
"""
|
|
|
|
nodes = service._parse_llm_response(
|
|
response, "test-aoi-id", LearningTheme.TOPOGRAPHIE
|
|
)
|
|
|
|
assert len(nodes) == 1
|
|
assert nodes[0].title == "Höhenbestimmung"
|
|
assert nodes[0].aoi_id == "test-aoi-id"
|
|
assert nodes[0].theme == LearningTheme.TOPOGRAPHIE
|
|
assert nodes[0].points == 10
|
|
|
|
def test_parse_llm_response_invalid_json(self, service):
|
|
"""Test parsing invalid LLM response."""
|
|
response = "This is not valid JSON"
|
|
|
|
nodes = service._parse_llm_response(
|
|
response, "test-aoi-id", LearningTheme.TOPOGRAPHIE
|
|
)
|
|
|
|
assert len(nodes) == 0
|
|
|
|
def test_generate_mock_nodes(self, service):
|
|
"""Test mock node generation."""
|
|
nodes = service._generate_mock_nodes(
|
|
aoi_id="test-aoi-id",
|
|
theme=LearningTheme.TOPOGRAPHIE,
|
|
difficulty="mittel",
|
|
node_count=3,
|
|
)
|
|
|
|
assert len(nodes) == 3
|
|
for node in nodes:
|
|
assert node.aoi_id == "test-aoi-id"
|
|
assert node.theme == LearningTheme.TOPOGRAPHIE
|
|
assert node.approved == False
|
|
assert len(node.hints) > 0
|
|
|
|
def test_generate_mock_nodes_orientierung(self, service):
|
|
"""Test mock node generation for orientation theme."""
|
|
nodes = service._generate_mock_nodes(
|
|
aoi_id="test-aoi-id",
|
|
theme=LearningTheme.ORIENTIERUNG,
|
|
difficulty="leicht",
|
|
node_count=2,
|
|
)
|
|
|
|
assert len(nodes) == 2
|
|
for node in nodes:
|
|
assert node.theme == LearningTheme.ORIENTIERUNG
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_nodes_for_aoi_empty(self, service):
|
|
"""Test getting nodes for non-existent AOI."""
|
|
nodes = await service.get_nodes_for_aoi("nonexistent-aoi")
|
|
assert nodes is None
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_statistics_empty(self, service):
|
|
"""Test statistics with no data."""
|
|
stats = await service.get_statistics()
|
|
assert stats["total_nodes"] == 0
|
|
assert stats["by_theme"] == {}
|
|
|
|
|
|
class TestLearningNodeModel:
|
|
"""Tests for Learning Node model."""
|
|
|
|
def test_learning_theme_values(self):
|
|
"""Test all theme enum values exist."""
|
|
themes = [
|
|
LearningTheme.TOPOGRAPHIE,
|
|
LearningTheme.LANDNUTZUNG,
|
|
LearningTheme.ORIENTIERUNG,
|
|
LearningTheme.GEOLOGIE,
|
|
LearningTheme.HYDROLOGIE,
|
|
LearningTheme.VEGETATION,
|
|
]
|
|
assert len(themes) == 6
|
|
|
|
def test_node_type_values(self):
|
|
"""Test all node type enum values exist."""
|
|
types = [
|
|
NodeType.QUESTION,
|
|
NodeType.OBSERVATION,
|
|
NodeType.EXPLORATION,
|
|
]
|
|
assert len(types) == 3
|
|
|
|
def test_create_learning_node(self):
|
|
"""Test creating a learning node."""
|
|
from models.learning_node import LearningNode
|
|
|
|
node = LearningNode(
|
|
id="test-id",
|
|
aoi_id="test-aoi",
|
|
title="Test Station",
|
|
theme=LearningTheme.TOPOGRAPHIE,
|
|
position={"latitude": 47.71, "longitude": 9.19},
|
|
question="Test question?",
|
|
hints=["Hint 1", "Hint 2"],
|
|
answer="Test answer",
|
|
explanation="Test explanation",
|
|
node_type=NodeType.QUESTION,
|
|
points=10,
|
|
approved=False,
|
|
)
|
|
|
|
assert node.id == "test-id"
|
|
assert node.title == "Test Station"
|
|
assert node.points == 10
|
|
assert len(node.hints) == 2
|