import * as path from 'node:path'; import { FileSessionTreeProvider } from '../src/modules/netaclaw/session-tree/file_provider.js'; import { MySqlSessionTreeProvider } from '../src/modules/netaclaw/session-tree/mysql_provider.js'; import { createSessionTreeProvider, resolveAgentSessionTreeConfig, } from '../src/modules/netaclaw/session-tree/factory.js'; describe('session-tree factory', () => { it('creates a file provider from agent session config and netaclaw data dir defaults', () => { const provider = createSessionTreeProvider({ session: resolveAgentSessionTreeConfig( { backend: 'file', }, { backend: 'mysql', dataDir: 'C:/neta-data', }, ), cwd: 'C:/workspace/neta', }); expect(provider).toBeInstanceOf(FileSessionTreeProvider); expect((provider as FileSessionTreeProvider).getSessionFilePath('session-1')).toBe( path.join('C:/neta-data', 'sessions', `${Buffer.from('session-1', 'utf8').toString('base64url')}.jsonl`), ); }); it('allows agent config to override the file session root dir', () => { const provider = createSessionTreeProvider({ session: resolveAgentSessionTreeConfig( { backend: 'file', file: { rootDir: 'D:/custom-sessions', }, }, { backend: 'mysql', dataDir: 'C:/neta-data', }, ), cwd: 'C:/workspace/neta', }); expect(provider).toBeInstanceOf(FileSessionTreeProvider); expect((provider as FileSessionTreeProvider).getSessionFilePath('session-2')).toBe( path.join('D:/custom-sessions', `${Buffer.from('session-2', 'utf8').toString('base64url')}.jsonl`), ); }); it('creates a mysql provider when agent session backend is mysql', () => { const sessionRepo = { find: jest.fn(), findOne: jest.fn(), findOneBy: jest.fn(), save: jest.fn(), delete: jest.fn(), }; const entryRepo = { find: jest.fn(), findOne: jest.fn(), findOneBy: jest.fn(), save: jest.fn(), delete: jest.fn(), }; const provider = createSessionTreeProvider({ session: resolveAgentSessionTreeConfig( { backend: 'mysql', }, { backend: 'file', dataDir: 'C:/neta-data', }, ), sessionRepo, entryRepo, }); expect(provider).toBeInstanceOf(MySqlSessionTreeProvider); }); it('throws when mysql backend is selected without repositories', () => { expect(() => createSessionTreeProvider({ session: resolveAgentSessionTreeConfig( { backend: 'mysql', }, { backend: 'file', dataDir: 'C:/neta-data', }, ), })).toThrow('MySqlSessionTreeProvider requires both sessionRepo and entryRepo'); }); }); describe('resolveAgentSessionTreeConfig', () => { it('defaults to the application backend when agent config is empty', () => { expect(resolveAgentSessionTreeConfig(undefined, { backend: 'file', dataDir: 'C:/neta-data', })).toEqual({ backend: 'file', file: { rootDir: path.join('C:/neta-data', 'sessions'), }, }); }); it('defaults to file backend when neither agent nor global config specifies a backend', () => { expect(resolveAgentSessionTreeConfig(undefined, { dataDir: 'C:/neta-data', })).toEqual({ backend: 'file', file: { rootDir: path.join('C:/neta-data', 'sessions'), }, }); }); it('keeps mysql agent config without injecting file-only options', () => { expect(resolveAgentSessionTreeConfig( { backend: 'mysql', }, { backend: 'file', dataDir: 'C:/neta-data', }, )).toEqual({ backend: 'mysql', }); }); });