Merge tag 'v2.7.2' into instance_only_statuses
This commit is contained in:
@ -1,7 +1,7 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::Activity::Announce do
|
||||
let(:sender) { Fabricate(:account) }
|
||||
let(:sender) { Fabricate(:account, followers_url: 'http://example.com/followers', uri: 'https://example.com/actor') }
|
||||
let(:recipient) { Fabricate(:account) }
|
||||
let(:status) { Fabricate(:status, account: recipient) }
|
||||
|
||||
@ -10,20 +10,162 @@ RSpec.describe ActivityPub::Activity::Announce do
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
id: 'foo',
|
||||
type: 'Announce',
|
||||
actor: ActivityPub::TagManager.instance.uri_for(sender),
|
||||
object: ActivityPub::TagManager.instance.uri_for(status),
|
||||
actor: 'https://example.com/actor',
|
||||
object: object_json,
|
||||
}.with_indifferent_access
|
||||
end
|
||||
|
||||
describe '#perform' do
|
||||
subject { described_class.new(json, sender) }
|
||||
let(:unknown_object_json) do
|
||||
{
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
id: 'https://example.com/actor/hello-world',
|
||||
type: 'Note',
|
||||
attributedTo: 'https://example.com/actor',
|
||||
content: 'Hello world',
|
||||
to: 'http://example.com/followers',
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
subject.perform
|
||||
subject { described_class.new(json, sender) }
|
||||
|
||||
describe '#perform' do
|
||||
context 'when sender is followed by a local account' do
|
||||
before do
|
||||
Fabricate(:account).follow!(sender)
|
||||
stub_request(:get, 'https://example.com/actor/hello-world').to_return(body: Oj.dump(unknown_object_json))
|
||||
subject.perform
|
||||
end
|
||||
|
||||
context 'a known status' do
|
||||
let(:object_json) do
|
||||
ActivityPub::TagManager.instance.uri_for(status)
|
||||
end
|
||||
|
||||
it 'creates a reblog by sender of status' do
|
||||
expect(sender.reblogged?(status)).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context 'an unknown status' do
|
||||
let(:object_json) { 'https://example.com/actor/hello-world' }
|
||||
|
||||
it 'creates a reblog by sender of status' do
|
||||
reblog = sender.statuses.first
|
||||
|
||||
expect(reblog).to_not be_nil
|
||||
expect(reblog.reblog.text).to eq 'Hello world'
|
||||
end
|
||||
end
|
||||
|
||||
context 'self-boost of a previously unknown status with missing attributedTo' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'https://example.com/actor#bar',
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
to: 'http://example.com/followers',
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates a reblog by sender of status' do
|
||||
expect(sender.reblogged?(sender.statuses.first)).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context 'self-boost of a previously unknown status with correct attributedTo' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'https://example.com/actor#bar',
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
attributedTo: 'https://example.com/actor',
|
||||
to: 'http://example.com/followers',
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates a reblog by sender of status' do
|
||||
expect(sender.reblogged?(sender.statuses.first)).to be true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'creates a reblog by sender of status' do
|
||||
expect(sender.reblogged?(status)).to be true
|
||||
context 'when the status belongs to a local user' do
|
||||
before do
|
||||
subject.perform
|
||||
end
|
||||
|
||||
let(:object_json) do
|
||||
ActivityPub::TagManager.instance.uri_for(status)
|
||||
end
|
||||
|
||||
it 'creates a reblog by sender of status' do
|
||||
expect(sender.reblogged?(status)).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the sender is relayed' do
|
||||
let!(:relay_account) { Fabricate(:account, inbox_url: 'https://relay.example.com/inbox') }
|
||||
let!(:relay) { Fabricate(:relay, inbox_url: 'https://relay.example.com/inbox') }
|
||||
|
||||
subject { described_class.new(json, sender, relayed_through_account: relay_account) }
|
||||
|
||||
context 'and the relay is enabled' do
|
||||
before do
|
||||
relay.update(state: :accepted)
|
||||
subject.perform
|
||||
end
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'https://example.com/actor#bar',
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
to: 'http://example.com/followers',
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates a reblog by sender of status' do
|
||||
expect(sender.statuses.count).to eq 2
|
||||
end
|
||||
end
|
||||
|
||||
context 'and the relay is disabled' do
|
||||
before do
|
||||
subject.perform
|
||||
end
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'https://example.com/actor#bar',
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
to: 'http://example.com/followers',
|
||||
}
|
||||
end
|
||||
|
||||
it 'does not create anything' do
|
||||
expect(sender.statuses.count).to eq 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the sender has no relevance to local activity' do
|
||||
before do
|
||||
subject.perform
|
||||
end
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: 'https://example.com/actor#bar',
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
to: 'http://example.com/followers',
|
||||
}
|
||||
end
|
||||
|
||||
it 'does not create anything' do
|
||||
expect(sender.statuses.count).to eq 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -13,8 +13,6 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||
}.with_indifferent_access
|
||||
end
|
||||
|
||||
subject { described_class.new(json, sender) }
|
||||
|
||||
before do
|
||||
sender.update(uri: ActivityPub::TagManager.instance.uri_for(sender))
|
||||
|
||||
@ -23,11 +21,402 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||
end
|
||||
|
||||
describe '#perform' do
|
||||
before do
|
||||
subject.perform
|
||||
context 'when fetching' do
|
||||
subject { described_class.new(json, sender) }
|
||||
|
||||
before do
|
||||
subject.perform
|
||||
end
|
||||
|
||||
context 'standalone' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.text).to eq 'Lorem ipsum'
|
||||
end
|
||||
|
||||
it 'missing to/cc defaults to direct privacy' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.visibility).to eq 'direct'
|
||||
end
|
||||
end
|
||||
|
||||
context 'public' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
to: 'https://www.w3.org/ns/activitystreams#Public',
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.visibility).to eq 'public'
|
||||
end
|
||||
end
|
||||
|
||||
context 'unlisted' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
cc: 'https://www.w3.org/ns/activitystreams#Public',
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.visibility).to eq 'unlisted'
|
||||
end
|
||||
end
|
||||
|
||||
context 'private' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
to: 'http://example.com/followers',
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.visibility).to eq 'private'
|
||||
end
|
||||
end
|
||||
|
||||
context 'limited' do
|
||||
let(:recipient) { Fabricate(:account) }
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
to: ActivityPub::TagManager.instance.uri_for(recipient),
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.visibility).to eq 'limited'
|
||||
end
|
||||
|
||||
it 'creates silent mention' do
|
||||
status = sender.statuses.first
|
||||
expect(status.mentions.first).to be_silent
|
||||
end
|
||||
end
|
||||
|
||||
context 'direct' do
|
||||
let(:recipient) { Fabricate(:account) }
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
to: ActivityPub::TagManager.instance.uri_for(recipient),
|
||||
tag: {
|
||||
type: 'Mention',
|
||||
href: ActivityPub::TagManager.instance.uri_for(recipient),
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.visibility).to eq 'direct'
|
||||
end
|
||||
end
|
||||
|
||||
context 'as a reply' do
|
||||
let(:original_status) { Fabricate(:status) }
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
inReplyTo: ActivityPub::TagManager.instance.uri_for(original_status),
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.thread).to eq original_status
|
||||
expect(status.reply?).to be true
|
||||
expect(status.in_reply_to_account).to eq original_status.account
|
||||
expect(status.conversation).to eq original_status.conversation
|
||||
end
|
||||
end
|
||||
|
||||
context 'with mentions' do
|
||||
let(:recipient) { Fabricate(:account) }
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
tag: [
|
||||
{
|
||||
type: 'Mention',
|
||||
href: ActivityPub::TagManager.instance.uri_for(recipient),
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.mentions.map(&:account)).to include(recipient)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with mentions missing href' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
tag: [
|
||||
{
|
||||
type: 'Mention',
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
expect(status).to_not be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'with media attachments' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
attachment: [
|
||||
{
|
||||
type: 'Document',
|
||||
mediaType: 'image/png',
|
||||
url: 'http://example.com/attachment.png',
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.media_attachments.map(&:remote_url)).to include('http://example.com/attachment.png')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with media attachments with focal points' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
attachment: [
|
||||
{
|
||||
type: 'Document',
|
||||
mediaType: 'image/png',
|
||||
url: 'http://example.com/attachment.png',
|
||||
focalPoint: [0.5, -0.7],
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.media_attachments.map(&:focus)).to include('0.5,-0.7')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with media attachments missing url' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
attachment: [
|
||||
{
|
||||
type: 'Document',
|
||||
mediaType: 'image/png',
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
expect(status).to_not be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'with hashtags' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
tag: [
|
||||
{
|
||||
type: 'Hashtag',
|
||||
href: 'http://example.com/blah',
|
||||
name: '#test',
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.tags.map(&:name)).to include('test')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with hashtags missing name' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
tag: [
|
||||
{
|
||||
type: 'Hashtag',
|
||||
href: 'http://example.com/blah',
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
expect(status).to_not be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'with emojis' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum :tinking:',
|
||||
tag: [
|
||||
{
|
||||
type: 'Emoji',
|
||||
icon: {
|
||||
url: 'http://example.com/emoji.png',
|
||||
},
|
||||
name: 'tinking',
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.emojis.map(&:shortcode)).to include('tinking')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with emojis missing name' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum :tinking:',
|
||||
tag: [
|
||||
{
|
||||
type: 'Emoji',
|
||||
icon: {
|
||||
url: 'http://example.com/emoji.png',
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
expect(status).to_not be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'with emojis missing icon' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum :tinking:',
|
||||
tag: [
|
||||
{
|
||||
type: 'Emoji',
|
||||
name: 'tinking',
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
expect(status).to_not be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'standalone' do
|
||||
context 'when sender is followed by local users' do
|
||||
subject { described_class.new(json, sender, delivery: true) }
|
||||
|
||||
before do
|
||||
Fabricate(:account).follow!(sender)
|
||||
subject.perform
|
||||
end
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
@ -42,78 +431,23 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||
expect(status).to_not be_nil
|
||||
expect(status.text).to eq 'Lorem ipsum'
|
||||
end
|
||||
|
||||
it 'missing to/cc defaults to direct privacy' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.visibility).to eq 'direct'
|
||||
end
|
||||
end
|
||||
|
||||
context 'public' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
to: 'https://www.w3.org/ns/activitystreams#Public',
|
||||
}
|
||||
context 'when sender replies to local status' do
|
||||
let!(:local_status) { Fabricate(:status) }
|
||||
|
||||
subject { described_class.new(json, sender, delivery: true) }
|
||||
|
||||
before do
|
||||
subject.perform
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.visibility).to eq 'public'
|
||||
end
|
||||
end
|
||||
|
||||
context 'unlisted' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
cc: 'https://www.w3.org/ns/activitystreams#Public',
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.visibility).to eq 'unlisted'
|
||||
end
|
||||
end
|
||||
|
||||
context 'private' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
to: 'http://example.com/followers',
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.visibility).to eq 'private'
|
||||
end
|
||||
end
|
||||
|
||||
context 'limited' do
|
||||
let(:recipient) { Fabricate(:account) }
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
to: ActivityPub::TagManager.instance.uri_for(recipient),
|
||||
inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status),
|
||||
}
|
||||
end
|
||||
|
||||
@ -121,28 +455,25 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.visibility).to eq 'limited'
|
||||
end
|
||||
|
||||
it 'creates silent mention' do
|
||||
status = sender.statuses.first
|
||||
expect(status.mentions.first).to be_silent
|
||||
expect(status.text).to eq 'Lorem ipsum'
|
||||
end
|
||||
end
|
||||
|
||||
context 'direct' do
|
||||
let(:recipient) { Fabricate(:account) }
|
||||
context 'when sender targets a local user' do
|
||||
let!(:local_account) { Fabricate(:account) }
|
||||
|
||||
subject { described_class.new(json, sender, delivery: true) }
|
||||
|
||||
before do
|
||||
subject.perform
|
||||
end
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
to: ActivityPub::TagManager.instance.uri_for(recipient),
|
||||
tag: {
|
||||
type: 'Mention',
|
||||
href: ActivityPub::TagManager.instance.uri_for(recipient),
|
||||
},
|
||||
to: ActivityPub::TagManager.instance.uri_for(local_account),
|
||||
}
|
||||
end
|
||||
|
||||
@ -150,19 +481,25 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.visibility).to eq 'direct'
|
||||
expect(status.text).to eq 'Lorem ipsum'
|
||||
end
|
||||
end
|
||||
|
||||
context 'as a reply' do
|
||||
let(:original_status) { Fabricate(:status) }
|
||||
context 'when sender cc\'s a local user' do
|
||||
let!(:local_account) { Fabricate(:account) }
|
||||
|
||||
subject { described_class.new(json, sender, delivery: true) }
|
||||
|
||||
before do
|
||||
subject.perform
|
||||
end
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
inReplyTo: ActivityPub::TagManager.instance.uri_for(original_status),
|
||||
cc: ActivityPub::TagManager.instance.uri_for(local_account),
|
||||
}
|
||||
end
|
||||
|
||||
@ -170,240 +507,27 @@ RSpec.describe ActivityPub::Activity::Create do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.thread).to eq original_status
|
||||
expect(status.reply?).to be true
|
||||
expect(status.in_reply_to_account).to eq original_status.account
|
||||
expect(status.conversation).to eq original_status.conversation
|
||||
expect(status.text).to eq 'Lorem ipsum'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with mentions' do
|
||||
let(:recipient) { Fabricate(:account) }
|
||||
context 'when the sender has no relevance to local activity' do
|
||||
subject { described_class.new(json, sender, delivery: true) }
|
||||
|
||||
before do
|
||||
subject.perform
|
||||
end
|
||||
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
tag: [
|
||||
{
|
||||
type: 'Mention',
|
||||
href: ActivityPub::TagManager.instance.uri_for(recipient),
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.mentions.map(&:account)).to include(recipient)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with mentions missing href' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
tag: [
|
||||
{
|
||||
type: 'Mention',
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
expect(status).to_not be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'with media attachments' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
attachment: [
|
||||
{
|
||||
type: 'Document',
|
||||
mediaType: 'image/png',
|
||||
url: 'http://example.com/attachment.png',
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.media_attachments.map(&:remote_url)).to include('http://example.com/attachment.png')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with media attachments with focal points' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
attachment: [
|
||||
{
|
||||
type: 'Document',
|
||||
mediaType: 'image/png',
|
||||
url: 'http://example.com/attachment.png',
|
||||
focalPoint: [0.5, -0.7],
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.media_attachments.map(&:focus)).to include('0.5,-0.7')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with media attachments missing url' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
attachment: [
|
||||
{
|
||||
type: 'Document',
|
||||
mediaType: 'image/png',
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
expect(status).to_not be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'with hashtags' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
tag: [
|
||||
{
|
||||
type: 'Hashtag',
|
||||
href: 'http://example.com/blah',
|
||||
name: '#test',
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.tags.map(&:name)).to include('test')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with hashtags missing name' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum',
|
||||
tag: [
|
||||
{
|
||||
type: 'Hashtag',
|
||||
href: 'http://example.com/blah',
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
expect(status).to_not be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'with emojis' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum :tinking:',
|
||||
tag: [
|
||||
{
|
||||
type: 'Emoji',
|
||||
icon: {
|
||||
url: 'http://example.com/emoji.png',
|
||||
},
|
||||
name: 'tinking',
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
|
||||
expect(status).to_not be_nil
|
||||
expect(status.emojis.map(&:shortcode)).to include('tinking')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with emojis missing name' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum :tinking:',
|
||||
tag: [
|
||||
{
|
||||
type: 'Emoji',
|
||||
icon: {
|
||||
url: 'http://example.com/emoji.png',
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
expect(status).to_not be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'with emojis missing icon' do
|
||||
let(:object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
|
||||
type: 'Note',
|
||||
content: 'Lorem ipsum :tinking:',
|
||||
tag: [
|
||||
{
|
||||
type: 'Emoji',
|
||||
name: 'tinking',
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates status' do
|
||||
status = sender.statuses.first
|
||||
expect(status).to_not be_nil
|
||||
it 'does not create anything' do
|
||||
expect(sender.statuses.count).to eq 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -74,10 +74,36 @@ RSpec.describe Formatter do
|
||||
end
|
||||
|
||||
context 'given a URL with a query string' do
|
||||
let(:text) { 'https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&q=autolink' }
|
||||
context 'with escaped unicode character' do
|
||||
let(:text) { 'https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&q=autolink' }
|
||||
|
||||
it 'matches the full URL' do
|
||||
is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&q=autolink"'
|
||||
it 'matches the full URL' do
|
||||
is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&q=autolink"'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with unicode character' do
|
||||
let(:text) { 'https://www.ruby-toolbox.com/search?utf8=✓&q=autolink' }
|
||||
|
||||
it 'matches the full URL' do
|
||||
is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=✓&q=autolink"'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with unicode character at the end' do
|
||||
let(:text) { 'https://www.ruby-toolbox.com/search?utf8=✓' }
|
||||
|
||||
it 'matches the full URL' do
|
||||
is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=✓"'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with escaped and not escaped unicode characters' do
|
||||
let(:text) { 'https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&utf81=✓&q=autolink' }
|
||||
|
||||
it 'preserves escaped unicode characters' do
|
||||
is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&utf81=✓&q=autolink"'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -89,6 +115,22 @@ RSpec.describe Formatter do
|
||||
end
|
||||
end
|
||||
|
||||
context 'given a URL in quotation marks' do
|
||||
let(:text) { '"https://example.com/"' }
|
||||
|
||||
it 'does not match the quotation marks' do
|
||||
is_expected.to include 'href="https://example.com/"'
|
||||
end
|
||||
end
|
||||
|
||||
context 'given a URL in angle brackets' do
|
||||
let(:text) { '<https://example.com/>' }
|
||||
|
||||
it 'does not match the angle brackets' do
|
||||
is_expected.to include 'href="https://example.com/"'
|
||||
end
|
||||
end
|
||||
|
||||
context 'given a URL with Japanese path string' do
|
||||
let(:text) { 'https://ja.wikipedia.org/wiki/日本' }
|
||||
|
||||
@ -105,6 +147,22 @@ RSpec.describe Formatter do
|
||||
end
|
||||
end
|
||||
|
||||
context 'given a URL with a full-width space' do
|
||||
let(:text) { 'https://example.com/ abc123' }
|
||||
|
||||
it 'does not match the full-width space' do
|
||||
is_expected.to include 'href="https://example.com/"'
|
||||
end
|
||||
end
|
||||
|
||||
context 'given a URL in Japanese quotation marks' do
|
||||
let(:text) { '「[https://example.org/」' }
|
||||
|
||||
it 'does not match the quotation marks' do
|
||||
is_expected.to include 'href="https://example.org/"'
|
||||
end
|
||||
end
|
||||
|
||||
context 'given a URL with Simplified Chinese path string' do
|
||||
let(:text) { 'https://baike.baidu.com/item/中华人民共和国' }
|
||||
|
||||
@ -124,7 +182,11 @@ RSpec.describe Formatter do
|
||||
context 'given a URL containing unsafe code (XSS attack, visible part)' do
|
||||
let(:text) { %q{http://example.com/b<del>b</del>} }
|
||||
|
||||
it 'escapes the HTML in the URL' do
|
||||
it 'does not include the HTML in the URL' do
|
||||
is_expected.to include '"http://example.com/b"'
|
||||
end
|
||||
|
||||
it 'escapes the HTML' do
|
||||
is_expected.to include '<del>b</del>'
|
||||
end
|
||||
end
|
||||
@ -132,7 +194,11 @@ RSpec.describe Formatter do
|
||||
context 'given a URL containing unsafe code (XSS attack, invisible part)' do
|
||||
let(:text) { %q{http://example.com/blahblahblahblah/a<script>alert("Hello")</script>} }
|
||||
|
||||
it 'escapes the HTML in the URL' do
|
||||
it 'does not include the HTML in the URL' do
|
||||
is_expected.to include '"http://example.com/blahblahblahblah/a"'
|
||||
end
|
||||
|
||||
it 'escapes the HTML' do
|
||||
is_expected.to include '<script>alert("Hello")</script>'
|
||||
end
|
||||
end
|
||||
@ -168,6 +234,14 @@ RSpec.describe Formatter do
|
||||
is_expected.to include '/tags/hashtag" class="mention hashtag" rel="tag">#<span>hashtag</span></a>'
|
||||
end
|
||||
end
|
||||
|
||||
context 'given text containing a hashtag with Unicode chars' do
|
||||
let(:text) { '#hashtagタグ' }
|
||||
|
||||
it 'creates a hashtag link' do
|
||||
is_expected.to include '/tags/hashtag%E3%82%BF%E3%82%B0" class="mention hashtag" rel="tag">#<span>hashtagタグ</span></a>'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#format_spoiler' do
|
||||
|
||||
@ -11,6 +11,7 @@ describe EmailMxValidator do
|
||||
|
||||
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::MX).and_return([])
|
||||
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::A).and_return([])
|
||||
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::AAAA).and_return([])
|
||||
allow(resolver).to receive(:timeouts=).and_return(nil)
|
||||
allow(Resolv::DNS).to receive(:open).and_yield(resolver)
|
||||
|
||||
@ -23,7 +24,9 @@ describe EmailMxValidator do
|
||||
|
||||
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::MX).and_return([double(exchange: 'mail.example.com')])
|
||||
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::A).and_return([])
|
||||
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::AAAA).and_return([])
|
||||
allow(resolver).to receive(:getresources).with('mail.example.com', Resolv::DNS::Resource::IN::A).and_return([])
|
||||
allow(resolver).to receive(:getresources).with('mail.example.com', Resolv::DNS::Resource::IN::AAAA).and_return([])
|
||||
allow(resolver).to receive(:timeouts=).and_return(nil)
|
||||
allow(Resolv::DNS).to receive(:open).and_yield(resolver)
|
||||
|
||||
@ -37,6 +40,21 @@ describe EmailMxValidator do
|
||||
|
||||
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::MX).and_return([])
|
||||
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::A).and_return([double(address: '1.2.3.4')])
|
||||
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::AAAA).and_return([])
|
||||
allow(resolver).to receive(:timeouts=).and_return(nil)
|
||||
allow(Resolv::DNS).to receive(:open).and_yield(resolver)
|
||||
|
||||
subject.validate(user)
|
||||
expect(user.errors).to have_received(:add)
|
||||
end
|
||||
|
||||
it 'adds an error if the AAAA record is blacklisted' do
|
||||
EmailDomainBlock.create!(domain: 'fd00::1')
|
||||
resolver = double
|
||||
|
||||
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::MX).and_return([])
|
||||
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::A).and_return([])
|
||||
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::AAAA).and_return([double(address: 'fd00::1')])
|
||||
allow(resolver).to receive(:timeouts=).and_return(nil)
|
||||
allow(Resolv::DNS).to receive(:open).and_yield(resolver)
|
||||
|
||||
@ -50,7 +68,25 @@ describe EmailMxValidator do
|
||||
|
||||
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::MX).and_return([double(exchange: 'mail.example.com')])
|
||||
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::A).and_return([])
|
||||
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::AAAA).and_return([])
|
||||
allow(resolver).to receive(:getresources).with('mail.example.com', Resolv::DNS::Resource::IN::A).and_return([double(address: '2.3.4.5')])
|
||||
allow(resolver).to receive(:getresources).with('mail.example.com', Resolv::DNS::Resource::IN::AAAA).and_return([])
|
||||
allow(resolver).to receive(:timeouts=).and_return(nil)
|
||||
allow(Resolv::DNS).to receive(:open).and_yield(resolver)
|
||||
|
||||
subject.validate(user)
|
||||
expect(user.errors).to have_received(:add)
|
||||
end
|
||||
|
||||
it 'adds an error if the MX IPv6 record is blacklisted' do
|
||||
EmailDomainBlock.create!(domain: 'fd00::2')
|
||||
resolver = double
|
||||
|
||||
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::MX).and_return([double(exchange: 'mail.example.com')])
|
||||
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::A).and_return([])
|
||||
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::AAAA).and_return([])
|
||||
allow(resolver).to receive(:getresources).with('mail.example.com', Resolv::DNS::Resource::IN::A).and_return([])
|
||||
allow(resolver).to receive(:getresources).with('mail.example.com', Resolv::DNS::Resource::IN::AAAA).and_return([double(address: 'fd00::2')])
|
||||
allow(resolver).to receive(:timeouts=).and_return(nil)
|
||||
allow(Resolv::DNS).to receive(:open).and_yield(resolver)
|
||||
|
||||
@ -64,7 +100,9 @@ describe EmailMxValidator do
|
||||
|
||||
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::MX).and_return([double(exchange: 'mail.example.com')])
|
||||
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::A).and_return([])
|
||||
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::AAAA).and_return([])
|
||||
allow(resolver).to receive(:getresources).with('mail.example.com', Resolv::DNS::Resource::IN::A).and_return([double(address: '2.3.4.5')])
|
||||
allow(resolver).to receive(:getresources).with('mail.example.com', Resolv::DNS::Resource::IN::AAAA).and_return([double(address: 'fd00::2')])
|
||||
allow(resolver).to receive(:timeouts=).and_return(nil)
|
||||
allow(Resolv::DNS).to receive(:open).and_yield(resolver)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user