Commit 0b7caa6e authored by Nathan Friend's avatar Nathan Friend Committed by Douglas Barbosa Alexandre

Add GraphQL mutation to update release

This commit adds a new GraphQL mutation - `releaseUpdate` - that updates
an existing release.
parent 35f1ce45
# frozen_string_literal: true
module Mutations
module Releases
class Update < Base
graphql_name 'ReleaseUpdate'
field :release,
Types::ReleaseType,
null: true,
description: 'The release after mutation.'
argument :tag_name, GraphQL::STRING_TYPE,
required: true, as: :tag,
description: 'Name of the tag associated with the release'
argument :name, GraphQL::STRING_TYPE,
required: false,
description: 'Name of the release'
argument :description, GraphQL::STRING_TYPE,
required: false,
description: 'Description (release notes) of the release'
argument :released_at, Types::TimeType,
required: false,
description: 'The release date'
argument :milestones, [GraphQL::STRING_TYPE],
required: false,
description: 'The title of each milestone the release is associated with. GitLab Premium customers can specify group milestones.'
authorize :update_release
def ready?(**args)
if args.key?(:released_at) && args[:released_at].nil?
raise Gitlab::Graphql::Errors::ArgumentError,
'if the releasedAt argument is provided, it cannot be null'
end
if args.key?(:milestones) && args[:milestones].nil?
raise Gitlab::Graphql::Errors::ArgumentError,
'if the milestones argument is provided, it cannot be null'
end
super
end
def resolve(project_path:, **scalars)
project = authorized_find!(full_path: project_path)
params = scalars.with_indifferent_access
release_result = ::Releases::UpdateService.new(project, current_user, params).execute
if release_result[:status] == :success
{
release: release_result[:release],
errors: []
}
else
{
release: nil,
errors: [release_result[:message]]
}
end
end
end
end
end
......@@ -65,6 +65,7 @@ module Types
mount_mutation Mutations::Notes::RepositionImageDiffNote
mount_mutation Mutations::Notes::Destroy
mount_mutation Mutations::Releases::Create
mount_mutation Mutations::Releases::Update
mount_mutation Mutations::Terraform::State::Delete
mount_mutation Mutations::Terraform::State::Lock
mount_mutation Mutations::Terraform::State::Unlock
......
---
title: Add GraphQL mutation to update a release
merge_request: 46611
author:
type: added
......@@ -13898,6 +13898,7 @@ type Mutation {
prometheusIntegrationUpdate(input: PrometheusIntegrationUpdateInput!): PrometheusIntegrationUpdatePayload
promoteToEpic(input: PromoteToEpicInput!): PromoteToEpicPayload
releaseCreate(input: ReleaseCreateInput!): ReleaseCreatePayload
releaseUpdate(input: ReleaseUpdateInput!): ReleaseUpdatePayload
removeAwardEmoji(input: RemoveAwardEmojiInput!): RemoveAwardEmojiPayload @deprecated(reason: "Use awardEmojiRemove. Deprecated in 13.2")
removeProjectFromSecurityDashboard(input: RemoveProjectFromSecurityDashboardInput!): RemoveProjectFromSecurityDashboardPayload
......@@ -18567,6 +18568,66 @@ type ReleaseSourceEdge {
node: ReleaseSource
}
"""
Autogenerated input type of ReleaseUpdate
"""
input ReleaseUpdateInput {
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
Description (release notes) of the release
"""
description: String
"""
The title of each milestone the release is associated with. GitLab Premium customers can specify group milestones.
"""
milestones: [String!]
"""
Name of the release
"""
name: String
"""
Full path of the project the release is associated with
"""
projectPath: ID!
"""
The release date
"""
releasedAt: Time
"""
Name of the tag associated with the release
"""
tagName: String!
}
"""
Autogenerated return type of ReleaseUpdate
"""
type ReleaseUpdatePayload {
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
Errors encountered during execution of the mutation.
"""
errors: [String!]!
"""
The release after mutation.
"""
release: Release
}
"""
Autogenerated input type of RemoveAwardEmoji
"""
......
......@@ -40466,6 +40466,33 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "releaseUpdate",
"description": null,
"args": [
{
"name": "input",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "ReleaseUpdateInput",
"ofType": null
}
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "ReleaseUpdatePayload",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "removeAwardEmoji",
"description": null,
......@@ -53636,6 +53663,170 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "ReleaseUpdateInput",
"description": "Autogenerated input type of ReleaseUpdate",
"fields": null,
"inputFields": [
{
"name": "projectPath",
"description": "Full path of the project the release is associated with",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "tagName",
"description": "Name of the tag associated with the release",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"defaultValue": null
},
{
"name": "name",
"description": "Name of the release",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "description",
"description": "Description (release notes) of the release",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "releasedAt",
"description": "The release date",
"type": {
"kind": "SCALAR",
"name": "Time",
"ofType": null
},
"defaultValue": null
},
{
"name": "milestones",
"description": "The title of each milestone the release is associated with. GitLab Premium customers can specify group milestones.",
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
},
"defaultValue": null
},
{
"name": "clientMutationId",
"description": "A unique identifier for the client performing the mutation.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
}
],
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "ReleaseUpdatePayload",
"description": "Autogenerated return type of ReleaseUpdate",
"fields": [
{
"name": "clientMutationId",
"description": "A unique identifier for the client performing the mutation.",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "errors",
"description": "Errors encountered during execution of the mutation.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "release",
"description": "The release after mutation.",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "Release",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "RemoveAwardEmojiInput",
......@@ -2629,6 +2629,16 @@ Represents the source code attached to a release in a particular format.
| `format` | String | Format of the source |
| `url` | String | Download URL of the source |
### ReleaseUpdatePayload
Autogenerated return type of ReleaseUpdate.
| Field | Type | Description |
| ----- | ---- | ----------- |
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
| `release` | Release | The release after mutation. |
### RemoveAwardEmojiPayload
Autogenerated return type of RemoveAwardEmoji.
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Mutations::Releases::Update do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :public, :repository, group: group) }
let_it_be(:milestone_12_3) { create(:milestone, project: project, title: '12.3') }
let_it_be(:milestone_12_4) { create(:milestone, project: project, title: '12.4') }
let_it_be(:group_milestone) { create(:milestone, group: group, title: '13.1') }
let_it_be(:developer) { create(:user) }
let_it_be(:tag) { 'v1.1.0'}
let_it_be(:release) do
create(:release, project: project, tag: tag)
end
let(:milestones) { [milestone_12_3.title, milestone_12_4.title] }
let(:mutation) { described_class.new(object: nil, context: { current_user: current_user }, field: nil) }
let(:mutation_arguments) do
{
project_path: project.full_path,
tag: tag,
milestones: milestones
}
end
around do |example|
freeze_time { example.run }
end
before do
project.add_developer(developer)
end
describe '#resolve' do
let(:current_user) { developer }
subject(:resolve) do
mutation.resolve(**mutation_arguments)
end
let(:updated_release) { subject[:release] }
context 'milestones' do
context 'when the provided milestones include a group milestone' do
let(:milestones) { [group_milestone.title] }
context 'when the group milestone association feature is licensed' do
before do
stub_licensed_features(group_milestone_project_releases: true)
end
it 'updates the milestone associations' do
expect(updated_release.milestones).to eq([group_milestone])
end
end
context 'when the group milestone association feature is not licensed' do
before do
stub_licensed_features(group_milestone_project_releases: false)
end
it 'returns the updated release as nil' do
expect(updated_release).to be_nil
end
it 'returns a validation error' do
expect(subject[:errors]).to eq(['Validation failed: None of the group milestones have the same project as the release'])
end
end
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Updating an existing release' do
include GraphqlHelpers
include Presentable
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :public, :repository, group: group) }
let_it_be(:milestone_12_3) { create(:milestone, project: project, title: '12.3') }
let_it_be(:milestone_12_4) { create(:milestone, project: project, title: '12.4') }
let_it_be(:group_milestone) { create(:milestone, group: group, title: '13.1') }
let_it_be(:developer) { create(:user) }
let_it_be(:tag_name) { 'v1.1.0'}
let_it_be(:release) do
create(:release, project: project, tag: tag_name)
end
let(:mutation_name) { :release_update }
let(:milestones) { [milestone_12_3.title, milestone_12_4.title, group_milestone.title] }
let(:mutation_arguments) do
{
projectPath: project.full_path,
tagName: tag_name,
milestones: milestones
}
end
let(:mutation) do
graphql_mutation(mutation_name, mutation_arguments, <<~FIELDS)
release {
milestones {
nodes {
title
}
}
}
errors
FIELDS
end
let(:update_release) { post_graphql_mutation(mutation, current_user: developer) }
let(:mutation_response) { graphql_mutation_response(mutation_name)&.with_indifferent_access }
before do
project.add_developer(developer)
end
context 'when the provided milestones include a group milestone' do
context 'when the group milestone association feature is licensed' do
before do
stub_licensed_features(group_milestone_project_releases: true)
update_release
end
it 'returns no errors' do
expect(graphql_errors).not_to be_present
end
it 'updates a release with both project and group milestone associations' do
returned_milestone_titles = mutation_response.dig(:release, :milestones, :nodes)
.map { |m| m[:title] }
# Right now the milestones are returned in a non-deterministic order.
# Once https://gitlab.com/gitlab-org/gitlab/-/issues/259012 is addressed,
# this test should be updated to expect a specific order.
expect(returned_milestone_titles).to match_array([
milestone_12_3.title,
milestone_12_4.title,
group_milestone.title
])
end
end
context 'when the group milestone association feature is not licensed' do
before do
stub_licensed_features(group_milestone_project_releases: false)
update_release
end
it 'returns an error-as-data field with a message about an invalid license' do
expect(mutation_response[:release]).to be_nil
expect(mutation_response[:errors].count).to eq(1)
expect(mutation_response[:errors].first).to match('Validation failed: None of the group milestones have the same project as the release')
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Mutations::Releases::Update do
let_it_be(:project) { create(:project, :public, :repository) }
let_it_be(:milestone_12_3) { create(:milestone, project: project, title: '12.3') }
let_it_be(:milestone_12_4) { create(:milestone, project: project, title: '12.4') }
let_it_be(:reporter) { create(:user) }
let_it_be(:developer) { create(:user) }
let_it_be(:tag) { 'v1.1.0'}
let_it_be(:name) { 'Version 1.0'}
let_it_be(:description) { 'The first release :rocket:' }
let_it_be(:released_at) { Time.parse('2018-12-10').utc }
let_it_be(:created_at) { Time.parse('2018-11-05').utc }
let_it_be(:milestones) { [milestone_12_3.title, milestone_12_4.title] }
let_it_be(:release) do
create(:release, project: project, tag: tag, name: name,
description: description, released_at: released_at,
created_at: created_at, milestones: [milestone_12_3, milestone_12_4])
end
let(:mutation) { described_class.new(object: nil, context: { current_user: current_user }, field: nil) }
let(:mutation_arguments) do
{
project_path: project.full_path,
tag: tag
}
end
around do |example|
freeze_time { example.run }
end
before do
project.add_reporter(reporter)
project.add_developer(developer)
end
shared_examples 'no changes to the release except for the' do |except_for|
it 'does not change other release properties' do
expect(updated_release.project).to eq(project)
expect(updated_release.tag).to eq(tag)
expect(updated_release.name).to eq(name) unless except_for == :name
expect(updated_release.description).to eq(description) unless except_for == :description
expect(updated_release.released_at).to eq(released_at) unless except_for == :released_at
# Right now the milestones are returned in a non-deterministic order.
# Because of this, we need to allow for milestones to be returned in any order.
# Once https://gitlab.com/gitlab-org/gitlab/-/issues/259012 has been
# fixed, this can be updated to expect a specific order.
expect(updated_release.milestones).to match_array([milestone_12_3, milestone_12_4]) unless except_for == :milestones
end
end
shared_examples 'validation error with message' do |message|
it 'returns the updated release as nil' do
expect(updated_release).to be_nil
end
it 'returns a validation error' do
expect(subject[:errors]).to eq([message])
end
end
describe '#ready?' do
let(:current_user) { developer }
subject(:ready) do
mutation.ready?(**mutation_arguments)
end
context 'when released_at is included as an argument but is passed nil' do
let(:mutation_arguments) { super().merge(released_at: nil) }
it 'raises a validation error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ArgumentError, 'if the releasedAt argument is provided, it cannot be null')
end
end
context 'when milestones is included as an argument but is passed nil' do
let(:mutation_arguments) { super().merge(milestones: nil) }
it 'raises a validation error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ArgumentError, 'if the milestones argument is provided, it cannot be null')
end
end
end
describe '#resolve' do
subject(:resolve) do
mutation.resolve(**mutation_arguments)
end
let(:updated_release) { subject[:release] }
context 'when the current user has access to create releases' do
let(:current_user) { developer }
context 'name' do
let(:mutation_arguments) { super().merge(name: updated_name) }
context 'when a new name is provided' do
let(:updated_name) { 'Updated name' }
it 'updates the name' do
expect(updated_release.name).to eq(updated_name)
end
it_behaves_like 'no changes to the release except for the', :name
end
context 'when nil is provided' do
let(:updated_name) { nil }
it 'updates the name to be the tag name' do
expect(updated_release.name).to eq(tag)
end
it_behaves_like 'no changes to the release except for the', :name
end
end
context 'description' do
let(:mutation_arguments) { super().merge(description: updated_description) }
context 'when a new description is provided' do
let(:updated_description) { 'Updated description' }
it 'updates the description' do
expect(updated_release.description).to eq(updated_description)
end
it_behaves_like 'no changes to the release except for the', :description
end
context 'when nil is provided' do
let(:updated_description) { nil }
it 'updates the description to nil' do
expect(updated_release.description).to eq(nil)
end
it_behaves_like 'no changes to the release except for the', :description
end
end
context 'released_at' do
let(:mutation_arguments) { super().merge(released_at: updated_released_at) }
context 'when a new released_at is provided' do
let(:updated_released_at) { Time.parse('2020-12-10').utc }
it 'updates the released_at' do
expect(updated_release.released_at).to eq(updated_released_at)
end
it_behaves_like 'no changes to the release except for the', :released_at
end
end
context 'milestones' do
let(:mutation_arguments) { super().merge(milestones: updated_milestones) }
context 'when a new set of milestones is provided provided' do
let(:updated_milestones) { [milestone_12_3.title] }
it 'updates the milestone associations' do
expect(updated_release.milestones).to eq([milestone_12_3])
end
it_behaves_like 'no changes to the release except for the', :milestones
end
context 'when an empty array is provided' do
let(:updated_milestones) { [] }
it 'removes all milestone associations' do
expect(updated_release.milestones).to eq([])
end
it_behaves_like 'no changes to the release except for the', :milestones
end
context 'when a non-existent milestone title is provided' do
let(:updated_milestones) { ['not real'] }
it_behaves_like 'validation error with message', 'Milestone(s) not found: not real'
end
context 'when a milestone title from a different project is provided' do
let(:milestone_in_different_project) { create(:milestone, title: 'milestone in different project') }
let(:updated_milestones) { [milestone_in_different_project.title] }
it_behaves_like 'validation error with message', 'Milestone(s) not found: milestone in different project'
end
end
context 'validation' do
context 'when no updated fields are provided' do
it_behaves_like 'validation error with message', 'params is empty'
end
context 'when the tag does not exist' do
let(:mutation_arguments) { super().merge(tag: 'not-a-real-tag') }
it_behaves_like 'validation error with message', 'Tag does not exist'
end
context 'when the project does not exist' do
let(:mutation_arguments) { super().merge(project_path: 'not/a/real/path') }
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable, "The resource that you are attempting to access does not exist or you don't have permission to perform this action")
end
end
end
end
context "when the current user doesn't have access to update releases" do
let(:current_user) { reporter }
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable, "The resource that you are attempting to access does not exist or you don't have permission to perform this action")
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Updating an existing release' do
include GraphqlHelpers
include Presentable
let_it_be(:public_user) { create(:user) }
let_it_be(:guest) { create(:user) }
let_it_be(:reporter) { create(:user) }
let_it_be(:developer) { create(:user) }
let_it_be(:project) { create(:project, :public, :repository) }
let_it_be(:milestone_12_3) { create(:milestone, project: project, title: '12.3') }
let_it_be(:milestone_12_4) { create(:milestone, project: project, title: '12.4') }
let_it_be(:tag_name) { 'v1.1.0' }
let_it_be(:name) { 'Version 7.12.5'}
let_it_be(:description) { 'Release 7.12.5 :rocket:' }
let_it_be(:released_at) { '2018-12-10' }
let_it_be(:created_at) { '2018-11-05' }
let_it_be(:milestones) { [milestone_12_3, milestone_12_4] }
let_it_be(:release) do
create(:release, project: project, tag: tag_name, name: name,
description: description, released_at: Time.parse(released_at).utc,
created_at: Time.parse(created_at).utc, milestones: milestones)
end
let(:mutation_name) { :release_update }
let(:mutation_arguments) do
{
projectPath: project.full_path,
tagName: tag_name
}
end
let(:mutation) do
graphql_mutation(mutation_name, mutation_arguments, <<~FIELDS)
release {
tagName
name
description
releasedAt
createdAt
milestones {
nodes {
title
}
}
}
errors
FIELDS
end
let(:update_release) { post_graphql_mutation(mutation, current_user: current_user) }
let(:mutation_response) { graphql_mutation_response(mutation_name)&.with_indifferent_access }
let(:expected_attributes) do
{
tagName: tag_name,
name: name,
description: description,
releasedAt: Time.parse(released_at).utc.iso8601,
createdAt: Time.parse(created_at).utc.iso8601,
milestones: {
nodes: milestones.map { |m| { title: m.title } }
}
}.with_indifferent_access
end
around do |example|
freeze_time { example.run }
end
before do
project.add_guest(guest)
project.add_reporter(reporter)
project.add_developer(developer)
stub_default_url_options(host: 'www.example.com')
end
shared_examples 'no errors' do
it 'returns no errors' do
update_release
expect(graphql_errors).not_to be_present
end
end
shared_examples 'top-level error with message' do |error_message|
it 'returns a top-level error with message' do
update_release
expect(mutation_response).to be_nil
expect(graphql_errors.count).to eq(1)
expect(graphql_errors.first['message']).to eq(error_message)
end
end
shared_examples 'errors-as-data with message' do |error_message|
it 'returns an error-as-data with message' do
update_release
expect(mutation_response[:release]).to be_nil
expect(mutation_response[:errors].count).to eq(1)
expect(mutation_response[:errors].first).to match(error_message)
end
end
shared_examples 'updates release fields' do |updates|
it_behaves_like 'no errors'
it 'updates the correct field and returns the release' do
update_release
expect(mutation_response[:release]).to include(expected_attributes.merge(updates).except(:milestones))
# Right now the milestones are returned in a non-deterministic order.
# Because of this, we need to test milestones separately to allow
# for them to be returned in any order.
# Once https://gitlab.com/gitlab-org/gitlab/-/issues/259012 has been
# fixed, this special milestone handling can be removed.
expected_milestones = expected_attributes.merge(updates)[:milestones]
expect(mutation_response[:release][:milestones][:nodes]).to match_array(expected_milestones[:nodes])
end
end
context 'when the current user has access to update releases' do
let(:current_user) { developer }
context 'name' do
context 'when a new name is provided' do
let(:mutation_arguments) { super().merge(name: 'Updated name') }
it_behaves_like 'updates release fields', name: 'Updated name'
end
context 'when null is provided' do
let(:mutation_arguments) { super().merge(name: nil) }
it_behaves_like 'updates release fields', name: 'v1.1.0'
end
end
context 'description' do
context 'when a new description is provided' do
let(:mutation_arguments) { super().merge(description: 'Updated description') }
it_behaves_like 'updates release fields', description: 'Updated description'
end
context 'when null is provided' do
let(:mutation_arguments) { super().merge(description: nil) }
it_behaves_like 'updates release fields', description: nil
end
end
context 'releasedAt' do
context 'when no time zone is provided' do
let(:mutation_arguments) { super().merge(releasedAt: '2015-05-05') }
it_behaves_like 'updates release fields', releasedAt: Time.parse('2015-05-05').utc.iso8601
end
context 'when a local time zone is provided' do
let(:mutation_arguments) { super().merge(releasedAt: Time.parse('2015-05-05').in_time_zone('Hawaii').iso8601) }
it_behaves_like 'updates release fields', releasedAt: Time.parse('2015-05-05').utc.iso8601
end
context 'when null is provided' do
let(:mutation_arguments) { super().merge(releasedAt: nil) }
it_behaves_like 'top-level error with message', 'if the releasedAt argument is provided, it cannot be null'
end
end
context 'milestones' do
context 'when a new set of milestones is provided provided' do
let(:mutation_arguments) { super().merge(milestones: ['12.3']) }
it_behaves_like 'updates release fields', milestones: { nodes: [{ title: '12.3' }] }
end
context 'when an empty array is provided' do
let(:mutation_arguments) { super().merge(milestones: []) }
it_behaves_like 'updates release fields', milestones: { nodes: [] }
end
context 'when null is provided' do
let(:mutation_arguments) { super().merge(milestones: nil) }
it_behaves_like 'top-level error with message', 'if the milestones argument is provided, it cannot be null'
end
context 'when a non-existent milestone title is provided' do
let(:mutation_arguments) { super().merge(milestones: ['not real']) }
it_behaves_like 'errors-as-data with message', 'Milestone(s) not found: not real'
end
context 'when a milestone title from a different project is provided' do
let(:milestone_in_different_project) { create(:milestone, title: 'milestone in different project') }
let(:mutation_arguments) { super().merge(milestones: [milestone_in_different_project.title]) }
it_behaves_like 'errors-as-data with message', 'Milestone(s) not found: milestone in different project'
end
end
context 'validation' do
context 'when no updated fields are provided' do
it_behaves_like 'errors-as-data with message', 'params is empty'
end
context 'when the tag does not exist' do
let(:mutation_arguments) { super().merge(tagName: 'not-a-real-tag') }
it_behaves_like 'errors-as-data with message', 'Tag does not exist'
end
context 'when the project does not exist' do
let(:mutation_arguments) { super().merge(projectPath: 'not/a/real/path') }
it_behaves_like 'top-level error with message', "The resource that you are attempting to access does not exist or you don't have permission to perform this action"
end
end
end
context "when the current user doesn't have access to update releases" do
expected_error_message = "The resource that you are attempting to access does not exist or you don't have permission to perform this action"
context 'when the current user is a Reporter' do
let(:current_user) { reporter }
it_behaves_like 'top-level error with message', expected_error_message
end
context 'when the current user is a Guest' do
let(:current_user) { guest }
it_behaves_like 'top-level error with message', expected_error_message
end
context 'when the current user is a public user' do
let(:current_user) { public_user }
it_behaves_like 'top-level error with message', expected_error_message
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment