Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-07-14 03:08:59 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-07-14 03:08:59 +0300
commit13d294a8d8be05421e7d5e34577033ba5b34059c (patch)
treef9aab2c3930cb85e9afbb111063930a1bdd1c156 /spec
parenta269fb8e7cca24b826dd3f53485641ffce93bbee (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/concerns/harbor/artifact_spec.rb10
-rw-r--r--spec/controllers/concerns/harbor/repository_spec.rb10
-rw-r--r--spec/controllers/concerns/harbor/tag_spec.rb10
-rw-r--r--spec/factories/integrations.rb2
-rw-r--r--spec/features/groups/merge_requests_spec.rb2
-rw-r--r--spec/lib/gitlab/harbor/client_spec.rb269
-rw-r--r--spec/lib/gitlab/harbor/query_spec.rb375
-rw-r--r--spec/migrations/20220606082910_add_tmp_index_for_potentially_misassociated_vulnerability_occurrences_spec.rb22
-rw-r--r--spec/models/ci/group_variable_spec.rb6
-rw-r--r--spec/models/ci/variable_spec.rb6
-rw-r--r--spec/models/group_spec.rb1
-rw-r--r--spec/models/integrations/datadog_spec.rb16
-rw-r--r--spec/models/integrations/harbor_spec.rb8
-rw-r--r--spec/models/integrations/youtrack_spec.rb6
-rw-r--r--spec/requests/groups/harbor/artifacts_controller_spec.rb10
-rw-r--r--spec/requests/groups/harbor/repositories_controller_spec.rb65
-rw-r--r--spec/requests/groups/harbor/tags_controller_spec.rb10
-rw-r--r--spec/requests/projects/harbor/artifacts_controller_spec.rb10
-rw-r--r--spec/requests/projects/harbor/repositories_controller_spec.rb65
-rw-r--r--spec/requests/projects/harbor/tags_controller_spec.rb10
-rw-r--r--spec/routing/group_routing_spec.rb12
-rw-r--r--spec/serializers/integrations/harbor_serializers/artifact_entity_spec.rb51
-rw-r--r--spec/serializers/integrations/harbor_serializers/artifact_serializer_spec.rb9
-rw-r--r--spec/serializers/integrations/harbor_serializers/repository_entity_spec.rb55
-rw-r--r--spec/serializers/integrations/harbor_serializers/repository_serializer_spec.rb9
-rw-r--r--spec/serializers/integrations/harbor_serializers/tag_entity_spec.rb38
-rw-r--r--spec/serializers/integrations/harbor_serializers/tag_serializer_spec.rb9
-rw-r--r--spec/support/helpers/harbor_helper.rb27
-rw-r--r--spec/support/shared_contexts/policies/group_policy_shared_context.rb1
-rw-r--r--spec/support/shared_contexts/policies/project_policy_shared_context.rb2
-rw-r--r--spec/support/shared_examples/harbor/artifacts_controller_shared_examples.rb162
-rw-r--r--spec/support/shared_examples/harbor/container_shared_examples.rb9
-rw-r--r--spec/support/shared_examples/harbor/repositories_controller_shared_examples.rb172
-rw-r--r--spec/support/shared_examples/harbor/tags_controller_shared_examples.rb155
34 files changed, 1495 insertions, 129 deletions
diff --git a/spec/controllers/concerns/harbor/artifact_spec.rb b/spec/controllers/concerns/harbor/artifact_spec.rb
new file mode 100644
index 00000000000..6716d615a3b
--- /dev/null
+++ b/spec/controllers/concerns/harbor/artifact_spec.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Harbor::Artifact do
+ controller(ActionController::Base) do
+ include ::Harbor::Artifact
+ end
+ it_behaves_like 'raises NotImplementedError when calling #container'
+end
diff --git a/spec/controllers/concerns/harbor/repository_spec.rb b/spec/controllers/concerns/harbor/repository_spec.rb
new file mode 100644
index 00000000000..cae038ceed2
--- /dev/null
+++ b/spec/controllers/concerns/harbor/repository_spec.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Harbor::Repository do
+ controller(ActionController::Base) do
+ include ::Harbor::Repository
+ end
+ it_behaves_like 'raises NotImplementedError when calling #container'
+end
diff --git a/spec/controllers/concerns/harbor/tag_spec.rb b/spec/controllers/concerns/harbor/tag_spec.rb
new file mode 100644
index 00000000000..0d72ef303b0
--- /dev/null
+++ b/spec/controllers/concerns/harbor/tag_spec.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Harbor::Tag do
+ controller(ActionController::Base) do
+ include ::Harbor::Tag
+ end
+ it_behaves_like 'raises NotImplementedError when calling #container'
+end
diff --git a/spec/factories/integrations.rb b/spec/factories/integrations.rb
index 3945637c2c3..5ac26b7a260 100644
--- a/spec/factories/integrations.rb
+++ b/spec/factories/integrations.rb
@@ -233,7 +233,7 @@ FactoryBot.define do
factory :harbor_integration, class: 'Integrations::Harbor' do
project
active { true }
- type { 'HarborService' }
+ type { 'Integrations::Harbor' }
url { 'https://demo.goharbor.io' }
project_name { 'testproject' }
diff --git a/spec/features/groups/merge_requests_spec.rb b/spec/features/groups/merge_requests_spec.rb
index 7541e54f014..be1db970e9d 100644
--- a/spec/features/groups/merge_requests_spec.rb
+++ b/spec/features/groups/merge_requests_spec.rb
@@ -86,7 +86,7 @@ RSpec.describe 'Group merge requests page' do
expect(page).to have_selector('.empty-state')
expect(page).to have_link('Select project to create merge request')
- expect(page).not_to have_selector('.issues-filters')
+ expect(page).to have_selector('.issues-filters')
end
context 'with no open merge requests' do
diff --git a/spec/lib/gitlab/harbor/client_spec.rb b/spec/lib/gitlab/harbor/client_spec.rb
index bc5b593370a..4e80b8b53e3 100644
--- a/spec/lib/gitlab/harbor/client_spec.rb
+++ b/spec/lib/gitlab/harbor/client_spec.rb
@@ -3,12 +3,277 @@
require 'spec_helper'
RSpec.describe Gitlab::Harbor::Client do
- let(:harbor_integration) { build(:harbor_integration) }
+ let_it_be(:harbor_integration) { create(:harbor_integration) }
subject(:client) { described_class.new(harbor_integration) }
+ describe '#initialize' do
+ context 'if integration is nil' do
+ let(:harbor_integration) { nil }
+
+ it 'raises ConfigError' do
+ expect { client }.to raise_error(described_class::ConfigError)
+ end
+ end
+
+ context 'integration is provided' do
+ it 'is initialized successfully' do
+ expect { client }.not_to raise_error
+ end
+ end
+ end
+
+ describe '#get_repositories' do
+ context 'with valid params' do
+ let(:mock_response) do
+ [
+ {
+ "artifact_count": 1,
+ "creation_time": "2022-03-13T09:36:43.240Z",
+ "id": 1,
+ "name": "jihuprivate/busybox",
+ "project_id": 4,
+ "pull_count": 0,
+ "update_time": "2022-03-13T09:36:43.240Z"
+ }
+ ]
+ end
+
+ let(:mock_repositories) do
+ {
+ body: mock_response,
+ total_count: 2
+ }
+ end
+
+ before do
+ stub_request(:get, "https://demo.goharbor.io/api/v2.0/projects/testproject/repositories")
+ .with(
+ headers: {
+ 'Authorization': 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=',
+ 'Content-Type': 'application/json'
+ })
+ .to_return(status: 200, body: mock_response.to_json, headers: { "x-total-count": 2 })
+ end
+
+ it 'get repositories' do
+ expect(client.get_repositories({}).deep_stringify_keys).to eq(mock_repositories.deep_stringify_keys)
+ end
+ end
+
+ context 'when harbor project does not exist' do
+ before do
+ stub_request(:get, "https://demo.goharbor.io/api/v2.0/projects/testproject/repositories")
+ .with(
+ headers: {
+ 'Authorization': 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=',
+ 'Content-Type': 'application/json'
+ })
+ .to_return(status: 404, body: {}.to_json)
+ end
+
+ it 'raises Gitlab::Harbor::Client::Error' do
+ expect do
+ client.get_repositories({})
+ end.to raise_error(Gitlab::Harbor::Client::Error, 'request error')
+ end
+ end
+
+ context 'with invalid response' do
+ before do
+ stub_request(:get, "https://demo.goharbor.io/api/v2.0/projects/testproject/repositories")
+ .with(
+ headers: {
+ 'Authorization': 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=',
+ 'Content-Type': 'application/json'
+ })
+ .to_return(status: 200, body: '[not json}')
+ end
+
+ it 'raises Gitlab::Harbor::Client::Error' do
+ expect do
+ client.get_repositories({})
+ end.to raise_error(Gitlab::Harbor::Client::Error, 'invalid response format')
+ end
+ end
+ end
+
+ describe '#get_artifacts' do
+ context 'with valid params' do
+ let(:mock_response) do
+ [
+ {
+ "digest": "sha256:661e8e44e5d7290fbd42d0495ab4ff6fdf1ad251a9f358969b3264a22107c14d",
+ "icon": "sha256:0048162a053eef4d4ce3fe7518615bef084403614f8bca43b40ae2e762e11e06",
+ "id": 1,
+ "project_id": 1,
+ "pull_time": "0001-01-01T00:00:00.000Z",
+ "push_time": "2022-04-23T08:04:08.901Z",
+ "repository_id": 1,
+ "size": 126745886,
+ "tags": [
+ {
+ "artifact_id": 1,
+ "id": 1,
+ "immutable": false,
+ "name": "2",
+ "pull_time": "0001-01-01T00:00:00.000Z",
+ "push_time": "2022-04-23T08:04:08.920Z",
+ "repository_id": 1,
+ "signed": false
+ }
+ ],
+ "type": "IMAGE"
+ }
+ ]
+ end
+
+ let(:mock_artifacts) do
+ {
+ body: mock_response,
+ total_count: 1
+ }
+ end
+
+ before do
+ stub_request(:get, "https://demo.goharbor.io/api/v2.0/projects/testproject/repositories/test/artifacts")
+ .with(
+ headers: {
+ 'Authorization': 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=',
+ 'Content-Type': 'application/json'
+ })
+ .to_return(status: 200, body: mock_response.to_json, headers: { "x-total-count": 1 })
+ end
+
+ it 'get artifacts' do
+ expect(client.get_artifacts({ repository_name: 'test' })
+ .deep_stringify_keys).to eq(mock_artifacts.deep_stringify_keys)
+ end
+ end
+
+ context 'when harbor repository does not exist' do
+ before do
+ stub_request(:get, "https://demo.goharbor.io/api/v2.0/projects/testproject/repositories/test/artifacts")
+ .with(
+ headers: {
+ 'Authorization': 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=',
+ 'Content-Type': 'application/json'
+ })
+ .to_return(status: 404, body: {}.to_json)
+ end
+
+ it 'raises Gitlab::Harbor::Client::Error' do
+ expect do
+ client.get_artifacts({ repository_name: 'test' })
+ end.to raise_error(Gitlab::Harbor::Client::Error, 'request error')
+ end
+ end
+
+ context 'with invalid response' do
+ before do
+ stub_request(:get, "https://demo.goharbor.io/api/v2.0/projects/testproject/repositories/test/artifacts")
+ .with(
+ headers: {
+ 'Authorization': 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=',
+ 'Content-Type': 'application/json'
+ })
+ .to_return(status: 200, body: '[not json}')
+ end
+
+ it 'raises Gitlab::Harbor::Client::Error' do
+ expect do
+ client.get_artifacts({ repository_name: 'test' })
+ end.to raise_error(Gitlab::Harbor::Client::Error, 'invalid response format')
+ end
+ end
+ end
+
+ describe '#get_tags' do
+ context 'with valid params' do
+ let(:mock_response) do
+ [
+ {
+ "artifact_id": 1,
+ "id": 1,
+ "immutable": false,
+ "name": "2",
+ "pull_time": "0001-01-01T00:00:00.000Z",
+ "push_time": "2022-04-23T08:04:08.920Z",
+ "repository_id": 1,
+ "signed": false
+ }
+ ]
+ end
+
+ let(:mock_tags) do
+ {
+ body: mock_response,
+ total_count: 1
+ }
+ end
+
+ before do
+ stub_request(:get, "https://demo.goharbor.io/api/v2.0/projects/testproject/repositories/test/artifacts/1/tags")
+ .with(
+ headers: {
+ 'Authorization': 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=',
+ 'Content-Type': 'application/json'
+ })
+ .to_return(status: 200, body: mock_response.to_json, headers: { "x-total-count": 1 })
+ end
+
+ it 'get tags' do
+ expect(client.get_tags({ repository_name: 'test', artifact_name: '1' })
+ .deep_stringify_keys).to eq(mock_tags.deep_stringify_keys)
+ end
+ end
+
+ context 'when harbor artifact does not exist' do
+ before do
+ stub_request(:get, "https://demo.goharbor.io/api/v2.0/projects/testproject/repositories/test/artifacts/1/tags")
+ .with(
+ headers: {
+ 'Authorization': 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=',
+ 'Content-Type': 'application/json'
+ })
+ .to_return(status: 404, body: {}.to_json)
+ end
+
+ it 'raises Gitlab::Harbor::Client::Error' do
+ expect do
+ client.get_tags({ repository_name: 'test', artifact_name: '1' })
+ end.to raise_error(Gitlab::Harbor::Client::Error, 'request error')
+ end
+ end
+
+ context 'with invalid response' do
+ before do
+ stub_request(:get, "https://demo.goharbor.io/api/v2.0/projects/testproject/repositories/test/artifacts/1/tags")
+ .with(
+ headers: {
+ 'Authorization': 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=',
+ 'Content-Type': 'application/json'
+ })
+ .to_return(status: 200, body: '[not json}')
+ end
+
+ it 'raises Gitlab::Harbor::Client::Error' do
+ expect do
+ client.get_tags({ repository_name: 'test', artifact_name: '1' })
+ end.to raise_error(Gitlab::Harbor::Client::Error, 'invalid response format')
+ end
+ end
+ end
+
describe '#ping' do
- let!(:harbor_ping_request) { stub_harbor_request("https://demo.goharbor.io/api/v2.0/ping") }
+ before do
+ stub_request(:get, "https://demo.goharbor.io/api/v2.0/ping")
+ .with(
+ headers: {
+ 'Content-Type': 'application/json'
+ })
+ .to_return(status: 200, body: 'pong')
+ end
it "calls api/v2.0/ping successfully" do
expect(client.ping).to eq(success: true)
diff --git a/spec/lib/gitlab/harbor/query_spec.rb b/spec/lib/gitlab/harbor/query_spec.rb
new file mode 100644
index 00000000000..dcb9a16b27b
--- /dev/null
+++ b/spec/lib/gitlab/harbor/query_spec.rb
@@ -0,0 +1,375 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Harbor::Query do
+ let_it_be(:harbor_integration) { create(:harbor_integration) }
+
+ let(:params) { {} }
+
+ subject(:query) { described_class.new(harbor_integration, ActionController::Parameters.new(params)) }
+
+ describe 'Validations' do
+ context 'page' do
+ context 'with valid page' do
+ let(:params) { { page: 1 } }
+
+ it 'initialize successfully' do
+ expect(query.valid?).to eq(true)
+ end
+ end
+
+ context 'with invalid page' do
+ let(:params) { { page: -1 } }
+
+ it 'initialize failed' do
+ expect(query.valid?).to eq(false)
+ end
+ end
+ end
+
+ context 'limit' do
+ context 'with valid limit' do
+ let(:params) { { limit: 1 } }
+
+ it 'initialize successfully' do
+ expect(query.valid?).to eq(true)
+ end
+ end
+
+ context 'with invalid limit' do
+ context 'with limit less than 0' do
+ let(:params) { { limit: -1 } }
+
+ it 'initialize failed' do
+ expect(query.valid?).to eq(false)
+ end
+ end
+
+ context 'with limit greater than 25' do
+ let(:params) { { limit: 26 } }
+
+ it 'initialize failed' do
+ expect(query.valid?).to eq(false)
+ end
+ end
+ end
+ end
+
+ context 'repository_id' do
+ context 'with valid repository_id' do
+ let(:params) { { repository_id: 'test' } }
+
+ it 'initialize successfully' do
+ expect(query.valid?).to eq(true)
+ end
+ end
+
+ context 'with invalid repository_id' do
+ let(:params) { { repository_id: 'test@@' } }
+
+ it 'initialize failed' do
+ expect(query.valid?).to eq(false)
+ end
+ end
+ end
+
+ context 'artifact_id' do
+ context 'with valid artifact_id' do
+ let(:params) { { artifact_id: 'test' } }
+
+ it 'initialize successfully' do
+ expect(query.valid?).to eq(true)
+ end
+ end
+
+ context 'with invalid artifact_id' do
+ let(:params) { { artifact_id: 'test@@' } }
+
+ it 'initialize failed' do
+ expect(query.valid?).to eq(false)
+ end
+ end
+ end
+
+ context 'sort' do
+ context 'with valid sort' do
+ let(:params) { { sort: 'creation_time desc' } }
+
+ it 'initialize successfully' do
+ expect(query.valid?).to eq(true)
+ end
+ end
+
+ context 'with invalid sort' do
+ let(:params) { { sort: 'blabla desc' } }
+
+ it 'initialize failed' do
+ expect(query.valid?).to eq(false)
+ end
+ end
+ end
+
+ context 'search' do
+ context 'with valid search' do
+ let(:params) { { search: 'name=desc' } }
+
+ it 'initialize successfully' do
+ expect(query.valid?).to eq(true)
+ end
+ end
+
+ context 'with invalid search' do
+ let(:params) { { search: 'blabla' } }
+
+ it 'initialize failed' do
+ expect(query.valid?).to eq(false)
+ end
+ end
+ end
+ end
+
+ describe '#repositories' do
+ let(:response) { { total_count: 0, repositories: [] } }
+
+ def expect_query_option_include(expected_params)
+ expect_next_instance_of(Gitlab::Harbor::Client) do |client|
+ expect(client).to receive(:get_repositories)
+ .with(hash_including(expected_params))
+ .and_return(response)
+ end
+
+ query.repositories
+ end
+
+ context 'when params is {}' do
+ it 'fills default params' do
+ expect_query_option_include(page_size: 10, page: 1)
+ end
+ end
+
+ context 'when params contains options' do
+ let(:params) { { search: 'name=bu', sort: 'creation_time desc', limit: 20, page: 3 } }
+
+ it 'fills params with standard of Harbor' do
+ expect_query_option_include(q: 'name=~bu', sort: '-creation_time', page_size: 20, page: 3)
+ end
+ end
+
+ context 'when params contains invalid sort option' do
+ let(:params) { { search: 'name=bu', sort: 'blabla desc', limit: 20, page: 3 } }
+
+ it 'ignores invalid sort params' do
+ expect(query.valid?).to eq(false)
+ end
+ end
+
+ context 'when client.get_repositories returns data' do
+ let(:response_with_data) do
+ {
+ total_count: 1,
+ body:
+ [
+ {
+ "id": 3,
+ "name": "testproject/thirdbusybox",
+ "artifact_count": 1,
+ "creation_time": "2022-03-15T07:12:14.479Z",
+ "update_time": "2022-03-15T07:12:14.479Z",
+ "project_id": 3,
+ "pull_count": 0
+ }.with_indifferent_access
+ ]
+ }
+ end
+
+ it 'returns the right repositories data' do
+ expect_next_instance_of(Gitlab::Harbor::Client) do |client|
+ expect(client).to receive(:get_repositories)
+ .with(hash_including(page_size: 10, page: 1))
+ .and_return(response_with_data)
+ end
+
+ expect(query.repositories.first).to include(
+ "name": "testproject/thirdbusybox",
+ "artifact_count": 1
+ )
+ end
+ end
+ end
+
+ describe '#artifacts' do
+ let(:response) { { total_count: 0, artifacts: [] } }
+
+ def expect_query_option_include(expected_params)
+ expect_next_instance_of(Gitlab::Harbor::Client) do |client|
+ expect(client).to receive(:get_artifacts)
+ .with(hash_including(expected_params))
+ .and_return(response)
+ end
+
+ query.artifacts
+ end
+
+ context 'when params is {}' do
+ it 'fills default params' do
+ expect_query_option_include(page_size: 10, page: 1)
+ end
+ end
+
+ context 'when params contains options' do
+ let(:params) do
+ { search: 'tags=1', repository_id: 'jihuprivate', sort: 'creation_time desc', limit: 20, page: 3 }
+ end
+
+ it 'fills params with standard of Harbor' do
+ expect_query_option_include(q: 'tags=~1', sort: '-creation_time', page_size: 20, page: 3)
+ end
+ end
+
+ context 'when params contains invalid sort option' do
+ let(:params) { { search: 'tags=1', repository_id: 'jihuprivate', sort: 'blabla desc', limit: 20, page: 3 } }
+
+ it 'ignores invalid sort params' do
+ expect(query.valid?).to eq(false)
+ end
+ end
+
+ context 'when client.get_artifacts returns data' do
+ let(:response_with_data) do
+ {
+ total_count: 1,
+ body:
+ [
+ {
+ "digest": "sha256:14d4f50961544fdb669075c442509f194bdc4c0e344bde06e35dbd55af842a38",
+ "icon": "sha256:0048162a053eef4d4ce3fe7518615bef084403614f8bca43b40ae2e762e11e06",
+ "id": 5,
+ "project_id": 14,
+ "push_time": "2022-03-22T09:04:56.170Z",
+ "repository_id": 5,
+ "size": 774790,
+ "tags": [
+ {
+ "artifact_id": 5,
+ "id": 7,
+ "immutable": false,
+ "name": "2",
+ "pull_time": "0001-01-01T00:00:00.000Z",
+ "push_time": "2022-03-22T09:05:04.844Z",
+ "repository_id": 5
+ },
+ {
+ "artifact_id": 5,
+ "id": 6,
+ "immutable": false,
+ "name": "1",
+ "pull_time": "0001-01-01T00:00:00.000Z",
+ "push_time": "2022-03-22T09:04:56.186Z",
+ "repository_id": 5
+ }
+ ],
+ "type": "IMAGE"
+ }.with_indifferent_access
+ ]
+ }
+ end
+
+ it 'returns the right artifacts data' do
+ expect_next_instance_of(Gitlab::Harbor::Client) do |client|
+ expect(client).to receive(:get_artifacts)
+ .with(hash_including(page_size: 10, page: 1))
+ .and_return(response_with_data)
+ end
+
+ artifact = query.artifacts.first
+
+ expect(artifact).to include(
+ "digest": "sha256:14d4f50961544fdb669075c442509f194bdc4c0e344bde06e35dbd55af842a38",
+ "push_time": "2022-03-22T09:04:56.170Z"
+ )
+ expect(artifact["tags"].size).to eq(2)
+ end
+ end
+ end
+
+ describe '#tags' do
+ let(:response) { { total_count: 0, tags: [] } }
+
+ def expect_query_option_include(expected_params)
+ expect_next_instance_of(Gitlab::Harbor::Client) do |client|
+ expect(client).to receive(:get_tags)
+ .with(hash_including(expected_params))
+ .and_return(response)
+ end
+
+ query.tags
+ end
+
+ context 'when params is {}' do
+ it 'fills default params' do
+ expect_query_option_include(page_size: 10, page: 1)
+ end
+ end
+
+ context 'when params contains options' do
+ let(:params) { { repository_id: 'jihuprivate', sort: 'creation_time desc', limit: 20, page: 3 } }
+
+ it 'fills params with standard of Harbor' do
+ expect_query_option_include(sort: '-creation_time', page_size: 20, page: 3)
+ end
+ end
+
+ context 'when params contains invalid sort option' do
+ let(:params) { { repository_id: 'jihuprivate', artifact_id: 'test', sort: 'blabla desc', limit: 20, page: 3 } }
+
+ it 'ignores invalid sort params' do
+ expect(query.valid?).to eq(false)
+ end
+ end
+
+ context 'when client.get_tags returns data' do
+ let(:response_with_data) do
+ {
+ total_count: 2,
+ body:
+ [
+ {
+ "artifact_id": 5,
+ "id": 7,
+ "immutable": false,
+ "name": "2",
+ "pull_time": "0001-01-01T00:00:00.000Z",
+ "push_time": "2022-03-22T09:05:04.844Z",
+ "repository_id": 5
+ },
+ {
+ "artifact_id": 5,
+ "id": 6,
+ "immutable": false,
+ "name": "1",
+ "pull_time": "0001-01-01T00:00:00.000Z",
+ "push_time": "2022-03-22T09:04:56.186Z",
+ "repository_id": 5
+ }.with_indifferent_access
+ ]
+ }
+ end
+
+ it 'returns the right tags data' do
+ expect_next_instance_of(Gitlab::Harbor::Client) do |client|
+ expect(client).to receive(:get_tags)
+ .with(hash_including(page_size: 10, page: 1))
+ .and_return(response_with_data)
+ end
+
+ tag = query.tags.first
+
+ expect(tag).to include(
+ "immutable": false,
+ "push_time": "2022-03-22T09:05:04.844Z"
+ )
+ end
+ end
+ end
+end
diff --git a/spec/migrations/20220606082910_add_tmp_index_for_potentially_misassociated_vulnerability_occurrences_spec.rb b/spec/migrations/20220606082910_add_tmp_index_for_potentially_misassociated_vulnerability_occurrences_spec.rb
new file mode 100644
index 00000000000..1450811b3b9
--- /dev/null
+++ b/spec/migrations/20220606082910_add_tmp_index_for_potentially_misassociated_vulnerability_occurrences_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+require_migration!
+
+RSpec.describe AddTmpIndexForPotentiallyMisassociatedVulnerabilityOccurrences do
+ let(:async_index) { Gitlab::Database::AsyncIndexes::PostgresAsyncIndex }
+ let(:index_name) { described_class::INDEX_NAME }
+
+ it "schedules the index" do
+ reversible_migration do |migration|
+ migration.before -> do
+ expect(async_index.where(name: index_name).count).to be(0)
+ end
+
+ migration.after -> do
+ expect(async_index.where(name: index_name).count).to be(1)
+ end
+ end
+ end
+end
diff --git a/spec/models/ci/group_variable_spec.rb b/spec/models/ci/group_variable_spec.rb
index 3a4b836e453..fc5a9c879f6 100644
--- a/spec/models/ci/group_variable_spec.rb
+++ b/spec/models/ci/group_variable_spec.rb
@@ -56,4 +56,10 @@ RSpec.describe Ci::GroupVariable do
let!(:parent) { model.group }
end
+
+ describe '#audit_details' do
+ it "equals to the group variable's key" do
+ expect(subject.audit_details).to eq(subject.key)
+ end
+ end
end
diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb
index 29ca088ee04..f0af229ff2c 100644
--- a/spec/models/ci/variable_spec.rb
+++ b/spec/models/ci/variable_spec.rb
@@ -51,4 +51,10 @@ RSpec.describe Ci::Variable do
let!(:model) { create(:ci_variable, project: parent) }
end
end
+
+ describe '#audit_details' do
+ it "equals to the variable's key" do
+ expect(subject.audit_details).to eq(subject.key)
+ end
+ end
end
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 91ba12a16d5..e8e805b2678 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -42,6 +42,7 @@ RSpec.describe Group do
it { is_expected.to have_many(:organizations).class_name('CustomerRelations::Organization') }
it { is_expected.to have_one(:crm_settings) }
it { is_expected.to have_one(:group_feature) }
+ it { is_expected.to have_one(:harbor_integration) }
describe '#members & #requesters' do
let(:requester) { create(:user) }
diff --git a/spec/models/integrations/datadog_spec.rb b/spec/models/integrations/datadog_spec.rb
index cfc44b22a84..47f916e8457 100644
--- a/spec/models/integrations/datadog_spec.rb
+++ b/spec/models/integrations/datadog_spec.rb
@@ -240,4 +240,20 @@ RSpec.describe Integrations::Datadog do
end
end
end
+
+ describe '#fields' do
+ it 'includes the archive_trace_events field' do
+ expect(instance.fields).to include(have_attributes(name: 'archive_trace_events'))
+ end
+
+ context 'when the FF :datadog_integration_logs_collection is disabled' do
+ before do
+ stub_feature_flags(datadog_integration_logs_collection: false)
+ end
+
+ it 'does not include the archive_trace_events field' do
+ expect(instance.fields).not_to include(have_attributes(name: 'archive_trace_events'))
+ end
+ end
+ end
end
diff --git a/spec/models/integrations/harbor_spec.rb b/spec/models/integrations/harbor_spec.rb
index 9e3d4b524a6..5d8597969a1 100644
--- a/spec/models/integrations/harbor_spec.rb
+++ b/spec/models/integrations/harbor_spec.rb
@@ -19,6 +19,14 @@ RSpec.describe Integrations::Harbor do
it { is_expected.to allow_value('helloworld').for(:password) }
end
+ describe 'url' do
+ subject { build(:harbor_integration) }
+
+ it { is_expected.not_to allow_value('https://192.168.1.1').for(:url) }
+ it { is_expected.not_to allow_value('https://127.0.0.1').for(:url) }
+ it { is_expected.to allow_value('https://demo.goharbor.io').for(:url)}
+ end
+
describe '#fields' do
it 'returns custom fields' do
expect(harbor_integration.fields.pluck(:name)).to eq(%w[url project_name username password])
diff --git a/spec/models/integrations/youtrack_spec.rb b/spec/models/integrations/youtrack_spec.rb
index f6a9dd8ef37..618ebcbb76a 100644
--- a/spec/models/integrations/youtrack_spec.rb
+++ b/spec/models/integrations/youtrack_spec.rb
@@ -37,4 +37,10 @@ RSpec.describe Integrations::Youtrack do
expect(described_class.reference_pattern.match('yt-123')[:issue]).to eq('yt-123')
end
end
+
+ describe '#fields' do
+ it 'only returns the project_url and issues_url fields' do
+ expect(subject.fields.pluck(:name)).to eq(%w[project_url issues_url])
+ end
+ end
end
diff --git a/spec/requests/groups/harbor/artifacts_controller_spec.rb b/spec/requests/groups/harbor/artifacts_controller_spec.rb
new file mode 100644
index 00000000000..ea9529119a6
--- /dev/null
+++ b/spec/requests/groups/harbor/artifacts_controller_spec.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Groups::Harbor::ArtifactsController do
+ it_behaves_like 'a harbor artifacts controller', anonymous_status_code: '404' do
+ let_it_be(:container) { create(:group) }
+ let_it_be(:harbor_integration) { create(:harbor_integration, group: container, project: nil) }
+ end
+end
diff --git a/spec/requests/groups/harbor/repositories_controller_spec.rb b/spec/requests/groups/harbor/repositories_controller_spec.rb
index 3e475dc410e..b4022561f54 100644
--- a/spec/requests/groups/harbor/repositories_controller_spec.rb
+++ b/spec/requests/groups/harbor/repositories_controller_spec.rb
@@ -3,67 +3,8 @@
require 'spec_helper'
RSpec.describe Groups::Harbor::RepositoriesController do
- let_it_be(:group, reload: true) { create(:group) }
- let_it_be(:user) { create(:user) }
-
- shared_examples 'responds with 404 status' do
- it 'returns 404' do
- subject
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
-
- shared_examples 'responds with 200 status' do
- it 'renders the index template' do
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to render_template(:index)
- end
- end
-
- before do
- stub_feature_flags(harbor_registry_integration: true)
- group.add_reporter(user)
- login_as(user)
- end
-
- describe 'GET #index' do
- subject do
- get group_harbor_registries_path(group)
- response
- end
-
- context 'with harbor registry feature flag enabled' do
- it_behaves_like 'responds with 200 status'
- end
-
- context 'with harbor registry feature flag disabled' do
- before do
- stub_feature_flags(harbor_registry_integration: false)
- end
-
- it_behaves_like 'responds with 404 status'
- end
- end
-
- describe 'GET #show' do
- subject do
- get group_harbor_registry_path(group, 1)
- response
- end
-
- context 'with harbor registry feature flag enabled' do
- it_behaves_like 'responds with 200 status'
- end
-
- context 'with harbor registry feature flag disabled' do
- before do
- stub_feature_flags(harbor_registry_integration: false)
- end
-
- it_behaves_like 'responds with 404 status'
- end
+ it_behaves_like 'a harbor repositories controller', anonymous_status_code: '404' do
+ let_it_be(:container, reload: true) { create(:group) }
+ let_it_be(:harbor_integration) { create(:harbor_integration, group: container, project: nil) }
end
end
diff --git a/spec/requests/groups/harbor/tags_controller_spec.rb b/spec/requests/groups/harbor/tags_controller_spec.rb
new file mode 100644
index 00000000000..257d4366837
--- /dev/null
+++ b/spec/requests/groups/harbor/tags_controller_spec.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Groups::Harbor::TagsController do
+ it_behaves_like 'a harbor tags controller', anonymous_status_code: '404' do
+ let_it_be(:container) { create(:group) }
+ let_it_be(:harbor_integration) { create(:harbor_integration, group: container, project: nil) }
+ end
+end
diff --git a/spec/requests/projects/harbor/artifacts_controller_spec.rb b/spec/requests/projects/harbor/artifacts_controller_spec.rb
new file mode 100644
index 00000000000..310fbcf0a0f
--- /dev/null
+++ b/spec/requests/projects/harbor/artifacts_controller_spec.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Projects::Harbor::ArtifactsController do
+ it_behaves_like 'a harbor artifacts controller', anonymous_status_code: '302' do
+ let_it_be(:container) { create(:project) }
+ let_it_be(:harbor_integration) { create(:harbor_integration, project: container) }
+ end
+end
diff --git a/spec/requests/projects/harbor/repositories_controller_spec.rb b/spec/requests/projects/harbor/repositories_controller_spec.rb
index cdb5a696d7e..751becaa20a 100644
--- a/spec/requests/projects/harbor/repositories_controller_spec.rb
+++ b/spec/requests/projects/harbor/repositories_controller_spec.rb
@@ -3,67 +3,8 @@
require 'spec_helper'
RSpec.describe Projects::Harbor::RepositoriesController do
- let_it_be(:project, reload: true) { create(:project) }
- let_it_be(:user) { create(:user) }
-
- shared_examples 'responds with 404 status' do
- it 'returns 404' do
- subject
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
-
- shared_examples 'responds with 200 status' do
- it 'renders the index template' do
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to render_template(:index)
- end
- end
-
- before do
- stub_feature_flags(harbor_registry_integration: true)
- project.add_developer(user)
- sign_in(user)
- end
-
- describe 'GET #index' do
- subject do
- get project_harbor_registry_index_path(project)
- response
- end
-
- context 'with harbor registry feature flag enabled' do
- it_behaves_like 'responds with 200 status'
- end
-
- context 'with harbor registry feature flag disabled' do
- before do
- stub_feature_flags(harbor_registry_integration: false)
- end
-
- it_behaves_like 'responds with 404 status'
- end
- end
-
- describe 'GET #show' do
- subject do
- get project_harbor_registry_path(project, 1)
- response
- end
-
- context 'with harbor registry feature flag enabled' do
- it_behaves_like 'responds with 200 status'
- end
-
- context 'with harbor registry feature flag disabled' do
- before do
- stub_feature_flags(harbor_registry_integration: false)
- end
-
- it_behaves_like 'responds with 404 status'
- end
+ it_behaves_like 'a harbor repositories controller', anonymous_status_code: '302' do
+ let_it_be(:container, reload: true) { create(:project) }
+ let_it_be(:harbor_integration) { create(:harbor_integration, project: container) }
end
end
diff --git a/spec/requests/projects/harbor/tags_controller_spec.rb b/spec/requests/projects/harbor/tags_controller_spec.rb
new file mode 100644
index 00000000000..119d1c746ac
--- /dev/null
+++ b/spec/requests/projects/harbor/tags_controller_spec.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Projects::Harbor::TagsController do
+ it_behaves_like 'a harbor tags controller', anonymous_status_code: '302' do
+ let_it_be(:container) { create(:project) }
+ let_it_be(:harbor_integration) { create(:harbor_integration, project: container) }
+ end
+end
diff --git a/spec/routing/group_routing_spec.rb b/spec/routing/group_routing_spec.rb
index 5c2ef62683e..9f5f821cc61 100644
--- a/spec/routing/group_routing_spec.rb
+++ b/spec/routing/group_routing_spec.rb
@@ -59,6 +59,18 @@ RSpec.shared_examples 'groups routing' do
expect(get('/groups/gitlabhq/-/boards')).to route_to('groups/boards#index', group_id: 'gitlabhq')
end
+
+ it 'routes to the harbor repositories controller' do
+ expect(get("groups/#{group_path}/-/harbor/repositories")).to route_to('groups/harbor/repositories#index', group_id: group_path)
+ end
+
+ it 'routes to the harbor artifacts controller' do
+ expect(get("groups/#{group_path}/-/harbor/repositories/test/artifacts")).to route_to('groups/harbor/artifacts#index', group_id: group_path, repository_id: 'test')
+ end
+
+ it 'routes to the harbor tags controller' do
+ expect(get("groups/#{group_path}/-/harbor/repositories/test/artifacts/test/tags")).to route_to('groups/harbor/tags#index', group_id: group_path, repository_id: 'test', artifact_id: 'test')
+ end
end
RSpec.describe "Groups", "routing" do
diff --git a/spec/serializers/integrations/harbor_serializers/artifact_entity_spec.rb b/spec/serializers/integrations/harbor_serializers/artifact_entity_spec.rb
new file mode 100644
index 00000000000..c9a95c02e19
--- /dev/null
+++ b/spec/serializers/integrations/harbor_serializers/artifact_entity_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Integrations::HarborSerializers::ArtifactEntity do
+ let_it_be(:harbor_integration) { create(:harbor_integration) }
+
+ let(:artifact) do
+ {
+ "digest": "sha256:14d4f50961544fdb669075c442509f194bdc4c0e344bde06e35dbd55af842a38",
+ "id": 5,
+ "project_id": 14,
+ "push_time": "2022-03-22T09:04:56.170Z",
+ "repository_id": 5,
+ "size": 774790,
+ "tags": [
+ {
+ "artifact_id": 5,
+ "id": 7,
+ "immutable": false,
+ "name": "2",
+ "push_time": "2022-03-22T09:05:04.844Z",
+ "repository_id": 5,
+ "signed": false
+ },
+ {
+ "artifact_id": 5,
+ "id": 6,
+ "immutable": false,
+ "name": "1",
+ "push_time": "2022-03-22T09:04:56.186Z",
+ "repository_id": 5,
+ "signed": false
+ }
+ ],
+ "type": "IMAGE"
+ }.deep_stringify_keys
+ end
+
+ subject { described_class.new(artifact).as_json }
+
+ it 'returns the Harbor artifact' do
+ expect(subject).to include({
+ harbor_id: 5,
+ size: 774790,
+ push_time: "2022-03-22T09:04:56.170Z".to_datetime,
+ digest: "sha256:14d4f50961544fdb669075c442509f194bdc4c0e344bde06e35dbd55af842a38",
+ tags: %w[2 1]
+ })
+ end
+end
diff --git a/spec/serializers/integrations/harbor_serializers/artifact_serializer_spec.rb b/spec/serializers/integrations/harbor_serializers/artifact_serializer_spec.rb
new file mode 100644
index 00000000000..9879c0a6434
--- /dev/null
+++ b/spec/serializers/integrations/harbor_serializers/artifact_serializer_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Integrations::HarborSerializers::ArtifactSerializer do
+ it 'represents Integrations::HarborSerializers::ArtifactEntity entities' do
+ expect(described_class.entity_class).to eq(Integrations::HarborSerializers::ArtifactEntity)
+ end
+end
diff --git a/spec/serializers/integrations/harbor_serializers/repository_entity_spec.rb b/spec/serializers/integrations/harbor_serializers/repository_entity_spec.rb
new file mode 100644
index 00000000000..29708bd0416
--- /dev/null
+++ b/spec/serializers/integrations/harbor_serializers/repository_entity_spec.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Integrations::HarborSerializers::RepositoryEntity do
+ let_it_be(:harbor_integration) { create(:harbor_integration) }
+
+ let(:repo) do
+ {
+ "artifact_count" => 1,
+ "creation_time" => "2022-03-13T09:36:43.240Z",
+ "id" => 1,
+ "name" => "jihuprivate/busybox",
+ "project_id" => 4,
+ "pull_count" => 0,
+ "update_time" => "2022-03-13T09:36:43.240Z"
+ }.deep_stringify_keys
+ end
+
+ subject { described_class.new(repo, url: "https://demo.goharbor.io", project_name: "jihuprivate").as_json }
+
+ context 'with normal repository data' do
+ it 'returns the Harbor repository' do
+ expect(subject).to include({
+ artifact_count: 1,
+ creation_time: "2022-03-13T09:36:43.240Z".to_datetime,
+ harbor_id: 1,
+ name: "jihuprivate/busybox",
+ harbor_project_id: 4,
+ pull_count: 0,
+ update_time: "2022-03-13T09:36:43.240Z".to_datetime,
+ location: "https://demo.goharbor.io/harbor/projects/4/repositories/busybox"
+ })
+ end
+ end
+
+ context 'with data that may contain path traversal attacks' do
+ before do
+ repo["project_id"] = './../../../../../etc/hosts'
+ end
+
+ it 'returns empty location' do
+ expect(subject).to include({
+ artifact_count: 1,
+ creation_time: "2022-03-13T09:36:43.240Z".to_datetime,
+ harbor_id: 1,
+ name: "jihuprivate/busybox",
+ harbor_project_id: './../../../../../etc/hosts',
+ pull_count: 0,
+ update_time: "2022-03-13T09:36:43.240Z".to_datetime,
+ location: "https://demo.goharbor.io/"
+ })
+ end
+ end
+end
diff --git a/spec/serializers/integrations/harbor_serializers/repository_serializer_spec.rb b/spec/serializers/integrations/harbor_serializers/repository_serializer_spec.rb
new file mode 100644
index 00000000000..1a4235bea1e
--- /dev/null
+++ b/spec/serializers/integrations/harbor_serializers/repository_serializer_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Integrations::HarborSerializers::RepositorySerializer do
+ it 'represents Integrations::HarborSerializers::RepositoryEntity entities' do
+ expect(described_class.entity_class).to eq(Integrations::HarborSerializers::RepositoryEntity)
+ end
+end
diff --git a/spec/serializers/integrations/harbor_serializers/tag_entity_spec.rb b/spec/serializers/integrations/harbor_serializers/tag_entity_spec.rb
new file mode 100644
index 00000000000..f4bc5f71d5b
--- /dev/null
+++ b/spec/serializers/integrations/harbor_serializers/tag_entity_spec.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Integrations::HarborSerializers::TagEntity do
+ let_it_be(:harbor_integration) { create(:harbor_integration) }
+
+ let(:push_time) { "2022-03-22T09:04:56.186Z" }
+ let(:pull_time) { "2022-03-23T09:04:56.186Z" }
+
+ let(:tag) do
+ {
+ "artifact_id": 5,
+ "id": 6,
+ "immutable": false,
+ "name": "1",
+ "push_time": push_time,
+ "pull_time": pull_time,
+ "repository_id": 5,
+ "signed": false
+ }.deep_stringify_keys
+ end
+
+ subject { described_class.new(tag).as_json }
+
+ it 'returns the Harbor artifact' do
+ expect(subject).to include({
+ harbor_repository_id: 5,
+ harbor_artifact_id: 5,
+ harbor_id: 6,
+ name: "1",
+ pull_time: pull_time.to_datetime.utc,
+ push_time: push_time.to_datetime.utc,
+ signed: false,
+ immutable: false
+ })
+ end
+end
diff --git a/spec/serializers/integrations/harbor_serializers/tag_serializer_spec.rb b/spec/serializers/integrations/harbor_serializers/tag_serializer_spec.rb
new file mode 100644
index 00000000000..45fee0b9b17
--- /dev/null
+++ b/spec/serializers/integrations/harbor_serializers/tag_serializer_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Integrations::HarborSerializers::TagSerializer do
+ it 'represents Integrations::HarborSerializers::TagEntity entities' do
+ expect(described_class.entity_class).to eq(Integrations::HarborSerializers::TagEntity)
+ end
+end
diff --git a/spec/support/helpers/harbor_helper.rb b/spec/support/helpers/harbor_helper.rb
new file mode 100644
index 00000000000..3f13710ede6
--- /dev/null
+++ b/spec/support/helpers/harbor_helper.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module HarborHelper
+ def harbor_repository_url(container, *args)
+ if container.is_a?(Project)
+ project_harbor_repositories_path(container, *args)
+ else
+ group_harbor_repositories_path(container, *args)
+ end
+ end
+
+ def harbor_artifact_url(container, *args)
+ if container.is_a?(Project)
+ project_harbor_repository_artifacts_path(container, *args)
+ else
+ group_harbor_repository_artifacts_path(container, *args)
+ end
+ end
+
+ def harbor_tag_url(container, *args)
+ if container.is_a?(Project)
+ project_harbor_repository_artifact_tags_path(container, *args)
+ else
+ group_harbor_repository_artifact_tags_path(container, *args)
+ end
+ end
+end
diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb
index 483bca07ba6..eec6e92c5fe 100644
--- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb
+++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb
@@ -31,6 +31,7 @@ RSpec.shared_context 'GroupPolicy context' do
admin_milestone
admin_issue_board
read_container_image
+ read_harbor_registry
read_metrics_dashboard_annotation
read_prometheus
read_crm_contact
diff --git a/spec/support/shared_contexts/policies/project_policy_shared_context.rb b/spec/support/shared_contexts/policies/project_policy_shared_context.rb
index 3bb05d0b6a6..789b385c435 100644
--- a/spec/support/shared_contexts/policies/project_policy_shared_context.rb
+++ b/spec/support/shared_contexts/policies/project_policy_shared_context.rb
@@ -30,7 +30,7 @@ RSpec.shared_context 'ProjectPolicy context' do
create_snippet create_incident daily_statistics create_merge_request_in download_code
download_wiki_code fork_project metrics_dashboard read_build
read_commit_status read_confidential_issues read_container_image
- read_deployment read_environment read_merge_request
+ read_harbor_registry read_deployment read_environment read_merge_request
read_metrics_dashboard_annotation read_pipeline read_prometheus
read_sentry_issue update_issue create_merge_request_in
]
diff --git a/spec/support/shared_examples/harbor/artifacts_controller_shared_examples.rb b/spec/support/shared_examples/harbor/artifacts_controller_shared_examples.rb
new file mode 100644
index 00000000000..85fcd426e3d
--- /dev/null
+++ b/spec/support/shared_examples/harbor/artifacts_controller_shared_examples.rb
@@ -0,0 +1,162 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'a harbor artifacts controller' do |args|
+ include HarborHelper
+ let_it_be(:user) { create(:user) }
+ let_it_be(:unauthorized_user) { create(:user) }
+ let_it_be(:json_header) { { accept: 'application/json' } }
+
+ let(:mock_artifacts) do
+ [
+ {
+ "digest": "sha256:661e8e44e5d7290fbd42d0495ab4ff6fdf1ad251a9f358969b3264a22107c14d",
+ "icon": "sha256:0048162a053eef4d4ce3fe7518615bef084403614f8bca43b40ae2e762e11e06",
+ "id": 1,
+ "project_id": 1,
+ "pull_time": "0001-01-01T00:00:00.000Z",
+ "push_time": "2022-04-23T08:04:08.901Z",
+ "repository_id": 1,
+ "size": 126745886,
+ "tags": [
+ {
+ "artifact_id": 1,
+ "id": 1,
+ "immutable": false,
+ "name": "2",
+ "pull_time": "0001-01-01T00:00:00.000Z",
+ "push_time": "2022-04-23T08:04:08.920Z",
+ "repository_id": 1,
+ "signed": false
+ }
+ ],
+ "type": "IMAGE"
+ }
+ ]
+ end
+
+ let(:repository_id) { 'test' }
+
+ shared_examples 'responds with 404 status' do
+ it 'returns 404' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ shared_examples 'responds with 200 status with json' do
+ it 'renders the index template' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).not_to render_template(:index)
+ end
+ end
+
+ shared_examples 'responds with 302 status' do
+ it 'returns 302' do
+ subject
+
+ expect(response).to redirect_to(new_user_session_path)
+ end
+ end
+
+ shared_examples 'responds with 422 status with json' do
+ it 'returns 422' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:unprocessable_entity)
+ end
+ end
+
+ before do
+ stub_request(:get,
+ "https://demo.goharbor.io/api/v2.0/projects/testproject/repositories/test/artifacts"\
+ "?page=1&page_size=10&with_tag=true")
+ .with(
+ headers: {
+ 'Authorization': 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=',
+ 'Content-Type': 'application/json'
+ }).to_return(status: 200, body: mock_artifacts.to_json, headers: { "x-total-count": 2 })
+ container.add_reporter(user)
+ sign_in(user)
+ end
+
+ describe 'GET #index.json' do
+ subject do
+ get harbor_artifact_url(container, repository_id), headers: json_header
+ end
+
+ context 'with harbor registry feature flag enabled' do
+ it_behaves_like 'responds with 200 status with json'
+ end
+
+ context 'with harbor registry feature flag disabled' do
+ before do
+ stub_feature_flags(harbor_registry_integration: false)
+ end
+
+ it_behaves_like 'responds with 404 status'
+ end
+
+ context 'with anonymous user' do
+ before do
+ sign_out(user)
+ end
+
+ it_behaves_like "responds with #{args[:anonymous_status_code]} status"
+ end
+
+ context 'with unauthorized user' do
+ before do
+ sign_in(unauthorized_user)
+ end
+
+ it_behaves_like 'responds with 404 status'
+ end
+
+ context 'with valid params' do
+ context 'with valid repository' do
+ subject do
+ get harbor_artifact_url(container, repository_id), headers: json_header
+ end
+
+ it_behaves_like 'responds with 200 status with json'
+ end
+
+ context 'with valid page' do
+ subject do
+ get harbor_artifact_url(container, repository_id, page: '1'), headers: json_header
+ end
+
+ it_behaves_like 'responds with 200 status with json'
+ end
+
+ context 'with valid limit' do
+ subject do
+ get harbor_artifact_url(container, repository_id, limit: '10'), headers: json_header
+ end
+
+ it_behaves_like 'responds with 200 status with json'
+ end
+ end
+
+ context 'with invalid params' do
+ context 'with invalid page' do
+ subject do
+ get harbor_artifact_url(container, repository_id, page: 'aaa'), headers: json_header
+ end
+
+ it_behaves_like 'responds with 422 status with json'
+ end
+
+ context 'with invalid limit' do
+ subject do
+ get harbor_artifact_url(container, repository_id, limit: 'aaa'), headers: json_header
+ end
+
+ it_behaves_like 'responds with 422 status with json'
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/harbor/container_shared_examples.rb b/spec/support/shared_examples/harbor/container_shared_examples.rb
new file mode 100644
index 00000000000..57274e0b457
--- /dev/null
+++ b/spec/support/shared_examples/harbor/container_shared_examples.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'raises NotImplementedError when calling #container' do
+ describe '#container' do
+ it 'raises NotImplementedError' do
+ expect { controller.send(:container) }.to raise_error(NotImplementedError)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/harbor/repositories_controller_shared_examples.rb b/spec/support/shared_examples/harbor/repositories_controller_shared_examples.rb
new file mode 100644
index 00000000000..b35595a10b2
--- /dev/null
+++ b/spec/support/shared_examples/harbor/repositories_controller_shared_examples.rb
@@ -0,0 +1,172 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'a harbor repositories controller' do |args|
+ include HarborHelper
+ let_it_be(:user) { create(:user) }
+ let_it_be(:unauthorized_user) { create(:user) }
+ let_it_be(:json_header) { { accept: 'application/json' } }
+
+ let(:mock_repositories) do
+ [
+ {
+ "artifact_count": 6,
+ "creation_time": "2022-04-24T10:59:02.719Z",
+ "id": 33,
+ "name": "test/photon",
+ "project_id": 3,
+ "pull_count": 12,
+ "update_time": "2022-04-24T11:06:27.678Z"
+ },
+ {
+ "artifact_count": 1,
+ "creation_time": "2022-04-23T08:04:08.880Z",
+ "id": 1,
+ "name": "test/gemnasium",
+ "project_id": 3,
+ "pull_count": 0,
+ "update_time": "2022-04-23T08:04:08.880Z"
+ }
+ ]
+ end
+
+ shared_examples 'responds with 404 status' do
+ it 'returns 404' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ shared_examples 'responds with 200 status with html' do
+ it 'renders the index template' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to render_template(:index)
+ end
+ end
+
+ shared_examples 'responds with 302 status' do
+ it 'returns 302' do
+ subject
+
+ expect(response).to redirect_to(new_user_session_path)
+ end
+ end
+
+ shared_examples 'responds with 200 status with json' do
+ it 'renders the index template' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).not_to render_template(:index)
+ end
+ end
+
+ shared_examples 'responds with 422 status with json' do
+ it 'returns 422' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:unprocessable_entity)
+ end
+ end
+
+ before do
+ stub_request(:get, "https://demo.goharbor.io/api/v2.0/projects/testproject/repositories?page=1&page_size=10")
+ .with(
+ headers: {
+ 'Authorization': 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=',
+ 'Content-Type': 'application/json'
+ }).to_return(status: 200, body: mock_repositories.to_json, headers: { "x-total-count": 2 })
+ container.add_reporter(user)
+ sign_in(user)
+ end
+
+ describe 'GET #index.html' do
+ subject do
+ get harbor_repository_url(container)
+ end
+
+ context 'with harbor registry feature flag enabled' do
+ it_behaves_like 'responds with 200 status with html'
+ end
+
+ context 'with harbor registry feature flag disabled' do
+ before do
+ stub_feature_flags(harbor_registry_integration: false)
+ end
+
+ it_behaves_like 'responds with 404 status'
+ end
+
+ context 'with anonymous user' do
+ before do
+ sign_out(user)
+ end
+
+ it_behaves_like "responds with #{args[:anonymous_status_code]} status"
+ end
+
+ context 'with unauthorized user' do
+ before do
+ sign_in(unauthorized_user)
+ end
+
+ it_behaves_like 'responds with 404 status'
+ end
+ end
+
+ describe 'GET #index.json' do
+ subject do
+ get harbor_repository_url(container), headers: json_header
+ end
+
+ context 'with harbor registry feature flag enabled' do
+ it_behaves_like 'responds with 200 status with json'
+ end
+
+ context 'with harbor registry feature flag disabled' do
+ before do
+ stub_feature_flags(harbor_registry_integration: false)
+ end
+
+ it_behaves_like 'responds with 404 status'
+ end
+
+ context 'with valid params' do
+ context 'with valid page params' do
+ subject do
+ get harbor_repository_url(container, page: '1'), headers: json_header
+ end
+
+ it_behaves_like 'responds with 200 status with json'
+ end
+
+ context 'with valid limit params' do
+ subject do
+ get harbor_repository_url(container, limit: '10'), headers: json_header
+ end
+
+ it_behaves_like 'responds with 200 status with json'
+ end
+ end
+
+ context 'with invalid params' do
+ context 'with invalid page params' do
+ subject do
+ get harbor_repository_url(container, page: 'aaa'), headers: json_header
+ end
+
+ it_behaves_like 'responds with 422 status with json'
+ end
+
+ context 'with invalid limit params' do
+ subject do
+ get harbor_repository_url(container, limit: 'aaa'), headers: json_header
+ end
+
+ it_behaves_like 'responds with 422 status with json'
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/harbor/tags_controller_shared_examples.rb b/spec/support/shared_examples/harbor/tags_controller_shared_examples.rb
new file mode 100644
index 00000000000..46fea7fdff6
--- /dev/null
+++ b/spec/support/shared_examples/harbor/tags_controller_shared_examples.rb
@@ -0,0 +1,155 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'a harbor tags controller' do |args|
+ include HarborHelper
+ let_it_be(:user) { create(:user) }
+ let_it_be(:unauthorized_user) { create(:user) }
+ let_it_be(:json_header) { { accept: 'application/json' } }
+
+ let(:mock_artifacts) do
+ [
+ {
+ "artifact_id": 1,
+ "id": 1,
+ "immutable": false,
+ "name": "2",
+ "pull_time": "0001-01-01T00:00:00.000Z",
+ "push_time": "2022-04-23T08:04:08.920Z",
+ "repository_id": 1,
+ "signed": false
+ }
+ ]
+ end
+
+ let(:repository_id) { 'test' }
+ let(:artifact_id) { '1' }
+
+ shared_examples 'responds with 404 status' do
+ it 'returns 404' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ shared_examples 'responds with 200 status with json' do
+ it 'renders the index template' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).not_to render_template(:index)
+ end
+ end
+
+ shared_examples 'responds with 302 status' do
+ it 'returns 302' do
+ subject
+
+ expect(response).to redirect_to(new_user_session_path)
+ end
+ end
+
+ shared_examples 'responds with 422 status with json' do
+ it 'returns 422' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:unprocessable_entity)
+ end
+ end
+
+ before do
+ stub_request(:get,
+ "https://demo.goharbor.io/api/v2.0/projects/testproject/repositories/test/artifacts/1/tags"\
+ "?page=1&page_size=10")
+ .with(
+ headers: {
+ 'Authorization': 'Basic aGFyYm9ydXNlcm5hbWU6aGFyYm9ycGFzc3dvcmQ=',
+ 'Content-Type': 'application/json'
+ }).to_return(status: 200, body: mock_artifacts.to_json, headers: { "x-total-count": 2 })
+ container.add_reporter(user)
+ sign_in(user)
+ end
+
+ describe 'GET #index.json' do
+ subject do
+ get(harbor_tag_url(container, repository_id, artifact_id),
+ headers: json_header)
+ end
+
+ context 'with harbor registry feature flag enabled' do
+ it_behaves_like 'responds with 200 status with json'
+ end
+
+ context 'with harbor registry feature flag disabled' do
+ before do
+ stub_feature_flags(harbor_registry_integration: false)
+ end
+
+ it_behaves_like 'responds with 404 status'
+ end
+
+ context 'with anonymous user' do
+ before do
+ sign_out(user)
+ end
+
+ it_behaves_like "responds with #{args[:anonymous_status_code]} status"
+ end
+
+ context 'with unauthorized user' do
+ before do
+ sign_in(unauthorized_user)
+ end
+
+ it_behaves_like 'responds with 404 status'
+ end
+
+ context 'with valid params' do
+ context 'with valid repository' do
+ subject do
+ get harbor_tag_url(container, repository_id, artifact_id), headers: json_header
+ end
+
+ it_behaves_like 'responds with 200 status with json'
+ end
+
+ context 'with valid page' do
+ subject do
+ get(harbor_tag_url(container, repository_id, artifact_id, page: '1'),
+ headers: json_header)
+ end
+
+ it_behaves_like 'responds with 200 status with json'
+ end
+
+ context 'with valid limit' do
+ subject do
+ get(harbor_tag_url(container, repository_id, artifact_id, limit: '10'),
+ headers: json_header)
+ end
+
+ it_behaves_like 'responds with 200 status with json'
+ end
+ end
+
+ context 'with invalid params' do
+ context 'with invalid page' do
+ subject do
+ get(harbor_tag_url(container, repository_id, artifact_id, page: 'aaa'),
+ headers: json_header)
+ end
+
+ it_behaves_like 'responds with 422 status with json'
+ end
+
+ context 'with invalid limit' do
+ subject do
+ get(harbor_tag_url(container, repository_id, artifact_id, limit: 'aaa'),
+ headers: json_header)
+ end
+
+ it_behaves_like 'responds with 422 status with json'
+ end
+ end
+ end
+end