Revert "Upstream 20240517"
This commit is contained in:
parent
9c006fd893
commit
f6dec44e95
2347 changed files with 26470 additions and 87494 deletions
|
@ -15,11 +15,15 @@ RSpec.describe 'credentials API' do
|
|||
|
||||
it_behaves_like 'forbidden for wrong scope', 'write write:accounts'
|
||||
|
||||
it 'returns http success with expected content' do
|
||||
it 'returns http success' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'returns the expected content' do
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
expect(body_as_json).to include({
|
||||
source: hash_including({
|
||||
discoverable: false,
|
||||
|
@ -28,70 +32,25 @@ RSpec.describe 'credentials API' do
|
|||
locked: true,
|
||||
})
|
||||
end
|
||||
|
||||
describe 'allows the read:me scope' do
|
||||
let(:scopes) { 'read:me' }
|
||||
|
||||
it 'returns the response successfully' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
|
||||
expect(body_as_json).to include({
|
||||
locked: true,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PATCH /api/v1/accounts/update_credentials' do
|
||||
describe 'POST /api/v1/accounts/update_credentials' do
|
||||
subject do
|
||||
patch '/api/v1/accounts/update_credentials', headers: headers, params: params
|
||||
end
|
||||
|
||||
before { allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_async) }
|
||||
|
||||
let(:params) do
|
||||
{
|
||||
avatar: fixture_file_upload('avatar.gif', 'image/gif'),
|
||||
discoverable: true,
|
||||
display_name: "Alice Isn't Dead",
|
||||
header: fixture_file_upload('attachment.jpg', 'image/jpeg'),
|
||||
indexable: true,
|
||||
locked: false,
|
||||
note: 'Hello!',
|
||||
source: {
|
||||
privacy: 'unlisted',
|
||||
sensitive: true,
|
||||
},
|
||||
}
|
||||
end
|
||||
let(:params) { { discoverable: true, locked: false, indexable: true } }
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'read read:accounts'
|
||||
|
||||
describe 'with empty source list' do
|
||||
let(:params) { { display_name: "I'm a cat", source: {} } }
|
||||
|
||||
it 'returns http success' do
|
||||
subject
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with invalid data' do
|
||||
let(:params) { { note: 'This is too long. ' * 30 } }
|
||||
|
||||
it 'returns http unprocessable entity' do
|
||||
subject
|
||||
expect(response).to have_http_status(422)
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns http success with updated JSON attributes' do
|
||||
it 'returns http success' do
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'returns JSON with updated attributes' do
|
||||
subject
|
||||
|
||||
expect(body_as_json).to include({
|
||||
source: hash_including({
|
||||
|
@ -100,27 +59,6 @@ RSpec.describe 'credentials API' do
|
|||
}),
|
||||
locked: false,
|
||||
})
|
||||
|
||||
expect(ActivityPub::UpdateDistributionWorker)
|
||||
.to have_received(:perform_async).with(user.account_id)
|
||||
end
|
||||
|
||||
def expect_account_updates
|
||||
expect(user.account.reload)
|
||||
.to have_attributes(
|
||||
display_name: eq("Alice Isn't Dead"),
|
||||
note: 'Hello!',
|
||||
avatar: exist,
|
||||
header: exist
|
||||
)
|
||||
end
|
||||
|
||||
def expect_user_updates
|
||||
expect(user.reload)
|
||||
.to have_attributes(
|
||||
setting_default_privacy: eq('unlisted'),
|
||||
setting_default_sensitive: be(true)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe 'API V1 Accounts FollowerAccounts' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:scopes) { 'read:accounts' }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
let(:account) { Fabricate(:account) }
|
||||
let(:alice) { Fabricate(:account) }
|
||||
let(:bob) { Fabricate(:account) }
|
||||
|
||||
before do
|
||||
alice.follow!(account)
|
||||
bob.follow!(account)
|
||||
end
|
||||
|
||||
describe 'GET /api/v1/accounts/:acount_id/followers' do
|
||||
it 'returns accounts following the given account', :aggregate_failures do
|
||||
get "/api/v1/accounts/#{account.id}/followers", params: { limit: 2 }, headers: headers
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(body_as_json.size).to eq 2
|
||||
expect([body_as_json[0][:id], body_as_json[1][:id]]).to contain_exactly(alice.id.to_s, bob.id.to_s)
|
||||
end
|
||||
|
||||
it 'does not return blocked users', :aggregate_failures do
|
||||
user.account.block!(bob)
|
||||
get "/api/v1/accounts/#{account.id}/followers", params: { limit: 2 }, headers: headers
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(body_as_json.size).to eq 1
|
||||
expect(body_as_json[0][:id]).to eq alice.id.to_s
|
||||
end
|
||||
|
||||
context 'when requesting user is blocked' do
|
||||
before do
|
||||
account.block!(user.account)
|
||||
end
|
||||
|
||||
it 'hides results' do
|
||||
get "/api/v1/accounts/#{account.id}/followers", params: { limit: 2 }, headers: headers
|
||||
expect(body_as_json.size).to eq 0
|
||||
end
|
||||
end
|
||||
|
||||
context 'when requesting user is the account owner' do
|
||||
let(:user) { account.user }
|
||||
|
||||
it 'returns all accounts, including muted accounts' do
|
||||
account.mute!(bob)
|
||||
get "/api/v1/accounts/#{account.id}/followers", params: { limit: 2 }, headers: headers
|
||||
|
||||
expect(body_as_json.size).to eq 2
|
||||
expect([body_as_json[0][:id], body_as_json[1][:id]]).to contain_exactly(alice.id.to_s, bob.id.to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,60 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe 'API V1 Accounts FollowingAccounts' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:scopes) { 'read:accounts' }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
let(:account) { Fabricate(:account) }
|
||||
let(:alice) { Fabricate(:account) }
|
||||
let(:bob) { Fabricate(:account) }
|
||||
|
||||
before do
|
||||
account.follow!(alice)
|
||||
account.follow!(bob)
|
||||
end
|
||||
|
||||
describe 'GET /api/v1/accounts/:account_id/following' do
|
||||
it 'returns accounts followed by the given account', :aggregate_failures do
|
||||
get "/api/v1/accounts/#{account.id}/following", params: { limit: 2 }, headers: headers
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(body_as_json.size).to eq 2
|
||||
expect([body_as_json[0][:id], body_as_json[1][:id]]).to contain_exactly(alice.id.to_s, bob.id.to_s)
|
||||
end
|
||||
|
||||
it 'does not return blocked users', :aggregate_failures do
|
||||
user.account.block!(bob)
|
||||
get "/api/v1/accounts/#{account.id}/following", params: { limit: 2 }, headers: headers
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(body_as_json.size).to eq 1
|
||||
expect(body_as_json[0][:id]).to eq alice.id.to_s
|
||||
end
|
||||
|
||||
context 'when requesting user is blocked' do
|
||||
before do
|
||||
account.block!(user.account)
|
||||
end
|
||||
|
||||
it 'hides results' do
|
||||
get "/api/v1/accounts/#{account.id}/following", params: { limit: 2 }, headers: headers
|
||||
expect(body_as_json.size).to eq 0
|
||||
end
|
||||
end
|
||||
|
||||
context 'when requesting user is the account owner' do
|
||||
let(:user) { account.user }
|
||||
|
||||
it 'returns all accounts, including muted accounts' do
|
||||
account.mute!(bob)
|
||||
get "/api/v1/accounts/#{account.id}/following", params: { limit: 2 }, headers: headers
|
||||
|
||||
expect(body_as_json.size).to eq 2
|
||||
expect([body_as_json[0][:id], body_as_json[1][:id]]).to contain_exactly(alice.id.to_s, bob.id.to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,149 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe 'API V1 Accounts Statuses' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:scopes) { 'read:statuses' }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
|
||||
describe 'GET /api/v1/accounts/:account_id/statuses' do
|
||||
it 'returns expected headers', :aggregate_failures do
|
||||
Fabricate(:status, account: user.account)
|
||||
get "/api/v1/accounts/#{user.account.id}/statuses", params: { limit: 1 }, headers: headers
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(links_from_header.size)
|
||||
.to eq(2)
|
||||
end
|
||||
|
||||
context 'with only media' do
|
||||
it 'returns http success' do
|
||||
get "/api/v1/accounts/#{user.account.id}/statuses", params: { only_media: true }, headers: headers
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with exclude replies' do
|
||||
let!(:status) { Fabricate(:status, account: user.account) }
|
||||
let!(:status_self_reply) { Fabricate(:status, account: user.account, thread: status) }
|
||||
|
||||
before do
|
||||
Fabricate(:status, account: user.account, thread: Fabricate(:status)) # Reply to another user
|
||||
get "/api/v1/accounts/#{user.account.id}/statuses", params: { exclude_replies: true }, headers: headers
|
||||
end
|
||||
|
||||
it 'returns posts along with self replies', :aggregate_failures do
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
expect(body_as_json)
|
||||
.to have_attributes(size: 2)
|
||||
.and contain_exactly(
|
||||
include(id: status.id.to_s),
|
||||
include(id: status_self_reply.id.to_s)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with only own pinned' do
|
||||
before do
|
||||
Fabricate(:status_pin, account: user.account, status: Fabricate(:status, account: user.account))
|
||||
end
|
||||
|
||||
it 'returns http success and includes a header link' do
|
||||
get "/api/v1/accounts/#{user.account.id}/statuses", params: { pinned: true }, headers: headers
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(links_from_header.size)
|
||||
.to eq(1)
|
||||
expect(links_from_header)
|
||||
.to contain_exactly(
|
||||
have_attributes(
|
||||
href: /pinned=true/,
|
||||
attr_pairs: contain_exactly(['rel', 'prev'])
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with enough pinned statuses to paginate' do
|
||||
before do
|
||||
stub_const 'Api::BaseController::DEFAULT_STATUSES_LIMIT', 1
|
||||
2.times { Fabricate(:status_pin, account: user.account) }
|
||||
end
|
||||
|
||||
it 'returns http success and header pagination links to prev and next' do
|
||||
get "/api/v1/accounts/#{user.account.id}/statuses", params: { pinned: true }, headers: headers
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(links_from_header.size)
|
||||
.to eq(2)
|
||||
expect(links_from_header)
|
||||
.to contain_exactly(
|
||||
have_attributes(
|
||||
href: /pinned=true/,
|
||||
attr_pairs: contain_exactly(['rel', 'next'])
|
||||
),
|
||||
have_attributes(
|
||||
href: /pinned=true/,
|
||||
attr_pairs: contain_exactly(['rel', 'prev'])
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "with someone else's pinned statuses" do
|
||||
let(:account) { Fabricate(:account, username: 'bob', domain: 'example.com') }
|
||||
let(:status) { Fabricate(:status, account: account) }
|
||||
let(:private_status) { Fabricate(:status, account: account, visibility: :private) }
|
||||
|
||||
before do
|
||||
Fabricate(:status_pin, account: account, status: status)
|
||||
Fabricate(:status_pin, account: account, status: private_status)
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
get "/api/v1/accounts/#{account.id}/statuses", params: { pinned: true }, headers: headers
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
context 'when user does not follow account' do
|
||||
it 'lists the public status only' do
|
||||
get "/api/v1/accounts/#{account.id}/statuses", params: { pinned: true }, headers: headers
|
||||
|
||||
expect(body_as_json)
|
||||
.to contain_exactly(
|
||||
a_hash_including(id: status.id.to_s)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user follows account' do
|
||||
before do
|
||||
user.account.follow!(account)
|
||||
end
|
||||
|
||||
it 'lists both the public and the private statuses' do
|
||||
get "/api/v1/accounts/#{account.id}/statuses", params: { pinned: true }, headers: headers
|
||||
|
||||
expect(body_as_json)
|
||||
.to contain_exactly(
|
||||
a_hash_including(id: status.id.to_s),
|
||||
a_hash_including(id: private_status.id.to_s)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def links_from_header
|
||||
response
|
||||
.headers['Link']
|
||||
.links
|
||||
end
|
||||
end
|
|
@ -8,22 +8,6 @@ describe '/api/v1/accounts' do
|
|||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
|
||||
describe 'GET /api/v1/accounts?ids[]=:id' do
|
||||
let(:account) { Fabricate(:account) }
|
||||
let(:other_account) { Fabricate(:account) }
|
||||
let(:scopes) { 'read:accounts' }
|
||||
|
||||
it 'returns expected response' do
|
||||
get '/api/v1/accounts', headers: headers, params: { ids: [account.id, other_account.id, 123_123] }
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(body_as_json).to contain_exactly(
|
||||
hash_including(id: account.id.to_s),
|
||||
hash_including(id: other_account.id.to_s)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /api/v1/accounts/:id' do
|
||||
context 'when logged out' do
|
||||
let(:account) { Fabricate(:account) }
|
||||
|
@ -99,8 +83,6 @@ describe '/api/v1/accounts' do
|
|||
|
||||
describe 'POST /api/v1/accounts/:id/follow' do
|
||||
let(:scopes) { 'write:follows' }
|
||||
let(:my_actor_type) { 'Person' }
|
||||
let(:lock_follow_from_bot) { false }
|
||||
let(:other_account) { Fabricate(:account, username: 'bob', locked: locked) }
|
||||
|
||||
context 'when posting to an other account' do
|
||||
|
@ -108,12 +90,6 @@ describe '/api/v1/accounts' do
|
|||
post "/api/v1/accounts/#{other_account.id}/follow", headers: headers
|
||||
end
|
||||
|
||||
before do
|
||||
other_account.user.settings['lock_follow_from_bot'] = lock_follow_from_bot
|
||||
other_account.user.save!
|
||||
user.account.update!(actor_type: my_actor_type)
|
||||
end
|
||||
|
||||
context 'with unlocked account' do
|
||||
let(:locked) { false }
|
||||
|
||||
|
@ -151,35 +127,6 @@ describe '/api/v1/accounts' do
|
|||
|
||||
it_behaves_like 'forbidden for wrong scope', 'read:accounts'
|
||||
end
|
||||
|
||||
context 'with unlocked account from bot' do
|
||||
let(:locked) { false }
|
||||
let(:lock_follow_from_bot) { true }
|
||||
let(:my_actor_type) { 'Service' }
|
||||
|
||||
it 'returns http success' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'returns JSON with following=false and requested=true' do
|
||||
subject
|
||||
|
||||
json = body_as_json
|
||||
|
||||
expect(json[:following]).to be false
|
||||
expect(json[:requested]).to be true
|
||||
end
|
||||
|
||||
it 'creates a follow request relation between user and target user' do
|
||||
subject
|
||||
|
||||
expect(user.account.requested?(other_account)).to be true
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'read:accounts'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when modifying follow options' do
|
||||
|
|
|
@ -49,7 +49,6 @@ RSpec.describe 'Domain Blocks' do
|
|||
{
|
||||
id: domain_block.id.to_s,
|
||||
domain: domain_block.domain,
|
||||
digest: domain_block.domain_digest,
|
||||
created_at: domain_block.created_at.strftime('%Y-%m-%dT%H:%M:%S.%LZ'),
|
||||
severity: domain_block.severity.to_s,
|
||||
reject_media: domain_block.reject_media,
|
||||
|
@ -57,15 +56,6 @@ RSpec.describe 'Domain Blocks' do
|
|||
private_comment: domain_block.private_comment,
|
||||
public_comment: domain_block.public_comment,
|
||||
obfuscate: domain_block.obfuscate,
|
||||
block_trends: domain_block.block_trends,
|
||||
reject_favourite: domain_block.reject_favourite,
|
||||
reject_hashtag: domain_block.reject_hashtag,
|
||||
detect_invalid_subscription: domain_block.detect_invalid_subscription,
|
||||
reject_new_follow: domain_block.reject_new_follow,
|
||||
reject_reply_exclude_followers: domain_block.reject_reply_exclude_followers,
|
||||
reject_send_sensitive: domain_block.reject_send_sensitive,
|
||||
reject_straight_follow: domain_block.reject_straight_follow,
|
||||
reject_friend: domain_block.reject_friend,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
@ -95,29 +85,6 @@ RSpec.describe 'Domain Blocks' do
|
|||
|
||||
let!(:domain_block) { Fabricate(:domain_block) }
|
||||
|
||||
let(:expected_response) do
|
||||
{
|
||||
id: domain_block.id.to_s,
|
||||
domain: domain_block.domain,
|
||||
created_at: domain_block.created_at.strftime('%Y-%m-%dT%H:%M:%S.%LZ'),
|
||||
severity: domain_block.severity.to_s,
|
||||
reject_media: domain_block.reject_media,
|
||||
reject_reports: domain_block.reject_reports,
|
||||
private_comment: domain_block.private_comment,
|
||||
public_comment: domain_block.public_comment,
|
||||
obfuscate: domain_block.obfuscate,
|
||||
block_trends: domain_block.block_trends,
|
||||
reject_favourite: domain_block.reject_favourite,
|
||||
reject_hashtag: domain_block.reject_hashtag,
|
||||
detect_invalid_subscription: domain_block.detect_invalid_subscription,
|
||||
reject_new_follow: domain_block.reject_new_follow,
|
||||
reject_reply_exclude_followers: domain_block.reject_reply_exclude_followers,
|
||||
reject_send_sensitive: domain_block.reject_send_sensitive,
|
||||
reject_straight_follow: domain_block.reject_straight_follow,
|
||||
reject_friend: domain_block.reject_friend,
|
||||
}
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'write:statuses'
|
||||
it_behaves_like 'forbidden for wrong role', ''
|
||||
it_behaves_like 'forbidden for wrong role', 'Moderator'
|
||||
|
@ -130,7 +97,6 @@ RSpec.describe 'Domain Blocks' do
|
|||
{
|
||||
id: domain_block.id.to_s,
|
||||
domain: domain_block.domain,
|
||||
digest: domain_block.domain_digest,
|
||||
created_at: domain_block.created_at.strftime('%Y-%m-%dT%H:%M:%S.%LZ'),
|
||||
severity: domain_block.severity.to_s,
|
||||
reject_media: domain_block.reject_media,
|
||||
|
@ -138,15 +104,6 @@ RSpec.describe 'Domain Blocks' do
|
|||
private_comment: domain_block.private_comment,
|
||||
public_comment: domain_block.public_comment,
|
||||
obfuscate: domain_block.obfuscate,
|
||||
block_trends: domain_block.block_trends,
|
||||
reject_favourite: domain_block.reject_favourite,
|
||||
reject_hashtag: domain_block.reject_hashtag,
|
||||
detect_invalid_subscription: domain_block.detect_invalid_subscription,
|
||||
reject_new_follow: domain_block.reject_new_follow,
|
||||
reject_reply_exclude_followers: domain_block.reject_reply_exclude_followers,
|
||||
reject_send_sensitive: domain_block.reject_send_sensitive,
|
||||
reject_straight_follow: domain_block.reject_straight_follow,
|
||||
reject_friend: domain_block.reject_friend,
|
||||
}
|
||||
)
|
||||
end
|
||||
|
@ -171,7 +128,7 @@ RSpec.describe 'Domain Blocks' do
|
|||
it_behaves_like 'forbidden for wrong role', ''
|
||||
it_behaves_like 'forbidden for wrong role', 'Moderator'
|
||||
|
||||
it 'creates a domain block with the expected domain name and severity', :aggregate_failures do
|
||||
it 'returns expected domain name and severity', :aggregate_failures do
|
||||
subject
|
||||
|
||||
body = body_as_json
|
||||
|
@ -187,44 +144,7 @@ RSpec.describe 'Domain Blocks' do
|
|||
expect(DomainBlock.find_by(domain: 'foo.bar.com')).to be_present
|
||||
end
|
||||
|
||||
context 'when a looser domain block already exists on a higher level domain' do
|
||||
let(:params) { { domain: 'foo.bar.com', severity: :suspend } }
|
||||
|
||||
before do
|
||||
Fabricate(:domain_block, domain: 'bar.com', severity: :silence)
|
||||
end
|
||||
|
||||
it 'creates a domain block with the expected domain name and severity', :aggregate_failures do
|
||||
subject
|
||||
|
||||
body = body_as_json
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(body).to match a_hash_including(
|
||||
{
|
||||
domain: 'foo.bar.com',
|
||||
severity: 'suspend',
|
||||
}
|
||||
)
|
||||
|
||||
expect(DomainBlock.find_by(domain: 'foo.bar.com')).to be_present
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a domain block already exists on the same domain' do
|
||||
before do
|
||||
Fabricate(:domain_block, domain: 'foo.bar.com', severity: :silence)
|
||||
end
|
||||
|
||||
it 'returns existing domain block in error', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(422)
|
||||
expect(body_as_json[:existing_domain_block][:domain]).to eq('foo.bar.com')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a stricter domain block already exists on a higher level domain' do
|
||||
context 'when a stricter domain block already exists' do
|
||||
before do
|
||||
Fabricate(:domain_block, domain: 'bar.com', severity: :suspend)
|
||||
end
|
||||
|
@ -268,7 +188,6 @@ RSpec.describe 'Domain Blocks' do
|
|||
{
|
||||
id: domain_block.id.to_s,
|
||||
domain: domain_block.domain,
|
||||
digest: domain_block.domain_digest,
|
||||
severity: 'suspend',
|
||||
}
|
||||
)
|
||||
|
|
|
@ -151,9 +151,7 @@ RSpec.describe 'Reports' do
|
|||
let(:params) { { category: 'spam' } }
|
||||
|
||||
it 'updates the report category', :aggregate_failures do
|
||||
expect { subject }
|
||||
.to change { report.reload.category }.from('other').to('spam')
|
||||
.and create_an_action_log
|
||||
expect { subject }.to change { report.reload.category }.from('other').to('spam')
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
|
||||
|
@ -186,9 +184,7 @@ RSpec.describe 'Reports' do
|
|||
it_behaves_like 'forbidden for wrong role', ''
|
||||
|
||||
it 'marks report as resolved', :aggregate_failures do
|
||||
expect { subject }
|
||||
.to change { report.reload.unresolved? }.from(true).to(false)
|
||||
.and create_an_action_log
|
||||
expect { subject }.to change { report.reload.unresolved? }.from(true).to(false)
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
@ -204,9 +200,7 @@ RSpec.describe 'Reports' do
|
|||
it_behaves_like 'forbidden for wrong role', ''
|
||||
|
||||
it 'marks report as unresolved', :aggregate_failures do
|
||||
expect { subject }
|
||||
.to change { report.reload.unresolved? }.from(false).to(true)
|
||||
.and create_an_action_log
|
||||
expect { subject }.to change { report.reload.unresolved? }.from(false).to(true)
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
@ -222,9 +216,7 @@ RSpec.describe 'Reports' do
|
|||
it_behaves_like 'forbidden for wrong role', ''
|
||||
|
||||
it 'assigns report to the requesting user', :aggregate_failures do
|
||||
expect { subject }
|
||||
.to change { report.reload.assigned_account_id }.from(nil).to(user.account.id)
|
||||
.and create_an_action_log
|
||||
expect { subject }.to change { report.reload.assigned_account_id }.from(nil).to(user.account.id)
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
@ -240,16 +232,8 @@ RSpec.describe 'Reports' do
|
|||
it_behaves_like 'forbidden for wrong role', ''
|
||||
|
||||
it 'unassigns report from assignee', :aggregate_failures do
|
||||
expect { subject }
|
||||
.to change { report.reload.assigned_account_id }.from(user.account.id).to(nil)
|
||||
.and create_an_action_log
|
||||
expect { subject }.to change { report.reload.assigned_account_id }.from(user.account.id).to(nil)
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_an_action_log
|
||||
change(Admin::ActionLog, :count).by(1)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe 'API V1 Admin Trends Links Preview Card Providers' do
|
||||
let(:role) { UserRole.find_by(name: 'Admin') }
|
||||
let(:user) { Fabricate(:user, role: role) }
|
||||
let(:scopes) { 'admin:read admin:write' }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
let(:account) { Fabricate(:account) }
|
||||
let(:preview_card_provider) { Fabricate(:preview_card_provider) }
|
||||
|
||||
describe 'GET /api/v1/admin/trends/links/publishers' do
|
||||
it 'returns http success' do
|
||||
get '/api/v1/admin/trends/links/publishers', params: { account_id: account.id, limit: 2 }, headers: headers
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /api/v1/admin/trends/links/publishers/:id/approve' do
|
||||
before do
|
||||
post "/api/v1/admin/trends/links/publishers/#{preview_card_provider.id}/approve", headers: headers
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'write:statuses'
|
||||
it_behaves_like 'forbidden for wrong role', ''
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /api/v1/admin/trends/links/publishers/:id/reject' do
|
||||
before do
|
||||
post "/api/v1/admin/trends/links/publishers/#{preview_card_provider.id}/reject", headers: headers
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'write:statuses'
|
||||
it_behaves_like 'forbidden for wrong role', ''
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,47 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe 'API V1 Admin Trends Statuses' do
|
||||
let(:role) { UserRole.find_by(name: 'Admin') }
|
||||
let(:user) { Fabricate(:user, role: role) }
|
||||
let(:scopes) { 'admin:read admin:write' }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:account) { Fabricate(:account) }
|
||||
let(:status) { Fabricate(:status) }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
|
||||
describe 'GET /api/v1/admin/trends/statuses' do
|
||||
it 'returns http success' do
|
||||
get '/api/v1/admin/trends/statuses', params: { account_id: account.id, limit: 2 }, headers: headers
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /api/v1/admin/trends/statuses/:id/approve' do
|
||||
before do
|
||||
post "/api/v1/admin/trends/statuses/#{status.id}/approve", headers: headers
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'write:statuses'
|
||||
it_behaves_like 'forbidden for wrong role', ''
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /api/v1/admin/trends/statuses/:id/unapprove' do
|
||||
before do
|
||||
post "/api/v1/admin/trends/statuses/#{status.id}/reject", headers: headers
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'write:statuses'
|
||||
it_behaves_like 'forbidden for wrong role', ''
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,47 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe 'API V1 Admin Trends Tags' do
|
||||
let(:role) { UserRole.find_by(name: 'Admin') }
|
||||
let(:user) { Fabricate(:user, role: role) }
|
||||
let(:scopes) { 'admin:read admin:write' }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:account) { Fabricate(:account) }
|
||||
let(:tag) { Fabricate(:tag) }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
|
||||
describe 'GET /api/v1/admin/trends/tags' do
|
||||
it 'returns http success' do
|
||||
get '/api/v1/admin/trends/tags', params: { account_id: account.id, limit: 2 }, headers: headers
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /api/v1/admin/trends/tags/:id/approve' do
|
||||
before do
|
||||
post "/api/v1/admin/trends/tags/#{tag.id}/approve", headers: headers
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'write:statuses'
|
||||
it_behaves_like 'forbidden for wrong role', ''
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /api/v1/admin/trends/tags/:id/reject' do
|
||||
before do
|
||||
post "/api/v1/admin/trends/tags/#{tag.id}/reject", headers: headers
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'write:statuses'
|
||||
it_behaves_like 'forbidden for wrong role', ''
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,61 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'API V1 Announcements Reactions' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:scopes) { 'write:favourites' }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
|
||||
let!(:announcement) { Fabricate(:announcement) }
|
||||
|
||||
describe 'PUT /api/v1/announcements/:announcement_id/reactions/:id' do
|
||||
context 'without token' do
|
||||
it 'returns http unauthorized' do
|
||||
put "/api/v1/announcements/#{announcement.id}/reactions/#{escaped_emoji}"
|
||||
|
||||
expect(response).to have_http_status 401
|
||||
end
|
||||
end
|
||||
|
||||
context 'with token' do
|
||||
before do
|
||||
put "/api/v1/announcements/#{announcement.id}/reactions/#{escaped_emoji}", headers: headers
|
||||
end
|
||||
|
||||
it 'creates reaction', :aggregate_failures do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(announcement.announcement_reactions.find_by(name: '😂', account: user.account)).to_not be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE /api/v1/announcements/:announcement_id/reactions/:id' do
|
||||
before do
|
||||
announcement.announcement_reactions.create!(account: user.account, name: '😂')
|
||||
end
|
||||
|
||||
context 'without token' do
|
||||
it 'returns http unauthorized' do
|
||||
delete "/api/v1/announcements/#{announcement.id}/reactions/#{escaped_emoji}"
|
||||
expect(response).to have_http_status 401
|
||||
end
|
||||
end
|
||||
|
||||
context 'with token' do
|
||||
before do
|
||||
delete "/api/v1/announcements/#{announcement.id}/reactions/#{escaped_emoji}", headers: headers
|
||||
end
|
||||
|
||||
it 'creates reaction', :aggregate_failures do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(announcement.announcement_reactions.find_by(name: '😂', account: user.account)).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def escaped_emoji
|
||||
CGI.escape('😂')
|
||||
end
|
||||
end
|
|
@ -1,55 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'API V1 Announcements' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:scopes) { 'read' }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
|
||||
let!(:announcement) { Fabricate(:announcement) }
|
||||
|
||||
describe 'GET /api/v1/announcements' do
|
||||
context 'without token' do
|
||||
it 'returns http unprocessable entity' do
|
||||
get '/api/v1/announcements'
|
||||
|
||||
expect(response).to have_http_status 422
|
||||
end
|
||||
end
|
||||
|
||||
context 'with token' do
|
||||
before do
|
||||
get '/api/v1/announcements', headers: headers
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /api/v1/announcements/:id/dismiss' do
|
||||
context 'without token' do
|
||||
it 'returns http unauthorized' do
|
||||
post "/api/v1/announcements/#{announcement.id}/dismiss"
|
||||
|
||||
expect(response).to have_http_status 401
|
||||
end
|
||||
end
|
||||
|
||||
context 'with token' do
|
||||
let(:scopes) { 'write:accounts' }
|
||||
|
||||
before do
|
||||
post "/api/v1/announcements/#{announcement.id}/dismiss", headers: headers
|
||||
end
|
||||
|
||||
it 'dismisses announcement', :aggregate_failures do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(announcement.announcement_mutes.find_by(account: user.account)).to_not be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,234 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Antennas' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:scopes) { 'read:lists write:lists' }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
|
||||
describe 'GET /api/v1/antennas' do
|
||||
subject do
|
||||
get '/api/v1/antennas', headers: headers
|
||||
end
|
||||
|
||||
let!(:antennas) do
|
||||
[
|
||||
Fabricate(:antenna, account: user.account, title: 'first antenna'),
|
||||
Fabricate(:antenna, account: user.account, title: 'second antenna', with_media_only: true),
|
||||
Fabricate(:antenna, account: user.account, title: 'third antenna', stl: true),
|
||||
Fabricate(:antenna, account: user.account, title: 'fourth antenna', ignore_reblog: true),
|
||||
]
|
||||
end
|
||||
|
||||
let(:expected_response) do
|
||||
antennas.map do |antenna|
|
||||
{
|
||||
id: antenna.id.to_s,
|
||||
title: antenna.title,
|
||||
with_media_only: antenna.with_media_only,
|
||||
ignore_reblog: antenna.ignore_reblog,
|
||||
stl: antenna.stl,
|
||||
ltl: antenna.ltl,
|
||||
insert_feeds: antenna.insert_feeds,
|
||||
list: nil,
|
||||
accounts_count: 0,
|
||||
domains_count: 0,
|
||||
tags_count: 0,
|
||||
keywords_count: 0,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
Fabricate(:antenna)
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'write write:lists'
|
||||
|
||||
it 'returns the expected antennas', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(body_as_json).to match_array(expected_response)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /api/v1/antennas/:id' do
|
||||
subject do
|
||||
get "/api/v1/antennas/#{antenna.id}", headers: headers
|
||||
end
|
||||
|
||||
let(:antenna) { Fabricate(:antenna, account: user.account) }
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'write write:lists'
|
||||
|
||||
it 'returns the requested antenna correctly', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(body_as_json).to eq({
|
||||
id: antenna.id.to_s,
|
||||
title: antenna.title,
|
||||
with_media_only: antenna.with_media_only,
|
||||
ignore_reblog: antenna.ignore_reblog,
|
||||
stl: antenna.stl,
|
||||
ltl: antenna.ltl,
|
||||
insert_feeds: antenna.insert_feeds,
|
||||
list: nil,
|
||||
accounts_count: 0,
|
||||
domains_count: 0,
|
||||
tags_count: 0,
|
||||
keywords_count: 0,
|
||||
})
|
||||
end
|
||||
|
||||
context 'when the antenna belongs to a different user' do
|
||||
let(:antenna) { Fabricate(:antenna) }
|
||||
|
||||
it 'returns http not found' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the antenna does not exist' do
|
||||
it 'returns http not found' do
|
||||
get '/api/v1/antennas/-1', headers: headers
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /api/v1/antennas' do
|
||||
subject do
|
||||
post '/api/v1/antennas', headers: headers, params: params
|
||||
end
|
||||
|
||||
let(:params) { { title: 'my antenna', ltl: 'true' } }
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'read read:lists'
|
||||
|
||||
it 'returns the new antenna', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(body_as_json).to match(a_hash_including(title: 'my antenna', ltl: true))
|
||||
expect(Antenna.where(account: user.account).count).to eq(1)
|
||||
end
|
||||
|
||||
context 'when a title is not given' do
|
||||
let(:params) { { title: '' } }
|
||||
|
||||
it 'returns http unprocessable entity' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(422)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT /api/v1/antennas/:id' do
|
||||
subject do
|
||||
put "/api/v1/antennas/#{antenna.id}", headers: headers, params: params
|
||||
end
|
||||
|
||||
let(:antenna) { Fabricate(:antenna, account: user.account, title: 'my antenna') }
|
||||
let(:params) { { title: 'antenna', ignore_reblog: 'true', insert_feeds: 'true' } }
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'read read:lists'
|
||||
|
||||
it 'returns the updated antenna and updates values', :aggregate_failures do
|
||||
expect { subject }
|
||||
.to change_antenna_title
|
||||
.and change_antenna_ignore_reblog
|
||||
.and change_antenna_insert_feeds
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
antenna.reload
|
||||
|
||||
expect(body_as_json).to eq({
|
||||
id: antenna.id.to_s,
|
||||
title: antenna.title,
|
||||
with_media_only: antenna.with_media_only,
|
||||
ignore_reblog: antenna.ignore_reblog,
|
||||
stl: antenna.stl,
|
||||
ltl: antenna.ltl,
|
||||
insert_feeds: antenna.insert_feeds,
|
||||
list: nil,
|
||||
accounts_count: 0,
|
||||
domains_count: 0,
|
||||
tags_count: 0,
|
||||
keywords_count: 0,
|
||||
})
|
||||
end
|
||||
|
||||
def change_antenna_title
|
||||
change { antenna.reload.title }.from('my antenna').to('antenna')
|
||||
end
|
||||
|
||||
def change_antenna_ignore_reblog
|
||||
change { antenna.reload.ignore_reblog }.from(false).to(true)
|
||||
end
|
||||
|
||||
def change_antenna_insert_feeds
|
||||
change { antenna.reload.insert_feeds }.from(false).to(true)
|
||||
end
|
||||
|
||||
context 'when the antenna does not exist' do
|
||||
it 'returns http not found' do
|
||||
put '/api/v1/antennas/-1', headers: headers, params: params
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the antenna belongs to another user' do
|
||||
let(:antenna) { Fabricate(:antenna) }
|
||||
|
||||
it 'returns http not found' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE /api/v1/antennas/:id' do
|
||||
subject do
|
||||
delete "/api/v1/antennas/#{antenna.id}", headers: headers
|
||||
end
|
||||
|
||||
let(:antenna) { Fabricate(:antenna, account: user.account) }
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'read read:lists'
|
||||
|
||||
it 'deletes the antenna', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(Antenna.where(id: antenna.id)).to_not exist
|
||||
end
|
||||
|
||||
context 'when the antenna does not exist' do
|
||||
it 'returns http not found' do
|
||||
delete '/api/v1/antennas/-1', headers: headers
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the antenna belongs to another user' do
|
||||
let(:antenna) { Fabricate(:antenna) }
|
||||
|
||||
it 'returns http not found' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -38,14 +38,16 @@ RSpec.describe 'Blocks' do
|
|||
expect(body_as_json.size).to eq(params[:limit])
|
||||
end
|
||||
|
||||
it 'sets correct link header pagination' do
|
||||
it 'sets the correct pagination header for the prev path' do
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to include_pagination_headers(
|
||||
prev: api_v1_blocks_url(limit: params[:limit], since_id: blocks.last.id),
|
||||
next: api_v1_blocks_url(limit: params[:limit], max_id: blocks.second.id)
|
||||
)
|
||||
expect(response.headers['Link'].find_link(%w(rel prev)).href).to eq(api_v1_blocks_url(limit: params[:limit], since_id: blocks.last.id))
|
||||
end
|
||||
|
||||
it 'sets the correct pagination header for the next path' do
|
||||
subject
|
||||
|
||||
expect(response.headers['Link'].find_link(%w(rel next)).href).to eq(api_v1_blocks_url(limit: params[:limit], max_id: blocks[1].id))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -42,14 +42,9 @@ RSpec.describe 'Bookmarks' do
|
|||
it 'paginates correctly', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(body_as_json.size)
|
||||
.to eq(params[:limit])
|
||||
|
||||
expect(response)
|
||||
.to include_pagination_headers(
|
||||
prev: api_v1_bookmarks_url(limit: params[:limit], min_id: bookmarks.last.id),
|
||||
next: api_v1_bookmarks_url(limit: params[:limit], max_id: bookmarks.second.id)
|
||||
)
|
||||
expect(body_as_json.size).to eq(params[:limit])
|
||||
expect(response.headers['Link'].find_link(%w(rel prev)).href).to eq(api_v1_bookmarks_url(limit: params[:limit], min_id: bookmarks.last.id))
|
||||
expect(response.headers['Link'].find_link(%w(rel next)).href).to eq(api_v1_bookmarks_url(limit: params[:limit], max_id: bookmarks[1].id))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,165 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Accounts' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:scopes) { 'read:lists write:lists' }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
|
||||
describe 'GET /api/v1/circles/:id/accounts' do
|
||||
subject do
|
||||
get "/api/v1/circles/#{circle.id}/accounts", headers: headers, params: params
|
||||
end
|
||||
|
||||
let(:params) { { limit: 0 } }
|
||||
let(:circle) { Fabricate(:circle, account: user.account) }
|
||||
let(:accounts) { Fabricate.times(3, :account) }
|
||||
|
||||
let(:expected_response) do
|
||||
accounts.map do |account|
|
||||
a_hash_including(id: account.id.to_s, username: account.username, acct: account.acct)
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
accounts.each { |account| account.follow!(user.account) }
|
||||
circle.accounts << accounts
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'write write:lists'
|
||||
|
||||
it 'returns the accounts in the requested circle', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(body_as_json).to match_array(expected_response)
|
||||
end
|
||||
|
||||
context 'with limit param' do
|
||||
let(:params) { { limit: 1 } }
|
||||
|
||||
it 'returns only the requested number of accounts' do
|
||||
subject
|
||||
|
||||
expect(body_as_json.size).to eq(params[:limit])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /api/v1/circles/:id/accounts' do
|
||||
subject do
|
||||
post "/api/v1/circles/#{circle.id}/accounts", headers: headers, params: params
|
||||
end
|
||||
|
||||
let(:circle) { Fabricate(:circle, account: user.account) }
|
||||
let(:bob) { Fabricate(:account, username: 'bob') }
|
||||
let(:params) { { account_ids: [bob.id] } }
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'read read:lists'
|
||||
|
||||
context 'when the added account is followed' do
|
||||
before do
|
||||
bob.follow!(user.account)
|
||||
end
|
||||
|
||||
it 'adds account to the circle', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(circle.accounts).to include(bob)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the added account is not followed' do
|
||||
it 'does not add the account to the circle', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
expect(circle.accounts).to_not include(bob)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the circle is not owned by the requesting user' do
|
||||
let(:circle) { Fabricate(:circle) }
|
||||
|
||||
before do
|
||||
bob.follow!(user.account)
|
||||
end
|
||||
|
||||
it 'returns http not found' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when account is already in the circle' do
|
||||
before do
|
||||
bob.follow!(user.account)
|
||||
circle.accounts << bob
|
||||
end
|
||||
|
||||
it 'returns http unprocessable entity' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(422)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE /api/v1/circles/:id/accounts' do
|
||||
subject do
|
||||
delete "/api/v1/circles/#{circle.id}/accounts", headers: headers, params: params
|
||||
end
|
||||
|
||||
context 'when the circle is owned by the requesting user' do
|
||||
let(:circle) { Fabricate(:circle, account: user.account) }
|
||||
let(:bob) { Fabricate(:account, username: 'bob') }
|
||||
let(:peter) { Fabricate(:account, username: 'peter') }
|
||||
let(:params) { { account_ids: [bob.id] } }
|
||||
|
||||
before do
|
||||
bob.follow!(user.account)
|
||||
peter.follow!(user.account)
|
||||
circle.accounts << [bob, peter]
|
||||
end
|
||||
|
||||
it 'removes the specified account from the circle', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(circle.accounts).to_not include(bob)
|
||||
end
|
||||
|
||||
it 'does not remove any other account from the circle' do
|
||||
subject
|
||||
|
||||
expect(circle.accounts).to include(peter)
|
||||
end
|
||||
|
||||
context 'when the specified account is not in the circle' do
|
||||
let(:params) { { account_ids: [0] } }
|
||||
|
||||
it 'does not remove any account from the circle', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(circle.accounts).to contain_exactly(bob, peter)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the circle is not owned by the requesting user' do
|
||||
let(:circle) { Fabricate(:circle) }
|
||||
let(:params) { {} }
|
||||
|
||||
it 'returns http not found' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,193 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Circles' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:scopes) { 'read:lists write:lists' }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
|
||||
describe 'GET /api/v1/circles' do
|
||||
subject do
|
||||
get '/api/v1/circles', headers: headers
|
||||
end
|
||||
|
||||
let!(:circles) do
|
||||
[
|
||||
Fabricate(:circle, account: user.account, title: 'first circle'),
|
||||
Fabricate(:circle, account: user.account, title: 'second circle'),
|
||||
Fabricate(:circle, account: user.account, title: 'third circle'),
|
||||
Fabricate(:circle, account: user.account, title: 'fourth circle'),
|
||||
]
|
||||
end
|
||||
|
||||
let(:expected_response) do
|
||||
circles.map do |circle|
|
||||
{
|
||||
id: circle.id.to_s,
|
||||
title: circle.title,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
Fabricate(:circle)
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'write write:lists'
|
||||
|
||||
it 'returns the expected circles', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(body_as_json).to match_array(expected_response)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /api/v1/circles/:id' do
|
||||
subject do
|
||||
get "/api/v1/circles/#{circle.id}", headers: headers
|
||||
end
|
||||
|
||||
let(:circle) { Fabricate(:circle, account: user.account) }
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'write write:lists'
|
||||
|
||||
it 'returns the requested circle correctly', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(body_as_json).to eq({
|
||||
id: circle.id.to_s,
|
||||
title: circle.title,
|
||||
})
|
||||
end
|
||||
|
||||
context 'when the circle belongs to a different user' do
|
||||
let(:circle) { Fabricate(:circle) }
|
||||
|
||||
it 'returns http not found' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the circle does not exist' do
|
||||
it 'returns http not found' do
|
||||
get '/api/v1/circles/-1', headers: headers
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /api/v1/circles' do
|
||||
subject do
|
||||
post '/api/v1/circles', headers: headers, params: params
|
||||
end
|
||||
|
||||
let(:params) { { title: 'my circle' } }
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'read read:lists'
|
||||
|
||||
it 'returns the new circle', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(body_as_json).to match(a_hash_including(title: 'my circle'))
|
||||
expect(Circle.where(account: user.account).count).to eq(1)
|
||||
end
|
||||
|
||||
context 'when a title is not given' do
|
||||
let(:params) { { title: '' } }
|
||||
|
||||
it 'returns http unprocessable entity' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(422)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT /api/v1/circles/:id' do
|
||||
subject do
|
||||
put "/api/v1/circles/#{circle.id}", headers: headers, params: params
|
||||
end
|
||||
|
||||
let(:circle) { Fabricate(:circle, account: user.account, title: 'my circle') }
|
||||
let(:params) { { title: 'circle' } }
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'read read:lists'
|
||||
|
||||
it 'returns the updated circle', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
circle.reload
|
||||
|
||||
expect(body_as_json).to eq({
|
||||
id: circle.id.to_s,
|
||||
title: circle.title,
|
||||
})
|
||||
end
|
||||
|
||||
it 'updates the circle title' do
|
||||
expect { subject }.to change { circle.reload.title }.from('my circle').to('circle')
|
||||
end
|
||||
|
||||
context 'when the circle does not exist' do
|
||||
it 'returns http not found' do
|
||||
put '/api/v1/circles/-1', headers: headers, params: params
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the circle belongs to another user' do
|
||||
let(:circle) { Fabricate(:circle) }
|
||||
|
||||
it 'returns http not found' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE /api/v1/circles/:id' do
|
||||
subject do
|
||||
delete "/api/v1/circles/#{circle.id}", headers: headers
|
||||
end
|
||||
|
||||
let(:circle) { Fabricate(:circle, account: user.account) }
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'read read:lists'
|
||||
|
||||
it 'deletes the circle', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(Circle.where(id: circle.id)).to_not exist
|
||||
end
|
||||
|
||||
context 'when the circle does not exist' do
|
||||
it 'returns http not found' do
|
||||
delete '/api/v1/circles/-1', headers: headers
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the circle belongs to another user' do
|
||||
let(:circle) { Fabricate(:circle) }
|
||||
|
||||
it 'returns http not found' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,52 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'API V1 Conversations' do
|
||||
let!(:user) { Fabricate(:user, account_attributes: { username: 'alice' }) }
|
||||
let(:scopes) { 'read:statuses' }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
|
||||
let(:other) { Fabricate(:user) }
|
||||
|
||||
describe 'GET /api/v1/conversations', :sidekiq_inline do
|
||||
before do
|
||||
user.account.follow!(other.account)
|
||||
PostStatusService.new.call(other.account, text: 'Hey @alice', visibility: 'direct')
|
||||
PostStatusService.new.call(user.account, text: 'Hey, nobody here', visibility: 'direct')
|
||||
end
|
||||
|
||||
it 'returns pagination headers', :aggregate_failures do
|
||||
get '/api/v1/conversations', params: { limit: 1 }, headers: headers
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.headers['Link'].links.size).to eq(2)
|
||||
end
|
||||
|
||||
it 'returns conversations', :aggregate_failures do
|
||||
get '/api/v1/conversations', headers: headers
|
||||
|
||||
expect(body_as_json.size).to eq 2
|
||||
expect(body_as_json[0][:accounts].size).to eq 1
|
||||
end
|
||||
|
||||
context 'with since_id' do
|
||||
context 'when requesting old posts' do
|
||||
it 'returns conversations' do
|
||||
get '/api/v1/conversations', params: { since_id: Mastodon::Snowflake.id_at(1.hour.ago, with_random: false) }, headers: headers
|
||||
|
||||
expect(body_as_json.size).to eq 2
|
||||
end
|
||||
end
|
||||
|
||||
context 'when requesting posts in the future' do
|
||||
it 'returns no conversation' do
|
||||
get '/api/v1/conversations', params: { since_id: Mastodon::Snowflake.id_at(1.hour.from_now, with_random: false) }, headers: headers
|
||||
|
||||
expect(body_as_json.size).to eq 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -45,14 +45,16 @@ RSpec.describe 'Favourites' do
|
|||
expect(body_as_json.size).to eq(params[:limit])
|
||||
end
|
||||
|
||||
it 'sets the correct pagination headers' do
|
||||
it 'sets the correct pagination header for the prev path' do
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to include_pagination_headers(
|
||||
prev: api_v1_favourites_url(limit: params[:limit], min_id: favourites.last.id),
|
||||
next: api_v1_favourites_url(limit: params[:limit], max_id: favourites.second.id)
|
||||
)
|
||||
expect(response.headers['Link'].find_link(%w(rel prev)).href).to eq(api_v1_favourites_url(limit: params[:limit], min_id: favourites.last.id))
|
||||
end
|
||||
|
||||
it 'sets the correct pagination header for the next path' do
|
||||
subject
|
||||
|
||||
expect(response.headers['Link'].find_link(%w(rel next)).href).to eq(api_v1_favourites_url(limit: params[:limit], max_id: favourites[1].id))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -7,35 +7,13 @@ describe 'Featured Tags Suggestions API' do
|
|||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:scopes) { 'read:accounts' }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
let(:account) { Fabricate(:account, user: user) }
|
||||
let(:account) { Fabricate(:account) }
|
||||
|
||||
describe 'GET /api/v1/featured_tags/suggestions' do
|
||||
let!(:unused_featured_tag) { Fabricate(:tag, name: 'unused_featured_tag') }
|
||||
let!(:used_tag) { Fabricate(:tag, name: 'used_tag') }
|
||||
let!(:used_featured_tag) { Fabricate(:tag, name: 'used_featured_tag') }
|
||||
it 'returns http success' do
|
||||
get '/api/v1/featured_tags/suggestions', params: { account_id: account.id, limit: 2 }, headers: headers
|
||||
|
||||
before do
|
||||
_unused_tag = Fabricate(:tag, name: 'unused_tag')
|
||||
|
||||
# Make relevant tags used by account
|
||||
status = Fabricate(:status, account: account)
|
||||
status.tags << used_tag
|
||||
status.tags << used_featured_tag
|
||||
|
||||
# Feature the relevant tags
|
||||
Fabricate :featured_tag, account: account, name: unused_featured_tag.name
|
||||
Fabricate :featured_tag, account: account, name: used_featured_tag.name
|
||||
end
|
||||
|
||||
it 'returns http success and recently used but not featured tags' do
|
||||
get '/api/v1/featured_tags/suggestions', params: { limit: 2 }, headers: headers
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
expect(body_as_json)
|
||||
.to contain_exactly(
|
||||
include(name: used_tag.name)
|
||||
)
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,103 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'API V1 Filters' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
|
||||
describe 'GET /api/v1/filters' do
|
||||
let(:scopes) { 'read:filters' }
|
||||
let!(:filter) { Fabricate(:custom_filter, account: user.account) }
|
||||
let!(:custom_filter_keyword) { Fabricate(:custom_filter_keyword, custom_filter: filter) }
|
||||
|
||||
it 'returns http success' do
|
||||
get '/api/v1/filters', headers: headers
|
||||
expect(response).to have_http_status(200)
|
||||
expect(body_as_json)
|
||||
.to contain_exactly(
|
||||
include(id: custom_filter_keyword.id.to_s)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /api/v1/filters' do
|
||||
let(:scopes) { 'write:filters' }
|
||||
let(:irreversible) { true }
|
||||
let(:whole_word) { false }
|
||||
|
||||
before do
|
||||
post '/api/v1/filters', params: { phrase: 'magic', context: %w(home), irreversible: irreversible, whole_word: whole_word }, headers: headers
|
||||
end
|
||||
|
||||
it 'creates a filter', :aggregate_failures do
|
||||
filter = user.account.custom_filters.first
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(filter).to_not be_nil
|
||||
expect(filter.keywords.pluck(:keyword, :whole_word)).to eq [['magic', whole_word]]
|
||||
expect(filter.context).to eq %w(home)
|
||||
expect(filter.irreversible?).to be irreversible
|
||||
expect(filter.expires_at).to be_nil
|
||||
end
|
||||
|
||||
context 'with different parameters' do
|
||||
let(:irreversible) { false }
|
||||
let(:whole_word) { true }
|
||||
|
||||
it 'creates a filter', :aggregate_failures do
|
||||
filter = user.account.custom_filters.first
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(filter).to_not be_nil
|
||||
expect(filter.keywords.pluck(:keyword, :whole_word)).to eq [['magic', whole_word]]
|
||||
expect(filter.context).to eq %w(home)
|
||||
expect(filter.irreversible?).to be irreversible
|
||||
expect(filter.expires_at).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /api/v1/filters/:id' do
|
||||
let(:scopes) { 'read:filters' }
|
||||
let(:filter) { Fabricate(:custom_filter, account: user.account) }
|
||||
let(:keyword) { Fabricate(:custom_filter_keyword, custom_filter: filter) }
|
||||
|
||||
it 'returns http success' do
|
||||
get "/api/v1/filters/#{keyword.id}", headers: headers
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT /api/v1/filters/:id' do
|
||||
let(:scopes) { 'write:filters' }
|
||||
let(:filter) { Fabricate(:custom_filter, account: user.account) }
|
||||
let(:keyword) { Fabricate(:custom_filter_keyword, custom_filter: filter) }
|
||||
|
||||
before do
|
||||
put "/api/v1/filters/#{keyword.id}", headers: headers, params: { phrase: 'updated' }
|
||||
end
|
||||
|
||||
it 'updates the filter', :aggregate_failures do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(keyword.reload.phrase).to eq 'updated'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE /api/v1/filters/:id' do
|
||||
let(:scopes) { 'write:filters' }
|
||||
let(:filter) { Fabricate(:custom_filter, account: user.account) }
|
||||
let(:keyword) { Fabricate(:custom_filter_keyword, custom_filter: filter) }
|
||||
|
||||
before do
|
||||
delete "/api/v1/filters/#{keyword.id}", headers: headers
|
||||
end
|
||||
|
||||
it 'removes the filter', :aggregate_failures do
|
||||
expect(response).to have_http_status(200)
|
||||
expect { keyword.reload }.to raise_error ActiveRecord::RecordNotFound
|
||||
end
|
||||
end
|
||||
end
|
|
@ -49,14 +49,16 @@ RSpec.describe 'Followed tags' do
|
|||
expect(body_as_json.size).to eq(params[:limit])
|
||||
end
|
||||
|
||||
it 'sets the correct pagination headers' do
|
||||
it 'sets the correct pagination header for the prev path' do
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to include_pagination_headers(
|
||||
prev: api_v1_followed_tags_url(limit: params[:limit], since_id: tag_follows.last.id),
|
||||
next: api_v1_followed_tags_url(limit: params[:limit], max_id: tag_follows.last.id)
|
||||
)
|
||||
expect(response.headers['Link'].find_link(%w(rel prev)).href).to eq(api_v1_followed_tags_url(limit: params[:limit], since_id: tag_follows.last.id))
|
||||
end
|
||||
|
||||
it 'sets the correct pagination header for the next path' do
|
||||
subject
|
||||
|
||||
expect(response.headers['Link'].find_link(%w(rel next)).href).to eq(api_v1_followed_tags_url(limit: params[:limit], max_id: tag_follows.last.id))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,11 +3,6 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Domain Blocks' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:scopes) { 'read' }
|
||||
let(:headers) { { Authorization: "Bearer #{token.token}" } }
|
||||
|
||||
describe 'GET /api/v1/instance/domain_blocks' do
|
||||
before do
|
||||
Fabricate(:domain_block)
|
||||
|
@ -27,16 +22,6 @@ RSpec.describe 'Domain Blocks' do
|
|||
.and(be_an(Array))
|
||||
.and(have_attributes(size: 1))
|
||||
end
|
||||
|
||||
context 'with hidden domain block' do
|
||||
before { Fabricate(:domain_block, domain: 'hello.com', hidden: true) }
|
||||
|
||||
it 'returns http success and dont include hidden record' do
|
||||
get api_v1_instance_domain_blocks_path
|
||||
|
||||
expect(body_as_json.pluck(:domain)).to_not include('hello.com')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with domain blocks set to users' do
|
||||
|
@ -50,32 +35,6 @@ RSpec.describe 'Domain Blocks' do
|
|||
end
|
||||
end
|
||||
|
||||
context 'with domain blocks set to users with access token' do
|
||||
before { Setting.show_domain_blocks = 'users' }
|
||||
|
||||
it 'returns http not found' do
|
||||
get api_v1_instance_domain_blocks_path, headers: headers
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
|
||||
expect(body_as_json)
|
||||
.to be_present
|
||||
.and(be_an(Array))
|
||||
.and(have_attributes(size: 1))
|
||||
end
|
||||
|
||||
context 'with hidden domain block' do
|
||||
before { Fabricate(:domain_block, domain: 'hello.com', hidden: true) }
|
||||
|
||||
it 'returns http success and dont include hidden record' do
|
||||
get api_v1_instance_domain_blocks_path, headers: headers
|
||||
|
||||
expect(body_as_json.pluck(:domain)).to_not include('hello.com')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with domain blocks set to disabled' do
|
||||
before { Setting.show_domain_blocks = 'disabled' }
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ RSpec.describe 'Lists' do
|
|||
Fabricate(:list, account: user.account, title: 'second list', replies_policy: :list),
|
||||
Fabricate(:list, account: user.account, title: 'third list', replies_policy: :none),
|
||||
Fabricate(:list, account: user.account, title: 'fourth list', exclusive: true),
|
||||
Fabricate(:list, account: user.account, title: 'fifth list', notify: true),
|
||||
]
|
||||
end
|
||||
|
||||
|
@ -30,8 +29,6 @@ RSpec.describe 'Lists' do
|
|||
title: list.title,
|
||||
replies_policy: list.replies_policy,
|
||||
exclusive: list.exclusive,
|
||||
antennas: list.antennas,
|
||||
notify: list.notify,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
@ -68,8 +65,6 @@ RSpec.describe 'Lists' do
|
|||
title: list.title,
|
||||
replies_policy: list.replies_policy,
|
||||
exclusive: list.exclusive,
|
||||
antennas: list.antennas,
|
||||
notify: list.notify,
|
||||
})
|
||||
end
|
||||
|
||||
|
@ -154,8 +149,6 @@ RSpec.describe 'Lists' do
|
|||
title: list.title,
|
||||
replies_policy: list.replies_policy,
|
||||
exclusive: list.exclusive,
|
||||
antennas: list.antennas,
|
||||
notify: list.notify,
|
||||
})
|
||||
end
|
||||
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'API Markers' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:scopes) { 'read:statuses write:statuses' }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
|
||||
describe 'GET /api/v1/markers' do
|
||||
before do
|
||||
Fabricate(:marker, timeline: 'home', last_read_id: 123, user: user)
|
||||
Fabricate(:marker, timeline: 'notifications', last_read_id: 456, user: user)
|
||||
|
||||
get '/api/v1/markers', headers: headers, params: { timeline: %w(home notifications) }
|
||||
end
|
||||
|
||||
it 'returns markers', :aggregate_failures do
|
||||
json = body_as_json
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(json.key?(:home)).to be true
|
||||
expect(json[:home][:last_read_id]).to eq '123'
|
||||
expect(json.key?(:notifications)).to be true
|
||||
expect(json[:notifications][:last_read_id]).to eq '456'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /api/v1/markers' do
|
||||
context 'when no marker exists' do
|
||||
before do
|
||||
post '/api/v1/markers', headers: headers, params: { home: { last_read_id: '69420' } }
|
||||
end
|
||||
|
||||
it 'creates a marker', :aggregate_failures do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(user.markers.first.timeline).to eq 'home'
|
||||
expect(user.markers.first.last_read_id).to eq 69_420
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a marker exists' do
|
||||
before do
|
||||
post '/api/v1/markers', headers: headers, params: { home: { last_read_id: '69420' } }
|
||||
post '/api/v1/markers', headers: headers, params: { home: { last_read_id: '70120' } }
|
||||
end
|
||||
|
||||
it 'updates a marker', :aggregate_failures do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(user.markers.first.timeline).to eq 'home'
|
||||
expect(user.markers.first.last_read_id).to eq 70_120
|
||||
end
|
||||
end
|
||||
|
||||
context 'when database object becomes stale' do
|
||||
before do
|
||||
allow(Marker).to receive(:transaction).and_raise(ActiveRecord::StaleObjectError)
|
||||
post '/api/v1/markers', headers: headers, params: { home: { last_read_id: '69420' } }
|
||||
end
|
||||
|
||||
it 'returns error json' do
|
||||
expect(response)
|
||||
.to have_http_status(409)
|
||||
expect(body_as_json)
|
||||
.to include(error: /Conflict during update/)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -76,14 +76,20 @@ RSpec.describe 'Media' do
|
|||
let(:params) { {} }
|
||||
|
||||
shared_examples 'a successful media upload' do |media_type|
|
||||
it 'uploads the file successfully and returns correct media content', :aggregate_failures do
|
||||
it 'uploads the file successfully', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(MediaAttachment.first).to be_present
|
||||
expect(MediaAttachment.first).to have_attached_file(:file)
|
||||
end
|
||||
|
||||
expect(body_as_json).to match(
|
||||
it 'returns the correct media content' do
|
||||
subject
|
||||
|
||||
body = body_as_json
|
||||
|
||||
expect(body).to match(
|
||||
a_hash_including(id: MediaAttachment.first.id.to_s, description: params[:description], type: media_type)
|
||||
)
|
||||
end
|
||||
|
|
|
@ -44,11 +44,10 @@ RSpec.describe 'Mutes' do
|
|||
it 'sets the correct pagination headers', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to include_pagination_headers(
|
||||
prev: api_v1_mutes_url(limit: params[:limit], since_id: mutes.last.id),
|
||||
next: api_v1_mutes_url(limit: params[:limit], max_id: mutes.last.id)
|
||||
)
|
||||
headers = response.headers['Link']
|
||||
|
||||
expect(headers.find_link(%w(rel prev)).href).to eq(api_v1_mutes_url(limit: params[:limit], since_id: mutes.last.id.to_s))
|
||||
expect(headers.find_link(%w(rel next)).href).to eq(api_v1_mutes_url(limit: params[:limit], max_id: mutes.last.id.to_s))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Policies' do
|
||||
let(:user) { Fabricate(:user, account_attributes: { username: 'alice' }) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:scopes) { 'read:notifications write:notifications' }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
|
||||
describe 'GET /api/v1/notifications/policy', :sidekiq_inline do
|
||||
subject do
|
||||
get '/api/v1/notifications/policy', headers: headers, params: params
|
||||
end
|
||||
|
||||
let(:params) { {} }
|
||||
|
||||
before do
|
||||
Fabricate(:notification_request, account: user.account)
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'write write:notifications'
|
||||
|
||||
context 'with no options' do
|
||||
it 'returns json with expected attributes', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(body_as_json).to include(
|
||||
filter_not_following: false,
|
||||
filter_not_followers: false,
|
||||
filter_new_accounts: false,
|
||||
filter_private_mentions: true,
|
||||
summary: a_hash_including(
|
||||
pending_requests_count: 1,
|
||||
pending_notifications_count: 0
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT /api/v1/notifications/policy' do
|
||||
subject do
|
||||
put '/api/v1/notifications/policy', headers: headers, params: params
|
||||
end
|
||||
|
||||
let(:params) { { filter_not_following: true } }
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'read read:notifications'
|
||||
|
||||
it 'changes notification policy and returns an updated json object', :aggregate_failures do
|
||||
expect { subject }
|
||||
.to change { NotificationPolicy.find_or_initialize_by(account: user.account).filter_not_following }.from(false).to(true)
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(body_as_json).to include(
|
||||
filter_not_following: true,
|
||||
filter_not_followers: false,
|
||||
filter_new_accounts: false,
|
||||
filter_private_mentions: true,
|
||||
summary: a_hash_including(
|
||||
pending_requests_count: 0,
|
||||
pending_notifications_count: 0
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,102 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Requests' do
|
||||
let(:user) { Fabricate(:user, account_attributes: { username: 'alice' }) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:scopes) { 'read:notifications write:notifications' }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
|
||||
describe 'GET /api/v1/notifications/requests', :sidekiq_inline do
|
||||
subject do
|
||||
get '/api/v1/notifications/requests', headers: headers, params: params
|
||||
end
|
||||
|
||||
let(:params) { {} }
|
||||
|
||||
before do
|
||||
Fabricate(:notification_request, account: user.account)
|
||||
Fabricate(:notification_request, account: user.account, dismissed: true)
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'write write:notifications'
|
||||
|
||||
context 'with no options' do
|
||||
it 'returns http success', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with dismissed' do
|
||||
let(:params) { { dismissed: '1' } }
|
||||
|
||||
it 'returns http success', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /api/v1/notifications/requests/:id/accept' do
|
||||
subject do
|
||||
post "/api/v1/notifications/requests/#{notification_request.id}/accept", headers: headers
|
||||
end
|
||||
|
||||
let(:notification_request) { Fabricate(:notification_request, account: user.account) }
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'read read:notifications'
|
||||
|
||||
it 'returns http success' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'creates notification permission' do
|
||||
subject
|
||||
|
||||
expect(NotificationPermission.find_by(account: notification_request.account, from_account: notification_request.from_account)).to_not be_nil
|
||||
end
|
||||
|
||||
context 'when notification request belongs to someone else' do
|
||||
let(:notification_request) { Fabricate(:notification_request) }
|
||||
|
||||
it 'returns http not found' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /api/v1/notifications/requests/:id/dismiss' do
|
||||
subject do
|
||||
post "/api/v1/notifications/requests/#{notification_request.id}/dismiss", headers: headers
|
||||
end
|
||||
|
||||
let(:notification_request) { Fabricate(:notification_request, account: user.account) }
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'read read:notifications'
|
||||
|
||||
it 'returns http success and dismisses the notification request', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(notification_request.reload.dismissed?).to be true
|
||||
end
|
||||
|
||||
context 'when notification request belongs to someone else' do
|
||||
let(:notification_request) { Fabricate(:notification_request) }
|
||||
|
||||
it 'returns http not found' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -98,14 +98,9 @@ RSpec.describe 'Notifications' do
|
|||
|
||||
notifications = user.account.notifications
|
||||
|
||||
expect(body_as_json.size)
|
||||
.to eq(params[:limit])
|
||||
|
||||
expect(response)
|
||||
.to include_pagination_headers(
|
||||
prev: api_v1_notifications_url(limit: params[:limit], min_id: notifications.last.id),
|
||||
next: api_v1_notifications_url(limit: params[:limit], max_id: notifications[2].id)
|
||||
)
|
||||
expect(body_as_json.size).to eq(params[:limit])
|
||||
expect(response.headers['Link'].find_link(%w(rel prev)).href).to eq(api_v1_notifications_url(limit: params[:limit], min_id: notifications.last.id.to_s))
|
||||
expect(response.headers['Link'].find_link(%w(rel next)).href).to eq(api_v1_notifications_url(limit: params[:limit], max_id: notifications[2].id.to_s))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe 'API Peers Search' do
|
||||
describe 'GET /api/v1/peers/search' do
|
||||
context 'when peers api is disabled' do
|
||||
before do
|
||||
Setting.peers_api_enabled = false
|
||||
end
|
||||
|
||||
it 'returns http not found response' do
|
||||
get '/api/v1/peers/search'
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(404)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with no search param' do
|
||||
it 'returns http success and empty response' do
|
||||
get '/api/v1/peers/search'
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
expect(body_as_json)
|
||||
.to be_blank
|
||||
end
|
||||
end
|
||||
|
||||
context 'with invalid search param' do
|
||||
it 'returns http success and empty response' do
|
||||
get '/api/v1/peers/search', params: { q: 'ftp://Invalid-Host!!.valüe' }
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
expect(body_as_json)
|
||||
.to be_blank
|
||||
end
|
||||
end
|
||||
|
||||
context 'with search param' do
|
||||
let!(:account) { Fabricate(:account, domain: 'host.example') }
|
||||
|
||||
before { Instance.refresh }
|
||||
|
||||
it 'returns http success and json with known domains' do
|
||||
get '/api/v1/peers/search', params: { q: 'host.example' }
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
expect(body_as_json.size)
|
||||
.to eq(1)
|
||||
expect(body_as_json.first)
|
||||
.to eq(account.domain)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,33 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'API V1 Polls Votes' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:scopes) { 'write:statuses' }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
|
||||
describe 'POST /api/v1/polls/:poll_id/votes' do
|
||||
let(:poll) { Fabricate(:poll) }
|
||||
|
||||
before do
|
||||
post "/api/v1/polls/#{poll.id}/votes", params: { choices: %w(1) }, headers: headers
|
||||
end
|
||||
|
||||
it 'creates a vote', :aggregate_failures do
|
||||
expect(response).to have_http_status(200)
|
||||
|
||||
expect(vote).to_not be_nil
|
||||
expect(vote.choice).to eq 1
|
||||
|
||||
expect(poll.reload.cached_tallies).to eq [0, 1]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def vote
|
||||
poll.votes.where(account: user.account).first
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,124 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe 'API V1 Push Subscriptions' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:create_payload) do
|
||||
{
|
||||
subscription: {
|
||||
endpoint: 'https://fcm.googleapis.com/fcm/send/fiuH06a27qE:APA91bHnSiGcLwdaxdyqVXNDR9w1NlztsHb6lyt5WDKOC_Z_Q8BlFxQoR8tWFSXUIDdkyw0EdvxTu63iqamSaqVSevW5LfoFwojws8XYDXv_NRRLH6vo2CdgiN4jgHv5VLt2A8ah6lUX',
|
||||
keys: {
|
||||
p256dh: 'BEm_a0bdPDhf0SOsrnB2-ategf1hHoCnpXgQsFj5JCkcoMrMt2WHoPfEYOYPzOIs9mZE8ZUaD7VA5vouy0kEkr8=',
|
||||
auth: 'eH_C8rq2raXqlcBVDa1gLg==',
|
||||
},
|
||||
},
|
||||
}.with_indifferent_access
|
||||
end
|
||||
let(:alerts_payload) do
|
||||
{
|
||||
data: {
|
||||
policy: 'all',
|
||||
|
||||
alerts: {
|
||||
follow: true,
|
||||
follow_request: true,
|
||||
favourite: false,
|
||||
reblog: true,
|
||||
mention: false,
|
||||
poll: true,
|
||||
status: false,
|
||||
},
|
||||
},
|
||||
}.with_indifferent_access
|
||||
end
|
||||
let(:scopes) { 'push' }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
|
||||
describe 'POST /api/v1/push/subscription' do
|
||||
subject { post '/api/v1/push/subscription', params: create_payload, headers: headers }
|
||||
|
||||
it 'saves push subscriptions and returns expected JSON' do
|
||||
subject
|
||||
|
||||
expect(endpoint_push_subscription)
|
||||
.to have_attributes(
|
||||
endpoint: eq(create_payload[:subscription][:endpoint]),
|
||||
key_p256dh: eq(create_payload[:subscription][:keys][:p256dh]),
|
||||
key_auth: eq(create_payload[:subscription][:keys][:auth]),
|
||||
user_id: eq(user.id),
|
||||
access_token_id: eq(token.id)
|
||||
)
|
||||
|
||||
expect(body_as_json.with_indifferent_access)
|
||||
.to include(
|
||||
{ endpoint: create_payload[:subscription][:endpoint], alerts: {}, policy: 'all' }
|
||||
)
|
||||
end
|
||||
|
||||
it 'replaces old subscription on repeat calls' do
|
||||
2.times { subject }
|
||||
|
||||
expect(endpoint_push_subscriptions.count)
|
||||
.to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT /api/v1/push/subscription' do
|
||||
subject { put '/api/v1/push/subscription', params: alerts_payload, headers: headers }
|
||||
|
||||
before { create_subscription_with_token }
|
||||
|
||||
it 'changes data policy and alert settings and returns expected JSON' do
|
||||
expect { subject }
|
||||
.to change { endpoint_push_subscription.reload.data }
|
||||
.from(nil)
|
||||
.to(include('policy' => alerts_payload[:data][:policy]))
|
||||
|
||||
%w(follow follow_request favourite reblog mention poll status).each do |type|
|
||||
expect(endpoint_push_subscription.data['alerts']).to include(
|
||||
type.to_s => eq(alerts_payload[:data][:alerts][type.to_sym].to_s)
|
||||
)
|
||||
end
|
||||
|
||||
expect(body_as_json.with_indifferent_access)
|
||||
.to include(
|
||||
endpoint: create_payload[:subscription][:endpoint],
|
||||
alerts: alerts_payload[:data][:alerts],
|
||||
policy: alerts_payload[:data][:policy]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE /api/v1/push/subscription' do
|
||||
subject { delete '/api/v1/push/subscription', headers: headers }
|
||||
|
||||
before { create_subscription_with_token }
|
||||
|
||||
it 'removes the subscription' do
|
||||
expect { subject }
|
||||
.to change { endpoint_push_subscription }.to(nil)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def endpoint_push_subscriptions
|
||||
Web::PushSubscription.where(
|
||||
endpoint: create_payload[:subscription][:endpoint]
|
||||
)
|
||||
end
|
||||
|
||||
def endpoint_push_subscription
|
||||
endpoint_push_subscriptions.first
|
||||
end
|
||||
|
||||
def create_subscription_with_token
|
||||
Fabricate(
|
||||
:web_push_subscription,
|
||||
endpoint: create_payload[:subscription][:endpoint],
|
||||
access_token_id: token.id
|
||||
)
|
||||
end
|
||||
end
|
|
@ -35,7 +35,7 @@ RSpec.describe 'Reports' do
|
|||
|
||||
it 'creates a report', :aggregate_failures do
|
||||
perform_enqueued_jobs do
|
||||
emails = capture_emails { subject }
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(body_as_json).to match(
|
||||
|
@ -49,13 +49,7 @@ RSpec.describe 'Reports' do
|
|||
expect(target_account.targeted_reports).to_not be_empty
|
||||
expect(target_account.targeted_reports.first.comment).to eq 'reasons'
|
||||
|
||||
expect(emails.size)
|
||||
.to eq(1)
|
||||
expect(emails.first)
|
||||
.to have_attributes(
|
||||
to: contain_exactly(admin.email),
|
||||
subject: eq(I18n.t('admin_mailer.new_report.subject', instance: Rails.configuration.x.local_domain, id: target_account.targeted_reports.first.id))
|
||||
)
|
||||
expect(ActionMailer::Base.deliveries.first.to).to eq([admin.email])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,271 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'EmojiReactions' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:scopes) { 'write:favourites' }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
|
||||
describe 'POST /api/v1/statuses/:status_id/emoji_reactions' do
|
||||
subject do
|
||||
post "/api/v1/statuses/#{status.id}/emoji_reactions", headers: headers, params: { emoji: emoji }
|
||||
end
|
||||
|
||||
let(:status) { Fabricate(:status) }
|
||||
let(:emoji) { '😀' }
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'read read:favourites'
|
||||
|
||||
context 'with public status' do
|
||||
it 'reacts the status successfully', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(user.account.emoji_reacted?(status, emoji)).to be true
|
||||
end
|
||||
|
||||
it 'returns json with updated attributes' do
|
||||
subject
|
||||
|
||||
expect(body_as_json).to match(
|
||||
a_hash_including(id: status.id.to_s, emoji_reactions_count: 1)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with private status of not-followed account' do
|
||||
let(:status) { Fabricate(:status, visibility: :private) }
|
||||
|
||||
it 'returns http not found' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with private status of followed account' do
|
||||
let(:status) { Fabricate(:status, visibility: :private) }
|
||||
|
||||
before do
|
||||
user.account.follow!(status.account)
|
||||
end
|
||||
|
||||
it 'reacts the status successfully', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(user.account.emoji_reacted?(status)).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when local custom emoji' do
|
||||
before { Fabricate(:custom_emoji, shortcode: 'ohagi') }
|
||||
|
||||
let(:emoji) { 'ohagi' }
|
||||
|
||||
it 'reacts the status succeessfully', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(user.account.emoji_reacted?(status, 'ohagi')).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when remote custom emoji' do
|
||||
let!(:custom_emoji) { Fabricate(:custom_emoji, shortcode: 'ohagi', domain: 'foo.bar', uri: 'https://foo.bar/emoji') }
|
||||
let(:emoji) { 'ohagi@foo.bar' }
|
||||
|
||||
before { Fabricate(:emoji_reaction, status: status, name: 'ohagi', custom_emoji: custom_emoji) }
|
||||
|
||||
it 'reacts the status succeessfully', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(user.account.emoji_reacted?(status, 'ohagi', 'foo.bar')).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not existing custom emoji' do
|
||||
let(:emoji) { 'ohagi' }
|
||||
|
||||
it 'reacts the status succeessfully', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(422)
|
||||
end
|
||||
end
|
||||
|
||||
context 'without an authorization header' do
|
||||
let(:headers) { {} }
|
||||
|
||||
it 'returns http unauthorized' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(401)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /api/v1/statuses/:status_id/emoji_unreaction' do
|
||||
subject do
|
||||
post "/api/v1/statuses/#{status.id}/emoji_unreaction", headers: headers, params: { emoji: emoji }
|
||||
end
|
||||
|
||||
let(:status) { Fabricate(:status) }
|
||||
let(:emoji) { '😀' }
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'read read:favourites'
|
||||
|
||||
context 'with public status' do
|
||||
before do
|
||||
EmojiReactService.new.call(user.account, status, emoji)
|
||||
end
|
||||
|
||||
it 'unreacts the status successfully', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(user.account.emoji_reacted?(status)).to be false
|
||||
end
|
||||
|
||||
it 'returns json with updated attributes' do
|
||||
subject
|
||||
|
||||
expect(body_as_json).to match(
|
||||
a_hash_including(id: status.id.to_s, emoji_reactions_count: 0)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the requesting user was blocked by the status author' do
|
||||
before do
|
||||
EmojiReactService.new.call(user.account, status, emoji)
|
||||
status.account.block!(user.account)
|
||||
end
|
||||
|
||||
it 'unreacts the status successfully', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(user.account.emoji_reacted?(status)).to be false
|
||||
end
|
||||
|
||||
it 'returns json with updated attributes' do
|
||||
subject
|
||||
|
||||
expect(body_as_json).to match(
|
||||
a_hash_including(id: status.id.to_s, emoji_reactions_count: 0)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when status is not reacted' do
|
||||
it 'returns http success' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with private status that was not reacted' do
|
||||
let(:status) { Fabricate(:status, visibility: :private) }
|
||||
|
||||
it 'returns http not found' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with private status that was not reacted without emoji parameter' do
|
||||
let(:status) { Fabricate(:status, visibility: :private) }
|
||||
let(:emoji) { nil }
|
||||
|
||||
it 'returns http not found' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when local custom emoji' do
|
||||
before do
|
||||
Fabricate(:custom_emoji, shortcode: 'ohagi')
|
||||
EmojiReactService.new.call(user.account, status, emoji)
|
||||
end
|
||||
|
||||
let(:emoji) { 'ohagi' }
|
||||
|
||||
it 'reacts the status succeessfully', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(user.account.emoji_reacted?(status)).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'when remote custom emoji' do
|
||||
let(:emoji) { 'ohagi@foo.bar' }
|
||||
|
||||
before do
|
||||
custom_emoji = Fabricate(:custom_emoji, shortcode: 'ohagi', domain: 'foo.bar', uri: 'https://foo.bar/emoji')
|
||||
Fabricate(:emoji_reaction, name: 'ohagi', status: status, custom_emoji: custom_emoji)
|
||||
EmojiReactService.new.call(user.account, status, emoji)
|
||||
end
|
||||
|
||||
it 'reacts the status succeessfully', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(user.account.emoji_reacted?(status)).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'when remote custom emoji but not specified domain' do
|
||||
let(:emoji) { 'ohagi' }
|
||||
|
||||
before do
|
||||
custom_emoji = Fabricate(:custom_emoji, shortcode: 'ohagi', domain: 'foo.bar', uri: 'https://foo.bar/emoji')
|
||||
Fabricate(:emoji_reaction, name: 'ohagi', status: status, custom_emoji: custom_emoji)
|
||||
EmojiReactService.new.call(user.account, status, 'ohagi@foo.bar')
|
||||
end
|
||||
|
||||
it 'reacts the status succeessfully', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(user.account.emoji_reacted?(status)).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context 'without specified domain and reacted same name multiple domains' do
|
||||
let(:emoji) { 'ohagi' }
|
||||
|
||||
before do
|
||||
Fabricate(:custom_emoji, shortcode: 'ohagi', domain: 'foo.bar', uri: 'https://foo.bar/emoji')
|
||||
Fabricate(:custom_emoji, shortcode: 'ohagi')
|
||||
EmojiReactService.new.call(user.account, status, 'ohagi')
|
||||
EmojiReactService.new.call(user.account, status, 'ohagi@foo.bar')
|
||||
end
|
||||
|
||||
it 'reacts the status succeessfully', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(user.account.emoji_reacted?(status)).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not existing custom emoji' do
|
||||
let(:emoji) { 'ohagi' }
|
||||
|
||||
it 'reacts the status succeessfully', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,93 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'API V1 Statuses Favourited by Accounts' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:scopes) { 'read:accounts' }
|
||||
# let(:app) { Fabricate(:application, name: 'Test app', website: 'http://testapp.com') }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
let(:alice) { Fabricate(:account) }
|
||||
let(:bob) { Fabricate(:account) }
|
||||
|
||||
context 'with an oauth token' do
|
||||
subject do
|
||||
get "/api/v1/statuses/#{status.id}/favourited_by", headers: headers, params: { limit: 2 }
|
||||
end
|
||||
|
||||
describe 'GET /api/v1/statuses/:status_id/favourited_by' do
|
||||
let(:status) { Fabricate(:status, account: user.account) }
|
||||
|
||||
before do
|
||||
Favourite.create!(account: alice, status: status)
|
||||
Favourite.create!(account: bob, status: status)
|
||||
end
|
||||
|
||||
it 'returns http success and accounts who favourited the status' do
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
expect(response.headers['Link'].links.size)
|
||||
.to eq(2)
|
||||
|
||||
expect(body_as_json.size)
|
||||
.to eq(2)
|
||||
expect(body_as_json)
|
||||
.to contain_exactly(
|
||||
include(id: alice.id.to_s),
|
||||
include(id: bob.id.to_s)
|
||||
)
|
||||
end
|
||||
|
||||
it 'does not return blocked users' do
|
||||
user.account.block!(bob)
|
||||
|
||||
subject
|
||||
|
||||
expect(body_as_json.size)
|
||||
.to eq 1
|
||||
expect(body_as_json.first[:id]).to eq(alice.id.to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'without an oauth token' do
|
||||
subject do
|
||||
get "/api/v1/statuses/#{status.id}/favourited_by", params: { limit: 2 }
|
||||
end
|
||||
|
||||
context 'with a private status' do
|
||||
let(:status) { Fabricate(:status, account: user.account, visibility: :private) }
|
||||
|
||||
describe 'GET #index' do
|
||||
before do
|
||||
Fabricate(:favourite, status: status)
|
||||
end
|
||||
|
||||
it 'returns http unauthorized' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a public status' do
|
||||
let(:status) { Fabricate(:status, account: user.account, visibility: :public) }
|
||||
|
||||
describe 'GET #index' do
|
||||
before do
|
||||
Fabricate(:favourite, status: status)
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,25 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe 'API V1 Statuses Histories' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:scopes) { 'read:statuses' }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
|
||||
context 'with an oauth token' do
|
||||
describe 'GET /api/v1/statuses/:status_id/history' do
|
||||
let(:status) { Fabricate(:status, account: user.account) }
|
||||
|
||||
before do
|
||||
get "/api/v1/statuses/#{status.id}/history", headers: headers
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(body_as_json.size).to_not be 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,39 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe 'API V1 Statuses Mutes' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:scopes) { 'write:mutes' }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
|
||||
context 'with an oauth token' do
|
||||
describe 'POST /api/v1/statuses/:status_id/mute' do
|
||||
let(:status) { Fabricate(:status, account: user.account) }
|
||||
|
||||
before do
|
||||
post "/api/v1/statuses/#{status.id}/mute", headers: headers
|
||||
end
|
||||
|
||||
it 'creates a conversation mute', :aggregate_failures do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(ConversationMute.find_by(account: user.account, conversation_id: status.conversation_id)).to_not be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /api/v1/statuses/:status_id/unmute' do
|
||||
let(:status) { Fabricate(:status, account: user.account) }
|
||||
|
||||
before do
|
||||
user.account.mute_conversation!(status.conversation)
|
||||
post "/api/v1/statuses/#{status.id}/unmute", headers: headers
|
||||
end
|
||||
|
||||
it 'destroys the conversation mute', :aggregate_failures do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(ConversationMute.find_by(account: user.account, conversation_id: status.conversation_id)).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,92 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'API V1 Statuses Reblogged by Accounts' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:scopes) { 'read:accounts' }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
let(:alice) { Fabricate(:account) }
|
||||
let(:bob) { Fabricate(:account) }
|
||||
|
||||
context 'with an oauth token' do
|
||||
subject do
|
||||
get "/api/v1/statuses/#{status.id}/reblogged_by", headers: headers, params: { limit: 2 }
|
||||
end
|
||||
|
||||
describe 'GET /api/v1/statuses/:status_id/reblogged_by' do
|
||||
let(:status) { Fabricate(:status, account: user.account) }
|
||||
|
||||
before do
|
||||
Fabricate(:status, account: alice, reblog_of_id: status.id)
|
||||
Fabricate(:status, account: bob, reblog_of_id: status.id)
|
||||
end
|
||||
|
||||
it 'returns accounts who reblogged the status', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(200)
|
||||
expect(response.headers['Link'].links.size)
|
||||
.to eq(2)
|
||||
|
||||
expect(body_as_json.size)
|
||||
.to eq(2)
|
||||
expect(body_as_json)
|
||||
.to contain_exactly(
|
||||
include(id: alice.id.to_s),
|
||||
include(id: bob.id.to_s)
|
||||
)
|
||||
end
|
||||
|
||||
it 'does not return blocked users' do
|
||||
user.account.block!(bob)
|
||||
|
||||
subject
|
||||
|
||||
expect(body_as_json.size)
|
||||
.to eq 1
|
||||
expect(body_as_json.first[:id]).to eq(alice.id.to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'without an oauth token' do
|
||||
subject do
|
||||
get "/api/v1/statuses/#{status.id}/reblogged_by", params: { limit: 2 }
|
||||
end
|
||||
|
||||
context 'with a private status' do
|
||||
let(:status) { Fabricate(:status, account: user.account, visibility: :private) }
|
||||
|
||||
describe 'GET #index' do
|
||||
before do
|
||||
Fabricate(:status, reblog_of_id: status.id)
|
||||
end
|
||||
|
||||
it 'returns http unauthorized' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a public status' do
|
||||
let(:status) { Fabricate(:status, account: user.account, visibility: :public) }
|
||||
|
||||
describe 'GET #index' do
|
||||
before do
|
||||
Fabricate(:status, reblog_of_id: status.id)
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,105 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe 'API V1 Statuses Reblogs' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:scopes) { 'write:statuses' }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
|
||||
context 'with an oauth token' do
|
||||
describe 'POST /api/v1/statuses/:status_id/reblog' do
|
||||
let(:status) { Fabricate(:status, account: user.account) }
|
||||
|
||||
before do
|
||||
post "/api/v1/statuses/#{status.id}/reblog", headers: headers
|
||||
end
|
||||
|
||||
context 'with public status' do
|
||||
it 'reblogs the status', :aggregate_failures do
|
||||
expect(response).to have_http_status(200)
|
||||
|
||||
expect(status.reblogs.count).to eq 1
|
||||
|
||||
expect(user.account.reblogged?(status)).to be true
|
||||
|
||||
hash_body = body_as_json
|
||||
|
||||
expect(hash_body[:reblog][:id]).to eq status.id.to_s
|
||||
expect(hash_body[:reblog][:reblogs_count]).to eq 1
|
||||
expect(hash_body[:reblog][:reblogged]).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context 'with private status of not-followed account' do
|
||||
let(:status) { Fabricate(:status, visibility: :private) }
|
||||
|
||||
it 'returns http not found' do
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /api/v1/statuses/:status_id/unreblog', :sidekiq_inline do
|
||||
context 'with public status' do
|
||||
let(:status) { Fabricate(:status, account: user.account) }
|
||||
|
||||
before do
|
||||
ReblogService.new.call(user.account, status)
|
||||
post "/api/v1/statuses/#{status.id}/unreblog", headers: headers
|
||||
end
|
||||
|
||||
it 'destroys the reblog', :aggregate_failures do
|
||||
expect(response).to have_http_status(200)
|
||||
|
||||
expect(status.reblogs.count).to eq 0
|
||||
|
||||
expect(user.account.reblogged?(status)).to be false
|
||||
|
||||
hash_body = body_as_json
|
||||
|
||||
expect(hash_body[:id]).to eq status.id.to_s
|
||||
expect(hash_body[:reblogs_count]).to eq 0
|
||||
expect(hash_body[:reblogged]).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'with public status when blocked by its author' do
|
||||
let(:status) { Fabricate(:status, account: user.account) }
|
||||
|
||||
before do
|
||||
ReblogService.new.call(user.account, status)
|
||||
status.account.block!(user.account)
|
||||
post "/api/v1/statuses/#{status.id}/unreblog", headers: headers
|
||||
end
|
||||
|
||||
it 'destroys the reblog', :aggregate_failures do
|
||||
expect(response).to have_http_status(200)
|
||||
|
||||
expect(status.reblogs.count).to eq 0
|
||||
|
||||
expect(user.account.reblogged?(status)).to be false
|
||||
|
||||
hash_body = body_as_json
|
||||
|
||||
expect(hash_body[:id]).to eq status.id.to_s
|
||||
expect(hash_body[:reblogs_count]).to eq 0
|
||||
expect(hash_body[:reblogged]).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'with private status that was not reblogged' do
|
||||
let(:status) { Fabricate(:status, visibility: :private) }
|
||||
|
||||
before do
|
||||
post "/api/v1/statuses/#{status.id}/unreblog", headers: headers
|
||||
end
|
||||
|
||||
it 'returns http not found' do
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,28 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe 'API V1 Statuses Translations' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:scopes) { 'read:statuses' }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
|
||||
context 'with an oauth token' do
|
||||
describe 'POST /api/v1/statuses/:status_id/translate' do
|
||||
let(:status) { Fabricate(:status, account: user.account, text: 'Hola', language: 'es') }
|
||||
|
||||
before do
|
||||
translation = TranslationService::Translation.new(text: 'Hello')
|
||||
service = instance_double(TranslationService::DeepL, translate: [translation])
|
||||
allow(TranslationService).to receive_messages(configured?: true, configured: service)
|
||||
Rails.cache.write('translation_service/languages', { 'es' => ['en'] })
|
||||
post "/api/v1/statuses/#{status.id}/translate", headers: headers
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -9,22 +9,6 @@ describe '/api/v1/statuses' do
|
|||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, application: client_app, scopes: scopes) }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
|
||||
describe 'GET /api/v1/statuses?ids[]=:id' do
|
||||
let(:status) { Fabricate(:status) }
|
||||
let(:other_status) { Fabricate(:status) }
|
||||
let(:scopes) { 'read:statuses' }
|
||||
|
||||
it 'returns expected response' do
|
||||
get '/api/v1/statuses', headers: headers, params: { ids: [status.id, other_status.id, 123_123] }
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(body_as_json).to contain_exactly(
|
||||
hash_including(id: status.id.to_s),
|
||||
hash_including(id: other_status.id.to_s)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /api/v1/statuses/:id' do
|
||||
subject do
|
||||
get "/api/v1/statuses/#{status.id}", headers: headers
|
||||
|
@ -112,48 +96,9 @@ describe '/api/v1/statuses' do
|
|||
describe 'GET /api/v1/statuses/:id/context' do
|
||||
let(:scopes) { 'read:statuses' }
|
||||
let(:status) { Fabricate(:status, account: user.account) }
|
||||
let!(:thread) { Fabricate(:status, account: user.account, thread: status) }
|
||||
|
||||
it 'returns http success' do
|
||||
get "/api/v1/statuses/#{status.id}/context", params: { id: status.id }
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
context 'when has also reference' do
|
||||
before do
|
||||
Fabricate(:status_reference, status: thread, target_status: status)
|
||||
end
|
||||
|
||||
it 'returns unique ancestors' do
|
||||
get "/api/v1/statuses/#{thread.id}/context"
|
||||
status_ids = body_as_json[:ancestors].map { |ref| ref[:id].to_i }
|
||||
|
||||
expect(status_ids).to eq [status.id]
|
||||
end
|
||||
|
||||
it 'returns unique references' do
|
||||
get "/api/v1/statuses/#{thread.id}/context", params: { with_reference: true }
|
||||
ancestor_status_ids = body_as_json[:ancestors].map { |ref| ref[:id].to_i }
|
||||
reference_status_ids = body_as_json[:references].map { |ref| ref[:id].to_i }
|
||||
|
||||
expect(ancestor_status_ids).to eq [status.id]
|
||||
expect(reference_status_ids).to eq []
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with reference' do
|
||||
let(:status) { Fabricate(:status, account: user.account) }
|
||||
let(:scopes) { 'read:statuses' }
|
||||
let(:referred) { Fabricate(:status) }
|
||||
let(:referred_private) { Fabricate(:status, visibility: :private) }
|
||||
let(:referred_private_following) { Fabricate(:status, visibility: :private) }
|
||||
|
||||
before do
|
||||
user.account.follow!(referred_private_following.account)
|
||||
Fabricate(:status_reference, status: status, target_status: referred)
|
||||
Fabricate(:status_reference, status: status, target_status: referred_private)
|
||||
Fabricate(:status_reference, status: status, target_status: referred_private_following)
|
||||
Fabricate(:status, account: user.account, thread: status)
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
|
@ -161,56 +106,6 @@ describe '/api/v1/statuses' do
|
|||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'returns empty references' do
|
||||
get "/api/v1/statuses/#{status.id}/context", headers: headers
|
||||
status_ids = body_as_json[:references].map { |ref| ref[:id].to_i }
|
||||
|
||||
expect(status_ids).to eq []
|
||||
end
|
||||
|
||||
it 'contains referred status' do
|
||||
get "/api/v1/statuses/#{status.id}/context", headers: headers
|
||||
status_ids = body_as_json[:ancestors].map { |ref| ref[:id].to_i }
|
||||
|
||||
expect(status_ids).to include referred.id
|
||||
expect(status_ids).to include referred_private_following.id
|
||||
end
|
||||
|
||||
it 'does not contain private status' do
|
||||
get "/api/v1/statuses/#{status.id}/context", headers: headers
|
||||
status_ids = body_as_json[:ancestors].map { |ref| ref[:id].to_i }
|
||||
|
||||
expect(status_ids).to_not include referred_private.id
|
||||
end
|
||||
|
||||
it 'does not contain private status when not autienticated' do
|
||||
get "/api/v1/statuses/#{status.id}/context"
|
||||
status_ids = body_as_json[:ancestors].map { |ref| ref[:id].to_i }
|
||||
|
||||
expect(status_ids).to_not include referred_private.id
|
||||
end
|
||||
|
||||
context 'when with_reference is enabled' do
|
||||
it 'returns http success' do
|
||||
get "/api/v1/statuses/#{status.id}/context", params: { with_reference: true }, headers: headers
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
|
||||
it 'returns empty ancestors' do
|
||||
get "/api/v1/statuses/#{status.id}/context", params: { with_reference: true }, headers: headers
|
||||
status_ids = body_as_json[:ancestors].map { |ref| ref[:id].to_i }
|
||||
|
||||
expect(status_ids).to eq []
|
||||
end
|
||||
|
||||
it 'contains referred status' do
|
||||
get "/api/v1/statuses/#{status.id}/context", params: { with_reference: true }, headers: headers
|
||||
status_ids = body_as_json[:references].map { |ref| ref[:id].to_i }
|
||||
|
||||
expect(status_ids).to include referred.id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /api/v1/statuses' do
|
||||
|
@ -272,16 +167,6 @@ describe '/api/v1/statuses' do
|
|||
expect(response.headers['X-RateLimit-Remaining']).to eq '0'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with missing thread' do
|
||||
let(:params) { { status: 'Hello world', in_reply_to_id: 0 } }
|
||||
|
||||
it 'returns http not found' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE /api/v1/statuses/:id' do
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe 'API V1 Streaming' do
|
||||
around do |example|
|
||||
before = Rails.configuration.x.streaming_api_base_url
|
||||
Rails.configuration.x.streaming_api_base_url = "wss://#{Rails.configuration.x.web_domain}"
|
||||
example.run
|
||||
Rails.configuration.x.streaming_api_base_url = before
|
||||
end
|
||||
|
||||
let(:headers) { { 'Host' => Rails.configuration.x.web_domain } }
|
||||
|
||||
context 'with streaming api on same host' do
|
||||
describe 'GET /api/v1/streaming' do
|
||||
it 'raises ActiveRecord::RecordNotFound' do
|
||||
get '/api/v1/streaming', headers: headers
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with streaming api on different host' do
|
||||
before do
|
||||
Rails.configuration.x.streaming_api_base_url = "wss://streaming-#{Rails.configuration.x.web_domain}"
|
||||
end
|
||||
|
||||
describe 'GET /api/v1/streaming' do
|
||||
it 'redirects to streaming host' do
|
||||
get '/api/v1/streaming', headers: headers, params: { access_token: 'deadbeef', stream: 'public' }
|
||||
|
||||
expect(response)
|
||||
.to have_http_status(301)
|
||||
|
||||
expect(redirect_to_uri)
|
||||
.to have_attributes(
|
||||
fragment: request_uri.fragment,
|
||||
host: eq(streaming_host),
|
||||
path: request_uri.path,
|
||||
query: request_uri.query,
|
||||
scheme: request_uri.scheme
|
||||
)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def request_uri
|
||||
URI.parse(request.url)
|
||||
end
|
||||
|
||||
def redirect_to_uri
|
||||
URI.parse(response.location)
|
||||
end
|
||||
|
||||
def streaming_host
|
||||
URI.parse(Rails.configuration.x.streaming_api_base_url).host
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,55 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe 'API V1 Timelines Antenna' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:scopes) { 'read:statuses' }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
let(:antenna) { Fabricate(:antenna, account: user.account) }
|
||||
|
||||
context 'with a user context' do
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:lists') }
|
||||
|
||||
describe 'GET /api/v1/timelines/antenna/:id' do
|
||||
before do
|
||||
subscribe = Fabricate(:antenna_account)
|
||||
antenna.antenna_accounts << subscribe
|
||||
PostStatusService.new.call(subscribe.account, text: 'New status for user home timeline.')
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
get "/api/v1/timelines/antenna/#{antenna.id}", headers: headers
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with the wrong user context' do
|
||||
let(:other_user) { Fabricate(:user) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: other_user.id, scopes: 'read') }
|
||||
|
||||
describe 'GET #show' do
|
||||
it 'returns http not found' do
|
||||
get "/api/v1/timelines/antenna/#{antenna.id}", headers: headers
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'without a user context' do
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: nil, scopes: 'read') }
|
||||
|
||||
describe 'GET #show' do
|
||||
it 'returns http unprocessable entity' do
|
||||
get "/api/v1/timelines/antenna/#{antenna.id}", headers: headers
|
||||
|
||||
expect(response).to have_http_status(422)
|
||||
expect(response.headers['Link']).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -55,11 +55,10 @@ describe 'Home', :sidekiq_inline do
|
|||
it 'sets the correct pagination headers', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to include_pagination_headers(
|
||||
prev: api_v1_timelines_home_url(limit: params[:limit], min_id: ana.statuses.first.id),
|
||||
next: api_v1_timelines_home_url(limit: params[:limit], max_id: ana.statuses.first.id)
|
||||
)
|
||||
headers = response.headers['Link']
|
||||
|
||||
expect(headers.find_link(%w(rel prev)).href).to eq(api_v1_timelines_home_url(limit: 1, min_id: ana.statuses.first.id.to_s))
|
||||
expect(headers.find_link(%w(rel next)).href).to eq(api_v1_timelines_home_url(limit: 1, max_id: ana.statuses.first.id.to_s))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe 'API V1 Timelines List' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:scopes) { 'read:statuses' }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
let(:list) { Fabricate(:list, account: user.account) }
|
||||
|
||||
context 'with a user context' do
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:lists') }
|
||||
|
||||
describe 'GET /api/v1/timelines/list/:id' do
|
||||
before do
|
||||
follow = Fabricate(:follow, account: user.account)
|
||||
list.accounts << follow.target_account
|
||||
PostStatusService.new.call(follow.target_account, text: 'New status for user home timeline.')
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
get "/api/v1/timelines/list/#{list.id}", headers: headers
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with the wrong user context' do
|
||||
let(:other_user) { Fabricate(:user) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: other_user.id, scopes: 'read') }
|
||||
|
||||
describe 'GET #show' do
|
||||
it 'returns http not found' do
|
||||
get "/api/v1/timelines/list/#{list.id}", headers: headers
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'without a user context' do
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: nil, scopes: 'read') }
|
||||
|
||||
describe 'GET #show' do
|
||||
it 'returns http unprocessable entity' do
|
||||
get "/api/v1/timelines/list/#{list.id}", headers: headers
|
||||
|
||||
expect(response).to have_http_status(422)
|
||||
expect(response.headers['Link']).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -7,12 +7,9 @@ describe 'Public' do
|
|||
let(:scopes) { 'read:statuses' }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
let(:ltl_enabled) { true }
|
||||
|
||||
shared_examples 'a successful request to the public timeline' do
|
||||
it 'returns the expected statuses successfully', :aggregate_failures do
|
||||
Form::AdminSettings.new(enable_local_timeline: '0').save unless ltl_enabled
|
||||
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
|
@ -25,14 +22,12 @@ describe 'Public' do
|
|||
get '/api/v1/timelines/public', headers: headers, params: params
|
||||
end
|
||||
|
||||
let!(:local_status) { Fabricate(:status, text: 'ohagi', account: Fabricate.build(:account, domain: nil)) }
|
||||
let!(:remote_status) { Fabricate(:status, text: 'ohagi', account: Fabricate.build(:account, domain: 'example.com')) }
|
||||
let!(:media_status) { Fabricate(:status, text: 'ohagi', media_attachments: [Fabricate.build(:media_attachment)]) }
|
||||
let(:params) { {} }
|
||||
let!(:private_status) { Fabricate(:status, visibility: :private) } # rubocop:disable RSpec/LetSetup
|
||||
let!(:local_status) { Fabricate(:status, account: Fabricate.build(:account, domain: nil)) }
|
||||
let!(:remote_status) { Fabricate(:status, account: Fabricate.build(:account, domain: 'example.com')) }
|
||||
let!(:media_status) { Fabricate(:status, media_attachments: [Fabricate.build(:media_attachment)]) }
|
||||
|
||||
before do
|
||||
Fabricate(:status, visibility: :private)
|
||||
end
|
||||
let(:params) { {} }
|
||||
|
||||
context 'when the instance allows public preview' do
|
||||
let(:expected_statuses) { [local_status, remote_status, media_status] }
|
||||
|
@ -52,13 +47,6 @@ describe 'Public' do
|
|||
let(:expected_statuses) { [local_status, media_status] }
|
||||
|
||||
it_behaves_like 'a successful request to the public timeline'
|
||||
|
||||
context 'when local timeline is disabled' do
|
||||
let(:expected_statuses) { [] }
|
||||
let(:ltl_enabled) { false }
|
||||
|
||||
it_behaves_like 'a successful request to the public timeline'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with remote param' do
|
||||
|
@ -66,13 +54,6 @@ describe 'Public' do
|
|||
let(:expected_statuses) { [remote_status] }
|
||||
|
||||
it_behaves_like 'a successful request to the public timeline'
|
||||
|
||||
context 'when local timeline is disabled' do
|
||||
let(:ltl_enabled) { false }
|
||||
let(:expected_statuses) { [local_status, remote_status, media_status] }
|
||||
|
||||
it_behaves_like 'a successful request to the public timeline'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with local and remote params' do
|
||||
|
@ -80,12 +61,6 @@ describe 'Public' do
|
|||
let(:expected_statuses) { [local_status, remote_status, media_status] }
|
||||
|
||||
it_behaves_like 'a successful request to the public timeline'
|
||||
|
||||
context 'when local timeline is disabled' do
|
||||
let(:ltl_enabled) { false }
|
||||
|
||||
it_behaves_like 'a successful request to the public timeline'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with only_media param' do
|
||||
|
@ -108,11 +83,10 @@ describe 'Public' do
|
|||
it 'sets the correct pagination headers', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to include_pagination_headers(
|
||||
prev: api_v1_timelines_public_url(limit: params[:limit], min_id: media_status.id),
|
||||
next: api_v1_timelines_public_url(limit: params[:limit], max_id: media_status.id)
|
||||
)
|
||||
headers = response.headers['Link']
|
||||
|
||||
expect(headers.find_link(%w(rel prev)).href).to eq(api_v1_timelines_public_url(limit: 1, min_id: media_status.id.to_s))
|
||||
expect(headers.find_link(%w(rel next)).href).to eq(api_v1_timelines_public_url(limit: 1, max_id: media_status.id.to_s))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -138,61 +112,5 @@ describe 'Public' do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user is setting filters' do
|
||||
subject do
|
||||
get '/api/v1/timelines/public', headers: headers, params: params
|
||||
body_as_json.filter { |status| status[:filtered].empty? || status[:filtered][0][:filter][:id] != filter.id.to_s }.map { |status| status[:id].to_i }
|
||||
end
|
||||
|
||||
before do
|
||||
Fabricate(:custom_filter_keyword, custom_filter: filter, keyword: 'ohagi')
|
||||
Fabricate(:follow, account: account, target_account: remote_account)
|
||||
end
|
||||
|
||||
let(:exclude_follows) { false }
|
||||
let(:exclude_localusers) { false }
|
||||
let(:include_quotes) { false }
|
||||
let(:account) { user.account }
|
||||
let(:remote_account) { remote_status.account }
|
||||
let!(:filter) { Fabricate(:custom_filter, account: account, exclude_follows: exclude_follows, exclude_localusers: exclude_localusers, with_quote: include_quotes) }
|
||||
let!(:quote_status) { Fabricate(:status, quote: Fabricate(:status, text: 'ohagi')) }
|
||||
|
||||
it 'load statuses', :aggregate_failures do
|
||||
ids = subject
|
||||
expect(ids).to_not include(local_status.id)
|
||||
expect(ids).to_not include(remote_status.id)
|
||||
end
|
||||
|
||||
context 'when exclude_followers' do
|
||||
let(:exclude_follows) { true }
|
||||
|
||||
it 'load statuses', :aggregate_failures do
|
||||
ids = subject
|
||||
expect(ids).to_not include(local_status.id)
|
||||
expect(ids).to include(remote_status.id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when exclude_localusers' do
|
||||
let(:exclude_localusers) { true }
|
||||
|
||||
it 'load statuses', :aggregate_failures do
|
||||
ids = subject
|
||||
expect(ids).to include(local_status.id)
|
||||
expect(ids).to_not include(remote_status.id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when include_quotes' do
|
||||
let(:with_quote) { true }
|
||||
|
||||
it 'load statuses', :aggregate_failures do
|
||||
ids = subject
|
||||
expect(ids).to_not include(local_status.id)
|
||||
expect(ids).to include(quote_status.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -71,11 +71,10 @@ RSpec.describe 'Tag' do
|
|||
it 'sets the correct pagination headers', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect(response)
|
||||
.to include_pagination_headers(
|
||||
prev: api_v1_timelines_tag_url(limit: params[:limit], min_id: love_status.id),
|
||||
next: api_v1_timelines_tag_url(limit: params[:limit], max_id: love_status.id)
|
||||
)
|
||||
headers = response.headers['Link']
|
||||
|
||||
expect(headers.find_link(%w(rel prev)).href).to eq(api_v1_timelines_tag_url(limit: 1, min_id: love_status.id.to_s))
|
||||
expect(headers.find_link(%w(rel next)).href).to eq(api_v1_timelines_tag_url(limit: 1, max_id: love_status.id.to_s))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'API V1 Trends Links' do
|
||||
describe 'GET /api/v1/trends/links' do
|
||||
context 'when trends are disabled' do
|
||||
before { Setting.trends = false }
|
||||
|
||||
it 'returns http success' do
|
||||
get '/api/v1/trends/links'
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when trends are enabled' do
|
||||
before { Setting.trends = true }
|
||||
|
||||
it 'returns http success' do
|
||||
prepare_trends
|
||||
stub_const('Api::V1::Trends::LinksController::DEFAULT_LINKS_LIMIT', 2)
|
||||
get '/api/v1/trends/links'
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.headers).to include('Link')
|
||||
end
|
||||
|
||||
def prepare_trends
|
||||
Fabricate.times(3, :preview_card, trendable: true, language: 'en').each do |link|
|
||||
2.times { |i| Trends.links.add(link, i) }
|
||||
end
|
||||
Trends::Links.new(threshold: 1).refresh
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,37 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'API V1 Trends Statuses' do
|
||||
describe 'GET /api/v1/trends/statuses' do
|
||||
context 'when trends are disabled' do
|
||||
before { Setting.trends = false }
|
||||
|
||||
it 'returns http success' do
|
||||
get '/api/v1/trends/statuses'
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when trends are enabled' do
|
||||
before { Setting.trends = true }
|
||||
|
||||
it 'returns http success' do
|
||||
prepare_trends
|
||||
stub_const('Api::BaseController::DEFAULT_STATUSES_LIMIT', 2)
|
||||
get '/api/v1/trends/statuses'
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.headers).to include('Link')
|
||||
end
|
||||
|
||||
def prepare_trends
|
||||
Fabricate.times(3, :status, trendable: true, language: 'en').each do |status|
|
||||
2.times { |i| Trends.statuses.add(status, i) }
|
||||
end
|
||||
Trends::Statuses.new(threshold: 1, decay_threshold: -1).refresh
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,38 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'API V1 Trends Tags' do
|
||||
describe 'GET /api/v1/trends/tags' do
|
||||
context 'when trends are disabled' do
|
||||
before { Setting.trends = false }
|
||||
|
||||
it 'returns http success' do
|
||||
get '/api/v1/trends/tags'
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.headers).to_not include('Link')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when trends are enabled' do
|
||||
before { Setting.trends = true }
|
||||
|
||||
it 'returns http success' do
|
||||
prepare_trends
|
||||
stub_const('Api::V1::Trends::TagsController::DEFAULT_TAGS_LIMIT', 2)
|
||||
get '/api/v1/trends/tags'
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
expect(response.headers).to include('Link')
|
||||
end
|
||||
|
||||
def prepare_trends
|
||||
Fabricate.times(3, :tag, trendable: true).each do |tag|
|
||||
2.times { |i| Trends.tags.add(tag, i) }
|
||||
end
|
||||
Trends::Tags.new(threshold: 1).refresh
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,91 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'API V2 Admin Accounts' do
|
||||
let(:role) { UserRole.find_by(name: 'Moderator') }
|
||||
let(:user) { Fabricate(:user, role: role) }
|
||||
let(:scopes) { 'admin:read admin:write' }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
let(:account) { Fabricate(:account) }
|
||||
|
||||
describe 'GET #index' do
|
||||
let!(:remote_account) { Fabricate(:account, domain: 'example.org') }
|
||||
let!(:other_remote_account) { Fabricate(:account, domain: 'foo.bar') }
|
||||
let!(:suspended_account) { Fabricate(:account, suspended: true) }
|
||||
let!(:suspended_remote) { Fabricate(:account, domain: 'foo.bar', suspended: true) }
|
||||
let!(:disabled_account) { Fabricate(:user, disabled: true).account }
|
||||
let!(:pending_account) { Fabricate(:user, approved: false).account }
|
||||
let!(:admin_account) { user.account }
|
||||
|
||||
let(:params) { {} }
|
||||
|
||||
before do
|
||||
pending_account.user.update(approved: false)
|
||||
|
||||
get '/api/v2/admin/accounts', params: params, headers: headers
|
||||
end
|
||||
|
||||
it_behaves_like 'forbidden for wrong scope', 'write:statuses'
|
||||
it_behaves_like 'forbidden for wrong role', ''
|
||||
|
||||
context 'when called with status active and origin local and permissions staff' do
|
||||
let(:params) { { status: 'active', origin: 'local', permissions: 'staff' } }
|
||||
|
||||
it 'returns the correct accounts' do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(body_json_ids).to eq([admin_account.id])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when called with by_domain value and origin remote' do
|
||||
let(:params) { { by_domain: 'example.org', origin: 'remote' } }
|
||||
|
||||
it 'returns the correct accounts' do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(body_json_ids).to include(remote_account.id)
|
||||
expect(body_json_ids).to_not include(other_remote_account.id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when called with status suspended' do
|
||||
let(:params) { { status: 'suspended' } }
|
||||
|
||||
it 'returns the correct accounts' do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(body_json_ids).to include(suspended_remote.id, suspended_account.id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when called with status disabled' do
|
||||
let(:params) { { status: 'disabled' } }
|
||||
|
||||
it 'returns the correct accounts' do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(body_json_ids).to include(disabled_account.id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when called with status pending' do
|
||||
let(:params) { { status: 'pending' } }
|
||||
|
||||
it 'returns the correct accounts' do
|
||||
expect(response).to have_http_status(200)
|
||||
expect(body_json_ids).to include(pending_account.id)
|
||||
end
|
||||
end
|
||||
|
||||
def body_json_ids
|
||||
body_as_json.map { |a| a[:id].to_i }
|
||||
end
|
||||
|
||||
context 'with limit param' do
|
||||
let(:params) { { limit: 1 } }
|
||||
|
||||
it 'sets the correct pagination headers' do
|
||||
expect(response.headers['Link'].find_link(%w(rel next)).href).to eq api_v2_admin_accounts_url(limit: 1, max_id: admin_account.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,133 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'API V2 Filters Keywords' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:filter) { Fabricate(:custom_filter, account: user.account) }
|
||||
let(:other_user) { Fabricate(:user) }
|
||||
let(:other_filter) { Fabricate(:custom_filter, account: other_user.account) }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
|
||||
describe 'GET /api/v2/filters/:filter_id/keywords' do
|
||||
let(:scopes) { 'read:filters' }
|
||||
let!(:keyword) { Fabricate(:custom_filter_keyword, custom_filter: filter) }
|
||||
|
||||
it 'returns http success' do
|
||||
get "/api/v2/filters/#{filter.id}/keywords", headers: headers
|
||||
expect(response).to have_http_status(200)
|
||||
expect(body_as_json)
|
||||
.to contain_exactly(
|
||||
include(id: keyword.id.to_s)
|
||||
)
|
||||
end
|
||||
|
||||
context "when trying to access another's user filters" do
|
||||
it 'returns http not found' do
|
||||
get "/api/v2/filters/#{other_filter.id}/keywords", headers: headers
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /api/v2/filters/:filter_id/keywords' do
|
||||
let(:scopes) { 'write:filters' }
|
||||
let(:filter_id) { filter.id }
|
||||
|
||||
before do
|
||||
post "/api/v2/filters/#{filter_id}/keywords", headers: headers, params: { keyword: 'magic', whole_word: false }
|
||||
end
|
||||
|
||||
it 'creates a filter', :aggregate_failures do
|
||||
expect(response).to have_http_status(200)
|
||||
|
||||
json = body_as_json
|
||||
expect(json[:keyword]).to eq 'magic'
|
||||
expect(json[:whole_word]).to be false
|
||||
|
||||
filter = user.account.custom_filters.first
|
||||
expect(filter).to_not be_nil
|
||||
expect(filter.keywords.pluck(:keyword)).to eq ['magic']
|
||||
end
|
||||
|
||||
context "when trying to add to another another's user filters" do
|
||||
let(:filter_id) { other_filter.id }
|
||||
|
||||
it 'returns http not found' do
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /api/v2/filters/keywords/:id' do
|
||||
let(:scopes) { 'read:filters' }
|
||||
let(:keyword) { Fabricate(:custom_filter_keyword, keyword: 'foo', whole_word: false, custom_filter: filter) }
|
||||
|
||||
before do
|
||||
get "/api/v2/filters/keywords/#{keyword.id}", headers: headers
|
||||
end
|
||||
|
||||
it 'responds with the keyword', :aggregate_failures do
|
||||
expect(response).to have_http_status(200)
|
||||
|
||||
json = body_as_json
|
||||
expect(json[:keyword]).to eq 'foo'
|
||||
expect(json[:whole_word]).to be false
|
||||
end
|
||||
|
||||
context "when trying to access another user's filter keyword" do
|
||||
let(:keyword) { Fabricate(:custom_filter_keyword, custom_filter: other_filter) }
|
||||
|
||||
it 'returns http not found' do
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT /api/v2/filters/keywords/:id' do
|
||||
let(:scopes) { 'write:filters' }
|
||||
let(:keyword) { Fabricate(:custom_filter_keyword, custom_filter: filter) }
|
||||
|
||||
before do
|
||||
put "/api/v2/filters/keywords/#{keyword.id}", headers: headers, params: { keyword: 'updated' }
|
||||
end
|
||||
|
||||
it 'updates the keyword', :aggregate_failures do
|
||||
expect(response).to have_http_status(200)
|
||||
|
||||
expect(keyword.reload.keyword).to eq 'updated'
|
||||
end
|
||||
|
||||
context "when trying to update another user's filter keyword" do
|
||||
let(:keyword) { Fabricate(:custom_filter_keyword, custom_filter: other_filter) }
|
||||
|
||||
it 'returns http not found' do
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE /api/v2/filters/keywords/:id' do
|
||||
let(:scopes) { 'write:filters' }
|
||||
let(:keyword) { Fabricate(:custom_filter_keyword, custom_filter: filter) }
|
||||
|
||||
before do
|
||||
delete "/api/v2/filters/keywords/#{keyword.id}", headers: headers
|
||||
end
|
||||
|
||||
it 'destroys the keyword', :aggregate_failures do
|
||||
expect(response).to have_http_status(200)
|
||||
|
||||
expect { keyword.reload }.to raise_error ActiveRecord::RecordNotFound
|
||||
end
|
||||
|
||||
context "when trying to update another user's filter keyword" do
|
||||
let(:keyword) { Fabricate(:custom_filter_keyword, custom_filter: other_filter) }
|
||||
|
||||
it 'returns http not found' do
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,109 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'API V2 Filters Statuses' do
|
||||
let(:user) { Fabricate(:user) }
|
||||
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
|
||||
let(:filter) { Fabricate(:custom_filter, account: user.account) }
|
||||
let(:other_user) { Fabricate(:user) }
|
||||
let(:other_filter) { Fabricate(:custom_filter, account: other_user.account) }
|
||||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
|
||||
describe 'GET /api/v2/filters/:filter_id/statuses' do
|
||||
let(:scopes) { 'read:filters' }
|
||||
let!(:status_filter) { Fabricate(:custom_filter_status, custom_filter: filter) }
|
||||
|
||||
it 'returns http success' do
|
||||
get "/api/v2/filters/#{filter.id}/statuses", headers: headers
|
||||
expect(response).to have_http_status(200)
|
||||
expect(body_as_json)
|
||||
.to contain_exactly(
|
||||
include(id: status_filter.id.to_s)
|
||||
)
|
||||
end
|
||||
|
||||
context "when trying to access another's user filters" do
|
||||
it 'returns http not found' do
|
||||
get "/api/v2/filters/#{other_filter.id}/statuses", headers: headers
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #create' do
|
||||
let(:scopes) { 'write:filters' }
|
||||
let(:filter_id) { filter.id }
|
||||
let!(:status) { Fabricate(:status) }
|
||||
|
||||
before do
|
||||
post "/api/v2/filters/#{filter_id}/statuses", headers: headers, params: { status_id: status.id }
|
||||
end
|
||||
|
||||
it 'creates a filter', :aggregate_failures do
|
||||
expect(response).to have_http_status(200)
|
||||
|
||||
json = body_as_json
|
||||
expect(json[:status_id]).to eq status.id.to_s
|
||||
|
||||
filter = user.account.custom_filters.first
|
||||
expect(filter).to_not be_nil
|
||||
expect(filter.statuses.pluck(:status_id)).to eq [status.id]
|
||||
end
|
||||
|
||||
context "when trying to add to another another's user filters" do
|
||||
let(:filter_id) { other_filter.id }
|
||||
|
||||
it 'returns http not found' do
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /api/v2/filters/statuses/:id' do
|
||||
let(:scopes) { 'read:filters' }
|
||||
let!(:status_filter) { Fabricate(:custom_filter_status, custom_filter: filter) }
|
||||
|
||||
before do
|
||||
get "/api/v2/filters/statuses/#{status_filter.id}", headers: headers
|
||||
end
|
||||
|
||||
it 'responds with the filter', :aggregate_failures do
|
||||
expect(response).to have_http_status(200)
|
||||
|
||||
json = body_as_json
|
||||
expect(json[:status_id]).to eq status_filter.status_id.to_s
|
||||
end
|
||||
|
||||
context "when trying to access another user's filter keyword" do
|
||||
let(:status_filter) { Fabricate(:custom_filter_status, custom_filter: other_filter) }
|
||||
|
||||
it 'returns http not found' do
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE /api/v2/filters/statuses/:id' do
|
||||
let(:scopes) { 'write:filters' }
|
||||
let(:status_filter) { Fabricate(:custom_filter_status, custom_filter: filter) }
|
||||
|
||||
before do
|
||||
delete "/api/v2/filters/statuses/#{status_filter.id}", headers: headers
|
||||
end
|
||||
|
||||
it 'destroys the filter', :aggregate_failures do
|
||||
expect(response).to have_http_status(200)
|
||||
|
||||
expect { status_filter.reload }.to raise_error ActiveRecord::RecordNotFound
|
||||
end
|
||||
|
||||
context "when trying to update another user's filter keyword" do
|
||||
let(:status_filter) { Fabricate(:custom_filter_status, custom_filter: other_filter) }
|
||||
|
||||
it 'returns http not found' do
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -47,16 +47,7 @@ RSpec.describe 'Filters' do
|
|||
it_behaves_like 'unauthorized for invalid token'
|
||||
|
||||
context 'with valid params' do
|
||||
let(:params) do
|
||||
{
|
||||
title: 'magic',
|
||||
context: %w(home),
|
||||
filter_action: 'hide',
|
||||
exclude_follows: true,
|
||||
exclude_localusers: true,
|
||||
keywords_attributes: [keyword: 'magic', whole_word: true],
|
||||
}
|
||||
end
|
||||
let(:params) { { title: 'magic', context: %w(home), filter_action: 'hide', keywords_attributes: [keyword: 'magic'] } }
|
||||
|
||||
it 'returns http success' do
|
||||
subject
|
||||
|
@ -73,8 +64,6 @@ RSpec.describe 'Filters' do
|
|||
expect(json[:filter_action]).to eq 'hide'
|
||||
expect(json[:context]).to eq ['home']
|
||||
expect(json[:keywords].map { |keyword| keyword.slice(:keyword, :whole_word) }).to eq [{ keyword: 'magic', whole_word: true }]
|
||||
expect(json[:exclude_follows]).to be true
|
||||
expect(json[:exclude_localusers]).to be true
|
||||
end
|
||||
|
||||
it 'creates a filter', :aggregate_failures do
|
||||
|
@ -85,15 +74,13 @@ RSpec.describe 'Filters' do
|
|||
expect(filter).to be_present
|
||||
expect(filter.keywords.pluck(:keyword)).to eq ['magic']
|
||||
expect(filter.context).to eq %w(home)
|
||||
expect(filter.exclude_follows).to be true
|
||||
expect(filter.exclude_localusers).to be true
|
||||
expect(filter.irreversible?).to be true
|
||||
expect(filter.expires_at).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the required title param is missing' do
|
||||
let(:params) { { context: %w(home), filter_action: 'hide', keywords_attributes: [keyword: 'magic'], exclude_follows: false, exclude_localusers: false } }
|
||||
let(:params) { { context: %w(home), filter_action: 'hide', keywords_attributes: [keyword: 'magic'] } }
|
||||
|
||||
it 'returns http unprocessable entity' do
|
||||
subject
|
||||
|
@ -103,7 +90,7 @@ RSpec.describe 'Filters' do
|
|||
end
|
||||
|
||||
context 'when the required context param is missing' do
|
||||
let(:params) { { title: 'magic', filter_action: 'hide', keywords_attributes: [keyword: 'magic'], exclude_follows: false, exclude_localusers: false } }
|
||||
let(:params) { { title: 'magic', filter_action: 'hide', keywords_attributes: [keyword: 'magic'] } }
|
||||
|
||||
it 'returns http unprocessable entity' do
|
||||
subject
|
||||
|
@ -112,18 +99,8 @@ RSpec.describe 'Filters' do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when the required kmyblue original params are missing' do
|
||||
let(:params) { { title: 'magic', context: %w(home), filter_action: 'hide', keywords_attributes: [keyword: 'magic'] } }
|
||||
|
||||
it 'returns http success' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the given context value is invalid' do
|
||||
let(:params) { { title: 'magic', context: %w(shaolin), filter_action: 'hide', keywords_attributes: [keyword: 'magic'], exclude_follows: false, exclude_localusers: false } }
|
||||
let(:params) { { title: 'magic', context: %w(shaolin), filter_action: 'hide', keywords_attributes: [keyword: 'magic'] } }
|
||||
|
||||
it 'returns http unprocessable entity' do
|
||||
subject
|
||||
|
@ -175,7 +152,7 @@ RSpec.describe 'Filters' do
|
|||
|
||||
context 'when updating filter parameters' do
|
||||
context 'with valid params' do
|
||||
let(:params) { { title: 'updated', context: %w(home public), exclude_follows: true, exclude_localusers: true } }
|
||||
let(:params) { { title: 'updated', context: %w(home public) } }
|
||||
|
||||
it 'updates the filter successfully', :aggregate_failures do
|
||||
subject
|
||||
|
@ -185,8 +162,6 @@ RSpec.describe 'Filters' do
|
|||
expect(response).to have_http_status(200)
|
||||
expect(filter.title).to eq 'updated'
|
||||
expect(filter.reload.context).to eq %w(home public)
|
||||
expect(filter.exclude_follows).to be true
|
||||
expect(filter.exclude_localusers).to be true
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@ describe 'Instances' do
|
|||
expect(body_as_json)
|
||||
.to be_present
|
||||
.and include(title: 'Mastodon')
|
||||
.and include_configuration_limits
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -32,26 +31,7 @@ describe 'Instances' do
|
|||
expect(body_as_json)
|
||||
.to be_present
|
||||
.and include(title: 'Mastodon')
|
||||
.and include_configuration_limits
|
||||
end
|
||||
end
|
||||
|
||||
def include_configuration_limits
|
||||
include(
|
||||
configuration: include(
|
||||
accounts: include(
|
||||
max_featured_tags: FeaturedTag::LIMIT,
|
||||
max_pinned_statuses: StatusPinValidator::PIN_LIMIT
|
||||
),
|
||||
statuses: include(
|
||||
max_characters: StatusLengthValidator::MAX_CHARS,
|
||||
max_media_attachments: 4 # TODO, move to constant somewhere
|
||||
),
|
||||
polls: include(
|
||||
max_options: PollValidator::MAX_OPTIONS
|
||||
)
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,28 +9,10 @@ describe 'Suggestions API' do
|
|||
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
|
||||
|
||||
describe 'GET /api/v2/suggestions' do
|
||||
let(:bob) { Fabricate(:account) }
|
||||
let(:jeff) { Fabricate(:account) }
|
||||
let(:params) { {} }
|
||||
|
||||
before do
|
||||
Setting.bootstrap_timeline_accounts = [bob, jeff].map(&:acct).join(',')
|
||||
end
|
||||
|
||||
it 'returns the expected suggestions' do
|
||||
it 'returns http success' do
|
||||
get '/api/v2/suggestions', headers: headers
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
|
||||
expect(body_as_json).to match_array(
|
||||
[bob, jeff].map do |account|
|
||||
hash_including({
|
||||
source: 'staff',
|
||||
sources: ['featured'],
|
||||
account: hash_including({ id: account.id.to_s }),
|
||||
})
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -137,18 +137,6 @@ RSpec.describe '/api/web/embed' do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when sanitizing the fragment fails' do
|
||||
let(:call_result) { { html: 'ok' } }
|
||||
|
||||
before { allow(Sanitize).to receive(:fragment).and_raise(ArgumentError) }
|
||||
|
||||
it 'returns http not found' do
|
||||
subject
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when failing to fetch OEmbed' do
|
||||
let(:call_result) { nil }
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue