Commit 1caa6006 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 7f833087
......@@ -268,11 +268,6 @@ Naming/RescuedExceptionsVariableName:
RSpec/ContextWording:
Enabled: false
# Offense count: 407
# Cop supports --auto-correct.
RSpec/EmptyLineAfterFinalLet:
Enabled: false
# Offense count: 719
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
......
......@@ -110,6 +110,7 @@ export const createTempEntry = (
commit(types.ADD_FILE_TO_CHANGED, file.path);
dispatch('setFileActive', file.path);
dispatch('triggerFilesChange');
dispatch('burstUnusedSeal');
}
if (parentPath && !state.entries[parentPath].opened) {
......@@ -222,7 +223,9 @@ export const deleteEntry = ({ commit, dispatch, state }, path) => {
dispatch('deleteEntry', prevPath);
return;
}
if (state.unusedSeal) dispatch('burstUnusedSeal');
dispatch('burstUnusedSeal');
if (entry.opened) dispatch('closeFile', entry);
if (isTree) {
......@@ -267,6 +270,7 @@ export const renameEntry = ({ dispatch, commit, state }, { path, name, parentPat
commit(types.REMOVE_FILE_FROM_STAGED_AND_CHANGED, newEntry);
} else if (!isInChanges) {
commit(types.ADD_FILE_TO_CHANGED, newPath);
dispatch('burstUnusedSeal');
}
if (!newEntry.tempFile) {
......
---
title: Fix "No changes" empty state showing up in changes tab, despite there being
changes
merge_request: 21713
author:
type: fixed
......@@ -9,7 +9,7 @@ class AddPrivilegedToRunner < ActiveRecord::Migration[4.2]
disable_ddl_transaction!
def up
add_column_with_default :clusters_applications_runners, :privileged, :boolean, default: true, allow_null: false
add_column_with_default :clusters_applications_runners, :privileged, :boolean, default: true, allow_null: false # rubocop:disable Migration/AddColumnWithDefault
end
def down
......
......@@ -5,7 +5,7 @@ class AddPagesAccessLevelToProjectFeature < ActiveRecord::Migration[4.2]
DOWNTIME = false
def up
add_column_with_default(:project_features, :pages_access_level, :integer, default: ProjectFeature::PUBLIC, allow_null: false)
add_column_with_default(:project_features, :pages_access_level, :integer, default: ProjectFeature::PUBLIC, allow_null: false) # rubocop:disable Migration/AddColumnWithDefault
change_column_default(:project_features, :pages_access_level, ProjectFeature::ENABLED)
end
......
......@@ -10,7 +10,7 @@ class AddSquashToMergeRequests < ActiveRecord::Migration[4.2]
def up
unless column_exists?(:merge_requests, :squash)
# rubocop:disable Migration/UpdateLargeTable
add_column_with_default :merge_requests, :squash, :boolean, default: false, allow_null: false
add_column_with_default :merge_requests, :squash, :boolean, default: false, allow_null: false # rubocop:disable Migration/AddColumnWithDefault
end
end
......
......@@ -11,7 +11,7 @@ class EnsureRemoteMirrorColumns < ActiveRecord::Migration[4.2]
add_column :remote_mirrors, :remote_name, :string unless column_exists?(:remote_mirrors, :remote_name) # rubocop:disable Migration/AddLimitToStringColumns
unless column_exists?(:remote_mirrors, :only_protected_branches)
add_column_with_default(:remote_mirrors,
add_column_with_default(:remote_mirrors, # rubocop:disable Migration/AddColumnWithDefault
:only_protected_branches,
:boolean,
default: false,
......
......@@ -10,7 +10,7 @@ class AddDeployStrategyToProjectAutoDevops < ActiveRecord::Migration[4.2]
disable_ddl_transaction!
def up
add_column_with_default :project_auto_devops, :deploy_strategy, :integer, default: 0, allow_null: false
add_column_with_default :project_auto_devops, :deploy_strategy, :integer, default: 0, allow_null: false # rubocop:disable Migration/AddColumnWithDefault
end
def down
......
......@@ -15,7 +15,7 @@ class AddStatusToDeployments < ActiveRecord::Migration[4.2]
# However, we have to use the default value for avoiding `NOT NULL` violation during the transition period.
# The default value should be removed in the future release.
def up
add_column_with_default(:deployments,
add_column_with_default(:deployments, # rubocop:disable Migration/AddColumnWithDefault
:status,
:integer,
limit: 2,
......
......@@ -12,7 +12,7 @@ class AddMaskedToCiVariables < ActiveRecord::Migration[5.0]
disable_ddl_transaction!
def up
add_column_with_default :ci_variables, :masked, :boolean, default: false, allow_null: false
add_column_with_default :ci_variables, :masked, :boolean, default: false, allow_null: false # rubocop:disable Migration/AddColumnWithDefault
end
def down
......
......@@ -12,7 +12,7 @@ class AddMaskedToCiGroupVariables < ActiveRecord::Migration[5.0]
disable_ddl_transaction!
def up
add_column_with_default :ci_group_variables, :masked, :boolean, default: false, allow_null: false
add_column_with_default :ci_group_variables, :masked, :boolean, default: false, allow_null: false # rubocop:disable Migration/AddColumnWithDefault
end
def down
......
......@@ -8,9 +8,11 @@ class AddMultiLineAttributesToSuggestion < ActiveRecord::Migration[5.0]
disable_ddl_transaction!
def up
# rubocop:disable Migration/AddColumnWithDefault
add_column_with_default :suggestions, :lines_above, :integer, default: 0, allow_null: false
add_column_with_default :suggestions, :lines_below, :integer, default: 0, allow_null: false
add_column_with_default :suggestions, :outdated, :boolean, default: false, allow_null: false
# rubocop:enable Migration/AddColumnWithDefault
end
def down
......
......@@ -8,7 +8,7 @@ class AddMergeTrainEnabledToCiCdSettings < ActiveRecord::Migration[5.1]
disable_ddl_transaction!
def up
add_column_with_default :project_ci_cd_settings, :merge_trains_enabled, :boolean, default: false, allow_null: false
add_column_with_default :project_ci_cd_settings, :merge_trains_enabled, :boolean, default: false, allow_null: false # rubocop:disable Migration/AddColumnWithDefault
end
def down
......
......@@ -8,7 +8,7 @@ class AddDeploymentEventsToServices < ActiveRecord::Migration[5.0]
disable_ddl_transaction!
def up
add_column_with_default(:services, :deployment_events, :boolean, default: false, allow_null: false)
add_column_with_default(:services, :deployment_events, :boolean, default: false, allow_null: false) # rubocop:disable Migration/AddColumnWithDefault
end
def down
......
......@@ -8,7 +8,7 @@ class AddRuleTypeToApprovalProjectRules < ActiveRecord::Migration[5.1]
disable_ddl_transaction!
def up
add_column_with_default :approval_project_rules, :rule_type, :integer, limit: 2, default: 0, allow_null: false
add_column_with_default :approval_project_rules, :rule_type, :integer, limit: 2, default: 0, allow_null: false # rubocop:disable Migration/AddColumnWithDefault
end
def down
......
......@@ -11,7 +11,7 @@ class AddShowWhitespaceInDiffsToUserPreferences < ActiveRecord::Migration[5.2]
disable_ddl_transaction!
def up
add_column_with_default :user_preferences, :show_whitespace_in_diffs, :boolean, default: true, allow_null: false
add_column_with_default :user_preferences, :show_whitespace_in_diffs, :boolean, default: true, allow_null: false # rubocop:disable Migration/AddColumnWithDefault
end
def down
......
......@@ -9,7 +9,7 @@ class AddCleanupStatusToCluster < ActiveRecord::Migration[5.2]
disable_ddl_transaction!
def up
add_column_with_default(:clusters, :cleanup_status,
add_column_with_default(:clusters, :cleanup_status, # rubocop:disable Migration/AddColumnWithDefault
:smallint,
default: 1,
allow_null: false)
......
......@@ -8,7 +8,7 @@ class AddEnabledToGrafanaIntegrations < ActiveRecord::Migration[5.2]
disable_ddl_transaction!
def up
add_column_with_default(
add_column_with_default( # rubocop:disable Migration/AddColumnWithDefault
:grafana_integrations,
:enabled,
:boolean,
......
......@@ -8,7 +8,7 @@ class AddArtifactsToCiBuildNeed < ActiveRecord::Migration[5.2]
disable_ddl_transaction!
def up
add_column_with_default(:ci_build_needs, :artifacts,
add_column_with_default(:ci_build_needs, :artifacts, # rubocop:disable Migration/AddColumnWithDefault
:boolean,
default: true,
allow_null: false)
......
......@@ -34,6 +34,9 @@ blocking access to the table being modified. See ["Adding Columns With Default
Values"](migration_style_guide.md#adding-columns-with-default-values) for more
information on how to use this method.
Note that usage of `add_column_with_default` with `allow_null: false` to also add
a `NOT NULL` constraint is [discouraged](https://gitlab.com/gitlab-org/gitlab/issues/38060).
## Dropping Columns
Removing columns is tricky because running GitLab processes may still be using
......
# frozen_string_literal: true
require_relative '../../migration_helpers'
module RuboCop
module Cop
module Migration
# Cop that checks if columns are added in a way that doesn't require
# downtime.
class AddColumnWithDefault < RuboCop::Cop::Cop
include MigrationHelpers
WHITELISTED_TABLES = [:application_settings].freeze
MSG = '`add_column_with_default` with `allow_null: false` may cause prolonged lock situations and downtime, ' \
'see https://gitlab.com/gitlab-org/gitlab/issues/38060'.freeze
def on_send(node)
return unless in_migration?(node)
name = node.children[1]
return unless name == :add_column_with_default
# Ignore whitelisted tables.
return if table_whitelisted?(node.children[2])
opts = node.children.last
return unless opts && opts.type == :hash
opts.each_node(:pair) do |pair|
if disallows_null_values?(pair)
add_offense(node, location: :selector)
end
end
end
def table_whitelisted?(symbol)
symbol && symbol.type == :sym &&
WHITELISTED_TABLES.include?(symbol.children[0])
end
def disallows_null_values?(pair)
options = [hash_key_type(pair), hash_key_name(pair), hash_value(pair)]
options == [:sym, :allow_null, :false] # rubocop:disable Lint/BooleanSymbol
end
def hash_key_type(pair)
pair.children[0].type
end
def hash_key_name(pair)
pair.children[0].children[0]
end
def hash_value(pair)
pair.children[1].type
end
end
end
end
end
......@@ -17,6 +17,7 @@ require_relative 'cop/prefer_class_methods_over_module'
require_relative 'cop/put_project_routes_under_scope'
require_relative 'cop/put_group_routes_under_scope'
require_relative 'cop/migration/add_column'
require_relative 'cop/migration/add_column_with_default'
require_relative 'cop/migration/add_concurrent_foreign_key'
require_relative 'cop/migration/add_concurrent_index'
require_relative 'cop/migration/add_index'
......
......@@ -4,6 +4,7 @@ require 'spec_helper'
describe Admin::UsersController do
let(:user) { create(:user) }
let_it_be(:admin) { create(:admin) }
before do
......
......@@ -12,6 +12,7 @@ describe ContinueParams do
end
end
end
subject(:controller) { controller_class.new }
def strong_continue_params(params)
......
......@@ -12,6 +12,7 @@ describe InternalRedirect do
end
end
end
subject(:controller) { controller_class.new }
describe '#safe_redirect_path' do
......
......@@ -35,6 +35,7 @@ describe Projects::BranchesController do
context "valid branch name, valid source" do
let(:branch) { "merge_branch" }
let(:ref) { "master" }
it 'redirects' do
expect(subject)
.to redirect_to("/#{project.full_path}/tree/merge_branch")
......@@ -44,6 +45,7 @@ describe Projects::BranchesController do
context "invalid branch name, valid ref" do
let(:branch) { "<script>alert('merge');</script>" }
let(:ref) { "master" }
it 'redirects' do
expect(subject)
.to redirect_to("/#{project.full_path}/tree/alert('merge');")
......@@ -53,18 +55,21 @@ describe Projects::BranchesController do
context "valid branch name, invalid ref" do
let(:branch) { "merge_branch" }
let(:ref) { "<script>alert('ref');</script>" }
it { is_expected.to render_template('new') }
end
context "invalid branch name, invalid ref" do
let(:branch) { "<script>alert('merge');</script>" }
let(:ref) { "<script>alert('ref');</script>" }
it { is_expected.to render_template('new') }
end
context "valid branch name with encoded slashes" do
let(:branch) { "feature%2Ftest" }
let(:ref) { "<script>alert('ref');</script>" }
it { is_expected.to render_template('new') }
it { project.repository.branch_exists?('feature/test') }
end
......
......@@ -26,6 +26,7 @@ describe Projects::ClustersController do
let(:project) { create(:project) }
let!(:enabled_cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
let!(:disabled_cluster) { create(:cluster, :disabled, :provided_by_gcp, :production_environment, projects: [project]) }
it 'lists available clusters' do
go
......
......@@ -28,11 +28,13 @@ describe Projects::FindFileController do
context "valid branch" do
let(:id) { 'master' }
it { is_expected.to respond_with(:success) }
end
context "invalid branch" do
let(:id) { 'invalid-branch' }
it { is_expected.to respond_with(:not_found) }
end
end
......@@ -50,6 +52,7 @@ describe Projects::FindFileController do
context "valid branch" do
let(:id) { 'master' }
it 'returns an array of file path list' do
go
......
......@@ -1357,6 +1357,7 @@ describe Projects::IssuesController do
describe 'GET #discussions' do
let!(:discussion) { create(:discussion_note_on_issue, noteable: issue, project: issue.project) }
context 'when authenticated' do
before do
project.add_developer(user)
......
......@@ -29,11 +29,13 @@ describe Projects::TagsController do
context "valid tag" do
let(:id) { 'v1.0.0' }
it { is_expected.to respond_with(:success) }
end
context "invalid tag" do
let(:id) { 'latest' }
it { is_expected.to respond_with(:not_found) }
end
end
......
......@@ -213,6 +213,7 @@ describe Projects::WikisController do
describe 'PATCH #update' do
let(:new_title) { 'New title' }
let(:new_content) { 'New content' }
subject do
patch(:update,
params: {
......
......@@ -81,6 +81,7 @@ describe UsersController do
context 'json with events' do
let(:project) { create(:project) }
before do
project.add_developer(user)
Gitlab::DataBuilder::Push.build_sample(project, user)
......
......@@ -91,6 +91,7 @@ describe 'Dashboard snippets' do
context 'as an external user' do
let(:user) { create(:user, :external) }
before do
sign_in(user)
visit dashboard_snippets_path
......
import Vue from 'vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
import loadingButton from '~/vue_shared/components/loading_button.vue';
import { shallowMount } from '@vue/test-utils';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
const LABEL = 'Hello';
describe('LoadingButton', function() {
let vm;
let LoadingButton;
beforeEach(() => {
LoadingButton = Vue.extend(loadingButton);
});
describe('LoadingButton', () => {
let wrapper;
afterEach(() => {
vm.$destroy();
wrapper.destroy();
});
const buildWrapper = (propsData = {}) => {
wrapper = shallowMount(LoadingButton, {
propsData,
});
};
const findButtonLabel = () => wrapper.find('.js-loading-button-label');
const findButtonIcon = () => wrapper.find('.js-loading-button-icon');
describe('loading spinner', () => {
it('shown when loading', () => {
vm = mountComponent(LoadingButton, {
loading: true,
});
buildWrapper({ loading: true });
expect(vm.$el.querySelector('.js-loading-button-icon')).toBeDefined();
expect(findButtonIcon().exists()).toBe(true);
});
});
describe('disabled state', () => {
it('disabled when loading', () => {
vm = mountComponent(LoadingButton, {
loading: true,
});
expect(vm.$el.disabled).toEqual(true);
buildWrapper({ loading: true });
expect(wrapper.attributes('disabled')).toBe('disabled');
});
it('not disabled when normal', () => {
vm = mountComponent(LoadingButton, {
loading: false,
});
buildWrapper({ loading: false });
expect(vm.$el.disabled).toEqual(false);
expect(wrapper.attributes('disabled')).toBe(undefined);
});
});
describe('label', () => {
it('shown when normal', () => {
vm = mountComponent(LoadingButton, {
loading: false,
label: LABEL,
});
const label = vm.$el.querySelector('.js-loading-button-label');
expect(label.textContent.trim()).toEqual(LABEL);
buildWrapper({ loading: false, label: LABEL });
expect(findButtonLabel().text()).toBe(LABEL);
});
it('shown when loading', () => {
vm = mountComponent(LoadingButton, {
loading: true,
label: LABEL,
});
const label = vm.$el.querySelector('.js-loading-button-label');
expect(label.textContent.trim()).toEqual(LABEL);
buildWrapper({ loading: false, label: LABEL });
expect(findButtonLabel().text()).toBe(LABEL);
});
});
describe('container class', () => {
it('should default to btn btn-align-content', () => {
vm = mountComponent(LoadingButton, {});
buildWrapper();
expect(vm.$el.classList.contains('btn')).toEqual(true);
expect(vm.$el.classList.contains('btn-align-content')).toEqual(true);
expect(wrapper.classes()).toContain('btn');
expect(wrapper.classes()).toContain('btn-align-content');
});
it('should be configurable through props', () => {
vm = mountComponent(LoadingButton, {
containerClass: 'test-class',
const containerClass = 'test-class';
buildWrapper({
containerClass,
});
expect(vm.$el.classList.contains('btn')).toEqual(false);
expect(vm.$el.classList.contains('btn-align-content')).toEqual(false);
expect(vm.$el.classList.contains('test-class')).toEqual(true);
expect(wrapper.classes()).not.toContain('btn');
expect(wrapper.classes()).not.toContain('btn-align-content');
expect(wrapper.classes()).toContain(containerClass);
});
});
describe('click callback prop', () => {
it('calls given callback when normal', () => {
vm = mountComponent(LoadingButton, {
buildWrapper({
loading: false,
});
spyOn(vm, '$emit');
vm.$el.click();
wrapper.trigger('click');
expect(vm.$emit).toHaveBeenCalledWith('click', jasmine.any(Object));
expect(wrapper.emitted('click')).toBeTruthy();
});
it('does not call given callback when disabled because of loading', () => {
vm = mountComponent(LoadingButton, {
buildWrapper({
loading: true,
});
spyOn(vm, '$emit');
vm.$el.click();
wrapper.trigger('click');
expect(vm.$emit).not.toHaveBeenCalled();
expect(wrapper.emitted('click')).toBeFalsy();
});
});
});
......@@ -319,7 +319,7 @@ describe('Multi-file store actions', () => {
{ type: types.TOGGLE_FILE_OPEN, payload: 'test' },
{ type: types.ADD_FILE_TO_CHANGED, payload: 'test' },
],
[
jasmine.arrayContaining([
{
type: 'setFileActive',
payload: 'test',
......@@ -327,7 +327,7 @@ describe('Multi-file store actions', () => {
{
type: 'triggerFilesChange',
},
],
]),
done,
);
});
......@@ -350,6 +350,21 @@ describe('Multi-file store actions', () => {
})
.catch(done.fail);
});
it('bursts unused seal', done => {
store
.dispatch('createTempEntry', {
name: 'test',
branchId: 'mybranch',
type: 'blob',
})
.then(() => {
expect(store.state.unusedSeal).toBe(false);
done();
})
.catch(done.fail);
});
});
});
......@@ -648,6 +663,19 @@ describe('Multi-file store actions', () => {
],
);
});
it('bursts unused seal', done => {
store.state.entries.test = file('test');
store
.dispatch('deleteEntry', 'test')
.then(() => {
expect(store.state.unusedSeal).toBe(false);
done();
})
.catch(done.fail);
});
});
describe('renameEntry', () => {
......@@ -747,7 +775,7 @@ describe('Multi-file store actions', () => {
payload: 'renamed',
},
],
[{ type: 'triggerFilesChange' }],
[{ type: 'burstUnusedSeal' }, { type: 'triggerFilesChange' }],
done,
);
});
......@@ -807,6 +835,20 @@ describe('Multi-file store actions', () => {
.then(done)
.catch(done.fail);
});
it('bursts unused seal', done => {
store
.dispatch('renameEntry', {
path: 'orig',
name: 'renamed',
})
.then(() => {
expect(store.state.unusedSeal).toBe(false);
done();
})
.catch(done.fail);
});
});
describe('folder', () => {
......
......@@ -424,6 +424,7 @@ describe Blob do
describe 'policy' do
let(:project) { build(:project) }
subject { described_class.new(fake_blob(path: 'foo'), project) }
it 'works with policy' do
......
......@@ -7,6 +7,7 @@ describe BlobViewer::Changelog do
let(:project) { create(:project, :repository) }
let(:blob) { fake_blob(path: 'CHANGELOG') }
subject { described_class.new(blob) }
describe '#render_error' do
......
......@@ -15,6 +15,7 @@ describe BlobViewer::ComposerJson do
SPEC
end
let(:blob) { fake_blob(path: 'composer.json', data: data) }
subject { described_class.new(blob) }
describe '#package_name' do
......
......@@ -15,6 +15,7 @@ describe BlobViewer::Gemspec do
SPEC
end
let(:blob) { fake_blob(path: 'activerecord.gemspec', data: data) }
subject { described_class.new(blob) }
describe '#package_name' do
......
......@@ -12,6 +12,7 @@ describe BlobViewer::GitlabCiYml do
let(:data) { File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) }
let(:blob) { fake_blob(path: '.gitlab-ci.yml', data: data) }
let(:sha) { sample_commit.id }
subject { described_class.new(blob) }
describe '#validation_message' do
......
......@@ -7,6 +7,7 @@ describe BlobViewer::License do
let(:project) { create(:project, :repository) }
let(:blob) { fake_blob(path: 'LICENSE') }
subject { described_class.new(blob) }
describe '#license' do
......
......@@ -15,6 +15,7 @@ describe BlobViewer::PackageJson do
SPEC
end
let(:blob) { fake_blob(path: 'package.json', data: data) }
subject { described_class.new(blob) }
describe '#package_name' do
......@@ -54,6 +55,7 @@ describe BlobViewer::PackageJson do
SPEC
end
let(:blob) { fake_blob(path: 'package.json', data: data) }
subject { described_class.new(blob) }
describe '#package_url' do
......
......@@ -15,6 +15,7 @@ describe BlobViewer::PodspecJson do
SPEC
end
let(:blob) { fake_blob(path: 'AFNetworking.podspec.json', data: data) }
subject { described_class.new(blob) }
describe '#package_name' do
......
......@@ -15,6 +15,7 @@ describe BlobViewer::Podspec do
SPEC
end
let(:blob) { fake_blob(path: 'Reachability.podspec', data: data) }
subject { described_class.new(blob) }
describe '#package_name' do
......
......@@ -7,6 +7,7 @@ describe BlobViewer::Readme do
let(:project) { create(:project, :repository, :wiki_repo) }
let(:blob) { fake_blob(path: 'README.md') }
subject { described_class.new(blob) }
describe '#render_error' do
......
......@@ -14,6 +14,7 @@ describe BlobViewer::RouteMap do
MAP
end
let(:blob) { fake_blob(path: '.gitlab/route-map.yml', data: data) }
subject { described_class.new(blob) }
describe '#validation_message' do
......
......@@ -610,6 +610,7 @@ describe Ci::Build do
context 'artifacts archive is a zip file and metadata exists' do
let(:build) { create(:ci_build, :artifacts) }
it { is_expected.to be_truthy }
end
end
......@@ -1408,6 +1409,7 @@ describe Ci::Build do
describe '#erased?' do
let!(:build) { create(:ci_build, :trace_artifact, :success, :artifacts) }
subject { build.erased? }
context 'job has not been erased' do
......@@ -1469,6 +1471,7 @@ describe Ci::Build do
describe '#first_pending' do
let!(:first) { create(:ci_build, pipeline: pipeline, status: 'pending', created_at: Date.yesterday) }
let!(:second) { create(:ci_build, pipeline: pipeline, status: 'pending') }
subject { described_class.first_pending }
it { is_expected.to be_a(described_class) }
......
......@@ -755,11 +755,13 @@ describe Ci::Runner do
context 'when group runner' do
let(:runner) { create(:ci_runner, :group, description: 'Group runner', groups: [group]) }
let(:group) { create(:group) }
it { is_expected.to be_falsey }
end
context 'when shared runner' do
let(:runner) { create(:ci_runner, :instance, description: 'Shared runner') }
it { is_expected.to be_falsey }
end
......
......@@ -146,6 +146,7 @@ describe Ci::Stage, :models do
let(:user) { create(:user) }
let(:stage) { create(:ci_stage_entity, status: :created) }
subject { stage.detailed_status(user) }
where(:statuses, :label) do
......
......@@ -123,6 +123,7 @@ describe Clusters::Applications::ElasticStack do
context "cluster doesn't have kubeclient" do
let(:cluster) { create(:cluster) }
subject { create(:clusters_applications_elastic_stack, cluster: cluster) }
it 'returns nil' do
......
......@@ -52,6 +52,7 @@ describe Clusters::Applications::Helm do
describe '#issue_client_cert' do
let(:application) { create(:clusters_applications_helm) }
subject { application.issue_client_cert }
it 'returns a new cert' do
......
......@@ -131,6 +131,7 @@ describe Clusters::Applications::Knative do
describe '#update_command' do
let!(:current_installed_version) { knative.version = '0.1.0' }
subject { knative.update_command }
it 'is initialized with current version' do
......
......@@ -66,6 +66,7 @@ describe Clusters::Applications::Prometheus do
context "cluster doesn't have kubeclient" do
let(:cluster) { create(:cluster) }
subject { create(:clusters_applications_prometheus, cluster: cluster) }
it 'returns nil' do
......
......@@ -92,6 +92,7 @@ describe CacheMarkdownField, :clean_gitlab_redis_cache do
describe '#latest_cached_markdown_version' do
let(:thing) { klass.new }
subject { thing.latest_cached_markdown_version }
it 'returns default version' do
......@@ -151,6 +152,7 @@ describe CacheMarkdownField, :clean_gitlab_redis_cache do
describe '#banzai_render_context' do
let(:thing) { klass.new(title: markdown, title_html: html, cached_markdown_version: cache_version) }
subject(:context) { thing.banzai_render_context(:title) }
it 'sets project to nil if the object lacks a project' do
......
......@@ -49,11 +49,13 @@ describe IgnorableColumns do
context 'with single column' do
let(:columns) { :name }
it_behaves_like 'storing removal information'
end
context 'with array column' do
let(:columns) { %i[name created_at] }
it_behaves_like 'storing removal information'
end
......
......@@ -4,6 +4,7 @@ require 'spec_helper'
describe LoadedInGroupList do
let(:parent) { create(:group) }
subject(:found_group) { Group.with_selects_for_list.find_by(id: parent.id) }
describe '.with_selects_for_list' do
......
......@@ -103,6 +103,7 @@ describe PrometheusAdapter, :use_clean_rails_memory_store_caching do
describe '#calculate_reactive_cache' do
let(:environment) { create(:environment, slug: 'env-slug') }
before do
service.manual_configuration = true
service.active = true
......
......@@ -5,6 +5,7 @@ require 'spec_helper'
describe Note, ResolvableNote do
let(:project) { create(:project, :repository) }
let(:merge_request) { create(:merge_request, source_project: project) }
subject { create(:discussion_note_on_merge_request, noteable: merge_request, project: project) }
context 'resolvability scopes' do
......
......@@ -13,6 +13,7 @@ end
describe User, 'TokenAuthenticatable' do
let(:token_field) { :feed_token }
it_behaves_like 'TokenAuthenticatable'
describe 'ensures authentication token' do
......
......@@ -8,6 +8,7 @@ describe Environment, :use_clean_rails_memory_store_caching do
include RepoHelpers
let(:project) { create(:project, :stubbed_repository) }
subject(:environment) { create(:environment, project: project) }
it { is_expected.to be_kind_of(ReactiveCaching) }
......@@ -676,6 +677,7 @@ describe Environment, :use_clean_rails_memory_store_caching do
context 'with deployment' do
let!(:deployment) { create(:deployment, :success, environment: environment) }
it { is_expected.to be_truthy }
end
......@@ -832,6 +834,7 @@ describe Environment, :use_clean_rails_memory_store_caching do
context 'and a deployment' do
let!(:deployment) { create(:deployment, environment: environment) }
it { is_expected.to be_truthy }
end
......@@ -866,6 +869,7 @@ describe Environment, :use_clean_rails_memory_store_caching do
describe '#metrics' do
let(:project) { create(:prometheus_project) }
subject { environment.metrics }
context 'when the environment has metrics' do
......@@ -947,6 +951,7 @@ describe Environment, :use_clean_rails_memory_store_caching do
describe '#additional_metrics' do
let(:project) { create(:prometheus_project) }
let(:metric_params) { [] }
subject { environment.additional_metrics(*metric_params) }
context 'when the environment has additional metrics' do
......
......@@ -33,6 +33,7 @@ describe ExternalIssue do
context 'if issue id is a number' do
let(:issue) { described_class.new('1234', project) }
it 'returns the issue ID prefixed by #' do
expect(issue.reference_link_text).to eq '#1234'
end
......
......@@ -162,6 +162,7 @@ describe GlobalMilestone do
describe '#initialize' do
let(:milestone1_project1) { create(:milestone, title: "Milestone v1.2", project: project1) }
subject(:global_milestone) { described_class.new(milestone1_project1) }
it 'has exactly one group milestone' do
......
......@@ -28,6 +28,7 @@ describe Group do
describe '#members & #requesters' do
let(:requester) { create(:user) }
let(:developer) { create(:user) }
before do
group.request_access(requester)
group.add_developer(developer)
......
......@@ -53,16 +53,19 @@ describe WebHookLog do
describe '2xx' do
let(:status) { '200' }
it { expect(web_hook_log.success?).to be_truthy }
end
describe 'not 2xx' do
let(:status) { '500' }
it { expect(web_hook_log.success?).to be_falsey }
end
describe 'internal erorr' do
let(:status) { 'internal error' }
it { expect(web_hook_log.success?).to be_falsey }
end
end
......
......@@ -48,6 +48,7 @@ describe InstanceConfiguration do
describe '#gitlab_pages' do
let(:gitlab_pages) { subject.settings[:gitlab_pages] }
it 'returns Settings.pages' do
gitlab_pages.delete(:ip_address)
......@@ -73,6 +74,7 @@ describe InstanceConfiguration do
describe '#gitlab_ci' do
let(:gitlab_ci) { subject.settings[:gitlab_ci] }
it 'returns Settings.gitalb_ci' do
gitlab_ci.delete(:artifacts_max_size)
......
......@@ -170,6 +170,7 @@ describe InternalId do
describe '.track_greatest' do
let(:value) { 9001 }
subject { described_class.track_greatest(issue, scope, usage, value, init) }
context 'in the absence of a record' do
......@@ -210,6 +211,7 @@ describe InternalId do
describe '#increment_and_save!' do
let(:id) { create(:internal_id) }
subject { id.increment_and_save! }
it 'returns incremented iid' do
......@@ -236,6 +238,7 @@ describe InternalId do
describe '#track_greatest_and_save!' do
let(:id) { create(:internal_id) }
let(:new_last_value) { 9001 }
subject { id.track_greatest_and_save!(new_last_value) }
it 'returns new last value' do
......
......@@ -259,6 +259,7 @@ describe Issue do
describe '#can_move?' do
let(:user) { create(:user) }
let(:issue) { create(:issue) }
subject { issue.can_move?(user) }
context 'user is not a member of project issue belongs to' do
......@@ -277,6 +278,7 @@ describe Issue do
context 'issue not persisted' do
let(:issue) { build(:issue, project: project) }
it { is_expected.to eq false }
end
......@@ -306,6 +308,7 @@ describe Issue do
describe '#moved?' do
let(:issue) { create(:issue) }
subject { issue.moved? }
context 'issue not moved' do
......@@ -322,6 +325,7 @@ describe Issue do
describe '#duplicated?' do
let(:issue) { create(:issue) }
subject { issue.duplicated? }
context 'issue not duplicated' do
......@@ -380,6 +384,7 @@ describe Issue do
describe '#has_related_branch?' do
let(:issue) { create(:issue, title: "Blue Bell Knoll") }
subject { issue.has_related_branch? }
context 'branch found' do
......@@ -442,6 +447,7 @@ describe Issue do
describe '#can_be_worked_on?' do
let(:project) { build(:project) }
subject { build(:issue, :opened, project: project) }
context 'is closed' do
......
......@@ -25,6 +25,7 @@ describe Key, :mailer do
describe "Methods" do
let(:user) { create(:user) }
it { is_expected.to respond_to :projects }
it { is_expected.to respond_to :publishable_key }
......
......@@ -110,6 +110,7 @@ describe MergeRequest do
describe '#squash?' do
let(:merge_request) { build(:merge_request, squash: squash) }
subject { merge_request.squash? }
context 'disabled in database' do
......@@ -851,6 +852,7 @@ describe MergeRequest do
describe '#modified_paths' do
let(:paths) { double(:paths) }
subject(:merge_request) { build(:merge_request) }
before do
......@@ -879,6 +881,7 @@ describe MergeRequest do
context 'when no arguments provided' do
let(:diff) { merge_request.merge_request_diff }
subject(:merge_request) { create(:merge_request, source_branch: 'feature', target_branch: 'master') }
it 'returns affected file paths for merge_request_diff' do
......@@ -1554,6 +1557,7 @@ describe MergeRequest do
describe '#calculate_reactive_cache' do
let(:project) { create(:project, :repository) }
let(:merge_request) { create(:merge_request, source_project: project) }
subject { merge_request.calculate_reactive_cache(service_class_name) }
context 'when given an unknown service class name' do
......@@ -3044,6 +3048,7 @@ describe MergeRequest do
describe 'transition to cannot_be_merged' do
let(:notification_service) { double(:notification_service) }
let(:todo_service) { double(:todo_service) }
subject { create(:merge_request, state, merge_status: :unchecked) }
before do
......@@ -3253,6 +3258,7 @@ describe MergeRequest do
describe 'when merge_when_pipeline_succeeds? is true' do
describe 'when merge user is author' do
let(:user) { create(:user) }
subject do
create(:merge_request,
merge_when_pipeline_succeeds: true,
......@@ -3267,6 +3273,7 @@ describe MergeRequest do
describe 'when merge user and author are different users' do
let(:merge_user) { create(:user) }
subject do
create(:merge_request,
merge_when_pipeline_succeeds: true,
......
......@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe ProjectDeployToken, type: :model do
let(:project) { create(:project) }
let(:deploy_token) { create(:deploy_token) }
subject(:project_deploy_token) { create(:project_deploy_token, project: project, deploy_token: deploy_token) }
it { is_expected.to belong_to :project }
......
......@@ -38,6 +38,7 @@ describe MicrosoftTeamsService do
describe "#execute" do
let(:user) { create(:user) }
set(:project) { create(:project, :repository, :wiki_repo) }
before do
......
......@@ -165,6 +165,7 @@ describe Project do
let(:project) { create(:project, :public) }
let(:requester) { create(:user) }
let(:developer) { create(:user) }
before do
project.request_access(requester)
project.add_developer(developer)
......@@ -815,6 +816,7 @@ describe Project do
context 'with external issues tracker' do
let!(:internal_issue) { create(:issue, project: project) }
before do
allow(project).to receive(:external_issue_tracker).and_return(true)
end
......@@ -2334,6 +2336,7 @@ describe Project do
describe '#has_remote_mirror?' do
let(:project) { create(:project, :remote_mirror, :import_started) }
subject { project.has_remote_mirror? }
before do
......@@ -2353,6 +2356,7 @@ describe Project do
describe '#update_remote_mirrors' do
let(:project) { create(:project, :remote_mirror, :import_started) }
delegate :update_remote_mirrors, to: :project
before do
......@@ -3460,6 +3464,7 @@ describe Project do
describe '#pipeline_status' do
let(:project) { create(:project, :repository) }
it 'builds a pipeline status' do
expect(project.pipeline_status).to be_a(Gitlab::Cache::Ci::ProjectPipelineStatus)
end
......@@ -4638,6 +4643,7 @@ describe Project do
describe '#execute_hooks' do
let(:data) { { ref: 'refs/heads/master', data: 'data' } }
it 'executes active projects hooks with the specified scope' do
hook = create(:project_hook, merge_requests_events: false, push_events: true)
expect(ProjectHook).to receive(:select_active)
......@@ -4968,6 +4974,7 @@ describe Project do
context 'when there is a gitlab deploy token associated but is has been revoked' do
let!(:deploy_token) { create(:deploy_token, :gitlab_deploy_token, :revoked, projects: [project]) }
it { is_expected.to be_nil }
end
......@@ -5011,6 +5018,7 @@ describe Project do
context '#members_among' do
let(:users) { create_list(:user, 3) }
set(:group) { create(:group) }
set(:project) { create(:project, namespace: group) }
......
......@@ -7,6 +7,7 @@ describe ReadmeBlob do
describe 'policy' do
let(:project) { build(:project, :repository) }
subject { described_class.new(fake_blob(path: 'README.md'), project.repository) }
it 'works with policy' do
......
......@@ -717,6 +717,7 @@ describe Repository do
describe "search_files_by_content" do
let(:results) { repository.search_files_by_content('feature', 'master') }
subject { results }
it { is_expected.to be_an Array }
......
......@@ -18,6 +18,7 @@ describe SentNotification do
context "when the project doesn't match the discussion project" do
let(:discussion_id) { create(:note).discussion_id }
subject { build(:sent_notification, in_reply_to_discussion_id: discussion_id) }
it "is invalid" do
......@@ -29,6 +30,7 @@ describe SentNotification do
let(:project) { create(:project, :repository) }
let(:issue) { create(:issue, project: project) }
let(:discussion_id) { create(:note, project: project, noteable: issue).discussion_id }
subject { build(:sent_notification, project: project, noteable: issue, in_reply_to_discussion_id: discussion_id) }
it "is valid" do
......@@ -196,6 +198,7 @@ describe SentNotification do
describe '#create_reply' do
context 'for issue' do
let(:issue) { create(:issue) }
subject { described_class.record(issue, issue.author.id) }
it 'creates a comment on the issue' do
......@@ -206,6 +209,7 @@ describe SentNotification do
context 'for issue comment' do
let(:note) { create(:note_on_issue) }
subject { described_class.record_note(note, note.author.id) }
it 'creates a comment on the issue' do
......@@ -217,6 +221,7 @@ describe SentNotification do
context 'for issue discussion' do
let(:note) { create(:discussion_note_on_issue) }
subject { described_class.record_note(note, note.author.id) }
it 'creates a reply on the discussion' do
......@@ -228,6 +233,7 @@ describe SentNotification do
context 'for merge request' do
let(:merge_request) { create(:merge_request) }
subject { described_class.record(merge_request, merge_request.author.id) }
it 'creates a comment on the merge_request' do
......@@ -238,6 +244,7 @@ describe SentNotification do
context 'for merge request comment' do
let(:note) { create(:note_on_merge_request) }
subject { described_class.record_note(note, note.author.id) }
it 'creates a comment on the merge request' do
......@@ -249,6 +256,7 @@ describe SentNotification do
context 'for merge request diff discussion' do
let(:note) { create(:diff_note_on_merge_request) }
subject { described_class.record_note(note, note.author.id) }
it 'creates a reply on the discussion' do
......@@ -260,6 +268,7 @@ describe SentNotification do
context 'for merge request non-diff discussion' do
let(:note) { create(:discussion_note_on_merge_request) }
subject { described_class.record_note(note, note.author.id) }
it 'creates a reply on the discussion' do
......@@ -272,6 +281,7 @@ describe SentNotification do
context 'for commit' do
let(:project) { create(:project, :repository) }
let(:commit) { project.commit }
subject { described_class.record(commit, project.creator.id) }
it 'creates a comment on the commit' do
......@@ -282,6 +292,7 @@ describe SentNotification do
context 'for commit comment' do
let(:note) { create(:note_on_commit) }
subject { described_class.record_note(note, note.author.id) }
it 'creates a comment on the commit' do
......@@ -293,6 +304,7 @@ describe SentNotification do
context 'for commit diff discussion' do
let(:note) { create(:diff_note_on_commit) }
subject { described_class.record_note(note, note.author.id) }
it 'creates a reply on the discussion' do
......@@ -304,6 +316,7 @@ describe SentNotification do
context 'for commit non-diff discussion' do
let(:note) { create(:discussion_note_on_commit) }
subject { described_class.record_note(note, note.author.id) }
it 'creates a reply on the discussion' do
......
......@@ -141,6 +141,7 @@ describe Snippet do
describe "#content_html_invalidated?" do
let(:snippet) { create(:snippet, content: "md", content_html: "html", file_name: "foo.md") }
it "invalidates the HTML cache of content when the filename changes" do
expect { snippet.file_name = "foo.rb" }.to change { snippet.content_html_invalidated? }.from(false).to(true)
end
......
......@@ -31,6 +31,7 @@ describe Uploads::Fog do
describe '#keys' do
let!(:uploads) { create_list(:upload, 2, :object_storage, uploader: FileUploader, model: project) }
subject { data_store.keys(relation) }
it 'returns keys' do
......@@ -41,6 +42,7 @@ describe Uploads::Fog do
describe '#delete_keys' do
let(:keys) { data_store.keys(relation) }
let!(:uploads) { create_list(:upload, 2, :with_file, :issuable_upload, model: project) }
subject { data_store.delete_keys(keys) }
before do
......
......@@ -15,6 +15,7 @@ describe Uploads::Local do
describe '#keys' do
let!(:uploads) { create_list(:upload, 2, uploader: FileUploader, model: project) }
subject { data_store.keys(relation) }
it 'returns keys' do
......@@ -25,6 +26,7 @@ describe Uploads::Local do
describe '#delete_keys' do
let(:keys) { data_store.keys(relation) }
let!(:uploads) { create_list(:upload, 2, :with_file, :issuable_upload, model: project) }
subject { data_store.delete_keys(keys) }
it 'deletes multiple data' do
......
......@@ -11,6 +11,7 @@ describe UserInteractedProject do
Event::ACTIONS.each do |action|
context "for all actions (event types)" do
let(:event) { build(:event, action: action) }
it 'creates a record' do
expect { subject }.to change { described_class.count }.from(0).to(1)
end
......
......@@ -2390,6 +2390,7 @@ describe User, :do_not_mock_admin_mode do
describe '#authorizations_for_projects' do
let!(:user) { create(:user) }
subject { Project.where("EXISTS (?)", user.authorizations_for_projects) }
it 'includes projects that belong to a user, but no other projects' do
......@@ -3686,6 +3687,7 @@ describe User, :do_not_mock_admin_mode do
describe '#required_terms_not_accepted?' do
let(:user) { build(:user) }
subject { user.required_terms_not_accepted? }
context "when terms are not enforced" do
......
# frozen_string_literal: true
require 'spec_helper'
require 'rubocop'
require 'rubocop/rspec/support'
require_relative '../../../../rubocop/cop/migration/add_column_with_default'
describe RuboCop::Cop::Migration::AddColumnWithDefault do
include CopHelper
let(:cop) { described_class.new }
context 'outside of a migration' do
it 'does not register any offenses' do
expect_no_offenses(<<~RUBY)
def up
add_reference(:projects, :users)
end
RUBY
end
end
context 'in a migration' do
before do
allow(cop).to receive(:in_migration?).and_return(true)
end
let(:offense) { '`add_column_with_default` with `allow_null: false` may cause prolonged lock situations and downtime, see https://gitlab.com/gitlab-org/gitlab/issues/38060' }
it 'registers an offense when specifying allow_null: false' do
expect_offense(<<~RUBY)
def up
add_column_with_default(:ci_build_needs, :artifacts, :boolean, default: true, allow_null: false)
^^^^^^^^^^^^^^^^^^^^^^^ #{offense}
end
RUBY
end
it 'registers no offense when specifying allow_null: true' do
expect_no_offenses(<<~RUBY)
def up
add_column_with_default(:ci_build_needs, :artifacts, :boolean, default: true, allow_null: true)
end
RUBY
end
it 'registers no offense when allow_null is not specified' do
expect_no_offenses(<<~RUBY)
def up
add_column_with_default(:ci_build_needs, :artifacts, :boolean, default: true)
end
RUBY
end
it 'registers no offense for application_settings (whitelisted table)' do
expect_no_offenses(<<~RUBY)
def up
add_column_with_default(:application_settings, :another_column, :boolean, default: true, allow_null: false)
end
RUBY
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