Files
LehrerApp/LehrerApp.Desktop.Tests/ReportGradeCalculationTests.cs
T

199 lines
8.9 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using LehrerApp.Core.Models;
using LehrerApp.Core.Services;
using LehrerApp.Desktop.ViewModels.Groups;
using Xunit;
namespace LehrerApp.Desktop.Tests;
/// Tests für die Zeugnisnotenberechnung (2.4): ReportGradeDialogViewModel kombiniert
/// Klausuren, Mitarbeit und sonstige Noten gemäß Gewichtungsschema zu einer Zeugnisnote.
public class ReportGradeCalculationTests
{
private static Guid GroupId { get; } = Guid.NewGuid();
private static Student Anna { get; } = new() { Id = Guid.NewGuid(), FirstName = "Anna", LastName = "Beispiel" };
private static Student Ben { get; } = new() { Id = Guid.NewGuid(), FirstName = "Ben", LastName = "Muster" };
private static ReportGradeDialogViewModel BuildViewModel(
FakeGrades grades, FakeSchemes schemes, FakeReportGrades reportGrades,
FakeExams exams, FakeResults results)
{
var students = new FakeStudents([Anna, Ben]);
var memberships = new FakeMemberships([]);
var grading = new GradingService();
return new ReportGradeDialogViewModel(grades, exams, results, students, memberships,
schemes, reportGrades, grading, GroupId, GroupType.Class, GradingSystem.Grades1To6,
"Testgruppe", "2025/26");
}
private static (Exam Exam1, Exam Exam2, FakeExams Exams, FakeResults Results) BuildExams()
{
var exam1 = new Exam { Id = Guid.NewGuid(), GroupId = GroupId, Date = new DateOnly(2025, 9, 10), Title = "K1" };
var exam2 = new Exam { Id = Guid.NewGuid(), GroupId = GroupId, Date = new DateOnly(2025, 9, 24), Title = "K2" };
var exams = new FakeExams([exam1, exam2]);
var results = new FakeResults();
results.Add(new ExamResult { ExamId = exam1.Id, StudentId = Anna.Id, Grade = "2" });
results.Add(new ExamResult { ExamId = exam2.Id, StudentId = Anna.Id, Grade = "4" });
return (exam1, exam2, exams, results);
}
[Fact]
public void CalculatedGrade_KombiniertKlausurenMitarbeitUndSonstigeGemaessSchema()
{
var (_, _, exams, results) = BuildExams();
var grades = new FakeGrades();
grades.Add(new Grade { StudentId = Anna.Id, GroupId = GroupId, Category = GradeCategory.Participation,
Date = new DateOnly(2025, 9, 5), Value = "3", Weight = 1.0 });
grades.Add(new Grade { StudentId = Anna.Id, GroupId = GroupId, Category = GradeCategory.Other,
Date = new DateOnly(2025, 9, 5), Value = "1", Weight = 1.0 });
var schemes = new FakeSchemes();
schemes.SetForGroup(GroupId, new GradingScheme { ExamsPercent = 50, ParticipationPercent = 40, OtherPercent = 10 });
var vm = BuildViewModel(grades, schemes, new FakeReportGrades(), exams, results);
// Klausuren Ø3 (Note 2 und 4), Mitarbeit 3, Sonstige 1 -> (3*50+3*40+1*10)/100 = 2.8 -> kaufmännisch 3
var anna = vm.Rows.Single(r => r.StudentId == Anna.Id);
Assert.Equal("3", anna.CalculatedValue);
}
[Fact]
public void CalculatedGrade_SchuelerOhneJedeNote_ZeigtKeinenWert()
{
var (_, _, exams, results) = BuildExams();
var schemes = new FakeSchemes();
var vm = BuildViewModel(new FakeGrades(), schemes, new FakeReportGrades(), exams, results);
var ben = vm.Rows.Single(r => r.StudentId == Ben.Id);
Assert.Null(ben.CalculatedValue);
Assert.Equal("", ben.CalculatedDisplay);
}
[Fact]
public void ResolveScheme_BevorzugtGruppenspezifischesSchemaVorVoreinstellung()
{
var (_, _, exams, results) = BuildExams();
var grades = new FakeGrades();
var schemes = new FakeSchemes();
schemes.SetForGroup(GroupId, new GradingScheme { ExamsPercent = 100, ParticipationPercent = 0, OtherPercent = 0 });
schemes.SetDefault(GroupType.Class, new GradingScheme { ExamsPercent = 0, ParticipationPercent = 0, OtherPercent = 100 });
var vm = BuildViewModel(grades, schemes, new FakeReportGrades(), exams, results);
// Gruppenspezifisches Schema gewichtet nur Klausuren (Ø3 aus Note 2 und 4) -> exakt "3"
Assert.Contains("Klausuren 100", vm.SchemeSummary);
var anna = vm.Rows.Single(r => r.StudentId == Anna.Id);
Assert.Equal("3", anna.CalculatedValue);
}
[Fact]
public void ResolveScheme_OhneGruppenschema_VerwendetVoreinstellungDesGruppentyps()
{
var (_, _, exams, results) = BuildExams();
var schemes = new FakeSchemes();
schemes.SetDefault(GroupType.Class, new GradingScheme { ExamsPercent = 70, ParticipationPercent = 20, OtherPercent = 10 });
var vm = BuildViewModel(new FakeGrades(), schemes, new FakeReportGrades(), exams, results);
Assert.Contains("Klausuren 70", vm.SchemeSummary);
}
[Fact]
public void ResolveScheme_OhneJedeVoreinstellung_VerwendetHartcodiertenFallback()
{
var (_, _, exams, results) = BuildExams();
var vm = BuildViewModel(new FakeGrades(), new FakeSchemes(), new FakeReportGrades(), exams, results);
Assert.Contains("Klausuren 50", vm.SchemeSummary);
Assert.Contains("Mitarbeit 40", vm.SchemeSummary);
Assert.Contains("Sonstige 10", vm.SchemeSummary);
}
[Fact]
public void Save_UebersteuernOhneBegruendung_LiefertValidierungsfehlerUndSpeichertNicht()
{
var (_, _, exams, results) = BuildExams();
var reportGrades = new FakeReportGrades();
var vm = BuildViewModel(new FakeGrades(), new FakeSchemes(), reportGrades, exams, results);
var anna = vm.Rows.Single(r => r.StudentId == Anna.Id);
anna.OverrideValue = "1";
anna.SaveCommand.Execute(null);
Assert.False(string.IsNullOrEmpty(anna.ValidationMessage));
Assert.Equal(0, reportGrades.SavedCount);
}
[Fact]
public void Save_UebersteuernMitBegruendung_AktualisiertFinalDisplay()
{
var (_, _, exams, results) = BuildExams();
var reportGrades = new FakeReportGrades();
var vm = BuildViewModel(new FakeGrades(), new FakeSchemes(), reportGrades, exams, results);
var anna = vm.Rows.Single(r => r.StudentId == Anna.Id);
anna.OverrideValue = "1";
anna.OverrideReason = "Mündliche Steigerung im letzten Quartal";
anna.SaveCommand.Execute(null);
Assert.True(string.IsNullOrEmpty(anna.ValidationMessage));
Assert.Equal("1", anna.FinalDisplay);
Assert.Equal(1, reportGrades.SavedCount);
}
[Fact]
public void ToggleLock_FriertDenStandEinUndIgnoriertSpaetereNeuberechnung()
{
var (exam1, _, exams, results) = BuildExams();
var reportGrades = new FakeReportGrades();
var vm = BuildViewModel(new FakeGrades(), new FakeSchemes(), reportGrades, exams, results);
var anna = vm.Rows.Single(r => r.StudentId == Anna.Id);
anna.OverrideValue = "1";
anna.OverrideReason = "Testgrund";
anna.SaveCommand.Execute(null);
anna.ToggleLockCommand.Execute(null);
// Neue, bessere Klausurnote nachtragen -> würde die Berechnung ändern, wenn ungesperrt.
results.Add(new ExamResult { ExamId = exam1.Id, StudentId = Anna.Id, Grade = "1" });
vm.SelectedPeriod = vm.PeriodOptions.First(p => p.Period == ParticipationPeriod.H1);
vm.SelectedPeriod = vm.PeriodOptions.First(p => p.Period == ParticipationPeriod.FullYear);
var annaNachSperre = vm.Rows.Single(r => r.StudentId == Anna.Id);
Assert.True(annaNachSperre.IsLocked);
Assert.Equal("1", annaNachSperre.FinalDisplay);
}
[Fact]
public void ExportCsv_EnthaeltAlleSchuelerDerGruppe()
{
var (_, _, exams, results) = BuildExams();
var vm = BuildViewModel(new FakeGrades(), new FakeSchemes(), new FakeReportGrades(), exams, results);
var csv = vm.ExportCsv();
Assert.Contains("Beispiel, Anna", csv);
Assert.Contains("Muster, Ben", csv);
}
[Fact]
public void RoundingRule_Paedagogisch_RundetGrenzfallZurBesserenNote()
{
var exam1 = new Exam { Id = Guid.NewGuid(), GroupId = GroupId, Date = new DateOnly(2025, 9, 10), Title = "K1" };
var exam2 = new Exam { Id = Guid.NewGuid(), GroupId = GroupId, Date = new DateOnly(2025, 9, 24), Title = "K2" };
var exams = new FakeExams([exam1, exam2]);
var results = new FakeResults();
// Ø exakt 2.5 -> kaufmännisch 3, pädagogisch 2 (bessere Note)
results.Add(new ExamResult { ExamId = exam1.Id, StudentId = Anna.Id, Grade = "2" });
results.Add(new ExamResult { ExamId = exam2.Id, StudentId = Anna.Id, Grade = "3" });
var schemes = new FakeSchemes();
schemes.SetForGroup(GroupId, new GradingScheme { ExamsPercent = 100, ParticipationPercent = 0, OtherPercent = 0 });
var vm = BuildViewModel(new FakeGrades(), schemes, new FakeReportGrades(), exams, results);
Assert.Equal("3", vm.Rows.Single(r => r.StudentId == Anna.Id).CalculatedValue);
vm.RoundingRuleName = "Pädagogisch";
Assert.Equal("2", vm.Rows.Single(r => r.StudentId == Anna.Id).CalculatedValue);
}
}