using LehrerApp.Core.Models; using LehrerApp.Desktop.Services; using LehrerApp.Desktop.Services.Mcp.Tools; using Xunit; namespace LehrerApp.Desktop.Tests; /// Aus Konsistenz mit den drei bestehenden WebUntis-Abgleichs-ViewModels bewusst ohne Tests für /// GetUntisAbsenceRows/GetNamedUntisAbsencePattern gelassen (siehe TODO.md) - beide rufen /// WebUntisIntegrationService.GetLessonAbsencesAsync auf, was einen echten WebUntis-JSON-RPC- /// Handshake voraussetzt. Getestet werden die Teile, die keinen WebUntis-Zugriff brauchen: /// GetUntisHubStatus (reine Delegation an UntisHubService) und die Validierungspfade von /// ApplyUntisAbsenceStatus, die vor jedem WebUntis-/Datenbankzugriff greifen. public sealed class UntisComparisonToolsTests { private static UntisComparisonTools Build( List? groups = null, List? students = null, List? sessions = null, FakeMcpConfirmation? confirmation = null) => new(new FakeGroups(groups ?? []), new FakeStudents(students ?? []), new FakeSessions(sessions ?? []), new FakeEntries(), TestSupport.BuildWebUntisIntegrationService(), TestSupport.BuildUntisHubService(groups ?? []), confirmation ?? new FakeMcpConfirmation()); [Fact] public void GetUntisHubStatus_DelegiertAnUntisHubServiceUndMapptKindUndDueStateAlsText() { var group = new LearningGroup { Name = "9a", WebUntisLessonId = 42 }; var tool = Build([group]); var rows = tool.GetUntisHubStatus(); Assert.Contains(rows, r => r.Kind == nameof(UntisHubJobKind.FehlzeitenKurz) && r.GroupName == "9a" && r.DueState == nameof(UntisHubDueState.Overdue)); Assert.Contains(rows, r => r.Kind == nameof(UntisHubJobKind.OffenePeriods) && r.GroupName == "Offene Stunden"); } [Fact] public async Task ApplyUntisAbsenceStatus_UnbekannterStatus_WirdOhneRowIdPruefungAbgelehnt() { var tool = Build(); var result = await tool.ApplyUntisAbsenceStatus("irgendeine-id", "Suspendiert"); Assert.False(result.Applied); Assert.Contains("Status", result.Message); } [Fact] public async Task ApplyUntisAbsenceStatus_NichtSelektierbarerAberGueltigerEnumWert_WirdAbgelehnt() { // "Truant"/"Suspended" etc. sind gültige AttendanceStatus-Werte, aber keine, die WebUntis // hier je melden würde (siehe SelectableStatuses) - müssen trotzdem abgelehnt werden, damit // ein KI-Client nicht versehentlich einen fachlich unpassenden Status setzen kann. var tool = Build(); var result = await tool.ApplyUntisAbsenceStatus("irgendeine-id", nameof(AttendanceStatus.Truant)); Assert.False(result.Applied); } [Fact] public async Task ApplyUntisAbsenceStatus_UnbekannteRowId_WirdAbgelehntOhneBestaetigung() { var confirmation = new FakeMcpConfirmation(); var tool = Build(confirmation: confirmation); var result = await tool.ApplyUntisAbsenceStatus("nie-vergeben", nameof(AttendanceStatus.Excused)); Assert.False(result.Applied); Assert.Contains("row-id", result.Message); Assert.Equal(0, confirmation.CallCount); // erst gar nicht bis zur Bestätigung vorgedrungen } }