Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-05-05 00:10:41 +00:00
parent 4f49d2c8cd
commit 415153114a
49 changed files with 491 additions and 35 deletions

View file

@ -7,6 +7,7 @@ module BoardsActions
included do
include BoardsResponses
before_action :authorize_read_board!, only: [:index, :show]
before_action :boards, only: :index
before_action :board, only: :show
before_action :push_licensed_features, only: [:index, :show]

View file

@ -5,7 +5,6 @@ class Groups::BoardsController < Groups::ApplicationController
include RecordUserLastActivity
include Gitlab::Utils::StrongMemoize
before_action :authorize_read_board!, only: [:index, :show]
before_action :assign_endpoint_vars
before_action do
push_frontend_feature_flag(:graphql_board_lists, group, default_enabled: false)

View file

@ -5,7 +5,6 @@ class Projects::BoardsController < Projects::ApplicationController
include IssuableCollections
before_action :check_issues_available!
before_action :authorize_read_board!, only: [:index, :show]
before_action :assign_endpoint_vars
before_action do
push_frontend_feature_flag(:swimlanes_buffered_rendering, project, default_enabled: :yaml)

View file

@ -33,7 +33,7 @@ module PrometheusAdapter
# This is a light-weight check if a prometheus client is properly configured.
def configured?
raise NotImplemented
raise NotImplementedError
end
# This is a heavy-weight check if a prometheus is properly configured and accessible from GitLab.

View file

@ -205,6 +205,7 @@ class User < ApplicationRecord
has_one :user_detail
has_one :user_highest_role
has_one :user_canonical_email
has_one :credit_card_validation, class_name: '::Users::CreditCardValidation'
has_one :atlassian_identity, class_name: 'Atlassian::Identity'
has_many :reviews, foreign_key: :author_id, inverse_of: :author
@ -317,6 +318,7 @@ class User < ApplicationRecord
accepts_nested_attributes_for :user_preference, update_only: true
accepts_nested_attributes_for :user_detail, update_only: true
accepts_nested_attributes_for :credit_card_validation, update_only: true
state_machine :state, initial: :active do
event :block do
@ -1223,6 +1225,10 @@ class User < ApplicationRecord
user_highest_role&.highest_access_level || Gitlab::Access::NO_ACCESS
end
def credit_card_validated_at
credit_card_validation&.credit_card_validated_at
end
def accessible_deploy_keys
DeployKey.from_union([
DeployKey.where(id: project_deploy_keys.select(:deploy_key_id)),

View file

@ -0,0 +1,9 @@
# frozen_string_literal: true
module Users
class CreditCardValidation < ApplicationRecord
self.table_name = 'user_credit_card_validations'
belongs_to :user
end
end

View file

@ -160,6 +160,13 @@
"description": ".NET Core, .NET Framework",
"variables": []
},
{
"name": "semgrep",
"label": "Semgrep",
"enabled": true,
"description": "Multi-language scanning",
"variables": []
},
{
"name": "sobelow",
"label": "Sobelow",

View file

@ -0,0 +1,5 @@
---
title: Validate foreign key on GroupHooks
merge_request: 60527
author:
type: other

View file

@ -0,0 +1,5 @@
---
title: Add semgrep to SAST config UI
merge_request: 60460
author:
type: added

View file

@ -0,0 +1,5 @@
---
title: Create table user_credit_card_validations
merge_request: 60626
author:
type: changed

View file

@ -0,0 +1,5 @@
---
title: Populate timelogs.project_id
merge_request: 60439
author: Lee Tickett @leetickett
type: added

View file

@ -0,0 +1,24 @@
# frozen_string_literal: true
class UpdateInvalidWebHooks < ActiveRecord::Migration[6.0]
disable_ddl_transaction!
class WebHook < ActiveRecord::Base
include EachBatch
self.table_name = 'web_hooks'
end
def up
WebHook.each_batch(of: 10_000, column: :id) do |relation|
relation.where(type: 'ProjectHook')
.where.not(project_id: nil)
.where.not(group_id: nil)
.update_all(group_id: nil)
end
end
def down
# no-op
end
end

View file

@ -0,0 +1,15 @@
# frozen_string_literal: true
class ValidateForeignKeyOnGroupHooks < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
CONSTRAINT_NAME = 'fk_rails_d35697648e'
def up
validate_foreign_key :web_hooks, :group_id, name: CONSTRAINT_NAME
end
def down
# no-op
end
end

View file

@ -0,0 +1,22 @@
# frozen_string_literal: true
class CreateUserCreditCardValidations < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
def up
with_lock_retries do
create_table :user_credit_card_validations, id: false do |t|
t.references :user, foreign_key: { on_delete: :cascade }, index: false, primary_key: true, default: nil
t.datetime_with_timezone :credit_card_validated_at, null: false
end
end
end
def down
with_lock_retries do
drop_table :user_credit_card_validations
end
end
end

View file

@ -0,0 +1,32 @@
# frozen_string_literal: true
class ScheduleUpdateTimelogsProjectId < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
BATCH_SIZE = 50_000
DELAY_INTERVAL = 2.minutes
MIGRATION = 'UpdateTimelogsProjectId'
disable_ddl_transaction!
class Timelog < ActiveRecord::Base
include EachBatch
self.table_name = 'timelogs'
self.inheritance_column = :_type_disabled
end
def up
queue_background_migration_jobs_by_range_at_intervals(
Timelog.all,
MIGRATION,
DELAY_INTERVAL,
batch_size: BATCH_SIZE
)
end
def down
# no-op
end
end

View file

@ -0,0 +1 @@
2ffe65c4abcb8f638198943e1b74de710387438fb7c93addb05ccb3e86729934

View file

@ -0,0 +1 @@
468373a97f7bd66197c81f01bebd27256cf96ec8fc226c5d73e579a7ecc3930d

View file

@ -0,0 +1 @@
3244023441c2afa450ad76345a494975b4a7154892298daf1ec4223d27fb7ca3

View file

@ -0,0 +1 @@
68ac54fa7b4e4ef99e58c31d8f960b6f986fd679c11ead235704c7a75b4617ac

View file

@ -18282,6 +18282,11 @@ CREATE SEQUENCE user_canonical_emails_id_seq
ALTER SEQUENCE user_canonical_emails_id_seq OWNED BY user_canonical_emails.id;
CREATE TABLE user_credit_card_validations (
user_id bigint NOT NULL,
credit_card_validated_at timestamp with time zone NOT NULL
);
CREATE TABLE user_custom_attributes (
id integer NOT NULL,
created_at timestamp without time zone NOT NULL,
@ -21655,6 +21660,9 @@ ALTER TABLE ONLY user_callouts
ALTER TABLE ONLY user_canonical_emails
ADD CONSTRAINT user_canonical_emails_pkey PRIMARY KEY (id);
ALTER TABLE ONLY user_credit_card_validations
ADD CONSTRAINT user_credit_card_validations_pkey PRIMARY KEY (user_id);
ALTER TABLE ONLY user_custom_attributes
ADD CONSTRAINT user_custom_attributes_pkey PRIMARY KEY (id);
@ -25941,6 +25949,9 @@ ALTER TABLE ONLY lfs_file_locks
ALTER TABLE ONLY project_alerting_settings
ADD CONSTRAINT fk_rails_27a84b407d FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY user_credit_card_validations
ADD CONSTRAINT fk_rails_27ebc03cbf FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
ALTER TABLE ONLY dast_site_validations
ADD CONSTRAINT fk_rails_285c617324 FOREIGN KEY (dast_site_token_id) REFERENCES dast_site_tokens(id) ON DELETE CASCADE;
@ -26923,7 +26934,7 @@ ALTER TABLE ONLY pool_repositories
ADD CONSTRAINT fk_rails_d2711daad4 FOREIGN KEY (source_project_id) REFERENCES projects(id) ON DELETE SET NULL;
ALTER TABLE ONLY web_hooks
ADD CONSTRAINT fk_rails_d35697648e FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE NOT VALID;
ADD CONSTRAINT fk_rails_d35697648e FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY group_group_links
ADD CONSTRAINT fk_rails_d3a0488427 FOREIGN KEY (shared_group_id) REFERENCES namespaces(id) ON DELETE CASCADE;

View file

@ -635,11 +635,12 @@ Implemented using Redis methods [PFADD](https://redis.io/commands/pfadd) and [PF
- `category`: event category. Used for getting total counts for events in a category, for easier
access to a group of events.
- `redis_slot`: optional Redis slot; default value: event name. Used if needed to calculate totals
for a group of metrics. Ensure keys are in the same slot. For example:
- `redis_slot`: optional Redis slot. Default value: event name. Only event data that is stored in the same slot
can be aggregated. Ensure keys are in the same slot. For example:
`users_creating_epics` with `redis_slot: 'users'` builds Redis key
`{users}_creating_epics-2020-34`. If `redis_slot` is not defined the Redis key will
be `{users_creating_epics}-2020-34`.
Recommended slots to use are: `users`, `projects`. This is the value we count.
- `expiry`: expiry time in days. Default: 29 days for daily aggregation and 6 weeks for weekly
aggregation.
- `aggregation`: may be set to a `:daily` or `:weekly` key. Defines how counting data is stored in Redis.

View file

@ -0,0 +1,44 @@
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
# Class to populate project_id for timelogs
class UpdateTimelogsProjectId
BATCH_SIZE = 1000
def perform(start_id, stop_id)
(start_id..stop_id).step(BATCH_SIZE).each do |offset|
update_issue_timelogs(offset, offset + BATCH_SIZE)
update_merge_request_timelogs(offset, offset + BATCH_SIZE)
end
end
def update_issue_timelogs(batch_start, batch_stop)
execute(<<~SQL)
UPDATE timelogs
SET project_id = issues.project_id
FROM issues
WHERE issues.id = timelogs.issue_id
AND timelogs.id BETWEEN #{batch_start} AND #{batch_stop}
AND timelogs.project_id IS NULL;
SQL
end
def update_merge_request_timelogs(batch_start, batch_stop)
execute(<<~SQL)
UPDATE timelogs
SET project_id = merge_requests.target_project_id
FROM merge_requests
WHERE merge_requests.id = timelogs.merge_request_id
AND timelogs.id BETWEEN #{batch_start} AND #{batch_stop}
AND timelogs.project_id IS NULL;
SQL
end
def execute(sql)
@connection ||= ::ActiveRecord::Base.connection
@connection.execute(sql)
end
end
end
end

View file

@ -292,7 +292,7 @@ semgrep-sast:
# SAST_ANALYZER_IMAGE is an undocumented variable used internally to allow QA to
# override the analyzer image with a custom value. This may be subject to change or
# breakage across GitLab releases.
SAST_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/semgrep:latest"
SAST_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/semgrep:$SAST_ANALYZER_IMAGE_TAG"
rules:
- if: $SAST_DISABLED
when: never

View file

@ -3,7 +3,7 @@
module Security
module CiConfiguration
class SastBuildAction < BaseBuildAction
SAST_DEFAULT_ANALYZERS = 'bandit, brakeman, eslint, flawfinder, gosec, kubesec, nodejs-scan, phpcs-security-audit, pmd-apex, security-code-scan, sobelow, spotbugs'
SAST_DEFAULT_ANALYZERS = 'bandit, brakeman, eslint, flawfinder, gosec, kubesec, nodejs-scan, phpcs-security-audit, pmd-apex, security-code-scan, semgrep, sobelow, spotbugs'
def initialize(auto_devops_enabled, params, existing_gitlab_ci_content)
super(auto_devops_enabled, existing_gitlab_ci_content)

View file

@ -277,6 +277,16 @@ FactoryBot.define do
end
end
trait :sast_minimal do
file_type { :sast }
file_format { :raw }
after(:build) do |artifact, _|
artifact.file = fixture_file_upload(
Rails.root.join('spec/fixtures/security_reports/master/gl-sast-report-minimal.json'), 'application/json')
end
end
trait :secret_detection do
file_type { :secret_detection }
file_format { :raw }

View file

@ -80,6 +80,12 @@ FactoryBot.define do
last_sign_in_ip { '127.0.0.1' }
end
trait :with_credit_card_validation do
after :create do |user|
create :credit_card_validation, user: user
end
end
trait :two_factor_via_otp do
before(:create) do |user|
user.otp_required_for_login = true

View file

@ -0,0 +1,9 @@
# frozen_string_literal: true
FactoryBot.define do
factory :credit_card_validation, class: 'Users::CreditCardValidation' do
user
credit_card_validated_at { Time.current }
end
end

View file

@ -0,0 +1,68 @@
{
"version": "14.0.0",
"vulnerabilities": [
{
"category": "sast",
"name": "Cipher with no integrity",
"message": "Cipher with no integrity",
"cve": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy:29:CIPHER_INTEGRITY",
"severity": "Medium",
"confidence": "High",
"scanner": {
"id": "find_sec_bugs",
"name": "Find Security Bugs"
},
"location": {
"file": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy",
"start_line": 29,
"end_line": 29,
"class": "com.gitlab.security_products.tests.App",
"method": "insecureCypher"
},
"identifiers": [
{
"type": "find_sec_bugs_type",
"name": "Find Security Bugs-CIPHER_INTEGRITY",
"value": "CIPHER_INTEGRITY",
"url": "https://find-sec-bugs.github.io/bugs.htm#CIPHER_INTEGRITY"
}
],
"tracking": {
"type": "source",
"items": [
{
"file": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy",
"start_line": 29,
"end_line": 29,
"signatures": [
{
"algorithm": "hash",
"value": "HASHVALUE"
},
{
"algorithm": "scope_offset",
"value": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy:App[0]:insecureCypher[0]:2"
}
]
}
]
}
}
],
"remediations": [],
"scan": {
"scanner": {
"id": "find_sec_bugs",
"name": "Find Security Bugs",
"url": "https://spotbugs.github.io",
"vendor": {
"name": "GitLab"
},
"version": "4.0.2"
},
"type": "sast",
"status": "success",
"start_time": "placeholder-value",
"end_time": "placeholder-value"
}
}

View file

@ -154,8 +154,8 @@
"items": [
{
"file": "groovy/src/main/java/com/gitlab/security_products/tests/App.groovy",
"start_line": 47,
"end_line": 47,
"start_line": 29,
"end_line": 29,
"signatures": [
{
"algorithm": "hash",

View file

@ -40,7 +40,7 @@ describe('AddContextCommitsModal', () => {
store,
propsData: {
contextCommitsPath: '',
targetBranch: 'master',
targetBranch: 'main',
mergeRequestIid: 1,
projectId: 1,
...props,

View file

@ -930,7 +930,7 @@ describe('Api', () => {
describe('createBranch', () => {
it('creates new branch', (done) => {
const ref = 'master';
const ref = 'main';
const branch = 'new-branch-name';
const dummyProjectPath = 'gitlab-org/gitlab-ce';
const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/projects/${encodeURIComponent(
@ -1262,7 +1262,7 @@ describe('Api', () => {
)}/merge_requests`;
const options = {
source_branch: 'feature',
target_branch: 'master',
target_branch: 'main',
title: 'Add feature',
};

View file

@ -16,7 +16,7 @@ function factory(initialState = {}) {
state: {
...createState(),
...initialState,
definitionPathPrefix: 'https://test.com/blob/master',
definitionPathPrefix: 'https://test.com/blob/main',
},
actions: {
fetchData,

View file

@ -12,11 +12,11 @@ describe('Code navigation mutations', () => {
it('sets initial data', () => {
mutations.SET_INITIAL_DATA(state, {
blobs: ['test'],
definitionPathPrefix: 'https://test.com/blob/master',
definitionPathPrefix: 'https://test.com/blob/main',
});
expect(state.blobs).toEqual(['test']);
expect(state.definitionPathPrefix).toBe('https://test.com/blob/master');
expect(state.definitionPathPrefix).toBe('https://test.com/blob/main');
});
});

View file

@ -10,7 +10,7 @@ describe('Commits List', () => {
beforeEach(() => {
setFixtures(`
<form class="commits-search-form" action="/h5bp/html5-boilerplate/commits/master">
<form class="commits-search-form" action="/h5bp/html5-boilerplate/commits/main">
<input id="commits-search">
</form>
<ol id="commits-list"></ol>
@ -59,7 +59,7 @@ describe('Commits List', () => {
jest.spyOn(window.history, 'replaceState').mockImplementation(() => {});
mock = new MockAdapter(axios);
mock.onGet('/h5bp/html5-boilerplate/commits/master').reply(200, {
mock.onGet('/h5bp/html5-boilerplate/commits/main').reply(200, {
html: '<li>Result</li>',
});

View file

@ -8,7 +8,7 @@ exports[`Contributors charts should render charts when loading completed and the
<h4
class="gl-mb-2 gl-mt-5"
>
Commits to master
Commits to main
</h4>
<span>

View file

@ -10,7 +10,7 @@ let mock;
let store;
const Component = Vue.extend(ContributorsCharts);
const endpoint = 'contributors';
const branch = 'master';
const branch = 'main';
const chartData = [
{ author_name: 'John', author_email: 'jawnnypoo@gmail.com', date: '2019-05-05' },
{ author_name: 'John', author_email: 'jawnnypoo@gmail.com', date: '2019-03-03' },

View file

@ -3,7 +3,7 @@ export const diffMetadata = {
size: 1,
branch_name: 'update-changelog',
source_branch_exists: true,
target_branch_name: 'master',
target_branch_name: 'main',
commit: null,
context_commits: null,
merge_request_diff: {

View file

@ -28,7 +28,7 @@ window.gl = window.gl || {};
gl.utils = gl.utils || {};
gl.utils.disableButtonIfEmptyField = () => {};
// the following test is unreliable and failing in master 2-3 times a day
// the following test is unreliable and failing in main 2-3 times a day
// see https://gitlab.com/gitlab-org/gitlab/issues/206906#note_290602581
// eslint-disable-next-line jest/no-disabled-tests
describe.skip('Old Notes (~/notes.js)', () => {

View file

@ -108,7 +108,7 @@ describe('Ref selector Vuex store mutations', () => {
const response = {
data: [
{
name: 'master',
name: 'main',
default: true,
// everything except "name" and "default" should be stripped
@ -130,7 +130,7 @@ describe('Ref selector Vuex store mutations', () => {
expect(state.matches.branches).toEqual({
list: [
{
name: 'master',
name: 'main',
default: true,
},
{

View file

@ -279,9 +279,7 @@ describe('Grouped test reports app', () => {
});
it('renders the recent failures count on the test case', () => {
expect(findIssueRecentFailures().text()).toBe(
'Failed 8 times in master in the last 14 days',
);
expect(findIssueRecentFailures().text()).toBe('Failed 8 times in main in the last 14 days');
});
});

View file

@ -52,7 +52,7 @@ describe('Reports Store Mutations', () => {
system_output: "Failure/Error: is_expected.to eq('gitlab')",
recent_failures: {
count: 4,
base_branch: 'master',
base_branch: 'main',
},
},
],

View file

@ -7,7 +7,7 @@ export const failedIssue = {
"Failure/Error: is_expected.to eq(3)\n\n expected: 3\n got: -1\n\n (compared using ==)\n./spec/test_spec.rb:12:in `block (4 levels) in \u003ctop (required)\u003e'",
recent_failures: {
count: 3,
base_branch: 'master',
base_branch: 'main',
},
};

View file

@ -12,7 +12,7 @@
"system_output": "Failure/Error: is_expected.to eq(3)\n\n expected: 3\n got: -1\n\n (compared using ==)\n./spec/test_spec.rb:12:in `block (4 levels) in <top (required)>'",
"recent_failures": {
"count": 8,
"base_branch": "master"
"base_branch": "main"
}
},
{
@ -38,7 +38,7 @@
"execution_time": 0.000562,
"recent_failures": {
"count": 3,
"base_branch": "master"
"base_branch": "main"
}
}
],

View file

@ -85,10 +85,14 @@ RSpec.describe LearnGitlabHelper do
it { is_expected.to eq(result) }
end
end
context 'when not signed in' do
it { is_expected.to eq(false) }
context 'when not signed in' do
before do
stub_experiment_for_subject(learn_gitlab_a: true, learn_gitlab_b: true)
end
it { is_expected.to eq(false) }
end
end
@ -118,10 +122,14 @@ RSpec.describe LearnGitlabHelper do
it { is_expected.to eq(result) }
end
end
context 'when not signed in' do
it { is_expected.to eq(nil) }
context 'when not signed in' do
before do
stub_experiment_for_subject(learn_gitlab_a: true, learn_gitlab_b: true)
end
it { is_expected.to eq(nil) }
end
end
end

View file

@ -0,0 +1,52 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::UpdateTimelogsProjectId, schema: 20210427212034 do
let!(:namespace) { table(:namespaces).create!(name: 'namespace', path: 'namespace') }
let!(:project1) { table(:projects).create!(namespace_id: namespace.id) }
let!(:project2) { table(:projects).create!(namespace_id: namespace.id) }
let!(:issue1) { table(:issues).create!(project_id: project1.id) }
let!(:issue2) { table(:issues).create!(project_id: project2.id) }
let!(:merge_request1) { table(:merge_requests).create!(target_project_id: project1.id, source_branch: 'master', target_branch: 'feature') }
let!(:merge_request2) { table(:merge_requests).create!(target_project_id: project2.id, source_branch: 'master', target_branch: 'feature') }
let!(:timelog1) { table(:timelogs).create!(issue_id: issue1.id, time_spent: 60) }
let!(:timelog2) { table(:timelogs).create!(issue_id: issue1.id, time_spent: 60) }
let!(:timelog3) { table(:timelogs).create!(issue_id: issue2.id, time_spent: 60) }
let!(:timelog4) { table(:timelogs).create!(merge_request_id: merge_request1.id, time_spent: 600) }
let!(:timelog5) { table(:timelogs).create!(merge_request_id: merge_request1.id, time_spent: 600) }
let!(:timelog6) { table(:timelogs).create!(merge_request_id: merge_request2.id, time_spent: 600) }
let!(:timelog7) { table(:timelogs).create!(issue_id: issue2.id, time_spent: 60, project_id: project1.id) }
let!(:timelog8) { table(:timelogs).create!(merge_request_id: merge_request2.id, time_spent: 600, project_id: project1.id) }
describe '#perform' do
context 'when timelogs belong to issues' do
it 'sets correct project_id' do
subject.perform(timelog1.id, timelog3.id)
expect(timelog1.reload.project_id).to eq(issue1.project_id)
expect(timelog2.reload.project_id).to eq(issue1.project_id)
expect(timelog3.reload.project_id).to eq(issue2.project_id)
end
end
context 'when timelogs belong to merge requests' do
it 'sets correct project ids' do
subject.perform(timelog4.id, timelog6.id)
expect(timelog4.reload.project_id).to eq(merge_request1.target_project_id)
expect(timelog5.reload.project_id).to eq(merge_request1.target_project_id)
expect(timelog6.reload.project_id).to eq(merge_request2.target_project_id)
end
end
context 'when timelogs already belong to projects' do
it 'does not update the project id' do
subject.perform(timelog7.id, timelog8.id)
expect(timelog7.reload.project_id).to eq(project1.id)
expect(timelog8.reload.project_id).to eq(project1.id)
end
end
end
end

View file

@ -0,0 +1,33 @@
# frozen_string_literal: true
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20210427212034_schedule_update_timelogs_project_id.rb')
RSpec.describe ScheduleUpdateTimelogsProjectId do
let!(:namespace) { table(:namespaces).create!(name: 'namespace', path: 'namespace') }
let!(:project) { table(:projects).create!(namespace_id: namespace.id) }
let!(:issue) { table(:issues).create!(project_id: project.id) }
let!(:merge_request) { table(:merge_requests).create!(target_project_id: project.id, source_branch: 'master', target_branch: 'feature') }
let!(:timelog1) { table(:timelogs).create!(issue_id: issue.id, time_spent: 60) }
let!(:timelog2) { table(:timelogs).create!(merge_request_id: merge_request.id, time_spent: 600) }
let!(:timelog3) { table(:timelogs).create!(merge_request_id: merge_request.id, time_spent: 60) }
let!(:timelog4) { table(:timelogs).create!(issue_id: issue.id, time_spent: 600) }
it 'correctly schedules background migrations' do
stub_const("#{described_class}::BATCH_SIZE", 2)
Sidekiq::Testing.fake! do
freeze_time do
migrate!
expect(described_class::MIGRATION)
.to be_scheduled_delayed_migration(2.minutes, timelog1.id, timelog2.id)
expect(described_class::MIGRATION)
.to be_scheduled_delayed_migration(4.minutes, timelog3.id, timelog4.id)
expect(BackgroundMigrationWorker.jobs.size).to eq(2)
end
end
end
end

View file

@ -0,0 +1,30 @@
# frozen_string_literal: true
require 'spec_helper'
require_migration!
RSpec.describe UpdateInvalidWebHooks do
let(:web_hooks) { table(:web_hooks) }
let(:groups) { table(:namespaces) }
let(:projects) { table(:projects) }
before do
group = groups.create!(name: 'gitlab', path: 'gitlab-org')
project = projects.create!(namespace_id: group.id)
web_hooks.create!(group_id: group.id, type: 'GroupHook')
web_hooks.create!(project_id: project.id, type: 'ProjectHook')
web_hooks.create!(group_id: group.id, project_id: project.id, type: 'ProjectHook')
end
it 'clears group_id when ProjectHook type and project_id are present', :aggregate_failures do
expect(web_hooks.where.not(group_id: nil).where.not(project_id: nil).count).to eq(1)
migrate!
expect(web_hooks.where.not(group_id: nil).where.not(project_id: nil).count).to eq(0)
expect(web_hooks.where(type: 'GroupHook').count).to eq(1)
expect(web_hooks.where(type: 'ProjectHook').count).to eq(2)
end
end

View file

@ -83,6 +83,7 @@ RSpec.describe User do
it { is_expected.to have_one(:user_detail) }
it { is_expected.to have_one(:atlassian_identity) }
it { is_expected.to have_one(:user_highest_role) }
it { is_expected.to have_one(:credit_card_validation) }
it { is_expected.to have_many(:snippets).dependent(:destroy) }
it { is_expected.to have_many(:members) }
it { is_expected.to have_many(:project_members) }
@ -1387,6 +1388,26 @@ RSpec.describe User do
end
end
describe '#credit_card_validated_at' do
let_it_be(:user) { create(:user) }
context 'when credit_card_validation does not exist' do
it 'returns nil' do
expect(user.credit_card_validated_at).to be nil
end
end
context 'when credit_card_validation exists' do
it 'returns the credit card validated time' do
credit_card_validated_time = Time.current - 1.day
create(:credit_card_validation, credit_card_validated_at: credit_card_validated_time, user: user)
expect(user.credit_card_validated_at).to eq(credit_card_validated_time)
end
end
end
describe '#update_tracked_fields!', :clean_gitlab_redis_shared_state do
let(:request) { OpenStruct.new(remote_ip: "127.0.0.1") }
let(:user) { create(:user) }
@ -5290,6 +5311,26 @@ RSpec.describe User do
end
end
describe 'user credit card validation' do
context 'when user is initialized' do
let(:user) { build(:user) }
it { expect(user.credit_card_validation).not_to be_present }
end
context 'when create user without credit card validation' do
let(:user) { create(:user) }
it { expect(user.credit_card_validation).not_to be_present }
end
context 'when user credit card validation exists' do
let(:user) { create(:user, :with_credit_card_validation) }
it { expect(user.credit_card_validation).to be_persisted }
end
end
describe 'user detail' do
context 'when user is initialized' do
let(:user) { build(:user) }

View file

@ -0,0 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Users::CreditCardValidation do
it { is_expected.to belong_to(:user) }
end