summaryrefslogtreecommitdiff
path: root/spec/requests/api/graphql/environments/deployments_query_spec.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/requests/api/graphql/environments/deployments_query_spec.rb')
-rw-r--r--spec/requests/api/graphql/environments/deployments_query_spec.rb487
1 files changed, 487 insertions, 0 deletions
diff --git a/spec/requests/api/graphql/environments/deployments_query_spec.rb b/spec/requests/api/graphql/environments/deployments_query_spec.rb
new file mode 100644
index 00000000000..6da00057449
--- /dev/null
+++ b/spec/requests/api/graphql/environments/deployments_query_spec.rb
@@ -0,0 +1,487 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Environments Deployments query' 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
+
+ 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