提交 1da3754b 编写于 作者: G GitLab Bot

Add latest changes from gitlab-org/gitlab@master

上级 25521def
import { secondsIn, timeWindowsKeyNames } from './constants';
const secondsToMilliseconds = seconds => seconds * 1000;
export const getTimeDiff = timeWindow => {
const end = Math.floor(Date.now() / 1000); // convert milliseconds to seconds
const difference = secondsIn[timeWindow] || secondsIn.eightHours;
const start = end - difference;
return {
start: new Date(start * 1000).toISOString(),
end: new Date(end * 1000).toISOString(),
start: new Date(secondsToMilliseconds(start)).toISOString(),
end: new Date(secondsToMilliseconds(end)).toISOString(),
};
};
export const getTimeWindow = ({ start, end }) =>
Object.entries(secondsIn).reduce((acc, [timeRange, value]) => {
if (end - start === value) {
if (new Date(end) - new Date(start) === secondsToMilliseconds(value)) {
return timeRange;
}
return acc;
......
......@@ -11,7 +11,7 @@ const textBuilder = results => {
const { failed, resolved, total } = results;
const failedString = failed
? n__('%d failed test result', '%d failed test results', failed)
? n__('%d failed/error test result', '%d failed/error test results', failed)
: null;
const resolvedString = resolved
? n__('%d fixed test result', '%d fixed test results', resolved)
......
......@@ -4,19 +4,15 @@ class HealthController < ActionController::Base
protect_from_forgery with: :exception, prepend: true
include RequiresWhitelistedMonitoringClient
CHECKS = [
Gitlab::HealthChecks::DbCheck,
Gitlab::HealthChecks::Redis::RedisCheck,
Gitlab::HealthChecks::Redis::CacheCheck,
Gitlab::HealthChecks::Redis::QueuesCheck,
Gitlab::HealthChecks::Redis::SharedStateCheck,
Gitlab::HealthChecks::GitalyCheck
].freeze
def readiness
results = CHECKS.map { |check| [check.name, check.readiness] }
results = checks.flat_map(&:readiness)
success = results.all?(&:success)
render_check_results(results)
# disable static error pages at the gitlab-workhorse level, we want to see this error response even in production
headers["X-GitLab-Custom-Error"] = 1 unless success
response = results.map { |result| [result.name, result.payload] }.to_h
render json: response, status: success ? :ok : :service_unavailable
end
def liveness
......@@ -25,26 +21,7 @@ class HealthController < ActionController::Base
private
def render_check_results(results)
flattened = results.flat_map do |name, result|
if result.is_a?(Gitlab::HealthChecks::Result)
[[name, result]]
else
result.map { |r| [name, r] }
end
end
success = flattened.all? { |name, r| r.success }
response = flattened.map do |name, r|
info = { status: r.success ? 'ok' : 'failed' }
info['message'] = r.message if r.message
info[:labels] = r.labels if r.labels
[name, info]
end
# disable static error pages at the gitlab-workhorse level, we want to see this error response even in production
headers["X-GitLab-Custom-Error"] = 1 unless success
render json: response.to_h, status: success ? :ok : :service_unavailable
def checks
::Gitlab::HealthChecks::CHECKS
end
end
......@@ -63,7 +63,9 @@ module Projects
:api_host,
:token,
project: [:slug, :name, :organization_slug, :organization_name]
]
],
grafana_integration_attributes: [:token, :grafana_url]
}
end
end
......
......@@ -32,8 +32,7 @@ module GroupsHelper
end
def can_disable_group_emails?(group)
Feature.enabled?(:emails_disabled, group, default_enabled: true) &&
can?(current_user, :set_emails_disabled, group) && !group.parent&.emails_disabled?
can?(current_user, :set_emails_disabled, group) && !group.parent&.emails_disabled?
end
def group_issues_count(state:)
......
......@@ -160,7 +160,7 @@ module ProjectsHelper
def can_disable_emails?(project, current_user)
return false if project.group&.emails_disabled?
can?(current_user, :set_emails_disabled, project) && Feature.enabled?(:emails_disabled, project, default_enabled: true)
can?(current_user, :set_emails_disabled, project)
end
def last_push_event
......@@ -354,6 +354,14 @@ module ProjectsHelper
@project.metrics_setting_external_dashboard_url
end
def grafana_integration_url
@project.grafana_integration&.grafana_url
end
def grafana_integration_token
@project.grafana_integration&.token
end
private
def get_project_nav_tabs(project, current_user)
......
# frozen_string_literal: true
class GrafanaIntegration < ApplicationRecord
belongs_to :project
attr_encrypted :token,
mode: :per_attribute_iv,
algorithm: 'aes-256-gcm',
key: Settings.attr_encrypted_db_key_base_32
validates :grafana_url,
length: { maximum: 1024 },
addressable_url: { enforce_sanitization: true, ascii_only: true }
validates :token, :project, presence: true
end
......@@ -182,7 +182,7 @@ class Namespace < ApplicationRecord
# any ancestor can disable emails for all descendants
def emails_disabled?
strong_memoize(:emails_disabled) do
Feature.enabled?(:emails_disabled, self, default_enabled: true) && self_and_ancestors.where(emails_disabled: true).exists?
self_and_ancestors.where(emails_disabled: true).exists?
end
end
......
......@@ -195,6 +195,7 @@ class Project < ApplicationRecord
has_one :project_repository, inverse_of: :project
has_one :error_tracking_setting, inverse_of: :project, class_name: 'ErrorTracking::ProjectErrorTrackingSetting'
has_one :metrics_setting, inverse_of: :project, class_name: 'ProjectMetricsSetting'
has_one :grafana_integration, inverse_of: :project
# Merge Requests for target project should be removed with it
has_many :merge_requests, foreign_key: 'target_project_id', inverse_of: :target_project
......@@ -311,6 +312,7 @@ class Project < ApplicationRecord
accepts_nested_attributes_for :error_tracking_setting, update_only: true
accepts_nested_attributes_for :metrics_setting, update_only: true, allow_destroy: true
accepts_nested_attributes_for :grafana_integration, update_only: true, allow_destroy: true
delegate :name, to: :owner, allow_nil: true, prefix: true
delegate :members, to: :team, prefix: true
......@@ -664,7 +666,7 @@ class Project < ApplicationRecord
def emails_disabled?
strong_memoize(:emails_disabled) do
# disabling in the namespace overrides the project setting
Feature.enabled?(:emails_disabled, self, default_enabled: true) && (super || namespace.emails_disabled?)
super || namespace.emails_disabled?
end
end
......
......@@ -27,8 +27,12 @@ module Storage
"#{base_dir}/#{disk_hash}" if disk_hash
end
# TODO: remove this method entirely after 12.4 https://gitlab.com/gitlab-org/gitlab/issues/33244
# we no longer need ensure_storage_path_exists to call add_namespace since both creating and moving
# repositories will be preceded by a mkdir -p in gitaly to ensure the parent of the destination directory
# exists.
def ensure_storage_path_exists
gitlab_shell.add_namespace(repository_storage, base_dir)
true
end
def rename_repo(old_full_path: nil, new_full_path: nil)
......
......@@ -23,10 +23,14 @@ module Storage
project.full_path
end
# TODO: remove this method entirely after 12.4 https://gitlab.com/gitlab-org/gitlab/issues/33244
# we no longer need ensure_storage_path_exists to call add_namespace since both creating and moving
# repositories will be preceded by a mkdir -p in gitaly to ensure the parent of the destination directory
# exists.
def ensure_storage_path_exists
return unless namespace
gitlab_shell.add_namespace(repository_storage, base_dir)
true
end
def rename_repo(old_full_path: nil, new_full_path: nil)
......
......@@ -12,7 +12,9 @@ module Projects
private
def project_update_params
error_tracking_params.merge(metrics_setting_params)
error_tracking_params
.merge(metrics_setting_params)
.merge(grafana_integration_params)
end
def metrics_setting_params
......@@ -44,6 +46,14 @@ module Projects
}
}
end
def grafana_integration_params
return {} unless attrs = params[:grafana_integration_attributes]
destroy = attrs[:grafana_url].blank? && attrs[:token].blank?
{ grafana_integration_attributes: attrs.merge(_destroy: destroy) }
end
end
end
end
......
---
title: Do not start mirroring via API when paused
merge_request: 17930
author:
type: changed
---
title: Time window filter in monitor dashboard gets reset
merge_request: 17972
author:
type: fixed
---
title: MR Test Summary now shows errors as failures.
merge_request: 17039
author:
type: changed
---
title: Create table for grafana api token for metrics embeds
merge_request: 17234
author:
type: added
......@@ -22,7 +22,7 @@ class LightSettings
end
def host
config['gitlab']['host']
config.dig('gitlab', 'host') || ENV['GITLAB_HOST'] || 'localhost'
end
def gl_subdomain?
......
......@@ -16,6 +16,9 @@ en:
api_url: "Sentry API URL"
project/metrics_setting:
external_dashboard_url: "External dashboard URL"
project/grafana_integration:
token: "Grafana HTTP API Token"
grafana_url: "Grafana API URL"
views:
pagination:
previous: "Prev"
......
# frozen_string_literal: true
class CreateGrafanaIntegrations < ActiveRecord::Migration[5.2]
DOWNTIME = false
def change
create_table :grafana_integrations do |t|
t.references :project, index: true, foreign_key: { on_delete: :cascade }, unique: true, null: false
t.timestamps_with_timezone null: false
t.string :encrypted_token, limit: 255, null: false
t.string :encrypted_token_iv, limit: 255, null: false
t.string :grafana_url, null: false, limit: 1024
end
end
end
......@@ -1704,6 +1704,16 @@ ActiveRecord::Schema.define(version: 2019_09_27_074328) do
t.index ["project_id"], name: "index_gpg_signatures_on_project_id"
end
create_table "grafana_integrations", force: :cascade do |t|
t.bigint "project_id", null: false
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
t.string "encrypted_token", limit: 255, null: false
t.string "encrypted_token_iv", limit: 255, null: false
t.string "grafana_url", limit: 1024, null: false
t.index ["project_id"], name: "index_grafana_integrations_on_project_id"
end
create_table "group_custom_attributes", id: :serial, force: :cascade do |t|
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
......@@ -3997,6 +4007,7 @@ ActiveRecord::Schema.define(version: 2019_09_27_074328) do
add_foreign_key "gpg_signatures", "gpg_key_subkeys", on_delete: :nullify
add_foreign_key "gpg_signatures", "gpg_keys", on_delete: :nullify
add_foreign_key "gpg_signatures", "projects", on_delete: :cascade
add_foreign_key "grafana_integrations", "projects", on_delete: :cascade
add_foreign_key "group_custom_attributes", "namespaces", column: "group_id", on_delete: :cascade
add_foreign_key "identities", "saml_providers", name: "fk_aade90f0fc", on_delete: :cascade
add_foreign_key "import_export_uploads", "projects", on_delete: :cascade
......
......@@ -49,6 +49,12 @@ module Gitlab
if data['failure']
status = ::Gitlab::Ci::Reports::TestCase::STATUS_FAILED
system_output = data['failure']
elsif data['error']
# For now, as an MVC, we are grouping error test cases together
# with failed ones. But we will improve this further on
# https://gitlab.com/gitlab-org/gitlab/issues/32046.
status = ::Gitlab::Ci::Reports::TestCase::STATUS_FAILED
system_output = data['error']
else
status = ::Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS
system_output = nil
......
# frozen_string_literal: true
module Gitlab
module HealthChecks
CHECKS = [
Gitlab::HealthChecks::DbCheck,
Gitlab::HealthChecks::Redis::RedisCheck,
Gitlab::HealthChecks::Redis::CacheCheck,
Gitlab::HealthChecks::Redis::QueuesCheck,
Gitlab::HealthChecks::Redis::SharedStateCheck,
Gitlab::HealthChecks::GitalyCheck
].freeze
end
end
......@@ -29,7 +29,13 @@ module Gitlab
def check(storage_name)
serv = Gitlab::GitalyClient::HealthCheckService.new(storage_name)
result = serv.check
HealthChecks::Result.new(result[:success], result[:message], shard: storage_name)
HealthChecks::Result.new(
name,
result[:success],
result[:message],
shard: storage_name
)
end
private
......
# frozen_string_literal: true
module Gitlab::HealthChecks
Metric = Struct.new(:name, :value, :labels)
module Gitlab
module HealthChecks
Metric = Struct.new(:name, :value, :labels)
end
end
# frozen_string_literal: true
module Gitlab
module HealthChecks
class PrometheusTextFormat
def marshal(metrics)
"#{metrics_with_type_declarations(metrics).join("\n")}\n"
end
private
def metrics_with_type_declarations(metrics)
type_declaration_added = {}
metrics.flat_map do |metric|
metric_lines = []
unless type_declaration_added.key?(metric.name)
type_declaration_added[metric.name] = true
metric_lines << metric_type_declaration(metric)
end
metric_lines << metric_text(metric)
end
end
def metric_type_declaration(metric)
"# TYPE #{metric.name} gauge"
end
def metric_text(metric)
labels = metric.labels&.map { |key, value| "#{key}=\"#{value}\"" }&.join(',') || ''
if labels.empty?
"#{metric.name} #{metric.value}"
else
"#{metric.name}{#{labels}} #{metric.value}"
end
end
end
end
end
# frozen_string_literal: true
module Gitlab::HealthChecks
Result = Struct.new(:success, :message, :labels)
module Gitlab
module HealthChecks
Result = Struct.new(:name, :success, :message, :labels) do
def payload
{
status: success ? 'ok' : 'failed',
message: message,
labels: labels
}.compact
end
end
end
end
......@@ -8,14 +8,14 @@ module Gitlab
def readiness
check_result = check
if successful?(check_result)
HealthChecks::Result.new(true)
HealthChecks::Result.new(name, true)
elsif check_result.is_a?(Timeout::Error)
HealthChecks::Result.new(false, "#{human_name} check timed out")
HealthChecks::Result.new(name, false, "#{human_name} check timed out")
else
HealthChecks::Result.new(false, "unexpected #{human_name} check result: #{check_result}")
HealthChecks::Result.new(name, false, "unexpected #{human_name} check result: #{check_result}")
end
rescue => e
HealthChecks::Result.new(false, "unexpected #{human_name} check result: #{e}")
HealthChecks::Result.new(name, false, "unexpected #{human_name} check result: #{e}")
end
def metrics
......
......@@ -113,10 +113,6 @@ module Gitlab
success
end
# Move repository reroutes to mv_directory which is an alias for
# mv_namespace. Given the underlying implementation is a move action,
# indescriminate of what the folders might be.
#
# storage - project's storage path
# path - project disk path
# new_path - new project disk path
......@@ -275,7 +271,6 @@ module Gitlab
false
end
alias_method :mv_directory, :mv_namespace # Note: ShellWorker uses this alias
def url_to_repo(path)
Gitlab.config.gitlab_shell.ssh_path_prefix + "#{path}.git"
......
......@@ -125,6 +125,11 @@ module Gitlab
# If the addr can't be resolved or the url is invalid (i.e http://1.1.1.1.1)
# we block the url
raise BlockedUrlError, "Host cannot be resolved or invalid"
rescue ArgumentError => error
# Addrinfo.getaddrinfo errors if the domain exceeds 1024 characters.
raise unless error.message.include?('hostname too long')
raise BlockedUrlError, "Host is too long (maximum is 1024 characters)"
end
def validate_local_request(
......
......@@ -95,8 +95,8 @@ msgid_plural "%d exporters"
msgstr[0] ""
msgstr[1] ""
msgid "%d failed test result"
msgid_plural "%d failed test results"
msgid "%d failed/error test result"
msgid_plural "%d failed/error test results"
msgstr[0] ""
msgstr[1] ""
......
......@@ -32,7 +32,8 @@ describe HealthController do
end
it 'responds with readiness checks data when a failure happens' do
allow(Gitlab::HealthChecks::Redis::RedisCheck).to receive(:readiness).and_return(Gitlab::HealthChecks::Result.new(false, "check error"))
allow(Gitlab::HealthChecks::Redis::RedisCheck).to receive(:readiness).and_return(
Gitlab::HealthChecks::Result.new('redis_check', false, "check error"))
subject
......
......@@ -180,6 +180,21 @@ describe Projects::Settings::OperationsController do
end
end
context 'grafana integration' do
describe 'PATCH #update' do
let(:params) do
{
grafana_integration_attributes: {
grafana_url: 'https://grafana.gitlab.com',
token: 'eyJrIjoicDRlRTREdjhhOEZ5WjZPWXUzazJOSW0zZHJUejVOd3IiLCJuIjoiVGVzdCBLZXkiLCJpZCI6MX0='
}
}
end
it_behaves_like 'PATCHable'
end
end
private
def project_params(project, params = {})
......
# frozen_string_literal: true
FactoryBot.define do
factory :grafana_integration, class: GrafanaIntegration do
project
grafana_url { 'https://grafana.com' }
token { SecureRandom.hex(10) }
end
end
......@@ -536,10 +536,10 @@ describe 'Merge request > User sees merge widget', :js do
within(".js-reports-container") do
click_button 'Expand'
expect(page).to have_content('Test summary contained 1 failed test result out of 2 total tests')
expect(page).to have_content('Test summary contained 1 failed/error test result out of 2 total tests')
within(".js-report-section-container") do
expect(page).to have_content('rspec found no changed test results out of 1 total test')
expect(page).to have_content('junit found 1 failed test result out of 1 total test')
expect(page).to have_content('junit found 1 failed/error test result out of 1 total test')
expect(page).to have_content('New')
expect(page).to have_content('addTest')
end
......@@ -581,9 +581,9 @@ describe 'Merge request > User sees merge widget', :js do
within(".js-reports-container") do
click_button 'Expand'
expect(page).to have_content('Test summary contained 1 failed test result out of 2 total tests')
expect(page).to have_content('Test summary contained 1 failed/error test result out of 2 total tests')
within(".js-report-section-container") do
expect(page).to have_content('rspec found 1 failed test result out of 1 total test')
expect(page).to have_content('rspec found 1 failed/error test result out of 1 total test')
expect(page).to have_content('junit found no changed test results out of 1 total test')
expect(page).not_to have_content('New')
expect(page).to have_content('Test#sum when a is 1 and b is 3 returns summary')
......@@ -677,10 +677,10 @@ describe 'Merge request > User sees merge widget', :js do
within(".js-reports-container") do
click_button 'Expand'
expect(page).to have_content('Test summary contained 20 failed test results out of 20 total tests')
expect(page).to have_content('Test summary contained 20 failed/error test results out of 20 total tests')
within(".js-report-section-container") do
expect(page).to have_content('rspec found 10 failed test results out of 10 total tests')
expect(page).to have_content('junit found 10 failed test results out of 10 total tests')
expect(page).to have_content('rspec found 10 failed/error test results out of 10 total tests')
expect(page).to have_content('junit found 10 failed/error test results out of 10 total tests')
expect(page).to have_content('Test#sum when a is 1 and b is 3 returns summary', count: 2)
end
......
......@@ -30,7 +30,9 @@ describe('Reports store utils', () => {
const data = { failed: 3, total: 10 };
const result = utils.summaryTextBuilder(name, data);
expect(result).toBe('Test summary contained 3 failed test results out of 10 total tests');
expect(result).toBe(
'Test summary contained 3 failed/error test results out of 10 total tests',
);
});
it('should render text for multiple fixed results', () => {
......@@ -47,7 +49,7 @@ describe('Reports store utils', () => {
const result = utils.summaryTextBuilder(name, data);
expect(result).toBe(
'Test summary contained 3 failed test results and 4 fixed test results out of 10 total tests',
'Test summary contained 3 failed/error test results and 4 fixed test results out of 10 total tests',
);
});
......@@ -57,7 +59,7 @@ describe('Reports store utils', () => {
const result = utils.summaryTextBuilder(name, data);
expect(result).toBe(
'Test summary contained 1 failed test result and 1 fixed test result out of 10 total tests',
'Test summary contained 1 failed/error test result and 1 fixed test result out of 10 total tests',
);
});
});
......@@ -84,7 +86,7 @@ describe('Reports store utils', () => {
const data = { failed: 3, total: 10 };
const result = utils.reportTextBuilder(name, data);
expect(result).toBe('Rspec found 3 failed test results out of 10 total tests');
expect(result).toBe('Rspec found 3 failed/error test results out of 10 total tests');
});
it('should render text for multiple fixed results', () => {
......@@ -101,7 +103,7 @@ describe('Reports store utils', () => {
const result = utils.reportTextBuilder(name, data);
expect(result).toBe(
'Rspec found 3 failed test results and 4 fixed test results out of 10 total tests',
'Rspec found 3 failed/error test results and 4 fixed test results out of 10 total tests',
);
});
......@@ -111,7 +113,7 @@ describe('Reports store utils', () => {
const result = utils.reportTextBuilder(name, data);
expect(result).toBe(
'Rspec found 1 failed test result and 1 fixed test result out of 10 total tests',
'Rspec found 1 failed/error test result and 1 fixed test result out of 10 total tests',
);
});
});
......
......@@ -902,4 +902,40 @@ describe ProjectsHelper do
end
end
end
describe '#grafana_integration_url' do
let(:project) { create(:project) }
before do
helper.instance_variable_set(:@project, project)
end
subject { helper.grafana_integration_url }
it { is_expected.to eq(nil) }
context 'grafana integration exists' do
let!(:grafana_integration) { create(:grafana_integration, project: project) }
it { is_expected.to eq(grafana_integration.grafana_url) }
end
end
describe '#grafana_integration_token' do
let(:project) { create(:project) }
before do
helper.instance_variable_set(:@project, project)
end
subject { helper.grafana_integration_token }
it { is_expected.to eq(nil) }
context 'grafana integration exists' do
let!(:grafana_integration) { create(:grafana_integration, project: project) }
it { is_expected.to eq(grafana_integration.token) }
end
end
end
......@@ -333,8 +333,8 @@ describe('Dashboard', () => {
});
it('shows a specific time window selected from the url params', done => {
const start = 1564439536;
const end = 1564441336;
const start = '2019-10-01T18:27:47.000Z';
const end = '2019-10-01T18:57:47.000Z';
spyOnDependency(Dashboard, 'getTimeDiff').and.returnValue({
start,
end,
......
import { getTimeDiff, graphDataValidatorForValues } from '~/monitoring/utils';
import { timeWindows } from '~/monitoring/constants';
import { getTimeDiff, getTimeWindow, graphDataValidatorForValues } from '~/monitoring/utils';
import { timeWindows, timeWindowsKeyNames } from '~/monitoring/constants';
import { graphDataPrometheusQuery, graphDataPrometheusQueryRange } from './mock_data';
describe('getTimeDiff', () => {
......@@ -39,6 +39,55 @@ describe('getTimeDiff', () => {
});
});
describe('getTimeWindow', () => {
[
{
args: [
{
start: '2019-10-01T18:27:47.000Z',
end: '2019-10-01T21:27:47.000Z',
},
],
expected: timeWindowsKeyNames.threeHours,
},
{
args: [
{
start: '2019-10-01T28:27:47.000Z',
end: '2019-10-01T21:27:47.000Z',
},
],
expected: timeWindowsKeyNames.eightHours,
},
{
args: [
{
start: '',
end: '',
},
],
expected: timeWindowsKeyNames.eightHours,
},
{
args: [
{
start: null,
end: null,
},
],
expected: timeWindowsKeyNames.eightHours,
},
{
args: [{}],
expected: timeWindowsKeyNames.eightHours,
},
].forEach(({ args, expected }) => {
it(`returns "${expected}" with args=${JSON.stringify(args)}`, () => {
expect(getTimeWindow(...args)).toEqual(expected);
});
});
});
describe('graphDataValidatorForValues', () => {
/*
* When dealing with a metric using the query format, e.g.
......
......@@ -83,11 +83,11 @@ describe('Grouped Test Reports App', () => {
setTimeout(() => {
expect(vm.$el.querySelector('.gl-spinner')).toBeNull();
expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
'Test summary contained 2 failed test results out of 11 total tests',
'Test summary contained 2 failed/error test results out of 11 total tests',
);
expect(vm.$el.textContent).toContain(
'rspec:pg found 2 failed test results out of 8 total tests',
'rspec:pg found 2 failed/error test results out of 8 total tests',
);
expect(vm.$el.textContent).toContain('New');
......@@ -111,16 +111,16 @@ describe('Grouped Test Reports App', () => {
setTimeout(() => {
expect(vm.$el.querySelector('.gl-spinner')).toBeNull();
expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
'Test summary contained 2 failed test results and 2 fixed test results out of 11 total tests',
'Test summary contained 2 failed/error test results and 2 fixed test results out of 11 total tests',
);
expect(vm.$el.textContent).toContain(
'rspec:pg found 1 failed test result and 2 fixed test results out of 8 total tests',
'rspec:pg found 1 failed/error test result and 2 fixed test results out of 8 total tests',
);
expect(vm.$el.textContent).toContain('New');
expect(vm.$el.textContent).toContain(
' java ant found 1 failed test result out of 3 total tests',
' java ant found 1 failed/error test result out of 3 total tests',
);
done();
}, 0);
......
......@@ -10,7 +10,6 @@ describe Backup::Repository do
before do
allow(progress).to receive(:puts)
allow(progress).to receive(:print)
allow(FileUtils).to receive(:mkdir_p).and_return(true)
allow(FileUtils).to receive(:mv).and_return(true)
allow_any_instance_of(described_class).to receive(:progress).and_return(progress)
......
......@@ -38,12 +38,14 @@ describe Gitlab::Ci::Parsers::Test::Junit do
end
end
context 'when there is only one <testcase> in <testsuite>' do
context 'when there is only one <testsuite> in <testsuites>' do
let(:junit) do
<<-EOF.strip_heredoc
<testsuite>
<testcase classname='Calculator' name='sumTest1' time='0.01'></testcase>
</testsuite>
<testsuites>
<testsuite>
<testcase classname='Calculator' name='sumTest1' time='0.01'></testcase>
</testsuite>
</testsuites>
EOF
end
......@@ -56,23 +58,65 @@ describe Gitlab::Ci::Parsers::Test::Junit do
end
end
context 'when there is only one <testsuite> in <testsuites>' do
context 'when there is <testcase>' do
let(:junit) do
<<-EOF.strip_heredoc
<testsuites>
<testsuite>
<testcase classname='Calculator' name='sumTest1' time='0.01'></testcase>
<testcase classname='Calculator' name='sumTest1' time='0.01'>
#{testcase_content}
</testcase>
</testsuite>
</testsuites>
EOF
end
it 'parses XML and adds a test case to a suite' do
let(:test_case) { test_cases[0] }
before do
expect { subject }.not_to raise_error
end
expect(test_cases[0].classname).to eq('Calculator')
expect(test_cases[0].name).to eq('sumTest1')
expect(test_cases[0].execution_time).to eq(0.01)
shared_examples_for '<testcase> XML parser' do |status, output|
it 'parses XML and adds a test case to the suite' do
aggregate_failures do
expect(test_case.classname).to eq('Calculator')
expect(test_case.name).to eq('sumTest1')
expect(test_case.execution_time).to eq(0.01)
expect(test_case.status).to eq(status)
expect(test_case.system_output).to eq(output)
end
end
end
context 'and has failure' do
let(:testcase_content) { '<failure>Some failure</failure>' }
it_behaves_like '<testcase> XML parser',
::Gitlab::Ci::Reports::TestCase::STATUS_FAILED,
'Some failure'
end
context 'and has error' do
let(:testcase_content) { '<error>Some error</error>' }
it_behaves_like '<testcase> XML parser',
::Gitlab::Ci::Reports::TestCase::STATUS_FAILED,
'Some error'
end
context 'and has an unknown type' do
let(:testcase_content) { '<foo>Some foo</foo>' }
it_behaves_like '<testcase> XML parser',
::Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS,
nil
end
context 'and has no content' do
let(:testcase_content) { '' }
it_behaves_like '<testcase> XML parser',
::Gitlab::Ci::Reports::TestCase::STATUS_SUCCESS,
nil
end
end
......
......@@ -18,13 +18,13 @@ describe Gitlab::HealthChecks::GitalyCheck do
context 'Gitaly server is up' do
let(:gitaly_check) { double(check: { success: true }) }
it { is_expected.to eq([result_class.new(true, nil, shard: 'default')]) }
it { is_expected.to eq([result_class.new('gitaly_check', true, nil, shard: 'default')]) }
end
context 'Gitaly server is down' do
let(:gitaly_check) { double(check: { success: false, message: 'Connection refused' }) }
it { is_expected.to eq([result_class.new(false, 'Connection refused', shard: 'default')]) }
it { is_expected.to eq([result_class.new('gitaly_check', false, 'Connection refused', shard: 'default')]) }
end
end
......
describe Gitlab::HealthChecks::PrometheusTextFormat do
let(:metric_class) { Gitlab::HealthChecks::Metric }
subject { described_class.new }
describe '#marshal' do
let(:sample_metrics) do
[metric_class.new('metric1', 1),
metric_class.new('metric2', 2)]
end
it 'marshal to text with non repeating type definition' do
expected = <<-EXPECTED.strip_heredoc
# TYPE metric1 gauge
metric1 1
# TYPE metric2 gauge
metric2 2
EXPECTED
expect(subject.marshal(sample_metrics)).to eq(expected)
end
context 'metrics where name repeats' do
let(:sample_metrics) do
[metric_class.new('metric1', 1),
metric_class.new('metric1', 2),
metric_class.new('metric2', 3)]
end
it 'marshal to text with non repeating type definition' do
expected = <<-EXPECTED.strip_heredoc
# TYPE metric1 gauge
metric1 1
metric1 2
# TYPE metric2 gauge
metric2 3
EXPECTED
expect(subject.marshal(sample_metrics)).to eq(expected)
end
end
end
end
......@@ -411,6 +411,7 @@ project:
- external_pull_requests
- pages_metadatum
- alerts_service
- grafana_integration
award_emoji:
- awardable
- user
......
......@@ -62,6 +62,14 @@ describe Gitlab::UrlBlocker do
expect { subject }.to raise_error(described_class::BlockedUrlError)
end
end
context 'when domain is too long' do
let(:import_url) { 'https://example' + 'a' * 1024 + '.com' }
it 'raises an error' do
expect { subject }.to raise_error(described_class::BlockedUrlError)
end
end
end
context 'when the URL hostname is an IP address' do
......
# frozen_string_literal: true
require 'spec_helper'
describe GrafanaIntegration do
describe 'associations' do
it { is_expected.to belong_to(:project) }
end
describe 'validations' do
it { is_expected.to validate_presence_of(:project) }
it { is_expected.to validate_presence_of(:token) }
it 'disallows invalid urls for grafana_url' do
unsafe_url = %{https://replaceme.com/'><script>alert(document.cookie)</script>}
non_ascii_url = 'http://gitlab.com/api/0/projects/project1/something€'
blank_url = ''
excessively_long_url = 'https://grafan' + 'a' * 1024 + '.com'
is_expected.not_to allow_values(
unsafe_url,
non_ascii_url,
blank_url,
excessively_long_url
).for(:grafana_url)
end
it 'allows valid urls for grafana_url' do
external_url = 'http://grafana.com/'
internal_url = 'http://192.168.1.1'
is_expected.to allow_value(
external_url,
internal_url
).for(:grafana_url)
end
end
end
......@@ -896,32 +896,6 @@ describe Namespace do
end
end
end
context 'when :emails_disabled feature flag is off' do
before do
stub_feature_flags(emails_disabled: false)
end
context 'when not a subgroup' do
it 'returns false' do
group = create(:group, emails_disabled: true)
expect(group.emails_disabled?).to be_falsey
end
end
context 'when a subgroup and ancestor emails are disabled' do
let(:grandparent) { create(:group) }
let(:parent) { create(:group, parent: grandparent) }
let(:group) { create(:group, parent: parent) }
it 'returns false' do
grandparent.update_attribute(:emails_disabled, true)
expect(group.emails_disabled?).to be_falsey
end
end
end
end
describe '#pages_virtual_domain' do
......
......@@ -2387,29 +2387,6 @@ describe Project do
expect(project.emails_disabled?).to be_truthy
end
end
context 'when :emails_disabled feature flag is off' do
before do
stub_feature_flags(emails_disabled: false)
end
context 'emails disabled in group' do
it 'returns false' do
allow(project.namespace).to receive(:emails_disabled?) { true }
expect(project.emails_disabled?).to be_falsey
end
end
context 'emails enabled in group' do
it 'returns false' do
allow(project.namespace).to receive(:emails_disabled?) { false }
project.update_attribute(:emails_disabled, true)
expect(project.emails_disabled?).to be_falsey
end
end
end
end
describe '#lfs_enabled?' do
......@@ -3671,14 +3648,6 @@ describe Project do
end
end
describe '#ensure_storage_path_exists' do
it 'delegates to gitlab_shell to ensure namespace is created' do
expect(gitlab_shell).to receive(:add_namespace).with(project.repository_storage, project.base_dir)
project.ensure_storage_path_exists
end
end
describe '#legacy_storage?' do
it 'returns true when storage_version is nil' do
project = build(:project, storage_version: nil)
......@@ -3793,16 +3762,6 @@ describe Project do
end
end
describe '#ensure_storage_path_exists' do
it 'delegates to gitlab_shell to ensure namespace is created' do
allow(project).to receive(:gitlab_shell).and_return(gitlab_shell)
expect(gitlab_shell).to receive(:add_namespace).with(project.repository_storage, hashed_prefix)
project.ensure_storage_path_exists
end
end
describe '#pages_path' do
it 'returns a path where pages are stored' do
expect(project.pages_path).to eq(File.join(Settings.pages.path, project.namespace.full_path, project.path))
......
......@@ -8,7 +8,7 @@ describe Groups::DestroyService do
let!(:user) { create(:user) }
let!(:group) { create(:group) }
let!(:nested_group) { create(:group, parent: group) }
let!(:project) { create(:project, :legacy_storage, namespace: group) }
let!(:project) { create(:project, :repository, :legacy_storage, namespace: group) }
let!(:notification_setting) { create(:notification_setting, source: group)}
let(:gitlab_shell) { Gitlab::Shell.new }
let(:remove_path) { group.path + "+#{group.id}+deleted" }
......
......@@ -170,5 +170,61 @@ describe Projects::Operations::UpdateService do
expect(project.reload.name).to eq(original_name)
end
end
context 'grafana integration' do
let(:params) do
{
grafana_integration_attributes: {
grafana_url: 'http://new.grafana.com',
token: 'VerySecureToken='
}
}
end
context 'without existing grafana integration' do
it 'creates an integration' do
expect(result[:status]).to eq(:success)
expected_attrs = params[:grafana_integration_attributes]
integration = project.reload.grafana_integration
expect(integration.grafana_url).to eq(expected_attrs[:grafana_url])
expect(integration.token).to eq(expected_attrs[:token])
end
end
context 'with an existing grafana integration' do
before do
create(:grafana_integration, project: project)
end
it 'updates the settings' do
expect(result[:status]).to eq(:success)
expected_attrs = params[:grafana_integration_attributes]
integration = project.reload.grafana_integration
expect(integration.grafana_url).to eq(expected_attrs[:grafana_url])
expect(integration.token).to eq(expected_attrs[:token])
end
context 'with all grafana attributes blank in params' do
let(:params) do
{
grafana_integration_attributes: {
grafana_url: '',
token: ''
}
}
end
it 'destroys the metrics_setting entry in DB' do
expect(result[:status]).to eq(:success)
expect(project.reload.grafana_integration).to be_nil
end
end
end
end
end
end
......@@ -30,8 +30,8 @@ describe RepositoryCheck::DispatchWorker do
context 'with unhealthy shard' do
let(:default_shard_name) { 'default' }
let(:unhealthy_shard_name) { 'unhealthy' }
let(:default_shard) { Gitlab::HealthChecks::Result.new(true, nil, shard: default_shard_name) }
let(:unhealthy_shard) { Gitlab::HealthChecks::Result.new(false, '14:Connect Failed', shard: unhealthy_shard_name) }
let(:default_shard) { Gitlab::HealthChecks::Result.new('gitaly_check', true, nil, shard: default_shard_name) }
let(:unhealthy_shard) { Gitlab::HealthChecks::Result.new('gitaly_check', false, '14:Connect Failed', shard: unhealthy_shard_name) }
before do
allow(Gitlab::HealthChecks::GitalyCheck).to receive(:readiness).and_return([default_shard, unhealthy_shard])
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册