fix: Restore all files lost during destructive rebase
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>
This commit is contained in:
194
geo-service/tests/test_learning.py
Normal file
194
geo-service/tests/test_learning.py
Normal file
@@ -0,0 +1,194 @@
|
||||
"""
|
||||
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
|
||||
Reference in New Issue
Block a user