#!/usr/bin/env python3 """ test_skill_kanten_generator.py Unit-Tests für skill-kanten-generator.py """ import json import sys import tempfile import unittest from pathlib import Path # Füge das aktuelle Verzeichnis zum Python-Pfad hinzu sys.path.insert(0, str(Path(__file__).parent)) from skill_kanten_generator import ( Edge, SkillInfo, deduplicate_edges, generate_metadata_edges, build_skill_index, generate_journey_graph, ) class TestEdge(unittest.TestCase): """Tests für die Edge-Datenklasse.""" def test_key_ungereicht(self): """Die key()-Methode sollte ungerichtet sein.""" edge1 = Edge("a", "b", 0.5, "category", "test") edge2 = Edge("b", "a", 0.5, "category", "test") self.assertEqual(edge1.key(), edge2.key()) def test_to_dict(self): """to_dict() sollte das richtige Format liefern.""" edge = Edge("a", "b", 0.9, "declared", "manual") result = edge.to_dict() self.assertEqual(result["skill_a"], "a") self.assertEqual(result["skill_b"], "b") self.assertEqual(result["weight"], 0.9) self.assertEqual(result["type"], "declared") self.assertEqual(result["source"], "manual") class TestDeduplicateEdges(unittest.TestCase): """Tests für die Deduplizierung.""" def test_keine_doppelt(self): """Keine Doppelte: gleiche Anzahl zurück.""" edges = [ Edge("a", "b", 0.5, "category", "test"), Edge("c", "d", 0.7, "manual", "test"), ] result = deduplicate_edges(edges) self.assertEqual(len(result), 2) def test_doppelt_hoehere_prioritaet(self): """Bei Doppelt mit gleichen Skills: höhere Priorität gewinnt.""" edges = [ Edge("a", "b", 0.5, "category", "test"), # category Edge("a", "b", 0.9, "declared", "test"), # declared (höher) ] result = deduplicate_edges(edges) self.assertEqual(len(result), 1) self.assertEqual(result[0].edge_type, "declared") def test_doppelt_hoeheres_gewicht(self): """Bei gleicher Priorität: höheres Gewicht gewinnt.""" edges = [ Edge("a", "b", 0.5, "category", "test"), Edge("a", "b", 0.7, "category", "test"), ] result = deduplicate_edges(edges) self.assertEqual(len(result), 1) self.assertEqual(result[0].weight, 0.7) class TestBuildSkillIndex(unittest.TestCase): """Tests für build_skill_index().""" def test_leere_index(self): """Mit leerem usage.json: leerer Index.""" with tempfile.TemporaryDirectory() as tmpdir: skills_dir = Path(tmpdir) / "skills" skills_dir.mkdir() (skills_dir / ".usage.json").write_text("{}") # Mock-Path import skill_kanten_generator as sg original = sg.SKILLS_DIR sg.SKILLS_DIR = skills_dir sg.USAGE_FILE = skills_dir / ".usage.json" try: index = build_skill_index() self.assertEqual(len(index), 0) finally: sg.SKILLS_DIR = original sg.USAGE_FILE = original / ".usage.json" def test_index_erstellt(self): """Ein Skill aus .usage.json sollte im Index erscheinen.""" with tempfile.TemporaryDirectory() as tmpdir: skills_dir = Path(tmpdir) / "skills" skills_dir.mkdir() usage = { "my-skill": { "use_count": 10, "state": "active", } } (skills_dir / ".usage.json").write_text(json.dumps(usage)) import skill_kanten_generator as sg original = sg.SKILLS_DIR sg.SKILLS_DIR = skills_dir sg.USAGE_FILE = skills_dir / ".usage.json" try: index = build_skill_index() self.assertIn("my-skill", index) self.assertEqual(index["my-skill"].use_count, 10) finally: sg.SKILLS_DIR = original sg.USAGE_FILE = original / ".usage.json" class TestGenerateMetadataEdges(unittest.TestCase): """Tests für generate_metadata_edges().""" def test_gleiche_kategorie(self): """Skills mit gleicher Kategorie erhalten eine Kante.""" skills = { "skill-a": SkillInfo(name="skill-a", category="test"), "skill-b": SkillInfo(name="skill-b", category="test"), } edges = generate_metadata_edges(skills) self.assertEqual(len(edges), 1) self.assertEqual(edges[0].edge_type, "category") self.assertEqual(edges[0].weight, 0.7) def test_different_categories_no_edge(self): """Skills mit verschiedenen Kategorien: keine Kante.""" skills = { "skill-a": SkillInfo(name="skill-a", category="cat-a"), "skill-b": SkillInfo(name="skill-b", category="cat-b"), } edges = generate_metadata_edges(skills) self.assertEqual(len(edges), 0) def test_related_skills(self): """Skills mit related_skills-Verbindung erhalten eine Kante.""" skills = { "skill-a": SkillInfo( name="skill-a", related_skills=["skill-b"], ), "skill-b": SkillInfo(name="skill-b"), } edges = generate_metadata_edges(skills) self.assertEqual(len(edges), 1) self.assertEqual(edges[0].edge_type, "declared") self.assertEqual(edges[0].skill_a, "skill-a") self.assertEqual(edges[0].skill_b, "skill-b") class TestFullWorkflow(unittest.TestCase): """Integrationstest für den kompletten Workflow.""" def test_journey_graph_generated(self): """Der komplette Workflow sollte einen Graphen erzeugen.""" with tempfile.TemporaryDirectory() as tmpdir: hermes_dir = Path(tmpdir) / "hermes" / "profiles" / "werkstatt" skills_dir = hermes_dir / "skills" skills_dir.mkdir(parents=True) # .usage.json usage = { "orchestrator": { "use_count": 10, "state": "active", }, "wartung": { "use_count": 7, "state": "active", }, } (skills_dir / ".usage.json").write_text(json.dumps(usage)) # SKILL.md mit gleicher Kategorie (skills_dir / "orchestrator").mkdir() (skills_dir / "orchestrator" / "SKILL.md").write_text( "---\n" "name: orchestrator\n" "category: devops\n" "---\n" "# Orchestrator\n" ) (skills_dir / "wartung").mkdir() (skills_dir / "wartung" / "SKILL.md").write_text( "---\n" "name: wartung\n" "category: devops\n" "---\n" "# Wartung\n" ) import skill_kanten_generator as sg original = sg.SKILLS_DIR sg.SKILLS_DIR = skills_dir sg.USAGE_FILE = skills_dir / ".usage.json" try: graph = generate_journey_graph() # Prüfe Struktur self.assertIn("nodes", graph) self.assertIn("edges", graph) self.assertIn("generated_at", graph) # Prüfe Knotenanzahl self.assertEqual(len(graph["nodes"]), 2) # Prüfe Kante (beide in gleicher Kategorie) edge_types = [e["type"] for e in graph["edges"]] self.assertIn("category", edge_types) finally: sg.SKILLS_DIR = original sg.USAGE_FILE = original / ".usage.json" if __name__ == "__main__": unittest.main()