threadStatusLifecycle.test.tsx1.9 KBView on GitHub
import { DEFAULT_THREAD_ID } from '@/modules/cedar-os/src/store/messages/MessageTypes';
import { useCedarStore } from '@/modules/cedar-os/src/store/CedarStore';

/**
 * Phase 3 (design: chat-thread-store) — status is driven by the send/finish lifecycle.
 * We drive the same store transitions the connection slice performs (setThreadStatus
 * 'streaming' on send, 'finished' on finish) and assert the derived reads follow.
 */
describe('agentThreadsSlice — status lifecycle', () => {
  beforeEach(() => {
    useCedarStore.setState((state) => ({
      ...state,
      threadMap: {
        [DEFAULT_THREAD_ID]: {
          id: DEFAULT_THREAD_ID,
          name: 'Live',
          lastLoaded: new Date().toISOString(),
          messages: [{ id: 'm1', role: 'user', type: 'text', content: 'hi' }],
        },
      },
      mainThreadId: DEFAULT_THREAD_ID,
      activeThreadId: DEFAULT_THREAD_ID,
      messages: [],
    }));
  });

  it('streaming → finished transition drives status, active view, and isThreadProcessing', () => {
    const store = () => useCedarStore.getState();

    // --- send ---
    store().setThreadStatus(DEFAULT_THREAD_ID, 'streaming');
    expect(store().threadMap[DEFAULT_THREAD_ID].status).toBe('streaming');
    expect(store().isThreadProcessing(DEFAULT_THREAD_ID)).toBe(true);
    expect(store().getThreadsByView('active').map((t) => t.id)).toEqual([DEFAULT_THREAD_ID]);

    // --- finish ---
    store().setThreadStatus(DEFAULT_THREAD_ID, 'finished');
    expect(store().threadMap[DEFAULT_THREAD_ID].status).toBe('finished');
    expect(store().isThreadProcessing(DEFAULT_THREAD_ID)).toBe(false);
    expect(store().getThreadsByView('active')).toHaveLength(0);
  });

  it('isThreadProcessing is false for idle/unknown threads', () => {
    expect(useCedarStore.getState().isThreadProcessing(DEFAULT_THREAD_ID)).toBe(false);
    expect(useCedarStore.getState().isThreadProcessing('nope')).toBe(false);
  });
});