From 7ab0cadbbdf42fdd316941b3260e294577d649f4 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Tue, 6 Jul 2021 13:14:47 +0000 Subject: Add latest changes from gitlab-org/gitlab@14-0-stable-ee --- .../sidebar/board_sidebar_time_tracker.vue | 1 + app/assets/javascripts/frequent_items/utils.js | 10 ++-- .../nav/components/top_nav_container_view.vue | 12 ++++- .../time_tracking/sidebar_time_tracking.vue | 5 ++ .../components/time_tracking/time_tracker.vue | 36 ++++++++++----- app/assets/javascripts/sidebar/mount_sidebar.js | 3 +- app/models/deploy_key.rb | 1 + app/models/namespace.rb | 9 ++-- app/policies/project_policy.rb | 18 ++++++++ .../components/sidebar/_time_tracker.html.haml | 1 + .../recursive_approach_for_all_projects.yml | 8 ++++ config/initializers/1_settings.rb | 9 ++-- doc/api/deployments.md | 7 +-- .../base_migrater.rb | 6 +-- .../sidebar/board_sidebar_time_tracker_spec.js | 2 + spec/frontend/frequent_items/utils_spec.js | 47 +++++++++---------- .../frontend/nav/components/responsive_app_spec.js | 2 + .../nav/components/top_nav_container_view_spec.js | 5 +- .../components/time_tracking/time_tracker_spec.js | 1 + .../artifact_migrater_spec.rb | 14 ++++++ .../pages_deployment_migrater_spec.rb | 14 ++++++ spec/models/deploy_key_spec.rb | 42 +++++++++++++++++ spec/models/namespace_spec.rb | 8 ++++ spec/policies/project_policy_spec.rb | 31 +++++++++++++ ...and_remote_storage_migration_shared_examples.rb | 53 ++++++++++++++++++++++ 25 files changed, 286 insertions(+), 59 deletions(-) create mode 100644 config/feature_flags/development/recursive_approach_for_all_projects.yml create mode 100644 spec/lib/gitlab/local_and_remote_storage_migration/artifact_migrater_spec.rb create mode 100644 spec/lib/gitlab/local_and_remote_storage_migration/pages_deployment_migrater_spec.rb create mode 100644 spec/support/shared_examples/lib/gitlab/local_and_remote_storage_migration_shared_examples.rb diff --git a/app/assets/javascripts/boards/components/sidebar/board_sidebar_time_tracker.vue b/app/assets/javascripts/boards/components/sidebar/board_sidebar_time_tracker.vue index 5d61f7b2887..a35b3f14be4 100644 --- a/app/assets/javascripts/boards/components/sidebar/board_sidebar_time_tracker.vue +++ b/app/assets/javascripts/boards/components/sidebar/board_sidebar_time_tracker.vue @@ -29,6 +29,7 @@ export default { diff --git a/app/assets/javascripts/sidebar/mount_sidebar.js b/app/assets/javascripts/sidebar/mount_sidebar.js index f53760eab93..67c72b17f1f 100644 --- a/app/assets/javascripts/sidebar/mount_sidebar.js +++ b/app/assets/javascripts/sidebar/mount_sidebar.js @@ -391,7 +391,7 @@ function mountSubscriptionsComponent() { function mountTimeTrackingComponent() { const el = document.getElementById('issuable-time-tracker'); - const { iid, fullPath, issuableType, timeTrackingLimitToHours } = getSidebarOptions(); + const { id, iid, fullPath, issuableType, timeTrackingLimitToHours } = getSidebarOptions(); if (!el) return; @@ -404,6 +404,7 @@ function mountTimeTrackingComponent() { createElement(SidebarTimeTracking, { props: { fullPath, + issuableId: id.toString(), issuableIid: iid.toString(), limitToHours: timeTrackingLimitToHours, }, diff --git a/app/models/deploy_key.rb b/app/models/deploy_key.rb index 25e3b9fe4f0..8f5a713af3f 100644 --- a/app/models/deploy_key.rb +++ b/app/models/deploy_key.rb @@ -3,6 +3,7 @@ class DeployKey < Key include FromUnion include IgnorableColumns + include PolicyActor has_many :deploy_keys_projects, inverse_of: :deploy_key, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :projects, through: :deploy_keys_projects diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 90e06e44165..5f41441058b 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -271,9 +271,12 @@ class Namespace < ApplicationRecord # Includes projects from this namespace and projects from all subgroups # that belongs to this namespace def all_projects - namespace = user? ? self : self_and_descendant_ids - - Project.where(namespace: namespace) + if Feature.enabled?(:recursive_approach_for_all_projects, default_enabled: :yaml) + namespace = user? ? self : self_and_descendants + Project.where(namespace: namespace) + else + Project.inside_path(full_path) + end end def has_parent? diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index e93c60c3710..3cb4644a60d 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -69,6 +69,16 @@ class ProjectPolicy < BasePolicy project.merge_requests_allowing_push_to_user(user).any? end + desc "Deploy key with read access" + condition(:download_code_deploy_key) do + user.is_a?(DeployKey) && user.has_access_to?(project) + end + + desc "Deploy key with write access" + condition(:push_code_deploy_key) do + user.is_a?(DeployKey) && user.can_push_to?(project) + end + desc "Deploy token with read_package_registry scope" condition(:read_package_registry_deploy_token) do user.is_a?(DeployToken) && user.has_access_to?(project) && user.read_package_registry @@ -616,6 +626,14 @@ class ProjectPolicy < BasePolicy prevent :move_design end + rule { download_code_deploy_key }.policy do + enable :download_code + end + + rule { push_code_deploy_key }.policy do + enable :push_code + end + rule { read_package_registry_deploy_token }.policy do enable :read_package enable :read_project diff --git a/app/views/shared/boards/components/sidebar/_time_tracker.html.haml b/app/views/shared/boards/components/sidebar/_time_tracker.html.haml index 9f230a4a09b..eea3ec35000 100644 --- a/app/views/shared/boards/components/sidebar/_time_tracker.html.haml +++ b/app/views/shared/boards/components/sidebar/_time_tracker.html.haml @@ -1,5 +1,6 @@ .block.time-tracking %time-tracker{ ":limit-to-hours" => "timeTrackingLimitToHours", + ":issuable-id" => "issue.id ? issue.id.toString() : ''", ":issuable-iid" => "issue.iid ? issue.iid.toString() : ''", ":full-path" => "issue.project ? issue.project.fullPath : ''", "root-path" => "#{root_url}" } diff --git a/config/feature_flags/development/recursive_approach_for_all_projects.yml b/config/feature_flags/development/recursive_approach_for_all_projects.yml new file mode 100644 index 00000000000..e2d656b7de2 --- /dev/null +++ b/config/feature_flags/development/recursive_approach_for_all_projects.yml @@ -0,0 +1,8 @@ +--- +name: recursive_approach_for_all_projects +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64632 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/334817 +milestone: '14.1' +type: development +group: group::fulfillment +default_enabled: true diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 8f4c6492cad..e311a364977 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -577,12 +577,9 @@ Settings.cron_jobs['users_deactivate_dormant_users_worker']['job_class'] = 'User Settings.cron_jobs['ci_delete_unit_tests_worker'] ||= Settingslogic.new({}) Settings.cron_jobs['ci_delete_unit_tests_worker']['cron'] ||= '0 0 * * *' Settings.cron_jobs['ci_delete_unit_tests_worker']['job_class'] = 'Ci::DeleteUnitTestsWorker' - -Gitlab.com do - Settings.cron_jobs['batched_background_migrations_worker'] ||= Settingslogic.new({}) - Settings.cron_jobs['batched_background_migrations_worker']['cron'] ||= '* * * * *' - Settings.cron_jobs['batched_background_migrations_worker']['job_class'] = 'Database::BatchedBackgroundMigrationWorker' -end +Settings.cron_jobs['batched_background_migrations_worker'] ||= Settingslogic.new({}) +Settings.cron_jobs['batched_background_migrations_worker']['cron'] ||= '* * * * *' +Settings.cron_jobs['batched_background_migrations_worker']['job_class'] = 'Database::BatchedBackgroundMigrationWorker' Gitlab.ee do Settings.cron_jobs['analytics_devops_adoption_create_all_snapshots_worker'] ||= Settingslogic.new({}) diff --git a/doc/api/deployments.md b/doc/api/deployments.md index 586f3edf51e..a2e56fc8557 100644 --- a/doc/api/deployments.md +++ b/doc/api/deployments.md @@ -9,9 +9,6 @@ type: concepts, howto ## List project deployments -> The `updated_after` and `updated_before` attributes were removed and replaced - by `finished_after` and `finished_before` respectively in GitLab 14.0. - Get a list of deployments in a project. ```plaintext @@ -23,8 +20,8 @@ GET /projects/:id/deployments | `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. | | `order_by` | string | no | Return deployments ordered by either one of `id`, `iid`, `created_at`, `updated_at` or `ref` fields. Default is `id`. | | `sort` | string | no | Return deployments sorted in `asc` or `desc` order. Default is `asc`. | -| `finished_after` | datetime | no | Return deployments updated after the specified date. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). | -| `finished_before` | datetime | no | Return deployments updated before the specified date. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). | +| `updated_after` | datetime | no | Return deployments updated after the specified date. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). | +| `updated_before` | datetime | no | Return deployments updated before the specified date. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). | | `environment` | string | no | The [name of the environment](../ci/environments/index.md) to filter deployments by. | | `status` | string | no | The status to filter deployments by. One of `created`, `running`, `success`, `failed`, `canceled`. diff --git a/lib/gitlab/local_and_remote_storage_migration/base_migrater.rb b/lib/gitlab/local_and_remote_storage_migration/base_migrater.rb index f859d293e76..0484957a6fe 100644 --- a/lib/gitlab/local_and_remote_storage_migration/base_migrater.rb +++ b/lib/gitlab/local_and_remote_storage_migration/base_migrater.rb @@ -8,7 +8,7 @@ module Gitlab end def migrate_to_remote_storage - logger.info('Starting transfer to remote storage') + logger.info('Starting transfer to object storage') migrate(items_with_files_stored_locally, ObjectStorage::Store::REMOTE) end @@ -38,11 +38,11 @@ module Gitlab end def log_success(item, store) - logger.info("Transferred #{item.class.name} ID #{item.id} of type #{item.file_type} with size #{item.size} to #{storage_label(store)} storage") + logger.info("Transferred #{item.class.name} ID #{item.id} with size #{item.size} to #{storage_label(store)} storage") end def log_error(err, item) - logger.warn("Failed to transfer #{item.class.name} of type #{item.file_type} and ID #{item.id} with error: #{err.message}") + logger.warn("Failed to transfer #{item.class.name} ID #{item.id} with error: #{err.message}") end def storage_label(store) diff --git a/spec/frontend/boards/components/sidebar/board_sidebar_time_tracker_spec.js b/spec/frontend/boards/components/sidebar/board_sidebar_time_tracker_spec.js index 74441e147cf..7c8996be0b8 100644 --- a/spec/frontend/boards/components/sidebar/board_sidebar_time_tracker_spec.js +++ b/spec/frontend/boards/components/sidebar/board_sidebar_time_tracker_spec.js @@ -26,6 +26,7 @@ describe('BoardSidebarTimeTracker', () => { store = createStore(); store.state.boardItems = { 1: { + id: 1, iid: 1, timeEstimate: 3600, totalTimeSpent: 1800, @@ -49,6 +50,7 @@ describe('BoardSidebarTimeTracker', () => { expect(wrapper.find(IssuableTimeTracker).props()).toEqual({ limitToHours: timeTrackingLimitToHours, showCollapsed: false, + issuableId: '1', issuableIid: '1', fullPath: '', initialTimeTracking: { diff --git a/spec/frontend/frequent_items/utils_spec.js b/spec/frontend/frequent_items/utils_spec.js index a7ab18b0d10..8c3841558f4 100644 --- a/spec/frontend/frequent_items/utils_spec.js +++ b/spec/frontend/frequent_items/utils_spec.js @@ -66,35 +66,36 @@ describe('Frequent Items utils spec', () => { }); describe('updateExistingFrequentItem', () => { - let mockedProject; - - beforeEach(() => { - mockedProject = { - ...mockProject, - frequency: 1, - lastAccessedOn: 1497979281815, - }; + const LAST_ACCESSED = 1497979281815; + const WITHIN_AN_HOUR = LAST_ACCESSED + HOUR_IN_MS; + const OVER_AN_HOUR = WITHIN_AN_HOUR + 1; + const EXISTING_ITEM = Object.freeze({ + ...mockProject, + frequency: 1, + lastAccessedOn: 1497979281815, }); - it('updates item if accessed over an hour ago', () => { - const newTimestamp = Date.now() + HOUR_IN_MS + 1; + it.each` + desc | existingProps | newProps | expected + ${'updates item if accessed over an hour ago'} | ${{}} | ${{ lastAccessedOn: OVER_AN_HOUR }} | ${{ lastAccessedOn: Date.now(), frequency: 2 }} + ${'does not update is accessed with an hour'} | ${{}} | ${{ lastAccessedOn: WITHIN_AN_HOUR }} | ${{ lastAccessedOn: EXISTING_ITEM.lastAccessedOn, frequency: 1 }} + ${'updates if lastAccessedOn not found'} | ${{ lastAccessedOn: undefined }} | ${{ lastAccessedOn: WITHIN_AN_HOUR }} | ${{ lastAccessedOn: Date.now(), frequency: 2 }} + `('$desc', ({ existingProps, newProps, expected }) => { const newItem = { - ...mockedProject, - lastAccessedOn: newTimestamp, + ...EXISTING_ITEM, + ...newProps, }; - const result = updateExistingFrequentItem(mockedProject, newItem); - - expect(result.frequency).toBe(mockedProject.frequency + 1); - }); - - it('does not update item if accessed within the hour', () => { - const newItem = { - ...mockedProject, - lastAccessedOn: mockedProject.lastAccessedOn + HOUR_IN_MS, + const existingItem = { + ...EXISTING_ITEM, + ...existingProps, }; - const result = updateExistingFrequentItem(mockedProject, newItem); - expect(result.frequency).toBe(mockedProject.frequency); + const result = updateExistingFrequentItem(existingItem, newItem); + + expect(result).toEqual({ + ...newItem, + ...expected, + }); }); }); diff --git a/spec/frontend/nav/components/responsive_app_spec.js b/spec/frontend/nav/components/responsive_app_spec.js index 7221ea2c5cd..e1b443745e3 100644 --- a/spec/frontend/nav/components/responsive_app_spec.js +++ b/spec/frontend/nav/components/responsive_app_spec.js @@ -111,6 +111,7 @@ describe('~/nav/components/responsive_app.vue', () => { containerClass: 'gl-px-3', frequentItemsDropdownType: ResponsiveApp.FREQUENT_ITEMS_PROJECTS.namespace, frequentItemsVuexModule: ResponsiveApp.FREQUENT_ITEMS_PROJECTS.vuexModule, + currentItem: {}, linksPrimary: TEST_NAV_DATA.views.projects.linksPrimary, linksSecondary: TEST_NAV_DATA.views.projects.linksSecondary, }; @@ -118,6 +119,7 @@ describe('~/nav/components/responsive_app.vue', () => { containerClass: 'gl-px-3', frequentItemsDropdownType: ResponsiveApp.FREQUENT_ITEMS_GROUPS.namespace, frequentItemsVuexModule: ResponsiveApp.FREQUENT_ITEMS_GROUPS.vuexModule, + currentItem: {}, linksPrimary: TEST_NAV_DATA.views.groups.linksPrimary, linksSecondary: TEST_NAV_DATA.views.groups.linksSecondary, }; diff --git a/spec/frontend/nav/components/top_nav_container_view_spec.js b/spec/frontend/nav/components/top_nav_container_view_spec.js index 06d2179b859..0218f09af0a 100644 --- a/spec/frontend/nav/components/top_nav_container_view_spec.js +++ b/spec/frontend/nav/components/top_nav_container_view_spec.js @@ -1,4 +1,5 @@ import { shallowMount } from '@vue/test-utils'; +import { merge } from 'lodash'; import { nextTick } from 'vue'; import FrequentItemsApp from '~/frequent_items/components/app.vue'; import { FREQUENT_ITEMS_PROJECTS } from '~/frequent_items/constants'; @@ -82,7 +83,9 @@ describe('~/nav/components/top_nav_container_view.vue', () => { it('renders frequent items app', () => { expect(findFrequentItemsApp()).toEqual({ vuexModule: DEFAULT_PROPS.frequentItemsVuexModule, - props: expect.objectContaining(TEST_OTHER_PROPS), + props: expect.objectContaining( + merge({ currentItem: { lastAccessedOn: Date.now() } }, TEST_OTHER_PROPS), + ), attributes: expect.objectContaining(EXTRA_ATTRS), }); }); diff --git a/spec/frontend/sidebar/components/time_tracking/time_tracker_spec.js b/spec/frontend/sidebar/components/time_tracking/time_tracker_spec.js index e08bd80b18e..eb202a8cfcc 100644 --- a/spec/frontend/sidebar/components/time_tracking/time_tracker_spec.js +++ b/spec/frontend/sidebar/components/time_tracking/time_tracker_spec.js @@ -19,6 +19,7 @@ describe('Issuable Time Tracker', () => { const defaultProps = { limitToHours: false, fullPath: 'gitlab-org/gitlab-test', + issuableId: '1', issuableIid: '1', initialTimeTracking: { ...issuableTimeTrackingResponse.data.workspace.issuable, diff --git a/spec/lib/gitlab/local_and_remote_storage_migration/artifact_migrater_spec.rb b/spec/lib/gitlab/local_and_remote_storage_migration/artifact_migrater_spec.rb new file mode 100644 index 00000000000..b3f2003c207 --- /dev/null +++ b/spec/lib/gitlab/local_and_remote_storage_migration/artifact_migrater_spec.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'support/shared_examples/lib/gitlab/local_and_remote_storage_migration_shared_examples' + +RSpec.describe Gitlab::LocalAndRemoteStorageMigration::ArtifactMigrater do + before do + stub_artifacts_object_storage(enabled: true) + end + + let!(:item) { create(:ci_job_artifact, :archive, file_store: start_store) } + + it_behaves_like 'local and remote storage migration' +end diff --git a/spec/lib/gitlab/local_and_remote_storage_migration/pages_deployment_migrater_spec.rb b/spec/lib/gitlab/local_and_remote_storage_migration/pages_deployment_migrater_spec.rb new file mode 100644 index 00000000000..2cc48b445f1 --- /dev/null +++ b/spec/lib/gitlab/local_and_remote_storage_migration/pages_deployment_migrater_spec.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'support/shared_examples/lib/gitlab/local_and_remote_storage_migration_shared_examples' + +RSpec.describe Gitlab::LocalAndRemoteStorageMigration::PagesDeploymentMigrater do + before do + stub_pages_object_storage(::Pages::DeploymentUploader, enabled: true) + end + + let!(:item) { create(:pages_deployment, file_store: start_store) } + + it_behaves_like 'local and remote storage migration' +end diff --git a/spec/models/deploy_key_spec.rb b/spec/models/deploy_key_spec.rb index d4ccaa6a10e..fa78527e366 100644 --- a/spec/models/deploy_key_spec.rb +++ b/spec/models/deploy_key_spec.rb @@ -93,4 +93,46 @@ RSpec.describe DeployKey, :mailer do end end end + + describe 'PolicyActor methods' do + let_it_be(:user) { create(:user) } + let_it_be(:deploy_key) { create(:deploy_key, user: user) } + let_it_be(:project) { create(:project, creator: user, namespace: user.namespace) } + + let(:methods) { PolicyActor.instance_methods } + + subject { deploy_key } + + it 'responds to all PolicyActor methods' do + methods.each do |method| + expect(subject.respond_to?(method)).to be true + end + end + + describe '#can?' do + it { expect(user.can?(:read_project, project)).to be true } + + context 'when a read deploy key is enabled in the project' do + let!(:deploy_keys_project) { create(:deploy_keys_project, project: project, deploy_key: deploy_key) } + + it { expect(subject.can?(:read_project, project)).to be false } + it { expect(subject.can?(:download_code, project)).to be true } + it { expect(subject.can?(:push_code, project)).to be false } + end + + context 'when a write deploy key is enabled in the project' do + let!(:deploy_keys_project) { create(:deploy_keys_project, :write_access, project: project, deploy_key: deploy_key) } + + it { expect(subject.can?(:read_project, project)).to be false } + it { expect(subject.can?(:download_code, project)).to be true } + it { expect(subject.can?(:push_code, project)).to be true } + end + + context 'when the deploy key is not enabled in the project' do + it { expect(subject.can?(:read_project, project)).to be false } + it { expect(subject.can?(:download_code, project)).to be false } + it { expect(subject.can?(:push_code, project)).to be false } + end + end + end end diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index 2c514593de8..373f3a89e14 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -1044,6 +1044,14 @@ RSpec.describe Namespace do end describe '#all_projects' do + context 'when recursive approach is disabled' do + before do + stub_feature_flags(recursive_approach_for_all_projects: false) + end + + include_examples '#all_projects' + end + context 'with use_traversal_ids feature flag enabled' do before do stub_feature_flags(use_traversal_ids: true) diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb index d0fe0cca8a1..f3c92751d06 100644 --- a/spec/policies/project_policy_spec.rb +++ b/spec/policies/project_policy_spec.rb @@ -795,6 +795,37 @@ RSpec.describe ProjectPolicy do end end + context 'deploy key access' do + context 'private project' do + let(:project) { private_project } + let!(:deploy_key) { create(:deploy_key, user: owner) } + + subject { described_class.new(deploy_key, project) } + + context 'when a read deploy key is enabled in the project' do + let!(:deploy_keys_project) { create(:deploy_keys_project, project: project, deploy_key: deploy_key) } + + it { is_expected.to be_allowed(:download_code) } + it { is_expected.to be_disallowed(:push_code) } + it { is_expected.to be_disallowed(:read_project) } + end + + context 'when a write deploy key is enabled in the project' do + let!(:deploy_keys_project) { create(:deploy_keys_project, :write_access, project: project, deploy_key: deploy_key) } + + it { is_expected.to be_allowed(:download_code) } + it { is_expected.to be_allowed(:push_code) } + it { is_expected.to be_disallowed(:read_project) } + end + + context 'when the deploy key is not enabled in the project' do + it { is_expected.to be_disallowed(:download_code) } + it { is_expected.to be_disallowed(:push_code) } + it { is_expected.to be_disallowed(:read_project) } + end + end + end + context 'deploy token access' do let!(:project_deploy_token) do create(:project_deploy_token, project: project, deploy_token: deploy_token) diff --git a/spec/support/shared_examples/lib/gitlab/local_and_remote_storage_migration_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/local_and_remote_storage_migration_shared_examples.rb new file mode 100644 index 00000000000..27ca27a9035 --- /dev/null +++ b/spec/support/shared_examples/lib/gitlab/local_and_remote_storage_migration_shared_examples.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'local and remote storage migration' do + let(:logger) { Logger.new("/dev/null") } + let(:migrater) { described_class.new(logger) } + + using RSpec::Parameterized::TableSyntax + + where(:start_store, :end_store, :method) do + ObjectStorage::Store::LOCAL | ObjectStorage::Store::REMOTE | :migrate_to_remote_storage + ObjectStorage::Store::REMOTE | ObjectStorage::Store::REMOTE | :migrate_to_remote_storage # rubocop:disable Lint/BinaryOperatorWithIdenticalOperands + ObjectStorage::Store::REMOTE | ObjectStorage::Store::LOCAL | :migrate_to_local_storage + ObjectStorage::Store::LOCAL | ObjectStorage::Store::LOCAL | :migrate_to_local_storage # rubocop:disable Lint/BinaryOperatorWithIdenticalOperands + end + + with_them do + let(:storage_name) { end_store == ObjectStorage::Store::REMOTE ? 'object' : 'local' } + + it 'successfully migrates' do + expect(logger).to receive(:info).with("Starting transfer to #{storage_name} storage") + + if start_store != end_store + expect(logger).to receive(:info).with("Transferred #{item.class.name} ID #{item.id} with size #{item.size} to #{storage_name} storage") + end + + expect(item.file_store).to eq(start_store) + + migrater.send(method) + + expect(item.reload.file_store).to eq(end_store) + end + end + + context 'when migration fails' do + let(:start_store) { ObjectStorage::Store::LOCAL } + + it 'prints error' do + expect_next_instance_of(item.file.class) do |file| + expect(file).to receive(:migrate!).and_raise("error message") + end + + expect(logger).to receive(:info).with("Starting transfer to object storage") + + expect(logger).to receive(:warn).with("Failed to transfer #{item.class.name} ID #{item.id} with error: error message") + + expect(item.file_store).to eq(start_store) + + migrater.migrate_to_remote_storage + + expect(item.reload.file_store).to eq(start_store) + end + end +end -- cgit v1.2.3