Update wire protocol to version 6 and enhance timetable functionality
- Bumped the wire protocol version to 6, reflecting changes in the communication structure.
- Expanded the timetable API with new endpoints for fetching and managing lesson schedules, including `GET /api/schools/{id}/timetable` and `POST /api/schools/{id}/timetable/pin`.
- Updated the protocol documentation to include detailed descriptions of the new timetable features and message structures.
- Enhanced the client-side implementation to support the new timetable functionalities, including lesson pinning and unpinning.
- Revised server-side logic to handle timetable operations and ensure proper integration with existing school management features.
- Added tests to validate the new timetable functionalities and ensure robustness in handling lesson data.
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
using System.Net.Http.Json;
|
||||
using System.Net.WebSockets;
|
||||
using HSchool.Protocol;
|
||||
|
||||
@@ -110,6 +111,8 @@ public class GameSocketTests(AppHostFixture fixture)
|
||||
Assert.Equal(16, classroom.PupilSlots);
|
||||
Assert.Contains(classroom.Items, item => item.Name == "Парта" && item.Count == 16);
|
||||
Assert.DoesNotContain(classroom.Items, item => item.Name == "Стул");
|
||||
Assert.Equal("", classroom.ActivitySubject);
|
||||
Assert.Empty(classroom.Present);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -157,6 +160,40 @@ public class GameSocketTests(AppHostFixture fixture)
|
||||
Assert.DoesNotContain(snapshot.Nodes, node => node.Id == "corridor-1");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task OpeningASchoolDuringAMathLesson_PutsOccupancyOnTheRoom()
|
||||
{
|
||||
using var client = fixture.App.CreateHttpClient("server");
|
||||
await SchoolApiTests.ResetAsync(client);
|
||||
var start = new DateTime(2012, 4, 3, 10, 20, 0, DateTimeKind.Utc);
|
||||
var school = await SchoolApiTests.CreateAsync(client, "Кто где сейчас", start);
|
||||
|
||||
var staffing = await client.GetFromJsonAsync<StaffingSnapshot>(
|
||||
$"/api/schools/{school.Id}/staffing",
|
||||
TestContext.Current.CancellationToken);
|
||||
Assert.NotNull(staffing);
|
||||
var applicant = staffing.Applicants[0];
|
||||
using var hire = await client.PostAsJsonAsync(
|
||||
$"/api/schools/{school.Id}/staff/hire",
|
||||
new { personId = applicant.Id, position = "Teacher" },
|
||||
TestContext.Current.CancellationToken);
|
||||
hire.EnsureSuccessStatusCode();
|
||||
using var assign = await client.PostAsJsonAsync(
|
||||
$"/api/schools/{school.Id}/staff/{Uri.EscapeDataString(applicant.Id)}/subjects",
|
||||
new { subject = "Mathematics" },
|
||||
TestContext.Current.CancellationToken);
|
||||
assign.EnsureSuccessStatusCode();
|
||||
|
||||
using var socket = await OpenSchoolAsync(school.Id);
|
||||
var snapshot = ProtocolCodec.ReadMapSnapshot(await ReceiveUntilAsync(socket, MessageType.ServerMapSnapshot));
|
||||
var occupied = Assert.Single(snapshot.Nodes, node => node.ActivitySubject.Length > 0);
|
||||
|
||||
Assert.Equal("Математика", occupied.ActivitySubject);
|
||||
Assert.NotEmpty(occupied.ActivityClass);
|
||||
Assert.Contains(applicant.FullName, occupied.Present);
|
||||
Assert.True(occupied.Present.Count > 1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Pausing_FreezesTheClock()
|
||||
{
|
||||
@@ -448,7 +485,8 @@ public class GameSocketTests(AppHostFixture fixture)
|
||||
using var cts = CancellationTokenSource.CreateLinkedTokenSource(TestContext.Current.CancellationToken);
|
||||
cts.CancelAfter(timeout ?? DefaultTimeout);
|
||||
|
||||
var buffer = new byte[ProtocolConstants.MaxMessageSize];
|
||||
var buffer = new byte[64 * 1024];
|
||||
var chunks = new List<byte>();
|
||||
|
||||
while (true)
|
||||
{
|
||||
@@ -467,11 +505,22 @@ public class GameSocketTests(AppHostFixture fixture)
|
||||
throw new InvalidOperationException($"Socket closed while waiting for {expected}: {socket.CloseStatus}.");
|
||||
}
|
||||
|
||||
var frame = buffer[..result.Count];
|
||||
chunks.AddRange(buffer.AsSpan(0, result.Count).ToArray());
|
||||
if (!result.EndOfMessage)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var frame = chunks.ToArray();
|
||||
chunks.Clear();
|
||||
if (ProtocolCodec.PeekMessageType(frame) == expected)
|
||||
{
|
||||
return frame;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed record StaffingSnapshot(IReadOnlyList<ApplicantSnapshot> Applicants);
|
||||
|
||||
private sealed record ApplicantSnapshot(string Id, string FullName);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user