diff --git a/src/stores/queueStore.ts b/src/stores/queueStore.ts index 5cb754ca6..99d8d0eb7 100644 --- a/src/stores/queueStore.ts +++ b/src/stores/queueStore.ts @@ -425,6 +425,47 @@ export class TaskItemImpl { } } +const extractPromptIds = (tasks: TaskItem[]): Set => + new Set(tasks.map((task) => task.prompt[1])) + +const isAddedAfter = (queueIndex: number) => (task: TaskItem) => + task.prompt[0] > queueIndex + +const sortNewestFirst = (a: TaskItemImpl, b: TaskItemImpl) => + b.queueIndex - a.queueIndex + +const toTaskItemImpls = (tasks: TaskItem[]): TaskItemImpl[] => + tasks.map( + (task) => + new TaskItemImpl( + task.taskType, + task.prompt, + 'status' in task ? task.status : undefined, + 'outputs' in task ? task.outputs : undefined + ) + ) + +const reconcileHistoryWithServer = ( + serverHistory: TaskItem[], + clientHistory: TaskItemImpl[], + lastKnownQueueIndex: number, + maxItems: number +): TaskItemImpl[] => { + const serverPromptIds = extractPromptIds(serverHistory) + + const itemsAddedSinceLastSync = toTaskItemImpls( + serverHistory.filter(isAddedAfter(lastKnownQueueIndex)) + ) + + const itemsStillOnServer = clientHistory.filter((item) => + serverPromptIds.has(item.promptId) + ) + + return [...itemsAddedSinceLastSync, ...itemsStillOnServer] + .sort(sortNewestFirst) + .slice(0, maxItems) +} + export const useQueueStore = defineStore('queue', () => { const runningTasks = ref([]) const pendingTasks = ref([]) @@ -459,37 +500,15 @@ export const useQueueStore = defineStore('queue', () => { api.getHistory(maxHistoryItems.value) ]) - const toClassAll = (tasks: TaskItem[]): TaskItemImpl[] => - tasks - .map( - (task: TaskItem) => - new TaskItemImpl( - task.taskType, - task.prompt, - // status and outputs only exist on history tasks - 'status' in task ? task.status : undefined, - 'outputs' in task ? task.outputs : undefined - ) - ) - .sort((a, b) => b.queueIndex - a.queueIndex) + runningTasks.value = toTaskItemImpls(queue.Running).sort(sortNewestFirst) + pendingTasks.value = toTaskItemImpls(queue.Pending).sort(sortNewestFirst) - runningTasks.value = toClassAll(queue.Running) - pendingTasks.value = toClassAll(queue.Pending) - - const allIndex = new Set( - history.History.map((item: TaskItem) => item.prompt[0]) + historyTasks.value = reconcileHistoryWithServer( + history.History, + historyTasks.value, + lastHistoryQueueIndex.value, + maxHistoryItems.value ) - const newHistoryItems = toClassAll( - history.History.filter( - (item) => item.prompt[0] > lastHistoryQueueIndex.value - ) - ) - const existingHistoryItems = historyTasks.value.filter((item) => - allIndex.has(item.queueIndex) - ) - historyTasks.value = [...newHistoryItems, ...existingHistoryItems] - .slice(0, maxHistoryItems.value) - .sort((a, b) => b.queueIndex - a.queueIndex) } finally { isLoading.value = false } diff --git a/tests-ui/tests/store/queueStore.test.ts b/tests-ui/tests/store/queueStore.test.ts index 313673e69..72a398279 100644 --- a/tests-ui/tests/store/queueStore.test.ts +++ b/tests-ui/tests/store/queueStore.test.ts @@ -1,6 +1,85 @@ -import { describe, expect, it } from 'vitest' +import { createPinia, setActivePinia } from 'pinia' +import { beforeEach, describe, expect, it, vi } from 'vitest' -import { TaskItemImpl } from '@/stores/queueStore' +import type { + HistoryTaskItem, + PendingTaskItem, + RunningTaskItem, + TaskOutput, + TaskPrompt, + TaskStatus +} from '@/schemas/apiSchema' +import { api } from '@/scripts/api' +import { TaskItemImpl, useQueueStore } from '@/stores/queueStore' + +// Fixture factories +const createTaskPrompt = ( + queueIndex: number, + promptId: string, + inputs: Record = {}, + extraData: Record = {}, + outputsToExecute: any[] = [] +): TaskPrompt => [queueIndex, promptId, inputs, extraData, outputsToExecute] + +const createTaskStatus = ( + statusStr: 'success' | 'error' = 'success', + messages: any[] = [] +): TaskStatus => ({ + status_str: statusStr, + completed: true, + messages +}) + +const createTaskOutput = ( + nodeId: string = 'node-1', + images: any[] = [] +): TaskOutput => ({ + [nodeId]: { + images + } +}) + +const createRunningTask = ( + queueIndex: number, + promptId: string +): RunningTaskItem => ({ + taskType: 'Running', + prompt: createTaskPrompt(queueIndex, promptId), + remove: { name: 'Cancel', cb: () => {} } +}) + +const createPendingTask = ( + queueIndex: number, + promptId: string +): PendingTaskItem => ({ + taskType: 'Pending', + prompt: createTaskPrompt(queueIndex, promptId) +}) + +const createHistoryTask = ( + queueIndex: number, + promptId: string, + outputs: TaskOutput = createTaskOutput(), + status: TaskStatus = createTaskStatus() +): HistoryTaskItem => ({ + taskType: 'History', + prompt: createTaskPrompt(queueIndex, promptId), + status, + outputs +}) + +// Mock API +vi.mock('@/scripts/api', () => ({ + api: { + getQueue: vi.fn(), + getHistory: vi.fn(), + clearItems: vi.fn(), + deleteItem: vi.fn(), + apiURL: vi.fn((path) => `/api${path}`), + addEventListener: vi.fn(), + removeEventListener: vi.fn() + } +})) describe('TaskItemImpl', () => { it('should remove animated property from outputs during construction', () => { @@ -154,3 +233,602 @@ describe('TaskItemImpl', () => { }) }) }) + +describe('useQueueStore', () => { + let store: ReturnType + + beforeEach(() => { + setActivePinia(createPinia()) + store = useQueueStore() + vi.clearAllMocks() + }) + + const mockGetQueue = vi.mocked(api.getQueue) + const mockGetHistory = vi.mocked(api.getHistory) + const mockClearItems = vi.mocked(api.clearItems) + const mockDeleteItem = vi.mocked(api.deleteItem) + + describe('initial state', () => { + it('should have empty state on initialization', () => { + expect(store.runningTasks).toEqual([]) + expect(store.pendingTasks).toEqual([]) + expect(store.historyTasks).toEqual([]) + expect(store.isLoading).toBe(false) + expect(store.maxHistoryItems).toBe(64) + }) + + it('should have empty computed tasks', () => { + expect(store.tasks).toEqual([]) + expect(store.flatTasks).toEqual([]) + expect(store.hasPendingTasks).toBe(false) + expect(store.lastHistoryQueueIndex).toBe(-1) + }) + }) + + describe('update() - basic functionality', () => { + it('should load running and pending tasks from API', async () => { + const runningTask = createRunningTask(1, 'run-1') + const pendingTask1 = createPendingTask(2, 'pend-1') + const pendingTask2 = createPendingTask(3, 'pend-2') + + mockGetQueue.mockResolvedValue({ + Running: [runningTask], + Pending: [pendingTask1, pendingTask2] + }) + mockGetHistory.mockResolvedValue({ History: [] }) + + await store.update() + + expect(store.runningTasks).toHaveLength(1) + expect(store.pendingTasks).toHaveLength(2) + expect(store.runningTasks[0].promptId).toBe('run-1') + expect(store.pendingTasks[0].promptId).toBe('pend-2') + expect(store.pendingTasks[1].promptId).toBe('pend-1') + }) + + it('should load history tasks from API', async () => { + const historyTask1 = createHistoryTask(5, 'hist-1') + const historyTask2 = createHistoryTask(4, 'hist-2') + + mockGetQueue.mockResolvedValue({ Running: [], Pending: [] }) + mockGetHistory.mockResolvedValue({ + History: [historyTask1, historyTask2] + }) + + await store.update() + + expect(store.historyTasks).toHaveLength(2) + expect(store.historyTasks[0].promptId).toBe('hist-1') + expect(store.historyTasks[1].promptId).toBe('hist-2') + }) + + it('should set loading state correctly', async () => { + mockGetQueue.mockResolvedValue({ Running: [], Pending: [] }) + mockGetHistory.mockResolvedValue({ History: [] }) + + expect(store.isLoading).toBe(false) + + const updatePromise = store.update() + expect(store.isLoading).toBe(true) + + await updatePromise + expect(store.isLoading).toBe(false) + }) + + it('should clear loading state even if API fails', async () => { + mockGetQueue.mockRejectedValue(new Error('API error')) + mockGetHistory.mockResolvedValue({ History: [] }) + + await expect(store.update()).rejects.toThrow('API error') + expect(store.isLoading).toBe(false) + }) + }) + + describe('update() - sorting', () => { + it('should sort tasks by queueIndex descending', async () => { + const task1 = createHistoryTask(1, 'hist-1') + const task2 = createHistoryTask(5, 'hist-2') + const task3 = createHistoryTask(3, 'hist-3') + + mockGetQueue.mockResolvedValue({ Running: [], Pending: [] }) + mockGetHistory.mockResolvedValue({ + History: [task1, task2, task3] + }) + + await store.update() + + expect(store.historyTasks[0].queueIndex).toBe(5) + expect(store.historyTasks[1].queueIndex).toBe(3) + expect(store.historyTasks[2].queueIndex).toBe(1) + }) + + it('should sort pending tasks by queueIndex descending', async () => { + const pend1 = createPendingTask(10, 'pend-1') + const pend2 = createPendingTask(15, 'pend-2') + const pend3 = createPendingTask(12, 'pend-3') + + mockGetQueue.mockResolvedValue({ + Running: [], + Pending: [pend1, pend2, pend3] + }) + mockGetHistory.mockResolvedValue({ History: [] }) + + await store.update() + + expect(store.pendingTasks[0].queueIndex).toBe(15) + expect(store.pendingTasks[1].queueIndex).toBe(12) + expect(store.pendingTasks[2].queueIndex).toBe(10) + }) + }) + + describe('update() - history reconciliation by promptId', () => { + it('should keep existing history items that are still in server response', async () => { + const hist1 = createHistoryTask(10, 'existing-1') + const hist2 = createHistoryTask(9, 'existing-2') + + mockGetQueue.mockResolvedValue({ Running: [], Pending: [] }) + mockGetHistory.mockResolvedValue({ History: [hist1, hist2] }) + + await store.update() + expect(store.historyTasks).toHaveLength(2) + + const hist3 = createHistoryTask(11, 'new-1') + mockGetHistory.mockResolvedValue({ + History: [hist3, hist1, hist2] + }) + + await store.update() + + expect(store.historyTasks).toHaveLength(3) + expect(store.historyTasks.map((t) => t.promptId)).toEqual([ + 'new-1', + 'existing-1', + 'existing-2' + ]) + }) + + it('should remove history items no longer in server response', async () => { + const hist1 = createHistoryTask(10, 'remove-me') + const hist2 = createHistoryTask(9, 'keep-me') + + mockGetQueue.mockResolvedValue({ Running: [], Pending: [] }) + mockGetHistory.mockResolvedValue({ History: [hist1, hist2] }) + + await store.update() + expect(store.historyTasks).toHaveLength(2) + + mockGetHistory.mockResolvedValue({ History: [hist2] }) + + await store.update() + + expect(store.historyTasks).toHaveLength(1) + expect(store.historyTasks[0].promptId).toBe('keep-me') + }) + + it('should add new history items with queueIndex > lastHistoryQueueIndex', async () => { + const hist1 = createHistoryTask(5, 'old-1') + + mockGetQueue.mockResolvedValue({ Running: [], Pending: [] }) + mockGetHistory.mockResolvedValue({ History: [hist1] }) + + await store.update() + expect(store.lastHistoryQueueIndex).toBe(5) + + const hist2 = createHistoryTask(6, 'new-1') + const hist3 = createHistoryTask(7, 'new-2') + mockGetHistory.mockResolvedValue({ + History: [hist3, hist2, hist1] + }) + + await store.update() + + expect(store.historyTasks).toHaveLength(3) + expect(store.historyTasks.map((t) => t.promptId)).toContain('new-1') + expect(store.historyTasks.map((t) => t.promptId)).toContain('new-2') + }) + + it('should NOT add history items with queueIndex <= lastHistoryQueueIndex', async () => { + const hist1 = createHistoryTask(10, 'existing-1') + + mockGetQueue.mockResolvedValue({ Running: [], Pending: [] }) + mockGetHistory.mockResolvedValue({ History: [hist1] }) + + await store.update() + + const oldHist = createHistoryTask(5, 'old-task-should-not-appear') + mockGetHistory.mockResolvedValue({ History: [hist1, oldHist] }) + + await store.update() + + expect(store.historyTasks).toHaveLength(1) + expect(store.historyTasks[0].promptId).toBe('existing-1') + }) + + it('should handle complete history replacement', async () => { + const hist1 = createHistoryTask(5, 'old-1') + const hist2 = createHistoryTask(4, 'old-2') + + mockGetQueue.mockResolvedValue({ Running: [], Pending: [] }) + mockGetHistory.mockResolvedValue({ History: [hist1, hist2] }) + + await store.update() + expect(store.historyTasks).toHaveLength(2) + + const newHist1 = createHistoryTask(10, 'new-1') + const newHist2 = createHistoryTask(9, 'new-2') + mockGetHistory.mockResolvedValue({ + History: [newHist1, newHist2] + }) + + await store.update() + + expect(store.historyTasks).toHaveLength(2) + expect(store.historyTasks.map((t) => t.promptId)).toEqual([ + 'new-1', + 'new-2' + ]) + }) + + it('should handle empty history from server', async () => { + const hist1 = createHistoryTask(5, 'will-be-removed') + + mockGetQueue.mockResolvedValue({ Running: [], Pending: [] }) + mockGetHistory.mockResolvedValue({ History: [hist1] }) + + await store.update() + expect(store.historyTasks).toHaveLength(1) + + mockGetHistory.mockResolvedValue({ History: [] }) + + await store.update() + + expect(store.historyTasks).toHaveLength(0) + }) + }) + + describe('update() - queue index collision (THE BUG FIX)', () => { + it('should NOT confuse different prompts with same queueIndex', async () => { + const hist1 = createHistoryTask(50, 'prompt-uuid-aaa') + + mockGetQueue.mockResolvedValue({ Running: [], Pending: [] }) + mockGetHistory.mockResolvedValue({ History: [hist1] }) + + await store.update() + expect(store.historyTasks).toHaveLength(1) + expect(store.historyTasks[0].promptId).toBe('prompt-uuid-aaa') + + const hist2 = createHistoryTask(51, 'prompt-uuid-bbb') + mockGetHistory.mockResolvedValue({ + History: [hist2] + }) + + await store.update() + + expect(store.historyTasks).toHaveLength(1) + expect(store.historyTasks[0].promptId).toBe('prompt-uuid-bbb') + expect(store.historyTasks[0].queueIndex).toBe(51) + }) + + it('should correctly reconcile when queueIndex is reused', async () => { + const hist1 = createHistoryTask(100, 'first-prompt-at-100') + const hist2 = createHistoryTask(99, 'prompt-at-99') + + mockGetQueue.mockResolvedValue({ Running: [], Pending: [] }) + mockGetHistory.mockResolvedValue({ History: [hist1, hist2] }) + + await store.update() + expect(store.historyTasks).toHaveLength(2) + + const hist3 = createHistoryTask(101, 'second-prompt-at-101') + mockGetHistory.mockResolvedValue({ + History: [hist3, hist2] + }) + + await store.update() + + expect(store.historyTasks).toHaveLength(2) + const promptIds = store.historyTasks.map((t) => t.promptId) + expect(promptIds).toContain('second-prompt-at-101') + expect(promptIds).toContain('prompt-at-99') + expect(promptIds).not.toContain('first-prompt-at-100') + }) + + it('should handle multiple queueIndex collisions simultaneously', async () => { + const hist1 = createHistoryTask(10, 'old-at-10') + const hist2 = createHistoryTask(20, 'old-at-20') + const hist3 = createHistoryTask(30, 'keep-at-30') + + mockGetQueue.mockResolvedValue({ Running: [], Pending: [] }) + mockGetHistory.mockResolvedValue({ + History: [hist3, hist2, hist1] + }) + + await store.update() + expect(store.historyTasks).toHaveLength(3) + + const newHist1 = createHistoryTask(31, 'new-at-31') + const newHist2 = createHistoryTask(32, 'new-at-32') + mockGetHistory.mockResolvedValue({ + History: [newHist2, newHist1, hist3] + }) + + await store.update() + + expect(store.historyTasks).toHaveLength(3) + const promptIds = store.historyTasks.map((t) => t.promptId) + expect(promptIds).toEqual(['new-at-32', 'new-at-31', 'keep-at-30']) + }) + }) + + describe('update() - maxHistoryItems limit', () => { + it('should enforce maxHistoryItems limit', async () => { + store.maxHistoryItems = 3 + + const tasks = Array.from({ length: 5 }, (_, i) => + createHistoryTask(10 - i, `hist-${i}`) + ) + + mockGetQueue.mockResolvedValue({ Running: [], Pending: [] }) + mockGetHistory.mockResolvedValue({ History: tasks }) + + await store.update() + + expect(store.historyTasks).toHaveLength(3) + expect(store.historyTasks[0].queueIndex).toBe(10) + expect(store.historyTasks[1].queueIndex).toBe(9) + expect(store.historyTasks[2].queueIndex).toBe(8) + }) + + it('should respect maxHistoryItems when combining new and existing', async () => { + store.maxHistoryItems = 5 + + const initial = Array.from({ length: 3 }, (_, i) => + createHistoryTask(10 + i, `existing-${i}`) + ) + + mockGetQueue.mockResolvedValue({ Running: [], Pending: [] }) + mockGetHistory.mockResolvedValue({ History: initial }) + + await store.update() + expect(store.historyTasks).toHaveLength(3) + + const newTasks = Array.from({ length: 4 }, (_, i) => + createHistoryTask(20 + i, `new-${i}`) + ) + mockGetHistory.mockResolvedValue({ + History: [...newTasks, ...initial] + }) + + await store.update() + + expect(store.historyTasks).toHaveLength(5) + expect(store.historyTasks[0].queueIndex).toBe(23) + }) + + it('should handle maxHistoryItems = 0', async () => { + store.maxHistoryItems = 0 + + const tasks = [createHistoryTask(10, 'hist-1')] + + mockGetQueue.mockResolvedValue({ Running: [], Pending: [] }) + mockGetHistory.mockResolvedValue({ History: tasks }) + + await store.update() + + expect(store.historyTasks).toHaveLength(0) + }) + + it('should handle maxHistoryItems = 1', async () => { + store.maxHistoryItems = 1 + + const tasks = [ + createHistoryTask(10, 'hist-1'), + createHistoryTask(9, 'hist-2') + ] + + mockGetQueue.mockResolvedValue({ Running: [], Pending: [] }) + mockGetHistory.mockResolvedValue({ History: tasks }) + + await store.update() + + expect(store.historyTasks).toHaveLength(1) + expect(store.historyTasks[0].queueIndex).toBe(10) + }) + + it('should dynamically adjust when maxHistoryItems changes', async () => { + store.maxHistoryItems = 10 + + const tasks = Array.from({ length: 15 }, (_, i) => + createHistoryTask(20 - i, `hist-${i}`) + ) + + mockGetQueue.mockResolvedValue({ Running: [], Pending: [] }) + mockGetHistory.mockResolvedValue({ History: tasks }) + + await store.update() + expect(store.historyTasks).toHaveLength(10) + + store.maxHistoryItems = 5 + mockGetHistory.mockResolvedValue({ History: tasks }) + + await store.update() + expect(store.historyTasks).toHaveLength(5) + }) + }) + + describe('computed properties', () => { + it('tasks should combine pending, running, and history in correct order', async () => { + const running = createRunningTask(5, 'run-1') + const pending1 = createPendingTask(6, 'pend-1') + const pending2 = createPendingTask(7, 'pend-2') + const hist1 = createHistoryTask(3, 'hist-1') + const hist2 = createHistoryTask(4, 'hist-2') + + mockGetQueue.mockResolvedValue({ + Running: [running], + Pending: [pending1, pending2] + }) + mockGetHistory.mockResolvedValue({ + History: [hist2, hist1] + }) + + await store.update() + + expect(store.tasks).toHaveLength(5) + expect(store.tasks[0].taskType).toBe('Pending') + expect(store.tasks[1].taskType).toBe('Pending') + expect(store.tasks[2].taskType).toBe('Running') + expect(store.tasks[3].taskType).toBe('History') + expect(store.tasks[4].taskType).toBe('History') + }) + + it('hasPendingTasks should be true when pending tasks exist', async () => { + mockGetQueue.mockResolvedValue({ + Running: [], + Pending: [createPendingTask(1, 'pend-1')] + }) + mockGetHistory.mockResolvedValue({ History: [] }) + + await store.update() + expect(store.hasPendingTasks).toBe(true) + }) + + it('hasPendingTasks should be false when no pending tasks', async () => { + mockGetQueue.mockResolvedValue({ Running: [], Pending: [] }) + mockGetHistory.mockResolvedValue({ History: [] }) + + await store.update() + expect(store.hasPendingTasks).toBe(false) + }) + + it('lastHistoryQueueIndex should return highest queue index', async () => { + const hist1 = createHistoryTask(10, 'hist-1') + const hist2 = createHistoryTask(25, 'hist-2') + const hist3 = createHistoryTask(15, 'hist-3') + + mockGetQueue.mockResolvedValue({ Running: [], Pending: [] }) + mockGetHistory.mockResolvedValue({ + History: [hist1, hist2, hist3] + }) + + await store.update() + expect(store.lastHistoryQueueIndex).toBe(25) + }) + + it('lastHistoryQueueIndex should be -1 when no history', async () => { + mockGetQueue.mockResolvedValue({ Running: [], Pending: [] }) + mockGetHistory.mockResolvedValue({ History: [] }) + + await store.update() + expect(store.lastHistoryQueueIndex).toBe(-1) + }) + }) + + describe('clear()', () => { + beforeEach(async () => { + mockGetQueue.mockResolvedValue({ + Running: [createRunningTask(1, 'run-1')], + Pending: [createPendingTask(2, 'pend-1')] + }) + mockGetHistory.mockResolvedValue({ + History: [createHistoryTask(3, 'hist-1')] + }) + await store.update() + }) + + it('should clear both queue and history by default', async () => { + mockClearItems.mockResolvedValue(undefined) + mockGetQueue.mockResolvedValue({ Running: [], Pending: [] }) + mockGetHistory.mockResolvedValue({ History: [] }) + + await store.clear() + + expect(mockClearItems).toHaveBeenCalledTimes(2) + expect(mockClearItems).toHaveBeenCalledWith('queue') + expect(mockClearItems).toHaveBeenCalledWith('history') + expect(store.runningTasks).toHaveLength(0) + expect(store.pendingTasks).toHaveLength(0) + expect(store.historyTasks).toHaveLength(0) + }) + + it('should clear only queue when specified', async () => { + mockClearItems.mockResolvedValue(undefined) + mockGetQueue.mockResolvedValue({ Running: [], Pending: [] }) + mockGetHistory.mockResolvedValue({ + History: [createHistoryTask(3, 'hist-1')] + }) + + await store.clear(['queue']) + + expect(mockClearItems).toHaveBeenCalledTimes(1) + expect(mockClearItems).toHaveBeenCalledWith('queue') + expect(store.historyTasks).toHaveLength(1) + }) + + it('should clear only history when specified', async () => { + mockClearItems.mockResolvedValue(undefined) + mockGetQueue.mockResolvedValue({ + Running: [createRunningTask(1, 'run-1')], + Pending: [createPendingTask(2, 'pend-1')] + }) + mockGetHistory.mockResolvedValue({ History: [] }) + + await store.clear(['history']) + + expect(mockClearItems).toHaveBeenCalledTimes(1) + expect(mockClearItems).toHaveBeenCalledWith('history') + expect(store.runningTasks).toHaveLength(1) + expect(store.pendingTasks).toHaveLength(1) + }) + + it('should do nothing when empty array passed', async () => { + await store.clear([]) + + expect(mockClearItems).not.toHaveBeenCalled() + }) + }) + + describe('delete()', () => { + it('should delete task from queue', async () => { + const task = new TaskItemImpl('Pending', createTaskPrompt(1, 'pend-1')) + + mockDeleteItem.mockResolvedValue(undefined) + mockGetQueue.mockResolvedValue({ Running: [], Pending: [] }) + mockGetHistory.mockResolvedValue({ History: [] }) + + await store.delete(task) + + expect(mockDeleteItem).toHaveBeenCalledWith('queue', 'pend-1') + }) + + it('should delete task from history', async () => { + const task = new TaskItemImpl( + 'History', + createTaskPrompt(1, 'hist-1'), + createTaskStatus(), + createTaskOutput() + ) + + mockDeleteItem.mockResolvedValue(undefined) + mockGetQueue.mockResolvedValue({ Running: [], Pending: [] }) + mockGetHistory.mockResolvedValue({ History: [] }) + + await store.delete(task) + + expect(mockDeleteItem).toHaveBeenCalledWith('history', 'hist-1') + }) + + it('should refresh store after deletion', async () => { + const task = new TaskItemImpl('Pending', createTaskPrompt(1, 'pend-1')) + + mockDeleteItem.mockResolvedValue(undefined) + mockGetQueue.mockResolvedValue({ Running: [], Pending: [] }) + mockGetHistory.mockResolvedValue({ History: [] }) + + await store.delete(task) + + expect(mockGetQueue).toHaveBeenCalled() + expect(mockGetHistory).toHaveBeenCalled() + }) + }) +})