feat: prepare Zhinian desktop client for pilot release
This commit is contained in:
@@ -13,14 +13,19 @@ const proxyAwareFetchMock = vi.fn();
|
||||
const saveChannelConfigMock = vi.fn();
|
||||
const setChannelDefaultAccountMock = vi.fn();
|
||||
const assignChannelAccountToAgentMock = vi.fn();
|
||||
const clearAllBindingsForChannelMock = vi.fn();
|
||||
const deleteAgentConfigMock = vi.fn();
|
||||
const deleteChannelAccountConfigMock = vi.fn();
|
||||
const deleteChannelConfigMock = vi.fn();
|
||||
const ensureChannelAgentForAccountMock = vi.fn();
|
||||
const clearChannelBindingMock = vi.fn();
|
||||
const parseJsonBodyMock = vi.fn();
|
||||
const testOpenClawConfigDir = join(tmpdir(), 'clawx-tests', 'channel-routes-openclaw');
|
||||
|
||||
vi.mock('@electron/utils/channel-config', () => ({
|
||||
cleanupDanglingWeChatPluginState: vi.fn(),
|
||||
deleteChannelAccountConfig: vi.fn(),
|
||||
deleteChannelConfig: vi.fn(),
|
||||
deleteChannelAccountConfig: (...args: unknown[]) => deleteChannelAccountConfigMock(...args),
|
||||
deleteChannelConfig: (...args: unknown[]) => deleteChannelConfigMock(...args),
|
||||
getChannelFormValues: vi.fn(),
|
||||
listConfiguredChannelAccounts: (...args: unknown[]) => listConfiguredChannelAccountsMock(...args),
|
||||
listConfiguredChannelAccountsFromConfig: (...args: unknown[]) => listConfiguredChannelAccountsMock(...args),
|
||||
@@ -36,8 +41,10 @@ vi.mock('@electron/utils/channel-config', () => ({
|
||||
|
||||
vi.mock('@electron/utils/agent-config', () => ({
|
||||
assignChannelAccountToAgent: (...args: unknown[]) => assignChannelAccountToAgentMock(...args),
|
||||
clearAllBindingsForChannel: vi.fn(),
|
||||
clearAllBindingsForChannel: (...args: unknown[]) => clearAllBindingsForChannelMock(...args),
|
||||
clearChannelBinding: (...args: unknown[]) => clearChannelBindingMock(...args),
|
||||
deleteAgentConfig: (...args: unknown[]) => deleteAgentConfigMock(...args),
|
||||
ensureChannelAgentForAccount: (...args: unknown[]) => ensureChannelAgentForAccountMock(...args),
|
||||
listAgentsSnapshot: (...args: unknown[]) => listAgentsSnapshotMock(...args),
|
||||
listAgentsSnapshotFromConfig: (...args: unknown[]) => listAgentsSnapshotMock(...args),
|
||||
}));
|
||||
@@ -203,6 +210,193 @@ describe('handleChannelRoutes', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('auto-creates missing AgentBus channel agent bindings while listing accounts', async () => {
|
||||
listConfiguredChannelsMock.mockResolvedValue(['agentbus']);
|
||||
listConfiguredChannelAccountsMock.mockResolvedValue({
|
||||
agentbus: {
|
||||
defaultAccountId: 'acct-20260427-070043-65935ec0',
|
||||
accountIds: ['acct-20260427-070043-65935ec0'],
|
||||
},
|
||||
});
|
||||
readOpenClawConfigMock.mockResolvedValue({
|
||||
bindings: [],
|
||||
channels: {
|
||||
agentbus: {
|
||||
defaultAccount: 'acct-20260427-070043-65935ec0',
|
||||
accounts: {
|
||||
'acct-20260427-070043-65935ec0': {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
listAgentsSnapshotMock.mockResolvedValue({
|
||||
agents: [],
|
||||
channelOwners: {},
|
||||
channelAccountOwners: {},
|
||||
});
|
||||
ensureChannelAgentForAccountMock.mockResolvedValue({
|
||||
agents: [],
|
||||
channelOwners: { agentbus: 'channel-agentbus-acct-20260427-070043-65935ec0' },
|
||||
channelAccountOwners: {
|
||||
'agentbus:acct-20260427-070043-65935ec0': 'channel-agentbus-acct-20260427-070043-65935ec0',
|
||||
},
|
||||
});
|
||||
|
||||
const rpc = vi.fn().mockResolvedValue({
|
||||
channels: {
|
||||
agentbus: {
|
||||
configured: true,
|
||||
},
|
||||
},
|
||||
channelAccounts: {
|
||||
agentbus: [
|
||||
{
|
||||
accountId: 'acct-20260427-070043-65935ec0',
|
||||
configured: true,
|
||||
connected: false,
|
||||
running: false,
|
||||
linked: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
channelDefaultAccountId: {
|
||||
agentbus: 'acct-20260427-070043-65935ec0',
|
||||
},
|
||||
});
|
||||
|
||||
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' }),
|
||||
getDiagnostics: () => ({ consecutiveHeartbeatMisses: 0, consecutiveRpcFailures: 0 }),
|
||||
debouncedReload: vi.fn(),
|
||||
debouncedRestart: vi.fn(),
|
||||
},
|
||||
} as never,
|
||||
);
|
||||
|
||||
expect(ensureChannelAgentForAccountMock).toHaveBeenCalledWith(
|
||||
'agentbus',
|
||||
'acct-20260427-070043-65935ec0',
|
||||
);
|
||||
expect(sendJsonMock).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
200,
|
||||
expect.objectContaining({
|
||||
success: true,
|
||||
channels: [
|
||||
expect.objectContaining({
|
||||
channelType: 'agentbus',
|
||||
accounts: [
|
||||
expect.objectContaining({
|
||||
accountId: 'acct-20260427-070043-65935ec0',
|
||||
agentId: 'channel-agentbus-acct-20260427-070043-65935ec0',
|
||||
status: 'connected',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('deletes the managed channel agent when deleting a channel account', async () => {
|
||||
readOpenClawConfigMock.mockResolvedValue({
|
||||
bindings: [
|
||||
{
|
||||
agentId: 'channel-agentbus-acct-20260427-070043-65935ec0',
|
||||
match: {
|
||||
channel: 'agentbus',
|
||||
accountId: 'acct-20260427-070043-65935ec0',
|
||||
},
|
||||
},
|
||||
],
|
||||
channels: {
|
||||
agentbus: {
|
||||
enabled: true,
|
||||
defaultAccount: 'acct-20260427-070043-65935ec0',
|
||||
accounts: {
|
||||
'acct-20260427-070043-65935ec0': { enabled: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { handleChannelRoutes } = await import('@electron/api/routes/channels');
|
||||
await handleChannelRoutes(
|
||||
{ method: 'DELETE' } as IncomingMessage,
|
||||
{} as ServerResponse,
|
||||
new URL('http://127.0.0.1:13210/api/channels/config/agentbus?accountId=acct-20260427-070043-65935ec0'),
|
||||
{
|
||||
gatewayManager: {
|
||||
rpc: vi.fn(),
|
||||
getStatus: () => ({ state: 'running' }),
|
||||
debouncedReload: vi.fn(),
|
||||
debouncedRestart: vi.fn(),
|
||||
},
|
||||
} as never,
|
||||
);
|
||||
|
||||
expect(deleteAgentConfigMock).toHaveBeenCalledWith('channel-agentbus-acct-20260427-070043-65935ec0');
|
||||
expect(deleteChannelAccountConfigMock).toHaveBeenCalledWith(
|
||||
'agentbus',
|
||||
'acct-20260427-070043-65935ec0',
|
||||
);
|
||||
expect(clearChannelBindingMock).toHaveBeenCalledWith('agentbus', 'acct-20260427-070043-65935ec0');
|
||||
});
|
||||
|
||||
it('does not delete a manually selected non-managed agent when deleting a channel account', async () => {
|
||||
readOpenClawConfigMock.mockResolvedValue({
|
||||
bindings: [
|
||||
{
|
||||
agentId: 'sales-agent',
|
||||
match: {
|
||||
channel: 'agentbus',
|
||||
accountId: 'acct-20260427-070043-65935ec0',
|
||||
},
|
||||
},
|
||||
],
|
||||
channels: {
|
||||
agentbus: {
|
||||
enabled: true,
|
||||
defaultAccount: 'acct-20260427-070043-65935ec0',
|
||||
accounts: {
|
||||
'acct-20260427-070043-65935ec0': { enabled: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { handleChannelRoutes } = await import('@electron/api/routes/channels');
|
||||
await handleChannelRoutes(
|
||||
{ method: 'DELETE' } as IncomingMessage,
|
||||
{} as ServerResponse,
|
||||
new URL('http://127.0.0.1:13210/api/channels/config/agentbus?accountId=acct-20260427-070043-65935ec0'),
|
||||
{
|
||||
gatewayManager: {
|
||||
rpc: vi.fn(),
|
||||
getStatus: () => ({ state: 'running' }),
|
||||
debouncedReload: vi.fn(),
|
||||
debouncedRestart: vi.fn(),
|
||||
},
|
||||
} as never,
|
||||
);
|
||||
|
||||
expect(deleteAgentConfigMock).not.toHaveBeenCalled();
|
||||
expect(deleteChannelAccountConfigMock).toHaveBeenCalledWith(
|
||||
'agentbus',
|
||||
'acct-20260427-070043-65935ec0',
|
||||
);
|
||||
expect(clearChannelBindingMock).toHaveBeenCalledWith('agentbus', 'acct-20260427-070043-65935ec0');
|
||||
});
|
||||
|
||||
it('rejects non-canonical account ID on channel config save', async () => {
|
||||
parseJsonBodyMock.mockResolvedValue({
|
||||
channelType: 'feishu',
|
||||
@@ -271,6 +465,7 @@ describe('handleChannelRoutes', () => {
|
||||
{ botToken: 'token', allowedUsers: '123456' },
|
||||
'Legacy_Account',
|
||||
);
|
||||
expect(ensureChannelAgentForAccountMock).toHaveBeenCalledWith('telegram', 'Legacy_Account');
|
||||
expect(sendJsonMock).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
200,
|
||||
@@ -760,6 +955,7 @@ describe('handleChannelRoutes', () => {
|
||||
);
|
||||
expect(assignChannelAccountToAgentMock).toHaveBeenCalledWith('main', 'telegram', 'default');
|
||||
expect(clearChannelBindingMock).toHaveBeenCalledWith('telegram');
|
||||
expect(ensureChannelAgentForAccountMock).toHaveBeenCalledWith('telegram', 'telegram-a1b2c3d4');
|
||||
expect(assignChannelAccountToAgentMock).not.toHaveBeenCalledWith('main', 'telegram', 'telegram-a1b2c3d4');
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user