diff options
author | Reverite <github@reverite.sh> | 2019-03-10 19:19:52 -0700 |
---|---|---|
committer | Reverite <github@reverite.sh> | 2019-03-10 19:19:52 -0700 |
commit | 229e2726cad36066afb210d7087a40cd9f955483 (patch) | |
tree | a14d55e9469f1f339fc40b777827aa4886bb7889 /spec | |
parent | 715c552fe4c1b2d59bf1f281d77b6e2546bdb531 (diff) | |
parent | 3cef04610cd809c7bd01adc00d34fb3d25261a16 (diff) |
Merge branch 'glitch'
Diffstat (limited to 'spec')
-rw-r--r-- | spec/controllers/api/v1/filters_controller_spec.rb (renamed from spec/controllers/api/v1/filter_controller_spec.rb) | 0 | ||||
-rw-r--r-- | spec/controllers/api/v1/polls/votes_controller_spec.rb | 34 | ||||
-rw-r--r-- | spec/controllers/api/v1/polls_controller_spec.rb | 23 | ||||
-rw-r--r-- | spec/fabricators/poll_fabricator.rb | 8 | ||||
-rw-r--r-- | spec/fabricators/poll_vote_fabricator.rb | 5 | ||||
-rw-r--r-- | spec/lib/activitypub/activity/create_spec.rb | 99 | ||||
-rw-r--r-- | spec/models/poll_spec.rb | 5 | ||||
-rw-r--r-- | spec/models/poll_vote_spec.rb | 5 | ||||
-rw-r--r-- | spec/serializers/activitypub/note_spec.rb | 44 | ||||
-rw-r--r-- | spec/services/activitypub/fetch_replies_service_spec.rb | 122 | ||||
-rw-r--r-- | spec/services/suspend_account_service_spec.rb | 44 | ||||
-rw-r--r-- | spec/workers/activitypub/fetch_replies_worker_spec.rb | 40 |
12 files changed, 427 insertions, 2 deletions
diff --git a/spec/controllers/api/v1/filter_controller_spec.rb b/spec/controllers/api/v1/filters_controller_spec.rb index 5948809e3..5948809e3 100644 --- a/spec/controllers/api/v1/filter_controller_spec.rb +++ b/spec/controllers/api/v1/filters_controller_spec.rb diff --git a/spec/controllers/api/v1/polls/votes_controller_spec.rb b/spec/controllers/api/v1/polls/votes_controller_spec.rb new file mode 100644 index 000000000..0ee3aa040 --- /dev/null +++ b/spec/controllers/api/v1/polls/votes_controller_spec.rb @@ -0,0 +1,34 @@ +require 'rails_helper' + +RSpec.describe Api::V1::Polls::VotesController, type: :controller do + render_views + + let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) } + let(:scopes) { 'write:statuses' } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + + before { allow(controller).to receive(:doorkeeper_token) { token } } + + describe 'POST #create' do + let(:poll) { Fabricate(:poll) } + + before do + post :create, params: { poll_id: poll.id, choices: %w(1) } + end + + it 'returns http success' do + expect(response).to have_http_status(200) + end + + it 'creates a vote' do + vote = poll.votes.where(account: user.account).first + + expect(vote).to_not be_nil + expect(vote.choice).to eq 1 + end + + it 'updates poll tallies' do + expect(poll.reload.cached_tallies).to eq [0, 1] + end + end +end diff --git a/spec/controllers/api/v1/polls_controller_spec.rb b/spec/controllers/api/v1/polls_controller_spec.rb new file mode 100644 index 000000000..2b8d5f3ef --- /dev/null +++ b/spec/controllers/api/v1/polls_controller_spec.rb @@ -0,0 +1,23 @@ +require 'rails_helper' + +RSpec.describe Api::V1::PollsController, type: :controller do + render_views + + let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) } + let(:scopes) { 'read:statuses' } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } + + before { allow(controller).to receive(:doorkeeper_token) { token } } + + describe 'GET #show' do + let(:poll) { Fabricate(:poll) } + + before do + get :show, params: { id: poll.id } + end + + it 'returns http success' do + expect(response).to have_http_status(200) + end + end +end diff --git a/spec/fabricators/poll_fabricator.rb b/spec/fabricators/poll_fabricator.rb new file mode 100644 index 000000000..746610f7c --- /dev/null +++ b/spec/fabricators/poll_fabricator.rb @@ -0,0 +1,8 @@ +Fabricator(:poll) do + account + status + expires_at { 7.days.from_now } + options %w(Foo Bar) + multiple false + hide_totals false +end diff --git a/spec/fabricators/poll_vote_fabricator.rb b/spec/fabricators/poll_vote_fabricator.rb new file mode 100644 index 000000000..51f9b006e --- /dev/null +++ b/spec/fabricators/poll_vote_fabricator.rb @@ -0,0 +1,5 @@ +Fabricator(:poll_vote) do + account + poll + choice 0 +end diff --git a/spec/lib/activitypub/activity/create_spec.rb b/spec/lib/activitypub/activity/create_spec.rb index 26cb84871..3a1463d95 100644 --- a/spec/lib/activitypub/activity/create_spec.rb +++ b/spec/lib/activitypub/activity/create_spec.rb @@ -1,7 +1,7 @@ require 'rails_helper' RSpec.describe ActivityPub::Activity::Create do - let(:sender) { Fabricate(:account, followers_url: 'http://example.com/followers') } + let(:sender) { Fabricate(:account, followers_url: 'http://example.com/followers', domain: 'example.com', uri: 'https://example.com/actor') } let(:json) do { @@ -28,6 +28,20 @@ RSpec.describe ActivityPub::Activity::Create do subject.perform end + context 'unknown object type' do + let(:object_json) do + { + id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, + type: 'Banana', + content: 'Lorem ipsum', + } + end + + it 'does not create a status' do + expect(sender.statuses.count).to be_zero + end + end + context 'standalone' do let(:object_json) do { @@ -407,6 +421,89 @@ RSpec.describe ActivityPub::Activity::Create do expect(status).to_not be_nil end end + + context 'with poll' do + let(:object_json) do + { + id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, + type: 'Question', + content: 'Which color was the submarine?', + oneOf: [ + { + name: 'Yellow', + replies: { + type: 'Collection', + totalItems: 10, + }, + }, + { + name: 'Blue', + replies: { + type: 'Collection', + totalItems: 3, + } + }, + ], + } + end + + it 'creates status' do + status = sender.statuses.first + expect(status).to_not be_nil + expect(status.poll).to_not be_nil + end + + it 'creates a poll' do + poll = sender.polls.first + expect(poll).to_not be_nil + expect(poll.status).to_not be_nil + expect(poll.options).to eq %w(Yellow Blue) + expect(poll.cached_tallies).to eq [10, 3] + end + end + + context 'when a vote to a local poll' do + let(:poll) { Fabricate(:poll, options: %w(Yellow Blue)) } + let!(:local_status) { Fabricate(:status, owned_poll: poll) } + + let(:object_json) do + { + id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, + type: 'Note', + name: 'Yellow', + inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status) + } + end + + it 'adds a vote to the poll with correct uri' do + vote = poll.votes.first + expect(vote).to_not be_nil + expect(vote.uri).to eq object_json[:id] + expect(poll.reload.cached_tallies).to eq [1, 0] + end + end + + context 'when a vote to an expired local poll' do + let(:poll) do + poll = Fabricate.build(:poll, options: %w(Yellow Blue), expires_at: 1.day.ago) + poll.save(validate: false) + poll + end + let!(:local_status) { Fabricate(:status, owned_poll: poll) } + + let(:object_json) do + { + id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join, + type: 'Note', + name: 'Yellow', + inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status) + } + end + + it 'does not add a vote to the poll' do + expect(poll.votes.first).to be_nil + end + end end context 'when sender is followed by local users' do diff --git a/spec/models/poll_spec.rb b/spec/models/poll_spec.rb new file mode 100644 index 000000000..666f8ca68 --- /dev/null +++ b/spec/models/poll_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe Poll, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/poll_vote_spec.rb b/spec/models/poll_vote_spec.rb new file mode 100644 index 000000000..354afd535 --- /dev/null +++ b/spec/models/poll_vote_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe PollVote, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/serializers/activitypub/note_spec.rb b/spec/serializers/activitypub/note_spec.rb new file mode 100644 index 000000000..55bfbc16b --- /dev/null +++ b/spec/serializers/activitypub/note_spec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe ActivityPub::NoteSerializer do + let!(:account) { Fabricate(:account) } + let!(:other) { Fabricate(:account) } + let!(:parent) { Fabricate(:status, account: account, visibility: :public) } + let!(:reply1) { Fabricate(:status, account: account, thread: parent, visibility: :public) } + let!(:reply2) { Fabricate(:status, account: account, thread: parent, visibility: :public) } + let!(:reply3) { Fabricate(:status, account: other, thread: parent, visibility: :public) } + let!(:reply4) { Fabricate(:status, account: account, thread: parent, visibility: :public) } + let!(:reply5) { Fabricate(:status, account: account, thread: parent, visibility: :direct) } + + before(:each) do + @serialization = ActiveModelSerializers::SerializableResource.new(parent, serializer: ActivityPub::NoteSerializer, adapter: ActivityPub::Adapter) + end + + subject { JSON.parse(@serialization.to_json) } + + it 'has a Note type' do + expect(subject['type']).to eql('Note') + end + + it 'has a replies collection' do + expect(subject['replies']['type']).to eql('Collection') + end + + it 'has a replies collection with a first Page' do + expect(subject['replies']['first']['type']).to eql('CollectionPage') + end + + it 'includes public self-replies in its replies collection' do + expect(subject['replies']['first']['items']).to include(reply1.uri, reply2.uri, reply4.uri) + end + + it 'does not include replies from others in its replies collection' do + expect(subject['replies']['first']['items']).to_not include(reply3.uri) + end + + it 'does not include replies with direct visibility in its replies collection' do + expect(subject['replies']['first']['items']).to_not include(reply5.uri) + end +end diff --git a/spec/services/activitypub/fetch_replies_service_spec.rb b/spec/services/activitypub/fetch_replies_service_spec.rb new file mode 100644 index 000000000..65c453341 --- /dev/null +++ b/spec/services/activitypub/fetch_replies_service_spec.rb @@ -0,0 +1,122 @@ +require 'rails_helper' + +RSpec.describe ActivityPub::FetchRepliesService, type: :service do + let(:actor) { Fabricate(:account, domain: 'example.com', uri: 'http://example.com/account') } + let(:status) { Fabricate(:status, account: actor) } + let(:collection_uri) { 'http://example.com/replies/1' } + + let(:items) do + [ + 'http://example.com/self-reply-1', + 'http://example.com/self-reply-2', + 'http://example.com/self-reply-3', + 'http://other.com/other-reply-1', + 'http://other.com/other-reply-2', + 'http://other.com/other-reply-3', + 'http://example.com/self-reply-4', + 'http://example.com/self-reply-5', + 'http://example.com/self-reply-6', + ] + end + + let(:payload) do + { + '@context': 'https://www.w3.org/ns/activitystreams', + type: 'Collection', + id: collection_uri, + items: items, + }.with_indifferent_access + end + + subject { described_class.new } + + describe '#call' do + context 'when the payload is a Collection with inlined replies' do + context 'when passing the collection itself' do + it 'spawns workers for up to 5 replies on the same server' do + allow(FetchReplyWorker).to receive(:push_bulk) + subject.call(status, payload) + expect(FetchReplyWorker).to have_received(:push_bulk).with(['http://example.com/self-reply-1', 'http://example.com/self-reply-2', 'http://example.com/self-reply-3', 'http://example.com/self-reply-4', 'http://example.com/self-reply-5']) + end + end + + context 'when passing the URL to the collection' do + before do + stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload)) + end + + it 'spawns workers for up to 5 replies on the same server' do + allow(FetchReplyWorker).to receive(:push_bulk) + subject.call(status, collection_uri) + expect(FetchReplyWorker).to have_received(:push_bulk).with(['http://example.com/self-reply-1', 'http://example.com/self-reply-2', 'http://example.com/self-reply-3', 'http://example.com/self-reply-4', 'http://example.com/self-reply-5']) + end + end + end + + context 'when the payload is an OrderedCollection with inlined replies' do + let(:payload) do + { + '@context': 'https://www.w3.org/ns/activitystreams', + type: 'OrderedCollection', + id: collection_uri, + orderedItems: items, + }.with_indifferent_access + end + + context 'when passing the collection itself' do + it 'spawns workers for up to 5 replies on the same server' do + allow(FetchReplyWorker).to receive(:push_bulk) + subject.call(status, payload) + expect(FetchReplyWorker).to have_received(:push_bulk).with(['http://example.com/self-reply-1', 'http://example.com/self-reply-2', 'http://example.com/self-reply-3', 'http://example.com/self-reply-4', 'http://example.com/self-reply-5']) + end + end + + context 'when passing the URL to the collection' do + before do + stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload)) + end + + it 'spawns workers for up to 5 replies on the same server' do + allow(FetchReplyWorker).to receive(:push_bulk) + subject.call(status, collection_uri) + expect(FetchReplyWorker).to have_received(:push_bulk).with(['http://example.com/self-reply-1', 'http://example.com/self-reply-2', 'http://example.com/self-reply-3', 'http://example.com/self-reply-4', 'http://example.com/self-reply-5']) + end + end + end + + context 'when the payload is a paginated Collection with inlined replies' do + let(:payload) do + { + '@context': 'https://www.w3.org/ns/activitystreams', + type: 'Collection', + id: collection_uri, + first: { + type: 'CollectionPage', + partOf: collection_uri, + items: items, + } + }.with_indifferent_access + end + + context 'when passing the collection itself' do + it 'spawns workers for up to 5 replies on the same server' do + allow(FetchReplyWorker).to receive(:push_bulk) + subject.call(status, payload) + expect(FetchReplyWorker).to have_received(:push_bulk).with(['http://example.com/self-reply-1', 'http://example.com/self-reply-2', 'http://example.com/self-reply-3', 'http://example.com/self-reply-4', 'http://example.com/self-reply-5']) + end + end + + context 'when passing the URL to the collection' do + before do + stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload)) + end + + it 'spawns workers for up to 5 replies on the same server' do + allow(FetchReplyWorker).to receive(:push_bulk) + subject.call(status, collection_uri) + expect(FetchReplyWorker).to have_received(:push_bulk).with(['http://example.com/self-reply-1', 'http://example.com/self-reply-2', 'http://example.com/self-reply-3', 'http://example.com/self-reply-4', 'http://example.com/self-reply-5']) + end + end + end + end +end diff --git a/spec/services/suspend_account_service_spec.rb b/spec/services/suspend_account_service_spec.rb index 8a5bd3301..6f45762aa 100644 --- a/spec/services/suspend_account_service_spec.rb +++ b/spec/services/suspend_account_service_spec.rb @@ -1,7 +1,7 @@ require 'rails_helper' RSpec.describe SuspendAccountService, type: :service do - describe '#call' do + describe '#call on local account' do before do stub_request(:post, "https://alice.com/inbox").to_return(status: 201) stub_request(:post, "https://bob.com/inbox").to_return(status: 201) @@ -43,4 +43,46 @@ RSpec.describe SuspendAccountService, type: :service do expect(a_request(:post, "https://bob.com/inbox")).to have_been_made.once end end + + describe '#call on remote account' do + before do + stub_request(:post, "https://alice.com/inbox").to_return(status: 201) + stub_request(:post, "https://bob.com/inbox").to_return(status: 201) + end + + subject do + -> { described_class.new.call(remote_bob) } + end + + let!(:account) { Fabricate(:account) } + let!(:remote_alice) { Fabricate(:account, inbox_url: 'https://alice.com/inbox', protocol: :activitypub) } + let!(:remote_bob) { Fabricate(:account, inbox_url: 'https://bob.com/inbox', protocol: :activitypub) } + let!(:status) { Fabricate(:status, account: remote_bob) } + let!(:media_attachment) { Fabricate(:media_attachment, account: remote_bob) } + let!(:notification) { Fabricate(:notification, account: remote_bob) } + let!(:favourite) { Fabricate(:favourite, account: remote_bob) } + let!(:active_relationship) { Fabricate(:follow, account: remote_bob, target_account: account) } + let!(:passive_relationship) { Fabricate(:follow, target_account: remote_bob) } + let!(:subscription) { Fabricate(:subscription, account: remote_bob) } + + it 'deletes associated records' do + is_expected.to change { + [ + remote_bob.statuses, + remote_bob.media_attachments, + remote_bob.stream_entries, + remote_bob.notifications, + remote_bob.favourites, + remote_bob.active_relationships, + remote_bob.passive_relationships, + remote_bob.subscriptions + ].map(&:count) + }.from([1, 1, 1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0, 0, 0]) + end + + it 'sends a reject follow to follwer inboxes' do + subject.call + expect(a_request(:post, remote_bob.inbox_url)).to have_been_made.once + end + end end diff --git a/spec/workers/activitypub/fetch_replies_worker_spec.rb b/spec/workers/activitypub/fetch_replies_worker_spec.rb new file mode 100644 index 000000000..91ef3c4b9 --- /dev/null +++ b/spec/workers/activitypub/fetch_replies_worker_spec.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe ActivityPub::FetchRepliesWorker do + subject { described_class.new } + + let(:account) { Fabricate(:account, uri: 'https://example.com/user/1') } + let(:status) { Fabricate(:status, account: account) } + + let(:payload) do + { + '@context': 'https://www.w3.org/ns/activitystreams', + id: 'https://example.com/statuses_replies/1', + type: 'Collection', + items: [], + } + end + + let(:json) { Oj.dump(payload) } + + describe 'perform' do + it 'performs a request if the collection URI is from the same host' do + stub_request(:get, 'https://example.com/statuses_replies/1').to_return(status: 200, body: json) + subject.perform(status.id, 'https://example.com/statuses_replies/1') + expect(a_request(:get, 'https://example.com/statuses_replies/1')).to have_been_made.once + end + + it 'does not perform a request if the collection URI is from a different host' do + stub_request(:get, 'https://other.com/statuses_replies/1').to_return(status: 200) + subject.perform(status.id, 'https://other.com/statuses_replies/1') + expect(a_request(:get, 'https://other.com/statuses_replies/1')).to_not have_been_made + end + + it 'raises when request fails' do + stub_request(:get, 'https://example.com/statuses_replies/1').to_return(status: 500) + expect { subject.perform(status.id, 'https://example.com/statuses_replies/1') }.to raise_error Mastodon::UnexpectedResponseError + end + end +end |