summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb58
-rw-r--r--spec/features/copy_as_gfm_spec.rb96
-rw-r--r--spec/features/markdown_spec.rb2
-rw-r--r--spec/features/projects/clusters/gcp_spec.rb6
-rw-r--r--spec/features/projects/clusters/user_spec.rb8
-rw-r--r--spec/features/projects/import_export/export_file_spec.rb2
-rw-r--r--spec/finders/labels_finder_spec.rb10
-rw-r--r--spec/fixtures/api/schemas/entities/merge_request_basic.json1
-rw-r--r--spec/fixtures/api/schemas/entities/merge_request_widget.json6
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/board.json86
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/boards.json4
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json3
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/user/basic.json2
-rw-r--r--spec/javascripts/groups/components/item_actions_spec.js12
-rw-r--r--spec/javascripts/lib/utils/text_utility_spec.js10
-rw-r--r--spec/javascripts/profile/account/components/delete_account_modal_spec.js8
-rw-r--r--spec/javascripts/projects/project_new_spec.js32
-rw-r--r--spec/javascripts/repo/components/new_dropdown/index_spec.js13
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_rebase_spec.js115
-rw-r--r--spec/javascripts/vue_shared/components/expand_button_spec.js32
-rw-r--r--spec/javascripts/vue_shared/components/modal_spec.js62
-rw-r--r--spec/lib/banzai/filter/mermaid_filter_spec.rb4
-rw-r--r--spec/lib/gitlab/background_migration/prepare_untracked_uploads_spec.rb13
-rw-r--r--spec/lib/gitlab/encoding_helper_spec.rb18
-rw-r--r--spec/lib/gitlab/git/blob_spec.rb15
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb40
-rw-r--r--spec/lib/gitlab/ldap/adapter_spec.rb10
-rw-r--r--spec/lib/gitlab/ldap/person_spec.rb73
-rw-r--r--spec/lib/gitlab/o_auth/user_spec.rb20
-rw-r--r--spec/migrations/migrate_stage_id_reference_in_background_spec.rb6
-rw-r--r--spec/migrations/migrate_stages_statuses_spec.rb6
-rw-r--r--spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb12
-rw-r--r--spec/migrations/schedule_merge_request_diff_migrations_spec.rb6
-rw-r--r--spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb6
-rw-r--r--spec/migrations/schedule_merge_request_latest_merge_request_diff_id_migrations_spec.rb6
-rw-r--r--spec/migrations/schedule_populate_merge_request_metrics_with_events_data_spec.rb4
-rw-r--r--spec/migrations/track_untracked_uploads_spec.rb12
-rw-r--r--spec/models/concerns/deployment_platform_spec.rb73
-rw-r--r--spec/models/merge_request_spec.rb46
-rw-r--r--spec/models/namespace_spec.rb10
-rw-r--r--spec/models/project_spec.rb46
-rw-r--r--spec/models/repository_spec.rb9
-rw-r--r--spec/models/service_spec.rb8
-rw-r--r--spec/presenters/merge_request_presenter_spec.rb63
-rw-r--r--spec/requests/api/boards_spec.rb179
-rw-r--r--spec/requests/api/issues_spec.rb12
-rw-r--r--spec/requests/api/merge_requests_spec.rb6
-rw-r--r--spec/requests/api/pages_domains_spec.rb1
-rw-r--r--spec/serializers/merge_request_widget_entity_spec.rb16
-rw-r--r--spec/services/merge_requests/rebase_service_spec.rb134
-rw-r--r--spec/services/projects/create_service_spec.rb2
-rw-r--r--spec/services/projects/hashed_storage/migrate_repository_service_spec.rb2
-rw-r--r--spec/services/projects/transfer_service_spec.rb4
-rw-r--r--spec/support/api/boards_shared_examples.rb180
-rw-r--r--spec/support/background_migrations_matchers.rb15
-rw-r--r--spec/support/shared_examples/requests/api/issuable_participants_examples.rb29
-rw-r--r--spec/support/test_env.rb7
-rw-r--r--spec/tasks/gitlab/git_rake_spec.rb38
-rw-r--r--spec/views/projects/merge_requests/show.html.haml_spec.rb2
-rw-r--r--spec/workers/rebase_worker_spec.rb27
60 files changed, 1403 insertions, 325 deletions
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index 45c424af8c4..c8cc6b374f6 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -684,4 +684,62 @@ describe Projects::MergeRequestsController do
format: :json
end
end
+
+ describe 'POST #rebase' do
+ let(:viewer) { user }
+
+ def post_rebase
+ post :rebase, namespace_id: project.namespace, project_id: project, id: merge_request
+ end
+
+ def expect_rebase_worker_for(user)
+ expect(RebaseWorker).to receive(:perform_async).with(merge_request.id, user.id)
+ end
+
+ context 'successfully' do
+ it 'enqeues a RebaseWorker' do
+ expect_rebase_worker_for(viewer)
+
+ post_rebase
+
+ expect(response.status).to eq(200)
+ end
+ end
+
+ context 'with a forked project' do
+ let(:fork_project) { create(:project, :repository, forked_from_project: project) }
+ let(:fork_owner) { fork_project.owner }
+
+ before do
+ merge_request.update!(source_project: fork_project)
+ fork_project.add_reporter(user)
+ end
+
+ context 'user cannot push to source branch' do
+ it 'returns 404' do
+ expect_rebase_worker_for(viewer).never
+
+ post_rebase
+
+ expect(response.status).to eq(404)
+ end
+ end
+
+ context 'user can push to source branch' do
+ before do
+ project.add_reporter(fork_owner)
+
+ sign_in(fork_owner)
+ end
+
+ it 'returns 200' do
+ expect_rebase_worker_for(fork_owner)
+
+ post_rebase
+
+ expect(response.status).to eq(200)
+ end
+ end
+ end
+ end
end
diff --git a/spec/features/copy_as_gfm_spec.rb b/spec/features/copy_as_gfm_spec.rb
index 1fcb8d5bc67..d8f1a919522 100644
--- a/spec/features/copy_as_gfm_spec.rb
+++ b/spec/features/copy_as_gfm_spec.rb
@@ -285,6 +285,102 @@ describe 'Copy as GFM', :js do
end
verify(
+ 'MermaidFilter: mermaid as converted from GFM to HTML',
+
+ <<-GFM.strip_heredoc
+ ```mermaid
+ graph TD;
+ A-->B;
+ ```
+ GFM
+ )
+
+ aggregate_failures('MermaidFilter: mermaid as transformed from HTML to SVG') do
+ gfm = <<-GFM.strip_heredoc
+ ```mermaid
+ graph TD;
+ A-->B;
+ ```
+ GFM
+
+ html = <<-HTML.strip_heredoc
+ <svg id="mermaidChart1" xmlns="http://www.w3.org/2000/svg" height="100%" viewBox="0 0 87.234375 174" style="max-width:87.234375px;" class="mermaid">
+ <style>
+ .mermaid {
+ /* Flowchart variables */
+ /* Sequence Diagram variables */
+ /* Gantt chart variables */
+ /** Section styling */
+ /* Grid and axis */
+ /* Today line */
+ /* Task styling */
+ /* Default task */
+ /* Specific task settings for the sections*/
+ /* Active task */
+ /* Completed task */
+ /* Tasks on the critical line */
+ }
+ </style>
+ <g>
+ <g class="output">
+ <g class="clusters"></g>
+ <g class="edgePaths">
+ <g class="edgePath" style="opacity: 1;">
+ <path class="path" d="M33.6171875,52L33.6171875,77L33.6171875,102" marker-end="url(#arrowhead65)" style="fill:none"></path>
+ <defs>
+ <marker id="arrowhead65" viewBox="0 0 10 10" refX="9" refY="5" markerUnits="strokeWidth" markerWidth="8" markerHeight="6" orient="auto">
+ <path d="M 0 0 L 10 5 L 0 10 z" class="arrowheadPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"></path>
+ </marker>
+ </defs>
+ </g>
+ </g>
+ <g class="edgeLabels">
+ <g class="edgeLabel" style="opacity: 1;" transform="">
+ <g transform="translate(0,0)" class="label">
+ <foreignObject width="0" height="0">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; white-space: nowrap;">
+ <span class="edgeLabel"></span>
+ </div>
+ </foreignObject>
+ </g>
+ </g>
+ </g>
+ <g class="nodes">
+ <g class="node" id="A" transform="translate(33.6171875,36)" style="opacity: 1;">
+ <rect rx="0" ry="0" x="-13.6171875" y="-16" width="27.234375" height="32"></rect>
+ <g class="label" transform="translate(0,0)">
+ <g transform="translate(-3.6171875,-6)">
+ <foreignObject width="7.234375" height="12">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; white-space: nowrap;">A</div>
+ </foreignObject>
+ </g>
+ </g>
+ </g>
+ <g class="node" id="B" transform="translate(33.6171875,118)" style="opacity: 1;">
+ <rect rx="0" ry="0" x="-13.6171875" y="-16" width="27.234375" height="32">
+ </rect>
+ <g class="label" transform="translate(0,0)">
+ <g transform="translate(-3.6171875,-6)">
+ <foreignObject width="7.234375" height="12">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: inline-block; white-space: nowrap;">B</div>
+ </foreignObject>
+ </g>
+ </g>
+ </g>
+ </g>
+ </g>
+ </g>
+ <text class="source" display="none">graph TD;
+ A--&gt;B;
+ </text>
+ </svg>
+ HTML
+
+ output_gfm = html_to_gfm(html)
+ expect(output_gfm.strip).to eq(gfm.strip)
+ end
+
+ verify(
'SanitizationFilter',
<<-GFM.strip_heredoc
diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb
index e285befc66f..a2b78a5e021 100644
--- a/spec/features/markdown_spec.rb
+++ b/spec/features/markdown_spec.rb
@@ -71,7 +71,7 @@ describe 'GitLab Markdown' do
it 'parses mermaid code block' do
aggregate_failures do
- expect(doc).to have_selector('pre.code.js-render-mermaid')
+ expect(doc).to have_selector('pre[lang=mermaid] > code.js-render-mermaid')
end
end
diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb
index 67b8901f8fb..882a2756b72 100644
--- a/spec/features/projects/clusters/gcp_spec.rb
+++ b/spec/features/projects/clusters/gcp_spec.rb
@@ -81,14 +81,14 @@ feature 'Gcp Cluster', :js do
end
it 'user sees a cluster details page' do
- expect(page).to have_button('Save')
+ expect(page).to have_button('Save changes')
expect(page.find(:css, '.cluster-name').value).to eq(cluster.name)
end
context 'when user disables the cluster' do
before do
page.find(:css, '.js-toggle-cluster').click
- click_button 'Save'
+ page.within('#cluster-integration') { click_button 'Save changes' }
end
it 'user sees the successful message' do
@@ -99,7 +99,7 @@ feature 'Gcp Cluster', :js do
context 'when user changes cluster parameters' do
before do
fill_in 'cluster_platform_kubernetes_attributes_namespace', with: 'my-namespace'
- click_button 'Save changes'
+ page.within('#js-cluster-details') { click_button 'Save changes' }
end
it 'user sees the successful message' do
diff --git a/spec/features/projects/clusters/user_spec.rb b/spec/features/projects/clusters/user_spec.rb
index 414f4acba86..a519b9f9c7e 100644
--- a/spec/features/projects/clusters/user_spec.rb
+++ b/spec/features/projects/clusters/user_spec.rb
@@ -29,7 +29,7 @@ feature 'User Cluster', :js do
end
it 'user sees a cluster details page' do
- expect(page).to have_content('Enable cluster integration')
+ expect(page).to have_content('Cluster integration')
expect(page.find_field('cluster[name]').value).to eq('dev-cluster')
expect(page.find_field('cluster[platform_kubernetes_attributes][api_url]').value)
.to have_content('http://example.com')
@@ -57,14 +57,14 @@ feature 'User Cluster', :js do
end
it 'user sees a cluster details page' do
- expect(page).to have_button('Save')
+ expect(page).to have_button('Save changes')
end
context 'when user disables the cluster' do
before do
page.find(:css, '.js-toggle-cluster').click
fill_in 'cluster_name', with: 'dev-cluster'
- click_button 'Save'
+ page.within('#cluster-integration') { click_button 'Save changes' }
end
it 'user sees the successful message' do
@@ -76,7 +76,7 @@ feature 'User Cluster', :js do
before do
fill_in 'cluster_name', with: 'my-dev-cluster'
fill_in 'cluster_platform_kubernetes_attributes_namespace', with: 'my-namespace'
- click_button 'Save changes'
+ page.within('#js-cluster-details') { click_button 'Save changes' }
end
it 'user sees the successful message' do
diff --git a/spec/features/projects/import_export/export_file_spec.rb b/spec/features/projects/import_export/export_file_spec.rb
index 461aa39d0ad..6732cf61767 100644
--- a/spec/features/projects/import_export/export_file_spec.rb
+++ b/spec/features/projects/import_export/export_file_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
# Integration test that exports a file using the Import/Export feature
# It looks up for any sensitive word inside the JSON, so if a sensitive word is found
-# we''l have to either include it adding the model that includes it to the +safe_list+
+# we'll have to either include it adding the model that includes it to the +safe_list+
# or make sure the attribute is blacklisted in the +import_export.yml+ configuration
feature 'Import/Export - project export integration test', :js do
include Select2Helper
diff --git a/spec/finders/labels_finder_spec.rb b/spec/finders/labels_finder_spec.rb
index d507af3fd3d..06031aee217 100644
--- a/spec/finders/labels_finder_spec.rb
+++ b/spec/finders/labels_finder_spec.rb
@@ -56,6 +56,16 @@ describe LabelsFinder do
expect(finder.execute).to eq [group_label_2, group_label_1, project_label_5]
end
+
+ context 'when only_group_labels is true' do
+ it 'returns only group labels' do
+ group_1.add_developer(user)
+
+ finder = described_class.new(user, group_id: group_1.id, only_group_labels: true)
+
+ expect(finder.execute).to eq [group_label_2, group_label_1]
+ end
+ end
end
context 'filtering by project_id' do
diff --git a/spec/fixtures/api/schemas/entities/merge_request_basic.json b/spec/fixtures/api/schemas/entities/merge_request_basic.json
index 995f13381ad..f1199468d53 100644
--- a/spec/fixtures/api/schemas/entities/merge_request_basic.json
+++ b/spec/fixtures/api/schemas/entities/merge_request_basic.json
@@ -9,6 +9,7 @@
"human_time_estimate": { "type": ["string", "null"] },
"human_total_time_spent": { "type": ["string", "null"] },
"merge_error": { "type": ["string", "null"] },
+ "rebase_in_progress": { "type": "boolean" },
"assignee_id": { "type": ["integer", "null"] },
"subscribed": { "type": ["boolean", "null"] },
"participants": { "type": "array" }
diff --git a/spec/fixtures/api/schemas/entities/merge_request_widget.json b/spec/fixtures/api/schemas/entities/merge_request_widget.json
index 9de27bee751..7f662098216 100644
--- a/spec/fixtures/api/schemas/entities/merge_request_widget.json
+++ b/spec/fixtures/api/schemas/entities/merge_request_widget.json
@@ -103,7 +103,11 @@
"remove_source_branch": { "type": ["boolean", "null"] },
"merge_ongoing": { "type": "boolean" },
"ff_only_enabled": { "type": ["boolean", false] },
- "should_be_rebased": { "type": "boolean" }
+ "should_be_rebased": { "type": "boolean" },
+ "rebase_commit_sha": { "type": ["string", "null"] },
+ "rebase_in_progress": { "type": "boolean" },
+ "can_push_to_source_branch": { "type": "boolean" },
+ "rebase_path": { "type": ["string", "null"] }
},
"additionalProperties": false
}
diff --git a/spec/fixtures/api/schemas/public_api/v4/board.json b/spec/fixtures/api/schemas/public_api/v4/board.json
new file mode 100644
index 00000000000..d667f1d631c
--- /dev/null
+++ b/spec/fixtures/api/schemas/public_api/v4/board.json
@@ -0,0 +1,86 @@
+{
+ "type": "object",
+ "required" : [
+ "id",
+ "project",
+ "lists"
+ ],
+ "properties" : {
+ "id": { "type": "integer" },
+ "project": {
+ "type": ["object", "null"],
+ "required": [
+ "id",
+ "avatar_url",
+ "description",
+ "default_branch",
+ "tag_list",
+ "ssh_url_to_repo",
+ "http_url_to_repo",
+ "web_url",
+ "name",
+ "name_with_namespace",
+ "path",
+ "path_with_namespace",
+ "star_count",
+ "forks_count",
+ "created_at",
+ "last_activity_at"
+ ],
+ "properties": {
+ "id": { "type": "integer" },
+ "avatar_url": { "type": ["string", "null"] },
+ "description": { "type": ["string", "null"] },
+ "default_branch": { "type": ["string", "null"] },
+ "tag_list": { "type": "array" },
+ "ssh_url_to_repo": { "type": "string" },
+ "http_url_to_repo": { "type": "string" },
+ "web_url": { "type": "string" },
+ "name": { "type": "string" },
+ "name_with_namespace": { "type": "string" },
+ "path": { "type": "string" },
+ "path_with_namespace": { "type": "string" },
+ "star_count": { "type": "integer" },
+ "forks_count": { "type": "integer" },
+ "created_at": { "type": "date" },
+ "last_activity_at": { "type": "date" }
+ },
+ "additionalProperties": false
+ },
+ "lists": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required" : [
+ "id",
+ "label",
+ "position"
+ ],
+ "properties" : {
+ "id": { "type": "integer" },
+ "label": {
+ "type": ["object", "null"],
+ "required": [
+ "id",
+ "color",
+ "description",
+ "name"
+ ],
+ "properties": {
+ "id": { "type": "integer" },
+ "color": {
+ "type": "string",
+ "pattern": "^#[0-9A-Fa-f]{3}{1,2}+$"
+ },
+ "description": { "type": ["string", "null"] },
+ "name": { "type": "string" }
+ }
+ },
+ "position": { "type": ["integer", "null"] }
+ },
+ "additionalProperties": false
+ }
+ }
+ },
+ "additionalProperties": true
+}
diff --git a/spec/fixtures/api/schemas/public_api/v4/boards.json b/spec/fixtures/api/schemas/public_api/v4/boards.json
new file mode 100644
index 00000000000..117564ef77a
--- /dev/null
+++ b/spec/fixtures/api/schemas/public_api/v4/boards.json
@@ -0,0 +1,4 @@
+{
+ "type": "array",
+ "items": { "$ref": "board.json" }
+}
diff --git a/spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json b/spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json
index 4ba6422406c..e8c17298b43 100644
--- a/spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json
+++ b/spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json
@@ -3,6 +3,7 @@
"properties": {
"domain": { "type": "string" },
"url": { "type": "uri" },
+ "project_id": { "type": "integer" },
"certificate_expiration": {
"type": "object",
"properties": {
@@ -13,6 +14,6 @@
"additionalProperties": false
}
},
- "required": ["domain", "url"],
+ "required": ["domain", "url", "project_id"],
"additionalProperties": false
}
diff --git a/spec/fixtures/api/schemas/public_api/v4/user/basic.json b/spec/fixtures/api/schemas/public_api/v4/user/basic.json
index 9f69d31971c..bf330d8278c 100644
--- a/spec/fixtures/api/schemas/public_api/v4/user/basic.json
+++ b/spec/fixtures/api/schemas/public_api/v4/user/basic.json
@@ -1,5 +1,5 @@
{
- "type": "object",
+ "type": ["object", "null"],
"required": [
"id",
"state",
diff --git a/spec/javascripts/groups/components/item_actions_spec.js b/spec/javascripts/groups/components/item_actions_spec.js
index 7a5c1da4d1d..6d6fb410859 100644
--- a/spec/javascripts/groups/components/item_actions_spec.js
+++ b/spec/javascripts/groups/components/item_actions_spec.js
@@ -47,17 +47,11 @@ describe('ItemActionsComponent', () => {
it('should change `modalStatus` prop to `false` and emit `leaveGroup` event with required params when called with `leaveConfirmed` as `true`', () => {
spyOn(eventHub, '$emit');
vm.modalStatus = true;
- vm.leaveGroup(true);
- expect(vm.modalStatus).toBeFalsy();
- expect(eventHub.$emit).toHaveBeenCalledWith('leaveGroup', vm.group, vm.parentGroup);
- });
- it('should change `modalStatus` prop to `false` and should NOT emit `leaveGroup` event when called with `leaveConfirmed` as `false`', () => {
- spyOn(eventHub, '$emit');
- vm.modalStatus = true;
- vm.leaveGroup(false);
+ vm.leaveGroup();
+
expect(vm.modalStatus).toBeFalsy();
- expect(eventHub.$emit).not.toHaveBeenCalled();
+ expect(eventHub.$emit).toHaveBeenCalledWith('leaveGroup', vm.group, vm.parentGroup);
});
});
});
diff --git a/spec/javascripts/lib/utils/text_utility_spec.js b/spec/javascripts/lib/utils/text_utility_spec.js
index 1f46c225071..6f8dad6b835 100644
--- a/spec/javascripts/lib/utils/text_utility_spec.js
+++ b/spec/javascripts/lib/utils/text_utility_spec.js
@@ -62,4 +62,14 @@ describe('text_utility', () => {
expect(textUtils.slugify('João')).toEqual('joão');
});
});
+
+ describe('stripeHtml', () => {
+ it('replaces html tag with the default replacement', () => {
+ expect(textUtils.stripeHtml('This is a text with <p>html</p>.')).toEqual('This is a text with html.');
+ });
+
+ it('replaces html tags with the provided replacement', () => {
+ expect(textUtils.stripeHtml('This is a text with <p>html</p>.', ' ')).toEqual('This is a text with html .');
+ });
+ });
});
diff --git a/spec/javascripts/profile/account/components/delete_account_modal_spec.js b/spec/javascripts/profile/account/components/delete_account_modal_spec.js
index 2e94948cfb2..588b61196a5 100644
--- a/spec/javascripts/profile/account/components/delete_account_modal_spec.js
+++ b/spec/javascripts/profile/account/components/delete_account_modal_spec.js
@@ -51,7 +51,7 @@ describe('DeleteAccountModal component', () => {
Vue.nextTick()
.then(() => {
expect(vm.enteredPassword).toBe(input.value);
- expect(submitButton).toHaveClass('disabled');
+ expect(submitButton).toHaveAttr('disabled', 'disabled');
submitButton.click();
expect(form.submit).not.toHaveBeenCalled();
})
@@ -68,7 +68,7 @@ describe('DeleteAccountModal component', () => {
Vue.nextTick()
.then(() => {
expect(vm.enteredPassword).toBe(input.value);
- expect(submitButton).not.toHaveClass('disabled');
+ expect(submitButton).not.toHaveAttr('disabled', 'disabled');
submitButton.click();
expect(form.submit).toHaveBeenCalled();
})
@@ -101,7 +101,7 @@ describe('DeleteAccountModal component', () => {
Vue.nextTick()
.then(() => {
expect(vm.enteredUsername).toBe(input.value);
- expect(submitButton).toHaveClass('disabled');
+ expect(submitButton).toHaveAttr('disabled', 'disabled');
submitButton.click();
expect(form.submit).not.toHaveBeenCalled();
})
@@ -118,7 +118,7 @@ describe('DeleteAccountModal component', () => {
Vue.nextTick()
.then(() => {
expect(vm.enteredUsername).toBe(input.value);
- expect(submitButton).not.toHaveClass('disabled');
+ expect(submitButton).not.toHaveAttr('disabled', 'disabled');
submitButton.click();
expect(form.submit).toHaveBeenCalled();
})
diff --git a/spec/javascripts/projects/project_new_spec.js b/spec/javascripts/projects/project_new_spec.js
index 850768f0e4f..c314ca8ab72 100644
--- a/spec/javascripts/projects/project_new_spec.js
+++ b/spec/javascripts/projects/project_new_spec.js
@@ -6,8 +6,12 @@ describe('New Project', () => {
beforeEach(() => {
setFixtures(`
- <input id="project_import_url" />
- <input id="project_path" />
+ <div class='toggle-import-form'>
+ <div class='import-url-data'>
+ <input id="project_import_url" />
+ <input id="project_path" />
+ </div>
+ </div>
`);
$projectImportUrl = $('#project_import_url');
@@ -25,7 +29,7 @@ describe('New Project', () => {
it('does not change project path for disabled $projectImportUrl', () => {
$projectImportUrl.attr('disabled', true);
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual(dummyImportUrl);
});
@@ -38,7 +42,7 @@ describe('New Project', () => {
it('does not change project path if it is set by user', () => {
$projectPath.keyup();
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual(dummyImportUrl);
});
@@ -46,7 +50,7 @@ describe('New Project', () => {
it('does not change project path for empty $projectImportUrl', () => {
$projectImportUrl.val('');
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual(dummyImportUrl);
});
@@ -54,7 +58,7 @@ describe('New Project', () => {
it('does not change project path for whitespace $projectImportUrl', () => {
$projectImportUrl.val(' ');
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual(dummyImportUrl);
});
@@ -62,7 +66,7 @@ describe('New Project', () => {
it('does not change project path for $projectImportUrl without slashes', () => {
$projectImportUrl.val('has-no-slash');
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual(dummyImportUrl);
});
@@ -70,7 +74,7 @@ describe('New Project', () => {
it('changes project path to last $projectImportUrl component', () => {
$projectImportUrl.val('/this/is/last');
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual('last');
});
@@ -78,7 +82,7 @@ describe('New Project', () => {
it('ignores trailing slashes in $projectImportUrl', () => {
$projectImportUrl.val('/has/trailing/slash/');
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual('slash');
});
@@ -86,7 +90,7 @@ describe('New Project', () => {
it('ignores fragment identifier in $projectImportUrl', () => {
$projectImportUrl.val('/this/has/a#fragment-identifier/');
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual('a');
});
@@ -94,7 +98,7 @@ describe('New Project', () => {
it('ignores query string in $projectImportUrl', () => {
$projectImportUrl.val('/url/with?query=string');
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual('with');
});
@@ -102,7 +106,7 @@ describe('New Project', () => {
it('ignores trailing .git in $projectImportUrl', () => {
$projectImportUrl.val('/repository.git');
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual('repository');
});
@@ -110,7 +114,7 @@ describe('New Project', () => {
it('changes project path for HTTPS URL in $projectImportUrl', () => {
$projectImportUrl.val('https://username:password@gitlab.company.com/group/project.git');
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual('project');
});
@@ -118,7 +122,7 @@ describe('New Project', () => {
it('changes project path for SSH URL in $projectImportUrl', () => {
$projectImportUrl.val('git@gitlab.com:gitlab-org/gitlab-ce.git');
- projectNew.deriveProjectPathFromUrl($projectImportUrl, $projectPath);
+ projectNew.deriveProjectPathFromUrl($projectImportUrl);
expect($projectPath.val()).toEqual('gitlab-ce');
});
diff --git a/spec/javascripts/repo/components/new_dropdown/index_spec.js b/spec/javascripts/repo/components/new_dropdown/index_spec.js
index b001c1655b4..6efbbf6d75e 100644
--- a/spec/javascripts/repo/components/new_dropdown/index_spec.js
+++ b/spec/javascripts/repo/components/new_dropdown/index_spec.js
@@ -57,16 +57,17 @@ describe('new dropdown component', () => {
});
});
- describe('toggleModalOpen', () => {
+ describe('hideModal', () => {
+ beforeAll((done) => {
+ vm.openModal = true;
+ Vue.nextTick(done);
+ });
+
it('closes modal after toggling', (done) => {
- vm.toggleModalOpen();
+ vm.hideModal();
Vue.nextTick()
.then(() => {
- expect(vm.$el.querySelector('.modal')).not.toBeNull();
- })
- .then(vm.toggleModalOpen)
- .then(() => {
expect(vm.$el.querySelector('.modal')).toBeNull();
})
.then(done)
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_rebase_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_rebase_spec.js
new file mode 100644
index 00000000000..66ecaa316c8
--- /dev/null
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_rebase_spec.js
@@ -0,0 +1,115 @@
+import Vue from 'vue';
+import eventHub from '~/vue_merge_request_widget/event_hub';
+import component from '~/vue_merge_request_widget/components/states/mr_widget_rebase.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('Merge request widget rebase component', () => {
+ let Component;
+ let vm;
+ beforeEach(() => {
+ Component = Vue.extend(component);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('While rebasing', () => {
+ it('should show progress message', () => {
+ vm = mountComponent(Component, {
+ mr: { rebaseInProgress: true },
+ service: {},
+ });
+
+ expect(
+ vm.$el.querySelector('.rebase-state-find-class-convention span').textContent.trim(),
+ ).toContain('Rebase in progress');
+ });
+ });
+
+ describe('With permissions', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ mr: {
+ rebaseInProgress: false,
+ canPushToSourceBranch: true,
+ },
+ service: {},
+ });
+ });
+
+ it('it should render rebase button and warning message', () => {
+ const text = vm.$el.querySelector('.rebase-state-find-class-convention span').textContent.trim();
+ expect(text).toContain('Fast-forward merge is not possible.');
+ expect(text).toContain('Rebase the source branch onto the target branch or merge target');
+ expect(text).toContain('branch into source branch to allow this merge request to be merged.');
+ });
+
+ it('it should render error message when it fails', (done) => {
+ vm.rebasingError = 'Something went wrong!';
+
+ Vue.nextTick(() => {
+ expect(
+ vm.$el.querySelector('.rebase-state-find-class-convention span').textContent.trim(),
+ ).toContain('Something went wrong!');
+ done();
+ });
+ });
+ });
+
+ describe('Without permissions', () => {
+ it('should render a message explaining user does not have permissions', () => {
+ vm = mountComponent(Component, {
+ mr: {
+ rebaseInProgress: false,
+ canPushToSourceBranch: false,
+ targetBranch: 'foo',
+ },
+ service: {},
+ });
+
+ const text = vm.$el.querySelector('.rebase-state-find-class-convention span').textContent.trim();
+
+ expect(text).toContain('Fast-forward merge is not possible.');
+ expect(text).toContain('Rebase the source branch onto');
+ expect(text).toContain('foo');
+ expect(text).toContain('to allow this merge request to be merged.');
+ });
+ });
+
+ describe('methods', () => {
+ it('checkRebaseStatus', (done) => {
+ spyOn(eventHub, '$emit');
+ vm = mountComponent(Component, {
+ mr: {},
+ service: {
+ rebase() {
+ return Promise.resolve();
+ },
+ poll() {
+ return Promise.resolve({
+ data: {
+ rebase_in_progress: false,
+ merge_error: null,
+ },
+ });
+ },
+ },
+ });
+
+ vm.rebase();
+
+ // Wait for the rebase request
+ vm.$nextTick()
+ // Wait for the polling request
+ .then(vm.$nextTick())
+ // Wait for the eventHub to be called
+ .then(vm.$nextTick())
+ .then(() => {
+ expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/expand_button_spec.js b/spec/javascripts/vue_shared/components/expand_button_spec.js
new file mode 100644
index 00000000000..a33ab689dd1
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/expand_button_spec.js
@@ -0,0 +1,32 @@
+import Vue from 'vue';
+import expandButton from '~/vue_shared/components/expand_button.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('expand button', () => {
+ let vm;
+
+ beforeEach(() => {
+ const Component = Vue.extend(expandButton);
+ vm = mountComponent(Component, {
+ slots: {
+ expanded: '<p>Expanded!</p>',
+ },
+ });
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders a collpased button', () => {
+ expect(vm.$el.textContent.trim()).toEqual('...');
+ });
+
+ it('hides expander on click', (done) => {
+ vm.$el.querySelector('button').click();
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('button').getAttribute('style')).toEqual('display: none;');
+ done();
+ });
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/modal_spec.js b/spec/javascripts/vue_shared/components/modal_spec.js
index 721f4044659..fe75a86cac8 100644
--- a/spec/javascripts/vue_shared/components/modal_spec.js
+++ b/spec/javascripts/vue_shared/components/modal_spec.js
@@ -2,11 +2,65 @@ import Vue from 'vue';
import modal from '~/vue_shared/components/modal.vue';
import mountComponent from '../../helpers/vue_mount_component_helper';
+const modalComponent = Vue.extend(modal);
+
describe('Modal', () => {
- it('does not render a primary button if no primaryButtonLabel', () => {
- const modalComponent = Vue.extend(modal);
- const vm = mountComponent(modalComponent);
+ let vm;
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('props', () => {
+ describe('without primaryButtonLabel', () => {
+ beforeEach(() => {
+ vm = mountComponent(modalComponent, {
+ primaryButtonLabel: null,
+ });
+ });
+
+ it('does not render a primary button', () => {
+ expect(vm.$el.querySelector('.js-primary-button')).toBeNull();
+ });
+ });
+
+ describe('with id', () => {
+ it('does not render a primary button', () => {
+ beforeEach(() => {
+ vm = mountComponent(modalComponent, {
+ id: 'my-modal',
+ });
+ });
+
+ it('assigns the id to the modal', () => {
+ expect(vm.$el.querySelector('#my-modal.modal')).not.toBeNull();
+ });
+
+ it('does not show the modal immediately', () => {
+ expect(vm.$el.querySelector('#my-modal.modal')).not.toHaveClass('show');
+ });
+
+ it('does not show a backdrop', () => {
+ expect(vm.$el.querySelector('modal-backdrop')).toBeNull();
+ });
+ });
+ });
+
+ it('works with data-toggle="modal"', (done) => {
+ setFixtures(`
+ <button id="modal-button" data-toggle="modal" data-target="#my-modal"></button>
+ <div id="modal-container"></div>
+ `);
+
+ const modalContainer = document.getElementById('modal-container');
+ const modalButton = document.getElementById('modal-button');
+ vm = mountComponent(modalComponent, {
+ id: 'my-modal',
+ }, modalContainer);
+ const modalElement = vm.$el.querySelector('#my-modal');
+ $(modalElement).on('shown.bs.modal', () => done());
- expect(vm.$el.querySelector('.js-primary-button')).toBeNull();
+ modalButton.click();
+ });
});
});
diff --git a/spec/lib/banzai/filter/mermaid_filter_spec.rb b/spec/lib/banzai/filter/mermaid_filter_spec.rb
index 532d25e121d..f6474c8936d 100644
--- a/spec/lib/banzai/filter/mermaid_filter_spec.rb
+++ b/spec/lib/banzai/filter/mermaid_filter_spec.rb
@@ -3,9 +3,9 @@ require 'spec_helper'
describe Banzai::Filter::MermaidFilter do
include FilterSpecHelper
- it 'adds `js-render-mermaid` class to the `pre` tag' do
+ it 'adds `js-render-mermaid` class to the `code` tag' do
doc = filter("<pre class='code highlight js-syntax-highlight mermaid' lang='mermaid' v-pre='true'><code>graph TD;\n A--&gt;B;\n</code></pre>")
- result = doc.xpath('descendant-or-self::pre').first
+ result = doc.css('code').first
expect(result[:class]).to include('js-render-mermaid')
end
diff --git a/spec/lib/gitlab/background_migration/prepare_untracked_uploads_spec.rb b/spec/lib/gitlab/background_migration/prepare_untracked_uploads_spec.rb
index cd3f1a45270..8bb9ebe0419 100644
--- a/spec/lib/gitlab/background_migration/prepare_untracked_uploads_spec.rb
+++ b/spec/lib/gitlab/background_migration/prepare_untracked_uploads_spec.rb
@@ -2,21 +2,10 @@ require 'spec_helper'
describe Gitlab::BackgroundMigration::PrepareUntrackedUploads, :sidekiq do
include TrackUntrackedUploadsHelpers
+ include MigrationsHelpers
let!(:untracked_files_for_uploads) { described_class::UntrackedFile }
- matcher :be_scheduled_migration do |*expected|
- match do |migration|
- BackgroundMigrationWorker.jobs.any? do |job|
- job['args'] == [migration, expected]
- end
- end
-
- failure_message do |migration|
- "Migration `#{migration}` with args `#{expected.inspect}` not scheduled!"
- end
- end
-
before do
DatabaseCleaner.clean
diff --git a/spec/lib/gitlab/encoding_helper_spec.rb b/spec/lib/gitlab/encoding_helper_spec.rb
index 87ec2698fc1..4e9367323cb 100644
--- a/spec/lib/gitlab/encoding_helper_spec.rb
+++ b/spec/lib/gitlab/encoding_helper_spec.rb
@@ -120,6 +120,24 @@ describe Gitlab::EncodingHelper do
it 'returns empty string on conversion errors' do
expect { ext_class.encode_utf8('') }.not_to raise_error(ArgumentError)
end
+
+ context 'with strings that can be forcefully encoded into utf8' do
+ let(:test_string) do
+ "refs/heads/FixSymbolsTitleDropdown".encode("ASCII-8BIT")
+ end
+ let(:expected_string) do
+ "refs/heads/FixSymbolsTitleDropdown".encode("UTF-8")
+ end
+
+ subject { ext_class.encode_utf8(test_string) }
+
+ it "doesn't use CharlockHolmes if the encoding can be forced into utf_8" do
+ expect(CharlockHolmes::EncodingDetector).not_to receive(:detect)
+
+ expect(subject).to eq(expected_string)
+ expect(subject.encoding.name).to eq('UTF-8')
+ end
+ end
end
describe '#clean' do
diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb
index c04a9688503..7f5946b1658 100644
--- a/spec/lib/gitlab/git/blob_spec.rb
+++ b/spec/lib/gitlab/git/blob_spec.rb
@@ -202,16 +202,6 @@ describe Gitlab::Git::Blob, seed_helper: true do
context 'limiting' do
subject { described_class.batch(repository, blob_references, blob_size_limit: blob_size_limit) }
- context 'default' do
- let(:blob_size_limit) { nil }
-
- it 'limits to MAX_DATA_DISPLAY_SIZE' do
- stub_const('Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE', 100)
-
- expect(subject.first.data.size).to eq(100)
- end
- end
-
context 'positive' do
let(:blob_size_limit) { 10 }
@@ -221,7 +211,10 @@ describe Gitlab::Git::Blob, seed_helper: true do
context 'zero' do
let(:blob_size_limit) { 0 }
- it { expect(subject.first.data).to eq('') }
+ it 'only loads the metadata' do
+ expect(subject.first.size).not_to be(0)
+ expect(subject.first.data).to eq('')
+ end
end
context 'negative' do
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index faccc2c8e00..f94234f6010 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -1030,12 +1030,50 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
+ context 'with max_count' do
+ it 'returns the number of commits with path ' do
+ options = { ref: 'master', max_count: 5 }
+
+ expect(repository.count_commits(options)).to eq(5)
+ end
+ end
+
context 'with path' do
it 'returns the number of commits with path ' do
- options = { ref: 'master', path: "encoding" }
+ options = { ref: 'master', path: 'encoding' }
+
+ expect(repository.count_commits(options)).to eq(2)
+ end
+ end
+
+ context 'with option :from and option :to' do
+ it 'returns the number of commits ahead for fix-mode..fix-blob-path' do
+ options = { from: 'fix-mode', to: 'fix-blob-path' }
expect(repository.count_commits(options)).to eq(2)
end
+
+ it 'returns the number of commits ahead for fix-blob-path..fix-mode' do
+ options = { from: 'fix-blob-path', to: 'fix-mode' }
+
+ expect(repository.count_commits(options)).to eq(1)
+ end
+
+ context 'with option :left_right' do
+ it 'returns the number of commits for fix-mode...fix-blob-path' do
+ options = { from: 'fix-mode', to: 'fix-blob-path', left_right: true }
+
+ expect(repository.count_commits(options)).to eq([1, 2])
+ end
+
+ context 'with max_count' do
+ it 'returns the number of commits with path ' do
+ options = { from: 'fix-mode', to: 'fix-blob-path', left_right: true, max_count: 1 }
+
+ expect(repository.count_commits(options)).to eq([1, 1])
+ end
+ end
+ end
end
context 'with max_count' do
diff --git a/spec/lib/gitlab/ldap/adapter_spec.rb b/spec/lib/gitlab/ldap/adapter_spec.rb
index d9ddb4326be..6132abd9b35 100644
--- a/spec/lib/gitlab/ldap/adapter_spec.rb
+++ b/spec/lib/gitlab/ldap/adapter_spec.rb
@@ -16,7 +16,7 @@ describe Gitlab::LDAP::Adapter do
expect(adapter).to receive(:ldap_search) do |arg|
expect(arg[:filter].to_s).to eq('(uid=johndoe)')
expect(arg[:base]).to eq('dc=example,dc=com')
- expect(arg[:attributes]).to match(%w{dn uid cn mail email userPrincipalName})
+ expect(arg[:attributes]).to match(ldap_attributes)
end.and_return({})
adapter.users('uid', 'johndoe')
@@ -26,7 +26,7 @@ describe Gitlab::LDAP::Adapter do
expect(adapter).to receive(:ldap_search).with(
base: 'uid=johndoe,ou=users,dc=example,dc=com',
scope: Net::LDAP::SearchScope_BaseObject,
- attributes: %w{dn uid cn mail email userPrincipalName},
+ attributes: ldap_attributes,
filter: nil
).and_return({})
@@ -63,7 +63,7 @@ describe Gitlab::LDAP::Adapter do
it 'uses the right uid attribute when non-default' do
stub_ldap_config(uid: 'sAMAccountName')
expect(adapter).to receive(:ldap_search).with(
- hash_including(attributes: %w{dn sAMAccountName cn mail email userPrincipalName})
+ hash_including(attributes: ldap_attributes)
).and_return({})
adapter.users('sAMAccountName', 'johndoe')
@@ -137,4 +137,8 @@ describe Gitlab::LDAP::Adapter do
end
end
end
+
+ def ldap_attributes
+ Gitlab::LDAP::Person.ldap_attributes(Gitlab::LDAP::Config.new('ldapmain'))
+ end
end
diff --git a/spec/lib/gitlab/ldap/person_spec.rb b/spec/lib/gitlab/ldap/person_spec.rb
index d204050ef66..ff29d9aa5be 100644
--- a/spec/lib/gitlab/ldap/person_spec.rb
+++ b/spec/lib/gitlab/ldap/person_spec.rb
@@ -8,13 +8,16 @@ describe Gitlab::LDAP::Person do
before do
stub_ldap_config(
options: {
+ 'uid' => 'uid',
'attributes' => {
- 'name' => 'cn',
- 'email' => %w(mail email userPrincipalName)
+ 'name' => 'cn',
+ 'email' => %w(mail email userPrincipalName),
+ 'username' => username_attribute
}
}
)
end
+ let(:username_attribute) { %w(uid sAMAccountName userid) }
describe '.normalize_dn' do
subject { described_class.normalize_dn(given) }
@@ -44,6 +47,34 @@ describe Gitlab::LDAP::Person do
end
end
+ describe '.ldap_attributes' do
+ it 'returns a compact and unique array' do
+ stub_ldap_config(
+ options: {
+ 'uid' => nil,
+ 'attributes' => {
+ 'name' => 'cn',
+ 'email' => 'mail',
+ 'username' => %w(uid mail memberof)
+ }
+ }
+ )
+ config = Gitlab::LDAP::Config.new('ldapmain')
+ ldap_attributes = described_class.ldap_attributes(config)
+
+ expect(ldap_attributes).to match_array(%w(dn uid cn mail memberof))
+ end
+ end
+
+ describe '.validate_entry' do
+ it 'raises InvalidEntryError' do
+ entry['foo'] = 'bar'
+
+ expect { described_class.new(entry, 'ldapmain') }
+ .to raise_error(Gitlab::LDAP::Person::InvalidEntryError)
+ end
+ end
+
describe '#name' do
it 'uses the configured name attribute and handles values as an array' do
name = 'John Doe'
@@ -72,6 +103,44 @@ describe Gitlab::LDAP::Person do
end
end
+ describe '#username' do
+ context 'with default uid username attribute' do
+ let(:username_attribute) { 'uid' }
+
+ it 'returns the proper username value' do
+ attr_value = 'johndoe'
+ entry[username_attribute] = attr_value
+ person = described_class.new(entry, 'ldapmain')
+
+ expect(person.username).to eq(attr_value)
+ end
+ end
+
+ context 'with a different username attribute' do
+ let(:username_attribute) { 'sAMAccountName' }
+
+ it 'returns the proper username value' do
+ attr_value = 'johndoe'
+ entry[username_attribute] = attr_value
+ person = described_class.new(entry, 'ldapmain')
+
+ expect(person.username).to eq(attr_value)
+ end
+ end
+
+ context 'with a non-standard username attribute' do
+ let(:username_attribute) { 'mail' }
+
+ it 'returns the proper username value' do
+ attr_value = 'john.doe@example.com'
+ entry[username_attribute] = attr_value
+ person = described_class.new(entry, 'ldapmain')
+
+ expect(person.username).to eq(attr_value)
+ end
+ end
+ end
+
def assert_generic_test(test_description, got, expected)
test_failure_message = "Failed test description: '#{test_description}'\n\n expected: #{expected}\n got: #{got}"
expect(got).to eq(expected), test_failure_message
diff --git a/spec/lib/gitlab/o_auth/user_spec.rb b/spec/lib/gitlab/o_auth/user_spec.rb
index 6334bcd0156..45fff4c5787 100644
--- a/spec/lib/gitlab/o_auth/user_spec.rb
+++ b/spec/lib/gitlab/o_auth/user_spec.rb
@@ -275,6 +275,26 @@ describe Gitlab::OAuth::User do
end
end
+ context 'and a corresponding LDAP person with a non-default username' do
+ before do
+ allow(ldap_user).to receive(:uid) { uid }
+ allow(ldap_user).to receive(:username) { 'johndoe@example.com' }
+ allow(ldap_user).to receive(:email) { %w(johndoe@example.com john2@example.com) }
+ allow(ldap_user).to receive(:dn) { dn }
+ end
+
+ context 'and no account for the LDAP user' do
+ it 'creates a user favoring the LDAP username and strips email domain' do
+ allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(ldap_user)
+
+ oauth_user.save
+
+ expect(gl_user).to be_valid
+ expect(gl_user.username).to eql 'johndoe'
+ end
+ end
+ end
+
context "and no corresponding LDAP person" do
before do
allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(nil)
diff --git a/spec/migrations/migrate_stage_id_reference_in_background_spec.rb b/spec/migrations/migrate_stage_id_reference_in_background_spec.rb
index 9b92f4b70b0..a837498e1b1 100644
--- a/spec/migrations/migrate_stage_id_reference_in_background_spec.rb
+++ b/spec/migrations/migrate_stage_id_reference_in_background_spec.rb
@@ -35,9 +35,9 @@ describe MigrateStageIdReferenceInBackground, :migration, :sidekiq do
Timecop.freeze do
migrate!
- expect(described_class::MIGRATION).to be_scheduled_migration(2.minutes, 1, 2)
- expect(described_class::MIGRATION).to be_scheduled_migration(2.minutes, 3, 3)
- expect(described_class::MIGRATION).to be_scheduled_migration(4.minutes, 4, 5)
+ 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
diff --git a/spec/migrations/migrate_stages_statuses_spec.rb b/spec/migrations/migrate_stages_statuses_spec.rb
index 094c9bc604e..79d2708f9ad 100644
--- a/spec/migrations/migrate_stages_statuses_spec.rb
+++ b/spec/migrations/migrate_stages_statuses_spec.rb
@@ -50,9 +50,9 @@ describe MigrateStagesStatuses, :migration do
Timecop.freeze do
migrate!
- expect(described_class::MIGRATION).to be_scheduled_migration(5.minutes, 1, 1)
- expect(described_class::MIGRATION).to be_scheduled_migration(5.minutes, 2, 2)
- expect(described_class::MIGRATION).to be_scheduled_migration(10.minutes, 3, 3)
+ 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
diff --git a/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb b/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb
index 0e884a7d910..65ec07da31c 100644
--- a/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb
+++ b/spec/migrations/schedule_create_gpg_key_subkeys_from_gpg_keys_spec.rb
@@ -2,18 +2,6 @@ require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20171005130944_schedule_create_gpg_key_subkeys_from_gpg_keys')
describe ScheduleCreateGpgKeySubkeysFromGpgKeys, :migration, :sidekiq do
- matcher :be_scheduled_migration do |*expected|
- match do |migration|
- BackgroundMigrationWorker.jobs.any? do |job|
- job['args'] == [migration, expected]
- end
- end
-
- failure_message do |migration|
- "Migration `#{migration}` with args `#{expected.inspect}` not scheduled!"
- end
- end
-
before do
create(:gpg_key, id: 1, key: GpgHelpers::User1.public_key)
create(:gpg_key, id: 2, key: GpgHelpers::User3.public_key)
diff --git a/spec/migrations/schedule_merge_request_diff_migrations_spec.rb b/spec/migrations/schedule_merge_request_diff_migrations_spec.rb
index 76afb6c19cf..d230f064444 100644
--- a/spec/migrations/schedule_merge_request_diff_migrations_spec.rb
+++ b/spec/migrations/schedule_merge_request_diff_migrations_spec.rb
@@ -24,9 +24,9 @@ describe ScheduleMergeRequestDiffMigrations, :migration, :sidekiq do
Timecop.freeze do
migrate!
- expect(described_class::MIGRATION).to be_scheduled_migration(5.minutes, 1, 1)
- expect(described_class::MIGRATION).to be_scheduled_migration(10.minutes, 2, 2)
- expect(described_class::MIGRATION).to be_scheduled_migration(15.minutes, 4, 4)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(5.minutes, 1, 1)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(10.minutes, 2, 2)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(15.minutes, 4, 4)
expect(BackgroundMigrationWorker.jobs.size).to eq 3
end
end
diff --git a/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb b/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb
index cf323973384..1aab4ae1650 100644
--- a/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb
+++ b/spec/migrations/schedule_merge_request_diff_migrations_take_two_spec.rb
@@ -24,9 +24,9 @@ describe ScheduleMergeRequestDiffMigrationsTakeTwo, :migration, :sidekiq do
Timecop.freeze do
migrate!
- expect(described_class::MIGRATION).to be_scheduled_migration(10.minutes, 1, 1)
- expect(described_class::MIGRATION).to be_scheduled_migration(20.minutes, 2, 2)
- expect(described_class::MIGRATION).to be_scheduled_migration(30.minutes, 4, 4)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(10.minutes, 1, 1)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(20.minutes, 2, 2)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(30.minutes, 4, 4)
expect(BackgroundMigrationWorker.jobs.size).to eq 3
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
index 158d0bc02ed..c9fdbe95d13 100644
--- 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
@@ -44,9 +44,9 @@ describe ScheduleMergeRequestLatestMergeRequestDiffIdMigrations, :migration, :si
Timecop.freeze do
migrate!
- expect(described_class::MIGRATION).to be_scheduled_migration(5.minutes, merge_request_1.id, merge_request_1.id)
- expect(described_class::MIGRATION).to be_scheduled_migration(10.minutes, merge_request_2.id, merge_request_2.id)
- expect(described_class::MIGRATION).to be_scheduled_migration(15.minutes, merge_request_4.id, merge_request_4.id)
+ 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
diff --git a/spec/migrations/schedule_populate_merge_request_metrics_with_events_data_spec.rb b/spec/migrations/schedule_populate_merge_request_metrics_with_events_data_spec.rb
index 97e089c5cb8..2e6b2cff0ab 100644
--- a/spec/migrations/schedule_populate_merge_request_metrics_with_events_data_spec.rb
+++ b/spec/migrations/schedule_populate_merge_request_metrics_with_events_data_spec.rb
@@ -12,10 +12,10 @@ describe SchedulePopulateMergeRequestMetricsWithEventsData, :migration, :sidekiq
migrate!
expect(described_class::MIGRATION)
- .to be_scheduled_migration(10.minutes, mrs.first.id, mrs.second.id)
+ .to be_scheduled_delayed_migration(10.minutes, mrs.first.id, mrs.second.id)
expect(described_class::MIGRATION)
- .to be_scheduled_migration(20.minutes, mrs.third.id, mrs.third.id)
+ .to be_scheduled_delayed_migration(20.minutes, mrs.third.id, mrs.third.id)
expect(BackgroundMigrationWorker.jobs.size).to eq(2)
end
diff --git a/spec/migrations/track_untracked_uploads_spec.rb b/spec/migrations/track_untracked_uploads_spec.rb
index 7fe7a140e2f..fe4d5b8a279 100644
--- a/spec/migrations/track_untracked_uploads_spec.rb
+++ b/spec/migrations/track_untracked_uploads_spec.rb
@@ -4,18 +4,6 @@ require Rails.root.join('db', 'post_migrate', '20171103140253_track_untracked_up
describe TrackUntrackedUploads, :migration, :sidekiq do
include TrackUntrackedUploadsHelpers
- matcher :be_scheduled_migration do
- match do |migration|
- BackgroundMigrationWorker.jobs.any? do |job|
- job['args'] == [migration]
- end
- end
-
- failure_message do |migration|
- "Migration `#{migration}` with args `#{expected.inspect}` not scheduled!"
- end
- end
-
it 'correctly schedules the follow-up background migration' do
Sidekiq::Testing.fake! do
migrate!
diff --git a/spec/models/concerns/deployment_platform_spec.rb b/spec/models/concerns/deployment_platform_spec.rb
new file mode 100644
index 00000000000..7bb89fe41dc
--- /dev/null
+++ b/spec/models/concerns/deployment_platform_spec.rb
@@ -0,0 +1,73 @@
+require 'rails_helper'
+
+describe DeploymentPlatform do
+ let(:project) { create(:project) }
+
+ describe '#deployment_platform' do
+ subject { project.deployment_platform }
+
+ context 'with no Kubernetes configuration on CI/CD, no Kubernetes Service and a Kubernetes template configured' do
+ let!(:kubernetes_service) { create(:kubernetes_service, template: true) }
+
+ it 'returns a platform kubernetes' do
+ expect(subject).to be_a_kind_of(Clusters::Platforms::Kubernetes)
+ end
+
+ it 'creates a cluster and a platform kubernetes' do
+ expect { subject }
+ .to change { Clusters::Cluster.count }.by(1)
+ .and change { Clusters::Platforms::Kubernetes.count }.by(1)
+ end
+
+ it 'includes appropriate attributes for Cluster' do
+ cluster = subject.cluster
+ expect(cluster.name).to eq('kubernetes-template')
+ expect(cluster.project).to eq(project)
+ expect(cluster.provider_type).to eq('user')
+ expect(cluster.platform_type).to eq('kubernetes')
+ end
+
+ it 'creates a platform kubernetes' do
+ expect { subject }.to change { Clusters::Platforms::Kubernetes.count }.by(1)
+ end
+
+ it 'copies attributes from Clusters::Platform::Kubernetes template into the new Cluster::Platforms::Kubernetes' do
+ expect(subject.api_url).to eq(kubernetes_service.api_url)
+ expect(subject.ca_pem).to eq(kubernetes_service.ca_pem)
+ expect(subject.token).to eq(kubernetes_service.token)
+ expect(subject.namespace).to eq(kubernetes_service.namespace)
+ end
+ end
+
+ context 'with no Kubernetes configuration on CI/CD, no Kubernetes Service and no Kubernetes template configured' do
+ it { is_expected.to be_nil }
+ end
+
+ context 'when user configured kubernetes from CI/CD > Clusters' do
+ let!(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
+ let(:platform_kubernetes) { cluster.platform_kubernetes }
+
+ it 'returns the Kubernetes platform' do
+ expect(subject).to eq(platform_kubernetes)
+ end
+ end
+
+ context 'when user configured kubernetes integration from project services' do
+ let!(:kubernetes_service) { create(:kubernetes_service, project: project) }
+
+ it 'returns the Kubernetes service' do
+ expect(subject).to eq(kubernetes_service)
+ end
+ end
+
+ context 'when the cluster creation fails' do
+ let!(:kubernetes_service) { create(:kubernetes_service, template: true) }
+
+ before do
+ allow_any_instance_of(Clusters::Cluster).to receive(:persisted?).and_return(false)
+ end
+
+ it { is_expected.to be_nil }
+ end
+ end
+end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index d8ebd46faab..07b3e1c1758 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -1903,4 +1903,50 @@ describe MergeRequest do
end
end
end
+
+ describe '#should_be_rebased?' do
+ let(:project) { create(:project, :repository) }
+
+ it 'returns false for the same source and target branches' do
+ merge_request = create(:merge_request, source_project: project, target_project: project)
+
+ expect(merge_request.should_be_rebased?).to be_falsey
+ end
+ end
+
+ describe '#rebase_in_progress?' do
+ # Create merge request and project before we stub file calls
+ before do
+ subject
+ end
+
+ it 'returns true when there is a current rebase directory' do
+ allow(File).to receive(:exist?).and_return(true)
+ allow(File).to receive(:mtime).and_return(Time.now)
+
+ expect(subject.rebase_in_progress?).to be_truthy
+ end
+
+ it 'returns false when there is no rebase directory' do
+ allow(File).to receive(:exist?).and_return(false)
+
+ expect(subject.rebase_in_progress?).to be_falsey
+ end
+
+ it 'returns false when the rebase directory has expired' do
+ allow(File).to receive(:exist?).and_return(true)
+ allow(File).to receive(:mtime).and_return(20.minutes.ago)
+
+ expect(subject.rebase_in_progress?).to be_falsey
+ end
+
+ it 'returns false when the source project has been removed' do
+ allow(subject).to receive(:source_project).and_return(nil)
+ allow(File).to receive(:exist?).and_return(true)
+ allow(File).to receive(:mtime).and_return(Time.now)
+
+ expect(File).not_to have_received(:exist?)
+ expect(subject.rebase_in_progress?).to be_falsey
+ end
+ end
end
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 0678cae9b93..b3f160f3119 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -250,9 +250,13 @@ describe Namespace do
parent.update(path: 'mygroup_new')
- expect(project_in_parent_group.repo.config['gitlab.fullpath']).to eq "mygroup_new/#{project_in_parent_group.path}"
- expect(hashed_project_in_subgroup.repo.config['gitlab.fullpath']).to eq "mygroup_new/mysubgroup/#{hashed_project_in_subgroup.path}"
- expect(legacy_project_in_subgroup.repo.config['gitlab.fullpath']).to eq "mygroup_new/mysubgroup/#{legacy_project_in_subgroup.path}"
+ expect(project_rugged(project_in_parent_group).config['gitlab.fullpath']).to eq "mygroup_new/#{project_in_parent_group.path}"
+ expect(project_rugged(hashed_project_in_subgroup).config['gitlab.fullpath']).to eq "mygroup_new/mysubgroup/#{hashed_project_in_subgroup.path}"
+ expect(project_rugged(legacy_project_in_subgroup).config['gitlab.fullpath']).to eq "mygroup_new/mysubgroup/#{legacy_project_in_subgroup.path}"
+ end
+
+ def project_rugged(project)
+ project.repository.rugged
end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index cea22bbd184..32f40f8c365 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -418,14 +418,21 @@ describe Project do
end
describe '#merge_method' do
- it 'returns "ff" merge_method when ff is enabled' do
- project = build(:project, merge_requests_ff_only_enabled: true)
- expect(project.merge_method).to be :ff
+ using RSpec::Parameterized::TableSyntax
+
+ where(:ff, :rebase, :method) do
+ true | true | :ff
+ true | false | :ff
+ false | true | :rebase_merge
+ false | false | :merge
end
- it 'returns "merge" merge_method when ff is disabled' do
- project = build(:project, merge_requests_ff_only_enabled: false)
- expect(project.merge_method).to be :merge
+ with_them do
+ let(:project) { build(:project, merge_requests_rebase_enabled: rebase, merge_requests_ff_only_enabled: ff) }
+
+ subject { project.merge_method }
+
+ it { is_expected.to eq(method) }
end
end
@@ -2632,7 +2639,7 @@ describe Project do
project.rename_repo
- expect(project.repo.config['gitlab.fullpath']).to eq(project.full_path)
+ expect(project.repository.rugged.config['gitlab.fullpath']).to eq(project.full_path)
end
end
@@ -2793,7 +2800,7 @@ describe Project do
it 'updates project full path in .git/config' do
project.rename_repo
- expect(project.repo.config['gitlab.fullpath']).to eq(project.full_path)
+ expect(project.repository.rugged.config['gitlab.fullpath']).to eq(project.full_path)
end
end
@@ -3137,38 +3144,19 @@ describe Project do
end
end
- describe '#deployment_platform' do
- subject { project.deployment_platform }
-
- let(:project) { create(:project) }
-
- context 'when user configured kubernetes from Integration > Kubernetes' do
- let!(:kubernetes_service) { create(:kubernetes_service, project: project) }
-
- it { is_expected.to eq(kubernetes_service) }
- end
-
- context 'when user configured kubernetes from CI/CD > Clusters' do
- let!(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
- let(:platform_kubernetes) { cluster.platform_kubernetes }
-
- it { is_expected.to eq(platform_kubernetes) }
- end
- end
-
describe '#write_repository_config' do
set(:project) { create(:project, :repository) }
it 'writes full path in .git/config when key is missing' do
project.write_repository_config
- expect(project.repo.config['gitlab.fullpath']).to eq project.full_path
+ expect(project.repository.rugged.config['gitlab.fullpath']).to eq project.full_path
end
it 'updates full path in .git/config when key is present' do
project.write_repository_config(gl_full_path: 'old/path')
- expect { project.write_repository_config }.to change { project.repo.config['gitlab.fullpath'] }.from('old/path').to(project.full_path)
+ expect { project.write_repository_config }.to change { project.repository.rugged.config['gitlab.fullpath'] }.from('old/path').to(project.full_path)
end
it 'does not raise an error with an empty repository' do
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 9a68ae086ea..48a75c9885b 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -2215,6 +2215,15 @@ describe Repository do
end
end
+ describe '#diverging_commit_counts' do
+ it 'returns the commit counts behind and ahead of default branch' do
+ result = repository.diverging_commit_counts(
+ repository.find_branch('fix'))
+
+ expect(result).to eq(behind: 29, ahead: 2)
+ end
+ end
+
describe '#cache_method_output', :use_clean_rails_memory_store_caching do
let(:fallback) { 10 }
diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb
index 540615de117..ab6678cab38 100644
--- a/spec/models/service_spec.rb
+++ b/spec/models/service_spec.rb
@@ -272,4 +272,12 @@ describe Service do
expect(service.deprecation_message).to be_nil
end
end
+
+ describe '.find_by_template' do
+ let!(:kubernetes_service) { create(:kubernetes_service, template: true) }
+
+ it 'returns service template' do
+ expect(KubernetesService.find_by_template).to eq(kubernetes_service)
+ end
+ end
end
diff --git a/spec/presenters/merge_request_presenter_spec.rb b/spec/presenters/merge_request_presenter_spec.rb
index 969c4753f33..e3b37739e8e 100644
--- a/spec/presenters/merge_request_presenter_spec.rb
+++ b/spec/presenters/merge_request_presenter_spec.rb
@@ -404,4 +404,67 @@ describe MergeRequestPresenter do
.to eq("<a href=\"/#{resource.source_project.full_path}/tree/#{resource.source_branch}\">#{resource.source_branch}</a>")
end
end
+
+ describe '#rebase_path' do
+ before do
+ allow(resource).to receive(:rebase_in_progress?) { rebase_in_progress }
+ allow(resource).to receive(:should_be_rebased?) { should_be_rebased }
+
+ allow_any_instance_of(Gitlab::UserAccess::RequestCacheExtension)
+ .to receive(:can_push_to_branch?)
+ .with(resource.source_branch)
+ .and_return(can_push_to_branch)
+ end
+
+ subject do
+ described_class.new(resource, current_user: user).rebase_path
+ end
+
+ context 'when can rebase' do
+ let(:rebase_in_progress) { false }
+ let(:can_push_to_branch) { true }
+ let(:should_be_rebased) { true }
+
+ before do
+ allow(resource).to receive(:source_branch_exists?) { true }
+ end
+
+ it 'returns path' do
+ is_expected
+ .to eq("/#{project.full_path}/merge_requests/#{resource.iid}/rebase")
+ end
+ end
+
+ context 'when cannot rebase' do
+ context 'when rebase in progress' do
+ let(:rebase_in_progress) { true }
+ let(:can_push_to_branch) { true }
+ let(:should_be_rebased) { true }
+
+ it 'returns nil' do
+ is_expected.to be_nil
+ end
+ end
+
+ context 'when user cannot merge' do
+ let(:rebase_in_progress) { false }
+ let(:can_push_to_branch) { false }
+ let(:should_be_rebased) { true }
+
+ it 'returns nil' do
+ is_expected.to be_nil
+ end
+ end
+
+ context 'should not be rebased' do
+ let(:rebase_in_progress) { false }
+ let(:can_push_to_branch) { true }
+ let(:should_be_rebased) { false }
+
+ it 'returns nil' do
+ is_expected.to be_nil
+ end
+ end
+ end
+ end
end
diff --git a/spec/requests/api/boards_spec.rb b/spec/requests/api/boards_spec.rb
index f65af69dc7f..c6c10025f7f 100644
--- a/spec/requests/api/boards_spec.rb
+++ b/spec/requests/api/boards_spec.rb
@@ -6,18 +6,18 @@ describe API::Boards do
set(:non_member) { create(:user) }
set(:guest) { create(:user) }
set(:admin) { create(:user, :admin) }
- set(:project) { create(:project, :public, creator_id: user.id, namespace: user.namespace ) }
+ set(:board_parent) { create(:project, :public, creator_id: user.id, namespace: user.namespace ) }
set(:dev_label) do
- create(:label, title: 'Development', color: '#FFAABB', project: project)
+ create(:label, title: 'Development', color: '#FFAABB', project: board_parent)
end
set(:test_label) do
- create(:label, title: 'Testing', color: '#FFAACC', project: project)
+ create(:label, title: 'Testing', color: '#FFAACC', project: board_parent)
end
set(:ux_label) do
- create(:label, title: 'UX', color: '#FF0000', project: project)
+ create(:label, title: 'UX', color: '#FF0000', project: board_parent)
end
set(:dev_list) do
@@ -28,180 +28,25 @@ describe API::Boards do
create(:list, label: test_label, position: 2)
end
- set(:board) do
- create(:board, project: project, lists: [dev_list, test_list])
- end
-
- before do
- project.add_reporter(user)
- project.add_guest(guest)
- end
+ set(:milestone) { create(:milestone, project: board_parent) }
+ set(:board_label) { create(:label, project: board_parent) }
+ set(:board) { create(:board, project: board_parent, lists: [dev_list, test_list]) }
- describe "GET /projects/:id/boards" do
- let(:base_url) { "/projects/#{project.id}/boards" }
+ it_behaves_like 'group and project boards', "/projects/:id/boards"
- context "when unauthenticated" do
- it "returns authentication error" do
- get api(base_url)
-
- expect(response).to have_gitlab_http_status(401)
- end
- end
-
- context "when authenticated" do
- it "returns the project issue board" do
- get api(base_url, user)
-
- expect(response).to have_gitlab_http_status(200)
- expect(response).to include_pagination_headers
- expect(json_response).to be_an Array
- expect(json_response.length).to eq(1)
- expect(json_response.first['id']).to eq(board.id)
- expect(json_response.first['lists']).to be_an Array
- expect(json_response.first['lists'].length).to eq(2)
- expect(json_response.first['lists'].last).to have_key('position')
- end
- end
- end
-
- describe "GET /projects/:id/boards/:board_id/lists" do
- let(:base_url) { "/projects/#{project.id}/boards/#{board.id}/lists" }
-
- it 'returns issue board lists' do
- get api(base_url, user)
-
- expect(response).to have_gitlab_http_status(200)
- expect(response).to include_pagination_headers
- expect(json_response).to be_an Array
- expect(json_response.length).to eq(2)
- expect(json_response.first['label']['name']).to eq(dev_label.title)
- end
-
- it 'returns 404 if board not found' do
- get api("/projects/#{project.id}/boards/22343/lists", user)
-
- expect(response).to have_gitlab_http_status(404)
- end
- end
-
- describe "GET /projects/:id/boards/:board_id/lists/:list_id" do
- let(:base_url) { "/projects/#{project.id}/boards/#{board.id}/lists" }
-
- it 'returns a list' do
- get api("#{base_url}/#{dev_list.id}", user)
-
- expect(response).to have_gitlab_http_status(200)
- expect(json_response['id']).to eq(dev_list.id)
- expect(json_response['label']['name']).to eq(dev_label.title)
- expect(json_response['position']).to eq(1)
- end
-
- it 'returns 404 if list not found' do
- get api("#{base_url}/5324", user)
-
- expect(response).to have_gitlab_http_status(404)
- end
- end
-
- describe "POST /projects/:id/board/lists" do
- let(:base_url) { "/projects/#{project.id}/boards/#{board.id}/lists" }
+ describe "POST /projects/:id/boards/lists" do
+ let(:url) { "/projects/#{board_parent.id}/boards/#{board.id}/lists" }
it 'creates a new issue board list for group labels' do
group = create(:group)
group_label = create(:group_label, group: group)
- project.update(group: group)
+ board_parent.update(group: group)
- post api(base_url, user), label_id: group_label.id
+ post api(url, user), label_id: group_label.id
expect(response).to have_gitlab_http_status(201)
expect(json_response['label']['name']).to eq(group_label.title)
expect(json_response['position']).to eq(3)
end
-
- it 'creates a new issue board list for project labels' do
- post api(base_url, user), label_id: ux_label.id
-
- expect(response).to have_gitlab_http_status(201)
- expect(json_response['label']['name']).to eq(ux_label.title)
- expect(json_response['position']).to eq(3)
- end
-
- it 'returns 400 when creating a new list if label_id is invalid' do
- post api(base_url, user), label_id: 23423
-
- expect(response).to have_gitlab_http_status(400)
- end
-
- it 'returns 403 for project members with guest role' do
- put api("#{base_url}/#{test_list.id}", guest), position: 1
-
- expect(response).to have_gitlab_http_status(403)
- end
- end
-
- describe "PUT /projects/:id/boards/:board_id/lists/:list_id to update only position" do
- let(:base_url) { "/projects/#{project.id}/boards/#{board.id}/lists" }
-
- it "updates a list" do
- put api("#{base_url}/#{test_list.id}", user),
- position: 1
-
- expect(response).to have_gitlab_http_status(200)
- expect(json_response['position']).to eq(1)
- end
-
- it "returns 404 error if list id not found" do
- put api("#{base_url}/44444", user),
- position: 1
-
- expect(response).to have_gitlab_http_status(404)
- end
-
- it "returns 403 for project members with guest role" do
- put api("#{base_url}/#{test_list.id}", guest),
- position: 1
-
- expect(response).to have_gitlab_http_status(403)
- end
- end
-
- describe "DELETE /projects/:id/board/lists/:list_id" do
- let(:base_url) { "/projects/#{project.id}/boards/#{board.id}/lists" }
-
- it "rejects a non member from deleting a list" do
- delete api("#{base_url}/#{dev_list.id}", non_member)
-
- expect(response).to have_gitlab_http_status(403)
- end
-
- it "rejects a user with guest role from deleting a list" do
- delete api("#{base_url}/#{dev_list.id}", guest)
-
- expect(response).to have_gitlab_http_status(403)
- end
-
- it "returns 404 error if list id not found" do
- delete api("#{base_url}/44444", user)
-
- expect(response).to have_gitlab_http_status(404)
- end
-
- context "when the user is project owner" do
- set(:owner) { create(:user) }
-
- before do
- project.update(namespace: owner.namespace)
- end
-
- it "deletes the list if an admin requests it" do
- delete api("#{base_url}/#{dev_list.id}", owner)
-
- expect(response).to have_gitlab_http_status(204)
- end
-
- it_behaves_like '412 response' do
- let(:request) { api("#{base_url}/#{dev_list.id}", owner) }
- end
- end
end
end
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index 00d9c795619..320217f2032 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -1582,4 +1582,16 @@ describe API::Issues, :mailer do
expect(json_response).to be_an Array
expect(json_response.length).to eq(size) if size
end
+
+ describe 'GET projects/:id/issues/:issue_iid/participants' do
+ it_behaves_like 'issuable participants endpoint' do
+ let(:entity) { issue }
+ end
+
+ it 'returns 404 if the issue is confidential' do
+ post api("/projects/#{project.id}/issues/#{confidential_issue.iid}/participants", non_member)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
end
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index ef3f610740d..0c9fbb1f187 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -500,6 +500,12 @@ describe API::MergeRequests do
end
end
+ describe 'GET /projects/:id/merge_requests/:merge_request_iid/participants' do
+ it_behaves_like 'issuable participants endpoint' do
+ let(:entity) { merge_request }
+ end
+ end
+
describe 'GET /projects/:id/merge_requests/:merge_request_iid/commits' do
it 'returns a 200 when merge request is valid' do
get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/commits", user)
diff --git a/spec/requests/api/pages_domains_spec.rb b/spec/requests/api/pages_domains_spec.rb
index d412b045e9f..5d01dc37f0e 100644
--- a/spec/requests/api/pages_domains_spec.rb
+++ b/spec/requests/api/pages_domains_spec.rb
@@ -46,6 +46,7 @@ describe API::PagesDomains do
expect(json_response).to be_an Array
expect(json_response.size).to eq(3)
expect(json_response.last).to have_key('domain')
+ expect(json_response.last).to have_key('project_id')
expect(json_response.last).to have_key('certificate_expiration')
expect(json_response.last['certificate_expiration']['expired']).to be true
expect(json_response.first).not_to have_key('certificate_expiration')
diff --git a/spec/serializers/merge_request_widget_entity_spec.rb b/spec/serializers/merge_request_widget_entity_spec.rb
index e25552eb0d8..80a271ba7fb 100644
--- a/spec/serializers/merge_request_widget_entity_spec.rb
+++ b/spec/serializers/merge_request_widget_entity_spec.rb
@@ -190,4 +190,20 @@ describe MergeRequestWidgetEntity do
end
end
end
+
+ describe 'when source project is deleted' do
+ let(:project) { create(:project, :repository) }
+ let(:fork_project) { create(:project, :repository, forked_from_project: project) }
+ let(:merge_request) { create(:merge_request, source_project: fork_project, target_project: project) }
+
+ it 'returns a blank rebase_path' do
+ allow(merge_request).to receive(:should_be_rebased?).and_return(true)
+ fork_project.destroy
+ merge_request.reload
+
+ entity = described_class.new(merge_request, request: request).as_json
+
+ expect(entity[:rebase_path]).to be_nil
+ end
+ end
end
diff --git a/spec/services/merge_requests/rebase_service_spec.rb b/spec/services/merge_requests/rebase_service_spec.rb
new file mode 100644
index 00000000000..d1b37cdd073
--- /dev/null
+++ b/spec/services/merge_requests/rebase_service_spec.rb
@@ -0,0 +1,134 @@
+require 'spec_helper'
+
+describe MergeRequests::RebaseService do
+ include ProjectForksHelper
+
+ let(:user) { create(:user) }
+ let(:merge_request) do
+ create(:merge_request,
+ source_branch: 'feature_conflict',
+ target_branch: 'master')
+ end
+ let(:project) { merge_request.project }
+ let(:repository) { project.repository.raw }
+
+ subject(:service) { described_class.new(project, user, {}) }
+
+ before do
+ project.add_master(user)
+ end
+
+ describe '#execute' do
+ context 'when another rebase is already in progress' do
+ before do
+ allow(merge_request).to receive(:rebase_in_progress?).and_return(true)
+ end
+
+ it 'saves the error message' do
+ subject.execute(merge_request)
+
+ expect(merge_request.reload.merge_error).to eq 'Rebase task canceled: Another rebase is already in progress'
+ end
+
+ it 'returns an error' do
+ expect(service.execute(merge_request)).to match(status: :error,
+ message: 'Failed to rebase. Should be done manually')
+ end
+ end
+
+ context 'when unexpected error occurs' do
+ before do
+ allow(repository).to receive(:run_git!).and_raise('Something went wrong')
+ end
+
+ it 'saves the error message' do
+ subject.execute(merge_request)
+
+ expect(merge_request.reload.merge_error).to eq 'Something went wrong'
+ end
+
+ it 'returns an error' do
+ expect(service.execute(merge_request)).to match(status: :error,
+ message: 'Failed to rebase. Should be done manually')
+ end
+ end
+
+ context 'with git command failure' do
+ before do
+ allow(repository).to receive(:run_git!).and_raise(Gitlab::Git::Repository::GitError, 'Something went wrong')
+ end
+
+ it 'saves the error message' do
+ subject.execute(merge_request)
+
+ expect(merge_request.reload.merge_error).to eq 'Something went wrong'
+ end
+
+ it 'returns an error' do
+ expect(service.execute(merge_request)).to match(status: :error,
+ message: 'Failed to rebase. Should be done manually')
+ end
+ end
+
+ context 'valid params' do
+ before do
+ service.execute(merge_request)
+ end
+
+ it 'rebases source branch' do
+ parent_sha = merge_request.source_project.repository.commit(merge_request.source_branch).parents.first.sha
+ target_branch_sha = merge_request.target_project.repository.commit(merge_request.target_branch).sha
+ expect(parent_sha).to eq(target_branch_sha)
+ end
+
+ it 'records the new SHA on the merge request' do
+ head_sha = merge_request.source_project.repository.commit(merge_request.source_branch).sha
+ expect(merge_request.reload.rebase_commit_sha).to eq(head_sha)
+ end
+
+ it 'logs correct author and commiter' do
+ head_commit = merge_request.source_project.repository.commit(merge_request.source_branch)
+
+ expect(head_commit.author_email).to eq('dmitriy.zaporozhets@gmail.com')
+ expect(head_commit.author_name).to eq('Dmitriy Zaporozhets')
+ expect(head_commit.committer_email).to eq(user.email)
+ expect(head_commit.committer_name).to eq(user.name)
+ end
+
+ context 'git commands' do
+ it 'sets GL_REPOSITORY env variable when calling git commands' do
+ expect(repository).to receive(:popen).exactly(3)
+ .with(anything, anything, hash_including('GL_REPOSITORY'))
+ .and_return(['', 0])
+
+ service.execute(merge_request)
+ end
+ end
+
+ context 'fork' do
+ let(:forked_project) do
+ fork_project(project, user, repository: true)
+ end
+
+ let(:merge_request_from_fork) do
+ forked_project.repository.create_file(
+ user,
+ 'new-file-to-target',
+ '',
+ message: 'Add new file to target',
+ branch_name: 'master')
+
+ create(:merge_request,
+ source_branch: 'master', source_project: forked_project,
+ target_branch: 'master', target_project: project)
+ end
+
+ it 'rebases source branch' do
+ parent_sha = forked_project.repository.commit(merge_request_from_fork.source_branch).parents.first.sha
+ target_branch_sha = project.repository.commit(merge_request_from_fork.target_branch).sha
+ expect(parent_sha).to eq(target_branch_sha)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index 1833078f37c..9a44dfde41b 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -255,7 +255,7 @@ describe Projects::CreateService, '#execute' do
it 'writes project full path to .git/config' do
project = create_project(user, opts)
- expect(project.repo.config['gitlab.fullpath']).to eq project.full_path
+ expect(project.repository.rugged.config['gitlab.fullpath']).to eq project.full_path
end
def create_project(user, opts)
diff --git a/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb b/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb
index ded864beb1d..7b536cc05cb 100644
--- a/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb
+++ b/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb
@@ -37,7 +37,7 @@ describe Projects::HashedStorage::MigrateRepositoryService do
it 'writes project full path to .git/config' do
service.execute
- expect(project.repo.config['gitlab.fullpath']).to eq project.full_path
+ expect(project.repository.rugged.config['gitlab.fullpath']).to eq project.full_path
end
end
diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb
index 7377c748698..39f6388c25e 100644
--- a/spec/services/projects/transfer_service_spec.rb
+++ b/spec/services/projects/transfer_service_spec.rb
@@ -58,7 +58,7 @@ describe Projects::TransferService do
it 'updates project full path in .git/config' do
transfer_project(project, user, group)
- expect(project.repo.config['gitlab.fullpath']).to eq "#{group.full_path}/#{project.path}"
+ expect(project.repository.rugged.config['gitlab.fullpath']).to eq "#{group.full_path}/#{project.path}"
end
end
@@ -95,7 +95,7 @@ describe Projects::TransferService do
it 'rolls back project full path in .git/config' do
attempt_project_transfer
- expect(project.repo.config['gitlab.fullpath']).to eq project.full_path
+ expect(project.repository.rugged.config['gitlab.fullpath']).to eq project.full_path
end
it "doesn't send move notifications" do
diff --git a/spec/support/api/boards_shared_examples.rb b/spec/support/api/boards_shared_examples.rb
new file mode 100644
index 00000000000..943c1f6ffd7
--- /dev/null
+++ b/spec/support/api/boards_shared_examples.rb
@@ -0,0 +1,180 @@
+shared_examples_for 'group and project boards' do |route_definition, ee = false|
+ let(:root_url) { route_definition.gsub(":id", board_parent.id.to_s) }
+
+ before do
+ board_parent.add_reporter(user)
+ board_parent.add_guest(guest)
+ end
+
+ def expect_schema_match_for(response, schema_file, ee)
+ if ee
+ expect(response).to match_response_schema(schema_file, dir: "ee")
+ else
+ expect(response).to match_response_schema(schema_file)
+ end
+ end
+
+ describe "GET #{route_definition}" do
+ context "when unauthenticated" do
+ it "returns authentication error" do
+ get api(root_url)
+
+ expect(response).to have_gitlab_http_status(401)
+ end
+ end
+
+ context "when authenticated" do
+ it "returns the issue boards" do
+ get api(root_url, user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+
+ expect_schema_match_for(response, 'public_api/v4/boards', ee)
+ end
+
+ describe "GET #{route_definition}/:board_id" do
+ let(:url) { "#{root_url}/#{board.id}" }
+
+ it 'get a single board by id' do
+ get api(url, user)
+
+ expect_schema_match_for(response, 'public_api/v4/board', ee)
+ end
+ end
+ end
+ end
+
+ describe "GET #{route_definition}/:board_id/lists" do
+ let(:url) { "#{root_url}/#{board.id}/lists" }
+
+ it 'returns issue board lists' do
+ get api(url, user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(2)
+ expect(json_response.first['label']['name']).to eq(dev_label.title)
+ end
+
+ it 'returns 404 if board not found' do
+ get api("#{root_url}/22343/lists", user)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+
+ describe "GET #{route_definition}/:board_id/lists/:list_id" do
+ let(:url) { "#{root_url}/#{board.id}/lists" }
+
+ it 'returns a list' do
+ get api("#{url}/#{dev_list.id}", user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['id']).to eq(dev_list.id)
+ expect(json_response['label']['name']).to eq(dev_label.title)
+ expect(json_response['position']).to eq(1)
+ end
+
+ it 'returns 404 if list not found' do
+ get api("#{url}/5324", user)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+
+ describe "POST #{route_definition}/lists" do
+ let(:url) { "#{root_url}/#{board.id}/lists" }
+
+ it 'creates a new issue board list for labels' do
+ post api(url, user), label_id: ux_label.id
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['label']['name']).to eq(ux_label.title)
+ expect(json_response['position']).to eq(3)
+ end
+
+ it 'returns 400 when creating a new list if label_id is invalid' do
+ post api(url, user), label_id: 23423
+
+ expect(response).to have_gitlab_http_status(400)
+ end
+
+ it 'returns 403 for members with guest role' do
+ put api("#{url}/#{test_list.id}", guest), position: 1
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+ end
+
+ describe "PUT #{route_definition}/:board_id/lists/:list_id to update only position" do
+ let(:url) { "#{root_url}/#{board.id}/lists" }
+
+ it "updates a list" do
+ put api("#{url}/#{test_list.id}", user),
+ position: 1
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['position']).to eq(1)
+ end
+
+ it "returns 404 error if list id not found" do
+ put api("#{url}/44444", user),
+ position: 1
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it "returns 403 for members with guest role" do
+ put api("#{url}/#{test_list.id}", guest),
+ position: 1
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+ end
+
+ describe "DELETE #{route_definition}/lists/:list_id" do
+ let(:url) { "#{root_url}/#{board.id}/lists" }
+
+ it "rejects a non member from deleting a list" do
+ delete api("#{url}/#{dev_list.id}", non_member)
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+
+ it "rejects a user with guest role from deleting a list" do
+ delete api("#{url}/#{dev_list.id}", guest)
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+
+ it "returns 404 error if list id not found" do
+ delete api("#{url}/44444", user)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ context "when the user is parent owner" do
+ set(:owner) { create(:user) }
+
+ before do
+ if board_parent.try(:namespace)
+ board_parent.update(namespace: owner.namespace)
+ else
+ board.parent.add_owner(owner)
+ end
+ end
+
+ it "deletes the list if an admin requests it" do
+ delete api("#{url}/#{dev_list.id}", owner)
+
+ expect(response).to have_gitlab_http_status(204)
+ end
+
+ it_behaves_like '412 response' do
+ let(:request) { api("#{url}/#{dev_list.id}", owner) }
+ end
+ end
+ end
+end
diff --git a/spec/support/background_migrations_matchers.rb b/spec/support/background_migrations_matchers.rb
index 423c0e4cefc..f4127efc6ae 100644
--- a/spec/support/background_migrations_matchers.rb
+++ b/spec/support/background_migrations_matchers.rb
@@ -1,4 +1,4 @@
-RSpec::Matchers.define :be_scheduled_migration do |delay, *expected|
+RSpec::Matchers.define :be_scheduled_delayed_migration do |delay, *expected|
match do |migration|
BackgroundMigrationWorker.jobs.any? do |job|
job['args'] == [migration, expected] &&
@@ -11,3 +11,16 @@ RSpec::Matchers.define :be_scheduled_migration do |delay, *expected|
'not scheduled in expected time!'
end
end
+
+RSpec::Matchers.define :be_scheduled_migration do |*expected|
+ match do |migration|
+ BackgroundMigrationWorker.jobs.any? do |job|
+ args = job['args'].size == 1 ? [BackgroundMigrationWorker.jobs[0]['args'][0], []] : job['args']
+ args == [migration, expected]
+ end
+ end
+
+ failure_message do |migration|
+ "Migration `#{migration}` with args `#{expected.inspect}` not scheduled!"
+ end
+end
diff --git a/spec/support/shared_examples/requests/api/issuable_participants_examples.rb b/spec/support/shared_examples/requests/api/issuable_participants_examples.rb
new file mode 100644
index 00000000000..96d59e0c472
--- /dev/null
+++ b/spec/support/shared_examples/requests/api/issuable_participants_examples.rb
@@ -0,0 +1,29 @@
+shared_examples 'issuable participants endpoint' do
+ let(:area) { entity.class.name.underscore.pluralize }
+
+ it 'returns participants' do
+ get api("/projects/#{project.id}/#{area}/#{entity.iid}/participants", user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.size).to eq(entity.participants.size)
+
+ last_participant = entity.participants.last
+ expect(json_response.last['id']).to eq(last_participant.id)
+ expect(json_response.last['name']).to eq(last_participant.name)
+ expect(json_response.last['username']).to eq(last_participant.username)
+ end
+
+ it 'returns a 404 when iid does not exist' do
+ get api("/projects/#{project.id}/#{area}/999/participants", user)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it 'returns a 404 when id is used instead of iid' do
+ get api("/projects/#{project.id}/#{area}/#{entity.id}/participants", user)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+end
diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb
index 1d99746b09f..664698fcbaf 100644
--- a/spec/support/test_env.rb
+++ b/spec/support/test_env.rb
@@ -1,4 +1,5 @@
require 'rspec/mocks'
+require 'toml'
module TestEnv
extend self
@@ -147,6 +148,9 @@ module TestEnv
version: Gitlab::GitalyClient.expected_server_version,
task: "gitlab:gitaly:install[#{gitaly_dir}]") do
+ # Always re-create config, in case it's outdated. This is fast anyway.
+ Gitlab::SetupHelper.create_gitaly_configuration(gitaly_dir, force: true)
+
start_gitaly(gitaly_dir)
end
end
@@ -347,6 +351,9 @@ module TestEnv
end
def component_needs_update?(component_folder, expected_version)
+ # Allow local overrides of the component for tests during development
+ return false if Rails.env.test? && File.symlink?(component_folder)
+
version = File.read(File.join(component_folder, 'VERSION')).strip
# Notice that this will always yield true when using branch versions
diff --git a/spec/tasks/gitlab/git_rake_spec.rb b/spec/tasks/gitlab/git_rake_spec.rb
new file mode 100644
index 00000000000..dacc5dc5ae7
--- /dev/null
+++ b/spec/tasks/gitlab/git_rake_spec.rb
@@ -0,0 +1,38 @@
+require 'rake_helper'
+
+describe 'gitlab:git rake tasks' do
+ before do
+ Rake.application.rake_require 'tasks/gitlab/git'
+
+ storages = { 'default' => { 'path' => Settings.absolute('tmp/tests/default_storage') } }
+
+ FileUtils.mkdir_p(Settings.absolute('tmp/tests/default_storage/@hashed/1/2/test.git'))
+ allow(Gitlab.config.repositories).to receive(:storages).and_return(storages)
+ allow_any_instance_of(String).to receive(:color) { |string, _color| string }
+
+ stub_warn_user_is_not_gitlab
+ end
+
+ after do
+ FileUtils.rm_rf(Settings.absolute('tmp/tests/default_storage'))
+ end
+
+ describe 'fsck' do
+ it 'outputs the integrity check for a repo' do
+ expect { run_rake_task('gitlab:git:fsck') }.to output(/Performed Checking integrity at .*@hashed\/1\/2\/test.git/).to_stdout
+ end
+
+ it 'errors out about config.lock issues' do
+ FileUtils.touch(Settings.absolute('tmp/tests/default_storage/@hashed/1/2/test.git/config.lock'))
+
+ expect { run_rake_task('gitlab:git:fsck') }.to output(/file exists\? ... yes/).to_stdout
+ end
+
+ it 'errors out about ref lock issues' do
+ FileUtils.mkdir_p(Settings.absolute('tmp/tests/default_storage/@hashed/1/2/test.git/refs/heads'))
+ FileUtils.touch(Settings.absolute('tmp/tests/default_storage/@hashed/1/2/test.git/refs/heads/blah.lock'))
+
+ expect { run_rake_task('gitlab:git:fsck') }.to output(/Ref lock files exist:/).to_stdout
+ end
+ end
+end
diff --git a/spec/views/projects/merge_requests/show.html.haml_spec.rb b/spec/views/projects/merge_requests/show.html.haml_spec.rb
index 28d54c2fb77..264e0ce0b40 100644
--- a/spec/views/projects/merge_requests/show.html.haml_spec.rb
+++ b/spec/views/projects/merge_requests/show.html.haml_spec.rb
@@ -54,6 +54,8 @@ describe 'projects/merge_requests/show.html.haml' do
it 'closes the merge request if the source project does not exist' do
closed_merge_request.update_attributes(state: 'open')
forked_project.destroy
+ # Reload merge request so MergeRequest#source_project turns to `nil`
+ closed_merge_request.reload
render
diff --git a/spec/workers/rebase_worker_spec.rb b/spec/workers/rebase_worker_spec.rb
new file mode 100644
index 00000000000..20aff020dbb
--- /dev/null
+++ b/spec/workers/rebase_worker_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+describe RebaseWorker, '#perform' do
+ context 'when rebasing an MR from a fork where upstream has protected branches' do
+ let(:upstream_project) { create(:project, :repository) }
+ let(:fork_project) { create(:project, :repository) }
+
+ let(:merge_request) do
+ create(:merge_request,
+ source_project: fork_project,
+ source_branch: 'feature_conflict',
+ target_project: upstream_project,
+ target_branch: 'master')
+ end
+
+ before do
+ create(:forked_project_link, forked_to_project: fork_project, forked_from_project: upstream_project)
+ end
+
+ it 'sets the correct project for running hooks' do
+ expect(MergeRequests::RebaseService)
+ .to receive(:new).with(fork_project, merge_request.author).and_call_original
+
+ subject.perform(merge_request, merge_request.author)
+ end
+ end
+end