Files
EJClaw/setup/migrate-json-state.test.ts

153 lines
4.3 KiB
TypeScript

import fs from 'fs';
import os from 'os';
import path from 'path';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { Database } from 'bun:sqlite';
describe('migrate json state step', () => {
const tempRoots: string[] = [];
afterEach(() => {
vi.resetModules();
vi.unstubAllEnvs();
vi.restoreAllMocks();
for (const root of tempRoots.splice(0)) {
fs.rmSync(root, { recursive: true, force: true });
}
});
it('does not migrate legacy json state during startup and leaves files in place', async () => {
const tempRoot = fs.mkdtempSync(
path.join(os.tmpdir(), 'ejclaw-migrate-json-init-'),
);
tempRoots.push(tempRoot);
const storeDir = path.join(tempRoot, 'store');
const dataDir = path.join(tempRoot, 'data');
fs.mkdirSync(storeDir, { recursive: true });
fs.mkdirSync(dataDir, { recursive: true });
vi.stubEnv('EJCLAW_STORE_DIR', storeDir);
vi.stubEnv('EJCLAW_DATA_DIR', dataDir);
fs.writeFileSync(
path.join(dataDir, 'router_state.json'),
JSON.stringify({ last_timestamp: '1234' }),
);
fs.writeFileSync(
path.join(dataDir, 'sessions.json'),
JSON.stringify({ 'group-a': 'session-123' }),
);
const { _initTestDatabase, initDatabase } = await import('../src/db.js');
expect(() => initDatabase()).toThrow(
/Legacy JSON state migration required before startup/,
);
expect(fs.existsSync(path.join(dataDir, 'router_state.json'))).toBe(true);
expect(fs.existsSync(path.join(dataDir, 'sessions.json'))).toBe(true);
expect(
fs.existsSync(path.join(dataDir, 'router_state.json.migrated')),
).toBe(false);
expect(fs.existsSync(path.join(dataDir, 'sessions.json.migrated'))).toBe(
false,
);
_initTestDatabase();
});
it('migrates router_state.json and sessions.json only via explicit setup step', async () => {
const tempRoot = fs.mkdtempSync(
path.join(os.tmpdir(), 'ejclaw-migrate-json-step-'),
);
tempRoots.push(tempRoot);
const storeDir = path.join(tempRoot, 'store');
const dataDir = path.join(tempRoot, 'data');
fs.mkdirSync(storeDir, { recursive: true });
fs.mkdirSync(dataDir, { recursive: true });
vi.stubEnv('EJCLAW_STORE_DIR', storeDir);
vi.stubEnv('EJCLAW_DATA_DIR', dataDir);
fs.writeFileSync(
path.join(dataDir, 'router_state.json'),
JSON.stringify({
last_timestamp: '1234',
last_agent_timestamp: {
'dc:test-room': '5678',
},
}),
);
fs.writeFileSync(
path.join(dataDir, 'sessions.json'),
JSON.stringify({
'group-a': 'session-123',
'group-b': 'session-456',
}),
);
const emitStatusMock = vi.fn();
vi.doMock('./status.js', () => ({
emitStatus: emitStatusMock,
}));
const { run } = await import('./migrate-json-state.js');
await run([]);
const db = new Database(path.join(storeDir, 'messages.db'), {
readonly: true,
});
expect(
db.prepare('SELECT key, value FROM router_state ORDER BY key').all(),
).toEqual([
{
key: 'last_agent_seq',
value: '{"dc:test-room":"5678"}',
},
{
key: 'last_seq',
value: '1234',
},
]);
expect(
db
.prepare(
`SELECT group_folder, agent_type, session_id
FROM sessions
ORDER BY group_folder`,
)
.all(),
).toEqual([
{
group_folder: 'group-a',
agent_type: 'claude-code',
session_id: 'session-123',
},
{
group_folder: 'group-b',
agent_type: 'claude-code',
session_id: 'session-456',
},
]);
db.close();
expect(fs.existsSync(path.join(dataDir, 'router_state.json'))).toBe(false);
expect(fs.existsSync(path.join(dataDir, 'sessions.json'))).toBe(false);
expect(
fs.existsSync(path.join(dataDir, 'router_state.json.migrated')),
).toBe(true);
expect(fs.existsSync(path.join(dataDir, 'sessions.json.migrated'))).toBe(
true,
);
expect(emitStatusMock).toHaveBeenLastCalledWith(
'MIGRATE_JSON_STATE',
expect.objectContaining({
MIGRATED_ROUTER_STATE_KEYS: 2,
MIGRATED_SESSIONS: 2,
RENAMED_ROUTER_STATE_JSON: true,
RENAMED_SESSIONS_JSON: true,
STATUS: 'success',
}),
);
});
});