Dunkelmodus über neuen Einstellungen-Tab "Darstellung" (Systemvorgabe/Hell/Dunkel), Fenstergröße/Maximiert-Status wird über Sitzungen hinweg gemerkt (bewusst ohne Fensterposition), und ein generischer Snapshot-basierter Papierkorb (30 Tage) für Sitzpläne, Noten, Notenschlüssel-Vorlagen, Aufgaben und Zeiteinträge. Details und bewusste Scope-Entscheidungen (Spaltenbreiten zurückgestellt, Farb-Audit für Dunkelmodus offen, welche Entitäten der Papierkorb abdeckt) in TODO.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
497 lines
21 KiB
C#
497 lines
21 KiB
C#
using LehrerApp.Core.Models;
|
|
using LehrerApp.Core.Services;
|
|
using LehrerApp.Data;
|
|
using LehrerApp.Desktop.Services;
|
|
using LehrerApp.Desktop.ViewModels.Settings;
|
|
using LehrerApp.Sync;
|
|
using LehrerApp.Sync.Models;
|
|
using System.Linq;
|
|
using Xunit;
|
|
|
|
namespace LehrerApp.Desktop.Tests;
|
|
|
|
public sealed class SettingsViewModelTests
|
|
{
|
|
private static SettingsViewModel BuildViewModel(FakeSchoolHolidays? holidays = null,
|
|
FakeSupervisionDuties? supervisionDuties = null,
|
|
FakeSubjects? subjects = null, FakeCompetencyDomains? competencyDomains = null,
|
|
EventQueue? eventQueue = null, SyncKeyStatus? syncKeyStatus = null,
|
|
SyncKeyRecoveryService? syncKeyRecovery = null, AppearanceSettingsService? appearance = null,
|
|
TrashViewModel? trashTab = null)
|
|
{
|
|
// Bewusst kein "using": SchoolCalendarSettingsService liest den Pfad erst bei SetState,
|
|
// das Verzeichnis muss über die Lebensdauer des ViewModels bestehen bleiben.
|
|
var tempPath = System.IO.Path.Combine(
|
|
System.IO.Path.GetTempPath(), $"lehrerapp-settingsvm-tests-{Guid.NewGuid():N}");
|
|
Directory.CreateDirectory(tempPath);
|
|
|
|
return new SettingsViewModel(
|
|
subjects ?? new FakeSubjects([]), competencyDomains ?? new FakeCompetencyDomains(), new FakeGradingKeyTemplates(),
|
|
new FakeSchemes(), new GradingService(), new BackupService(tempPath),
|
|
new DatabaseEncryptionService(), new AppLockService(tempPath),
|
|
new LiteDbContext(new MemoryStream()), new PrivacySettingsService(tempPath),
|
|
new FakeDocumentation(), new FakeStudents([]), new FakeShorthandCodes([]),
|
|
holidays ?? new FakeSchoolHolidays(), new SchoolCalendarSettingsService(tempPath),
|
|
new PeriodScheduleService(tempPath), supervisionDuties ?? new FakeSupervisionDuties(),
|
|
new LetterTemplateService(tempPath), TestSupport.BuildAiSettingsService(), TestSupport.BuildAiPlanningService(),
|
|
TestSupport.BuildSyncSettingsService(), TestSupport.BuildSyncAuthService(),
|
|
eventQueue ?? TestSupport.BuildEventQueue(), TestSupport.BuildAppLogger(),
|
|
syncKeyStatus ?? TestSupport.BuildSyncKeyStatus(),
|
|
syncKeyRecovery ?? TestSupport.BuildSyncKeyRecoveryService(),
|
|
appearance ?? TestSupport.BuildAppearanceSettingsService(),
|
|
trashTab ?? TestSupport.BuildTrashViewModel());
|
|
}
|
|
|
|
// ── Darstellung (12.4) ────────────────────────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public void SelectedTheme_SpiegeltDenGespeichertenStandBeimStartWider()
|
|
{
|
|
var appearance = TestSupport.BuildAppearanceSettingsService();
|
|
appearance.Save(AppTheme.Dark);
|
|
|
|
var vm = BuildViewModel(appearance: appearance);
|
|
|
|
Assert.Equal(AppTheme.Dark, vm.SelectedTheme);
|
|
Assert.Equal("Dunkel", vm.SelectedThemeName);
|
|
}
|
|
|
|
[Fact]
|
|
public void SelectedThemeName_Aendern_PersistiertUndBenachrichtigtDenCodeBehind()
|
|
{
|
|
var appearance = TestSupport.BuildAppearanceSettingsService();
|
|
var vm = BuildViewModel(appearance: appearance);
|
|
AppTheme? notified = null;
|
|
vm.OnThemeChanged = t => notified = t;
|
|
|
|
vm.SelectedThemeName = "Dunkel";
|
|
|
|
Assert.Equal(AppTheme.Dark, notified);
|
|
Assert.Equal(AppTheme.Dark, appearance.Load());
|
|
}
|
|
|
|
// ── Sync-Schlüssel: Warnung + Wiederherstellungscode (10.3.2) ────────────────────────────
|
|
//
|
|
// Der "glückliche" Redeem-Pfad (gültiger Code + Datei + Bestätigung) endet in
|
|
// AppBootstrapper.RestartApplication(), das Environment.Exit(0) aufruft - das darf ein Test
|
|
// niemals auslösen (würde den Testlauf beenden). Deshalb sind hier nur die Zweige getestet,
|
|
// die VOR diesem Aufruf zurückkehren; genau dasselbe Muster gilt bereits für
|
|
// RedeemPairingCode, das aus demselben Grund ebenfalls keinen Erfolgstest hat. Der
|
|
// eigentliche Kryptografie-/Datei-Roundtrip ist in SyncKeyRecoveryServiceTests abgedeckt.
|
|
|
|
[Fact]
|
|
public void SyncKeyWasRegenerated_SpiegeltDenBeimStartErkanntenStatusWider()
|
|
{
|
|
var vm = BuildViewModel(syncKeyStatus: new SyncKeyStatus(keyWasRegenerated: true));
|
|
|
|
Assert.True(vm.SyncKeyWasRegenerated);
|
|
}
|
|
|
|
[Fact]
|
|
public void SyncKeyWasRegenerated_NormalerStart_IstFalse()
|
|
{
|
|
var vm = BuildViewModel(syncKeyStatus: new SyncKeyStatus(keyWasRegenerated: false));
|
|
|
|
Assert.False(vm.SyncKeyWasRegenerated);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CreateRecoveryCode_SpeichernErfolgreich_ZeigtCodeUndUebergibtDateiinhalt()
|
|
{
|
|
var vm = BuildViewModel();
|
|
string? savedContent = null;
|
|
vm.OnSaveRecoveryFile = content => { savedContent = content; return Task.FromResult(true); };
|
|
|
|
await vm.CreateRecoveryCodeCommand.ExecuteAsync(null);
|
|
|
|
Assert.NotEmpty(vm.RecoveryCode);
|
|
Assert.NotNull(savedContent);
|
|
Assert.Contains("notieren", vm.RecoveryStatus);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CreateRecoveryCode_AbbruchBeimSpeichern_ZeigtKeinenCode()
|
|
{
|
|
var vm = BuildViewModel();
|
|
vm.OnSaveRecoveryFile = _ => Task.FromResult(false);
|
|
|
|
await vm.CreateRecoveryCodeCommand.ExecuteAsync(null);
|
|
|
|
Assert.Empty(vm.RecoveryCode);
|
|
Assert.Equal("Abgebrochen.", vm.RecoveryStatus);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task RedeemRecoveryCode_LeererCode_FragtNieNachEinerDatei()
|
|
{
|
|
var vm = BuildViewModel();
|
|
var filePickerCalled = false;
|
|
vm.OnPickRecoveryFile = () => { filePickerCalled = true; return Task.FromResult<string?>(null); };
|
|
vm.RecoveryCodeInput = " ";
|
|
|
|
await vm.RedeemRecoveryCodeCommand.ExecuteAsync(null);
|
|
|
|
Assert.False(filePickerCalled);
|
|
Assert.Equal("Bitte Code eingeben.", vm.RecoveryStatus);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task RedeemRecoveryCode_DateiauswahlAbgebrochen_TutNichts()
|
|
{
|
|
var vm = BuildViewModel();
|
|
vm.OnPickRecoveryFile = () => Task.FromResult<string?>(null);
|
|
vm.RecoveryCodeInput = "IRGENDEIN-CODE";
|
|
|
|
var exception = await Record.ExceptionAsync(() => vm.RedeemRecoveryCodeCommand.ExecuteAsync(null));
|
|
|
|
Assert.Null(exception);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task RedeemRecoveryCode_FalscherCode_ZeigtFehlerUndWirftNicht()
|
|
{
|
|
var recovery = TestSupport.BuildSyncKeyRecoveryService();
|
|
var vm = BuildViewModel(syncKeyRecovery: recovery);
|
|
vm.OnPickRecoveryFile = () => Task.FromResult<string?>(recovery.CreateRecoveryFile("RICHTIGER-CODE"));
|
|
vm.OnConfirmRecoveryRestore = () => Task.FromResult(true);
|
|
vm.RecoveryCodeInput = "FALSCHER-CODE";
|
|
|
|
var exception = await Record.ExceptionAsync(() => vm.RedeemRecoveryCodeCommand.ExecuteAsync(null));
|
|
|
|
Assert.Null(exception);
|
|
Assert.Contains("passen nicht zusammen", vm.RecoveryStatus);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task RedeemRecoveryCode_BestaetigungAbgelehnt_TutNichts()
|
|
{
|
|
var recovery = TestSupport.BuildSyncKeyRecoveryService();
|
|
var vm = BuildViewModel(syncKeyRecovery: recovery);
|
|
vm.OnPickRecoveryFile = () => Task.FromResult<string?>(recovery.CreateRecoveryFile("EIN-CODE"));
|
|
vm.OnConfirmRecoveryRestore = () => Task.FromResult(false);
|
|
vm.RecoveryCodeInput = "EIN-CODE";
|
|
|
|
var exception = await Record.ExceptionAsync(() => vm.RedeemRecoveryCodeCommand.ExecuteAsync(null));
|
|
|
|
Assert.Null(exception);
|
|
}
|
|
|
|
[Fact]
|
|
public void SyncConflicts_ZeigtUnreviewedKonflikteBeimLaden()
|
|
{
|
|
var queue = TestSupport.BuildEventQueue();
|
|
var conflict = new ConflictEntry
|
|
{
|
|
LocalEvent = new SyncEvent { EntityType = "Student", EntityId = Guid.NewGuid().ToString(), Operation = "Save" },
|
|
RemoteEvent = new SyncEvent { EntityType = "Student", EntityId = Guid.NewGuid().ToString(), Operation = "Save" },
|
|
Resolution = "RemoteWon",
|
|
};
|
|
queue.AddConflict(conflict);
|
|
|
|
var vm = BuildViewModel(eventQueue: queue);
|
|
|
|
var item = Assert.Single(vm.SyncConflicts);
|
|
Assert.Equal(conflict.Id, item.Id);
|
|
Assert.Contains("anderen Gerät", item.ResolutionDisplay);
|
|
}
|
|
|
|
[Fact]
|
|
public void MarkConflictReviewed_EntferntKonfliktAusListeUndAusDerQueue()
|
|
{
|
|
var queue = TestSupport.BuildEventQueue();
|
|
var conflict = new ConflictEntry
|
|
{
|
|
LocalEvent = new SyncEvent { EntityType = "Student", EntityId = Guid.NewGuid().ToString(), Operation = "Save" },
|
|
RemoteEvent = new SyncEvent { EntityType = "Student", EntityId = Guid.NewGuid().ToString(), Operation = "Save" },
|
|
Resolution = "LocalWon",
|
|
};
|
|
queue.AddConflict(conflict);
|
|
var vm = BuildViewModel(eventQueue: queue);
|
|
var item = vm.SyncConflicts.Single();
|
|
|
|
vm.MarkConflictReviewedCommand.Execute(item);
|
|
|
|
Assert.Empty(vm.SyncConflicts);
|
|
Assert.Empty(queue.GetUnreviewed());
|
|
}
|
|
|
|
[Fact]
|
|
public void AddSchoolHoliday_GueltigeEingabe_WirdGespeichertUndInListeAngezeigt()
|
|
{
|
|
var holidays = new FakeSchoolHolidays();
|
|
var vm = BuildViewModel(holidays);
|
|
vm.NewHolidayName = "Sommerferien";
|
|
vm.NewHolidayStartText = "01.07.2026";
|
|
vm.NewHolidayEndText = "10.08.2026";
|
|
|
|
vm.AddSchoolHolidayCommand.Execute(null);
|
|
|
|
Assert.Single(holidays.GetAll());
|
|
Assert.Single(vm.SchoolHolidayEntries);
|
|
}
|
|
|
|
[Fact]
|
|
public void AddSchoolHoliday_EndeVorBeginn_SetztFehlerUndSpeichertNicht()
|
|
{
|
|
var holidays = new FakeSchoolHolidays();
|
|
var vm = BuildViewModel(holidays);
|
|
vm.NewHolidayName = "Ungültig";
|
|
vm.NewHolidayStartText = "10.08.2026";
|
|
vm.NewHolidayEndText = "01.07.2026";
|
|
|
|
vm.AddSchoolHolidayCommand.Execute(null);
|
|
|
|
Assert.Empty(holidays.GetAll());
|
|
Assert.NotEqual("", vm.HolidayDateError);
|
|
}
|
|
|
|
[Fact]
|
|
public void AddSchoolHoliday_FehlenderName_SetztFehlerUndSpeichertNicht()
|
|
{
|
|
var holidays = new FakeSchoolHolidays();
|
|
var vm = BuildViewModel(holidays);
|
|
vm.NewHolidayStartText = "01.07.2026";
|
|
vm.NewHolidayEndText = "10.08.2026";
|
|
|
|
vm.AddSchoolHolidayCommand.Execute(null);
|
|
|
|
Assert.Empty(holidays.GetAll());
|
|
Assert.NotEqual("", vm.HolidayNameError);
|
|
}
|
|
|
|
[Fact]
|
|
public void RemoveSchoolHoliday_EntferntEintragAusRepositoryUndListe()
|
|
{
|
|
var holidays = new FakeSchoolHolidays();
|
|
var holiday = new SchoolHoliday { Name = "Herbstferien", StartDate = new DateOnly(2026, 10, 12), EndDate = new DateOnly(2026, 10, 24) };
|
|
holidays.Add(holiday);
|
|
var vm = BuildViewModel(holidays);
|
|
|
|
vm.RemoveSchoolHolidayCommand.Execute(vm.SchoolHolidayEntries[0]);
|
|
|
|
Assert.Empty(holidays.GetAll());
|
|
Assert.Empty(vm.SchoolHolidayEntries);
|
|
}
|
|
|
|
[Fact]
|
|
public void SelectedStateName_Aendern_PersistiertUeberSchoolCalendarSettings()
|
|
{
|
|
var tempPath = System.IO.Path.Combine(
|
|
System.IO.Path.GetTempPath(), $"lehrerapp-settingsvm-state-tests-{Guid.NewGuid():N}");
|
|
Directory.CreateDirectory(tempPath);
|
|
var calendarSettings = new SchoolCalendarSettingsService(tempPath);
|
|
|
|
var vm = new SettingsViewModel(
|
|
new FakeSubjects([]), new FakeCompetencyDomains(), new FakeGradingKeyTemplates(),
|
|
new FakeSchemes(), new GradingService(), new BackupService(tempPath),
|
|
new DatabaseEncryptionService(), new AppLockService(tempPath),
|
|
new LiteDbContext(new MemoryStream()), new PrivacySettingsService(tempPath),
|
|
new FakeDocumentation(), new FakeStudents([]), new FakeShorthandCodes([]),
|
|
new FakeSchoolHolidays(), calendarSettings, new PeriodScheduleService(tempPath),
|
|
new FakeSupervisionDuties(), new LetterTemplateService(tempPath), TestSupport.BuildAiSettingsService(), TestSupport.BuildAiPlanningService(),
|
|
TestSupport.BuildSyncSettingsService(), TestSupport.BuildSyncAuthService(), TestSupport.BuildEventQueue(),
|
|
TestSupport.BuildAppLogger(), TestSupport.BuildSyncKeyStatus(), TestSupport.BuildSyncKeyRecoveryService(),
|
|
TestSupport.BuildAppearanceSettingsService(), TestSupport.BuildTrashViewModel());
|
|
|
|
vm.SelectedStateName = "Bayern";
|
|
|
|
Assert.Equal(GermanState.BY, calendarSettings.State);
|
|
}
|
|
|
|
[Fact]
|
|
public void SavePeriodTimes_GueltigeEingabe_WirdPersistiert()
|
|
{
|
|
var tempPath = System.IO.Path.Combine(
|
|
System.IO.Path.GetTempPath(), $"lehrerapp-settingsvm-periods-tests-{Guid.NewGuid():N}");
|
|
Directory.CreateDirectory(tempPath);
|
|
var periodSchedule = new PeriodScheduleService(tempPath);
|
|
|
|
var vm = new SettingsViewModel(
|
|
new FakeSubjects([]), new FakeCompetencyDomains(), new FakeGradingKeyTemplates(),
|
|
new FakeSchemes(), new GradingService(), new BackupService(tempPath),
|
|
new DatabaseEncryptionService(), new AppLockService(tempPath),
|
|
new LiteDbContext(new MemoryStream()), new PrivacySettingsService(tempPath),
|
|
new FakeDocumentation(), new FakeStudents([]), new FakeShorthandCodes([]),
|
|
new FakeSchoolHolidays(), new SchoolCalendarSettingsService(tempPath), periodSchedule,
|
|
new FakeSupervisionDuties(), new LetterTemplateService(tempPath), TestSupport.BuildAiSettingsService(), TestSupport.BuildAiPlanningService(),
|
|
TestSupport.BuildSyncSettingsService(), TestSupport.BuildSyncAuthService(), TestSupport.BuildEventQueue(),
|
|
TestSupport.BuildAppLogger(), TestSupport.BuildSyncKeyStatus(), TestSupport.BuildSyncKeyRecoveryService(),
|
|
TestSupport.BuildAppearanceSettingsService(), TestSupport.BuildTrashViewModel());
|
|
|
|
vm.PeriodTimes[0].StartText = "08:00";
|
|
vm.PeriodTimes[0].EndText = "08:45";
|
|
|
|
vm.SavePeriodTimesCommand.Execute(null);
|
|
|
|
Assert.Equal("", vm.PeriodTimesError);
|
|
Assert.Equal(45, periodSchedule.GetDurationMinutes(1));
|
|
}
|
|
|
|
[Fact]
|
|
public void SavePeriodTimes_EndeVorBeginn_SetztFehlerUndSpeichertNicht()
|
|
{
|
|
var tempPath = System.IO.Path.Combine(
|
|
System.IO.Path.GetTempPath(), $"lehrerapp-settingsvm-periods-invalid-tests-{Guid.NewGuid():N}");
|
|
Directory.CreateDirectory(tempPath);
|
|
var periodSchedule = new PeriodScheduleService(tempPath);
|
|
|
|
var vm = new SettingsViewModel(
|
|
new FakeSubjects([]), new FakeCompetencyDomains(), new FakeGradingKeyTemplates(),
|
|
new FakeSchemes(), new GradingService(), new BackupService(tempPath),
|
|
new DatabaseEncryptionService(), new AppLockService(tempPath),
|
|
new LiteDbContext(new MemoryStream()), new PrivacySettingsService(tempPath),
|
|
new FakeDocumentation(), new FakeStudents([]), new FakeShorthandCodes([]),
|
|
new FakeSchoolHolidays(), new SchoolCalendarSettingsService(tempPath), periodSchedule,
|
|
new FakeSupervisionDuties(), new LetterTemplateService(tempPath), TestSupport.BuildAiSettingsService(), TestSupport.BuildAiPlanningService(),
|
|
TestSupport.BuildSyncSettingsService(), TestSupport.BuildSyncAuthService(), TestSupport.BuildEventQueue(),
|
|
TestSupport.BuildAppLogger(), TestSupport.BuildSyncKeyStatus(), TestSupport.BuildSyncKeyRecoveryService(),
|
|
TestSupport.BuildAppearanceSettingsService(), TestSupport.BuildTrashViewModel());
|
|
|
|
vm.PeriodTimes[0].StartText = "08:45";
|
|
vm.PeriodTimes[0].EndText = "08:00";
|
|
|
|
vm.SavePeriodTimesCommand.Execute(null);
|
|
|
|
Assert.NotEqual("", vm.PeriodTimesError);
|
|
Assert.Equal(0, periodSchedule.GetDurationMinutes(1));
|
|
}
|
|
|
|
[Fact]
|
|
public void AddSupervisionDuty_GueltigeEingabe_WirdGespeichertUndInListeAngezeigt()
|
|
{
|
|
var duties = new FakeSupervisionDuties();
|
|
var vm = BuildViewModel(supervisionDuties: duties);
|
|
vm.NewDutyWeekdayName = "Montag";
|
|
vm.NewDutyAfterPeriod = 2;
|
|
vm.NewDutyLocation = "Pausenhof";
|
|
|
|
vm.AddSupervisionDutyCommand.Execute(null);
|
|
|
|
Assert.Single(duties.GetAll());
|
|
Assert.Single(vm.SupervisionDuties);
|
|
Assert.Equal(DayOfWeek.Monday, duties.GetAll()[0].Weekday);
|
|
Assert.Equal(2, duties.GetAll()[0].AfterPeriod);
|
|
}
|
|
|
|
[Fact]
|
|
public void AddSupervisionDuty_FehlenderOrt_SetztFehlerUndSpeichertNicht()
|
|
{
|
|
var duties = new FakeSupervisionDuties();
|
|
var vm = BuildViewModel(supervisionDuties: duties);
|
|
vm.NewDutyWeekdayName = "Montag";
|
|
vm.NewDutyAfterPeriod = 2;
|
|
|
|
vm.AddSupervisionDutyCommand.Execute(null);
|
|
|
|
Assert.Empty(duties.GetAll());
|
|
Assert.NotEqual("", vm.NewDutyError);
|
|
}
|
|
|
|
[Fact]
|
|
public void AddSupervisionDuty_PauseBereitsBelegt_ZeigtFreundlicheFehlermeldung()
|
|
{
|
|
var duties = new FakeSupervisionDuties();
|
|
duties.Add(new SupervisionDuty { Weekday = DayOfWeek.Monday, AfterPeriod = 2, Location = "Pausenhof" });
|
|
var vm = BuildViewModel(supervisionDuties: duties);
|
|
vm.NewDutyWeekdayName = "Montag";
|
|
vm.NewDutyAfterPeriod = 2;
|
|
vm.NewDutyLocation = "Bibliothek";
|
|
|
|
vm.AddSupervisionDutyCommand.Execute(null);
|
|
|
|
Assert.Single(duties.GetAll());
|
|
Assert.Equal("Für diese Pause ist bereits eine Aufsicht eingetragen.", vm.NewDutyError);
|
|
}
|
|
|
|
[Fact]
|
|
public void RemoveSupervisionDuty_EntferntEintragAusRepositoryUndListe()
|
|
{
|
|
var duties = new FakeSupervisionDuties();
|
|
duties.Add(new SupervisionDuty { Weekday = DayOfWeek.Monday, AfterPeriod = 2, Location = "Pausenhof" });
|
|
var vm = BuildViewModel(supervisionDuties: duties);
|
|
|
|
vm.RemoveSupervisionDutyCommand.Execute(vm.SupervisionDuties[0]);
|
|
|
|
Assert.Empty(duties.GetAll());
|
|
Assert.Empty(vm.SupervisionDuties);
|
|
}
|
|
|
|
[Fact]
|
|
public void Kompetenzbereiche_Umsortieren_PersistiertSortOrder()
|
|
{
|
|
var subject = new Subject { Name = "Biologie", ShortName = "Bio" };
|
|
var repository = new FakeCompetencyDomains();
|
|
repository.Add(new CompetencyDomain
|
|
{
|
|
SubjectId = subject.Id, GradeLevel = 10, Name = "Erster Bereich", SortOrder = 0,
|
|
});
|
|
repository.Add(new CompetencyDomain
|
|
{
|
|
SubjectId = subject.Id, GradeLevel = 10, Name = "Zweiter Bereich", SortOrder = 1,
|
|
});
|
|
var vm = BuildViewModel(subjects: new FakeSubjects([subject]), competencyDomains: repository);
|
|
vm.CatalogSubject = vm.Subjects.Single();
|
|
|
|
vm.Domains[1].MoveUpCommand.Execute(null);
|
|
|
|
Assert.Equal(["Zweiter Bereich", "Erster Bereich"], vm.Domains.Select(x => x.Name));
|
|
var stored = repository.GetBySubjectAndGrade(subject.Id, 10);
|
|
Assert.Equal(["Zweiter Bereich", "Erster Bereich"], stored.Select(x => x.Name));
|
|
Assert.Equal([0, 1], stored.Select(x => x.SortOrder));
|
|
}
|
|
|
|
[Fact]
|
|
public void Kompetenzen_Umsortieren_PersistiertSortOrder()
|
|
{
|
|
var repository = new FakeCompetencyDomains();
|
|
var domain = new CompetencyDomain
|
|
{
|
|
Name = "Bereich",
|
|
Items =
|
|
[
|
|
new CompetencyItem { Code = "A", Description = "Erste", SortOrder = 0 },
|
|
new CompetencyItem { Code = "B", Description = "Zweite", SortOrder = 1 },
|
|
],
|
|
};
|
|
repository.Add(domain);
|
|
var vm = new DomainEditItem(domain, repository);
|
|
|
|
vm.Items[1].MoveUpCommand.Execute(null);
|
|
|
|
Assert.Equal(["B", "A"], vm.Items.Select(x => x.Code));
|
|
var stored = repository.GetById(domain.Id)!;
|
|
Assert.Equal(["B", "A"], stored.Items.Select(x => x.Code));
|
|
Assert.Equal([0, 1], stored.Items.Select(x => x.SortOrder));
|
|
}
|
|
|
|
[Fact]
|
|
public void KatalogKopieren_BereitetSichereVorschauFuerZielstufeVor()
|
|
{
|
|
var subject = new Subject { Name = "Biologie", ShortName = "Bio" };
|
|
var repository = new FakeCompetencyDomains();
|
|
repository.Add(new CompetencyDomain
|
|
{
|
|
SubjectId = subject.Id,
|
|
GradeLevel = 10,
|
|
Name = "Erkenntnisgewinnung",
|
|
Code = "EG",
|
|
Items = [new CompetencyItem { Code = "EG1", Description = "Modelle verwenden" }],
|
|
});
|
|
var vm = BuildViewModel(subjects: new FakeSubjects([subject]), competencyDomains: repository);
|
|
vm.CatalogSubject = vm.Subjects.Single();
|
|
|
|
var preview = vm.PrepareCatalogCopy(11);
|
|
|
|
Assert.NotNull(preview);
|
|
Assert.Equal(11, preview.GradeLevel);
|
|
Assert.Equal(1, preview.NewDomains);
|
|
Assert.Equal(1, preview.NewCompetencies);
|
|
|
|
vm.ApplyCatalogImport(preview, CompetencyCatalogImportMode.Merge, new HashSet<string>());
|
|
var copied = repository.GetBySubjectAndGrade(subject.Id, 11);
|
|
Assert.Single(copied);
|
|
Assert.NotEqual(repository.GetBySubjectAndGrade(subject.Id, 10)[0].Id, copied[0].Id);
|
|
Assert.Equal("EG1", copied[0].Items.Single().Code);
|
|
}
|
|
}
|