Archived
邮件契约修复:地址校验保持严格 z.email(),改测试夹具而非放宽生产 schema
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TM4kUBvekALu9qqmp7D3wr
This commit is contained in:
@@ -37,9 +37,11 @@ describe('mailAccountInputSchema', () => {
|
|||||||
expect(mailAccountInputSchema.safeParse({ kind: 'wecom', name: 'x', settings: {} }).success).toBe(false);
|
expect(mailAccountInputSchema.safeParse({ kind: 'wecom', name: 'x', settings: {} }).success).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('轮询间隔下限 10 秒;地址必须是邮箱;kind 只认两种', () => {
|
it('轮询间隔下限 10 秒;地址必须是带 TLD 的邮箱;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: 'a@b.com', pollIntervalSec: 5 }).success).toBe(false);
|
||||||
expect(mailAccountInputSchema.safeParse({ kind: 'imap-smtp', name: 'a', address: 'not-mail' }).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(mailAccountInputSchema.safeParse({ kind: 'pop3', name: 'a' }).success).toBe(false);
|
||||||
expect(MAIL_ACCOUNT_KINDS).toEqual(['imap-smtp', 'wecom']);
|
expect(MAIL_ACCOUNT_KINDS).toEqual(['imap-smtp', 'wecom']);
|
||||||
});
|
});
|
||||||
@@ -55,10 +57,10 @@ describe('mailAccountViewSchema', () => {
|
|||||||
|
|
||||||
describe('mailSendInputSchema', () => {
|
describe('mailSendInputSchema', () => {
|
||||||
it('收件人至少一个、主题必填;附件默认空数组;requestedBy 必带 kind', () => {
|
it('收件人至少一个、主题必填;附件默认空数组;requestedBy 必带 kind', () => {
|
||||||
const ok = mailSendInputSchema.safeParse({ to: ['a@b.c'], subject: 'hi', text: 'x', requestedBy: { kind: 'ops' } });
|
const ok = mailSendInputSchema.safeParse({ to: ['a@b.com'], subject: 'hi', text: 'x', requestedBy: { kind: 'ops' } });
|
||||||
expect(ok.success).toBe(true);
|
expect(ok.success).toBe(true);
|
||||||
if (ok.success) expect(ok.data.attachments).toEqual([]);
|
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: [], subject: 'hi', text: 'x', requestedBy: { kind: 'ops' } }).success).toBe(false);
|
||||||
expect(mailSendInputSchema.safeParse({ to: ['a@b.c'], subject: 'hi', text: 'x' }).success).toBe(false);
|
expect(mailSendInputSchema.safeParse({ to: ['a@b.com'], subject: 'hi', text: 'x' }).success).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -55,8 +55,7 @@ const accountCommon = {
|
|||||||
export const mailAccountInputSchema = z.discriminatedUnion('kind', [
|
export const mailAccountInputSchema = z.discriminatedUnion('kind', [
|
||||||
z.object({
|
z.object({
|
||||||
kind: z.literal('imap-smtp'),
|
kind: z.literal('imap-smtp'),
|
||||||
/** zod 默认 email 正则要求顶级域至少两个字母(`.c` 会被拒);邮箱地址理应更宽 —— 改用 html5Email 正则 */
|
address: z.email(),
|
||||||
address: z.email({ pattern: z.regexes.html5Email }),
|
|
||||||
...accountCommon,
|
...accountCommon,
|
||||||
settings: imapSmtpSettingsSchema.prefault({}),
|
settings: imapSmtpSettingsSchema.prefault({}),
|
||||||
/** 只写不读:缺省 = 不改(与 LLM 密钥同款) */
|
/** 只写不读:缺省 = 不改(与 LLM 密钥同款) */
|
||||||
@@ -179,9 +178,9 @@ export const mailSendInputSchema = z.object({
|
|||||||
accountId: z.string().optional(),
|
accountId: z.string().optional(),
|
||||||
/** 指定发件地址(channel 用):必须对上一个已启用账号,不静默回落 */
|
/** 指定发件地址(channel 用):必须对上一个已启用账号,不静默回落 */
|
||||||
from: z.string().optional(),
|
from: z.string().optional(),
|
||||||
to: z.array(z.email({ pattern: z.regexes.html5Email })).min(1),
|
to: z.array(z.email()).min(1),
|
||||||
cc: z.array(z.email({ pattern: z.regexes.html5Email })).default([]),
|
cc: z.array(z.email()).default([]),
|
||||||
bcc: z.array(z.email({ pattern: z.regexes.html5Email })).default([]),
|
bcc: z.array(z.email()).default([]),
|
||||||
subject: z.string().min(1),
|
subject: z.string().min(1),
|
||||||
text: z.string().optional(),
|
text: z.string().optional(),
|
||||||
html: z.string().optional(),
|
html: z.string().optional(),
|
||||||
|
|||||||
Reference in New Issue
Block a user