Skip to main content
Glama
FilePersistenceProvider.test.ts10.5 kB
/** * File Persistence Provider Tests * * Tests for file-based memory persistence functionality. */ import { promises as fs } from "fs"; import { join } from "path"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { MemorySnapshot } from "../../../interfaces/persistence.js"; import { FilePersistenceProvider } from "../../../utils/persistence/FilePersistenceProvider.js"; import { getVersion } from "../../../utils/version.js"; describe("FilePersistenceProvider", () => { let provider: FilePersistenceProvider; let testDir: string; let testFilePath: string; beforeEach(async () => { // Use unique test directory to avoid conflicts const testId = Math.random().toString(36).substring(7); testDir = join(process.cwd(), `test-data-${testId}`); testFilePath = join(testDir, "test-memory.json"); provider = new FilePersistenceProvider(); // Clean up test directory try { await fs.rm(testDir, { recursive: true, force: true }); } catch { // Directory might not exist } }); afterEach(async () => { await provider.cleanup(); // Clean up test directory try { await fs.rm(testDir, { recursive: true, force: true }); } catch { // Directory might not exist } }); describe("initialization", () => { it("should initialize with default config", async () => { await provider.initialize({ storage_type: "file", file_path: testFilePath, backup_interval_ms: 0, // Disable auto-backup for tests max_backups: 5, compression_enabled: false, encryption_enabled: false, }); const status = provider.getStatus(); expect(status.initialized).toBe(true); expect(status.provider_type).toBe("file"); }); it("should create directory if it doesn't exist", async () => { await provider.initialize({ storage_type: "file", file_path: testFilePath, backup_interval_ms: 0, max_backups: 5, compression_enabled: false, encryption_enabled: false, }); // Check that directory was created const dirExists = await fs .access(testDir) .then(() => true) .catch(() => false); expect(dirExists).toBe(true); }); it("should throw error if file path is not provided", async () => { await expect( provider.initialize({ storage_type: "file", file_path: undefined, backup_interval_ms: 0, max_backups: 5, compression_enabled: false, encryption_enabled: false, }) ).rejects.toThrow("File path is required"); }); }); describe("save and load", () => { beforeEach(async () => { await provider.initialize({ storage_type: "file", file_path: testFilePath, backup_interval_ms: 0, max_backups: 5, compression_enabled: false, encryption_enabled: false, }); }); it("should save and load memory snapshot", async () => { const snapshot: MemorySnapshot = { timestamp: Date.now(), version: getVersion(), episodic_memories: [ { content: { text: "test memory" }, context: { session_id: "test", domain: "test" }, timestamp: Date.now(), emotional_tags: ["positive"], importance: 0.8, decay_factor: 1.0, }, ], semantic_concepts: [ { id: "concept1", content: { type: "test", value: "concept" }, relations: [], activation: 0.9, last_accessed: Date.now(), }, ], semantic_relations: [ { from: "concept1", to: "concept2", type: "related_to", strength: 0.7, }, ], metadata: { total_episodes: 1, total_concepts: 1, total_relations: 1, last_consolidation: Date.now(), }, }; await provider.save(snapshot); const loaded = await provider.load(); expect(loaded).not.toBeNull(); expect(loaded!.version).toBe(snapshot.version); expect(loaded!.episodic_memories).toHaveLength(1); expect(loaded!.semantic_concepts).toHaveLength(1); expect(loaded!.semantic_relations).toHaveLength(1); }); it("should return null when loading non-existent file", async () => { const loaded = await provider.load(); expect(loaded).toBeNull(); }); it("should handle compression", async () => { // Reinitialize with compression enabled await provider.cleanup(); await provider.initialize({ storage_type: "file", file_path: testFilePath, backup_interval_ms: 0, max_backups: 5, compression_enabled: true, encryption_enabled: false, }); const snapshot: MemorySnapshot = { timestamp: Date.now(), version: getVersion(), episodic_memories: [], semantic_concepts: [], semantic_relations: [], metadata: { total_episodes: 0, total_concepts: 0, total_relations: 0, last_consolidation: Date.now(), }, }; await provider.save(snapshot); const loaded = await provider.load(); expect(loaded).not.toBeNull(); expect(loaded!.version).toBe(snapshot.version); }); it("should validate snapshot structure", async () => { // Create invalid snapshot file await fs.writeFile(testFilePath, JSON.stringify({ invalid: "data" })); await expect(provider.load()).rejects.toThrow("Invalid snapshot"); }); }); describe("backup and restore", () => { beforeEach(async () => { await provider.initialize({ storage_type: "file", file_path: testFilePath, backup_interval_ms: 0, max_backups: 3, compression_enabled: false, encryption_enabled: false, }); }); it("should create and restore backup", async () => { const snapshot: MemorySnapshot = { timestamp: Date.now(), version: getVersion(), episodic_memories: [], semantic_concepts: [], semantic_relations: [], metadata: { total_episodes: 0, total_concepts: 0, total_relations: 0, last_consolidation: Date.now(), }, }; await provider.save(snapshot); await provider.backup("test-backup"); const backups = await provider.listBackups(); expect(backups).toContain("test-backup"); const restored = await provider.restore("test-backup"); expect(restored.version).toBe(snapshot.version); }); it("should cleanup old backups", async () => { const snapshot: MemorySnapshot = { timestamp: Date.now(), version: getVersion(), episodic_memories: [], semantic_concepts: [], semantic_relations: [], metadata: { total_episodes: 0, total_concepts: 0, total_relations: 0, last_consolidation: Date.now(), }, }; await provider.save(snapshot); // Create more backups than max_backups for (let i = 0; i < 5; i++) { await provider.backup(`backup-${i}`); // Small delay to ensure different timestamps await new Promise((resolve) => setTimeout(resolve, 10)); } const backups = await provider.listBackups(); expect(backups.length).toBeLessThanOrEqual(3); }); it("should delete backup", async () => { const snapshot: MemorySnapshot = { timestamp: Date.now(), version: getVersion(), episodic_memories: [], semantic_concepts: [], semantic_relations: [], metadata: { total_episodes: 0, total_concepts: 0, total_relations: 0, last_consolidation: Date.now(), }, }; await provider.save(snapshot); await provider.backup("test-backup"); let backups = await provider.listBackups(); expect(backups).toContain("test-backup"); await provider.deleteBackup("test-backup"); backups = await provider.listBackups(); expect(backups).not.toContain("test-backup"); }); it("should verify backup checksum", async () => { const snapshot: MemorySnapshot = { timestamp: Date.now(), version: getVersion(), episodic_memories: [], semantic_concepts: [], semantic_relations: [], metadata: { total_episodes: 0, total_concepts: 0, total_relations: 0, last_consolidation: Date.now(), }, }; await provider.save(snapshot); await provider.backup("test-backup"); // Corrupt the backup metadata const backupDir = join(testDir, "backups"); const metadataPath = join(backupDir, "test-backup.meta"); const metadata = JSON.parse(await fs.readFile(metadataPath, "utf8")); metadata.checksum = "invalid"; await fs.writeFile(metadataPath, JSON.stringify(metadata)); await expect(provider.restore("test-backup")).rejects.toThrow( "checksum verification failed" ); }); }); describe("error handling", () => { it("should handle save errors gracefully", async () => { // Test initialization error with a truly invalid path (null character) await expect( provider.initialize({ storage_type: "file", file_path: "/dev/null/\0invalid/file.json", backup_interval_ms: 0, max_backups: 5, compression_enabled: false, encryption_enabled: false, }) ).rejects.toThrow(); }); it("should handle operations when not initialized", async () => { const snapshot: MemorySnapshot = { timestamp: Date.now(), version: getVersion(), episodic_memories: [], semantic_concepts: [], semantic_relations: [], metadata: { total_episodes: 0, total_concepts: 0, total_relations: 0, last_consolidation: Date.now(), }, }; await expect(provider.save(snapshot)).rejects.toThrow("not initialized"); await expect(provider.load()).rejects.toThrow("not initialized"); await expect(provider.backup("test")).rejects.toThrow("not initialized"); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/keyurgolani/ThoughtMcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server