dsh-im-ops/test/channels/feishu/plugin-config-store.test.mjs

114 lines
4.3 KiB
JavaScript

import assert from 'node:assert/strict';
import { mkdir, mkdtemp, readFile, stat, unlink, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import test from 'node:test';
import { PluginConfigStore } from '../../../src/channels/feishu/plugin-config-store.mjs';
test('PluginConfigStore persists non-secret onboarding facts', async () => {
const dir = await mkdtemp(join(tmpdir(), 'dsh-feishu-config-'));
const path = join(dir, 'nested', 'config.json');
const store = await new PluginConfigStore(path).load();
assert.equal(store.get(), null);
await store.save({
appId: 'cli_test',
ownerOpenId: 'ou_owner',
domain: 'feishu',
botName: '北汇星河助手',
appSecret: 'must-not-be-written',
});
const raw = await readFile(path, 'utf8');
assert.doesNotMatch(raw, /must-not-be-written/);
assert.equal((await stat(path)).mode & 0o777, 0o600);
assert.equal((await new PluginConfigStore(path).load()).get().appId, 'cli_test');
assert.equal(store.get().groupResponseMode, 'mention');
assert.equal(store.get().groupMessagePermissionGranted, false);
await store.save({
...store.get(),
groupResponseMode: 'all',
groupMessagePermissionGranted: true,
});
const reloaded = (await new PluginConfigStore(path).load()).get();
assert.equal(reloaded.groupResponseMode, 'all');
assert.equal(reloaded.groupMessagePermissionGranted, true);
await store.clear();
assert.equal(store.get(), null);
});
test('PluginConfigStore stays unconfigured after a failed write and can retry', async () => {
const dir = await mkdtemp(join(tmpdir(), 'dsh-feishu-config-retry-'));
const blockedParent = join(dir, 'blocked');
const path = join(blockedParent, 'config.json');
const store = await new PluginConfigStore(path).load();
await writeFile(blockedParent, 'not a directory');
const value = { appId: 'cli_retry', ownerOpenId: 'ou_retry', domain: 'feishu' };
await assert.rejects(store.save(value));
assert.equal(store.get(), null);
await unlink(blockedParent);
await mkdir(blockedParent);
await store.save(value);
assert.equal(store.get().appId, 'cli_retry');
});
test('PluginConfigStore migrates a v1 bot atomically without moving its credential', async () => {
const dir = await mkdtemp(join(tmpdir(), 'dsh-feishu-config-migrate-'));
const path = join(dir, 'config.json');
await writeFile(path, JSON.stringify({
version: 1,
appId: 'cli_legacy',
ownerOpenId: 'ou_legacy',
domain: 'feishu',
botName: '旧机器人',
}));
const store = await new PluginConfigStore(path).load();
const migrated = JSON.parse(await readFile(path, 'utf8'));
assert.equal(migrated.version, 2);
assert.equal(migrated.bots.length, 1);
assert.match(migrated.bots[0].id, /^bot_[a-f0-9]{24}$/);
assert.equal(migrated.bots[0].secretRef, 'DSH_FEISHU_APP_SECRET');
assert.deepEqual(migrated.bots[0].ownerOpenIds, ['ou_legacy']);
assert.equal(store.get().ownerOpenId, 'ou_legacy');
assert.equal(store.list()[0].appId, 'cli_legacy');
assert.equal(store.list()[0].groupResponseMode, 'mention');
assert.equal(store.list()[0].groupMessagePermissionGranted, false);
});
test('PluginConfigStore rejects an invalid or duplicate v2 document without dropping entries', async () => {
const dir = await mkdtemp(join(tmpdir(), 'dsh-feishu-config-invalid-v2-'));
const invalidPath = join(dir, 'invalid.json');
await writeFile(invalidPath, JSON.stringify({
version: 2,
bots: [
{
id: 'bot_good', appId: 'cli_good', secretRef: 'DSH_FEISHU_APP_SECRET_GOOD',
ownerOpenIds: ['ou_good'], domain: 'feishu',
},
{ id: '../bad', appId: 'cli_bad', secretRef: 'not-a-credential-ref', ownerOpenIds: ['ou_bad'] },
],
}));
await assert.rejects(new PluginConfigStore(invalidPath).load(), /invalid bot entry/);
const duplicatePath = join(dir, 'duplicate.json');
await writeFile(duplicatePath, JSON.stringify({
version: 2,
bots: [
{
id: 'bot_one', appId: 'cli_same', secretRef: 'DSH_FEISHU_APP_SECRET_ONE',
ownerOpenIds: ['ou_one'], domain: 'feishu',
},
{
id: 'bot_two', appId: 'cli_same', secretRef: 'DSH_FEISHU_APP_SECRET_TWO',
ownerOpenIds: ['ou_two'], domain: 'feishu',
},
],
}));
await assert.rejects(new PluginConfigStore(duplicatePath).load(), /duplicate bot identities/);
});