cc-haha/adapters/dingtalk/__tests__/permission-card.test.ts
程序员阿江(Relakkes) f1c5f86b80 feat: keep IM permission approvals consistent
DingTalk can receive interactive card callbacks, but the card UI still depends on a published template, so the adapter now supports a template-backed card path with text commands as the reliable fallback. Telegram and WeChat use the same allow-once, allow-always, and deny semantics so manual authorization behaves the same across platforms.

Constraint: DingTalk button rendering requires an operator-provided interactive card template id
Rejected: Treat the existing AI streaming card template as a permission card | it cannot guarantee visible action buttons
Confidence: high
Scope-risk: moderate
Directive: Do not remove the text approval fallback unless DingTalk card template provisioning is guaranteed
Tested: bun test adapters/common/__tests__/permission.test.ts adapters/dingtalk/__tests__/permission-card.test.ts adapters/telegram/__tests__/telegram.test.ts adapters/common/__tests__/config.test.ts src/server/__tests__/adapters.test.ts
Tested: bunx tsc -p adapters/tsconfig.json --noEmit
Tested: bun run check:adapters
Tested: cd desktop && bun run build
Tested: bun run check:server
Tested: bun run check:docs
Tested: git diff --check
Not-tested: bun run quality:pr is blocked by existing CLI core approval policy; see artifacts/quality-runs/2026-05-03T13-19-56-232Z/report.md
2026-05-04 19:03:36 +08:00

48 lines
1.7 KiB
TypeScript

import { describe, expect, it } from 'bun:test'
import {
buildDingTalkPermissionCardParams,
parseDingTalkPermissionCardAction,
} from '../permission-card.js'
describe('DingTalk permission card helpers', () => {
it('builds template params with three permission actions', () => {
const params = buildDingTalkPermissionCardParams('Bash', { command: 'npm test' }, 'req-1')
expect(params.requestId).toBe('req-1')
expect(params.toolName).toBe('Bash')
expect(String(params.inputPreview)).toContain('npm test')
expect(JSON.parse(String(params.allowValue))).toEqual({ action: 'permit', requestId: 'req-1', allowed: true })
expect(JSON.parse(String(params.alwaysValue))).toEqual({ action: 'permit', requestId: 'req-1', allowed: true, rule: 'always' })
expect(JSON.parse(String(params.denyValue))).toEqual({ action: 'permit', requestId: 'req-1', allowed: false })
})
it('parses nested card private params', () => {
const action = parseDingTalkPermissionCardAction({
outTrackId: 'permission_req-1',
content: JSON.stringify({
cardPrivateData: {
params: {
action: 'permit',
requestId: 'req-1',
allowed: true,
rule: 'always',
},
},
}),
})
expect(action).toEqual({ requestId: 'req-1', allowed: true, rule: 'always' })
})
it('parses compact callback values', () => {
expect(parseDingTalkPermissionCardAction({ actionValue: 'permit:req-2:no' })).toEqual({
requestId: 'req-2',
allowed: false,
})
})
it('ignores callbacks without permission action data', () => {
expect(parseDingTalkPermissionCardAction({ action: 'open_url', url: 'https://example.com' })).toBeNull()
})
})