mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-05-18 23:36:00 +00:00
- Mobile responsive - new toast design - web document render issue fix - posthog analytics - ui improvements
452 lines
12 KiB
TypeScript
452 lines
12 KiB
TypeScript
import { describe, it, expect, beforeEach } from "vitest"
|
|
import { createClaudeMemoryTool, type MemoryCommand } from "./claude-memory"
|
|
import "dotenv/config"
|
|
|
|
// Test configuration
|
|
const TEST_CONFIG = {
|
|
apiKey: process.env.SUPERMEMORY_API_KEY || "test-api-key",
|
|
baseUrl: process.env.SUPERMEMORY_BASE_URL,
|
|
projectId: "test-claude-memory",
|
|
memoryContainerTag: "claude_memory_test",
|
|
}
|
|
|
|
describe("Claude Memory Tool", () => {
|
|
let memoryTool: ReturnType<typeof createClaudeMemoryTool>
|
|
|
|
beforeEach(() => {
|
|
memoryTool = createClaudeMemoryTool(TEST_CONFIG.apiKey, {
|
|
projectId: TEST_CONFIG.projectId,
|
|
memoryContainerTag: TEST_CONFIG.memoryContainerTag,
|
|
baseUrl: TEST_CONFIG.baseUrl,
|
|
})
|
|
})
|
|
|
|
describe("Path validation", () => {
|
|
it("should reject invalid paths", async () => {
|
|
const invalidPaths = [
|
|
"/etc/passwd",
|
|
"/home/user/file.txt",
|
|
"../../../secrets.txt",
|
|
"/memories/../../../secrets.txt",
|
|
]
|
|
|
|
for (const path of invalidPaths) {
|
|
const result = await memoryTool.handleCommand({
|
|
command: "view",
|
|
path,
|
|
})
|
|
expect(result.success).toBe(false)
|
|
expect(result.error).toContain("Invalid path")
|
|
}
|
|
})
|
|
|
|
it("should accept valid memory paths", async () => {
|
|
const validPaths = [
|
|
"/memories/",
|
|
"/memories/notes.txt",
|
|
"/memories/project/ideas.md",
|
|
"/memories/deep/nested/path/file.txt",
|
|
]
|
|
|
|
// These should not fail due to path validation (though they might fail for other reasons like file not found)
|
|
for (const path of validPaths) {
|
|
const result = await memoryTool.handleCommand({
|
|
command: "view",
|
|
path,
|
|
})
|
|
// Should not fail with "Invalid path" error
|
|
if (!result.success) {
|
|
expect(result.error).not.toContain("Invalid path")
|
|
}
|
|
}
|
|
})
|
|
})
|
|
|
|
describe("File operations", () => {
|
|
const testFilePath = "/memories/test-file.txt"
|
|
const testContent = "Hello, World!\nThis is a test file.\nLine 3 here."
|
|
|
|
it("should create a file", async () => {
|
|
const result = await memoryTool.handleCommand({
|
|
command: "create",
|
|
path: testFilePath,
|
|
file_text: testContent,
|
|
})
|
|
|
|
expect(result.success).toBe(true)
|
|
expect(result.content).toContain("File created")
|
|
})
|
|
|
|
it("should read a file", async () => {
|
|
// First create the file
|
|
await memoryTool.handleCommand({
|
|
command: "create",
|
|
path: testFilePath,
|
|
file_text: testContent,
|
|
})
|
|
|
|
// Then read it
|
|
const result = await memoryTool.handleCommand({
|
|
command: "view",
|
|
path: testFilePath,
|
|
})
|
|
|
|
expect(result.success).toBe(true)
|
|
expect(result.content).toContain("Hello, World!")
|
|
expect(result.content).toContain("This is a test file.")
|
|
// Should include line numbers
|
|
expect(result.content).toMatch(/\s*1\s+Hello, World!/)
|
|
})
|
|
|
|
it("should read file with line range", async () => {
|
|
// First create the file
|
|
await memoryTool.handleCommand({
|
|
command: "create",
|
|
path: testFilePath,
|
|
file_text: testContent,
|
|
})
|
|
|
|
// Read only lines 1-2
|
|
const result = await memoryTool.handleCommand({
|
|
command: "view",
|
|
path: testFilePath,
|
|
view_range: [1, 2],
|
|
})
|
|
|
|
expect(result.success).toBe(true)
|
|
expect(result.content).toContain("Hello, World!")
|
|
expect(result.content).toContain("This is a test file.")
|
|
expect(result.content).not.toContain("Line 3 here.")
|
|
})
|
|
|
|
it("should replace string in file", async () => {
|
|
// First create the file
|
|
await memoryTool.handleCommand({
|
|
command: "create",
|
|
path: testFilePath,
|
|
file_text: testContent,
|
|
})
|
|
|
|
// Replace text
|
|
const result = await memoryTool.handleCommand({
|
|
command: "str_replace",
|
|
path: testFilePath,
|
|
old_str: "Hello, World!",
|
|
new_str: "Greetings, Universe!",
|
|
})
|
|
|
|
expect(result.success).toBe(true)
|
|
|
|
// Verify the change
|
|
const readResult = await memoryTool.handleCommand({
|
|
command: "view",
|
|
path: testFilePath,
|
|
})
|
|
expect(readResult.content).toContain("Greetings, Universe!")
|
|
expect(readResult.content).not.toContain("Hello, World!")
|
|
})
|
|
|
|
it("should insert text at specific line", async () => {
|
|
// First create the file
|
|
await memoryTool.handleCommand({
|
|
command: "create",
|
|
path: testFilePath,
|
|
file_text: testContent,
|
|
})
|
|
|
|
// Insert text at line 2
|
|
const result = await memoryTool.handleCommand({
|
|
command: "insert",
|
|
path: testFilePath,
|
|
insert_line: 2,
|
|
insert_text: "This is an inserted line.",
|
|
})
|
|
|
|
expect(result.success).toBe(true)
|
|
|
|
// Verify the insertion
|
|
const readResult = await memoryTool.handleCommand({
|
|
command: "view",
|
|
path: testFilePath,
|
|
})
|
|
expect(readResult.content).toContain("This is an inserted line.")
|
|
})
|
|
|
|
it("should rename/move file", async () => {
|
|
const oldPath = "/memories/old-name.txt"
|
|
const newPath = "/memories/new-name.txt"
|
|
|
|
// First create the file
|
|
await memoryTool.handleCommand({
|
|
command: "create",
|
|
path: oldPath,
|
|
file_text: testContent,
|
|
})
|
|
|
|
// Rename it
|
|
const result = await memoryTool.handleCommand({
|
|
command: "rename",
|
|
path: oldPath,
|
|
new_path: newPath,
|
|
})
|
|
|
|
expect(result.success).toBe(true)
|
|
|
|
// Verify the file exists at new location
|
|
const readResult = await memoryTool.handleCommand({
|
|
command: "view",
|
|
path: newPath,
|
|
})
|
|
expect(readResult.success).toBe(true)
|
|
expect(readResult.content).toContain("Hello, World!")
|
|
})
|
|
|
|
it("should delete file", async () => {
|
|
// First create the file
|
|
await memoryTool.handleCommand({
|
|
command: "create",
|
|
path: testFilePath,
|
|
file_text: testContent,
|
|
})
|
|
|
|
// Delete it
|
|
const result = await memoryTool.handleCommand({
|
|
command: "delete",
|
|
path: testFilePath,
|
|
})
|
|
|
|
expect(result.success).toBe(true)
|
|
})
|
|
})
|
|
|
|
describe("Directory operations", () => {
|
|
it("should list empty directory", async () => {
|
|
const result = await memoryTool.handleCommand({
|
|
command: "view",
|
|
path: "/memories/",
|
|
})
|
|
|
|
expect(result.success).toBe(true)
|
|
expect(result.content).toContain("Directory: /memories/")
|
|
})
|
|
|
|
it("should list directory with files", async () => {
|
|
// Create some test files
|
|
await memoryTool.handleCommand({
|
|
command: "create",
|
|
path: "/memories/file1.txt",
|
|
file_text: "Content 1",
|
|
})
|
|
|
|
await memoryTool.handleCommand({
|
|
command: "create",
|
|
path: "/memories/file2.md",
|
|
file_text: "Content 2",
|
|
})
|
|
|
|
await memoryTool.handleCommand({
|
|
command: "create",
|
|
path: "/memories/subdir/file3.txt",
|
|
file_text: "Content 3",
|
|
})
|
|
|
|
// List root directory
|
|
const result = await memoryTool.handleCommand({
|
|
command: "view",
|
|
path: "/memories/",
|
|
})
|
|
|
|
expect(result.success).toBe(true)
|
|
expect(result.content).toContain("file1.txt")
|
|
expect(result.content).toContain("file2.md")
|
|
expect(result.content).toContain("subdir/")
|
|
})
|
|
})
|
|
|
|
describe("Error handling", () => {
|
|
it("should handle missing file", async () => {
|
|
const result = await memoryTool.handleCommand({
|
|
command: "view",
|
|
path: "/memories/nonexistent.txt",
|
|
})
|
|
|
|
expect(result.success).toBe(false)
|
|
expect(result.error).toContain("File not found")
|
|
})
|
|
|
|
it("should handle missing parameters", async () => {
|
|
const commands: MemoryCommand[] = [
|
|
{ command: "create", path: "/memories/test.txt" }, // Missing file_text
|
|
{ command: "str_replace", path: "/memories/test.txt", old_str: "old" }, // Missing new_str
|
|
{ command: "insert", path: "/memories/test.txt", insert_line: 1 }, // Missing insert_text
|
|
{ command: "rename", path: "/memories/test.txt" }, // Missing new_path
|
|
]
|
|
|
|
for (const cmd of commands) {
|
|
const result = await memoryTool.handleCommand(cmd)
|
|
expect(result.success).toBe(false)
|
|
expect(result.error).toContain("required")
|
|
}
|
|
})
|
|
|
|
it("should handle string not found in str_replace", async () => {
|
|
// Create a file
|
|
await memoryTool.handleCommand({
|
|
command: "create",
|
|
path: "/memories/test.txt",
|
|
file_text: "Some content here",
|
|
})
|
|
|
|
// Try to replace non-existent string
|
|
const result = await memoryTool.handleCommand({
|
|
command: "str_replace",
|
|
path: "/memories/test.txt",
|
|
old_str: "This string does not exist",
|
|
new_str: "replacement",
|
|
})
|
|
|
|
expect(result.success).toBe(false)
|
|
expect(result.error).toContain("String not found")
|
|
})
|
|
|
|
it("should handle invalid line number for insert", async () => {
|
|
// Create a 3-line file
|
|
await memoryTool.handleCommand({
|
|
command: "create",
|
|
path: "/memories/test.txt",
|
|
file_text: "Line 1\nLine 2\nLine 3",
|
|
})
|
|
|
|
// Try to insert at invalid line number
|
|
const result = await memoryTool.handleCommand({
|
|
command: "insert",
|
|
path: "/memories/test.txt",
|
|
insert_line: 10, // Way beyond file length
|
|
insert_text: "New line",
|
|
})
|
|
|
|
expect(result.success).toBe(false)
|
|
expect(result.error).toContain("Invalid line number")
|
|
})
|
|
})
|
|
})
|
|
|
|
/**
|
|
* Manual test runner - run this directly to test the memory tool
|
|
* Usage: bun run src/claude-memory.test.ts
|
|
*/
|
|
async function runManualTests() {
|
|
console.log("🧪 Running Claude Memory Tool Manual Tests")
|
|
console.log("==========================================")
|
|
|
|
if (!process.env.SUPERMEMORY_API_KEY) {
|
|
console.error("❌ SUPERMEMORY_API_KEY environment variable is required")
|
|
console.log("Set your API key in .env file or environment variable")
|
|
process.exit(1)
|
|
}
|
|
|
|
const memoryTool = createClaudeMemoryTool(process.env.SUPERMEMORY_API_KEY, {
|
|
projectId: "manual-test-project",
|
|
memoryContainerTag: "claude_memory_manual_test",
|
|
baseUrl: process.env.SUPERMEMORY_BASE_URL,
|
|
})
|
|
|
|
const testCases = [
|
|
{
|
|
name: "Create a test file",
|
|
command: {
|
|
command: "create" as const,
|
|
path: "/memories/manual-test.md",
|
|
file_text:
|
|
"# Manual Test File\n\nThis is a test file for manual testing.\n\n- Item 1\n- Item 2\n- Item 3",
|
|
},
|
|
},
|
|
{
|
|
name: "Read the test file",
|
|
command: {
|
|
command: "view" as const,
|
|
path: "/memories/manual-test.md",
|
|
},
|
|
},
|
|
{
|
|
name: "Read specific lines",
|
|
command: {
|
|
command: "view" as const,
|
|
path: "/memories/manual-test.md",
|
|
view_range: [1, 3] as [number, number],
|
|
},
|
|
},
|
|
{
|
|
name: "Replace text in file",
|
|
command: {
|
|
command: "str_replace" as const,
|
|
path: "/memories/manual-test.md",
|
|
old_str: "Manual Test File",
|
|
new_str: "Updated Manual Test File",
|
|
},
|
|
},
|
|
{
|
|
name: "Insert text at line 4",
|
|
command: {
|
|
command: "insert" as const,
|
|
path: "/memories/manual-test.md",
|
|
insert_line: 4,
|
|
insert_text: "This line was inserted!",
|
|
},
|
|
},
|
|
{
|
|
name: "List directory contents",
|
|
command: {
|
|
command: "view" as const,
|
|
path: "/memories/",
|
|
},
|
|
},
|
|
{
|
|
name: "Rename the file",
|
|
command: {
|
|
command: "rename" as const,
|
|
path: "/memories/manual-test.md",
|
|
new_path: "/memories/renamed-manual-test.md",
|
|
},
|
|
},
|
|
{
|
|
name: "Test invalid path (should fail)",
|
|
command: {
|
|
command: "view" as const,
|
|
path: "/etc/passwd",
|
|
},
|
|
},
|
|
]
|
|
|
|
for (const testCase of testCases) {
|
|
console.log(`\n🔄 ${testCase.name}`)
|
|
console.log(`Command: ${JSON.stringify(testCase.command, null, 2)}`)
|
|
|
|
try {
|
|
const result = await memoryTool.handleCommand(testCase.command)
|
|
|
|
if (result.success) {
|
|
console.log("✅ Success")
|
|
if (result.content) {
|
|
console.log("📄 Content:")
|
|
console.log(result.content)
|
|
}
|
|
} else {
|
|
console.log("❌ Failed")
|
|
console.log("Error:", result.error)
|
|
}
|
|
} catch (error) {
|
|
console.log("💥 Exception:", error)
|
|
}
|
|
}
|
|
|
|
console.log("\n✨ Manual tests completed!")
|
|
console.log(
|
|
"Check your supermemory instance to verify the memory files were created correctly.",
|
|
)
|
|
}
|
|
|
|
// If this file is run directly, execute manual tests
|
|
if (import.meta.main) {
|
|
runManualTests()
|
|
}
|