mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-16 13:03:31 +08:00
DingTalk bot unbind was routed through the generic adapter config update, but undefined credential fields are dropped during JSON serialization. That left stored Client ID and Secret values intact after clicking the unbind button. Match the working WeChat path with a dedicated server endpoint and frontend API call so credential removal is explicit. Constraint: JSON request bodies cannot preserve undefined fields for generic config clearing Rejected: Keep using updateConfig with undefined fields | serialized requests omit the fields before the server can clear them Confidence: high Scope-risk: narrow Directive: Keep credential-clearing flows on explicit unbind endpoints rather than relying on partial config merges Tested: bun test src/server/__tests__/adapters.test.ts Tested: cd desktop && bun run test src/stores/adapterStore.test.ts Tested: bun run check:desktop Tested: bun run check:server Not-tested: bun run quality:pr is blocked by repository policy requiring allow-cli-core-change approval for the broader branch impact
77 lines
2.5 KiB
TypeScript
77 lines
2.5 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
describe('adapterStore IM pairing behavior', () => {
|
|
const adaptersApi = {
|
|
getConfig: vi.fn(),
|
|
updateConfig: vi.fn(),
|
|
startWechatLogin: vi.fn(),
|
|
pollWechatLogin: vi.fn(),
|
|
unbindWechat: vi.fn(),
|
|
unbindDingtalk: vi.fn(),
|
|
beginDingtalkRegistration: vi.fn(),
|
|
pollDingtalkRegistration: vi.fn(),
|
|
}
|
|
|
|
beforeEach(() => {
|
|
vi.resetModules()
|
|
vi.clearAllMocks()
|
|
adaptersApi.updateConfig.mockImplementation(async (patch) => patch)
|
|
vi.doMock('../api/adapters', () => ({ adaptersApi }))
|
|
vi.doMock('@tauri-apps/api/core', () => ({ invoke: vi.fn() }))
|
|
})
|
|
|
|
it('removes a WeChat paired user without clearing the bound account', async () => {
|
|
const { useAdapterStore } = await import('./adapterStore')
|
|
useAdapterStore.setState({
|
|
config: {
|
|
wechat: {
|
|
accountId: 'wx-account',
|
|
botToken: '****oken',
|
|
userId: 'wx-login-user',
|
|
pairedUsers: [
|
|
{ userId: 'wx-user-1', displayName: 'User 1', pairedAt: 1 },
|
|
{ userId: 'wx-user-2', displayName: 'User 2', pairedAt: 2 },
|
|
],
|
|
},
|
|
},
|
|
})
|
|
|
|
await useAdapterStore.getState().removePairedUser('wechat', 'wx-user-1')
|
|
|
|
expect(adaptersApi.unbindWechat).not.toHaveBeenCalled()
|
|
expect(adaptersApi.updateConfig).toHaveBeenCalledWith({
|
|
wechat: {
|
|
accountId: 'wx-account',
|
|
botToken: '****oken',
|
|
userId: 'wx-login-user',
|
|
pairedUsers: [{ userId: 'wx-user-2', displayName: 'User 2', pairedAt: 2 }],
|
|
},
|
|
})
|
|
})
|
|
|
|
it('unbinds the WeChat account only through the explicit account action', async () => {
|
|
const nextConfig = { wechat: { pairedUsers: [] } }
|
|
adaptersApi.unbindWechat.mockResolvedValue(nextConfig)
|
|
|
|
const { useAdapterStore } = await import('./adapterStore')
|
|
|
|
await useAdapterStore.getState().unbindWechatAccount()
|
|
|
|
expect(adaptersApi.unbindWechat).toHaveBeenCalledTimes(1)
|
|
expect(useAdapterStore.getState().config).toBe(nextConfig)
|
|
})
|
|
|
|
it('unbinds the DingTalk bot through the explicit bot action', async () => {
|
|
const nextConfig = { dingtalk: { pairedUsers: [], allowedUsers: [] } }
|
|
adaptersApi.unbindDingtalk.mockResolvedValue(nextConfig)
|
|
|
|
const { useAdapterStore } = await import('./adapterStore')
|
|
|
|
await useAdapterStore.getState().unbindDingtalkBot()
|
|
|
|
expect(adaptersApi.updateConfig).not.toHaveBeenCalled()
|
|
expect(adaptersApi.unbindDingtalk).toHaveBeenCalledTimes(1)
|
|
expect(useAdapterStore.getState().config).toBe(nextConfig)
|
|
})
|
|
})
|