feat: MCP-Tools für Fach-/Kompetenzverwaltung + Kompetenzzuordnung zu Lessons (Nutzer-Nachtrag)
Neue CompetencyTools.cs: create/update/delete_subject, create/update/delete_competency_domain, add/update/remove_competency_item, get_subjects, get_competency_catalog. delete_subject und delete_competency_domain als weitere Destructive-Ausnahmen neben delete_lesson (kein Papierkorb). Lesson bekommt ein neues Competencies-Feld (analog zu Unit.Competencies) sowie add_lesson_competency/remove_lesson_competency in LessonPlanTools.cs. ICompetencyDomainRepository um GetBySubject() erweitert, um den Katalog eines Fachs ohne bekannte Klassenstufe abzufragen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,9 +15,9 @@ public sealed class McpToolsTests
|
||||
Assert.Equal(
|
||||
new[]
|
||||
{
|
||||
"download_lesson_attachment", "get_exams", "get_grades", "get_lesson_plans",
|
||||
"get_schedule", "get_students", "get_time_entries", "list_letter_templates",
|
||||
"render_letter",
|
||||
"download_lesson_attachment", "get_competency_catalog", "get_exams", "get_grades",
|
||||
"get_lesson_plans", "get_schedule", "get_students", "get_subjects", "get_time_entries",
|
||||
"list_letter_templates", "render_letter",
|
||||
},
|
||||
McpToolScope.AllowedReadTools.OrderBy(n => n, StringComparer.Ordinal));
|
||||
}
|
||||
@@ -28,17 +28,22 @@ public sealed class McpToolsTests
|
||||
Assert.Equal(
|
||||
new[]
|
||||
{
|
||||
"add_lesson_attachment", "add_lesson_phase", "create_grade_entry", "create_lesson",
|
||||
"create_time_entry", "create_unit", "move_lesson", "remove_lesson_phase",
|
||||
"update_lesson", "update_lesson_phase", "update_student_group_assignment", "update_unit",
|
||||
"add_competency_item", "add_lesson_attachment", "add_lesson_competency", "add_lesson_phase",
|
||||
"create_competency_domain", "create_grade_entry", "create_lesson", "create_subject",
|
||||
"create_time_entry", "create_unit", "move_lesson", "remove_competency_item",
|
||||
"remove_lesson_competency", "remove_lesson_phase", "update_competency_domain",
|
||||
"update_competency_item", "update_lesson", "update_lesson_phase", "update_student_group_assignment",
|
||||
"update_subject", "update_unit",
|
||||
},
|
||||
McpToolScope.AllowedWriteTools.OrderBy(n => n, StringComparer.Ordinal));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AllowedDestructiveWriteTools_EnthaeltNurDeleteLesson()
|
||||
public void AllowedDestructiveWriteTools_EnthaeltDeleteLessonSubjectUndCompetencyDomain()
|
||||
{
|
||||
Assert.Equal(["delete_lesson"], McpToolScope.AllowedDestructiveWriteTools);
|
||||
Assert.Equal(
|
||||
new[] { "delete_competency_domain", "delete_lesson", "delete_subject" },
|
||||
McpToolScope.AllowedDestructiveWriteTools.OrderBy(n => n, StringComparer.Ordinal));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -639,4 +644,237 @@ public sealed class McpToolsTests
|
||||
Assert.False(result.Applied);
|
||||
Assert.Equal(0, confirmation.CallCount);
|
||||
}
|
||||
|
||||
// ── LessonPlanTools — Kompetenzzuordnung ────────────────────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public async Task AddLessonCompetency_NutzerBestaetigt_OrdnetCodeZu()
|
||||
{
|
||||
var lesson = new Lesson { Topic = "Brechung" };
|
||||
var lessons = new FakeLessons();
|
||||
lessons.Add(lesson);
|
||||
var tool = BuildLessonPlanTools(lessons: lessons);
|
||||
|
||||
var result = await tool.AddLessonCompetency(lesson.Id, "PH.9.1");
|
||||
|
||||
Assert.True(result.Applied);
|
||||
Assert.Contains("PH.9.1", lessons.GetById(lesson.Id)!.Competencies);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddLessonCompetency_BereitsZugeordnet_FragtNichtNochmalNach()
|
||||
{
|
||||
var lesson = new Lesson { Topic = "Brechung" };
|
||||
lesson.Competencies.Add("PH.9.1");
|
||||
var lessons = new FakeLessons();
|
||||
lessons.Add(lesson);
|
||||
var confirmation = new FakeMcpConfirmation();
|
||||
var tool = BuildLessonPlanTools(lessons: lessons, confirmation: confirmation);
|
||||
|
||||
var result = await tool.AddLessonCompetency(lesson.Id, "PH.9.1");
|
||||
|
||||
Assert.True(result.Applied);
|
||||
Assert.Equal(0, confirmation.CallCount);
|
||||
Assert.Single(lessons.GetById(lesson.Id)!.Competencies);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RemoveLessonCompetency_NutzerBestaetigt_EntferntCode()
|
||||
{
|
||||
var lesson = new Lesson { Topic = "Brechung" };
|
||||
lesson.Competencies.Add("PH.9.1");
|
||||
var lessons = new FakeLessons();
|
||||
lessons.Add(lesson);
|
||||
var tool = BuildLessonPlanTools(lessons: lessons);
|
||||
|
||||
var result = await tool.RemoveLessonCompetency(lesson.Id, "PH.9.1");
|
||||
|
||||
Assert.True(result.Applied);
|
||||
Assert.Empty(lessons.GetById(lesson.Id)!.Competencies);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RemoveLessonCompetency_NichtZugeordnet_LiefertFehlerOhneNachfrage()
|
||||
{
|
||||
var lesson = new Lesson { Topic = "Brechung" };
|
||||
var lessons = new FakeLessons();
|
||||
lessons.Add(lesson);
|
||||
var confirmation = new FakeMcpConfirmation();
|
||||
var tool = BuildLessonPlanTools(lessons: lessons, confirmation: confirmation);
|
||||
|
||||
var result = await tool.RemoveLessonCompetency(lesson.Id, "PH.9.1");
|
||||
|
||||
Assert.False(result.Applied);
|
||||
Assert.Equal(0, confirmation.CallCount);
|
||||
}
|
||||
|
||||
// ── CompetencyTools ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public void GetSubjects_LiefertAlleFaecher()
|
||||
{
|
||||
var subject = new Subject { Name = "Mathematik", ShortName = "Ma" };
|
||||
var tool = new CompetencyTools(new FakeSubjects([subject]), new FakeCompetencyDomains(), new FakeMcpConfirmation());
|
||||
|
||||
var dto = Assert.Single(tool.GetSubjects());
|
||||
|
||||
Assert.Equal("Mathematik", dto.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateSubject_NutzerBestaetigt_SpeichertFach()
|
||||
{
|
||||
var subjects = new FakeSubjects([]);
|
||||
var tool = new CompetencyTools(subjects, new FakeCompetencyDomains(), new FakeMcpConfirmation());
|
||||
|
||||
var result = await tool.CreateSubject("Mathematik", "Ma");
|
||||
|
||||
Assert.True(result.Applied);
|
||||
var subject = Assert.Single(subjects.GetAll());
|
||||
Assert.Equal("Mathematik", subject.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateSubject_NameBereitsVergeben_LiefertFehler()
|
||||
{
|
||||
var subjects = new FakeSubjects([new Subject { Name = "Mathematik" }]);
|
||||
var tool = new CompetencyTools(subjects, new FakeCompetencyDomains(), new FakeMcpConfirmation());
|
||||
|
||||
var result = await tool.CreateSubject("Mathematik");
|
||||
|
||||
Assert.False(result.Applied);
|
||||
Assert.Single(subjects.GetAll());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateSubject_AendertNurAngegebeneFelder()
|
||||
{
|
||||
var subject = new Subject { Name = "Mathematik", ShortName = "Ma" };
|
||||
var subjects = new FakeSubjects([subject]);
|
||||
var tool = new CompetencyTools(subjects, new FakeCompetencyDomains(), new FakeMcpConfirmation());
|
||||
|
||||
var result = await tool.UpdateSubject(subject.Id, shortName: "M");
|
||||
|
||||
Assert.True(result.Applied);
|
||||
var updated = subjects.GetById(subject.Id)!;
|
||||
Assert.Equal("Mathematik", updated.Name);
|
||||
Assert.Equal("M", updated.ShortName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteSubject_NutzerBestaetigt_LoeschtFach()
|
||||
{
|
||||
var subject = new Subject { Name = "Mathematik" };
|
||||
var subjects = new FakeSubjects([subject]);
|
||||
var tool = new CompetencyTools(subjects, new FakeCompetencyDomains(), new FakeMcpConfirmation());
|
||||
|
||||
var result = await tool.DeleteSubject(subject.Id);
|
||||
|
||||
Assert.True(result.Applied);
|
||||
Assert.Empty(subjects.GetAll());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteSubject_UnbekannteId_LiefertFehlerOhneNachfrage()
|
||||
{
|
||||
var confirmation = new FakeMcpConfirmation();
|
||||
var tool = new CompetencyTools(new FakeSubjects([]), new FakeCompetencyDomains(), confirmation);
|
||||
|
||||
var result = await tool.DeleteSubject(Guid.NewGuid());
|
||||
|
||||
Assert.False(result.Applied);
|
||||
Assert.Equal(0, confirmation.CallCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetCompetencyCatalog_OhneKlassenstufe_LiefertAlleKlassenstufenDesFachs()
|
||||
{
|
||||
var subjectId = Guid.NewGuid();
|
||||
var domains = new FakeCompetencyDomains();
|
||||
domains.Add(new CompetencyDomain { SubjectId = subjectId, GradeLevel = 5, Name = "Zahlen" });
|
||||
domains.Add(new CompetencyDomain { SubjectId = subjectId, GradeLevel = 9, Name = "Funktionen" });
|
||||
var tool = new CompetencyTools(new FakeSubjects([]), domains, new FakeMcpConfirmation());
|
||||
|
||||
var result = tool.GetCompetencyCatalog(subjectId);
|
||||
|
||||
Assert.Equal(2, result.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateCompetencyDomain_NutzerBestaetigt_SpeichertBereich()
|
||||
{
|
||||
var subject = new Subject { Name = "Mathematik" };
|
||||
var domains = new FakeCompetencyDomains();
|
||||
var tool = new CompetencyTools(new FakeSubjects([subject]), domains, new FakeMcpConfirmation());
|
||||
|
||||
var result = await tool.CreateCompetencyDomain(subject.Id, 9, "Funktionen");
|
||||
|
||||
Assert.True(result.Applied);
|
||||
var domain = Assert.Single(domains.GetBySubjectAndGrade(subject.Id, 9));
|
||||
Assert.Equal("Funktionen", domain.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteCompetencyDomain_NutzerBestaetigt_LoeschtBereichMitItems()
|
||||
{
|
||||
var domain = new CompetencyDomain { Name = "Funktionen" };
|
||||
domain.Items.Add(new CompetencyItem { Code = "M.9.1", Description = "..." });
|
||||
var domains = new FakeCompetencyDomains();
|
||||
domains.Add(domain);
|
||||
var tool = new CompetencyTools(new FakeSubjects([]), domains, new FakeMcpConfirmation());
|
||||
|
||||
var result = await tool.DeleteCompetencyDomain(domain.Id);
|
||||
|
||||
Assert.True(result.Applied);
|
||||
Assert.Null(domains.GetById(domain.Id));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddCompetencyItem_NutzerBestaetigt_HaengtItemAn()
|
||||
{
|
||||
var domain = new CompetencyDomain { Name = "Funktionen" };
|
||||
var domains = new FakeCompetencyDomains();
|
||||
domains.Add(domain);
|
||||
var tool = new CompetencyTools(new FakeSubjects([]), domains, new FakeMcpConfirmation());
|
||||
|
||||
var result = await tool.AddCompetencyItem(domain.Id, "M.9.1", "lineare Funktionen erkennen");
|
||||
|
||||
Assert.True(result.Applied);
|
||||
var item = Assert.Single(domains.GetById(domain.Id)!.Items);
|
||||
Assert.Equal("M.9.1", item.Code);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateCompetencyItem_AendertNurAngegebeneFelder()
|
||||
{
|
||||
var item = new CompetencyItem { Code = "M.9.1", Description = "alt" };
|
||||
var domain = new CompetencyDomain { Name = "Funktionen" };
|
||||
domain.Items.Add(item);
|
||||
var domains = new FakeCompetencyDomains();
|
||||
domains.Add(domain);
|
||||
var tool = new CompetencyTools(new FakeSubjects([]), domains, new FakeMcpConfirmation());
|
||||
|
||||
var result = await tool.UpdateCompetencyItem(domain.Id, item.Id, description: "neu");
|
||||
|
||||
Assert.True(result.Applied);
|
||||
var updated = domains.GetById(domain.Id)!.Items.Single();
|
||||
Assert.Equal("M.9.1", updated.Code);
|
||||
Assert.Equal("neu", updated.Description);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RemoveCompetencyItem_NutzerBestaetigt_EntferntItem()
|
||||
{
|
||||
var item = new CompetencyItem { Code = "M.9.1", Description = "..." };
|
||||
var domain = new CompetencyDomain { Name = "Funktionen" };
|
||||
domain.Items.Add(item);
|
||||
var domains = new FakeCompetencyDomains();
|
||||
domains.Add(domain);
|
||||
var tool = new CompetencyTools(new FakeSubjects([]), domains, new FakeMcpConfirmation());
|
||||
|
||||
var result = await tool.RemoveCompetencyItem(domain.Id, item.Id);
|
||||
|
||||
Assert.True(result.Applied);
|
||||
Assert.Empty(domains.GetById(domain.Id)!.Items);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user