core(M2): машина состояний сессии Ayla LAN + mock-модуль + интеграционные сценарии

- session.{hpp,cpp}: state machine (idle/registering/online/recovering/
  offline/key_error); httpd-обработчики key_exchange (200/426/412, re-key
  прозрачно), commands (одна команда, 206/200, envelope, глобальный seq_no),
  datapoint (unpack -> PropertyEvent / 401+тишина 50с для re-key-восстановления);
  сессионный поток: local_reg POST?dsn/PUT (local_ip_for), keep-alive, backoff
  x1.6->60с, 503->offline/NoSlot, activation-timeout->recovering, delete_session
  с ожиданием выдачи; очередь с coalescing + batch; телеметрия; колбэки из
  двух потоков с задокументированным контрактом; буферы datapoint-пути в Impl.
- platform: local_ip_for (UDP-connect) posix+esp-idf; стек httpd 24576
  (переполнение 16КБ поймано gdb на Release).
- mock_ac.py: мок-модуль, stdlib-only чистый python AES-256 (свёрстан с
  pycryptodome); сценарии: 503, no-poll, rekey-every, stale-gap (эмуляция
  'вернувшегося' приложения), fail-pushes (битая подпись), garbage-pushes
  (обрыв блока), break-outbound (исходящий десинк -> модуль ре-кает на
  local_reg, как probe1-3), push-every, fail-first-ke.
- session_runner + test_session_mock.py: 9 сценариев через ctest, включая
  самосинхронизацию CBC и восстановление после исходящего десинка.
- Прибор AP-WC1E: активация <=1с; re-key семантика ИСПРАВЛЕНА по живым
  тестам: re-key при зазоре local_reg >= ~44-50с (не по возрасту сессии!);
  при честном keep-alive 15с сессия стабильна без re-key; PROTOCOL/LEGACY/
  PLAN обновлены; восстановление = тишина >порога + возврат.
- CI: 7/7 x3 (gcc-Rel, gcc-ASan/UBSan, clang); ESP-IDF esp32 build complete.
Ревью под-агентом: 2 круга (стек httpd, залипание состояний, dangling cfg,
физика десинка) — APPROVED.
This commit is contained in:
2026-09-27 10:53:28 +03:00
parent 345fe19ca7
commit e74f3dc67a
15 changed files with 2009 additions and 26 deletions

View File

@@ -20,9 +20,23 @@ function(fgl_add_test name)
add_test(NAME ${name} COMMAND test_${name})
endfunction()
# Раннер сессии — не тест, приложение для интеграционных сценариев
# (запускается tests/ayla/test_session_mock.py через ctest).
add_executable(session_runner ayla/session_runner.cpp)
target_compile_features(session_runner PRIVATE cxx_std_20)
target_link_libraries(session_runner PRIVATE fgl-aircon)
target_include_directories(session_runner PRIVATE "${CMAKE_SOURCE_DIR}/src")
fgl_add_test(ayla_platform ayla/test_platform.cpp)
fgl_add_test(ayla_httpd ayla/test_httpd.cpp)
fgl_add_test(ayla_crypto ayla/test_crypto.cpp)
fgl_add_test(ayla_envelope ayla/test_envelope.cpp)
fgl_add_test(ayla_json ayla/test_json.cpp)
fgl_add_test(ayla_httpc ayla/test_httpc.cpp)
# Интеграционные сценарии с mock-модулем (python, stdlib-only).
find_package(Python3 COMPONENTS Interpreter REQUIRED)
add_test(NAME ayla_session_mock
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/ayla/test_session_mock.py
$<TARGET_FILE:session_runner> ${CMAKE_CURRENT_SOURCE_DIR}/ayla/mock_ac.py)
set_tests_properties(ayla_session_mock PROPERTIES TIMEOUT 180)