CI / build-and-test (push) Canceled after 0s
- UntisHubService.RecordRun: ein abgeschlossener Langzeit-Fehlzeitenabgleich schliesst die kurzfristige Kadenz derselben Gruppe automatisch mit ab (nicht umgekehrt). - Fehlzeitenabgleich-Dialog: neue "Uebernahme als"-ComboBox statt starrem Zielstatus, vorbelegt mit dem berechneten Vorschlag, aber frei aenderbar. - Neuer ai-backend-Endpunkt untis-status.php + AiPlanningService.RequestUntisStatusSuggestionsAsync: gebuendelter, anonymisierter KI-Statusvorschlag (nur Positions-Id + Rohsignale, nie Name/Klasse/ Datum), mit hartem Id-Mengen-Abgleich gegen Verwechslung. - Neue MCP-Tools (UntisComparisonTools): get_untis_hub_status, get_untis_absence_rows/ apply_untis_absence_status (anonymer Weg ueber ENr-Zuordnung) sowie get_named_untis_absence_pattern als bewusste, eng begrenzte Ausnahme (Name+Fehlzeiten fuer explizit angegebene Schueler-IDs, mit Bestaetigung ohne Sitzungsfreigabe - dafuer IMcpConfirmationService.ConfirmAsync um allowSessionTrust erweitert). - MapStatus/ENr-Zuordnung aus dem ViewModel in das neue, geteilte UntisLessonAbsenceHelper gezogen, damit Dialog und MCP-Tool nie unterschiedliche Statusvorschlaege berechnen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
73 lines
3.2 KiB
C#
73 lines
3.2 KiB
C#
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<LearningGroup>? groups = null, List<Student>? students = null,
|
|
List<ParticipationSession>? 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
|
|
}
|
|
}
|