summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorSean McGivern <sean@gitlab.com>2019-07-03 10:28:13 +0100
committerSean McGivern <sean@gitlab.com>2019-07-03 10:28:13 +0100
commitb94daa35a4be584623792653b537d6ab68bdabdd (patch)
treecd620ef82dc85cec1cb401c7a5cf880c47418708 /spec
parent83330822d6ee3c59a057adeda35b23ab0021b63a (diff)
parentf90a7601c40c82bd230f9c014bc4f64744e77b5e (diff)
downloadgitlab-ce-b94daa35a4be584623792653b537d6ab68bdabdd.tar.gz
Merge branch 'master' into michel.engelen/gitlab-ce-issue/55953
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/projects/settings/repository_controller_spec.rb20
-rw-r--r--spec/factories/project_statistics.rb15
-rw-r--r--spec/factories/releases.rb1
-rw-r--r--spec/factories/services.rb34
-rw-r--r--spec/features/container_registry_spec.rb2
-rw-r--r--spec/features/groups/issues_spec.rb19
-rw-r--r--spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb2
-rw-r--r--spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb4
-rw-r--r--spec/features/projects/clusters/applications_spec.rb23
-rw-r--r--spec/features/projects/releases/user_views_releases_spec.rb12
-rw-r--r--spec/features/projects/settings/repository_settings_spec.rb6
-rw-r--r--spec/finders/releases_finder_spec.rb6
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/release.json4
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/release/release_for_guest.json4
-rw-r--r--spec/frontend/diffs/components/diff_discussion_reply_spec.js90
-rw-r--r--spec/frontend/diffs/components/diff_gutter_avatars_spec.js113
-rw-r--r--spec/frontend/diffs/mock_data/diff_discussions.js529
-rw-r--r--spec/frontend/lib/utils/text_utility_spec.js19
-rw-r--r--spec/frontend/notes/components/discussion_notes_spec.js2
-rw-r--r--spec/frontend/notes/components/discussion_reply_placeholder_spec.js14
-rw-r--r--spec/javascripts/diffs/components/diff_file_header_spec.js6
-rw-r--r--spec/javascripts/diffs/components/diff_gutter_avatars_spec.js146
-rw-r--r--spec/javascripts/diffs/components/inline_diff_view_spec.js3
-rw-r--r--spec/javascripts/diffs/store/actions_spec.js1
-rw-r--r--spec/javascripts/notes/components/noteable_discussion_spec.js6
-rw-r--r--spec/javascripts/notes/stores/actions_spec.js16
-rw-r--r--spec/javascripts/performance_bar/components/simple_metric_spec.js47
-rw-r--r--spec/javascripts/registry/components/app_spec.js33
-rw-r--r--spec/javascripts/releases/components/release_block_spec.js12
-rw-r--r--spec/lib/feature_spec.rb2
-rw-r--r--spec/lib/gitlab/auth_spec.rb9
-rw-r--r--spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb125
-rw-r--r--spec/lib/gitlab/background_migration/delete_conflicting_redirect_routes_range_spec.rb35
-rw-r--r--spec/lib/gitlab/background_migration/migrate_events_to_push_event_payloads_spec.rb433
-rw-r--r--spec/lib/gitlab/background_migration/migrate_stage_status_spec.rb92
-rw-r--r--spec/lib/gitlab/background_migration/normalize_ldap_extern_uids_range_spec.rb36
-rw-r--r--spec/lib/gitlab/background_migration/populate_fork_networks_range_spec.rb97
-rw-r--r--spec/lib/gitlab/background_migration/populate_merge_requests_latest_merge_request_diff_id_spec.rb62
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml2
-rw-r--r--spec/lib/gitlab/legacy_github_import/importer_spec.rb2
-rw-r--r--spec/lib/gitlab/legacy_github_import/release_formatter_spec.rb4
-rw-r--r--spec/migrations/add_foreign_key_to_merge_requests_spec.rb39
-rw-r--r--spec/migrations/add_head_pipeline_for_each_merge_request_spec.rb32
-rw-r--r--spec/migrations/backfill_and_add_not_null_constraint_to_released_at_column_on_releases_table_spec.rb28
-rw-r--r--spec/migrations/calculate_conv_dev_index_percentages_spec.rb59
-rw-r--r--spec/migrations/clean_appearance_symlinks_spec.rb46
-rw-r--r--spec/migrations/clean_stage_id_reference_migration_spec.rb34
-rw-r--r--spec/migrations/clean_stages_statuses_migration_spec.rb51
-rw-r--r--spec/migrations/clean_upload_symlinks_spec.rb46
-rw-r--r--spec/migrations/cleanup_move_system_upload_folder_symlink_spec.rb35
-rw-r--r--spec/migrations/cleanup_nonexisting_namespace_pending_delete_projects_spec.rb29
-rw-r--r--spec/migrations/convert_custom_notification_settings_to_columns_spec.rb120
-rw-r--r--spec/migrations/delete_conflicting_redirect_routes_spec.rb42
-rw-r--r--spec/migrations/fix_wrongly_renamed_routes_spec.rb86
-rw-r--r--spec/migrations/issues_moved_to_id_foreign_key_spec.rb24
-rw-r--r--spec/migrations/migrate_build_stage_reference_again_spec.rb62
-rw-r--r--spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb181
-rw-r--r--spec/migrations/migrate_issues_to_ghost_user_spec.rb51
-rw-r--r--spec/migrations/migrate_kubernetes_service_to_new_clusters_architectures_spec.rb312
-rw-r--r--spec/migrations/migrate_old_artifacts_spec.rb140
-rw-r--r--spec/migrations/migrate_pipeline_sidekiq_queues_spec.rb49
-rw-r--r--spec/migrations/migrate_pipeline_stages_spec.rb56
-rw-r--r--spec/migrations/migrate_process_commit_worker_jobs_spec.rb197
-rw-r--r--spec/migrations/migrate_stage_id_reference_in_background_spec.rb55
-rw-r--r--spec/migrations/migrate_stages_statuses_spec.rb68
-rw-r--r--spec/migrations/migrate_user_activities_to_users_last_activity_on_spec.rb49
-rw-r--r--spec/migrations/migrate_user_authentication_token_to_personal_access_token_spec.rb25
-rw-r--r--spec/migrations/migrate_user_project_view_spec.rb17
-rw-r--r--spec/migrations/move_personal_snippets_files_spec.rb197
-rw-r--r--spec/migrations/move_system_upload_folder_spec.rb80
-rw-r--r--spec/migrations/move_uploads_to_system_dir_spec.rb68
-rw-r--r--spec/migrations/normalize_ldap_extern_uids_spec.rb56
-rw-r--r--spec/migrations/populate_can_push_from_deploy_keys_projects_spec.rb43
-rw-r--r--spec/migrations/remove_assignee_id_from_issue_spec.rb37
-rw-r--r--spec/migrations/remove_dot_git_from_usernames_spec.rb58
-rw-r--r--spec/migrations/remove_duplicate_mr_events_spec.rb26
-rw-r--r--spec/migrations/remove_empty_fork_networks_spec.rb35
-rw-r--r--spec/migrations/rename_duplicated_variable_key_spec.rb34
-rw-r--r--spec/migrations/rename_more_reserved_project_names_spec.rb57
-rw-r--r--spec/migrations/rename_reserved_project_names_spec.rb61
-rw-r--r--spec/migrations/rename_users_with_renamed_namespace_spec.rb22
-rw-r--r--spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb64
-rw-r--r--spec/migrations/track_untracked_uploads_spec.rb15
-rw-r--r--spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb70
-rw-r--r--spec/migrations/update_legacy_diff_notes_type_for_import_spec.rb22
-rw-r--r--spec/migrations/update_notes_type_for_import_spec.rb22
-rw-r--r--spec/migrations/update_retried_for_ci_build_spec.rb17
-rw-r--r--spec/migrations/update_upload_paths_to_system_spec.rb59
-rw-r--r--spec/models/clusters/applications/ingress_spec.rb16
-rw-r--r--spec/models/deploy_token_spec.rb35
-rw-r--r--spec/models/namespace/aggregation_schedule_spec.rb73
-rw-r--r--spec/models/namespace/root_storage_statistics_spec.rb65
-rw-r--r--spec/models/namespace_spec.rb16
-rw-r--r--spec/models/project_services/bugzilla_service_spec.rb45
-rw-r--r--spec/models/project_services/custom_issue_tracker_service_spec.rb43
-rw-r--r--spec/models/project_services/gitlab_issue_tracker_service_spec.rb43
-rw-r--r--spec/models/project_services/jira_service_spec.rb138
-rw-r--r--spec/models/project_services/redmine_service_spec.rb43
-rw-r--r--spec/models/project_services/youtrack_service_spec.rb43
-rw-r--r--spec/models/release_spec.rb10
-rw-r--r--spec/models/service_spec.rb9
-rw-r--r--spec/requests/api/releases_spec.rb81
-rw-r--r--spec/requests/rack_attack_global_spec.rb43
-rw-r--r--spec/services/deploy_tokens/create_service_spec.rb16
-rw-r--r--spec/services/namespaces/statistics_refresher_service_spec.rb58
-rw-r--r--spec/support/features/discussion_comments_shared_example.rb2
-rw-r--r--spec/support/matchers/gitaly_matchers.rb2
-rw-r--r--spec/support/shared_examples/models/services_fields_shared_examples.rb31
-rw-r--r--spec/support/shared_examples/models/update_project_statistics_shared_examples.rb90
-rw-r--r--spec/workers/namespaces/prune_aggregation_schedules_worker_spec.rb35
-rw-r--r--spec/workers/namespaces/root_statistics_worker_spec.rb88
-rw-r--r--spec/workers/namespaces/schedule_aggregation_worker_spec.rb66
112 files changed, 2028 insertions, 4127 deletions
diff --git a/spec/controllers/projects/settings/repository_controller_spec.rb b/spec/controllers/projects/settings/repository_controller_spec.rb
index b34053fc993..7f67f67e775 100644
--- a/spec/controllers/projects/settings/repository_controller_spec.rb
+++ b/spec/controllers/projects/settings/repository_controller_spec.rb
@@ -32,4 +32,24 @@ describe Projects::Settings::RepositoryController do
expect(RepositoryCleanupWorker).to have_received(:perform_async).once
end
end
+
+ describe 'POST create_deploy_token' do
+ let(:deploy_token_params) do
+ {
+ name: 'deployer_token',
+ expires_at: 1.month.from_now.to_date.to_s,
+ username: 'deployer',
+ read_repository: '1'
+ }
+ end
+
+ subject(:create_deploy_token) { post :create_deploy_token, params: { namespace_id: project.namespace, project_id: project, deploy_token: deploy_token_params } }
+
+ it 'creates deploy token' do
+ expect { create_deploy_token }.to change { DeployToken.active.count }.by(1)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to render_template(:show)
+ end
+ end
end
diff --git a/spec/factories/project_statistics.rb b/spec/factories/project_statistics.rb
index 2d0f698475d..3d4174eb852 100644
--- a/spec/factories/project_statistics.rb
+++ b/spec/factories/project_statistics.rb
@@ -6,5 +6,20 @@ FactoryBot.define do
# statistics are automatically created when a project is created
project&.statistics || new
end
+
+ transient do
+ with_data { false }
+ size_multiplier { 1 }
+ end
+
+ after(:build) do |project_statistics, evaluator|
+ if evaluator.with_data
+ project_statistics.repository_size = evaluator.size_multiplier
+ project_statistics.wiki_size = evaluator.size_multiplier * 2
+ project_statistics.lfs_objects_size = evaluator.size_multiplier * 3
+ project_statistics.build_artifacts_size = evaluator.size_multiplier * 4
+ project_statistics.packages_size = evaluator.size_multiplier * 5
+ end
+ end
end
end
diff --git a/spec/factories/releases.rb b/spec/factories/releases.rb
index cab6b4a811f..4cacc77c182 100644
--- a/spec/factories/releases.rb
+++ b/spec/factories/releases.rb
@@ -6,6 +6,7 @@ FactoryBot.define do
description "Awesome release"
project
author
+ released_at { Time.zone.parse('2018-10-20T18:00:00Z') }
trait :legacy do
sha nil
diff --git a/spec/factories/services.rb b/spec/factories/services.rb
index 763909f30bd..ecb481ed84a 100644
--- a/spec/factories/services.rb
+++ b/spec/factories/services.rb
@@ -6,8 +6,6 @@ FactoryBot.define do
factory :custom_issue_tracker_service, class: CustomIssueTrackerService do
project
- type 'CustomIssueTrackerService'
- category 'issue_tracker'
active true
properties(
project_url: 'https://project.url.com',
@@ -54,6 +52,38 @@ FactoryBot.define do
)
end
+ factory :bugzilla_service do
+ project
+ active true
+ issue_tracker
+ end
+
+ factory :redmine_service do
+ project
+ active true
+ issue_tracker
+ end
+
+ factory :youtrack_service do
+ project
+ active true
+ issue_tracker
+ end
+
+ factory :gitlab_issue_tracker_service do
+ project
+ active true
+ issue_tracker
+ end
+
+ trait :issue_tracker do
+ properties(
+ project_url: 'http://issue-tracker.example.com',
+ issues_url: 'http://issue-tracker.example.com',
+ new_issue_url: 'http://issue-tracker.example.com'
+ )
+ end
+
factory :jira_cloud_service, class: JiraService do
project
active true
diff --git a/spec/features/container_registry_spec.rb b/spec/features/container_registry_spec.rb
index 21d97aba0c5..1b5943bd5d8 100644
--- a/spec/features/container_registry_spec.rb
+++ b/spec/features/container_registry_spec.rb
@@ -19,7 +19,7 @@ describe "Container Registry", :js do
it 'user visits container registry main page' do
visit_container_registry
- expect(page).to have_content 'No container images'
+ expect(page).to have_content 'no container images'
end
end
diff --git a/spec/features/groups/issues_spec.rb b/spec/features/groups/issues_spec.rb
index c000165ccd9..0ada530781c 100644
--- a/spec/features/groups/issues_spec.rb
+++ b/spec/features/groups/issues_spec.rb
@@ -150,6 +150,25 @@ describe 'Group issues page' do
check_issue_order
end
+ it 'issues should not be draggable when user is not logged in', :js do
+ sign_out(user_in_group)
+
+ visit issues_group_path(group, sort: 'relative_position')
+
+ drag_to(selector: '.manual-ordering',
+ from_index: 0,
+ to_index: 2)
+
+ wait_for_requests
+
+ # Issue order should remain the same
+ page.within('.manual-ordering') do
+ expect(find('.issue:nth-child(1) .title')).to have_content('Issue #1')
+ expect(find('.issue:nth-child(2) .title')).to have_content('Issue #2')
+ expect(find('.issue:nth-child(3) .title')).to have_content('Issue #3')
+ end
+ end
+
def check_issue_order
page.within('.manual-ordering') do
expect(find('.issue:nth-child(1) .title')).to have_content('Issue #2')
diff --git a/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb b/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb
index 0d946182c6f..1b1a31d0723 100644
--- a/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb
+++ b/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb
@@ -54,7 +54,7 @@ describe 'Resolve an open thread in a merge request by creating an issue', :js d
context 'creating the issue' do
before do
- find(resolve_discussion_selector).click
+ find(resolve_discussion_selector, match: :first).click
end
it 'has a hidden field for the thread' do
diff --git a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
index a6b846edd54..10fe60cb075 100644
--- a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
+++ b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
@@ -368,8 +368,8 @@ describe 'Merge request > User resolves diff notes and threads', :js do
all_discussion_replies = page.all('.discussion-reply-holder')
expect(all_discussion_replies.count).to eq(2)
- expect(all_discussion_replies.first.all('.discussion-next-btn').count).to eq(1)
- expect(all_discussion_replies.last.all('.discussion-next-btn').count).to eq(1)
+ expect(all_discussion_replies.first.all('.discussion-next-btn').count).to eq(2)
+ expect(all_discussion_replies.last.all('.discussion-next-btn').count).to eq(2)
end
it 'displays next thread even if hidden' do
diff --git a/spec/features/projects/clusters/applications_spec.rb b/spec/features/projects/clusters/applications_spec.rb
index 527508b3519..c75259d1b0c 100644
--- a/spec/features/projects/clusters/applications_spec.rb
+++ b/spec/features/projects/clusters/applications_spec.rb
@@ -21,8 +21,7 @@ describe 'Clusters Applications', :js do
it 'user is unable to install applications' do
page.within('.js-cluster-application-row-helm') do
- expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true')
- expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Install')
+ expect(page).to have_css('.js-cluster-application-install-button[disabled]', exact_text: 'Install')
end
end
end
@@ -53,19 +52,16 @@ describe 'Clusters Applications', :js do
it 'they see status transition' do
page.within('.js-cluster-application-row-helm') do
# FE sends request and gets the response, then the buttons is "Installing"
- expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true')
- expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installing')
+ expect(page).to have_css('.js-cluster-application-install-button[disabled]', exact_text: 'Installing')
Clusters::Cluster.last.application_helm.make_installing!
# FE starts polling and update the buttons to "Installing"
- expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true')
- expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installing')
+ expect(page).to have_css('.js-cluster-application-install-button[disabled]', exact_text: 'Installing')
Clusters::Cluster.last.application_helm.make_installed!
- expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true')
- expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installed')
+ expect(page).to have_css('.js-cluster-application-install-button[disabled]', exact_text: 'Installed')
end
expect(page).to have_content('Helm Tiller was successfully installed on your Kubernetes cluster')
@@ -212,26 +208,25 @@ describe 'Clusters Applications', :js do
it 'they see status transition' do
page.within('.js-cluster-application-row-ingress') do
# FE sends request and gets the response, then the buttons is "Installing"
- expect(page).to have_css('.js-cluster-application-install-button[disabled]')
- expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installing')
+ expect(page).to have_css('.js-cluster-application-install-button[disabled]', exact_text: 'Installing')
Clusters::Cluster.last.application_ingress.make_installing!
# FE starts polling and update the buttons to "Installing"
- expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installing')
- expect(page).to have_css('.js-cluster-application-install-button[disabled]')
+ expect(page).to have_css('.js-cluster-application-install-button[disabled]', exact_text: 'Installing')
# The application becomes installed but we keep waiting for external IP address
Clusters::Cluster.last.application_ingress.make_installed!
- expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installed')
- expect(page).to have_css('.js-cluster-application-install-button[disabled]')
+ expect(page).to have_css('.js-cluster-application-install-button[disabled]', exact_text: 'Installed')
expect(page).to have_selector('.js-no-endpoint-message')
expect(page).to have_selector('.js-ingress-ip-loading-icon')
# We receive the external IP address and display
Clusters::Cluster.last.application_ingress.update!(external_ip: '192.168.1.100')
+ expect(page).not_to have_css('.js-cluster-application-install-button')
+ expect(page).to have_css('.js-cluster-application-uninstall-button:not([disabled])', exact_text: 'Uninstall')
expect(page).not_to have_selector('.js-no-endpoint-message')
expect(page.find('.js-endpoint').value).to eq('192.168.1.100')
end
diff --git a/spec/features/projects/releases/user_views_releases_spec.rb b/spec/features/projects/releases/user_views_releases_spec.rb
index 317ffb6a2ff..725d7173bce 100644
--- a/spec/features/projects/releases/user_views_releases_spec.rb
+++ b/spec/features/projects/releases/user_views_releases_spec.rb
@@ -16,6 +16,7 @@ describe 'User views releases', :js do
expect(page).to have_content(release.name)
expect(page).to have_content(release.tag)
+ expect(page).not_to have_content('Upcoming Release')
end
context 'when there is a link as an asset' do
@@ -43,4 +44,15 @@ describe 'User views releases', :js do
end
end
end
+
+ context 'with an upcoming release' do
+ let(:tomorrow) { Time.zone.now + 1.day }
+ let!(:release) { create(:release, project: project, released_at: tomorrow ) }
+
+ it 'sees the upcoming tag' do
+ visit project_releases_path(project)
+
+ expect(page).to have_content('Upcoming Release')
+ end
+ end
end
diff --git a/spec/features/projects/settings/repository_settings_spec.rb b/spec/features/projects/settings/repository_settings_spec.rb
index 8c7bc192c50..1edfee705c8 100644
--- a/spec/features/projects/settings/repository_settings_spec.rb
+++ b/spec/features/projects/settings/repository_settings_spec.rb
@@ -112,11 +112,17 @@ describe 'Projects > Settings > Repository settings' do
it 'add a new deploy token' do
fill_in 'deploy_token_name', with: 'new_deploy_key'
fill_in 'deploy_token_expires_at', with: (Date.today + 1.month).to_s
+ fill_in 'deploy_token_username', with: 'deployer'
check 'deploy_token_read_repository'
check 'deploy_token_read_registry'
click_button 'Create deploy token'
expect(page).to have_content('Your new project deploy token has been created')
+
+ within('.created-deploy-token-container') do
+ expect(page).to have_selector("input[name='deploy-token-user'][value='deployer']")
+ expect(page).to have_selector("input[name='deploy-token'][readonly='readonly']")
+ end
end
end
diff --git a/spec/finders/releases_finder_spec.rb b/spec/finders/releases_finder_spec.rb
index 32ee15134a2..5ffb8c74bf5 100644
--- a/spec/finders/releases_finder_spec.rb
+++ b/spec/finders/releases_finder_spec.rb
@@ -12,8 +12,8 @@ describe ReleasesFinder do
subject { described_class.new(project, user)}
before do
- v1_0_0.update_attribute(:created_at, 2.days.ago)
- v1_1_0.update_attribute(:created_at, 1.day.ago)
+ v1_0_0.update_attribute(:released_at, 2.days.ago)
+ v1_1_0.update_attribute(:released_at, 1.day.ago)
end
describe '#execute' do
@@ -30,7 +30,7 @@ describe ReleasesFinder do
project.add_developer(user)
end
- it 'sorts by creation date' do
+ it 'sorts by release date' do
releases = subject.execute
expect(releases).to be_present
diff --git a/spec/fixtures/api/schemas/public_api/v4/release.json b/spec/fixtures/api/schemas/public_api/v4/release.json
index 6ea0781c1ed..ec3fa59cdb1 100644
--- a/spec/fixtures/api/schemas/public_api/v4/release.json
+++ b/spec/fixtures/api/schemas/public_api/v4/release.json
@@ -1,12 +1,14 @@
{
"type": "object",
- "required": ["name", "tag_name", "commit"],
+ "required": ["name", "tag_name", "commit", "released_at"],
"properties": {
"name": { "type": "string" },
"tag_name": { "type": "string" },
"description": { "type": "string" },
"description_html": { "type": "string" },
"created_at": { "type": "date" },
+ "released_at": { "type": "date" },
+ "upcoming_release": { "type": "boolean" },
"commit": {
"oneOf": [{ "type": "null" }, { "$ref": "commit/basic.json" }]
},
diff --git a/spec/fixtures/api/schemas/public_api/v4/release/release_for_guest.json b/spec/fixtures/api/schemas/public_api/v4/release/release_for_guest.json
index e78398ad1d5..0c1e8fd5fb3 100644
--- a/spec/fixtures/api/schemas/public_api/v4/release/release_for_guest.json
+++ b/spec/fixtures/api/schemas/public_api/v4/release/release_for_guest.json
@@ -1,11 +1,13 @@
{
"type": "object",
- "required": ["name"],
+ "required": ["name", "released_at"],
"properties": {
"name": { "type": "string" },
"description": { "type": "string" },
"description_html": { "type": "string" },
"created_at": { "type": "date" },
+ "released_at": { "type": "date" },
+ "upcoming_release": { "type": "boolean" },
"author": {
"oneOf": [{ "type": "null" }, { "$ref": "../user/basic.json" }]
},
diff --git a/spec/frontend/diffs/components/diff_discussion_reply_spec.js b/spec/frontend/diffs/components/diff_discussion_reply_spec.js
new file mode 100644
index 00000000000..28689ab07de
--- /dev/null
+++ b/spec/frontend/diffs/components/diff_discussion_reply_spec.js
@@ -0,0 +1,90 @@
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import Vuex from 'vuex';
+import DiffDiscussionReply from '~/diffs/components/diff_discussion_reply.vue';
+import ReplyPlaceholder from '~/notes/components/discussion_reply_placeholder.vue';
+import NoteSignedOutWidget from '~/notes/components/note_signed_out_widget.vue';
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+describe('DiffDiscussionReply', () => {
+ let wrapper;
+ let getters;
+ let store;
+
+ const createComponent = (props = {}, slots = {}) => {
+ wrapper = shallowMount(DiffDiscussionReply, {
+ store,
+ localVue,
+ sync: false,
+ propsData: {
+ ...props,
+ },
+ slots: {
+ ...slots,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('if user can reply', () => {
+ beforeEach(() => {
+ getters = {
+ userCanReply: () => true,
+ getUserData: () => ({
+ path: 'test-path',
+ avatar_url: 'avatar_url',
+ name: 'John Doe',
+ }),
+ };
+
+ store = new Vuex.Store({
+ getters,
+ });
+ });
+
+ it('should render a form if component has form', () => {
+ createComponent(
+ {
+ renderReplyPlaceholder: false,
+ hasForm: true,
+ },
+ {
+ form: `<div id="test-form"></div>`,
+ },
+ );
+
+ expect(wrapper.find('#test-form').exists()).toBe(true);
+ });
+
+ it('should render a reply placeholder if there is no form', () => {
+ createComponent({
+ renderReplyPlaceholder: true,
+ hasForm: false,
+ });
+
+ expect(wrapper.find(ReplyPlaceholder).exists()).toBe(true);
+ });
+ });
+
+ it('renders a signed out widget when user is not logged in', () => {
+ getters = {
+ userCanReply: () => false,
+ getUserData: () => null,
+ };
+
+ store = new Vuex.Store({
+ getters,
+ });
+
+ createComponent({
+ renderReplyPlaceholder: false,
+ hasForm: false,
+ });
+
+ expect(wrapper.find(NoteSignedOutWidget).exists()).toBe(true);
+ });
+});
diff --git a/spec/frontend/diffs/components/diff_gutter_avatars_spec.js b/spec/frontend/diffs/components/diff_gutter_avatars_spec.js
new file mode 100644
index 00000000000..48ee5c63f35
--- /dev/null
+++ b/spec/frontend/diffs/components/diff_gutter_avatars_spec.js
@@ -0,0 +1,113 @@
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import DiffGutterAvatars from '~/diffs/components/diff_gutter_avatars.vue';
+import discussionsMockData from '../mock_data/diff_discussions';
+
+const localVue = createLocalVue();
+const getDiscussionsMockData = () => [Object.assign({}, discussionsMockData)];
+
+describe('DiffGutterAvatars', () => {
+ let wrapper;
+
+ const findCollapseButton = () => wrapper.find('.diff-notes-collapse');
+ const findMoreCount = () => wrapper.find('.diff-comments-more-count');
+ const findUserAvatars = () => wrapper.findAll('.diff-comment-avatar');
+
+ const createComponent = (props = {}) => {
+ wrapper = shallowMount(DiffGutterAvatars, {
+ localVue,
+ sync: false,
+ propsData: {
+ ...props,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('when expanded', () => {
+ beforeEach(() => {
+ createComponent({
+ discussions: getDiscussionsMockData(),
+ discussionsExpanded: true,
+ });
+ });
+
+ it('renders a collapse button when discussions are expanded', () => {
+ expect(findCollapseButton().exists()).toBe(true);
+ });
+
+ it('should emit toggleDiscussions event on button click', () => {
+ findCollapseButton().trigger('click');
+
+ expect(wrapper.emitted().toggleLineDiscussions).toBeTruthy();
+ });
+ });
+
+ describe('when collapsed', () => {
+ beforeEach(() => {
+ createComponent({
+ discussions: getDiscussionsMockData(),
+ discussionsExpanded: false,
+ });
+ });
+
+ it('renders user avatars and moreCount text', () => {
+ expect(findUserAvatars().exists()).toBe(true);
+ expect(findMoreCount().exists()).toBe(true);
+ });
+
+ it('renders correct amount of user avatars', () => {
+ expect(findUserAvatars().length).toBe(3);
+ });
+
+ it('renders correct moreCount number', () => {
+ expect(findMoreCount().text()).toBe('+2');
+ });
+
+ it('should emit toggleDiscussions event on avatars click', () => {
+ findUserAvatars()
+ .at(0)
+ .trigger('click');
+
+ expect(wrapper.emitted().toggleLineDiscussions).toBeTruthy();
+ });
+
+ it('should emit toggleDiscussions event on more count text click', () => {
+ findMoreCount().trigger('click');
+
+ expect(wrapper.emitted().toggleLineDiscussions).toBeTruthy();
+ });
+ });
+
+ it('renders an empty more count string if there are no discussions', () => {
+ createComponent({
+ discussions: [],
+ discussionsExpanded: false,
+ });
+
+ expect(findMoreCount().exists()).toBe(false);
+ });
+
+ describe('tooltip text', () => {
+ beforeEach(() => {
+ createComponent({
+ discussions: getDiscussionsMockData(),
+ discussionsExpanded: false,
+ });
+ });
+
+ it('returns original comment if it is shorter than max length', () => {
+ const note = wrapper.vm.discussions[0].notes[0];
+
+ expect(wrapper.vm.getTooltipText(note)).toEqual('Administrator: comment 1');
+ });
+
+ it('returns truncated version of comment if it is longer than max length', () => {
+ const note = wrapper.vm.discussions[0].notes[1];
+
+ expect(wrapper.vm.getTooltipText(note)).toEqual('Fatih Acet: comment 2 is r...');
+ });
+ });
+});
diff --git a/spec/frontend/diffs/mock_data/diff_discussions.js b/spec/frontend/diffs/mock_data/diff_discussions.js
new file mode 100644
index 00000000000..711ab543411
--- /dev/null
+++ b/spec/frontend/diffs/mock_data/diff_discussions.js
@@ -0,0 +1,529 @@
+export default {
+ id: '6b232e05bea388c6b043ccc243ba505faac04ea8',
+ reply_id: '6b232e05bea388c6b043ccc243ba505faac04ea8',
+ position: {
+ old_line: null,
+ new_line: 2,
+ old_path: 'CHANGELOG',
+ new_path: 'CHANGELOG',
+ base_sha: 'e63f41fe459e62e1228fcef60d7189127aeba95a',
+ start_sha: 'd9eaefe5a676b820c57ff18cf5b68316025f7962',
+ head_sha: 'c48ee0d1bf3b30453f5b32250ce03134beaa6d13',
+ },
+ line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_2',
+ expanded: true,
+ notes: [
+ {
+ id: '1749',
+ type: 'DiffNote',
+ attachment: null,
+ author: {
+ id: 1,
+ name: 'Administrator',
+ username: 'root',
+ state: 'active',
+ avatar_url:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ path: '/root',
+ },
+ created_at: '2018-04-03T21:06:21.521Z',
+ updated_at: '2018-04-08T08:50:41.762Z',
+ system: false,
+ noteable_id: 51,
+ noteable_type: 'MergeRequest',
+ noteable_iid: 20,
+ human_access: 'Owner',
+ note: 'comment 1',
+ note_html: '<p dir="auto">comment 1</p>',
+ last_edited_at: '2018-04-08T08:50:41.762Z',
+ last_edited_by: {
+ id: 1,
+ name: 'Administrator',
+ username: 'root',
+ state: 'active',
+ avatar_url:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ path: '/root',
+ },
+ current_user: {
+ can_edit: true,
+ can_award_emoji: true,
+ },
+ resolved: false,
+ resolvable: true,
+ resolved_by: null,
+ discussion_id: '6b232e05bea388c6b043ccc243ba505faac04ea8',
+ emoji_awardable: true,
+ award_emoji: [],
+ toggle_award_path: '/gitlab-org/gitlab-test/notes/1749/toggle_award_emoji',
+ report_abuse_path:
+ '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-test%2Fmerge_requests%2F20%23note_1749&user_id=1',
+ path: '/gitlab-org/gitlab-test/notes/1749',
+ noteable_note_url: 'http://localhost:3000/gitlab-org/gitlab-test/merge_requests/20#note_1749',
+ resolve_path:
+ '/gitlab-org/gitlab-test/merge_requests/20/discussions/6b232e05bea388c6b043ccc243ba505faac04ea8/resolve',
+ resolve_with_issue_path:
+ '/gitlab-org/gitlab-test/issues/new?discussion_to_resolve=6b232e05bea388c6b043ccc243ba505faac04ea8&merge_request_to_resolve_discussions_of=20',
+ },
+ {
+ id: '1753',
+ type: 'DiffNote',
+ attachment: null,
+ author: {
+ id: 1,
+ name: 'Fatih Acet',
+ username: 'fatihacet',
+ state: 'active',
+ avatar_url:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ path: '/fatihacevt',
+ },
+ created_at: '2018-04-08T08:49:35.804Z',
+ updated_at: '2018-04-08T08:50:45.915Z',
+ system: false,
+ noteable_id: 51,
+ noteable_type: 'MergeRequest',
+ noteable_iid: 20,
+ human_access: 'Owner',
+ note: 'comment 2 is really long one',
+ note_html: '<p dir="auto">comment 2 is really long one</p>',
+ last_edited_at: '2018-04-08T08:50:45.915Z',
+ last_edited_by: {
+ id: 1,
+ name: 'Administrator',
+ username: 'root',
+ state: 'active',
+ avatar_url:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ path: '/root',
+ },
+ current_user: {
+ can_edit: true,
+ can_award_emoji: true,
+ },
+ resolved: false,
+ resolvable: true,
+ resolved_by: null,
+ discussion_id: '6b232e05bea388c6b043ccc243ba505faac04ea8',
+ emoji_awardable: true,
+ award_emoji: [],
+ toggle_award_path: '/gitlab-org/gitlab-test/notes/1753/toggle_award_emoji',
+ report_abuse_path:
+ '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-test%2Fmerge_requests%2F20%23note_1753&user_id=1',
+ path: '/gitlab-org/gitlab-test/notes/1753',
+ noteable_note_url: 'http://localhost:3000/gitlab-org/gitlab-test/merge_requests/20#note_1753',
+ resolve_path:
+ '/gitlab-org/gitlab-test/merge_requests/20/discussions/6b232e05bea388c6b043ccc243ba505faac04ea8/resolve',
+ resolve_with_issue_path:
+ '/gitlab-org/gitlab-test/issues/new?discussion_to_resolve=6b232e05bea388c6b043ccc243ba505faac04ea8&merge_request_to_resolve_discussions_of=20',
+ },
+ {
+ id: '1754',
+ type: 'DiffNote',
+ attachment: null,
+ author: {
+ id: 1,
+ name: 'Administrator',
+ username: 'root',
+ state: 'active',
+ avatar_url:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ path: '/root',
+ },
+ created_at: '2018-04-08T08:50:48.294Z',
+ updated_at: '2018-04-08T08:50:48.294Z',
+ system: false,
+ noteable_id: 51,
+ noteable_type: 'MergeRequest',
+ noteable_iid: 20,
+ human_access: 'Owner',
+ note: 'comment 3',
+ note_html: '<p dir="auto">comment 3</p>',
+ current_user: {
+ can_edit: true,
+ can_award_emoji: true,
+ },
+ resolved: false,
+ resolvable: true,
+ resolved_by: null,
+ discussion_id: '6b232e05bea388c6b043ccc243ba505faac04ea8',
+ emoji_awardable: true,
+ award_emoji: [],
+ toggle_award_path: '/gitlab-org/gitlab-test/notes/1754/toggle_award_emoji',
+ report_abuse_path:
+ '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-test%2Fmerge_requests%2F20%23note_1754&user_id=1',
+ path: '/gitlab-org/gitlab-test/notes/1754',
+ noteable_note_url: 'http://localhost:3000/gitlab-org/gitlab-test/merge_requests/20#note_1754',
+ resolve_path:
+ '/gitlab-org/gitlab-test/merge_requests/20/discussions/6b232e05bea388c6b043ccc243ba505faac04ea8/resolve',
+ resolve_with_issue_path:
+ '/gitlab-org/gitlab-test/issues/new?discussion_to_resolve=6b232e05bea388c6b043ccc243ba505faac04ea8&merge_request_to_resolve_discussions_of=20',
+ },
+ {
+ id: '1755',
+ type: 'DiffNote',
+ attachment: null,
+ author: {
+ id: 1,
+ name: 'Administrator',
+ username: 'root',
+ state: 'active',
+ avatar_url:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ path: '/root',
+ },
+ created_at: '2018-04-08T08:50:50.911Z',
+ updated_at: '2018-04-08T08:50:50.911Z',
+ system: false,
+ noteable_id: 51,
+ noteable_type: 'MergeRequest',
+ noteable_iid: 20,
+ human_access: 'Owner',
+ note: 'comment 4',
+ note_html: '<p dir="auto">comment 4</p>',
+ current_user: {
+ can_edit: true,
+ can_award_emoji: true,
+ },
+ resolved: false,
+ resolvable: true,
+ resolved_by: null,
+ discussion_id: '6b232e05bea388c6b043ccc243ba505faac04ea8',
+ emoji_awardable: true,
+ award_emoji: [],
+ toggle_award_path: '/gitlab-org/gitlab-test/notes/1755/toggle_award_emoji',
+ report_abuse_path:
+ '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-test%2Fmerge_requests%2F20%23note_1755&user_id=1',
+ path: '/gitlab-org/gitlab-test/notes/1755',
+ noteable_note_url: 'http://localhost:3000/gitlab-org/gitlab-test/merge_requests/20#note_1755',
+ resolve_path:
+ '/gitlab-org/gitlab-test/merge_requests/20/discussions/6b232e05bea388c6b043ccc243ba505faac04ea8/resolve',
+ resolve_with_issue_path:
+ '/gitlab-org/gitlab-test/issues/new?discussion_to_resolve=6b232e05bea388c6b043ccc243ba505faac04ea8&merge_request_to_resolve_discussions_of=20',
+ },
+ {
+ id: '1756',
+ type: 'DiffNote',
+ attachment: null,
+ author: {
+ id: 1,
+ name: 'Administrator',
+ username: 'root',
+ state: 'active',
+ avatar_url:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ path: '/root',
+ },
+ created_at: '2018-04-08T08:50:53.895Z',
+ updated_at: '2018-04-08T08:50:53.895Z',
+ system: false,
+ noteable_id: 51,
+ noteable_type: 'MergeRequest',
+ noteable_iid: 20,
+ human_access: 'Owner',
+ note: 'comment 5',
+ note_html: '<p dir="auto">comment 5</p>',
+ current_user: {
+ can_edit: true,
+ can_award_emoji: true,
+ },
+ resolved: false,
+ resolvable: true,
+ resolved_by: null,
+ discussion_id: '6b232e05bea388c6b043ccc243ba505faac04ea8',
+ emoji_awardable: true,
+ award_emoji: [],
+ toggle_award_path: '/gitlab-org/gitlab-test/notes/1756/toggle_award_emoji',
+ report_abuse_path:
+ '/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-test%2Fmerge_requests%2F20%23note_1756&user_id=1',
+ path: '/gitlab-org/gitlab-test/notes/1756',
+ noteable_note_url: 'http://localhost:3000/gitlab-org/gitlab-test/merge_requests/20#note_1756',
+ resolve_path:
+ '/gitlab-org/gitlab-test/merge_requests/20/discussions/6b232e05bea388c6b043ccc243ba505faac04ea8/resolve',
+ resolve_with_issue_path:
+ '/gitlab-org/gitlab-test/issues/new?discussion_to_resolve=6b232e05bea388c6b043ccc243ba505faac04ea8&merge_request_to_resolve_discussions_of=20',
+ },
+ ],
+ individual_note: false,
+ resolvable: true,
+ resolved: false,
+ resolve_path:
+ '/gitlab-org/gitlab-test/merge_requests/20/discussions/6b232e05bea388c6b043ccc243ba505faac04ea8/resolve',
+ resolve_with_issue_path:
+ '/gitlab-org/gitlab-test/issues/new?discussion_to_resolve=6b232e05bea388c6b043ccc243ba505faac04ea8&merge_request_to_resolve_discussions_of=20',
+ diff_file: {
+ submodule: false,
+ submodule_link: null,
+ blob: {
+ id: '9e10516ca50788acf18c518a231914a21e5f16f7',
+ path: 'CHANGELOG',
+ name: 'CHANGELOG',
+ mode: '100644',
+ readable_text: true,
+ icon: 'file-text-o',
+ },
+ blob_path: 'CHANGELOG',
+ blob_name: 'CHANGELOG',
+ blob_icon: '<i aria-hidden="true" data-hidden="true" class="fa fa-file-text-o fa-fw"></i>',
+ file_hash: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a',
+ file_path: 'CHANGELOG.rb',
+ new_file: false,
+ deleted_file: false,
+ renamed_file: false,
+ old_path: 'CHANGELOG',
+ new_path: 'CHANGELOG',
+ mode_changed: false,
+ a_mode: '100644',
+ b_mode: '100644',
+ text: true,
+ added_lines: 2,
+ removed_lines: 0,
+ diff_refs: {
+ base_sha: 'e63f41fe459e62e1228fcef60d7189127aeba95a',
+ start_sha: 'd9eaefe5a676b820c57ff18cf5b68316025f7962',
+ head_sha: 'c48ee0d1bf3b30453f5b32250ce03134beaa6d13',
+ },
+ content_sha: 'c48ee0d1bf3b30453f5b32250ce03134beaa6d13',
+ stored_externally: null,
+ external_storage: null,
+ old_path_html: 'CHANGELOG_OLD',
+ new_path_html: 'CHANGELOG',
+ is_fully_expanded: true,
+ context_lines_path:
+ '/gitlab-org/gitlab-test/blob/c48ee0d1bf3b30453f5b32250ce03134beaa6d13/CHANGELOG/diff',
+ highlighted_diff_lines: [
+ {
+ line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_1',
+ type: 'new',
+ old_line: null,
+ new_line: 1,
+ text: '<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n',
+ rich_text: '<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n',
+ meta_data: null,
+ },
+ {
+ line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_2',
+ type: 'new',
+ old_line: null,
+ new_line: 2,
+ text: '<span id="LC2" class="line" lang="plaintext"></span>\n',
+ rich_text: '<span id="LC2" class="line" lang="plaintext"></span>\n',
+ meta_data: null,
+ },
+ {
+ line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3',
+ type: null,
+ old_line: 1,
+ new_line: 3,
+ text: '<span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n',
+ rich_text: '<span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n',
+ meta_data: null,
+ },
+ {
+ line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_2_4',
+ type: null,
+ old_line: 2,
+ new_line: 4,
+ text: '<span id="LC4" class="line" lang="plaintext"></span>\n',
+ rich_text: '<span id="LC4" class="line" lang="plaintext"></span>\n',
+ meta_data: null,
+ },
+ {
+ line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_3_5',
+ type: null,
+ old_line: 3,
+ new_line: 5,
+ text: ' <span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n',
+ rich_text: ' <span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n',
+ meta_data: null,
+ },
+ {
+ line_code: null,
+ type: 'match',
+ old_line: null,
+ new_line: null,
+ text: '',
+ rich_text: '',
+ meta_data: {
+ old_pos: 3,
+ new_pos: 5,
+ },
+ },
+ {
+ line_code: null,
+ type: 'match',
+ old_line: null,
+ new_line: null,
+ text: '',
+ rich_text: '',
+ meta_data: {
+ old_pos: 3,
+ new_pos: 5,
+ },
+ },
+ {
+ line_code: null,
+ type: 'match',
+ old_line: null,
+ new_line: null,
+ text: '',
+ rich_text: '',
+ meta_data: {
+ old_pos: 3,
+ new_pos: 5,
+ },
+ },
+ ],
+ parallel_diff_lines: [
+ {
+ left: null,
+ right: {
+ line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_1',
+ type: 'new',
+ old_line: null,
+ new_line: 1,
+ text: '<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n',
+ rich_text: '<span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n',
+ meta_data: null,
+ },
+ },
+ {
+ left: null,
+ right: {
+ line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_2',
+ type: 'new',
+ old_line: null,
+ new_line: 2,
+ text: '<span id="LC2" class="line" lang="plaintext"></span>\n',
+ rich_text: '<span id="LC2" class="line" lang="plaintext"></span>\n',
+ meta_data: null,
+ },
+ },
+ {
+ left: {
+ line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3',
+ type: null,
+ old_line: 1,
+ new_line: 3,
+ text: '<span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n',
+ rich_text: '<span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n',
+ meta_data: null,
+ },
+ right: {
+ line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_1_3',
+ type: null,
+ old_line: 1,
+ new_line: 3,
+ text: '<span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n',
+ rich_text: '<span id="LC3" class="line" lang="plaintext">v6.8.0</span>\n',
+ meta_data: null,
+ },
+ },
+ {
+ left: {
+ line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_2_4',
+ type: null,
+ old_line: 2,
+ new_line: 4,
+ text: '<span id="LC4" class="line" lang="plaintext"></span>\n',
+ rich_text: '<span id="LC4" class="line" lang="plaintext"></span>\n',
+ meta_data: null,
+ },
+ right: {
+ line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_2_4',
+ type: null,
+ old_line: 2,
+ new_line: 4,
+ text: '<span id="LC4" class="line" lang="plaintext"></span>\n',
+ rich_text: '<span id="LC4" class="line" lang="plaintext"></span>\n',
+ meta_data: null,
+ },
+ },
+ {
+ left: {
+ line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_3_5',
+ type: null,
+ old_line: 3,
+ new_line: 5,
+ text: ' <span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n',
+ rich_text: ' <span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n',
+ meta_data: null,
+ },
+ right: {
+ line_code: '1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a_3_5',
+ type: null,
+ old_line: 3,
+ new_line: 5,
+ text: ' <span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n',
+ rich_text: ' <span id="LC5" class="line" lang="plaintext">v6.7.0</span>\n',
+ meta_data: null,
+ },
+ },
+ {
+ left: {
+ line_code: null,
+ type: 'match',
+ old_line: null,
+ new_line: null,
+ text: '',
+ rich_text: '',
+ meta_data: {
+ old_pos: 3,
+ new_pos: 5,
+ },
+ },
+ right: {
+ line_code: null,
+ type: 'match',
+ old_line: null,
+ new_line: null,
+ text: '',
+ rich_text: '',
+ meta_data: {
+ old_pos: 3,
+ new_pos: 5,
+ },
+ },
+ },
+ ],
+ viewer: {
+ name: 'text',
+ error: null,
+ },
+ },
+ diff_discussion: true,
+ truncated_diff_lines: [
+ {
+ text: 'line',
+ rich_text:
+ '<tr class="line_holder new" id="">\n<td class="diff-line-num new old_line" data-linenumber="1">\n \n</td>\n<td class="diff-line-num new new_line" data-linenumber="1">\n1\n</td>\n<td class="line_content new"><span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n</td>\n</tr>\n<tr class="line_holder new" id="">\n<td class="diff-line-num new old_line" data-linenumber="1">\n \n</td>\n<td class="diff-line-num new new_line" data-linenumber="2">\n2\n</td>\n<td class="line_content new"><span id="LC2" class="line" lang="plaintext"></span>\n</td>\n</tr>\n',
+ can_receive_suggestion: true,
+ line_code: '6f209374f7e565f771b95720abf46024c41d1885_1_1',
+ type: 'new',
+ old_line: null,
+ new_line: 1,
+ meta_data: null,
+ },
+ ],
+};
+
+export const imageDiffDiscussions = [
+ {
+ id: '1',
+ position: {
+ x: 10,
+ y: 10,
+ width: 100,
+ height: 200,
+ },
+ },
+ {
+ id: '2',
+ position: {
+ x: 5,
+ y: 5,
+ width: 100,
+ height: 200,
+ },
+ },
+];
diff --git a/spec/frontend/lib/utils/text_utility_spec.js b/spec/frontend/lib/utils/text_utility_spec.js
index 9e920d59093..dc886d0db3b 100644
--- a/spec/frontend/lib/utils/text_utility_spec.js
+++ b/spec/frontend/lib/utils/text_utility_spec.js
@@ -55,9 +55,24 @@ describe('text_utility', () => {
});
});
- describe('slugifyWithHyphens', () => {
+ describe('slugify', () => {
+ it('should remove accents and convert to lower case', () => {
+ expect(textUtils.slugify('João')).toEqual('jo-o');
+ });
it('should replaces whitespaces with hyphens and convert to lower case', () => {
- expect(textUtils.slugifyWithHyphens('My Input String')).toEqual('my-input-string');
+ expect(textUtils.slugify('My Input String')).toEqual('my-input-string');
+ });
+ it('should remove trailing whitespace and replace whitespaces within string with a hyphen', () => {
+ expect(textUtils.slugify(' a new project ')).toEqual('a-new-project');
+ });
+ it('should only remove non-allowed special characters', () => {
+ expect(textUtils.slugify('test!_pro-ject~')).toEqual('test-_pro-ject-');
+ });
+ it('should squash multiple hypens', () => {
+ expect(textUtils.slugify('test!!!!_pro-ject~')).toEqual('test-_pro-ject-');
+ });
+ it('should return empty string if only non-allowed characters', () => {
+ expect(textUtils.slugify('здрасти')).toEqual('');
});
});
diff --git a/spec/frontend/notes/components/discussion_notes_spec.js b/spec/frontend/notes/components/discussion_notes_spec.js
index 394666403ee..58d367077e8 100644
--- a/spec/frontend/notes/components/discussion_notes_spec.js
+++ b/spec/frontend/notes/components/discussion_notes_spec.js
@@ -87,7 +87,7 @@ describe('DiscussionNotes', () => {
discussion.notes[0],
];
discussion.notes = notesData;
- createComponent({ discussion });
+ createComponent({ discussion, shouldRenderDiffs: true });
const notes = wrapper.findAll('.notes > li');
expect(notes.at(0).is(PlaceholderSystemNote)).toBe(true);
diff --git a/spec/frontend/notes/components/discussion_reply_placeholder_spec.js b/spec/frontend/notes/components/discussion_reply_placeholder_spec.js
index 07a366cf339..e008f4ed093 100644
--- a/spec/frontend/notes/components/discussion_reply_placeholder_spec.js
+++ b/spec/frontend/notes/components/discussion_reply_placeholder_spec.js
@@ -2,13 +2,19 @@ import ReplyPlaceholder from '~/notes/components/discussion_reply_placeholder.vu
import { shallowMount, createLocalVue } from '@vue/test-utils';
const localVue = createLocalVue();
+const buttonText = 'Test Button Text';
describe('ReplyPlaceholder', () => {
let wrapper;
+ const findButton = () => wrapper.find({ ref: 'button' });
+
beforeEach(() => {
wrapper = shallowMount(ReplyPlaceholder, {
localVue,
+ propsData: {
+ buttonText,
+ },
});
});
@@ -17,9 +23,7 @@ describe('ReplyPlaceholder', () => {
});
it('emits onClick even on button click', () => {
- const button = wrapper.find({ ref: 'button' });
-
- button.trigger('click');
+ findButton().trigger('click');
expect(wrapper.emitted()).toEqual({
onClick: [[]],
@@ -27,8 +31,6 @@ describe('ReplyPlaceholder', () => {
});
it('should render reply button', () => {
- const button = wrapper.find({ ref: 'button' });
-
- expect(button.text()).toEqual('Reply...');
+ expect(findButton().text()).toEqual(buttonText);
});
});
diff --git a/spec/javascripts/diffs/components/diff_file_header_spec.js b/spec/javascripts/diffs/components/diff_file_header_spec.js
index 596a1ba5ad2..d4280d3ec2c 100644
--- a/spec/javascripts/diffs/components/diff_file_header_spec.js
+++ b/spec/javascripts/diffs/components/diff_file_header_spec.js
@@ -521,7 +521,7 @@ describe('diff_file_header', () => {
});
describe('with discussions', () => {
- it('dispatches toggleFileDiscussions when user clicks on toggle discussions button', () => {
+ it('dispatches toggleFileDiscussionWrappers when user clicks on toggle discussions button', () => {
const propsCopy = Object.assign({}, props);
propsCopy.diffFile.submodule = false;
propsCopy.diffFile.blob = {
@@ -552,11 +552,11 @@ describe('diff_file_header', () => {
}),
});
- spyOn(vm, 'toggleFileDiscussions');
+ spyOn(vm, 'toggleFileDiscussionWrappers');
vm.$el.querySelector('.js-btn-vue-toggle-comments').click();
- expect(vm.toggleFileDiscussions).toHaveBeenCalled();
+ expect(vm.toggleFileDiscussionWrappers).toHaveBeenCalled();
});
});
});
diff --git a/spec/javascripts/diffs/components/diff_gutter_avatars_spec.js b/spec/javascripts/diffs/components/diff_gutter_avatars_spec.js
deleted file mode 100644
index cdd30919b09..00000000000
--- a/spec/javascripts/diffs/components/diff_gutter_avatars_spec.js
+++ /dev/null
@@ -1,146 +0,0 @@
-import Vue from 'vue';
-import DiffGutterAvatarsComponent from '~/diffs/components/diff_gutter_avatars.vue';
-import { COUNT_OF_AVATARS_IN_GUTTER } from '~/diffs/constants';
-import store from '~/mr_notes/stores';
-import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
-import discussionsMockData from '../mock_data/diff_discussions';
-
-describe('DiffGutterAvatars', () => {
- let component;
- const getDiscussionsMockData = () => [Object.assign({}, discussionsMockData)];
-
- beforeEach(() => {
- component = createComponentWithStore(Vue.extend(DiffGutterAvatarsComponent), store, {
- discussions: getDiscussionsMockData(),
- }).$mount();
- });
-
- describe('computed', () => {
- describe('discussionsExpanded', () => {
- it('should return true when all discussions are expanded', () => {
- expect(component.discussionsExpanded).toEqual(true);
- });
-
- it('should return false when all discussions are not expanded', () => {
- component.discussions[0].expanded = false;
-
- expect(component.discussionsExpanded).toEqual(false);
- });
- });
-
- describe('allDiscussions', () => {
- it('should return an array of notes', () => {
- expect(component.allDiscussions).toEqual([...component.discussions[0].notes]);
- });
- });
-
- describe('notesInGutter', () => {
- it('should return a subset of discussions to show in gutter', () => {
- expect(component.notesInGutter.length).toEqual(COUNT_OF_AVATARS_IN_GUTTER);
- expect(component.notesInGutter[0]).toEqual({
- note: component.discussions[0].notes[0].note,
- author: component.discussions[0].notes[0].author,
- });
- });
- });
-
- describe('moreCount', () => {
- it('should return count of remaining discussions from gutter', () => {
- expect(component.moreCount).toEqual(2);
- });
- });
-
- describe('moreText', () => {
- it('should return proper text if moreCount > 0', () => {
- expect(component.moreText).toEqual('2 more comments');
- });
-
- it('should return empty string if there is no discussion', () => {
- component.discussions = [];
-
- expect(component.moreText).toEqual('');
- });
- });
- });
-
- describe('methods', () => {
- describe('getTooltipText', () => {
- it('should return original comment if it is shorter than max length', () => {
- const note = component.discussions[0].notes[0];
-
- expect(component.getTooltipText(note)).toEqual('Administrator: comment 1');
- });
-
- it('should return truncated version of comment', () => {
- const note = component.discussions[0].notes[1];
-
- expect(component.getTooltipText(note)).toEqual('Fatih Acet: comment 2 is r...');
- });
- });
-
- describe('toggleDiscussions', () => {
- it('should toggle all discussions', () => {
- expect(component.discussions[0].expanded).toEqual(true);
-
- component.$store.dispatch('setInitialNotes', getDiscussionsMockData());
- component.discussions = component.$store.state.notes.discussions;
- component.toggleDiscussions();
-
- expect(component.discussions[0].expanded).toEqual(false);
- component.$store.dispatch('setInitialNotes', []);
- });
-
- it('forces expansion of all discussions', () => {
- spyOn(component.$store, 'dispatch');
-
- component.discussions[0].expanded = true;
- component.discussions.push({
- ...component.discussions[0],
- id: '123test',
- expanded: false,
- });
-
- component.toggleDiscussions();
-
- expect(component.$store.dispatch.calls.argsFor(0)).toEqual([
- 'toggleDiscussion',
- {
- discussionId: component.discussions[0].id,
- forceExpanded: true,
- },
- ]);
-
- expect(component.$store.dispatch.calls.argsFor(1)).toEqual([
- 'toggleDiscussion',
- {
- discussionId: component.discussions[1].id,
- forceExpanded: true,
- },
- ]);
- });
- });
- });
-
- describe('template', () => {
- const buttonSelector = '.js-diff-comment-button';
- const svgSelector = `${buttonSelector} svg`;
- const avatarSelector = '.js-diff-comment-avatar';
- const plusCountSelector = '.js-diff-comment-plus';
-
- it('should have button to collapse discussions when the discussions expanded', () => {
- expect(component.$el.querySelector(buttonSelector)).toBeDefined();
- expect(component.$el.querySelector(svgSelector)).toBeDefined();
- });
-
- it('should have user avatars when discussions collapsed', () => {
- component.discussions[0].expanded = false;
-
- Vue.nextTick(() => {
- expect(component.$el.querySelector(buttonSelector)).toBeNull();
- expect(component.$el.querySelectorAll(avatarSelector).length).toEqual(4);
- expect(component.$el.querySelector(plusCountSelector)).toBeDefined();
- expect(component.$el.querySelector(plusCountSelector).textContent).toEqual('+2');
- });
- });
- });
-});
diff --git a/spec/javascripts/diffs/components/inline_diff_view_spec.js b/spec/javascripts/diffs/components/inline_diff_view_spec.js
index 4452106580a..0b3890b68d6 100644
--- a/spec/javascripts/diffs/components/inline_diff_view_spec.js
+++ b/spec/javascripts/diffs/components/inline_diff_view_spec.js
@@ -36,10 +36,11 @@ describe('InlineDiffView', () => {
it('should render discussions', done => {
const el = component.$el;
component.diffLines[1].discussions = getDiscussionsMockData();
+ component.diffLines[1].discussionsExpanded = true;
Vue.nextTick(() => {
expect(el.querySelectorAll('.notes_holder').length).toEqual(1);
- expect(el.querySelectorAll('.notes_holder .note-discussion li').length).toEqual(5);
+ expect(el.querySelectorAll('.notes_holder .note-discussion li').length).toEqual(6);
expect(el.innerText.indexOf('comment 5')).toBeGreaterThan(-1);
component.$store.dispatch('setInitialNotes', []);
diff --git a/spec/javascripts/diffs/store/actions_spec.js b/spec/javascripts/diffs/store/actions_spec.js
index f973728cfe1..f8872a3eb13 100644
--- a/spec/javascripts/diffs/store/actions_spec.js
+++ b/spec/javascripts/diffs/store/actions_spec.js
@@ -206,6 +206,7 @@ describe('DiffsStoreActions', () => {
position_type: 'text',
},
},
+ hash: 'diff-content-1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a',
},
},
],
diff --git a/spec/javascripts/notes/components/noteable_discussion_spec.js b/spec/javascripts/notes/components/noteable_discussion_spec.js
index c98366dd54f..74805ca8c00 100644
--- a/spec/javascripts/notes/components/noteable_discussion_spec.js
+++ b/spec/javascripts/notes/components/noteable_discussion_spec.js
@@ -36,6 +36,12 @@ describe('noteable_discussion component', () => {
});
it('should render user avatar', () => {
+ const discussion = { ...discussionMock };
+ discussion.diff_file = mockDiffFile;
+ discussion.diff_discussion = true;
+
+ wrapper.setProps({ discussion, renderDiffFile: true });
+
expect(wrapper.find('.user-avatar-link').exists()).toBe(true);
});
diff --git a/spec/javascripts/notes/stores/actions_spec.js b/spec/javascripts/notes/stores/actions_spec.js
index 7a9f32ddcff..65f72a135aa 100644
--- a/spec/javascripts/notes/stores/actions_spec.js
+++ b/spec/javascripts/notes/stores/actions_spec.js
@@ -373,7 +373,7 @@ describe('Actions Notes Store', () => {
type: 'updateMergeRequestWidget',
},
{
- type: 'updateResolvableDiscussonsCounts',
+ type: 'updateResolvableDiscussionsCounts',
},
],
done,
@@ -400,7 +400,7 @@ describe('Actions Notes Store', () => {
type: 'updateMergeRequestWidget',
},
{
- type: 'updateResolvableDiscussonsCounts',
+ type: 'updateResolvableDiscussionsCounts',
},
{
type: 'diffs/removeDiscussionsFromDiff',
@@ -452,7 +452,7 @@ describe('Actions Notes Store', () => {
type: 'startTaskList',
},
{
- type: 'updateResolvableDiscussonsCounts',
+ type: 'updateResolvableDiscussionsCounts',
},
],
done,
@@ -527,7 +527,7 @@ describe('Actions Notes Store', () => {
],
[
{
- type: 'updateResolvableDiscussonsCounts',
+ type: 'updateResolvableDiscussionsCounts',
},
{
type: 'updateMergeRequestWidget',
@@ -552,7 +552,7 @@ describe('Actions Notes Store', () => {
],
[
{
- type: 'updateResolvableDiscussonsCounts',
+ type: 'updateResolvableDiscussionsCounts',
},
{
type: 'updateMergeRequestWidget',
@@ -587,10 +587,10 @@ describe('Actions Notes Store', () => {
});
});
- describe('updateResolvableDiscussonsCounts', () => {
+ describe('updateResolvableDiscussionsCounts', () => {
it('commits UPDATE_RESOLVABLE_DISCUSSIONS_COUNTS', done => {
testAction(
- actions.updateResolvableDiscussonsCounts,
+ actions.updateResolvableDiscussionsCounts,
null,
{},
[{ type: 'UPDATE_RESOLVABLE_DISCUSSIONS_COUNTS' }],
@@ -712,7 +712,7 @@ describe('Actions Notes Store', () => {
[
{ type: 'updateMergeRequestWidget' },
{ type: 'startTaskList' },
- { type: 'updateResolvableDiscussonsCounts' },
+ { type: 'updateResolvableDiscussionsCounts' },
],
done,
);
diff --git a/spec/javascripts/performance_bar/components/simple_metric_spec.js b/spec/javascripts/performance_bar/components/simple_metric_spec.js
deleted file mode 100644
index 98b843e9711..00000000000
--- a/spec/javascripts/performance_bar/components/simple_metric_spec.js
+++ /dev/null
@@ -1,47 +0,0 @@
-import Vue from 'vue';
-import simpleMetric from '~/performance_bar/components/simple_metric.vue';
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
-
-describe('simpleMetric', () => {
- let vm;
-
- afterEach(() => {
- vm.$destroy();
- });
-
- describe('when the current request has no details', () => {
- beforeEach(() => {
- vm = mountComponent(Vue.extend(simpleMetric), {
- currentRequest: {},
- metric: 'gitaly',
- });
- });
-
- it('does not display details', () => {
- expect(vm.$el.innerText).not.toContain('/');
- });
-
- it('displays the metric name', () => {
- expect(vm.$el.innerText).toContain('gitaly');
- });
- });
-
- describe('when the current request has details', () => {
- beforeEach(() => {
- vm = mountComponent(Vue.extend(simpleMetric), {
- currentRequest: {
- details: { gitaly: { duration: '123ms', calls: '456' } },
- },
- metric: 'gitaly',
- });
- });
-
- it('diplays details', () => {
- expect(vm.$el.innerText.replace(/\s+/g, ' ')).toContain('123ms / 456');
- });
-
- it('displays the metric name', () => {
- expect(vm.$el.innerText).toContain('gitaly');
- });
- });
-});
diff --git a/spec/javascripts/registry/components/app_spec.js b/spec/javascripts/registry/components/app_spec.js
index 76a17e6fb31..87237d2853d 100644
--- a/spec/javascripts/registry/components/app_spec.js
+++ b/spec/javascripts/registry/components/app_spec.js
@@ -8,6 +8,13 @@ import { reposServerResponse } from '../mock_data';
describe('Registry List', () => {
const Component = Vue.extend(registry);
+ const props = {
+ endpoint: `${TEST_HOST}/foo`,
+ helpPagePath: 'foo',
+ noContainersImage: 'foo',
+ containersErrorImage: 'foo',
+ repositoryUrl: 'foo',
+ };
let vm;
let mock;
@@ -24,7 +31,7 @@ describe('Registry List', () => {
beforeEach(() => {
mock.onGet(`${TEST_HOST}/foo`).replyOnce(200, reposServerResponse);
- vm = mountComponent(Component, { endpoint: `${TEST_HOST}/foo` });
+ vm = mountComponent(Component, { ...props });
});
it('should render a list of repos', done => {
@@ -72,7 +79,7 @@ describe('Registry List', () => {
beforeEach(() => {
mock.onGet(`${TEST_HOST}/foo`).replyOnce(200, []);
- vm = mountComponent(Component, { endpoint: `${TEST_HOST}/foo` });
+ vm = mountComponent(Component, { ...props });
});
it('should render empty message', done => {
@@ -83,7 +90,7 @@ describe('Registry List', () => {
.textContent.trim()
.replace(/[\r\n]+/g, ' '),
).toEqual(
- 'No container images stored for this project. Add one by following the instructions above.',
+ 'With the Container Registry, every project can have its own space to store its Docker images. Learn more about the Container Registry.',
);
done();
}, 0);
@@ -94,7 +101,7 @@ describe('Registry List', () => {
beforeEach(() => {
mock.onGet(`${TEST_HOST}/foo`).replyOnce(200, []);
- vm = mountComponent(Component, { endpoint: `${TEST_HOST}/foo` });
+ vm = mountComponent(Component, { ...props });
});
it('should render a loading spinner', done => {
@@ -104,4 +111,22 @@ describe('Registry List', () => {
});
});
});
+
+ describe('invalid characters in path', () => {
+ beforeEach(() => {
+ mock.onGet(`${TEST_HOST}/foo`).replyOnce(200, []);
+
+ vm = mountComponent(Component, {
+ ...props,
+ characterError: true,
+ });
+ });
+
+ it('should render invalid characters error message', done => {
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.container-message')).not.toBe(null);
+ done();
+ });
+ });
+ });
});
diff --git a/spec/javascripts/releases/components/release_block_spec.js b/spec/javascripts/releases/components/release_block_spec.js
index e98c665f99d..f761a18e326 100644
--- a/spec/javascripts/releases/components/release_block_spec.js
+++ b/spec/javascripts/releases/components/release_block_spec.js
@@ -14,7 +14,7 @@ describe('Release block', () => {
description_html: '<div><h2>changelog</h2><ul><li>line1</li<li>line 2</li></ul></div>',
author_name: 'Release bot',
author_email: 'release-bot@example.com',
- created_at: '2012-05-28T05:00:00-07:00',
+ released_at: '2012-05-28T05:00:00-07:00',
author: {
avatar_url: 'uploads/-/system/user/avatar/johndoe/avatar.png',
id: 482476,
@@ -101,7 +101,7 @@ describe('Release block', () => {
});
it('renders release date', () => {
- expect(vm.$el.textContent).toContain(timeagoMixin.methods.timeFormated(release.created_at));
+ expect(vm.$el.textContent).toContain(timeagoMixin.methods.timeFormated(release.released_at));
});
it('renders number of assets provided', () => {
@@ -152,13 +152,13 @@ describe('Release block', () => {
});
});
- describe('with pre_release flag', () => {
+ describe('with upcoming_release flag', () => {
beforeEach(() => {
- vm = factory(Object.assign({}, release, { pre_release: true }));
+ vm = factory(Object.assign({}, release, { upcoming_release: true }));
});
- it('renders pre-release badge', () => {
- expect(vm.$el.textContent).toContain('Pre-release');
+ it('renders upcoming release badge', () => {
+ expect(vm.$el.textContent).toContain('Upcoming Release');
});
});
});
diff --git a/spec/lib/feature_spec.rb b/spec/lib/feature_spec.rb
index 6f05914f915..403e0785d1b 100644
--- a/spec/lib/feature_spec.rb
+++ b/spec/lib/feature_spec.rb
@@ -40,7 +40,7 @@ describe Feature do
.once
.and_call_original
- expect(Rails.cache)
+ expect(Gitlab::ThreadMemoryCache.cache_backend)
.to receive(:fetch)
.once
.with('flipper:persisted_names', expires_in: 1.minute)
diff --git a/spec/lib/gitlab/auth_spec.rb b/spec/lib/gitlab/auth_spec.rb
index 3b5ca7c950c..d9c73cff01e 100644
--- a/spec/lib/gitlab/auth_spec.rb
+++ b/spec/lib/gitlab/auth_spec.rb
@@ -309,6 +309,15 @@ describe Gitlab::Auth do
.to eq(auth_success)
end
+ it 'succeeds when custom login and token are valid' do
+ deploy_token = create(:deploy_token, username: 'deployer', read_registry: false, projects: [project])
+ auth_success = Gitlab::Auth::Result.new(deploy_token, project, :deploy_token, [:download_code])
+
+ expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: 'deployer')
+ expect(gl_auth.find_for_git_client('deployer', deploy_token.token, project: project, ip: 'ip'))
+ .to eq(auth_success)
+ end
+
it 'fails when login is not valid' do
expect(gl_auth).to receive(:rate_limit!).with('ip', success: false, login: 'random_login')
expect(gl_auth.find_for_git_client('random_login', deploy_token.token, project: project, ip: 'ip'))
diff --git a/spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb b/spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb
deleted file mode 100644
index 5076996474f..00000000000
--- a/spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb
+++ /dev/null
@@ -1,125 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::BackgroundMigration::CreateForkNetworkMembershipsRange, :migration, schema: 20170929131201 do
- let(:migration) { described_class.new }
- let(:projects) { table(:projects) }
-
- let(:base1) { projects.create }
- let(:base1_fork1) { projects.create }
- let(:base1_fork2) { projects.create }
-
- let(:base2) { projects.create }
- let(:base2_fork1) { projects.create }
- let(:base2_fork2) { projects.create }
-
- let(:fork_of_fork) { projects.create }
- let(:fork_of_fork2) { projects.create }
- let(:second_level_fork) { projects.create }
- let(:third_level_fork) { projects.create }
-
- let(:fork_network1) { fork_networks.find_by(root_project_id: base1.id) }
- let(:fork_network2) { fork_networks.find_by(root_project_id: base2.id) }
-
- let!(:forked_project_links) { table(:forked_project_links) }
- let!(:fork_networks) { table(:fork_networks) }
- let!(:fork_network_members) { table(:fork_network_members) }
-
- before do
- # The fork-network relation created for the forked project
- fork_networks.create(id: 1, root_project_id: base1.id)
- fork_network_members.create(project_id: base1.id, fork_network_id: 1)
- fork_networks.create(id: 2, root_project_id: base2.id)
- fork_network_members.create(project_id: base2.id, fork_network_id: 2)
-
- # Normal fork links
- forked_project_links.create(id: 1, forked_from_project_id: base1.id, forked_to_project_id: base1_fork1.id)
- forked_project_links.create(id: 2, forked_from_project_id: base1.id, forked_to_project_id: base1_fork2.id)
- forked_project_links.create(id: 3, forked_from_project_id: base2.id, forked_to_project_id: base2_fork1.id)
- forked_project_links.create(id: 4, forked_from_project_id: base2.id, forked_to_project_id: base2_fork2.id)
-
- # Fork links
- forked_project_links.create(id: 5, forked_from_project_id: base1_fork1.id, forked_to_project_id: fork_of_fork.id)
- forked_project_links.create(id: 6, forked_from_project_id: base1_fork1.id, forked_to_project_id: fork_of_fork2.id)
-
- # Forks 3 levels down
- forked_project_links.create(id: 7, forked_from_project_id: fork_of_fork.id, forked_to_project_id: second_level_fork.id)
- forked_project_links.create(id: 8, forked_from_project_id: second_level_fork.id, forked_to_project_id: third_level_fork.id)
-
- migration.perform(1, 8)
- end
-
- it 'creates a memberships for the direct forks' do
- base1_fork1_membership = fork_network_members.find_by(fork_network_id: fork_network1.id,
- project_id: base1_fork1.id)
- base1_fork2_membership = fork_network_members.find_by(fork_network_id: fork_network1.id,
- project_id: base1_fork2.id)
- base2_fork1_membership = fork_network_members.find_by(fork_network_id: fork_network2.id,
- project_id: base2_fork1.id)
- base2_fork2_membership = fork_network_members.find_by(fork_network_id: fork_network2.id,
- project_id: base2_fork2.id)
-
- expect(base1_fork1_membership.forked_from_project_id).to eq(base1.id)
- expect(base1_fork2_membership.forked_from_project_id).to eq(base1.id)
- expect(base2_fork1_membership.forked_from_project_id).to eq(base2.id)
- expect(base2_fork2_membership.forked_from_project_id).to eq(base2.id)
- end
-
- it 'adds the fork network members for forks of forks' do
- fork_of_fork_membership = fork_network_members.find_by(project_id: fork_of_fork.id,
- fork_network_id: fork_network1.id)
- fork_of_fork2_membership = fork_network_members.find_by(project_id: fork_of_fork2.id,
- fork_network_id: fork_network1.id)
- second_level_fork_membership = fork_network_members.find_by(project_id: second_level_fork.id,
- fork_network_id: fork_network1.id)
- third_level_fork_membership = fork_network_members.find_by(project_id: third_level_fork.id,
- fork_network_id: fork_network1.id)
-
- expect(fork_of_fork_membership.forked_from_project_id).to eq(base1_fork1.id)
- expect(fork_of_fork2_membership.forked_from_project_id).to eq(base1_fork1.id)
- expect(second_level_fork_membership.forked_from_project_id).to eq(fork_of_fork.id)
- expect(third_level_fork_membership.forked_from_project_id).to eq(second_level_fork.id)
- end
-
- it 'reschedules itself when there are missing members' do
- allow(migration).to receive(:missing_members?).and_return(true)
-
- expect(BackgroundMigrationWorker)
- .to receive(:perform_in).with(described_class::RESCHEDULE_DELAY, "CreateForkNetworkMembershipsRange", [1, 3])
-
- migration.perform(1, 3)
- end
-
- it 'can be repeated without effect' do
- expect { fork_network_members.count }.not_to change { migration.perform(1, 7) }
- end
-
- it 'knows it is finished for this range' do
- expect(migration.missing_members?(1, 8)).to be_falsy
- end
-
- it 'does not miss members for forks of forks for which the root was deleted' do
- forked_project_links.create(id: 9, forked_from_project_id: base1_fork1.id, forked_to_project_id: projects.create.id)
- base1.destroy
-
- expect(migration.missing_members?(7, 10)).to be_falsy
- end
-
- context 'with more forks' do
- before do
- forked_project_links.create(id: 9, forked_from_project_id: fork_of_fork.id, forked_to_project_id: projects.create.id)
- forked_project_links.create(id: 10, forked_from_project_id: fork_of_fork.id, forked_to_project_id: projects.create.id)
- end
-
- it 'only processes a single batch of links at a time' do
- expect(fork_network_members.count).to eq(10)
-
- migration.perform(8, 10)
-
- expect(fork_network_members.count).to eq(12)
- end
-
- it 'knows when not all memberships within a batch have been created' do
- expect(migration.missing_members?(8, 10)).to be_truthy
- end
- end
-end
diff --git a/spec/lib/gitlab/background_migration/delete_conflicting_redirect_routes_range_spec.rb b/spec/lib/gitlab/background_migration/delete_conflicting_redirect_routes_range_spec.rb
deleted file mode 100644
index 9bae7e53b71..00000000000
--- a/spec/lib/gitlab/background_migration/delete_conflicting_redirect_routes_range_spec.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::BackgroundMigration::DeleteConflictingRedirectRoutesRange, :migration, schema: 20170907170235 do
- let!(:redirect_routes) { table(:redirect_routes) }
- let!(:routes) { table(:routes) }
-
- before do
- routes.create!(id: 1, source_id: 1, source_type: 'Namespace', path: 'foo1')
- routes.create!(id: 2, source_id: 2, source_type: 'Namespace', path: 'foo2')
- routes.create!(id: 3, source_id: 3, source_type: 'Namespace', path: 'foo3')
- routes.create!(id: 4, source_id: 4, source_type: 'Namespace', path: 'foo4')
- routes.create!(id: 5, source_id: 5, source_type: 'Namespace', path: 'foo5')
-
- # Valid redirects
- redirect_routes.create!(source_id: 1, source_type: 'Namespace', path: 'bar')
- redirect_routes.create!(source_id: 1, source_type: 'Namespace', path: 'bar2')
- redirect_routes.create!(source_id: 2, source_type: 'Namespace', path: 'bar3')
-
- # Conflicting redirects
- redirect_routes.create!(source_id: 2, source_type: 'Namespace', path: 'foo1')
- redirect_routes.create!(source_id: 1, source_type: 'Namespace', path: 'foo2')
- redirect_routes.create!(source_id: 1, source_type: 'Namespace', path: 'foo3')
- redirect_routes.create!(source_id: 1, source_type: 'Namespace', path: 'foo4')
- redirect_routes.create!(source_id: 1, source_type: 'Namespace', path: 'foo5')
- end
-
- # No-op. See https://gitlab.com/gitlab-com/infrastructure/issues/3460#note_53223252
- it 'NO-OP: does not delete any redirect_routes' do
- expect(redirect_routes.count).to eq(8)
-
- described_class.new.perform(1, 5)
-
- expect(redirect_routes.count).to eq(8)
- end
-end
diff --git a/spec/lib/gitlab/background_migration/migrate_events_to_push_event_payloads_spec.rb b/spec/lib/gitlab/background_migration/migrate_events_to_push_event_payloads_spec.rb
deleted file mode 100644
index 188969951a6..00000000000
--- a/spec/lib/gitlab/background_migration/migrate_events_to_push_event_payloads_spec.rb
+++ /dev/null
@@ -1,433 +0,0 @@
-require 'spec_helper'
-
-# rubocop:disable RSpec/FactoriesInMigrationSpecs
-describe Gitlab::BackgroundMigration::MigrateEventsToPushEventPayloads::Event, :migration, schema: 20170608152748 do
- describe '#commit_title' do
- it 'returns nil when there are no commits' do
- expect(described_class.new.commit_title).to be_nil
- end
-
- it 'returns nil when there are commits without commit messages' do
- event = described_class.new
-
- allow(event).to receive(:commits).and_return([{ id: '123' }])
-
- expect(event.commit_title).to be_nil
- end
-
- it 'returns the commit message when it is less than 70 characters long' do
- event = described_class.new
-
- allow(event).to receive(:commits).and_return([{ message: 'Hello world' }])
-
- expect(event.commit_title).to eq('Hello world')
- end
-
- it 'returns the first line of a commit message if multiple lines are present' do
- event = described_class.new
-
- allow(event).to receive(:commits).and_return([{ message: "Hello\n\nworld" }])
-
- expect(event.commit_title).to eq('Hello')
- end
-
- it 'truncates the commit to 70 characters when it is too long' do
- event = described_class.new
-
- allow(event).to receive(:commits).and_return([{ message: 'a' * 100 }])
-
- expect(event.commit_title).to eq(('a' * 67) + '...')
- end
- end
-
- describe '#commit_from_sha' do
- it 'returns nil when pushing to a new ref' do
- event = described_class.new
-
- allow(event).to receive(:create?).and_return(true)
-
- expect(event.commit_from_sha).to be_nil
- end
-
- it 'returns the ID of the first commit when pushing to an existing ref' do
- event = described_class.new
-
- allow(event).to receive(:create?).and_return(false)
- allow(event).to receive(:data).and_return(before: '123')
-
- expect(event.commit_from_sha).to eq('123')
- end
- end
-
- describe '#commit_to_sha' do
- it 'returns nil when removing an existing ref' do
- event = described_class.new
-
- allow(event).to receive(:remove?).and_return(true)
-
- expect(event.commit_to_sha).to be_nil
- end
-
- it 'returns the ID of the last commit when pushing to an existing ref' do
- event = described_class.new
-
- allow(event).to receive(:remove?).and_return(false)
- allow(event).to receive(:data).and_return(after: '123')
-
- expect(event.commit_to_sha).to eq('123')
- end
- end
-
- describe '#data' do
- it 'returns the deserialized data' do
- event = described_class.new(data: { before: '123' })
-
- expect(event.data).to eq(before: '123')
- end
-
- it 'returns an empty hash when no data is present' do
- event = described_class.new
-
- expect(event.data).to eq({})
- end
- end
-
- describe '#commits' do
- it 'returns an Array of commits' do
- event = described_class.new(data: { commits: [{ id: '123' }] })
-
- expect(event.commits).to eq([{ id: '123' }])
- end
-
- it 'returns an empty array when no data is present' do
- event = described_class.new
-
- expect(event.commits).to eq([])
- end
- end
-
- describe '#commit_count' do
- it 'returns the number of commits' do
- event = described_class.new(data: { total_commits_count: 2 })
-
- expect(event.commit_count).to eq(2)
- end
-
- it 'returns 0 when no data is present' do
- event = described_class.new
-
- expect(event.commit_count).to eq(0)
- end
- end
-
- describe '#ref' do
- it 'returns the name of the ref' do
- event = described_class.new(data: { ref: 'refs/heads/master' })
-
- expect(event.ref).to eq('refs/heads/master')
- end
- end
-
- describe '#trimmed_ref_name' do
- it 'returns the trimmed ref name for a branch' do
- event = described_class.new(data: { ref: 'refs/heads/master' })
-
- expect(event.trimmed_ref_name).to eq('master')
- end
-
- it 'returns the trimmed ref name for a tag' do
- event = described_class.new(data: { ref: 'refs/tags/v1.2' })
-
- expect(event.trimmed_ref_name).to eq('v1.2')
- end
- end
-
- describe '#create?' do
- it 'returns true when creating a new ref' do
- event = described_class.new(data: { before: described_class::BLANK_REF })
-
- expect(event.create?).to eq(true)
- end
-
- it 'returns false when pushing to an existing ref' do
- event = described_class.new(data: { before: '123' })
-
- expect(event.create?).to eq(false)
- end
- end
-
- describe '#remove?' do
- it 'returns true when removing an existing ref' do
- event = described_class.new(data: { after: described_class::BLANK_REF })
-
- expect(event.remove?).to eq(true)
- end
-
- it 'returns false when pushing to an existing ref' do
- event = described_class.new(data: { after: '123' })
-
- expect(event.remove?).to eq(false)
- end
- end
-
- describe '#push_action' do
- let(:event) { described_class.new }
-
- it 'returns :created when creating a new ref' do
- allow(event).to receive(:create?).and_return(true)
-
- expect(event.push_action).to eq(:created)
- end
-
- it 'returns :removed when removing an existing ref' do
- allow(event).to receive(:create?).and_return(false)
- allow(event).to receive(:remove?).and_return(true)
-
- expect(event.push_action).to eq(:removed)
- end
-
- it 'returns :pushed when pushing to an existing ref' do
- allow(event).to receive(:create?).and_return(false)
- allow(event).to receive(:remove?).and_return(false)
-
- expect(event.push_action).to eq(:pushed)
- end
- end
-
- describe '#ref_type' do
- let(:event) { described_class.new }
-
- it 'returns :tag for a tag' do
- allow(event).to receive(:ref).and_return('refs/tags/1.2')
-
- expect(event.ref_type).to eq(:tag)
- end
-
- it 'returns :branch for a branch' do
- allow(event).to receive(:ref).and_return('refs/heads/1.2')
-
- expect(event.ref_type).to eq(:branch)
- end
- end
-end
-
-##
-# The background migration relies on a temporary table, hence we're migrating
-# to a specific version of the database where said table is still present.
-#
-describe Gitlab::BackgroundMigration::MigrateEventsToPushEventPayloads, :migration, schema: 20170825154015 do
- let(:user_class) do
- Class.new(ActiveRecord::Base) do
- self.table_name = 'users'
- end
- end
-
- let(:migration) { described_class.new }
- let(:user_class) { table(:users) }
- let(:author) { build(:user).becomes(user_class).tap(&:save!).becomes(User) }
- let(:namespace) { create(:namespace, owner: author) }
- let(:projects) { table(:projects) }
- let(:project) { projects.create(namespace_id: namespace.id, creator_id: author.id) }
-
- # We can not rely on FactoryBot as the state of Event may change in ways that
- # the background migration does not expect, hence we use the Event class of
- # the migration itself.
- def create_push_event(project, author, data = nil)
- klass = Gitlab::BackgroundMigration::MigrateEventsToPushEventPayloads::Event
-
- klass.create!(
- action: klass::PUSHED,
- project_id: project.id,
- author_id: author.id,
- data: data
- )
- end
-
- describe '#perform' do
- it 'returns if data should not be migrated' do
- allow(migration).to receive(:migrate?).and_return(false)
-
- expect(migration).not_to receive(:find_events)
-
- migration.perform(1, 10)
- end
-
- it 'migrates the range of events if data is to be migrated' do
- event1 = create_push_event(project, author, { commits: [] })
- event2 = create_push_event(project, author, { commits: [] })
-
- allow(migration).to receive(:migrate?).and_return(true)
-
- expect(migration).to receive(:process_event).twice
-
- migration.perform(event1.id, event2.id)
- end
- end
-
- describe '#process_event' do
- it 'processes a regular event' do
- event = double(:event, push_event?: false)
-
- expect(migration).to receive(:replicate_event)
- expect(migration).not_to receive(:create_push_event_payload)
-
- migration.process_event(event)
- end
-
- it 'processes a push event' do
- event = double(:event, push_event?: true)
-
- expect(migration).to receive(:replicate_event)
- expect(migration).to receive(:create_push_event_payload)
-
- migration.process_event(event)
- end
-
- it 'handles an error gracefully' do
- event1 = create_push_event(project, author, { commits: [] })
-
- expect(migration).to receive(:replicate_event).and_call_original
- expect(migration).to receive(:create_push_event_payload).and_raise(ActiveRecord::InvalidForeignKey, 'invalid foreign key')
-
- migration.process_event(event1)
-
- expect(described_class::EventForMigration.all.count).to eq(0)
- end
- end
-
- describe '#replicate_event' do
- it 'replicates the event to the "events_for_migration" table' do
- event = create_push_event(
- project,
- author,
- data: { commits: [] },
- title: 'bla'
- )
-
- attributes = event
- .attributes.with_indifferent_access.except(:title, :data)
-
- expect(described_class::EventForMigration)
- .to receive(:create!)
- .with(attributes)
-
- migration.replicate_event(event)
- end
- end
-
- describe '#create_push_event_payload' do
- let(:push_data) do
- {
- commits: [],
- ref: 'refs/heads/master',
- before: '156e0e9adc587a383a7eeb5b21ddecb9044768a8',
- after: '0' * 40,
- total_commits_count: 1
- }
- end
-
- let(:event) do
- create_push_event(project, author, push_data)
- end
-
- before do
- # The foreign key in push_event_payloads at this point points to the
- # "events_for_migration" table so we need to make sure a row exists in
- # said table.
- migration.replicate_event(event)
- end
-
- it 'creates a push event payload for an event' do
- payload = migration.create_push_event_payload(event)
-
- expect(PushEventPayload.count).to eq(1)
- expect(payload.valid?).to eq(true)
- end
-
- it 'does not create push event payloads for removed events' do
- allow(event).to receive(:id).and_return(-1)
-
- expect { migration.create_push_event_payload(event) }.to raise_error(ActiveRecord::InvalidForeignKey)
-
- expect(PushEventPayload.count).to eq(0)
- end
-
- it 'encodes and decodes the commit IDs from and to binary data' do
- payload = migration.create_push_event_payload(event)
- packed = migration.pack(push_data[:before])
-
- expect(payload.commit_from).to eq(packed)
- expect(payload.commit_to).to be_nil
- end
- end
-
- describe '#find_events' do
- it 'returns the events for the given ID range' do
- event1 = create_push_event(project, author, { commits: [] })
- event2 = create_push_event(project, author, { commits: [] })
- event3 = create_push_event(project, author, { commits: [] })
- events = migration.find_events(event1.id, event2.id)
-
- expect(events.length).to eq(2)
- expect(events.pluck(:id)).not_to include(event3.id)
- end
- end
-
- describe '#migrate?' do
- it 'returns true when data should be migrated' do
- allow(described_class::Event)
- .to receive(:table_exists?).and_return(true)
-
- allow(described_class::PushEventPayload)
- .to receive(:table_exists?).and_return(true)
-
- allow(described_class::EventForMigration)
- .to receive(:table_exists?).and_return(true)
-
- expect(migration.migrate?).to eq(true)
- end
-
- it 'returns false if the "events" table does not exist' do
- allow(described_class::Event)
- .to receive(:table_exists?).and_return(false)
-
- expect(migration.migrate?).to eq(false)
- end
-
- it 'returns false if the "push_event_payloads" table does not exist' do
- allow(described_class::Event)
- .to receive(:table_exists?).and_return(true)
-
- allow(described_class::PushEventPayload)
- .to receive(:table_exists?).and_return(false)
-
- expect(migration.migrate?).to eq(false)
- end
-
- it 'returns false when the "events_for_migration" table does not exist' do
- allow(described_class::Event)
- .to receive(:table_exists?).and_return(true)
-
- allow(described_class::PushEventPayload)
- .to receive(:table_exists?).and_return(true)
-
- allow(described_class::EventForMigration)
- .to receive(:table_exists?).and_return(false)
-
- expect(migration.migrate?).to eq(false)
- end
- end
-
- describe '#pack' do
- it 'packs a SHA1 into a 20 byte binary string' do
- packed = migration.pack('156e0e9adc587a383a7eeb5b21ddecb9044768a8')
-
- expect(packed.bytesize).to eq(20)
- end
-
- it 'returns nil if the input value is nil' do
- expect(migration.pack(nil)).to be_nil
- end
- end
-end
-# rubocop:enable RSpec/FactoriesInMigrationSpecs
diff --git a/spec/lib/gitlab/background_migration/migrate_stage_status_spec.rb b/spec/lib/gitlab/background_migration/migrate_stage_status_spec.rb
deleted file mode 100644
index 89b56906ed0..00000000000
--- a/spec/lib/gitlab/background_migration/migrate_stage_status_spec.rb
+++ /dev/null
@@ -1,92 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-describe Gitlab::BackgroundMigration::MigrateStageStatus, :migration, schema: 20170711145320 do
- let(:projects) { table(:projects) }
- let(:pipelines) { table(:ci_pipelines) }
- let(:stages) { table(:ci_stages) }
- let(:jobs) { table(:ci_builds) }
-
- let(:statuses) do
- {
- created: 0,
- pending: 1,
- running: 2,
- success: 3,
- failed: 4,
- canceled: 5,
- skipped: 6,
- manual: 7
- }
- end
-
- before do
- projects.create!(id: 1, name: 'gitlab1', path: 'gitlab1')
- pipelines.create!(id: 1, project_id: 1, ref: 'master', sha: 'adf43c3a')
- stages.create!(id: 1, pipeline_id: 1, project_id: 1, name: 'test', status: nil)
- stages.create!(id: 2, pipeline_id: 1, project_id: 1, name: 'deploy', status: nil)
- end
-
- context 'when stage status is known' do
- before do
- create_job(project: 1, pipeline: 1, stage: 'test', status: 'success')
- create_job(project: 1, pipeline: 1, stage: 'test', status: 'running')
- create_job(project: 1, pipeline: 1, stage: 'deploy', status: 'failed')
- end
-
- it 'sets a correct stage status' do
- described_class.new.perform(1, 2)
-
- expect(stages.first.status).to eq statuses[:running]
- expect(stages.second.status).to eq statuses[:failed]
- end
- end
-
- context 'when stage status is not known' do
- it 'sets a skipped stage status' do
- described_class.new.perform(1, 2)
-
- expect(stages.first.status).to eq statuses[:skipped]
- expect(stages.second.status).to eq statuses[:skipped]
- end
- end
-
- context 'when stage status includes status of a retried job' do
- before do
- create_job(project: 1, pipeline: 1, stage: 'test', status: 'canceled')
- create_job(project: 1, pipeline: 1, stage: 'deploy', status: 'failed', retried: true)
- create_job(project: 1, pipeline: 1, stage: 'deploy', status: 'success')
- end
-
- it 'sets a correct stage status' do
- described_class.new.perform(1, 2)
-
- expect(stages.first.status).to eq statuses[:canceled]
- expect(stages.second.status).to eq statuses[:success]
- end
- end
-
- context 'when some job in the stage is blocked / manual' do
- before do
- create_job(project: 1, pipeline: 1, stage: 'test', status: 'failed')
- create_job(project: 1, pipeline: 1, stage: 'test', status: 'manual')
- create_job(project: 1, pipeline: 1, stage: 'deploy', status: 'success', when: 'manual')
- end
-
- it 'sets a correct stage status' do
- described_class.new.perform(1, 2)
-
- expect(stages.first.status).to eq statuses[:manual]
- expect(stages.second.status).to eq statuses[:success]
- end
- end
-
- def create_job(project:, pipeline:, stage:, status:, **opts)
- stages = { test: 1, build: 2, deploy: 3 }
-
- jobs.create!(project_id: project, commit_id: pipeline,
- stage_idx: stages[stage.to_sym], stage: stage,
- status: status, **opts)
- end
-end
diff --git a/spec/lib/gitlab/background_migration/normalize_ldap_extern_uids_range_spec.rb b/spec/lib/gitlab/background_migration/normalize_ldap_extern_uids_range_spec.rb
deleted file mode 100644
index dfbf1bb681a..00000000000
--- a/spec/lib/gitlab/background_migration/normalize_ldap_extern_uids_range_spec.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::BackgroundMigration::NormalizeLdapExternUidsRange, :migration, schema: 20170921101004 do
- let!(:identities) { table(:identities) }
-
- before do
- # LDAP identities
- (1..4).each do |i|
- identities.create!(id: i, provider: 'ldapmain', extern_uid: " uid = foo #{i}, ou = People, dc = example, dc = com ", user_id: i)
- end
-
- # Non-LDAP identity
- identities.create!(id: 5, provider: 'foo', extern_uid: " uid = foo 5, ou = People, dc = example, dc = com ", user_id: 5)
-
- # Another LDAP identity
- identities.create!(id: 6, provider: 'ldapmain', extern_uid: " uid = foo 6, ou = People, dc = example, dc = com ", user_id: 6)
- end
-
- it 'normalizes the LDAP identities in the range' do
- described_class.new.perform(1, 3)
- expect(identities.find(1).extern_uid).to eq("uid=foo 1,ou=people,dc=example,dc=com")
- expect(identities.find(2).extern_uid).to eq("uid=foo 2,ou=people,dc=example,dc=com")
- expect(identities.find(3).extern_uid).to eq("uid=foo 3,ou=people,dc=example,dc=com")
- expect(identities.find(4).extern_uid).to eq(" uid = foo 4, ou = People, dc = example, dc = com ")
- expect(identities.find(5).extern_uid).to eq(" uid = foo 5, ou = People, dc = example, dc = com ")
- expect(identities.find(6).extern_uid).to eq(" uid = foo 6, ou = People, dc = example, dc = com ")
-
- described_class.new.perform(4, 6)
- expect(identities.find(1).extern_uid).to eq("uid=foo 1,ou=people,dc=example,dc=com")
- expect(identities.find(2).extern_uid).to eq("uid=foo 2,ou=people,dc=example,dc=com")
- expect(identities.find(3).extern_uid).to eq("uid=foo 3,ou=people,dc=example,dc=com")
- expect(identities.find(4).extern_uid).to eq("uid=foo 4,ou=people,dc=example,dc=com")
- expect(identities.find(5).extern_uid).to eq(" uid = foo 5, ou = People, dc = example, dc = com ")
- expect(identities.find(6).extern_uid).to eq("uid=foo 6,ou=people,dc=example,dc=com")
- end
-end
diff --git a/spec/lib/gitlab/background_migration/populate_fork_networks_range_spec.rb b/spec/lib/gitlab/background_migration/populate_fork_networks_range_spec.rb
deleted file mode 100644
index 0e73c8c59c9..00000000000
--- a/spec/lib/gitlab/background_migration/populate_fork_networks_range_spec.rb
+++ /dev/null
@@ -1,97 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::BackgroundMigration::PopulateForkNetworksRange, :migration, schema: 20170929131201 do
- let(:migration) { described_class.new }
- let(:projects) { table(:projects) }
- let(:base1) { projects.create }
-
- let(:base2) { projects.create }
- let(:base2_fork1) { projects.create }
-
- let!(:forked_project_links) { table(:forked_project_links) }
- let!(:fork_networks) { table(:fork_networks) }
- let!(:fork_network_members) { table(:fork_network_members) }
-
- let(:fork_network1) { fork_networks.find_by(root_project_id: base1.id) }
- let(:fork_network2) { fork_networks.find_by(root_project_id: base2.id) }
-
- before do
- # A normal fork link
- forked_project_links.create(id: 1,
- forked_from_project_id: base1.id,
- forked_to_project_id: projects.create.id)
- forked_project_links.create(id: 2,
- forked_from_project_id: base1.id,
- forked_to_project_id: projects.create.id)
- forked_project_links.create(id: 3,
- forked_from_project_id: base2.id,
- forked_to_project_id: base2_fork1.id)
-
- # create a fork of a fork
- forked_project_links.create(id: 4,
- forked_from_project_id: base2_fork1.id,
- forked_to_project_id: projects.create.id)
- forked_project_links.create(id: 5,
- forked_from_project_id: projects.create.id,
- forked_to_project_id: projects.create.id)
-
- # Stub out the calls to the other migrations
- allow(BackgroundMigrationWorker).to receive(:perform_in)
-
- migration.perform(1, 3)
- end
-
- it 'creates the fork network' do
- expect(fork_network1).not_to be_nil
- expect(fork_network2).not_to be_nil
- end
-
- it 'does not create a fork network for a fork-of-fork' do
- # perfrom the entire batch
- migration.perform(1, 5)
-
- expect(fork_networks.find_by(root_project_id: base2_fork1.id)).to be_nil
- end
-
- it 'creates memberships for the root of fork networks' do
- base1_membership = fork_network_members.find_by(fork_network_id: fork_network1.id,
- project_id: base1.id)
- base2_membership = fork_network_members.find_by(fork_network_id: fork_network2.id,
- project_id: base2.id)
-
- expect(base1_membership).not_to be_nil
- expect(base2_membership).not_to be_nil
- end
-
- it 'creates a fork network for the fork of which the source was deleted' do
- fork = projects.create
- forked_project_links.create(id: 6, forked_from_project_id: 99999, forked_to_project_id: fork.id)
-
- migration.perform(5, 8)
-
- expect(fork_networks.find_by(root_project_id: 99999)).to be_nil
- expect(fork_networks.find_by(root_project_id: fork.id)).not_to be_nil
- expect(fork_network_members.find_by(project_id: fork.id)).not_to be_nil
- end
-
- it 'schedules a job for inserting memberships for forks-of-forks' do
- delay = Gitlab::BackgroundMigration::CreateForkNetworkMembershipsRange::RESCHEDULE_DELAY
-
- expect(BackgroundMigrationWorker)
- .to receive(:perform_in).with(delay, "CreateForkNetworkMembershipsRange", [1, 3])
-
- migration.perform(1, 3)
- end
-
- it 'only processes a single batch of links at a time' do
- expect(fork_networks.count).to eq(2)
-
- migration.perform(3, 5)
-
- expect(fork_networks.count).to eq(3)
- end
-
- it 'can be repeated without effect' do
- expect { migration.perform(1, 3) }.not_to change { fork_network_members.count }
- end
-end
diff --git a/spec/lib/gitlab/background_migration/populate_merge_requests_latest_merge_request_diff_id_spec.rb b/spec/lib/gitlab/background_migration/populate_merge_requests_latest_merge_request_diff_id_spec.rb
deleted file mode 100644
index 0cb753c5853..00000000000
--- a/spec/lib/gitlab/background_migration/populate_merge_requests_latest_merge_request_diff_id_spec.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::BackgroundMigration::PopulateMergeRequestsLatestMergeRequestDiffId, :migration, schema: 20171026082505 do
- let(:projects_table) { table(:projects) }
- let(:merge_requests_table) { table(:merge_requests) }
- let(:merge_request_diffs_table) { table(:merge_request_diffs) }
-
- let(:project) { projects_table.create!(name: 'gitlab', path: 'gitlab-org/gitlab-ce') }
-
- def create_mr!(name, diffs: 0)
- merge_request =
- merge_requests_table.create!(target_project_id: project.id,
- target_branch: 'master',
- source_project_id: project.id,
- source_branch: name,
- title: name)
-
- diffs.times do
- merge_request_diffs_table.create!(merge_request_id: merge_request.id)
- end
-
- merge_request
- end
-
- def diffs_for(merge_request)
- merge_request_diffs_table.where(merge_request_id: merge_request.id)
- end
-
- describe '#perform' do
- it 'ignores MRs without diffs' do
- merge_request_without_diff = create_mr!('without_diff')
- mr_id = merge_request_without_diff.id
-
- expect(merge_request_without_diff.latest_merge_request_diff_id).to be_nil
-
- expect { subject.perform(mr_id, mr_id) }
- .not_to change { merge_request_without_diff.reload.latest_merge_request_diff_id }
- end
-
- it 'ignores MRs that have a diff ID already set' do
- merge_request_with_multiple_diffs = create_mr!('with_multiple_diffs', diffs: 3)
- diff_id = diffs_for(merge_request_with_multiple_diffs).minimum(:id)
- mr_id = merge_request_with_multiple_diffs.id
-
- merge_request_with_multiple_diffs.update!(latest_merge_request_diff_id: diff_id)
-
- expect { subject.perform(mr_id, mr_id) }
- .not_to change { merge_request_with_multiple_diffs.reload.latest_merge_request_diff_id }
- end
-
- it 'migrates multiple MR diffs to the correct values' do
- merge_requests = Array.new(3).map.with_index { |_, i| create_mr!(i, diffs: 3) }
-
- subject.perform(merge_requests.first.id, merge_requests.last.id)
-
- merge_requests.each do |merge_request|
- expect(merge_request.reload.latest_merge_request_diff_id)
- .to eq(diffs_for(merge_request).maximum(:id))
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index a406c25b1d8..28b187c3676 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -123,6 +123,7 @@ Release:
- project_id
- created_at
- updated_at
+- released_at
Releases::Link:
- id
- release_id
@@ -429,6 +430,7 @@ Service:
- confidential_issues_events
- confidential_note_events
- deployment_events
+- description
ProjectHook:
- id
- url
diff --git a/spec/lib/gitlab/legacy_github_import/importer_spec.rb b/spec/lib/gitlab/legacy_github_import/importer_spec.rb
index a0c664da185..9163019514b 100644
--- a/spec/lib/gitlab/legacy_github_import/importer_spec.rb
+++ b/spec/lib/gitlab/legacy_github_import/importer_spec.rb
@@ -132,6 +132,7 @@ describe Gitlab::LegacyGithubImport::Importer do
body: 'Release v1.0.0',
draft: false,
created_at: created_at,
+ published_at: created_at,
updated_at: updated_at,
url: "#{api_root}/repos/octocat/Hello-World/releases/1"
)
@@ -144,6 +145,7 @@ describe Gitlab::LegacyGithubImport::Importer do
body: nil,
draft: false,
created_at: created_at,
+ published_at: created_at,
updated_at: updated_at,
url: "#{api_root}/repos/octocat/Hello-World/releases/2"
)
diff --git a/spec/lib/gitlab/legacy_github_import/release_formatter_spec.rb b/spec/lib/gitlab/legacy_github_import/release_formatter_spec.rb
index c57b96fb00d..534cf219520 100644
--- a/spec/lib/gitlab/legacy_github_import/release_formatter_spec.rb
+++ b/spec/lib/gitlab/legacy_github_import/release_formatter_spec.rb
@@ -4,6 +4,7 @@ describe Gitlab::LegacyGithubImport::ReleaseFormatter do
let!(:project) { create(:project, namespace: create(:namespace, path: 'octocat')) }
let(:octocat) { double(id: 123456, login: 'octocat') }
let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') }
+ let(:published_at) { DateTime.strptime('2011-01-26T20:00:00Z') }
let(:base_data) do
{
@@ -11,7 +12,7 @@ describe Gitlab::LegacyGithubImport::ReleaseFormatter do
name: 'First release',
draft: false,
created_at: created_at,
- published_at: created_at,
+ published_at: published_at,
body: 'Release v1.0.0'
}
end
@@ -28,6 +29,7 @@ describe Gitlab::LegacyGithubImport::ReleaseFormatter do
name: 'First release',
description: 'Release v1.0.0',
created_at: created_at,
+ released_at: published_at,
updated_at: created_at
}
diff --git a/spec/migrations/add_foreign_key_to_merge_requests_spec.rb b/spec/migrations/add_foreign_key_to_merge_requests_spec.rb
deleted file mode 100644
index d9ad9a585f0..00000000000
--- a/spec/migrations/add_foreign_key_to_merge_requests_spec.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'migrate', '20170713104829_add_foreign_key_to_merge_requests.rb')
-
-describe AddForeignKeyToMergeRequests, :migration do
- let(:projects) { table(:projects) }
- let(:merge_requests) { table(:merge_requests) }
- let(:pipelines) { table(:ci_pipelines) }
-
- before do
- projects.create!(name: 'gitlab', path: 'gitlab-org/gitlab-ce')
- pipelines.create!(project_id: projects.first.id,
- ref: 'some-branch',
- sha: 'abc12345')
-
- # merge request without a pipeline
- create_merge_request(head_pipeline_id: nil)
-
- # merge request with non-existent pipeline
- create_merge_request(head_pipeline_id: 1234)
-
- # merge reqeust with existing pipeline assigned
- create_merge_request(head_pipeline_id: pipelines.first.id)
- end
-
- it 'correctly adds a foreign key to head_pipeline_id' do
- migrate!
-
- expect(merge_requests.first.head_pipeline_id).to be_nil
- expect(merge_requests.second.head_pipeline_id).to be_nil
- expect(merge_requests.third.head_pipeline_id).to eq pipelines.first.id
- end
-
- def create_merge_request(**opts)
- merge_requests.create!(source_project_id: projects.first.id,
- target_project_id: projects.first.id,
- source_branch: 'some-branch',
- target_branch: 'master', **opts)
- end
-end
diff --git a/spec/migrations/add_head_pipeline_for_each_merge_request_spec.rb b/spec/migrations/add_head_pipeline_for_each_merge_request_spec.rb
deleted file mode 100644
index 13dc62595b5..00000000000
--- a/spec/migrations/add_head_pipeline_for_each_merge_request_spec.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20170508170547_add_head_pipeline_for_each_merge_request.rb')
-
-describe AddHeadPipelineForEachMergeRequest, :migration do
- let(:migration) { described_class.new }
-
- let!(:project) { table(:projects).create! }
- let!(:other_project) { table(:projects).create! }
-
- let!(:pipeline_1) { table(:ci_pipelines).create!(project_id: project.id, ref: "branch_1") }
- let!(:pipeline_2) { table(:ci_pipelines).create!(project_id: other_project.id, ref: "branch_1") }
- let!(:pipeline_3) { table(:ci_pipelines).create!(project_id: other_project.id, ref: "branch_1") }
- let!(:pipeline_4) { table(:ci_pipelines).create!(project_id: project.id, ref: "branch_2") }
-
- let!(:mr_1) { table(:merge_requests).create!(source_project_id: project.id, target_project_id: project.id, source_branch: "branch_1", target_branch: "target_1") }
- let!(:mr_2) { table(:merge_requests).create!(source_project_id: other_project.id, target_project_id: project.id, source_branch: "branch_1", target_branch: "target_2") }
- let!(:mr_3) { table(:merge_requests).create!(source_project_id: project.id, target_project_id: project.id, source_branch: "branch_2", target_branch: "master") }
- let!(:mr_4) { table(:merge_requests).create!(source_project_id: project.id, target_project_id: project.id, source_branch: "branch_3", target_branch: "master") }
-
- context "#up" do
- context "when source_project and source_branch of pipeline are the same of merge request" do
- it "sets head_pipeline_id of given merge requests" do
- migration.up
-
- expect(mr_1.reload.head_pipeline_id).to eq(pipeline_1.id)
- expect(mr_2.reload.head_pipeline_id).to eq(pipeline_3.id)
- expect(mr_3.reload.head_pipeline_id).to eq(pipeline_4.id)
- expect(mr_4.reload.head_pipeline_id).to be_nil
- end
- end
- end
-end
diff --git a/spec/migrations/backfill_and_add_not_null_constraint_to_released_at_column_on_releases_table_spec.rb b/spec/migrations/backfill_and_add_not_null_constraint_to_released_at_column_on_releases_table_spec.rb
new file mode 100644
index 00000000000..9cae1daacea
--- /dev/null
+++ b/spec/migrations/backfill_and_add_not_null_constraint_to_released_at_column_on_releases_table_spec.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require Rails.root.join('db', 'migrate', '20190628185004_backfill_and_add_not_null_constraint_to_released_at_column_on_releases_table.rb')
+
+describe BackfillAndAddNotNullConstraintToReleasedAtColumnOnReleasesTable, :migration do
+ let(:releases) { table(:releases) }
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+
+ subject(:migration) { described_class.new }
+
+ it 'fills released_at with the value of created_at' do
+ created_at_a = Time.zone.parse('2019-02-10T08:00:00Z')
+ created_at_b = Time.zone.parse('2019-03-10T18:00:00Z')
+ namespace = namespaces.create(name: 'foo', path: 'foo')
+ project = projects.create!(namespace_id: namespace.id)
+ release_a = releases.create!(project_id: project.id, created_at: created_at_a)
+ release_b = releases.create!(project_id: project.id, created_at: created_at_b)
+
+ disable_migrations_output { migration.up }
+
+ release_a.reload
+ release_b.reload
+ expect(release_a.released_at).to eq(created_at_a)
+ expect(release_b.released_at).to eq(created_at_b)
+ end
+end
diff --git a/spec/migrations/calculate_conv_dev_index_percentages_spec.rb b/spec/migrations/calculate_conv_dev_index_percentages_spec.rb
deleted file mode 100644
index 09c78d02890..00000000000
--- a/spec/migrations/calculate_conv_dev_index_percentages_spec.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-# encoding: utf-8
-
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20170803090603_calculate_conv_dev_index_percentages.rb')
-
-describe CalculateConvDevIndexPercentages, :migration do
- let(:migration) { described_class.new }
- let!(:conv_dev_index) do
- table(:conversational_development_index_metrics).create!(
- leader_issues: 9.256,
- leader_notes: 0,
- leader_milestones: 16.2456,
- leader_boards: 5.2123,
- leader_merge_requests: 1.2,
- leader_ci_pipelines: 12.1234,
- leader_environments: 3.3333,
- leader_deployments: 1.200,
- leader_projects_prometheus_active: 0.111,
- leader_service_desk_issues: 15.891,
- instance_issues: 1.234,
- instance_notes: 28.123,
- instance_milestones: 0,
- instance_boards: 3.254,
- instance_merge_requests: 0.6,
- instance_ci_pipelines: 2.344,
- instance_environments: 2.2222,
- instance_deployments: 0.771,
- instance_projects_prometheus_active: 0.109,
- instance_service_desk_issues: 13.345,
- percentage_issues: 0,
- percentage_notes: 0,
- percentage_milestones: 0,
- percentage_boards: 0,
- percentage_merge_requests: 0,
- percentage_ci_pipelines: 0,
- percentage_environments: 0,
- percentage_deployments: 0,
- percentage_projects_prometheus_active: 0,
- percentage_service_desk_issues: 0)
- end
-
- describe '#up' do
- it 'calculates percentages correctly' do
- migration.up
- conv_dev_index.reload
-
- expect(conv_dev_index.percentage_issues).to be_within(0.1).of(13.3)
- expect(conv_dev_index.percentage_notes).to be_zero # leader 0
- expect(conv_dev_index.percentage_milestones).to be_zero # instance 0
- expect(conv_dev_index.percentage_boards).to be_within(0.1).of(62.4)
- expect(conv_dev_index.percentage_merge_requests).to eq(50.0)
- expect(conv_dev_index.percentage_ci_pipelines).to be_within(0.1).of(19.3)
- expect(conv_dev_index.percentage_environments).to be_within(0.1).of(66.7)
- expect(conv_dev_index.percentage_deployments).to be_within(0.1).of(64.2)
- expect(conv_dev_index.percentage_projects_prometheus_active).to be_within(0.1).of(98.2)
- expect(conv_dev_index.percentage_service_desk_issues).to be_within(0.1).of(84.0)
- end
- end
-end
diff --git a/spec/migrations/clean_appearance_symlinks_spec.rb b/spec/migrations/clean_appearance_symlinks_spec.rb
deleted file mode 100644
index 9225dc0d894..00000000000
--- a/spec/migrations/clean_appearance_symlinks_spec.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20170613111224_clean_appearance_symlinks.rb')
-
-describe CleanAppearanceSymlinks do
- let(:migration) { described_class.new }
- let(:test_dir) { File.join(Rails.root, "tmp", "tests", "clean_appearance_test") }
- let(:uploads_dir) { File.join(test_dir, "public", "uploads") }
- let(:new_uploads_dir) { File.join(uploads_dir, "system") }
- let(:original_path) { File.join(new_uploads_dir, 'appearance') }
- let(:symlink_path) { File.join(uploads_dir, 'appearance') }
-
- before do
- FileUtils.remove_dir(test_dir) if File.directory?(test_dir)
- FileUtils.mkdir_p(uploads_dir)
- allow(migration).to receive(:base_directory).and_return(test_dir)
- allow(migration).to receive(:say)
- end
-
- describe "#up" do
- before do
- FileUtils.mkdir_p(original_path)
- FileUtils.ln_s(original_path, symlink_path)
- end
-
- it 'removes the symlink' do
- migration.up
-
- expect(File.symlink?(symlink_path)).to be(false)
- end
- end
-
- describe '#down' do
- before do
- FileUtils.mkdir_p(File.join(original_path))
- FileUtils.touch(File.join(original_path, 'dummy.file'))
- end
-
- it 'creates a symlink' do
- expected_path = File.join(symlink_path, "dummy.file")
- migration.down
-
- expect(File.exist?(expected_path)).to be(true)
- expect(File.symlink?(symlink_path)).to be(true)
- end
- end
-end
diff --git a/spec/migrations/clean_stage_id_reference_migration_spec.rb b/spec/migrations/clean_stage_id_reference_migration_spec.rb
deleted file mode 100644
index 9a581df28a2..00000000000
--- a/spec/migrations/clean_stage_id_reference_migration_spec.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'migrate', '20170710083355_clean_stage_id_reference_migration.rb')
-
-describe CleanStageIdReferenceMigration, :migration, :sidekiq, :redis do
- let(:migration_class) { 'MigrateBuildStageIdReference' }
- let(:migration) { spy('migration') }
-
- before do
- allow(Gitlab::BackgroundMigration.const_get(migration_class))
- .to receive(:new).and_return(migration)
- end
-
- context 'when there are pending background migrations' do
- it 'processes pending jobs synchronously' do
- Sidekiq::Testing.disable! do
- BackgroundMigrationWorker.perform_in(2.minutes, migration_class, [1, 1])
- BackgroundMigrationWorker.perform_async(migration_class, [1, 1])
-
- migrate!
-
- expect(migration).to have_received(:perform).with(1, 1).twice
- end
- end
- end
- context 'when there are no background migrations pending' do
- it 'does nothing' do
- Sidekiq::Testing.disable! do
- migrate!
-
- expect(migration).not_to have_received(:perform)
- end
- end
- end
-end
diff --git a/spec/migrations/clean_stages_statuses_migration_spec.rb b/spec/migrations/clean_stages_statuses_migration_spec.rb
deleted file mode 100644
index 38705f8eaae..00000000000
--- a/spec/migrations/clean_stages_statuses_migration_spec.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'migrate', '20170912113435_clean_stages_statuses_migration.rb')
-
-describe CleanStagesStatusesMigration, :migration, :sidekiq, :redis do
- let(:migration) { spy('migration') }
-
- before do
- allow(Gitlab::BackgroundMigration::MigrateStageStatus)
- .to receive(:new).and_return(migration)
- end
-
- context 'when there are pending background migrations' do
- it 'processes pending jobs synchronously' do
- Sidekiq::Testing.disable! do
- BackgroundMigrationWorker
- .perform_in(2.minutes, 'MigrateStageStatus', [1, 1])
- BackgroundMigrationWorker
- .perform_async('MigrateStageStatus', [1, 1])
-
- migrate!
-
- expect(migration).to have_received(:perform).with(1, 1).twice
- end
- end
- end
-
- context 'when there are no background migrations pending' do
- it 'does nothing' do
- Sidekiq::Testing.disable! do
- migrate!
-
- expect(migration).not_to have_received(:perform)
- end
- end
- end
-
- context 'when there are still unmigrated stages afterwards' do
- let(:stages) { table('ci_stages') }
-
- before do
- stages.create!(status: nil, name: 'build')
- stages.create!(status: nil, name: 'test')
- end
-
- it 'migrates statuses sequentially in batches' do
- migrate!
-
- expect(migration).to have_received(:perform).once
- end
- end
-end
diff --git a/spec/migrations/clean_upload_symlinks_spec.rb b/spec/migrations/clean_upload_symlinks_spec.rb
deleted file mode 100644
index 26653b9c008..00000000000
--- a/spec/migrations/clean_upload_symlinks_spec.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20170406111121_clean_upload_symlinks.rb')
-
-describe CleanUploadSymlinks do
- let(:migration) { described_class.new }
- let(:test_dir) { File.join(Rails.root, "tmp", "tests", "move_uploads_test") }
- let(:uploads_dir) { File.join(test_dir, "public", "uploads") }
- let(:new_uploads_dir) { File.join(uploads_dir, "-", "system") }
- let(:original_path) { File.join(new_uploads_dir, 'user') }
- let(:symlink_path) { File.join(uploads_dir, 'user') }
-
- before do
- FileUtils.remove_dir(test_dir) if File.directory?(test_dir)
- FileUtils.mkdir_p(uploads_dir)
- allow(migration).to receive(:base_directory).and_return(test_dir)
- allow(migration).to receive(:say)
- end
-
- describe "#up" do
- before do
- FileUtils.mkdir_p(original_path)
- FileUtils.ln_s(original_path, symlink_path)
- end
-
- it 'removes the symlink' do
- migration.up
-
- expect(File.symlink?(symlink_path)).to be(false)
- end
- end
-
- describe '#down' do
- before do
- FileUtils.mkdir_p(File.join(original_path))
- FileUtils.touch(File.join(original_path, 'dummy.file'))
- end
-
- it 'creates a symlink' do
- expected_path = File.join(symlink_path, "dummy.file")
- migration.down
-
- expect(File.exist?(expected_path)).to be(true)
- expect(File.symlink?(symlink_path)).to be(true)
- end
- end
-end
diff --git a/spec/migrations/cleanup_move_system_upload_folder_symlink_spec.rb b/spec/migrations/cleanup_move_system_upload_folder_symlink_spec.rb
deleted file mode 100644
index 3a9fa8c7113..00000000000
--- a/spec/migrations/cleanup_move_system_upload_folder_symlink_spec.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-require 'spec_helper'
-require Rails.root.join("db", "post_migrate", "20170717111152_cleanup_move_system_upload_folder_symlink.rb")
-
-describe CleanupMoveSystemUploadFolderSymlink do
- let(:migration) { described_class.new }
- let(:test_base) { File.join(Rails.root, 'tmp', 'tests', 'move-system-upload-folder') }
- let(:test_folder) { File.join(test_base, '-', 'system') }
-
- before do
- allow(migration).to receive(:base_directory).and_return(test_base)
- FileUtils.rm_rf(test_base)
- FileUtils.mkdir_p(test_folder)
- allow(migration).to receive(:say)
- end
-
- describe '#up' do
- before do
- FileUtils.ln_s(test_folder, File.join(test_base, 'system'))
- end
-
- it 'removes the symlink' do
- migration.up
-
- expect(File.exist?(File.join(test_base, 'system'))).to be_falsey
- end
- end
-
- describe '#down' do
- it 'creates the symlink' do
- migration.down
-
- expect(File.symlink?(File.join(test_base, 'system'))).to be_truthy
- end
- end
-end
diff --git a/spec/migrations/cleanup_nonexisting_namespace_pending_delete_projects_spec.rb b/spec/migrations/cleanup_nonexisting_namespace_pending_delete_projects_spec.rb
deleted file mode 100644
index 0e6bded29b4..00000000000
--- a/spec/migrations/cleanup_nonexisting_namespace_pending_delete_projects_spec.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20170816102555_cleanup_nonexisting_namespace_pending_delete_projects.rb')
-
-describe CleanupNonexistingNamespacePendingDeleteProjects, :migration do
- let(:projects) { table(:projects) }
- let(:namespaces) { table(:namespaces) }
-
- describe '#up' do
- let!(:some_project) { projects.create! }
- let(:namespace) { namespaces.create!(name: 'test', path: 'test') }
-
- it 'only cleans up when namespace does not exist' do
- projects.create!(pending_delete: true, namespace_id: namespace.id)
- project = projects.create!(pending_delete: true, namespace_id: 0)
-
- expect(NamespacelessProjectDestroyWorker).to receive(:bulk_perform_async).with([[project.id]])
-
- described_class.new.up
- end
-
- it 'does nothing when no pending delete projects without namespace found' do
- projects.create!(pending_delete: true, namespace_id: namespace.id)
-
- expect(NamespacelessProjectDestroyWorker).not_to receive(:bulk_perform_async)
-
- described_class.new.up
- end
- end
-end
diff --git a/spec/migrations/convert_custom_notification_settings_to_columns_spec.rb b/spec/migrations/convert_custom_notification_settings_to_columns_spec.rb
deleted file mode 100644
index d1bf6bdf9d6..00000000000
--- a/spec/migrations/convert_custom_notification_settings_to_columns_spec.rb
+++ /dev/null
@@ -1,120 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20170607121233_convert_custom_notification_settings_to_columns')
-
-describe ConvertCustomNotificationSettingsToColumns, :migration do
- let(:user_class) { table(:users) }
-
- let(:settings_params) do
- [
- { level: 0, events: [:new_note] }, # disabled, single event
- { level: 3, events: [:new_issue, :reopen_issue, :close_issue, :reassign_issue] }, # global, multiple events
- { level: 5, events: described_class::EMAIL_EVENTS }, # custom, all events
- { level: 5, events: [] } # custom, no events
- ]
- end
-
- let(:notification_settings_before) do
- settings_params.map do |params|
- events = {}
-
- params[:events].each do |event|
- events[event] = true
- end
-
- user = user_class.create!(email: "user-#{SecureRandom.hex}@example.org", username: "user-#{SecureRandom.hex}", encrypted_password: '12345678')
- create_params = { user_id: user.id, level: params[:level], events: events }
- notification_setting = described_class::NotificationSetting.create(create_params)
-
- [notification_setting, params]
- end
- end
-
- let(:notification_settings_after) do
- settings_params.map do |params|
- events = {}
-
- params[:events].each do |event|
- events[event] = true
- end
-
- user = user_class.create!(email: "user-#{SecureRandom.hex}@example.org", username: "user-#{SecureRandom.hex}", encrypted_password: '12345678')
- create_params = events.merge(user_id: user.id, level: params[:level])
- notification_setting = described_class::NotificationSetting.create(create_params)
-
- [notification_setting, params]
- end
- end
-
- describe '#up' do
- it 'migrates all settings where a custom event is enabled, even if they are not currently using the custom level' do
- notification_settings_before
-
- described_class.new.up
-
- notification_settings_before.each do |(notification_setting, params)|
- notification_setting.reload
-
- expect(notification_setting.read_attribute_before_type_cast(:events)).to be_nil
- expect(notification_setting.level).to eq(params[:level])
-
- described_class::EMAIL_EVENTS.each do |event|
- # We don't set the others to false, just let them default to nil
- expected = params[:events].include?(event) || nil
-
- expect(notification_setting.read_attribute(event)).to eq(expected)
- end
- end
- end
- end
-
- describe '#down' do
- it 'creates a custom events hash for all settings where at least one event is enabled' do
- notification_settings_after
-
- described_class.new.down
-
- notification_settings_after.each do |(notification_setting, params)|
- notification_setting.reload
-
- expect(notification_setting.level).to eq(params[:level])
-
- if params[:events].empty?
- # We don't migrate empty settings
- expect(notification_setting.events).to eq({})
- else
- described_class::EMAIL_EVENTS.each do |event|
- expected = params[:events].include?(event)
-
- expect(notification_setting.events[event]).to eq(expected)
- expect(notification_setting.read_attribute(event)).to be_nil
- end
- end
- end
- end
-
- it 'reverts the database to the state it was in before' do
- notification_settings_before
-
- described_class.new.up
- described_class.new.down
-
- notification_settings_before.each do |(notification_setting, params)|
- notification_setting.reload
-
- expect(notification_setting.level).to eq(params[:level])
-
- if params[:events].empty?
- # We don't migrate empty settings
- expect(notification_setting.events).to eq({})
- else
- described_class::EMAIL_EVENTS.each do |event|
- expected = params[:events].include?(event)
-
- expect(notification_setting.events[event]).to eq(expected)
- expect(notification_setting.read_attribute(event)).to be_nil
- end
- end
- end
- end
- end
-end
diff --git a/spec/migrations/delete_conflicting_redirect_routes_spec.rb b/spec/migrations/delete_conflicting_redirect_routes_spec.rb
deleted file mode 100644
index 8a191bd7139..00000000000
--- a/spec/migrations/delete_conflicting_redirect_routes_spec.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20170907170235_delete_conflicting_redirect_routes')
-
-describe DeleteConflictingRedirectRoutes, :migration, :sidekiq do
- let!(:redirect_routes) { table(:redirect_routes) }
- let!(:routes) { table(:routes) }
-
- around do |example|
- Timecop.freeze { example.run }
- end
-
- before do
- routes.create!(id: 1, source_id: 1, source_type: 'Namespace', path: 'foo1')
- routes.create!(id: 2, source_id: 2, source_type: 'Namespace', path: 'foo2')
- routes.create!(id: 3, source_id: 3, source_type: 'Namespace', path: 'foo3')
- routes.create!(id: 4, source_id: 4, source_type: 'Namespace', path: 'foo4')
- routes.create!(id: 5, source_id: 5, source_type: 'Namespace', path: 'foo5')
-
- # Valid redirects
- redirect_routes.create!(source_id: 1, source_type: 'Namespace', path: 'bar')
- redirect_routes.create!(source_id: 1, source_type: 'Namespace', path: 'bar2')
- redirect_routes.create!(source_id: 2, source_type: 'Namespace', path: 'bar3')
-
- # Conflicting redirects
- redirect_routes.create!(source_id: 2, source_type: 'Namespace', path: 'foo1')
- redirect_routes.create!(source_id: 1, source_type: 'Namespace', path: 'foo2')
- redirect_routes.create!(source_id: 1, source_type: 'Namespace', path: 'foo3')
- redirect_routes.create!(source_id: 1, source_type: 'Namespace', path: 'foo4')
- redirect_routes.create!(source_id: 1, source_type: 'Namespace', path: 'foo5')
- end
-
- # No-op. See https://gitlab.com/gitlab-com/infrastructure/issues/3460#note_53223252
- it 'NO-OP: does not schedule any background migrations' do
- Sidekiq::Testing.fake! do
- Timecop.freeze do
- migrate!
-
- expect(BackgroundMigrationWorker.jobs.size).to eq 0
- end
- end
- end
-end
diff --git a/spec/migrations/fix_wrongly_renamed_routes_spec.rb b/spec/migrations/fix_wrongly_renamed_routes_spec.rb
deleted file mode 100644
index 543cf55f076..00000000000
--- a/spec/migrations/fix_wrongly_renamed_routes_spec.rb
+++ /dev/null
@@ -1,86 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20170518231126_fix_wrongly_renamed_routes.rb')
-
-describe FixWronglyRenamedRoutes, :migration do
- let(:subject) { described_class.new }
- let(:namespaces_table) { table(:namespaces) }
- let(:projects_table) { table(:projects) }
- let(:routes_table) { table(:routes) }
- let(:broken_namespace) do
- namespaces_table.create!(name: 'apiis', path: 'apiis').tap do |namespace|
- routes_table.create!(source_type: 'Namespace', source_id: namespace.id, name: 'api0is', path: 'api0is')
- end
- end
- let(:broken_namespace_route) { routes_table.where(source_type: 'Namespace', source_id: broken_namespace.id).first }
-
- describe '#wrongly_renamed' do
- it "includes routes that have names that don't match their namespace" do
- broken_namespace
- other_namespace = namespaces_table.create!(name: 'api0', path: 'api0')
- routes_table.create!(source_type: 'Namespace', source_id: other_namespace.id, name: 'api0', path: 'api0')
-
- expect(subject.wrongly_renamed.map(&:id))
- .to contain_exactly(broken_namespace_route.id)
- end
- end
-
- describe "#paths_and_corrections" do
- it 'finds the wrong path and gets the correction from the namespace' do
- broken_namespace
- namespaces_table.create!(name: 'uploads-test', path: 'uploads-test').tap do |namespace|
- routes_table.create!(source_type: 'Namespace', source_id: namespace.id, name: 'uploads-test', path: 'uploads0-test')
- end
-
- expected_result = [
- { 'namespace_path' => 'apiis', 'path' => 'api0is' },
- { 'namespace_path' => 'uploads-test', 'path' => 'uploads0-test' }
- ]
-
- expect(subject.paths_and_corrections).to include(*expected_result)
- end
- end
-
- describe '#routes_in_namespace_query' do
- it 'includes only the required routes' do
- namespace = namespaces_table.create!(name: 'hello', path: 'hello')
- namespace_route = routes_table.create!(source_type: 'Namespace', source_id: namespace.id, name: 'hello', path: 'hello')
- project = projects_table.new(name: 'my-project', path: 'my-project', namespace_id: namespace.id).tap do |project|
- project.save!(validate: false)
- end
- routes_table.create!(source_type: 'Project', source_id: project.id, name: 'my-project', path: 'hello/my-project')
- _other_namespace = namespaces_table.create!(name: 'hello0', path: 'hello0')
-
- result = routes_table.where(subject.routes_in_namespace_query('hello'))
- project_route = routes_table.where(source_type: 'Project', source_id: project.id).first
-
- expect(result).to contain_exactly(namespace_route, project_route)
- end
- end
-
- describe '#up' do
- it 'renames incorrectly named routes' do
- broken_project =
- projects_table.new(name: 'broken-project', path: 'broken-project', namespace_id: broken_namespace.id).tap do |project|
- project.save!(validate: false)
- routes_table.create!(source_type: 'Project', source_id: project.id, name: 'broken-project', path: 'api0is/broken-project')
- end
-
- subject.up
-
- broken_project_route = routes_table.where(source_type: 'Project', source_id: broken_project.id).first
-
- expect(broken_project_route.path).to eq('apiis/broken-project')
- expect(broken_namespace_route.reload.path).to eq('apiis')
- end
-
- it "doesn't touch namespaces that look like something that should be renamed" do
- namespaces_table.create!(name: 'apiis', path: 'apiis')
- namespace = namespaces_table.create!(name: 'hello', path: 'api0')
- namespace_route = routes_table.create!(source_type: 'Namespace', source_id: namespace.id, name: 'hello', path: 'api0')
-
- subject.up
-
- expect(namespace_route.reload.path).to eq('api0')
- end
- end
-end
diff --git a/spec/migrations/issues_moved_to_id_foreign_key_spec.rb b/spec/migrations/issues_moved_to_id_foreign_key_spec.rb
deleted file mode 100644
index 71a4e71ac8a..00000000000
--- a/spec/migrations/issues_moved_to_id_foreign_key_spec.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'migrate', '20171106151218_issues_moved_to_id_foreign_key.rb')
-
-describe IssuesMovedToIdForeignKey, :migration do
- let(:issues) { table(:issues) }
-
- let!(:issue_third) { issues.create! }
- let!(:issue_second) { issues.create!(moved_to_id: issue_third.id) }
- let!(:issue_first) { issues.create!(moved_to_id: issue_second.id) }
-
- subject { described_class.new }
-
- it 'removes the orphaned moved_to_id' do
- subject.down
-
- issue_third.update!(moved_to_id: 0)
-
- subject.up
-
- expect(issue_first.reload.moved_to_id).to eq(issue_second.id)
- expect(issue_second.reload.moved_to_id).to eq(issue_third.id)
- expect(issue_third.reload.moved_to_id).to be_nil
- end
-end
diff --git a/spec/migrations/migrate_build_stage_reference_again_spec.rb b/spec/migrations/migrate_build_stage_reference_again_spec.rb
deleted file mode 100644
index 6be480ce58e..00000000000
--- a/spec/migrations/migrate_build_stage_reference_again_spec.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20170526190000_migrate_build_stage_reference_again.rb')
-
-describe MigrateBuildStageReferenceAgain, :migration do
- ##
- # Create test data - pipeline and CI/CD jobs.
- #
-
- let(:jobs) { table(:ci_builds) }
- let(:stages) { table(:ci_stages) }
- let(:pipelines) { table(:ci_pipelines) }
- let(:projects) { table(:projects) }
-
- before do
- # Create projects
- #
- projects.create!(id: 123, name: 'gitlab1', path: 'gitlab1')
- projects.create!(id: 456, name: 'gitlab2', path: 'gitlab2')
-
- # Create CI/CD pipelines
- #
- pipelines.create!(id: 1, project_id: 123, ref: 'master', sha: 'adf43c3a')
- pipelines.create!(id: 2, project_id: 456, ref: 'feature', sha: '21a3deb')
-
- # Create CI/CD jobs
- #
- jobs.create!(id: 1, commit_id: 1, project_id: 123, stage_idx: 2, stage: 'build')
- jobs.create!(id: 2, commit_id: 1, project_id: 123, stage_idx: 2, stage: 'build')
- jobs.create!(id: 3, commit_id: 1, project_id: 123, stage_idx: 1, stage: 'test')
- jobs.create!(id: 4, commit_id: 1, project_id: 123, stage_idx: 3, stage: 'deploy')
- jobs.create!(id: 5, commit_id: 2, project_id: 456, stage_idx: 2, stage: 'test:2')
- jobs.create!(id: 6, commit_id: 2, project_id: 456, stage_idx: 1, stage: 'test:1')
- jobs.create!(id: 7, commit_id: 2, project_id: 456, stage_idx: 1, stage: 'test:1')
- jobs.create!(id: 8, commit_id: 3, project_id: 789, stage_idx: 3, stage: 'deploy')
-
- # Create CI/CD stages
- #
- stages.create(id: 101, pipeline_id: 1, project_id: 123, name: 'test')
- stages.create(id: 102, pipeline_id: 1, project_id: 123, name: 'build')
- stages.create(id: 103, pipeline_id: 1, project_id: 123, name: 'deploy')
- stages.create(id: 104, pipeline_id: 2, project_id: 456, name: 'test:1')
- stages.create(id: 105, pipeline_id: 2, project_id: 456, name: 'test:2')
- stages.create(id: 106, pipeline_id: 2, project_id: 456, name: 'deploy')
- end
-
- it 'correctly migrate build stage references' do
- expect(jobs.where(stage_id: nil).count).to eq 8
-
- migrate!
-
- expect(jobs.where(stage_id: nil).count).to eq 1
-
- expect(jobs.find(1).stage_id).to eq 102
- expect(jobs.find(2).stage_id).to eq 102
- expect(jobs.find(3).stage_id).to eq 101
- expect(jobs.find(4).stage_id).to eq 103
- expect(jobs.find(5).stage_id).to eq 105
- expect(jobs.find(6).stage_id).to eq 104
- expect(jobs.find(7).stage_id).to eq 104
- expect(jobs.find(8).stage_id).to eq nil
- end
-end
diff --git a/spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb b/spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb
deleted file mode 100644
index ba4c66057d4..00000000000
--- a/spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb
+++ /dev/null
@@ -1,181 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20171013104327_migrate_gcp_clusters_to_new_clusters_architectures.rb')
-
-describe MigrateGcpClustersToNewClustersArchitectures, :migration do
- let(:projects) { table(:projects) }
- let(:project) { projects.create }
- let(:users) { table(:users) }
- let(:user) { users.create! }
- let(:service) { GcpMigrationSpec::KubernetesService.create!(project_id: project.id) }
-
- module GcpMigrationSpec
- class KubernetesService < ActiveRecord::Base
- self.table_name = 'services'
-
- serialize :properties, JSON
-
- default_value_for :active, true
- default_value_for :type, 'KubernetesService'
- default_value_for :properties, {
- api_url: 'https://kubernetes.example.com',
- token: 'a' * 40
- }
- end
- end
-
- context 'when cluster is being created' do
- let(:project_id) { project.id }
- let(:user_id) { user.id }
- let(:service_id) { service.id }
- let(:status) { 2 } # creating
- let(:gcp_cluster_size) { 1 }
- let(:created_at) { "'2017-10-17 20:24:02'" }
- let(:updated_at) { "'2017-10-17 20:28:44'" }
- let(:enabled) { true }
- let(:status_reason) { "''" }
- let(:project_namespace) { "'sample-app'" }
- let(:endpoint) { 'NULL' }
- let(:ca_cert) { 'NULL' }
- let(:encrypted_kubernetes_token) { 'NULL' }
- let(:encrypted_kubernetes_token_iv) { 'NULL' }
- let(:username) { 'NULL' }
- let(:encrypted_password) { 'NULL' }
- let(:encrypted_password_iv) { 'NULL' }
- let(:gcp_project_id) { "'gcp_project_id'" }
- let(:gcp_cluster_zone) { "'gcp_cluster_zone'" }
- let(:gcp_cluster_name) { "'gcp_cluster_name'" }
- let(:gcp_machine_type) { "'gcp_machine_type'" }
- let(:gcp_operation_id) { 'NULL' }
- let(:encrypted_gcp_token) { "'encrypted_gcp_token'" }
- let(:encrypted_gcp_token_iv) { "'encrypted_gcp_token_iv'" }
-
- let(:cluster) { described_class::Cluster.last }
- let(:cluster_id) { cluster.id }
-
- before do
- ActiveRecord::Base.connection.execute <<-SQL
- INSERT INTO gcp_clusters (project_id, user_id, service_id, status, gcp_cluster_size, created_at, updated_at, enabled, status_reason, project_namespace, endpoint, ca_cert, encrypted_kubernetes_token, encrypted_kubernetes_token_iv, username, encrypted_password, encrypted_password_iv, gcp_project_id, gcp_cluster_zone, gcp_cluster_name, gcp_machine_type, gcp_operation_id, encrypted_gcp_token, encrypted_gcp_token_iv)
- VALUES (#{project_id}, #{user_id}, #{service_id}, #{status}, #{gcp_cluster_size}, #{created_at}, #{updated_at}, #{enabled}, #{status_reason}, #{project_namespace}, #{endpoint}, #{ca_cert}, #{encrypted_kubernetes_token}, #{encrypted_kubernetes_token_iv}, #{username}, #{encrypted_password}, #{encrypted_password_iv}, #{gcp_project_id}, #{gcp_cluster_zone}, #{gcp_cluster_name}, #{gcp_machine_type}, #{gcp_operation_id}, #{encrypted_gcp_token}, #{encrypted_gcp_token_iv});
- SQL
- end
-
- it 'correctly migrate to new clusters architectures' do
- migrate!
-
- expect(described_class::Cluster.count).to eq(1)
- expect(described_class::ClustersProject.count).to eq(1)
- expect(described_class::ProvidersGcp.count).to eq(1)
- expect(described_class::PlatformsKubernetes.count).to eq(1)
-
- expect(cluster.user_id).to eq(user.id)
- expect(cluster.enabled).to be_truthy
- expect(cluster.name).to eq(gcp_cluster_name.delete!("'"))
- expect(cluster.provider_type).to eq('gcp')
- expect(cluster.platform_type).to eq('kubernetes')
-
- expect(cluster.project_ids).to include(project.id)
-
- expect(cluster.provider_gcp.cluster_id).to eq(cluster.id)
- expect(cluster.provider_gcp.status).to eq(status)
- expect(cluster.provider_gcp.status_reason).to eq(tr(status_reason))
- expect(cluster.provider_gcp.gcp_project_id).to eq(tr(gcp_project_id))
- expect(cluster.provider_gcp.zone).to eq(tr(gcp_cluster_zone))
- expect(cluster.provider_gcp.num_nodes).to eq(gcp_cluster_size)
- expect(cluster.provider_gcp.machine_type).to eq(tr(gcp_machine_type))
- expect(cluster.provider_gcp.operation_id).to be_nil
- expect(cluster.provider_gcp.endpoint).to be_nil
- expect(cluster.provider_gcp.encrypted_access_token).to eq(tr(encrypted_gcp_token))
- expect(cluster.provider_gcp.encrypted_access_token_iv).to eq(tr(encrypted_gcp_token_iv))
-
- expect(cluster.platform_kubernetes.cluster_id).to eq(cluster.id)
- expect(cluster.platform_kubernetes.api_url).to be_nil
- expect(cluster.platform_kubernetes.ca_cert).to be_nil
- expect(cluster.platform_kubernetes.namespace).to eq(tr(project_namespace))
- expect(cluster.platform_kubernetes.username).to be_nil
- expect(cluster.platform_kubernetes.encrypted_password).to be_nil
- expect(cluster.platform_kubernetes.encrypted_password_iv).to be_nil
- expect(cluster.platform_kubernetes.encrypted_token).to be_nil
- expect(cluster.platform_kubernetes.encrypted_token_iv).to be_nil
- end
- end
-
- context 'when cluster has been created' do
- let(:project_id) { project.id }
- let(:user_id) { user.id }
- let(:service_id) { service.id }
- let(:status) { 3 } # created
- let(:gcp_cluster_size) { 1 }
- let(:created_at) { "'2017-10-17 20:24:02'" }
- let(:updated_at) { "'2017-10-17 20:28:44'" }
- let(:enabled) { true }
- let(:status_reason) { "'general error'" }
- let(:project_namespace) { "'sample-app'" }
- let(:endpoint) { "'111.111.111.111'" }
- let(:ca_cert) { "'ca_cert'" }
- let(:encrypted_kubernetes_token) { "'encrypted_kubernetes_token'" }
- let(:encrypted_kubernetes_token_iv) { "'encrypted_kubernetes_token_iv'" }
- let(:username) { "'username'" }
- let(:encrypted_password) { "'encrypted_password'" }
- let(:encrypted_password_iv) { "'encrypted_password_iv'" }
- let(:gcp_project_id) { "'gcp_project_id'" }
- let(:gcp_cluster_zone) { "'gcp_cluster_zone'" }
- let(:gcp_cluster_name) { "'gcp_cluster_name'" }
- let(:gcp_machine_type) { "'gcp_machine_type'" }
- let(:gcp_operation_id) { "'gcp_operation_id'" }
- let(:encrypted_gcp_token) { "'encrypted_gcp_token'" }
- let(:encrypted_gcp_token_iv) { "'encrypted_gcp_token_iv'" }
-
- let(:cluster) { described_class::Cluster.last }
- let(:cluster_id) { cluster.id }
-
- before do
- ActiveRecord::Base.connection.execute <<-SQL
- INSERT INTO gcp_clusters (project_id, user_id, service_id, status, gcp_cluster_size, created_at, updated_at, enabled, status_reason, project_namespace, endpoint, ca_cert, encrypted_kubernetes_token, encrypted_kubernetes_token_iv, username, encrypted_password, encrypted_password_iv, gcp_project_id, gcp_cluster_zone, gcp_cluster_name, gcp_machine_type, gcp_operation_id, encrypted_gcp_token, encrypted_gcp_token_iv)
- VALUES (#{project_id}, #{user_id}, #{service_id}, #{status}, #{gcp_cluster_size}, #{created_at}, #{updated_at}, #{enabled}, #{status_reason}, #{project_namespace}, #{endpoint}, #{ca_cert}, #{encrypted_kubernetes_token}, #{encrypted_kubernetes_token_iv}, #{username}, #{encrypted_password}, #{encrypted_password_iv}, #{gcp_project_id}, #{gcp_cluster_zone}, #{gcp_cluster_name}, #{gcp_machine_type}, #{gcp_operation_id}, #{encrypted_gcp_token}, #{encrypted_gcp_token_iv});
- SQL
- end
-
- it 'correctly migrate to new clusters architectures' do
- migrate!
-
- expect(described_class::Cluster.count).to eq(1)
- expect(described_class::ClustersProject.count).to eq(1)
- expect(described_class::ProvidersGcp.count).to eq(1)
- expect(described_class::PlatformsKubernetes.count).to eq(1)
-
- expect(cluster.user_id).to eq(user.id)
- expect(cluster.enabled).to be_truthy
- expect(cluster.name).to eq(tr(gcp_cluster_name))
- expect(cluster.provider_type).to eq('gcp')
- expect(cluster.platform_type).to eq('kubernetes')
-
- expect(cluster.project_ids).to include(project.id)
-
- expect(cluster.provider_gcp.cluster_id).to eq(cluster.id)
- expect(cluster.provider_gcp.status).to eq(status)
- expect(cluster.provider_gcp.status_reason).to eq(tr(status_reason))
- expect(cluster.provider_gcp.gcp_project_id).to eq(tr(gcp_project_id))
- expect(cluster.provider_gcp.zone).to eq(tr(gcp_cluster_zone))
- expect(cluster.provider_gcp.num_nodes).to eq(gcp_cluster_size)
- expect(cluster.provider_gcp.machine_type).to eq(tr(gcp_machine_type))
- expect(cluster.provider_gcp.operation_id).to eq(tr(gcp_operation_id))
- expect(cluster.provider_gcp.endpoint).to eq(tr(endpoint))
- expect(cluster.provider_gcp.encrypted_access_token).to eq(tr(encrypted_gcp_token))
- expect(cluster.provider_gcp.encrypted_access_token_iv).to eq(tr(encrypted_gcp_token_iv))
-
- expect(cluster.platform_kubernetes.cluster_id).to eq(cluster.id)
- expect(cluster.platform_kubernetes.api_url).to eq('https://' + tr(endpoint))
- expect(cluster.platform_kubernetes.ca_cert).to eq(tr(ca_cert))
- expect(cluster.platform_kubernetes.namespace).to eq(tr(project_namespace))
- expect(cluster.platform_kubernetes.username).to eq(tr(username))
- expect(cluster.platform_kubernetes.encrypted_password).to eq(tr(encrypted_password))
- expect(cluster.platform_kubernetes.encrypted_password_iv).to eq(tr(encrypted_password_iv))
- expect(cluster.platform_kubernetes.encrypted_token).to eq(tr(encrypted_kubernetes_token))
- expect(cluster.platform_kubernetes.encrypted_token_iv).to eq(tr(encrypted_kubernetes_token_iv))
- end
- end
-
- def tr(str)
- str.delete("'")
- end
-end
diff --git a/spec/migrations/migrate_issues_to_ghost_user_spec.rb b/spec/migrations/migrate_issues_to_ghost_user_spec.rb
deleted file mode 100644
index 0016f058a17..00000000000
--- a/spec/migrations/migrate_issues_to_ghost_user_spec.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'migrate', '20170825104051_migrate_issues_to_ghost_user.rb')
-
-describe MigrateIssuesToGhostUser, :migration do
- describe '#up' do
- let(:projects) { table(:projects) }
- let(:issues) { table(:issues) }
- let(:users) { table(:users) }
-
- before do
- project = projects.create!(name: 'gitlab', namespace_id: 1)
- user = users.create(email: 'test@example.com')
- issues.create(title: 'Issue 1', author_id: nil, project_id: project.id)
- issues.create(title: 'Issue 2', author_id: user.id, project_id: project.id)
- end
-
- context 'when ghost user exists' do
- let!(:ghost) { users.create(ghost: true, email: 'ghost@example.com') }
-
- it 'does not create a new user' do
- expect { migrate! }.not_to change { User.count }
- end
-
- it 'migrates issues where author = nil to the ghost user' do
- migrate!
-
- expect(issues.first.reload.author_id).to eq(ghost.id)
- end
-
- it 'does not change issues authored by an existing user' do
- expect { migrate! }.not_to change { issues.second.reload.author_id}
- end
- end
-
- context 'when ghost user does not exist' do
- it 'creates a new user' do
- expect { migrate! }.to change { User.count }.by(1)
- end
-
- it 'migrates issues where author = nil to the ghost user' do
- migrate!
-
- expect(issues.first.reload.author_id).to eq(User.ghost.id)
- end
-
- it 'does not change issues authored by an existing user' do
- expect { migrate! }.not_to change { issues.second.reload.author_id}
- end
- end
- end
-end
diff --git a/spec/migrations/migrate_kubernetes_service_to_new_clusters_architectures_spec.rb b/spec/migrations/migrate_kubernetes_service_to_new_clusters_architectures_spec.rb
deleted file mode 100644
index df0015b6dd3..00000000000
--- a/spec/migrations/migrate_kubernetes_service_to_new_clusters_architectures_spec.rb
+++ /dev/null
@@ -1,312 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20171124104327_migrate_kubernetes_service_to_new_clusters_architectures.rb')
-
-describe MigrateKubernetesServiceToNewClustersArchitectures, :migration do
- context 'when unique KubernetesService exists' do
- shared_examples 'KubernetesService migration' do
- let(:sample_num) { 2 }
-
- let(:projects) do
- (1..sample_num).each_with_object([]) do |n, array|
- array << MigrateKubernetesServiceToNewClustersArchitectures::Project.create!
- end
- end
-
- let!(:kubernetes_services) do
- projects.map do |project|
- MigrateKubernetesServiceToNewClustersArchitectures::Service.create!(
- project: project,
- active: active,
- category: 'deployment',
- type: 'KubernetesService',
- properties: "{\"namespace\":\"prod\",\"api_url\":\"https://kubernetes#{project.id}.com\",\"ca_pem\":\"ca_pem#{project.id}\",\"token\":\"token#{project.id}\"}")
- end
- end
-
- it 'migrates the KubernetesService to Platform::Kubernetes' do
- expect { migrate! }.to change { MigrateKubernetesServiceToNewClustersArchitectures::Cluster.count }.by(sample_num)
-
- projects.each do |project|
- project.clusters.last.tap do |cluster|
- expect(cluster.enabled).to eq(active)
- expect(cluster.platform_kubernetes.api_url).to eq(project.kubernetes_service.api_url)
- expect(cluster.platform_kubernetes.ca_cert).to eq(project.kubernetes_service.ca_pem)
- expect(cluster.platform_kubernetes.token).to eq(project.kubernetes_service.token)
- expect(project.kubernetes_service).not_to be_active
- end
- end
- end
- end
-
- context 'when KubernetesService is active' do
- let(:active) { true }
-
- it_behaves_like 'KubernetesService migration'
- end
- end
-
- context 'when unique KubernetesService spawned from Service Template' do
- let(:sample_num) { 2 }
-
- let(:projects) do
- (1..sample_num).each_with_object([]) do |n, array|
- array << MigrateKubernetesServiceToNewClustersArchitectures::Project.create!
- end
- end
-
- let!(:kubernetes_service_template) do
- MigrateKubernetesServiceToNewClustersArchitectures::Service.create!(
- template: true,
- category: 'deployment',
- type: 'KubernetesService',
- properties: "{\"namespace\":\"prod\",\"api_url\":\"https://sample.kubernetes.com\",\"ca_pem\":\"ca_pem-sample\",\"token\":\"token-sample\"}")
- end
-
- let!(:kubernetes_services) do
- projects.map do |project|
- MigrateKubernetesServiceToNewClustersArchitectures::Service.create!(
- project: project,
- category: 'deployment',
- type: 'KubernetesService',
- properties: "{\"namespace\":\"prod\",\"api_url\":\"#{kubernetes_service_template.api_url}\",\"ca_pem\":\"#{kubernetes_service_template.ca_pem}\",\"token\":\"#{kubernetes_service_template.token}\"}")
- end
- end
-
- it 'migrates the KubernetesService to Platform::Kubernetes without template' do
- expect { migrate! }.to change { MigrateKubernetesServiceToNewClustersArchitectures::Cluster.count }.by(sample_num)
-
- projects.each do |project|
- project.clusters.last.tap do |cluster|
- expect(cluster.platform_kubernetes.api_url).to eq(project.kubernetes_service.api_url)
- expect(cluster.platform_kubernetes.ca_cert).to eq(project.kubernetes_service.ca_pem)
- expect(cluster.platform_kubernetes.token).to eq(project.kubernetes_service.token)
- expect(project.kubernetes_service).not_to be_active
- end
- end
- end
- end
-
- context 'when managed KubernetesService exists' do
- let(:project) { MigrateKubernetesServiceToNewClustersArchitectures::Project.create! }
-
- let(:cluster) do
- MigrateKubernetesServiceToNewClustersArchitectures::Cluster.create!(
- projects: [project],
- name: 'sample-cluster',
- platform_type: :kubernetes,
- provider_type: :user,
- platform_kubernetes_attributes: {
- api_url: 'https://sample.kubernetes.com',
- ca_cert: 'ca_pem-sample',
- token: 'token-sample'
- } )
- end
-
- let!(:kubernetes_service) do
- MigrateKubernetesServiceToNewClustersArchitectures::Service.create!(
- project: project,
- active: cluster.enabled,
- category: 'deployment',
- type: 'KubernetesService',
- properties: "{\"api_url\":\"#{cluster.platform_kubernetes.api_url}\"}")
- end
-
- it 'does not migrate the KubernetesService and disables the kubernetes_service' do # Because the corresponding Platform::Kubernetes already exists
- expect { migrate! }.not_to change { MigrateKubernetesServiceToNewClustersArchitectures::Cluster.count }
-
- kubernetes_service.reload
- expect(kubernetes_service).not_to be_active
- end
- end
-
- context 'when production cluster has already been existed' do # i.e. There are no environment_scope conflicts
- let(:project) { MigrateKubernetesServiceToNewClustersArchitectures::Project.create! }
-
- let(:cluster) do
- MigrateKubernetesServiceToNewClustersArchitectures::Cluster.create!(
- projects: [project],
- name: 'sample-cluster',
- platform_type: :kubernetes,
- provider_type: :user,
- environment_scope: 'production/*',
- platform_kubernetes_attributes: {
- api_url: 'https://sample.kubernetes.com',
- ca_cert: 'ca_pem-sample',
- token: 'token-sample'
- } )
- end
-
- let!(:kubernetes_service) do
- MigrateKubernetesServiceToNewClustersArchitectures::Service.create!(
- project: project,
- active: true,
- category: 'deployment',
- type: 'KubernetesService',
- properties: "{\"api_url\":\"https://debug.kube.com\"}")
- end
-
- it 'migrates the KubernetesService to Platform::Kubernetes' do
- expect { migrate! }.to change { MigrateKubernetesServiceToNewClustersArchitectures::Cluster.count }.by(1)
-
- kubernetes_service.reload
- project.clusters.last.tap do |cluster|
- expect(cluster.environment_scope).to eq('*')
- expect(cluster.platform_kubernetes.api_url).to eq(kubernetes_service.api_url)
- expect(cluster.platform_kubernetes.ca_cert).to eq(kubernetes_service.ca_pem)
- expect(cluster.platform_kubernetes.token).to eq(kubernetes_service.token)
- expect(kubernetes_service).not_to be_active
- end
- end
- end
-
- context 'when default cluster has already been existed' do
- let(:project) { MigrateKubernetesServiceToNewClustersArchitectures::Project.create! }
-
- let!(:cluster) do
- MigrateKubernetesServiceToNewClustersArchitectures::Cluster.create!(
- projects: [project],
- name: 'sample-cluster',
- platform_type: :kubernetes,
- provider_type: :user,
- environment_scope: '*',
- platform_kubernetes_attributes: {
- api_url: 'https://sample.kubernetes.com',
- ca_cert: 'ca_pem-sample',
- token: 'token-sample'
- } )
- end
-
- let!(:kubernetes_service) do
- MigrateKubernetesServiceToNewClustersArchitectures::Service.create!(
- project: project,
- active: true,
- category: 'deployment',
- type: 'KubernetesService',
- properties: "{\"api_url\":\"https://debug.kube.com\"}")
- end
-
- it 'migrates the KubernetesService to Platform::Kubernetes with dedicated environment_scope' do # Because environment_scope is duplicated
- expect { migrate! }.to change { MigrateKubernetesServiceToNewClustersArchitectures::Cluster.count }.by(1)
-
- kubernetes_service.reload
- project.clusters.last.tap do |cluster|
- expect(cluster.environment_scope).to eq('migrated/*')
- expect(cluster.platform_kubernetes.api_url).to eq(kubernetes_service.api_url)
- expect(cluster.platform_kubernetes.ca_cert).to eq(kubernetes_service.ca_pem)
- expect(cluster.platform_kubernetes.token).to eq(kubernetes_service.token)
- expect(kubernetes_service).not_to be_active
- end
- end
- end
-
- context 'when default cluster and migrated cluster has already been existed' do
- let(:project) { MigrateKubernetesServiceToNewClustersArchitectures::Project.create! }
-
- let!(:cluster) do
- MigrateKubernetesServiceToNewClustersArchitectures::Cluster.create!(
- projects: [project],
- name: 'sample-cluster',
- platform_type: :kubernetes,
- provider_type: :user,
- environment_scope: '*',
- platform_kubernetes_attributes: {
- api_url: 'https://sample.kubernetes.com',
- ca_cert: 'ca_pem-sample',
- token: 'token-sample'
- } )
- end
-
- let!(:migrated_cluster) do
- MigrateKubernetesServiceToNewClustersArchitectures::Cluster.create!(
- projects: [project],
- name: 'sample-cluster',
- platform_type: :kubernetes,
- provider_type: :user,
- environment_scope: 'migrated/*',
- platform_kubernetes_attributes: {
- api_url: 'https://sample.kubernetes.com',
- ca_cert: 'ca_pem-sample',
- token: 'token-sample'
- } )
- end
-
- let!(:kubernetes_service) do
- MigrateKubernetesServiceToNewClustersArchitectures::Service.create!(
- project: project,
- active: true,
- category: 'deployment',
- type: 'KubernetesService',
- properties: "{\"api_url\":\"https://debug.kube.com\"}")
- end
-
- it 'migrates the KubernetesService to Platform::Kubernetes with dedicated environment_scope' do # Because environment_scope is duplicated
- expect { migrate! }.to change { MigrateKubernetesServiceToNewClustersArchitectures::Cluster.count }.by(1)
-
- kubernetes_service.reload
- project.clusters.last.tap do |cluster|
- expect(cluster.environment_scope).to eq('migrated0/*')
- expect(cluster.platform_kubernetes.api_url).to eq(kubernetes_service.api_url)
- expect(cluster.platform_kubernetes.ca_cert).to eq(kubernetes_service.ca_pem)
- expect(cluster.platform_kubernetes.token).to eq(kubernetes_service.token)
- expect(kubernetes_service).not_to be_active
- end
- end
- end
-
- context 'when KubernetesService has nullified parameters' do
- let(:project) { MigrateKubernetesServiceToNewClustersArchitectures::Project.create! }
-
- before do
- MigrateKubernetesServiceToNewClustersArchitectures::Service.create!(
- project: project,
- active: false,
- category: 'deployment',
- type: 'KubernetesService',
- properties: "{}")
- end
-
- it 'does not migrate the KubernetesService and disables the kubernetes_service' do
- expect { migrate! }.not_to change { MigrateKubernetesServiceToNewClustersArchitectures::Cluster.count }
-
- expect(project.kubernetes_service).not_to be_active
- end
- end
-
- # Platforms::Kubernetes validates `token` reagdless of the activeness,
- # whereas KubernetesService validates `token` if only it's activated
- # However, in this migration file, there are no validations because of the re-defined model class
- # therefore, we should safely add this raw to Platform::Kubernetes
- context 'when KubernetesService has empty token' do
- let(:project) { MigrateKubernetesServiceToNewClustersArchitectures::Project.create! }
-
- before do
- MigrateKubernetesServiceToNewClustersArchitectures::Service.create!(
- project: project,
- active: false,
- category: 'deployment',
- type: 'KubernetesService',
- properties: "{\"namespace\":\"prod\",\"api_url\":\"http://111.111.111.111\",\"ca_pem\":\"a\",\"token\":\"\"}")
- end
-
- it 'does not migrate the KubernetesService and disables the kubernetes_service' do
- expect { migrate! }.to change { MigrateKubernetesServiceToNewClustersArchitectures::Cluster.count }.by(1)
-
- project.clusters.last.tap do |cluster|
- expect(cluster.environment_scope).to eq('*')
- expect(cluster.platform_kubernetes.namespace).to eq('prod')
- expect(cluster.platform_kubernetes.api_url).to eq('http://111.111.111.111')
- expect(cluster.platform_kubernetes.ca_cert).to eq('a')
- expect(cluster.platform_kubernetes.token).to be_empty
- expect(project.kubernetes_service).not_to be_active
- end
- end
- end
-
- context 'when KubernetesService does not exist' do
- let!(:project) { MigrateKubernetesServiceToNewClustersArchitectures::Project.create! }
-
- it 'does not migrate the KubernetesService' do
- expect { migrate! }.not_to change { MigrateKubernetesServiceToNewClustersArchitectures::Cluster.count }
- end
- end
-end
diff --git a/spec/migrations/migrate_old_artifacts_spec.rb b/spec/migrations/migrate_old_artifacts_spec.rb
deleted file mode 100644
index bc826d91471..00000000000
--- a/spec/migrations/migrate_old_artifacts_spec.rb
+++ /dev/null
@@ -1,140 +0,0 @@
-# encoding: utf-8
-
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20170523083112_migrate_old_artifacts.rb')
-
-# Adding the ci_job_artifacts table (from the 20170918072948 schema)
-# makes the use of model code below easier.
-describe MigrateOldArtifacts, :migration, schema: 20170918072948 do
- let(:migration) { described_class.new }
- let!(:directory) { Dir.mktmpdir }
-
- before do
- allow(Gitlab.config.artifacts).to receive(:path).and_return(directory)
- end
-
- after do
- FileUtils.remove_entry_secure(directory)
- end
-
- context 'with migratable data' do
- let(:projects) { table(:projects) }
- let(:ci_pipelines) { table(:ci_pipelines) }
- let(:ci_builds) { table(:ci_builds) }
-
- let!(:project1) { projects.create!(ci_id: 2) }
- let!(:project2) { projects.create!(ci_id: 3) }
- let!(:project3) { projects.create! }
-
- let!(:pipeline1) { ci_pipelines.create!(project_id: project1.id) }
- let!(:pipeline2) { ci_pipelines.create!(project_id: project2.id) }
- let!(:pipeline3) { ci_pipelines.create!(project_id: project3.id) }
-
- let!(:build_with_legacy_artifacts) { ci_builds.create!(commit_id: pipeline1.id, project_id: project1.id, type: 'Ci::Build').becomes(Ci::Build) }
- let!(:build_without_artifacts) { ci_builds.create!(commit_id: pipeline1.id, project_id: project1.id, type: 'Ci::Build').becomes(Ci::Build) }
- let!(:build2) { ci_builds.create!(commit_id: pipeline2.id, project_id: project2.id, type: 'Ci::Build').becomes(Ci::Build) }
- let!(:build3) { ci_builds.create!(commit_id: pipeline3.id, project_id: project3.id, type: 'Ci::Build').becomes(Ci::Build) }
-
- before do
- setup_builds(build2, build3)
-
- store_artifacts_in_legacy_path(build_with_legacy_artifacts)
- end
-
- it "legacy artifacts are not accessible" do
- expect(build_with_legacy_artifacts.artifacts?).to be_falsey
- end
-
- describe '#min_id' do
- subject { migration.send(:min_id) }
-
- it 'returns the newest build for which ci_id is not defined' do
- is_expected.to eq(build3.id)
- end
- end
-
- describe '#builds_with_artifacts' do
- subject { migration.send(:builds_with_artifacts).map(&:id) }
-
- it 'returns a list of builds that has artifacts and could be migrated' do
- is_expected.to contain_exactly(build_with_legacy_artifacts.id, build2.id)
- end
- end
-
- describe '#up' do
- context 'when migrating artifacts' do
- before do
- migration.up
- end
-
- it 'all files do have artifacts' do
- Ci::Build.with_artifacts_archive do |build|
- expect(build).to have_artifacts
- end
- end
-
- it 'artifacts are no longer present on legacy path' do
- expect(File.exist?(legacy_path(build_with_legacy_artifacts))).to eq(false)
- end
- end
-
- context 'when there are artifacts in old and new directory' do
- before do
- store_artifacts_in_legacy_path(build2)
-
- migration.up
- end
-
- it 'does not move old files' do
- expect(File.exist?(legacy_path(build2))).to eq(true)
- end
- end
- end
-
- private
-
- def store_artifacts_in_legacy_path(build)
- FileUtils.mkdir_p(legacy_path(build))
-
- FileUtils.copy(
- Rails.root.join('spec/fixtures/ci_build_artifacts.zip'),
- File.join(legacy_path(build), "ci_build_artifacts.zip"))
-
- FileUtils.copy(
- Rails.root.join('spec/fixtures/ci_build_artifacts_metadata.gz'),
- File.join(legacy_path(build), "ci_build_artifacts_metadata.gz"))
-
- build.update_columns(
- artifacts_file: 'ci_build_artifacts.zip',
- artifacts_metadata: 'ci_build_artifacts_metadata.gz')
-
- build.reload
- end
-
- def legacy_path(build)
- File.join(directory,
- build.created_at.utc.strftime('%Y_%m'),
- build.project.ci_id.to_s,
- build.id.to_s)
- end
-
- def new_legacy_path(build)
- File.join(directory,
- build.created_at.utc.strftime('%Y_%m'),
- build.project_id.to_s,
- build.id.to_s)
- end
-
- def setup_builds(*builds)
- builds.each do |build|
- FileUtils.mkdir_p(new_legacy_path(build))
-
- build.update_columns(
- artifacts_file: 'ci_build_artifacts.zip',
- artifacts_metadata: 'ci_build_artifacts_metadata.gz')
-
- build.reload
- end
- end
- end
-end
diff --git a/spec/migrations/migrate_pipeline_sidekiq_queues_spec.rb b/spec/migrations/migrate_pipeline_sidekiq_queues_spec.rb
deleted file mode 100644
index e38044ccceb..00000000000
--- a/spec/migrations/migrate_pipeline_sidekiq_queues_spec.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20170822101017_migrate_pipeline_sidekiq_queues.rb')
-
-describe MigratePipelineSidekiqQueues, :sidekiq, :redis do
- include Gitlab::Database::MigrationHelpers
- include StubWorker
-
- context 'when there are jobs in the queues' do
- it 'correctly migrates queue when migrating up' do
- Sidekiq::Testing.disable! do
- stub_worker(queue: :pipeline).perform_async('Something', [1])
- stub_worker(queue: :build).perform_async('Something', [1])
-
- described_class.new.up
-
- expect(sidekiq_queue_length('pipeline')).to eq 0
- expect(sidekiq_queue_length('build')).to eq 0
- expect(sidekiq_queue_length('pipeline_default')).to eq 2
- end
- end
-
- it 'correctly migrates queue when migrating down' do
- Sidekiq::Testing.disable! do
- stub_worker(queue: :pipeline_default).perform_async('Class', [1])
- stub_worker(queue: :pipeline_processing).perform_async('Class', [2])
- stub_worker(queue: :pipeline_hooks).perform_async('Class', [3])
- stub_worker(queue: :pipeline_cache).perform_async('Class', [4])
-
- described_class.new.down
-
- expect(sidekiq_queue_length('pipeline')).to eq 4
- expect(sidekiq_queue_length('pipeline_default')).to eq 0
- expect(sidekiq_queue_length('pipeline_processing')).to eq 0
- expect(sidekiq_queue_length('pipeline_hooks')).to eq 0
- expect(sidekiq_queue_length('pipeline_cache')).to eq 0
- end
- end
- end
-
- context 'when there are no jobs in the queues' do
- it 'does not raise error when migrating up' do
- expect { described_class.new.up }.not_to raise_error
- end
-
- it 'does not raise error when migrating down' do
- expect { described_class.new.down }.not_to raise_error
- end
- end
-end
diff --git a/spec/migrations/migrate_pipeline_stages_spec.rb b/spec/migrations/migrate_pipeline_stages_spec.rb
deleted file mode 100644
index c47f2bb8ff9..00000000000
--- a/spec/migrations/migrate_pipeline_stages_spec.rb
+++ /dev/null
@@ -1,56 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20170526185842_migrate_pipeline_stages.rb')
-
-describe MigratePipelineStages, :migration do
- ##
- # Create test data - pipeline and CI/CD jobs.
- #
-
- let(:jobs) { table(:ci_builds) }
- let(:stages) { table(:ci_stages) }
- let(:pipelines) { table(:ci_pipelines) }
- let(:projects) { table(:projects) }
-
- before do
- # Create projects
- #
- projects.create!(id: 123, name: 'gitlab1', path: 'gitlab1')
- projects.create!(id: 456, name: 'gitlab2', path: 'gitlab2')
-
- # Create CI/CD pipelines
- #
- pipelines.create!(id: 1, project_id: 123, ref: 'master', sha: 'adf43c3a')
- pipelines.create!(id: 2, project_id: 456, ref: 'feature', sha: '21a3deb')
-
- # Create CI/CD jobs
- #
- jobs.create!(id: 1, commit_id: 1, project_id: 123, stage_idx: 2, stage: 'build')
- jobs.create!(id: 2, commit_id: 1, project_id: 123, stage_idx: 2, stage: 'build')
- jobs.create!(id: 3, commit_id: 1, project_id: 123, stage_idx: 1, stage: 'test')
- jobs.create!(id: 4, commit_id: 1, project_id: 123, stage_idx: 1, stage: 'test')
- jobs.create!(id: 5, commit_id: 1, project_id: 123, stage_idx: 3, stage: 'deploy')
- jobs.create!(id: 6, commit_id: 2, project_id: 456, stage_idx: 3, stage: 'deploy')
- jobs.create!(id: 7, commit_id: 2, project_id: 456, stage_idx: 2, stage: 'test:2')
- jobs.create!(id: 8, commit_id: 2, project_id: 456, stage_idx: 1, stage: 'test:1')
- jobs.create!(id: 9, commit_id: 2, project_id: 456, stage_idx: 1, stage: 'test:1')
- jobs.create!(id: 10, commit_id: 2, project_id: 456, stage_idx: 2, stage: 'test:2')
- jobs.create!(id: 11, commit_id: 3, project_id: 456, stage_idx: 3, stage: 'deploy')
- jobs.create!(id: 12, commit_id: 2, project_id: 789, stage_idx: 3, stage: 'deploy')
- end
-
- it 'correctly migrates pipeline stages' do
- expect(stages.count).to be_zero
-
- migrate!
-
- expect(stages.count).to eq 6
- expect(stages.all.pluck(:name))
- .to match_array %w[test build deploy test:1 test:2 deploy]
- expect(stages.where(pipeline_id: 1).order(:id).pluck(:name))
- .to eq %w[test build deploy]
- expect(stages.where(pipeline_id: 2).order(:id).pluck(:name))
- .to eq %w[test:1 test:2 deploy]
- expect(stages.where(pipeline_id: 3).count).to be_zero
- expect(stages.where(project_id: 789).count).to be_zero
- end
-end
diff --git a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb
deleted file mode 100644
index 6219a67c900..00000000000
--- a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb
+++ /dev/null
@@ -1,197 +0,0 @@
-# encoding: utf-8
-
-require 'spec_helper'
-require Rails.root.join('db', 'migrate', '20161124141322_migrate_process_commit_worker_jobs.rb')
-
-describe MigrateProcessCommitWorkerJobs do
- set(:project) { create(:project, :legacy_storage, :repository) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- set(:user) { create(:user) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- let(:commit) do
- Gitlab::Git::Commit.last(project.repository.raw)
- end
-
- describe 'Project' do
- describe 'find_including_path' do
- it 'returns Project instances' do
- expect(described_class::Project.find_including_path(project.id))
- .to be_an_instance_of(described_class::Project)
- end
-
- it 'selects the full path for every Project' do
- migration_project = described_class::Project
- .find_including_path(project.id)
-
- expect(migration_project[:path_with_namespace])
- .to eq(project.full_path)
- end
- end
-
- describe '#repository' do
- it 'returns a mock implemention of ::Repository' do
- migration_project = described_class::Project
- .find_including_path(project.id)
-
- expect(migration_project.repository).to respond_to(:storage)
- expect(migration_project.repository).to respond_to(:gitaly_repository)
- end
- end
- end
-
- describe '#up', :clean_gitlab_redis_shared_state do
- let(:migration) { described_class.new }
-
- def job_count
- Sidekiq.redis { |r| r.llen('queue:process_commit') }
- end
-
- def pop_job
- JSON.parse(Sidekiq.redis { |r| r.lpop('queue:process_commit') })
- end
-
- before do
- Sidekiq.redis do |redis|
- job = JSON.dump(args: [project.id, user.id, commit.id])
- redis.lpush('queue:process_commit', job)
- end
- end
-
- it 'skips jobs using a project that no longer exists' do
- allow(described_class::Project).to receive(:find_including_path)
- .with(project.id)
- .and_return(nil)
-
- migration.up
-
- expect(job_count).to eq(0)
- end
-
- it 'skips jobs using commits that no longer exist' do
- allow_any_instance_of(Gitlab::GitalyClient::CommitService)
- .to receive(:find_commit)
- .with(commit.id)
- .and_return(nil)
-
- migration.up
-
- expect(job_count).to eq(0)
- end
-
- it 'inserts migrated jobs back into the queue' do
- migration.up
-
- expect(job_count).to eq(1)
- end
-
- it 'encodes data to UTF-8' do
- allow(commit).to receive(:body)
- .and_return('김치'.force_encoding('BINARY'))
-
- migration.up
-
- job = pop_job
-
- # We don't care so much about what is being stored, instead we just want
- # to make sure the encoding is right so that JSON encoding the data
- # doesn't produce any errors.
- expect(job['args'][2]['message'].encoding).to eq(Encoding::UTF_8)
- end
-
- context 'a migrated job' do
- let(:job) do
- migration.up
- pop_job
- end
-
- let(:commit_hash) do
- job['args'][2]
- end
-
- it 'includes the project ID' do
- expect(job['args'][0]).to eq(project.id)
- end
-
- it 'includes the user ID' do
- expect(job['args'][1]).to eq(user.id)
- end
-
- it 'includes the commit ID' do
- expect(commit_hash['id']).to eq(commit.id)
- end
-
- it 'includes the commit message' do
- expect(commit_hash['message']).to eq(commit.message)
- end
-
- it 'includes the parent IDs' do
- expect(commit_hash['parent_ids']).to eq(commit.parent_ids)
- end
-
- it 'includes the author date' do
- expect(commit_hash['authored_date']).to eq(commit.authored_date.to_s)
- end
-
- it 'includes the author name' do
- expect(commit_hash['author_name']).to eq(commit.author_name)
- end
-
- it 'includes the author Email' do
- expect(commit_hash['author_email']).to eq(commit.author_email)
- end
-
- it 'includes the commit date' do
- expect(commit_hash['committed_date']).to eq(commit.committed_date.to_s)
- end
-
- it 'includes the committer name' do
- expect(commit_hash['committer_name']).to eq(commit.committer_name)
- end
-
- it 'includes the committer Email' do
- expect(commit_hash['committer_email']).to eq(commit.committer_email)
- end
- end
- end
-
- describe '#down', :clean_gitlab_redis_shared_state do
- let(:migration) { described_class.new }
-
- def job_count
- Sidekiq.redis { |r| r.llen('queue:process_commit') }
- end
-
- before do
- Sidekiq.redis do |redis|
- job = JSON.dump(args: [project.id, user.id, commit.id])
- redis.lpush('queue:process_commit', job)
-
- migration.up
- end
- end
-
- it 'pushes migrated jobs back into the queue' do
- migration.down
-
- expect(job_count).to eq(1)
- end
-
- context 'a migrated job' do
- let(:job) do
- migration.down
-
- JSON.parse(Sidekiq.redis { |r| r.lpop('queue:process_commit') })
- end
-
- it 'includes the project ID' do
- expect(job['args'][0]).to eq(project.id)
- end
-
- it 'includes the user ID' do
- expect(job['args'][1]).to eq(user.id)
- end
-
- it 'includes the commit SHA' do
- expect(job['args'][2]).to eq(commit.id)
- end
- end
- end
-end
diff --git a/spec/migrations/migrate_stage_id_reference_in_background_spec.rb b/spec/migrations/migrate_stage_id_reference_in_background_spec.rb
deleted file mode 100644
index dd6f5325750..00000000000
--- a/spec/migrations/migrate_stage_id_reference_in_background_spec.rb
+++ /dev/null
@@ -1,55 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20170628080858_migrate_stage_id_reference_in_background')
-
-describe MigrateStageIdReferenceInBackground, :migration, :sidekiq do
- let(:jobs) { table(:ci_builds) }
- let(:stages) { table(:ci_stages) }
- let(:pipelines) { table(:ci_pipelines) }
- let(:projects) { table(:projects) }
-
- before do
- stub_const("#{described_class.name}::BATCH_SIZE", 3)
- stub_const("#{described_class.name}::RANGE_SIZE", 2)
-
- projects.create!(id: 123, name: 'gitlab1', path: 'gitlab1')
- projects.create!(id: 345, name: 'gitlab2', path: 'gitlab2')
-
- pipelines.create!(id: 1, project_id: 123, ref: 'master', sha: 'adf43c3a')
- pipelines.create!(id: 2, project_id: 345, ref: 'feature', sha: 'cdf43c3c')
-
- jobs.create!(id: 1, commit_id: 1, project_id: 123, stage_idx: 2, stage: 'build')
- jobs.create!(id: 2, commit_id: 1, project_id: 123, stage_idx: 2, stage: 'build')
- jobs.create!(id: 3, commit_id: 1, project_id: 123, stage_idx: 1, stage: 'test')
- jobs.create!(id: 4, commit_id: 1, project_id: 123, stage_idx: 3, stage: 'deploy')
- jobs.create!(id: 5, commit_id: 2, project_id: 345, stage_idx: 1, stage: 'test')
-
- stages.create(id: 101, pipeline_id: 1, project_id: 123, name: 'test')
- stages.create(id: 102, pipeline_id: 1, project_id: 123, name: 'build')
- stages.create(id: 103, pipeline_id: 1, project_id: 123, name: 'deploy')
-
- jobs.create!(id: 6, commit_id: 2, project_id: 345, stage_id: 101, stage_idx: 1, stage: 'test')
- end
-
- it 'correctly schedules background migrations' do
- Sidekiq::Testing.fake! do
- Timecop.freeze do
- migrate!
-
- expect(described_class::MIGRATION).to be_scheduled_delayed_migration(2.minutes, 1, 2)
- expect(described_class::MIGRATION).to be_scheduled_delayed_migration(2.minutes, 3, 3)
- expect(described_class::MIGRATION).to be_scheduled_delayed_migration(4.minutes, 4, 5)
- expect(BackgroundMigrationWorker.jobs.size).to eq 3
- end
- end
- end
-
- it 'schedules background migrations' do
- perform_enqueued_jobs do
- expect(jobs.where(stage_id: nil).count).to eq 5
-
- migrate!
-
- expect(jobs.where(stage_id: nil).count).to eq 1
- end
- end
-end
diff --git a/spec/migrations/migrate_stages_statuses_spec.rb b/spec/migrations/migrate_stages_statuses_spec.rb
deleted file mode 100644
index 5483e24fce7..00000000000
--- a/spec/migrations/migrate_stages_statuses_spec.rb
+++ /dev/null
@@ -1,68 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20170711145558_migrate_stages_statuses.rb')
-
-describe MigrateStagesStatuses, :sidekiq, :migration do
- let(:jobs) { table(:ci_builds) }
- let(:stages) { table(:ci_stages) }
- let(:pipelines) { table(:ci_pipelines) }
- let(:projects) { table(:projects) }
-
- STATUSES = { created: 0, pending: 1, running: 2, success: 3,
- failed: 4, canceled: 5, skipped: 6, manual: 7 }.freeze
-
- before do
- stub_const("#{described_class.name}::BATCH_SIZE", 2)
- stub_const("#{described_class.name}::RANGE_SIZE", 1)
-
- projects.create!(id: 1, name: 'gitlab1', path: 'gitlab1')
- projects.create!(id: 2, name: 'gitlab2', path: 'gitlab2')
-
- pipelines.create!(id: 1, project_id: 1, ref: 'master', sha: 'adf43c3a')
- pipelines.create!(id: 2, project_id: 2, ref: 'feature', sha: '21a3deb')
-
- create_job(project: 1, pipeline: 1, stage: 'test', status: 'success')
- create_job(project: 1, pipeline: 1, stage: 'test', status: 'running')
- create_job(project: 1, pipeline: 1, stage: 'build', status: 'success')
- create_job(project: 1, pipeline: 1, stage: 'build', status: 'failed')
- create_job(project: 2, pipeline: 2, stage: 'test', status: 'success')
- create_job(project: 2, pipeline: 2, stage: 'test', status: 'success')
- create_job(project: 2, pipeline: 2, stage: 'test', status: 'failed', retried: true)
-
- stages.create!(id: 1, pipeline_id: 1, project_id: 1, name: 'test', status: nil)
- stages.create!(id: 2, pipeline_id: 1, project_id: 1, name: 'build', status: nil)
- stages.create!(id: 3, pipeline_id: 2, project_id: 2, name: 'test', status: nil)
- end
-
- it 'correctly migrates stages statuses' do
- perform_enqueued_jobs do
- expect(stages.where(status: nil).count).to eq 3
-
- migrate!
-
- expect(stages.where(status: nil)).to be_empty
- expect(stages.all.order('id ASC').pluck(:status))
- .to eq [STATUSES[:running], STATUSES[:failed], STATUSES[:success]]
- end
- end
-
- it 'correctly schedules background migrations' do
- Sidekiq::Testing.fake! do
- Timecop.freeze do
- migrate!
-
- expect(described_class::MIGRATION).to be_scheduled_delayed_migration(5.minutes, 1, 1)
- expect(described_class::MIGRATION).to be_scheduled_delayed_migration(5.minutes, 2, 2)
- expect(described_class::MIGRATION).to be_scheduled_delayed_migration(10.minutes, 3, 3)
- expect(BackgroundMigrationWorker.jobs.size).to eq 3
- end
- end
- end
-
- def create_job(project:, pipeline:, stage:, status:, **opts)
- stages = { test: 1, build: 2, deploy: 3 }
-
- jobs.create!(project_id: project, commit_id: pipeline,
- stage_idx: stages[stage.to_sym], stage: stage,
- status: status, **opts)
- end
-end
diff --git a/spec/migrations/migrate_user_activities_to_users_last_activity_on_spec.rb b/spec/migrations/migrate_user_activities_to_users_last_activity_on_spec.rb
deleted file mode 100644
index 88aef3b70b4..00000000000
--- a/spec/migrations/migrate_user_activities_to_users_last_activity_on_spec.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-# encoding: utf-8
-
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20170324160416_migrate_user_activities_to_users_last_activity_on.rb')
-
-describe MigrateUserActivitiesToUsersLastActivityOn, :clean_gitlab_redis_shared_state, :migration do
- let(:migration) { described_class.new }
- let!(:user_active_1) { table(:users).create!(email: 'test1', username: 'test1') }
- let!(:user_active_2) { table(:users).create!(email: 'test2', username: 'test2') }
-
- def record_activity(user, time)
- Gitlab::Redis::SharedState.with do |redis|
- redis.zadd(described_class::USER_ACTIVITY_SET_KEY, time.to_i, user.username)
- end
- end
-
- around do |example|
- Timecop.freeze { example.run }
- end
-
- before do
- record_activity(user_active_1, described_class::TIME_WHEN_ACTIVITY_SET_WAS_INTRODUCED + 2.months)
- record_activity(user_active_2, described_class::TIME_WHEN_ACTIVITY_SET_WAS_INTRODUCED + 3.months)
- mute_stdout { migration.up }
- end
-
- describe '#up' do
- it 'fills last_activity_on from the legacy Redis Sorted Set' do
- expect(user_active_1.reload.last_activity_on).to eq((described_class::TIME_WHEN_ACTIVITY_SET_WAS_INTRODUCED + 2.months).to_date)
- expect(user_active_2.reload.last_activity_on).to eq((described_class::TIME_WHEN_ACTIVITY_SET_WAS_INTRODUCED + 3.months).to_date)
- end
- end
-
- describe '#down' do
- it 'sets last_activity_on to NULL for all users' do
- mute_stdout { migration.down }
-
- expect(user_active_1.reload.last_activity_on).to be_nil
- expect(user_active_2.reload.last_activity_on).to be_nil
- end
- end
-
- def mute_stdout
- orig_stdout = $stdout
- $stdout = StringIO.new
- yield
- $stdout = orig_stdout
- end
-end
diff --git a/spec/migrations/migrate_user_authentication_token_to_personal_access_token_spec.rb b/spec/migrations/migrate_user_authentication_token_to_personal_access_token_spec.rb
deleted file mode 100644
index b4834705011..00000000000
--- a/spec/migrations/migrate_user_authentication_token_to_personal_access_token_spec.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'migrate', '20171012125712_migrate_user_authentication_token_to_personal_access_token.rb')
-
-describe MigrateUserAuthenticationTokenToPersonalAccessToken, :migration do
- let(:users) { table(:users) }
- let(:personal_access_tokens) { table(:personal_access_tokens) }
-
- let!(:user) { users.create!(id: 1, email: 'user@example.com', authentication_token: 'user-token', admin: false) }
- let!(:admin) { users.create!(id: 2, email: 'admin@example.com', authentication_token: 'admin-token', admin: true) }
-
- it 'migrates private tokens to Personal Access Tokens' do
- migrate!
-
- expect(personal_access_tokens.count).to eq(2)
-
- user_token = personal_access_tokens.find_by(user_id: user.id)
- admin_token = personal_access_tokens.find_by(user_id: admin.id)
-
- expect(user_token.token).to eq('user-token')
- expect(admin_token.token).to eq('admin-token')
-
- expect(user_token.scopes).to eq(%w[api].to_yaml)
- expect(admin_token.scopes).to eq(%w[api sudo].to_yaml)
- end
-end
diff --git a/spec/migrations/migrate_user_project_view_spec.rb b/spec/migrations/migrate_user_project_view_spec.rb
deleted file mode 100644
index a0179ab3ceb..00000000000
--- a/spec/migrations/migrate_user_project_view_spec.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# encoding: utf-8
-
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20170406142253_migrate_user_project_view.rb')
-
-describe MigrateUserProjectView, :migration do
- let(:migration) { described_class.new }
- let!(:user) { table(:users).create!(project_view: User.project_views['readme']) }
-
- describe '#up' do
- it 'updates project view setting with new value' do
- migration.up
-
- expect(user.reload.project_view).to eq(User.project_views['files'])
- end
- end
-end
diff --git a/spec/migrations/move_personal_snippets_files_spec.rb b/spec/migrations/move_personal_snippets_files_spec.rb
deleted file mode 100644
index d94ae1e52f5..00000000000
--- a/spec/migrations/move_personal_snippets_files_spec.rb
+++ /dev/null
@@ -1,197 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20170612071012_move_personal_snippets_files.rb')
-
-describe MovePersonalSnippetsFiles, :migration do
- let(:migration) { described_class.new }
- let(:test_dir) { File.join(Rails.root, "tmp", "tests", "move_snippet_files_test") }
- let(:uploads_dir) { File.join(test_dir, 'uploads') }
- let(:new_uploads_dir) { File.join(uploads_dir, '-', 'system') }
-
- let(:notes) { table(:notes) }
- let(:snippets) { table(:snippets) }
- let(:uploads) { table(:uploads) }
-
- let(:user) { table(:users).create!(email: 'user@example.com', projects_limit: 10) }
- let(:project) { table(:projects).create!(name: 'gitlab', namespace_id: 1) }
-
- before do
- allow(CarrierWave).to receive(:root).and_return(test_dir)
- allow(migration).to receive(:base_directory).and_return(test_dir)
- FileUtils.remove_dir(test_dir) if File.directory?(test_dir)
- allow(migration).to receive(:say)
- end
-
- describe "#up" do
- let(:snippet) do
- snippet = snippets.create!(author_id: user.id)
- create_upload('picture.jpg', snippet)
- snippet.update(description: markdown_linking_file('picture.jpg', snippet))
- snippet
- end
-
- let(:snippet_with_missing_file) do
- snippet = snippets.create!(author_id: user.id, project_id: project.id)
- create_upload('picture.jpg', snippet, create_file: false)
- snippet.update(description: markdown_linking_file('picture.jpg', snippet))
- snippet
- end
-
- it 'moves the files' do
- source_path = File.join(uploads_dir, model_file_path('picture.jpg', snippet))
- destination_path = File.join(new_uploads_dir, model_file_path('picture.jpg', snippet))
-
- migration.up
-
- expect(File.exist?(source_path)).to be_falsy
- expect(File.exist?(destination_path)).to be_truthy
- end
-
- describe 'updating the markdown' do
- it 'includes the new path when the file exists' do
- secret = "secret#{snippet.id}"
- file_location = "/uploads/-/system/personal_snippet/#{snippet.id}/#{secret}/picture.jpg"
-
- migration.up
-
- expect(snippet.reload.description).to include(file_location)
- end
-
- it 'does not update the markdown when the file is missing' do
- secret = "secret#{snippet_with_missing_file.id}"
- file_location = "/uploads/personal_snippet/#{snippet_with_missing_file.id}/#{secret}/picture.jpg"
-
- migration.up
-
- expect(snippet_with_missing_file.reload.description).to include(file_location)
- end
-
- it 'updates the note markdown' do
- secret = "secret#{snippet.id}"
- file_location = "/uploads/-/system/personal_snippet/#{snippet.id}/#{secret}/picture.jpg"
- markdown = markdown_linking_file('picture.jpg', snippet)
- note = notes.create!(noteable_id: snippet.id,
- noteable_type: Snippet,
- note: "with #{markdown}",
- author_id: user.id)
-
- migration.up
-
- expect(note.reload.note).to include(file_location)
- end
- end
- end
-
- describe "#down" do
- let(:snippet) do
- snippet = snippets.create!(author_id: user.id)
- create_upload('picture.jpg', snippet, in_new_path: true)
- snippet.update(description: markdown_linking_file('picture.jpg', snippet, in_new_path: true))
- snippet
- end
-
- let(:snippet_with_missing_file) do
- snippet = snippets.create!(author_id: user.id)
- create_upload('picture.jpg', snippet, create_file: false, in_new_path: true)
- snippet.update(description: markdown_linking_file('picture.jpg', snippet, in_new_path: true))
- snippet
- end
-
- it 'moves the files' do
- source_path = File.join(new_uploads_dir, model_file_path('picture.jpg', snippet))
- destination_path = File.join(uploads_dir, model_file_path('picture.jpg', snippet))
-
- migration.down
-
- expect(File.exist?(source_path)).to be_falsey
- expect(File.exist?(destination_path)).to be_truthy
- end
-
- describe 'updating the markdown' do
- it 'includes the new path when the file exists' do
- secret = "secret#{snippet.id}"
- file_location = "/uploads/personal_snippet/#{snippet.id}/#{secret}/picture.jpg"
-
- migration.down
-
- expect(snippet.reload.description).to include(file_location)
- end
-
- it 'keeps the markdown as is when the file is missing' do
- secret = "secret#{snippet_with_missing_file.id}"
- file_location = "/uploads/-/system/personal_snippet/#{snippet_with_missing_file.id}/#{secret}/picture.jpg"
-
- migration.down
-
- expect(snippet_with_missing_file.reload.description).to include(file_location)
- end
-
- it 'updates the note markdown' do
- markdown = markdown_linking_file('picture.jpg', snippet, in_new_path: true)
- secret = "secret#{snippet.id}"
- file_location = "/uploads/personal_snippet/#{snippet.id}/#{secret}/picture.jpg"
- note = notes.create!(noteable_id: snippet.id,
- noteable_type: Snippet,
- note: "with #{markdown}",
- author_id: user.id)
-
- migration.down
-
- expect(note.reload.note).to include(file_location)
- end
- end
- end
-
- describe '#update_markdown' do
- it 'escapes sql in the snippet description' do
- migration.instance_variable_set('@source_relative_location', '/uploads/personal_snippet')
- migration.instance_variable_set('@destination_relative_location', '/uploads/system/personal_snippet')
-
- secret = '123456789'
- filename = 'hello.jpg'
- snippet = snippets.create!(author_id: user.id)
-
- path_before = "/uploads/personal_snippet/#{snippet.id}/#{secret}/#{filename}"
- path_after = "/uploads/system/personal_snippet/#{snippet.id}/#{secret}/#{filename}"
- description_before = "Hello world; ![image](#{path_before})'; select * from users;"
- description_after = "Hello world; ![image](#{path_after})'; select * from users;"
-
- migration.update_markdown(snippet.id, secret, filename, description_before)
-
- expect(snippet.reload.description).to eq(description_after)
- end
- end
-
- def create_upload(filename, snippet, create_file: true, in_new_path: false)
- secret = "secret#{snippet.id}"
- absolute_path = if in_new_path
- File.join(new_uploads_dir, model_file_path(filename, snippet))
- else
- File.join(uploads_dir, model_file_path(filename, snippet))
- end
-
- if create_file
- FileUtils.mkdir_p(File.dirname(absolute_path))
- FileUtils.touch(absolute_path)
- end
-
- uploads.create!(model_id: snippet.id,
- model_type: snippet.class,
- path: "#{secret}/#{filename}",
- uploader: PersonalFileUploader,
- size: 100.kilobytes)
- end
-
- def markdown_linking_file(filename, snippet, in_new_path: false)
- markdown = "![#{filename.split('.')[0]}]"
- markdown += '(/uploads'
- markdown += '/-/system' if in_new_path
- markdown += "/#{model_file_path(filename, snippet)})"
- markdown
- end
-
- def model_file_path(filename, snippet)
- secret = "secret#{snippet.id}"
-
- File.join('personal_snippet', snippet.id.to_s, secret, filename)
- end
-end
diff --git a/spec/migrations/move_system_upload_folder_spec.rb b/spec/migrations/move_system_upload_folder_spec.rb
deleted file mode 100644
index d3180477db3..00000000000
--- a/spec/migrations/move_system_upload_folder_spec.rb
+++ /dev/null
@@ -1,80 +0,0 @@
-require 'spec_helper'
-require Rails.root.join("db", "migrate", "20170717074009_move_system_upload_folder.rb")
-
-describe MoveSystemUploadFolder do
- let(:migration) { described_class.new }
- let(:test_base) { File.join(Rails.root, 'tmp', 'tests', 'move-system-upload-folder') }
-
- before do
- allow(migration).to receive(:base_directory).and_return(test_base)
- FileUtils.rm_rf(test_base)
- FileUtils.mkdir_p(test_base)
- allow(migration).to receive(:say)
- end
-
- describe '#up' do
- let(:test_folder) { File.join(test_base, 'system') }
- let(:test_file) { File.join(test_folder, 'file') }
-
- before do
- FileUtils.mkdir_p(test_folder)
- FileUtils.touch(test_file)
- end
-
- it 'moves the related folder' do
- migration.up
-
- expect(File.exist?(File.join(test_base, '-', 'system', 'file'))).to be_truthy
- end
-
- it 'creates a symlink linking making the new folder available on the old path' do
- migration.up
-
- expect(File.symlink?(File.join(test_base, 'system'))).to be_truthy
- expect(File.exist?(File.join(test_base, 'system', 'file'))).to be_truthy
- end
-
- it 'does not move if the target directory already exists' do
- FileUtils.mkdir_p(File.join(test_base, '-', 'system'))
-
- expect(FileUtils).not_to receive(:mv)
- expect(migration).to receive(:say).with(/already exists. No need to redo the move/)
-
- migration.up
- end
- end
-
- describe '#down' do
- let(:test_folder) { File.join(test_base, '-', 'system') }
- let(:test_file) { File.join(test_folder, 'file') }
-
- before do
- FileUtils.mkdir_p(test_folder)
- FileUtils.touch(test_file)
- end
-
- it 'moves the system folder back to the old location' do
- migration.down
-
- expect(File.exist?(File.join(test_base, 'system', 'file'))).to be_truthy
- end
-
- it 'removes the symlink if it existed' do
- FileUtils.ln_s(test_folder, File.join(test_base, 'system'))
-
- migration.down
-
- expect(File.directory?(File.join(test_base, 'system'))).to be_truthy
- expect(File.symlink?(File.join(test_base, 'system'))).to be_falsey
- end
-
- it 'does not move if the old directory already exists' do
- FileUtils.mkdir_p(File.join(test_base, 'system'))
-
- expect(FileUtils).not_to receive(:mv)
- expect(migration).to receive(:say).with(/already exists and is not a symlink, no need to revert/)
-
- migration.down
- end
- end
-end
diff --git a/spec/migrations/move_uploads_to_system_dir_spec.rb b/spec/migrations/move_uploads_to_system_dir_spec.rb
deleted file mode 100644
index ca11a2004c5..00000000000
--- a/spec/migrations/move_uploads_to_system_dir_spec.rb
+++ /dev/null
@@ -1,68 +0,0 @@
-require "spec_helper"
-require Rails.root.join("db", "migrate", "20170316163845_move_uploads_to_system_dir.rb")
-
-describe MoveUploadsToSystemDir do
- let(:migration) { described_class.new }
- let(:test_dir) { File.join(Rails.root, "tmp", "move_uploads_test") }
- let(:uploads_dir) { File.join(test_dir, "public", "uploads") }
- let(:new_uploads_dir) { File.join(uploads_dir, "-", "system") }
-
- before do
- FileUtils.remove_dir(test_dir) if File.directory?(test_dir)
- FileUtils.mkdir_p(uploads_dir)
- allow(migration).to receive(:base_directory).and_return(test_dir)
- allow(migration).to receive(:say)
- end
-
- describe "#up" do
- before do
- FileUtils.mkdir_p(File.join(uploads_dir, 'user'))
- FileUtils.touch(File.join(uploads_dir, 'user', 'dummy.file'))
- end
-
- it 'moves the directory to the new path' do
- expected_path = File.join(new_uploads_dir, 'user', 'dummy.file')
-
- migration.up
-
- expect(File.exist?(expected_path)).to be(true)
- end
-
- it 'creates a symlink in the old location' do
- symlink_path = File.join(uploads_dir, 'user')
- expected_path = File.join(symlink_path, 'dummy.file')
-
- migration.up
-
- expect(File.exist?(expected_path)).to be(true)
- expect(File.symlink?(symlink_path)).to be(true)
- end
- end
-
- describe "#down" do
- before do
- FileUtils.mkdir_p(File.join(new_uploads_dir, 'user'))
- FileUtils.touch(File.join(new_uploads_dir, 'user', 'dummy.file'))
- end
-
- it 'moves the directory to the old path' do
- expected_path = File.join(uploads_dir, 'user', 'dummy.file')
-
- migration.down
-
- expect(File.exist?(expected_path)).to be(true)
- end
-
- it 'removes the symlink if it existed' do
- FileUtils.ln_s(File.join(new_uploads_dir, 'user'), File.join(uploads_dir, 'user'))
-
- directory = File.join(uploads_dir, 'user')
- expected_path = File.join(directory, 'dummy.file')
-
- migration.down
-
- expect(File.exist?(expected_path)).to be(true)
- expect(File.symlink?(directory)).to be(false)
- end
- end
-end
diff --git a/spec/migrations/normalize_ldap_extern_uids_spec.rb b/spec/migrations/normalize_ldap_extern_uids_spec.rb
deleted file mode 100644
index a23a5d54e0a..00000000000
--- a/spec/migrations/normalize_ldap_extern_uids_spec.rb
+++ /dev/null
@@ -1,56 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20170921101004_normalize_ldap_extern_uids')
-
-describe NormalizeLdapExternUids, :migration, :sidekiq do
- let!(:identities) { table(:identities) }
-
- around do |example|
- Timecop.freeze { example.run }
- end
-
- before do
- stub_const("Gitlab::Database::MigrationHelpers::BACKGROUND_MIGRATION_BATCH_SIZE", 2)
- stub_const("Gitlab::Database::MigrationHelpers::BACKGROUND_MIGRATION_JOB_BUFFER_SIZE", 2)
-
- # LDAP identities
- (1..4).each do |i|
- identities.create!(id: i, provider: 'ldapmain', extern_uid: " uid = foo #{i}, ou = People, dc = example, dc = com ", user_id: i)
- end
-
- # Non-LDAP identity
- identities.create!(id: 5, provider: 'foo', extern_uid: " uid = foo 5, ou = People, dc = example, dc = com ", user_id: 5)
- end
-
- it 'correctly schedules background migrations' do
- Sidekiq::Testing.fake! do
- Timecop.freeze do
- migrate!
-
- expect(BackgroundMigrationWorker.jobs[0]['args']).to eq([described_class::MIGRATION, [1, 2]])
- expect(BackgroundMigrationWorker.jobs[0]['at']).to eq(2.minutes.from_now.to_f)
- expect(BackgroundMigrationWorker.jobs[1]['args']).to eq([described_class::MIGRATION, [3, 4]])
- expect(BackgroundMigrationWorker.jobs[1]['at']).to eq(4.minutes.from_now.to_f)
- expect(BackgroundMigrationWorker.jobs[2]['args']).to eq([described_class::MIGRATION, [5, 5]])
- expect(BackgroundMigrationWorker.jobs[2]['at']).to eq(6.minutes.from_now.to_f)
- expect(BackgroundMigrationWorker.jobs.size).to eq 3
- end
- end
- end
-
- it 'migrates the LDAP identities' do
- perform_enqueued_jobs do
- migrate!
- identities.where(id: 1..4).each do |identity|
- expect(identity.extern_uid).to eq("uid=foo #{identity.id},ou=people,dc=example,dc=com")
- end
- end
- end
-
- it 'does not modify non-LDAP identities' do
- perform_enqueued_jobs do
- migrate!
- identity = identities.last
- expect(identity.extern_uid).to eq(" uid = foo 5, ou = People, dc = example, dc = com ")
- end
- end
-end
diff --git a/spec/migrations/populate_can_push_from_deploy_keys_projects_spec.rb b/spec/migrations/populate_can_push_from_deploy_keys_projects_spec.rb
deleted file mode 100644
index 0ff98933d5c..00000000000
--- a/spec/migrations/populate_can_push_from_deploy_keys_projects_spec.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'migrate', '20171215113714_populate_can_push_from_deploy_keys_projects.rb')
-
-describe PopulateCanPushFromDeployKeysProjects, :migration do
- let(:migration) { described_class.new }
- let(:deploy_keys) { table(:keys) }
- let(:deploy_keys_projects) { table(:deploy_keys_projects) }
- let(:projects) { table(:projects) }
-
- before do
- deploy_keys.inheritance_column = nil
-
- projects.create!(id: 1, name: 'gitlab1', path: 'gitlab1')
- (1..10).each do |index|
- deploy_keys.create!(id: index, title: 'dummy', type: 'DeployKey', key: Spec::Support::Helpers::KeyGeneratorHelper.new(1024).generate + ' dummy@gitlab.com')
- deploy_keys_projects.create!(id: index, deploy_key_id: index, project_id: 1)
- end
- end
-
- describe '#up' do
- it 'migrates can_push from deploy_keys to deploy_keys_projects' do
- deploy_keys.limit(5).update_all(can_push: true)
-
- expected = deploy_keys.order(:id).pluck(:id, :can_push)
-
- migration.up
-
- expect(deploy_keys_projects.order(:id).pluck(:deploy_key_id, :can_push)).to eq expected
- end
- end
-
- describe '#down' do
- it 'migrates can_push from deploy_keys_projects to deploy_keys' do
- deploy_keys_projects.limit(5).update_all(can_push: true)
-
- expected = deploy_keys_projects.order(:id).pluck(:deploy_key_id, :can_push)
-
- migration.down
-
- expect(deploy_keys.order(:id).pluck(:id, :can_push)).to eq expected
- end
- end
-end
diff --git a/spec/migrations/remove_assignee_id_from_issue_spec.rb b/spec/migrations/remove_assignee_id_from_issue_spec.rb
deleted file mode 100644
index 2c6f992d3ae..00000000000
--- a/spec/migrations/remove_assignee_id_from_issue_spec.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20170523073948_remove_assignee_id_from_issue.rb')
-
-describe RemoveAssigneeIdFromIssue, :migration do
- let(:issues) { table(:issues) }
- let(:issue_assignees) { table(:issue_assignees) }
- let(:users) { table(:users) }
-
- let!(:user_1) { users.create(email: 'email1@example.com') }
- let!(:user_2) { users.create(email: 'email2@example.com') }
- let!(:user_3) { users.create(email: 'email3@example.com') }
-
- def create_issue(assignees:)
- issues.create.tap do |issue|
- assignees.each do |assignee|
- issue_assignees.create(issue_id: issue.id, user_id: assignee.id)
- end
- end
- end
-
- let!(:issue_single_assignee) { create_issue(assignees: [user_1]) }
- let!(:issue_no_assignee) { create_issue(assignees: []) }
- let!(:issue_multiple_assignees) { create_issue(assignees: [user_2, user_3]) }
-
- describe '#down' do
- it 'sets the assignee_id to a random matching assignee from the assignees table' do
- migrate!
- disable_migrations_output { described_class.new.down }
-
- expect(issue_single_assignee.reload.assignee_id).to eq(user_1.id)
- expect(issue_no_assignee.reload.assignee_id).to be_nil
- expect(issue_multiple_assignees.reload.assignee_id).to eq(user_2.id).or(user_3.id)
-
- disable_migrations_output { described_class.new.up }
- end
- end
-end
diff --git a/spec/migrations/remove_dot_git_from_usernames_spec.rb b/spec/migrations/remove_dot_git_from_usernames_spec.rb
deleted file mode 100644
index f11880a83e9..00000000000
--- a/spec/migrations/remove_dot_git_from_usernames_spec.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-# encoding: utf-8
-
-require 'spec_helper'
-require Rails.root.join('db', 'migrate', '20161226122833_remove_dot_git_from_usernames.rb')
-
-describe RemoveDotGitFromUsernames do
- let(:user) { create(:user) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- let(:migration) { described_class.new }
-
- describe '#up' do
- before do
- update_namespace(user, 'test.git')
- end
-
- it 'renames user with .git in username' do
- migration.up
-
- expect(user.reload.username).to eq('test_git')
- expect(user.namespace.reload.path).to eq('test_git')
- expect(user.namespace.route.path).to eq('test_git')
- end
- end
-
- context 'when new path exists already' do
- describe '#up' do
- let(:user2) { create(:user) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
-
- before do
- update_namespace(user, 'test.git')
- update_namespace(user2, 'test_git')
-
- default_hash = Gitlab.config.repositories.storages.default.to_h
- default_hash['path'] = 'tmp/tests/custom_repositories'
- storages = { 'default' => Gitlab::GitalyClient::StorageSettings.new(default_hash) }
-
- allow(Gitlab.config.repositories).to receive(:storages).and_return(storages)
- allow(migration).to receive(:route_exists?).with('test_git').and_return(true)
- allow(migration).to receive(:route_exists?).with('test_git1').and_return(false)
- end
-
- it 'renames user with .git in username' do
- migration.up
-
- expect(user.reload.username).to eq('test_git1')
- expect(user.namespace.reload.path).to eq('test_git1')
- expect(user.namespace.route.path).to eq('test_git1')
- end
- end
- end
-
- def update_namespace(user, path)
- namespace = user.namespace
- namespace.path = path
- namespace.save!(validate: false)
-
- user.update_column(:username, path)
- end
-end
diff --git a/spec/migrations/remove_duplicate_mr_events_spec.rb b/spec/migrations/remove_duplicate_mr_events_spec.rb
deleted file mode 100644
index 2509ac6afd6..00000000000
--- a/spec/migrations/remove_duplicate_mr_events_spec.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20170815060945_remove_duplicate_mr_events.rb')
-
-describe RemoveDuplicateMrEvents, :delete do
- let(:migration) { described_class.new }
-
- describe '#up' do
- let(:user) { create(:user) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- let(:merge_requests) { create_list(:merge_request, 2) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- let(:issue) { create(:issue) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- let!(:events) do
- [
- create(:event, :created, author: user, target: merge_requests.first), # rubocop:disable RSpec/FactoriesInMigrationSpecs
- create(:event, :created, author: user, target: merge_requests.first), # rubocop:disable RSpec/FactoriesInMigrationSpecs
- create(:event, :updated, author: user, target: merge_requests.first), # rubocop:disable RSpec/FactoriesInMigrationSpecs
- create(:event, :created, author: user, target: merge_requests.second), # rubocop:disable RSpec/FactoriesInMigrationSpecs
- create(:event, :created, author: user, target: issue), # rubocop:disable RSpec/FactoriesInMigrationSpecs
- create(:event, :created, author: user, target: issue) # rubocop:disable RSpec/FactoriesInMigrationSpecs
- ]
- end
-
- it 'removes duplicated merge request create records' do
- expect { migration.up }.to change { Event.count }.from(6).to(5)
- end
- end
-end
diff --git a/spec/migrations/remove_empty_fork_networks_spec.rb b/spec/migrations/remove_empty_fork_networks_spec.rb
deleted file mode 100644
index f6d030ab25c..00000000000
--- a/spec/migrations/remove_empty_fork_networks_spec.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20171114104051_remove_empty_fork_networks.rb')
-
-describe RemoveEmptyForkNetworks, :migration do
- let!(:fork_networks) { table(:fork_networks) }
- let!(:projects) { table(:projects) }
- let!(:fork_network_members) { table(:fork_network_members) }
-
- let(:deleted_project) { projects.create! }
- let!(:empty_network) { fork_networks.create!(id: 1, root_project_id: deleted_project.id) }
- let!(:other_network) { fork_networks.create!(id: 2, root_project_id: projects.create.id) }
-
- before do
- fork_network_members.create(fork_network_id: empty_network.id,
- project_id: empty_network.root_project_id)
- fork_network_members.create(fork_network_id: other_network.id,
- project_id: other_network.root_project_id)
-
- deleted_project.destroy!
- end
-
- after do
- Upload.reset_column_information
- end
-
- it 'deletes only the fork network without members' do
- expect(fork_networks.count).to eq(2)
-
- migrate!
-
- expect(fork_networks.find_by(id: empty_network.id)).to be_nil
- expect(fork_networks.find_by(id: other_network.id)).not_to be_nil
- expect(fork_networks.count).to eq(1)
- end
-end
diff --git a/spec/migrations/rename_duplicated_variable_key_spec.rb b/spec/migrations/rename_duplicated_variable_key_spec.rb
deleted file mode 100644
index 11096564dfa..00000000000
--- a/spec/migrations/rename_duplicated_variable_key_spec.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'migrate', '20170622135451_rename_duplicated_variable_key.rb')
-
-describe RenameDuplicatedVariableKey, :migration do
- let(:variables) { table(:ci_variables) }
- let(:projects) { table(:projects) }
-
- before do
- projects.create!(id: 1)
- variables.create!(id: 1, key: 'key1', project_id: 1)
- variables.create!(id: 2, key: 'key2', project_id: 1)
- variables.create!(id: 3, key: 'keyX', project_id: 1)
- variables.create!(id: 4, key: 'keyX', project_id: 1)
- variables.create!(id: 5, key: 'keyY', project_id: 1)
- variables.create!(id: 6, key: 'keyX', project_id: 1)
- variables.create!(id: 7, key: 'key7', project_id: 1)
- variables.create!(id: 8, key: 'keyY', project_id: 1)
- end
-
- it 'correctly remove duplicated records with smaller id' do
- migrate!
-
- expect(variables.pluck(:id, :key)).to contain_exactly(
- [1, 'key1'],
- [2, 'key2'],
- [3, 'keyX_3'],
- [4, 'keyX_4'],
- [5, 'keyY_5'],
- [6, 'keyX'],
- [7, 'key7'],
- [8, 'keyY']
- )
- end
-end
diff --git a/spec/migrations/rename_more_reserved_project_names_spec.rb b/spec/migrations/rename_more_reserved_project_names_spec.rb
deleted file mode 100644
index 80ae209e9d1..00000000000
--- a/spec/migrations/rename_more_reserved_project_names_spec.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-# encoding: utf-8
-
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20170313133418_rename_more_reserved_project_names.rb')
-
-# This migration uses multiple threads, and thus different transactions. This
-# means data created in this spec may not be visible to some threads. To work
-# around this we use the DELETE cleaning strategy.
-describe RenameMoreReservedProjectNames, :delete do
- let(:migration) { described_class.new }
- let!(:project) { create(:project) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
-
- before do
- project.path = 'artifacts'
- project.save!(validate: false)
- end
-
- describe '#up' do
- context 'when project repository exists' do
- before do
- project.create_repository
- end
-
- context 'when no exception is raised' do
- it 'renames project with reserved names' do
- migration.up
-
- expect(project.reload.path).to eq('artifacts0')
- end
- end
-
- context 'when exception is raised during rename' do
- before do
- service = instance_double('service')
-
- allow(service)
- .to receive(:execute)
- .and_raise(Projects::AfterRenameService::RenameFailedError)
-
- expect(migration)
- .to receive(:after_rename_service)
- .and_return(service)
- end
-
- it 'captures exception from project rename' do
- expect { migration.up }.not_to raise_error
- end
- end
- end
-
- context 'when project repository does not exist' do
- it 'does not raise error' do
- expect { migration.up }.not_to raise_error
- end
- end
- end
-end
diff --git a/spec/migrations/rename_reserved_project_names_spec.rb b/spec/migrations/rename_reserved_project_names_spec.rb
deleted file mode 100644
index 93e5c032287..00000000000
--- a/spec/migrations/rename_reserved_project_names_spec.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-# encoding: utf-8
-
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20161221153951_rename_reserved_project_names.rb')
-
-# This migration is using factories, which set fields that don't actually
-# exist in the DB schema previous to 20161221153951. Thus we just use the
-# latest schema when testing this migration.
-# This is ok-ish because:
-# 1. This migration is a data migration
-# 2. It only relies on very stable DB fields: routes.id, routes.path, namespaces.id, projects.namespace_id
-# Ideally, the test should not use factories and rely on the `table` helper instead.
-describe RenameReservedProjectNames, :migration, schema: :latest do
- let(:migration) { described_class.new }
- let!(:project) { create(:project) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
-
- before do
- project.path = 'projects'
- project.save!(validate: false)
- end
-
- describe '#up' do
- context 'when project repository exists' do
- before do
- project.create_repository
- end
-
- context 'when no exception is raised' do
- it 'renames project with reserved names' do
- migration.up
-
- expect(project.reload.path).to eq('projects0')
- end
- end
-
- context 'when exception is raised during rename' do
- before do
- service = instance_double('service')
-
- allow(service)
- .to receive(:execute)
- .and_raise(Projects::AfterRenameService::RenameFailedError)
-
- expect(migration)
- .to receive(:after_rename_service)
- .and_return(service)
- end
-
- it 'captures exception from project rename' do
- expect { migration.up }.not_to raise_error
- end
- end
- end
-
- context 'when project repository does not exist' do
- it 'does not raise error' do
- expect { migration.up }.not_to raise_error
- end
- end
- end
-end
diff --git a/spec/migrations/rename_users_with_renamed_namespace_spec.rb b/spec/migrations/rename_users_with_renamed_namespace_spec.rb
deleted file mode 100644
index b8a4dc2b2c0..00000000000
--- a/spec/migrations/rename_users_with_renamed_namespace_spec.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20170518200835_rename_users_with_renamed_namespace.rb')
-
-describe RenameUsersWithRenamedNamespace, :delete do
- it 'renames a user that had their namespace renamed to the namespace path' do
- other_user = create(:user, username: 'kodingu') # rubocop:disable RSpec/FactoriesInMigrationSpecs
- other_user1 = create(:user, username: 'api0') # rubocop:disable RSpec/FactoriesInMigrationSpecs
-
- user = create(:user, username: "Users0") # rubocop:disable RSpec/FactoriesInMigrationSpecs
- user.update_column(:username, 'Users')
- user1 = create(:user, username: "import0") # rubocop:disable RSpec/FactoriesInMigrationSpecs
- user1.update_column(:username, 'import')
-
- described_class.new.up
-
- expect(user.reload.username).to eq('Users0')
- expect(user1.reload.username).to eq('import0')
-
- expect(other_user.reload.username).to eq('kodingu')
- expect(other_user1.reload.username).to eq('api0')
- end
-end
diff --git a/spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb b/spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb
deleted file mode 100644
index 76fe16581ac..00000000000
--- a/spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20171026082505_schedule_merge_request_latest_merge_request_diff_id_migrations')
-
-describe ScheduleMergeRequestLatestMergeRequestDiffIdMigrations, :migration, :sidekiq do
- let(:projects_table) { table(:projects) }
- let(:merge_requests_table) { table(:merge_requests) }
- let(:merge_request_diffs_table) { table(:merge_request_diffs) }
-
- let(:project) { projects_table.create!(name: 'gitlab', path: 'gitlab-org/gitlab-ce') }
-
- let!(:merge_request_1) { create_mr!('mr_1', diffs: 1) }
- let!(:merge_request_2) { create_mr!('mr_2', diffs: 2) }
- let!(:merge_request_migrated) { create_mr!('merge_request_migrated', diffs: 3) }
- let!(:merge_request_4) { create_mr!('mr_4', diffs: 3) }
-
- def create_mr!(name, diffs: 0)
- merge_request =
- merge_requests_table.create!(target_project_id: project.id,
- target_branch: 'master',
- source_project_id: project.id,
- source_branch: name,
- title: name)
-
- diffs.times do
- merge_request_diffs_table.create!(merge_request_id: merge_request.id)
- end
-
- merge_request
- end
-
- def diffs_for(merge_request)
- merge_request_diffs_table.where(merge_request_id: merge_request.id)
- end
-
- before do
- stub_const("#{described_class.name}::BATCH_SIZE", 1)
-
- diff_id = diffs_for(merge_request_migrated).minimum(:id)
- merge_request_migrated.update!(latest_merge_request_diff_id: diff_id)
- end
-
- it 'correctly schedules background migrations' do
- Sidekiq::Testing.fake! do
- Timecop.freeze do
- migrate!
-
- expect(described_class::MIGRATION).to be_scheduled_delayed_migration(5.minutes, merge_request_1.id, merge_request_1.id)
- expect(described_class::MIGRATION).to be_scheduled_delayed_migration(10.minutes, merge_request_2.id, merge_request_2.id)
- expect(described_class::MIGRATION).to be_scheduled_delayed_migration(15.minutes, merge_request_4.id, merge_request_4.id)
- expect(BackgroundMigrationWorker.jobs.size).to eq 3
- end
- end
- end
-
- it 'schedules background migrations' do
- perform_enqueued_jobs do
- expect(merge_requests_table.where(latest_merge_request_diff_id: nil).count).to eq 3
-
- migrate!
-
- expect(merge_requests_table.where(latest_merge_request_diff_id: nil).count).to eq 0
- end
- end
-end
diff --git a/spec/migrations/track_untracked_uploads_spec.rb b/spec/migrations/track_untracked_uploads_spec.rb
deleted file mode 100644
index 2fccfb3f12c..00000000000
--- a/spec/migrations/track_untracked_uploads_spec.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20171103140253_track_untracked_uploads')
-
-describe TrackUntrackedUploads, :migration, :sidekiq do
- include MigrationsHelpers::TrackUntrackedUploadsHelpers
-
- it 'correctly schedules the follow-up background migration' do
- Sidekiq::Testing.fake! do
- migrate!
-
- expect(described_class::MIGRATION).to be_scheduled_migration
- expect(BackgroundMigrationWorker.jobs.size).to eq(1)
- end
- end
-end
diff --git a/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb b/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb
deleted file mode 100644
index 5f5ba426d69..00000000000
--- a/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb
+++ /dev/null
@@ -1,70 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'migrate', '20170503140202_turn_nested_groups_into_regular_groups_for_mysql.rb')
-
-describe TurnNestedGroupsIntoRegularGroupsForMysql do
- let!(:parent_group) { create(:group) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- let!(:child_group) { create(:group, parent: parent_group) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- let!(:project) { create(:project, :legacy_storage, :empty_repo, namespace: child_group) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- let!(:member) { create(:user) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- let(:migration) { described_class.new }
-
- before do
- parent_group.add_developer(member)
-
- allow(migration).to receive(:run_migration?).and_return(true)
- allow(migration).to receive(:verbose).and_return(false)
- end
-
- describe '#up' do
- let(:updated_project) do
- # path_with_namespace is memoized in an instance variable so we retrieve a
- # new row here to work around that.
- Project.find(project.id)
- end
-
- before do
- migration.up
- end
-
- it 'unsets the parent_id column' do
- expect(Namespace.where('parent_id IS NOT NULL').any?).to eq(false)
- end
-
- it 'adds members of parent groups as members to the migrated group' do
- is_member = child_group.members
- .where(user_id: member, access_level: Gitlab::Access::DEVELOPER).any?
-
- expect(is_member).to eq(true)
- end
-
- it 'update the path of the nested group' do
- child_group.reload
-
- expect(child_group.path).to eq("#{parent_group.name}-#{child_group.name}")
- end
-
- it 'renames projects of the nested group' do
- expect(updated_project.full_path)
- .to eq("#{parent_group.name}-#{child_group.name}/#{updated_project.path}")
- end
-
- it 'renames the repository of any projects' do
- repo_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- updated_project.repository.path
- end
-
- expect(repo_path)
- .to end_with("#{parent_group.name}-#{child_group.name}/#{updated_project.path}.git")
-
- expect(File.directory?(repo_path)).to eq(true)
- end
-
- it 'creates a redirect route for renamed projects' do
- exists = RedirectRoute
- .where(source_type: 'Project', source_id: project.id)
- .any?
-
- expect(exists).to eq(true)
- end
- end
-end
diff --git a/spec/migrations/update_legacy_diff_notes_type_for_import_spec.rb b/spec/migrations/update_legacy_diff_notes_type_for_import_spec.rb
deleted file mode 100644
index d625b60ff50..00000000000
--- a/spec/migrations/update_legacy_diff_notes_type_for_import_spec.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20170927112318_update_legacy_diff_notes_type_for_import.rb')
-
-describe UpdateLegacyDiffNotesTypeForImport, :migration do
- let(:notes) { table(:notes) }
-
- before do
- notes.inheritance_column = nil
-
- notes.create(type: 'Note')
- notes.create(type: 'LegacyDiffNote')
- notes.create(type: 'Github::Import::Note')
- notes.create(type: 'Github::Import::LegacyDiffNote')
- end
-
- it 'updates the notes type' do
- migrate!
-
- expect(notes.pluck(:type))
- .to contain_exactly('Note', 'Github::Import::Note', 'LegacyDiffNote', 'LegacyDiffNote')
- end
-end
diff --git a/spec/migrations/update_notes_type_for_import_spec.rb b/spec/migrations/update_notes_type_for_import_spec.rb
deleted file mode 100644
index 06195d970d8..00000000000
--- a/spec/migrations/update_notes_type_for_import_spec.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20170927112319_update_notes_type_for_import.rb')
-
-describe UpdateNotesTypeForImport, :migration do
- let(:notes) { table(:notes) }
-
- before do
- notes.inheritance_column = nil
-
- notes.create(type: 'Note')
- notes.create(type: 'LegacyDiffNote')
- notes.create(type: 'Github::Import::Note')
- notes.create(type: 'Github::Import::LegacyDiffNote')
- end
-
- it 'updates the notes type' do
- migrate!
-
- expect(notes.pluck(:type))
- .to contain_exactly('Note', 'Note', 'LegacyDiffNote', 'Github::Import::LegacyDiffNote')
- end
-end
diff --git a/spec/migrations/update_retried_for_ci_build_spec.rb b/spec/migrations/update_retried_for_ci_build_spec.rb
deleted file mode 100644
index 637dcbb8e01..00000000000
--- a/spec/migrations/update_retried_for_ci_build_spec.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20170503004427_update_retried_for_ci_build.rb')
-
-describe UpdateRetriedForCiBuild, :delete do
- let(:pipeline) { create(:ci_pipeline) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- let!(:build_old) { create(:ci_build, pipeline: pipeline, name: 'test') } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- let!(:build_new) { create(:ci_build, pipeline: pipeline, name: 'test') } # rubocop:disable RSpec/FactoriesInMigrationSpecs
-
- before do
- described_class.new.up
- end
-
- it 'updates ci_builds.is_retried' do
- expect(build_old.reload).to be_retried
- expect(build_new.reload).not_to be_retried
- end
-end
diff --git a/spec/migrations/update_upload_paths_to_system_spec.rb b/spec/migrations/update_upload_paths_to_system_spec.rb
deleted file mode 100644
index 984b428a020..00000000000
--- a/spec/migrations/update_upload_paths_to_system_spec.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20170317162059_update_upload_paths_to_system.rb')
-
-describe UpdateUploadPathsToSystem, :migration do
- let(:migration) { described_class.new }
- let(:uploads_table) { table(:uploads) }
- let(:base_upload_attributes) { { size: 42, uploader: 'John Doe' } }
-
- before do
- allow(migration).to receive(:say)
- end
-
- describe '#uploads_to_switch_to_new_path' do
- it 'contains only uploads with the old path for the correct models' do
- _upload_for_other_type = create_upload('Pipeline', 'uploads/ci_pipeline/avatar.jpg')
- _upload_with_system_path = create_upload('Project', 'uploads/-/system/project/avatar.jpg')
- _upload_with_other_path = create_upload('Project', 'thelongsecretforafileupload/avatar.jpg')
- old_upload = create_upload('Project', 'uploads/project/avatar.jpg')
- group_upload = create_upload('Namespace', 'uploads/group/avatar.jpg')
-
- expect(uploads_table.where(migration.uploads_to_switch_to_new_path)).to contain_exactly(old_upload, group_upload)
- end
- end
-
- describe '#uploads_to_switch_to_old_path' do
- it 'contains only uploads with the new path for the correct models' do
- _upload_for_other_type = create_upload('Pipeline', 'uploads/ci_pipeline/avatar.jpg')
- upload_with_system_path = create_upload('Project', 'uploads/-/system/project/avatar.jpg')
- _upload_with_other_path = create_upload('Project', 'thelongsecretforafileupload/avatar.jpg')
- _old_upload = create_upload('Project', 'uploads/project/avatar.jpg')
-
- expect(uploads_table.where(migration.uploads_to_switch_to_old_path)).to contain_exactly(upload_with_system_path)
- end
- end
-
- describe '#up' do
- it 'updates old upload records to the new path' do
- old_upload = create_upload('Project', 'uploads/project/avatar.jpg')
-
- migration.up
-
- expect(old_upload.reload.path).to eq('uploads/-/system/project/avatar.jpg')
- end
- end
-
- describe '#down' do
- it 'updates the new system patsh to the old paths' do
- new_upload = create_upload('Project', 'uploads/-/system/project/avatar.jpg')
-
- migration.down
-
- expect(new_upload.reload.path).to eq('uploads/project/avatar.jpg')
- end
- end
-
- def create_upload(type, path)
- uploads_table.create(base_upload_attributes.merge(model_type: type, path: path))
- end
-end
diff --git a/spec/models/clusters/applications/ingress_spec.rb b/spec/models/clusters/applications/ingress_spec.rb
index 292ddabd2d8..057517d3820 100644
--- a/spec/models/clusters/applications/ingress_spec.rb
+++ b/spec/models/clusters/applications/ingress_spec.rb
@@ -21,7 +21,21 @@ describe Clusters::Applications::Ingress do
describe '#can_uninstall?' do
subject { ingress.can_uninstall? }
- it { is_expected.to be_falsey }
+ it 'returns true if application_jupyter_nil_or_installable? AND external_ip_or_hostname? are true' do
+ ingress.external_ip = 'IP'
+
+ is_expected.to be_truthy
+ end
+
+ it 'returns false if application_jupyter_nil_or_installable? is false' do
+ create(:clusters_applications_jupyter, :installed, cluster: ingress.cluster)
+
+ is_expected.to be_falsey
+ end
+
+ it 'returns false if external_ip_or_hostname? is false' do
+ is_expected.to be_falsey
+ end
end
describe '#make_installed!' do
diff --git a/spec/models/deploy_token_spec.rb b/spec/models/deploy_token_spec.rb
index 2fe82eaa778..8d951ab6f0f 100644
--- a/spec/models/deploy_token_spec.rb
+++ b/spec/models/deploy_token_spec.rb
@@ -8,6 +8,15 @@ describe DeployToken do
it { is_expected.to have_many :project_deploy_tokens }
it { is_expected.to have_many(:projects).through(:project_deploy_tokens) }
+ describe 'validations' do
+ let(:username_format_message) { "can contain only letters, digits, '_', '-', '+', and '.'" }
+
+ it { is_expected.to validate_length_of(:username).is_at_most(255) }
+ it { is_expected.to allow_value('GitLab+deploy_token-3.14').for(:username) }
+ it { is_expected.not_to allow_value('<script>').for(:username).with_message(username_format_message) }
+ it { is_expected.not_to allow_value('').for(:username).with_message(username_format_message) }
+ end
+
describe '#ensure_token' do
it 'ensures a token' do
deploy_token.token = nil
@@ -87,8 +96,30 @@ describe DeployToken do
end
describe '#username' do
- it 'returns a harcoded username' do
- expect(deploy_token.username).to eq("gitlab+deploy-token-#{deploy_token.id}")
+ context 'persisted records' do
+ it 'returns a default username if none is set' do
+ expect(deploy_token.username).to eq("gitlab+deploy-token-#{deploy_token.id}")
+ end
+
+ it 'returns the username provided if one is set' do
+ deploy_token = create(:deploy_token, username: 'deployer')
+
+ expect(deploy_token.username).to eq('deployer')
+ end
+ end
+
+ context 'new records' do
+ it 'returns nil if no username is set' do
+ deploy_token = build(:deploy_token)
+
+ expect(deploy_token.username).to be_nil
+ end
+
+ it 'returns the username provided if one is set' do
+ deploy_token = build(:deploy_token, username: 'deployer')
+
+ expect(deploy_token.username).to eq('deployer')
+ end
end
end
diff --git a/spec/models/namespace/aggregation_schedule_spec.rb b/spec/models/namespace/aggregation_schedule_spec.rb
index 5ba7547ff4d..8ed0248e1b2 100644
--- a/spec/models/namespace/aggregation_schedule_spec.rb
+++ b/spec/models/namespace/aggregation_schedule_spec.rb
@@ -2,6 +2,77 @@
require 'spec_helper'
-RSpec.describe Namespace::AggregationSchedule, type: :model do
+RSpec.describe Namespace::AggregationSchedule, :clean_gitlab_redis_shared_state, type: :model do
+ include ExclusiveLeaseHelpers
+
it { is_expected.to belong_to :namespace }
+
+ describe '.delay_timeout' do
+ context 'when timeout is set on redis' do
+ it 'uses personalized timeout' do
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.set(described_class::REDIS_SHARED_KEY, 1.hour)
+ end
+
+ expect(described_class.delay_timeout).to eq(1.hour)
+ end
+ end
+
+ context 'when timeout is not set on redis' do
+ it 'uses default timeout' do
+ expect(described_class.delay_timeout).to eq(3.hours)
+ end
+ end
+ end
+
+ describe '#schedule_root_storage_statistics' do
+ let(:namespace) { create(:namespace) }
+ let(:aggregation_schedule) { namespace.build_aggregation_schedule }
+ let(:lease_key) { "namespace:namespaces_root_statistics:#{namespace.id}" }
+
+ context "when we can't obtain the lease" do
+ it 'does not schedule the workers' do
+ stub_exclusive_lease_taken(lease_key, timeout: described_class::DEFAULT_LEASE_TIMEOUT)
+
+ expect(Namespaces::RootStatisticsWorker)
+ .not_to receive(:perform_async)
+
+ expect(Namespaces::RootStatisticsWorker)
+ .not_to receive(:perform_in)
+
+ aggregation_schedule.save!
+ end
+ end
+
+ context 'when we can obtain the lease' do
+ it 'schedules a root storage statistics after create' do
+ stub_exclusive_lease(lease_key, timeout: described_class::DEFAULT_LEASE_TIMEOUT)
+
+ expect(Namespaces::RootStatisticsWorker)
+ .to receive(:perform_async).once
+
+ expect(Namespaces::RootStatisticsWorker)
+ .to receive(:perform_in).once
+ .with(described_class::DEFAULT_LEASE_TIMEOUT, aggregation_schedule.namespace_id )
+
+ aggregation_schedule.save!
+ end
+ end
+
+ context 'with a personalized lease timeout' do
+ before do
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.set(described_class::REDIS_SHARED_KEY, 1.hour)
+ end
+ end
+
+ it 'uses a personalized time' do
+ expect(Namespaces::RootStatisticsWorker)
+ .to receive(:perform_in)
+ .with(1.hour, aggregation_schedule.namespace_id)
+
+ aggregation_schedule.save!
+ end
+ end
+ end
end
diff --git a/spec/models/namespace/root_storage_statistics_spec.rb b/spec/models/namespace/root_storage_statistics_spec.rb
index f6fb5af5aae..3229a32234e 100644
--- a/spec/models/namespace/root_storage_statistics_spec.rb
+++ b/spec/models/namespace/root_storage_statistics_spec.rb
@@ -7,4 +7,69 @@ RSpec.describe Namespace::RootStorageStatistics, type: :model do
it { is_expected.to have_one(:route).through(:namespace) }
it { is_expected.to delegate_method(:all_projects).to(:namespace) }
+
+ describe '#recalculate!' do
+ let(:namespace) { create(:group) }
+ let(:root_storage_statistics) { create(:namespace_root_storage_statistics, namespace: namespace) }
+
+ let(:project1) { create(:project, namespace: namespace) }
+ let(:project2) { create(:project, namespace: namespace) }
+
+ let!(:stat1) { create(:project_statistics, project: project1, with_data: true, size_multiplier: 100) }
+ let!(:stat2) { create(:project_statistics, project: project2, with_data: true, size_multiplier: 200) }
+
+ shared_examples 'data refresh' do
+ it 'aggregates project statistics' do
+ root_storage_statistics.recalculate!
+
+ root_storage_statistics.reload
+
+ total_repository_size = stat1.repository_size + stat2.repository_size
+ total_wiki_size = stat1.wiki_size + stat2.wiki_size
+ total_lfs_objects_size = stat1.lfs_objects_size + stat2.lfs_objects_size
+ total_build_artifacts_size = stat1.build_artifacts_size + stat2.build_artifacts_size
+ total_packages_size = stat1.packages_size + stat2.packages_size
+ total_storage_size = stat1.storage_size + stat2.storage_size
+
+ expect(root_storage_statistics.repository_size).to eq(total_repository_size)
+ expect(root_storage_statistics.wiki_size).to eq(total_wiki_size)
+ expect(root_storage_statistics.lfs_objects_size).to eq(total_lfs_objects_size)
+ expect(root_storage_statistics.build_artifacts_size).to eq(total_build_artifacts_size)
+ expect(root_storage_statistics.packages_size).to eq(total_packages_size)
+ expect(root_storage_statistics.storage_size).to eq(total_storage_size)
+ end
+
+ it 'works when there are no projects' do
+ Project.delete_all
+
+ root_storage_statistics.recalculate!
+
+ root_storage_statistics.reload
+ expect(root_storage_statistics.repository_size).to eq(0)
+ expect(root_storage_statistics.wiki_size).to eq(0)
+ expect(root_storage_statistics.lfs_objects_size).to eq(0)
+ expect(root_storage_statistics.build_artifacts_size).to eq(0)
+ expect(root_storage_statistics.packages_size).to eq(0)
+ expect(root_storage_statistics.storage_size).to eq(0)
+ end
+ end
+
+ it_behaves_like 'data refresh'
+
+ context 'with subgroups', :nested_groups do
+ let(:subgroup1) { create(:group, parent: namespace)}
+ let(:subgroup2) { create(:group, parent: subgroup1)}
+
+ let(:project1) { create(:project, namespace: subgroup1) }
+ let(:project2) { create(:project, namespace: subgroup2) }
+
+ it_behaves_like 'data refresh'
+ end
+
+ context 'with a personal namespace' do
+ let(:namespace) { create(:user).namespace }
+
+ it_behaves_like 'data refresh'
+ end
+ end
end
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 30e49cf204f..f908f3504e0 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -837,4 +837,20 @@ describe Namespace do
it { is_expected.to be_falsy }
end
end
+
+ describe '#aggregation_scheduled?' do
+ let(:namespace) { create(:namespace) }
+
+ subject { namespace.aggregation_scheduled? }
+
+ context 'with an aggregation scheduled association' do
+ let(:namespace) { create(:namespace, :with_aggregation_schedule) }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'without an aggregation scheduled association' do
+ it { is_expected.to be_falsy }
+ end
+ end
end
diff --git a/spec/models/project_services/bugzilla_service_spec.rb b/spec/models/project_services/bugzilla_service_spec.rb
index 6818db48fee..d5b0f94f461 100644
--- a/spec/models/project_services/bugzilla_service_spec.rb
+++ b/spec/models/project_services/bugzilla_service_spec.rb
@@ -32,4 +32,49 @@ describe BugzillaService do
it { is_expected.not_to validate_presence_of(:new_issue_url) }
end
end
+
+ context 'overriding properties' do
+ let(:default_title) { 'JIRA' }
+ let(:default_description) { 'JiraService|Jira issue tracker' }
+ let(:url) { 'http://bugzilla.example.com' }
+ let(:access_params) do
+ { project_url: url, issues_url: url, new_issue_url: url }
+ end
+
+ # this will be removed as part of https://gitlab.com/gitlab-org/gitlab-ce/issues/63084
+ context 'when data are stored in properties' do
+ let(:properties) { access_params.merge(title: title, description: description) }
+ let(:service) { create(:bugzilla_service, properties: properties) }
+
+ include_examples 'issue tracker fields'
+ end
+
+ context 'when data are stored in separated fields' do
+ let(:service) do
+ create(:bugzilla_service, title: title, description: description, properties: access_params)
+ end
+
+ include_examples 'issue tracker fields'
+ end
+
+ context 'when data are stored in both properties and separated fields' do
+ let(:properties) { access_params.merge(title: 'wrong title', description: 'wrong description') }
+ let(:service) do
+ create(:bugzilla_service, title: title, description: description, properties: properties)
+ end
+
+ include_examples 'issue tracker fields'
+ end
+
+ context 'when no title & description are set' do
+ let(:service) do
+ create(:bugzilla_service, properties: access_params)
+ end
+
+ it 'returns default values' do
+ expect(service.title).to eq('Bugzilla')
+ expect(service.description).to eq('Bugzilla issue tracker')
+ end
+ end
+ end
end
diff --git a/spec/models/project_services/custom_issue_tracker_service_spec.rb b/spec/models/project_services/custom_issue_tracker_service_spec.rb
index f0e7551693d..56b0bda6626 100644
--- a/spec/models/project_services/custom_issue_tracker_service_spec.rb
+++ b/spec/models/project_services/custom_issue_tracker_service_spec.rb
@@ -48,4 +48,47 @@ describe CustomIssueTrackerService do
end
end
end
+
+ context 'overriding properties' do
+ let(:url) { 'http://custom.example.com' }
+ let(:access_params) do
+ { project_url: url, issues_url: url, new_issue_url: url }
+ end
+
+ # this will be removed as part of https://gitlab.com/gitlab-org/gitlab-ce/issues/63084
+ context 'when data are stored in properties' do
+ let(:properties) { access_params.merge(title: title, description: description) }
+ let(:service) { create(:custom_issue_tracker_service, properties: properties) }
+
+ include_examples 'issue tracker fields'
+ end
+
+ context 'when data are stored in separated fields' do
+ let(:service) do
+ create(:custom_issue_tracker_service, title: title, description: description, properties: access_params)
+ end
+
+ include_examples 'issue tracker fields'
+ end
+
+ context 'when data are stored in both properties and separated fields' do
+ let(:properties) { access_params.merge(title: 'wrong title', description: 'wrong description') }
+ let(:service) do
+ create(:custom_issue_tracker_service, title: title, description: description, properties: properties)
+ end
+
+ include_examples 'issue tracker fields'
+ end
+
+ context 'when no title & description are set' do
+ let(:service) do
+ create(:custom_issue_tracker_service, properties: access_params)
+ end
+
+ it 'returns default values' do
+ expect(service.title).to eq('Custom Issue Tracker')
+ expect(service.description).to eq('Custom issue tracker')
+ end
+ end
+ end
end
diff --git a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb
index 11f96c03d46..a3726f09dc5 100644
--- a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb
+++ b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb
@@ -51,4 +51,47 @@ describe GitlabIssueTrackerService do
end
end
end
+
+ context 'overriding properties' do
+ let(:url) { 'http://gitlab.example.com' }
+ let(:access_params) do
+ { project_url: url, issues_url: url, new_issue_url: url }
+ end
+
+ # this will be removed as part of https://gitlab.com/gitlab-org/gitlab-ce/issues/63084
+ context 'when data are stored in properties' do
+ let(:properties) { access_params.merge(title: title, description: description) }
+ let(:service) { create(:gitlab_issue_tracker_service, properties: properties) }
+
+ include_examples 'issue tracker fields'
+ end
+
+ context 'when data are stored in separated fields' do
+ let(:service) do
+ create(:gitlab_issue_tracker_service, title: title, description: description, properties: access_params)
+ end
+
+ include_examples 'issue tracker fields'
+ end
+
+ context 'when data are stored in both properties and separated fields' do
+ let(:properties) { access_params.merge(title: 'wrong title', description: 'wrong description') }
+ let(:service) do
+ create(:gitlab_issue_tracker_service, title: title, description: description, properties: properties)
+ end
+
+ include_examples 'issue tracker fields'
+ end
+
+ context 'when no title & description are set' do
+ let(:service) do
+ create(:gitlab_issue_tracker_service, properties: access_params)
+ end
+
+ it 'returns default values' do
+ expect(service.title).to eq('GitLab')
+ expect(service.description).to eq('GitLab issue tracker')
+ end
+ end
+ end
end
diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb
index fc08457a3c5..9b122d85293 100644
--- a/spec/models/project_services/jira_service_spec.rb
+++ b/spec/models/project_services/jira_service_spec.rb
@@ -115,6 +115,70 @@ describe JiraService do
end
end
+ describe '#create' do
+ let(:params) do
+ {
+ project: create(:project), title: 'custom title', description: 'custom description'
+ }
+ end
+
+ subject { described_class.create(params) }
+
+ it 'does not store title & description into properties' do
+ expect(subject.properties.keys).not_to include('title', 'description')
+ end
+
+ it 'sets title & description correctly' do
+ service = subject
+
+ expect(service.title).to eq('custom title')
+ expect(service.description).to eq('custom description')
+ end
+ end
+
+ context 'overriding properties' do
+ let(:url) { 'http://issue_tracker.example.com' }
+ let(:access_params) do
+ { url: url, username: 'username', password: 'password' }
+ end
+
+ # this will be removed as part of https://gitlab.com/gitlab-org/gitlab-ce/issues/63084
+ context 'when data are stored in properties' do
+ let(:properties) { access_params.merge(title: title, description: description) }
+ let(:service) { create(:jira_service, properties: properties) }
+
+ include_examples 'issue tracker fields'
+ end
+
+ context 'when data are stored in separated fields' do
+ let(:service) do
+ create(:jira_service, title: title, description: description, properties: access_params)
+ end
+
+ include_examples 'issue tracker fields'
+ end
+
+ context 'when data are stored in both properties and separated fields' do
+ let(:properties) { access_params.merge(title: 'wrong title', description: 'wrong description') }
+ let(:service) do
+ create(:jira_service, title: title, description: description, properties: properties)
+ end
+
+ include_examples 'issue tracker fields'
+ end
+
+ context 'when no title & description are set' do
+ let(:service) do
+ create(:jira_service, properties: access_params)
+ end
+
+ it 'returns default values' do
+ expect(service.title).to eq('Jira')
+ expect(service.description).to eq('Jira issue tracker')
+ end
+ end
+ end
+
describe '#close_issue' do
let(:custom_base_url) { 'http://custom_url' }
let(:user) { create(:user) }
@@ -450,36 +514,54 @@ describe JiraService do
end
describe 'description and title' do
- let(:project) { create(:project) }
+ let(:title) { 'Jira One' }
+ let(:description) { 'Jira One issue tracker' }
+ let(:properties) do
+ {
+ url: 'http://jira.example.com/web',
+ username: 'mic',
+ password: 'password',
+ title: title,
+ description: description
+ }
+ end
context 'when it is not set' do
- before do
- @service = project.create_jira_service(active: true)
- end
+ it 'default values are returned' do
+ service = create(:jira_service)
- after do
- @service.destroy!
+ expect(service.title).to eq('Jira')
+ expect(service.description).to eq('Jira issue tracker')
end
+ end
- it 'is initialized' do
- expect(@service.title).to eq('Jira')
- expect(@service.description).to eq('Jira issue tracker')
+ context 'when it is set in properties' do
+ it 'values from properties are returned' do
+ service = create(:jira_service, properties: properties)
+
+ expect(service.title).to eq(title)
+ expect(service.description).to eq(description)
end
end
- context 'when it is set' do
- before do
- properties = { 'title' => 'Jira One', 'description' => 'Jira One issue tracker' }
- @service = project.create_jira_service(active: true, properties: properties)
- end
+ context 'when it is in title & description fields' do
+ it 'values from title and description fields are returned' do
+ service = create(:jira_service, title: title, description: description)
- after do
- @service.destroy!
+ expect(service.title).to eq(title)
+ expect(service.description).to eq(description)
end
+ end
+
+ context 'when it is in both properites & title & description fields' do
+ it 'values from title and description fields are returned' do
+ title2 = 'Jira 2'
+ description2 = 'Jira description 2'
- it 'is correct' do
- expect(@service.title).to eq('Jira One')
- expect(@service.description).to eq('Jira One issue tracker')
+ service = create(:jira_service, title: title2, description: description2, properties: properties)
+
+ expect(service.title).to eq(title2)
+ expect(service.description).to eq(description2)
end
end
end
@@ -505,29 +587,21 @@ describe JiraService do
end
describe 'project and issue urls' do
- let(:project) { create(:project) }
-
context 'when gitlab.yml was initialized' do
- before do
+ it 'is prepopulated with the settings' do
settings = {
'jira' => {
- 'title' => 'Jira',
'url' => 'http://jira.sample/projects/project_a',
'api_url' => 'http://jira.sample/api'
}
}
allow(Gitlab.config).to receive(:issues_tracker).and_return(settings)
- @service = project.create_jira_service(active: true)
- end
- after do
- @service.destroy!
- end
+ project = create(:project)
+ service = project.create_jira_service(active: true)
- it 'is prepopulated with the settings' do
- expect(@service.properties['title']).to eq('Jira')
- expect(@service.properties['url']).to eq('http://jira.sample/projects/project_a')
- expect(@service.properties['api_url']).to eq('http://jira.sample/api')
+ expect(service.properties['url']).to eq('http://jira.sample/projects/project_a')
+ expect(service.properties['api_url']).to eq('http://jira.sample/api')
end
end
end
diff --git a/spec/models/project_services/redmine_service_spec.rb b/spec/models/project_services/redmine_service_spec.rb
index ac570ac27e1..806e3695962 100644
--- a/spec/models/project_services/redmine_service_spec.rb
+++ b/spec/models/project_services/redmine_service_spec.rb
@@ -40,4 +40,47 @@ describe RedmineService do
expect(described_class.reference_pattern.match('#123')[:issue]).to eq('123')
end
end
+
+ context 'overriding properties' do
+ let(:url) { 'http://redmine.example.com' }
+ let(:access_params) do
+ { project_url: url, issues_url: url, new_issue_url: url }
+ end
+
+ # this will be removed as part of https://gitlab.com/gitlab-org/gitlab-ce/issues/63084
+ context 'when data are stored in properties' do
+ let(:properties) { access_params.merge(title: title, description: description) }
+ let(:service) { create(:redmine_service, properties: properties) }
+
+ include_examples 'issue tracker fields'
+ end
+
+ context 'when data are stored in separated fields' do
+ let(:service) do
+ create(:redmine_service, title: title, description: description, properties: access_params)
+ end
+
+ include_examples 'issue tracker fields'
+ end
+
+ context 'when data are stored in both properties and separated fields' do
+ let(:properties) { access_params.merge(title: 'wrong title', description: 'wrong description') }
+ let(:service) do
+ create(:redmine_service, title: title, description: description, properties: properties)
+ end
+
+ include_examples 'issue tracker fields'
+ end
+
+ context 'when no title & description are set' do
+ let(:service) do
+ create(:redmine_service, properties: access_params)
+ end
+
+ it 'returns default values' do
+ expect(service.title).to eq('Redmine')
+ expect(service.description).to eq('Redmine issue tracker')
+ end
+ end
+ end
end
diff --git a/spec/models/project_services/youtrack_service_spec.rb b/spec/models/project_services/youtrack_service_spec.rb
index bf9d892f66c..b47ef6702b4 100644
--- a/spec/models/project_services/youtrack_service_spec.rb
+++ b/spec/models/project_services/youtrack_service_spec.rb
@@ -37,4 +37,47 @@ describe YoutrackService do
expect(described_class.reference_pattern.match('YT-123')[:issue]).to eq('YT-123')
end
end
+
+ context 'overriding properties' do
+ let(:url) { 'http://youtrack.example.com' }
+ let(:access_params) do
+ { project_url: url, issues_url: url, new_issue_url: url }
+ end
+
+ # this will be removed as part of https://gitlab.com/gitlab-org/gitlab-ce/issues/63084
+ context 'when data are stored in properties' do
+ let(:properties) { access_params.merge(title: title, description: description) }
+ let(:service) { create(:youtrack_service, properties: properties) }
+
+ include_examples 'issue tracker fields'
+ end
+
+ context 'when data are stored in separated fields' do
+ let(:service) do
+ create(:youtrack_service, title: title, description: description, properties: access_params)
+ end
+
+ include_examples 'issue tracker fields'
+ end
+
+ context 'when data are stored in both properties and separated fields' do
+ let(:properties) { access_params.merge(title: 'wrong title', description: 'wrong description') }
+ let(:service) do
+ create(:youtrack_service, title: title, description: description, properties: properties)
+ end
+
+ include_examples 'issue tracker fields'
+ end
+
+ context 'when no title & description are set' do
+ let(:service) do
+ create(:youtrack_service, properties: access_params)
+ end
+
+ it 'returns default values' do
+ expect(service.title).to eq('YouTrack')
+ expect(service.description).to eq('YouTrack issue tracker')
+ end
+ end
+ end
end
diff --git a/spec/models/release_spec.rb b/spec/models/release_spec.rb
index 7c106ce6b85..e9d846e7291 100644
--- a/spec/models/release_spec.rb
+++ b/spec/models/release_spec.rb
@@ -64,4 +64,14 @@ RSpec.describe Release do
is_expected.to all(be_a(Releases::Source))
end
end
+
+ describe '#upcoming_release?' do
+ context 'during the backfill migration when released_at could be nil' do
+ it 'handles a nil released_at value and returns false' do
+ allow(release).to receive(:released_at).and_return nil
+
+ expect(release.upcoming_release?).to eq(false)
+ end
+ end
+ end
end
diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb
index d442c73c118..0797b9a9d83 100644
--- a/spec/models/service_spec.rb
+++ b/spec/models/service_spec.rb
@@ -244,7 +244,8 @@ describe Service do
let(:service) do
GitlabIssueTrackerService.create(
project: create(:project),
- title: 'random title'
+ title: 'random title',
+ project_url: 'http://gitlab.example.com'
)
end
@@ -252,8 +253,12 @@ describe Service do
expect { service }.not_to raise_error
end
+ it 'sets title correctly' do
+ expect(service.title).to eq('random title')
+ end
+
it 'creates the properties' do
- expect(service.properties).to eq({ "title" => "random title" })
+ expect(service.properties).to eq({ "project_url" => "http://gitlab.example.com" })
end
end
diff --git a/spec/requests/api/releases_spec.rb b/spec/requests/api/releases_spec.rb
index 8603fa2a73d..206e898381d 100644
--- a/spec/requests/api/releases_spec.rb
+++ b/spec/requests/api/releases_spec.rb
@@ -24,7 +24,7 @@ describe API::Releases do
project: project,
tag: 'v0.1',
author: maintainer,
- created_at: 2.days.ago)
+ released_at: 2.days.ago)
end
let!(:release_2) do
@@ -32,7 +32,7 @@ describe API::Releases do
project: project,
tag: 'v0.2',
author: maintainer,
- created_at: 1.day.ago)
+ released_at: 1.day.ago)
end
it 'returns 200 HTTP status' do
@@ -41,7 +41,7 @@ describe API::Releases do
expect(response).to have_gitlab_http_status(:ok)
end
- it 'returns releases ordered by created_at' do
+ it 'returns releases ordered by released_at' do
get api("/projects/#{project.id}/releases", maintainer)
expect(json_response.count).to eq(2)
@@ -56,6 +56,26 @@ describe API::Releases do
end
end
+ it 'returns an upcoming_release status for a future release' do
+ tomorrow = Time.now.utc + 1.day
+ create(:release, project: project, tag: 'v0.1', author: maintainer, released_at: tomorrow)
+
+ get api("/projects/#{project.id}/releases", maintainer)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response.first['upcoming_release']).to eq(true)
+ end
+
+ it 'returns an upcoming_release status for a past release' do
+ yesterday = Time.now.utc - 1.day
+ create(:release, project: project, tag: 'v0.1', author: maintainer, released_at: yesterday)
+
+ get api("/projects/#{project.id}/releases", maintainer)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response.first['upcoming_release']).to eq(false)
+ end
+
context 'when tag does not exist in git repository' do
let!(:release) { create(:release, project: project, tag: 'v1.1.5') }
@@ -316,6 +336,51 @@ describe API::Releases do
expect(project.releases.last.description).to eq('Super nice release')
end
+ it 'sets the released_at to the current time if the released_at parameter is not provided' do
+ now = Time.zone.parse('2015-08-25 06:00:00Z')
+ Timecop.freeze(now) do
+ post api("/projects/#{project.id}/releases", maintainer), params: params
+
+ expect(project.releases.last.released_at).to eq(now)
+ end
+ end
+
+ it 'sets the released_at to the value in the parameters if specified' do
+ params = {
+ name: 'New release',
+ tag_name: 'v0.1',
+ description: 'Super nice release',
+ released_at: '2019-03-20T10:00:00Z'
+ }
+ post api("/projects/#{project.id}/releases", maintainer), params: params
+
+ expect(project.releases.last.released_at).to eq('2019-03-20T10:00:00Z')
+ end
+
+ it 'assumes the utc timezone for released_at if the timezone is not provided' do
+ params = {
+ name: 'New release',
+ tag_name: 'v0.1',
+ description: 'Super nice release',
+ released_at: '2019-03-25 10:00:00'
+ }
+ post api("/projects/#{project.id}/releases", maintainer), params: params
+
+ expect(project.releases.last.released_at).to eq('2019-03-25T10:00:00Z')
+ end
+
+ it 'allows specifying a released_at with a local time zone' do
+ params = {
+ name: 'New release',
+ tag_name: 'v0.1',
+ description: 'Super nice release',
+ released_at: '2019-03-25T10:00:00+09:00'
+ }
+ post api("/projects/#{project.id}/releases", maintainer), params: params
+
+ expect(project.releases.last.released_at).to eq('2019-03-25T01:00:00Z')
+ end
+
context 'when description is empty' do
let(:params) do
{
@@ -540,6 +605,7 @@ describe API::Releases do
project: project,
tag: 'v0.1',
name: 'New release',
+ released_at: '2018-03-01T22:00:00Z',
description: 'Super nice release')
end
@@ -560,6 +626,7 @@ describe API::Releases do
expect(project.releases.last.tag).to eq('v0.1')
expect(project.releases.last.name).to eq('New release')
+ expect(project.releases.last.released_at).to eq('2018-03-01T22:00:00Z')
end
it 'matches response schema' do
@@ -568,6 +635,14 @@ describe API::Releases do
expect(response).to match_response_schema('public_api/v4/release')
end
+ it 'updates released_at' do
+ params = { released_at: '2015-10-10T05:00:00Z' }
+
+ put api("/projects/#{project.id}/releases/v0.1", maintainer), params: params
+
+ expect(project.releases.last.released_at).to eq('2015-10-10T05:00:00Z')
+ end
+
context 'when user tries to update sha' do
let(:params) { { sha: 'xxx' } }
diff --git a/spec/requests/rack_attack_global_spec.rb b/spec/requests/rack_attack_global_spec.rb
index 89adbc77a7f..d832963292c 100644
--- a/spec/requests/rack_attack_global_spec.rb
+++ b/spec/requests/rack_attack_global_spec.rb
@@ -102,6 +102,27 @@ describe 'Rack Attack global throttles' do
expect_rejection { get(*get_args) }
end
+
+ it 'logs RackAttack info into structured logs' do
+ requests_per_period.times do
+ get(*get_args)
+ expect(response).to have_http_status 200
+ end
+
+ arguments = {
+ message: 'Rack_Attack',
+ env: :throttle,
+ ip: '127.0.0.1',
+ request_method: 'GET',
+ fullpath: get_args.first,
+ user_id: user.id,
+ username: user.username
+ }
+
+ expect(Gitlab::AuthLogger).to receive(:error).with(arguments).once
+
+ expect_rejection { get(*get_args) }
+ end
end
context 'when the throttle is disabled' do
@@ -189,7 +210,15 @@ describe 'Rack Attack global throttles' do
expect(response).to have_http_status 200
end
- expect(Gitlab::AuthLogger).to receive(:error).once
+ arguments = {
+ message: 'Rack_Attack',
+ env: :throttle,
+ ip: '127.0.0.1',
+ request_method: 'GET',
+ fullpath: '/users/sign_in'
+ }
+
+ expect(Gitlab::AuthLogger).to receive(:error).with(arguments)
get url_that_does_not_require_authentication
end
@@ -345,7 +374,17 @@ describe 'Rack Attack global throttles' do
expect(response).to have_http_status 200
end
- expect(Gitlab::AuthLogger).to receive(:error).once
+ arguments = {
+ message: 'Rack_Attack',
+ env: :throttle,
+ ip: '127.0.0.1',
+ request_method: 'GET',
+ fullpath: '/dashboard/snippets',
+ user_id: user.id,
+ username: user.username
+ }
+
+ expect(Gitlab::AuthLogger).to receive(:error).with(arguments).once
get url_that_requires_authentication
end
diff --git a/spec/services/deploy_tokens/create_service_spec.rb b/spec/services/deploy_tokens/create_service_spec.rb
index 886ffd4593d..fbb66fe4cb7 100644
--- a/spec/services/deploy_tokens/create_service_spec.rb
+++ b/spec/services/deploy_tokens/create_service_spec.rb
@@ -32,6 +32,22 @@ describe DeployTokens::CreateService do
end
end
+ context 'when username is empty string' do
+ let(:deploy_token_params) { attributes_for(:deploy_token, username: '') }
+
+ it 'converts it to nil' do
+ expect(subject.read_attribute(:username)).to be_nil
+ end
+ end
+
+ context 'when username is provided' do
+ let(:deploy_token_params) { attributes_for(:deploy_token, username: 'deployer') }
+
+ it 'keeps the provided username' do
+ expect(subject.read_attribute(:username)).to eq('deployer')
+ end
+ end
+
context 'when the deploy token is invalid' do
let(:deploy_token_params) { attributes_for(:deploy_token, read_repository: false, read_registry: false) }
diff --git a/spec/services/namespaces/statistics_refresher_service_spec.rb b/spec/services/namespaces/statistics_refresher_service_spec.rb
new file mode 100644
index 00000000000..f4d9c96f7f4
--- /dev/null
+++ b/spec/services/namespaces/statistics_refresher_service_spec.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Namespaces::StatisticsRefresherService, '#execute' do
+ let(:group) { create(:group) }
+ let(:projects) { create_list(:project, 5, namespace: group) }
+ let(:service) { described_class.new }
+
+ context 'without a root storage statistics relation' do
+ it 'creates one' do
+ expect do
+ service.execute(group)
+ end.to change(Namespace::RootStorageStatistics, :count).by(1)
+
+ expect(group.reload.root_storage_statistics).to be_present
+ end
+
+ it 'recalculate the namespace statistics' do
+ expect_any_instance_of(Namespace::RootStorageStatistics).to receive(:recalculate!).once
+
+ service.execute(group)
+ end
+ end
+
+ context 'with a root storage statistics relation' do
+ before do
+ Namespace::AggregationSchedule.safe_find_or_create_by!(namespace_id: group.id)
+ end
+
+ it 'does not create one' do
+ expect do
+ service.execute(group)
+ end.not_to change(Namespace::RootStorageStatistics, :count)
+ end
+
+ it 'recalculate the namespace statistics' do
+ expect(Namespace::RootStorageStatistics)
+ .to receive(:safe_find_or_create_by!).with({ namespace_id: group.id })
+ .and_return(group.root_storage_statistics)
+
+ service.execute(group)
+ end
+ end
+
+ context 'when something goes wrong' do
+ before do
+ allow_any_instance_of(Namespace::RootStorageStatistics)
+ .to receive(:recalculate!).and_raise(ActiveRecord::ActiveRecordError)
+ end
+
+ it 'raises RefreshError' do
+ expect do
+ service.execute(group)
+ end.to raise_error(Namespaces::StatisticsRefresherService::RefresherError)
+ end
+ end
+end
diff --git a/spec/support/features/discussion_comments_shared_example.rb b/spec/support/features/discussion_comments_shared_example.rb
index c4bf5da0cf0..7c8e57702ae 100644
--- a/spec/support/features/discussion_comments_shared_example.rb
+++ b/spec/support/features/discussion_comments_shared_example.rb
@@ -176,7 +176,7 @@ shared_examples 'thread comments' do |resource_name|
if resource_name == 'merge request'
let(:note_id) { find("#{comments_selector} .note:first-child", match: :first)['data-note-id'] }
- let(:reply_id) { find("#{comments_selector} .note:last-child", match: :first)['data-note-id'] }
+ let(:reply_id) { find("#{comments_selector} .note:last-of-type", match: :first)['data-note-id'] }
it 'can be replied to after resolving' do
click_button "Resolve thread"
diff --git a/spec/support/matchers/gitaly_matchers.rb b/spec/support/matchers/gitaly_matchers.rb
index ebfabcd8f24..933ed22b5d0 100644
--- a/spec/support/matchers/gitaly_matchers.rb
+++ b/spec/support/matchers/gitaly_matchers.rb
@@ -9,6 +9,6 @@ end
RSpec::Matchers.define :gitaly_request_with_params do |params|
match do |actual|
- params.reduce(true) { |r, (key, val)| r && actual.send(key) == val }
+ params.reduce(true) { |r, (key, val)| r && actual[key.to_s] == val }
end
end
diff --git a/spec/support/shared_examples/models/services_fields_shared_examples.rb b/spec/support/shared_examples/models/services_fields_shared_examples.rb
new file mode 100644
index 00000000000..6fbd0da9383
--- /dev/null
+++ b/spec/support/shared_examples/models/services_fields_shared_examples.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+shared_examples 'issue tracker fields' do
+ let(:title) { 'custom title' }
+ let(:description) { 'custom description' }
+ let(:url) { 'http://issue_tracker.example.com' }
+
+ context 'when data are stored in the properties' do
+ describe '#update' do
+ before do
+ service.update(title: 'new_title', description: 'new description')
+ end
+
+ it 'removes title and description from properties' do
+ expect(service.reload.properties).not_to include('title', 'description')
+ end
+
+ it 'stores title & description in services table' do
+ expect(service.read_attribute(:title)).to eq('new_title')
+ expect(service.read_attribute(:description)).to eq('new description')
+ end
+ end
+
+ describe 'reading fields' do
+ it 'returns correct values' do
+ expect(service.title).to eq(title)
+ expect(service.description).to eq(description)
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb b/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb
index 1b09c3dd636..aad63982e7a 100644
--- a/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb
+++ b/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb
@@ -25,16 +25,36 @@ shared_examples_for 'UpdateProjectStatistics' do
.to change { reload_stat }
.by(delta)
end
+
+ it 'schedules a namespace statistics worker' do
+ expect(Namespaces::ScheduleAggregationWorker)
+ .to receive(:perform_async).once
+
+ subject.save!
+ end
+
+ context 'when feature flag is disabled for the namespace' do
+ it 'does not schedules a namespace statistics worker' do
+ namespace = subject.project.root_ancestor
+
+ stub_feature_flags(update_statistics_namespace: false, namespace: namespace)
+
+ expect(Namespaces::ScheduleAggregationWorker)
+ .not_to receive(:perform_async)
+
+ subject.save!
+ end
+ end
end
context 'when updating' do
+ let(:delta) { 42 }
+
before do
subject.save!
end
it 'updates project statistics' do
- delta = 42
-
expect(ProjectStatistics)
.to receive(:increment_statistic)
.and_call_original
@@ -45,6 +65,42 @@ shared_examples_for 'UpdateProjectStatistics' do
.to change { reload_stat }
.by(delta)
end
+
+ it 'schedules a namespace statistics worker' do
+ expect(Namespaces::ScheduleAggregationWorker)
+ .to receive(:perform_async).once
+
+ subject.write_attribute(statistic_attribute, read_attribute + delta)
+ subject.save!
+ end
+
+ it 'avoids N + 1 queries' do
+ subject.write_attribute(statistic_attribute, read_attribute + delta)
+
+ control_count = ActiveRecord::QueryRecorder.new do
+ subject.save!
+ end
+
+ subject.write_attribute(statistic_attribute, read_attribute + delta)
+
+ expect do
+ subject.save!
+ end.not_to exceed_query_limit(control_count)
+ end
+
+ context 'when the feature flag is disabled for the namespace' do
+ it 'does not schedule a namespace statistics worker' do
+ namespace = subject.project.root_ancestor
+
+ stub_feature_flags(update_statistics_namespace: false, namespace: namespace)
+
+ expect(Namespaces::ScheduleAggregationWorker)
+ .not_to receive(:perform_async)
+
+ subject.write_attribute(statistic_attribute, read_attribute + delta)
+ subject.save!
+ end
+ end
end
context 'when destroying' do
@@ -59,11 +115,18 @@ shared_examples_for 'UpdateProjectStatistics' do
.to receive(:increment_statistic)
.and_call_original
- expect { subject.destroy }
+ expect { subject.destroy! }
.to change { reload_stat }
.by(delta)
end
+ it 'schedules a namespace statistics worker' do
+ expect(Namespaces::ScheduleAggregationWorker)
+ .to receive(:perform_async).once
+
+ subject.destroy!
+ end
+
context 'when it is destroyed from the project level' do
it 'does not update the project statistics' do
expect(ProjectStatistics)
@@ -72,6 +135,27 @@ shared_examples_for 'UpdateProjectStatistics' do
project.update(pending_delete: true)
project.destroy!
end
+
+ it 'does not schedule a namespace statistics worker' do
+ expect(Namespaces::ScheduleAggregationWorker)
+ .not_to receive(:perform_async)
+
+ project.update(pending_delete: true)
+ project.destroy!
+ end
+ end
+
+ context 'when feature flag is disabled for the namespace' do
+ it 'does not schedule a namespace statistics worker' do
+ namespace = subject.project.root_ancestor
+
+ stub_feature_flags(update_statistics_namespace: false, namespace: namespace)
+
+ expect(Namespaces::ScheduleAggregationWorker)
+ .not_to receive(:perform_async)
+
+ subject.destroy!
+ end
end
end
end
diff --git a/spec/workers/namespaces/prune_aggregation_schedules_worker_spec.rb b/spec/workers/namespaces/prune_aggregation_schedules_worker_spec.rb
new file mode 100644
index 00000000000..b069b080531
--- /dev/null
+++ b/spec/workers/namespaces/prune_aggregation_schedules_worker_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Namespaces::PruneAggregationSchedulesWorker, '#perform', :clean_gitlab_redis_shared_state do
+ include ExclusiveLeaseHelpers
+
+ let(:namespaces) { create_list(:namespace, 5, :with_aggregation_schedule) }
+ let(:timeout) { Namespace::AggregationSchedule::DEFAULT_LEASE_TIMEOUT }
+
+ subject(:worker) { described_class.new }
+
+ before do
+ allow(Namespaces::RootStatisticsWorker)
+ .to receive(:perform_async).and_return(nil)
+
+ allow(Namespaces::RootStatisticsWorker)
+ .to receive(:perform_in).and_return(nil)
+
+ namespaces.each do |namespace|
+ lease_key = "namespace:namespaces_root_statistics:#{namespace.id}"
+ stub_exclusive_lease(lease_key, timeout: timeout)
+ end
+ end
+
+ it 'schedules a worker per pending aggregation' do
+ expect(Namespaces::RootStatisticsWorker)
+ .to receive(:perform_async).exactly(5).times
+
+ expect(Namespaces::RootStatisticsWorker)
+ .to receive(:perform_in).exactly(5).times
+
+ worker.perform
+ end
+end
diff --git a/spec/workers/namespaces/root_statistics_worker_spec.rb b/spec/workers/namespaces/root_statistics_worker_spec.rb
new file mode 100644
index 00000000000..8dd74b96d49
--- /dev/null
+++ b/spec/workers/namespaces/root_statistics_worker_spec.rb
@@ -0,0 +1,88 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Namespaces::RootStatisticsWorker, '#perform' do
+ let(:group) { create(:group, :with_aggregation_schedule) }
+
+ subject(:worker) { described_class.new }
+
+ context 'with a namespace' do
+ it 'executes refresher service' do
+ expect_any_instance_of(Namespaces::StatisticsRefresherService)
+ .to receive(:execute)
+
+ worker.perform(group.id)
+ end
+
+ it 'deletes namespace aggregated schedule row' do
+ worker.perform(group.id)
+
+ expect(group.reload.aggregation_schedule).to be_nil
+ end
+
+ context 'when something goes wrong when updating' do
+ before do
+ allow_any_instance_of(Namespaces::StatisticsRefresherService)
+ .to receive(:execute)
+ .and_raise(Namespaces::StatisticsRefresherService::RefresherError, 'error')
+ end
+
+ it 'does not delete the aggregation schedule' do
+ worker.perform(group.id)
+
+ expect(group.reload.aggregation_schedule).to be_present
+ end
+
+ it 'logs the error' do
+ # A Namespace::RootStatisticsWorker is scheduled when
+ # a Namespace::AggregationSchedule is created, so having
+ # create(:group, :with_aggregation_schedule), will execute
+ # another worker
+ allow_any_instance_of(Namespace::AggregationSchedule)
+ .to receive(:schedule_root_storage_statistics).and_return(nil)
+
+ expect(Gitlab::SidekiqLogger).to receive(:error).once
+
+ worker.perform(group.id)
+ end
+ end
+ end
+
+ context 'with no namespace' do
+ before do
+ group.destroy
+ end
+
+ it 'does not execute the refresher service' do
+ expect_any_instance_of(Namespaces::StatisticsRefresherService)
+ .not_to receive(:execute)
+
+ worker.perform(group.id)
+ end
+ end
+
+ context 'with a namespace with no aggregation scheduled' do
+ before do
+ group.aggregation_schedule.destroy
+ end
+
+ it 'does not execute the refresher service' do
+ expect_any_instance_of(Namespaces::StatisticsRefresherService)
+ .not_to receive(:execute)
+
+ worker.perform(group.id)
+ end
+ end
+
+ context 'when update_statistics_namespace is off' do
+ it 'does not create a new one' do
+ stub_feature_flags(update_statistics_namespace: false, namespace: group)
+
+ expect_any_instance_of(Namespaces::StatisticsRefresherService)
+ .not_to receive(:execute)
+
+ worker.perform(group.id)
+ end
+ end
+end
diff --git a/spec/workers/namespaces/schedule_aggregation_worker_spec.rb b/spec/workers/namespaces/schedule_aggregation_worker_spec.rb
new file mode 100644
index 00000000000..7432ca12f2a
--- /dev/null
+++ b/spec/workers/namespaces/schedule_aggregation_worker_spec.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Namespaces::ScheduleAggregationWorker, '#perform' do
+ let(:group) { create(:group) }
+
+ subject(:worker) { described_class.new }
+
+ context 'when group is the root ancestor' do
+ context 'when aggregation schedule exists' do
+ it 'does not create a new one' do
+ Namespace::AggregationSchedule.safe_find_or_create_by!(namespace_id: group.id)
+
+ expect do
+ worker.perform(group.id)
+ end.not_to change(Namespace::AggregationSchedule, :count)
+ end
+ end
+
+ context 'when update_statistics_namespace is off' do
+ it 'does not create a new one' do
+ stub_feature_flags(update_statistics_namespace: false, namespace: group)
+
+ expect do
+ worker.perform(group.id)
+ end.not_to change(Namespace::AggregationSchedule, :count)
+ end
+ end
+
+ context 'when aggregation schedule does not exist' do
+ it 'creates one' do
+ allow_any_instance_of(Namespace::AggregationSchedule)
+ .to receive(:schedule_root_storage_statistics).and_return(nil)
+
+ expect do
+ worker.perform(group.id)
+ end.to change(Namespace::AggregationSchedule, :count).by(1)
+
+ expect(group.aggregation_schedule).to be_present
+ end
+ end
+ end
+
+ context 'when group is not the root ancestor' do
+ let(:parent_group) { create(:group) }
+ let(:group) { create(:group, parent: parent_group) }
+
+ it 'creates an aggregation schedule for the root' do
+ allow_any_instance_of(Namespace::AggregationSchedule)
+ .to receive(:schedule_root_storage_statistics).and_return(nil)
+
+ worker.perform(group.id)
+
+ expect(parent_group.aggregation_schedule).to be_present
+ end
+ end
+
+ context 'when namespace does not exist' do
+ it 'logs the error' do
+ expect(Gitlab::SidekiqLogger).to receive(:error).once
+
+ worker.perform(12345)
+ end
+ end
+end