From 64cea557bebdb0a5b4f17362d8e66845b0a77113 Mon Sep 17 00:00:00 2001 From: Michael Manfre Date: Mon, 5 Dec 2022 23:44:26 -0500 Subject: [PATCH] Collapse linkify mentions (#123) --- activities/models/post.py | 6 +++++ tests/activities/models/test_post.py | 35 +++++++++++++++++++----- tests/conftest.py | 40 ++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 6 deletions(-) diff --git a/activities/models/post.py b/activities/models/post.py index daf61e8..55d2f65 100644 --- a/activities/models/post.py +++ b/activities/models/post.py @@ -276,6 +276,8 @@ class Post(StatorModel): possible_matches[mention.username] = url possible_matches[f"{mention.username}@{mention.domain_id}"] = url + collapse_name: dict[str, str] = {} + def replacer(match): precursor = match.group(1) handle = match.group(2).lower() @@ -284,6 +286,10 @@ class Post(StatorModel): else: short_handle = handle if handle in possible_matches: + if short_handle not in collapse_name: + collapse_name[short_handle] = handle + elif collapse_name.get(short_handle) != handle: + short_handle = handle return f'{precursor}@{short_handle}' else: return match.group() diff --git a/tests/activities/models/test_post.py b/tests/activities/models/test_post.py index 06d26ed..a5f2f79 100644 --- a/tests/activities/models/test_post.py +++ b/tests/activities/models/test_post.py @@ -32,7 +32,9 @@ def test_fetch_post(httpx_mock: HTTPXMock, config_system): @pytest.mark.django_db -def test_linkify_mentions_remote(identity, remote_identity): +def test_linkify_mentions_remote( + identity, identity2, remote_identity, remote_identity2 +): """ Tests that we can linkify post mentions properly for remote use """ @@ -77,9 +79,28 @@ def test_linkify_mentions_remote(identity, remote_identity): == '

Hey @test

' ) + # Test that collapsing only applies to the first unique, short username + post = Post.objects.create( + content="

Hey @TeSt@remote.test and @test@remote2.test

", + author=identity, + local=True, + ) + post.mentions.set([remote_identity, remote_identity2]) + assert post.safe_content_remote() == ( + '

Hey @test ' + 'and @test@remote2.test

' + ) + + post.content = "

Hey @TeSt, @Test@remote.test and @test

" + assert post.safe_content_remote() == ( + '

Hey @test, ' + '@test@remote.test ' + 'and @test

' + ) + @pytest.mark.django_db -def test_linkify_mentions_local(identity, remote_identity): +def test_linkify_mentions_local(identity, identity2, remote_identity): """ Tests that we can linkify post mentions properly for local use """ @@ -96,14 +117,16 @@ def test_linkify_mentions_local(identity, remote_identity): ) # Test a full username (local) post = Post.objects.create( - content="

@test@example.com, welcome!

", + content="

@test@example.com, welcome! @test@example2.com @test@example.com

", author=identity, local=True, ) post.mentions.add(identity) - assert ( - post.safe_content_local() - == '

@test, welcome!

' + post.mentions.add(identity2) + assert post.safe_content_local() == ( + '

@test, welcome!' + ' @test@example2.com' + ' @test

' ) # Test a full username (remote) with no

post = Post.objects.create( diff --git a/tests/conftest.py b/tests/conftest.py index 80622f0..c67717c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -78,6 +78,12 @@ def domain() -> Domain: return Domain.objects.create(domain="example.com", local=True, public=True) +@pytest.fixture +@pytest.mark.django_db +def domain2() -> Domain: + return Domain.objects.create(domain="example2.com", local=True, public=True) + + @pytest.fixture @pytest.mark.django_db def identity(user, domain) -> Identity: @@ -95,6 +101,23 @@ def identity(user, domain) -> Identity: return identity +@pytest.fixture +@pytest.mark.django_db +def identity2(user, domain2) -> Identity: + """ + Creates a basic test identity with a user and domain. + """ + identity = Identity.objects.create( + actor_uri="https://example2.com/@test@example2.com/", + username="test", + domain=domain2, + name="Test User Domain2", + local=True, + ) + identity.users.set([user]) + return identity + + @pytest.fixture def other_identity(user, domain) -> Identity: """ @@ -128,6 +151,23 @@ def remote_identity() -> Identity: ) +@pytest.fixture +@pytest.mark.django_db +def remote_identity2() -> Identity: + """ + Creates a basic remote test identity with a domain. + """ + domain = Domain.objects.create(domain="remote2.test", local=False) + return Identity.objects.create( + actor_uri="https://remote2.test/test-actor/", + profile_uri="https://remote2.test/@test/", + username="test", + domain=domain, + name="Test2 Remote User", + local=False, + ) + + @pytest.fixture def stator(config_system) -> StatorRunner: """