Add ActivityPub serializer specs (#37706)

This commit is contained in:
David Roetzel 2026-02-02 17:47:42 +01:00 committed by GitHub
parent 9de54635ed
commit 6188de3efc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 568 additions and 0 deletions

View File

@ -0,0 +1,102 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe ActivityPub::ActivitySerializer do
subject { serialized_record_json(presenter, described_class, adapter: ActivityPub::Adapter) }
let(:tag_manager) { ActivityPub::TagManager.instance }
let(:status) { Fabricate(:status, created_at: Time.utc(2026, 0o1, 27, 15, 29, 31)) }
context 'with a new status' do
let(:presenter) { ActivityPub::ActivityPresenter.from_status(status) }
it 'serializes to the expected json' do
expect(subject).to include({
'id' => tag_manager.activity_uri_for(status),
'type' => 'Create',
'actor' => tag_manager.uri_for(status.account),
'published' => '2026-01-27T15:29:31Z',
'to' => ['https://www.w3.org/ns/activitystreams#Public'],
'cc' => [a_string_matching(/followers$/)],
'object' => a_hash_including(
'id' => tag_manager.uri_for(status)
),
})
expect(subject).to_not have_key('target')
end
end
context 'with a new reblog' do
let(:reblog) { Fabricate(:status, reblog: status, created_at: Time.utc(2026, 0o1, 27, 16, 21, 44)) }
let(:presenter) { ActivityPub::ActivityPresenter.from_status(reblog) }
it 'serializes to the expected json' do
expect(subject).to include({
'id' => tag_manager.activity_uri_for(reblog),
'type' => 'Announce',
'actor' => tag_manager.uri_for(reblog.account),
'published' => '2026-01-27T16:21:44Z',
'to' => ['https://www.w3.org/ns/activitystreams#Public'],
'cc' => [tag_manager.uri_for(status.account), a_string_matching(/followers$/)],
'object' => tag_manager.uri_for(status),
})
expect(subject).to_not have_key('target')
end
context 'when inlining of private local status is allowed' do
let(:status) { Fabricate(:status, visibility: :private, created_at: Time.utc(2026, 0o1, 27, 15, 29, 31)) }
let(:reblog) { Fabricate(:status, reblog: status, account: status.account, created_at: Time.utc(2026, 0o1, 27, 16, 21, 44)) }
let(:presenter) { ActivityPub::ActivityPresenter.from_status(reblog, allow_inlining: true) }
it 'serializes to the expected json' do
expect(subject).to include({
'id' => tag_manager.activity_uri_for(reblog),
'type' => 'Announce',
'actor' => tag_manager.uri_for(reblog.account),
'published' => '2026-01-27T16:21:44Z',
'to' => ['https://www.w3.org/ns/activitystreams#Public'],
'cc' => [tag_manager.uri_for(status.account), a_string_matching(/followers$/)],
'object' => a_hash_including(
'id' => tag_manager.uri_for(status)
),
})
expect(subject).to_not have_key('target')
end
end
end
context 'with a custom presenter for a status `Update`' do
let(:status) { Fabricate(:status, edited_at: Time.utc(2026, 0o1, 27, 15, 29, 31)) }
let(:presenter) do
ActivityPub::ActivityPresenter.new(
id: 'https://localhost/status/1#updates/1769527771',
type: 'Update',
actor: 'https://localhost/actor/1',
published: status.edited_at,
to: ['https://www.w3.org/ns/activitystreams#Public'],
cc: ['https://localhost/actor/1/followers'],
virtual_object: status
)
end
it 'serializes to the expected json' do
expect(subject).to include({
'id' => 'https://localhost/status/1#updates/1769527771',
'type' => 'Update',
'actor' => 'https://localhost/actor/1',
'published' => '2026-01-27T15:29:31Z',
'to' => ['https://www.w3.org/ns/activitystreams#Public'],
'cc' => ['https://localhost/actor/1/followers'],
'object' => a_hash_including(
'id' => tag_manager.uri_for(status)
),
})
expect(subject).to_not have_key('target')
end
end
end

View File

@ -52,4 +52,68 @@ RSpec.describe ActivityPub::AddSerializer do
it { is_expected.to match(%r{/#{object.account_id}/featured_collections$}) }
end
end
describe 'Serialization' do
subject { serialized_record_json(object, described_class, adapter: ActivityPub::Adapter) }
let(:tag_manager) { ActivityPub::TagManager.instance }
context 'with a status' do
let(:object) { Fabricate(:status) }
it 'serializes to the expected json' do
expect(subject).to include({
'type' => 'Add',
'actor' => tag_manager.uri_for(object.account),
'target' => a_string_matching(%r{/featured$}),
'object' => tag_manager.uri_for(object),
})
expect(subject).to_not have_key('id')
expect(subject).to_not have_key('published')
expect(subject).to_not have_key('to')
expect(subject).to_not have_key('cc')
end
end
context 'with a featured tag' do
let(:object) { Fabricate(:featured_tag) }
it 'serializes to the expected json' do
expect(subject).to include({
'type' => 'Add',
'actor' => tag_manager.uri_for(object.account),
'target' => a_string_matching(%r{/featured$}),
'object' => a_hash_including({
'type' => 'Hashtag',
}),
})
expect(subject).to_not have_key('id')
expect(subject).to_not have_key('published')
expect(subject).to_not have_key('to')
expect(subject).to_not have_key('cc')
end
end
context 'with a collection' do
let(:object) { Fabricate(:collection) }
it 'serializes to the expected json' do
expect(subject).to include({
'type' => 'Add',
'actor' => tag_manager.uri_for(object.account),
'target' => a_string_matching(%r{/featured_collections$}),
'object' => a_hash_including({
'type' => 'FeaturedCollection',
}),
})
expect(subject).to_not have_key('id')
expect(subject).to_not have_key('published')
expect(subject).to_not have_key('to')
expect(subject).to_not have_key('cc')
end
end
end
end

View File

@ -0,0 +1,24 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe ActivityPub::BlockSerializer do
subject { serialized_record_json(block, described_class, adapter: ActivityPub::Adapter) }
let(:tag_manager) { ActivityPub::TagManager.instance }
let(:block) { Fabricate(:block, uri: 'https://localhost/block/1') }
it 'serializes to the expected json' do
expect(subject).to include({
'id' => 'https://localhost/block/1',
'type' => 'Block',
'actor' => tag_manager.uri_for(block.account),
'object' => tag_manager.uri_for(block.target_account),
})
expect(subject).to_not have_key('published')
expect(subject).to_not have_key('to')
expect(subject).to_not have_key('cc')
expect(subject).to_not have_key('target')
end
end

View File

@ -0,0 +1,24 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe ActivityPub::DeleteActorSerializer do
subject { serialized_record_json(account, described_class, adapter: ActivityPub::Adapter) }
let(:tag_manager) { ActivityPub::TagManager.instance }
let(:account) { Fabricate(:account) }
it 'serializes to the expected json' do
expect(subject).to include({
'id' => "#{tag_manager.uri_for(account)}#delete",
'type' => 'Delete',
'actor' => tag_manager.uri_for(account),
'to' => ['https://www.w3.org/ns/activitystreams#Public'],
'object' => tag_manager.uri_for(account),
})
expect(subject).to_not have_key('published')
expect(subject).to_not have_key('cc')
expect(subject).to_not have_key('target')
end
end

View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe ActivityPub::DeleteSerializer do
subject { serialized_record_json(status, described_class, adapter: ActivityPub::Adapter) }
let(:tag_manager) { ActivityPub::TagManager.instance }
let(:status) { Fabricate(:status) }
it 'serializes to the expected json' do
expect(subject).to include({
'id' => "#{tag_manager.uri_for(status)}#delete",
'type' => 'Delete',
'actor' => tag_manager.uri_for(status.account),
'to' => ['https://www.w3.org/ns/activitystreams#Public'],
'object' => a_hash_including({
'id' => tag_manager.uri_for(status),
'type' => 'Tombstone',
}),
})
expect(subject).to_not have_key('published')
expect(subject).to_not have_key('cc')
expect(subject).to_not have_key('target')
end
end

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe ActivityPub::FlagSerializer do
subject { serialized_record_json(report, described_class, adapter: ActivityPub::Adapter) }
let(:tag_manager) { ActivityPub::TagManager.instance }
let(:report) { Fabricate(:report, comment: 'good reason') }
it 'serializes to the expected json' do
expect(subject).to include({
'id' => tag_manager.uri_for(report),
'type' => 'Flag',
'actor' => tag_manager.uri_for(report.account),
'content' => 'good reason',
'object' => [tag_manager.uri_for(report.target_account)],
})
expect(subject).to_not have_key('published')
expect(subject).to_not have_key('to')
expect(subject).to_not have_key('cc')
expect(subject).to_not have_key('target')
end
end

View File

@ -0,0 +1,24 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe ActivityPub::FollowSerializer do
subject { serialized_record_json(follow, described_class, adapter: ActivityPub::Adapter) }
let(:tag_manager) { ActivityPub::TagManager.instance }
let(:follow) { Fabricate(:follow, uri: 'https://localhost/follow/1') }
it 'serializes to the expected json' do
expect(subject).to include({
'id' => 'https://localhost/follow/1',
'type' => 'Follow',
'actor' => tag_manager.uri_for(follow.account),
'object' => tag_manager.uri_for(follow.target_account),
})
expect(subject).to_not have_key('published')
expect(subject).to_not have_key('to')
expect(subject).to_not have_key('cc')
expect(subject).to_not have_key('target')
end
end

View File

@ -0,0 +1,24 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe ActivityPub::LikeSerializer do
subject { serialized_record_json(favourite, described_class, adapter: ActivityPub::Adapter) }
let(:tag_manager) { ActivityPub::TagManager.instance }
let(:favourite) { Fabricate(:favourite) }
it 'serializes to the expected json' do
expect(subject).to include({
'id' => "#{tag_manager.uri_for(favourite.account)}#likes/#{favourite.id}",
'type' => 'Like',
'actor' => tag_manager.uri_for(favourite.account),
'object' => tag_manager.uri_for(favourite.status),
})
expect(subject).to_not have_key('published')
expect(subject).to_not have_key('to')
expect(subject).to_not have_key('cc')
expect(subject).to_not have_key('target')
end
end

View File

@ -0,0 +1,24 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe ActivityPub::MoveSerializer do
subject { serialized_record_json(account_migration, described_class, adapter: ActivityPub::Adapter) }
let(:tag_manager) { ActivityPub::TagManager.instance }
let(:account_migration) { Fabricate(:account_migration) }
it 'serializes to the expected json' do
expect(subject).to include({
'id' => "#{tag_manager.uri_for(account_migration.account)}#moves/#{account_migration.id}",
'type' => 'Move',
'actor' => tag_manager.uri_for(account_migration.account),
'target' => tag_manager.uri_for(account_migration.target_account),
'object' => tag_manager.uri_for(account_migration.account),
})
expect(subject).to_not have_key('published')
expect(subject).to_not have_key('to')
expect(subject).to_not have_key('cc')
end
end

View File

@ -0,0 +1,51 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe ActivityPub::QuoteRequestSerializer do
subject { serialized_record_json(quote, described_class, adapter: ActivityPub::Adapter, options: { allow_post_inlining: inlining }) }
let(:tag_manager) { ActivityPub::TagManager.instance }
let(:quote) { Fabricate(:quote) }
context 'without inlining' do
let(:inlining) { false }
it 'serializes to the expected json' do
expect(subject).to include({
'id' => quote.activity_uri,
'type' => 'QuoteRequest',
'actor' => tag_manager.uri_for(quote.account),
'instrument' => tag_manager.uri_for(quote.status),
'object' => tag_manager.uri_for(quote.quoted_status),
})
expect(subject).to_not have_key('published')
expect(subject).to_not have_key('to')
expect(subject).to_not have_key('cc')
expect(subject).to_not have_key('target')
end
end
context 'with inlining' do
let(:inlining) { true }
it 'serializes to the expected json' do
expect(subject).to include({
'id' => quote.activity_uri,
'type' => 'QuoteRequest',
'actor' => tag_manager.uri_for(quote.account),
'instrument' => a_hash_including({
'id' => tag_manager.uri_for(quote.status),
'type' => 'Note',
}),
'object' => tag_manager.uri_for(quote.quoted_status),
})
expect(subject).to_not have_key('published')
expect(subject).to_not have_key('to')
expect(subject).to_not have_key('cc')
expect(subject).to_not have_key('target')
end
end
end

View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe ActivityPub::RejectQuoteRequestSerializer do
subject { serialized_record_json(quote, described_class, adapter: ActivityPub::Adapter) }
let(:tag_manager) { ActivityPub::TagManager.instance }
let(:quote) { Fabricate(:quote) }
it 'serializes to the expected json' do
expect(subject).to include({
'id' => "#{tag_manager.uri_for(quote.quoted_account)}#rejects/quote_requests/#{quote.id}",
'type' => 'Reject',
'actor' => tag_manager.uri_for(quote.quoted_account),
'object' => a_hash_including({
'id' => quote.activity_uri,
'type' => 'QuoteRequest',
}),
})
expect(subject).to_not have_key('published')
expect(subject).to_not have_key('to')
expect(subject).to_not have_key('cc')
expect(subject).to_not have_key('target')
end
end

View File

@ -24,4 +24,48 @@ RSpec.describe ActivityPub::RemoveSerializer do
it { is_expected.to eq(ActiveModel::Serializer::CollectionSerializer) }
end
end
describe 'Serialization' do
subject { serialized_record_json(object, described_class, adapter: ActivityPub::Adapter) }
let(:tag_manager) { ActivityPub::TagManager.instance }
context 'with a status' do
let(:object) { Fabricate(:status) }
it 'serializes to the expected json' do
expect(subject).to include({
'type' => 'Remove',
'actor' => tag_manager.uri_for(object.account),
'target' => a_string_matching(%r{/featured$}),
'object' => tag_manager.uri_for(object),
})
expect(subject).to_not have_key('id')
expect(subject).to_not have_key('published')
expect(subject).to_not have_key('to')
expect(subject).to_not have_key('cc')
end
end
context 'with a featured tag' do
let(:object) { Fabricate(:featured_tag) }
it 'serializes to the expected json' do
expect(subject).to include({
'type' => 'Remove',
'actor' => tag_manager.uri_for(object.account),
'target' => a_string_matching(%r{/featured$}),
'object' => a_hash_including({
'type' => 'Hashtag',
}),
})
expect(subject).to_not have_key('id')
expect(subject).to_not have_key('published')
expect(subject).to_not have_key('to')
expect(subject).to_not have_key('cc')
end
end
end
end

View File

@ -0,0 +1,29 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe ActivityPub::UndoAnnounceSerializer do
subject { serialized_record_json(reblog, described_class, adapter: ActivityPub::Adapter) }
let(:tag_manager) { ActivityPub::TagManager.instance }
let(:status) { Fabricate(:status) }
let(:reblog) { Fabricate(:status, reblog: status) }
it 'serializes to the expected json' do
expect(subject).to include({
'id' => "#{tag_manager.uri_for(reblog.account)}#announces/#{reblog.id}/undo",
'type' => 'Undo',
'actor' => tag_manager.uri_for(reblog.account),
'to' => ['https://www.w3.org/ns/activitystreams#Public'],
'object' => a_hash_including({
'id' => tag_manager.activity_uri_for(reblog),
'type' => 'Announce',
'object' => tag_manager.uri_for(status),
}),
})
expect(subject).to_not have_key('published')
expect(subject).to_not have_key('cc')
expect(subject).to_not have_key('target')
end
end

View File

@ -0,0 +1,26 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe ActivityPub::UndoBlockSerializer do
subject { serialized_record_json(block, described_class, adapter: ActivityPub::Adapter) }
let(:tag_manager) { ActivityPub::TagManager.instance }
let(:block) { Fabricate(:block) }
it 'serializes to the expected json' do
expect(subject).to include({
'id' => "#{tag_manager.uri_for(block.account)}#blocks/#{block.id}/undo",
'type' => 'Undo',
'actor' => tag_manager.uri_for(block.account),
'object' => a_hash_including({
'type' => 'Block',
}),
})
expect(subject).to_not have_key('published')
expect(subject).to_not have_key('to')
expect(subject).to_not have_key('cc')
expect(subject).to_not have_key('target')
end
end

View File

@ -0,0 +1,26 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe ActivityPub::UndoFollowSerializer do
subject { serialized_record_json(follow, described_class, adapter: ActivityPub::Adapter) }
let(:tag_manager) { ActivityPub::TagManager.instance }
let(:follow) { Fabricate(:follow) }
it 'serializes to the expected json' do
expect(subject).to include({
'id' => "#{tag_manager.uri_for(follow.account)}#follows/#{follow.id}/undo",
'type' => 'Undo',
'actor' => tag_manager.uri_for(follow.account),
'object' => a_hash_including({
'type' => 'Follow',
}),
})
expect(subject).to_not have_key('published')
expect(subject).to_not have_key('to')
expect(subject).to_not have_key('cc')
expect(subject).to_not have_key('target')
end
end

View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe ActivityPub::UpdateSerializer do
subject { serialized_record_json(account, described_class, adapter: ActivityPub::Adapter) }
let(:tag_manager) { ActivityPub::TagManager.instance }
let(:account) { Fabricate(:account) }
it 'serializes to the expected json' do
expect(subject).to include({
'id' => "#{tag_manager.uri_for(account)}#updates/#{account.updated_at.to_i}",
'type' => 'Update',
'actor' => tag_manager.uri_for(account),
'to' => ['https://www.w3.org/ns/activitystreams#Public'],
'object' => a_hash_including({
'id' => tag_manager.uri_for(account),
'type' => 'Person',
}),
})
expect(subject).to_not have_key('published')
expect(subject).to_not have_key('cc')
expect(subject).to_not have_key('target')
end
end