Vorarbeit: WebUntis-API
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Xunit;
|
||||
|
||||
namespace LehrerApp.Api.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,\"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), Options.Create(new WebUntisOptions()));
|
||||
|
||||
await Assert.ThrowsAsync<WebUntisConfigurationException>(
|
||||
() => client.GetSchoolYearsAsync(CancellationToken.None));
|
||||
|
||||
Assert.Empty(handler.Requests);
|
||||
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),
|
||||
Options.Create(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);
|
||||
}
|
||||
Reference in New Issue
Block a user