conversation-logger.test.js 3.15 KB
/**
 * Unit Tests — conversation-logger.js
 */
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import fs from 'fs';
import path from 'path';

const { ConversationLogger } = require('../../services/conversation-logger');

describe('ConversationLogger', () => {
  const TMP = '/tmp/test-conv-logger';
  let logger;
  const convId = 'test-conv-001';
  const msgId = 'msg-001';

  beforeAll(() => {
    fs.mkdirSync(TMP, { recursive: true });
    logger = new ConversationLogger(TMP, convId, msgId, { userId: 'user-123' });
  });

  afterAll(() => { try { fs.rmSync(TMP, { recursive: true }); } catch {} });

  describe('constructor', () => {
    it('should create logger with correct properties', () => {
      expect(logger.conversationsDir).toBe(TMP);
      expect(logger.conversationId).toBe(convId);
      expect(logger.messageId).toBe(msgId);
      expect(logger.userId).toBe('user-123');
    });

    it('should create directories', () => {
      expect(fs.existsSync(logger.messageDir)).toBe(true);
      expect(fs.existsSync(logger.useDir)).toBe(true);
      expect(fs.existsSync(logger.trainDir)).toBe(true);
    });
  });

  describe('ensureMeta', () => {
    it('should create meta.json', () => {
      logger.ensureMeta('Test Conversation');
      expect(fs.existsSync(logger._metaPath)).toBe(true);
    });

    it('should not overwrite existing meta', () => {
      const before = JSON.parse(fs.readFileSync(logger._metaPath, 'utf-8'));
      logger.ensureMeta('Different Title');
      const after = JSON.parse(fs.readFileSync(logger._metaPath, 'utf-8'));
      expect(after.title).toBe(before.title); // unchanged
    });
  });

  describe('logUserMessage', () => {
    it('should log without error', () => {
      expect(() => logger.logUserMessage('Hello, how are you?')).not.toThrow();
    });

    it('should persist to messages file', () => {
      const content = fs.readFileSync(logger._messagesPath, 'utf-8');
      expect(content).toContain('Hello');
    });
  });

  describe('logStreamChunk', () => {
    it('should log a stream chunk', () => {
      expect(() => logger.logStreamChunk('master', 'Processing...')).not.toThrow();
    });
  });

  describe('logProgress', () => {
    it('should log progress step', () => {
      expect(() => logger.logProgress('hunt', 'Searching...')).not.toThrow();
    });
  });

  describe('logToolCall', () => {
    it('should log tool calls', () => {
      expect(() => logger.logToolCall('read_file', '/path/to/file')).not.toThrow();
    });
  });

  describe('logResult', () => {
    it('should log a result', () => {
      expect(() => logger.logResult({ success: true, output: 'Done' })).not.toThrow();
    });
  });

  describe('logError', () => {
    it('should log an error', () => {
      expect(() => logger.logError({ message: 'Something failed', stack: 'trace' })).not.toThrow();
    });
  });

  describe('addStats', () => {
    it('should add stats delta', () => {
      expect(() => logger.addStats({ tokensIn: 100, tokensOut: 200 })).not.toThrow();
    });
  });

  describe('finalize', () => {
    it('should finalize with success status', () => {
      expect(() => logger.finalize('success')).not.toThrow();
    });
  });
});