feat(ads/seo/release): v1.2.0 with live Google Ads, isolated ad profile, ads.txt, and SEO/AEO/GEO optimization

This commit is contained in:
Yun Chan 2026-09-14 00:39:44 +09:00
parent a88aafa1e5
commit 0e568f1f0a
975 changed files with 130593 additions and 16783 deletions

View file

@ -0,0 +1,114 @@
using Paca.App.ViewModels;
using Paca.Core.Detection;
using Paca.Tests.Infrastructure;
using Xunit;
namespace Paca.Tests.Browser;
/// <summary>
/// 19차 4대 핵심 비즈니스 시나리오 파이프라인 검증 테스트.
/// </summary>
public class AppScenarioPipelineTests
{
[Fact]
public void 시나리오1_미디어감지_이벤트발화_및_중복필터링_파이프라인()
{
using var pipeline = AppScenarioPipeline.Create();
// 1. HLS 미디어 감지
pipeline.SimulateMediaDetection("https://example.com/live/playlist.m3u8", MediaKind.Hls);
pipeline.AssertQueueCount(1);
pipeline.AssertDetectedCount(1);
// 2. DASH 미디어 감지
pipeline.SimulateMediaDetection("https://example.com/video/manifest.mpd", MediaKind.Dash);
pipeline.AssertQueueCount(2);
pipeline.AssertDetectedCount(2);
// 3. 동일 HLS 미디어 중복 감지 시도 -> 큐 및 감지 이벤트가 중복되지 않아야 함
pipeline.SimulateMediaDetection("https://example.com/live/playlist.m3u8", MediaKind.Hls);
pipeline.AssertQueueCount(2);
pipeline.AssertDetectedCount(2);
}
[Fact]
public void 시나리오2_재생목록_생명주기_및_순환루프_파이프라인()
{
using var pipeline = AppScenarioPipeline.Create();
// 1. 트랙 3개 추가
pipeline.AddPlaylistTrack("https://test.local/t1.mp4", "Track 1", "Direct")
.AddPlaylistTrack("https://test.local/t2.mp4", "Track 2", "Direct")
.AddPlaylistTrack("https://test.local/t3.mp4", "Track 3", "Direct");
pipeline.AssertPlaylistCount(3);
pipeline.AssertCurrentTrack(t => t != null && t.Title == "Track 1");
// 2. 첫 번째 트랙 재생 완료 마킹 후 다음 트랙 이동
var t1 = pipeline.Playlist.Current!;
pipeline.SimulateTrackPlayed(t1);
Assert.True(pipeline.Playlist.Tracks[0].Played);
pipeline.SimulateNextTrack();
pipeline.AssertCurrentTrack(t => t != null && t.Title == "Track 2");
// 3. 마지막 트랙까지 이동 후 순환 루프(all 모드) 검증
pipeline.SimulateNextTrack();
pipeline.AssertCurrentTrack(t => t != null && t.Title == "Track 3");
pipeline.SimulateNextTrack();
pipeline.AssertCurrentTrack(t => t != null && t.Title == "Track 1");
}
[Fact]
public void 시나리오3_탭_세션_직렬화_및_콜드_복원_파이프라인()
{
using var pipeline = AppScenarioPipeline.Create();
// 1. 탭 3개 오픈
pipeline.OpenTab("https://site1.com")
.OpenTab("https://site2.com")
.OpenTab("https://site3.com");
Assert.Equal(3, pipeline.TabManager.Tabs.Count);
// 2. 세션 스냅샷 저장
var snapshot = pipeline.SaveSession();
Assert.NotNull(snapshot);
Assert.Equal(3, snapshot.Urls.Count);
// 3. 새로운 세션에 복원
using var restorePipeline = AppScenarioPipeline.Create();
restorePipeline.RestoreSession(snapshot);
Assert.Equal(3, restorePipeline.TabManager.Tabs.Count);
Assert.Equal("https://site1.com", restorePipeline.TabManager.Tabs[0].Url);
Assert.Equal("https://site2.com", restorePipeline.TabManager.Tabs[1].Url);
Assert.Equal("https://site3.com", restorePipeline.TabManager.Tabs[2].Url);
}
[Fact]
public void 시나리오4_다운로드_오류_발생_및_재시도_상태복구_파이프라인()
{
using var pipeline = AppScenarioPipeline.Create();
var item = new DownloadItemViewModel("Fail-Retry Test")
{
MediaUrl = new Uri("https://example.com/video.mp4"),
Kind = MediaKind.Direct,
Status = "실패",
Error = "Network connection timeout"
};
pipeline.DownloadManager.Items.Add(item);
Assert.Equal("실패", item.Status);
Assert.False(string.IsNullOrEmpty(item.Error));
// 재시도 요청
_ = pipeline.DownloadManager.RetryDownload(item);
// 에러가 초기화되고 재시도 카운트가 올라가야 함
Assert.Null(item.Error);
Assert.Equal(1, item.RetryCount);
}
}