- PersonaSeparationTests: two personas of one login follow the same account and post; nothing the API maps for one contains the other's id, username or the root id. - tools/smoke/mastodon-api.sh checks what a client meets first (instance v1/v2, discovery, app registration, client credentials, an app token refused by a user endpoint, the public timeline, revocation) and, given a persona token, verify_credentials, home and notifications. deploy.yml runs it after every deploy. - OAuthPruner removes invalid tokens and authorizations older than two weeks, every six hours. - The consent page no longer sets form-action, which browsers apply to the redirect back to the client after the form is posted. CLAUDE.md gains the Mastodon API layout and invariants. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012CzABvBkbcFqoHdmi8b9WB
62 lines
2.4 KiB
C#
62 lines
2.4 KiB
C#
using Microsoft.Extensions.Caching.Memory;
|
|
|
|
using MongoDB.Entities;
|
|
|
|
using PrivaPub.Api.Mastodon.Infrastructure;
|
|
using PrivaPub.Api.Mastodon.Mappers;
|
|
using PrivaPub.ClientModels.Post;
|
|
using PrivaPub.ClientModels.Social;
|
|
using PrivaPub.Models.Post;
|
|
using PrivaPub.Tests.Support;
|
|
|
|
using System.Text.Json;
|
|
|
|
namespace PrivaPub.Tests.Api
|
|
{
|
|
[Trait("Category", "Integration")]
|
|
public sealed class PersonaSeparationTests : 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 Nothing_shown_to_one_persona_names_its_sibling()
|
|
{
|
|
var token = TestContext.Current.CancellationToken;
|
|
var (root, alice) = await _harness.Persona("alice");
|
|
var (_, sibling) = await _harness.Persona("sibling", root);
|
|
var (bobRoot, bob) = await _harness.Persona("bob");
|
|
await _harness.Follows.Follow(root, new FollowForm { AvatarId = alice.Id, Target = bob.UserName }, token);
|
|
await _harness.Follows.Follow(root, new FollowForm { AvatarId = sibling.Id, Target = bob.UserName }, token);
|
|
await _harness.Posts.InsertPost(root, new InsertPostForm { AvatarId = sibling.Id, Text = "from the sibling" }, token);
|
|
await _harness.Posts.InsertPost(root, new InsertPostForm { AvatarId = alice.Id, Text = "from alice" }, token);
|
|
await _harness.Posts.InsertPost(bobRoot, new InsertPostForm { AvatarId = bob.Id, Text = "hi both" }, token);
|
|
|
|
var mapper = new MastodonMapper(_harness.Db, _harness.Local);
|
|
var account = await mapper.Local(alice, withSource: true, token);
|
|
var homeIds = (await _harness.Db.TimelineEntries.Match(e => e.AvatarId == alice.Id).ExecuteAsync(token)).Select(e => e.PostId).ToList();
|
|
var home = await _harness.Db.Posts.Match(p => homeIds.Contains(p.ID)).ExecuteAsync(token);
|
|
var statuses = await mapper.Statuses(home, alice.Id, token);
|
|
var bobAsSeenByAlice = await mapper.Account(bob.Id, token);
|
|
|
|
var json = JsonSerializer.Serialize(new object[] { account, statuses, bobAsSeenByAlice }, MastodonJson.Options);
|
|
Assert.DoesNotContain(sibling.Id, json);
|
|
Assert.DoesNotContain(sibling.UserName, json);
|
|
Assert.DoesNotContain(root, json);
|
|
Assert.Contains("hi both", json);
|
|
Assert.Equal(2, bobAsSeenByAlice.FollowersCount);
|
|
}
|
|
}
|
|
}
|