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:
parent
a88aafa1e5
commit
0e568f1f0a
975 changed files with 130593 additions and 16783 deletions
107
tests/Paca.Tests/Platform/AvaloniaAndMobileDiscoveryGateTests.cs
Normal file
107
tests/Paca.Tests/Platform/AvaloniaAndMobileDiscoveryGateTests.cs
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Paca.Avalonia;
|
||||
using Paca.Mobile.Core;
|
||||
using Xunit;
|
||||
|
||||
namespace Paca.Tests.Platform;
|
||||
|
||||
public class AvaloniaAndMobileDiscoveryGateTests
|
||||
{
|
||||
private static string SolutionRoot
|
||||
{
|
||||
get
|
||||
{
|
||||
var dir = AppContext.BaseDirectory;
|
||||
while (!string.IsNullOrEmpty(dir) && !File.Exists(Path.Combine(dir, "Paca.slnx")) && !File.Exists(Path.Combine(dir, "Paca.sln")))
|
||||
{
|
||||
var parent = Directory.GetParent(dir);
|
||||
if (parent == null) break;
|
||||
dir = parent.FullName;
|
||||
}
|
||||
return dir ?? AppContext.BaseDirectory;
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Gate 1: Avalonia MainViewModel CTS 리소스 누수 방지 검증
|
||||
// =========================================================================
|
||||
[Fact]
|
||||
public async Task Gate_Avalonia_MainViewModel_Cts_Is_Disposed_And_Nulled_After_Execution()
|
||||
{
|
||||
var vm = new MainViewModel();
|
||||
vm.Url = "https://example.com/not-supported"; // 즉시 실패/완료 유도
|
||||
await vm.RunDownloadAsync();
|
||||
|
||||
Assert.False(vm.IsBusy);
|
||||
Assert.False(vm.HasActiveCts, "다운로드 완료 또는 실패 후 CTS는 Dispose되고 null 처리되어야 합니다.");
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Gate 2: Avalonia MainViewModel Log 컬렉션 슬라이딩 윈도우 상한(200개) 검증
|
||||
// =========================================================================
|
||||
[Fact]
|
||||
public void Gate_Avalonia_MainViewModel_Log_Sliding_Window_Limit()
|
||||
{
|
||||
var vm = new MainViewModel();
|
||||
for (int i = 0; i < 300; i++)
|
||||
{
|
||||
vm.AddLogEntry($"Log item {i}");
|
||||
}
|
||||
|
||||
Assert.True(vm.Log.Count <= 200, $"Log 항목 수는 최대 200개 이하로 캡핑되어야 합니다. 현재: {vm.Log.Count}");
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Gate 3: Avalonia MainWindow.axaml 바인딩 무결성 (CompileBindings 및 WPF 잔재 제거)
|
||||
// =========================================================================
|
||||
[Fact]
|
||||
public void Gate_Avalonia_MainWindow_Axaml_Clean_Bindings()
|
||||
{
|
||||
var axamlPath = Path.Combine(SolutionRoot, "src", "Paca.Avalonia", "MainWindow.axaml");
|
||||
Assert.True(File.Exists(axamlPath), $"MainWindow.axaml must exist at {axamlPath}");
|
||||
|
||||
var content = File.ReadAllText(axamlPath);
|
||||
Assert.DoesNotContain("x:CompileBindings=\"False\"", content);
|
||||
Assert.DoesNotContain("UpdateSourceTrigger=PropertyChanged", content);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Gate 4: LanDiscoveryMessage 디바이스명에 파이프(|) 포함 시 파싱 무결성 검증
|
||||
// =========================================================================
|
||||
[Fact]
|
||||
public void Gate_LanDiscoveryMessage_Parses_DeviceName_With_Pipes()
|
||||
{
|
||||
var devNameWithPipes = "My|Studio|PC";
|
||||
var endpoint = "192.168.1.100:5000";
|
||||
var rawPayload = $"PACA_DISCOVERY|PONG|{devNameWithPipes}|{endpoint}|1|abc123fingerprint";
|
||||
|
||||
var parsed = LanDiscoveryMessage.Parse(rawPayload);
|
||||
Assert.NotNull(parsed);
|
||||
Assert.Equal(LanDiscoveryMessageType.Pong, parsed.Type);
|
||||
Assert.Equal(devNameWithPipes, parsed.DeviceName);
|
||||
Assert.Equal(endpoint, parsed.Endpoint);
|
||||
Assert.True(parsed.IsPairingAllowed);
|
||||
Assert.Equal("abc123fingerprint", parsed.Fingerprint);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Gate 5: LanDiscoveryProber IPv6 엔드포인트 브래킷 포맷팅 검증
|
||||
// =========================================================================
|
||||
[Fact]
|
||||
public void Gate_LanDiscoveryProber_Formats_IPv6_Endpoint_With_Brackets()
|
||||
{
|
||||
using var prober = new LanDiscoveryProber();
|
||||
DiscoveredLanDevice? foundDevice = null;
|
||||
prober.OnDeviceFound += d => foundDevice = d;
|
||||
|
||||
// 원격 송신자가 0.0.0.0:5000을 보냈고, 송신자 IP가 IPv6인 경우
|
||||
var ipv6Sender = "fe80::1";
|
||||
prober.HandleReceivedPayload("PACA_DISCOVERY|PONG|TestNode|0.0.0.0:5000|1|fp", ipv6Sender);
|
||||
|
||||
Assert.NotNull(foundDevice);
|
||||
Assert.Contains("[fe80::1]:5000", foundDevice.Endpoint);
|
||||
Assert.Equal("http://[fe80::1]:5000", foundDevice.HttpBaseUrl);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue