mirror of
https://github.com/simstudioai/sim.git
synced 2026-02-12 23:45:07 -05:00
* v0 * v1 * Basic ss tes * Ss tests * Stuff * Add mcp * mcp v1 * Improvement * Fix * BROKEN * Checkpoint * Streaming * Fix abort * Things are broken * Streaming seems to work but copilot is dumb * Fix edge issue * LUAAAA * Fix stream buffer * Fix lint * Checkpoint * Initial temp state, in the middle of a refactor * Initial test shows diff store still working * Tool refactor * First cleanup pass complete - untested * Continued cleanup * Refactor * Refactor complete - no testing yet * Fix - cursor makes me sad * Fix mcp * Clean up mcp * Updated mcp * Add respond to subagents * Fix definitions * Add tools * Add tools * Add copilot mcp tracking * Fix lint * Fix mcp * Fix * Updates * Clean up mcp * Fix copilot mcp tool names to be sim prefixed * Add opus 4.6 * Fix discovery tool * Fix * Remove logs * Fix go side tool rendering * Update docs * Fix hydration * Fix tool call resolution * Fix * Fix lint * Fix superagent and autoallow integrations * Fix always allow * Update block * Remove plan docs * Fix hardcoded ff * Fix dropped provider * Fix lint * Fix tests * Fix dead messages array * Fix discovery * Fix run workflow * Fix run block * Fix run from block in copilot * Fix lint * Fix skip and mtb * Fix typing * Fix tool call * Bump api version * Fix bun lock * Nuke bad files
120 lines
3.8 KiB
TypeScript
120 lines
3.8 KiB
TypeScript
/**
|
|
* @vitest-environment node
|
|
*/
|
|
|
|
import { loggerMock } from '@sim/testing'
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
vi.mock('@sim/logger', () => loggerMock)
|
|
|
|
type StoredEntry = { score: number; value: string }
|
|
|
|
const createRedisStub = () => {
|
|
const events = new Map<string, StoredEntry[]>()
|
|
const counters = new Map<string, number>()
|
|
|
|
const readEntries = (key: string, min: number, max: number) => {
|
|
const list = events.get(key) || []
|
|
return list
|
|
.filter((entry) => entry.score >= min && entry.score <= max)
|
|
.sort((a, b) => a.score - b.score)
|
|
.map((entry) => entry.value)
|
|
}
|
|
|
|
return {
|
|
del: vi.fn().mockResolvedValue(1),
|
|
hset: vi.fn().mockResolvedValue(1),
|
|
hgetall: vi.fn().mockResolvedValue({}),
|
|
expire: vi.fn().mockResolvedValue(1),
|
|
eval: vi
|
|
.fn()
|
|
.mockImplementation(
|
|
(
|
|
_lua: string,
|
|
_keysCount: number,
|
|
seqKey: string,
|
|
eventsKey: string,
|
|
_ttl: number,
|
|
_limit: number,
|
|
streamId: string,
|
|
eventJson: string
|
|
) => {
|
|
const current = counters.get(seqKey) || 0
|
|
const next = current + 1
|
|
counters.set(seqKey, next)
|
|
const entry = JSON.stringify({ eventId: next, streamId, event: JSON.parse(eventJson) })
|
|
const list = events.get(eventsKey) || []
|
|
list.push({ score: next, value: entry })
|
|
events.set(eventsKey, list)
|
|
return next
|
|
}
|
|
),
|
|
incrby: vi.fn().mockImplementation((key: string, amount: number) => {
|
|
const current = counters.get(key) || 0
|
|
const next = current + amount
|
|
counters.set(key, next)
|
|
return next
|
|
}),
|
|
zrangebyscore: vi.fn().mockImplementation((key: string, min: string, max: string) => {
|
|
const minVal = Number(min)
|
|
const maxVal = max === '+inf' ? Number.POSITIVE_INFINITY : Number(max)
|
|
return Promise.resolve(readEntries(key, minVal, maxVal))
|
|
}),
|
|
pipeline: vi.fn().mockImplementation(() => {
|
|
const api: Record<string, any> = {}
|
|
api.zadd = vi.fn().mockImplementation((key: string, ...args: Array<string | number>) => {
|
|
const list = events.get(key) || []
|
|
for (let i = 0; i < args.length; i += 2) {
|
|
list.push({ score: Number(args[i]), value: String(args[i + 1]) })
|
|
}
|
|
events.set(key, list)
|
|
return api
|
|
})
|
|
api.expire = vi.fn().mockReturnValue(api)
|
|
api.zremrangebyrank = vi.fn().mockReturnValue(api)
|
|
api.exec = vi.fn().mockResolvedValue([])
|
|
return api
|
|
}),
|
|
}
|
|
}
|
|
|
|
let mockRedis: ReturnType<typeof createRedisStub>
|
|
|
|
vi.mock('@/lib/core/config/redis', () => ({
|
|
getRedisClient: () => mockRedis,
|
|
}))
|
|
|
|
import {
|
|
appendStreamEvent,
|
|
createStreamEventWriter,
|
|
readStreamEvents,
|
|
} from '@/lib/copilot/orchestrator/stream-buffer'
|
|
|
|
describe('stream-buffer', () => {
|
|
beforeEach(() => {
|
|
mockRedis = createRedisStub()
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
it.concurrent('replays events after a given event id', async () => {
|
|
await appendStreamEvent('stream-1', { type: 'content', data: 'hello' })
|
|
await appendStreamEvent('stream-1', { type: 'content', data: 'world' })
|
|
|
|
const allEvents = await readStreamEvents('stream-1', 0)
|
|
expect(allEvents.map((entry) => entry.event.data)).toEqual(['hello', 'world'])
|
|
|
|
const replayed = await readStreamEvents('stream-1', 1)
|
|
expect(replayed.map((entry) => entry.event.data)).toEqual(['world'])
|
|
})
|
|
|
|
it.concurrent('flushes buffered events for resume', async () => {
|
|
const writer = createStreamEventWriter('stream-2')
|
|
await writer.write({ type: 'content', data: 'a' })
|
|
await writer.write({ type: 'content', data: 'b' })
|
|
await writer.flush()
|
|
|
|
const events = await readStreamEvents('stream-2', 0)
|
|
expect(events.map((entry) => entry.event.data)).toEqual(['a', 'b'])
|
|
})
|
|
})
|