feat(agents): 简化微信绑定并校验操作终态
This commit is contained in:
1 parent
0fa6f8cca0
commit
d58584489b
11 files changed
+2419
-769
No files matched your search
@@ -592,6 +592,68 @@ describe('Main cloud Agents boundary', () => {
|
||||
.toBe('/api/makelore/channel-operations/op-route');
|
||||
});
|
||||
|
||||
it('limits unified WeChat unbinding to its owned account and revision contract', () => {
|
||||
const plan = operationPlan({ operation: 'unbindChannelAccount', input: {
|
||||
channel_account_id: 'wechat-1', operation_id: input.operation_id, expected_revision: 2,
|
||||
target_agent_slug: draft.slug, access_token: 'private',
|
||||
} });
|
||||
expect(plan.path).toBe('/api/makelore/channel-accounts/wechat-1/unbind');
|
||||
expect(plan.method).toBe('POST');
|
||||
expect(plan.body).toEqual({ operation_id: input.operation_id, expected_revision: 2 });
|
||||
expect(plan.project({ channel: { id: 'wechat-1', desired_state: 'paused', sync_state: 'ready', access_token: 'private' },
|
||||
unbound: true, disconnected: true, wechat: { status: 'empty', token: 'private' } })).toEqual({
|
||||
channel: { id: 'wechat-1', desired_state: 'paused', sync_state: 'ready' },
|
||||
unbound: true, disconnected: true, wechat: { status: 'empty' },
|
||||
});
|
||||
for (const invalid of [
|
||||
{ operation_id: input.operation_id, expected_revision: 2 },
|
||||
{ channel_account_id: 'wechat-1', expected_revision: 2 },
|
||||
{ channel_account_id: 'wechat-1', operation_id: input.operation_id },
|
||||
{ channel_account_id: '../other', operation_id: input.operation_id, expected_revision: 2 },
|
||||
]) expect(() => operationPlan({ operation: 'unbindChannelAccount', input: invalid })).toThrow();
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ health: 'connected' }, { enabled: true }, { target_agent_slug: draft.slug }, { id: 'another-account' },
|
||||
])('keeps unified unbinding pending when a success response contains unfinished account state %o', async unfinished => {
|
||||
const channel = { id: 'wechat-1', enabled: false, target_agent_slug: null, health: 'login_required', ...unfinished };
|
||||
const fetchImpl = vi.fn().mockResolvedValueOnce(session())
|
||||
.mockResolvedValueOnce(json({ channel, unbound: true, disconnected: true }));
|
||||
const module = moduleFor(fetchImpl);
|
||||
const original = { channel_account_id: 'wechat-1', operation_id: input.operation_id, expected_revision: 2 };
|
||||
await expect(module.execute({ operation: 'unbindChannelAccount', input: original })).rejects.toMatchObject({
|
||||
code: 'channel_operation_unconfirmed', commandOutcome: 'unknown',
|
||||
});
|
||||
expect((await module.recovery()).pending).toEqual([expect.objectContaining({
|
||||
operation: 'unbindChannelAccount', input: original,
|
||||
})]);
|
||||
});
|
||||
|
||||
it('requires compound completion and retries the same unbind intent after a partial success response', async () => {
|
||||
const channel = { id: 'wechat-1', enabled: false, target_agent_slug: null, health: 'login_required' };
|
||||
const fetchImpl = vi.fn().mockResolvedValueOnce(session())
|
||||
.mockResolvedValueOnce(json({ channel, disconnected: true }))
|
||||
.mockResolvedValueOnce(json({ channel, unbound: true, disconnected: true }));
|
||||
const module = moduleFor(fetchImpl);
|
||||
const original = { channel_account_id: 'wechat-1', operation_id: input.operation_id, expected_revision: 2 };
|
||||
await expect(module.execute({ operation: 'unbindChannelAccount', input: original })).rejects.toMatchObject({
|
||||
code: 'channel_operation_unconfirmed',
|
||||
});
|
||||
const pending = (await module.recovery()).pending[0];
|
||||
expect(await module.resolvePending({ id: pending.id })).toMatchObject({
|
||||
operation: 'unbindChannelAccount', input: original, result: { channel, unbound: true, disconnected: true },
|
||||
});
|
||||
expect((await module.recovery()).pending).toEqual([]);
|
||||
expect(fetchImpl.mock.calls.slice(1).map(call => [call[0], JSON.parse(call[1].body)])).toEqual([
|
||||
['https://agents.example.test/api/makelore/channel-accounts/wechat-1/unbind', {
|
||||
operation_id: input.operation_id, expected_revision: 2,
|
||||
}],
|
||||
['https://agents.example.test/api/makelore/channel-accounts/wechat-1/unbind', {
|
||||
operation_id: input.operation_id, expected_revision: 2,
|
||||
}],
|
||||
]);
|
||||
});
|
||||
|
||||
it('recovers a completed WeChat verification from its receipt without replaying the code', async () => {
|
||||
const fetchImpl = vi.fn().mockResolvedValueOnce(session()).mockRejectedValueOnce(new Error('lost response'))
|
||||
.mockResolvedValueOnce(json({ operation_id: input.operation_id, status: 'completed', result: { channel: { id: 'channel-1' } } }));
|
||||
|
||||
Reference in new issue
Block a user