fix(channel): support channel names that include numbers; legacy test names containing numbers may still appear (#796)
Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Haze <hazeone@users.noreply.github.com>
This commit is contained in:
@@ -458,4 +458,25 @@ describe('agent config lifecycle', () => {
|
||||
expect(snapshot.channelAccountOwners['feishu:default']).toBeUndefined();
|
||||
expect(snapshot.channelAccountOwners['telegram:default']).toBe('main');
|
||||
});
|
||||
|
||||
it('avoids numeric-only ids when creating agents from CJK names', async () => {
|
||||
await writeOpenClawJson({
|
||||
agents: {
|
||||
list: [{ id: 'main', name: 'Main', default: true }],
|
||||
},
|
||||
});
|
||||
|
||||
const { createAgent, listAgentsSnapshot } = await import('@electron/utils/agent-config');
|
||||
|
||||
await createAgent('测试2');
|
||||
await createAgent('测试1');
|
||||
|
||||
const snapshot = await listAgentsSnapshot();
|
||||
const agentIds = snapshot.agents.map((agent) => agent.id);
|
||||
|
||||
expect(agentIds).toContain('agent');
|
||||
expect(agentIds).toContain('agent-2');
|
||||
expect(agentIds).not.toContain('2');
|
||||
expect(agentIds).not.toContain('1');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -250,3 +250,55 @@ describe('WeChat dangling plugin cleanup', () => {
|
||||
expect(existsSync(join(testHome, '.openclaw', 'openclaw-weixin'))).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('configured channel account extraction', () => {
|
||||
beforeEach(async () => {
|
||||
vi.resetAllMocks();
|
||||
vi.resetModules();
|
||||
await rm(testHome, { recursive: true, force: true });
|
||||
await rm(testUserData, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('ignores malformed array-shaped accounts and falls back to default account', async () => {
|
||||
const { listConfiguredChannelAccountsFromConfig } = await import('@electron/utils/channel-config');
|
||||
|
||||
const result = listConfiguredChannelAccountsFromConfig({
|
||||
channels: {
|
||||
feishu: {
|
||||
enabled: true,
|
||||
defaultAccount: 'default',
|
||||
accounts: [null, null, { appId: 'ghost-account' }],
|
||||
appId: 'cli_real_app',
|
||||
appSecret: 'real_secret',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.feishu).toEqual({
|
||||
defaultAccountId: 'default',
|
||||
accountIds: ['default'],
|
||||
});
|
||||
expect(result.feishu.accountIds).not.toContain('2');
|
||||
});
|
||||
|
||||
it('keeps intentionally configured numeric account ids from object-shaped accounts', async () => {
|
||||
const { listConfiguredChannelAccountsFromConfig } = await import('@electron/utils/channel-config');
|
||||
|
||||
const result = listConfiguredChannelAccountsFromConfig({
|
||||
channels: {
|
||||
feishu: {
|
||||
enabled: true,
|
||||
defaultAccount: '2',
|
||||
accounts: {
|
||||
'2': { enabled: true, appId: 'cli_numeric' },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.feishu).toEqual({
|
||||
defaultAccountId: '2',
|
||||
accountIds: ['2'],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -274,6 +274,85 @@ describe('handleChannelRoutes', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('filters runtime-only stale accounts when not configured locally', async () => {
|
||||
listConfiguredChannelsMock.mockResolvedValue(['feishu']);
|
||||
listConfiguredChannelAccountsMock.mockResolvedValue({
|
||||
feishu: {
|
||||
defaultAccountId: 'default',
|
||||
accountIds: ['default'],
|
||||
},
|
||||
});
|
||||
readOpenClawConfigMock.mockResolvedValue({
|
||||
channels: {
|
||||
feishu: {
|
||||
defaultAccount: 'default',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const rpc = vi.fn().mockResolvedValue({
|
||||
channels: {
|
||||
feishu: {
|
||||
configured: true,
|
||||
},
|
||||
},
|
||||
channelAccounts: {
|
||||
feishu: [
|
||||
{
|
||||
accountId: 'default',
|
||||
configured: true,
|
||||
connected: true,
|
||||
running: true,
|
||||
},
|
||||
{
|
||||
accountId: '2',
|
||||
configured: false,
|
||||
connected: false,
|
||||
running: false,
|
||||
lastError: 'stale runtime session',
|
||||
},
|
||||
],
|
||||
},
|
||||
channelDefaultAccountId: {
|
||||
feishu: 'default',
|
||||
},
|
||||
});
|
||||
|
||||
const { handleChannelRoutes } = await import('@electron/api/routes/channels');
|
||||
await handleChannelRoutes(
|
||||
{ method: 'GET' } as IncomingMessage,
|
||||
{} as ServerResponse,
|
||||
new URL('http://127.0.0.1:13210/api/channels/accounts'),
|
||||
{
|
||||
gatewayManager: {
|
||||
rpc,
|
||||
getStatus: () => ({ state: 'running' }),
|
||||
debouncedReload: vi.fn(),
|
||||
debouncedRestart: vi.fn(),
|
||||
},
|
||||
} as never,
|
||||
);
|
||||
|
||||
expect(sendJsonMock).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
200,
|
||||
expect.objectContaining({
|
||||
success: true,
|
||||
channels: [
|
||||
expect.objectContaining({
|
||||
channelType: 'feishu',
|
||||
accounts: [expect.objectContaining({ accountId: 'default' })],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
);
|
||||
const payload = sendJsonMock.mock.calls.at(-1)?.[2] as {
|
||||
channels?: Array<{ channelType: string; accounts: Array<{ accountId: string }> }>;
|
||||
};
|
||||
const feishu = payload.channels?.find((entry) => entry.channelType === 'feishu');
|
||||
expect(feishu?.accounts.map((entry) => entry.accountId)).toEqual(['default']);
|
||||
});
|
||||
|
||||
it('lists known QQ Bot targets for a configured account', async () => {
|
||||
const knownUsersPath = join(testOpenClawConfigDir, 'qqbot', 'data');
|
||||
mkdirSync(knownUsersPath, { recursive: true });
|
||||
|
||||
Reference in New Issue
Block a user