releases/tests/Paca.Tests/Mobile/MobileSmartClipboardSnifferGateTests.cs

124 lines
5.1 KiB
C#

using System;
using System.Threading.Tasks;
using Paca.Mobile.Core;
using Xunit;
namespace Paca.Tests.Mobile;
/// <summary>
/// Gate 143 ~ 146: 모바일 스마트 클립보드 스니퍼 & 원클릭 Send-to-PC 플로팅 시트 기계적 검증
/// </summary>
public class MobileSmartClipboardSnifferGateTests
{
private class MockClipboardReader : IClipboardReader
{
private string? _clipboardText;
public void SetText(string? text) => _clipboardText = text;
public Task<string?> GetTextAsync() => Task.FromResult(_clipboardText);
}
[Theory]
[InlineData("https://youtu.be/dQw4w9WgXcQ", true, "YouTube")]
[InlineData("https://www.youtube.com/shorts/abcdef123", true, "YouTube Shorts")]
[InlineData("https://www.tiktok.com/@user/video/7123456789", true, "TikTok")]
[InlineData("https://x.com/user/status/1234567890?s=20", true, "X (Twitter)")]
[InlineData("https://twitter.com/user/status/1234567890", true, "X (Twitter)")]
[InlineData("https://www.instagram.com/reel/C123456/", true, "Instagram Reel")]
[InlineData("https://chzzk.naver.com/video/98765", true, "CHZZK")]
[InlineData("https://vod.sooplive.co.kr/player/112233", true, "SOOP")]
[InlineData("https://stream.example.com/live/playlist.m3u8", true, "Direct HLS Stream")]
[InlineData("https://cdn.example.com/videos/bunny.mp4?token=abc", true, "Direct Video File")]
[InlineData("안녕하세요 일반 텍스트 메모입니다.", false, "None")]
[InlineData("https://www.google.com/search?q=csharp", false, "None")]
[InlineData("", false, "None")]
[InlineData(null, false, "None")]
public void Gate143_SmartClipboard_Detects_Supported_Video_Platforms(string? inputUrl, bool expectedIsVideo, string expectedPlatform)
{
var result = SmartClipboardSniffer.AnalyzeUrl(inputUrl);
Assert.Equal(expectedIsVideo, result.IsVideoUrl);
Assert.Equal(expectedPlatform, result.DetectedPlatform);
if (expectedIsVideo)
{
Assert.False(string.IsNullOrWhiteSpace(result.NormalizedUrl));
}
}
[Fact]
public async Task Gate144_SmartClipboard_Deduplication_And_Cooldown()
{
var mockClipboard = new MockClipboardReader();
var sniffer = new SmartClipboardSniffer(mockClipboard, cacheTtl: TimeSpan.FromMinutes(5));
// 1. 첫 복사 시도 -> 감지 성공
mockClipboard.SetText("https://youtu.be/dQw4w9WgXcQ?si=tracking123");
var firstResult = await sniffer.CheckClipboardAsync();
Assert.NotNull(firstResult);
Assert.True(firstResult.IsVideoUrl);
// 2. 동일한 클립보드 상태로 즉시 재검사 -> 캐시/중복 방지로 null 반환 (불필요한 팝업 방지)
var secondResult = await sniffer.CheckClipboardAsync();
Assert.Null(secondResult);
// 3. 새로운 비디오 URL로 클립보드 변경 -> 감지 성공
mockClipboard.SetText("https://www.tiktok.com/@creator/video/998877");
var thirdResult = await sniffer.CheckClipboardAsync();
Assert.NotNull(thirdResult);
Assert.True(thirdResult.IsVideoUrl);
Assert.Equal("TikTok", thirdResult.DetectedPlatform);
// 4. 소비(Dismiss/Sent) 처리 후 재요청 시 다시 차단 유지 확인
sniffer.DismissCurrent();
var dismissedResult = await sniffer.CheckClipboardAsync();
Assert.Null(dismissedResult);
}
[Fact]
public void Gate145_SmartClipboard_Url_Normalization()
{
var dirtyUrl = "https://youtu.be/dQw4w9WgXcQ?si=junkTrackingToken&utm_source=app&fbclid=XYZ123";
var result = SmartClipboardSniffer.AnalyzeUrl(dirtyUrl);
Assert.True(result.IsVideoUrl);
// 트래킹 파라미터는 정규화되어 제거됨
Assert.DoesNotContain("si=junkTrackingToken", result.NormalizedUrl);
Assert.DoesNotContain("utm_source=app", result.NormalizedUrl);
Assert.DoesNotContain("fbclid=XYZ123", result.NormalizedUrl);
Assert.Equal("https://youtu.be/dQw4w9WgXcQ", result.NormalizedUrl);
}
[Fact]
public async Task Gate146_SmartClipboard_Banner_Action_Dispatches_To_Active_Connection()
{
var mockClipboard = new MockClipboardReader();
var sniffer = new SmartClipboardSniffer(mockClipboard);
mockClipboard.SetText("https://x.com/elonmusk/status/987654321");
var result = await sniffer.CheckClipboardAsync();
Assert.NotNull(result);
bool sentToPcCalled = false;
string? sentUrl = null;
string? sentTarget = null;
var bannerAction = new SmartClipboardBannerAction(
Result: result,
SendToPcAction: async (url, targetDevice) =>
{
sentToPcCalled = true;
sentUrl = url;
sentTarget = targetDevice;
await Task.Yield();
return true;
}
);
var success = await bannerAction.ExecuteSendToPcAsync("My-Desktop-PC");
Assert.True(success);
Assert.True(sentToPcCalled);
Assert.Equal(result.NormalizedUrl, sentUrl);
Assert.Equal("My-Desktop-PC", sentTarget);
}
}