using LehrerApp.Core.Models; using LiteDB; using Xunit; namespace LehrerApp.Data.Tests; public sealed class LiteDbContextTests { [Fact] public void MigratesLegacyEnrollmentAndSubjectTextWithoutLosingData() { using var temp = new TempDatabase(); var groupId = Guid.NewGuid(); var studentId = Guid.NewGuid(); var membershipId = Guid.NewGuid(); var examId = Guid.NewGuid(); var gradeId = Guid.NewGuid(); var entryId = Guid.NewGuid(); var addedOn = new DateOnly(2026, 6, 23); using (var legacy = new LiteDatabase(temp.Path)) { legacy.GetCollection("subjects").Insert(new Subject { Name = "mathematik" }); legacy.GetCollection("groups").Insert(new BsonDocument { ["_id"] = groupId, [nameof(LearningGroup.Name)] = "Mathe G", ["Subject"] = "Mathematik", [nameof(LearningGroup.SchoolYear)] = "2025/26", }); legacy.GetCollection("enrollments").Insert(new BsonDocument { ["_id"] = membershipId, [nameof(GroupMembership.StudentId)] = studentId, [nameof(GroupMembership.GroupId)] = groupId, ["SchoolYear"] = "2025/26", ["EnrolledAt"] = BsonMapper.Global.Serialize(addedOn), }); legacy.GetCollection("exams").Insert(new BsonDocument { ["_id"] = examId, [nameof(Exam.GroupId)] = groupId, ["Subject"] = "Mathematik", }); legacy.GetCollection("grades").Insert(new BsonDocument { ["_id"] = gradeId, [nameof(Grade.StudentId)] = studentId, [nameof(Grade.GroupId)] = groupId, ["SchoolYear"] = "2025/26", }); legacy.GetCollection("participation").Insert(new BsonDocument { ["_id"] = entryId, [nameof(ParticipationEntry.SessionId)] = Guid.NewGuid(), [nameof(ParticipationEntry.StudentId)] = studentId, ["GroupId"] = groupId, ["Date"] = BsonMapper.Global.Serialize(addedOn), }); } using (var context = new LiteDbContext(temp.Path)) { var group = context.Groups.FindById(groupId); var membership = context.Memberships.FindById(membershipId); Assert.NotNull(group); Assert.NotNull(group.SubjectId); Assert.Equal("mathematik", context.Subjects.FindById(group.SubjectId.Value)?.Name); Assert.Equal(1, context.Subjects.Count()); Assert.NotNull(membership); Assert.Equal(addedOn, membership.AddedOn); } using var migrated = new LiteDatabase(temp.Path); Assert.DoesNotContain("enrollments", migrated.GetCollectionNames()); Assert.Contains("group_memberships", migrated.GetCollectionNames()); var rawGroup = migrated.GetCollection("groups").FindById(groupId); var rawMembership = migrated.GetCollection("group_memberships").FindById(membershipId); var rawExam = migrated.GetCollection("exams").FindById(examId); var rawGrade = migrated.GetCollection("grades").FindById(gradeId); var rawEntry = migrated.GetCollection("participation").FindById(entryId); Assert.False(rawGroup.ContainsKey("Subject")); Assert.False(rawMembership.ContainsKey("SchoolYear")); Assert.False(rawMembership.ContainsKey("EnrolledAt")); Assert.True(rawMembership.ContainsKey(nameof(GroupMembership.AddedOn))); Assert.False(rawExam.ContainsKey("Subject")); Assert.False(rawGrade.ContainsKey("SchoolYear")); Assert.False(rawEntry.ContainsKey("GroupId")); Assert.False(rawEntry.ContainsKey("Date")); } [Fact] public void RejectsASecondMembershipForTheSameStudentAndGroup() { using var temp = new TempDatabase(); using var context = new LiteDbContext(temp.Path); var studentId = Guid.NewGuid(); var groupId = Guid.NewGuid(); context.Memberships.Insert(new GroupMembership { StudentId = studentId, GroupId = groupId }); Assert.Throws(() => context.Memberships.Insert( new GroupMembership { StudentId = studentId, GroupId = groupId })); } [Fact] public void RejectsASecondExamResultForTheSameStudentAndExam() { using var temp = new TempDatabase(); using var context = new LiteDbContext(temp.Path); var studentId = Guid.NewGuid(); var examId = Guid.NewGuid(); context.ExamResults.Insert(new ExamResult { StudentId = studentId, ExamId = examId }); Assert.Throws(() => context.ExamResults.Insert( new ExamResult { StudentId = studentId, ExamId = examId })); } private sealed class TempDatabase : IDisposable { private readonly string _directory = System.IO.Path.Combine( System.IO.Path.GetTempPath(), $"lehrerapp-tests-{Guid.NewGuid():N}"); public string Path { get; } public TempDatabase() { Directory.CreateDirectory(_directory); Path = System.IO.Path.Combine(_directory, "test.db"); } public void Dispose() { if (Directory.Exists(_directory)) Directory.Delete(_directory, recursive: true); } } }