skill-kanten-generator: Deploy-Skript für automatische Skill-Verknüpfung
This commit is contained in:
@@ -0,0 +1,239 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user