Archived
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TM4kUBvekALu9qqmp7D3wr
67 lines
3.2 KiB
TypeScript
67 lines
3.2 KiB
TypeScript
/** 邮件契约 —— 账号入参按 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 秒;地址必须是带 TLD 的邮箱;kind 只认两种', () => {
|
|
expect(mailAccountInputSchema.safeParse({ kind: 'imap-smtp', name: 'a', address: 'a@b.com', pollIntervalSec: 5 }).success).toBe(false);
|
|
expect(mailAccountInputSchema.safeParse({ kind: 'imap-smtp', name: 'a', address: 'not-mail' }).success).toBe(false);
|
|
// 漏了 TLD 的地址(sales@corp)是打字错误,在 API 边界就拦下 —— 别拖到 SMTP 才炸
|
|
expect(mailAccountInputSchema.safeParse({ kind: 'imap-smtp', name: 'a', address: 'sales@corp' }).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.com'], 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.com'], subject: 'hi', text: 'x' }).success).toBe(false);
|
|
});
|
|
});
|