diff --git a/src/core/queue.ts b/src/core/queue.ts index 55f2021..d536c78 100644 --- a/src/core/queue.ts +++ b/src/core/queue.ts @@ -27,6 +27,7 @@ export class QueueRunner { // Повторный вызов во время работы не запускает параллельный проход, // а просит ровно один дополнительный после текущего. async tick(): Promise { + if (this.controller.signal.aborted) return; if (this.running) { this.rerun = true; return; @@ -36,7 +37,7 @@ export class QueueRunner { do { this.rerun = false; await this.pass(); - } while (this.rerun); + } while (this.rerun && !this.controller.signal.aborted); } finally { this.running = false; } @@ -57,10 +58,14 @@ export class QueueRunner { (r) => ({ record: transition(r, { type: 'temporary', message: INTERRUPTED_SEND }, now) }), ); } - for (const item of due(items, now, env.deviceId)) await this.send(item); + for (const item of due(items, now, env.deviceId)) { + if (this.controller.signal.aborted) break; + await this.send(item); + } } private async send(item: Located): Promise { + if (this.controller.signal.aborted) return; const { recordings, transcriber, env } = this.deps; const now = env.now(); // Список мог устареть (metadataCache) — решение принимаем по актуальному файлу. diff --git a/tests/queue.test.ts b/tests/queue.test.ts index 0abae77..2dc8af5 100644 --- a/tests/queue.test.ts +++ b/tests/queue.test.ts @@ -237,4 +237,26 @@ describe('QueueRunner.tick', () => { ctx.runner.dispose(); expect(ctx.transcriber.calls[0].signal.aborted).toBe(true); }); + + it('dispose during a request stops the pass without touching later due records', async () => { + const ctx = setup(); + const first = seed(ctx, rec('a')); + const second = seed(ctx, rec('b')); + const beforeSecond = ctx.notes.files.get(second); + ctx.transcriber.steps.push(async () => { + ctx.runner.dispose(); + throw new Error('aborted'); + }); + + await ctx.runner.tick(); + + expect(ctx.transcriber.calls).toHaveLength(1); + expect(fields(ctx, first)).toMatchObject({ 'voice-status': 'retry', 'voice-attempts': '1' }); + expect(fields(ctx, second)['voice-status']).toBe('saved'); + expect(ctx.notes.files.get(second)).toBe(beforeSecond); + + const listCallsBefore = ctx.notes.listCalls; + await ctx.runner.tick(); + expect(ctx.notes.listCalls).toBe(listCallsBefore); + }); });