Files
LehrerApp/LehrerApp.Desktop.Tests/StudentReportTests.cs
T
2026-10-22 11:24:43 +02:00

130 lines
7.1 KiB
C#

using LehrerApp.Core.Models;
using LehrerApp.Core.Services;
using LehrerApp.Desktop.Services;
using LehrerApp.Desktop.ViewModels.Students;
using LehrerApp.Templating;
using Xunit;
namespace LehrerApp.Desktop.Tests;
public sealed class StudentReportTests
{
private readonly Student _student = new() { FirstName = "Erika", LastName = "Beispiel" };
private readonly LearningGroup _group = new() { Name = "9 Ch G", SchoolYear = "2026/27" };
private readonly DateOnly _date = new(2026, 10, 1);
private readonly FakeDocumentation _docs = new();
private readonly FakeEntries _entries = new();
private readonly FakeAspects _aspects = new();
private readonly List<ParticipationSession> _sessions = [];
private StudentReportService Service() => new(new FakeSessions(_sessions), _entries, _aspects,
new FakeExams([]), new FakeResults(), new FakeGrades(), new FakeMemberships([]),
new FakeCompetencyDomains(), _docs, new GradingService(), new CompetencyAnalysisService());
private Dictionary<string, PlaceholderValue> Build(StudentReportOptions? options = null) => Service().Build(
_student, _group, StudentReportTemplate.Create(), options ?? new(_date, _date.AddMonths(1), null, IncludeInterim: false));
[Fact]
public void Dokumentation_IstAufSchuelerGruppeZeitraumUndFreigabeBegrenzt()
{
Documentation Doc(string title) => new() { StudentId = _student.Id, GroupId = _group.Id, Date = _date, Title = title };
_docs.Add(Doc("sichtbar"));
var confidential = Doc("vertraulich"); confidential.IsConfidential = true; _docs.Add(confidential);
var draft = Doc("Entwurf"); draft.IsDraft = true; _docs.Add(draft);
var deleted = Doc("gelöscht"); deleted.IsDeleted = true; _docs.Add(deleted);
var otherGroup = Doc("fremdeGruppe"); otherGroup.GroupId = Guid.NewGuid(); _docs.Add(otherGroup);
var otherStudent = Doc("fremderSchüler"); otherStudent.StudentId = Guid.NewGuid(); _docs.Add(otherStudent);
var outside = Doc("außerhalb"); outside.Date = _date.AddDays(-1); _docs.Add(outside);
var text = Text((DrawingValue)Build()[StudentReportTemplate.Documentation]);
Assert.Contains("sichtbar", text);
foreach (var hidden in new[] { "vertraulich", "Entwurf", "gelöscht", "fremdeGruppe", "fremderSchüler", "außerhalb" })
Assert.DoesNotContain(hidden, text);
var included = Build(new(_date, _date, null, IncludeInterim: false, IncludeConfidential: true));
Assert.Contains("[Vertraulich]", Text((DrawingValue)included[StudentReportTemplate.Documentation]));
}
[Fact]
public void Zwischenstand_MussZumSchuelerZurGruppeUndZumZeitraumPassen()
{
var interim = new InterimGrade { StudentId = _student.Id, GroupId = _group.Id, Date = _date,
Occasion = "Sprechtag", OralValue = "2", WrittenValue = "3", OverallValue = "2-3" };
var values = Build(new(_date, _date, interim));
Assert.Contains("2-3", Text((DrawingValue)values[ReportTextDrawing.InterimGrades]));
interim.StudentId = Guid.NewGuid();
Assert.Throws<InvalidDataException>(() => Build(new(_date, _date, interim)));
interim.StudentId = _student.Id; interim.GroupId = Guid.NewGuid();
Assert.Throws<InvalidDataException>(() => Build(new(_date, _date, interim)));
interim.GroupId = _group.Id; interim.Date = _date.AddDays(1);
Assert.Throws<InvalidDataException>(() => Build(new(_date, _date, interim)));
}
[Fact]
public void AbgewaehlteBausteineBleibenLeer()
{
var values = Build(new(_date, _date, null, false, false, false, false, false));
Assert.All(StudentReportTemplate.Drawings, name => Assert.Empty(((DrawingValue)values[name]).Commands));
}
[Fact]
public void Hausaufgaben_AltdatenUndFremdeSitzungen()
{
var session = new ParticipationSession { GroupId = _group.Id, Date = _date };
_sessions.Add(session);
_entries.Add(new() { StudentId = _student.Id, SessionId = session.Id, HomeworkMissing = true });
_entries.Add(new() { StudentId = _student.Id, SessionId = Guid.NewGuid(), Homework = HomeworkStatus.Completed });
var text = Text((DrawingValue)Build()[StudentReportTemplate.Homework]);
Assert.Contains("Nicht gemacht", text);
Assert.DoesNotContain("Hausaufgabe gemacht", text);
}
[Fact]
public void Standardbericht_RenderbarMitGraphUndMehrseitigerDokumentation()
{
_aspects.Save(new() { Key = "quality", Label = "Qualität", Weight = 1 });
for (var i = 0; i < 4; i++)
{
var session = new ParticipationSession { GroupId = _group.Id, Date = _date.AddDays(i) };
_sessions.Add(session);
_entries.Add(new() { StudentId = _student.Id, SessionId = session.Id,
Ratings = [new() { Key = "quality", Value = i - 2 }], Homework = HomeworkStatus.Completed });
}
for (var i = 0; i < 55; i++) _docs.Add(new() { StudentId = _student.Id, GroupId = _group.Id,
Date = _date, Title = $"Eintrag {i:D2}", Content = "Lange Beobachtung mit einem ausführlichen Text über die regelmäßige Mitarbeit im Unterricht und die Lernfortschritte." });
var pdf = new QuestTemplateRenderer().RenderToPdf(StudentReportTemplate.Create(), new ReportProvider(Build()));
using var document = UglyToad.PdfPig.PdfDocument.Open(pdf);
Assert.True(document.NumberOfPages > 1);
Assert.All(document.GetPages(), page => Assert.Contains(_student.FullName.Replace(" ", ""), page.Text.Replace(" ", "")));
var text = string.Concat(document.GetPages().Select(p => p.Text)).Replace(" ", "");
Assert.Contains(_student.FullName.Replace(" ", ""), text);
Assert.Contains("Lerngruppe:9ChG", text);
Assert.Contains("GewichteteMitarbeit", text);
Assert.Contains("Eintrag54", text);
Assert.Contains("Lernfortschritte.", text);
}
[Fact]
public async Task Dialog_UngueltigeDatumsangabenErzeugenKeineDatei()
{
var directory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"));
try
{
var vm = new StudentReportDialogViewModel(_student,
new FakeMemberships([new() { StudentId = _student.Id, GroupId = _group.Id }]),
new FakeGroups([_group]), new FakeInterimGrades(), Service(), new QuestTemplateRenderer(),
new SchoolYearService(), [new TemplateStore(directory)]);
vm.From = "31.02.2026";
var output = Path.Combine(directory, "bericht.pdf");
Assert.False(await vm.GenerateAsync(output));
Assert.False(File.Exists(output));
Assert.Contains("TT.MM.JJJJ", vm.StatusMessage);
}
finally { if (Directory.Exists(directory)) Directory.Delete(directory, true); }
}
private sealed class ReportProvider(IReadOnlyDictionary<string, PlaceholderValue> values) : ITemplateDataProvider
{ public IReadOnlyDictionary<string, PlaceholderValue> GetValues() => values; }
private static string Text(DrawingValue drawing) => string.Join(" ", drawing.Commands.OfType<DrawStringEx>().Select(c => c.Text));
}