diff options
Diffstat (limited to 'spec/lib')
-rw-r--r-- | spec/lib/activitypub/activity/accept_spec.rb | 5 | ||||
-rw-r--r-- | spec/lib/activitypub/activity/add_spec.rb | 42 | ||||
-rw-r--r-- | spec/lib/activitypub/activity/update_spec.rb | 110 | ||||
-rw-r--r-- | spec/lib/activitypub/tag_manager_spec.rb | 8 | ||||
-rw-r--r-- | spec/lib/link_details_extractor_spec.rb | 29 | ||||
-rw-r--r-- | spec/lib/permalink_redirector_spec.rb | 42 | ||||
-rw-r--r-- | spec/lib/proof_provider/keybase/verifier_spec.rb | 82 | ||||
-rw-r--r-- | spec/lib/status_reach_finder_spec.rb | 109 |
8 files changed, 311 insertions, 116 deletions
diff --git a/spec/lib/activitypub/activity/accept_spec.rb b/spec/lib/activitypub/activity/accept_spec.rb index 883bab6ac..304cf2208 100644 --- a/spec/lib/activitypub/activity/accept_spec.rb +++ b/spec/lib/activitypub/activity/accept_spec.rb @@ -23,6 +23,7 @@ RSpec.describe ActivityPub::Activity::Accept do subject { described_class.new(json, sender) } before do + allow(RemoteAccountRefreshWorker).to receive(:perform_async) Fabricate(:follow_request, account: recipient, target_account: sender) subject.perform end @@ -34,6 +35,10 @@ RSpec.describe ActivityPub::Activity::Accept do it 'removes the follow request' do expect(recipient.requested?(sender)).to be false end + + it 'queues a refresh' do + expect(RemoteAccountRefreshWorker).to have_received(:perform_async).with(sender.id) + end end context 'given a relay' do diff --git a/spec/lib/activitypub/activity/add_spec.rb b/spec/lib/activitypub/activity/add_spec.rb index 16db71c88..e6408b610 100644 --- a/spec/lib/activitypub/activity/add_spec.rb +++ b/spec/lib/activitypub/activity/add_spec.rb @@ -1,8 +1,8 @@ require 'rails_helper' RSpec.describe ActivityPub::Activity::Add do - let(:sender) { Fabricate(:account, featured_collection_url: 'https://example.com/featured') } - let(:status) { Fabricate(:status, account: sender) } + let(:sender) { Fabricate(:account, featured_collection_url: 'https://example.com/featured', domain: 'example.com') } + let(:status) { Fabricate(:status, account: sender, visibility: :private) } let(:json) do { @@ -24,6 +24,8 @@ RSpec.describe ActivityPub::Activity::Add do end context 'when status was not known before' do + let(:service_stub) { double } + let(:json) do { '@context': 'https://www.w3.org/ns/activitystreams', @@ -36,12 +38,40 @@ RSpec.describe ActivityPub::Activity::Add do end before do - stub_request(:get, 'https://example.com/unknown').to_return(status: 410) + allow(ActivityPub::FetchRemoteStatusService).to receive(:new).and_return(service_stub) + end + + context 'when there is a local follower' do + before do + account = Fabricate(:account) + account.follow!(sender) + end + + it 'fetches the status and pins it' do + allow(service_stub).to receive(:call) do |uri, id: true, on_behalf_of: nil| + expect(uri).to eq 'https://example.com/unknown' + expect(id).to eq true + expect(on_behalf_of&.following?(sender)).to eq true + status + end + subject.perform + expect(service_stub).to have_received(:call) + expect(sender.pinned?(status)).to be true + end end - it 'fetches the status' do - subject.perform - expect(a_request(:get, 'https://example.com/unknown')).to have_been_made.at_least_once + context 'when there is no local follower' do + it 'tries to fetch the status' do + allow(service_stub).to receive(:call) do |uri, id: true, on_behalf_of: nil| + expect(uri).to eq 'https://example.com/unknown' + expect(id).to eq true + expect(on_behalf_of).to eq nil + nil + end + subject.perform + expect(service_stub).to have_received(:call) + expect(sender.pinned?(status)).to be false + end end end end diff --git a/spec/lib/activitypub/activity/update_spec.rb b/spec/lib/activitypub/activity/update_spec.rb index 1c9bcf43b..4cd853af2 100644 --- a/spec/lib/activitypub/activity/update_spec.rb +++ b/spec/lib/activitypub/activity/update_spec.rb @@ -4,43 +4,97 @@ RSpec.describe ActivityPub::Activity::Update do let!(:sender) { Fabricate(:account) } before do - stub_request(:get, actor_json[:outbox]).to_return(status: 404) - stub_request(:get, actor_json[:followers]).to_return(status: 404) - stub_request(:get, actor_json[:following]).to_return(status: 404) - stub_request(:get, actor_json[:featured]).to_return(status: 404) - sender.update!(uri: ActivityPub::TagManager.instance.uri_for(sender)) end - let(:modified_sender) do - sender.tap do |modified_sender| - modified_sender.display_name = 'Totally modified now' - end - end + subject { described_class.new(json, sender) } - let(:actor_json) do - ActiveModelSerializers::SerializableResource.new(modified_sender, serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter).as_json - end + describe '#perform' do + context 'with an Actor object' do + let(:modified_sender) do + sender.tap do |modified_sender| + modified_sender.display_name = 'Totally modified now' + end + end - let(:json) do - { - '@context': 'https://www.w3.org/ns/activitystreams', - id: 'foo', - type: 'Update', - actor: ActivityPub::TagManager.instance.uri_for(sender), - object: actor_json, - }.with_indifferent_access - end + let(:actor_json) do + ActiveModelSerializers::SerializableResource.new(modified_sender, serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter).as_json + end - describe '#perform' do - subject { described_class.new(json, sender) } + let(:json) do + { + '@context': 'https://www.w3.org/ns/activitystreams', + id: 'foo', + type: 'Update', + actor: ActivityPub::TagManager.instance.uri_for(sender), + object: actor_json, + }.with_indifferent_access + end - before do - subject.perform + before do + stub_request(:get, actor_json[:outbox]).to_return(status: 404) + stub_request(:get, actor_json[:followers]).to_return(status: 404) + stub_request(:get, actor_json[:following]).to_return(status: 404) + stub_request(:get, actor_json[:featured]).to_return(status: 404) + + subject.perform + end + + it 'updates profile' do + expect(sender.reload.display_name).to eq 'Totally modified now' + end end - it 'updates profile' do - expect(sender.reload.display_name).to eq 'Totally modified now' + context 'with a Question object' do + let!(:at_time) { Time.now.utc } + let!(:status) { Fabricate(:status, account: sender, poll: Poll.new(account: sender, options: %w(Bar Baz), cached_tallies: [0, 0], expires_at: at_time + 5.days)) } + + let(:json) do + { + '@context': 'https://www.w3.org/ns/activitystreams', + id: 'foo', + type: 'Update', + actor: ActivityPub::TagManager.instance.uri_for(sender), + object: { + type: 'Question', + id: ActivityPub::TagManager.instance.uri_for(status), + content: 'Foo', + endTime: (at_time + 5.days).iso8601, + oneOf: [ + { + type: 'Note', + name: 'Bar', + replies: { + type: 'Collection', + totalItems: 0, + }, + }, + + { + type: 'Note', + name: 'Baz', + replies: { + type: 'Collection', + totalItems: 12, + }, + }, + ], + }, + }.with_indifferent_access + end + + before do + status.update!(uri: ActivityPub::TagManager.instance.uri_for(status)) + subject.perform + end + + it 'updates poll numbers' do + expect(status.preloadable_poll.cached_tallies).to eq [0, 12] + end + + it 'does not set status as edited' do + expect(status.edited_at).to be_nil + end end end end diff --git a/spec/lib/activitypub/tag_manager_spec.rb b/spec/lib/activitypub/tag_manager_spec.rb index 1c5c6f0ed..606a1de2e 100644 --- a/spec/lib/activitypub/tag_manager_spec.rb +++ b/spec/lib/activitypub/tag_manager_spec.rb @@ -42,6 +42,14 @@ RSpec.describe ActivityPub::TagManager do expect(subject.to(status)).to eq [subject.uri_for(mentioned)] end + it "returns URIs of mentioned group's followers for direct statuses to groups" do + status = Fabricate(:status, visibility: :direct) + mentioned = Fabricate(:account, domain: 'remote.org', uri: 'https://remote.org/group', followers_url: 'https://remote.org/group/followers', actor_type: 'Group') + status.mentions.create(account: mentioned) + expect(subject.to(status)).to include(subject.uri_for(mentioned)) + expect(subject.to(status)).to include(subject.followers_uri_for(mentioned)) + end + it "returns URIs of mentions for direct silenced author's status only if they are followers or requesting to be" do bob = Fabricate(:account, username: 'bob') alice = Fabricate(:account, username: 'alice') diff --git a/spec/lib/link_details_extractor_spec.rb b/spec/lib/link_details_extractor_spec.rb new file mode 100644 index 000000000..850857b2d --- /dev/null +++ b/spec/lib/link_details_extractor_spec.rb @@ -0,0 +1,29 @@ +require 'rails_helper' + +RSpec.describe LinkDetailsExtractor do + let(:original_url) { '' } + let(:html) { '' } + let(:html_charset) { nil } + + subject { described_class.new(original_url, html, html_charset) } + + describe '#canonical_url' do + let(:original_url) { 'https://foo.com/article?bar=baz123' } + + context 'when canonical URL points to another host' do + let(:html) { '<!doctype html><link rel="canonical" href="https://bar.com/different-article" />' } + + it 'ignores the canonical URLs' do + expect(subject.canonical_url).to eq original_url + end + end + + context 'when canonical URL points to the same host' do + let(:html) { '<!doctype html><link rel="canonical" href="https://foo.com/article" />' } + + it 'ignores the canonical URLs' do + expect(subject.canonical_url).to eq 'https://foo.com/article' + end + end + end +end diff --git a/spec/lib/permalink_redirector_spec.rb b/spec/lib/permalink_redirector_spec.rb new file mode 100644 index 000000000..b916b33b2 --- /dev/null +++ b/spec/lib/permalink_redirector_spec.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe PermalinkRedirector do + describe '#redirect_url' do + before do + account = Fabricate(:account, username: 'alice', id: 1) + Fabricate(:status, account: account, id: 123) + end + + it 'returns path for legacy account links' do + redirector = described_class.new('web/accounts/1') + expect(redirector.redirect_path).to eq 'https://cb6e6126.ngrok.io/@alice' + end + + it 'returns path for legacy status links' do + redirector = described_class.new('web/statuses/123') + expect(redirector.redirect_path).to eq 'https://cb6e6126.ngrok.io/@alice/123' + end + + it 'returns path for legacy tag links' do + redirector = described_class.new('web/timelines/tag/hoge') + expect(redirector.redirect_path).to eq '/tags/hoge' + end + + it 'returns path for pretty account links' do + redirector = described_class.new('web/@alice') + expect(redirector.redirect_path).to eq 'https://cb6e6126.ngrok.io/@alice' + end + + it 'returns path for pretty status links' do + redirector = described_class.new('web/@alice/123') + expect(redirector.redirect_path).to eq 'https://cb6e6126.ngrok.io/@alice/123' + end + + it 'returns path for pretty tag links' do + redirector = described_class.new('web/tags/hoge') + expect(redirector.redirect_path).to eq '/tags/hoge' + end + end +end diff --git a/spec/lib/proof_provider/keybase/verifier_spec.rb b/spec/lib/proof_provider/keybase/verifier_spec.rb deleted file mode 100644 index 0081a735d..000000000 --- a/spec/lib/proof_provider/keybase/verifier_spec.rb +++ /dev/null @@ -1,82 +0,0 @@ -require 'rails_helper' - -describe ProofProvider::Keybase::Verifier do - let(:my_domain) { Rails.configuration.x.local_domain } - - let(:keybase_proof) do - local_proof = AccountIdentityProof.new( - provider: 'Keybase', - provider_username: 'cryptoalice', - token: '11111111111111111111111111' - ) - - described_class.new('alice', 'cryptoalice', '11111111111111111111111111', my_domain) - end - - let(:query_params) do - "domain=#{my_domain}&kb_username=cryptoalice&sig_hash=11111111111111111111111111&username=alice" - end - - describe '#valid?' do - let(:base_url) { 'https://keybase.io/_/api/1.0/sig/proof_valid.json' } - - context 'when valid' do - before do - json_response_body = '{"status":{"code":0,"name":"OK"},"proof_valid":true}' - stub_request(:get, "#{base_url}?#{query_params}").to_return(status: 200, body: json_response_body) - end - - it 'calls out to keybase and returns true' do - expect(keybase_proof.valid?).to eq true - end - end - - context 'when invalid' do - before do - json_response_body = '{"status":{"code":0,"name":"OK"},"proof_valid":false}' - stub_request(:get, "#{base_url}?#{query_params}").to_return(status: 200, body: json_response_body) - end - - it 'calls out to keybase and returns false' do - expect(keybase_proof.valid?).to eq false - end - end - - context 'with an unexpected api response' do - before do - json_response_body = '{"status":{"code":100,"desc":"wrong size hex_id","fields":{"sig_hash":"wrong size hex_id"},"name":"INPUT_ERROR"}}' - stub_request(:get, "#{base_url}?#{query_params}").to_return(status: 200, body: json_response_body) - end - - it 'swallows the error and returns false' do - expect(keybase_proof.valid?).to eq false - end - end - end - - describe '#status' do - let(:base_url) { 'https://keybase.io/_/api/1.0/sig/proof_live.json' } - - context 'with a normal response' do - before do - json_response_body = '{"status":{"code":0,"name":"OK"},"proof_live":false,"proof_valid":true}' - stub_request(:get, "#{base_url}?#{query_params}").to_return(status: 200, body: json_response_body) - end - - it 'calls out to keybase and returns the status fields as proof_valid and proof_live' do - expect(keybase_proof.status).to include({ 'proof_valid' => true, 'proof_live' => false }) - end - end - - context 'with an unexpected keybase response' do - before do - json_response_body = '{"status":{"code":100,"desc":"missing non-optional field sig_hash","fields":{"sig_hash":"missing non-optional field sig_hash"},"name":"INPUT_ERROR"}}' - stub_request(:get, "#{base_url}?#{query_params}").to_return(status: 200, body: json_response_body) - end - - it 'raises a ProofProvider::Keybase::UnexpectedResponseError' do - expect { keybase_proof.status }.to raise_error ProofProvider::Keybase::UnexpectedResponseError - end - end - end -end diff --git a/spec/lib/status_reach_finder_spec.rb b/spec/lib/status_reach_finder_spec.rb new file mode 100644 index 000000000..f0c22b165 --- /dev/null +++ b/spec/lib/status_reach_finder_spec.rb @@ -0,0 +1,109 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe StatusReachFinder do + describe '#inboxes' do + context 'for a local status' do + let(:parent_status) { nil } + let(:visibility) { :public } + let(:alice) { Fabricate(:account, username: 'alice') } + let(:status) { Fabricate(:status, account: alice, thread: parent_status, visibility: visibility) } + + subject { described_class.new(status) } + + context 'when it contains mentions of remote accounts' do + let(:bob) { Fabricate(:account, username: 'bob', domain: 'foo.bar', protocol: :activitypub, inbox_url: 'https://foo.bar/inbox') } + + before do + status.mentions.create!(account: bob) + end + + it 'includes the inbox of the mentioned account' do + expect(subject.inboxes).to include 'https://foo.bar/inbox' + end + end + + context 'when it has been reblogged by a remote account' do + let(:bob) { Fabricate(:account, username: 'bob', domain: 'foo.bar', protocol: :activitypub, inbox_url: 'https://foo.bar/inbox') } + + before do + bob.statuses.create!(reblog: status) + end + + it 'includes the inbox of the reblogger' do + expect(subject.inboxes).to include 'https://foo.bar/inbox' + end + + context 'when status is not public' do + let(:visibility) { :private } + + it 'does not include the inbox of the reblogger' do + expect(subject.inboxes).to_not include 'https://foo.bar/inbox' + end + end + end + + context 'when it has been favourited by a remote account' do + let(:bob) { Fabricate(:account, username: 'bob', domain: 'foo.bar', protocol: :activitypub, inbox_url: 'https://foo.bar/inbox') } + + before do + bob.favourites.create!(status: status) + end + + it 'includes the inbox of the favouriter' do + expect(subject.inboxes).to include 'https://foo.bar/inbox' + end + + context 'when status is not public' do + let(:visibility) { :private } + + it 'does not include the inbox of the favouriter' do + expect(subject.inboxes).to_not include 'https://foo.bar/inbox' + end + end + end + + context 'when it has been replied to by a remote account' do + let(:bob) { Fabricate(:account, username: 'bob', domain: 'foo.bar', protocol: :activitypub, inbox_url: 'https://foo.bar/inbox') } + + before do + bob.statuses.create!(thread: status, text: 'Hoge') + end + + context do + it 'includes the inbox of the replier' do + expect(subject.inboxes).to include 'https://foo.bar/inbox' + end + end + + context 'when status is not public' do + let(:visibility) { :private } + + it 'does not include the inbox of the replier' do + expect(subject.inboxes).to_not include 'https://foo.bar/inbox' + end + end + end + + context 'when it is a reply to a remote account' do + let(:bob) { Fabricate(:account, username: 'bob', domain: 'foo.bar', protocol: :activitypub, inbox_url: 'https://foo.bar/inbox') } + let(:parent_status) { Fabricate(:status, account: bob) } + + context do + it 'includes the inbox of the replied-to account' do + expect(subject.inboxes).to include 'https://foo.bar/inbox' + end + end + + context 'when status is not public and replied-to account is not mentioned' do + let(:visibility) { :private } + + it 'does not include the inbox of the replied-to account' do + expect(subject.inboxes).to_not include 'https://foo.bar/inbox' + end + end + end + end + end +end |