diff --git a/app/graphql/resolvers/concerns/resolves_merge_requests.rb b/app/graphql/resolvers/concerns/resolves_merge_requests.rb
index d356e1685722e3b4a4111567f3b41f673eb5b47e..7e21e3617f4d99f927db0a663f03702e994ac338 100644
--- a/app/graphql/resolvers/concerns/resolves_merge_requests.rb
+++ b/app/graphql/resolvers/concerns/resolves_merge_requests.rb
@@ -61,6 +61,7 @@ def preloads
commit_count: [:metrics],
diff_stats_summary: [:metrics],
approved_by: [:approved_by_users],
+ merge_after: [:merge_schedule],
milestone: [:milestone],
security_auto_fix: [:author],
head_pipeline: [:merge_request_diff, { head_pipeline: [:merge_request] }],
diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb
index 8daef281b603aa50dbd17496608ff5a41968fbef..e261b55300f069a836ac5f4167d3a39824be2898 100644
--- a/app/graphql/types/merge_request_type.rb
+++ b/app/graphql/types/merge_request_type.rb
@@ -100,6 +100,11 @@ class MergeRequestType < BaseObject
method: :public_merge_status, null: true,
description: 'Merge status of the merge request.'
+ field :merge_after, ::Types::TimeType,
+ null: true,
+ description: 'Date after which the merge request can be merged.',
+ alpha: { milestone: '17.4' }
+
field :detailed_merge_status, ::Types::MergeRequests::DetailedMergeStatusEnum, null: true,
calls_gitaly: true,
description: 'Detailed merge status of the merge request.'
@@ -347,6 +352,10 @@ def merge_user
object.metrics&.merged_by || object.merge_user
end
+ def merge_after
+ object.merge_schedule&.merge_after
+ end
+
def detailed_merge_status
::MergeRequests::Mergeability::DetailedMergeStatusService.new(merge_request: object).execute
end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 93ec04308668089865565dd2822374168490aedb..699af3ec43cd2528e1a40321b679ab80acc458e6 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -66,6 +66,8 @@ class MergeRequest < ApplicationRecord
has_one :predictions, inverse_of: :merge_request
delegate :suggested_reviewers, to: :predictions
+ has_one :merge_schedule, class_name: 'MergeRequests::MergeSchedule', inverse_of: :merge_request
+
belongs_to :latest_merge_request_diff, class_name: 'MergeRequestDiff'
manual_inverse_association :latest_merge_request_diff, :merge_request
@@ -378,6 +380,7 @@ def public_merge_status
preload_routables.preload(
:assignees, :author, :unresolved_notes, :labels, :milestone,
:timelogs, :latest_merge_request_diff, :reviewers,
+ :merge_schedule,
target_project: :project_feature,
metrics: [:latest_closed_by, :merged_by]
)
diff --git a/app/models/merge_requests/merge_schedule.rb b/app/models/merge_requests/merge_schedule.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5749768b5a10d8368d1c1df0634091013001a19e
--- /dev/null
+++ b/app/models/merge_requests/merge_schedule.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module MergeRequests
+ class MergeSchedule < ApplicationRecord
+ self.table_name = 'merge_request_merge_schedules'
+
+ belongs_to :merge_request, optional: false, inverse_of: :merge_schedule
+
+ before_validation :set_sharding_key
+
+ def set_sharding_key
+ self.project_id = merge_request&.target_project&.id
+ end
+ end
+end
diff --git a/db/docs/merge_request_merge_schedules.yml b/db/docs/merge_request_merge_schedules.yml
new file mode 100644
index 0000000000000000000000000000000000000000..7923501b06b70fd65c34cd93a21a04645fb5e997
--- /dev/null
+++ b/db/docs/merge_request_merge_schedules.yml
@@ -0,0 +1,12 @@
+---
+table_name: merge_request_merge_schedules
+classes:
+- MergeRequests::MergeSchedule
+feature_categories:
+- code_review_workflow
+description: Stores timestamps for scheduled merges
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/165092
+milestone: '17.4'
+gitlab_schema: gitlab_main_cell
+sharding_key:
+ project_id: projects
diff --git a/db/migrate/20240911181854_create_merge_request_merge_schedules.rb b/db/migrate/20240911181854_create_merge_request_merge_schedules.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2c694ceead3ec72fcf6ff6ff76dac257a154bbbb
--- /dev/null
+++ b/db/migrate/20240911181854_create_merge_request_merge_schedules.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class CreateMergeRequestMergeSchedules < Gitlab::Database::Migration[2.2]
+ milestone '17.4'
+
+ def change
+ create_table :merge_request_merge_schedules do |t| # rubocop:disable Migration/EnsureFactoryForTable -- factory exists in spec/factories/merge_request_merge_schedule.rb
+ t.references :merge_request, foreign_key: { on_delete: :cascade }, index: false, null: false
+ t.datetime_with_timezone :merge_after
+
+ t.bigint :project_id, null: false
+
+ t.index :merge_request_id, unique: true
+ t.index :project_id
+ end
+ end
+end
diff --git a/db/migrate/20240911181855_create_merge_request_merge_schedules_sharding_key_fk.rb b/db/migrate/20240911181855_create_merge_request_merge_schedules_sharding_key_fk.rb
new file mode 100644
index 0000000000000000000000000000000000000000..143248ee3c256257b41a8e681a8a64ac18bc89dd
--- /dev/null
+++ b/db/migrate/20240911181855_create_merge_request_merge_schedules_sharding_key_fk.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class CreateMergeRequestMergeSchedulesShardingKeyFk < Gitlab::Database::Migration[2.2]
+ milestone '17.4'
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_foreign_key :merge_request_merge_schedules, :projects, column: :project_id, on_delete: :cascade
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key :merge_request_merge_schedules, column: :project_id
+ end
+ end
+end
diff --git a/db/schema_migrations/20240911181854 b/db/schema_migrations/20240911181854
new file mode 100644
index 0000000000000000000000000000000000000000..75e2bbe9721c9af32eebb040835ee346c618b2bd
--- /dev/null
+++ b/db/schema_migrations/20240911181854
@@ -0,0 +1 @@
+6dd9484b5c7d61943dae2ed684c8d84d0c0319e009c26d1bcbfcbe0ae69939e7
\ No newline at end of file
diff --git a/db/schema_migrations/20240911181855 b/db/schema_migrations/20240911181855
new file mode 100644
index 0000000000000000000000000000000000000000..b3cf9c49b4c858afcb4ff96c6c63cbc6fa97d5db
--- /dev/null
+++ b/db/schema_migrations/20240911181855
@@ -0,0 +1 @@
+4e56708b920c40ba9c6050f2441f313715e3db134ad6114fb9091829506736c1
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 54a815ca16c76e1da0f08c9833fa2e0ceeee022e..d9b5da79c3194950aa6485ece6a8a810232edee3 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -13105,6 +13105,22 @@ CREATE SEQUENCE merge_request_diffs_id_seq
ALTER SEQUENCE merge_request_diffs_id_seq OWNED BY merge_request_diffs.id;
+CREATE TABLE merge_request_merge_schedules (
+ id bigint NOT NULL,
+ merge_request_id bigint NOT NULL,
+ merge_after timestamp with time zone,
+ project_id bigint NOT NULL
+);
+
+CREATE SEQUENCE merge_request_merge_schedules_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE merge_request_merge_schedules_id_seq OWNED BY merge_request_merge_schedules.id;
+
CREATE TABLE merge_request_metrics (
merge_request_id bigint NOT NULL,
latest_build_started_at timestamp without time zone,
@@ -21872,6 +21888,8 @@ ALTER TABLE ONLY merge_request_diff_details ALTER COLUMN merge_request_diff_id S
ALTER TABLE ONLY merge_request_diffs ALTER COLUMN id SET DEFAULT nextval('merge_request_diffs_id_seq'::regclass);
+ALTER TABLE ONLY merge_request_merge_schedules ALTER COLUMN id SET DEFAULT nextval('merge_request_merge_schedules_id_seq'::regclass);
+
ALTER TABLE ONLY merge_request_metrics ALTER COLUMN id SET DEFAULT nextval('merge_request_metrics_id_seq'::regclass);
ALTER TABLE ONLY merge_request_predictions ALTER COLUMN merge_request_id SET DEFAULT nextval('merge_request_predictions_merge_request_id_seq'::regclass);
@@ -24213,6 +24231,9 @@ ALTER TABLE ONLY merge_request_diff_files
ALTER TABLE ONLY merge_request_diffs
ADD CONSTRAINT merge_request_diffs_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY merge_request_merge_schedules
+ ADD CONSTRAINT merge_request_merge_schedules_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY merge_request_metrics
ADD CONSTRAINT merge_request_metrics_pkey PRIMARY KEY (id);
@@ -28900,6 +28921,10 @@ CREATE INDEX index_merge_request_diffs_on_project_id ON merge_request_diffs USIN
CREATE UNIQUE INDEX index_merge_request_diffs_on_unique_merge_request_id ON merge_request_diffs USING btree (merge_request_id) WHERE (diff_type = 2);
+CREATE UNIQUE INDEX index_merge_request_merge_schedules_on_merge_request_id ON merge_request_merge_schedules USING btree (merge_request_id);
+
+CREATE INDEX index_merge_request_merge_schedules_on_project_id ON merge_request_merge_schedules USING btree (project_id);
+
CREATE INDEX index_merge_request_metrics_on_first_deployed_to_production_at ON merge_request_metrics USING btree (first_deployed_to_production_at);
CREATE INDEX index_merge_request_metrics_on_latest_closed_at ON merge_request_metrics USING btree (latest_closed_at) WHERE (latest_closed_at IS NOT NULL);
@@ -34008,6 +34033,9 @@ ALTER TABLE ONLY operations_strategies
ALTER TABLE ONLY lfs_objects_projects
ADD CONSTRAINT fk_a56e02279c FOREIGN KEY (lfs_object_id) REFERENCES lfs_objects(id) ON DELETE RESTRICT NOT VALID;
+ALTER TABLE ONLY merge_request_merge_schedules
+ ADD CONSTRAINT fk_a5ff9339a9 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY merge_requests
ADD CONSTRAINT fk_a6963e8447 FOREIGN KEY (target_project_id) REFERENCES projects(id) ON DELETE CASCADE;
@@ -35214,6 +35242,9 @@ ALTER TABLE zoekt_tasks
ALTER TABLE ONLY ml_models
ADD CONSTRAINT fk_rails_51e87f7c50 FOREIGN KEY (project_id) REFERENCES projects(id);
+ALTER TABLE ONLY merge_request_merge_schedules
+ ADD CONSTRAINT fk_rails_5294434bc3 FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY elastic_group_index_statuses
ADD CONSTRAINT fk_rails_52b9969b12 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 779de53af3077c0198942d230f6e1b5a5d9ddc05..02a1652ddf21cb7cef44e336cab4796d4b348256 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -25494,6 +25494,7 @@ Defines which user roles, users, or groups can merge into a protected branch.
| `iid` | [`String!`](#string) | Internal ID of the merge request. |
| `inProgressMergeCommitSha` | [`String`](#string) | Commit SHA of the merge request if merge is in progress. |
| `labels` | [`LabelConnection`](#labelconnection) | Labels of the merge request. (see [Connections](#connections)) |
+| `mergeAfter` **{warning-solid}** | [`Time`](#time) | **Introduced** in GitLab 17.4. **Status**: Experiment. Date after which the merge request can be merged. |
| `mergeCommitSha` | [`String`](#string) | SHA of the merge request commit (set once merged). |
| `mergeError` | [`String`](#string) | Error message due to a merge error. |
| `mergeOngoing` | [`Boolean!`](#boolean) | Indicates if a merge is currently occurring. |
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index 9aa9bf6fe5d3dea8d912719ac396fc7c9f6ee23e..c8f86761561c8b4d1e718bd9ef08d602c58f51cc 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -17,6 +17,7 @@ DETAILS:
> - `with_merge_status_recheck` [changed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/115948) in GitLab 15.11 [with a flag](../administration/feature_flags.md) named `restrict_merge_status_recheck` to be ignored for requests from users insufficient permissions. Disabled by default.
> - `approvals_before_merge` [deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/119503) in GitLab 16.0.
> - `prepared_at` [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/122001) in GitLab 16.1.
+> - `merge_after` [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/165092) in GitLab 17.4.
All API calls to non-public information require authentication.
@@ -118,6 +119,7 @@ Example response:
"web_url": "https://gitlab.com/DouweM"
},
"merged_at": "2018-09-07T11:16:17.520Z",
+ "merge_after": "2018-09-07T11:16:00.000Z",
"prepared_at": "2018-09-04T11:16:17.520Z",
"closed_by": null,
"closed_at": null,
@@ -367,6 +369,7 @@ Example response:
"web_url": "https://gitlab.com/DouweM"
},
"merged_at": "2018-09-07T11:16:17.520Z",
+ "merge_after": "2018-09-07T11:16:00.000Z",
"prepared_at": "2018-09-04T11:16:17.520Z",
"closed_by": null,
"closed_at": null,
@@ -553,6 +556,7 @@ Example response:
"web_url": "https://gitlab.com/DouweM"
},
"merged_at": "2018-09-07T11:16:17.520Z",
+ "merge_after": "2018-09-07T11:16:00.000Z",
"prepared_at": "2018-09-04T11:16:17.520Z",
"closed_by": null,
"closed_at": null,
@@ -747,6 +751,7 @@ Example response:
"merged_by": null, // Deprecated and will be removed in API v5. Use `merge_user` instead.
"merge_user": null,
"merged_at": null,
+ "merge_after": "2018-09-07T11:16:00.000Z",
"prepared_at": "2018-09-04T11:16:17.520Z",
"closed_by": null,
"closed_at": null,
@@ -1134,6 +1139,7 @@ Example response:
"merged_by": null,
"merge_user": null,
"merged_at": null,
+ "merge_after": "2018-09-07T11:16:00.000Z",
"closed_by": null,
"closed_at": null,
"target_branch": "master",
@@ -1243,6 +1249,7 @@ Example response:
"merged_by": null,
"merge_user": null,
"merged_at": null,
+ "merge_after": "2018-09-07T11:16:00.000Z",
"closed_by": null,
"closed_at": null,
"target_branch": "master",
@@ -1787,6 +1794,7 @@ Example response:
"web_url": "https://gitlab.com/DouweM"
},
"merged_at": "2018-09-07T11:16:17.520Z",
+ "merge_after": "2018-09-07T11:16:00.000Z",
"prepared_at": "2018-09-04T11:16:17.520Z",
"closed_by": null,
"closed_at": null,
@@ -1960,6 +1968,7 @@ Example response:
"web_url": "https://gitlab.com/DouweM"
},
"merged_at": "2018-09-07T11:16:17.520Z",
+ "merge_after": "2018-09-07T11:16:00.000Z",
"prepared_at": "2018-09-04T11:16:17.520Z",
"closed_by": null,
"closed_at": null,
@@ -2153,6 +2162,7 @@ Example response:
"web_url": "https://gitlab.com/DouweM"
},
"merged_at": "2018-09-07T11:16:17.520Z",
+ "merge_after": "2018-09-07T11:16:00.000Z",
"prepared_at": "2018-09-04T11:16:17.520Z",
"closed_by": null,
"closed_at": null,
@@ -2354,6 +2364,7 @@ Example response:
"web_url": "https://gitlab.com/DouweM"
},
"merged_at": "2018-09-07T11:16:17.520Z",
+ "merge_after": "2018-09-07T11:16:00.000Z",
"prepared_at": "2018-09-04T11:16:17.520Z",
"closed_by": null,
"closed_at": null,
@@ -2742,6 +2753,7 @@ Example response:
"web_url": "https://gitlab.com/DouweM"
},
"merged_at": "2018-09-07T11:16:17.520Z",
+ "merge_after": "2018-09-07T11:16:00.000Z",
"prepared_at": "2018-09-04T11:16:17.520Z",
"closed_by": null,
"closed_at": null,
@@ -2906,6 +2918,7 @@ Example response:
"web_url": "https://gitlab.com/DouweM"
},
"merged_at": "2018-09-07T11:16:17.520Z",
+ "merge_after": "2018-09-07T11:16:00.000Z",
"prepared_at": "2018-09-04T11:16:17.520Z",
"closed_by": null,
"closed_at": null,
diff --git a/lib/api/entities/merge_request_basic.rb b/lib/api/entities/merge_request_basic.rb
index fce85ff1c49b851eb514d630e32a92602cded076..4348d292eb4b1a051b042370480b5148cd67a605 100644
--- a/lib/api/entities/merge_request_basic.rb
+++ b/lib/api/entities/merge_request_basic.rb
@@ -64,6 +64,11 @@ class MergeRequestBasic < IssuableEntity
merge_request.public_merge_status
end
expose :detailed_merge_status
+
+ expose :merge_after do |merge_request, _options|
+ merge_request.merge_schedule&.merge_after
+ end
+
expose :diff_head_sha, as: :sha
expose :merge_commit_sha
expose :squash_commit_sha
diff --git a/lib/gitlab/import_export/base/relation_factory.rb b/lib/gitlab/import_export/base/relation_factory.rb
index a48d282307416a77f6e2750f9117db85d15cefec..952aef5625548f765fedf3c5ea0acdfbd22e2d84 100644
--- a/lib/gitlab/import_export/base/relation_factory.rb
+++ b/lib/gitlab/import_export/base/relation_factory.rb
@@ -9,7 +9,7 @@ class RelationFactory
IMPORTED_OBJECT_MAX_RETRIES = 5
- OVERRIDES = { user_contributions: :user }.freeze
+ OVERRIDES = { user_contributions: :user, merge_schedule: 'MergeRequests::MergeSchedule' }.freeze
EXISTING_OBJECT_RELATIONS = %i[].freeze
# This represents all relations that have unique key on `project_id` or `group_id`
diff --git a/spec/factories/merge_request_merge_schedule.rb b/spec/factories/merge_request_merge_schedule.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2be331efe1591202ff342cf1443eb3c14867db73
--- /dev/null
+++ b/spec/factories/merge_request_merge_schedule.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :merge_request_merge_schedule, class: 'MergeRequests::MergeSchedule' do
+ merge_request
+ end
+end
diff --git a/spec/graphql/types/merge_request_type_spec.rb b/spec/graphql/types/merge_request_type_spec.rb
index ebd3cd5f4fceec5718d2678126afcd3c15e5cbfe..773565955f7d4caacbe0b102cda25e235e3d592f 100644
--- a/spec/graphql/types/merge_request_type_spec.rb
+++ b/spec/graphql/types/merge_request_type_spec.rb
@@ -37,7 +37,7 @@
squash_on_merge available_auto_merge_strategies
has_ci mergeable commits committers commits_without_merge_commits squash security_auto_fix default_squash_commit_message
auto_merge_strategy merge_user award_emoji prepared_at codequality_reports_comparer supports_lock_on_merge
- mergeability_checks
+ mergeability_checks merge_after
allows_multiple_assignees allows_multiple_reviewers retargeted name
]
diff --git a/spec/lib/api/entities/merge_request_basic_spec.rb b/spec/lib/api/entities/merge_request_basic_spec.rb
index 09f064c4cdd001f024051b4f125a1003bbfd19b5..d0a056547f7b8f1134c416d50c7e3b458672c076 100644
--- a/spec/lib/api/entities/merge_request_basic_spec.rb
+++ b/spec/lib/api/entities/merge_request_basic_spec.rb
@@ -20,7 +20,7 @@ def present(obj)
expected_fields = %i[
merged_by merge_user merged_at closed_by closed_at target_branch user_notes_count upvotes downvotes
author assignees assignee reviewers source_project_id target_project_id labels draft work_in_progress
- milestone merge_when_pipeline_succeeds merge_status detailed_merge_status sha merge_commit_sha
+ milestone merge_when_pipeline_succeeds merge_status detailed_merge_status merge_after sha merge_commit_sha
squash_commit_sha discussion_locked should_remove_source_branch force_remove_source_branch prepared_at
reference references web_url time_stats squash task_completion_status has_conflicts blocking_discussions_resolved
imported imported_from
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index baa29e4e3ce681dbe152d834a1ee2f04a9714e0b..246feda51d53247664627cc428652b7af30cf53b 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -227,6 +227,7 @@ merge_requests:
- merge_request_context_commits
- merge_request_context_commit_diff_files
- merge_request_stage_events
+- merge_schedule
- events
- merge_requests_closing_issues
- cached_closes_issues
@@ -293,6 +294,8 @@ merge_request_diff_files:
merge_request_context_commits:
- merge_request
- diff_files
+merge_schedule:
+- merge_request
cleanup_schedule:
- merge_request
ci_pipelines:
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 8ef7220667569bbaa4e32c07f8d9a433b423342a..2fab1f07887caf431e16ff6a095a8b7676fbfde4 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -36,6 +36,7 @@
it { is_expected.to have_many(:reviews).inverse_of(:merge_request) }
it { is_expected.to have_many(:reviewed_by_users).through(:reviews).source(:author) }
it { is_expected.to have_one(:cleanup_schedule).inverse_of(:merge_request) }
+ it { is_expected.to have_one(:merge_schedule).class_name('MergeRequests::MergeSchedule').inverse_of(:merge_request) }
it { is_expected.to have_many(:created_environments).class_name('Environment').inverse_of(:merge_request) }
it { is_expected.to have_many(:assignment_events).class_name('ResourceEvents::MergeRequestAssignmentEvent').inverse_of(:merge_request) }
diff --git a/spec/models/merge_requests/merge_schedule_spec.rb b/spec/models/merge_requests/merge_schedule_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a869032f118cb320b299d817522e448138812403
--- /dev/null
+++ b/spec/models/merge_requests/merge_schedule_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe MergeRequests::MergeSchedule, feature_category: :code_review_workflow do
+ subject { create(:merge_request_merge_schedule) }
+
+ describe 'associations' do
+ it { is_expected.to belong_to(:merge_request).required }
+ end
+
+ describe 'callbacks' do
+ let(:merge_request) { create(:merge_request) }
+ let(:schedule) do
+ create(:merge_request_merge_schedule, merge_request: merge_request,
+ project_id: merge_request.target_project.id + 1)
+ end
+
+ it 'overrides project_id to the correct sharding key' do
+ expect(schedule.project_id).to eq(merge_request.target_project.id)
+ end
+ end
+end