Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
2ed368929a
commit
150effab27
16 changed files with 328 additions and 43 deletions
|
@ -1,5 +1,6 @@
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import csrf from './csrf';
|
import csrf from './csrf';
|
||||||
|
import suppressAjaxErrorsDuringNavigation from './suppress_ajax_errors_during_navigation';
|
||||||
|
|
||||||
axios.defaults.headers.common[csrf.headerKey] = csrf.token;
|
axios.defaults.headers.common[csrf.headerKey] = csrf.token;
|
||||||
// Used by Rails to check if it is a valid XHR request
|
// Used by Rails to check if it is a valid XHR request
|
||||||
|
@ -25,6 +26,20 @@ axios.interceptors.response.use(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let isUserNavigating = false;
|
||||||
|
window.addEventListener('beforeunload', () => {
|
||||||
|
isUserNavigating = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ignore AJAX errors caused by requests
|
||||||
|
// being cancelled due to browser navigation
|
||||||
|
const { gon } = window;
|
||||||
|
const featureFlagEnabled = gon && gon.features && gon.features.suppressAjaxNavigationErrors;
|
||||||
|
axios.interceptors.response.use(
|
||||||
|
response => response,
|
||||||
|
err => suppressAjaxErrorsDuringNavigation(err, isUserNavigating, featureFlagEnabled),
|
||||||
|
);
|
||||||
|
|
||||||
export default axios;
|
export default axios;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
/**
|
||||||
|
* An Axios error interceptor that suppresses AJAX errors caused
|
||||||
|
* by the request being cancelled when the user navigates to a new page
|
||||||
|
*/
|
||||||
|
export default (err, isUserNavigating, featureFlagEnabled) => {
|
||||||
|
if (featureFlagEnabled && isUserNavigating && err.code === 'ECONNABORTED') {
|
||||||
|
// If the user is navigating away from the current page,
|
||||||
|
// prevent .then() and .catch() handlers from being
|
||||||
|
// called by returning a Promise that never resolves
|
||||||
|
return new Promise(() => {});
|
||||||
|
}
|
||||||
|
|
||||||
|
// The error is not related to browser navigation,
|
||||||
|
// so propagate the error
|
||||||
|
return Promise.reject(err);
|
||||||
|
};
|
|
@ -0,0 +1,29 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
|
||||||
|
# for more information on how to write migrations for GitLab.
|
||||||
|
class UpdateCsVulnerabilityConfidenceColumn < ActiveRecord::Migration[5.2]
|
||||||
|
include Gitlab::Database::MigrationHelpers
|
||||||
|
|
||||||
|
disable_ddl_transaction!
|
||||||
|
|
||||||
|
DOWNTIME = false
|
||||||
|
BATCH_SIZE = 1_000
|
||||||
|
INTERVAL = 5.minutes
|
||||||
|
|
||||||
|
# 137_424 records to be updated on GitLab.com,
|
||||||
|
# giving us an estimated runtime of 12 hours.
|
||||||
|
def up
|
||||||
|
migration = Gitlab::BackgroundMigration::UpdateVulnerabilityConfidence
|
||||||
|
migration_name = migration.to_s.demodulize
|
||||||
|
relation = migration::Occurrence.container_scanning_reports_with_medium_confidence
|
||||||
|
queue_background_migration_jobs_by_range_at_intervals(relation,
|
||||||
|
migration_name,
|
||||||
|
INTERVAL,
|
||||||
|
batch_size: BATCH_SIZE)
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
# no-op
|
||||||
|
end
|
||||||
|
end
|
|
@ -71,8 +71,16 @@ high availability configuration documentation for
|
||||||
[PostgreSQL](../../high_availability/database.md#configuring-the-application-nodes)
|
[PostgreSQL](../../high_availability/database.md#configuring-the-application-nodes)
|
||||||
and [Redis](../../high_availability/redis.md#example-configuration-for-the-gitlab-application).
|
and [Redis](../../high_availability/redis.md#example-configuration-for-the-gitlab-application).
|
||||||
|
|
||||||
The **primary** database will require modification later, as part of
|
### Step 2: Configure the **primary** database
|
||||||
[step 2](#step-2-configure-the-main-read-only-replica-postgresql-database-on-the-secondary-node).
|
|
||||||
|
1. Edit `/etc/gitlab/gitlab.rb` and add the following:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
##
|
||||||
|
## Configure the Geo primary role and the PostgreSQL role
|
||||||
|
##
|
||||||
|
roles ['geo_primary_role', 'postgres_role']
|
||||||
|
```
|
||||||
|
|
||||||
## Configure a **secondary** node
|
## Configure a **secondary** node
|
||||||
|
|
||||||
|
@ -115,9 +123,9 @@ the **primary** database. Use the following as a guide.
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
##
|
##
|
||||||
## Configure the PostgreSQL role
|
## Configure the Geo secondary role and the PostgreSQL role
|
||||||
##
|
##
|
||||||
roles ['postgres_role']
|
roles ['geo_secondary_role', 'postgres_role']
|
||||||
|
|
||||||
##
|
##
|
||||||
## Secondary address
|
## Secondary address
|
||||||
|
|
|
@ -19,7 +19,8 @@ let testMatch = ['<rootDir>/spec/frontend/**/*_spec.js', '<rootDir>/ee/spec/fron
|
||||||
|
|
||||||
// workaround for eslint-import-resolver-jest only resolving in test files
|
// workaround for eslint-import-resolver-jest only resolving in test files
|
||||||
// see https://github.com/JoinColony/eslint-import-resolver-jest#note
|
// see https://github.com/JoinColony/eslint-import-resolver-jest#note
|
||||||
const isESLint = module.parent.filename.includes('/eslint-import-resolver-jest/');
|
const { filename: parentModuleName } = module.parent;
|
||||||
|
const isESLint = parentModuleName && parentModuleName.includes('/eslint-import-resolver-jest/');
|
||||||
if (isESLint) {
|
if (isESLint) {
|
||||||
testMatch = testMatch.map(path => path.replace('_spec.js', ''));
|
testMatch = testMatch.map(path => path.replace('_spec.js', ''));
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
# rubocop:disable Style/Documentation
|
||||||
|
module Gitlab
|
||||||
|
module BackgroundMigration
|
||||||
|
class UpdateVulnerabilityConfidence
|
||||||
|
class Occurrence < ActiveRecord::Base
|
||||||
|
include ::EachBatch
|
||||||
|
|
||||||
|
self.table_name = 'vulnerability_occurrences'
|
||||||
|
|
||||||
|
REPORT_TYPES = {
|
||||||
|
container_scanning: 2
|
||||||
|
}.freeze
|
||||||
|
|
||||||
|
CONFIDENCE_LEVELS = {
|
||||||
|
unknown: 2,
|
||||||
|
medium: 5
|
||||||
|
}.freeze
|
||||||
|
|
||||||
|
enum confidences: CONFIDENCE_LEVELS
|
||||||
|
enum report_type: REPORT_TYPES
|
||||||
|
|
||||||
|
def self.container_scanning_reports_with_medium_confidence
|
||||||
|
where(report_type: self.report_types[:container_scanning], confidence: self.confidences[:medium])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform(start_id, stop_id)
|
||||||
|
Occurrence.container_scanning_reports_with_medium_confidence
|
||||||
|
.where(id: start_id..stop_id)
|
||||||
|
.update_all(confidence: Occurrence.confidences[:unknown])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -40,3 +40,5 @@ module Gitlab
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Gitlab::GlRepository::RepoType.prepend_if_ee('EE::Gitlab::GlRepository::RepoType')
|
||||||
|
|
|
@ -38,6 +38,10 @@ module Gitlab
|
||||||
gon.current_user_fullname = current_user.name
|
gon.current_user_fullname = current_user.name
|
||||||
gon.current_user_avatar_url = current_user.avatar_url
|
gon.current_user_avatar_url = current_user.avatar_url
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Initialize gon.features with any flags that should be
|
||||||
|
# made globally available to the frontend
|
||||||
|
push_frontend_feature_flag(:suppress_ajax_navigation_errors, default_enabled: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Exposes the state of a feature flag to the frontend code.
|
# Exposes the state of a feature flag to the frontend code.
|
||||||
|
|
|
@ -1,2 +1,7 @@
|
||||||
// eslint-disable-next-line import/prefer-default-export
|
export const FIXTURES_PATH = `/fixtures`;
|
||||||
export const TEST_HOST = 'http://test.host';
|
export const TEST_HOST = 'http://test.host';
|
||||||
|
|
||||||
|
export const DUMMY_IMAGE_URL = `${FIXTURES_PATH}/static/images/one_white_pixel.png`;
|
||||||
|
|
||||||
|
export const GREEN_BOX_IMAGE_URL = `${FIXTURES_PATH}/static/images/green_box.png`;
|
||||||
|
export const RED_BOX_IMAGE_URL = `${FIXTURES_PATH}/static/images/red_box.png`;
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
import suppressAjaxErrorsDuringNavigation from '~/lib/utils/suppress_ajax_errors_during_navigation';
|
||||||
|
import waitForPromises from 'helpers/wait_for_promises';
|
||||||
|
|
||||||
|
describe('suppressAjaxErrorsDuringNavigation', () => {
|
||||||
|
const OTHER_ERR_CODE = 'foo';
|
||||||
|
const NAV_ERR_CODE = 'ECONNABORTED';
|
||||||
|
|
||||||
|
it.each`
|
||||||
|
isFeatureFlagEnabled | isUserNavigating | code
|
||||||
|
${false} | ${false} | ${OTHER_ERR_CODE}
|
||||||
|
${false} | ${false} | ${NAV_ERR_CODE}
|
||||||
|
${false} | ${true} | ${OTHER_ERR_CODE}
|
||||||
|
${false} | ${true} | ${NAV_ERR_CODE}
|
||||||
|
${true} | ${false} | ${OTHER_ERR_CODE}
|
||||||
|
${true} | ${false} | ${NAV_ERR_CODE}
|
||||||
|
${true} | ${true} | ${OTHER_ERR_CODE}
|
||||||
|
`('should return a rejected Promise', ({ isFeatureFlagEnabled, isUserNavigating, code }) => {
|
||||||
|
const err = { code };
|
||||||
|
const actual = suppressAjaxErrorsDuringNavigation(err, isUserNavigating, isFeatureFlagEnabled);
|
||||||
|
|
||||||
|
return expect(actual).rejects.toBe(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a Promise that never resolves', () => {
|
||||||
|
const err = { code: NAV_ERR_CODE };
|
||||||
|
const actual = suppressAjaxErrorsDuringNavigation(err, true, true);
|
||||||
|
|
||||||
|
const thenCallback = jest.fn();
|
||||||
|
const catchCallback = jest.fn();
|
||||||
|
actual.then(thenCallback).catch(catchCallback);
|
||||||
|
|
||||||
|
return waitForPromises().then(() => {
|
||||||
|
expect(thenCallback).not.toHaveBeenCalled();
|
||||||
|
expect(catchCallback).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -236,8 +236,15 @@ describe('Frequent Items App Component', () => {
|
||||||
.then(() => {
|
.then(() => {
|
||||||
expect(vm.$el.querySelector('.loading-animation')).toBeDefined();
|
expect(vm.$el.querySelector('.loading-animation')).toBeDefined();
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// This test waits for multiple ticks in order to allow the responses to
|
||||||
|
// propagate through each interceptor installed on the Axios instance.
|
||||||
|
// This shouldn't be necessary; this test should be refactored to avoid this.
|
||||||
|
// https://gitlab.com/gitlab-org/gitlab/issues/32479
|
||||||
.then(vm.$nextTick)
|
.then(vm.$nextTick)
|
||||||
.then(vm.$nextTick)
|
.then(vm.$nextTick)
|
||||||
|
.then(vm.$nextTick)
|
||||||
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
expect(vm.$el.querySelectorAll('.frequent-items-list-container li').length).toBe(
|
expect(vm.$el.querySelectorAll('.frequent-items-list-container li').length).toBe(
|
||||||
mockSearchedProjects.length,
|
mockSearchedProjects.length,
|
||||||
|
|
|
@ -1,7 +1 @@
|
||||||
export const FIXTURES_PATH = `/fixtures`;
|
export * from '../frontend/helpers/test_constants';
|
||||||
export const TEST_HOST = 'http://test.host';
|
|
||||||
|
|
||||||
export const DUMMY_IMAGE_URL = `${FIXTURES_PATH}/static/images/one_white_pixel.png`;
|
|
||||||
|
|
||||||
export const GREEN_BOX_IMAGE_URL = `${FIXTURES_PATH}/static/images/green_box.png`;
|
|
||||||
export const RED_BOX_IMAGE_URL = `${FIXTURES_PATH}/static/images/red_box.png`;
|
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Gitlab::BackgroundMigration::UpdateVulnerabilityConfidence, :migration, schema: 20190909141517 do
|
||||||
|
let(:vulnerabilities) { table(:vulnerability_occurrences) }
|
||||||
|
let(:identifiers) { table(:vulnerability_identifiers) }
|
||||||
|
let(:scanners) { table(:vulnerability_scanners) }
|
||||||
|
let(:projects) { table(:projects) }
|
||||||
|
let(:vul1) { attributes_for(:vulnerabilities_occurrence) }
|
||||||
|
let(:vul2) { attributes_for(:vulnerabilities_occurrence) }
|
||||||
|
let(:vul3) { attributes_for(:vulnerabilities_occurrence) }
|
||||||
|
|
||||||
|
it 'updates confidence level for container scanning reports' do
|
||||||
|
projects.create!(id: 123, namespace_id: 12, name: 'gitlab', path: 'gitlab')
|
||||||
|
|
||||||
|
(1..3).to_a.each do |identifier_id|
|
||||||
|
identifiers.create!(id: identifier_id,
|
||||||
|
project_id: 123,
|
||||||
|
fingerprint: 'd432c2ad2953e8bd587a3a43b3ce309b5b0154c' + identifier_id.to_s,
|
||||||
|
external_type: 'SECURITY_ID',
|
||||||
|
external_id: 'SECURITY_0',
|
||||||
|
name: 'SECURITY_IDENTIFIER 0')
|
||||||
|
end
|
||||||
|
|
||||||
|
scanners.create!(id: 6, project_id: 123, external_id: 'clair', name: 'Security Scanner')
|
||||||
|
|
||||||
|
vulnerabilities.create!(container_scanning_vuln_params(vul1, 1))
|
||||||
|
vulnerabilities.create!(container_scanning_vuln_params(vul2, 2))
|
||||||
|
vulnerabilities.create!(container_scanning_vuln_params(vul3, 3).merge(report_type: 1))
|
||||||
|
|
||||||
|
expect(vulnerabilities.where(report_type: 2, confidence: 2).count). to eq(0)
|
||||||
|
expect(vulnerabilities.exists?(report_type: 2, confidence: 5)).to be_truthy
|
||||||
|
|
||||||
|
described_class.new.perform(1, 3)
|
||||||
|
|
||||||
|
expect(vulnerabilities.exists?(report_type: 2, confidence: 5)).to be_falsy
|
||||||
|
expect(vulnerabilities.where(report_type: 2, confidence: 2).count). to eq(2)
|
||||||
|
end
|
||||||
|
|
||||||
|
def container_scanning_vuln_params(vul, primary_identifier_id)
|
||||||
|
{
|
||||||
|
id: vul[:id],
|
||||||
|
severity: 2,
|
||||||
|
confidence: 5,
|
||||||
|
report_type: 2,
|
||||||
|
project_id: 123,
|
||||||
|
scanner_id: 6,
|
||||||
|
primary_identifier_id: primary_identifier_id,
|
||||||
|
project_fingerprint: vul[:project_fingerprint],
|
||||||
|
location_fingerprint: vul[:location_fingerprint],
|
||||||
|
uuid: vul[:uuid],
|
||||||
|
name: vul[:name],
|
||||||
|
metadata_version: '1.3',
|
||||||
|
raw_metadata: vul3[:raw_metadata]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,36 +4,6 @@ require 'spec_helper'
|
||||||
describe Gitlab::GlRepository::RepoType do
|
describe Gitlab::GlRepository::RepoType do
|
||||||
set(:project) { create(:project) }
|
set(:project) { create(:project) }
|
||||||
|
|
||||||
shared_examples 'a repo type' do
|
|
||||||
describe "#identifier_for_subject" do
|
|
||||||
subject { described_class.identifier_for_subject(project) }
|
|
||||||
|
|
||||||
it { is_expected.to eq(expected_identifier) }
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#fetch_id" do
|
|
||||||
it "finds an id match in the identifier" do
|
|
||||||
expect(described_class.fetch_id(expected_identifier)).to eq(expected_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not break on other identifiers' do
|
|
||||||
expect(described_class.fetch_id("wiki-noid")).to eq(nil)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#path_suffix" do
|
|
||||||
subject { described_class.path_suffix }
|
|
||||||
|
|
||||||
it { is_expected.to eq(expected_suffix) }
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#repository_for" do
|
|
||||||
it "finds the repository for the repo type" do
|
|
||||||
expect(described_class.repository_for(project)).to eq(expected_repository)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe Gitlab::GlRepository::PROJECT do
|
describe Gitlab::GlRepository::PROJECT do
|
||||||
it_behaves_like 'a repo type' do
|
it_behaves_like 'a repo type' do
|
||||||
let(:expected_identifier) { "project-#{project.id}" }
|
let(:expected_identifier) { "project-#{project.id}" }
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
require Rails.root.join('db', 'post_migrate', '20190909141517_update_cs_vulnerability_confidence_column.rb')
|
||||||
|
|
||||||
|
describe UpdateCsVulnerabilityConfidenceColumn, :migration, :sidekiq do
|
||||||
|
let(:vulnerabilities) { table(:vulnerability_occurrences) }
|
||||||
|
let(:identifiers) { table(:vulnerability_identifiers) }
|
||||||
|
let(:scanners) { table(:vulnerability_scanners) }
|
||||||
|
let(:projects) { table(:projects) }
|
||||||
|
let(:vul1) { attributes_for(:vulnerabilities_occurrence, id: 1, report_type: 2, confidence: 5) }
|
||||||
|
let(:vul2) { attributes_for(:vulnerabilities_occurrence, id: 2, report_type: 2, confidence: 5) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_const("#{described_class}::BATCH_SIZE", 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates confidence levels for container scanning reports' do
|
||||||
|
projects.create!(id: 123, namespace_id: 12, name: 'gitlab', path: 'gitlab')
|
||||||
|
|
||||||
|
identifiers.create!(id: 1,
|
||||||
|
project_id: 123,
|
||||||
|
fingerprint: 'd432c2ad2953e8bd587a3a43b3ce309b5b0154c2',
|
||||||
|
external_type: 'SECURITY_ID',
|
||||||
|
external_id: 'SECURITY_0',
|
||||||
|
name: 'SECURITY_IDENTIFIER 0')
|
||||||
|
|
||||||
|
identifiers.create!(id: 2,
|
||||||
|
project_id: 123,
|
||||||
|
fingerprint: 'd432c2ad2953e8bd587a3a43b3ce309b5b0154c3',
|
||||||
|
external_type: 'SECURITY_ID',
|
||||||
|
external_id: 'SECURITY_0',
|
||||||
|
name: 'SECURITY_IDENTIFIER 0')
|
||||||
|
|
||||||
|
scanners.create!(id: 6, project_id: 123, external_id: 'clair', name: 'Security Scanner')
|
||||||
|
|
||||||
|
vulnerabilities.create!(id: vul1[:id],
|
||||||
|
severity: 2,
|
||||||
|
confidence: 5,
|
||||||
|
report_type: 2,
|
||||||
|
project_id: 123,
|
||||||
|
scanner_id: 6,
|
||||||
|
primary_identifier_id: 1,
|
||||||
|
project_fingerprint: vul1[:project_fingerprint],
|
||||||
|
location_fingerprint: vul1[:location_fingerprint],
|
||||||
|
uuid: vul1[:uuid],
|
||||||
|
name: vul1[:name],
|
||||||
|
metadata_version: '1.3',
|
||||||
|
raw_metadata: vul1[:raw_metadata])
|
||||||
|
|
||||||
|
vulnerabilities.create!(id: vul2[:id],
|
||||||
|
severity: 2,
|
||||||
|
confidence: 5,
|
||||||
|
report_type: 2,
|
||||||
|
project_id: 123,
|
||||||
|
scanner_id: 6,
|
||||||
|
primary_identifier_id: 2,
|
||||||
|
project_fingerprint: vul2[:project_fingerprint],
|
||||||
|
location_fingerprint: vul2[:location_fingerprint],
|
||||||
|
uuid: vul2[:uuid],
|
||||||
|
name: vul2[:name],
|
||||||
|
metadata_version: '1.3',
|
||||||
|
raw_metadata: vul2[:raw_metadata])
|
||||||
|
|
||||||
|
expect(vulnerabilities.where(report_type: 2, confidence: 2).count). to eq(0)
|
||||||
|
expect(vulnerabilities.exists?(report_type: 2, confidence: 5)).to be_truthy
|
||||||
|
|
||||||
|
migrate!
|
||||||
|
|
||||||
|
expect(vulnerabilities.exists?(report_type: 2, confidence: 5)).to be_falsy
|
||||||
|
expect(vulnerabilities.where(report_type: 2, confidence: 2).count). to eq(2)
|
||||||
|
end
|
||||||
|
end
|
31
spec/support/shared_examples/repo_type_shared_examples.rb
Normal file
31
spec/support/shared_examples/repo_type_shared_examples.rb
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
shared_examples 'a repo type' do
|
||||||
|
describe "#identifier_for_subject" do
|
||||||
|
subject { described_class.identifier_for_subject(project) }
|
||||||
|
|
||||||
|
it { is_expected.to eq(expected_identifier) }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#fetch_id" do
|
||||||
|
it "finds an id match in the identifier" do
|
||||||
|
expect(described_class.fetch_id(expected_identifier)).to eq(expected_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not break on other identifiers' do
|
||||||
|
expect(described_class.fetch_id("wiki-noid")).to eq(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#path_suffix" do
|
||||||
|
subject { described_class.path_suffix }
|
||||||
|
|
||||||
|
it { is_expected.to eq(expected_suffix) }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#repository_for" do
|
||||||
|
it "finds the repository for the repo type" do
|
||||||
|
expect(described_class.repository_for(project)).to eq(expected_repository)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue