debian-mirror-gitlab/spec/requests/api/graphql/environments/deployments_spec.rb
2023-03-05 14:24:40 +05:30

524 lines
16 KiB
Ruby

# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Environments Deployments query', feature_category: :continuous_delivery do
include GraphqlHelpers
let_it_be(:project) { create(:project, :private, :repository) }
let_it_be(:environment) { create(:environment, project: project) }
let_it_be(:developer) { create(:user).tap { |u| project.add_developer(u) } }
let_it_be(:guest) { create(:user).tap { |u| project.add_guest(u) } }
let(:user) { developer }
subject { GitlabSchema.execute(query, context: { current_user: user }).as_json }
context 'when there are deployments in the environment' do
let_it_be(:finished_deployment_old) do
create(:deployment, :success, environment: environment, project: project, finished_at: 2.days.ago)
end
let_it_be(:finished_deployment_new) do
create(:deployment, :success, environment: environment, project: project, finished_at: 1.day.ago)
end
let_it_be(:upcoming_deployment_old) do
create(:deployment, :created, environment: environment, project: project, created_at: 2.hours.ago)
end
let_it_be(:upcoming_deployment_new) do
create(:deployment, :created, environment: environment, project: project, created_at: 1.hour.ago)
end
let_it_be(:other_environment) { create(:environment, project: project) }
let_it_be(:other_deployment) { create(:deployment, :success, environment: other_environment, project: project) }
let(:query) do
%(
query {
project(fullPath: "#{project.full_path}") {
environment(name: "#{environment.name}") {
deployments {
nodes {
id
iid
ref
tag
sha
createdAt
updatedAt
finishedAt
status
}
}
}
}
}
)
end
it 'returns all deployments of the environment' do
deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
expect(deployments.count).to eq(4)
end
context 'when query last deployment' do
let(:query) do
%(
query {
project(fullPath: "#{project.full_path}") {
environment(name: "#{environment.name}") {
deployments(statuses: [SUCCESS], orderBy: { finishedAt: DESC }, first: 1) {
nodes {
iid
}
}
}
}
}
)
end
it 'returns deployment' do
deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
expect(deployments.count).to eq(1)
expect(deployments[0]['iid']).to eq(finished_deployment_new.iid.to_s)
end
end
context 'when query latest upcoming deployment' do
let(:query) do
%(
query {
project(fullPath: "#{project.full_path}") {
environment(name: "#{environment.name}") {
deployments(statuses: [CREATED RUNNING BLOCKED], orderBy: { createdAt: DESC }, first: 1) {
nodes {
iid
}
}
}
}
}
)
end
it 'returns deployment' do
deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
expect(deployments.count).to eq(1)
expect(deployments[0]['iid']).to eq(upcoming_deployment_new.iid.to_s)
end
end
context 'when query finished deployments in descending order' do
let(:query) do
%(
query {
project(fullPath: "#{project.full_path}") {
environment(name: "#{environment.name}") {
deployments(statuses: [SUCCESS FAILED CANCELED], orderBy: { finishedAt: DESC }) {
nodes {
iid
}
}
}
}
}
)
end
it 'returns deployments' do
deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
expect(deployments.count).to eq(2)
expect(deployments[0]['iid']).to eq(finished_deployment_new.iid.to_s)
expect(deployments[1]['iid']).to eq(finished_deployment_old.iid.to_s)
end
end
context 'when query finished deployments in ascending order' do
let(:query) do
%(
query {
project(fullPath: "#{project.full_path}") {
environment(name: "#{environment.name}") {
deployments(statuses: [SUCCESS FAILED CANCELED], orderBy: { finishedAt: ASC }) {
nodes {
iid
}
}
}
}
}
)
end
it 'returns deployments' do
deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
expect(deployments.count).to eq(2)
expect(deployments[0]['iid']).to eq(finished_deployment_old.iid.to_s)
expect(deployments[1]['iid']).to eq(finished_deployment_new.iid.to_s)
end
end
context 'when query upcoming deployments in descending order' do
let(:query) do
%(
query {
project(fullPath: "#{project.full_path}") {
environment(name: "#{environment.name}") {
deployments(statuses: [CREATED RUNNING BLOCKED], orderBy: { createdAt: DESC }) {
nodes {
iid
}
}
}
}
}
)
end
it 'returns deployments' do
deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
expect(deployments.count).to eq(2)
expect(deployments[0]['iid']).to eq(upcoming_deployment_new.iid.to_s)
expect(deployments[1]['iid']).to eq(upcoming_deployment_old.iid.to_s)
end
end
context 'when query upcoming deployments in ascending order' do
let(:query) do
%(
query {
project(fullPath: "#{project.full_path}") {
environment(name: "#{environment.name}") {
deployments(statuses: [CREATED RUNNING BLOCKED], orderBy: { createdAt: ASC }) {
nodes {
iid
}
}
}
}
}
)
end
it 'returns deployments' do
deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
expect(deployments.count).to eq(2)
expect(deployments[0]['iid']).to eq(upcoming_deployment_old.iid.to_s)
expect(deployments[1]['iid']).to eq(upcoming_deployment_new.iid.to_s)
end
end
context 'when query last deployments of multiple environments' do
let(:query) do
%(
query {
project(fullPath: "#{project.full_path}") {
environments {
nodes {
name
deployments(statuses: [SUCCESS], orderBy: { finishedAt: DESC }, first: 1) {
nodes {
iid
}
}
}
}
}
}
)
end
it 'returns an error for preventing N+1 queries' do
expect(subject['errors'][0]['message'])
.to include('"deployments" field can be requested only for 1 Environment(s) at a time.')
end
end
context 'when query finished and upcoming deployments together' do
let(:query) do
%(
query {
project(fullPath: "#{project.full_path}") {
environment(name: "#{environment.name}") {
deployments(statuses: [CREATED SUCCESS]) {
nodes {
iid
}
}
}
}
}
)
end
it 'raises an error' do
expect { subject }.to raise_error(DeploymentsFinder::InefficientQueryError)
end
end
context 'when multiple orderBy input are specified' do
let(:query) do
%(
query {
project(fullPath: "#{project.full_path}") {
environment(name: "#{environment.name}") {
deployments(orderBy: { finishedAt: DESC, createdAt: ASC }) {
nodes {
iid
}
}
}
}
}
)
end
it 'raises an error' do
expect(subject['errors'][0]['message']).to include('orderBy parameter must contain one key-value pair.')
end
end
context 'when user is guest' do
let(:user) { guest }
it 'returns nothing' do
expect(subject['data']['project']['environment']).to be_nil
end
end
shared_examples_for 'avoids N+1 database queries' do
it 'does not increase the query count' do
create_deployments
baseline = ActiveRecord::QueryRecorder.new do
run_with_clean_state(query, context: { current_user: user })
end
create_deployments
multi = ActiveRecord::QueryRecorder.new do
run_with_clean_state(query, context: { current_user: user })
end
expect(multi).not_to exceed_query_limit(baseline)
end
def create_deployments
create_list(:deployment, 3, environment: environment, project: project).each do |deployment|
deployment.user = create(:user).tap { |u| project.add_developer(u) }
deployment.deployable =
create(:ci_build, project: project, environment: environment.name, deployment: deployment,
user: deployment.user)
deployment.save!
end
end
end
context 'when requesting commits of deployments' do
let(:query) do
%(
query {
project(fullPath: "#{project.full_path}") {
environment(name: "#{environment.name}") {
deployments {
nodes {
iid
commit {
author {
avatarUrl
name
webPath
}
fullTitle
webPath
sha
}
}
}
}
}
}
)
end
it_behaves_like 'avoids N+1 database queries'
it 'returns commits of deployments' do
deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
deployments.each do |deployment|
deployment_in_record = project.deployments.find_by_iid(deployment['iid'])
expect(deployment_in_record.sha).to eq(deployment['commit']['sha'])
end
end
end
context 'when requesting triggerers of deployments' do
let(:query) do
%(
query {
project(fullPath: "#{project.full_path}") {
environment(name: "#{environment.name}") {
deployments {
nodes {
iid
triggerer {
id
avatarUrl
name
webPath
}
}
}
}
}
}
)
end
it_behaves_like 'avoids N+1 database queries'
it 'returns triggerers of deployments' do
deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
deployments.each do |deployment|
deployment_in_record = project.deployments.find_by_iid(deployment['iid'])
expect(deployment_in_record.deployed_by.name).to eq(deployment['triggerer']['name'])
end
end
end
context 'when requesting jobs of deployments' do
let(:query) do
%(
query {
project(fullPath: "#{project.full_path}") {
environment(name: "#{environment.name}") {
deployments {
nodes {
iid
job {
id
status
name
webPath
}
}
}
}
}
}
)
end
it_behaves_like 'avoids N+1 database queries'
it 'returns jobs of deployments' do
deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
deployments.each do |deployment|
deployment_in_record = project.deployments.find_by_iid(deployment['iid'])
expect(deployment_in_record.build.to_global_id.to_s).to eq(deployment['job']['id'])
end
end
end
context 'when requesting user permissions' do
let(:query) do
%(
query {
project(fullPath: "#{project.full_path}") {
environment(name: "#{environment.name}") {
deployments {
nodes {
iid
userPermissions {
updateDeployment
destroyDeployment
}
}
}
}
}
}
)
end
it_behaves_like 'avoids N+1 database queries'
it 'returns user permissions of the deployments', :aggregate_failures do
deployments = subject.dig('data', 'project', 'environment', 'deployments', 'nodes')
deployments.each do |deployment|
deployment_in_record = project.deployments.find_by_iid(deployment['iid'])
expect(deployment['userPermissions']['updateDeployment'])
.to eq(Ability.allowed?(user, :update_deployment, deployment_in_record))
expect(deployment['userPermissions']['destroyDeployment'])
.to eq(Ability.allowed?(user, :destroy_deployment, deployment_in_record))
end
end
end
describe 'sorting and pagination' do
let(:data_path) { [:project, :environment, :deployments] }
let(:current_user) { user }
def pagination_query(params)
%(
query {
project(fullPath: "#{project.full_path}") {
environment(name: "#{environment.name}") {
deployments(statuses: [SUCCESS], #{params}) {
nodes {
iid
}
pageInfo {
startCursor
endCursor
hasNextPage
hasPreviousPage
}
}
}
}
}
)
end
def pagination_results_data(nodes)
nodes.map { |deployment| deployment['iid'].to_i }
end
context 'when sorting by finished_at in ascending order' do
it_behaves_like 'sorted paginated query' do
let(:sort_argument) { graphql_args(orderBy: { finishedAt: :ASC }) }
let(:first_param) { 2 }
let(:all_records) { [finished_deployment_old.iid, finished_deployment_new.iid] }
end
end
context 'when sorting by finished_at in descending order' do
it_behaves_like 'sorted paginated query' do
let(:sort_argument) { graphql_args(orderBy: { finishedAt: :DESC }) }
let(:first_param) { 2 }
let(:all_records) { [finished_deployment_new.iid, finished_deployment_old.iid] }
end
end
end
end
end