Share specs for command signatures even more

Those looked the same across all queries and commands,
so we share on that level. We still afford to keep different
examples for commands and queries.
This commit is contained in:
Jan Sandbrink
2025-11-19 13:55:52 +01:00
parent ae5362dc19
commit 7c6f99c1b0
39 changed files with 83 additions and 175 deletions
@@ -45,7 +45,7 @@ module Storages
let(:auth_strategy) { Registry["nextcloud.authentication.user_bound"].call(user, storage) }
let(:input_data) { Input::CreateFolder.build(folder_name:, parent_location:).value! }
it_behaves_like "adapter create_folder_command: basic command setup"
it_behaves_like "storage adapter: command call signature", "create_folder"
context "when creating a folder in the root", vcr: "nextcloud/create_folder_root" do
let(:folder_name) { "Földer CreatedBy Çommand" }
@@ -45,7 +45,7 @@ module Storages
let(:auth_strategy) { Registry.resolve("nextcloud.authentication.user_bound").call(user, storage) }
let(:input_data) { Input::RenameFile.build(location: file_id, new_name: name).value! }
it_behaves_like "adapter rename_file_command: basic command setup"
it_behaves_like "storage adapter: command call signature", "rename_file"
context "when renaming a folder", vcr: "nextcloud/rename_file_success" do
let(:file_id) { "169" }
@@ -46,7 +46,7 @@ module Storages
end
end
it_behaves_like "adapter set_permissions_command: basic command setup"
it_behaves_like "storage adapter: command call signature", "set_permissions"
context "if folder does not exists", vcr: "nextcloud/set_permissions_not_found_folder" do
let(:error_source) { Queries::FileInfoQuery }
@@ -48,7 +48,7 @@ module Storages
let(:file_name) { "test-file.txt" }
let(:io) { StringIO.new("This is the file content.") }
it_behaves_like "adapter upload_file_command: basic command setup"
it_behaves_like "storage adapter: command call signature", "upload_file"
context "when uploading a file to the root folder", vcr: "nextcloud/upload_file_root" do
it_behaves_like "adapter upload_file_command: successful file upload"
@@ -43,7 +43,7 @@ module Storages
let(:auth_strategy) { Registry["nextcloud.authentication.user_bound"].call(user, storage) }
let(:input_data) { Input::FileInfo.build(file_id:).value! }
it_behaves_like "adapter file_info_query: basic query setup"
it_behaves_like "storage adapter: query call signature", "file_info"
context "with a file id requested", vcr: "nextcloud/file_info_query_success_file" do
let(:file_id) { "267" }
@@ -46,7 +46,7 @@ module Storages
let(:depth) { Float::INFINITY }
let(:input_data) { Input::FilePathToIdMap.build(folder:, depth:).value! }
it_behaves_like "adapter file_path_to_id_map_query: basic query setup"
it_behaves_like "storage adapter: query call signature", "file_path_to_id_map"
context "with parent folder being root" do
let(:folder) { "/" }
@@ -46,7 +46,7 @@ module Storages
end
let(:input_data) { Input::FilesInfo.build(file_ids:).value! }
it_behaves_like "adapter files_info_query: basic query setup"
it_behaves_like "storage adapter: query call signature", "files_info"
context "with an empty array of file ids" do
let(:file_ids) { [] }
@@ -48,7 +48,7 @@ module Storages
let(:auth_strategy) { Registry["nextcloud.authentication.user_bound"].call(user, storage) }
let(:input_data) { Input::Files.build(folder:).value! }
it_behaves_like "adapter files_query: basic query setup"
it_behaves_like "storage adapter: query call signature", "files"
context "with parent folder being root", vcr: "nextcloud/files_query_root" do
let(:folder) { "/" }
@@ -44,7 +44,7 @@ module Storages
let(:input_data) { Input::OpenFileLink.build(file_id:, open_location:).value! }
let(:open_file_link) { "#{storage.host}/index.php/f/#{file_id}?openfile=#{open_location ? '0' : '1'}" }
it_behaves_like "adapter open_file_link_query: basic query setup"
it_behaves_like "storage adapter: query call signature", "open_file_link"
context "with open location flag not set" do
it_behaves_like "adapter open_file_link_query: successful link response"
@@ -43,7 +43,7 @@ module Storages
end
let(:auth_strategy) { Registry["nextcloud.authentication.user_bound"].call(user, storage) }
it_behaves_like "adapter upload_link_query: basic query setup"
it_behaves_like "storage adapter: query call signature", "upload_link"
context "when requesting an upload link for an existing file", vcr: "nextcloud/upload_link_success" do
let(:input_data) do
@@ -41,7 +41,7 @@ module Storages
let(:auth_strategy) { Registry.resolve("one_drive.authentication.userless").call }
let(:input_data) { Input::CreateFolder.build(folder_name:, parent_location:).value! }
it_behaves_like "adapter create_folder_command: basic command setup"
it_behaves_like "storage adapter: command call signature", "create_folder"
context "when creating a folder in the root", vcr: "one_drive/create_folder_root" do
let(:folder_name) { "Földer CreatedBy Çommand" }
@@ -41,7 +41,7 @@ module Storages
let(:auth_strategy) { Registry.resolve("one_drive.authentication.userless").call }
let(:input_data) { Input::RenameFile.build(location: file_id, new_name: name).value! }
it_behaves_like "adapter rename_file_command: basic command setup"
it_behaves_like "storage adapter: command call signature", "rename_file"
context "when renaming a folder", vcr: "one_drive/rename_file_success" do
let(:file_id) { "01AZJL5PMAXGDWAAKMEBALX4Q6GSN5BSBR" }
@@ -52,7 +52,7 @@ module Storages
.call(storage:, auth_strategy:, input_data: test_folder_data).value!
end
it_behaves_like "adapter set_permissions_command: basic command setup"
it_behaves_like "storage adapter: command call signature", "set_permissions"
context "if folder does not exists", vcr: "one_drive/set_permissions_not_found_folder" do
let(:error_source) { described_class }
@@ -44,7 +44,7 @@ module Storages
let(:input_data) { Input::FileInfo.build(file_id:).value! }
it_behaves_like "adapter file_info_query: basic query setup"
it_behaves_like "storage adapter: query call signature", "file_info"
context "with a file id requested", vcr: "one_drive/file_info_query_success_file" do
let(:file_id) { "01AZJL5PNCQCEBFI3N7JGZSX5AOX32Z3LA" }
@@ -42,7 +42,7 @@ module Storages
let(:depth) { Float::INFINITY }
let(:input_data) { Input::FilePathToIdMap.build(folder:, depth:).value! }
it_behaves_like "adapter file_path_to_id_map_query: basic query setup"
it_behaves_like "storage adapter: query call signature", "file_path_to_id_map"
context "with parent folder being root", vcr: "one_drive/file_path_to_id_map_query_root" do
let(:folder) { "/" }
@@ -42,7 +42,7 @@ module Storages
let(:auth_strategy) { Registry["one_drive.authentication.user_bound"].call(user, storage) }
let(:input_data) { Input::FilesInfo.build(file_ids:).value! }
it_behaves_like "adapter files_info_query: basic query setup"
it_behaves_like "storage adapter: query call signature", "files_info"
context "with an empty array of file ids" do
let(:file_ids) { [] }
@@ -42,7 +42,7 @@ module Storages
let(:auth_strategy) { Registry["one_drive.authentication.user_bound"].call(user, storage) }
let(:input_data) { Input::Files.build(folder:).value! }
it_behaves_like "adapter files_query: basic query setup"
it_behaves_like "storage adapter: query call signature", "files"
context "with parent folder being root", vcr: "one_drive/files_query_root" do
let(:folder) { "/" }
@@ -41,7 +41,7 @@ module Storages
let(:storage) { create(:one_drive_sandbox_storage, oauth_client_token_user: user) }
let(:auth_strategy) { Registry.resolve("one_drive.authentication.user_bound").call(user, storage) }
it_behaves_like "adapter open_file_link_query: basic query setup"
it_behaves_like "storage adapter: query call signature", "open_file_link"
context "with outbound requests successful" do
context "with open location flag not set", vcr: "one_drive/open_file_link_query_success" do
@@ -40,7 +40,7 @@ module Storages
let(:storage) { create(:one_drive_sandbox_storage) }
let(:auth_strategy) { Registry["one_drive.authentication.userless"].call }
it_behaves_like "adapter upload_link_query: basic query setup"
it_behaves_like "storage adapter: query call signature", "upload_link"
context "when requesting an upload link for an existing file", vcr: "one_drive/upload_link_success" do
let(:input_data) do
@@ -42,7 +42,7 @@ module Storages
let(:base_drive) { "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY9jo6leJDqrT7muzvmiWjFW" }
let(:input_data) { Input::CreateFolder.build(folder_name:, parent_location:).value! }
it_behaves_like "adapter create_folder_command: basic command setup"
it_behaves_like "storage adapter: command call signature", "create_folder"
context "when creating a folder in the root", vcr: "sharepoint/create_folder_root" do
let(:folder_name) { "Földer CreatedBy Çommand" }
@@ -42,7 +42,7 @@ module Storages
let(:input_data) { Input::RenameFile.build(location: file_id, new_name: name).value! }
let(:base_drive) { "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY8CfNaHr_0ERYs5kgmEWFrX" }
it_behaves_like "adapter rename_file_command: basic command setup"
it_behaves_like "storage adapter: command call signature", "rename_file"
context "when renaming a folder", vcr: "sharepoint/rename_file_success" do
let(:file_id) { "#{base_drive}:01ANJ53W7XPDQZRCOJK5CJC2M72EB6WKEG" }
@@ -53,7 +53,7 @@ module Storages
let(:base_drive) { "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY8CfNaHr_0ERYs5kgmEWFrX" }
it_behaves_like "adapter set_permissions_command: basic command setup"
it_behaves_like "storage adapter: command call signature", "set_permissions"
context "if folder does not exists", vcr: "sharepoint/set_permissions_not_found_folder" do
let(:error_source) { Queries::Internal::DriveItemQuery }
@@ -45,7 +45,7 @@ module Storages
let(:input_data) { Input::FileInfo.build(file_id:).value! }
it_behaves_like "adapter file_info_query: basic query setup"
it_behaves_like "storage adapter: query call signature", "file_info"
context "with a file id requested", vcr: "sharepoint/file_info_query_success_file" do
let(:file_id) { "#{drive_id}:01ANJ53W5UJK2CQO6IY5HLBVYBVNJ4TKHZ" }
@@ -43,7 +43,7 @@ module Storages
let(:depth) { Float::INFINITY }
let(:input_data) { Input::FilePathToIdMap.build(folder:, depth:).value! }
it_behaves_like "adapter file_path_to_id_map_query: basic query setup"
it_behaves_like "storage adapter: query call signature", "file_path_to_id_map"
context "with parent folder being root", vcr: "sharepoint/file_path_to_id_map_query_root" do
let(:folder) { "#{base_drive}:/" }
@@ -42,7 +42,7 @@ module Storages
let(:auth_strategy) { Registry["sharepoint.authentication.user_bound"].call(user, storage) }
let(:input_data) { Input::FilesInfo.build(file_ids:).value! }
it_behaves_like "adapter files_info_query: basic query setup"
it_behaves_like "storage adapter: query call signature", "files_info"
context "with an empty array of file ids" do
let(:file_ids) { [] }
@@ -43,7 +43,7 @@ module Storages
let(:auth_strategy) { Registry["sharepoint.authentication.userless"].call(false) }
let(:input_data) { Input::Files.build(folder:).value! }
it_behaves_like "adapter files_query: basic query setup"
it_behaves_like "storage adapter: query call signature", "files"
context "when parent folder is root", vcr: "sharepoint/files_query_root" do
let(:folder) { "/" }
@@ -43,7 +43,7 @@ module Storages
let(:drive_id) { "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY87vnZ6fgfvQanZHX-XCAyw" }
let(:separator) { SharepointStorage::IDENTIFIER_SEPARATOR }
it_behaves_like "adapter open_file_link_query: basic query setup"
it_behaves_like "storage adapter: query call signature", "open_file_link"
context "with outbound requests successful" do
context "with open location flag not set", vcr: "sharepoint/open_file_link_query_success" do
@@ -43,7 +43,7 @@ module Storages
let(:input_data) { Input::UploadLink.build(folder_id:, file_name:).value! }
let(:file_name) { "DeathStart_blueprints.tiff" }
it_behaves_like "adapter upload_link_query: basic query setup"
it_behaves_like "storage adapter: query call signature", "upload_link"
context "when creating an upload link to the root folder of a list", vcr: "sharepoint/upload_link_success" do
let(:folder_id) { "b!FeOZEMfQx0eGQKqVBLcP__BG8mq-4-9FuRqOyk3MXY8Qconfm2i6SKEoCmuGYqQK" }
@@ -0,0 +1,55 @@
# frozen_string_literal: true
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++
RSpec.shared_examples_for "storage adapter: command call signature" do |command_name|
it "is registered as commands.#{command_name}" do
expect(Storages::Adapters::Registry.resolve("#{storage}.commands.#{command_name}")).to eq(described_class)
end
it "responds to #call with correct parameters" do
expect(described_class).to respond_to(:call)
method = described_class.method(:call)
expect(method.parameters).to contain_exactly(%i[keyreq storage], %i[keyreq auth_strategy], %i[keyreq input_data])
end
end
RSpec.shared_examples_for "storage adapter: query call signature" do |query_name|
it "is registered as queries.#{query_name}" do
expect(Storages::Adapters::Registry.resolve("#{storage}.queries.#{query_name}")).to eq(described_class)
end
it "responds to #call with correct parameters" do
expect(described_class).to respond_to(:call)
method = described_class.method(:call)
expect(method.parameters).to contain_exactly(%i[keyreq storage], %i[keyreq auth_strategy], %i[keyreq input_data])
end
end
@@ -28,19 +28,6 @@
# See COPYRIGHT and LICENSE files for more details.
#++
RSpec.shared_examples_for "adapter create_folder_command: basic command setup" do
it "is registered as commands.create_folder" do
expect(Storages::Adapters::Registry.resolve("#{storage}.commands.create_folder")).to eq(described_class)
end
it "responds to #call with correct parameters" do
expect(described_class).to respond_to(:call)
method = described_class.method(:call)
expect(method.parameters).to contain_exactly(%i[keyreq storage], %i[keyreq auth_strategy], %i[keyreq input_data])
end
end
RSpec.shared_examples_for "adapter create_folder_command: successful folder creation" do
it "creates a folder" do
result = described_class.call(storage:, auth_strategy:, input_data:)
@@ -28,22 +28,6 @@
# See COPYRIGHT and LICENSE files for more details.
#++
RSpec.shared_examples_for "adapter file_info_query: basic query setup" do
it "is registered as queries.file_info" do
expect(Storages::Adapters::Registry
.resolve("#{storage}.queries.file_info")).to eq(described_class)
end
it "responds to #call with correct parameters" do
expect(described_class).to respond_to(:call)
method = described_class.method(:call)
expect(method.parameters).to contain_exactly(%i[keyreq storage],
%i[keyreq auth_strategy],
%i[keyreq input_data])
end
end
RSpec.shared_examples_for "adapter file_info_query: successful file/folder response" do
it "returns a file info object" do
result = described_class.call(storage:, auth_strategy:, input_data:)
@@ -28,22 +28,6 @@
# See COPYRIGHT and LICENSE files for more details.
#++
RSpec.shared_examples_for "adapter file_path_to_id_map_query: basic query setup" do
it "is registered as queries.file_path_to_id_map" do
expect(Storages::Adapters::Registry
.resolve("#{storage}.queries.file_path_to_id_map")).to eq(described_class)
end
it "responds to #call with correct parameters" do
expect(described_class).to respond_to(:call)
method = described_class.method(:call)
expect(method.parameters).to contain_exactly(%i[keyreq storage],
%i[keyreq auth_strategy],
%i[keyreq input_data])
end
end
RSpec.shared_examples_for "adapter file_path_to_id_map_query: successful query" do
it "returns a map of locations to file ids" do
result = described_class.call(storage:, auth_strategy:, input_data:)
@@ -28,22 +28,6 @@
# See COPYRIGHT and LICENSE files for more details.
#++
RSpec.shared_examples_for "adapter files_info_query: basic query setup" do
it "is registered as queries.files_info" do
expect(Storages::Adapters::Registry
.resolve("#{storage}.queries.files_info")).to eq(described_class)
end
it "responds to #call with correct parameters" do
expect(described_class).to respond_to(:call)
method = described_class.method(:call)
expect(method.parameters).to contain_exactly(%i[keyreq storage],
%i[keyreq auth_strategy],
%i[keyreq input_data])
end
end
RSpec.shared_examples_for "adapter files_info_query: successful list response" do
it "returns an array of file info objects" do
result = described_class.call(storage:, auth_strategy:, input_data:)
@@ -28,21 +28,6 @@
# See COPYRIGHT and LICENSE files for more details.
#++
RSpec.shared_examples_for "adapter files_query: basic query setup" do
it "is registered as queries.files" do
expect(Storages::Adapters::Registry.resolve("#{storage}.queries.files")).to eq(described_class)
end
it "responds to #call with correct parameters" do
expect(described_class).to respond_to(:call)
method = described_class.method(:call)
expect(method.parameters).to contain_exactly(%i[keyreq storage],
%i[keyreq auth_strategy],
%i[keyreq input_data])
end
end
RSpec.shared_examples_for "adapter files_query: successful files response" do
it "returns a storage file collection object" do
result = described_class.call(storage:, auth_strategy:, input_data:)
@@ -28,22 +28,6 @@
# See COPYRIGHT and LICENSE files for more details.
#++
RSpec.shared_examples_for "adapter open_file_link_query: basic query setup" do
it "is registered as queries.open_file_link" do
expect(Storages::Adapters::Registry
.resolve("#{storage}.queries.open_file_link")).to eq(described_class)
end
it "responds to #call with correct parameters" do
expect(described_class).to respond_to(:call)
method = described_class.method(:call)
expect(method.parameters).to contain_exactly(%i[keyreq storage],
%i[keyreq auth_strategy],
%i[keyreq input_data])
end
end
RSpec.shared_examples_for "adapter open_file_link_query: successful link response" do
it "returns a link result object" do
result = described_class.call(storage:, auth_strategy:, input_data:)
@@ -28,19 +28,6 @@
# See COPYRIGHT and LICENSE files for more details.
#++
RSpec.shared_examples_for "adapter rename_file_command: basic command setup" do
it "is registered as commands.rename_file" do
expect(Storages::Adapters::Registry.resolve("#{storage}.commands.rename_file")).to eq(described_class)
end
it "responds to #call with correct parameters" do
expect(described_class).to respond_to(:call)
method = described_class.method(:call)
expect(method.parameters).to contain_exactly(%i[keyreq storage], %i[keyreq auth_strategy], %i[keyreq input_data])
end
end
RSpec.shared_examples_for "adapter rename_file_command: successful file renaming" do
it "returns success and the renamed file" do
result = described_class.call(storage:, auth_strategy:, input_data:)
@@ -28,20 +28,6 @@
# See COPYRIGHT and LICENSE files for more details.
#++
RSpec.shared_examples_for "adapter set_permissions_command: basic command setup" do
it "is registered as commands.set_permissions" do
expect(Storages::Adapters::Registry
.resolve("#{storage}.commands.set_permissions")).to eq(described_class)
end
it "responds to #call with correct parameters" do
expect(described_class).to respond_to(:call)
method = described_class.method(:call)
expect(method.parameters).to contain_exactly(%i[keyreq storage], %i[keyreq auth_strategy], %i[keyreq input_data])
end
end
RSpec.shared_examples_for "adapter set_permissions_command: replaces already set permissions" do
it "replaces fully the previously set permissions" do
file_id = test_folder.id
@@ -28,20 +28,6 @@
# See COPYRIGHT and LICENSE files for more details.
#++
# TODO: That's so generic, it could probably be in a shared example defined once (making the registry entry configurable)
RSpec.shared_examples_for "adapter upload_file_command: basic command setup" do
it "is registered as commands.upload_file_command" do
expect(Storages::Adapters::Registry.resolve("#{storage}.commands.upload_file")).to eq(described_class)
end
it "responds to #call with correct parameters" do
expect(described_class).to respond_to(:call)
method = described_class.method(:call)
expect(method.parameters).to contain_exactly(%i[keyreq storage], %i[keyreq auth_strategy], %i[keyreq input_data])
end
end
RSpec.shared_examples_for "adapter upload_file_command: successful file upload" do
it "uploads the file" do
result = described_class.call(storage:, auth_strategy:, input_data:)
@@ -28,20 +28,6 @@
# See COPYRIGHT and LICENSE files for more details.
#++
RSpec.shared_examples_for "adapter upload_link_query: basic query setup" do
it "is registered as queries.upload_link" do
expect(Storages::Adapters::Registry
.resolve("#{storage}.queries.upload_link")).to eq(described_class)
end
it "responds to #call with correct parameters" do
expect(described_class).to respond_to(:call)
method = described_class.method(:call)
expect(method.parameters).to contain_exactly(%i[keyreq storage], %i[keyreq auth_strategy], %i[keyreq input_data])
end
end
RSpec.shared_examples_for "adapter upload_link_query: successful upload link response" do
it "returns an upload link" do
result = described_class.call(storage:, auth_strategy:, input_data:)