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

212 lines
8.5 KiB
C#

using LehrerApp.Core.Models;
using LehrerApp.Core.Services;
using Xunit;
namespace LehrerApp.Tests;
public sealed class CompetencyAnalysisServiceTests
{
private readonly CompetencyAnalysisService _service = new();
private static readonly CompetencyItem K1 = new() { Code = "K1", Description = "Erste Kompetenz" };
private static readonly CompetencyItem K2 = new() { Code = "K2", Description = "Zweite Kompetenz", SortOrder = 1 };
private static CompetencyReference Ref(CompetencyItem item) =>
new() { ItemId = item.Id, Code = item.Code, Description = item.Description };
[Fact]
public void Analyze_UnterscheidetPlanungBehandlungUndPruefung()
{
var groupId = Guid.NewGuid();
var catalog = Catalog();
var units = new List<Unit>
{
new() { GroupId = groupId, Status = UnitStatus.Planned, Competencies = [Ref(K1)] },
new() { GroupId = groupId, Status = UnitStatus.Active, Competencies = [Ref(K1)] },
new() { GroupId = groupId, Status = UnitStatus.Completed, Competencies = [Ref(K1)] },
};
var exams = new List<Exam>
{
new() { GroupId = groupId, Status = ExamStatus.Planned,
Tasks = [new ExamTask { MaxPoints = 10, Competencies = [Ref(K1)] }] },
new() { GroupId = groupId, Status = ExamStatus.Graded,
Tasks = [new ExamTask { MaxPoints = 10, Competencies = [Ref(K1)] }] },
};
var item = Assert.Single(_service.Analyze(catalog, units, exams, []));
Assert.Equal(1, item.PlannedUnitCount);
Assert.Equal(2, item.TaughtUnitCount);
Assert.Equal(1, item.PlannedExamCount);
Assert.Equal(1, item.AssessedExamCount);
}
[Fact]
public void Analyze_BerechnetGewichteteGruppenUndSchuelerprofileAusAufgabenpunkten()
{
var firstStudent = Guid.NewGuid();
var secondStudent = Guid.NewGuid();
var absentStudent = Guid.NewGuid();
var exam = new Exam
{
Status = ExamStatus.Graded,
Tasks =
[
new ExamTask { MaxPoints = 10, Competencies = [Ref(K1)] },
new ExamTask { MaxPoints = 20, Competencies = [Ref(K1)] },
],
};
var results = new List<ExamResult>
{
new() { ExamId = exam.Id, StudentId = firstStudent, Points = [5, 10] },
new() { ExamId = exam.Id, StudentId = secondStudent, Points = [10, 20] },
new() { ExamId = exam.Id, StudentId = absentStudent, Points = [0, 0], Absent = true },
};
var item = Assert.Single(_service.Analyze(Catalog(), [], [exam], results));
Assert.NotNull(item.GroupScore);
Assert.Equal(75, item.GroupScore.Percent, 6);
Assert.Equal(2, item.GroupScore.ParticipantCount);
Assert.Equal(4, item.GroupScore.EvidenceCount);
Assert.Equal(50, item.StudentScores[firstStudent].Percent, 6);
Assert.Equal(100, item.StudentScores[secondStudent].Percent, 6);
Assert.False(item.StudentScores.ContainsKey(absentStudent));
}
[Fact]
public void Analyze_AufgabeMitMehrerenKompetenzen_WertetJedeZugeordneteKompetenzAus()
{
var studentId = Guid.NewGuid();
var exam = new Exam
{
Status = ExamStatus.Returned,
Tasks = [new ExamTask { MaxPoints = 8, Competencies = [Ref(K1), Ref(K2)] }],
};
var result = new ExamResult { ExamId = exam.Id, StudentId = studentId, Points = [6] };
var analysis = _service.Analyze(Catalog(includeSecond: true), [], [exam], [result]);
Assert.All(analysis, item => Assert.Equal(75, item.StudentScores[studentId].Percent, 6));
}
[Fact]
public void Analyze_DurchgefuehrteAberNichtKorrigierteKlausur_LiefertNochKeinProfil()
{
var studentId = Guid.NewGuid();
var exam = new Exam
{
Status = ExamStatus.Conducted,
Tasks = [new ExamTask { MaxPoints = 10, Competencies = [Ref(K1)] }],
};
var result = new ExamResult { ExamId = exam.Id, StudentId = studentId, Points = [5] };
var item = Assert.Single(_service.Analyze(Catalog(), [], [exam], [result]));
Assert.Equal(1, item.AssessedExamCount);
Assert.Null(item.GroupScore);
Assert.Empty(item.StudentScores);
}
[Fact]
public void Analyze_MitarbeitMitAutomatischerSchaetzung_FuelltKompetenzprofil()
{
var studentId = Guid.NewGuid();
var session = new ParticipationSession { Competencies = [Ref(K1)] };
var entry = new ParticipationEntry
{
SessionId = session.Id,
StudentId = studentId,
Ratings =
[
new AspectRating { Key = "quality", Value = 1 },
new AspectRating { Key = "quantity", Value = 2 },
],
};
var item = Assert.Single(_service.Analyze(Catalog(), [], [], [], [session], [entry],
[.. DefaultParticipationAspects.All]));
Assert.Equal(1, item.ParticipationSessionCount);
Assert.Equal(100, item.GroupScore!.Percent, 6); // Mittel 1,5 wird wie in der UI auf +2 gerundet.
Assert.Equal(1, item.GroupScore.ParticipationEvidenceCount);
Assert.Equal(100, item.StudentScores[studentId].Percent, 6);
}
[Fact]
public void Analyze_ManuelleKompetenzbewertung_UeberschreibtAutomatischeSchaetzung()
{
var studentId = Guid.NewGuid();
var session = new ParticipationSession { Competencies = [Ref(K1)] };
var entry = new ParticipationEntry
{
SessionId = session.Id,
StudentId = studentId,
Ratings = [new AspectRating { Key = "quality", Value = 2 }],
CompetencyRatings = [new CompetencyRating { ItemId = K1.Id, Value = -1 }],
};
var item = Assert.Single(_service.Analyze(Catalog(), [], [], [], [session], [entry],
[.. DefaultParticipationAspects.All]));
Assert.Equal(25, item.StudentScores[studentId].Percent, 6);
}
[Fact]
public void Analyze_GeloeschteKompetenz_BleibtMitGespeicherterMomentaufnahmeSichtbar()
{
// ItemId zeigt auf kein (mehr) existierendes Katalog-Item (z.B. gelöscht) - Code/Description
// stammen aus der zum Zuordnungszeitpunkt gespeicherten Momentaufnahme.
var reference = new CompetencyReference
{
ItemId = Guid.NewGuid(), Code = "ALT1", Description = "Alte, inzwischen gelöschte Kompetenz",
};
var unit = new Unit { Status = UnitStatus.Active, Competencies = [reference] };
var analysis = _service.Analyze(Catalog(), [unit], [], []);
var unknown = Assert.Single(analysis, x => x.Code == "ALT1");
Assert.Equal("Nicht im Katalog", unknown.DomainName);
Assert.Equal("Alte, inzwischen gelöschte Kompetenz", unknown.Description);
Assert.Equal(1, unknown.TaughtUnitCount);
}
[Fact]
public void Analyze_NieAufgeloesteReferenz_OhneBeschreibung_ZeigtFallbacktext()
{
// ItemId == Guid.Empty simuliert eine nie aufgelöste Referenz (z.B. aus einem
// plattformübergreifenden Import über PlanningExchangeService) ohne gespeicherte Beschreibung.
var reference = new CompetencyReference { ItemId = Guid.Empty, Code = "IMPORT1", Description = "" };
var unit = new Unit { Status = UnitStatus.Active, Competencies = [reference] };
var analysis = _service.Analyze(Catalog(), [unit], [], []);
var unknown = Assert.Single(analysis, x => x.Code == "IMPORT1");
Assert.Contains("nicht (mehr) im aktuellen Katalog", unknown.Description);
}
[Fact]
public void Analyze_UmbenannteKompetenz_ZeigtLiveFassungStattMomentaufnahme()
{
// Referenz trägt eine veraltete Momentaufnahme (Description von vor dem Rename) - solange
// ItemId noch im Katalog auflösbar ist, muss die LIVE-Fassung gewinnen.
var reference = new CompetencyReference { ItemId = K1.Id, Code = "K1", Description = "Veraltete Beschreibung" };
var unit = new Unit { Status = UnitStatus.Active, Competencies = [reference] };
var item = Assert.Single(_service.Analyze(Catalog(), [unit], [], []));
Assert.Equal("Erste Kompetenz", item.Description);
Assert.Equal("Bereich", item.DomainName);
}
private static List<CompetencyDomain> Catalog(bool includeSecond = false) =>
[
new CompetencyDomain
{
Name = "Bereich",
Code = "B",
Items = includeSecond ? [K1, K2] : [K1],
},
];
}