open-multi-agent/tests/message-store.test.ts

108 lines
3.5 KiB
TypeScript

import { describe, it, expect } from 'vitest'
import { InMemoryMessageStore } from '../src/memory/in-memory-message-store.js'
import { MessageBus } from '../src/team/messaging.js'
const msg = (overrides: Partial<{ id: string; from: string; to: string; content: string }> = {}) => ({
id: overrides.id ?? 'm1',
from: overrides.from ?? 'alice',
to: overrides.to ?? 'bob',
content: overrides.content ?? 'hello',
timestamp: new Date().toISOString(),
})
describe('InMemoryMessageStore', () => {
it('saves and gets a message', async () => {
const store = new InMemoryMessageStore()
const m = msg()
await store.save(m)
expect(await store.get('m1')).toEqual(m)
})
it('returns null for unknown id', async () => {
const store = new InMemoryMessageStore()
expect(await store.get('nope')).toBeNull()
})
it('query filters by to', async () => {
const store = new InMemoryMessageStore()
await store.save(msg({ id: 'm1', to: 'bob' }))
await store.save(msg({ id: 'm2', to: 'carol' }))
const results = await store.query({ to: 'bob' })
expect(results).toHaveLength(1)
expect(results[0].id).toBe('m1')
})
it('query filters by from', async () => {
const store = new InMemoryMessageStore()
await store.save(msg({ id: 'm1', from: 'alice' }))
await store.save(msg({ id: 'm2', from: 'dave' }))
const results = await store.query({ from: 'alice' })
expect(results).toHaveLength(1)
})
it('query with no filter returns all', async () => {
const store = new InMemoryMessageStore()
await store.save(msg({ id: 'm1' }))
await store.save(msg({ id: 'm2' }))
expect(await store.query({})).toHaveLength(2)
})
it('tracks read state per agent', async () => {
const store = new InMemoryMessageStore()
await store.save(msg({ id: 'm1', to: 'bob' }))
await store.save(msg({ id: 'm2', to: 'bob' }))
await store.markRead('bob', ['m1'])
const unread = await store.getUnreadIds('bob')
expect(unread.has('m1')).toBe(false)
expect(unread.has('m2')).toBe(true)
})
it('getUnreadIds ignores messages not addressed to agent', async () => {
const store = new InMemoryMessageStore()
await store.save(msg({ id: 'm1', from: 'alice', to: 'bob' }))
const unread = await store.getUnreadIds('carol')
expect(unread.size).toBe(0)
})
it('markRead is idempotent', async () => {
const store = new InMemoryMessageStore()
await store.save(msg({ id: 'm1', to: 'bob' }))
await store.markRead('bob', ['m1'])
await store.markRead('bob', ['m1'])
const unread = await store.getUnreadIds('bob')
expect(unread.size).toBe(0)
})
})
describe('MessageBus with injected store', () => {
it('delegates persistence to injected store', async () => {
const backingStore = new InMemoryMessageStore()
const bus = new MessageBus(backingStore)
bus.send('alice', 'bob', 'hello')
const stored = await backingStore.query({ to: 'bob' })
expect(stored).toHaveLength(1)
expect(stored[0].content).toBe('hello')
})
it('defaults to working without store', () => {
const bus = new MessageBus()
bus.send('alice', 'bob', 'hi')
const unread = bus.getUnread('bob')
expect(unread).toHaveLength(1)
})
it('existing getAll/getConversation still work with store', () => {
const backingStore = new InMemoryMessageStore()
const bus = new MessageBus(backingStore)
bus.send('alice', 'bob', 'test')
bus.send('bob', 'alice', 'reply')
expect(bus.getAll('bob')).toHaveLength(1)
expect(bus.getConversation('alice', 'bob')).toHaveLength(2)
})
})