Wait for a person on a node before asserting the dump.

Needs.Count is true for everyone, so the old OR never caught empty occupancy.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Leonid Pershin
2026-08-20 12:45:18 +03:00
co-authored by Cursor
parent 9a6cbff55a
commit 0d6173431a
2 changed files with 43 additions and 12 deletions
+7 -7
View File
@@ -11,17 +11,17 @@
## Задачи
- [ ] `Dump_ReturnsPeopleNodesAndTimetable`: нанять, **включить** часы, дождаться человека с
- [x] `Dump_ReturnsPeopleNodesAndTimetable`: нанять, **включить** часы, дождаться человека с
`NodeId`, затем дамп. Не ждать людей на паузе
- [ ] Нужды по-прежнему не пустые у всех в выборке, но явка — отдельный assert
- [ ] Урок математики в дампе — как сейчас
- [x] Нужды по-прежнему не пустые у всех в выборке, но явка — отдельный assert
- [x] Урок математики в дампе — как сейчас
## Тесты, без которых фаза не закрыта
- [ ] После найма математика и хода часов дамп содержит человека с непустым `NodeId`
- [ ] Все люди в дампе с id, именем и непустым `Needs`
- [ ] В `dump.Lessons` есть Mathematics
- [ ] Неизвестный id — по-прежнему 404 `unknown-school`
- [x] После найма математика и хода часов дамп содержит человека с непустым `NodeId`
- [x] Все люди в дампе с id, именем и непустым `Needs`
- [x] В `dump.Lessons` есть Mathematics
- [x] Неизвестный id — по-прежнему 404 `unknown-school`
## Критерий готовности
@@ -89,16 +89,17 @@ public class ServiceabilityTests(AppHostFixture fixture)
using var client = fixture.App.CreateHttpClient("server");
await SchoolApiTests.ResetAsync(client);
var school = await SchoolApiTests.CreateAsync(client, "Дамп", TuesdayMorning);
// Create starts the clock. Presence still goes out while paused (often empty); waiting
// for people then hangs. Hire, keep time moving, then dump.
Assert.True(school.Running);
await HireMathAsync(client, school.Id);
var dump = await client.GetFromJsonAsync<SchoolDumpResponse>(
$"/api/dev/schools/{school.Id}/dump",
TestContext.Current.CancellationToken);
var dump = await WaitUntilSomeoneIsOnANodeAsync(client, school.Id);
Assert.NotNull(dump);
Assert.Equal(school.Id, dump.Id);
Assert.True(dump.Running);
Assert.NotEmpty(dump.People);
Assert.Contains(dump.People, person => person.NodeId is not null || person.Needs.Count > 0);
Assert.Contains(dump.People, person => !string.IsNullOrWhiteSpace(person.NodeId));
Assert.All(dump.People, person =>
{
Assert.False(string.IsNullOrWhiteSpace(person.Id));
@@ -119,6 +120,36 @@ public class ServiceabilityTests(AppHostFixture fixture)
Assert.Equal("unknown-school", await ProblemCodeAsync(response));
}
/// <summary>
/// Polls the dump while the clock is moving. A paused school would keep answering with empty
/// occupancy; fail immediately instead of waiting that out.
/// </summary>
private static async Task<SchoolDumpResponse> WaitUntilSomeoneIsOnANodeAsync(HttpClient client, int schoolId)
{
SchoolDumpResponse? dump = null;
for (var attempt = 0; attempt < 120; attempt++)
{
dump = await client.GetFromJsonAsync<SchoolDumpResponse>(
$"/api/dev/schools/{schoolId}/dump",
TestContext.Current.CancellationToken);
Assert.NotNull(dump);
if (!dump.Running)
{
Assert.Fail("Clock is paused; occupancy stays empty and this wait would hang.");
}
if (dump.People.Any(person => !string.IsNullOrWhiteSpace(person.NodeId)))
{
return dump;
}
await Task.Delay(250, TestContext.Current.CancellationToken);
}
throw new InvalidOperationException(
$"Nobody had a NodeId after hire with the clock running at {dump?.GameTime:HH:mm}.");
}
private static async Task HireMathAsync(HttpClient client, int schoolId)
{
var staffing = await client.GetFromJsonAsync<StaffingResponse>(