Files
LehrerApp/LehrerApp.Desktop.Tests/WebUntisRestSyncProcessorTests.cs
T
2026-09-20 22:17:29 +02:00

140 lines
7.2 KiB
C#

using LehrerApp.Core.Interfaces;
using LehrerApp.Core.Models;
using LehrerApp.Core.Services;
using LehrerApp.Desktop.Services;
using Xunit;
namespace LehrerApp.Desktop.Tests;
public sealed class WebUntisRestSyncProcessorTests
{
private static readonly DateOnly Monday = new(2026, 9, 28);
[Fact]
public void Process_AusfallImDoppelblock_SchreibtBeideStundenUndIstIdempotent()
{
var group = new LearningGroup { Id = Guid.NewGuid(), Name = "8c" };
var mappings = new FakeUntisSlotMappings();
mappings.Add(new UntisSlotMapping
{
Weekday = DayOfWeek.Tuesday, StartTime = new TimeOnly(9, 40), Summary = "NAT",
ClassToken = "8c", Kind = SubstitutionKind.Lesson, GroupId = group.Id,
PeriodNumber = 3, CoveredPeriods = [3, 4], Confirmed = true,
});
var substitutions = new FakeSubstitutionEntries();
var processor = BuildProcessor(mappings, substitutions, new RecordingExams(), [group]);
var cancelled = Entry([100, 101], "2026-09-29T09:40", "2026-09-29T11:10",
"NORMAL_TEACHING_PERIOD", "CANCELLED", "NAT", "8c");
var first = processor.Process([cancelled], Monday, Monday.AddDays(13), Monday.AddDays(27));
var second = processor.Process([cancelled], Monday, Monday.AddDays(13), Monday.AddDays(27));
Assert.Equal(2, first.SubstitutionChanges);
Assert.Equal(0, second.SubstitutionChanges);
Assert.Equal([3, 4], substitutions.GetAll().Select(entry => entry.PeriodNumber).Order().ToArray());
Assert.All(substitutions.GetAll(), entry => Assert.Equal(SubstitutionKind.Cancelled, entry.Kind));
}
[Fact]
public void Process_Exam_LegtArbeitMitTitelAnUndEntferntSieNachVerschwinden()
{
var group = new LearningGroup { Id = Guid.NewGuid(), Name = "Mat_E" };
var mappings = new FakeUntisSlotMappings();
mappings.Add(new UntisSlotMapping
{
Weekday = DayOfWeek.Tuesday, StartTime = new TimeOnly(9, 40), Summary = "Mat_E",
ClassToken = "10a;10b;10c;Gastro", Kind = SubstitutionKind.Lesson, GroupId = group.Id,
PeriodNumber = 3, CoveredPeriods = [3, 4], Confirmed = true,
});
var exams = new RecordingExams();
var processor = BuildProcessor(mappings, new FakeSubstitutionEntries(), exams, [group]);
var exam = Entry([2002979, 2002982], "2026-09-29T09:40", "2026-09-29T11:10",
"EXAM", "REGULAR", "Mat_E", "10a", "Arbeit 1. Mathearbeit");
var imported = processor.Process([exam], Monday, Monday.AddDays(13), Monday.AddDays(27));
var unchanged = processor.Process([exam], Monday, Monday.AddDays(13), Monday.AddDays(27));
var removed = processor.Process([], Monday, Monday.AddDays(13), Monday.AddDays(27));
Assert.Equal(1, imported.ExamChanges);
Assert.Equal(0, unchanged.ExamChanges);
Assert.Equal(1, removed.ExamChanges);
Assert.Empty(exams.GetAll());
}
[Fact]
public void Process_ManuelleArbeitAmSelbenTag_WirdNichtDupliziertOderGeloescht()
{
var group = new LearningGroup { Id = Guid.NewGuid(), Name = "Mat_E" };
var manual = new Exam { GroupId = group.Id, Date = new DateOnly(2026, 9, 29), Title = "1. Arbeit" };
var exams = new RecordingExams([manual]);
var mappings = new FakeUntisSlotMappings();
mappings.Add(new UntisSlotMapping
{
Weekday = DayOfWeek.Tuesday, StartTime = new TimeOnly(9, 40), Summary = "Mat_E",
ClassToken = "10a", GroupId = group.Id, PeriodNumber = 3, CoveredPeriods = [3, 4], Confirmed = true,
});
var processor = BuildProcessor(mappings, new FakeSubstitutionEntries(), exams, [group]);
processor.Process([Entry([1], "2026-09-29T09:40", "2026-09-29T11:10", "EXAM", "REGULAR",
"Mat_E", "10a", "Arbeit aus WebUntis")], Monday, Monday.AddDays(13), Monday.AddDays(27));
Assert.Same(manual, Assert.Single(exams.GetAll()));
Assert.Null(manual.ExternalId);
}
[Fact]
public void Process_UebernommeneRegulaereAufsicht_WirdAlsAenderungErfasst()
{
var duties = new FakeSupervisionDuties();
duties.Add(new SupervisionDuty { Weekday = DayOfWeek.Wednesday, AfterPeriod = 2, Location = "PH grün" });
var substitutions = new FakeSubstitutionEntries();
var processor = BuildProcessor(new FakeUntisSlotMappings(), substitutions, new RecordingExams(), [], duties);
var entry = new UntisTimetableGridEntryDto([17], new DateTime(2026, 9, 30, 9, 20, 0),
new DateTime(2026, 9, 30, 9, 40, 0), "BREAK_SUPERVISION", "CHANGED", "SUBSTITUTED",
null, null, null, null,
[new UntisGridResourceChangeDto(null,
new UntisGridResourceDto("TEACHER", "REMOVED", "HED", null, "HED"))]);
processor.Process([entry], Monday, Monday.AddDays(13), Monday.AddDays(27));
var change = Assert.Single(substitutions.GetAll());
Assert.Equal(SubstitutionKind.Supervision, change.Kind);
Assert.Equal(2, change.AfterPeriod);
Assert.Contains("übernommen", change.Description);
}
private static WebUntisRestSyncProcessor BuildProcessor(FakeUntisSlotMappings mappings,
FakeSubstitutionEntries substitutions, IExamRepository exams, List<LearningGroup> groups,
FakeSupervisionDuties? supervisionDuties = null)
{
var path = Path.Combine(Path.GetTempPath(), $"lehrerapp-rest-sync-tests-{Guid.NewGuid():N}");
Directory.CreateDirectory(path);
var periods = new PeriodScheduleService(path);
periods.SetPeriods([
new PeriodTimeEntry { PeriodNumber = 2, Start = new TimeOnly(8, 35), End = new TimeOnly(9, 20) },
new PeriodTimeEntry { PeriodNumber = 3, Start = new TimeOnly(9, 40), End = new TimeOnly(10, 25) },
new PeriodTimeEntry { PeriodNumber = 4, Start = new TimeOnly(10, 25), End = new TimeOnly(11, 10) },
]);
return new WebUntisRestSyncProcessor(mappings, substitutions, exams, new FakeGroups(groups),
supervisionDuties ?? new FakeSupervisionDuties(), periods);
}
private static UntisTimetableGridEntryDto Entry(IReadOnlyList<int> ids, string start, string end,
string type, string status, string subject, string className, string? lessonInfo = null) => new(
ids, DateTime.Parse(start), DateTime.Parse(end), type, status, null, null, null, lessonInfo, null,
[
new UntisGridResourceChangeDto(new UntisGridResourceDto("CLASS", "REGULAR", className, null, className), null),
new UntisGridResourceChangeDto(new UntisGridResourceDto("SUBJECT", "REGULAR", subject, null, subject), null),
]);
private sealed class RecordingExams(List<Exam>? initial = null) : IExamRepository
{
private readonly List<Exam> _all = initial ?? [];
public Exam? GetById(Guid id) => _all.FirstOrDefault(exam => exam.Id == id);
public List<Exam> GetAll() => _all.ToList();
public List<Exam> GetByGroup(Guid groupId) => _all.Where(exam => exam.GroupId == groupId).ToList();
public void Save(Exam exam) { _all.RemoveAll(item => item.Id == exam.Id); _all.Add(exam); }
public void Delete(Guid id) => _all.RemoveAll(exam => exam.Id == id);
}
}