WIP (unstable): WebUntis-iCal-Abgleich für Vertretungen/Ausfälle
Erkennt Vertretungen, Ausfälle und Zusatzaufsichten aus dem persönlichen WebUntis-iCal-Feed und schreibt sie automatisch als SubstitutionEntry. Bekannter offener Bug: es tauchen weiterhin falsche Vertretungen für Stunden auf, die real unverändert sind — wird in einem Folge-Commit untersucht, deshalb vorerst auf diesem Branch statt main. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
using LehrerApp.Core.Models;
|
||||
using LehrerApp.Core.Services;
|
||||
using LehrerApp.Desktop.Services;
|
||||
using LehrerApp.Desktop.ViewModels.Planning;
|
||||
using Xunit;
|
||||
|
||||
namespace LehrerApp.Desktop.Tests;
|
||||
|
||||
public sealed class UntisMappingReviewDialogViewModelTests
|
||||
{
|
||||
private static string BuildTempPath()
|
||||
{
|
||||
var path = Path.Combine(Path.GetTempPath(), $"lehrerapp-untisreview-tests-{Guid.NewGuid():N}");
|
||||
Directory.CreateDirectory(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
private static UntisSyncService BuildUntisSyncService(FakeUntisSlotMappings mappings) => new(
|
||||
new HttpClient(), new WebUntisSettingsService(BuildTempPath()),
|
||||
new FakeUntisSnapshots(), mappings, new FakeSubstitutionEntries(), new FakeGroups([]),
|
||||
new FakeTimetableSlots(), new FakeSupervisionDuties(), new FakeSchoolHolidays(),
|
||||
new PublicHolidayService(), new SchoolCalendarSettingsService(BuildTempPath()),
|
||||
new PeriodScheduleService(BuildTempPath()), new UntisMatchingService(), new UntisDiffService());
|
||||
|
||||
private static UntisMappingReviewDialogViewModel BuildDialogVm(FakeUntisSlotMappings mappings,
|
||||
List<LearningGroup>? groups = null) =>
|
||||
new(BuildUntisSyncService(mappings), mappings, groups ?? [], new FakeSubjects([]));
|
||||
|
||||
private static List<UntisGroupOption> Options(params LearningGroup[] groups) =>
|
||||
groups.Select(g => new UntisGroupOption(g, null)).ToList();
|
||||
|
||||
private static UntisSlotMatch BuildLessonMatch(Guid? groupId, string classToken = "10c") => new()
|
||||
{
|
||||
Pattern = new UntisWeeklyPattern
|
||||
{
|
||||
Weekday = DayOfWeek.Monday, StartTime = new TimeOnly(7, 50), EndTime = new TimeOnly(9, 20),
|
||||
Summary = "SOL", ClassTokens = [classToken], OccurrenceCount = 40,
|
||||
},
|
||||
PeriodNumber = 1, SuggestedGroupId = groupId, IsConfident = groupId is not null,
|
||||
};
|
||||
|
||||
private static UntisSlotMatch BuildSupervisionMatch() => new()
|
||||
{
|
||||
Pattern = new UntisWeeklyPattern
|
||||
{
|
||||
Weekday = DayOfWeek.Tuesday, StartTime = new TimeOnly(9, 20), EndTime = new TimeOnly(9, 40),
|
||||
Summary = null, ClassTokens = [], OccurrenceCount = 40,
|
||||
},
|
||||
AfterPeriod = 2,
|
||||
};
|
||||
|
||||
[Fact]
|
||||
public void UntisMappingRow_VorschlagWirdVorausgewaehlt()
|
||||
{
|
||||
var group = new LearningGroup { Name = "10c" };
|
||||
var row = new UntisMappingRow(BuildLessonMatch(group.Id), Options(group), existing: null);
|
||||
|
||||
Assert.Equal(group.Id, row.SelectedGroup?.Group.Id);
|
||||
Assert.True(row.IsConfident);
|
||||
Assert.False(row.IsSupervisionCandidate);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UntisMappingRow_OhneVorschlag_BleibtLeer()
|
||||
{
|
||||
var row = new UntisMappingRow(BuildLessonMatch(null), [], existing: null);
|
||||
|
||||
Assert.Null(row.SelectedGroup);
|
||||
Assert.False(row.IsConfident);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UntisGroupOption_GleicherNameVerschiedeneFaecher_ZeigtFachImDisplayLabel()
|
||||
{
|
||||
// Nutzer-Feedback: "Meine Klasse habe ich 3-mal. Ohne das Fach dabei, kann ich nicht
|
||||
// sicher die richtige Lerngruppe hier auswählen."
|
||||
var option = new UntisGroupOption(new LearningGroup { Name = "10c" }, "Chemie");
|
||||
|
||||
Assert.Equal("10c (Chemie)", option.DisplayLabel);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UntisGroupOption_OhneFach_ZeigtNurDenNamen()
|
||||
{
|
||||
var option = new UntisGroupOption(new LearningGroup { Name = "10c" }, null);
|
||||
|
||||
Assert.Equal("10c", option.DisplayLabel);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UntisMappingRow_AufsichtsMuster_WirdAlsSupervisionCandidateErkanntUndKannAufgeloestWerden()
|
||||
{
|
||||
// Nutzer-Feedback: "Zwei Termine sind meine Aufsichten [...] vom Zeitraster und von der
|
||||
// Dauer her könnten die erfasst werden" - kein Klassenbezug, aber AfterPeriod auflösbar.
|
||||
var row = new UntisMappingRow(BuildSupervisionMatch(), [], existing: null);
|
||||
|
||||
Assert.True(row.IsSupervisionCandidate);
|
||||
Assert.True(row.CanResolve);
|
||||
Assert.False(row.ConfirmAsSupervision);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Save_SchreibtNurZeilenMitAusgewaehlterGruppeAlsBestaetigt()
|
||||
{
|
||||
var groupA = new LearningGroup { Name = "10c" };
|
||||
var groupB = new LearningGroup { Name = "10d" };
|
||||
var mappings = new FakeUntisSlotMappings();
|
||||
var vm = BuildDialogVm(mappings, [groupA, groupB]);
|
||||
vm.Rows.Add(new UntisMappingRow(BuildLessonMatch(groupA.Id), Options(groupA, groupB), existing: null));
|
||||
vm.Rows.Add(new UntisMappingRow(BuildLessonMatch(null, "9z"), Options(groupA, groupB), existing: null)); // ignoriert
|
||||
|
||||
vm.SaveCommand.Execute(null);
|
||||
|
||||
var saved = Assert.Single(mappings.GetAll());
|
||||
Assert.True(saved.Confirmed);
|
||||
Assert.Equal(SubstitutionKind.Lesson, saved.Kind);
|
||||
Assert.Equal(groupA.Id, saved.GroupId);
|
||||
Assert.Equal(1, saved.PeriodNumber);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Save_AufsichtBestaetigt_SchreibtSupervisionMapping()
|
||||
{
|
||||
var mappings = new FakeUntisSlotMappings();
|
||||
var vm = BuildDialogVm(mappings);
|
||||
var row = new UntisMappingRow(BuildSupervisionMatch(), [], existing: null) { ConfirmAsSupervision = true };
|
||||
vm.Rows.Add(row);
|
||||
|
||||
vm.SaveCommand.Execute(null);
|
||||
|
||||
var saved = Assert.Single(mappings.GetAll());
|
||||
Assert.Equal(SubstitutionKind.Supervision, saved.Kind);
|
||||
Assert.Null(saved.GroupId);
|
||||
Assert.Equal(2, saved.AfterPeriod);
|
||||
Assert.True(saved.Confirmed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Save_ErneutesSpeichernDerselbenZeile_AktualisiertStattDupliziert()
|
||||
{
|
||||
// Regression für "Kann es sein, dass er meine Verbesserungen gar nicht einspeichert" -
|
||||
// wiederholtes Bestätigen desselben Slots darf keine zweite Zeile anlegen (sonst
|
||||
// ArgumentException in UntisDiffService.Diff beim nächsten Poll, siehe dortigen Test).
|
||||
var groupA = new LearningGroup { Name = "10c" };
|
||||
var groupB = new LearningGroup { Name = "10d" };
|
||||
var mappings = new FakeUntisSlotMappings();
|
||||
var vm1 = BuildDialogVm(mappings, [groupA, groupB]);
|
||||
vm1.Rows.Add(new UntisMappingRow(BuildLessonMatch(groupA.Id), Options(groupA, groupB), existing: null));
|
||||
vm1.SaveCommand.Execute(null);
|
||||
|
||||
var firstSaved = Assert.Single(mappings.GetAll());
|
||||
|
||||
// Dialog erneut geöffnet - die bereits bestätigte Gruppe muss vorbefüllt sein.
|
||||
var existing = mappings.GetAll().Single();
|
||||
var row2 = new UntisMappingRow(BuildLessonMatch(groupA.Id), Options(groupA, groupB), existing);
|
||||
Assert.Equal(groupA.Id, row2.SelectedGroup?.Group.Id);
|
||||
|
||||
row2.SelectedGroup = Options(groupB).Single(); // Nutzer korrigiert
|
||||
var vm2 = BuildDialogVm(mappings, [groupA, groupB]);
|
||||
vm2.Rows.Add(row2);
|
||||
vm2.SaveCommand.Execute(null);
|
||||
|
||||
var saved = Assert.Single(mappings.GetAll());
|
||||
Assert.Equal(firstSaved.Id, saved.Id);
|
||||
Assert.Equal(groupB.Id, saved.GroupId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Save_NutzerAendertVorschlag_UebernimmtNeueAuswahl()
|
||||
{
|
||||
var groupA = new LearningGroup { Name = "10c" };
|
||||
var groupB = new LearningGroup { Name = "10d" };
|
||||
var mappings = new FakeUntisSlotMappings();
|
||||
var vm = BuildDialogVm(mappings, [groupA, groupB]);
|
||||
var row = new UntisMappingRow(BuildLessonMatch(groupA.Id), Options(groupA, groupB), existing: null);
|
||||
row.SelectedGroup = Options(groupB).Single();
|
||||
vm.Rows.Add(row);
|
||||
|
||||
vm.SaveCommand.Execute(null);
|
||||
|
||||
Assert.Equal(groupB.Id, Assert.Single(mappings.GetAll()).GroupId);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user