import { CardSeoService } from '../../../src/modules/card/service/seo'; import { CardPortalService } from '../../../src/modules/card/service/portal'; import { OpenCardPortalController, PortalRequestContext, } from '../../../src/modules/card/controller/open/portal'; import { CardPortalDetail } from '../../../src/comm/types/omni'; describe('Point 29-B: 社交爬虫探测与微官网短链服务端渲染集成 (Social Crawler & SEO SSR)', () => { let cardSeoService: CardSeoService; let cardPortalService: CardPortalService; let portalController: OpenCardPortalController; // 模拟真实名片聚合资产 const mockAlexCardDetail: CardPortalDetail = { id: 101, slug: 'alex-zheng', name: 'Alex Zheng', title: 'VP of Global Expansion', company: 'OmniCard Global Inc.', slogan: 'Empowering Global Business Connections', avatarUrl: 'https://cdn.omnicard.ai/avatars/alex.png', phone: '+86-13800138000', email: 'alex@omnicard.ai', whatsapp: '+8613800138000', linkedin: 'https://linkedin.com/in/alex-zheng', timezone: 'Asia/Shanghai', audioPronunciationUrl: 'https://cdn.omnicard.ai/audio/alex.mp3', themeCode: 'corporate_navy', status: 'published', bentoBlocks: [ { id: 1, cardId: 101, type: 'hero', colSpan: 2, rowSpan: 1, sortOrder: 1, title: 'About Alex', content: '10+ years in international business expansion.', }, ], cachedAt: Date.now(), }; // 常用社交爬虫与普通用户 UA 常量 const USER_AGENTS = { whatsapp: 'WhatsApp/2.21.12.21 A', whatsappIos: 'WhatsApp/2.23.20.0 i', linkedin: 'LinkedInBot/1.0 (compatible; Mozilla/5.0; Apache-HttpClient +http://www.linkedin.com)', twitter: 'Twitterbot/1.0', facebook: 'facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)', facebot: 'Facebot', telegram: 'TelegramBot (like TwitterBot)', slack: 'Slackbot-LinkExpanding 1.0 (+https://api.slack.com/robots)', discord: 'Mozilla/5.0 (compatible; Discordbot/2.0; +https://discordapp.com)', googlebot: 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)', safariMobile: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Mobile/15E148 Safari/604.1', chromeDesktop: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36', }; beforeEach(() => { cardSeoService = new CardSeoService(); cardPortalService = new CardPortalService(); interface CustomHttpError extends Error { status?: number; } // Mock cardPortalService.getPortalDetail jest .spyOn(cardPortalService, 'getPortalDetail') .mockImplementation(async (slugOrId: string | number) => { if (slugOrId === 'alex-zheng' || slugOrId === 101 || slugOrId === '101') { return { ...mockAlexCardDetail }; } const error = new Error('名片不存在或已下线') as CustomHttpError; error.status = 404; throw error; }); portalController = new OpenCardPortalController(); portalController.cardPortalService = cardPortalService; portalController.cardSeoService = cardSeoService; }); describe('1. 社交爬虫与机器人 User-Agent 精准探测识别', () => { it('应准确识别 WhatsApp 爬虫 User-Agent', () => { expect(cardSeoService.isCrawler(USER_AGENTS.whatsapp)).toBe(true); expect(cardSeoService.isCrawler(USER_AGENTS.whatsappIos)).toBe(true); expect(cardSeoService.detectCrawler(USER_AGENTS.whatsapp)).toBe('whatsapp'); }); it('应准确识别 LinkedInBot 爬虫 User-Agent', () => { expect(cardSeoService.isCrawler(USER_AGENTS.linkedin)).toBe(true); expect(cardSeoService.detectCrawler(USER_AGENTS.linkedin)).toBe('linkedin'); }); it('应准确识别 Twitterbot 爬虫 User-Agent', () => { expect(cardSeoService.isCrawler(USER_AGENTS.twitter)).toBe(true); expect(cardSeoService.detectCrawler(USER_AGENTS.twitter)).toBe('twitter'); }); it('应准确识别 facebookexternalhit 与 Facebot 爬虫 User-Agent', () => { expect(cardSeoService.isCrawler(USER_AGENTS.facebook)).toBe(true); expect(cardSeoService.isCrawler(USER_AGENTS.facebot)).toBe(true); expect(cardSeoService.detectCrawler(USER_AGENTS.facebook)).toBe('facebook'); }); it('应准确识别 TelegramBot 爬虫 User-Agent', () => { expect(cardSeoService.isCrawler(USER_AGENTS.telegram)).toBe(true); expect(cardSeoService.detectCrawler(USER_AGENTS.telegram)).toBe('telegram'); }); it('应支持识别 Slackbot、Discordbot 及主流搜索引擎机器人', () => { expect(cardSeoService.isCrawler(USER_AGENTS.slack)).toBe(true); expect(cardSeoService.isCrawler(USER_AGENTS.discord)).toBe(true); expect(cardSeoService.isCrawler(USER_AGENTS.googlebot)).toBe(true); }); it('常规移动端与桌面端浏览器不应被判定为爬虫', () => { expect(cardSeoService.isCrawler(USER_AGENTS.safariMobile)).toBe(false); expect(cardSeoService.isCrawler(USER_AGENTS.chromeDesktop)).toBe(false); expect(cardSeoService.detectCrawler(USER_AGENTS.chromeDesktop)).toBeNull(); }); it('对于空字符串或 undefined User-Agent 应安全降级返回 false', () => { expect(cardSeoService.isCrawler('')).toBe(false); expect(cardSeoService.isCrawler(undefined)).toBe(false); expect(cardSeoService.detectCrawler('')).toBeNull(); expect(cardSeoService.detectCrawler(undefined)).toBeNull(); }); }); describe('2. OpenGraph 元数据提炼与微官网 HTML 模板服务端渲染', () => { it('应正确从名片提取 og:title、og:image、og:description 等元数据', () => { const ogMeta = cardSeoService.generateOgMeta(mockAlexCardDetail); expect(ogMeta.title).toBe('Alex Zheng - VP of Global Expansion | OmniCard Global Inc.'); expect(ogMeta.image).toBe('https://cdn.omnicard.ai/avatars/alex.png'); expect(ogMeta.description).toBe('Empowering Global Business Connections'); expect(ogMeta.type).toBe('profile'); }); it('渲染输出的 HTML 必须包含完整的 OpenGraph、Twitter Cards 与 JSON-LD 标签', () => { const html = cardSeoService.renderCardHtml(mockAlexCardDetail); // HTML5 规范结构 expect(html).toContain(''); expect(html).toContain(''); expect(html).toContain(''); expect(html).toContain('
'); expect(html).toContain(''); // OpenGraph 协议标准断言 expect(html).toContain(''); expect(html).toContain('"@type":"Person"'); expect(html).toContain('"name":"Alex Zheng"'); expect(html).toContain('"jobTitle":"VP of Global Expansion"'); // 包含名片主要信息 expect(html).toContain('Alex Zheng'); expect(html).toContain('VP of Global Expansion'); expect(html).toContain('OmniCard Global Inc.'); }); it('处理特殊字符时必须严格 HTML 实体转义以防范 XSS 注入', () => { const maliciousCard: CardPortalDetail = { ...mockAlexCardDetail, name: 'Evil ', company: 'Hack & Co "Global"', slogan: 'Test