469 lines
22 KiB
C#
469 lines
22 KiB
C#
using System;
|
|
using System.IO;
|
|
using Paca.App.Browser.Features;
|
|
using Paca.Browser.Ads;
|
|
using Paca.Browser.NewTab;
|
|
using Paca.Tests.Infrastructure;
|
|
using Xunit;
|
|
|
|
namespace Paca.Tests.Browser;
|
|
|
|
/// <summary>
|
|
/// 데스크톱 광고 슬롯 시스템, NTP 스폰서 카드 주입 및 배너 컨트롤 UI TDD 게이트 테스트 (Gate 197 ~ Gate 199)
|
|
/// </summary>
|
|
public class DesktopAdSlotAndNtpSponsoredGateTests
|
|
{
|
|
private static string AppDir => Directory.Exists(Path.Combine(SourceTree.Root, "src", "Paca.App"))
|
|
? Path.Combine(SourceTree.Root, "src", "Paca.App")
|
|
: Path.Combine(SourceTree.Root, "Paca.App");
|
|
|
|
private static string BrowserDir => Directory.Exists(Path.Combine(SourceTree.Root, "src", "Paca.Browser"))
|
|
? Path.Combine(SourceTree.Root, "src", "Paca.Browser")
|
|
: Path.Combine(SourceTree.Root, "Paca.Browser");
|
|
|
|
[Fact]
|
|
public void Gate197_DesktopAdManager_Models_Placement_And_Dismissal_Audit()
|
|
{
|
|
var adManager = new DesktopAdManager();
|
|
|
|
// 1. 새 탭 페이지(NTP) 스폰서 슬롯 조회
|
|
var ntpAd = adManager.GetAdForPlacement(DesktopAdPlacement.NewTabPageSponsoredCard);
|
|
Assert.NotNull(ntpAd);
|
|
Assert.True(ntpAd.Badge == "AD" || ntpAd.Badge == "SPONSORED");
|
|
Assert.False(string.IsNullOrWhiteSpace(ntpAd.Title));
|
|
Assert.False(string.IsNullOrWhiteSpace(ntpAd.Advertiser));
|
|
Assert.False(string.IsNullOrWhiteSpace(ntpAd.TargetUrl));
|
|
Assert.True(ntpAd.IsVisible);
|
|
|
|
// 2. 다운로드 뷰 배너 슬롯 조회
|
|
var dlAd = adManager.GetAdForPlacement(DesktopAdPlacement.DownloadViewBanner);
|
|
Assert.NotNull(dlAd);
|
|
Assert.True(dlAd.IsVisible);
|
|
|
|
// 3. 인상(Impression) 및 클릭(Click) 트래킹
|
|
int initImpressions = adManager.GetImpressionCount(ntpAd.Id);
|
|
int initClicks = adManager.GetClickCount(ntpAd.Id);
|
|
|
|
adManager.TrackImpression(ntpAd.Id);
|
|
Assert.Equal(initImpressions + 1, adManager.GetImpressionCount(ntpAd.Id));
|
|
|
|
adManager.TrackClick(ntpAd.Id);
|
|
Assert.Equal(initClicks + 1, adManager.GetClickCount(ntpAd.Id));
|
|
|
|
// 4. 닫기(Dismiss) 및 가시성 제어
|
|
bool dismissedFired = false;
|
|
adManager.AdDismissed += id =>
|
|
{
|
|
if (id == ntpAd.Id) dismissedFired = true;
|
|
};
|
|
|
|
adManager.DismissAd(ntpAd.Id);
|
|
Assert.True(dismissedFired);
|
|
Assert.False(ntpAd.IsVisible);
|
|
|
|
// 닫힌 후 조회 시 null
|
|
var dismissedCheck = adManager.GetAdForPlacement(DesktopAdPlacement.NewTabPageSponsoredCard);
|
|
Assert.Null(dismissedCheck);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate198_NewTabPageBuilder_SponsoredCard_Markup_And_Accessibility_Audit()
|
|
{
|
|
// 1. NewTabPageBuilder에 스폰서 광고 데이터가 제공될 때 HTML 생성 검증
|
|
var adData = new DesktopAdData
|
|
{
|
|
Id = "test-desktop-sponsor",
|
|
Badge = "SPONSORED",
|
|
Title = "PACA Cloud Pro 10Gbps 가속",
|
|
Description = "초고속 클라우드 다운로드 릴레이 및 8K 인코딩",
|
|
Advertiser = "Paca Media Lab",
|
|
CtaText = "체험하기",
|
|
TargetUrl = "https://paca.cloud/pro",
|
|
IsVisible = true,
|
|
IsDismissible = true
|
|
};
|
|
|
|
var html = NewTabPageBuilder.BuildNtp(
|
|
shortcuts: null,
|
|
settings: null,
|
|
recentHistory: null,
|
|
recentDownloads: null,
|
|
isDark: true,
|
|
sponsoredAd: adData
|
|
);
|
|
|
|
// 2. 스폰서 컨테이너 및 속성 감사
|
|
Assert.Contains("ntp-sponsored-container", html);
|
|
Assert.Contains("data-sponsored-ad=\"true\"", html);
|
|
Assert.Contains("PACA Cloud Pro 10Gbps 가속", html);
|
|
Assert.Contains("Paca Media Lab", html);
|
|
Assert.Contains("https://paca.cloud/pro", html);
|
|
Assert.Contains("SPONSORED", html);
|
|
|
|
// 3. WCAG 2.2 접근성 계약: aria-label 및 dismiss 버튼
|
|
Assert.Contains("aria-label=\"스폰서 추천\"", html);
|
|
Assert.Contains("aria-label=\"광고 닫기\"", html);
|
|
|
|
// 4. 스폰서 광고가 비활성화(null)되었을 때는 렌더링되지 않아야 함
|
|
var cleanHtml = NewTabPageBuilder.BuildNtp(
|
|
shortcuts: null,
|
|
settings: null,
|
|
recentHistory: null,
|
|
recentDownloads: null,
|
|
isDark: true,
|
|
sponsoredAd: null
|
|
);
|
|
Assert.DoesNotContain("data-sponsored-ad=\"true\"", cleanHtml);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate199_DesktopAdBannerControl_Xaml_And_DesignSystem_Audit()
|
|
{
|
|
// 1. WPF DesktopAdBannerControl XAML 및 코드비하인드 파일 존재 확인
|
|
var xamlPath = Path.Combine(AppDir, "Controls", "DesktopAdBannerControl.xaml");
|
|
var csPath = Path.Combine(AppDir, "Controls", "DesktopAdBannerControl.xaml.cs");
|
|
|
|
Assert.True(File.Exists(xamlPath), "DesktopAdBannerControl.xaml 파일이 존재해야 합니다.");
|
|
Assert.True(File.Exists(csPath), "DesktopAdBannerControl.xaml.cs 파일이 존재해야 합니다.");
|
|
|
|
var xaml = File.ReadAllText(xamlPath);
|
|
var cs = File.ReadAllText(csPath);
|
|
|
|
// 2. Designpaca 25대 게이트 감사
|
|
// - 최소 32x32px 클릭 타깃 및 마진 8px 배수
|
|
Assert.Contains("Height=\"32\"", xaml);
|
|
// - AutomationProperties.Name 스크린리더 계약
|
|
Assert.Contains("AutomationProperties.Name", xaml);
|
|
// - 닫기 버튼 및 CTA 버튼
|
|
Assert.Contains("PART_DismissBtn", xaml);
|
|
Assert.Contains("PART_CtaBtn", xaml);
|
|
// - 폰트 및 라운드 코너
|
|
Assert.Contains("CornerRadius", xaml);
|
|
|
|
// 3. 코드비하인드에서 DesktopAdManager 연동 및 트래킹 호출 감사
|
|
Assert.Contains("TrackImpression", cs);
|
|
Assert.Contains("TrackClick", cs);
|
|
Assert.Contains("DismissAd", cs);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate202_DesktopAdConfig_AdMobUnitIds_And_EffectiveTestAd_Audit()
|
|
{
|
|
// 1. AdMob 콘솔에서 생성한 실제 배너 광고 단위 ID 일치성 단정
|
|
var config = DesktopAdConfig.PacaProduction;
|
|
Assert.Equal("ca-app-pub-1039714767792854~5680462775", config.AppId);
|
|
Assert.Equal("ca-app-pub-1039714767792854/1521297311", config.DownloadBannerAdUnitId);
|
|
Assert.Equal("ca-app-pub-1039714767792854/4387520844", config.NtpBannerAdUnitId);
|
|
|
|
// 2. 검토 대기(스토어 미연결) 상태 기본 테스트 모드: Google 공식 실시간 웹 디스플레이 테스트 슬롯 라우팅
|
|
Assert.True(config.ForceTestAds);
|
|
Assert.Equal(DesktopAdConfig.GoogleTestSlot, config.EffectiveDownloadBannerUnitId);
|
|
Assert.Equal(DesktopAdConfig.GoogleTestSlot, config.EffectiveNtpBannerUnitId);
|
|
|
|
// 3. 프로덕션 강제 지정 시 실제 콘솔 광고 단위 ID 반환
|
|
var prodConfig = config with { ForceTestAds = false };
|
|
Assert.Equal("ca-app-pub-1039714767792854/1521297311", prodConfig.EffectiveDownloadBannerUnitId);
|
|
Assert.Equal("ca-app-pub-1039714767792854/4387520844", prodConfig.EffectiveNtpBannerUnitId);
|
|
|
|
// 4. Google Publisher Tag (GPT) 스니펫 생성기 검증
|
|
var snippet = DesktopAdConfig.GenerateGoogleTagSnippet(DesktopAdConfig.GoogleTestSlot, 728, 90, "div-gpt-test");
|
|
Assert.Contains("securepubads.g.doubleclick.net/tag/js/gpt.js", snippet);
|
|
Assert.Contains($"googletag.defineSlot('{DesktopAdConfig.GoogleTestSlot}'", snippet);
|
|
Assert.Contains("[728, 90]", snippet);
|
|
Assert.Contains("googletag.display('div-gpt-test')", snippet);
|
|
|
|
// 5. DesktopAdData 실광고 속성 모델 검증
|
|
var ad = new DesktopAdData
|
|
{
|
|
AdUnitId = "ca-app-pub-1039714767792854/1521297311",
|
|
IsRealAd = true,
|
|
HtmlEmbedSnippet = snippet
|
|
};
|
|
Assert.True(ad.IsRealAd);
|
|
Assert.Equal("ca-app-pub-1039714767792854/1521297311", ad.AdUnitId);
|
|
Assert.NotNull(ad.HtmlEmbedSnippet);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate203_NewTabPageBuilder_RealAdUnit_GooglePublisherTag_Audit()
|
|
{
|
|
// Arrange: 실시간 구글 광고 태그가 포함된 DesktopAdData 구성
|
|
var realAd = new DesktopAdData
|
|
{
|
|
Id = "paca-desktop-ntp-real-ad",
|
|
AdUnitId = "ca-app-pub-1039714767792854/4387520844",
|
|
IsRealAd = true,
|
|
Badge = "AD",
|
|
Title = "Google Sponsor Network",
|
|
Advertiser = "Google Ad Manager",
|
|
HtmlEmbedSnippet = DesktopAdConfig.GenerateGoogleTagSnippet(DesktopAdConfig.GoogleTestSlot, 728, 90, "div-gpt-ad-ntp"),
|
|
IsVisible = true
|
|
};
|
|
|
|
// Act: NewTabPageBuilder HTML 생성
|
|
var html = NewTabPageBuilder.BuildNtp(
|
|
shortcuts: null,
|
|
settings: null,
|
|
recentHistory: null,
|
|
recentDownloads: null,
|
|
isDark: true,
|
|
sponsoredAd: realAd
|
|
);
|
|
|
|
// Assert: Google Publisher Tag 비동기 스크립트 및 실시간 광고 슬롯 컨테이너 마크업 검증
|
|
Assert.Contains("ntp-sponsored-container", html);
|
|
Assert.Contains("ntp-real-ad-container", html);
|
|
Assert.Contains("data-real-ad=\"true\"", html);
|
|
Assert.Contains("securepubads.g.doubleclick.net/tag/js/gpt.js", html);
|
|
Assert.Contains($"googletag.defineSlot('{DesktopAdConfig.GoogleTestSlot}'", html);
|
|
Assert.Contains("googletag.display('div-gpt-ad-ntp')", html);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate204_DesktopAdBannerControl_RealAdHost_And_WebView2_Audit()
|
|
{
|
|
var xamlPath = Path.Combine(AppDir, "Controls", "DesktopAdBannerControl.xaml");
|
|
var csPath = Path.Combine(AppDir, "Controls", "DesktopAdBannerControl.xaml.cs");
|
|
|
|
var xaml = File.ReadAllText(xamlPath);
|
|
var cs = File.ReadAllText(csPath);
|
|
|
|
// 1. 실광고 호스트 및 네이티브 카드 2단 계층 XAML 감사
|
|
Assert.Contains("PART_RealAdHost", xaml);
|
|
Assert.Contains("PART_NativeCard", xaml);
|
|
|
|
// 2. 코드비하인드에서 WebView2 환경 주입 및 실광고 내비게이션 지원 감사
|
|
Assert.Contains("AttachEnvironment", cs);
|
|
Assert.Contains("GenerateGoogleTagSnippet", cs);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate205_DownloadsPanelView_BottomAdBanner_And_RichImageCard_Audit()
|
|
{
|
|
// 1. MainWindow.xaml 다운로드 뷰 하단(Grid.Row=2) 배너 배치 감사
|
|
var mainWindowXamlPath = Path.Combine(AppDir, "MainWindow.xaml");
|
|
Assert.True(File.Exists(mainWindowXamlPath), "MainWindow.xaml 파일이 존재해야 합니다.");
|
|
var mainXaml = File.ReadAllText(mainWindowXamlPath);
|
|
|
|
// DownloadsPanelView 그리드 행 정의에 Row 2(Auto)가 추가되어 있어야 함
|
|
Assert.Contains("<Grid x:Name=\"DownloadsPanelView\"", mainXaml);
|
|
Assert.Contains("x:Name=\"DownloadsAdBanner\" Grid.Row=\"2\"", mainXaml);
|
|
|
|
// 2. DesktopAdBannerControl.xaml 리치 이미지 썸네일 컨트롤 감사
|
|
var adBannerXamlPath = Path.Combine(AppDir, "Controls", "DesktopAdBannerControl.xaml");
|
|
var bannerXaml = File.ReadAllText(adBannerXamlPath);
|
|
Assert.Contains("PART_AdImage", bannerXaml);
|
|
|
|
// 3. DesktopAdBannerControl.xaml.cs 이미지 바인딩 및 표시 로직 감사
|
|
var adBannerCsPath = Path.Combine(AppDir, "Controls", "DesktopAdBannerControl.xaml.cs");
|
|
var bannerCs = File.ReadAllText(adBannerCsPath);
|
|
Assert.Contains("PART_AdImage", bannerCs);
|
|
Assert.Contains("IconOrImageUrl", bannerCs);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate206_DesktopAd_Vibrant_DisplayCreative_And_DirectCapture_Audit()
|
|
{
|
|
var adManager = new DesktopAdManager();
|
|
|
|
// 1. 하단 메인 배너(728x90) 실제 상용 수준 메타데이터 감사
|
|
var bottomAd = adManager.GetAdForPlacement(DesktopAdPlacement.BottomAppBanner);
|
|
Assert.NotNull(bottomAd);
|
|
Assert.True(bottomAd.IsRealAd);
|
|
Assert.False(string.IsNullOrWhiteSpace(bottomAd.Title));
|
|
Assert.False(string.IsNullOrWhiteSpace(bottomAd.Description), "하단 배너 설명문이 비어있어서는 안 됩니다.");
|
|
Assert.False(string.IsNullOrWhiteSpace(bottomAd.Advertiser));
|
|
Assert.False(string.IsNullOrWhiteSpace(bottomAd.IconOrImageUrl), "하단 배너 브랜드/아이콘 이미지가 비어있어서는 안 됩니다.");
|
|
Assert.False(string.IsNullOrWhiteSpace(bottomAd.CtaText));
|
|
|
|
// 2. 다운로드 뷰 배너(300x250) 메타데이터 감사
|
|
var dlAd = adManager.GetAdForPlacement(DesktopAdPlacement.DownloadViewBanner);
|
|
Assert.NotNull(dlAd);
|
|
Assert.True(dlAd.IsRealAd);
|
|
Assert.False(string.IsNullOrWhiteSpace(dlAd.Title));
|
|
Assert.False(string.IsNullOrWhiteSpace(dlAd.Description));
|
|
Assert.False(string.IsNullOrWhiteSpace(dlAd.Advertiser));
|
|
Assert.False(string.IsNullOrWhiteSpace(dlAd.IconOrImageUrl));
|
|
|
|
// 3. DesktopAdBannerControl.xaml.cs 직접 웹뷰 캡처 메서드 감사
|
|
var csPath = Path.Combine(AppDir, "Controls", "DesktopAdBannerControl.xaml.cs");
|
|
var cs = File.ReadAllText(csPath);
|
|
Assert.Contains("CaptureAdPreviewAsync", cs);
|
|
Assert.Contains("CapturePreviewAsync", cs);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate207_DesktopAd_Sizing_Encoding_And_OverlayRobustness_Audit()
|
|
{
|
|
// 1. DesktopAdBannerControl.xaml.cs 규격 및 인코딩, 타이밍 방어 감사
|
|
var adBannerCsPath = Path.Combine(AppDir, "Controls", "DesktopAdBannerControl.xaml.cs");
|
|
var cs = File.ReadAllText(adBannerCsPath);
|
|
|
|
// 728 및 300 명시적 Width 지정
|
|
Assert.Contains("Width = 728", cs);
|
|
Assert.Contains("Width = 300", cs);
|
|
|
|
// 웹뷰 자체 크기 명시적 지정
|
|
Assert.Contains("_webView.Width = adWidth", cs);
|
|
Assert.Contains("_webView.Height = adHeight", cs);
|
|
|
|
// UTF-8 BOM 인코딩 감사
|
|
Assert.Contains("UTF8Encoding(true)", cs);
|
|
|
|
// 캐시 버스팅 감사
|
|
Assert.Contains("v=", cs);
|
|
|
|
// 플렉스박스 붕괴 방지 min-width 및 margin-left: auto 감사
|
|
Assert.Contains("min-width: 320px", cs);
|
|
Assert.Contains("margin-left: auto", cs);
|
|
|
|
// 첫 프레임 페인트 대기 delay 감사
|
|
Assert.Contains("Task.Delay", cs);
|
|
|
|
// 2. AppDebugScreenshotService.cs 파일 락 방지 FileShare.ReadWrite 감사
|
|
var screenshotCsPath = Path.Combine(AppDir, "Services", "AppDebugScreenshotService.cs");
|
|
var screenshotCs = File.ReadAllText(screenshotCsPath);
|
|
Assert.Contains("FileShare.ReadWrite", screenshotCs);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate208_AdPreviewImages_MustContainVibrantRenderedPixels()
|
|
{
|
|
var previewsDir = Path.Combine(AppPaths.DataDir, "ad_previews");
|
|
var bottomFile = Path.Combine(previewsDir, "ad_BottomAppBanner.png");
|
|
var dlFile = Path.Combine(previewsDir, "ad_DownloadViewBanner.png");
|
|
|
|
if (!File.Exists(bottomFile) || !File.Exists(dlFile))
|
|
{
|
|
var canonicalDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Paca", "ad_previews");
|
|
if (File.Exists(Path.Combine(canonicalDir, "ad_BottomAppBanner.png")))
|
|
{
|
|
bottomFile = Path.Combine(canonicalDir, "ad_BottomAppBanner.png");
|
|
dlFile = Path.Combine(canonicalDir, "ad_DownloadViewBanner.png");
|
|
}
|
|
}
|
|
|
|
Assert.True(File.Exists(bottomFile), "Bottom banner preview file must exist");
|
|
Assert.True(File.Exists(dlFile), "Download banner preview file must exist");
|
|
|
|
using var bottomBmp = new System.Drawing.Bitmap(bottomFile);
|
|
using var dlBmp = new System.Drawing.Bitmap(dlFile);
|
|
|
|
// 2. 하단 배너 중앙 영역(x: 150~450)에 텍스트 픽셀(밝은 색상 텍스트)이 존재해야 함
|
|
int textPixels = 0;
|
|
for (int x = 150; x < 450; x++)
|
|
{
|
|
for (int y = 15; y < 75; y++)
|
|
{
|
|
var c = bottomBmp.GetPixel(x, y);
|
|
// 흰색/밝은 텍스트(#F8FAFC, #A5B4FC, #38BDF8) 픽셀 카운트
|
|
if (c.R > 120 && c.G > 120 && c.B > 160)
|
|
{
|
|
textPixels++;
|
|
}
|
|
}
|
|
}
|
|
|
|
Assert.True(textPixels > 50, $"BottomAppBanner must contain visible text pixels in the center content area (Found {textPixels} text pixels)");
|
|
|
|
// 1. 다운로드 배너가 단색 빈 화면(762바이트)이 아니어야 함
|
|
var dlInfo = new FileInfo(dlFile);
|
|
Assert.True(dlInfo.Length > 2000, $"DownloadViewBanner preview must not be a blank solid rectangle (Length was {dlInfo.Length} bytes)");
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate209_GoogleAds_AdsTxt_And_AppAdsTxt_Verification_Audit()
|
|
{
|
|
// 1. site/ads.txt 및 site/app-ads.txt 배포 파일 존재 및 내용 검증
|
|
var siteDir = Path.Combine(SourceTree.Root, "site");
|
|
var adsTxtPath = Path.Combine(siteDir, "ads.txt");
|
|
var appAdsTxtPath = Path.Combine(siteDir, "app-ads.txt");
|
|
|
|
Assert.True(File.Exists(adsTxtPath), "site/ads.txt 파일이 배포 루트에 존재해야 합니다.");
|
|
Assert.True(File.Exists(appAdsTxtPath), "site/app-ads.txt 파일이 배포 루트에 존재해야 합니다.");
|
|
|
|
var adsTxt = File.ReadAllText(adsTxtPath).Trim();
|
|
var appAdsTxt = File.ReadAllText(appAdsTxtPath).Trim();
|
|
|
|
// Google 공식 인증 레코드: google.com, pub-1039714767792854, DIRECT, f08c47fec0942fa0
|
|
const string expectedRecord = "google.com, pub-1039714767792854, DIRECT, f08c47fec0942fa0";
|
|
Assert.Contains(expectedRecord, adsTxt);
|
|
Assert.Contains(expectedRecord, appAdsTxt);
|
|
|
|
// 2. DesktopAdConfig 공식 퍼블리셔 ID 및 클라이언트 ID 단정
|
|
Assert.Equal("pub-1039714767792854", DesktopAdConfig.PublisherId);
|
|
Assert.Equal("ca-pub-1039714767792854", DesktopAdConfig.AdClient);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate210_ZeroMockAdvertising_And_RealGoogleOrigin_Audit()
|
|
{
|
|
// 1. DesktopAdModels.cs에 가짜 모의 광고(쿠팡, 삼성 등)가 완전히 제거되었는지 감사
|
|
var modelsCsPath = Path.Combine(BrowserDir, "Ads", "DesktopAdModels.cs");
|
|
Assert.True(File.Exists(modelsCsPath));
|
|
var modelsCs = File.ReadAllText(modelsCsPath);
|
|
|
|
Assert.DoesNotContain("쿠팡", modelsCs);
|
|
Assert.DoesNotContain("Coupang", modelsCs);
|
|
Assert.DoesNotContain("Galaxy S24", modelsCs);
|
|
Assert.DoesNotContain("삼성전자", modelsCs);
|
|
|
|
// 2. DesktopAdBannerControl.xaml.cs에서 가짜 목업이 아닌 실시간 Google 태그 호스팅 및 가상 HTTPS 오리진 감사
|
|
var bannerCsPath = Path.Combine(AppDir, "Controls", "DesktopAdBannerControl.xaml.cs");
|
|
Assert.True(File.Exists(bannerCsPath));
|
|
var bannerCs = File.ReadAllText(bannerCsPath);
|
|
|
|
Assert.Contains("ads.paca.cloud", bannerCs);
|
|
Assert.Contains("SetVirtualHostNameToFolderMapping", bannerCs);
|
|
Assert.Contains("ad-google-wrapper", bannerCs);
|
|
Assert.DoesNotContain("ad-logo-box", bannerCs);
|
|
Assert.DoesNotContain("ad-logo-fallback", bannerCs);
|
|
|
|
// 3. AdSense 스니펫 생성기 동작 검증
|
|
var adsenseSnippet = DesktopAdConfig.GenerateAdSenseSnippet(DesktopAdConfig.AdClient, "4387520844", 728, 90);
|
|
Assert.Contains("pagead2.googlesyndication.com/pagead/js/adsbygoogle.js", adsenseSnippet);
|
|
Assert.Contains("data-ad-client=\"ca-pub-1039714767792854\"", adsenseSnippet);
|
|
Assert.Contains("data-ad-slot=\"4387520844\"", adsenseSnippet);
|
|
}
|
|
|
|
[Fact]
|
|
public void Gate214_RealGoogleAds_AdSense_ProductionSnippets_And_DomainOrigin_Audit()
|
|
{
|
|
// 1. 프로덕션 모드 DesktopAdManager 생성 시 실제 단위 슬롯이 주입되어야 함
|
|
var prodConfig = DesktopAdConfig.PacaProduction with { ForceTestAds = false };
|
|
var adManager = new DesktopAdManager(prodConfig);
|
|
|
|
var bottomAd = adManager.GetAdForPlacement(DesktopAdPlacement.BottomAppBanner);
|
|
Assert.NotNull(bottomAd);
|
|
Assert.True(bottomAd.IsRealAd);
|
|
Assert.Equal("ca-app-pub-1039714767792854/4387520844", bottomAd.AdUnitId);
|
|
Assert.NotNull(bottomAd.HtmlEmbedSnippet);
|
|
Assert.Contains("ca-pub-1039714767792854", bottomAd.HtmlEmbedSnippet);
|
|
Assert.Contains("4387520844", bottomAd.HtmlEmbedSnippet);
|
|
Assert.Contains("pagead2.googlesyndication.com/pagead/js/adsbygoogle.js", bottomAd.HtmlEmbedSnippet);
|
|
|
|
var dlAd = adManager.GetAdForPlacement(DesktopAdPlacement.DownloadViewBanner);
|
|
Assert.NotNull(dlAd);
|
|
Assert.True(dlAd.IsRealAd);
|
|
Assert.Equal("ca-app-pub-1039714767792854/1521297311", dlAd.AdUnitId);
|
|
Assert.NotNull(dlAd.HtmlEmbedSnippet);
|
|
Assert.Contains("ca-pub-1039714767792854", dlAd.HtmlEmbedSnippet);
|
|
Assert.Contains("1521297311", dlAd.HtmlEmbedSnippet);
|
|
Assert.Contains("pagead2.googlesyndication.com/pagead/js/adsbygoogle.js", dlAd.HtmlEmbedSnippet);
|
|
|
|
// 2. DesktopAdConfig.GenerateProductionAdSnippet 검증
|
|
var snippet = DesktopAdConfig.GenerateProductionAdSnippet("ca-app-pub-1039714767792854/4387520844", 728, 90, "div-bottom");
|
|
Assert.Contains("ca-pub-1039714767792854", snippet);
|
|
Assert.Contains("data-ad-slot=\"4387520844\"", snippet);
|
|
Assert.Contains("data-ad-status", snippet);
|
|
|
|
// 3. DesktopAdBannerControl.xaml.cs에서 실 도메인 mediabrowser.chanpaca.net 및 크롬 User-Agent 감사
|
|
var bannerCsPath = Path.Combine(AppDir, "Controls", "DesktopAdBannerControl.xaml.cs");
|
|
var bannerCs = File.ReadAllText(bannerCsPath);
|
|
Assert.Contains("mediabrowser.chanpaca.net", bannerCs);
|
|
Assert.Contains("Chrome/130", bannerCs);
|
|
Assert.Contains("Settings.UserAgent", bannerCs);
|
|
}
|
|
}
|
|
|
|
|