From 60205398bb9c552d8027d3ede4b11b9dea7535ec Mon Sep 17 00:00:00 2001 From: agent_coder Date: Sat, 11 Jul 2026 08:25:29 +0300 Subject: [PATCH] =?UTF-8?q?test(ai-chat):=20=D0=B7=D0=B0=D1=81=D1=82=D0=B0?= =?UTF-8?q?=D0=B1=D0=B8=D1=82=D1=8C=20findAllByChat=20=D0=B2=20lifecycle-?= =?UTF-8?q?=D0=BC=D0=BE=D0=BA=D0=B5=20=D0=BF=D0=BE=D0=B4=20convert-before-?= =?UTF-8?q?insert=20(#489)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Коммит 1 (#489) прогоняет загрузку истории + convertToModelMessages ДО insert user-строки (convert-before-insert, чтобы ретрай не плодил дубли), поэтому stream() теперь зовёт реальный метод репо findAllByChat перед insert. Хэнд-роллед мок в тесте «exception after beginRun → settled to error» стабил только insert, и тест ловил «findAllByChat is not a function» вместо «insert boom». Порядок инварианта НЕ изменился: и findAllByChat, и insert идут ПОСЛЕ beginRun (reconcileChat между ними — best-effort, глотает ошибку), так что бросок по- прежнему происходит ПОСЛЕ начала рана и обязан сеттлить ран в error. Застабил findAllByChat → [] (реальный репо-метод, см. ai-chat.controller.ts), тест снова доходит до insert boom и проверяет settle-to-error. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../src/core/ai-chat/ai-chat.service.lifecycle.spec.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/server/src/core/ai-chat/ai-chat.service.lifecycle.spec.ts b/apps/server/src/core/ai-chat/ai-chat.service.lifecycle.spec.ts index a6f3e75c..22a721e8 100644 --- a/apps/server/src/core/ai-chat/ai-chat.service.lifecycle.spec.ts +++ b/apps/server/src/core/ai-chat/ai-chat.service.lifecycle.spec.ts @@ -97,8 +97,14 @@ describe('AiChatService.stream run-lifecycle safety net (#184)', () => { }; const runService = new AiChatRunService(runRepo as never, { isCloud: () => false } as never); - // The user-message insert (the first bare await after beginRun) throws. + // The user-message insert throws. #489 runs the history load + convert BEFORE + // the insert (convert-before-insert, so a retry cannot duplicate the user row), + // so `findAllByChat` (a real repo method) is now called first — stub it to an + // empty history so the flow reaches the insert. Both awaits are AFTER beginRun, + // so the "exception after beginRun -> settled to error" invariant is unchanged; + // the throw point simply moved from insert to a later insert after a no-op load. const aiChatMessageRepo = { + findAllByChat: jest.fn().mockResolvedValue([]), insert: jest.fn().mockRejectedValue(new Error('insert boom')), }; const aiChatRepo = {