123 lines
3.3 KiB
TypeScript
123 lines
3.3 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
|
|
import {
|
|
buildInterruptedRestartAnnouncement,
|
|
getInterruptedRecoveryCandidates,
|
|
type RestartContext,
|
|
} from './restart-context.js';
|
|
import type { RegisteredGroup } from './types.js';
|
|
|
|
function makeGroup(folder: string): RegisteredGroup {
|
|
return {
|
|
name: folder,
|
|
folder,
|
|
trigger: '@Andy',
|
|
added_at: new Date().toISOString(),
|
|
requiresTrigger: false,
|
|
};
|
|
}
|
|
|
|
describe('getInterruptedRecoveryCandidates', () => {
|
|
it('returns only registered interrupted groups and deduplicates by chatJid', () => {
|
|
const roomBindings: Record<string, RegisteredGroup> = {
|
|
'dc:1': makeGroup('group-one'),
|
|
'dc:2': makeGroup('group-two'),
|
|
};
|
|
|
|
const context: RestartContext = {
|
|
chatJid: 'dc:main',
|
|
summary: 'restart',
|
|
verify: [],
|
|
writtenAt: new Date().toISOString(),
|
|
interruptedGroups: [
|
|
{
|
|
chatJid: 'dc:1',
|
|
groupName: 'one',
|
|
status: 'processing',
|
|
elapsedMs: 1000,
|
|
pendingMessages: true,
|
|
pendingTasks: 0,
|
|
rewindToSeq: 42,
|
|
},
|
|
{
|
|
chatJid: 'dc:1',
|
|
groupName: 'one-duplicate',
|
|
status: 'waiting',
|
|
elapsedMs: null,
|
|
pendingMessages: false,
|
|
pendingTasks: 1,
|
|
},
|
|
{
|
|
chatJid: 'dc:2',
|
|
groupName: 'two',
|
|
status: 'idle',
|
|
elapsedMs: null,
|
|
pendingMessages: false,
|
|
pendingTasks: 0,
|
|
},
|
|
{
|
|
chatJid: 'dc:3',
|
|
groupName: 'missing',
|
|
status: 'processing',
|
|
elapsedMs: 500,
|
|
pendingMessages: true,
|
|
pendingTasks: 0,
|
|
},
|
|
],
|
|
};
|
|
|
|
expect(getInterruptedRecoveryCandidates(context, roomBindings)).toEqual([
|
|
{
|
|
chatJid: 'dc:1',
|
|
groupFolder: 'group-one',
|
|
status: 'processing',
|
|
pendingMessages: true,
|
|
pendingTasks: 0,
|
|
rewindToSeq: 42,
|
|
},
|
|
{
|
|
chatJid: 'dc:2',
|
|
groupFolder: 'group-two',
|
|
status: 'idle',
|
|
pendingMessages: false,
|
|
pendingTasks: 0,
|
|
rewindToSeq: null,
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('returns empty when there is no explicit restart context', () => {
|
|
expect(getInterruptedRecoveryCandidates(null, {})).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe('buildInterruptedRestartAnnouncement', () => {
|
|
it('promises auto-resume when a rewind seq is captured', () => {
|
|
const message = buildInterruptedRestartAnnouncement({
|
|
chatJid: 'dc:1',
|
|
groupName: 'room',
|
|
status: 'processing',
|
|
elapsedMs: 12000,
|
|
pendingMessages: true,
|
|
pendingTasks: 0,
|
|
rewindToSeq: 100,
|
|
});
|
|
expect(message).toContain('서비스 재시작으로 이전 작업이 중단됐습니다.');
|
|
expect(message).toContain('자동으로 이어갑니다');
|
|
expect(message).not.toContain('필요하면 이어서');
|
|
});
|
|
|
|
it('falls back to manual prompt when no rewind seq exists', () => {
|
|
const message = buildInterruptedRestartAnnouncement({
|
|
chatJid: 'dc:1',
|
|
groupName: 'room',
|
|
status: 'waiting',
|
|
elapsedMs: null,
|
|
pendingMessages: false,
|
|
pendingTasks: 0,
|
|
});
|
|
expect(message).toContain('필요하면 이어서');
|
|
expect(message).not.toContain('자동으로 이어갑니다');
|
|
});
|
|
});
|