using LehrerApp.Core.Models; using LehrerApp.Core.Services; using LehrerApp.Desktop.ViewModels.Groups; using Xunit; namespace LehrerApp.Desktop.Tests; /// Tests für den Schüler-Leistungsüberblick (Nutzer-Feedback): Klausuren/Mitarbeit/Sonstige /// im Überblick, berechnete Zeugnisnote, Zielnoten- und Was-wäre-wenn-Rechner sowie der /// Bewerter-/Schülermodus-Unterschied (Kursdurchschnitt, Bearbeitbarkeit). public sealed class StudentPerformanceOverviewViewModelTests { private static readonly Guid GroupId = Guid.NewGuid(); private static readonly Guid StudentId = Guid.NewGuid(); private static readonly Guid OtherStudentId = Guid.NewGuid(); private static StudentPerformanceOverviewViewModel BuildViewModel( FakeExams? exams = null, FakeResults? results = null, FakeGrades? grades = null, FakeSchemes? schemes = null, GradingSystem system = GradingSystem.Grades1To6) { var scheme = new GradingScheme { ExamsPercent = 50, ParticipationPercent = 40, OtherPercent = 10 }; var schemesRepo = schemes ?? new FakeSchemes(); if (schemes is null) schemesRepo.SetForGroup(GroupId, scheme); return new StudentPerformanceOverviewViewModel( grades ?? new FakeGrades(), exams ?? new FakeExams([]), results ?? new FakeResults(), new FakeMemberships([]), schemesRepo, new GradingService(), GroupId, StudentId, GroupType.Class, system, "Beispiel, Anna", "Testkurs", "2025/26"); } [Fact] public void ExamRows_ZeigtEigeneNoteUndKursdurchschnitt() { var exam = new Exam { Id = Guid.NewGuid(), GroupId = GroupId, Title = "K1", Date = new DateOnly(2025, 9, 10) }; var exams = new FakeExams([exam]); var results = new FakeResults(); results.Add(new ExamResult { ExamId = exam.Id, StudentId = StudentId, Grade = "2" }); results.Add(new ExamResult { ExamId = exam.Id, StudentId = OtherStudentId, Grade = "4" }); var vm = BuildViewModel(exams, results); var row = Assert.Single(vm.ExamRows); Assert.Equal("2", row.OwnGradeDisplay); Assert.Equal("3.0", row.ClassAverageDisplay); } [Fact] public void ExamHistory_ZeigtEigenenVerlaufUndMarkiertNotenabfall() { var exam1 = new Exam { Id = Guid.NewGuid(), GroupId = GroupId, Title = "K1", Date = new DateOnly(2025, 9, 10) }; var exam2 = new Exam { Id = Guid.NewGuid(), GroupId = GroupId, Title = "K2", Date = new DateOnly(2025, 9, 24) }; var exams = new FakeExams([exam1, exam2]); var results = new FakeResults(); results.Add(new ExamResult { ExamId = exam1.Id, StudentId = StudentId, Grade = "2" }); results.Add(new ExamResult { ExamId = exam2.Id, StudentId = StudentId, Grade = "5" }); // Abfall + mangelhaft var vm = BuildViewModel(exams, results); Assert.Equal(2, vm.ExamHistory.Points.Count); Assert.False(vm.ExamHistory.Points[0].IsWarning); Assert.True(vm.ExamHistory.Points[1].IsWarning); Assert.Contains("Abfall", vm.ExamHistory.Points[1].WarningText); Assert.Contains("Versetzungsgefährdung", vm.ExamHistory.Points[1].WarningText); Assert.True(vm.ExamHistory.Points[1].BarHeight < vm.ExamHistory.Points[0].BarHeight); } [Fact] public void AktuelleZeugnisnote_KombiniertAlleDreiBereiche() { var exam = new Exam { Id = Guid.NewGuid(), GroupId = GroupId, Title = "K1", Date = new DateOnly(2025, 9, 10) }; var exams = new FakeExams([exam]); var results = new FakeResults(); results.Add(new ExamResult { ExamId = exam.Id, StudentId = StudentId, Grade = "2" }); var grades = new FakeGrades(); grades.Add(new Grade { StudentId = StudentId, GroupId = GroupId, Category = GradeCategory.Participation, Date = new DateOnly(2025, 9, 5), Value = "3" }); grades.Add(new Grade { StudentId = StudentId, GroupId = GroupId, Category = GradeCategory.Other, Date = new DateOnly(2025, 9, 5), Value = "1" }); var vm = BuildViewModel(exams, results, grades); // (2*50 + 3*40 + 1*10) / 100 = 2.3 -> kaufmännisch 2 Assert.Equal("Note 2", vm.CurrentReportGradeDisplay); } [Fact] public void Zielnote_BerechnetNoetigenKlausurschnitt() { var grades = new FakeGrades(); grades.Add(new Grade { StudentId = StudentId, GroupId = GroupId, Category = GradeCategory.Participation, Date = new DateOnly(2025, 9, 5), Value = "3" }); grades.Add(new Grade { StudentId = StudentId, GroupId = GroupId, Category = GradeCategory.Other, Date = new DateOnly(2025, 9, 5), Value = "2" }); var vm = BuildViewModel(grades: grades); vm.TargetGradeText = "2"; vm.TargetBucketName = "Klausuren"; // Ziel 2,0 = (x*50 + 3*40 + 2*10)/100 => 200 = 50x+120+20 => x = 1,2 Assert.Contains("1.2", vm.TargetResultDisplay); } [Fact] public void WasWaereWenn_ZeigtZeugnisnoteMitZusaetzlicherKlausurAn() { var exam = new Exam { Id = Guid.NewGuid(), GroupId = GroupId, Title = "K1", Date = new DateOnly(2025, 9, 10) }; var exams = new FakeExams([exam]); var results = new FakeResults(); results.Add(new ExamResult { ExamId = exam.Id, StudentId = StudentId, Grade = "2" }); var grades = new FakeGrades(); grades.Add(new Grade { StudentId = StudentId, GroupId = GroupId, Category = GradeCategory.Participation, Date = new DateOnly(2025, 9, 5), Value = "2" }); var vm = BuildViewModel(exams, results, grades); vm.WhatIfExamGradeText = "6"; // Klausuren neu: (2+6)/2=4 (50%), Mitarbeit 2 (40%, Sonstige fehlt) -> (4*50+2*40)/90 = 3,11 -> kaufm. 3 Assert.Contains("3", vm.WhatIfResultDisplay); } [Fact] public void SaveCommand_AufMitarbeitszeile_SpeichertUndAktualisiertDurchschnitt() { var grades = new FakeGrades(); var grade = new Grade { StudentId = StudentId, GroupId = GroupId, Category = GradeCategory.Participation, Date = new DateOnly(2025, 9, 5), Value = "3" }; grades.Add(grade); var vm = BuildViewModel(grades: grades); var row = Assert.Single(vm.ParticipationRows); row.Value = "1"; row.SaveCommand.Execute(null); Assert.Equal("1.0", vm.ParticipationAverageDisplay); Assert.Equal("1", grades.GetByStudentAndGroup(StudentId, GroupId).Single().Value); } [Fact] public void OhneWerteImZeitraum_ZeigtHinweisStattZeugnisnote() { var vm = BuildViewModel(); Assert.Contains("Noch nicht berechenbar", vm.CurrentReportGradeDisplay); } }