diff options
author | Sean McGivern <sean@mcgivern.me.uk> | 2018-02-07 14:33:29 +0000 |
---|---|---|
committer | Sean McGivern <sean@mcgivern.me.uk> | 2018-02-07 14:33:29 +0000 |
commit | b7a2ce52ed0f893ba7d245c2525b0ae13b0aecbf (patch) | |
tree | e92afff270ba57a5e3683c0193ff9bb1c0b8f3b0 /spec | |
parent | ead97c55eac773444dee547a934112aa282c2e2e (diff) | |
parent | 86e98c832a4eeaac616daef4ef9ddebeb7191fdd (diff) | |
download | gitlab-ce-b7a2ce52ed0f893ba7d245c2525b0ae13b0aecbf.tar.gz |
Merge branch '41763-search-api' into 'master'
Search API
Closes #41763
See merge request gitlab-org/gitlab-ce!16878
Diffstat (limited to 'spec')
-rw-r--r-- | spec/fixtures/api/schemas/public_api/v4/blobs.json | 18 | ||||
-rw-r--r-- | spec/fixtures/api/schemas/public_api/v4/issue.json | 96 | ||||
-rw-r--r-- | spec/fixtures/api/schemas/public_api/v4/issues.json | 95 | ||||
-rw-r--r-- | spec/fixtures/api/schemas/public_api/v4/merge_requests.json | 2 | ||||
-rw-r--r-- | spec/fixtures/api/schemas/public_api/v4/milestones.json | 24 | ||||
-rw-r--r-- | spec/fixtures/api/schemas/public_api/v4/notes.json | 34 | ||||
-rw-r--r-- | spec/fixtures/api/schemas/public_api/v4/projects.json | 36 | ||||
-rw-r--r-- | spec/fixtures/api/schemas/public_api/v4/snippets.json | 33 | ||||
-rw-r--r-- | spec/lib/gitlab/search_results_spec.rb | 6 | ||||
-rw-r--r-- | spec/requests/api/search_spec.rb | 298 | ||||
-rw-r--r-- | spec/support/matchers/pagination_matcher.rb | 6 |
11 files changed, 553 insertions, 95 deletions
diff --git a/spec/fixtures/api/schemas/public_api/v4/blobs.json b/spec/fixtures/api/schemas/public_api/v4/blobs.json new file mode 100644 index 00000000000..9cb1eae3762 --- /dev/null +++ b/spec/fixtures/api/schemas/public_api/v4/blobs.json @@ -0,0 +1,18 @@ +{ + "type": "array", + "items": { + "type": "object", + "properties" : { + "basename": { "type": "string" }, + "data": { "type": "string" }, + "filename": { "type": ["string"] }, + "id": { "type": ["string", "null"] }, + "ref": { "type": "string" }, + "startline": { "type": "integer" } + }, + "required": [ + "basename", "data", "filename", "id", "ref", "startline" + ], + "additionalProperties": false + } +} diff --git a/spec/fixtures/api/schemas/public_api/v4/issue.json b/spec/fixtures/api/schemas/public_api/v4/issue.json new file mode 100644 index 00000000000..147f53239e0 --- /dev/null +++ b/spec/fixtures/api/schemas/public_api/v4/issue.json @@ -0,0 +1,96 @@ +{ + "type": "object", + "properties" : { + "id": { "type": "integer" }, + "iid": { "type": "integer" }, + "project_id": { "type": "integer" }, + "title": { "type": "string" }, + "description": { "type": ["string", "null"] }, + "state": { "type": "string" }, + "discussion_locked": { "type": ["boolean", "null"] }, + "closed_at": { "type": "date" }, + "created_at": { "type": "date" }, + "updated_at": { "type": "date" }, + "labels": { + "type": "array", + "items": { + "type": "string" + } + }, + "milestone": { + "type": ["object", "null"], + "properties": { + "id": { "type": "integer" }, + "iid": { "type": "integer" }, + "project_id": { "type": ["integer", "null"] }, + "group_id": { "type": ["integer", "null"] }, + "title": { "type": "string" }, + "description": { "type": ["string", "null"] }, + "state": { "type": "string" }, + "created_at": { "type": "date" }, + "updated_at": { "type": "date" }, + "due_date": { "type": "date" }, + "start_date": { "type": "date" } + }, + "additionalProperties": false + }, + "assignees": { + "type": "array", + "items": { + "type": ["object", "null"], + "properties": { + "name": { "type": "string" }, + "username": { "type": "string" }, + "id": { "type": "integer" }, + "state": { "type": "string" }, + "avatar_url": { "type": "uri" }, + "web_url": { "type": "uri" } + }, + "additionalProperties": false + } + }, + "assignee": { + "type": ["object", "null"], + "properties": { + "name": { "type": "string" }, + "username": { "type": "string" }, + "id": { "type": "integer" }, + "state": { "type": "string" }, + "avatar_url": { "type": "uri" }, + "web_url": { "type": "uri" } + }, + "additionalProperties": false + }, + "author": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "username": { "type": "string" }, + "id": { "type": "integer" }, + "state": { "type": "string" }, + "avatar_url": { "type": "uri" }, + "web_url": { "type": "uri" } + }, + "additionalProperties": false + }, + "user_notes_count": { "type": "integer" }, + "upvotes": { "type": "integer" }, + "downvotes": { "type": "integer" }, + "due_date": { "type": ["date", "null"] }, + "confidential": { "type": "boolean" }, + "web_url": { "type": "uri" }, + "time_stats": { + "time_estimate": { "type": "integer" }, + "total_time_spent": { "type": "integer" }, + "human_time_estimate": { "type": ["string", "null"] }, + "human_total_time_spent": { "type": ["string", "null"] } + } + }, + "required": [ + "id", "iid", "project_id", "title", "description", + "state", "created_at", "updated_at", "labels", + "milestone", "assignees", "author", "user_notes_count", + "upvotes", "downvotes", "due_date", "confidential", + "web_url" + ] +} diff --git a/spec/fixtures/api/schemas/public_api/v4/issues.json b/spec/fixtures/api/schemas/public_api/v4/issues.json index 5c08dbc3b96..c76806705e8 100644 --- a/spec/fixtures/api/schemas/public_api/v4/issues.json +++ b/spec/fixtures/api/schemas/public_api/v4/issues.json @@ -3,98 +3,7 @@ "items": { "type": "object", "properties" : { - "id": { "type": "integer" }, - "iid": { "type": "integer" }, - "project_id": { "type": "integer" }, - "title": { "type": "string" }, - "description": { "type": ["string", "null"] }, - "state": { "type": "string" }, - "discussion_locked": { "type": ["boolean", "null"] }, - "closed_at": { "type": "date" }, - "created_at": { "type": "date" }, - "updated_at": { "type": "date" }, - "labels": { - "type": "array", - "items": { - "type": "string" - } - }, - "milestone": { - "type": "object", - "properties": { - "id": { "type": "integer" }, - "iid": { "type": "integer" }, - "project_id": { "type": ["integer", "null"] }, - "group_id": { "type": ["integer", "null"] }, - "title": { "type": "string" }, - "description": { "type": ["string", "null"] }, - "state": { "type": "string" }, - "created_at": { "type": "date" }, - "updated_at": { "type": "date" }, - "due_date": { "type": "date" }, - "start_date": { "type": "date" } - }, - "additionalProperties": false - }, - "assignees": { - "type": "array", - "items": { - "type": ["object", "null"], - "properties": { - "name": { "type": "string" }, - "username": { "type": "string" }, - "id": { "type": "integer" }, - "state": { "type": "string" }, - "avatar_url": { "type": "uri" }, - "web_url": { "type": "uri" } - }, - "additionalProperties": false - } - }, - "assignee": { - "type": ["object", "null"], - "properties": { - "name": { "type": "string" }, - "username": { "type": "string" }, - "id": { "type": "integer" }, - "state": { "type": "string" }, - "avatar_url": { "type": "uri" }, - "web_url": { "type": "uri" } - }, - "additionalProperties": false - }, - "author": { - "type": "object", - "properties": { - "name": { "type": "string" }, - "username": { "type": "string" }, - "id": { "type": "integer" }, - "state": { "type": "string" }, - "avatar_url": { "type": "uri" }, - "web_url": { "type": "uri" } - }, - "additionalProperties": false - }, - "user_notes_count": { "type": "integer" }, - "upvotes": { "type": "integer" }, - "downvotes": { "type": "integer" }, - "due_date": { "type": ["date", "null"] }, - "confidential": { "type": "boolean" }, - "web_url": { "type": "uri" }, - "time_stats": { - "time_estimate": { "type": "integer" }, - "total_time_spent": { "type": "integer" }, - "human_time_estimate": { "type": ["string", "null"] }, - "human_total_time_spent": { "type": ["string", "null"] } - } - }, - "required": [ - "id", "iid", "project_id", "title", "description", - "state", "created_at", "updated_at", "labels", - "milestone", "assignees", "author", "user_notes_count", - "upvotes", "downvotes", "due_date", "confidential", - "web_url" - ], - "additionalProperties": false + "$ref": "./issue.json" + } } } diff --git a/spec/fixtures/api/schemas/public_api/v4/merge_requests.json b/spec/fixtures/api/schemas/public_api/v4/merge_requests.json index 034509091a5..e86176e5316 100644 --- a/spec/fixtures/api/schemas/public_api/v4/merge_requests.json +++ b/spec/fixtures/api/schemas/public_api/v4/merge_requests.json @@ -28,7 +28,7 @@ "additionalProperties": false }, "assignee": { - "type": "object", + "type": ["object", "null"], "properties": { "name": { "type": "string" }, "username": { "type": "string" }, diff --git a/spec/fixtures/api/schemas/public_api/v4/milestones.json b/spec/fixtures/api/schemas/public_api/v4/milestones.json new file mode 100644 index 00000000000..c3c42b6ee60 --- /dev/null +++ b/spec/fixtures/api/schemas/public_api/v4/milestones.json @@ -0,0 +1,24 @@ +{ + "type": "array", + "items": { + "type": "object", + "properties" : { + "id": { "type": "integer" }, + "iid": { "type": "integer" }, + "project_id": { "type": ["integer", "null"] }, + "group_id": { "type": ["integer", "null"] }, + "title": { "type": "string" }, + "description": { "type": ["string", "null"] }, + "state": { "type": "string" }, + "created_at": { "type": "date" }, + "updated_at": { "type": "date" }, + "start_date": { "type": "date" }, + "due_date": { "type": "date" } + }, + "required": [ + "id", "iid", "title", "description", "state", + "state", "created_at", "updated_at", "start_date", "due_date" + ], + "additionalProperties": false + } +} diff --git a/spec/fixtures/api/schemas/public_api/v4/notes.json b/spec/fixtures/api/schemas/public_api/v4/notes.json new file mode 100644 index 00000000000..6525f7c2c80 --- /dev/null +++ b/spec/fixtures/api/schemas/public_api/v4/notes.json @@ -0,0 +1,34 @@ +{ + "type": "array", + "items": { + "type": "object", + "properties" : { + "id": { "type": "integer" }, + "body": { "type": "string" }, + "attachment": { "type": ["string", "null"] }, + "author": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "username": { "type": "string" }, + "id": { "type": "integer" }, + "state": { "type": "string" }, + "avatar_url": { "type": "uri" }, + "web_url": { "type": "uri" } + }, + "additionalProperties": false + }, + "created_at": { "type": "date" }, + "updated_at": { "type": "date" }, + "system": { "type": "boolean" }, + "noteable_id": { "type": "integer" }, + "noteable_iid": { "type": "integer" }, + "noteable_type": { "type": "string" } + }, + "required": [ + "id", "body", "attachment", "author", "created_at", "updated_at", + "system", "noteable_id", "noteable_type" + ], + "additionalProperties": false + } +} diff --git a/spec/fixtures/api/schemas/public_api/v4/projects.json b/spec/fixtures/api/schemas/public_api/v4/projects.json new file mode 100644 index 00000000000..d89eeea89a5 --- /dev/null +++ b/spec/fixtures/api/schemas/public_api/v4/projects.json @@ -0,0 +1,36 @@ +{ + "type": "array", + "items": { + "type": "object", + "properties" : { + "id": { "type": "integer" }, + "name": { "type": "string" }, + "name_with_namespace": { "type": "string" }, + "description": { "type": ["string", "null"] }, + "path": { "type": "string" }, + "path_with_namespace": { "type": "string" }, + "created_at": { "type": "date" }, + "default_branch": { "type": ["string", "null"] }, + "tag_list": { + "type": "array", + "items": { + "type": "string" + } + }, + "ssh_url_to_repo": { "type": "string" }, + "http_url_to_repo": { "type": "string" }, + "web_url": { "type": "string" }, + "avatar_url": { "type": ["string", "null"] }, + "star_count": { "type": "integer" }, + "forks_count": { "type": "integer" }, + "last_activity_at": { "type": "date" } + }, + "required": [ + "id", "name", "name_with_namespace", "description", "path", + "path_with_namespace", "created_at", "default_branch", "tag_list", + "ssh_url_to_repo", "http_url_to_repo", "web_url", "avatar_url", + "star_count", "last_activity_at" + ], + "additionalProperties": false + } +} diff --git a/spec/fixtures/api/schemas/public_api/v4/snippets.json b/spec/fixtures/api/schemas/public_api/v4/snippets.json new file mode 100644 index 00000000000..e37e9704649 --- /dev/null +++ b/spec/fixtures/api/schemas/public_api/v4/snippets.json @@ -0,0 +1,33 @@ +{ + "type": "array", + "items": { + "type": "object", + "properties" : { + "id": { "type": "integer" }, + "project_id": { "type": ["integer", "null"] }, + "title": { "type": "string" }, + "file_name": { "type": ["string", "null"] }, + "description": { "type": ["string", "null"] }, + "web_url": { "type": "string" }, + "created_at": { "type": "date" }, + "updated_at": { "type": "date" }, + "author": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "username": { "type": "string" }, + "id": { "type": "integer" }, + "state": { "type": "string" }, + "avatar_url": { "type": "uri" }, + "web_url": { "type": "uri" } + }, + "additionalProperties": false + } + }, + "required": [ + "id", "title", "file_name", "description", "web_url", + "created_at", "updated_at", "author" + ], + "additionalProperties": false + } +} diff --git a/spec/lib/gitlab/search_results_spec.rb b/spec/lib/gitlab/search_results_spec.rb index 17b48b3d062..9dbab95f70e 100644 --- a/spec/lib/gitlab/search_results_spec.rb +++ b/spec/lib/gitlab/search_results_spec.rb @@ -20,9 +20,13 @@ describe Gitlab::SearchResults do end describe '#objects' do - it 'returns without_page collection by default' do + it 'returns without_counts collection by default' do expect(results.objects('projects')).to be_kind_of(Kaminari::PaginatableWithoutCount) end + + it 'returns with counts collection when requested' do + expect(results.objects('projects', 1, false)).not_to be_kind_of(Kaminari::PaginatableWithoutCount) + end end describe '#projects_count' do diff --git a/spec/requests/api/search_spec.rb b/spec/requests/api/search_spec.rb new file mode 100644 index 00000000000..a0026c6e11c --- /dev/null +++ b/spec/requests/api/search_spec.rb @@ -0,0 +1,298 @@ +require 'spec_helper' + +describe API::Search do + set(:user) { create(:user) } + set(:group) { create(:group) } + set(:project) { create(:project, :public, name: 'awesome project', group: group) } + set(:repo_project) { create(:project, :public, :repository, group: group) } + + shared_examples 'response is correct' do |schema:, size: 1| + it { expect(response).to have_gitlab_http_status(200) } + it { expect(response).to match_response_schema(schema) } + it { expect(response).to include_limited_pagination_headers } + it { expect(json_response.size).to eq(size) } + end + + describe 'GET /search' do + context 'when user is not authenticated' do + it 'returns 401 error' do + get api('/search'), scope: 'projects', search: 'awesome' + + expect(response).to have_gitlab_http_status(401) + end + end + + context 'when scope is not supported' do + it 'returns 400 error' do + get api('/search', user), scope: 'unsupported', search: 'awesome' + + expect(response).to have_gitlab_http_status(400) + end + end + + context 'when scope is missing' do + it 'returns 400 error' do + get api('/search', user), search: 'awesome' + + expect(response).to have_gitlab_http_status(400) + end + end + + context 'with correct params' do + context 'for projects scope' do + before do + get api('/search', user), scope: 'projects', search: 'awesome' + end + + it_behaves_like 'response is correct', schema: 'public_api/v4/projects' + end + + context 'for issues scope' do + before do + create(:issue, project: project, title: 'awesome issue') + + get api('/search', user), scope: 'issues', search: 'awesome' + end + + it_behaves_like 'response is correct', schema: 'public_api/v4/issues' + end + + context 'for merge_requests scope' do + before do + create(:merge_request, source_project: repo_project, title: 'awesome mr') + + get api('/search', user), scope: 'merge_requests', search: 'awesome' + end + + it_behaves_like 'response is correct', schema: 'public_api/v4/merge_requests' + end + + context 'for milestones scope' do + before do + create(:milestone, project: project, title: 'awesome milestone') + + get api('/search', user), scope: 'milestones', search: 'awesome' + end + + it_behaves_like 'response is correct', schema: 'public_api/v4/milestones' + end + + context 'for snippet_titles scope' do + before do + create(:snippet, :public, title: 'awesome snippet', content: 'snippet content') + + get api('/search', user), scope: 'snippet_titles', search: 'awesome' + end + + it_behaves_like 'response is correct', schema: 'public_api/v4/snippets' + end + + context 'for snippet_blobs scope' do + before do + create(:snippet, :public, title: 'awesome snippet', content: 'snippet content') + + get api('/search', user), scope: 'snippet_blobs', search: 'content' + end + + it_behaves_like 'response is correct', schema: 'public_api/v4/snippets' + end + end + end + + describe "GET /groups/:id/-/search" do + context 'when user is not authenticated' do + it 'returns 401 error' do + get api("/groups/#{group.id}/-/search"), scope: 'projects', search: 'awesome' + + expect(response).to have_gitlab_http_status(401) + end + end + + context 'when scope is not supported' do + it 'returns 400 error' do + get api("/groups/#{group.id}/-/search", user), scope: 'unsupported', search: 'awesome' + + expect(response).to have_gitlab_http_status(400) + end + end + + context 'when scope is missing' do + it 'returns 400 error' do + get api("/groups/#{group.id}/-/search", user), search: 'awesome' + + expect(response).to have_gitlab_http_status(400) + end + end + + context 'when group does not exist' do + it 'returns 404 error' do + get api('/groups/9999/-/search', user), scope: 'issues', search: 'awesome' + + expect(response).to have_gitlab_http_status(404) + end + end + + context 'when user does can not see the group' do + it 'returns 404 error' do + private_group = create(:group, :private) + + get api("/groups/#{private_group.id}/-/search", user), scope: 'issues', search: 'awesome' + + expect(response).to have_gitlab_http_status(404) + end + end + + context 'with correct params' do + context 'for projects scope' do + before do + get api("/groups/#{group.id}/-/search", user), scope: 'projects', search: 'awesome' + end + + it_behaves_like 'response is correct', schema: 'public_api/v4/projects' + end + + context 'for issues scope' do + before do + create(:issue, project: project, title: 'awesome issue') + + get api("/groups/#{group.id}/-/search", user), scope: 'issues', search: 'awesome' + end + + it_behaves_like 'response is correct', schema: 'public_api/v4/issues' + end + + context 'for merge_requests scope' do + before do + create(:merge_request, source_project: repo_project, title: 'awesome mr') + + get api("/groups/#{group.id}/-/search", user), scope: 'merge_requests', search: 'awesome' + end + + it_behaves_like 'response is correct', schema: 'public_api/v4/merge_requests' + end + + context 'for milestones scope' do + before do + create(:milestone, project: project, title: 'awesome milestone') + + get api("/groups/#{group.id}/-/search", user), scope: 'milestones', search: 'awesome' + end + + it_behaves_like 'response is correct', schema: 'public_api/v4/milestones' + end + end + end + + describe "GET /projects/:id/search" do + context 'when user is not authenticated' do + it 'returns 401 error' do + get api("/projects/#{project.id}/-/search"), scope: 'issues', search: 'awesome' + + expect(response).to have_gitlab_http_status(401) + end + end + + context 'when scope is not supported' do + it 'returns 400 error' do + get api("/projects/#{project.id}/-/search", user), scope: 'unsupported', search: 'awesome' + + expect(response).to have_gitlab_http_status(400) + end + end + + context 'when scope is missing' do + it 'returns 400 error' do + get api("/projects/#{project.id}/-/search", user), search: 'awesome' + + expect(response).to have_gitlab_http_status(400) + end + end + + context 'when project does not exist' do + it 'returns 404 error' do + get api('/projects/9999/-/search', user), scope: 'issues', search: 'awesome' + + expect(response).to have_gitlab_http_status(404) + end + end + + context 'when user does can not see the project' do + it 'returns 404 error' do + project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE) + + get api("/projects/#{project.id}/-/search", user), scope: 'issues', search: 'awesome' + + expect(response).to have_gitlab_http_status(404) + end + end + + context 'with correct params' do + context 'for issues scope' do + before do + create(:issue, project: project, title: 'awesome issue') + + get api("/projects/#{project.id}/-/search", user), scope: 'issues', search: 'awesome' + end + + it_behaves_like 'response is correct', schema: 'public_api/v4/issues' + end + + context 'for merge_requests scope' do + before do + create(:merge_request, source_project: repo_project, title: 'awesome mr') + + get api("/projects/#{repo_project.id}/-/search", user), scope: 'merge_requests', search: 'awesome' + end + + it_behaves_like 'response is correct', schema: 'public_api/v4/merge_requests' + end + + context 'for milestones scope' do + before do + create(:milestone, project: project, title: 'awesome milestone') + + get api("/projects/#{project.id}/-/search", user), scope: 'milestones', search: 'awesome' + end + + it_behaves_like 'response is correct', schema: 'public_api/v4/milestones' + end + + context 'for notes scope' do + before do + create(:note_on_merge_request, project: project, note: 'awesome note') + + get api("/projects/#{project.id}/-/search", user), scope: 'notes', search: 'awesome' + end + + it_behaves_like 'response is correct', schema: 'public_api/v4/notes' + end + + context 'for wiki_blobs scope' do + before do + wiki = create(:project_wiki, project: project) + create(:wiki_page, wiki: wiki, attrs: { title: 'home', content: "Awesome page" }) + + get api("/projects/#{project.id}/-/search", user), scope: 'wiki_blobs', search: 'awesome' + end + + it_behaves_like 'response is correct', schema: 'public_api/v4/blobs' + end + + context 'for commits scope' do + before do + get api("/projects/#{repo_project.id}/-/search", user), scope: 'commits', search: '498214de67004b1da3d820901307bed2a68a8ef6' + end + + it_behaves_like 'response is correct', schema: 'public_api/v4/commits' + end + + context 'for blobs scope' do + before do + get api("/projects/#{repo_project.id}/-/search", user), scope: 'blobs', search: 'monitors' + end + + it_behaves_like 'response is correct', schema: 'public_api/v4/blobs', size: 2 + end + end + end +end diff --git a/spec/support/matchers/pagination_matcher.rb b/spec/support/matchers/pagination_matcher.rb index 60f5e8239a7..9a7697e2bfc 100644 --- a/spec/support/matchers/pagination_matcher.rb +++ b/spec/support/matchers/pagination_matcher.rb @@ -3,3 +3,9 @@ RSpec::Matchers.define :include_pagination_headers do |expected| expect(actual.headers).to include('X-Total', 'X-Total-Pages', 'X-Per-Page', 'X-Page', 'X-Next-Page', 'X-Prev-Page', 'Link') end end + +RSpec::Matchers.define :include_limited_pagination_headers do |expected| + match do |actual| + expect(actual.headers).to include('X-Per-Page', 'X-Page', 'X-Next-Page', 'X-Prev-Page', 'Link') + end +end |