using LehrerApp.Core.Models; using LehrerApp.Desktop.ViewModels.Groups; using Xunit; namespace LehrerApp.Desktop.Tests; public sealed class StudentGroupGeneratorViewModelTests { private static (StudentGroupGeneratorViewModel Vm, List Students, LearningGroup Group, FakeSessions Sessions, FakeEntries Entries) Build(int studentCount = 10, FakeStudentGroupPlans? savedPlans = null) { var group = new LearningGroup { Name = "8a", GradingSystem = GradingSystem.Grades1To6 }; var students = Enumerable.Range(1, studentCount).Select(i => new Student { FirstName = $"Kind{i:00}", LastName = "Test", }).ToList(); var memberships = students.Select(s => new GroupMembership { GroupId = group.Id, StudentId = s.Id, }).ToList(); var sessions = new FakeSessions([]); var entries = new FakeEntries(); var vm = new StudentGroupGeneratorViewModel( new FakeStudents(students), new FakeGroups([group]), new FakeMemberships(memberships), sessions, entries, new FakeGrades(), group.Id, new Random(7), savedPlans); return (vm, students, group, sessions, entries); } [Fact] public void ManuelleUmverteilung_WirdGespeichertUndSpaeterWiederGeladen() { var plans = new FakeStudentGroupPlans(); var (vm, students, group, _, _) = Build(6, plans); vm.TargetGroupCount = 2; vm.CountPriority = 100; vm.GenerateCommand.Execute(null); var source = vm.GeneratedGroups[0]; var target = vm.GeneratedGroups[1]; var member = source.Members[0]; Assert.True(vm.MoveStudent(member, target)); Assert.False(vm.MoveStudent(member, target)); Assert.DoesNotContain(member, source.Members); vm.PlanName = "Projektwoche"; vm.SavePlanCommand.Execute(null); var saved = Assert.Single(plans.GetByGroup(group.Id)); Assert.Equal("Projektwoche", saved.Name); Assert.Contains(member.Id, saved.Groups[1].StudentIds); Assert.False(vm.HasUnsavedChanges); vm.GenerateCommand.Execute(null); vm.SelectedSavedPlan = Assert.Single(vm.SavedPlans); vm.LoadSavedPlanCommand.Execute(null); Assert.Contains(vm.GeneratedGroups[1].Members, m => m.Id == member.Id); Assert.DoesNotContain(vm.GeneratedGroups[0].Members, m => m.Id == member.Id); Assert.False(vm.HasUnsavedChanges); Assert.Equal("Projektwoche", vm.PlanName); var reopened = new StudentGroupGeneratorViewModel( new FakeStudents(students), new FakeGroups([group]), new FakeMemberships(students.Select(s => new GroupMembership { GroupId = group.Id, StudentId = s.Id, }).ToList()), new FakeSessions([]), new FakeEntries(), new FakeGrades(), group.Id, savedPlans: plans); reopened.SelectedSavedPlan = Assert.Single(reopened.SavedPlans); reopened.LoadSavedPlanCommand.Execute(null); Assert.Contains(reopened.GeneratedGroups[1].Members, m => m.Id == member.Id); reopened.PlanName = "Projektwoche aktualisiert"; reopened.SavePlanCommand.Execute(null); Assert.Equal("Projektwoche aktualisiert", Assert.Single(plans.GetByGroup(group.Id)).Name); } [Fact] public void SpeichernUeberschreibtAusgewaehltenPlanErstNachDemLaden() { var plans = new FakeStudentGroupPlans(); var (vm, _, group, _, _) = Build(6, plans); vm.GenerateCommand.Execute(null); vm.PlanName = "Alter Plan"; vm.SavePlanCommand.Execute(null); vm.GenerateCommand.Execute(null); vm.SelectedSavedPlan = Assert.Single(vm.SavedPlans); vm.PlanName = "Neuer Plan"; vm.SavePlanCommand.Execute(null); Assert.Equal(2, plans.GetByGroup(group.Id).Count); Assert.Contains(plans.GetByGroup(group.Id), p => p.Name == "Alter Plan"); Assert.Contains(plans.GetByGroup(group.Id), p => p.Name == "Neuer Plan"); } [Fact] public void FesteZielgroesse_ErzeugtPassendeAnzahlMitKleinerAusnahme() { var (vm, _, _, _, _) = Build(10); vm.TargetGroupSize = 4; vm.TargetGroupCount = 2; vm.SizePriority = 100; vm.CountPriority = 20; vm.GenerateCommand.Execute(null); Assert.Equal(3, vm.GeneratedGroups.Count); Assert.Equal(new[] { 3, 3, 4 }, vm.GeneratedGroups.Select(g => g.Members.Count).OrderBy(x => x)); Assert.Contains("nicht gleichzeitig", vm.WarningText); } [Fact] public void FesteGruppenanzahl_HatVorrangVorZielgroesse() { var (vm, _, _, _, _) = Build(10); vm.TargetGroupSize = 3; vm.TargetGroupCount = 2; vm.SizePriority = 20; vm.CountPriority = 100; vm.GenerateCommand.Execute(null); Assert.Equal(2, vm.GeneratedGroups.Count); Assert.All(vm.GeneratedGroups, group => Assert.Equal(5, group.Members.Count)); } [Fact] public void AdHoc_SchliesstHeuteAbwesendeAus() { var (vm, students, group, sessions, entries) = Build(6); var session = new ParticipationSession { GroupId = group.Id, Date = DateOnly.FromDateTime(DateTime.Today) }; sessions.Save(session); entries.Add(new ParticipationEntry { SessionId = session.Id, StudentId = students[0].Id, Attendance = AttendanceStatus.Excused, }); vm.ParticipantModeName = StudentGroupGeneratorViewModel.AdHocModeLabel; vm.TargetGroupCount = 2; vm.CountPriority = 100; vm.GenerateCommand.Execute(null); var names = vm.GeneratedGroups.SelectMany(g => g.Members).Select(m => m.Name).ToList(); Assert.Equal(5, names.Count); Assert.DoesNotContain(students[0].FullName, names); Assert.Contains("fehlt heute", vm.WarningText); } [Fact] public void AdHoc_OhneHeutigeErfassung_WarntUndNutztAlleAktiven() { var (vm, students, _, _, _) = Build(6); vm.ParticipantModeName = StudentGroupGeneratorViewModel.AdHocModeLabel; vm.GenerateCommand.Execute(null); Assert.Equal(students.Count, vm.GeneratedGroups.SelectMany(g => g.Members).Count()); Assert.Contains("keine Anwesenheit", vm.WarningText); } [Fact] public void Leistungsverteilung_UnterscheidetHomogeneUndHeterogeneGruppen() { var group = new LearningGroup { Name = "Kurs", GradingSystem = GradingSystem.Grades1To6 }; var students = new[] { "A", "B", "C", "D" }.Select(name => new Student { FirstName = name, LastName = "Test", }).ToList(); var memberships = students.Select(s => new GroupMembership { GroupId = group.Id, StudentId = s.Id }).ToList(); var grades = new FakeGrades(); var values = new[] { "1", "2", "5", "6" }; for (var i = 0; i < students.Count; i++) grades.Add(new Grade { GroupId = group.Id, StudentId = students[i].Id, Value = values[i] }); var vm = new StudentGroupGeneratorViewModel( new FakeStudents(students), new FakeGroups([group]), new FakeMemberships(memberships), new FakeSessions([]), new FakeEntries(), grades, group.Id, new Random(3)) { TargetGroupCount = 2, CountPriority = 100, PerformanceDistribution = -100, }; vm.GenerateCommand.Execute(null); var homogeneousFirst = vm.GeneratedGroups[0].Members.Select(m => m.Name).ToHashSet(); Assert.Contains(students[0].FullName, homogeneousFirst); Assert.Contains(students[1].FullName, homogeneousFirst); vm.PerformanceDistribution = 100; vm.GenerateCommand.Execute(null); Assert.All(vm.GeneratedGroups, generated => { var names = generated.Members.Select(m => m.Name).ToHashSet(); Assert.True(names.Overlaps([students[0].FullName, students[1].FullName])); Assert.True(names.Overlaps([students[2].FullName, students[3].FullName])); }); } [Fact] public void Paarregeln_ErzwingenZusammenUndTrennenMitHoherPrioritaet() { var (vm, students, _, _, _) = Build(6); vm.TargetGroupCount = 2; vm.CountPriority = 100; vm.PerformanceDistribution = 0; vm.ForcedPairPriority = 100; vm.SelectedPairStudentA = vm.PairCandidates.Single(s => s.Id == students[0].Id); vm.SelectedPairStudentB = vm.PairCandidates.Single(s => s.Id == students[1].Id); vm.AddForcedPairCommand.Execute(null); vm.SelectedPairStudentA = vm.PairCandidates.Single(s => s.Id == students[2].Id); vm.SelectedPairStudentB = vm.PairCandidates.Single(s => s.Id == students[3].Id); vm.AddExcludedPairCommand.Execute(null); vm.GenerateCommand.Execute(null); var groupByName = vm.GeneratedGroups.SelectMany((group, index) => group.Members.Select(member => (member.Name, index))) .ToDictionary(x => x.Name, x => x.index); Assert.Equal(groupByName[students[0].FullName], groupByName[students[1].FullName]); Assert.NotEqual(groupByName[students[2].FullName], groupByName[students[3].FullName]); } [Fact] public void FoerderschuelerWerdenMoeglichstGleichmaessigVerteilt() { var group = new LearningGroup { Name = "Kurs" }; var students = Enumerable.Range(1, 8).Select(i => new Student { FirstName = $"Kind{i}", LastName = "Test", }).ToList(); var memberships = students.Select((student, index) => new GroupMembership { GroupId = group.Id, StudentId = student.Id, Niveau = index < 4 ? Niveau.Foerder : null, }).ToList(); var vm = new StudentGroupGeneratorViewModel( new FakeStudents(students), new FakeGroups([group]), new FakeMemberships(memberships), new FakeSessions([]), new FakeEntries(), new FakeGrades(), group.Id, new Random(4)) { TargetGroupCount = 2, CountPriority = 100, PerformanceDistribution = -100, DistributeSupportStudents = true, }; vm.GenerateCommand.Execute(null); var supportNames = students.Take(4).Select(s => s.FullName).ToHashSet(); Assert.All(vm.GeneratedGroups, generated => Assert.Equal(2, generated.Members.Count(member => supportNames.Contains(member.Name)))); } }