diff --git a/app/graphql/resolvers/projects/branches_tipping_at_commit_resolver.rb b/app/graphql/resolvers/projects/branches_tipping_at_commit_resolver.rb deleted file mode 100644 index 7e2661f3f7728f0187847477bfb903fc44c56a68..0000000000000000000000000000000000000000 --- a/app/graphql/resolvers/projects/branches_tipping_at_commit_resolver.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -module Resolvers - module Projects - class BranchesTippingAtCommitResolver < RefTippingAtCommitResolver - MAX_LIMIT = 100 - - calls_gitaly! - - type ::Types::Projects::CommitParentNamesType, null: true - - # the methode ref_prefix is implemented - # because this class is prepending Resolver::CommitParentNamesResolver module - # through it's parent ::Resolvers::RefTippingAtCommitResolver - def ref_prefix - Gitlab::Git::BRANCH_REF_PREFIX - end - end - end -end diff --git a/app/graphql/resolvers/projects/commit_parent_names_resolver.rb b/app/graphql/resolvers/projects/commit_parent_names_resolver.rb deleted file mode 100644 index f52776d715a8f5439ec9de0dafd20f0ac9762686..0000000000000000000000000000000000000000 --- a/app/graphql/resolvers/projects/commit_parent_names_resolver.rb +++ /dev/null @@ -1,33 +0,0 @@ -# frozen_string_literal: true - -module Resolvers - module Projects - module CommitParentNamesResolver - extend ActiveSupport::Concern - - prepended do - argument :commit_sha, GraphQL::Types::String, - required: true, - description: 'Project commit SHA identifier. For example, `287774414568010855642518513f085491644061`.' - - argument :limit, GraphQL::Types::Int, - required: false, - description: 'Number of branch names to return.' - - alias_method :project, :object - end - - def compute_limit(limit) - max = self.class::MAX_LIMIT - - limit ? [limit, max].min : max - end - - def get_tipping_refs(project, sha, limit: 0) - # the methode ref_prefix needs to be implemented in all classes prepending this module - refs = project.repository.refs_by_oid(oid: sha, ref_patterns: [ref_prefix], limit: limit) - refs.map { |n| n.delete_prefix(ref_prefix) } - end - end - end -end diff --git a/app/graphql/resolvers/projects/commit_references_resolver.rb b/app/graphql/resolvers/projects/commit_references_resolver.rb new file mode 100644 index 0000000000000000000000000000000000000000..ca25bad468c90c5fee1e23398e99633894f154ee --- /dev/null +++ b/app/graphql/resolvers/projects/commit_references_resolver.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module Resolvers + module Projects + class CommitReferencesResolver < BaseResolver + include Gitlab::Graphql::Authorize::AuthorizeResource + + argument :commit_sha, GraphQL::Types::String, + required: true, + description: 'Project commit SHA identifier. For example, `287774414568010855642518513f085491644061`.' + + authorize :read_commit + + alias_method :project, :object + + calls_gitaly! + + type ::Types::CommitReferencesType, null: true + + def resolve(commit_sha:) + authorized_find!(oid: commit_sha) + end + + def find_object(oid:) + project.repository.commit(oid) + end + end + end +end diff --git a/app/graphql/resolvers/projects/ref_tipping_at_commit_resolver.rb b/app/graphql/resolvers/projects/ref_tipping_at_commit_resolver.rb deleted file mode 100644 index 3259a29ac9c761d6cf956bb94727b5da19810942..0000000000000000000000000000000000000000 --- a/app/graphql/resolvers/projects/ref_tipping_at_commit_resolver.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module Resolvers - module Projects - class RefTippingAtCommitResolver < BaseResolver - include Gitlab::Graphql::Authorize::AuthorizeResource - prepend CommitParentNamesResolver - - type ::Types::Projects::CommitParentNamesType, null: true - - authorize :read_code - - def resolve(commit_sha:, limit: nil) - final_limit = compute_limit(limit) - - names = get_tipping_refs(project, commit_sha, limit: final_limit) - - { - names: names, - total_count: nil - } - end - end - end -end diff --git a/app/graphql/resolvers/projects/tags_tipping_at_commit_resolver.rb b/app/graphql/resolvers/projects/tags_tipping_at_commit_resolver.rb deleted file mode 100644 index 78ee9c997d51ae83580d2b82e11da9377d608f21..0000000000000000000000000000000000000000 --- a/app/graphql/resolvers/projects/tags_tipping_at_commit_resolver.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -module Resolvers - module Projects - class TagsTippingAtCommitResolver < RefTippingAtCommitResolver - MAX_LIMIT = 100 - - calls_gitaly! - - type ::Types::Projects::CommitParentNamesType, null: true - - # the methode ref_prefix is implemented - # because this class is prepending Resolver::CommitParentNamesResolver module - # through it's parent ::Resolvers::RefTippingAtCommitResolver - def ref_prefix - Gitlab::Git::TAG_REF_PREFIX - end - end - end -end diff --git a/app/graphql/types/commit_references_type.rb b/app/graphql/types/commit_references_type.rb new file mode 100644 index 0000000000000000000000000000000000000000..2844a552f3e0945aa83d3f04ed4f51c70e2becf8 --- /dev/null +++ b/app/graphql/types/commit_references_type.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +module Types + class CommitReferencesType < BaseObject + graphql_name 'CommitReferences' + + authorize :read_commit + + def self.field_for_tipping_refs(field_name, field_description) + field field_name, ::Types::Projects::CommitParentNamesType, + null: true, + calls_gitaly: true, + description: field_description do + argument :limit, GraphQL::Types::Int, + required: true, + default_value: 100, + description: 'Number of ref names to return.', + validates: { numericality: { within: 1..1000 } } + end + end + + def self.field_for_containing_refs(field_name, field_description) + field field_name, ::Types::Projects::CommitParentNamesType, + null: true, + calls_gitaly: true, + description: field_description do + argument :exclude_tipped, GraphQL::Types::Boolean, + required: true, + default_value: false, + description: 'Exclude tipping refs. WARNING: This argument can be confusing, if there is a limit. + for example set the limit to 5 and in the 5 out a total of 25 refs there is 2 tipped refs, + then the method will only 3 refs, even though there is more.' + # rubocop: disable GraphQL/ArgumentUniqueness + argument :limit, GraphQL::Types::Int, + required: true, + default_value: 100, + description: 'Number of ref names to return.', + validates: { numericality: { within: 1..1000 } } + # rubocop: enable GraphQL/ArgumentUniqueness + end + end + + field_for_tipping_refs :tipping_tags, "Get tag names tipping at a given commit." + + field_for_tipping_refs :tipping_branches, "Get branch names tipping at a given commit." + + field_for_containing_refs :containing_tags, "Get tag names containing a given commit." + + field_for_containing_refs :containing_branches, "Get branch names containing a given commit." + + def tipping_tags(limit:) + { names: object.tipping_tags(limit: limit) } + end + + def tipping_branches(limit:) + { names: object.tipping_branches(limit: limit) } + end + + def containing_tags(limit:, exclude_tipped:) + { names: object.tags_containing(limit: limit, exclude_tipped: exclude_tipped) } + end + + def containing_branches(limit:, exclude_tipped:) + { names: object.branches_containing(limit: limit, exclude_tipped: exclude_tipped) } + end + end +end diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb index 7a26c80606155f3b3e0bd923fc57300accf229aa..f8a516501c393251457db5af08f4b98788346131 100644 --- a/app/graphql/types/project_type.rb +++ b/app/graphql/types/project_type.rb @@ -617,15 +617,11 @@ class ProjectType < BaseObject authorize: :read_cycle_analytics, alpha: { milestone: '15.10' } - field :tags_tipping_at_commit, ::Types::Projects::CommitParentNamesType, - null: true, - resolver: Resolvers::Projects::TagsTippingAtCommitResolver, - description: "Get tag names tipping at a given commit." - - field :branches_tipping_at_commit, ::Types::Projects::CommitParentNamesType, - null: true, - resolver: Resolvers::Projects::BranchesTippingAtCommitResolver, - description: "Get branch names tipping at a given commit." + field :commit_references, ::Types::CommitReferencesType, + null: true, + resolver: Resolvers::Projects::CommitReferencesResolver, + alpha: { milestone: '16.0' }, + description: "Get tag names containing a given commit." def timelog_categories object.project_namespace.timelog_categories if Feature.enabled?(:timelog_categories) diff --git a/app/graphql/types/projects/commit_parent_names_type.rb b/app/graphql/types/projects/commit_parent_names_type.rb index 0aa1ca768e91f53e3dc73a92c171e17efdf718ad..39f8f1cdd07922cd386fe9860878f8f407ae471d 100644 --- a/app/graphql/types/projects/commit_parent_names_type.rb +++ b/app/graphql/types/projects/commit_parent_names_type.rb @@ -7,7 +7,6 @@ class CommitParentNamesType < BaseObject graphql_name 'CommitParentNames' field :names, [GraphQL::Types::String], null: true, description: 'Names of the commit parent (branch or tag).' - field :total_count, GraphQL::Types::Int, null: true, description: 'Total of parent branches or tags.' end # rubocop: enable Graphql/AuthorizeTypes end diff --git a/app/models/commit.rb b/app/models/commit.rb index b618845705c0f34d5c2050d8ad7bcf1c7efc339c..6d17d7f495d014e736e06b9490eccee13db46091 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -572,8 +572,43 @@ def user_mention_identifier } end + def tipping_branches(limit: 0) + tipping_refs(Gitlab::Git::BRANCH_REF_PREFIX, limit: limit) + end + + def tipping_tags(limit: 0) + tipping_refs(Gitlab::Git::TAG_REF_PREFIX, limit: limit) + end + + def branches_containing(limit: 0, exclude_tipped: false) + # WARNING: This argument can be confusing, if there is a limit. + # for example set the limit to 5 and in the 5 out a total of 25 refs there is 2 tipped refs, + # then the method will only 3 refs, even though there is more. + excluded = exclude_tipped ? tipping_branches : [] + + refs = repository.branch_names_contains(id, limit: limit) || [] + refs - excluded + end + + def tags_containing(limit: 0, exclude_tipped: false) + # WARNING: This argument can be confusing, if there is a limit. + # for example set the limit to 5 and in the 5 out a total of 25 refs there is 2 tipped refs, + # then the method will only 3 refs, even though there is more. + excluded = exclude_tipped ? tipping_tags : [] + + refs = repository.tag_names_contains(id, limit: limit) || [] + refs - excluded + end + private + def tipping_refs(ref_prefix, limit: 0) + strong_memoize_with(:tipping_tags, ref_prefix, limit) do + refs = repository.refs_by_oid(oid: id, ref_patterns: [ref_prefix], limit: limit) + refs.map { |n| n.delete_prefix(ref_prefix) } + end + end + def expire_note_etag_cache_for_related_mrs MergeRequest.includes(target_project: :namespace).by_commit_sha(id).find_each(&:expire_note_etag_cache) end diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index c068397c50b2cab20dee842d62b6576673c5df2d..e3abed66d770dd6b731f47b7de0dde82d329a6b3 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -12792,7 +12792,60 @@ four standard [pagination arguments](#connection-pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | | <a id="commitparentnamesnames"></a>`names` | [`[String!]`](#string) | Names of the commit parent (branch or tag). | -| <a id="commitparentnamestotalcount"></a>`totalCount` | [`Int`](#int) | Total of parent branches or tags. | + +### `CommitReferences` + +#### Fields with arguments + +##### `CommitReferences.containingBranches` + +Get branch names containing a given commit. + +Returns [`CommitParentNames`](#commitparentnames). + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="commitreferencescontainingbranchesexcludetipped"></a>`excludeTipped` | [`Boolean!`](#boolean) | Exclude tipping refs. WARNING: This argument can be confusing, if there is a limit. for example set the limit to 5 and in the 5 out a total of 25 refs there is 2 tipped refs, then the method will only 3 refs, even though there is more. | +| <a id="commitreferencescontainingbrancheslimit"></a>`limit` | [`Int!`](#int) | Number of ref names to return. | + +##### `CommitReferences.containingTags` + +Get tag names containing a given commit. + +Returns [`CommitParentNames`](#commitparentnames). + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="commitreferencescontainingtagsexcludetipped"></a>`excludeTipped` | [`Boolean!`](#boolean) | Exclude tipping refs. WARNING: This argument can be confusing, if there is a limit. for example set the limit to 5 and in the 5 out a total of 25 refs there is 2 tipped refs, then the method will only 3 refs, even though there is more. | +| <a id="commitreferencescontainingtagslimit"></a>`limit` | [`Int!`](#int) | Number of ref names to return. | + +##### `CommitReferences.tippingBranches` + +Get branch names tipping at a given commit. + +Returns [`CommitParentNames`](#commitparentnames). + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="commitreferencestippingbrancheslimit"></a>`limit` | [`Int!`](#int) | Number of ref names to return. | + +##### `CommitReferences.tippingTags` + +Get tag names tipping at a given commit. + +Returns [`CommitParentNames`](#commitparentnames). + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="commitreferencestippingtagslimit"></a>`limit` | [`Int!`](#int) | Number of ref names to return. | ### `ComplianceFramework` @@ -19195,19 +19248,6 @@ four standard [pagination arguments](#connection-pagination-arguments): | ---- | ---- | ----------- | | <a id="projectboardsid"></a>`id` | [`BoardID`](#boardid) | Find a board by its ID. | -##### `Project.branchesTippingAtCommit` - -Get branch names tipping at a given commit. - -Returns [`CommitParentNames`](#commitparentnames). - -###### Arguments - -| Name | Type | Description | -| ---- | ---- | ----------- | -| <a id="projectbranchestippingatcommitcommitsha"></a>`commitSha` | [`String!`](#string) | Project commit SHA identifier. For example, `287774414568010855642518513f085491644061`. | -| <a id="projectbranchestippingatcommitlimit"></a>`limit` | [`Int`](#int) | Number of branch names to return. | - ##### `Project.ciConfigVariables` CI/CD config variable. @@ -19283,6 +19323,22 @@ four standard [pagination arguments](#connection-pagination-arguments): | <a id="projectclusteragentshasremotedevelopmentagentconfig"></a>`hasRemoteDevelopmentAgentConfig` | [`Boolean`](#boolean) | Returns only cluster agents which have an associated remote development agent config. | | <a id="projectclusteragentshasvulnerabilities"></a>`hasVulnerabilities` | [`Boolean`](#boolean) | Returns only cluster agents which have vulnerabilities. | +##### `Project.commitReferences` + +Get tag names containing a given commit. + +WARNING: +**Introduced** in 16.0. +This feature is an Experiment. It can be changed or removed at any time. + +Returns [`CommitReferences`](#commitreferences). + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="projectcommitreferencescommitsha"></a>`commitSha` | [`String!`](#string) | Project commit SHA identifier. For example, `287774414568010855642518513f085491644061`. | + ##### `Project.containerRepositories` Container repositories of the project. @@ -20238,19 +20294,6 @@ four standard [pagination arguments](#connection-pagination-arguments): | <a id="projectsnippetsids"></a>`ids` | [`[SnippetID!]`](#snippetid) | Array of global snippet IDs. For example, `gid://gitlab/ProjectSnippet/1`. | | <a id="projectsnippetsvisibility"></a>`visibility` | [`VisibilityScopesEnum`](#visibilityscopesenum) | Visibility of the snippet. | -##### `Project.tagsTippingAtCommit` - -Get tag names tipping at a given commit. - -Returns [`CommitParentNames`](#commitparentnames). - -###### Arguments - -| Name | Type | Description | -| ---- | ---- | ----------- | -| <a id="projecttagstippingatcommitcommitsha"></a>`commitSha` | [`String!`](#string) | Project commit SHA identifier. For example, `287774414568010855642518513f085491644061`. | -| <a id="projecttagstippingatcommitlimit"></a>`limit` | [`Int`](#int) | Number of branch names to return. | - ##### `Project.terraformState` Find a single Terraform state by name. diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index df630d6336cb3f0556cea94b775e7956f23755fb..edb856d34df84258f90afdf526f7bc9da367678d 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -885,4 +885,94 @@ expect(commit.has_been_reverted?(user, issue.notes_with_associations)).to eq(false) end end + + describe '#tipping_refs' do + let_it_be(:tag_name) { 'v1.1.0' } + let_it_be(:branch_names) { %w[master not-merged-branch v1.1.0] } + + shared_examples 'tipping ref names' do + context 'when called without limits' do + it 'return tipping refs names' do + expect(called_method.call).to eq(expected) + end + end + + context 'when called with limits' do + it 'return tipping refs names' do + limit = 1 + expect(called_method.call(limit).size).to be <= limit + end + end + + describe '#tipping_branches' do + let(:called_method) { ->(limit = 0) { commit.tipping_branches(limit: limit) } } + let(:expected) { branch_names } + + it_behaves_like 'with tipping ref names' + end + + describe '#tipping_tags' do + let(:called_method) { ->(limit = 0) { commit.tipping_tags(limit: limit) } } + let(:expected) { [tag_name] } + + it_behaves_like 'with tipping ref names' + end + end + end + + context 'containing refs' do + shared_examples 'containing ref names' do + context 'without arguments' do + it 'returns branch names containing the commit' do + expect(ref_containing.call).to eq(containing_refs) + end + end + + context 'with limit argument' do + it 'returns the appropriate amount branch names' do + limit = 2 + expect(ref_containing.call(limit: limit).size).to be <= limit + end + end + + context 'with tipping refs excluded' do + let(:excluded_refs) do + project.repository.refs_by_oid(oid: commit_sha, ref_patterns: [ref_prefix]).map { |n| n.delete_prefix(ref_prefix) } + end + + it 'returns branch names containing the commit without the one with the commit at tip' do + expect(ref_containing.call(excluded_tipped: true)).to eq(containing_refs - excluded_refs) + end + + it 'returns the appropriate amount branch names with limit argument' do + limit = 2 + expect(ref_containing.call(limit: limit, excluded_tipped: true).size).to be <= limit + end + end + end + + describe '#branches_containing' do + let_it_be(:commit_sha) { project.commit.sha } + let_it_be(:containing_refs) { project.repository.branch_names_contains(commit_sha) } + + let(:ref_prefix) { Gitlab::Git::BRANCH_REF_PREFIX } + + let(:ref_containing) { ->(limit: 0, excluded_tipped: false) { commit.branches_containing(exclude_tipped: excluded_tipped, limit: limit) } } + + it_behaves_like 'containing ref names' + end + + describe '#tags_containing' do + let_it_be(:tag_name) { 'v1.1.0' } + let_it_be(:commit_sha) { project.repository.find_tag(tag_name).target_commit.sha } + let_it_be(:containing_refs) { %w[v1.1.0 v1.1.1] } + + let(:ref_prefix) { Gitlab::Git::TAG_REF_PREFIX } + + let(:commit) { project.repository.commit(commit_sha) } + let(:ref_containing) { ->(limit: 0, excluded_tipped: false) { commit.tags_containing(exclude_tipped: excluded_tipped, limit: limit) } } + + it_behaves_like 'containing ref names' + end + end end diff --git a/spec/requests/api/graphql/project/branches_tipping_at_commit_spec.rb b/spec/requests/api/graphql/project/branches_tipping_at_commit_spec.rb deleted file mode 100644 index bba8977078d8dde29cb0249024c9ab4f058a6a65..0000000000000000000000000000000000000000 --- a/spec/requests/api/graphql/project/branches_tipping_at_commit_spec.rb +++ /dev/null @@ -1,67 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Query.project(fullPath).tagsTippingAtCommit(commitSha)', feature_category: :source_code_management do - include GraphqlHelpers - include Presentable - - let_it_be(:project) { create(:project, :repository) } - let_it_be(:repository) { project.repository.raw } - let_it_be(:current_user) { project.first_owner } - let_it_be(:branches_names) { %w[master not-merged-branch v1.1.0] } - - let(:post_query) { post_graphql(query, current_user: current_user) } - let(:path) { %w[project branchesTippingAtCommit names] } - let(:data) { graphql_data.dig(*path) } - - let(:query) do - graphql_query_for( - :project, - { fullPath: project.full_path }, - query_graphql_field(:branchesTippingAtCommit, { commitSha: commit_sha }, :names) - ) - end - - context 'when commit exists and is tipping branches' do - let_it_be(:commit_sha) { repository.commit.id } - - context 'with authorized user' do - it 'returns branches names tipping the commit' do - post_query - - expect(data).to eq(branches_names) - end - end - - context 'when user is not authorized' do - let(:current_user) { create(:user) } - - it 'returns branches names tipping the commit' do - post_query - - expect(data).to eq(nil) - end - end - end - - context 'when commit does not exist' do - let(:commit_sha) { '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff4' } - - it 'returns tags names tipping the commit' do - post_query - - expect(data).to eq([]) - end - end - - context 'when commit exists but does not tip any branches' do - let(:commit_sha) { project.repository.commits(nil, { limit: 4 }).commits[2].id } - - it 'returns tags names tipping the commit' do - post_query - - expect(data).to eq([]) - end - end -end diff --git a/spec/requests/api/graphql/project/commit_references_spec.rb b/spec/requests/api/graphql/project/commit_references_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..4b545adee12fd59232ca41f816b173750155d718 --- /dev/null +++ b/spec/requests/api/graphql/project/commit_references_spec.rb @@ -0,0 +1,240 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Query.project(fullPath).commitReferences(commitSha)', feature_category: :source_code_management do + include GraphqlHelpers + include Presentable + + let_it_be(:project) { create(:project, :repository) } + let_it_be(:repository) { project.repository.raw } + let_it_be(:current_user) { project.first_owner } + let_it_be(:branches_names) { %w[master not-merged-branch v1.1.0] } + let_it_be(:tag_name) { 'v1.0.0' } + let_it_be(:commit_sha) { repository.commit.id } + + let(:post_query) { post_graphql(query, current_user: current_user) } + let(:data) { graphql_data.dig(*path) } + let(:base_args) { {} } + let(:args) { base_args } + + shared_context 'with the limit argument' do + context 'with limit of 2' do + let(:args) { { limit: 2 } } + + it 'returns the right amount of refs' do + post_query + expect(data.count).to be <= 2 + end + end + + context 'with limit of -2' do + let(:args) { { limit: -2 } } + + it 'casts an argument error "limit must be greater then 0"' do + post_query + expect(graphql_errors).to include(custom_graphql_error(path - ['names'], + 'limit must be within 1..1000')) + end + end + + context 'with limit of 1001' do + let(:args) { { limit: 1001 } } + + it 'casts an argument error "limit must be greater then 0"' do + post_query + expect(graphql_errors).to include(custom_graphql_error(path - ['names'], + 'limit must be within 1..1000')) + end + end + end + + describe 'the path commitReferences should return nil' do + let(:path) { %w[project commitReferences] } + + let(:query) do + graphql_query_for(:project, { fullPath: project.full_path }, + query_graphql_field( + :commitReferences, + { commitSha: commit_sha }, + query_graphql_field(:tippingTags, :names) + ) + ) + end + + context 'when commit does not exist' do + let(:commit_sha) { '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff4' } + + it 'commitReferences returns nil' do + post_query + expect(data).to eq(nil) + end + end + + context 'when sha length is incorrect' do + let(:commit_sha) { 'foo' } + + it 'commitReferences returns nil' do + post_query + expect(data).to eq(nil) + end + end + + context 'when user is not authorized' do + let(:commit_sha) { repository.commit.id } + let(:current_user) { create(:user) } + + it 'commitReferences returns nil' do + post_query + expect(data).to eq(nil) + end + end + end + + context 'with containing refs' do + let(:base_args) { { excludeTipped: false } } + let(:excluded_tipped_args) do + hash = base_args.dup + hash[:excludeTipped] = true + hash + end + + context 'with path Query.project(fullPath).commitReferences(commitSha).containingTags' do + let_it_be(:commit_sha) { repository.find_tag(tag_name).target_commit.sha } + let_it_be(:path) { %w[project commitReferences containingTags names] } + let(:query) do + graphql_query_for( + :project, + { fullPath: project.full_path }, + query_graphql_field( + :commitReferences, + { commitSha: commit_sha }, + query_graphql_field(:containingTags, args, :names) + ) + ) + end + + context 'without excludeTipped argument' do + it 'returns tags names containing the commit' do + post_query + expect(data).to eq(%w[v1.0.0 v1.1.0 v1.1.1]) + end + end + + context 'with excludeTipped argument' do + let_it_be(:ref_prefix) { Gitlab::Git::TAG_REF_PREFIX } + + let(:args) { excluded_tipped_args } + + it 'returns tags names containing the commit without the tipped tags' do + excluded_refs = project.repository + .refs_by_oid(oid: commit_sha, ref_patterns: [ref_prefix]) + .map { |n| n.delete_prefix(ref_prefix) } + + post_query + expect(data).to eq(%w[v1.0.0 v1.1.0 v1.1.1] - excluded_refs) + end + end + + include_context 'with the limit argument' + end + + context 'with path Query.project(fullPath).commitReferences(commitSha).containingBranches' do + let_it_be(:ref_prefix) { Gitlab::Git::BRANCH_REF_PREFIX } + let_it_be(:path) { %w[project commitReferences containingBranches names] } + + let(:query) do + graphql_query_for( + :project, + { fullPath: project.full_path }, + query_graphql_field( + :commitReferences, + { commitSha: commit_sha }, + query_graphql_field(:containingBranches, args, :names) + ) + ) + end + + context 'without excludeTipped argument' do + it 'returns branch names containing the commit' do + refs = project.repository.branch_names_contains(commit_sha) + + post_query + + expect(data).to eq(refs) + end + end + + context 'with excludeTipped argument' do + let(:args) { excluded_tipped_args } + + it 'returns branch names containing the commit without the tipped branch' do + refs = project.repository.branch_names_contains(commit_sha) + + excluded_refs = project.repository + .refs_by_oid(oid: commit_sha, ref_patterns: [ref_prefix]) + .map { |n| n.delete_prefix(ref_prefix) } + + post_query + + expect(data).to eq(refs - excluded_refs) + end + end + + include_context 'with the limit argument' + end + end + + context 'with tipping refs' do + context 'with path Query.project(fullPath).commitReferences(commitSha).tippingTags' do + let(:commit_sha) { repository.find_tag(tag_name).dereferenced_target.sha } + let(:path) { %w[project commitReferences tippingTags names] } + + let(:query) do + graphql_query_for( + :project, + { fullPath: project.full_path }, + query_graphql_field( + :commitReferences, + { commitSha: commit_sha }, + query_graphql_field(:tippingTags, args, :names) + ) + ) + end + + context 'with authorized user' do + it 'returns tags names tipping the commit' do + post_query + + expect(data).to eq([tag_name]) + end + end + + include_context 'with the limit argument' + end + + context 'with path Query.project(fullPath).commitReferences(commitSha).tippingBranches' do + let(:path) { %w[project commitReferences tippingBranches names] } + + let(:query) do + graphql_query_for( + :project, + { fullPath: project.full_path }, + query_graphql_field( + :commitReferences, + { commitSha: commit_sha }, + query_graphql_field(:tippingBranches, args, :names) + ) + ) + end + + it 'returns branches names tipping the commit' do + post_query + + expect(data).to eq(branches_names) + end + + include_context 'with the limit argument' + end + end +end diff --git a/spec/requests/api/graphql/project/tags_tipping_at_commit_spec.rb b/spec/requests/api/graphql/project/tags_tipping_at_commit_spec.rb deleted file mode 100644 index a5e26482a9ef448c69453a4a19bf5f195198ff5a..0000000000000000000000000000000000000000 --- a/spec/requests/api/graphql/project/tags_tipping_at_commit_spec.rb +++ /dev/null @@ -1,67 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Query.project(fullPath).tagsTippingAtCommit(commitSha)', feature_category: :source_code_management do - include GraphqlHelpers - include Presentable - - let_it_be(:project) { create(:project, :repository) } - let_it_be(:repository) { project.repository.raw } - let_it_be(:current_user) { project.first_owner } - let_it_be(:tag_name) { 'v1.0.0' } - - let(:post_query) { post_graphql(query, current_user: current_user) } - let(:path) { %w[project tagsTippingAtCommit names] } - let(:data) { graphql_data.dig(*path) } - - let(:query) do - graphql_query_for( - :project, - { fullPath: project.full_path }, - query_graphql_field(:tagsTippingAtCommit, { commitSha: commit_sha }, :names) - ) - end - - context 'when commit exists and is tipping tags' do - let(:commit_sha) { repository.find_tag(tag_name).dereferenced_target.sha } - - context 'with authorized user' do - it 'returns tags names tipping the commit' do - post_query - - expect(data).to eq([tag_name]) - end - end - - context 'when user is not authorized' do - let(:current_user) { create(:user) } - - it 'returns tags names tipping the commit' do - post_query - - expect(data).to eq(nil) - end - end - end - - context 'when commit does not exist' do - let(:commit_sha) { '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff4' } - - it 'returns tags names tipping the commit' do - post_query - - expect(data).to eq([]) - end - end - - context 'when commit exists but does not tip any tags' do - let(:commit_sha) { project.repository.commits(nil, { limit: 4 }).commits[2].id } - - it 'returns tags names tipping the commit' do - post_query - - expect(data).to eq([]) - end - end -end