feat: Teilnehmerlisten in Lerngruppen importieren
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
using System.Text;
|
||||
using LehrerApp.Core.Importing;
|
||||
using LehrerApp.Core.Models;
|
||||
using Xunit;
|
||||
|
||||
namespace LehrerApp.Tests;
|
||||
|
||||
public sealed class LessonStudentListCsvImportHandlerTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task Parse_UebernimmtTeilnehmerUndOptionaleKontaktdaten()
|
||||
{
|
||||
var handler = new LessonStudentListCsvImportHandler();
|
||||
var result = await handler.ParseAsync(File(LessonList()));
|
||||
|
||||
Assert.True(result.IsValid);
|
||||
var student = Assert.Single(result.Items);
|
||||
Assert.Equal("Max", student.FirstName);
|
||||
Assert.Equal("Beispiel", student.LastName);
|
||||
Assert.Equal("10c", student.GroupName);
|
||||
Assert.Equal(Gender.M, student.Gender);
|
||||
Assert.Equal("max@example.invalid", student.Contact?.Email);
|
||||
Assert.Equal("0151 123", student.Contact?.MobilePhone);
|
||||
Assert.Equal("0123 456", student.Contact?.Phone);
|
||||
Assert.Equal("Zeile 2", student.SourceReference);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Detect_UnterscheidetTeilnehmerlisteVonStammdatenliste()
|
||||
{
|
||||
var handler = new LessonStudentListCsvImportHandler();
|
||||
|
||||
var detection = await handler.DetectAsync(File(LessonList()));
|
||||
|
||||
Assert.True(detection.IsMatch);
|
||||
Assert.Equal(100, detection.Confidence);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MarksExport_WirdErkanntUndErklaertFehlendeTeilnehmerdaten()
|
||||
{
|
||||
var handler = new MarksPerLessonCsvImportHandler();
|
||||
const string header = "Datum\tName\tKlasse\tFach\tPrüfungsart\tNote\tBemerkung\tBenutzer\tSchlüssel (extern)\tGesamtnote\n";
|
||||
|
||||
var detection = await handler.DetectAsync(File(header));
|
||||
var result = await handler.ParseAsync(File(header));
|
||||
|
||||
Assert.True(detection.IsMatch);
|
||||
Assert.False(result.IsValid);
|
||||
Assert.Empty(result.Items);
|
||||
Assert.Contains(result.Messages, message => message.Code == "marks-per-lesson.no-participants");
|
||||
}
|
||||
|
||||
private static ImportFile File(string text) => new("export.csv", Encoding.UTF8.GetBytes(text));
|
||||
|
||||
private static string LessonList() => string.Join('\n',
|
||||
"Langname\tVorname\tKlasse\tGeschlecht\tEintrittsdatum\tAustrittsdatum\tE-Mail Adresse\tMobiltelefon\tTelefonnummer",
|
||||
"Beispiel\tMax\t10c\tm\t01.08.2025\t\tmax@example.invalid\t0151 123\t0123 456") + "\n";
|
||||
}
|
||||
@@ -305,6 +305,67 @@ public sealed class StudentImportServiceTests
|
||||
Assert.Equal(ownClass.Id, Assert.Single(preview.GroupAssignments).AutomaticGroupId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Apply_ExpliziteZielgruppeIgnoriertHeimatklasseUndVervollstaendigtTeilnehmerliste()
|
||||
{
|
||||
var repository = new InMemoryStudentRepository();
|
||||
var target = new LearningGroup { Name = "Mathematik 10", IsActive = true };
|
||||
var groups = new InMemoryGroupRepository([target]);
|
||||
var memberships = new InMemoryGroupMembershipRepository();
|
||||
var service = Service(repository,
|
||||
[
|
||||
new ImportedStudent
|
||||
{
|
||||
FirstName = "Max", LastName = "Beispiel", GroupName = "10c",
|
||||
},
|
||||
], groups, memberships);
|
||||
|
||||
var preview = await service.AnalyzeAsync(File(), target.Id);
|
||||
var result = await service.ApplyAsync(preview);
|
||||
|
||||
Assert.True(preview.CanApply);
|
||||
Assert.Equal(target.Id, preview.TargetGroupId);
|
||||
Assert.Equal(target.Id, Assert.Single(preview.GroupAssignments).AutomaticGroupId);
|
||||
Assert.Equal(1, result.CreatedStudents);
|
||||
Assert.Equal(1, result.CreatedMemberships);
|
||||
Assert.Equal(target.Id, Assert.Single(memberships.Memberships).GroupId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Analyze_ExpliziteZielgruppeErkenntBereitsZugeordnetenNamensgleichenSchueler()
|
||||
{
|
||||
var existing = new Student { FirstName = "Max", LastName = "Beispiel" };
|
||||
var repository = new InMemoryStudentRepository([existing]);
|
||||
var target = new LearningGroup { Name = "Mathematik 10", IsActive = true };
|
||||
var groups = new InMemoryGroupRepository([target]);
|
||||
var memberships = new InMemoryGroupMembershipRepository();
|
||||
memberships.Save(new GroupMembership { StudentId = existing.Id, GroupId = target.Id });
|
||||
var service = Service(repository,
|
||||
[new ImportedStudent { FirstName = "Max", LastName = "Beispiel" }],
|
||||
groups, memberships);
|
||||
|
||||
var preview = await service.AnalyzeAsync(File(), target.Id);
|
||||
|
||||
Assert.Empty(preview.Conflicts);
|
||||
var resolution = Assert.Single(preview.Entries).AutomaticResolution;
|
||||
Assert.Equal(StudentImportResolutionKind.UseExisting, resolution?.Kind);
|
||||
Assert.Equal(existing.Id, resolution?.ExistingStudentId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Analyze_ArchivierteZielgruppeVerhindertImport()
|
||||
{
|
||||
var target = new LearningGroup { Name = "Alter Kurs", IsActive = false };
|
||||
var service = Service(new InMemoryStudentRepository(),
|
||||
[new ImportedStudent { FirstName = "Max", LastName = "Beispiel" }],
|
||||
new InMemoryGroupRepository([target]));
|
||||
|
||||
var preview = await service.AnalyzeAsync(File(), target.Id);
|
||||
|
||||
Assert.False(preview.CanApply);
|
||||
Assert.Contains(preview.Messages, message => message.Code == "student.target-group.unavailable");
|
||||
}
|
||||
|
||||
private static StudentImportService Service(
|
||||
InMemoryStudentRepository repository,
|
||||
IReadOnlyList<ImportedStudent> imported,
|
||||
|
||||
Reference in New Issue
Block a user