Revert "Upstream 20240517"

This commit is contained in:
KMY(雪あすか) 2024-05-24 08:15:12 +09:00 committed by GitHub
parent 9c006fd893
commit f6dec44e95
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2347 changed files with 26470 additions and 87494 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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',
}
)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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' }

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 }