Files
ClawX/tests/unit/plugin-install-index.test.ts

137 lines
4.8 KiB
TypeScript

// @vitest-environment node
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { mkdtempSync, rmSync, existsSync } from 'node:fs';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
const mockLoggerInfo = vi.fn();
const mockLoggerWarn = vi.fn();
vi.mock('@electron/utils/logger', () => ({
logger: {
info: mockLoggerInfo,
warn: mockLoggerWarn,
},
}));
describe('plugin install index sqlite persistence', () => {
let stateDir: string;
let previousStateDir: string | undefined;
beforeEach(() => {
vi.resetModules();
vi.clearAllMocks();
stateDir = mkdtempSync(join(tmpdir(), 'clawx-openclaw-state-'));
previousStateDir = process.env.OPENCLAW_STATE_DIR;
process.env.OPENCLAW_STATE_DIR = stateDir;
});
afterEach(() => {
if (previousStateDir === undefined) {
delete process.env.OPENCLAW_STATE_DIR;
} else {
process.env.OPENCLAW_STATE_DIR = previousStateDir;
}
rmSync(stateDir, { recursive: true, force: true });
});
it('creates sqlite and upserts trusted whatsapp install records', async () => {
const { upsertPluginInstallRecordsIntoSqlite } = await import('@electron/utils/plugin-install-index');
const sqlitePath = join(stateDir, 'state', 'openclaw.sqlite');
const record = {
source: 'npm',
spec: '@openclaw/whatsapp',
installPath: '/home/test/.openclaw/extensions/whatsapp',
version: '2026.6.10',
resolvedName: '@openclaw/whatsapp',
resolvedVersion: '2026.6.10',
resolvedSpec: '@openclaw/whatsapp@2026.6.10',
installedAt: '2026-01-01T00:00:00.000Z',
};
expect(upsertPluginInstallRecordsIntoSqlite({ whatsapp: record })).toBe(true);
expect(existsSync(sqlitePath)).toBe(true);
const { DatabaseSync } = await import('node:sqlite');
const db = new DatabaseSync(sqlitePath);
const row = db.prepare(`
SELECT install_records_json
FROM installed_plugin_index
WHERE index_key = 'installed-plugin-index'
`).get() as { install_records_json: string };
db.close();
const persisted = JSON.parse(row.install_records_json) as Record<string, unknown>;
expect(persisted.whatsapp).toEqual(record);
expect(mockLoggerInfo).toHaveBeenCalledWith(
'[plugin] Persisted trusted install metadata to SQLite for: whatsapp',
);
});
it('updates stale sqlite records when installPath changes', async () => {
const { upsertPluginInstallRecordsIntoSqlite } = await import('@electron/utils/plugin-install-index');
const sqlitePath = join(stateDir, 'state', 'openclaw.sqlite');
const stale = {
source: 'npm',
spec: '@openclaw/whatsapp',
installPath: '/old/path/whatsapp',
version: '2026.6.10',
resolvedName: '@openclaw/whatsapp',
resolvedVersion: '2026.6.10',
resolvedSpec: '@openclaw/whatsapp@2026.6.10',
};
const fresh = {
...stale,
installPath: '/home/test/.openclaw/extensions/whatsapp',
installedAt: '2026-01-02T00:00:00.000Z',
};
expect(upsertPluginInstallRecordsIntoSqlite({ whatsapp: stale })).toBe(true);
expect(upsertPluginInstallRecordsIntoSqlite({ whatsapp: fresh })).toBe(true);
const { DatabaseSync } = await import('node:sqlite');
const db = new DatabaseSync(sqlitePath);
const row = db.prepare(`
SELECT install_records_json
FROM installed_plugin_index
WHERE index_key = 'installed-plugin-index'
`).get() as { install_records_json: string };
db.close();
const persisted = JSON.parse(row.install_records_json) as Record<string, Record<string, unknown>>;
expect(persisted.whatsapp.installPath).toBe(fresh.installPath);
});
it('removes compatibility-patched plugin records from canonical and legacy databases', async () => {
const {
removePluginInstallRecordsFromSqlite,
upsertPluginInstallRecordsIntoSqlite,
} = await import('@electron/utils/plugin-install-index');
const sqlitePath = join(stateDir, 'state', 'openclaw.sqlite');
const record = {
source: 'npm',
spec: '@wecom/wecom-openclaw-plugin',
installPath: '/home/test/.openclaw/extensions/wecom',
version: '2026.7.2',
resolvedName: '@wecom/wecom-openclaw-plugin',
resolvedVersion: '2026.7.2',
resolvedSpec: '@wecom/wecom-openclaw-plugin@2026.7.2',
};
expect(upsertPluginInstallRecordsIntoSqlite({ wecom: record })).toBe(true);
expect(removePluginInstallRecordsFromSqlite(['wecom'])).toBe(true);
const { DatabaseSync } = await import('node:sqlite');
const db = new DatabaseSync(sqlitePath);
const row = db.prepare(`
SELECT install_records_json
FROM installed_plugin_index
WHERE index_key = 'installed-plugin-index'
`).get() as { install_records_json: string };
db.close();
expect(JSON.parse(row.install_records_json)).not.toHaveProperty('wecom');
});
});