140 lines
7.2 KiB
C#
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);
|
|
}
|
|
}
|