Located posts: local only, rounded, matched against their own radius
A post given a latitude and longitude becomes LocalGeo: its position is rounded to two decimals (about a kilometre) and stored as a 2dsphere point, its radius clamped to 1-50 km, and it is local only - no Create, no delivery, no outbox, never in the Mastodon API or on a profile page. /clientapi/post/nearby takes the viewer's position for the query only (it is neither stored nor logged), runs $geoNear and keeps posts within each post's own radius, minus authors the viewer blocks or mutes. A located post cannot be direct or in a group. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012CzABvBkbcFqoHdmi8b9WB
This commit is contained in:
1 parent
28b581b6b1
commit
c00865d805
8 files changed
+150
-3
No files matched your search
@@ -0,0 +1,77 @@
|
||||
using MongoDB.Entities;
|
||||
|
||||
using PrivaPub.ClientModels.Post;
|
||||
using PrivaPub.Domain.Privacy;
|
||||
using PrivaPub.Federation.Outbox;
|
||||
using PrivaPub.Models.Jobs;
|
||||
using PrivaPub.Models.Post;
|
||||
using PrivaPub.Tests.Support;
|
||||
|
||||
using System.Text.Json;
|
||||
|
||||
namespace PrivaPub.Tests.Domain
|
||||
{
|
||||
[Trait("Category", "Integration")]
|
||||
public sealed class NearbyTests : 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();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task A_located_post_reaches_only_those_within_its_radius_and_never_leaves_the_server()
|
||||
{
|
||||
var token = TestContext.Current.CancellationToken;
|
||||
var latitude = 10 + Random.Shared.NextDouble() * 50;
|
||||
var longitude = 10 + Random.Shared.NextDouble() * 50;
|
||||
var (aliceRoot, alice) = await _harness.Persona("alice");
|
||||
var (bobRoot, bob) = await _harness.Persona("bob");
|
||||
var (carolRoot, carol) = await _harness.Persona("carol");
|
||||
var follower = new RemoteActor(_harness.Peer, "follower");
|
||||
await _harness.FollowedBy(alice, follower);
|
||||
|
||||
var created = await _harness.Posts.InsertPost(aliceRoot, new InsertPostForm
|
||||
{
|
||||
AvatarId = alice.Id, Text = "free bread at the bakery", Latitude = latitude, Longitude = longitude, RangeKm = 5
|
||||
}, token);
|
||||
var post = await DB.Default.Find<Post>().OneAsync(((ViewPost)created.Data).Id, token);
|
||||
|
||||
var three = (List<ViewPost>)(await _harness.Posts.Nearby(bobRoot, bob.Id, latitude + 0.027, longitude, token)).Data;
|
||||
var eight = (List<ViewPost>)(await _harness.Posts.Nearby(carolRoot, carol.Id, latitude + 0.072, longitude, token)).Data;
|
||||
|
||||
Assert.Equal(PostVisibility.LocalGeo, post.Visibility);
|
||||
Assert.Equal(Math.Round(latitude, 2), post.Geo.Coordinates[1]);
|
||||
Assert.Contains(three, p => p.Id == post.ID);
|
||||
Assert.InRange(three.First(p => p.Id == post.ID).DistanceKm.Value, 2.0, 4.0);
|
||||
Assert.DoesNotContain(eight, p => p.Id == post.ID);
|
||||
Assert.False(await VisibilityPolicy.CanSee(post, bob.Id, token));
|
||||
var outgoing = (await DB.Default.Find<Job>().Match(j => j.Kind == JobKind.Deliver).ExecuteAsync(token))
|
||||
.Select(j => JsonSerializer.Deserialize<DeliveryPayload>(j.Payload))
|
||||
.Where(p => p.Body.Contains(post.ID));
|
||||
Assert.Empty(outgoing);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task A_located_post_cannot_be_direct_or_in_a_group()
|
||||
{
|
||||
var (root, alice) = await _harness.Persona("alice");
|
||||
|
||||
var result = await _harness.Statuses.Publish(alice, new PrivaPub.Domain.Statuses.StatusDraft
|
||||
{
|
||||
Text = "x", Latitude = 45, Longitude = 7, Visibility = PostVisibility.Direct
|
||||
}, TestContext.Current.CancellationToken);
|
||||
|
||||
Assert.False(result.Ok);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user