using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Caching.Memory; using MongoDB.Entities; using NetVips; using PrivaPub.Domain.Media; using PrivaPub.Domain.Statuses; using PrivaPub.Federation.Rendering; using PrivaPub.Models.Media; using PrivaPub.Tests.Support; namespace PrivaPub.Tests.Domain { [Trait("Category", "Integration")] public sealed class MediaFlowTests : IAsyncLifetime { Harness _harness; public async ValueTask InitializeAsync() { Assert.SkipUnless(MongoFixture.Enabled, MongoFixture.Skip); _harness = await Harness.Start(); } public async ValueTask DisposeAsync() { if (_harness != default) await _harness.DisposeAsync(); } static byte[] Png(int width, int height) { using var image = (Image.Black(width, height, bands: 3) + new double[] { 10, 120, 200 }).Cast(Enums.BandFormat.Uchar); return image.WriteToBuffer(".png"); } static IFormFile Upload(byte[] bytes, string contentType) => new FormFile(new MemoryStream(bytes), 0, bytes.Length, "file", "picture") { Headers = new HeaderDictionary(), ContentType = contentType }; [Fact] public async Task An_upload_is_attached_once_and_federated_with_its_alt_text() { var token = TestContext.Current.CancellationToken; var (_, alice) = await _harness.Persona("alice"); var (_, mallory) = await _harness.Persona("mallory"); var upload = await _harness.Media.Upload(alice, Upload(Png(300, 200), "image/png"), "a blue square", "0.25,-0.5", token); Assert.True(upload.Ok); Assert.True(File.Exists(Path.Combine(_harness.Media.Root, upload.Attachment.FilePath))); var stolen = await _harness.Statuses.Publish(mallory, new StatusDraft { Text = "mine", MediaIds = new[] { upload.Attachment.ID } }, token); Assert.False(stolen.Ok); var posted = await _harness.Statuses.Publish(alice, new StatusDraft { Text = "look", MediaIds = new[] { upload.Attachment.ID } }, token); Assert.True(posted.Ok); Assert.Equal(posted.Post.ID, (await DB.Default.Find().OneAsync(upload.Attachment.ID, token)).PostId); var note = ActivityPubRenderer.Note(posted.Post, alice, default, default); var attachment = note["attachment"]![0]!; Assert.Equal("a blue square", attachment["name"]!.GetValue()); Assert.Equal(300, attachment["width"]!.GetValue()); Assert.Equal(-0.5f, attachment["focalPoint"]![1]!.GetValue()); Assert.StartsWith("https://privapub.test/media/files/", attachment["url"]!.GetValue()); var reused = await _harness.Statuses.Publish(alice, new StatusDraft { Text = "again", MediaIds = new[] { upload.Attachment.ID } }, token); Assert.False(reused.Ok); } [Fact] public async Task Unsupported_files_are_refused() { var (_, alice) = await _harness.Persona("alice"); var outcome = await _harness.Media.Upload(alice, Upload(System.Text.Encoding.UTF8.GetBytes(""), "image/svg+xml"), default, default, TestContext.Current.CancellationToken); Assert.False(outcome.Ok); Assert.Equal(422, outcome.Status); } [Fact] public async Task The_proxy_serves_signed_remote_media_and_nothing_else() { var token = TestContext.Current.CancellationToken; var path = $"/files/{Guid.NewGuid():N}.png"; _harness.Peer.ServeFile(path, Png(10, 10), "image/png"); var proxy = new MediaProxy(_harness.Local, Peer.Http(), _harness.Media, new StaticOptions(new MediaOptions())); var wrapped = proxy.Wrap(_harness.Peer.A + path); var parts = new Uri(wrapped).AbsolutePath.Split('/'); var (file, contentType) = await proxy.Fetch(parts[3], parts[4], token); var (tampered, _) = await proxy.Fetch(parts[3].Replace(parts[3][0], parts[3][0] == 'A' ? 'B' : 'A'), parts[4], token); Assert.StartsWith("https://privapub.test/media/proxy/", wrapped); Assert.Equal("image/png", contentType); Assert.True(File.Exists(file)); Assert.StartsWith(_harness.Media.ProxyRoot, file); Assert.Null(tampered); } } }