'
)
end
diff --git a/spec/lib/gitlab/audit/unauthenticated_author_spec.rb b/spec/lib/gitlab/audit/unauthenticated_author_spec.rb
index 4e5c477fc2a..70716ee7f4c 100644
--- a/spec/lib/gitlab/audit/unauthenticated_author_spec.rb
+++ b/spec/lib/gitlab/audit/unauthenticated_author_spec.rb
@@ -13,5 +13,11 @@ RSpec.describe Gitlab::Audit::UnauthenticatedAuthor do
expect(described_class.new)
.to have_attributes(id: -1, name: 'An unauthenticated user')
end
+
+ describe '#impersonated?' do
+ it 'returns false' do
+ expect(described_class.new.impersonated?).to be(false)
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/diff/rendered/notebook/diff_file_helper_spec.rb b/spec/lib/gitlab/diff/rendered/notebook/diff_file_helper_spec.rb
new file mode 100644
index 00000000000..4810d55b541
--- /dev/null
+++ b/spec/lib/gitlab/diff/rendered/notebook/diff_file_helper_spec.rb
@@ -0,0 +1,134 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+require 'rspec-parameterized'
+
+RSpec.describe Gitlab::Diff::Rendered::Notebook::DiffFileHelper do
+ let(:dummy) { Class.new { include Gitlab::Diff::Rendered::Notebook::DiffFileHelper }.new }
+
+ describe '#strip_diff_frontmatter' do
+ using RSpec::Parameterized::TableSyntax
+
+ subject { dummy.strip_diff_frontmatter(diff) }
+
+ where(:diff, :result) do
+ "FileLine1\nFileLine2\n@@ -1,76 +1,74 @@\nhello\n" | "@@ -1,76 +1,74 @@\nhello\n"
+ "" | nil
+ nil | nil
+ end
+
+ with_them do
+ it { is_expected.to eq(result) }
+ end
+ end
+
+ describe '#map_transformed_line_to_source' do
+ using RSpec::Parameterized::TableSyntax
+
+ subject { dummy.map_transformed_line_to_source(1, transformed_blocks) }
+
+ where(:case, :transformed_blocks, :result) do
+ 'if transformed diff is empty' | [] | 0
+ 'if the transformed line does not map to any in the original file' | [{ source_line: nil }] | 0
+ 'if the transformed line maps to a line in the source file' | [{ source_line: 2 }] | 3
+ end
+
+ with_them do
+ it { is_expected.to eq(result) }
+ end
+ end
+
+ describe '#map_diff_block_to_source_line' do
+ let(:file_added) { false }
+ let(:file_deleted) { false }
+ let(:old_positions) { [1] }
+ let(:new_positions) { [1] }
+ let(:lines) { old_positions.zip(new_positions).map { |old, new| Gitlab::Diff::Line.new("", "", 0, old, new) } }
+
+ subject { dummy.map_diff_block_to_source_line(lines, file_added, file_deleted)}
+
+ context 'only additions' do
+ let(:old_positions) { [1, 2, 2, 2] }
+ let(:new_positions) { [1, 2, 3, 4] }
+
+ it 'computes the removals correctly' do
+ expect(subject[0]).to eq({ 1 => 1, 2 => 4 })
+ end
+
+ it 'computes the additions correctly' do
+ expect(subject[1]).to eq({ 1 => 1, 2 => 2, 3 => 2, 4 => 2 })
+ end
+ end
+
+ context 'only additions' do
+ let(:old_positions) { [1, 2, 3, 4] }
+ let(:new_positions) { [1, 2, 2, 2] }
+
+ it 'computes the removals correctly' do
+ expect(subject[0]).to eq({ 1 => 1, 2 => 2, 3 => 2, 4 => 2 })
+ end
+
+ it 'computes the additions correctly' do
+ expect(subject[1]).to eq({ 1 => 1, 2 => 4 })
+ end
+ end
+
+ context 'with additions and removals' do
+ let(:old_positions) { [1, 2, 3, 4, 4, 4] }
+ let(:new_positions) { [1, 2, 2, 2, 3, 4] }
+
+ it 'computes the removals correctly' do
+ expect(subject[0]).to eq({ 1 => 1, 2 => 2, 3 => 2, 4 => 4 })
+ end
+
+ it 'computes the additions correctly' do
+ expect(subject[1]).to eq({ 1 => 1, 2 => 4, 3 => 4, 4 => 4 })
+ end
+ end
+
+ context 'is new file' do
+ let(:file_added) { true }
+
+ it 'removals is empty' do
+ expect(subject[0]).to be_empty
+ end
+ end
+
+ context 'is deleted file' do
+ let(:file_deleted) { true }
+
+ it 'additions is empty' do
+ expect(subject[1]).to be_empty
+ end
+ end
+ end
+
+ describe '#image_as_rich_text' do
+ let(:img) { 'data:image/png;base64,some_image_here' }
+ let(:line_text) { " ![](#{img})"}
+
+ subject { dummy.image_as_rich_text(line_text) }
+
+ context 'text does not contain image' do
+ let(:img) { "not an image" }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'text contains image' do
+ it { is_expected.to eq("
![](\"#{img}\")
") }
+ end
+
+ context 'text contains image that has malicious html' do
+ let(:img) { 'data:image/png;base64,some_image_here"
Hello
' }
+
+ it 'sanitizes the html' do
+ expect(subject).not_to include('
Hello')
+ end
+
+ it 'adds image to src' do
+ expect(subject).to end_with('/div>">')
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/count_imported_projects_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/count_imported_projects_metric_spec.rb
index ac1d1f5d74d..b7da9b27e19 100644
--- a/spec/lib/gitlab/usage/metrics/instrumentations/count_imported_projects_metric_spec.rb
+++ b/spec/lib/gitlab/usage/metrics/instrumentations/count_imported_projects_metric_spec.rb
@@ -4,15 +4,30 @@ require 'spec_helper'
RSpec.describe Gitlab::Usage::Metrics::Instrumentations::CountImportedProjectsMetric do
let_it_be(:user) { create(:user) }
- let_it_be(:gitea_imports) do
- create_list(:project, 3, import_type: 'gitea', creator_id: user.id, created_at: 3.weeks.ago)
- end
-
- let_it_be(:bitbucket_imports) do
- create_list(:project, 2, import_type: 'bitbucket', creator_id: user.id, created_at: 3.weeks.ago)
- end
+ # Project records have to be created chronologically, because of
+ # metric SQL query optimizations that rely on the fact that `id`s
+ # increment monotonically over time.
+ #
+ # See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89701
let_it_be(:old_import) { create(:project, import_type: 'gitea', creator_id: user.id, created_at: 2.months.ago) }
+ let_it_be(:gitea_import_1) { create(:project, import_type: 'gitea', creator_id: user.id, created_at: 21.days.ago) }
+
+ let_it_be(:gitea_import_2) do
+ create(:project, import_type: 'gitea', creator_id: user.id, created_at: 20.days.ago)
+ end
+
+ let_it_be(:gitea_import_3) do
+ create(:project, import_type: 'gitea', creator_id: user.id, created_at: 19.days.ago)
+ end
+
+ let_it_be(:bitbucket_import_1) do
+ create(:project, import_type: 'bitbucket', creator_id: user.id, created_at: 2.weeks.ago)
+ end
+
+ let_it_be(:bitbucket_import_2) do
+ create(:project, import_type: 'bitbucket', creator_id: user.id, created_at: 1.week.ago)
+ end
context 'with import_type gitea' do
context 'with all time frame' do
@@ -53,7 +68,7 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::CountImportedProjectsMe
options: { import_type: 'bitbucket' }
end
- context 'for 28d time frame', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/362591' do
+ context 'for 28d time frame' do
let(:expected_value) { 2 }
let(:start) { 30.days.ago.to_s(:db) }
let(:finish) { 2.days.ago.to_s(:db) }
diff --git a/spec/models/concerns/as_cte_spec.rb b/spec/models/concerns/as_cte_spec.rb
index c92d46ef25f..06d9650ec46 100644
--- a/spec/models/concerns/as_cte_spec.rb
+++ b/spec/models/concerns/as_cte_spec.rb
@@ -21,7 +21,7 @@ RSpec.describe AsCte do
it { expect(subject.query).to eq(query) }
it { expect(subject.table.name).to eq(name.to_s) }
- context 'with materialized parameter' do
+ context 'with materialized parameter', if: Gitlab::Database::AsWithMaterialized.materialized_supported? do
subject { query.as_cte(name, materialized: materialized).to_arel.to_sql }
context 'as true' do
diff --git a/spec/requests/api/graphql/ci/runner_spec.rb b/spec/requests/api/graphql/ci/runner_spec.rb
index 91008dd57e7..44646b02df0 100644
--- a/spec/requests/api/graphql/ci/runner_spec.rb
+++ b/spec/requests/api/graphql/ci/runner_spec.rb
@@ -407,17 +407,32 @@ RSpec.describe 'Query.runner(id)' do
<<~SINGLE
runner(id: "#{runner.to_global_id}") {
#{all_graphql_fields_for('CiRunner', excluded: excluded_fields)}
+ groups {
+ nodes {
+ id
+ }
+ }
+ projects {
+ nodes {
+ id
+ }
+ }
}
SINGLE
end
+ let(:active_project_runner2) { create(:ci_runner, :project) }
+ let(:active_group_runner2) { create(:ci_runner, :group) }
+
# Currently excluding a known N+1 issue, see https://gitlab.com/gitlab-org/gitlab/-/issues/334759
- let(:excluded_fields) { %w[jobCount] }
+ let(:excluded_fields) { %w[jobCount groups projects] }
let(:single_query) do
<<~QUERY
{
- active: #{runner_query(active_instance_runner)}
+ instance_runner1: #{runner_query(active_instance_runner)}
+ project_runner1: #{runner_query(active_project_runner)}
+ group_runner1: #{runner_query(active_group_runner)}
}
QUERY
end
@@ -425,22 +440,49 @@ RSpec.describe 'Query.runner(id)' do
let(:double_query) do
<<~QUERY
{
- active: #{runner_query(active_instance_runner)}
- inactive: #{runner_query(inactive_instance_runner)}
+ instance_runner1: #{runner_query(active_instance_runner)}
+ instance_runner2: #{runner_query(inactive_instance_runner)}
+ group_runner1: #{runner_query(active_group_runner)}
+ group_runner2: #{runner_query(active_group_runner2)}
+ project_runner1: #{runner_query(active_project_runner)}
+ project_runner2: #{runner_query(active_project_runner2)}
}
QUERY
end
it 'does not execute more queries per runner', :aggregate_failures do
# warm-up license cache and so on:
- post_graphql(single_query, current_user: user)
+ post_graphql(double_query, current_user: user)
control = ActiveRecord::QueryRecorder.new { post_graphql(single_query, current_user: user) }
expect { post_graphql(double_query, current_user: user) }
.not_to exceed_query_limit(control)
- expect(graphql_data_at(:active)).not_to be_nil
- expect(graphql_data_at(:inactive)).not_to be_nil
+
+ expect(graphql_data.count).to eq 6
+ expect(graphql_data).to match(
+ a_hash_including(
+ 'instance_runner1' => a_hash_including('id' => active_instance_runner.to_global_id.to_s),
+ 'instance_runner2' => a_hash_including('id' => inactive_instance_runner.to_global_id.to_s),
+ 'group_runner1' => a_hash_including(
+ 'id' => active_group_runner.to_global_id.to_s,
+ 'groups' => { 'nodes' => [a_hash_including('id' => group.to_global_id.to_s)] }
+ ),
+ 'group_runner2' => a_hash_including(
+ 'id' => active_group_runner2.to_global_id.to_s,
+ 'groups' => { 'nodes' => [a_hash_including('id' => active_group_runner2.groups[0].to_global_id.to_s)] }
+ ),
+ 'project_runner1' => a_hash_including(
+ 'id' => active_project_runner.to_global_id.to_s,
+ 'projects' => { 'nodes' => [a_hash_including('id' => active_project_runner.projects[0].to_global_id.to_s)] }
+ ),
+ 'project_runner2' => a_hash_including(
+ 'id' => active_project_runner2.to_global_id.to_s,
+ 'projects' => {
+ 'nodes' => [a_hash_including('id' => active_project_runner2.projects[0].to_global_id.to_s)]
+ }
+ )
+ ))
end
end
end
diff --git a/spec/requests/api/release/links_spec.rb b/spec/requests/api/release/links_spec.rb
index 00326426af5..2345c0063dd 100644
--- a/spec/requests/api/release/links_spec.rb
+++ b/spec/requests/api/release/links_spec.rb
@@ -49,6 +49,20 @@ RSpec.describe API::Release::Links do
expect(response).to match_response_schema('release/links')
end
+
+ context 'when using JOB-TOKEN auth' do
+ let(:job) { create(:ci_build, :running, user: maintainer) }
+
+ it 'returns releases links' do
+ get api("/projects/#{project.id}/releases/v0.1/assets/links", job_token: job.token)
+
+ aggregate_failures "testing response" do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('release/links')
+ expect(json_response.count).to eq(2)
+ end
+ end
+ end
end
context 'when release does not exist' do
@@ -116,6 +130,20 @@ RSpec.describe API::Release::Links do
expect(response).to match_response_schema('release/link')
end
+ context 'when using JOB-TOKEN auth' do
+ let(:job) { create(:ci_build, :running, user: maintainer) }
+
+ it 'returns releases link' do
+ get api("/projects/#{project.id}/releases/v0.1/assets/links/#{release_link.id}", job_token: job.token)
+
+ aggregate_failures "testing response" do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('release/link')
+ expect(json_response['name']).to eq(release_link.name)
+ end
+ end
+ end
+
context 'when specified tag is not found in the project' do
it_behaves_like '404 response' do
let(:request) { get api("/projects/#{project.id}/releases/non_existing_tag/assets/links/#{release_link.id}", maintainer) }
@@ -198,6 +226,25 @@ RSpec.describe API::Release::Links do
expect(response).to match_response_schema('release/link')
end
+ context 'when using JOB-TOKEN auth' do
+ let(:job) { create(:ci_build, :running, user: maintainer) }
+
+ it 'creates a new release link' do
+ expect do
+ post api("/projects/#{project.id}/releases/v0.1/assets/links"), params: params.merge(job_token: job.token)
+ end.to change { Releases::Link.count }.by(1)
+
+ release.reload
+
+ aggregate_failures "testing response" do
+ expect(response).to have_gitlab_http_status(:created)
+ expect(last_release_link.name).to eq('awesome-app.dmg')
+ expect(last_release_link.filepath).to eq('/binaries/awesome-app.dmg')
+ expect(last_release_link.url).to eq('https://example.com/download/awesome-app.dmg')
+ end
+ end
+ end
+
context 'with protected tag' do
context 'when user has access to the protected tag' do
let!(:protected_tag) { create(:protected_tag, :developers_can_create, name: '*', project: project) }
@@ -314,6 +361,20 @@ RSpec.describe API::Release::Links do
expect(response).to match_response_schema('release/link')
end
+ context 'when using JOB-TOKEN auth' do
+ let(:job) { create(:ci_build, :running, user: maintainer) }
+
+ it 'updates the release link' do
+ put api("/projects/#{project.id}/releases/v0.1/assets/links/#{release_link.id}"), params: params.merge(job_token: job.token)
+
+ aggregate_failures "testing response" do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('release/link')
+ expect(json_response['name']).to eq('awesome-app.msi')
+ end
+ end
+ end
+
context 'with protected tag' do
context 'when user has access to the protected tag' do
let!(:protected_tag) { create(:protected_tag, :developers_can_create, name: '*', project: project) }
@@ -411,6 +472,21 @@ RSpec.describe API::Release::Links do
expect(response).to match_response_schema('release/link')
end
+ context 'when using JOB-TOKEN auth' do
+ let(:job) { create(:ci_build, :running, user: maintainer) }
+
+ it 'deletes the release link' do
+ expect do
+ delete api("/projects/#{project.id}/releases/v0.1/assets/links/#{release_link.id}", job_token: job.token)
+ end.to change { Releases::Link.count }.by(-1)
+
+ aggregate_failures "testing response" do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('release/link')
+ end
+ end
+ end
+
context 'with protected tag' do
context 'when user has access to the protected tag' do
let!(:protected_tag) { create(:protected_tag, :developers_can_create, name: '*', project: project) }