diff options
Diffstat (limited to 'spec/graphql/resolvers/design_management')
8 files changed, 653 insertions, 0 deletions
diff --git a/spec/graphql/resolvers/design_management/design_at_version_resolver_spec.rb b/spec/graphql/resolvers/design_management/design_at_version_resolver_spec.rb new file mode 100644 index 00000000000..a5054ae3ebf --- /dev/null +++ b/spec/graphql/resolvers/design_management/design_at_version_resolver_spec.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Resolvers::DesignManagement::DesignAtVersionResolver do + include GraphqlHelpers + include DesignManagementTestHelpers + + let_it_be(:issue) { create(:issue) } + let_it_be(:project) { issue.project } + let_it_be(:user) { create(:user) } + let_it_be(:design_a) { create(:design, issue: issue) } + let_it_be(:version_a) { create(:design_version, issue: issue, created_designs: [design_a]) } + + let(:current_user) { user } + let(:object) { issue.design_collection } + let(:global_id) { GitlabSchema.id_from_object(design_at_version).to_s } + + let(:design_at_version) { ::DesignManagement::DesignAtVersion.new(design: design_a, version: version_a) } + + let(:resource_not_available) { ::Gitlab::Graphql::Errors::ResourceNotAvailable } + + before do + enable_design_management + project.add_developer(user) + end + + describe '#resolve' do + context 'when the user cannot see designs' do + let(:current_user) { create(:user) } + + it 'raises ResourceNotAvailable' do + expect { resolve_design }.to raise_error(resource_not_available) + end + end + + it 'returns the specified design' do + expect(resolve_design).to eq(design_at_version) + end + + context 'the ID belongs to a design on another issue' do + let(:other_dav) do + create(:design_at_version, issue: create(:issue, project: project)) + end + + let(:global_id) { global_id_of(other_dav) } + + it 'raises ResourceNotAvailable' do + expect { resolve_design }.to raise_error(resource_not_available) + end + + context 'the current object does not constrain the issue' do + let(:object) { nil } + + it 'returns the object' do + expect(resolve_design).to eq(other_dav) + end + end + end + end + + private + + def resolve_design + args = { id: global_id } + ctx = { current_user: current_user } + eager_resolve(described_class, obj: object, args: args, ctx: ctx) + end +end diff --git a/spec/graphql/resolvers/design_management/design_resolver_spec.rb b/spec/graphql/resolvers/design_management/design_resolver_spec.rb new file mode 100644 index 00000000000..857acc3d371 --- /dev/null +++ b/spec/graphql/resolvers/design_management/design_resolver_spec.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Resolvers::DesignManagement::DesignResolver do + include GraphqlHelpers + include DesignManagementTestHelpers + + before do + enable_design_management + end + + describe '#resolve' do + let_it_be(:issue) { create(:issue) } + let_it_be(:project) { issue.project } + let_it_be(:first_version) { create(:design_version) } + let_it_be(:first_design) { create(:design, issue: issue, versions: [first_version]) } + let_it_be(:current_user) { create(:user) } + let_it_be(:design_on_other_issue) do + create(:design, issue: create(:issue, project: project), versions: [create(:design_version)]) + end + + let(:args) { { id: GitlabSchema.id_from_object(first_design).to_s } } + let(:gql_context) { { current_user: current_user } } + + before do + project.add_developer(current_user) + end + + context 'when the user cannot see designs' do + let(:gql_context) { { current_user: create(:user) } } + + it 'returns nothing' do + expect(resolve_design).to be_nil + end + end + + context 'when no argument has been passed' do + let(:args) { {} } + + it 'raises an error' do + expect { resolve_design }.to raise_error(::Gitlab::Graphql::Errors::ArgumentError, /must/) + end + end + + context 'when both arguments have been passed' do + let(:args) { { filename: first_design.filename, id: GitlabSchema.id_from_object(first_design).to_s } } + + it 'raises an error' do + expect { resolve_design }.to raise_error(::Gitlab::Graphql::Errors::ArgumentError, /may/) + end + end + + context 'by ID' do + it 'returns the specified design' do + expect(resolve_design).to eq(first_design) + end + + context 'the ID belongs to a design on another issue' do + let(:args) { { id: GitlabSchema.id_from_object(design_on_other_issue).to_s } } + + it 'returns nothing' do + expect(resolve_design).to be_nil + end + end + end + + context 'by filename' do + let(:args) { { filename: first_design.filename } } + + it 'returns the specified design' do + expect(resolve_design).to eq(first_design) + end + + context 'the filename belongs to a design on another issue' do + let(:args) { { filename: design_on_other_issue.filename } } + + it 'returns nothing' do + expect(resolve_design).to be_nil + end + end + end + end + + def resolve_design + resolve(described_class, obj: issue.design_collection, args: args, ctx: gql_context) + end +end diff --git a/spec/graphql/resolvers/design_management/designs_resolver_spec.rb b/spec/graphql/resolvers/design_management/designs_resolver_spec.rb new file mode 100644 index 00000000000..28fc9e2151d --- /dev/null +++ b/spec/graphql/resolvers/design_management/designs_resolver_spec.rb @@ -0,0 +1,93 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Resolvers::DesignManagement::DesignsResolver do + include GraphqlHelpers + include DesignManagementTestHelpers + + before do + enable_design_management + end + + describe '#resolve' do + let_it_be(:issue) { create(:issue) } + let_it_be(:project) { issue.project } + let_it_be(:first_version) { create(:design_version) } + let_it_be(:first_design) { create(:design, issue: issue, versions: [first_version]) } + let_it_be(:current_user) { create(:user) } + let(:gql_context) { { current_user: current_user } } + let(:args) { {} } + + before do + project.add_developer(current_user) + end + + context 'when the user cannot see designs' do + let(:gql_context) { { current_user: create(:user) } } + + it 'returns nothing' do + expect(resolve_designs).to be_empty + end + end + + context 'for a design collection' do + context 'which contains just a single design' do + it 'returns just that design' do + expect(resolve_designs).to contain_exactly(first_design) + end + end + + context 'which contains another design' do + it 'returns all designs' do + second_version = create(:design_version) + second_design = create(:design, issue: issue, versions: [second_version]) + + expect(resolve_designs).to contain_exactly(first_design, second_design) + end + end + end + + describe 'filtering' do + describe 'by filename' do + let(:second_version) { create(:design_version) } + let(:second_design) { create(:design, issue: issue, versions: [second_version]) } + let(:args) { { filenames: [second_design.filename] } } + + it 'resolves to just the relevant design, ignoring designs with the same filename on different issues' do + create(:design, issue: create(:issue, project: project), filename: second_design.filename) + + expect(resolve_designs).to contain_exactly(second_design) + end + end + + describe 'by id' do + let(:second_version) { create(:design_version) } + let(:second_design) { create(:design, issue: issue, versions: [second_version]) } + + context 'the ID is on the current issue' do + let(:args) { { ids: [GitlabSchema.id_from_object(second_design).to_s] } } + + it 'resolves to just the relevant design' do + expect(resolve_designs).to contain_exactly(second_design) + end + end + + context 'the ID is on a different issue' do + let(:third_version) { create(:design_version) } + let(:third_design) { create(:design, issue: create(:issue, project: project), versions: [third_version]) } + + let(:args) { { ids: [GitlabSchema.id_from_object(third_design).to_s] } } + + it 'ignores it' do + expect(resolve_designs).to be_empty + end + end + end + end + end + + def resolve_designs + resolve(described_class, obj: issue.design_collection, args: args, ctx: gql_context) + end +end diff --git a/spec/graphql/resolvers/design_management/version/design_at_version_resolver_spec.rb b/spec/graphql/resolvers/design_management/version/design_at_version_resolver_spec.rb new file mode 100644 index 00000000000..cc9c0436885 --- /dev/null +++ b/spec/graphql/resolvers/design_management/version/design_at_version_resolver_spec.rb @@ -0,0 +1,93 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Resolvers::DesignManagement::Version::DesignAtVersionResolver do + include GraphqlHelpers + + include_context 'four designs in three versions' + + let(:current_user) { authorized_user } + let(:gql_context) { { current_user: current_user } } + + let(:version) { third_version } + let(:design) { design_a } + + let(:all_singular_args) do + { + design_at_version_id: global_id_of(dav(design)), + design_id: global_id_of(design), + filename: design.filename + } + end + + shared_examples 'a bad argument' do + let(:err_class) { ::Gitlab::Graphql::Errors::ArgumentError } + + it 'raises an appropriate error' do + expect { resolve_objects }.to raise_error(err_class) + end + end + + describe '#resolve' do + describe 'passing combinations of arguments' do + context 'passing no arguments' do + let(:args) { {} } + + it_behaves_like 'a bad argument' + end + + context 'passing all arguments' do + let(:args) { all_singular_args } + + it_behaves_like 'a bad argument' + end + + context 'passing any two arguments' do + let(:args) { all_singular_args.slice(*all_singular_args.keys.sample(2)) } + + it_behaves_like 'a bad argument' + end + end + + %i[design_at_version_id design_id filename].each do |arg| + describe "passing #{arg}" do + let(:args) { all_singular_args.slice(arg) } + + it 'finds the design' do + expect(resolve_objects).to eq(dav(design)) + end + + context 'when the user cannot see designs' do + let(:current_user) { create(:user) } + + it 'returns nothing' do + expect(resolve_objects).to be_nil + end + end + end + end + + describe 'attempting to retrieve an object not visible at this version' do + let(:design) { design_d } + + %i[design_at_version_id design_id filename].each do |arg| + describe "passing #{arg}" do + let(:args) { all_singular_args.slice(arg) } + + it 'does not find the design' do + expect(resolve_objects).to be_nil + end + end + end + end + end + + def resolve_objects + resolve(described_class, obj: version, args: args, ctx: gql_context) + end + + def dav(design) + build(:design_at_version, design: design, version: version) + end +end diff --git a/spec/graphql/resolvers/design_management/version/designs_at_version_resolver_spec.rb b/spec/graphql/resolvers/design_management/version/designs_at_version_resolver_spec.rb new file mode 100644 index 00000000000..123b26862d0 --- /dev/null +++ b/spec/graphql/resolvers/design_management/version/designs_at_version_resolver_spec.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Resolvers::DesignManagement::Version::DesignsAtVersionResolver do + include GraphqlHelpers + + include_context 'four designs in three versions' + + let_it_be(:current_user) { authorized_user } + let(:gql_context) { { current_user: current_user } } + + let(:version) { third_version } + + describe '.single' do + let(:single) { ::Resolvers::DesignManagement::Version::DesignAtVersionResolver } + + it 'returns the single context resolver' do + expect(described_class.single).to eq(single) + end + end + + describe '#resolve' do + let(:args) { {} } + + context 'when the user cannot see designs' do + let(:current_user) { create(:user) } + + it 'returns nothing' do + expect(resolve_objects).to be_empty + end + end + + context 'for the current version' do + it 'returns all designs visible at that version' do + expect(resolve_objects).to contain_exactly(dav(design_a), dav(design_b), dav(design_c)) + end + end + + context 'for a previous version with more objects' do + let(:version) { second_version } + + it 'returns objects that were later deleted' do + expect(resolve_objects).to contain_exactly(dav(design_a), dav(design_b), dav(design_c), dav(design_d)) + end + end + + context 'for a previous version with fewer objects' do + let(:version) { first_version } + + it 'does not return objects that were later created' do + expect(resolve_objects).to contain_exactly(dav(design_a)) + end + end + + describe 'filtering' do + describe 'by filename' do + let(:red_herring) { create(:design, issue: create(:issue, project: project)) } + let(:args) { { filenames: [design_b.filename, red_herring.filename] } } + + it 'resolves to just the relevant design' do + create(:design, issue: create(:issue, project: project), filename: design_b.filename) + + expect(resolve_objects).to contain_exactly(dav(design_b)) + end + end + + describe 'by id' do + let(:red_herring) { create(:design, issue: create(:issue, project: project)) } + let(:args) { { ids: [design_a, red_herring].map { |x| global_id_of(x) } } } + + it 'resolves to just the relevant design, ignoring objects on other issues' do + expect(resolve_objects).to contain_exactly(dav(design_a)) + end + end + end + end + + def resolve_objects + resolve(described_class, obj: version, args: args, ctx: gql_context) + end + + def dav(design) + build(:design_at_version, design: design, version: version) + end +end diff --git a/spec/graphql/resolvers/design_management/version_in_collection_resolver_spec.rb b/spec/graphql/resolvers/design_management/version_in_collection_resolver_spec.rb new file mode 100644 index 00000000000..ef50598d241 --- /dev/null +++ b/spec/graphql/resolvers/design_management/version_in_collection_resolver_spec.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Resolvers::DesignManagement::VersionInCollectionResolver do + include GraphqlHelpers + include DesignManagementTestHelpers + + let(:resolver) { described_class } + + describe '#resolve' do + let_it_be(:issue) { create(:issue) } + let_it_be(:current_user) { create(:user) } + let_it_be(:first_version) { create(:design_version, issue: issue) } + + let(:project) { issue.project } + let(:params) { {} } + + before do + enable_design_management + project.add_developer(current_user) + end + + let(:appropriate_error) { ::Gitlab::Graphql::Errors::ArgumentError } + + subject(:result) { resolve_version(issue.design_collection) } + + context 'Neither id nor sha is passed as parameters' do + it 'raises an appropriate error' do + expect { result }.to raise_error(appropriate_error) + end + end + + context 'we pass an id' do + let(:params) { { id: global_id_of(first_version) } } + + it { is_expected.to eq(first_version) } + end + + context 'we pass a sha' do + let(:params) { { sha: first_version.sha } } + + it { is_expected.to eq(first_version) } + end + + context 'we pass an inconsistent mixture of sha and version id' do + let(:params) { { sha: first_version.sha, id: global_id_of(create(:design_version)) } } + + it { is_expected.to be_nil } + end + + context 'we pass the id of something that is not a design_version' do + let(:params) { { id: global_id_of(project) } } + + it 'raises an appropriate error' do + expect { result }.to raise_error(appropriate_error) + end + end + end + + def resolve_version(obj, context = { current_user: current_user }) + resolve(resolver, obj: obj, args: params, ctx: context) + end +end diff --git a/spec/graphql/resolvers/design_management/version_resolver_spec.rb b/spec/graphql/resolvers/design_management/version_resolver_spec.rb new file mode 100644 index 00000000000..e7c09351204 --- /dev/null +++ b/spec/graphql/resolvers/design_management/version_resolver_spec.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Resolvers::DesignManagement::VersionResolver do + include GraphqlHelpers + include DesignManagementTestHelpers + + let_it_be(:issue) { create(:issue) } + let_it_be(:current_user) { create(:user) } + let_it_be(:version) { create(:design_version, issue: issue) } + let_it_be(:developer) { create(:user) } + + let(:project) { issue.project } + let(:params) { { id: global_id_of(version) } } + + before do + enable_design_management + project.add_developer(developer) + end + + context 'the current user is not authorized' do + let(:current_user) { create(:user) } + + it 'raises an error on resolution' do + expect { resolve_version }.to raise_error(::Gitlab::Graphql::Errors::ResourceNotAvailable) + end + end + + context 'the current user is authorized' do + let(:current_user) { developer } + + context 'the id parameter is provided' do + it 'returns the specified version' do + expect(resolve_version).to eq(version) + end + end + end + + def resolve_version + resolve(described_class, obj: nil, args: params, ctx: { current_user: current_user }) + end +end diff --git a/spec/graphql/resolvers/design_management/versions_resolver_spec.rb b/spec/graphql/resolvers/design_management/versions_resolver_spec.rb new file mode 100644 index 00000000000..d5bab025e45 --- /dev/null +++ b/spec/graphql/resolvers/design_management/versions_resolver_spec.rb @@ -0,0 +1,117 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Resolvers::DesignManagement::VersionsResolver do + include GraphqlHelpers + include DesignManagementTestHelpers + + describe '#resolve' do + let(:resolver) { described_class } + let_it_be(:issue) { create(:issue) } + let_it_be(:authorized_user) { create(:user) } + let_it_be(:first_version) { create(:design_version, issue: issue) } + let_it_be(:other_version) { create(:design_version, issue: issue) } + let_it_be(:first_design) { create(:design, issue: issue, versions: [first_version, other_version]) } + let_it_be(:other_design) { create(:design, :with_versions, issue: issue) } + + let(:project) { issue.project } + let(:params) { {} } + let(:current_user) { authorized_user } + let(:parent_args) { { irrelevant: 1.2 } } + let(:parent) { double('Parent', parent: nil, irep_node: double(arguments: parent_args)) } + + before do + enable_design_management + project.add_developer(authorized_user) + end + + shared_examples 'a source of versions' do + subject(:result) { resolve_versions(object) } + + let_it_be(:all_versions) { object.versions.ordered } + + context 'when the user is not authorized' do + let(:current_user) { create(:user) } + + it { is_expected.to be_empty } + end + + context 'without constraints' do + it 'returns the ordered versions' do + expect(result).to eq(all_versions) + end + end + + context 'when constrained' do + let_it_be(:matching) { all_versions.earlier_or_equal_to(first_version) } + + shared_examples 'a query for all_versions up to the first_version' do + it { is_expected.to eq(matching) } + end + + context 'by earlier_or_equal_to_id' do + let(:params) { { id: global_id_of(first_version) } } + + it_behaves_like 'a query for all_versions up to the first_version' + end + + context 'by earlier_or_equal_to_sha' do + let(:params) { { sha: first_version.sha } } + + it_behaves_like 'a query for all_versions up to the first_version' + end + + context 'by earlier_or_equal_to_sha AND earlier_or_equal_to_id' do + context 'and they match' do + # This usage is rather dumb, but so long as they match, this will + # return successfully + let(:params) do + { + sha: first_version.sha, + id: global_id_of(first_version) + } + end + + it_behaves_like 'a query for all_versions up to the first_version' + end + + context 'and they do not match' do + let(:params) do + { + sha: first_version.sha, + id: global_id_of(other_version) + } + end + + it 'raises a suitable error' do + expect { result }.to raise_error(GraphQL::ExecutionError) + end + end + end + + context 'by at_version in parent' do + let(:parent_args) { { atVersion: global_id_of(first_version) } } + + it_behaves_like 'a query for all_versions up to the first_version' + end + end + end + + describe 'a design collection' do + let_it_be(:object) { DesignManagement::DesignCollection.new(issue) } + + it_behaves_like 'a source of versions' + end + + describe 'a design' do + let_it_be(:object) { first_design } + + it_behaves_like 'a source of versions' + end + + def resolve_versions(obj, context = { current_user: current_user }) + eager_resolve(resolver, obj: obj, args: params.merge(parent: parent), ctx: context) + end + end +end |