/** * message.js 文件单元测试 */ import { describe, expect, it } from 'vitest'; import { MESSAGE_TYPES } from '../../src/config.js'; import { buildActionCardMessage, buildFeedCardMessage, buildLinkMessage, buildMarkdownMessage, buildMessage, buildTextMessage, getMessageSummary, validateMessage, } from '../../src/message.js'; import { assertDingTalkMessage } from '../helpers/assertions.js'; describe('测试 message.js 文件', () => { describe('测试 buildTextMessage 函数', () => { it('应该构建基本的文本消息', () => { const message = buildTextMessage('Hello, World!'); assertDingTalkMessage(message, MESSAGE_TYPES.TEXT); expect(message.msgtype).toBe(MESSAGE_TYPES.TEXT); expect(message.text.content).toBe('Hello, World!'); expect(message.at).toBeUndefined(); }); it('应该构建包含@手机号的文本消息', () => { const atMobiles = ['13800138000', '13900139000']; const message = buildTextMessage('Hello, World!', atMobiles); expect(message.msgtype).toBe(MESSAGE_TYPES.TEXT); expect(message.text.content).toBe('Hello, World!'); expect(message.at).toEqual({ atMobiles: ['13800138000', '13900139000'], isAtAll: false, }); }); it('应该构建@所有人的文本消息', () => { const message = buildTextMessage('Hello, World!', [], true); expect(message.msgtype).toBe(MESSAGE_TYPES.TEXT); expect(message.text.content).toBe('Hello, World!'); expect(message.at).toEqual({ atMobiles: [], isAtAll: true, }); }); it('应该构建同时包含@手机号和@所有人的文本消息', () => { const atMobiles = ['13800138000']; const message = buildTextMessage('Hello, World!', atMobiles, true); expect(message.at).toEqual({ atMobiles: ['13800138000'], isAtAll: true, }); }); it('应该对缺少内容抛出错误', () => { expect(() => buildTextMessage()).toThrow('Content is required for text message'); expect(() => buildTextMessage('')).toThrow('Content is required for text message'); expect(() => buildTextMessage(null)).toThrow('Content is required for text message'); expect(() => buildTextMessage(123)).toThrow('Content is required for text message'); }); it('应该处理空的@手机号数组', () => { const message = buildTextMessage('Hello, World!', []); expect(message.at).toBeUndefined(); }); it('应该处理 null/undefined 的@手机号', () => { const message1 = buildTextMessage('Hello, World!', null); const message2 = buildTextMessage('Hello, World!', undefined); expect(message1.at).toBeUndefined(); expect(message2.at).toBeUndefined(); }); }); describe('测试 buildMarkdownMessage 函数', () => { it('应该构建基本的 Markdown 消息', () => { const message = buildMarkdownMessage('标题', '## Markdown 内容'); assertDingTalkMessage(message, MESSAGE_TYPES.MARKDOWN); expect(message.msgtype).toBe(MESSAGE_TYPES.MARKDOWN); expect(message.markdown.title).toBe('标题'); expect(message.markdown.text).toBe('## Markdown 内容'); expect(message.at).toBeUndefined(); }); it('应该构建包含@手机号的 Markdown 消息', () => { const atMobiles = ['13800138000', '13900139000']; const message = buildMarkdownMessage('标题', '## Markdown 内容', atMobiles); expect(message.msgtype).toBe(MESSAGE_TYPES.MARKDOWN); expect(message.markdown.title).toBe('标题'); expect(message.markdown.text).toBe('## Markdown 内容\n\n@13800138000 @13900139000'); expect(message.at).toEqual({ atMobiles: ['13800138000', '13900139000'], isAtAll: false, }); }); it('应该构建@所有人的 Markdown 消息', () => { const message = buildMarkdownMessage('标题', '## Markdown 内容', [], true); expect(message.msgtype).toBe(MESSAGE_TYPES.MARKDOWN); expect(message.markdown.text).toBe('## Markdown 内容\n\n@所有人'); expect(message.at).toEqual({ atMobiles: [], isAtAll: true, }); }); it('应该构建同时包含@手机号和@所有人的 Markdown 消息', () => { const atMobiles = ['13800138000']; const message = buildMarkdownMessage('标题', '## Markdown 内容', atMobiles, true); expect(message.markdown.text).toBe('## Markdown 内容\n\n@13800138000\n\n@所有人'); expect(message.at).toEqual({ atMobiles: ['13800138000'], isAtAll: true, }); }); it('应该对缺少标题抛出错误', () => { expect(() => buildMarkdownMessage()).toThrow('Title is required for markdown message'); expect(() => buildMarkdownMessage('')).toThrow('Title is required for markdown message'); expect(() => buildMarkdownMessage(null)).toThrow('Title is required for markdown message'); expect(() => buildMarkdownMessage(123)).toThrow('Title is required for markdown message'); }); it('应该对缺少内容抛出错误', () => { expect(() => buildMarkdownMessage('标题')).toThrow( 'Content is required for markdown message', ); expect(() => buildMarkdownMessage('标题', '')).toThrow( 'Content is required for markdown message', ); expect(() => buildMarkdownMessage('标题', null)).toThrow( 'Content is required for markdown message', ); expect(() => buildMarkdownMessage('标题', 123)).toThrow( 'Content is required for markdown message', ); }); }); describe('测试 buildLinkMessage 函数', () => { it('应该构建基本的链接消息', () => { const message = buildLinkMessage('链接标题', '链接描述', 'https://example.com'); assertDingTalkMessage(message, MESSAGE_TYPES.LINK); expect(message.msgtype).toBe(MESSAGE_TYPES.LINK); expect(message.link.title).toBe('链接标题'); expect(message.link.text).toBe('链接描述'); expect(message.link.messageUrl).toBe('https://example.com'); expect(message.link.picUrl).toBeUndefined(); }); it('应该构建包含图片 URL 的链接消息', () => { const message = buildLinkMessage( '链接标题', '链接描述', 'https://example.com', 'https://example.com/image.jpg', ); expect(message.link.picUrl).toBe('https://example.com/image.jpg'); }); it('应该忽略无效的图片 URL', () => { const message = buildLinkMessage( '链接标题', '链接描述', 'https://example.com', 'not-a-valid-url', ); expect(message.link.picUrl).toBeUndefined(); }); it('应该忽略非字符串的图片 URL', () => { const message = buildLinkMessage('链接标题', '链接描述', 'https://example.com', 123); expect(message.link.picUrl).toBeUndefined(); }); it('应该对缺少标题抛出错误', () => { expect(() => buildLinkMessage()).toThrow('Title is required for link message'); expect(() => buildLinkMessage('')).toThrow('Title is required for link message'); expect(() => buildLinkMessage(null)).toThrow('Title is required for link message'); expect(() => buildLinkMessage(123)).toThrow('Title is required for link message'); }); it('应该对缺少内容抛出错误', () => { expect(() => buildLinkMessage('标题')).toThrow('Content is required for link message'); expect(() => buildLinkMessage('标题', '')).toThrow('Content is required for link message'); expect(() => buildLinkMessage('标题', null)).toThrow('Content is required for link message'); expect(() => buildLinkMessage('标题', 123)).toThrow('Content is required for link message'); }); it('应该对缺少链接 URL 抛出错误', () => { expect(() => buildLinkMessage('标题', '内容')).toThrow( 'Link URL is required for link message', ); expect(() => buildLinkMessage('标题', '内容', '')).toThrow( 'Link URL is required for link message', ); expect(() => buildLinkMessage('标题', '内容', null)).toThrow( 'Link URL is required for link message', ); expect(() => buildLinkMessage('标题', '内容', 123)).toThrow( 'Link URL is required for link message', ); }); it('应该对无效的链接 URL 格式抛出错误', () => { expect(() => buildLinkMessage('标题', '内容', 'not-a-url')).toThrow( 'Link URL must be a valid HTTP/HTTPS URL', ); expect(() => buildLinkMessage('标题', '内容', 'ftp://example.com')).toThrow( 'Link URL must be a valid HTTP/HTTPS URL', ); }); it('应该接受 HTTP 和 HTTPS URL', () => { const httpMessage = buildLinkMessage('标题', '内容', 'http://example.com'); const httpsMessage = buildLinkMessage('标题', '内容', 'https://example.com'); expect(httpMessage.link.messageUrl).toBe('http://example.com'); expect(httpsMessage.link.messageUrl).toBe('https://example.com'); }); }); describe('测试 buildActionCardMessage 函数', () => { it('应该构建单按钮 ActionCard 消息', () => { const options = { btnOrientation: '0', singleTitle: '查看详情', singleURL: 'https://example.com/detail', }; const message = buildActionCardMessage('ActionCard 标题', 'ActionCard 内容', options); assertDingTalkMessage(message, MESSAGE_TYPES.ACTION_CARD); expect(message.msgtype).toBe(MESSAGE_TYPES.ACTION_CARD); expect(message.actionCard.title).toBe('ActionCard 标题'); expect(message.actionCard.text).toBe('ActionCard 内容'); expect(message.actionCard.btnOrientation).toBe('0'); expect(message.actionCard.singleTitle).toBe('查看详情'); expect(message.actionCard.singleURL).toBe('https://example.com/detail'); expect(message.actionCard.btns).toBeUndefined(); }); it('应该构建多按钮 ActionCard 消息', () => { const options = { btnOrientation: '1', buttons: [ { title: '按钮1', actionURL: 'https://example.com/action1' }, { title: '按钮2', actionURL: 'https://example.com/action2' }, ], }; const message = buildActionCardMessage('ActionCard 标题', 'ActionCard 内容', options); expect(message.msgtype).toBe(MESSAGE_TYPES.ACTION_CARD); expect(message.actionCard.btnOrientation).toBe('1'); expect(message.actionCard.btns).toEqual([ { title: '按钮1', actionURL: 'https://example.com/action1' }, { title: '按钮2', actionURL: 'https://example.com/action2' }, ]); expect(message.actionCard.singleTitle).toBeUndefined(); expect(message.actionCard.singleURL).toBeUndefined(); }); it('应该使用默认的按钮方向', () => { const options = { singleTitle: '查看详情', singleURL: 'https://example.com/detail', }; const message = buildActionCardMessage('ActionCard 标题', 'ActionCard 内容', options); expect(message.actionCard.btnOrientation).toBe('0'); }); it('应该处理空选项', () => { expect(() => buildActionCardMessage('标题', '内容', {})).toThrow( 'actionCard requires either singleTitle/singleURL or buttons', ); }); it('应该处理未定义的选项', () => { expect(() => buildActionCardMessage('标题', '内容')).toThrow( 'actionCard requires either singleTitle/singleURL or buttons', ); }); it('应该对缺少标题抛出错误', () => { expect(() => buildActionCardMessage()).toThrow('Title is required for actionCard message'); expect(() => buildActionCardMessage('')).toThrow('Title is required for actionCard message'); expect(() => buildActionCardMessage(null)).toThrow( 'Title is required for actionCard message', ); expect(() => buildActionCardMessage(123)).toThrow('Title is required for actionCard message'); }); it('应该对缺少内容抛出错误', () => { expect(() => buildActionCardMessage('标题')).toThrow( 'Content is required for actionCard message', ); expect(() => buildActionCardMessage('标题', '')).toThrow( 'Content is required for actionCard message', ); expect(() => buildActionCardMessage('标题', null)).toThrow( 'Content is required for actionCard message', ); expect(() => buildActionCardMessage('标题', 123)).toThrow( 'Content is required for actionCard message', ); }); it('应该对既没有单按钮也没有按钮数组的情况抛出错误', () => { expect(() => buildActionCardMessage('标题', '内容', {})).toThrow( 'actionCard requires either singleTitle/singleURL or buttons', ); }); it('应该处理空的按钮数组', () => { const options = { buttons: [] }; expect(() => buildActionCardMessage('标题', '内容', options)).toThrow( 'actionCard requires either singleTitle/singleURL or buttons', ); }); it('应该处理非数组的按钮', () => { const options = { buttons: 'not-an-array' }; expect(() => buildActionCardMessage('标题', '内容', options)).toThrow( 'actionCard requires either singleTitle/singleURL or buttons', ); }); it('当同时提供单按钮和按钮数组时应该优先使用单按钮', () => { const options = { singleTitle: '单按钮', singleURL: 'https://example.com/single', buttons: [{ title: '多按钮', actionURL: 'https://example.com/multi' }], }; const message = buildActionCardMessage('标题', '内容', options); expect(message.actionCard.singleTitle).toBe('单按钮'); expect(message.actionCard.singleURL).toBe('https://example.com/single'); expect(message.actionCard.btns).toEqual([ { title: '多按钮', actionURL: 'https://example.com/multi' }, ]); }); }); describe('测试 buildFeedCardMessage 函数', () => { it('应该构建 FeedCard 消息', () => { const links = [ { title: '新闻1', messageURL: 'https://example.com/news1', picURL: 'https://example.com/pic1.jpg', }, { title: '新闻2', messageURL: 'https://example.com/news2', picURL: 'https://example.com/pic2.jpg', }, ]; const message = buildFeedCardMessage(links); assertDingTalkMessage(message, MESSAGE_TYPES.FEED_CARD); expect(message.msgtype).toBe(MESSAGE_TYPES.FEED_CARD); expect(message.feedCard.links).toEqual(links); }); it('应该构建包含单个链接的 FeedCard 消息', () => { const links = [ { title: '单个新闻', messageURL: 'https://example.com/news', picURL: 'https://example.com/pic.jpg', }, ]; const message = buildFeedCardMessage(links); expect(message.feedCard.links).toHaveLength(1); expect(message.feedCard.links[0]).toEqual(links[0]); }); it('应该对缺少链接抛出错误', () => { expect(() => buildFeedCardMessage()).toThrow('FeedCard requires a non-empty links array'); expect(() => buildFeedCardMessage(null)).toThrow('FeedCard requires a non-empty links array'); expect(() => buildFeedCardMessage('not-an-array')).toThrow( 'FeedCard requires a non-empty links array', ); }); it('应该对空链接数组抛出错误', () => { expect(() => buildFeedCardMessage([])).toThrow('FeedCard requires a non-empty links array'); }); it('应该正确映射链接属性', () => { const links = [ { title: '测试标题', messageURL: 'https://test.com', picURL: 'https://test.com/pic.jpg', extraProperty: 'should be ignored', }, ]; const message = buildFeedCardMessage(links); expect(message.feedCard.links[0]).toEqual({ title: '测试标题', messageURL: 'https://test.com', picURL: 'https://test.com/pic.jpg', }); expect(message.feedCard.links[0].extraProperty).toBeUndefined(); }); }); describe('测试 buildMessage 函数', () => { it('应该根据配置构建文本消息', () => { const config = { messageType: 'text', content: 'Hello, World!', atMobiles: ['13800138000'], atAll: false, }; const message = buildMessage(config); expect(message.msgtype).toBe(MESSAGE_TYPES.TEXT); expect(message.text.content).toBe('Hello, World!'); expect(message.at.atMobiles).toEqual(['13800138000']); }); it('应该根据配置构建 Markdown 消息', () => { const config = { messageType: 'markdown', title: 'Markdown 标题', content: '## Markdown 内容', atMobiles: [], atAll: true, }; const message = buildMessage(config); expect(message.msgtype).toBe(MESSAGE_TYPES.MARKDOWN); expect(message.markdown.title).toBe('Markdown 标题'); expect(message.at.isAtAll).toBe(true); }); it('应该根据配置构建链接消息', () => { const config = { messageType: 'link', title: '链接标题', content: '链接描述', linkUrl: 'https://example.com', picUrl: 'https://example.com/pic.jpg', }; const message = buildMessage(config); expect(message.msgtype).toBe(MESSAGE_TYPES.LINK); expect(message.link.title).toBe('链接标题'); expect(message.link.messageUrl).toBe('https://example.com'); expect(message.link.picUrl).toBe('https://example.com/pic.jpg'); }); it('应该根据配置构建 ActionCard 消息', () => { const config = { messageType: 'actionCard', title: 'ActionCard 标题', content: 'ActionCard 内容', btnOrientation: '1', buttons: [{ title: '按钮1', actionURL: 'https://example.com/action1' }], }; const message = buildMessage(config); expect(message.msgtype).toBe(MESSAGE_TYPES.ACTION_CARD); expect(message.actionCard.title).toBe('ActionCard 标题'); expect(message.actionCard.btnOrientation).toBe('1'); expect(message.actionCard.btns).toHaveLength(1); }); it('应该根据配置构建 FeedCard 消息', () => { const config = { messageType: 'feedCard', feedLinks: [ { title: '新闻1', messageURL: 'https://example.com/news1', picURL: 'https://example.com/pic1.jpg', }, ], }; const message = buildMessage(config); expect(message.msgtype).toBe(MESSAGE_TYPES.FEED_CARD); expect(message.feedCard.links).toHaveLength(1); }); it('应该对不支持的消息类型抛出错误', () => { const config = { messageType: 'unsupported', content: 'Test content', }; expect(() => buildMessage(config)).toThrow('Unsupported message type: unsupported'); }); }); describe('测试 validateMessage 函数', () => { it('应该验证有效的文本消息', () => { const message = { msgtype: 'text', text: { content: 'Hello, World!', }, }; const result = validateMessage(message); expect(result.isValid).toBe(true); expect(result.errors).toEqual([]); }); it('应该验证有效的 Markdown 消息', () => { const message = { msgtype: 'markdown', markdown: { title: '标题', text: '## 内容', }, }; const result = validateMessage(message); expect(result.isValid).toBe(true); expect(result.errors).toEqual([]); }); it('应该验证有效的链接消息', () => { const message = { msgtype: 'link', link: { title: '链接标题', text: '链接描述', messageUrl: 'https://example.com', }, }; const result = validateMessage(message); expect(result.isValid).toBe(true); expect(result.errors).toEqual([]); }); it('应该验证有效的单按钮 ActionCard 消息', () => { const message = { msgtype: 'actionCard', actionCard: { title: 'ActionCard 标题', text: 'ActionCard 内容', singleTitle: '查看详情', singleURL: 'https://example.com', }, }; const result = validateMessage(message); expect(result.isValid).toBe(true); expect(result.errors).toEqual([]); }); it('应该验证有效的多按钮 ActionCard 消息', () => { const message = { msgtype: 'actionCard', actionCard: { title: 'ActionCard 标题', text: 'ActionCard 内容', btns: [ { title: '按钮1', actionURL: 'https://example.com/action1' }, { title: '按钮2', actionURL: 'https://example.com/action2' }, ], }, }; const result = validateMessage(message); expect(result.isValid).toBe(true); expect(result.errors).toEqual([]); }); it('应该验证有效的 FeedCard 消息', () => { const message = { msgtype: 'feedCard', feedCard: { links: [ { title: '新闻1', messageURL: 'https://example.com/news1', picURL: 'https://example.com/pic1.jpg', }, ], }, }; const result = validateMessage(message); expect(result.isValid).toBe(true); expect(result.errors).toEqual([]); }); it('应该拒绝 null/undefined 消息', () => { expect(validateMessage(null).isValid).toBe(false); expect(validateMessage(undefined).isValid).toBe(false); expect(validateMessage('not-an-object').isValid).toBe(false); }); it('应该拒绝没有 msgtype 的消息', () => { const message = { text: { content: 'Hello' }, }; const result = validateMessage(message); expect(result.isValid).toBe(false); expect(result.errors).toContain('Invalid or missing msgtype'); }); it('应该拒绝带有无效 msgtype 的消息', () => { const message = { msgtype: 'invalid', text: { content: 'Hello' }, }; const result = validateMessage(message); expect(result.isValid).toBe(false); expect(result.errors).toContain('Invalid or missing msgtype'); }); it('应该拒绝没有内容的文本消息', () => { const message = { msgtype: 'text', text: {}, }; const result = validateMessage(message); expect(result.isValid).toBe(false); expect(result.errors).toContain('Text message must have content'); }); it('应该拒绝没有标题或内容的 Markdown 消息', () => { const message1 = { msgtype: 'markdown', markdown: { text: '内容' }, }; const message2 = { msgtype: 'markdown', markdown: { title: '标题' }, }; expect(validateMessage(message1).isValid).toBe(false); expect(validateMessage(message2).isValid).toBe(false); }); it('应该拒绝缺少字段的链接消息', () => { const message = { msgtype: 'link', link: { title: '标题' }, }; const result = validateMessage(message); expect(result.isValid).toBe(false); expect(result.errors).toContain('Link message must have title, text, and messageUrl'); }); it('应该拒绝单按钮不完整的 ActionCard 消息', () => { const message = { msgtype: 'actionCard', actionCard: { title: '标题', text: '内容', singleTitle: '按钮', // missing singleURL }, }; const result = validateMessage(message); expect(result.isValid).toBe(false); expect(result.errors).toContain( 'ActionCard single button requires both singleTitle and singleURL', ); }); it('应该拒绝带有无效按钮的 ActionCard 消息', () => { const message = { msgtype: 'actionCard', actionCard: { title: '标题', text: '内容', btns: [ { title: '按钮1' }, // missing actionURL ], }, }; const result = validateMessage(message); expect(result.isValid).toBe(false); expect(result.errors).toContain('Each ActionCard button must have title and actionURL'); }); it('应该拒绝没有链接的 FeedCard 消息', () => { const message = { msgtype: 'feedCard', feedCard: {}, }; const result = validateMessage(message); expect(result.isValid).toBe(false); expect(result.errors).toContain('FeedCard must have a non-empty links array'); }); it('应该拒绝带有无效链接的 FeedCard 消息', () => { const message = { msgtype: 'feedCard', feedCard: { links: [ { title: '新闻1' }, // missing messageURL and picURL ], }, }; const result = validateMessage(message); expect(result.isValid).toBe(false); expect(result.errors).toContain('Each FeedCard link must have title, messageURL and picURL'); }); }); describe('测试 getMessageSummary 函数', () => { it('应该返回文本消息的摘要', () => { const message = { msgtype: 'text', text: { content: 'Hello, World!', }, }; const summary = getMessageSummary(message); expect(summary).toBe('Type: text, Content: "Hello, World!"'); }); it('应该截断长文本内容', () => { const longContent = 'A'.repeat(60); const message = { msgtype: 'text', text: { content: longContent, }, }; const summary = getMessageSummary(message); expect(summary).toContain(`Type: text, Content: "${'A'.repeat(50)}..."`); }); it('应该返回 Markdown 消息的摘要', () => { const message = { msgtype: 'markdown', markdown: { title: 'Markdown 标题', text: '## Markdown 内容', }, }; const summary = getMessageSummary(message); expect(summary).toBe('Type: markdown, Title: "Markdown 标题", Text: "## Markdown 内容"'); }); it('应该返回链接消息的摘要', () => { const message = { msgtype: 'link', link: { title: '链接标题', text: '链接描述', messageUrl: 'https://example.com', }, }; const summary = getMessageSummary(message); expect(summary).toBe('Type: link, Title: "链接标题", URL: "https://example.com"'); }); it('应该返回单按钮 ActionCard 消息的摘要', () => { const message = { msgtype: 'actionCard', actionCard: { title: 'ActionCard 标题', text: 'ActionCard 内容', singleTitle: '查看详情', }, }; const summary = getMessageSummary(message); expect(summary).toBe('Type: actionCard, Title: "ActionCard 标题", Single: "查看详情"'); }); it('应该返回多按钮 ActionCard 消息的摘要', () => { const message = { msgtype: 'actionCard', actionCard: { title: 'ActionCard 标题', text: 'ActionCard 内容', btns: [ { title: '按钮1', actionURL: 'https://example.com/action1' }, { title: '按钮2', actionURL: 'https://example.com/action2' }, ], }, }; const summary = getMessageSummary(message); expect(summary).toBe('Type: actionCard, Title: "ActionCard 标题", Buttons: 2'); }); it('应该返回 FeedCard 消息的摘要', () => { const message = { msgtype: 'feedCard', feedCard: { links: [ { title: '新闻1', messageURL: 'https://example.com/news1', picURL: 'https://example.com/pic1.jpg', }, { title: '新闻2', messageURL: 'https://example.com/news2', picURL: 'https://example.com/pic2.jpg', }, ], }, }; const summary = getMessageSummary(message); expect(summary).toBe('Type: feedCard, Links: 2'); }); it('应该包含 @所有人 信息', () => { const message = { msgtype: 'text', text: { content: 'Hello, World!', }, at: { isAtAll: true, atMobiles: [], }, }; const summary = getMessageSummary(message); expect(summary).toBe('Type: text, Content: "Hello, World!", @All: true'); }); it('应该包含 @手机号 信息', () => { const message = { msgtype: 'text', text: { content: 'Hello, World!', }, at: { isAtAll: false, atMobiles: ['13800138000', '13900139000'], }, }; const summary = getMessageSummary(message); expect(summary).toBe('Type: text, Content: "Hello, World!", @Mobiles: 2'); }); it('应该同时包含 @所有人 和 @手机号 信息', () => { const message = { msgtype: 'text', text: { content: 'Hello, World!', }, at: { isAtAll: true, atMobiles: ['13800138000'], }, }; const summary = getMessageSummary(message); expect(summary).toBe('Type: text, Content: "Hello, World!", @All: true, @Mobiles: 1'); }); it('应该处理无效的消息', () => { expect(getMessageSummary(null)).toBe('Invalid message'); expect(getMessageSummary({})).toBe('Invalid message'); expect(getMessageSummary({ msgtype: null })).toBe('Invalid message'); }); }); });