212 lines
9.5 KiB
C#
212 lines
9.5 KiB
C#
using System.Net;
|
|
using System.Text;
|
|
using LehrerApp.WebUntis;
|
|
using Xunit;
|
|
|
|
namespace LehrerApp.WebUntis.Tests;
|
|
|
|
public sealed class WebUntisClientTests
|
|
{
|
|
[Fact]
|
|
public async Task AufeinanderfolgendeAbrufe_VerwendenDieselbeSessionBisZumDispose()
|
|
{
|
|
var latin1 = Encoding.Latin1.GetBytes(
|
|
"id\texternKey\tklasse.name\tlongName\tforeName\r\n" +
|
|
"1\t10\t7a\tMüller\tAda\r\n" +
|
|
"2\t20\t8b\tMeier\tBerta\r\n");
|
|
var handler = new QueueHandler(
|
|
Json("{\"jsonrpc\":\"2.0\",\"result\":{\"sessionId\":\"session-1\"}}"),
|
|
Json("{\"data\":{\"finished\":true,\"error\":false,\"reportParams\":\"foo=bar\"}}"),
|
|
new HttpResponseMessage(HttpStatusCode.OK)
|
|
{
|
|
Content = new ByteArrayContent(latin1),
|
|
},
|
|
Json("{\"jsonrpc\":\"2.0\",\"result\":[{\"id\":7,\"name\":\"2026/27\",\"startDate\":20260801,\"endDate\":20270731}]}"),
|
|
Json("{\"jsonrpc\":\"2.0\",\"result\":{}}"));
|
|
var client = CreateClient(handler);
|
|
|
|
var result = await client.GetStudentReportAsync("7a", CancellationToken.None);
|
|
var schoolYears = await client.GetSchoolYearsAsync(CancellationToken.None);
|
|
|
|
var student = Assert.Single(result.Students);
|
|
Assert.Equal("Ada Müller", student.DisplayName);
|
|
Assert.Equal("7a", result.ClassNameFilter);
|
|
Assert.Equal("2026/27", Assert.Single(schoolYears).Name);
|
|
Assert.Equal(4, handler.Requests.Count);
|
|
Assert.Contains("jsonrpc.do?school=meine-schule", handler.Requests[0].Uri);
|
|
Assert.Contains("\"method\":\"authenticate\"", handler.Requests[0].Body);
|
|
Assert.Contains("reports.do?name=Student", handler.Requests[1].Uri);
|
|
Assert.Contains("schoolname=\"_", handler.Requests[1].Cookie);
|
|
Assert.EndsWith("reports.do?foo=bar", handler.Requests[2].Uri);
|
|
Assert.Contains("\"method\":\"getSchoolyears\"", handler.Requests[3].Body);
|
|
Assert.Single(handler.Requests,
|
|
request => request.Body.Contains("\"method\":\"authenticate\""));
|
|
|
|
await client.DisposeAsync();
|
|
|
|
Assert.Equal(5, handler.Requests.Count);
|
|
Assert.Contains("\"method\":\"logout\"", handler.Requests[4].Body);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetTimetableAsync_SendetElementUndZeitraumAlsSeparateAbfrage()
|
|
{
|
|
var handler = new QueueHandler(
|
|
Json("{\"result\":{\"sessionId\":\"s\"}}"),
|
|
Json("{\"result\":[{\"id\":99,\"date\":20260824,\"startTime\":800,\"endTime\":845," +
|
|
"\"kl\":[{\"id\":4,\"name\":\"7a\"}],\"te\":[],\"su\":[],\"ro\":[]}]}"),
|
|
Json("{\"result\":{}}"));
|
|
var client = CreateClient(handler);
|
|
|
|
var periods = await client.GetTimetableAsync(UntisTimetableElementType.Teacher, 42,
|
|
20260824, 20260828, CancellationToken.None);
|
|
|
|
var period = Assert.Single(periods);
|
|
Assert.Equal(99, period.Id);
|
|
Assert.Equal("7a", Assert.Single(period.Classes).Name);
|
|
Assert.Contains("\"method\":\"getTimetable\"", handler.Requests[1].Body);
|
|
Assert.Contains("\"element\":{\"id\":42,\"type\":2}", handler.Requests[1].Body);
|
|
Assert.Contains("\"startDate\":20260824", handler.Requests[1].Body);
|
|
|
|
await client.DisposeAsync();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task FehlendeKonfiguration_BrichtVorHttpRequestAb()
|
|
{
|
|
var handler = new QueueHandler();
|
|
var client = new WebUntisClient(new HttpClient(handler), new WebUntisOptions());
|
|
|
|
await Assert.ThrowsAsync<WebUntisConfigurationException>(
|
|
() => client.GetSchoolYearsAsync(CancellationToken.None));
|
|
|
|
Assert.Empty(handler.Requests);
|
|
await client.DisposeAsync();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task VollstaendigeLoginUrl_TrenntRegionalenServerUndSchulkennung()
|
|
{
|
|
var handler = new QueueHandler(
|
|
Json("{\"result\":{\"sessionId\":\"s\"}}"),
|
|
Json("{\"result\":[{\"id\":1,\"name\":\"2026/27\",\"startDate\":20260801,\"endDate\":20270731}]}"),
|
|
Json("{\"result\":{}}"));
|
|
var client = new WebUntisClient(new HttpClient(handler), new WebUntisOptions
|
|
{
|
|
School = "https://arche.webuntis.com/WebUntis/?school=bk-ostvest#/basic/login",
|
|
Username = "api-user",
|
|
Password = "secret",
|
|
});
|
|
|
|
await client.GetSchoolYearsAsync(CancellationToken.None);
|
|
|
|
Assert.StartsWith("https://arche.webuntis.com/WebUntis/jsonrpc.do", handler.Requests[0].Uri);
|
|
Assert.Contains("school=bk-ostvest", handler.Requests[0].Uri);
|
|
await client.DisposeAsync();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task VollstaendigeServerUrl_WirdOhnePfadVerwendet()
|
|
{
|
|
var handler = new QueueHandler(
|
|
Json("{\"result\":{\"sessionId\":\"s\"}}"),
|
|
Json("{\"result\":[{\"id\":1,\"name\":\"2026/27\",\"startDate\":20260801,\"endDate\":20270731}]}"),
|
|
Json("{\"result\":{}}"));
|
|
var client = new WebUntisClient(new HttpClient(handler), new WebUntisOptions
|
|
{
|
|
School = "bk-ostvest",
|
|
Host = "https://arche.webuntis.com/WebUntis/?school=bk-ostvest#/basic/login",
|
|
Username = "api-user",
|
|
Password = "secret",
|
|
});
|
|
|
|
await client.GetSchoolYearsAsync(CancellationToken.None);
|
|
|
|
Assert.StartsWith("https://arche.webuntis.com/WebUntis/jsonrpc.do", handler.Requests[0].Uri);
|
|
Assert.Contains("school=bk-ostvest", handler.Requests[0].Uri);
|
|
await client.DisposeAsync();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task FehlzeitenUndKlassenbuch_WerdenNachSchuelerAbgerufenUndTypisiert()
|
|
{
|
|
var handler = new QueueHandler(
|
|
Json("{\"result\":{\"sessionId\":\"s\"}}"),
|
|
Json("{\"result\":{\"periodsWithAbsences\":[" +
|
|
"{\"studentId\":9001,\"date\":20260901,\"startTime\":800,\"endTime\":845," +
|
|
"\"absentTime\":45,\"checked\":true,\"absenceReason\":\"Krank\"," +
|
|
"\"excuseStatus\":\"entschuldigt\",\"subjectId\":12,\"teacherIds\":[\"7\"]}," +
|
|
"{\"studentId\":9999,\"date\":20260901,\"startTime\":800,\"endTime\":845," +
|
|
"\"absentTime\":45,\"checked\":false}]}}"),
|
|
Json("{\"result\":[{\"studentid\":9001,\"surname\":\"Müller\",\"forname\":\"Ada\"," +
|
|
"\"date\":20260902,\"subject\":\"MA\",\"categoryId\":3,\"reason\":\"Material\"," +
|
|
"\"text\":\"Buch vergessen\"}]}"),
|
|
Json("{\"result\":[{\"id\":3,\"name\":\"Material\",\"longName\":\"Material vergessen\"," +
|
|
"\"groupId\":2}]}"),
|
|
Json("{\"result\":[{\"id\":2,\"name\":\"Organisation\"}]}"),
|
|
Json("{\"result\":{}}"));
|
|
var client = CreateClient(handler);
|
|
|
|
var report = await client.GetStudentAbsencesAsync(9001, 20260801, 20270731,
|
|
CancellationToken.None);
|
|
var entries = await client.GetClassRegisterEntriesAsync(17, 20260801, 20270731,
|
|
CancellationToken.None);
|
|
var categories = await client.GetClassRegisterCategoriesAsync(CancellationToken.None);
|
|
var groups = await client.GetClassRegisterCategoryGroupsAsync(CancellationToken.None);
|
|
|
|
var absence = Assert.Single(report.Absences);
|
|
Assert.Equal(45, report.AbsentMinutes);
|
|
Assert.Equal("entschuldigt", absence.ExcuseStatus);
|
|
Assert.Equal(7, Assert.Single(absence.TeacherIds));
|
|
var entry = Assert.Single(entries);
|
|
Assert.Equal("Ada Müller", entry.DisplayName);
|
|
Assert.Equal("Buch vergessen", entry.Text);
|
|
Assert.Equal(3, Assert.Single(categories).Id);
|
|
Assert.Equal("Organisation", Assert.Single(groups).Name);
|
|
Assert.Contains("\"method\":\"getTimetableWithAbsences\"", handler.Requests[1].Body);
|
|
Assert.Contains("\"method\":\"getClassregEvents\"", handler.Requests[2].Body);
|
|
Assert.Contains("\"id\":17,\"type\":5", handler.Requests[2].Body);
|
|
Assert.Single(handler.Requests,
|
|
request => request.Body.Contains("\"method\":\"authenticate\""));
|
|
|
|
await client.DisposeAsync();
|
|
Assert.Contains("\"method\":\"logout\"", handler.Requests[^1].Body);
|
|
}
|
|
|
|
private static WebUntisClient CreateClient(HttpMessageHandler handler) => new(
|
|
new HttpClient(handler),
|
|
new WebUntisOptions
|
|
{
|
|
School = "meine-schule",
|
|
Host = "meine-schule.webuntis.com",
|
|
Username = "api-user",
|
|
Password = "secret",
|
|
Client = "tests",
|
|
});
|
|
|
|
private static HttpResponseMessage Json(string json) => new(HttpStatusCode.OK)
|
|
{
|
|
Content = new StringContent(json, Encoding.UTF8, "application/json"),
|
|
};
|
|
|
|
private sealed class QueueHandler(params HttpResponseMessage[] responses) : HttpMessageHandler
|
|
{
|
|
private readonly Queue<HttpResponseMessage> _responses = new(responses);
|
|
public List<CapturedRequest> Requests { get; } = [];
|
|
|
|
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
Requests.Add(new CapturedRequest(
|
|
request.RequestUri?.ToString() ?? "",
|
|
request.Content is null ? "" : await request.Content.ReadAsStringAsync(cancellationToken),
|
|
string.Join("; ", request.Headers.TryGetValues("Cookie", out var cookies) ? cookies : [])));
|
|
return _responses.Count > 0
|
|
? _responses.Dequeue()
|
|
: throw new InvalidOperationException("Keine Testantwort mehr vorhanden.");
|
|
}
|
|
}
|
|
|
|
private sealed record CapturedRequest(string Uri, string Body, string Cookie);
|
|
}
|