Skip to main content
Glama
persona-operations.test.ts6.37 kB
import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import fs from 'fs/promises'; import path from 'path'; import os from 'os'; // Test directory const TEST_PERSONA_DIR = path.join(os.tmpdir(), '.persona-test-' + Date.now()); // Import functions to test (we'll need to export them or use a test-specific module) // For now, we'll test via file operations directly describe('Persona File Operations', () => { beforeEach(async () => { // Create test directory await fs.mkdir(TEST_PERSONA_DIR, { recursive: true }); }); afterEach(async () => { // Clean up test directory try { await fs.rm(TEST_PERSONA_DIR, { recursive: true, force: true }); } catch (error) { // Ignore cleanup errors } }); describe('File Creation', () => { it('should create persona file successfully', async () => { const filePath = path.join(TEST_PERSONA_DIR, 'test-persona.txt'); const content = 'You are a test persona.'; await fs.writeFile(filePath, content, 'utf-8'); const readContent = await fs.readFile(filePath, 'utf-8'); expect(readContent).toBe(content); }); it('should handle UTF-8 encoding correctly', async () => { const filePath = path.join(TEST_PERSONA_DIR, 'korean-persona.txt'); const content = '당신은 한국어를 사용하는 어시스턴트입니다.'; await fs.writeFile(filePath, content, 'utf-8'); const readContent = await fs.readFile(filePath, 'utf-8'); expect(readContent).toBe(content); }); }); describe('File Reading', () => { it('should read existing persona file', async () => { const filePath = path.join(TEST_PERSONA_DIR, 'existing.txt'); const content = 'Existing persona content'; await fs.writeFile(filePath, content, 'utf-8'); const readContent = await fs.readFile(filePath, 'utf-8'); expect(readContent).toBe(content); }); it('should throw error for non-existent file', async () => { const filePath = path.join(TEST_PERSONA_DIR, 'nonexistent.txt'); await expect(fs.readFile(filePath, 'utf-8')).rejects.toThrow(); }); }); describe('File Deletion', () => { it('should delete persona file successfully', async () => { const filePath = path.join(TEST_PERSONA_DIR, 'to-delete.txt'); await fs.writeFile(filePath, 'content', 'utf-8'); await fs.unlink(filePath); await expect(fs.readFile(filePath, 'utf-8')).rejects.toThrow(); }); }); describe('Directory Listing', () => { it('should list all .txt files', async () => { await fs.writeFile(path.join(TEST_PERSONA_DIR, 'persona1.txt'), 'content1', 'utf-8'); await fs.writeFile(path.join(TEST_PERSONA_DIR, 'persona2.txt'), 'content2', 'utf-8'); await fs.writeFile(path.join(TEST_PERSONA_DIR, 'other.json'), '{}', 'utf-8'); const files = await fs.readdir(TEST_PERSONA_DIR); const txtFiles = files.filter(f => f.endsWith('.txt')); expect(txtFiles.length).toBe(2); expect(txtFiles).toContain('persona1.txt'); expect(txtFiles).toContain('persona2.txt'); }); it('should return empty array for empty directory', async () => { const files = await fs.readdir(TEST_PERSONA_DIR); expect(files.length).toBe(0); }); }); describe('File Update', () => { it('should update existing file', async () => { const filePath = path.join(TEST_PERSONA_DIR, 'update-test.txt'); await fs.writeFile(filePath, 'original content', 'utf-8'); await fs.writeFile(filePath, 'updated content', 'utf-8'); const content = await fs.readFile(filePath, 'utf-8'); expect(content).toBe('updated content'); }); }); describe('Security Tests', () => { it('should not allow path traversal via path.join', async () => { // path.join normalizes paths and prevents traversal const maliciousPath = path.join(TEST_PERSONA_DIR, '../../../etc/passwd'); // The normalized path should NOT escape TEST_PERSONA_DIR's parent structure // On Windows, this might behave differently, so we check if it's contained const normalized = path.normalize(maliciousPath); // The malicious attempt should be normalized away from sensitive areas expect(normalized).not.toContain('etc/passwd'); }); it('should prevent directory creation outside designated area', async () => { // Attempting to use .. in path should be normalized const safePath = path.join(TEST_PERSONA_DIR, '..', 'malicious'); const normalized = path.normalize(safePath); // Normalized path should not allow breaking out expect(normalized.includes(TEST_PERSONA_DIR)).toBe(false); expect(normalized.includes('malicious')).toBe(true); }); }); }); describe('Analytics Operations', () => { const ANALYTICS_FILE = path.join(TEST_PERSONA_DIR, '.analytics.json'); beforeEach(async () => { await fs.mkdir(TEST_PERSONA_DIR, { recursive: true }); }); afterEach(async () => { try { await fs.rm(TEST_PERSONA_DIR, { recursive: true, force: true }); } catch (error) { // Ignore } }); it('should create analytics file with valid JSON', async () => { const analytics = { usage: { 'professional': 5 }, contextPatterns: { 'professional': { 'business': 3 } } }; await fs.writeFile(ANALYTICS_FILE, JSON.stringify(analytics, null, 2), 'utf-8'); const content = await fs.readFile(ANALYTICS_FILE, 'utf-8'); const parsed = JSON.parse(content); expect(parsed).toEqual(analytics); }); it('should handle missing analytics file gracefully', async () => { try { await fs.readFile(ANALYTICS_FILE, 'utf-8'); // Should not reach here expect(true).toBe(false); } catch (error) { // Expected error expect(error).toBeDefined(); } }); it('should update analytics incrementally', async () => { const initial = { usage: { 'coder': 1 }, contextPatterns: {} }; await fs.writeFile(ANALYTICS_FILE, JSON.stringify(initial), 'utf-8'); const read = JSON.parse(await fs.readFile(ANALYTICS_FILE, 'utf-8')); read.usage['coder']++; await fs.writeFile(ANALYTICS_FILE, JSON.stringify(read), 'utf-8'); const final = JSON.parse(await fs.readFile(ANALYTICS_FILE, 'utf-8')); expect(final.usage['coder']).toBe(2); }); });

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/seanshin0214/persona-mcp'

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