feat: Anhänge je Stunde, Klausur-Sitzplan mischen, Gefährdungsbeurteilungs-Assistent
Lesson bekommt dieselbe Anhang-Infrastruktur wie Documentation (Material, Arbeitsblätter, Experimentunterlagen), samt Fix einer Sync-Lücke, die Anhang- Dateibytes bisher nur für Documentation statt generisch übertragen hat (IHasAttachments). Sitzplan-Tab bekommt einen "Plätze mischen"-Button für Klausursitzpläne. Neu: mehrschrittiger Gefährdungsbeurteilungs-Assistent mit optionalem KI-Entwurf (ai-backend/gbu.php) und PDF-Export, Format bewusst als JSON-Anhang statt eigener Datenbank-Entität. Details und Architekturentscheidungen in TODO.md (4.2, 7.1.5, 10.1.8). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,283 @@
|
||||
using LehrerApp.Core.Models;
|
||||
using LehrerApp.Desktop.ViewModels.Groups;
|
||||
using Xunit;
|
||||
|
||||
namespace LehrerApp.Desktop.Tests;
|
||||
|
||||
public sealed class HazardAssessmentWizardViewModelTests
|
||||
{
|
||||
private static HazardAssessmentWizardViewModel Build(HazardAssessment? editing = null) => new(editing, "10c");
|
||||
|
||||
[Fact]
|
||||
public void Neu_StartetAufSchritt0OhneKiOhneEditingKennzeichnung()
|
||||
{
|
||||
var vm = Build();
|
||||
|
||||
Assert.Equal(0, vm.StepIndex);
|
||||
Assert.True(vm.IsBasicsStep);
|
||||
Assert.True(vm.IsFirstStep);
|
||||
Assert.Equal("Gefährdungsbeurteilung erstellen", vm.DialogTitle);
|
||||
Assert.False(vm.CanUseAi);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Next_OhneTitel_BleibtAufSchritt0UndSetztFehler()
|
||||
{
|
||||
var vm = Build();
|
||||
|
||||
vm.NextCommand.Execute(null);
|
||||
|
||||
Assert.Equal(0, vm.StepIndex);
|
||||
Assert.NotEqual("", vm.TitleError);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Next_MitTitel_GehtEinenSchrittWeiterUndAktualisiertStepFlags()
|
||||
{
|
||||
var vm = Build();
|
||||
vm.Title = "Natrium in Wasser";
|
||||
|
||||
vm.NextCommand.Execute(null);
|
||||
|
||||
Assert.Equal(1, vm.StepIndex);
|
||||
Assert.True(vm.IsSubstancesStep);
|
||||
Assert.False(vm.IsFirstStep);
|
||||
Assert.Equal("", vm.TitleError);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Back_AufSchritt0_BleibtAufSchritt0()
|
||||
{
|
||||
var vm = Build();
|
||||
|
||||
vm.BackCommand.Execute(null);
|
||||
|
||||
Assert.Equal(0, vm.StepIndex);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DurchlaufAllerSchritte_LandetAufZusammenfassung()
|
||||
{
|
||||
var vm = Build();
|
||||
vm.Title = "Natrium in Wasser";
|
||||
|
||||
for (var i = 0; i < vm.StepCount - 1; i++) vm.NextCommand.Execute(null);
|
||||
|
||||
Assert.True(vm.IsSummaryStep);
|
||||
Assert.True(vm.IsLastStep);
|
||||
|
||||
vm.NextCommand.Execute(null);
|
||||
Assert.True(vm.IsSummaryStep, "Weiter auf dem letzten Schritt darf nicht darüber hinausgehen.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddSubstance_UndRemove_AktualisierenDieListeUndHasSubstances()
|
||||
{
|
||||
var vm = Build();
|
||||
Assert.False(vm.HasSubstances);
|
||||
|
||||
vm.AddSubstanceCommand.Execute(null);
|
||||
Assert.True(vm.HasSubstances);
|
||||
var item = vm.Substances[0];
|
||||
item.Name = "Natrium";
|
||||
item.Amount = "ca. 0,5 g";
|
||||
item.PictogramOptions.First(o => o.Value == GhsPictogram.Flammable).IsSelected = true;
|
||||
|
||||
item.RemoveCommand.Execute(null);
|
||||
|
||||
Assert.False(vm.HasSubstances);
|
||||
Assert.Empty(vm.Substances);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddHazard_TrimmtUndLeertDasEingabefeld()
|
||||
{
|
||||
var vm = Build();
|
||||
|
||||
vm.NewHazard = " Verätzungsgefahr ";
|
||||
vm.AddHazardCommand.Execute(null);
|
||||
|
||||
Assert.Equal(["Verätzungsgefahr"], vm.Hazards);
|
||||
Assert.Equal("", vm.NewHazard);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddHazard_MitLeeremTextTutNichts()
|
||||
{
|
||||
var vm = Build();
|
||||
|
||||
vm.NewHazard = " ";
|
||||
vm.AddHazardCommand.Execute(null);
|
||||
|
||||
Assert.Empty(vm.Hazards);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoveHazard_EntferntGenauDenGenanntenEintrag()
|
||||
{
|
||||
var vm = Build();
|
||||
vm.NewHazard = "A"; vm.AddHazardCommand.Execute(null);
|
||||
vm.NewHazard = "B"; vm.AddHazardCommand.Execute(null);
|
||||
|
||||
vm.RemoveHazardCommand.Execute("A");
|
||||
|
||||
Assert.Equal(["B"], vm.Hazards);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddProtectiveMeasure_TrimmtUndLeertDasEingabefeld()
|
||||
{
|
||||
var vm = Build();
|
||||
|
||||
vm.NewProtectiveMeasure = " Schutzbrille ";
|
||||
vm.AddProtectiveMeasureCommand.Execute(null);
|
||||
|
||||
Assert.Equal(["Schutzbrille"], vm.ProtectiveMeasures);
|
||||
Assert.Equal("", vm.NewProtectiveMeasure);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Save_OhneTitel_SetztFehlerUndSpringtZurueckAufSchritt0()
|
||||
{
|
||||
var vm = Build();
|
||||
vm.Title = "Etwas";
|
||||
vm.NextCommand.Execute(null); // Schritt 1
|
||||
vm.Title = " ";
|
||||
|
||||
vm.SaveCommand.Execute(null);
|
||||
|
||||
Assert.Null(vm.Result);
|
||||
Assert.Equal(0, vm.StepIndex);
|
||||
Assert.NotEqual("", vm.TitleError);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Save_BautVollstaendigesErgebnisAusAllenSchritten()
|
||||
{
|
||||
var vm = Build();
|
||||
vm.Title = "Natrium in Wasser";
|
||||
vm.GroupLabel = "10c";
|
||||
vm.DateText = "12.09.2026";
|
||||
vm.KindName = "Lehrerversuch";
|
||||
vm.Procedure = "Kurzbeschreibung";
|
||||
vm.AddSubstanceCommand.Execute(null);
|
||||
vm.Substances[0].Name = "Natrium";
|
||||
vm.Substances[0].Amount = "ca. 0,5 g";
|
||||
vm.Substances[0].HStatements = "H260";
|
||||
vm.Substances[0].PictogramOptions.First(o => o.Value == GhsPictogram.Flammable).IsSelected = true;
|
||||
vm.NewHazard = "Verpuffung"; vm.AddHazardCommand.Execute(null);
|
||||
vm.NewProtectiveMeasure = "Schutzbrille"; vm.AddProtectiveMeasureCommand.Execute(null);
|
||||
vm.FirstAid = "Wasser spülen";
|
||||
vm.Disposal = "Abreagieren lassen";
|
||||
vm.Notes = "Hinweis";
|
||||
|
||||
vm.SaveCommand.Execute(null);
|
||||
|
||||
Assert.NotNull(vm.Result);
|
||||
var result = vm.Result!;
|
||||
Assert.Equal("Natrium in Wasser", result.Title);
|
||||
Assert.Equal("10c", result.GroupLabel);
|
||||
Assert.Equal(new DateOnly(2026, 9, 12), result.Date);
|
||||
Assert.Equal(ExperimentKind.Lehrerversuch, result.Kind);
|
||||
var substance = Assert.Single(result.Substances);
|
||||
Assert.Equal("Natrium", substance.Name);
|
||||
Assert.Equal([GhsPictogram.Flammable], substance.GhsPictograms);
|
||||
Assert.Equal(["Verpuffung"], result.Hazards);
|
||||
Assert.Equal(["Schutzbrille"], result.ProtectiveMeasures);
|
||||
Assert.Equal("Wasser spülen", result.FirstAid);
|
||||
Assert.Equal("Abreagieren lassen", result.Disposal);
|
||||
Assert.Equal("Hinweis", result.Notes);
|
||||
Assert.False(result.IsAiAssisted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Konstruktor_MitEditingLaedtAlleFelderVor()
|
||||
{
|
||||
var editing = new HazardAssessment
|
||||
{
|
||||
Title = "Bestehende GBU",
|
||||
GroupLabel = "9a",
|
||||
Date = new DateOnly(2026, 1, 15),
|
||||
Kind = ExperimentKind.Schuelerversuch,
|
||||
Procedure = "Ablauf",
|
||||
Substances = [new HazardSubstance { Name = "Salzsäure", GhsPictograms = [GhsPictogram.Corrosive] }],
|
||||
Hazards = ["Verätzung"],
|
||||
ProtectiveMeasures = ["Handschuhe"],
|
||||
FirstAid = "Erste Hilfe Text",
|
||||
Disposal = "Entsorgungstext",
|
||||
Notes = "Notiz",
|
||||
IsAiAssisted = true,
|
||||
};
|
||||
|
||||
var vm = Build(editing);
|
||||
|
||||
Assert.Equal("Gefährdungsbeurteilung bearbeiten", vm.DialogTitle);
|
||||
Assert.Equal("Bestehende GBU", vm.Title);
|
||||
Assert.Equal("9a", vm.GroupLabel);
|
||||
Assert.Equal("15.01.2026", vm.DateText);
|
||||
Assert.Equal("Schülerversuch", vm.KindName);
|
||||
Assert.Equal("Ablauf", vm.Procedure);
|
||||
var substance = Assert.Single(vm.Substances);
|
||||
Assert.Equal("Salzsäure", substance.Name);
|
||||
Assert.True(substance.PictogramOptions.Single(o => o.Value == GhsPictogram.Corrosive).IsSelected);
|
||||
Assert.Equal(["Verätzung"], vm.Hazards);
|
||||
Assert.Equal(["Handschuhe"], vm.ProtectiveMeasures);
|
||||
Assert.Equal("Erste Hilfe Text", vm.FirstAid);
|
||||
Assert.Equal("Entsorgungstext", vm.Disposal);
|
||||
Assert.Equal("Notiz", vm.Notes);
|
||||
Assert.True(vm.IsAiAssisted);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ExperimentKindDisplayTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(ExperimentKind.Lehrerversuch, "Lehrerversuch")]
|
||||
[InlineData(ExperimentKind.Schuelerversuch, "Schülerversuch")]
|
||||
[InlineData(ExperimentKind.Demonstrationsversuch, "Demonstrationsversuch")]
|
||||
public void Label_UndFromLabel_SindZueinanderInvers(ExperimentKind kind, string label)
|
||||
{
|
||||
Assert.Equal(label, ExperimentKindDisplay.Label(kind));
|
||||
Assert.Equal(kind, ExperimentKindDisplay.FromLabel(label));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FromLabel_UnbekanntFaelltAufLehrerversuchZurueck() =>
|
||||
Assert.Equal(ExperimentKind.Lehrerversuch, ExperimentKindDisplay.FromLabel("Unbekannt"));
|
||||
|
||||
[Theory]
|
||||
[InlineData("Schuelerversuch", ExperimentKind.Schuelerversuch)]
|
||||
[InlineData("Demonstrationsversuch", ExperimentKind.Demonstrationsversuch)]
|
||||
[InlineData("etwas anderes", ExperimentKind.Lehrerversuch)]
|
||||
public void FromWireValue_LiestDenUnmarkiertenEnumNamenDerKiAntwort(string wire, ExperimentKind expected) =>
|
||||
Assert.Equal(expected, ExperimentKindDisplay.FromWireValue(wire));
|
||||
}
|
||||
|
||||
public sealed class GhsPictogramDisplayTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(GhsPictogram.Explosive, "GHS01")]
|
||||
[InlineData(GhsPictogram.Corrosive, "GHS05")]
|
||||
[InlineData(GhsPictogram.Environmental, "GHS09")]
|
||||
public void Code_LiefertDenErwartetenGhsCode(GhsPictogram value, string code) =>
|
||||
Assert.Equal(code, GhsPictogramDisplay.Code(value));
|
||||
|
||||
[Fact]
|
||||
public void FromCode_IstFallUnabhaengigUndTrimmt() =>
|
||||
Assert.Equal(GhsPictogram.Corrosive, GhsPictogramDisplay.FromCode(" ghs05 "));
|
||||
|
||||
[Fact]
|
||||
public void FromCode_UnbekannterCodeLiefertNull() =>
|
||||
Assert.Null(GhsPictogramDisplay.FromCode("GHS99"));
|
||||
|
||||
[Fact]
|
||||
public void BuildOptions_MarkiertNurDieUebergebenenAlsAusgewaehlt()
|
||||
{
|
||||
var options = GhsPictogramDisplay.BuildOptions([GhsPictogram.Toxic, GhsPictogram.Flammable]);
|
||||
|
||||
Assert.Equal(9, options.Count);
|
||||
Assert.True(options.Single(o => o.Value == GhsPictogram.Toxic).IsSelected);
|
||||
Assert.True(options.Single(o => o.Value == GhsPictogram.Flammable).IsSelected);
|
||||
Assert.False(options.Single(o => o.Value == GhsPictogram.Corrosive).IsSelected);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using LehrerApp.Core.Interfaces;
|
||||
using LehrerApp.Core.Models;
|
||||
using LehrerApp.Core.Services;
|
||||
using LehrerApp.Desktop.ViewModels.Groups;
|
||||
@@ -18,7 +19,7 @@ public sealed class LessonDialogViewModelTests
|
||||
private static LessonDialogViewModel BuildVm(Guid unitId, Guid groupId, Lesson? editing = null,
|
||||
FakeTimetableSlots? slots = null, PeriodScheduleService? periodSchedule = null) =>
|
||||
new(new FakeLessons(), new FakeShorthandCodes([]), new FakeAlternativeLessonPaths([]),
|
||||
slots ?? new FakeTimetableSlots(), periodSchedule ?? NewPeriodSchedule(),
|
||||
slots ?? new FakeTimetableSlots(), periodSchedule ?? NewPeriodSchedule(), new FakeAttachmentStorage(),
|
||||
unitId, groupId, "10c", "Chemie", [], [], editing);
|
||||
|
||||
/// Nutzer-Feedback: bei einer Klasse, die in mehreren Fächern unterrichtet wird (mehrere
|
||||
@@ -92,7 +93,7 @@ public sealed class LessonDialogViewModelTests
|
||||
{
|
||||
var codes = new FakeShorthandCodes([new ShorthandCode { Code = "Tb" }, new ShorthandCode { Code = "SH" }]);
|
||||
var vm = new LessonDialogViewModel(new FakeLessons(), codes, new FakeAlternativeLessonPaths([]),
|
||||
new FakeTimetableSlots(), NewPeriodSchedule(),
|
||||
new FakeTimetableSlots(), NewPeriodSchedule(), new FakeAttachmentStorage(),
|
||||
Guid.NewGuid(), Guid.NewGuid(), "10c", "Chemie", [], ["Plenum", "LDE", "Tb"], null); // "Tb" doppelt (Katalog + Historie), soll nur einmal erscheinen
|
||||
|
||||
Assert.Equal(["LDE", "Plenum", "SH", "Tb"], vm.ShorthandSuggestions);
|
||||
@@ -155,7 +156,7 @@ public sealed class LessonDialogViewModelTests
|
||||
var groupId = Guid.NewGuid();
|
||||
var lessons = new FakeLessons();
|
||||
var vm = new LessonDialogViewModel(lessons, new FakeShorthandCodes([]), new FakeAlternativeLessonPaths([]),
|
||||
new FakeTimetableSlots(), NewPeriodSchedule(), unitId, groupId, "10c", "Chemie", [], [], null)
|
||||
new FakeTimetableSlots(), NewPeriodSchedule(), new FakeAttachmentStorage(), unitId, groupId, "10c", "Chemie", [], [], null)
|
||||
{
|
||||
Topic = "Brechung", DateText = "01.09.2025", StartTimeText = "11:45",
|
||||
};
|
||||
@@ -179,6 +180,58 @@ public sealed class LessonDialogViewModelTests
|
||||
Assert.Single(lessons.GetByUnit(unitId));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddAttachment_LaedtHochUndSaveUebernimmtIhnInDasErgebnis()
|
||||
{
|
||||
var vm = BuildVm(Guid.NewGuid(), Guid.NewGuid());
|
||||
vm.Topic = "Thema"; vm.DateText = "01.09.2025";
|
||||
|
||||
vm.AddAttachment("gbu-natrium.pdf", new MemoryStream([1, 2, 3]));
|
||||
vm.SaveCommand.Execute(null);
|
||||
|
||||
Assert.NotNull(vm.Result);
|
||||
var attachment = Assert.Single(vm.Result!.Attachments);
|
||||
Assert.Equal("gbu-natrium.pdf", attachment.FileName);
|
||||
Assert.Equal(3, attachment.SizeBytes);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddAttachment_ZuGrosseDateiWirdAbgelehnt()
|
||||
{
|
||||
var vm = BuildVm(Guid.NewGuid(), Guid.NewGuid());
|
||||
|
||||
vm.AddAttachment("riesig.pdf", new MemoryStream(new byte[IAttachmentStorage.MaxSizeBytes + 1]));
|
||||
|
||||
Assert.Empty(vm.Attachments);
|
||||
Assert.NotEqual("", vm.AttachmentError);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RemoveAttachmentCommand_EntferntDenAnhangAusDerListe()
|
||||
{
|
||||
var vm = BuildVm(Guid.NewGuid(), Guid.NewGuid());
|
||||
vm.AddAttachment("gbu-natrium.pdf", new MemoryStream([1, 2, 3]));
|
||||
var item = vm.Attachments[0];
|
||||
|
||||
vm.RemoveAttachmentCommand.Execute(item);
|
||||
|
||||
Assert.Empty(vm.Attachments);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BearbeiteteStunde_LaedtVorhandeneAnhaengeVor()
|
||||
{
|
||||
var editing = new Lesson
|
||||
{
|
||||
Attachments = [new DocumentAttachment { StorageId = "abc", FileName = "gbu.pdf", SizeBytes = 42 }],
|
||||
};
|
||||
|
||||
var vm = BuildVm(Guid.NewGuid(), Guid.NewGuid(), editing);
|
||||
|
||||
var attachment = Assert.Single(vm.Attachments);
|
||||
Assert.Equal("gbu.pdf", attachment.FileName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasHomeworkText_OhneHausaufgabe_IstFalse()
|
||||
{
|
||||
@@ -305,7 +358,7 @@ public sealed class LessonDialogViewModelTests
|
||||
};
|
||||
|
||||
var vm = new LessonDialogViewModel(new FakeLessons(), new FakeShorthandCodes([]), alternativePaths,
|
||||
new FakeTimetableSlots(), NewPeriodSchedule(), Guid.NewGuid(), Guid.NewGuid(), "10c", "Chemie", [], [], editing);
|
||||
new FakeTimetableSlots(), NewPeriodSchedule(), new FakeAttachmentStorage(), Guid.NewGuid(), Guid.NewGuid(), "10c", "Chemie", [], [], editing);
|
||||
|
||||
Assert.True(vm.Phases[0].HasAlternativePath);
|
||||
Assert.Equal("Kurzversion", vm.Phases[0].AlternativePathName);
|
||||
@@ -466,7 +519,7 @@ public sealed class LessonDialogViewModelTests
|
||||
slots.Add(new TimetableSlot { GroupId = groupId, Weekday = DayOfWeek.Tuesday, PeriodNumber = 3 });
|
||||
|
||||
var vm = new LessonDialogViewModel(lessons, new FakeShorthandCodes([]), new FakeAlternativeLessonPaths([]),
|
||||
slots, NewPeriodSchedule(), unitId, groupId, "10c", "Chemie", [], [], null);
|
||||
slots, NewPeriodSchedule(), new FakeAttachmentStorage(), unitId, groupId, "10c", "Chemie", [], [], null);
|
||||
|
||||
// Nächster Dienstag nach dem 06.01.2099 (einem Dienstag) ist der 13.01.2099.
|
||||
Assert.Equal("13.01.2099", vm.DateText);
|
||||
@@ -485,7 +538,7 @@ public sealed class LessonDialogViewModelTests
|
||||
slots.Add(new TimetableSlot { GroupId = groupId, Weekday = weekday, PeriodNumber = 3 });
|
||||
|
||||
var vm = new LessonDialogViewModel(lessons, new FakeShorthandCodes([]), new FakeAlternativeLessonPaths([]),
|
||||
slots, NewPeriodSchedule(), unitId, groupId, "10c", "Chemie", [], [], null);
|
||||
slots, NewPeriodSchedule(), new FakeAttachmentStorage(), unitId, groupId, "10c", "Chemie", [], [], null);
|
||||
|
||||
var suggested = DateOnly.ParseExact(vm.DateText, "dd.MM.yyyy");
|
||||
Assert.True(suggested >= DateOnly.FromDateTime(DateTime.Today));
|
||||
@@ -506,7 +559,7 @@ public sealed class LessonDialogViewModelTests
|
||||
slots.Add(new TimetableSlot { GroupId = groupId, Weekday = DayOfWeek.Tuesday, PeriodNumber = 3 });
|
||||
|
||||
var vm = new LessonDialogViewModel(lessons, new FakeShorthandCodes([]), new FakeAlternativeLessonPaths([]),
|
||||
slots, NewPeriodSchedule(), unitId, groupId, "10c", "Chemie", [], [], null);
|
||||
slots, NewPeriodSchedule(), new FakeAttachmentStorage(), unitId, groupId, "10c", "Chemie", [], [], null);
|
||||
|
||||
Assert.Equal(3, vm.LessonNumber); // frühere Periode, nicht 4
|
||||
}
|
||||
@@ -523,7 +576,7 @@ public sealed class LessonDialogViewModelTests
|
||||
periodSchedule.SetPeriods([new PeriodTimeEntry { PeriodNumber = 2, Start = new TimeOnly(8, 50), End = new TimeOnly(9, 35) }]);
|
||||
|
||||
var vm = new LessonDialogViewModel(new FakeLessons(), new FakeShorthandCodes([]), new FakeAlternativeLessonPaths([]),
|
||||
slots, periodSchedule, unitId, groupId, "10c", "Chemie", [], [], null);
|
||||
slots, periodSchedule, new FakeAttachmentStorage(), unitId, groupId, "10c", "Chemie", [], [], null);
|
||||
|
||||
Assert.Equal(2, vm.LessonNumber);
|
||||
Assert.Equal("08:50", vm.StartTimeText);
|
||||
|
||||
@@ -154,4 +154,32 @@ public sealed class PdfExportServiceTests
|
||||
|
||||
AssertIsPdf(Service.BuildStudentDocumentationPdf(data));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildHazardAssessmentPdf_MitStoffenUndKiHinweis_LiefertGueltigesPdf()
|
||||
{
|
||||
var data = new HazardAssessmentPrintData(
|
||||
"Natrium in Wasser", "10c", "12.09.2026", "Lehrerversuch",
|
||||
"Ein kleines Stück Natrium wird in eine Schale mit Wasser gegeben.",
|
||||
[
|
||||
new HazardSubstancePrintRow("Natrium", "ca. 0,5 g", "GHS02, GHS05",
|
||||
"H260 - Reagiert mit Wasser unter Bildung entzündbarer Gase", "P223, P231+P232, P370+P378"),
|
||||
],
|
||||
["Wasserstoffbildung, Verpuffungsgefahr", "Verätzungsgefahr durch Natronlauge"],
|
||||
["Schutzbrille", "Sicherheitsabstand einhalten", "Abzug/Freiluftversuch"],
|
||||
"Betroffene Stellen mit viel Wasser spülen, Arzt hinzuziehen.",
|
||||
"Reste unter Aufsicht vollständig abreagieren lassen.",
|
||||
"", IsAiAssisted: true);
|
||||
|
||||
AssertIsPdf(Service.BuildHazardAssessmentPdf(data));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildHazardAssessmentPdf_OhneStoffeUndOhneKiHinweis_LiefertGueltigesPdf()
|
||||
{
|
||||
var data = new HazardAssessmentPrintData(
|
||||
"Papierchromatographie", "8b", "", "Schülerversuch", "", [], [], [], "", "", "", IsAiAssisted: false);
|
||||
|
||||
AssertIsPdf(Service.BuildHazardAssessmentPdf(data));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -542,4 +542,69 @@ public sealed class SeatingPlanViewModelTests
|
||||
Assert.Equal("Sitzplan", session.Comment);
|
||||
Assert.Equal(session.Id, vm.SelectedSession?.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShuffleSeats_ErzeugtNeuenPlanMitVertauschtenPlaetzenUndBehaeltDenUrsprungsplan()
|
||||
{
|
||||
var groupId = Guid.NewGuid();
|
||||
var anna = new Student { FirstName = "Anna", LastName = "A" };
|
||||
var ben = new Student { FirstName = "Ben", LastName = "B" };
|
||||
var cara = new Student { FirstName = "Cara", LastName = "C" };
|
||||
var students = new FakeStudents([anna, ben, cara]);
|
||||
var memberships = new FakeMemberships([
|
||||
new GroupMembership { GroupId = groupId, StudentId = anna.Id },
|
||||
new GroupMembership { GroupId = groupId, StudentId = ben.Id },
|
||||
new GroupMembership { GroupId = groupId, StudentId = cara.Id },
|
||||
]);
|
||||
var plan = new SeatingPlan
|
||||
{
|
||||
GroupId = groupId, Name = "Klausur", Room = "B204", Rows = 1, Columns = 3,
|
||||
Assignments =
|
||||
[
|
||||
new SeatAssignment { Row = 0, Column = 0, StudentId = anna.Id },
|
||||
new SeatAssignment { Row = 0, Column = 1, StudentId = ben.Id },
|
||||
new SeatAssignment { Row = 0, Column = 2, StudentId = cara.Id },
|
||||
],
|
||||
};
|
||||
var plans = new FakeSeatingPlans([plan]);
|
||||
var vm = new SeatingPlanTabViewModel(plans, students, memberships,
|
||||
new FakeSessions([]), new FakeEntries(), new FakeAspects());
|
||||
vm.Initialize(groupId, isReadOnly: false);
|
||||
vm.IsEditMode = true;
|
||||
|
||||
vm.ShuffleSeatsCommand.Execute(null);
|
||||
|
||||
var all = plans.GetByGroup(groupId);
|
||||
Assert.Equal(2, all.Count);
|
||||
var shuffled = Assert.Single(all, p => p.Id != plan.Id);
|
||||
Assert.Equal("B204", shuffled.Room);
|
||||
Assert.Equal(1, shuffled.Rows);
|
||||
Assert.Equal(3, shuffled.Columns);
|
||||
// Dieselben drei Schüler auf denselben drei Koordinaten, nur die Zuordnung untereinander
|
||||
// wurde neu gewürfelt.
|
||||
Assert.Equal(new[] { anna.Id, ben.Id, cara.Id }.OrderBy(id => id),
|
||||
shuffled.Assignments.Select(a => a.StudentId).OrderBy(id => id));
|
||||
Assert.Equal(new[] { (0, 0), (0, 1), (0, 2) },
|
||||
shuffled.Assignments.Select(a => (a.Row, a.Column)).OrderBy(t => t.Item2));
|
||||
// Ursprungsplan bleibt unverändert erhalten.
|
||||
Assert.Equal(3, plans.GetById(plan.Id)!.Assignments.Count);
|
||||
Assert.Equal(vm.SelectedPlan?.Id, shuffled.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShuffleSeatsCommand_OhneBearbeitungsmodus_NichtAusfuehrbar()
|
||||
{
|
||||
var groupId = Guid.NewGuid();
|
||||
var plan = new SeatingPlan { GroupId = groupId, Name = "Standard", Rows = 1, Columns = 1 };
|
||||
var vm = new SeatingPlanTabViewModel(
|
||||
new FakeSeatingPlans([plan]), new FakeStudents([]), new FakeMemberships([]),
|
||||
new FakeSessions([]), new FakeEntries(), new FakeAspects());
|
||||
vm.Initialize(groupId, isReadOnly: false);
|
||||
|
||||
Assert.False(vm.ShuffleSeatsCommand.CanExecute(null));
|
||||
|
||||
vm.IsEditMode = true;
|
||||
|
||||
Assert.True(vm.ShuffleSeatsCommand.CanExecute(null));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user