using System; using System.IO; using System.Text.Json; using System.Xml.Linq; using Xunit; namespace Paca.Tests.Infrastructure; /// /// PACA 웹사이트 및 제품의 SEO(검색엔진), AEO(답변엔진), GEO(생성형 AI) 최적화 인프라 무결성 검증 게이트 /// public class SiteSeoAeoGeoGateTests { private static string SiteDir => Path.Combine(SourceTree.Root, "site"); [Fact] public void Gate211_SchemaOrgStructuredDataIntegrity() { // 1. index.html의 SoftwareApplication 및 FAQPage Schema.org 검증 var indexPath = Path.Combine(SiteDir, "index.html"); Assert.True(File.Exists(indexPath), "site/index.html 파일이 존재해야 합니다."); var indexHtml = File.ReadAllText(indexPath); Assert.Contains("application/ld+json", indexHtml); Assert.Contains("\"@type\": \"SoftwareApplication\"", indexHtml); Assert.Contains("\"offers\"", indexHtml); Assert.Contains("\"operatingSystem\"", indexHtml); // 2. how-to-download-m3u8.html (HLS m3u8 다운로드 완벽 가이드) 및 HowTo Schema.org 검증 var m3u8GuidePath = Path.Combine(SiteDir, "how-to-download-m3u8.html"); Assert.True(File.Exists(m3u8GuidePath), "site/how-to-download-m3u8.html 랜딩 페이지가 존재해야 합니다."); var m3u8Html = File.ReadAllText(m3u8GuidePath); Assert.Contains("\"@type\": \"HowTo\"", m3u8Html); Assert.Contains("\"step\"", m3u8Html); Assert.Contains("m3u8", m3u8Html); Assert.Contains("HLS", m3u8Html); Assert.Contains("MP4", m3u8Html); // 3. vs-idm-alternative.html (IDM / 4K Video Downloader 대체 비교 가이드) 검증 var vsIdmPath = Path.Combine(SiteDir, "vs-idm-alternative.html"); Assert.True(File.Exists(vsIdmPath), "site/vs-idm-alternative.html 랜딩 페이지가 존재해야 합니다."); var vsIdmHtml = File.ReadAllText(vsIdmPath); Assert.Contains("IDM", vsIdmHtml); Assert.Contains("4K Video Downloader", vsIdmHtml); Assert.Contains("Internet Download Manager", vsIdmHtml); Assert.Contains("\"@type\": \"FAQPage\"", vsIdmHtml); } [Fact] public void Gate212_AiCrawlerAndLlmStandardIntegrity() { // 1. robots.txt 검증: 모든 AI 크롤러 봇 (GPTBot, PerplexityBot, ClaudeBot, Google-Extended 등) 접근 허용 var robotsPath = Path.Combine(SiteDir, "robots.txt"); Assert.True(File.Exists(robotsPath), "site/robots.txt 파일이 존재해야 합니다."); var robotsTxt = File.ReadAllText(robotsPath); Assert.Contains("User-agent: *", robotsTxt); Assert.Contains("GPTBot", robotsTxt); Assert.Contains("PerplexityBot", robotsTxt); Assert.Contains("ClaudeBot", robotsTxt); Assert.Contains("Allow: /", robotsTxt); Assert.Contains("llms.txt", robotsTxt); Assert.Contains("sitemap.xml", robotsTxt); // 2. llms.txt (Answer.AI 표준 AI 모델용 제품 요약) 검증 var llmsPath = Path.Combine(SiteDir, "llms.txt"); Assert.True(File.Exists(llmsPath), "site/llms.txt 파일이 존재해야 합니다."); var llmsTxt = File.ReadAllText(llmsPath); Assert.Contains("# Paca", llmsTxt); Assert.Contains("HLS", llmsTxt); Assert.Contains("DASH", llmsTxt); Assert.Contains("AES-128", llmsTxt); Assert.Contains("ffmpeg", llmsTxt); Assert.Contains("llms-full.txt", llmsTxt); // 3. llms-full.txt (전체 상세 기술 명세 및 인용 문서) 검증 var llmsFullPath = Path.Combine(SiteDir, "llms-full.txt"); Assert.True(File.Exists(llmsFullPath), "site/llms-full.txt 파일이 존재해야 합니다."); var llmsFullTxt = File.ReadAllText(llmsFullPath); Assert.True(llmsFullTxt.Length >= 2500, "llms-full.txt는 AI 모델이 충분히 인용할 수 있도록 2,500자 이상의 상세 기술 명세를 담아야 합니다."); Assert.Contains("IDM", llmsFullTxt); Assert.Contains("Benchmark", llmsFullTxt); Assert.Contains("AES-128", llmsFullTxt); Assert.Contains("FAQ", llmsFullTxt); } [Fact] public void Gate213_AeoComparisonMatrixAndSitemapIntegrity() { // 1. AEO 경쟁 툴 비교 테이블 (Comparison Matrix) 존재성 검증 var vsIdmPath = Path.Combine(SiteDir, "vs-idm-alternative.html"); var indexPath = Path.Combine(SiteDir, "index.html"); Assert.True(File.Exists(vsIdmPath)); var vsHtml = File.ReadAllText(vsIdmPath); Assert.Contains("