- Updated `PeopleApiTests` to correctly handle family structures, ensuring tests account for single-parent scenarios and sorting behavior for surnames. - Added a new test to validate roster integrity after the yearly intake, confirming that the composition reflects changes post-intake. - Revised assertions to ensure accurate comparisons of student and family data, improving test reliability. - Enhanced documentation within tests to clarify the purpose and expected outcomes of new functionalities. - Addressed discrepancies between design and implementation regarding the layout of the people management interface.
18 KiB
18 KiB
Проверенные срезы
Журнал ревью. Фаза с ✅ в README.md — заявка автора; строка здесь — проверенный
факт: каждое обещание фазы сверено с кодом и тестом, найденное дописано или исправлено.
Коммит в «Проверен на» делает журнал самопроверяющимся. Срез перестаёт считаться проверенным,
как только git log <коммит>..HEAD -- <пути> перестаёт быть пустым — поэтому «Пути» обязательны.
Срез 1. Оболочка и карта
- Фазы: 0–4
- Проверен на:
b135a9c, 2026-08-19 - Пути:
src/HSchool.Client,src/HSchool.Protocol,src/HSchool.Content,src/HSchool.Server/Game,src/HSchool.Server/Api/ModEndpoints.cs,src/HSchool.Server/Api/SchoolEndpoints.cs,src/HSchool.Server/Net,tests/HSchool.Content.Tests,tests/HSchool.Protocol.Tests,tests/HSchool.AppHost.Tests/SchoolApiTests.cs,tests/HSchool.AppHost.Tests/GameSocketTests.cs - Итог: дописано 2 теста (оба прогнаны на чистом
b135a9c), расхождений кода с дизайном не найдено кроме одного — панель «Общие события» из фазы 1 снята с экрана в фазе 8 и решение нигде не записано (оставлено открытым, см. ниже)
Что подтверждено:
- Фаза 0.
pixi.jsнет ни вpackage.json, ни в lockfile, ни в импортах;npm run buildпроходит. - Фаза 1. Дерево карты, панель локации со всеми пятью секциями и пустыми состояниями, клик по
дереву фильтрует на клиенте,
localize()перекрашивает подписи. Панели «Общие события» на экране нет — единственное расхождение среза. - Фаза 2.
SchoolWorker—LongRunning-поток, свойPeriodicTimer, свойWorld, аккумулятор с потолком в 5 шагов. Супервизор держит таблицу и не трогаетWorld(инвариант 3). Сейв пишется на create, на shutdown и поSaveIntervalSeconds; настройки коалесцируются. Требуемый фазой тест «create → reload → та же школа с тем же временем» — этоGameSocketTests.ReloadFromDisk_RestoresAPausedClock(точное равенство времени, паузы и скорости), плюсSchoolApiTests.ReloadFromDisk_RestoresCreatedSchools. Независимость школ —GameSocketTests.PausingOneSchool_DoesNotStopAnother. - Фаза 3. Все восемь тестов из списка фазы на месте в
HSchool.Content.Tests(JSONC-фикстуры, last-wins поdefNameи по ключу локали, parent/abstract и цикл, патчaddвactions, связность, пустая комната, одна комната без двора). Ванильная раскладка валидируется (VanillaCoreTests.CoreDefaultMap_PassesValidation). Пропавшая папка мода не стартует школу и не трогает файл: исключение летит до созданияSchool, поэтомуfinallyсPersist()недостижим, а супервизор только снимает школу с таблицы. - Фаза 4.
GET /api/mods,GET /api/catalog?lang=,POST /api/schoolsс картой и модами, снимок карты при OpenSchool в языке Hello — всё покрыто тестами (OpeningASchool_SendsAMapSnapshot,LabelsTheSnapshotInTheHelloLocale,WithACustomMap_ReturnsThatLayout,ReloadFromDisk_RestoresACustomMap). Кодек,protocol.tsиdocs/protocol.mdописывают один и тот жеMapSnapshot, байтовые смещения проверены с обеих сторон; версия 7 совпадает вProtocolConstantsиPROTOCOL_VERSION. - Инварианты.
Content/People/Schedule/Aiне ссылаются на Arch и ASP.NET,Simulation— только на Arch;DateTime.Now/UtcNowв этих проектах нет. - «Things that will bite you». Ни одна из относящихся к срезу регрессий не вернулась:
modal.tsрезолвит промис явно,.dialog--screenдержитheight: fit-content,DefaultStartDateфорсирует UTC-kind, аккумулятор работника на месте,MapSnapshotSizeсчитается от сообщения,GameClientпереносит обаWaitToReadAsyncмежду итерациями, outbox роняет старейший кадр, меню патчит карточки черезcard.update.
Дописано:
SchoolApiTests.Catalog_WithAnUnknownMod_IsRejected—GET /api/catalog?mods=с неизвестной пачкой описан вdocs/protocol.mdкак400 unknown-mod, но не проверялся.SchoolApiTests.ModId_ThatEscapesTheModsFolder_IsRejectedOnBothEndpoints— инвариант 6: id пачки приходит из браузера и подставляется в путь.ModContent.IsSafePackIdего чистит на обоих входах (каталог и создание школы), тестом это закреплено не было.
Открыто:
- Панель «Общие события» пропала. Фаза 1 требует три области,
design/near-term.mdдержит их в «Что уже зафиксировано», аdesign/people.mdописывает список людей как панель «рядом с картой и событиями». Коммитd8f8db6(фаза 8) заменил панель вкладками Карта/Люди, не сказав об этом; ключиeventsTitle/eventsEmptyостались вi18n/strings.tsмёртвыми. Решать надо одно из двух: вернуть пустую панель (или третью вкладку) — или записать отказ от неё в дизайн и убрать ключи. Молча менять экран ревью не стало.
Замечено рядом (за границами среза):
SchoolRegistryпосле фазы 2 в бою не используется: сервер тикает школы работниками, а из класса вызывается толькоTryNormalizeName.SchoolRegistryTests.Tick_AdvancesOnlyRunningSchoolsпроверяет путь, которым сервер больше не ходит.PeopleApiTests.Card_IncludesFamilyAndLiveNeedsзависит от порядка тестов (фаза 8, срез 2). Проверено на чистом checkoutb135a9cв отдельном worktree: в одиночку класс проходит (8/8), в полном прогоне класса падает наAssert.Equal(2, card.Family.Parents.Count)— ожидалось 2, пришёл 1. Причина: тест берётPeople[0]и требует у него двух родителей, а состав семьи зависит от сида, сид — от id школы, id — от того, сколько школ создали тесты до него (и отsaves/index.jsonна машине). На машине с накопленными сейвами тест зелёный, на чистом клоне — красный. Чинить надо тест: брать ученика, у которого два родителя, а не первого попавшегося. Неполные семьи — сознательное решениеdesign/people.md, не баг генерации.GameSocketTests.OpeningASchoolDuringAMathLesson_...не выдерживает полный прогон (срез 4). В одиночку проходит за 1 с, в полном прогоне падает с «No presence frame matched within 40 frames» через 22 с. Похоже на нехватку кадров под нагрузкой, а не на ошибку присутствия.- Полный
dotnet testодин раз упал: процессHSchool.Simulation.Testsумер с0xC0000005, успев отчитаться о 44 тестах из 63, и дваActivityTests(фаза 20) не прошли. Повторить не удалось. Скорее всего дерево правилось прямо во время прогона (см. ниже). - На момент проверки в рабочем дереве шла правка фазы 21 (
Decision.cs,LessonLearning.cs,LessonLearningSystem.cs,PresenceSystem.cs,gameScreen.ts), и какое-то время решение не компилировалось. Выводам по срезу 1 это не мешает: его файлы правка не трогает, а дописанные тесты прогнаны и на чистомb135a9c, и на дереве после починки сборки.
Срез 2. Люди
- Фазы: 5–9
- Проверен на:
5cd5a6d, 2026-08-19 - Пути:
src/HSchool.People,src/HSchool.Content/PeopleDefs.cs,src/HSchool.Simulation/RosterSpawner.cs,src/HSchool.Simulation/School.cs,src/HSchool.Server/Api/SchoolEndpoints.cs,src/HSchool.Server/Game/PersonCardReader.cs,src/HSchool.Client/src/ui/peoplePanel.ts,src/HSchool.Client/src/ui/personCard.ts,tests/HSchool.People.Tests,tests/HSchool.Content.Tests/PeopleDefTests.cs,tests/HSchool.Simulation.Tests/PeopleInSchoolTests.cs,tests/HSchool.AppHost.Tests/PeopleApiTests.cs - Итог: исправлен один тест, который падал на чистом клоне, укреплён второй, дописан один; расхождение с дизайном одно и то же, что в срезе 1 (раскладка оболочки менеджера)
Что подтверждено:
- Фаза 5. Все шесть пунктов списка тестов на месте в
PeopleDefTests(разбор каждого вида, наследование навыка, патч черты, неизвестная ссылка — ошибка каталога, склонение по модели и перекрытие явной таблицей), плюсExtraPack_AddsANameSetAndTrait— критерий «мод со своим набором имён виден в каталоге». Наборы имён вGET /api/catalogпроверены вSchoolApiTests.Catalog_LabelsDefsInTheRequestedLanguage. - Фаза 6. Все шесть тестов из списка фазы существуют и проходят. Критерий «в проекте нет
сокетов, ECS и
DateTime.Now» закреплён самими тестами —Assembly_DoesNotReferenceArchAspNetOrSocketsиSources_DoNotUseWallClock. - Фаза 7. Одиннадцать кабинетов (
VanillaMap_HasElevenHomeroomsNotTheComputerLab), нужды тикают со школой, ростер публикуется снимком,PersistPeopleзовётся только когда состав изменился — файл не переписывается по таймеру. Ростер, не сходящийся с картой, оставляет школу незапущенной (RosterFit.Matches→SchoolContentUnavailableException). - Фаза 8. Список читает опубликованный снимок (
loop.FindPeople), карточка идёт через очередь команд сTaskCompletionSource— инвариант 3 цел. Все пять требуемых фазой тестов API на месте. Клиент: фильтры, сортируемые колонки, пейджер, семейные ссылки, всё черезt(...); в строке списка только лёгкие поля. - Фаза 9. Выпуск старшей имеющейся параллели, сохранение вместимости, семьи через поколения и
уход родителей выпускников покрыты в
YearlyIntakeTestsиPeopleInSchoolTests. - Дизайн.
design/people.mdсверен построчно: неполные семьи ≈8% (тест требует 5–10%), фамилия и отчество отцовские даже у одинокой матери, идентификатор ребёнка — счётчик, места раздаются вперемешку. Расхождений нет.
Исправлено:
PeopleApiTests.Card_IncludesFamilyAndLiveNeedsпадал на чистом клоне. Тест брал первого ученика страницы и требовал у него ровно двух родителей. Состав семьи выводится из сида, а сид — это id школы (SchoolWorker.InstallPeople:seed = school.Id), то есть зависит от того, сколько школ создали тесты до него и что лежит вsaves/index.json. Замер по сидам 1–30: у 28 первый пятиклассник с двумя родителями, у 2 — с одним, и полный прогон на свежем checkoutb135a9cв отдельном worktree ловил как раз такой сид. Одинокие родители — сознательное решение дизайна, поэтому неправ был тест: теперь он ищет ученика, у которого есть мать, а количество родителей проверяет диапазоном. Проверено тем же способом, каким ловилось: полный прогон на чистомb135a9cтрижды, 60/60.PeopleApiTests.List_SortsBySurnameмерил не тем. Тест требовал ordinal-порядка, а сервер ставит Ё на место Е (RosterBrowser.NameOrder, тестSurnameSort_FollowsTheAlphabetNotCodePoints). Совпадали они только потому, что вcoreнет фамилий на Ё — мод с такой фамилией уронил бы тест, а не код. Теперь тест складывает Ё в Е так же, как сервер.
Дописано:
PeopleApiTests.ReloadAfterTheYearlyIntake_RestoresTheNewComposition— критерии фаз 7 и 9 «перезапуск поднимает тех же людей» и «перезапуск после перехода поднимает новый состав» не проверялись сквозь сервер. Просто сравнить людей до и после reload недостаточно: генерация детерминирована от id школы, поэтому забывший файл сервер собрал бы тот же ростер и тест бы ничего не поймал. Тест поэтому ждёт 1 сентября (школа создаётся за 15 игровых минут до полуночи, это около трёх секунд реального времени), а после reload требует состав после набора и явно отличный от исходного.
Открыто:
- Раскладка оболочки менеджера разошлась с дизайном — то же расхождение, что записано в срезе
- Фаза 8 обещает панель «Люди» нижним рядом во всю ширину,
design/people.mdфиксирует «панель в оболочке менеджера, нижний ряд; не отдельный экран», а на экране она вкладкой рядом с картой. Тот же коммитd8f8db6, что убрал панель событий. Решается одним решением на оба пункта: либо вернуть нижний ряд, либо переписать обе строки дизайна.
- Фаза 8 обещает панель «Люди» нижним рядом во всю ширину,
Замечено рядом (за границами среза):
design/people.mdи задача фазы 5 говорят «скорость убывания нужд вcore— ноль», а вdefs/needsтеперьdecayPerHourот 0.05 до 0.15. Это сознательная работа фазы 20, не регрессия; строка дизайна просто устарела и относится к срезу 5.- Срез 1 успел сдвинуться: коммит
5cd5a6dтронулgameScreen.tsиstrings.ts(подпись идущего человека). По правилу журнала он больше не «проверен наb135a9c» — правка маленькая и относится к срезу 5, но следующий проход это увидит.