diff options
Diffstat (limited to 'spec/serializers/activity_pub')
7 files changed, 354 insertions, 0 deletions
diff --git a/spec/serializers/activity_pub/activity_streams_serializer_spec.rb b/spec/serializers/activity_pub/activity_streams_serializer_spec.rb new file mode 100644 index 00000000000..c74beba7a81 --- /dev/null +++ b/spec/serializers/activity_pub/activity_streams_serializer_spec.rb @@ -0,0 +1,157 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ActivityPub::ActivityStreamsSerializer, feature_category: :integrations do + let(:implementer_class) do + Class.new(described_class) do + include WithPagination + end + end + + let(:entity_class) do + Class.new(Grape::Entity) do + expose :id do |*| + 'https://example.com/unique/url' + end + + expose :type do |*| + 'Person' + end + + expose :name do |*| + 'Alice' + end + end + end + + shared_examples_for 'ActivityStreams document' do + it 'belongs to the ActivityStreams namespace' do + expect(subject['@context']).to eq 'https://www.w3.org/ns/activitystreams' + end + + it 'has a unique identifier' do + expect(subject).to have_key 'id' + end + + it 'has a type' do + expect(subject).to have_key 'type' + end + end + + before do + implementer_class.entity entity_class + end + + context 'when the serializer is not paginated' do + let(:resource) { build_stubbed(:release) } + let(:outbox_url) { 'https://example.com/unique/url/outbox' } + + context 'with a valid represented entity' do + subject { implementer_class.new.represent(resource, outbox: outbox_url) } + + it_behaves_like 'ActivityStreams document' + + it 'exposes an outbox' do + expect(subject['outbox']).to eq 'https://example.com/unique/url/outbox' + end + + it 'includes serialized data' do + expect(subject['name']).to eq 'Alice' + end + end + + context 'when the represented entity provides no identifier' do + subject { implementer_class.new.represent(resource, outbox: outbox_url) } + + before do + allow(entity_class).to receive(:represent).and_return({ type: 'Person' }) + end + + it 'raises an exception' do + expect { subject }.to raise_error(ActivityPub::ActivityStreamsSerializer::MissingIdentifierError) + end + end + + context 'when the represented entity provides no type' do + subject { implementer_class.new.represent(resource, outbox: outbox_url) } + + before do + allow(entity_class).to receive(:represent).and_return({ id: 'https://example.com/unique/url' }) + end + + it 'raises an exception' do + expect { subject }.to raise_error(ActivityPub::ActivityStreamsSerializer::MissingTypeError) + end + end + + context 'when the caller provides no outbox parameter' do + subject { implementer_class.new.represent(resource) } + + it 'raises an exception' do + expect { subject }.to raise_error(ActivityPub::ActivityStreamsSerializer::MissingOutboxError) + end + end + end + + context 'when the serializer is paginated' do + let(:resources) { build_stubbed_list(:release, 3) } + let(:request) { ActionDispatch::Request.new(request_data) } + let(:response) { ActionDispatch::Response.new } + let(:url) { 'https://example.com/resource/url' } + let(:decorated) { implementer_class.new.with_pagination(request, response) } + + before do + allow(resources).to receive(:page).and_return(resources) + allow(resources).to receive(:per).and_return(resources) + allow(resources).to receive(:current_page).and_return(2) + allow(resources).to receive(:total_pages).and_return(3) + allow(resources).to receive(:total_count).and_return(10) + allow(decorated.paginator).to receive(:paginate).and_return(resources) + end + + context 'when no page parameter is provided' do + subject { decorated.represent(resources) } + + let(:request_data) do + { "rack.url_scheme" => "https", "HTTP_HOST" => "example.com", "PATH_INFO" => '/resource/url' } + end + + it_behaves_like 'ActivityStreams document' + + it 'is an index document for the pagination' do + expect(subject['type']).to eq 'OrderedCollection' + end + + it 'contains the total amount of items' do + expect(subject['totalItems']).to eq 10 + end + + it 'contains links to first and last page' do + expect(subject['first']).to eq "#{url}?page=1" + expect(subject['last']).to eq "#{url}?page=3" + end + end + + context 'when a page parameter is provided' do + subject { decorated.represent(resources) } + + let(:request_data) do + { 'rack.url_scheme' => 'https', 'HTTP_HOST' => 'example.com', 'PATH_INFO' => '/resource/url', + 'QUERY_STRING' => 'page=2&per_page=1' } + end + + it_behaves_like 'ActivityStreams document' + + it 'is a page document' do + expect(subject['type']).to eq 'OrderedCollectionPage' + end + + it 'contains navigation links' do + expect(subject['prev']).to be_present + expect(subject['next']).to be_present + expect(subject['partOf']).to be_present + end + end + end +end diff --git a/spec/serializers/activity_pub/project_entity_spec.rb b/spec/serializers/activity_pub/project_entity_spec.rb new file mode 100644 index 00000000000..f273acace73 --- /dev/null +++ b/spec/serializers/activity_pub/project_entity_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ActivityPub::ProjectEntity, feature_category: :groups_and_projects do + let(:project) { build_stubbed(:project, name: 'Fooify', path: 'fooify') } + let(:entity) { described_class.new(project) } + + context 'as json' do + subject { entity.as_json } + + it 'has releases page as id' do + expect(subject[:id]).to match(%r{/fooify$}) + end + + it 'is an Application actor' do + expect(subject[:type]).to eq 'Application' + end + + it 'provides project name' do + expect(subject[:name]).to eq project.name + end + + it 'provides a description of the project' do + expect(subject[:summary]).to eq project.description + end + + it 'provides an url for web content' do + expect(subject[:url]).to match(%r{/fooify$}) + end + end +end diff --git a/spec/serializers/activity_pub/release_entity_spec.rb b/spec/serializers/activity_pub/release_entity_spec.rb new file mode 100644 index 00000000000..a473fbcc2bd --- /dev/null +++ b/spec/serializers/activity_pub/release_entity_spec.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ActivityPub::ReleaseEntity, feature_category: :groups_and_projects do + let(:release) { build_stubbed(:release) } + let(:entity) { described_class.new(release, url: '/outbox') } + + context 'as json' do + subject { entity.as_json } + + it 'has tag as id' do + expect(subject[:id]).to match(/##{release.tag}$/) + end + + it 'is a Create activity' do + expect(subject[:type]).to eq 'Create' + end + + it 'is addressed to public' do + expect(subject[:to]).to eq 'https://www.w3.org/ns/activitystreams#Public' + end + + it 'has an author' do + expect(subject[:actor]).to include(:id, :type, :name, :preferredUsername, :url) + end + + it 'embeds the release as an Application actor' do + expect(subject[:object][:type]).to eq 'Application' + end + + it 'provides release name' do + expect(subject[:object][:name]).to eq release.name + end + + it 'provides release description' do + expect(subject[:object][:content]).to eq release.description + end + + it 'provides an url for web content' do + expect(subject[:object][:url]).to include release.tag + end + + it 'provides project data as context' do + expect(subject[:object][:context]).to include(:id, :type, :name, :summary, :url) + end + end +end diff --git a/spec/serializers/activity_pub/releases_actor_entity_spec.rb b/spec/serializers/activity_pub/releases_actor_entity_spec.rb new file mode 100644 index 00000000000..fe388968867 --- /dev/null +++ b/spec/serializers/activity_pub/releases_actor_entity_spec.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ActivityPub::ReleasesActorEntity, feature_category: :groups_and_projects do + let(:project) { build_stubbed(:project, name: 'Fooify', path: 'fooify') } + let(:releases) { build_stubbed_list(:release, 3, project: project) } + + let(:entity) { described_class.new(project) } + + context 'as json' do + subject { entity.as_json } + + it 'has releases page as id' do + expect(subject[:id]).to include "/fooify/-/releases" + end + + it 'is an Application actor' do + expect(subject[:type]).to eq 'Application' + end + + it 'has a recognizable username' do + expect(subject[:preferredUsername]).to include 'releases' + end + + it 'has a recognizable full name' do + expect(subject[:name]).to eq 'Releases - Fooify' + end + + it 'provides a description of the project' do + expect(subject[:content]).to eq project.description + end + + it 'provides project data as context' do + expect(subject[:context]).to include(:id, :type, :name, :summary, :url) + expect(subject[:context][:id]).to match(%r{/fooify$}) + end + end +end diff --git a/spec/serializers/activity_pub/releases_actor_serializer_spec.rb b/spec/serializers/activity_pub/releases_actor_serializer_spec.rb new file mode 100644 index 00000000000..bc754eabe5c --- /dev/null +++ b/spec/serializers/activity_pub/releases_actor_serializer_spec.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ActivityPub::ReleasesActorSerializer, feature_category: :groups_and_projects do + let(:project) { build_stubbed(:project, name: 'Fooify', path: 'fooify') } + let(:releases) { build_stubbed_list(:release, 3, project: project) } + + context 'when there is a single object provided' do + subject { described_class.new.represent(project, outbox: '/outbox') } + + it 'serializes the actor attributes' do + expect(subject).to include(:id, :type, :preferredUsername, :name, :content, :context) + end + end +end diff --git a/spec/serializers/activity_pub/releases_outbox_serializer_spec.rb b/spec/serializers/activity_pub/releases_outbox_serializer_spec.rb new file mode 100644 index 00000000000..606b0130e0f --- /dev/null +++ b/spec/serializers/activity_pub/releases_outbox_serializer_spec.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ActivityPub::ReleasesOutboxSerializer, feature_category: :groups_and_projects do + let(:decorated) { described_class.new.with_pagination(request, response) } + + let(:project) { build_stubbed(:project, name: 'Fooify', path: 'fooify') } + let(:releases) { build_stubbed_list(:release, 3, project: project) } + + before do + allow(releases).to receive(:page).and_return(releases) + allow(releases).to receive(:per).and_return(releases) + allow(releases).to receive(:current_page).and_return(1) + allow(releases).to receive(:total_pages).and_return(1) + allow(decorated.paginator).to receive(:paginate).and_return(releases) + end + + context 'when there is a list of objects provided' do + subject { decorated.represent(releases, url: '/outbox') } + + let(:request) { ActionDispatch::Request.new({ 'QUERY_STRING' => 'page=1' }) } + let(:response) { ActionDispatch::Response.new } + + it 'is a OrderedCollection document' do + expect(subject[:type]).to eq 'OrderedCollectionPage' + end + + it 'serializes the releases' do + expect(subject[:orderedItems].count).to eq 3 + expect(subject[:orderedItems][0]).to include(:id, :type, :to, :actor, :object) + end + end +end diff --git a/spec/serializers/activity_pub/user_entity_spec.rb b/spec/serializers/activity_pub/user_entity_spec.rb new file mode 100644 index 00000000000..d9ab7a11ecf --- /dev/null +++ b/spec/serializers/activity_pub/user_entity_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ActivityPub::UserEntity, feature_category: :user_profile do + let(:user) { build_stubbed(:user, name: 'Alice', username: 'alice') } + let(:entity) { described_class.new(user) } + + context 'as json' do + subject { entity.as_json } + + it 'has releases page as id' do + expect(subject[:id]).to match(%r{/alice$}) + end + + it 'is a Person actor' do + expect(subject[:type]).to eq 'Person' + end + + it 'provides project name' do + expect(subject[:name]).to eq 'Alice' + end + + it 'provides an url for web content' do + expect(subject[:url]).to match(%r{/alice$}) + end + end +end |