Files
LehrerApp/LehrerApp.Desktop.Tests/TimetableSlotDialogViewModelTests.cs
adminandClaude Sonnet 5 888df39f37 Fix: ArgumentException im Stundenplan bei gleichnamigen Lerngruppen
TimetableSlotDialogViewModel baute die Gruppenauswahl als Dictionary,
geschlüsselt nach LearningGroup.Name. Bei zwei Lerngruppen mit demselben
Namen (dieselbe Klasse in zwei Fächern unterrichtet, z.B. zwei "10c") warf
ToDictionary eine ArgumentException, der Zuweisen-Dialog ließ sich gar
nicht mehr öffnen.

Behoben durch eindeutige Anzeige-Labels statt des rohen Namens: bei einem
Namenskonflikt wird das Fach angehängt ("10c (Chemie)" vs. "10c
(Mathematik)"), mit nummeriertem Fallback für den Restfall gleicher Name
und gleiches Fach.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-17 00:27:14 +02:00

128 lines
5.2 KiB
C#

using LehrerApp.Core.Models;
using LehrerApp.Core.Services;
using LehrerApp.Desktop.ViewModels.Planning;
using Xunit;
namespace LehrerApp.Desktop.Tests;
public sealed class TimetableSlotDialogViewModelTests
{
private static SchoolYearService SchoolYear() => new();
[Fact]
public void Save_OhneAusgewaehlteGruppe_SetztFehlerUndSpeichertNicht()
{
var slots = new FakeTimetableSlots();
var groups = new FakeGroups([]);
var vm = new TimetableSlotDialogViewModel(slots, groups, new FakeSubjects([]), SchoolYear(), DayOfWeek.Monday, 1, null);
vm.SaveCommand.Execute(null);
Assert.Null(vm.Result);
Assert.NotEqual("", vm.GroupError);
}
[Fact]
public void Save_MitAusgewaehlterGruppe_LegtNeuenSlotAn()
{
var group = new LearningGroup { Name = "Q1 Chemie" };
var slots = new FakeTimetableSlots();
var groups = new FakeGroups([group]);
var vm = new TimetableSlotDialogViewModel(slots, groups, new FakeSubjects([]), SchoolYear(), DayOfWeek.Monday, 1, null)
{
SelectedGroupName = "Q1 Chemie", Room = "R204",
};
vm.SaveCommand.Execute(null);
Assert.NotNull(vm.Result);
Assert.Equal(group.Id, vm.Result!.GroupId);
Assert.Equal("R204", vm.Result.Room);
Assert.Single(slots.GetAll());
}
[Fact]
public void Save_BelegteStunde_ZeigtFreundlicheFehlermeldungStattAbsturz()
{
var groupA = new LearningGroup { Name = "Q1 Chemie" };
var groupB = new LearningGroup { Name = "Q1 Physik" };
var slots = new FakeTimetableSlots();
slots.Add(new TimetableSlot { GroupId = groupA.Id, Weekday = DayOfWeek.Monday, PeriodNumber = 1 });
var groups = new FakeGroups([groupA, groupB]);
var vm = new TimetableSlotDialogViewModel(slots, groups, new FakeSubjects([]), SchoolYear(), DayOfWeek.Monday, 1, null)
{
SelectedGroupName = "Q1 Physik",
};
vm.SaveCommand.Execute(null);
Assert.Null(vm.Result);
Assert.Equal("Diese Stunde ist bereits belegt.", vm.GroupError);
}
[Fact]
public void Delete_BeimBearbeitenEinesVorhandenenSlots_EntferntIhn()
{
var group = new LearningGroup { Name = "Q1 Chemie" };
var slot = new TimetableSlot { GroupId = group.Id, Weekday = DayOfWeek.Monday, PeriodNumber = 1 };
var slots = new FakeTimetableSlots();
slots.Add(slot);
var groups = new FakeGroups([group]);
var vm = new TimetableSlotDialogViewModel(slots, groups, new FakeSubjects([]), SchoolYear(), DayOfWeek.Monday, 1, slot);
vm.DeleteCommand.Execute(null);
Assert.True(vm.Deleted);
Assert.Empty(slots.GetAll());
}
/// Regressionstest für den vom Nutzer gemeldeten Absturz: dieselbe Klasse ("10c") wird in
/// zwei Fächern unterrichtet und hat deshalb zwei LearningGroup-Datensätze mit demselben
/// Namen — das Anlegen der Gruppenliste darf dabei nicht mehr mit einer ArgumentException
/// abstürzen, und beide Gruppen müssen über eindeutige, unterscheidbare Labels wählbar bleiben.
[Fact]
public void KonstruktorMitGleichnamigenGruppenInVerschiedenenFaechern_StuerztNichtAbUndBleibtUnterscheidbar()
{
var chemie = new Subject { Name = "Chemie" };
var mathe = new Subject { Name = "Mathematik" };
var groupChemie = new LearningGroup { Name = "10c", SubjectId = chemie.Id };
var groupMathe = new LearningGroup { Name = "10c", SubjectId = mathe.Id };
var groups = new FakeGroups([groupChemie, groupMathe]);
var subjects = new FakeSubjects([chemie, mathe]);
var slots = new FakeTimetableSlots();
var vm = new TimetableSlotDialogViewModel(slots, groups, subjects, SchoolYear(), DayOfWeek.Monday, 1, null);
Assert.Equal(2, vm.GroupOptions.Length);
Assert.Equal(vm.GroupOptions.Distinct().Count(), vm.GroupOptions.Length);
Assert.Contains("10c (Chemie)", vm.GroupOptions);
Assert.Contains("10c (Mathematik)", vm.GroupOptions);
vm.SelectedGroupName = "10c (Mathematik)";
vm.SaveCommand.Execute(null);
Assert.NotNull(vm.Result);
Assert.Equal(groupMathe.Id, vm.Result!.GroupId);
}
/// Beim Bearbeiten eines bestehenden Slots muss das vorausgewählte Label zur tatsächlich
/// zugewiesenen Gruppe passen, auch wenn deren Name mehrdeutig ist.
[Fact]
public void KonstruktorBeimBearbeiten_WaehltDasLabelDerTatsaechlichZugewiesenenGruppeVor()
{
var chemie = new Subject { Name = "Chemie" };
var mathe = new Subject { Name = "Mathematik" };
var groupChemie = new LearningGroup { Name = "10c", SubjectId = chemie.Id };
var groupMathe = new LearningGroup { Name = "10c", SubjectId = mathe.Id };
var groups = new FakeGroups([groupChemie, groupMathe]);
var subjects = new FakeSubjects([chemie, mathe]);
var slot = new TimetableSlot { GroupId = groupChemie.Id, Weekday = DayOfWeek.Monday, PeriodNumber = 1 };
var slots = new FakeTimetableSlots();
slots.Add(slot);
var vm = new TimetableSlotDialogViewModel(slots, groups, subjects, SchoolYear(), DayOfWeek.Monday, 1, slot);
Assert.Equal("10c (Chemie)", vm.SelectedGroupName);
}
}