Состояние модулей: ответ опроса прежнего проекта не возвращает событие (#124, #116)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andrey Epifancev 2026-10-26 01:03:32 +04:00
parent b5b5b33954
commit 231ca2f199
2 changed files with 36 additions and 1 deletions

View File

@ -124,7 +124,8 @@ function useTracker(m: (typeof TRACKED)[number], { qc, project, pollMs, agents }
}, [qc, status, setActiveNow]);
useEffect(() => {
if (!active) return;
// #116: ответ обрабатывается, только если отслеживаемый пакет — тот же объект, что в activeRef (смена проекта обнуляет его синхронно)
if (!active || activeRef.current !== active) return;
const b = batch.data, e = batch.error;
if (b && b.id === active.id && !batch.isError) {
if (terminal(b)) {

View File

@ -337,6 +337,40 @@ describe("ActivityProvider", () => {
expect(m.now().jira.event).toMatchObject({ kind: "batch", id: 5 });
});
it("#116: ответ опроса прежнего проекта, пришедший в коммите смены проекта, не возвращает событие", async () => {
const late = deferred<void>();
let polls = 0;
hub({
...listing((mod, project) => (mod === "wiki" && project === "AWPCA" ? [item(9, "wiki")] : [])),
"GET /batches/:id": async () => {
if (++polls > 1) await late.promise; // первый ответ показан сразу, следующий держится до смены проекта
return batch("running", { type: "wiki_sync" }, { id: 9, module: "wiki" });
},
});
const m = mount({ pollMs: 5 });
await waitFor(() => expect(m.now().wiki.event).toMatchObject({ kind: "batch", id: 9 }));
await waitFor(() => expect(polls).toBeGreaterThanOrEqual(2)); // повторный опрос ушёл и висит
// смена проекта и ответ опроса — одним коммитом: оба обновления копятся в act и рендерятся вместе на выходе из него
await act(async () => { m.setProject("STOCKS_DRRK"); late.resolve(); await sleep(20); });
await waitFor(() => expect(m.now().wiki.event).toBeNull());
await sleep(50);
expect(m.now().wiki.event).toBeNull();
expect(m.now().wiki.busy).toBe(false);
});
it("#116: A → B → A — после возврата на AWPCA незавершённый пакет 9 снова подхватывается", async () => {
hub({
...listing((mod, project) => (mod === "wiki" && project === "AWPCA" ? [item(9, "wiki")] : [])),
...byId({ 9: "wiki_sync" }, () => "running"),
});
const m = mount();
await waitFor(() => expect(m.now().wiki.event).toMatchObject({ kind: "batch", id: 9 }));
m.setProject("STOCKS_DRRK");
await waitFor(() => expect(m.now().wiki.event).toBeNull());
m.setProject("AWPCA");
await waitFor(() => expect(m.now().wiki.event).toMatchObject({ kind: "batch", id: 9 }));
});
it("M-2: пакет, отданный track для нового проекта, переживает смену проекта на него; чужой — сбрасывается", async () => {
hub({ ...byId({ 9: "wiki_sync" }, () => "running") });
const m = mount();