邮件契约:账号入参按 kind 分两形、视图只回 secretBound、入站 / 出站 / 探测 schema

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TM4kUBvekALu9qqmp7D3wr
This commit is contained in:
xcs
2026-08-25 23:45:37 +08:00
co-authored by Claude Fable 5
parent 26f07f3487
commit 404a32fa0a
3 changed files with 319 additions and 0 deletions
@@ -0,0 +1,64 @@
/** 邮件契约 —— 账号入参按 kind 分两形,默认值与视图不带密文 */
import { describe, expect, it } from 'vitest';
import {
mailAccountInputSchema,
mailAccountViewSchema,
mailSendInputSchema,
MAIL_ACCOUNT_KINDS,
} from '../mail.ts';
describe('mailAccountInputSchema', () => {
it('imap-smtp:只给地址与名字,腾讯的服务器 / 端口 / TLS / 文件夹全部有默认值', () => {
const parsed = mailAccountInputSchema.parse({ kind: 'imap-smtp', name: '销售信箱', address: 'sales@we.com' });
expect(parsed.kind).toBe('imap-smtp');
if (parsed.kind !== 'imap-smtp') throw new Error('unreachable');
expect(parsed.settings).toEqual({
imapHost: 'imap.exmail.qq.com',
imapPort: 993,
imapTls: true,
smtpHost: 'smtp.exmail.qq.com',
smtpPort: 465,
smtpTls: true,
folder: 'INBOX',
});
expect(parsed.enabled).toBe(true);
expect(parsed.isDefault).toBe(false);
expect(parsed.pollIntervalSec).toBe(30);
expect(parsed.channelId).toBeNull();
expect(parsed.backfillFrom).toBeNull();
expect(parsed.secret).toBeUndefined();
});
it('wecom:corpId 必填,地址不由人填(探测回填)', () => {
const parsed = mailAccountInputSchema.parse({ kind: 'wecom', name: '应用邮箱', settings: { corpId: 'ww123' } });
if (parsed.kind !== 'wecom') throw new Error('unreachable');
expect(parsed.settings.apiBase).toBe('https://qyapi.weixin.qq.com');
expect('address' in parsed).toBe(false);
expect(mailAccountInputSchema.safeParse({ kind: 'wecom', name: 'x', settings: {} }).success).toBe(false);
});
it('轮询间隔下限 10 秒;地址必须是邮箱;kind 只认两种', () => {
expect(mailAccountInputSchema.safeParse({ kind: 'imap-smtp', name: 'a', address: 'a@b.c', pollIntervalSec: 5 }).success).toBe(false);
expect(mailAccountInputSchema.safeParse({ kind: 'imap-smtp', name: 'a', address: 'not-mail' }).success).toBe(false);
expect(mailAccountInputSchema.safeParse({ kind: 'pop3', name: 'a' }).success).toBe(false);
expect(MAIL_ACCOUNT_KINDS).toEqual(['imap-smtp', 'wecom']);
});
});
describe('mailAccountViewSchema', () => {
it('视图只有 secretBound,没有任何密文 / 密码字段', () => {
const keys = Object.keys(mailAccountViewSchema.options[0]!.shape);
expect(keys).toContain('secretBound');
expect(keys.some((k) => /cipher|password|secret$/i.test(k))).toBe(false);
});
});
describe('mailSendInputSchema', () => {
it('收件人至少一个、主题必填;附件默认空数组;requestedBy 必带 kind', () => {
const ok = mailSendInputSchema.safeParse({ to: ['a@b.c'], subject: 'hi', text: 'x', requestedBy: { kind: 'ops' } });
expect(ok.success).toBe(true);
if (ok.success) expect(ok.data.attachments).toEqual([]);
expect(mailSendInputSchema.safeParse({ to: [], subject: 'hi', text: 'x', requestedBy: { kind: 'ops' } }).success).toBe(false);
expect(mailSendInputSchema.safeParse({ to: ['a@b.c'], subject: 'hi', text: 'x' }).success).toBe(false);
});
});