A Note's `location` that is a Place with coordinates (Pixelfed sends
them as strings) is kept in the new Post.Place and shown as
Status.privapub.place {name, latitude, longitude, country}; an edit
replaces it. An Event's location stays its own `places`, and nothing
renders a place back out. Closes the INTEROP P2 Pixelfed location gap
and the ROADMAP long-tail item.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
107 lines
3.6 KiB
C#
107 lines
3.6 KiB
C#
using MongoDB.Entities;
|
|
|
|
using PrivaPub.Models.Post;
|
|
using PrivaPub.Tests.Support;
|
|
using PrivaPub.Tests.Support.Host;
|
|
|
|
using System.Text.Json.Nodes;
|
|
|
|
namespace PrivaPub.Tests.Http
|
|
{
|
|
// A Pixelfed post's own place: stored from `location`, shown as `privapub.place`, changed by an edit.
|
|
[Trait("Category", "Integration")]
|
|
public sealed class PostPlaceTests : IAsyncLifetime
|
|
{
|
|
PrivaPubHost _host;
|
|
Peer _peer;
|
|
|
|
public async ValueTask InitializeAsync()
|
|
{
|
|
Assert.SkipUnless(MongoFixture.Enabled, MongoFixture.Skip);
|
|
_host = await PrivaPubHost.Shared();
|
|
_peer = await Peer.Start();
|
|
}
|
|
|
|
public async ValueTask DisposeAsync()
|
|
{
|
|
if (_peer != default)
|
|
await _peer.DisposeAsync();
|
|
}
|
|
|
|
static CancellationToken Token => TestContext.Current.CancellationToken;
|
|
|
|
static JsonObject Location(string name, string latitude, string longitude) => new()
|
|
{
|
|
["type"] = "Place", ["name"] = name, ["latitude"] = latitude, ["longitude"] = longitude, ["country"] = "Italy"
|
|
};
|
|
|
|
[Fact]
|
|
public async Task A_pixelfed_place_is_shown_and_follows_the_authors_edits()
|
|
{
|
|
var alice = await _host.Mastodon("alice");
|
|
var ann = new RemoteActor(_peer, "ann");
|
|
var post = await _host.PublicPostFrom(ann, alice, "<p>the duomo</p>", note => note["location"] = Location("Milan", "45.4642", "9.19"));
|
|
|
|
var place = (await alice.Client.Get($"/api/v1/statuses/{post.ID}")).Ok().Body["privapub"]!["place"];
|
|
|
|
Assert.Equal(("Milan", "Italy"), (place.Text("name"), place.Text("country")));
|
|
Assert.Equal(45.4642, place["latitude"]!.GetValue<double>());
|
|
Assert.Equal(9.19, place["longitude"]!.GetValue<double>());
|
|
|
|
await Edit(ann, alice, post, note => note["location"] = Location("Turin", "45.0703", "7.6869"));
|
|
Assert.Equal("Turin", (await alice.Client.Get($"/api/v1/statuses/{post.ID}")).Ok().Body["privapub"]!["place"].Text("name"));
|
|
|
|
await Edit(ann, alice, post, _ => { });
|
|
Assert.Null((await alice.Client.Get($"/api/v1/statuses/{post.ID}")).Ok().Body["privapub"]!["place"]);
|
|
Assert.Null((await DB.Default.Find<Post>().MatchID(post.ID).ExecuteFirstAsync(Token)).Place);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task A_post_without_a_place_has_none()
|
|
{
|
|
var alice = await _host.Mastodon("alice");
|
|
var post = await _host.PublicPostFrom(new RemoteActor(_peer, "plain"), alice);
|
|
|
|
Assert.Null((await alice.Client.Get($"/api/v1/statuses/{post.ID}")).Ok().Body["privapub"]!["place"]);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task A_local_post_never_carries_a_location()
|
|
{
|
|
var alice = await _host.Mastodon("alice");
|
|
var status = await alice.Status("made here");
|
|
|
|
using var client = _host.Client();
|
|
client.DefaultRequestHeaders.Accept.ParseAdd("application/activity+json");
|
|
var note = JsonNode.Parse(await client.GetStringAsync(status.Text("uri"), Token))!;
|
|
|
|
Assert.Null(note["location"]);
|
|
}
|
|
|
|
async Task Edit(RemoteActor author, Mastodon mentioned, Post post, Action<JsonObject> shape)
|
|
{
|
|
var note = new JsonObject
|
|
{
|
|
["id"] = post.ObjectURI,
|
|
["type"] = "Note",
|
|
["attributedTo"] = author.Id,
|
|
["to"] = new JsonArray(MastodonHelpers.Public),
|
|
["cc"] = new JsonArray(mentioned.Uri),
|
|
["content"] = "<p>the duomo, again</p>",
|
|
["published"] = post.CreationDate.ToString("O"),
|
|
["updated"] = DateTime.UtcNow.ToString("O"),
|
|
["tag"] = new JsonArray(new JsonObject { ["type"] = "Mention", ["href"] = mentioned.Uri, ["name"] = "@" + mentioned.UserName })
|
|
};
|
|
shape(note);
|
|
await _host.Deliver(author, mentioned.Mouth, new JsonObject
|
|
{
|
|
["id"] = $"{post.ObjectURI}/updates/{Guid.NewGuid():N}",
|
|
["type"] = "Update",
|
|
["actor"] = author.Id,
|
|
["to"] = new JsonArray(MastodonHelpers.Public),
|
|
["object"] = note
|
|
});
|
|
}
|
|
}
|
|
}
|