supermemory/packages/tools/test/mastra/unit.test.ts
MaheshtheDev 5c24e67d60 feat(tools)!: @supermemory/tools 3.0 on the v5 API, retire @supermemory/ai-sdk (#1773)
Every integration (AI SDK, OpenAI, Mastra, VoltAgent, Claude memory) now calls v5 through `supermemory@5`. The config takes one `namespace` instead of `containerTags` or `projectId`, and `withSupermemory` takes `namespace` and `id` instead of `containerTag` and `customId`. No aliases. With no config the tools still use `sm_project_default`.

Conversations are stored as one document per conversation keyed by `id` instead of `/v4/conversations`. `memoryForget` drops `reason`; forgetting by text previews with `forgetMatching` and then forgets exact matches by id. Claude memory marks its files with `metadata.source` instead of a second tag. Search keeps the 2.x defaults so results do not shift.

`packages/ai-sdk` is removed: npm already deprecates it in favor of `@supermemory/tools/ai-sdk`, so its CI steps and playground wiring go too. `apps/sdk-playground` moves to the new tools API and v5 routes, including its Python server.

Tested against production: add, search, profile, list and delete. 112 unit tests pass; type errors drop from 148 to 141, none new. The `supermemory` dependency pins the rc until 5.0.0 is published.
2026-10-06 17:03:47 +00:00

979 lines
26 KiB
TypeScript

/**
* Unit tests for the Mastra integration
* Tests processors, wrapper, and factory functions
*/
import { describe, it, expect, beforeEach, vi, afterEach } from "vitest"
import {
addBody,
errorResponse,
jsonResponse,
profileBody,
requestJson,
requestPath,
} from "../v5-fetch"
import {
RequestContext,
MASTRA_THREAD_ID_KEY,
} from "@mastra/core/request-context"
import {
SupermemoryInputProcessor,
SupermemoryOutputProcessor,
createSupermemoryProcessor,
createSupermemoryOutputProcessor,
createSupermemoryProcessors,
withSupermemory,
} from "../../src/mastra"
import type {
ProcessInputArgs,
ProcessOutputResultArgs,
MessageList,
MastraDBMessage,
MastraMessageContentV2,
Processor,
} from "../../src/mastra"
const TEST_CONFIG = {
apiKey: "test-api-key",
baseUrl: "https://api.supermemory.ai",
namespace: "test-mastra-user",
id: "test-conversation",
}
interface MockAgentConfig {
id: string
name?: string
model?: string
customProp?: string
inputProcessors?: Processor[]
outputProcessors?: Processor[]
[key: string]: unknown
}
/**
* Helper to create MastraMessageContentV2 from text
*/
function createMessageContent(text: string): MastraMessageContentV2 {
return {
format: 2,
parts: [{ type: "text", text }],
}
}
/**
* Helper to create a MastraDBMessage
*/
function createMessage(
role: "user" | "assistant" | "system",
text: string,
): MastraDBMessage {
return {
id: `msg-${Date.now()}-${Math.random().toString(36).slice(2)}`,
role,
content: createMessageContent(text),
createdAt: new Date(),
}
}
const createMockMessageList = (): MessageList & {
calls: { method: string; args: unknown[] }[]
} => {
const calls: { method: string; args: unknown[] }[] = []
return {
calls,
clearSystemMessages: vi.fn(),
addSystem: vi.fn((content: string, _id?: string) => {
calls.push({ method: "addSystem", args: [content, _id] })
}),
addUser: vi.fn((content: string) => {
calls.push({ method: "addUser", args: [content] })
}),
addAssistant: vi.fn((content: string) => {
calls.push({ method: "addAssistant", args: [content] })
}),
} as unknown as MessageList & { calls: { method: string; args: unknown[] }[] }
}
const createMockProfileResponse = profileBody
const createMockConversationResponse = () => addBody("mem-123")
describe("SupermemoryInputProcessor", () => {
let originalEnv: string | undefined
let originalFetch: typeof globalThis.fetch
let fetchMock: ReturnType<typeof vi.fn>
beforeEach(() => {
originalEnv = process.env.SUPERMEMORY_API_KEY
process.env.SUPERMEMORY_API_KEY = TEST_CONFIG.apiKey
originalFetch = globalThis.fetch
fetchMock = vi.fn()
globalThis.fetch = fetchMock as unknown as typeof fetch
vi.clearAllMocks()
})
afterEach(() => {
if (originalEnv) {
process.env.SUPERMEMORY_API_KEY = originalEnv
} else {
delete process.env.SUPERMEMORY_API_KEY
}
globalThis.fetch = originalFetch
})
describe("constructor", () => {
it("should create processor with required options", () => {
const processor = new SupermemoryInputProcessor({
namespace: TEST_CONFIG.namespace,
id: TEST_CONFIG.id,
})
expect(processor.id).toBe("supermemory-input")
expect(processor.name).toBe("Supermemory Memory Injection")
})
it("should throw error if API key is not set", () => {
delete process.env.SUPERMEMORY_API_KEY
expect(() => {
new SupermemoryInputProcessor({
namespace: TEST_CONFIG.namespace,
id: TEST_CONFIG.id,
})
}).toThrow("SUPERMEMORY_API_KEY is not set")
})
it("should accept API key via options", () => {
delete process.env.SUPERMEMORY_API_KEY
const processor = new SupermemoryInputProcessor({
namespace: TEST_CONFIG.namespace,
id: TEST_CONFIG.id,
apiKey: "custom-key",
})
expect(processor.id).toBe("supermemory-input")
})
})
describe("processInput", () => {
it("should inject memories into messageList", async () => {
fetchMock.mockImplementation(async () =>
jsonResponse(
createMockProfileResponse(
["User likes TypeScript"],
["Recent interest in AI"],
),
),
)
const processor = new SupermemoryInputProcessor({
namespace: TEST_CONFIG.namespace,
id: TEST_CONFIG.id,
apiKey: TEST_CONFIG.apiKey,
mode: "profile",
})
const messageList = createMockMessageList()
const messages: MastraDBMessage[] = [createMessage("user", "Hello")]
const args: ProcessInputArgs = {
messages,
systemMessages: [],
messageList,
abort: vi.fn() as never,
retryCount: 0,
}
await processor.processInput(args)
expect(messageList.addSystem).toHaveBeenCalled()
const systemCall = messageList.calls.find((c) => c.method === "addSystem")
expect(systemCall).toBeDefined()
expect(systemCall?.args[0]).toContain("TypeScript")
expect(systemCall?.args[0]).toContain(
'<supermemory context="user-memories" readonly>',
)
expect(systemCall?.args[1]).toBe("supermemory")
})
it("should use cached memories on second call with same message", async () => {
fetchMock.mockImplementation(async () =>
jsonResponse(createMockProfileResponse(["Cached memory"])),
)
const processor = new SupermemoryInputProcessor({
namespace: TEST_CONFIG.namespace,
id: TEST_CONFIG.id,
apiKey: TEST_CONFIG.apiKey,
mode: "profile",
})
const messages: MastraDBMessage[] = [createMessage("user", "Hello")]
const args1: ProcessInputArgs = {
messages,
systemMessages: [],
messageList: createMockMessageList(),
abort: vi.fn() as never,
retryCount: 0,
}
await processor.processInput(args1)
expect(fetchMock).toHaveBeenCalledTimes(1)
const args2: ProcessInputArgs = {
messages,
systemMessages: [],
messageList: createMockMessageList(),
abort: vi.fn() as never,
retryCount: 0,
}
await processor.processInput(args2)
expect(fetchMock).toHaveBeenCalledTimes(1)
})
it("should refetch memories for different user message", async () => {
let callCount = 0
fetchMock.mockImplementation(() => {
callCount++
return Promise.resolve(
jsonResponse(
createMockProfileResponse([`Memory from call ${callCount}`]),
),
)
})
const processor = new SupermemoryInputProcessor({
namespace: TEST_CONFIG.namespace,
id: TEST_CONFIG.id,
apiKey: TEST_CONFIG.apiKey,
mode: "query",
})
const args1: ProcessInputArgs = {
messages: [createMessage("user", "First message")],
systemMessages: [],
messageList: createMockMessageList(),
abort: vi.fn() as never,
retryCount: 0,
}
await processor.processInput(args1)
expect(fetchMock).toHaveBeenCalledTimes(1)
const args2: ProcessInputArgs = {
messages: [createMessage("user", "Different message")],
systemMessages: [],
messageList: createMockMessageList(),
abort: vi.fn() as never,
retryCount: 0,
}
await processor.processInput(args2)
expect(fetchMock).toHaveBeenCalledTimes(2)
})
it("should return messageList in query mode when no user message", async () => {
const processor = new SupermemoryInputProcessor({
namespace: TEST_CONFIG.namespace,
id: TEST_CONFIG.id,
apiKey: TEST_CONFIG.apiKey,
mode: "query",
})
const messageList = createMockMessageList()
const args: ProcessInputArgs = {
messages: [],
systemMessages: [],
messageList,
abort: vi.fn() as never,
retryCount: 0,
}
const result = await processor.processInput(args)
expect(result).toBe(messageList)
expect(fetchMock).not.toHaveBeenCalled()
expect(messageList.addSystem).not.toHaveBeenCalled()
})
it("should handle API errors gracefully", async () => {
fetchMock.mockImplementation(async () => errorResponse(500))
const processor = new SupermemoryInputProcessor({
namespace: TEST_CONFIG.namespace,
id: TEST_CONFIG.id,
apiKey: TEST_CONFIG.apiKey,
mode: "profile",
})
const messageList = createMockMessageList()
const args: ProcessInputArgs = {
messages: [createMessage("user", "Hello")],
systemMessages: [],
messageList,
abort: vi.fn() as never,
retryCount: 0,
}
const result = await processor.processInput(args)
expect(result).toBe(messageList)
expect(messageList.addSystem).not.toHaveBeenCalled()
})
it("should use threadId from options", async () => {
fetchMock.mockImplementation(async () =>
jsonResponse(createMockProfileResponse(["Memory"])),
)
const processor = new SupermemoryInputProcessor({
namespace: TEST_CONFIG.namespace,
id: "thread-123",
apiKey: TEST_CONFIG.apiKey,
mode: "profile",
})
const args: ProcessInputArgs = {
messages: [createMessage("user", "Hello")],
systemMessages: [],
messageList: createMockMessageList(),
abort: vi.fn() as never,
retryCount: 0,
}
await processor.processInput(args)
expect(fetchMock).toHaveBeenCalled()
})
it("should use threadId from requestContext when not in options", async () => {
fetchMock.mockImplementation(async () =>
jsonResponse(createMockProfileResponse(["Memory"])),
)
const processor = new SupermemoryInputProcessor({
namespace: TEST_CONFIG.namespace,
id: TEST_CONFIG.id,
apiKey: TEST_CONFIG.apiKey,
mode: "profile",
})
const requestContext = new RequestContext()
requestContext.set(MASTRA_THREAD_ID_KEY, "ctx-thread-456")
const args: ProcessInputArgs = {
messages: [createMessage("user", "Hello")],
systemMessages: [],
messageList: createMockMessageList(),
abort: vi.fn() as never,
retryCount: 0,
requestContext,
}
await processor.processInput(args)
expect(fetchMock).toHaveBeenCalled()
})
it("should handle messages with array content parts", async () => {
fetchMock.mockImplementation(async () =>
jsonResponse(createMockProfileResponse(["Memory"])),
)
const processor = new SupermemoryInputProcessor({
namespace: TEST_CONFIG.namespace,
id: TEST_CONFIG.id,
apiKey: TEST_CONFIG.apiKey,
mode: "query",
})
const messages: MastraDBMessage[] = [
{
id: "msg-1",
role: "user",
content: {
format: 2,
parts: [
{ type: "text", text: "Hello " },
{ type: "text", text: "World" },
],
},
createdAt: new Date(),
},
]
const messageList = createMockMessageList()
const args: ProcessInputArgs = {
messages,
systemMessages: [],
messageList,
abort: vi.fn() as never,
retryCount: 0,
}
await processor.processInput(args)
expect(fetchMock).toHaveBeenCalled()
})
})
})
describe("SupermemoryOutputProcessor", () => {
let originalEnv: string | undefined
let originalFetch: typeof globalThis.fetch
let fetchMock: ReturnType<typeof vi.fn>
beforeEach(() => {
originalEnv = process.env.SUPERMEMORY_API_KEY
process.env.SUPERMEMORY_API_KEY = TEST_CONFIG.apiKey
originalFetch = globalThis.fetch
fetchMock = vi.fn()
globalThis.fetch = fetchMock as unknown as typeof fetch
vi.clearAllMocks()
})
afterEach(() => {
if (originalEnv) {
process.env.SUPERMEMORY_API_KEY = originalEnv
} else {
delete process.env.SUPERMEMORY_API_KEY
}
globalThis.fetch = originalFetch
})
describe("constructor", () => {
it("should create processor with required options", () => {
const processor = new SupermemoryOutputProcessor({
namespace: TEST_CONFIG.namespace,
id: TEST_CONFIG.id,
})
expect(processor.id).toBe("supermemory-output")
expect(processor.name).toBe("Supermemory Conversation Save")
})
})
describe("processOutputResult", () => {
it("should save conversation when addMemory is always", async () => {
fetchMock.mockImplementation(async () =>
jsonResponse(createMockConversationResponse()),
)
const processor = new SupermemoryOutputProcessor({
namespace: TEST_CONFIG.namespace,
id: "conv-456",
apiKey: TEST_CONFIG.apiKey,
addMemory: "always",
})
const messages: MastraDBMessage[] = [
createMessage("user", "Hello"),
createMessage("assistant", "Hi there!"),
]
const args: ProcessOutputResultArgs = {
messages,
messageList: createMockMessageList(),
abort: vi.fn() as never,
retryCount: 0,
}
await processor.processOutputResult(args)
expect(fetchMock).toHaveBeenCalledTimes(1)
const [url, init] = fetchMock.mock.calls[0] as [string, RequestInit]
expect(requestPath(url)).toBe(`/ns/${TEST_CONFIG.namespace}/document`)
expect(init.method).toBe("POST")
expect(new Headers(init.headers).get("authorization")).toBe(
`Bearer ${TEST_CONFIG.apiKey}`,
)
const callBody = requestJson(fetchMock.mock.calls[0]?.[1])
expect(callBody.id).toBe("conv-456")
expect(String(callBody.content).split("\n\n")).toHaveLength(2)
expect(callBody.dreaming).toBe("instant")
})
it("should not save conversation when addMemory is never", async () => {
const processor = new SupermemoryOutputProcessor({
namespace: TEST_CONFIG.namespace,
id: "conv-456",
apiKey: TEST_CONFIG.apiKey,
addMemory: "never",
})
const args: ProcessOutputResultArgs = {
messages: [
createMessage("user", "Hello"),
createMessage("assistant", "Hi!"),
],
messageList: createMockMessageList(),
abort: vi.fn() as never,
retryCount: 0,
}
await processor.processOutputResult(args)
expect(fetchMock).not.toHaveBeenCalled()
})
it("should use id from options for conversation save", async () => {
fetchMock.mockImplementation(async () =>
jsonResponse(createMockConversationResponse()),
)
const processor = new SupermemoryOutputProcessor({
namespace: TEST_CONFIG.namespace,
id: "my-custom-id",
apiKey: TEST_CONFIG.apiKey,
addMemory: "always",
})
const args: ProcessOutputResultArgs = {
messages: [
createMessage("user", "Hello"),
createMessage("assistant", "Hi!"),
],
messageList: createMockMessageList(),
abort: vi.fn() as never,
retryCount: 0,
}
await processor.processOutputResult(args)
expect(fetchMock).toHaveBeenCalledTimes(1)
const callBody = requestJson(fetchMock.mock.calls[0]?.[1])
expect(callBody.id).toBe("my-custom-id")
})
it("should use threadId from requestContext (takes precedence over id)", async () => {
fetchMock.mockImplementation(async () =>
jsonResponse(createMockConversationResponse()),
)
const processor = new SupermemoryOutputProcessor({
namespace: TEST_CONFIG.namespace,
id: "fallback-custom-id",
apiKey: TEST_CONFIG.apiKey,
addMemory: "always",
})
const requestContext = new RequestContext()
requestContext.set(MASTRA_THREAD_ID_KEY, "ctx-thread-789")
const args: ProcessOutputResultArgs = {
messages: [
createMessage("user", "Hello"),
createMessage("assistant", "Hi!"),
],
messageList: createMockMessageList(),
abort: vi.fn() as never,
retryCount: 0,
requestContext,
}
await processor.processOutputResult(args)
expect(fetchMock).toHaveBeenCalledTimes(1)
const callBody = requestJson(fetchMock.mock.calls[0]?.[1])
// RequestContext threadId takes precedence for per-request dynamic IDs
expect(callBody.id).toBe("ctx-thread-789")
})
it("should fall back to id when requestContext has no threadId", async () => {
fetchMock.mockImplementation(async () =>
jsonResponse(createMockConversationResponse()),
)
const processor = new SupermemoryOutputProcessor({
namespace: TEST_CONFIG.namespace,
id: "fallback-custom-id",
apiKey: TEST_CONFIG.apiKey,
addMemory: "always",
})
const args: ProcessOutputResultArgs = {
messages: [
createMessage("user", "Hello"),
createMessage("assistant", "Hi!"),
],
messageList: createMockMessageList(),
abort: vi.fn() as never,
retryCount: 0,
}
await processor.processOutputResult(args)
expect(fetchMock).toHaveBeenCalledTimes(1)
const callBody = requestJson(fetchMock.mock.calls[0]?.[1])
// Falls back to id when no RequestContext threadId
expect(callBody.id).toBe("fallback-custom-id")
})
it("should skip system messages when saving", async () => {
fetchMock.mockImplementation(async () =>
jsonResponse(createMockConversationResponse()),
)
const processor = new SupermemoryOutputProcessor({
namespace: TEST_CONFIG.namespace,
id: "conv-456",
apiKey: TEST_CONFIG.apiKey,
addMemory: "always",
})
const messages: MastraDBMessage[] = [
createMessage("system", "You are a helpful assistant"),
createMessage("user", "Hello"),
createMessage("assistant", "Hi there!"),
]
const args: ProcessOutputResultArgs = {
messages,
messageList: createMockMessageList(),
abort: vi.fn() as never,
retryCount: 0,
}
await processor.processOutputResult(args)
const callBody = requestJson(fetchMock.mock.calls[0]?.[1])
expect(String(callBody.content).split("\n\n")).toHaveLength(2)
expect(callBody.content).not.toContain("System:")
})
it("should handle messages with array content parts", async () => {
fetchMock.mockImplementation(async () =>
jsonResponse(createMockConversationResponse()),
)
const processor = new SupermemoryOutputProcessor({
namespace: TEST_CONFIG.namespace,
id: "conv-456",
apiKey: TEST_CONFIG.apiKey,
addMemory: "always",
})
const messages: MastraDBMessage[] = [
{
id: "msg-1",
role: "user",
content: {
format: 2,
parts: [
{ type: "text", text: "Hello" },
{ type: "text", text: " World" },
],
},
createdAt: new Date(),
},
{
id: "msg-2",
role: "assistant",
content: {
format: 2,
parts: [{ type: "text", text: "Hi!" }],
},
createdAt: new Date(),
},
]
const args: ProcessOutputResultArgs = {
messages,
messageList: createMockMessageList(),
abort: vi.fn() as never,
retryCount: 0,
}
await processor.processOutputResult(args)
const callBody = requestJson(fetchMock.mock.calls[0]?.[1])
expect(String(callBody.content).split("\n\n")).toHaveLength(2)
})
it("should handle save errors gracefully", async () => {
fetchMock.mockImplementation(async () => errorResponse(500))
const processor = new SupermemoryOutputProcessor({
namespace: TEST_CONFIG.namespace,
id: "conv-456",
apiKey: TEST_CONFIG.apiKey,
addMemory: "always",
})
const args: ProcessOutputResultArgs = {
messages: [
createMessage("user", "Hello"),
createMessage("assistant", "Hi!"),
],
messageList: createMockMessageList(),
abort: vi.fn() as never,
retryCount: 0,
}
// Should not throw
await expect(processor.processOutputResult(args)).resolves.toBeDefined()
})
it("should not save when no messages to save", async () => {
const processor = new SupermemoryOutputProcessor({
namespace: TEST_CONFIG.namespace,
id: "conv-456",
apiKey: TEST_CONFIG.apiKey,
addMemory: "always",
})
const args: ProcessOutputResultArgs = {
messages: [],
messageList: createMockMessageList(),
abort: vi.fn() as never,
retryCount: 0,
}
await processor.processOutputResult(args)
expect(fetchMock).not.toHaveBeenCalled()
})
})
})
describe("Factory functions", () => {
let originalEnv: string | undefined
beforeEach(() => {
originalEnv = process.env.SUPERMEMORY_API_KEY
process.env.SUPERMEMORY_API_KEY = TEST_CONFIG.apiKey
})
afterEach(() => {
if (originalEnv) {
process.env.SUPERMEMORY_API_KEY = originalEnv
} else {
delete process.env.SUPERMEMORY_API_KEY
}
})
describe("createSupermemoryProcessor", () => {
it("should create input processor", () => {
const processor = createSupermemoryProcessor({
namespace: TEST_CONFIG.namespace,
id: TEST_CONFIG.id,
})
expect(processor).toBeInstanceOf(SupermemoryInputProcessor)
expect(processor.id).toBe("supermemory-input")
})
it("should pass options to processor", () => {
const processor = createSupermemoryProcessor({
namespace: TEST_CONFIG.namespace,
id: TEST_CONFIG.id,
apiKey: "custom-key",
mode: "full",
})
expect(processor).toBeInstanceOf(SupermemoryInputProcessor)
})
})
describe("createSupermemoryOutputProcessor", () => {
it("should create output processor", () => {
const processor = createSupermemoryOutputProcessor({
namespace: TEST_CONFIG.namespace,
id: TEST_CONFIG.id,
})
expect(processor).toBeInstanceOf(SupermemoryOutputProcessor)
expect(processor.id).toBe("supermemory-output")
})
it("should pass options to processor", () => {
const processor = createSupermemoryOutputProcessor({
namespace: TEST_CONFIG.namespace,
id: "conv-123",
apiKey: "custom-key",
addMemory: "always",
})
expect(processor).toBeInstanceOf(SupermemoryOutputProcessor)
})
})
describe("createSupermemoryProcessors", () => {
it("should create both input and output processors", () => {
const { input, output } = createSupermemoryProcessors({
namespace: TEST_CONFIG.namespace,
id: TEST_CONFIG.id,
})
expect(input).toBeInstanceOf(SupermemoryInputProcessor)
expect(output).toBeInstanceOf(SupermemoryOutputProcessor)
})
it("should share options between processors", () => {
const { input, output } = createSupermemoryProcessors({
namespace: TEST_CONFIG.namespace,
id: "conv-123",
apiKey: "custom-key",
mode: "full",
addMemory: "always",
})
expect(input.id).toBe("supermemory-input")
expect(output.id).toBe("supermemory-output")
})
})
})
describe("withSupermemory", () => {
let originalEnv: string | undefined
beforeEach(() => {
originalEnv = process.env.SUPERMEMORY_API_KEY
process.env.SUPERMEMORY_API_KEY = TEST_CONFIG.apiKey
})
afterEach(() => {
if (originalEnv) {
process.env.SUPERMEMORY_API_KEY = originalEnv
} else {
delete process.env.SUPERMEMORY_API_KEY
}
})
describe("API key validation", () => {
it("should throw error if API key is not set", () => {
delete process.env.SUPERMEMORY_API_KEY
const config: MockAgentConfig = { id: "test-agent", name: "Test Agent" }
expect(() => {
withSupermemory(config, {
namespace: TEST_CONFIG.namespace,
id: TEST_CONFIG.id,
})
}).toThrow("SUPERMEMORY_API_KEY is not set")
})
it("should accept API key via options", () => {
delete process.env.SUPERMEMORY_API_KEY
const config: MockAgentConfig = { id: "test-agent", name: "Test Agent" }
const enhanced = withSupermemory(config, {
namespace: TEST_CONFIG.namespace,
id: TEST_CONFIG.id,
apiKey: "custom-key",
})
expect(enhanced).toBeDefined()
expect(enhanced.inputProcessors).toHaveLength(1)
})
})
describe("processor injection", () => {
it("should inject input and output processors", () => {
const config: MockAgentConfig = { id: "test-agent", name: "Test Agent" }
const enhanced = withSupermemory(config, {
namespace: TEST_CONFIG.namespace,
id: TEST_CONFIG.id,
})
expect(enhanced.inputProcessors).toHaveLength(1)
expect(enhanced.outputProcessors).toHaveLength(1)
expect(enhanced.inputProcessors?.[0]?.id).toBe("supermemory-input")
expect(enhanced.outputProcessors?.[0]?.id).toBe("supermemory-output")
})
it("should preserve original config properties", () => {
const config: MockAgentConfig = {
id: "test-agent",
name: "Test Agent",
model: "gpt-4",
customProp: "value",
}
const enhanced = withSupermemory(config, {
namespace: TEST_CONFIG.namespace,
id: TEST_CONFIG.id,
})
expect(enhanced.id).toBe("test-agent")
expect(enhanced.name).toBe("Test Agent")
expect(enhanced.model).toBe("gpt-4")
expect(enhanced.customProp).toBe("value")
})
it("should prepend input processor to existing processors", () => {
const existingInputProcessor: Processor = {
id: "existing-input",
name: "Existing Input",
}
const config: MockAgentConfig = {
id: "test-agent",
name: "Test Agent",
inputProcessors: [existingInputProcessor],
}
const enhanced = withSupermemory(config, {
namespace: TEST_CONFIG.namespace,
id: TEST_CONFIG.id,
})
expect(enhanced.inputProcessors).toHaveLength(2)
expect(enhanced.inputProcessors?.[0]?.id).toBe("supermemory-input")
expect(enhanced.inputProcessors?.[1]?.id).toBe("existing-input")
})
it("should append output processor to existing processors", () => {
const existingOutputProcessor: Processor = {
id: "existing-output",
name: "Existing Output",
}
const config: MockAgentConfig = {
id: "test-agent",
name: "Test Agent",
outputProcessors: [existingOutputProcessor],
}
const enhanced = withSupermemory(config, {
namespace: TEST_CONFIG.namespace,
id: TEST_CONFIG.id,
})
expect(enhanced.outputProcessors).toHaveLength(2)
expect(enhanced.outputProcessors?.[0]?.id).toBe("existing-output")
expect(enhanced.outputProcessors?.[1]?.id).toBe("supermemory-output")
})
it("should handle configs with both existing input and output processors", () => {
const existingInput: Processor = { id: "existing-input" }
const existingOutput: Processor = { id: "existing-output" }
const config: MockAgentConfig = {
id: "test-agent",
name: "Test Agent",
inputProcessors: [existingInput],
outputProcessors: [existingOutput],
}
const enhanced = withSupermemory(config, {
namespace: TEST_CONFIG.namespace,
id: TEST_CONFIG.id,
})
expect(enhanced.inputProcessors).toHaveLength(2)
expect(enhanced.outputProcessors).toHaveLength(2)
expect(enhanced.inputProcessors?.[0]?.id).toBe("supermemory-input")
expect(enhanced.inputProcessors?.[1]?.id).toBe("existing-input")
expect(enhanced.outputProcessors?.[0]?.id).toBe("existing-output")
expect(enhanced.outputProcessors?.[1]?.id).toBe("supermemory-output")
})
})
describe("options passthrough", () => {
it("should pass options to processors", () => {
const config: MockAgentConfig = { id: "test-agent", name: "Test Agent" }
const enhanced = withSupermemory(config, {
namespace: TEST_CONFIG.namespace,
id: "conv-123",
mode: "full",
addMemory: "always",
verbose: true,
})
expect(enhanced.inputProcessors).toHaveLength(1)
expect(enhanced.outputProcessors).toHaveLength(1)
})
})
})