Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-06-03 12:09:05 +00:00
parent 6be8ed5a95
commit c74f702c74
32 changed files with 352 additions and 76 deletions

View File

@ -1 +1 @@
7c2fcde23bd4a962409897adbbb71da11c6db99a
a31bd1be25d0ff03efaa7f756321ea9440122b24

View File

@ -13,6 +13,7 @@ import { __ } from '~/locale';
import Tracking from '~/tracking';
import {
NOT_ENOUGH_DATA_ERROR,
FIELD_KEY_TITLE,
PAGINATION_SORT_FIELD_END_EVENT,
PAGINATION_SORT_FIELD_DURATION,
PAGINATION_SORT_DIRECTION_ASC,
@ -22,7 +23,8 @@ import TotalTime from './total_time.vue';
const DEFAULT_WORKFLOW_TITLE_PROPERTIES = {
thClass: 'gl-w-half',
key: PAGINATION_SORT_FIELD_END_EVENT,
key: FIELD_KEY_TITLE,
sortable: false,
};
const WORKFLOW_COLUMN_TITLES = {
@ -132,14 +134,16 @@ export default {
return [
this.workflowTitle,
{
key: PAGINATION_SORT_FIELD_DURATION,
label: __('Time'),
thClass: 'gl-w-half',
key: PAGINATION_SORT_FIELD_END_EVENT,
label: __('Last event'),
sortable: this.sortable,
},
].map((field) => ({
...field,
sortable: this.sortable,
}));
{
key: PAGINATION_SORT_FIELD_DURATION,
label: __('Duration'),
sortable: this.sortable,
},
];
},
prevPage() {
return Math.max(this.pagination.page - 1, 0);
@ -201,7 +205,7 @@ export default {
:empty-text="emptyStateMessage"
@sort-changed="onSort"
>
<template v-if="stageCount" #head(end_event)="data">
<template v-if="stageCount" #head(title)="data">
<span>{{ data.label }}</span
><gl-badge class="gl-ml-2" size="sm"
><formatted-stage-count :stage-count="stageCount"
@ -210,7 +214,10 @@ export default {
<template #head(duration)="data">
<span data-testid="vsa-stage-header-duration">{{ data.label }}</span>
</template>
<template #cell(end_event)="{ item }">
<template #head(end_event)="data">
<span data-testid="vsa-stage-header-last-event">{{ data.label }}</span>
</template>
<template #cell(title)="{ item }">
<div data-testid="vsa-stage-event">
<div v-if="item.id" data-testid="vsa-stage-content">
<p class="gl-m-0">
@ -282,6 +289,9 @@ export default {
<template #cell(duration)="{ item }">
<total-time :time="item.totalTime" data-testid="vsa-stage-event-time" />
</template>
<template #cell(end_event)="{ item }">
<span data-testid="vsa-stage-last-event">{{ item.endEventTimestamp }}</span>
</template>
</gl-table>
<gl-pagination
v-if="pagination && !isLoading && !isEmptyStage"

View File

@ -22,6 +22,7 @@ export const PAGINATION_SORT_FIELD_END_EVENT = 'end_event';
export const PAGINATION_SORT_FIELD_DURATION = 'duration';
export const PAGINATION_SORT_DIRECTION_DESC = 'desc';
export const PAGINATION_SORT_DIRECTION_ASC = 'asc';
export const FIELD_KEY_TITLE = 'title';
export const I18N_VSA_ERROR_STAGES = __(
'There was an error fetching value stream analytics stages.',

View File

@ -64,9 +64,10 @@ export default {
v-if="isActive"
name="arrow-right"
class="icon-arrow-right gl-absolute gl-display-block"
:size="14"
/>
<ci-icon :status="job.status" />
<ci-icon :status="job.status" class="gl-mr-2" :size="14" />
<span class="gl-text-truncate gl-w-full">{{ jobName }}</span>

View File

@ -170,12 +170,6 @@
width: 289px;
overflow: auto;
svg {
margin-right: 3px;
height: 14px;
width: 14px;
}
a {
padding: $gl-padding 10px $gl-padding 40px;
width: 270px;

View File

@ -78,6 +78,7 @@ module ContainerRegistry
return unless project
return unless Feature.enabled?(:container_registry_project_statistics, project)
Rails.cache.delete(project.root_ancestor.container_repositories_size_cache_key)
ProjectCacheWorker.perform_async(project.id, [], [:container_registry_size])
end
end

View File

@ -863,6 +863,12 @@ class Group < Namespace
end
end
def gitlab_deploy_token
strong_memoize(:gitlab_deploy_token) do
deploy_tokens.gitlab_deploy_token
end
end
private
def feature_flag_enabled_for_self_or_ancestor?(feature_flag)

View File

@ -427,14 +427,21 @@ class Namespace < ApplicationRecord
aggregation_schedule.present?
end
def container_repositories_size_cache_key
"namespaces:#{id}:container_repositories_size"
end
def container_repositories_size
strong_memoize(:container_repositories_size) do
next unless Gitlab.com?
next unless root?
next unless ContainerRegistry::GitlabApiClient.supports_gitlab_api?
next 0 if all_container_repositories.empty?
next unless all_container_repositories.all_migrated?
ContainerRegistry::GitlabApiClient.deduplicated_size(full_path)
Rails.cache.fetch(container_repositories_size_cache_key, expires_in: 7.days) do
ContainerRegistry::GitlabApiClient.deduplicated_size(full_path)
end
end
end

View File

@ -81,7 +81,9 @@ class PoolRepository < ApplicationRecord
object_pool.link(repository.raw)
end
def mark_obsolete_if_last(repository)
def unlink_repository(repository)
repository.disconnect_alternates
if member_projects.where.not(id: repository.project.id).exists?
true
else

View File

@ -2509,7 +2509,13 @@ class Project < ApplicationRecord
end
def gitlab_deploy_token
@gitlab_deploy_token ||= deploy_tokens.gitlab_deploy_token
strong_memoize(:gitlab_deploy_token) do
if Feature.enabled?(:ci_variable_for_group_gitlab_deploy_token, self)
deploy_tokens.gitlab_deploy_token || group&.gitlab_deploy_token
else
deploy_tokens.gitlab_deploy_token
end
end
end
def any_lfs_file_locks?
@ -2566,7 +2572,7 @@ class Project < ApplicationRecord
end
def leave_pool_repository
pool_repository&.mark_obsolete_if_last(repository) && update_column(:pool_repository_id, nil)
pool_repository&.unlink_repository(repository) && update_column(:pool_repository_id, nil)
end
def link_pool_repository

View File

@ -0,0 +1,8 @@
---
name: ci_variable_for_group_gitlab_deploy_token
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/88696
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/363621
milestone: '15.1'
type: development
group: group::pipeline authoring
default_enabled: false

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
class AddLicenseUsageDataExportedToApplicationSettings < Gitlab::Database::Migration[2.0]
enable_lock_retries!
def change
add_column :application_settings, :license_usage_data_exported, :boolean, default: false, null: false
end
end

View File

@ -0,0 +1 @@
55c13dd2cf8db2ca54d3fb1bd09d459e90a90e01b3c1f7ad950e4b618df241af

View File

@ -11319,6 +11319,7 @@ CREATE TABLE application_settings (
jira_connect_application_key text,
globally_allowed_ips text DEFAULT ''::text NOT NULL,
container_registry_pre_import_tags_rate numeric(6,2) DEFAULT 0.5 NOT NULL,
license_usage_data_exported boolean DEFAULT false NOT NULL,
CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)),
CONSTRAINT app_settings_container_registry_pre_import_tags_rate_positive CHECK ((container_registry_pre_import_tags_rate >= (0)::numeric)),
CONSTRAINT app_settings_dep_proxy_ttl_policies_worker_capacity_positive CHECK ((dependency_proxy_ttl_group_policy_worker_capacity >= 0)),

View File

@ -898,6 +898,44 @@ def down
end
```
## Dropping a sequence
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/88387) in GitLab 15.1.
Dropping a sequence is uncommon, but you can use the `drop_sequence` method provided by the database team.
Under the hood, it works like this:
Remove a sequence:
- Remove the default value if the sequence is actually used.
- Execute `DROP SEQUENCE`.
Re-add a sequence:
- Create the sequence, with the possibility of specifying the current value.
- Change the default value of the column.
A Rails migration example:
```ruby
class DropSequenceTest < Gitlab::Database::Migration[2.0]
def up
drop_sequence(:ci_pipelines_config, :pipeline_id, :ci_pipelines_config_pipeline_id_seq)
end
def down
default_value = Ci::Pipeline.maximum(:id) + 10_000
add_sequence(:ci_pipelines_config, :pipeline_id, :ci_pipelines_config_pipeline_id_seq, default_value)
end
end
```
NOTE:
`add_sequence` should be avoided for columns with foreign keys.
Adding sequence to these columns is **only allowed** in the down method (restore previous schema state).
## Integer column type
By default, an integer column can hold up to a 4-byte (32-bit) number. That is

View File

@ -43,8 +43,7 @@ To view value stream analytics for your project:
- In the **From** field, select a start date.
- In the **To** field, select an end date.
1. Optional. Sort results by ascending or descending:
- To sort by most recent or oldest workflow item, select the **Merge requests** or **Issues**
header. The header name differs based on the stage you select.
- To sort by most recent or oldest workflow item, select the **Last event** header.
- To sort by most or least amount of time spent in each stage, select the **Time** header.
The table shows a list of related workflow items for the selected stage. Based on the stage you choose, this can be:

View File

@ -50,8 +50,7 @@ To view value stream analytics for your group:
- In the **To** field, select an end date. The charts and list show workflow items created
during the date range.
1. Optional. Sort results by ascending or descending:
- To sort by most recent or oldest workflow item, select the **Merge requests** or **Issues**
header. The header name differs based on the stage you select.
- To sort by most recent or oldest workflow item, select the **Last event** header.
- To sort by most or least amount of time spent in each stage, select the **Time** header.
A badge next to the workflow items table header shows the number of workflow items that

View File

@ -190,6 +190,8 @@ To pull images from the Dependency Proxy, you must:
### GitLab deploy token
> Support for `gitlab-deploy-token` at the group level [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214014) in GitLab 15.1 [with a flag](../../../administration/feature_flags.md) named `ci_variable_for_group_gitlab_deploy_token`. Disabled by default.
There's a special case when it comes to deploy tokens. If a user creates one
named `gitlab-deploy-token`, the username and token of the deploy token is
automatically exposed to the CI/CD jobs as CI/CD variables: `CI_DEPLOY_USER`
@ -203,9 +205,10 @@ docker login -u $CI_DEPLOY_USER -p $CI_DEPLOY_PASSWORD $CI_REGISTRY
```
NOTE:
The special handling for the `gitlab-deploy-token` deploy token is not
implemented for group deploy tokens. To make the group-level deploy token available for
CI/CD jobs, the `CI_DEPLOY_USER` and `CI_DEPLOY_PASSWORD` variables should be set under **Settings** to the name and token of the group deploy token respectively.
In GitLab 15.0 and earlier, the special handling for the `gitlab-deploy-token` deploy token
does not work for group deploy tokens. To make the group-level deploy token available
for CI/CD jobs, the `CI_DEPLOY_USER` and `CI_DEPLOY_PASSWORD` CI/CD variables must be
set in **Settings > CI/CD > Variables** to the name and token of the group deploy token.
## Troubleshooting

View File

@ -394,3 +394,27 @@ run tests:
coverage_format: cobertura
path: coverage/coverage.xml
```
## Troubleshooting
### Test coverage visualization not displayed
If the test coverage visualization is not displayed in the diff view, you can check
the coverage report itself and verify that:
- The file you are viewing in the diff view is mentioned in the coverage report.
- The `source` and `filename` nodes in the report follows the [expected structure](#automatic-class-path-correction)
to match the files in your repository.
Report artifacts are not downloadable by default. If you want the report to be downloadable
from the job details page, add your coverage report to the artifact `paths`:
```yaml
artifacts:
paths:
- coverage/cobertura-coverage.xml
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
```

View File

@ -1499,6 +1499,20 @@ into similar problems in the future (e.g. when new tables are created).
SQL
end
def drop_sequence(table_name, column_name, sequence_name)
execute <<~SQL
ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} DROP DEFAULT;
DROP SEQUENCE IF EXISTS #{quote_table_name(sequence_name)}
SQL
end
def add_sequence(table_name, column_name, sequence_name, start_value)
execute <<~SQL
CREATE SEQUENCE #{quote_table_name(sequence_name)} START #{start_value};
ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT nextval(#{quote(sequence_name)})
SQL
end
private
def create_temporary_columns_and_triggers(table, columns, primary_key: :id, data_type: :bigint)

View File

@ -22362,6 +22362,9 @@ msgstr ""
msgid "Last edited by %{link_start}%{avatar} %{name}%{link_end}"
msgstr ""
msgid "Last event"
msgstr ""
msgid "Last item before this page loaded in your browser:"
msgstr ""
@ -33858,9 +33861,6 @@ msgstr ""
msgid "SecurityOrchestration|Enforce security for this project. %{linkStart}More information.%{linkEnd}"
msgstr ""
msgid "SecurityOrchestration|Group level policy"
msgstr ""
msgid "SecurityOrchestration|If any scanner finds a newly detected critical vulnerability in an open merge request targeting the master branch, then require two approvals from any member of App security."
msgstr ""

View File

@ -27,6 +27,7 @@ const findTableHeadColumns = () => findTableHead().findAll('th');
const findStageEventTitle = (ev) => extendedWrapper(ev).findByTestId('vsa-stage-event-title');
const findStageEventLink = (ev) => extendedWrapper(ev).findByTestId('vsa-stage-event-link');
const findStageTime = () => wrapper.findByTestId('vsa-stage-event-time');
const findStageLastEvent = () => wrapper.findByTestId('vsa-stage-last-event');
const findIcon = (name) => wrapper.findByTestId(`${name}-icon`);
function createComponent(props = {}, shallow = false) {
@ -128,6 +129,10 @@ describe('StageTable', () => {
expect(findStageTime().text()).toBe(createdAt);
});
it('will render the end event', () => {
expect(findStageLastEvent().text()).toBe(firstIssueEvent.endEventTimestamp);
});
it('will render the author', () => {
expect(wrapper.findByTestId('vsa-stage-event-author').text()).toContain(
firstIssueEvent.author.name,
@ -303,10 +308,20 @@ describe('StageTable', () => {
wrapper.destroy();
});
it('can sort the table by each column', () => {
findTableHeadColumns().wrappers.forEach((w) => {
expect(w.attributes('aria-sort')).toBe('none');
});
it('can sort the end event or duration', () => {
findTableHeadColumns()
.wrappers.slice(1)
.forEach((w) => {
expect(w.attributes('aria-sort')).toBe('none');
});
});
it('cannot be sorted by title', () => {
findTableHeadColumns()
.wrappers.slice(0, 1)
.forEach((w) => {
expect(w.attributes('aria-sort')).toBeUndefined();
});
});
it('clicking a table column will send tracking information', () => {

View File

@ -3281,4 +3281,20 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
model.rename_constraint(:test_table, :fk_old_name, :fk_new_name)
end
end
describe '#drop_sequence' do
it "executes the statement to drop the sequence" do
expect(model).to receive(:execute).with /ALTER TABLE "test_table" ALTER COLUMN "test_column" DROP DEFAULT;\nDROP SEQUENCE IF EXISTS "test_table_id_seq"/
model.drop_sequence(:test_table, :test_column, :test_table_id_seq)
end
end
describe '#add_sequence' do
it "executes the statement to add the sequence" do
expect(model).to receive(:execute).with "CREATE SEQUENCE \"test_table_id_seq\" START 1;\nALTER TABLE \"test_table\" ALTER COLUMN \"test_column\" SET DEFAULT nextval(\'test_table_id_seq\')\n"
model.add_sequence(:test_table, :test_column, :test_table_id_seq, 1)
end
end
end

View File

@ -3538,7 +3538,7 @@ RSpec.describe Ci::Build do
]
end
context 'when gitlab-deploy-token exists' do
context 'when gitlab-deploy-token exists for project' do
before do
project.deploy_tokens << deploy_token
end
@ -3548,11 +3548,32 @@ RSpec.describe Ci::Build do
end
end
context 'when gitlab-deploy-token does not exist' do
context 'when gitlab-deploy-token does not exist for project' do
it 'does not include deploy token variables' do
expect(subject.find { |v| v[:key] == 'CI_DEPLOY_USER'}).to be_nil
expect(subject.find { |v| v[:key] == 'CI_DEPLOY_PASSWORD'}).to be_nil
end
context 'when gitlab-deploy-token exists for group' do
before do
group.deploy_tokens << deploy_token
end
it 'includes deploy token variables' do
is_expected.to include(*deploy_token_variables)
end
context 'when the FF ci_variable_for_group_gitlab_deploy_token is disabled' do
before do
stub_feature_flags(ci_variable_for_group_gitlab_deploy_token: false)
end
it 'does not include deploy token variables' do
expect(subject.find { |v| v[:key] == 'CI_DEPLOY_USER'}).to be_nil
expect(subject.find { |v| v[:key] == 'CI_DEPLOY_PASSWORD'}).to be_nil
end
end
end
end
end

View File

@ -46,6 +46,12 @@ RSpec.describe ContainerRegistry::Event do
handle!
end
it 'clears the cache for the namespace container repositories size' do
expect(Rails.cache).to receive(:delete).with(group.container_repositories_size_cache_key)
handle!
end
shared_examples 'event without project statistics update' do
it 'does not queue a project statistics update' do
expect(ProjectCacheWorker).not_to receive(:perform_async)

View File

@ -3396,4 +3396,42 @@ RSpec.describe Group do
end
end
end
describe '#gitlab_deploy_token' do
subject(:gitlab_deploy_token) { group.gitlab_deploy_token }
context 'when there is a gitlab deploy token associated' do
let!(:deploy_token) { create(:deploy_token, :group, :gitlab_deploy_token, groups: [group]) }
it { is_expected.to eq(deploy_token) }
end
context 'when there is no a gitlab deploy token associated' do
it { is_expected.to be_nil }
end
context 'when there is a gitlab deploy token associated but is has been revoked' do
let!(:deploy_token) { create(:deploy_token, :group, :gitlab_deploy_token, :revoked, groups: [group]) }
it { is_expected.to be_nil }
end
context 'when there is a gitlab deploy token associated but it is expired' do
let!(:deploy_token) { create(:deploy_token, :group, :gitlab_deploy_token, :expired, groups: [group]) }
it { is_expected.to be_nil }
end
context 'when there is a deploy token associated with a different name' do
let!(:deploy_token) { create(:deploy_token, :group, groups: [group]) }
it { is_expected.to be_nil }
end
context 'when there is a gitlab deploy token associated to a different group' do
let!(:deploy_token) { create(:deploy_token, :group, :gitlab_deploy_token, groups: [create(:group)]) }
it { is_expected.to be_nil }
end
end
end

View File

@ -583,7 +583,13 @@ RSpec.describe Namespace do
end
end
describe '#container_repositories_size' do
describe '#container_repositories_size_cache_key' do
it 'returns the correct cache key' do
expect(namespace.container_repositories_size_cache_key).to eq "namespaces:#{namespace.id}:container_repositories_size"
end
end
describe '#container_repositories_size', :clean_gitlab_redis_cache do
let(:project_namespace) { create(:namespace) }
subject { project_namespace.container_repositories_size }
@ -611,12 +617,29 @@ RSpec.describe Namespace do
end
it { is_expected.to eq(expected_result) }
it 'caches the result when all migrated' do
if all_migrated
expect(Rails.cache)
.to receive(:fetch)
.with(project_namespace.container_repositories_size_cache_key, expires_in: 7.days)
subject
end
end
end
end
context 'not on gitlab.com' do
it { is_expected.to eq(nil) }
end
context 'for a sub-group' do
let(:parent_namespace) { create(:group) }
let(:project_namespace) { create(:group, parent: parent_namespace) }
it { is_expected.to eq(nil) }
end
end
describe '#all_container_repositories' do

View File

@ -24,23 +24,35 @@ RSpec.describe PoolRepository do
end
end
describe '#mark_obsolete_if_last' do
describe '#unlink_repository' do
let(:pool) { create(:pool_repository, :ready) }
let(:repository_path) { File.join(TestEnv.repos_path, pool.source_project.repository.relative_path) }
let(:alternates_file) { File.join(repository_path, 'objects', 'info', 'alternates') }
before do
pool.link_repository(pool.source_project.repository)
end
context 'when the last member leaves' do
it 'schedules pool removal' do
expect(::ObjectPool::DestroyWorker).to receive(:perform_async).with(pool.id).and_call_original
pool.mark_obsolete_if_last(pool.source_project.repository)
pool.unlink_repository(pool.source_project.repository)
expect(File).not_to exist(alternates_file)
end
end
context 'when the second member leaves' do
it 'does not schedule pool removal' do
create(:project, :repository, pool_repository: pool)
other_project = create(:project, :repository, pool_repository: pool)
pool.link_repository(other_project.repository)
expect(::ObjectPool::DestroyWorker).not_to receive(:perform_async).with(pool.id)
pool.mark_obsolete_if_last(pool.source_project.repository)
pool.unlink_repository(pool.source_project.repository)
expect(File).not_to exist(alternates_file)
end
end
end

View File

@ -6220,7 +6220,7 @@ RSpec.describe Project, factory_default: :keep do
describe '#gitlab_deploy_token' do
let(:project) { create(:project) }
subject { project.gitlab_deploy_token }
subject(:gitlab_deploy_token) { project.gitlab_deploy_token }
context 'when there is a gitlab deploy token associated' do
let!(:deploy_token) { create(:deploy_token, :gitlab_deploy_token, projects: [project]) }
@ -6252,10 +6252,43 @@ RSpec.describe Project, factory_default: :keep do
context 'when there is a deploy token associated to a different project' do
let(:project_2) { create(:project) }
let!(:deploy_token) { create(:deploy_token, projects: [project_2]) }
let!(:deploy_token) { create(:deploy_token, :gitlab_deploy_token, projects: [project_2]) }
it { is_expected.to be_nil }
end
context 'when the project group has a gitlab deploy token associated' do
let(:group) { create(:group) }
let(:project) { create(:project, group: group) }
let!(:deploy_token) { create(:deploy_token, :gitlab_deploy_token, :group, groups: [group]) }
it { is_expected.to eq(deploy_token) }
context 'when the FF ci_variable_for_group_gitlab_deploy_token is disabled' do
before do
stub_feature_flags(ci_variable_for_group_gitlab_deploy_token: false)
end
it { is_expected.to be_nil }
end
end
context 'when the project and its group has a gitlab deploy token associated' do
let(:group) { create(:group) }
let(:project) { create(:project, group: group) }
let!(:project_deploy_token) { create(:deploy_token, :gitlab_deploy_token, projects: [project]) }
let!(:group_deploy_token) { create(:deploy_token, :gitlab_deploy_token, :group, groups: [group]) }
it { is_expected.to eq(project_deploy_token) }
context 'when the FF ci_variable_for_group_gitlab_deploy_token is disabled' do
before do
stub_feature_flags(ci_variable_for_group_gitlab_deploy_token: false)
end
it { is_expected.to eq(project_deploy_token) }
end
end
end
context 'with uploads' do

View File

@ -14,20 +14,7 @@ RSpec.describe Projects::EnvironmentsController do
sign_in(project.owner)
end
it 'avoids N+1 queries', :use_sql_query_cache do
create_deployment_with_associations(commit_depth: 19)
control = ActiveRecord::QueryRecorder.new(skip_cached: false) do
get project_environment_path(project, environment), params: environment_params
end
18.downto(0).each { |n| create_deployment_with_associations(commit_depth: n) }
# N+1s exist for loading commit emails and users
expect do
get project_environment_path(project, environment), params: environment_params
end.not_to exceed_all_query_limit(control).with_threshold(9)
end
include_examples 'avoids N+1 queries on environment detail page'
end
def environment_params(opts = {})

View File

@ -65,20 +65,3 @@ RSpec.shared_examples 'failed response for #cancel_auto_stop' do
end
end
end
RSpec.shared_examples 'avoids N+1 queries on environment detail page' do
render_views
before do
create_deployment_with_associations(sequence: 0)
end
it 'avoids N+1 queries' do
control = ActiveRecord::QueryRecorder.new { get :show, params: environment_params }
create_deployment_with_associations(sequence: 1)
create_deployment_with_associations(sequence: 2)
expect { get :show, params: environment_params }.not_to exceed_query_limit(control.count).with_threshold(34)
end
end

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
RSpec.shared_examples 'avoids N+1 queries on environment detail page' do
it 'avoids N+1 queries', :use_sql_query_cache do
create_deployment_with_associations(commit_depth: 19)
control = ActiveRecord::QueryRecorder.new(skip_cached: false) do
get project_environment_path(project, environment), params: environment_params
end
18.downto(0).each { |n| create_deployment_with_associations(commit_depth: n) }
# N+1s exist for loading commit emails and users
expect do
get project_environment_path(project, environment), params: environment_params
end.not_to exceed_all_query_limit(control).with_threshold(9)
end
end