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
140
tests/Paca.Tests/Core/DirectDownloaderIntegrityGateTests.cs
Normal file
140
tests/Paca.Tests/Core/DirectDownloaderIntegrityGateTests.cs
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Paca.Core.Config;
|
||||
using Paca.Core.Hls;
|
||||
using Xunit;
|
||||
|
||||
namespace Paca.Tests.Core;
|
||||
|
||||
public class DirectDownloaderIntegrityGateTests : IDisposable
|
||||
{
|
||||
private readonly string _testDir;
|
||||
|
||||
public DirectDownloaderIntegrityGateTests()
|
||||
{
|
||||
_testDir = Path.Combine(Path.GetTempPath(), "paca_direct_test_" + Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(_testDir);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
try { if (Directory.Exists(_testDir)) Directory.Delete(_testDir, recursive: true); }
|
||||
catch { }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Gate471_DirectDownloader_GuessExt_DetectsExtensions_FromUrlAndMediaType()
|
||||
{
|
||||
// Direct extension in URL
|
||||
Assert.Equal(".mp4", DirectDownloader.GuessExt("", new Uri("https://paca.stream/video.mp4")));
|
||||
Assert.Equal(".webm", DirectDownloader.GuessExt("", new Uri("https://paca.stream/clip.webm")));
|
||||
Assert.Equal(".mkv", DirectDownloader.GuessExt("", new Uri("https://paca.stream/movie.mkv")));
|
||||
Assert.Equal(".mp3", DirectDownloader.GuessExt("", new Uri("https://paca.stream/audio.mp3")));
|
||||
Assert.Equal(".flac", DirectDownloader.GuessExt("", new Uri("https://paca.stream/track.flac")));
|
||||
|
||||
// Fallback from MIME ContentType
|
||||
Assert.Equal(".mp4", DirectDownloader.GuessExt("video/mp4", new Uri("https://paca.stream/stream?id=123")));
|
||||
Assert.Equal(".webm", DirectDownloader.GuessExt("video/webm", new Uri("https://paca.stream/stream?id=123")));
|
||||
Assert.Equal(".m4a", DirectDownloader.GuessExt("audio/mp4", new Uri("https://paca.stream/stream?id=123")));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Gate472_DirectDownloader_VerifiesChecksum_WhenProvided_AndMatches()
|
||||
{
|
||||
var content = "PACA-DIRECT-DOWNLOAD-VERIFIED-DATA";
|
||||
var bytes = Encoding.UTF8.GetBytes(content);
|
||||
var sha256 = Convert.ToHexString(SHA256.HashData(bytes)).ToLowerInvariant();
|
||||
|
||||
var handler = new SimpleMockHttp(bytes);
|
||||
using var client = new HttpClient(handler);
|
||||
var config = new AppConfig();
|
||||
var downloader = new DirectDownloader(client, config);
|
||||
|
||||
var outputBase = Path.Combine(_testDir, "test_success");
|
||||
var resultPath = await downloader.DownloadAsync(
|
||||
new Uri("https://paca.test/file.mp4"),
|
||||
outputBase,
|
||||
progress: null,
|
||||
ct: CancellationToken.None,
|
||||
expectedSha256: sha256
|
||||
);
|
||||
|
||||
Assert.True(File.Exists(resultPath));
|
||||
Assert.Equal(bytes.Length, new FileInfo(resultPath).Length);
|
||||
Assert.False(File.Exists(outputBase + ".part"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Gate473_DirectDownloader_ThrowsInvalidDataException_WhenChecksumMismatches()
|
||||
{
|
||||
var content = "TAMPERED-DATA-PAYLOAD";
|
||||
var bytes = Encoding.UTF8.GetBytes(content);
|
||||
|
||||
var handler = new SimpleMockHttp(bytes);
|
||||
using var client = new HttpClient(handler);
|
||||
var config = new AppConfig();
|
||||
var downloader = new DirectDownloader(client, config);
|
||||
|
||||
var outputBase = Path.Combine(_testDir, "test_corrupt");
|
||||
|
||||
await Assert.ThrowsAsync<InvalidDataException>(async () =>
|
||||
{
|
||||
await downloader.DownloadAsync(
|
||||
new Uri("https://paca.test/file.mp4"),
|
||||
outputBase,
|
||||
progress: null,
|
||||
ct: CancellationToken.None,
|
||||
expectedSha256: "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Gate474_DirectDownloader_AtomicPartCommit_CleansPartFile_UponCompletion()
|
||||
{
|
||||
var content = "PACA-ATOMIC-COMMIT-STREAM";
|
||||
var bytes = Encoding.UTF8.GetBytes(content);
|
||||
|
||||
var handler = new SimpleMockHttp(bytes);
|
||||
using var client = new HttpClient(handler);
|
||||
var config = new AppConfig();
|
||||
var downloader = new DirectDownloader(client, config);
|
||||
|
||||
var outputBase = Path.Combine(_testDir, "atomic_output");
|
||||
var final = await downloader.DownloadAsync(
|
||||
new Uri("https://paca.test/video.mp4"),
|
||||
outputBase
|
||||
);
|
||||
|
||||
Assert.True(File.Exists(final));
|
||||
Assert.False(File.Exists(outputBase + ".part"));
|
||||
}
|
||||
|
||||
private sealed class SimpleMockHttp : HttpMessageHandler
|
||||
{
|
||||
private readonly byte[] _data;
|
||||
|
||||
public SimpleMockHttp(byte[] data)
|
||||
{
|
||||
_data = data;
|
||||
}
|
||||
|
||||
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
var response = new HttpResponseMessage(HttpStatusCode.OK)
|
||||
{
|
||||
Content = new ByteArrayContent(_data)
|
||||
};
|
||||
response.Content.Headers.ContentType = new MediaTypeHeaderValue("video/mp4");
|
||||
response.Content.Headers.ContentLength = _data.Length;
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue