Commit df3db8e8 authored by Regis's avatar Regis

Merge branch 'master' into keep_mini_graph_down_pipelines_index

parents f90685b8 ff75bd04
/* eslint-disable no-param-reassign */
/* eslint-disable no-param-reassign, no-new */
/* global Vue */
/* global EnvironmentsService */
/* global Flash */
//= require vue
//= require vue-resource
......@@ -10,41 +11,6 @@
(() => {
window.gl = window.gl || {};
/**
* Given the visibility prop provided by the url query parameter and which
* changes according to the active tab we need to filter which environments
* should be visible.
*
* The environments array is a recursive tree structure and we need to filter
* both root level environments and children environments.
*
* In order to acomplish that, both `filterState` and `filterEnvironmentsByState`
* functions work together.
* The first one works as the filter that verifies if the given environment matches
* the given state.
* The second guarantees both root level and children elements are filtered as well.
*/
const filterState = state => environment => environment.state === state && environment;
/**
* Given the filter function and the array of environments will return only
* the environments that match the state provided to the filter function.
*
* @param {Function} fn
* @param {Array} array
* @return {Array}
*/
const filterEnvironmentsByState = (fn, arr) => arr.map((item) => {
if (item.children) {
const filteredChildren = filterEnvironmentsByState(fn, item.children).filter(Boolean);
if (filteredChildren.length) {
item.children = filteredChildren;
return item;
}
}
return fn(item);
}).filter(Boolean);
gl.environmentsList.EnvironmentsComponent = Vue.component('environment-component', {
props: {
store: {
......@@ -81,10 +47,6 @@
},
computed: {
filteredEnvironments() {
return filterEnvironmentsByState(filterState(this.visibility), this.state.environments);
},
scope() {
return this.$options.getQueryParameter('scope');
},
......@@ -111,7 +73,7 @@
const scope = this.$options.getQueryParameter('scope');
if (scope) {
this.visibility = scope;
this.store.storeVisibility(scope);
}
this.isLoading = true;
......@@ -121,6 +83,10 @@
.then((json) => {
this.store.storeEnvironments(json);
this.isLoading = false;
})
.catch(() => {
this.isLoading = false;
new Flash('An error occurred while fetching the environments.', 'alert');
});
},
......@@ -188,7 +154,7 @@
<div class="blank-state blank-state-no-icon"
v-if="!isLoading && state.environments.length === 0">
<h2 class="blank-state-title">
<h2 class="blank-state-title js-blank-state-title">
You don't have any environments right now.
</h2>
<p class="blank-state-text">
......@@ -202,13 +168,13 @@
<a
v-if="canCreateEnvironmentParsed"
:href="newEnvironmentPath"
class="btn btn-create">
class="btn btn-create js-new-environment-button">
New Environment
</a>
</div>
<div class="table-holder"
v-if="!isLoading && state.environments.length > 0">
v-if="!isLoading && state.filteredEnvironments.length > 0">
<table class="table ci-table environments">
<thead>
<tr>
......@@ -221,7 +187,7 @@
</tr>
</thead>
<tbody>
<template v-for="model in filteredEnvironments"
<template v-for="model in state.filteredEnvironments"
v-bind:model="model">
<tr
......
......@@ -10,6 +10,8 @@
this.state.environments = [];
this.state.stoppedCounter = 0;
this.state.availableCounter = 0;
this.state.visibility = 'available';
this.state.filteredEnvironments = [];
return this;
},
......@@ -59,7 +61,7 @@
if (occurs.length) {
acc[acc.indexOf(occurs[0])].children.push(environment);
acc[acc.indexOf(occurs[0])].children.sort(this.sortByName);
acc[acc.indexOf(occurs[0])].children.slice().sort(this.sortByName);
} else {
acc.push({
name: environment.environment_type,
......@@ -73,13 +75,70 @@
}
return acc;
}, []).sort(this.sortByName);
}, []).slice().sort(this.sortByName);
this.state.environments = environmentsTree;
this.filterEnvironmentsByVisibility(this.state.environments);
return environmentsTree;
},
storeVisibility(visibility) {
this.state.visibility = visibility;
},
/**
* Given the visibility prop provided by the url query parameter and which
* changes according to the active tab we need to filter which environments
* should be visible.
*
* The environments array is a recursive tree structure and we need to filter
* both root level environments and children environments.
*
* In order to acomplish that, both `filterState` and `filterEnvironmentsByVisibility`
* functions work together.
* The first one works as the filter that verifies if the given environment matches
* the given state.
* The second guarantees both root level and children elements are filtered as well.
*
* Given array of environments will return only
* the environments that match the state stored.
*
* @param {Array} array
* @return {Array}
*/
filterEnvironmentsByVisibility(arr) {
const filteredEnvironments = arr.map((item) => {
if (item.children) {
const filteredChildren = this.filterEnvironmentsByVisibility(
item.children,
).filter(Boolean);
if (filteredChildren.length) {
item.children = filteredChildren;
return item;
}
}
return this.filterState(this.state.visibility, item);
}).filter(Boolean);
this.state.filteredEnvironments = filteredEnvironments;
return filteredEnvironments;
},
/**
* Given the state and the environment,
* returns only if the environment state matches the one provided.
*
* @param {String} state
* @param {Object} environment
* @return {Object}
*/
filterState(state, environment) {
return environment.state === state && environment;
},
/**
* Toggles folder open property given the environment type.
*
......
......@@ -10,9 +10,9 @@
* The container should be the table element.
*
* The stage icon clicked needs to have the following HTML structure:
* <div>
* <button class="dropdown js-builds-dropdown-button"></button>
* <div class="js-builds-dropdown-container"></div>
* <div class="dropdown">
* <button class="dropdown js-builds-dropdown-button" data-toggle="dropdown"></button>
* <div class="js-builds-dropdown-container dropdown-menu"></div>
* </div>
*/
(() => {
......@@ -26,13 +26,11 @@
}
/**
* Adds and removes the event listener.
* Adds the event listener when the dropdown is opened.
* All dropdown events are fired at the .dropdown-menu's parent element.
*/
bindEvents() {
const dropdownButtonSelector = 'button.js-builds-dropdown-button';
$(this.container).off('click', dropdownButtonSelector, this.getBuildsList)
.on('click', dropdownButtonSelector, this.getBuildsList);
$(this.container).on('shown.bs.dropdown', this.getBuildsList);
}
/**
......@@ -52,11 +50,14 @@
/**
* For the clicked stage, gets the list of builds.
*
* @param {Object} e
* All dropdown events have a relatedTarget property,
* whose value is the toggling anchor element.
*
* @param {Object} e bootstrap dropdown event
* @return {Promise}
*/
getBuildsList(e) {
const button = e.currentTarget;
const button = e.relatedTarget;
const endpoint = button.dataset.stageEndpoint;
return $.ajax({
......
......@@ -57,7 +57,7 @@
return function(response) {
var error;
if (response.errorCode) {
error = new U2FError(response.errorCode);
error = new U2FError(response.errorCode, 'authenticate');
return _this.renderError(error);
} else {
return _this.renderAuthenticated(JSON.stringify(response));
......
......@@ -5,21 +5,21 @@
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
this.U2FError = (function() {
function U2FError(errorCode) {
function U2FError(errorCode, u2fFlowType) {
this.errorCode = errorCode;
this.message = bind(this.message, this);
this.httpsDisabled = window.location.protocol !== 'https:';
this.u2fFlowType = u2fFlowType;
}
U2FError.prototype.message = function() {
switch (false) {
case !(this.errorCode === u2f.ErrorCodes.BAD_REQUEST && this.httpsDisabled):
return "U2F only works with HTTPS-enabled websites. Contact your administrator for more details.";
case this.errorCode !== u2f.ErrorCodes.DEVICE_INELIGIBLE:
return "This device has already been registered with us.";
default:
return "There was a problem communicating with your device.";
if (this.errorCode === u2f.ErrorCodes.BAD_REQUEST && this.httpsDisabled) {
return 'U2F only works with HTTPS-enabled websites. Contact your administrator for more details.';
} else if (this.errorCode === u2f.ErrorCodes.DEVICE_INELIGIBLE) {
if (this.u2fFlowType === 'authenticate') return 'This device has not been registered with us.';
if (this.u2fFlowType === 'register') return 'This device has already been registered with us.';
}
return "There was a problem communicating with your device.";
};
return U2FError;
......
......@@ -39,7 +39,7 @@
return function(response) {
var error;
if (response.errorCode) {
error = new U2FError(response.errorCode);
error = new U2FError(response.errorCode, 'register');
return _this.renderError(error);
} else {
return _this.renderRegistered(JSON.stringify(response));
......
......@@ -10,7 +10,7 @@
max-width: 100%;
}
*:first-child {
*:first-child:not(.katex-display) {
margin-top: 0;
}
......
......@@ -377,6 +377,10 @@
display: inline-block;
padding: 5px;
&:nth-of-type(7n) {
padding-right: 0;
}
.author_link {
display: block;
}
......
......@@ -6,21 +6,15 @@ class Projects::HooksController < Projects::ApplicationController
layout "project_settings"
def index
@hooks = @project.hooks
@hook = ProjectHook.new
end
def create
@hook = @project.hooks.new(hook_params)
@hook.save
if @hook.valid?
redirect_to namespace_project_hooks_path(@project.namespace, @project)
else
unless @hook.valid?
@hooks = @project.hooks.select(&:persisted?)
render :index
flash[:alert] = @hook.errors.full_messages.join.html_safe
end
redirect_to namespace_project_settings_integrations_path(@project.namespace, @project)
end
def test
......@@ -44,7 +38,7 @@ class Projects::HooksController < Projects::ApplicationController
def destroy
hook.destroy
redirect_to namespace_project_hooks_path(@project.namespace, @project)
redirect_to namespace_project_settings_integrations_path(@project.namespace, @project)
end
private
......
......@@ -9,10 +9,6 @@ class Projects::ServicesController < Projects::ApplicationController
layout "project_settings"
def index
@services = @project.find_or_initialize_services
end
def edit
end
......
module Projects
module Settings
class IntegrationsController < Projects::ApplicationController
include ServiceParams
before_action :authorize_admin_project!
layout "project_settings"
def show
@hooks = @project.hooks
@hook = ProjectHook.new
# Services
@services = @project.find_or_initialize_services
end
end
end
end
......@@ -208,6 +208,10 @@ module GitlabRoutingHelper
end
# Settings
def project_settings_integrations_path(project, *args)
namespace_project_settings_integrations_path(project.namespace, project, *args)
end
def project_settings_members_path(project, *args)
namespace_project_settings_members_path(project.namespace, project, *args)
end
......
......@@ -13,6 +13,49 @@ class ApplicationSetting < ActiveRecord::Base
[\r\n] # any number of newline characters
}x
DEFAULTS_CE = {
after_sign_up_text: nil,
akismet_enabled: false,
container_registry_token_expire_delay: 5,
default_branch_protection: Settings.gitlab['default_branch_protection'],
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_projects_limit: Settings.gitlab['default_projects_limit'],
default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
disabled_oauth_sign_in_sources: [],
domain_whitelist: Settings.gitlab['domain_whitelist'],
gravatar_enabled: Settings.gravatar['enabled'],
help_page_text: nil,
housekeeping_bitmaps_enabled: true,
housekeeping_enabled: true,
housekeeping_full_repack_period: 50,
housekeeping_gc_period: 200,
housekeeping_incremental_repack_period: 10,
import_sources: Gitlab::ImportSources.values,
koding_enabled: false,
koding_url: nil,
max_artifacts_size: Settings.artifacts['max_size'],
max_attachment_size: Settings.gitlab['max_attachment_size'],
plantuml_enabled: false,
plantuml_url: nil,
recaptcha_enabled: false,
repository_checks_enabled: true,
repository_storages: ['default'],
require_two_factor_authentication: false,
restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
session_expire_delay: Settings.gitlab['session_expire_delay'],
send_user_confirmation_email: false,
shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
shared_runners_text: nil,
sidekiq_throttling_enabled: false,
sign_in_text: nil,
signin_enabled: Settings.gitlab['signin_enabled'],
signup_enabled: Settings.gitlab['signup_enabled'],
two_factor_grace_period: 48,
user_default_external: false
}
DEFAULTS = DEFAULTS_CE
serialize :restricted_visibility_levels
serialize :import_sources
serialize :disabled_oauth_sign_in_sources, Array
......@@ -163,46 +206,7 @@ class ApplicationSetting < ActiveRecord::Base
end
def self.create_from_defaults
create(
default_projects_limit: Settings.gitlab['default_projects_limit'],
default_branch_protection: Settings.gitlab['default_branch_protection'],
signup_enabled: Settings.gitlab['signup_enabled'],
signin_enabled: Settings.gitlab['signin_enabled'],
gravatar_enabled: Settings.gravatar['enabled'],
sign_in_text: nil,
after_sign_up_text: nil,
help_page_text: nil,
shared_runners_text: nil,
restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
max_attachment_size: Settings.gitlab['max_attachment_size'],
session_expire_delay: Settings.gitlab['session_expire_delay'],
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
domain_whitelist: Settings.gitlab['domain_whitelist'],
import_sources: Gitlab::ImportSources.values,
shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
max_artifacts_size: Settings.artifacts['max_size'],
require_two_factor_authentication: false,
two_factor_grace_period: 48,
recaptcha_enabled: false,
akismet_enabled: false,
koding_enabled: false,
koding_url: nil,
plantuml_enabled: false,
plantuml_url: nil,
repository_checks_enabled: true,
disabled_oauth_sign_in_sources: [],
send_user_confirmation_email: false,
container_registry_token_expire_delay: 5,
repository_storages: ['default'],
user_default_external: false,
sidekiq_throttling_enabled: false,
housekeeping_enabled: true,
housekeeping_bitmaps_enabled: true,
housekeeping_incremental_repack_period: 10,
housekeeping_full_repack_period: 50,
housekeeping_gc_period: 200,
)
create(DEFAULTS)
end
def home_page_url_column_exist
......
......@@ -92,6 +92,12 @@ module Ci
end
state_machine :status do
after_transition any => [:pending] do |build|
build.run_after_commit do
BuildQueueWorker.perform_async(id)
end
end
after_transition pending: :running do |build|
build.run_after_commit do
BuildHooksWorker.perform_async(id)
......
......@@ -2,6 +2,7 @@ module Ci
class Runner < ActiveRecord::Base
extend Ci::Model
RUNNER_QUEUE_EXPIRY_TIME = 60.minutes
LAST_CONTACT_TIME = 1.hour.ago
AVAILABLE_SCOPES = %w[specific shared active paused online]
FORM_EDITABLE = %i[description tag_list active run_untagged locked]
......@@ -21,6 +22,8 @@ module Ci
scope :online, ->() { where('contacted_at > ?', LAST_CONTACT_TIME) }
scope :ordered, ->() { order(id: :desc) }
after_save :tick_runner_queue, if: :form_editable_changed?
scope :owned_or_shared, ->(project_id) do
joins('LEFT JOIN ci_runner_projects ON ci_runner_projects.runner_id = ci_runners.id')
.where("ci_runner_projects.gl_project_id = :project_id OR ci_runners.is_shared = true", project_id: project_id)
......@@ -122,8 +125,38 @@ module Ci
]
end
def tick_runner_queue
SecureRandom.hex.tap do |new_update|
Gitlab::Redis.with do |redis|
redis.set(runner_queue_key, new_update, ex: RUNNER_QUEUE_EXPIRY_TIME)
end
end
end
def ensure_runner_queue_value
Gitlab::Redis.with do |redis|
value = SecureRandom.hex
redis.set(runner_queue_key, value, ex: RUNNER_QUEUE_EXPIRY_TIME, nx: true)
redis.get(runner_queue_key)
end
end
def is_runner_queue_value_latest?(value)
ensure_runner_queue_value == value if value.present?
end
private
def runner_queue_key
"runner:build_queue:#{self.token}"
end
def form_editable_changed?
FORM_EDITABLE.any? do |editable|
public_send("#{editable}_changed?")
end
end
def tag_constraints
unless has_tags? || run_untagged?
errors.add(:tags_list,
......
......@@ -4,6 +4,8 @@ class Key < ActiveRecord::Base
include AfterCommitQueue
include Sortable
LAST_USED_AT_REFRESH_TIME = 1.day.to_i
belongs_to :user
before_validation :generate_fingerprint
......@@ -50,7 +52,10 @@ class Key < ActiveRecord::Base
end
def update_last_used_at
UseKeyWorker.perform_async(self.id)
lease = Gitlab::ExclusiveLease.new("key_update_last_used_at:#{id}", timeout: LAST_USED_AT_REFRESH_TIME)
return unless lease.try_obtain
UseKeyWorker.perform_async(id)
end
def add_to_shell
......
module Ci
class UpdateBuildQueueService
def execute(build)
build.project.runners.each do |runner|
if runner.can_pick?(build)
runner.tick_runner_queue
end
end
return unless build.project.shared_runners_enabled?
Ci::Runner.shared.each do |runner|
if runner.can_pick?(build)
runner.tick_runner_queue
end
end
end
end
end
......@@ -8,14 +8,10 @@
= link_to namespace_project_deploy_keys_path(@project.namespace, @project), title: 'Deploy Keys' do
%span
Deploy Keys
= nav_link(controller: :hooks) do
= link_to namespace_project_hooks_path(@project.namespace, @project), title: 'Webhooks' do
= nav_link(controller: :integrations) do
= link_to namespace_project_settings_integrations_path(@project.namespace, @project), title: 'Integrations' do
%span
Webhooks
= nav_link(controller: :services) do
= link_to namespace_project_services_path(@project.namespace, @project), title: 'Services' do
%span
Services
Integrations
= nav_link(controller: :protected_branches) do
= link_to namespace_project_protected_branches_path(@project.namespace, @project), title: 'Protected Branches' do
%span
......
......@@ -13,7 +13,7 @@
%a.close{ href: "#", "data-dismiss" => "modal" } ×
%h3.page-title== #{label} this #{commit.change_type_title(current_user)}
.modal-body
= form_tag [type.underscore, @project.namespace, @project, commit], method: :post, remote: false, class: "form-horizontal js-#{type}-form js-requires-input" do
= form_tag [type.underscore, @project.namespace.becomes(Namespace), @project, commit], method: :post, remote: false, class: "form-horizontal js-#{type}-form js-requires-input" do
.form-group.branch
= label_tag 'target_branch', target_label, class: 'control-label'
.col-sm-10
......
- page_title "Services"
.row.prepend-top-default.append-bottom-default
.col-lg-3
%h4.prepend-top-0
......
- page_title 'Integrations'
= render 'projects/hooks/index'
= render 'projects/services/index'
- page_title "Webhooks"
- context_title = @project ? 'project' : 'group'
.row.prepend-top-default
.col-lg-3
%h4.prepend-top-0
......
class BuildQueueWorker
include Sidekiq::Worker
include BuildQueue
def perform(build_id)
Ci::Build.find_by(id: build_id).try do |build|
Ci::UpdateBuildQueueService.new.execute(build)
end
end
end
---
title: Reduce DB-load for build-queues by storing last_update in Redis
merge_request: 8084
author:
---
title: Handle HTTP errors in environment list
merge_request:
author:
---
title: Add margin to markdown math blocks
merge_request:
author:
---
title: Fixes builds dropdown making request when clicked to be closed
merge_request: 8545
author:
---
title: Fix import/export wrong user mapping
merge_request:
author:
---
title: Fix participants margins to fit on one line
merge_request:
author:
---
title: Record used SSH keys only once per day
merge_request: 8655
author:
......@@ -307,9 +307,9 @@ constraints(ProjectUrlConstrainer.new) do
end
end
end
namespace :settings do
resource :members, only: [:show]
resource :integrations, only: [:show]
end
# Since both wiki and repository routing contains wildcard characters
......
class AddEstimateToIssuablesCe < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
unless column_exists?(:issues, :time_estimate)
add_column :issues, :time_estimate, :integer
end
unless column_exists?(:merge_requests, :time_estimate)
add_column :merge_requests, :time_estimate, :integer
end
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddTimeEstimateToIssuables < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
# When a migration requires downtime you **must** uncomment the following
# constant and define a short and easy to understand explanation as to why the
# migration requires downtime.
# DOWNTIME_REASON = ''
# When using the methods "add_concurrent_index" or "add_column_with_default"
# you must disable the use of transactions as these methods can not run in an
# existing transaction. When using "add_concurrent_index" make sure that this
# method is the _only_ method called in the migration, any other changes
# should go in a separate migration. This ensures that upon failure _only_ the
# index creation fails and can be retried or reverted easily.
#
# To disable transactions uncomment the following line and remove these
# comments:
# disable_ddl_transaction!
def change
add_column :issues, :time_estimate, :integer
add_column :merge_requests, :time_estimate, :integer
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class CreateTimelogs < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
# When a migration requires downtime you **must** uncomment the following
# constant and define a short and easy to understand explanation as to why the
# migration requires downtime.
# DOWNTIME_REASON = ''
# When using the methods "add_concurrent_index" or "add_column_with_default"
# you must disable the use of transactions as these methods can not run in an
# existing transaction. When using "add_concurrent_index" make sure that this
# method is the _only_ method called in the migration, any other changes
# should go in a separate migration. This ensures that upon failure _only_ the
# index creation fails and can be retried or reverted easily.
#
# To disable transactions uncomment the following line and remove these
# comments:
# disable_ddl_transaction!
def change
create_table :timelogs do |t|
t.integer :time_spent, null: false
t.references :trackable, polymorphic: true
t.references :user
t.timestamps null: false
end
add_index :timelogs, [:trackable_type, :trackable_id]
add_index :timelogs, :user_id
end
end
class CreateTimelogsCe < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
unless table_exists?(:timelogs)
create_table :timelogs do |t|
t.integer :time_spent, null: false
t.references :trackable, polymorphic: true
t.references :user
t.timestamps null: false
end
add_index :timelogs, [:trackable_type, :trackable_id]
add_index :timelogs, :user_id
end
end
end
# GitLab Community Edition documentation
## University
[University](university/README.md) contain guides to learn Git and GitLab through courses and videos.
## User documentation
- [Account Security](user/account/security.md) Securing your account via two-factor authentication, etc.
......@@ -19,7 +23,6 @@
- [SSH](ssh/README.md) Setup your ssh keys and deploy keys for secure access to your projects.
- [Webhooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project.
- [Workflow](workflow/README.md) Using GitLab functionality and importing projects from GitHub and SVN.
- [University](university/README.md) Learn Git and GitLab through videos and courses.
- [Git Attributes](user/project/git_attributes.md) Managing Git attributes using a `.gitattributes` file.
- [Git cheatsheet](https://gitlab.com/gitlab-com/marketing/raw/master/design/print/git-cheatsheet/print-pdf/git-cheatsheet.pdf) Download a PDF describing the most used Git operations.
......
......@@ -104,6 +104,13 @@ that needs access to the GitLab API.
Once you have your token, pass it to the API using either the `private_token`
parameter or the `PRIVATE-TOKEN` header.
> [Introduced][ce-5951] in GitLab 8.15.
Personal Access Tokens can be created with one or more scopes that allow various actions
that a given token can perform. Although there are only two scopes available at the
moment – `read_user` and `api` – the groundwork has been laid to add more scopes easily.
At any time you can revoke any personal access token by just clicking **Revoke**.
### Session Cookie
......@@ -380,3 +387,4 @@ programming languages. Visit the [GitLab website] for a complete list.
[GitLab website]: https://about.gitlab.com/applications/#api-clients "Clients using the GitLab API"
[lib-api-url]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/lib/api/api.rb
[ce-3749]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3749
[ce-5951]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5951
......@@ -61,7 +61,18 @@ correctly with your CI builds:
1. First, make sure you have used [relative URLs](#configuring-the-gitmodules-file)
for the submodules located in the same GitLab server.
1. Then, use `git submodule sync/update` in `before_script`:
1. Next, if you are using `gitlab-ci-multi-runner` v1.10+, you can set the
`GIT_SUBMODULE_STRATEGY` variable to either `normal` or `recursive` to tell
the runner to fetch your submodules before the build:
```yaml
variables:
GIT_SUBMODULE_STRATEGY: recursive
```
See the [`.gitlab-ci.yml` reference](yaml/README.md#git-submodule-strategy)
for more details about `GIT_SUBMODULE_STRATEGY`.
1. If you are using an older version of `gitlab-ci-multi-runner`, then use
`git submodule sync/update` in `before_script`:
```yaml
before_script:
......
......@@ -1034,6 +1034,41 @@ variables:
GIT_STRATEGY: none
```
## Git Submodule Strategy
> Requires GitLab Runner v1.10+.
The `GIT_SUBMODULE_STRATEGY` variable is used to control if / how Git
submodules are included when fetching the code before a build. Like
`GIT_STRATEGY`, it can be set in either the global [`variables`](#variables)
section or the [`variables`](#job-variables) section for individual jobs.
There are three posible values: `none`, `normal`, and `recursive`:
- `none` means that submodules will not be included when fetching the project
code. This is the default, which matches the pre-v1.10 behavior.
- `normal` means that only the top-level submodules will be included. It is
equivalent to:
```
$ git submodule sync
$ git submodule update --init
```
- `recursive` means that all submodules (including submodules of submodules)
will be included. It is equivalent to:
```
$ git submodule sync --recursive
$ git submodule update --init --recursive
```
Note that for this feature to work correctly, the submodules must be configured
(in `.gitmodules`) with either:
- the HTTP(S) URL of a publicly-accessible repository, or
- a relative path to another repository on the same GitLab server. See the
[Git submodules](../git_submodules.md) documentation.
## Build stages attempts
> Introduced in GitLab, it requires GitLab Runner v1.9+.
......
......@@ -74,8 +74,10 @@ in the **Authorized applications** section under **Profile Settings > Applicatio
---
As you can see, the default scope `api` is used, which is the only scope that
GitLab supports so far. At any time you can revoke any access by just clicking
**Revoke**.
GitLab's OAuth applications support scopes, which allow various actions that any given
application can perform. Although there are only two scopes available at the
moment – `read_user` and `api` – the groundwork has been laid to add more scopes easily.
At any time you can revoke any access by just clicking **Revoke**.
[oauth]: http://oauth.net/2/ "OAuth website"
......@@ -52,7 +52,7 @@ for anonymous users. The group page now has a visibility level icon.
## Visibility of users
The public page of a user, located at `/u/username`, is always visible whether
The public page of a user, located at `/username`, is always visible whether
you are logged in or not.
When visiting the public page of a user, you can only see the projects which
......@@ -60,10 +60,13 @@ you are privileged to.
If the public level is restricted, user profiles are only visible to logged in users.
## Restricting the use of public or internal projects
In the Admin area under **Settings** (`/admin/application_settings`), you can
restrict the use of visibility levels for users when they create a project or a
snippet. This is useful to prevent people exposing their repositories to public
snippet:
![Restrict visibility levels](img/restrict_visibility_levels.png)
This is useful to prevent people exposing their repositories to public
by accident. The restricted visibility settings do not apply to admin users.
......@@ -39,10 +39,10 @@ Feature: Project Active Tab
# Sub Tabs: Settings
Scenario: On Project Settings/Hooks
Scenario: On Project Settings/Integrations
Given I visit my project's settings page
And I click the "Hooks" tab
Then the active sub nav should be Hooks
And I click the "Integrations" tab
Then the active sub nav should be Integrations
And no other sub navs should be active
And the active main tab should be Settings
......
......@@ -27,8 +27,8 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps
end
end
step 'I click the "Hooks" tab' do
click_link('Webhooks')
step 'I click the "Integrations" tab' do
click_link('Integrations')
end
step 'I click the "Deploy Keys" tab' do
......@@ -39,8 +39,8 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps
ensure_active_sub_nav('Members')
end
step 'the active sub nav should be Hooks' do
ensure_active_sub_nav('Webhooks')
step 'the active sub nav should be Integrations' do
ensure_active_sub_nav('Integrations')
end
step 'the active sub nav should be Deploy Keys' do
......
......@@ -36,12 +36,12 @@ class Spinach::Features::ProjectHooks < Spinach::FeatureSteps
end
step 'I should see newly created hook' do
expect(current_path).to eq namespace_project_hooks_path(current_project.namespace, current_project)
expect(current_path).to eq namespace_project_settings_integrations_path(current_project.namespace, current_project)
expect(page).to have_content(@url)
end
step 'I should see newly created hook with SSL verification enabled' do
expect(current_path).to eq namespace_project_hooks_path(current_project.namespace, current_project)
expect(current_path).to eq namespace_project_settings_integrations_path(current_project.namespace, current_project)
expect(page).to have_content(@url)
expect(page).to have_content("SSL Verification: enabled")
end
......@@ -57,7 +57,7 @@ class Spinach::Features::ProjectHooks < Spinach::FeatureSteps
end
step 'hook should be triggered' do
expect(current_path).to eq namespace_project_hooks_path(current_project.namespace, current_project)
expect(current_path).to eq namespace_project_settings_integrations_path(current_project.namespace, current_project)
expect(page).to have_selector '.flash-notice',
text: 'Hook executed successfully: HTTP 200'
end
......
......@@ -4,7 +4,7 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps
include SharedPaths
step 'I visit project "Shop" services page' do
visit namespace_project_services_path(@project.namespace, @project)
visit namespace_project_settings_integrations_path(@project.namespace, @project)
end
step 'I should see list of available services' do
......
......@@ -256,7 +256,7 @@ module SharedPaths
end
step 'I visit project hooks page' do
visit namespace_project_hooks_path(@project.namespace, @project)
visit namespace_project_settings_integrations_path(@project.namespace, @project)
end
step 'I visit project deploy keys page' do
......
......@@ -226,7 +226,7 @@ module API
end
def render_api_error!(message, status)
error!({ 'message' => message }, status)
error!({ 'message' => message }, status, header)
end
def handle_api_exception(exception)
......
......@@ -16,6 +16,13 @@ module Ci
not_found! unless current_runner.active?
update_runner_info
if current_runner.is_runner_queue_value_latest?(params[:last_update])
header 'X-GitLab-Last-Update', params[:last_update]
return build_not_found!
end
new_update = current_runner.ensure_runner_queue_value
build = Ci::RegisterBuildService.new.execute(current_runner)
if build
......@@ -26,6 +33,8 @@ module Ci
else
Gitlab::Metrics.add_event(:build_not_found)
header 'X-GitLab-Last-Update', new_update
build_not_found!
end
end
......
......@@ -9,7 +9,9 @@ module Gitlab
end
def ensure_application_settings!
if connect_to_db?
return fake_application_settings unless connect_to_db?
unless ENV['IN_MEMORY_APPLICATION_SETTINGS'] == 'true'
begin
settings = ::ApplicationSetting.current
# In case Redis isn't running or the Redis UNIX socket file is not available
......@@ -20,43 +22,23 @@ module Gitlab
settings ||= ::ApplicationSetting.create_from_defaults unless ActiveRecord::Migrator.needs_migration?
end
settings || fake_application_settings
settings || in_memory_application_settings
end
def sidekiq_throttling_enabled?
current_application_settings.sidekiq_throttling_enabled?
end
def in_memory_application_settings
@in_memory_application_settings ||= ApplicationSetting.new(ApplicationSetting::DEFAULTS)
# In case migrations the application_settings table is not created yet,
# we fallback to a simple OpenStruct
rescue ActiveRecord::StatementInvalid
fake_application_settings
end
def fake_application_settings
OpenStruct.new(
default_projects_limit: Settings.gitlab['default_projects_limit'],
default_branch_protection: Settings.gitlab['default_branch_protection'],
signup_enabled: Settings.gitlab['signup_enabled'],
signin_enabled: Settings.gitlab['signin_enabled'],
gravatar_enabled: Settings.gravatar['enabled'],
koding_enabled: false,
plantuml_enabled: false,
sign_in_text: nil,
after_sign_up_text: nil,
help_page_text: nil,
shared_runners_text: nil,
restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
max_attachment_size: Settings.gitlab['max_attachment_size'],
session_expire_delay: Settings.gitlab['session_expire_delay'],
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
domain_whitelist: Settings.gitlab['domain_whitelist'],
import_sources: %w[gitea github bitbucket gitlab google_code fogbugz git gitlab_project],
shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
max_artifacts_size: Settings.artifacts['max_size'],
require_two_factor_authentication: false,
two_factor_grace_period: 48,
akismet_enabled: false,
repository_checks_enabled: true,
container_registry_token_expire_delay: 5,
user_default_external: false,
sidekiq_throttling_enabled: false,
)
OpenStruct.new(ApplicationSetting::DEFAULTS)
end
private
......
module Gitlab
module GithubImport
class ProjectCreator
include Gitlab::CurrentSettings
attr_reader :repo, :name, :namespace, :current_user, :session_data, :type
def initialize(repo, name, namespace, current_user, session_data, type: 'github')
......@@ -34,7 +36,7 @@ module Gitlab
end
def visibility_level
repo.private ? Gitlab::VisibilityLevel::PRIVATE : ApplicationSetting.current.default_project_visibility
repo.private ? Gitlab::VisibilityLevel::PRIVATE : current_application_settings.default_project_visibility
end
#
......
module Gitlab
module ImportExport
class MembersMapper
attr_reader :missing_author_ids
def initialize(exported_members:, user:, project:)
@exported_members = exported_members
@exported_members = user.admin? ? exported_members : []
@user = user
@project = project
@missing_author_ids = []
# This needs to run first, as second call would be from #map
# which means project members already exist.
......@@ -39,7 +36,6 @@ module Gitlab
def missing_keys_tracking_hash
Hash.new do |_, key|
@missing_author_ids << key
default_user_id
end
end
......@@ -64,7 +60,7 @@ module Gitlab
end
def find_project_user_query(member)
user_arel[:username].eq(member['user']['username']).or(user_arel[:email].eq(member['user']['email']))
user_arel[:email].eq(member['user']['email']).or(user_arel[:username].eq(member['user']['username']))
end
def user_arel
......
......@@ -14,7 +14,7 @@ module Gitlab
priorities: :label_priorities,
label: :project_label }.freeze
USER_REFERENCES = %w[author_id assignee_id updated_by_id user_id created_by_id merge_user_id].freeze
USER_REFERENCES = %w[author_id assignee_id updated_by_id user_id created_by_id merge_user_id resolved_by_id].freeze
PROJECT_REFERENCES = %w[project_id source_project_id gl_project_id target_project_id].freeze
......@@ -80,17 +80,13 @@ module Gitlab
# is left.
def set_note_author
old_author_id = @relation_hash['author_id']
# Users with admin access can map users
@relation_hash['author_id'] = admin_user? ? @members_mapper.map[old_author_id] : @members_mapper.default_user_id
author = @relation_hash.delete('author')
update_note_for_missing_author(author['name']) if missing_author?(old_author_id)
update_note_for_missing_author(author['name']) unless has_author?(old_author_id)
end
def missing_author?(old_author_id)
!admin_user? || @members_mapper.missing_author_ids.include?(old_author_id)
def has_author?(old_author_id)
admin_user? && @members_mapper.map.keys.include?(old_author_id)
end
def missing_author_note(updated_at, author_name)
......
require 'spec_helper'
describe HealthCheckController do
include StubENV
let(:token) { current_application_settings.health_check_access_token }
let(:json_response) { JSON.parse(response.body) }
let(:xml_response) { Hash.from_xml(response.body)['hash'] }
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
end
describe 'GET #index' do
context 'when services are up but NO access token' do
it 'returns a not found page' do
......
require 'spec_helper'
describe Projects::Settings::IntegrationsController do
let(:project) { create(:project, :public) }
let(:user) { create(:user) }
before do
project.team << [user, :master]
sign_in(user)
end
describe 'GET show' do
it 'renders show with 200 status code' do
get :show, namespace_id: project.namespace, project_id: project
expect(response).to have_http_status(200)
expect(response).to render_template(:show)
end
end
end
require 'rails_helper'
feature 'Admin disables Git access protocol', feature: true do
include StubENV
let(:project) { create(:empty_project, :empty_repo) }
let(:admin) { create(:admin) }
background do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
login_as(admin)
end
......
require 'spec_helper'
feature "Admin Health Check", feature: true do
include StubENV
include WaitForAjax
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
login_as :admin
end
......@@ -12,11 +14,12 @@ feature "Admin Health Check", feature: true do
visit admin_health_check_path
end
it { page.has_text? 'Health Check' }
it { page.has_text? 'Health information can be retrieved' }
it 'has a health check access token' do
page.has_text? 'Health Check'
page.has_text? 'Health information can be retrieved'
token = current_application_settings.health_check_access_token
expect(page).to have_content("Access token is #{token}")
expect(page).to have_selector('#health-check-token', text: token)
end
......
require 'spec_helper'
describe "Admin Runners" do
include StubENV
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
login_as :admin
end
......
require 'spec_helper'
feature 'Admin updates settings', feature: true do
before(:each) do
include StubENV
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
login_as :admin
visit admin_application_settings_path
end
......
require 'rails_helper'
feature 'Admin uses repository checks', feature: true do
before { login_as :admin }
include StubENV
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
login_as :admin
end
scenario 'to trigger a single check' do
project = create(:empty_project)
......
......@@ -2,7 +2,8 @@ require 'spec_helper'
describe 'Cherry-pick Merge Requests' do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:group) { create(:group) }
let(:project) { create(:project, namespace: group) }
let(:merge_request) { create(:merge_request_with_diffs, source_project: project, author: user) }
before do
......
......@@ -2,7 +2,8 @@ require 'spec_helper'
include WaitForAjax
describe 'Cherry-pick Commits' do
let(:project) { create(:project) }
let(:group) { create(:group) }
let(:project) { create(:project, namespace: group) }
let(:master_pickable_commit) { project.commit('7d3b0f7cff5f37573aea97cebfd5692ea1689924') }
let(:master_pickable_merge) { project.commit('e56497bb5f03a90a51293fc6d516788730953899') }
......
......@@ -262,8 +262,8 @@ describe "Internal Project Access", feature: true do
it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/hooks" do
subject { namespace_project_hooks_path(project.namespace, project) }
describe "GET /:project_path/settings/integrations" do
subject { namespace_project_settings_integrations_path(project.namespace, project) }
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
......
......@@ -234,8 +234,8 @@ describe "Private Project Access", feature: true do
it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/hooks" do
subject { namespace_project_hooks_path(project.namespace, project) }
describe "GET /:project_path/namespace/hooks" do
subject { namespace_project_settings_integrations_path(project.namespace, project) }
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
......
......@@ -400,8 +400,8 @@ describe "Public Project Access", feature: true do
it { is_expected.to be_allowed_for(:visitor) }
end
describe "GET /:project_path/hooks" do
subject { namespace_project_hooks_path(project.namespace, project) }
describe "GET /:project_path/settings/integrations" do
subject { namespace_project_settings_integrations_path(project.namespace, project) }
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
......
......@@ -165,7 +165,7 @@ describe 'Dashboard Todos', feature: true do
end
it 'shows the todo' do
expect(page).to have_content 'The build failed for your merge request'
expect(page).to have_content 'The build failed for merge request'
end
it 'links to the pipelines for the merge request' do
......
......@@ -2,13 +2,13 @@ require 'spec_helper'
describe MoveToProjectFinder do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:project) { create(:empty_project) }
let(:no_access_project) { create(:project) }
let(:guest_project) { create(:project) }
let(:reporter_project) { create(:project) }
let(:developer_project) { create(:project) }
let(:master_project) { create(:project) }
let(:no_access_project) { create(:empty_project) }
let(:guest_project) { create(:empty_project) }
let(:reporter_project) { create(:empty_project) }
let(:developer_project) { create(:empty_project) }
let(:master_project) { create(:empty_project) }
subject { described_class.new(user) }
......@@ -37,7 +37,7 @@ describe MoveToProjectFinder do
it 'does not return archived projects' do
reporter_project.team << [user, :reporter]
reporter_project.update_attributes(archived: true)
other_reporter_project = create(:project)
other_reporter_project = create(:empty_project)
other_reporter_project.team << [user, :reporter]
expect(subject.execute(project).to_a).to eq([other_reporter_project])
......@@ -46,7 +46,7 @@ describe MoveToProjectFinder do
it 'does not return projects for which issues are disabled' do
reporter_project.team << [user, :reporter]
reporter_project.update_attributes(issues_enabled: false)
other_reporter_project = create(:project)
other_reporter_project = create(:empty_project)
other_reporter_project.team << [user, :reporter]
expect(subject.execute(project).to_a).to eq([other_reporter_project])
......@@ -83,10 +83,10 @@ describe MoveToProjectFinder do
end
it 'returns projects matching a search query' do
foo_project = create(:project)
foo_project = create(:empty_project)
foo_project.team << [user, :master]
wadus_project = create(:project, name: 'wadus')
wadus_project = create(:empty_project, name: 'wadus')
wadus_project.team << [user, :master]
expect(subject.execute(project).to_a).to eq([wadus_project, foo_project])
......
/* global Vue, environment */
//= require vue
//= require vue-resource
//= require flash
//= require environments/stores/environments_store
//= require environments/components/environment
//= require ./mock_data
describe('Environment', () => {
preloadFixtures('environments/environments');
let component;
beforeEach(() => {
loadFixtures('environments/environments');
});
describe('successfull request', () => {
describe('without environments', () => {
const environmentsEmptyResponseInterceptor = (request, next) => {
next(request.respondWith(JSON.stringify([]), {
status: 200,
}));
};
beforeEach(() => {
Vue.http.interceptors.push(environmentsEmptyResponseInterceptor);
});
afterEach(() => {
Vue.http.interceptors = _.without(
Vue.http.interceptors, environmentsEmptyResponseInterceptor,
);
});
it('should render the empty state', (done) => {
component = new gl.environmentsList.EnvironmentsComponent({
el: document.querySelector('#environments-list-view'),
propsData: {
store: gl.environmentsList.EnvironmentsStore.create(),
},
});
setTimeout(() => {
expect(
component.$el.querySelector('.js-new-environment-button').textContent,
).toContain('New Environment');
expect(
component.$el.querySelector('.js-blank-state-title').textContent,
).toContain('You don\'t have any environments right now.');
done();
}, 0);
});
});
describe('with environments', () => {
const environmentsResponseInterceptor = (request, next) => {
next(request.respondWith(JSON.stringify([environment]), {
status: 200,
}));
};
beforeEach(() => {
Vue.http.interceptors.push(environmentsResponseInterceptor);
});
afterEach(() => {
Vue.http.interceptors = _.without(
Vue.http.interceptors, environmentsResponseInterceptor,
);
});
it('should render a table with environments', (done) => {
component = new gl.environmentsList.EnvironmentsComponent({
el: document.querySelector('#environments-list-view'),
propsData: {
store: gl.environmentsList.EnvironmentsStore.create(),
},
});
setTimeout(() => {
expect(
component.$el.querySelectorAll('table tbody tr').length,
).toEqual(1);
done();
}, 0);
});
});
});
describe('unsuccessfull request', () => {
const environmentsErrorResponseInterceptor = (request, next) => {
next(request.respondWith(JSON.stringify([]), {
status: 500,
}));
};
beforeEach(() => {
Vue.http.interceptors.push(environmentsErrorResponseInterceptor);
});
afterEach(() => {
Vue.http.interceptors = _.without(
Vue.http.interceptors, environmentsErrorResponseInterceptor,
);
});
it('should render empty state', (done) => {
component = new gl.environmentsList.EnvironmentsComponent({
el: document.querySelector('#environments-list-view'),
propsData: {
store: gl.environmentsList.EnvironmentsStore.create(),
},
});
setTimeout(() => {
expect(
component.$el.querySelector('.js-blank-state-title').textContent,
).toContain('You don\'t have any environments right now.');
done();
}, 0);
});
});
});
......@@ -133,3 +133,17 @@ const environmentsList = [
updated_at: '2016-11-07T11:11:16.525Z',
},
];
const environment = {
id: 4,
name: 'production',
state: 'available',
external_url: 'http://production.',
environment_type: null,
last_deployment: {},
'stoppable?': false,
environment_path: '/root/review-app/environments/4',
stop_path: '/root/review-app/environments/4/stop',
created_at: '2016-12-16T11:51:04.690Z',
updated_at: '2016-12-16T12:04:51.133Z',
};
%div
#environments-list-view{ data: { environments_data: "https://gitlab.com/foo/environments",
#environments-list-view{ data: { environments_data: "foo/environments",
"can-create-deployment" => "true",
"can-read-environment" => "true",
"can-create-environment" => "true",
......
%div.js-builds-dropdown-tests
%button.dropdown.js-builds-dropdown-button{'data-stage-endpoint' => 'foobar'}
%div.js-builds-dropdown-tests.dropdown.dropdown.js-mini-pipeline-graph
%button.js-builds-dropdown-button{'data-stage-endpoint' => 'foobar', data: { toggle: 'dropdown'} }
Dropdown
%div.js-builds-dropdown-container
%div.js-builds-dropdown-list
%div.js-builds-dropdown-loading.builds-dropdown-loading.hidden
%ul.dropdown-menu.mini-pipeline-graph-dropdown-menu.js-builds-dropdown-container
.js-builds-dropdown-list.scrollable-menu
.js-builds-dropdown-loading.builds-dropdown-loading.hidden
%span.fa.fa-spinner.fa-spin
require 'spec_helper'
describe Gitlab::CurrentSettings do
include StubENV
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
end
describe '#current_application_settings' do
it 'attempts to use cached values first' do
context 'with DB available' do
before do
allow_any_instance_of(Gitlab::CurrentSettings).to receive(:connect_to_db?).and_return(true)
expect(ApplicationSetting).to receive(:current).and_return(::ApplicationSetting.create_from_defaults)
expect(ApplicationSetting).not_to receive(:last)
expect(current_application_settings).to be_a(ApplicationSetting)
end
it 'does not attempt to connect to DB or Redis' do
allow_any_instance_of(Gitlab::CurrentSettings).to receive(:connect_to_db?).and_return(false)
expect(ApplicationSetting).not_to receive(:current)
it 'attempts to use cached values first' do
expect(ApplicationSetting).to receive(:current)
expect(ApplicationSetting).not_to receive(:last)
expect(current_application_settings).to eq fake_application_settings
expect(current_application_settings).to be_a(ApplicationSetting)
end
it 'falls back to DB if Redis returns an empty value' do
allow_any_instance_of(Gitlab::CurrentSettings).to receive(:connect_to_db?).and_return(true)
expect(ApplicationSetting).to receive(:last).and_call_original
expect(current_application_settings).to be_a(ApplicationSetting)
end
it 'falls back to DB if Redis fails' do
allow_any_instance_of(Gitlab::CurrentSettings).to receive(:connect_to_db?).and_return(true)
expect(ApplicationSetting).to receive(:current).and_raise(::Redis::BaseError)
expect(ApplicationSetting).to receive(:last).and_call_original
expect(current_application_settings).to be_a(ApplicationSetting)
end
end
context 'with DB unavailable' do
before do
allow_any_instance_of(Gitlab::CurrentSettings).to receive(:connect_to_db?).and_return(false)
end
it 'returns an in-memory ApplicationSetting object' do
expect(ApplicationSetting).not_to receive(:current)
expect(ApplicationSetting).not_to receive(:last)
expect(current_application_settings).to be_a(OpenStruct)
end
end
context 'when ENV["IN_MEMORY_APPLICATION_SETTINGS"] is true' do
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'true')
end
it 'returns an in-memory ApplicationSetting object' do
expect(ApplicationSetting).not_to receive(:current)
expect(ApplicationSetting).not_to receive(:last)
expect(current_application_settings).to be_a(ApplicationSetting)
expect(current_application_settings).not_to be_persisted
end
end
end
end
......@@ -2,7 +2,7 @@ require 'spec_helper'
describe Gitlab::ImportExport::MembersMapper, services: true do
describe 'map members' do
let(:user) { create(:user, authorized_projects_populated: true) }
let(:user) { create(:admin, authorized_projects_populated: true) }
let(:project) { create(:project, :public, name: 'searchable_project') }
let(:user2) { create(:user, authorized_projects_populated: true) }
let(:exported_user_id) { 99 }
......@@ -24,7 +24,7 @@ describe Gitlab::ImportExport::MembersMapper, services: true do
{
"id" => exported_user_id,
"email" => user2.email,
"username" => user2.username
"username" => 'test'
}
},
{
......@@ -48,6 +48,10 @@ describe Gitlab::ImportExport::MembersMapper, services: true do
exported_members: exported_members, user: user, project: project)
end
it 'includes the exported user ID in the map' do
expect(members_mapper.map.keys).to include(exported_user_id)
end
it 'maps a project member' do
expect(members_mapper.map[exported_user_id]).to eq(user2.id)
end
......@@ -56,12 +60,6 @@ describe Gitlab::ImportExport::MembersMapper, services: true do
expect(members_mapper.map[-1]).to eq(user.id)
end
it 'updates missing author IDs on missing project member' do
members_mapper.map[-1]
expect(members_mapper.missing_author_ids.first).to eq(-1)
end
it 'has invited members with no user' do
members_mapper.map
......@@ -74,5 +72,25 @@ describe Gitlab::ImportExport::MembersMapper, services: true do
expect(user.authorized_project?(project)).to be true
expect(user2.authorized_project?(project)).to be true
end
context 'user is not an admin' do
let(:user) { create(:user, authorized_projects_populated: true) }
it 'does not map a project member' do
expect(members_mapper.map[exported_user_id]).to eq(user.id)
end
it 'defaults to importer project member if it does not exist' do
expect(members_mapper.map[-1]).to eq(user.id)
end
end
context 'chooses the one with an email first' do
let(:user3) { create(:user, username: 'test') }
it 'maps the project member that has a matching email first' do
expect(members_mapper.map[exported_user_id]).to eq(user2.id)
end
end
end
end
......@@ -3,7 +3,7 @@ require 'spec_helper'
describe Gitlab::ImportExport::RelationFactory, lib: true do
let(:project) { create(:empty_project) }
let(:members_mapper) { double('members_mapper').as_null_object }
let(:user) { create(:user) }
let(:user) { create(:admin) }
let(:created_object) do
described_class.create(relation_sym: relation_sym,
relation_hash: relation_hash,
......@@ -122,4 +122,60 @@ describe Gitlab::ImportExport::RelationFactory, lib: true do
expect(created_object.values).not_to include(99)
end
end
context 'Notes user references' do
let(:relation_sym) { :notes }
let(:new_user) { create(:user) }
let(:exported_member) do
{
"id" => 111,
"access_level" => 30,
"source_id" => 1,
"source_type" => "Project",
"user_id" => 3,
"notification_level" => 3,
"created_at" => "2016-11-18T09:29:42.634Z",
"updated_at" => "2016-11-18T09:29:42.634Z",
"user" => {
"id" => 999,
"email" => new_user.email,
"username" => new_user.username
}
}
end
let(:relation_hash) do
{
"id" => 4947,
"note" => "merged",
"noteable_type" => "MergeRequest",
"author_id" => 999,
"created_at" => "2016-11-18T09:29:42.634Z",
"updated_at" => "2016-11-18T09:29:42.634Z",
"project_id" => 1,
"attachment" => {
"url" => nil
},
"noteable_id" => 377,
"system" => true,
"author" => {
"name" => "Administrator"
},
"events" => [
]
}
end
let(:members_mapper) do
Gitlab::ImportExport::MembersMapper.new(
exported_members: [exported_member],
user: user,
project: project)
end
it 'maps the right author to the imported note' do
expect(created_object.author).to eq(new_user)
end
end
end
......@@ -1401,4 +1401,14 @@ describe Ci::Build, :models do
it { is_expected.to eq(%w[predefined project pipeline yaml secret]) }
end
end
describe 'State transition: any => [:pending]' do
let(:build) { create(:ci_build, :created) }
it 'queues BuildQueueWorker' do
expect(BuildQueueWorker).to receive(:perform_async).with(build.id)
build.enqueue
end
end
end
......@@ -263,6 +263,62 @@ describe Ci::Runner, models: true do
end
end
describe '#tick_runner_queue' do
let(:runner) { create(:ci_runner) }
it 'returns a new last_update value' do
expect(runner.tick_runner_queue).not_to be_empty
end
end
describe '#ensure_runner_queue_value' do
let(:runner) { create(:ci_runner) }
it 'sets a new last_update value when it is called the first time' do
last_update = runner.ensure_runner_queue_value
expect_value_in_redis.to eq(last_update)
end
it 'does not change if it is not expired and called again' do
last_update = runner.ensure_runner_queue_value
expect(runner.ensure_runner_queue_value).to eq(last_update)
expect_value_in_redis.to eq(last_update)
end
context 'updates runner queue after changing editable value' do
let!(:last_update) { runner.ensure_runner_queue_value }
before do
runner.update(description: 'new runner')
end
it 'sets a new last_update value' do
expect_value_in_redis.not_to eq(last_update)
end
end
context 'does not update runner value after save' do
let!(:last_update) { runner.ensure_runner_queue_value }
before do
runner.touch
end
it 'has an old last_update value' do
expect_value_in_redis.to eq(last_update)
end
end
def expect_value_in_redis
Gitlab::Redis.with do |redis|
runner_queue_key = runner.send(:runner_queue_key)
expect(redis.get(runner_queue_key))
end
end
end
describe '.assignable_for' do
let(:runner) { create(:ci_runner) }
let(:project) { create(:project) }
......
......@@ -30,13 +30,32 @@ describe Key, models: true do
end
describe "#update_last_used_at" do
it "enqueues a UseKeyWorker job" do
key = create(:key)
let(:key) { create(:key) }
context 'when key was not updated during the last day' do
before do
allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).
and_return('000000')
end
it 'enqueues a UseKeyWorker job' do
expect(UseKeyWorker).to receive(:perform_async).with(key.id)
key.update_last_used_at
end
end
context 'when key was updated during the last day' do
before do
allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).
and_return(false)
end
it 'does not enqueue a UseKeyWorker job' do
expect(UseKeyWorker).not_to receive(:perform_async)
key.update_last_used_at
end
end
end
end
context "validation of uniqueness (based on fingerprint uniqueness)" do
......
......@@ -65,7 +65,7 @@ describe MergeRequest, models: true do
end
describe '#target_branch_sha' do
let(:project) { create(:project) }
let(:project) { create(:project, :repository) }
subject { create(:merge_request, source_project: project, target_project: project) }
......@@ -150,7 +150,7 @@ describe MergeRequest, models: true do
end
it 'supports a cross-project reference' do
another_project = build(:project, name: 'another-project', namespace: project.namespace)
another_project = build(:empty_project, name: 'another-project', namespace: project.namespace)
expect(merge_request.to_reference(another_project)).to eq "sample-project!1"
end
......@@ -245,8 +245,8 @@ describe MergeRequest, models: true do
describe '#for_fork?' do
it 'returns true if the merge request is for a fork' do
subject.source_project = create(:project, namespace: create(:group))
subject.target_project = create(:project, namespace: create(:group))
subject.source_project = build_stubbed(:empty_project, namespace: create(:group))
subject.target_project = build_stubbed(:empty_project, namespace: create(:group))
expect(subject.for_fork?).to be_truthy
end
......@@ -501,8 +501,8 @@ describe MergeRequest, models: true do
end
describe '#diverged_commits_count' do
let(:project) { create(:project) }
let(:fork_project) { create(:project, forked_from_project: project) }
let(:project) { create(:project, :repository) }
let(:fork_project) { create(:project, :repository, forked_from_project: project) }
context 'when the target branch does not exist anymore' do
subject { create(:merge_request, source_project: project, target_project: project) }
......@@ -727,7 +727,7 @@ describe MergeRequest, models: true do
end
describe '#participants' do
let(:project) { create(:project, :public) }
let(:project) { create(:empty_project, :public) }
let(:mr) do
create(:merge_request, source_project: project, target_project: project)
......@@ -768,7 +768,7 @@ describe MergeRequest, models: true do
end
describe '#check_if_can_be_merged' do
let(:project) { create(:project, only_allow_merge_if_build_succeeds: true) }
let(:project) { create(:empty_project, only_allow_merge_if_build_succeeds: true) }
subject { create(:merge_request, source_project: project, merge_status: :unchecked) }
......@@ -810,7 +810,7 @@ describe MergeRequest, models: true do
end
describe '#mergeable?' do
let(:project) { create(:project) }
let(:project) { create(:empty_project) }
subject { create(:merge_request, source_project: project) }
......@@ -830,7 +830,7 @@ describe MergeRequest, models: true do
end
describe '#mergeable_state?' do
let(:project) { create(:project) }
let(:project) { create(:project, :repository) }
subject { create(:merge_request, source_project: project) }
......@@ -957,7 +957,7 @@ describe MergeRequest, models: true do
let(:merge_request) { create(:merge_request_with_diff_notes, source_project: project) }
context 'when project.only_allow_merge_if_all_discussions_are_resolved == true' do
let(:project) { create(:project, only_allow_merge_if_all_discussions_are_resolved: true) }
let(:project) { create(:project, :repository, only_allow_merge_if_all_discussions_are_resolved: true) }
context 'with all discussions resolved' do
before do
......@@ -991,7 +991,7 @@ describe MergeRequest, models: true do
end
context 'when project.only_allow_merge_if_all_discussions_are_resolved == false' do
let(:project) { create(:project, only_allow_merge_if_all_discussions_are_resolved: false) }
let(:project) { create(:project, :repository, only_allow_merge_if_all_discussions_are_resolved: false) }
context 'with unresolved discussions' do
before do
......@@ -1006,7 +1006,7 @@ describe MergeRequest, models: true do
end
describe "#environments" do
let(:project) { create(:project) }
let(:project) { create(:project, :repository) }
let(:merge_request) { create(:merge_request, source_project: project) }
context 'with multiple environments' do
......@@ -1024,7 +1024,7 @@ describe MergeRequest, models: true do
context 'with environments on source project' do
let(:source_project) do
create(:project) do |fork_project|
create(:project, :repository) do |fork_project|
fork_project.create_forked_project_link(forked_to_project_id: fork_project.id, forked_from_project_id: project.id)
end
end
......@@ -1401,8 +1401,8 @@ describe MergeRequest, models: true do
end
describe "#source_project_missing?" do
let(:project) { create(:project) }
let(:fork_project) { create(:project, forked_from_project: project) }
let(:project) { create(:empty_project) }
let(:fork_project) { create(:empty_project, forked_from_project: project) }
let(:user) { create(:user) }
let(:unlink_project) { Projects::UnlinkForkService.new(fork_project, user) }
......@@ -1439,8 +1439,8 @@ describe MergeRequest, models: true do
end
describe "#closed_without_fork?" do
let(:project) { create(:project) }
let(:fork_project) { create(:project, forked_from_project: project) }
let(:project) { create(:empty_project) }
let(:fork_project) { create(:empty_project, forked_from_project: project) }
let(:user) { create(:user) }
let(:unlink_project) { Projects::UnlinkForkService.new(fork_project, user) }
......@@ -1485,9 +1485,9 @@ describe MergeRequest, models: true do
end
context 'forked project' do
let(:project) { create(:project) }
let(:project) { create(:empty_project) }
let(:user) { create(:user) }
let(:fork_project) { create(:project, forked_from_project: project, namespace: user.namespace) }
let(:fork_project) { create(:empty_project, forked_from_project: project, namespace: user.namespace) }
let!(:merge_request) do
create(:closed_merge_request,
......@@ -1531,7 +1531,7 @@ describe MergeRequest, models: true do
status: status)
end
let(:project) { create(:project, :public, only_allow_merge_if_build_succeeds: true) }
let(:project) { create(:project, :public, :repository, only_allow_merge_if_build_succeeds: true) }
let(:developer) { create(:user) }
let(:user) { create(:user) }
let(:merge_request) { create(:merge_request, source_project: project) }
......
......@@ -73,9 +73,7 @@ describe Project, models: true do
context 'after initialized' do
it "has a project_feature" do
project = FactoryGirl.build(:project)
expect(project.project_feature.present?).to be_present
expect(Project.new.project_feature).to be_present
end
end
......@@ -129,7 +127,7 @@ describe Project, models: true do
end
describe 'validation' do
let!(:project) { create(:project) }
let!(:project) { create(:empty_project) }
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_uniqueness_of(:name).scoped_to(:namespace_id) }
......@@ -148,7 +146,7 @@ describe Project, models: true do
it { is_expected.to validate_presence_of(:repository_storage) }
it 'does not allow new projects beyond user limits' do
project2 = build(:project)
project2 = build(:empty_project)
allow(project2).to receive(:creator).and_return(double(can_create_project?: false, projects_limit: 0).as_null_object)
expect(project2).not_to be_valid
expect(project2.errors[:limit_reached].first).to match(/Personal project creation is not allowed/)
......@@ -157,7 +155,7 @@ describe Project, models: true do
describe 'wiki path conflict' do
context "when the new path has been used by the wiki of other Project" do
it 'has an error on the name attribute' do
new_project = build_stubbed(:project, namespace_id: project.namespace_id, path: "#{project.path}.wiki")
new_project = build_stubbed(:empty_project, namespace_id: project.namespace_id, path: "#{project.path}.wiki")
expect(new_project).not_to be_valid
expect(new_project.errors[:name].first).to eq('has already been taken')
......@@ -166,8 +164,8 @@ describe Project, models: true do
context "when the new wiki path has been used by the path of other Project" do
it 'has an error on the name attribute' do
project_with_wiki_suffix = create(:project, path: 'foo.wiki')
new_project = build_stubbed(:project, namespace_id: project_with_wiki_suffix.namespace_id, path: 'foo')
project_with_wiki_suffix = create(:empty_project, path: 'foo.wiki')
new_project = build_stubbed(:empty_project, namespace_id: project_with_wiki_suffix.namespace_id, path: 'foo')
expect(new_project).not_to be_valid
expect(new_project.errors[:name].first).to eq('has already been taken')
......@@ -176,7 +174,7 @@ describe Project, models: true do
end
context 'repository storages inclussion' do
let(:project2) { build(:project, repository_storage: 'missing') }
let(:project2) { build(:empty_project, repository_storage: 'missing') }
before do
storages = { 'custom' => 'tmp/tests/custom_repositories' }
......@@ -352,7 +350,7 @@ describe Project, models: true do
end
describe '#repository_storage_path' do
let(:project) { create(:project, repository_storage: 'custom') }
let(:project) { create(:empty_project, repository_storage: 'custom') }
before do
FileUtils.mkdir('tmp/tests/custom_repositories')
......@@ -412,7 +410,7 @@ describe Project, models: true do
describe 'last_activity methods' do
let(:timestamp) { 2.hours.ago }
# last_activity_at gets set to created_at upon creation
let(:project) { create(:project, created_at: timestamp, updated_at: timestamp) }
let(:project) { create(:empty_project, created_at: timestamp, updated_at: timestamp) }
describe 'last_activity' do
it 'alias last_activity to last_event' do
......@@ -496,7 +494,7 @@ describe Project, models: true do
context 'with namespace' do
before do
@group = create :group, name: 'gitlab'
@project = create(:project, name: 'gitlabhq', namespace: @group)
@project = create(:empty_project, name: 'gitlabhq', namespace: @group)
end
it { expect(@project.to_param).to eq('gitlabhq') }
......@@ -522,7 +520,7 @@ describe Project, models: true do
end
describe '#repository' do
let(:project) { create(:project) }
let(:project) { create(:project, :repository) }
it 'returns valid repo' do
expect(project.repository).to be_kind_of(Repository)
......@@ -530,20 +528,22 @@ describe Project, models: true do
end
describe '#default_issues_tracker?' do
let(:project) { create(:project) }
let(:ext_project) { create(:redmine_project) }
it "is true if used internal tracker" do
project = build(:empty_project)
expect(project.default_issues_tracker?).to be_truthy
end
it "is false if used other tracker" do
expect(ext_project.default_issues_tracker?).to be_falsey
# NOTE: The current nature of this factory requires persistence
project = create(:redmine_project)
expect(project.default_issues_tracker?).to be_falsey
end
end
describe '#external_issue_tracker' do
let(:project) { create(:project) }
let(:project) { create(:empty_project) }
let(:ext_project) { create(:redmine_project) }
context 'on existing projects with no value for has_external_issue_tracker' do
......@@ -578,7 +578,7 @@ describe Project, models: true do
end
describe '#cache_has_external_issue_tracker' do
let(:project) { create(:project, has_external_issue_tracker: nil) }
let(:project) { create(:empty_project, has_external_issue_tracker: nil) }
it 'stores true if there is any external_issue_tracker' do
services = double(:service, external_issue_trackers: [RedmineService.new])
......@@ -600,9 +600,9 @@ describe Project, models: true do
end
describe '#has_wiki?' do
let(:no_wiki_project) { create(:project, wiki_access_level: ProjectFeature::DISABLED, has_external_wiki: false) }
let(:wiki_enabled_project) { create(:project) }
let(:external_wiki_project) { create(:project, has_external_wiki: true) }
let(:no_wiki_project) { create(:empty_project, wiki_access_level: ProjectFeature::DISABLED, has_external_wiki: false) }
let(:wiki_enabled_project) { create(:empty_project) }
let(:external_wiki_project) { create(:empty_project, has_external_wiki: true) }
it 'returns true if project is wiki enabled or has external wiki' do
expect(wiki_enabled_project).to have_wiki
......@@ -612,7 +612,7 @@ describe Project, models: true do
end
describe '#external_wiki' do
let(:project) { create(:project) }
let(:project) { create(:empty_project) }
context 'with an active external wiki' do
before do
......@@ -663,7 +663,7 @@ describe Project, models: true do
end
describe '#open_branches' do
let(:project) { create(:project) }
let(:project) { create(:project, :repository) }
before do
project.protected_branches.create(name: 'master')
......@@ -685,7 +685,7 @@ describe Project, models: true do
it 'counts stars from multiple users' do
user1 = create :user
user2 = create :user
project = create :project, :public
project = create(:empty_project, :public)
expect(project.star_count).to eq(0)
......@@ -707,8 +707,8 @@ describe Project, models: true do
it 'counts stars on the right project' do
user = create :user
project1 = create :project, :public
project2 = create :project, :public
project1 = create(:empty_project, :public)
project2 = create(:empty_project, :public)
expect(project1.star_count).to eq(0)
expect(project2.star_count).to eq(0)
......@@ -740,7 +740,7 @@ describe Project, models: true do
end
describe '#avatar_type' do
let(:project) { create(:project) }
let(:project) { create(:empty_project) }
it 'is true if avatar is image' do
project.update_attribute(:avatar, 'uploads/avatar.png')
......@@ -756,7 +756,7 @@ describe Project, models: true do
describe '#avatar_url' do
subject { project.avatar_url }
let(:project) { create(:project) }
let(:project) { create(:empty_project) }
context 'When avatar file is uploaded' do
before do
......@@ -791,7 +791,7 @@ describe Project, models: true do
end
describe '#pipeline_for' do
let(:project) { create(:project) }
let(:project) { create(:project, :repository) }
let!(:pipeline) { create_pipeline }
shared_examples 'giving the correct pipeline' do
......@@ -825,7 +825,7 @@ describe Project, models: true do
end
describe '#builds_enabled' do
let(:project) { create :project }
let(:project) { create(:empty_project) }
subject { project.builds_enabled }
......@@ -877,7 +877,7 @@ describe Project, models: true do
end
describe '.visible_to_user' do
let!(:project) { create(:project, :private) }
let!(:project) { create(:empty_project, :private) }
let!(:user) { create(:user) }
subject { described_class.visible_to_user(user) }
......@@ -975,7 +975,7 @@ describe Project, models: true do
end
describe '#visibility_level_allowed?' do
let(:project) { create(:project, :internal) }
let(:project) { create(:empty_project, :internal) }
context 'when checking on non-forked project' do
it { expect(project.visibility_level_allowed?(Gitlab::VisibilityLevel::PRIVATE)).to be_truthy }
......@@ -984,8 +984,8 @@ describe Project, models: true do
end
context 'when checking on forked project' do
let(:project) { create(:project, :internal) }
let(:forked_project) { create(:project, forked_from_project: project) }
let(:project) { create(:empty_project, :internal) }
let(:forked_project) { create(:empty_project, forked_from_project: project) }
it { expect(forked_project.visibility_level_allowed?(Gitlab::VisibilityLevel::PRIVATE)).to be_truthy }
it { expect(forked_project.visibility_level_allowed?(Gitlab::VisibilityLevel::INTERNAL)).to be_truthy }
......@@ -994,7 +994,7 @@ describe Project, models: true do
end
describe '.search' do
let(:project) { create(:project, description: 'kitten mittens') }
let(:project) { create(:empty_project, description: 'kitten mittens') }
it 'returns projects with a matching name' do
expect(described_class.search(project.name)).to eq([project])
......@@ -1052,7 +1052,7 @@ describe Project, models: true do
end
describe '#rename_repo' do
let(:project) { create(:project) }
let(:project) { create(:project, :repository) }
let(:gitlab_shell) { Gitlab::Shell.new }
before do
......@@ -1102,7 +1102,7 @@ describe Project, models: true do
end
describe '#expire_caches_before_rename' do
let(:project) { create(:project) }
let(:project) { create(:project, :repository) }
let(:repo) { double(:repo, exists?: true) }
let(:wiki) { double(:wiki, exists?: true) }
......@@ -1123,7 +1123,7 @@ describe Project, models: true do
end
describe '.search_by_title' do
let(:project) { create(:project, name: 'kittens') }
let(:project) { create(:empty_project, name: 'kittens') }
it 'returns projects with a matching name' do
expect(described_class.search_by_title(project.name)).to eq([project])
......@@ -1142,8 +1142,8 @@ describe Project, models: true do
let(:private_group) { create(:group, visibility_level: 0) }
let(:internal_group) { create(:group, visibility_level: 10) }
let(:private_project) { create :project, :private, group: private_group }
let(:internal_project) { create :project, :internal, group: internal_group }
let(:private_project) { create :empty_project, :private, group: private_group }
let(:internal_project) { create :empty_project, :internal, group: internal_group }
context 'when group is private project can not be internal' do
it { expect(private_project.visibility_level_allowed?(Gitlab::VisibilityLevel::INTERNAL)).to be_falsey }
......@@ -1155,7 +1155,7 @@ describe Project, models: true do
end
describe '#create_repository' do
let(:project) { create(:project) }
let(:project) { create(:project, :repository) }
let(:shell) { Gitlab::Shell.new }
before do
......@@ -1197,7 +1197,7 @@ describe Project, models: true do
describe '#protected_branch?' do
context 'existing project' do
let(:project) { create(:project) }
let(:project) { create(:project, :repository) }
it 'returns true when the branch matches a protected branch via direct match' do
create(:protected_branch, project: project, name: "foo")
......@@ -1381,7 +1381,7 @@ describe Project, models: true do
name: name)
end
let(:project) { create(:project) }
let(:project) { create(:project, :repository) }
let(:pipeline) { create_pipeline }
context 'with many builds' do
......@@ -1461,7 +1461,7 @@ describe Project, models: true do
end
context 'not forked' do
let(:project) { create(:project) }
let(:project) { create(:empty_project) }
it 'schedules a RepositoryImportWorker job' do
expect(RepositoryImportWorker).to receive(:perform_async).with(project.id)
......@@ -1472,19 +1472,19 @@ describe Project, models: true do
end
describe '#gitlab_project_import?' do
subject(:project) { build(:project, import_type: 'gitlab_project') }
subject(:project) { build(:empty_project, import_type: 'gitlab_project') }
it { expect(project.gitlab_project_import?).to be true }
end
describe '#gitea_import?' do
subject(:project) { build(:project, import_type: 'gitea') }
subject(:project) { build(:empty_project, import_type: 'gitea') }
it { expect(project.gitea_import?).to be true }
end
describe '#lfs_enabled?' do
let(:project) { create(:project) }
let(:project) { create(:empty_project) }
shared_examples 'project overrides group' do
it 'returns true when enabled in project' do
......@@ -1546,7 +1546,7 @@ describe Project, models: true do
end
describe '#change_head' do
let(:project) { create(:project) }
let(:project) { create(:project, :repository) }
it 'calls the before_change_head and after_change_head methods' do
expect(project.repository).to receive(:before_change_head)
......@@ -1574,7 +1574,7 @@ describe Project, models: true do
end
describe '#pushes_since_gc' do
let(:project) { create(:project) }
let(:project) { create(:empty_project) }
after do
project.reset_pushes_since_gc
......@@ -1596,7 +1596,7 @@ describe Project, models: true do
end
describe '#increment_pushes_since_gc' do
let(:project) { create(:project) }
let(:project) { create(:empty_project) }
after do
project.reset_pushes_since_gc
......@@ -1610,7 +1610,7 @@ describe Project, models: true do
end
describe '#reset_pushes_since_gc' do
let(:project) { create(:project) }
let(:project) { create(:empty_project) }
after do
project.reset_pushes_since_gc
......@@ -1626,7 +1626,7 @@ describe Project, models: true do
end
describe '#environments_for' do
let(:project) { create(:project) }
let(:project) { create(:project, :repository) }
let(:environment) { create(:environment, project: project) }
context 'tagged deployment' do
......@@ -1678,7 +1678,7 @@ describe Project, models: true do
end
describe '#environments_recently_updated_on_branch' do
let(:project) { create(:project) }
let(:project) { create(:project, :repository) }
let(:environment) { create(:environment, project: project) }
context 'when last deployment to environment is the most recent one' do
......
......@@ -48,7 +48,7 @@ describe User, models: true do
describe '#project_members' do
it 'does not include project memberships for which user is a requester' do
user = create(:user)
project = create(:project, :public, :access_requestable)
project = create(:empty_project, :public, :access_requestable)
project.request_access(user)
expect(user.project_members).to be_empty
......@@ -386,13 +386,15 @@ describe User, models: true do
describe 'projects' do
before do
@user = create :user
@project = create :project, namespace: @user.namespace
@project_2 = create :project, group: create(:group) # Grant MASTER access to the user
@project_3 = create :project, group: create(:group) # Grant DEVELOPER access to the user
@user = create(:user)
@project_2.team << [@user, :master]
@project_3.team << [@user, :developer]
@project = create(:empty_project, namespace: @user.namespace)
@project_2 = create(:empty_project, group: create(:group)) do |project|
project.add_master(@user)
end
@project_3 = create(:empty_project, group: create(:group)) do |project|
project.add_developer(@user)
end
end
it { expect(@user.authorized_projects).to include(@project) }
......@@ -435,7 +437,7 @@ describe User, models: true do
describe 'namespaced' do
before do
@user = create :user
@project = create :project, namespace: @user.namespace
@project = create(:empty_project, namespace: @user.namespace)
end
it { expect(@user.several_namespaces?).to be_falsey }
......@@ -517,7 +519,7 @@ describe User, models: true do
before do
User.delete_all
@user = create :user
@project = create :project
@project = create(:empty_project)
end
it { expect(User.not_in_project(@project)).to include(@user, @project.owner) }
......@@ -927,8 +929,8 @@ describe User, models: true do
describe "#starred?" do
it "determines if user starred a project" do
user = create :user
project1 = create :project, :public
project2 = create :project, :public
project1 = create(:empty_project, :public)
project2 = create(:empty_project, :public)
expect(user.starred?(project1)).to be_falsey
expect(user.starred?(project2)).to be_falsey
......@@ -954,7 +956,7 @@ describe User, models: true do
describe "#toggle_star" do
it "toggles stars" do
user = create :user
project = create :project, :public
project = create(:empty_project, :public)
expect(user.starred?(project)).to be_falsey
user.toggle_star(project)
......@@ -994,9 +996,9 @@ describe User, models: true do
describe "#contributed_projects" do
subject { create(:user) }
let!(:project1) { create(:project) }
let!(:project2) { create(:project, forked_from_project: project3) }
let!(:project3) { create(:project) }
let!(:project1) { create(:empty_project) }
let!(:project2) { create(:empty_project, forked_from_project: project3) }
let!(:project3) { create(:empty_project) }
let!(:merge_request) { create(:merge_request, source_project: project2, target_project: project3, author: subject) }
let!(:push_event) { create(:event, action: Event::PUSHED, project: project1, target: project1, author: subject) }
let!(:merge_event) { create(:event, action: Event::CREATED, project: project3, target: merge_request, author: subject) }
......@@ -1038,8 +1040,8 @@ describe User, models: true do
describe "#recent_push" do
subject { create(:user) }
let!(:project1) { create(:project) }
let!(:project2) { create(:project, forked_from_project: project1) }
let!(:project1) { create(:project, :repository) }
let!(:project2) { create(:project, :repository, forked_from_project: project1) }
let!(:push_data) do
Gitlab::DataBuilder::Push.build_sample(project2, subject)
end
......@@ -1113,7 +1115,7 @@ describe User, models: true do
it "includes user's personal projects" do
user = create(:user)
project = create(:project, :private, namespace: user.namespace)
project = create(:empty_project, :private, namespace: user.namespace)
expect(user.authorized_projects).to include(project)
end
......@@ -1121,7 +1123,7 @@ describe User, models: true do
it "includes personal projects user has been given access to" do
user1 = create(:user)
user2 = create(:user)
project = create(:project, :private, namespace: user1.namespace)
project = create(:empty_project, :private, namespace: user1.namespace)
project.team << [user2, Gitlab::Access::DEVELOPER]
......@@ -1130,7 +1132,7 @@ describe User, models: true do
it "includes projects of groups user has been added to" do
group = create(:group)
project = create(:project, group: group)
project = create(:empty_project, group: group)
user = create(:user)
group.add_developer(user)
......@@ -1140,7 +1142,7 @@ describe User, models: true do
it "does not include projects of groups user has been removed from" do
group = create(:group)
project = create(:project, group: group)
project = create(:empty_project, group: group)
user = create(:user)
member = group.add_developer(user)
......@@ -1152,7 +1154,7 @@ describe User, models: true do
it "includes projects shared with user's group" do
user = create(:user)
project = create(:project, :private)
project = create(:empty_project, :private)
group = create(:group)
group.add_reporter(user)
......@@ -1164,7 +1166,7 @@ describe User, models: true do
it "does not include destroyed projects user had access to" do
user1 = create(:user)
user2 = create(:user)
project = create(:project, :private, namespace: user1.namespace)
project = create(:empty_project, :private, namespace: user1.namespace)
project.team << [user2, Gitlab::Access::DEVELOPER]
expect(user2.authorized_projects).to include(project)
......@@ -1175,7 +1177,7 @@ describe User, models: true do
it "does not include projects of destroyed groups user had access to" do
group = create(:group)
project = create(:project, namespace: group)
project = create(:empty_project, namespace: group)
user = create(:user)
group.add_developer(user)
......@@ -1190,14 +1192,9 @@ describe User, models: true do
let(:user) { create(:user) }
it 'includes projects for which the user access level is above or equal to reporter' do
create(:project)
reporter_project = create(:project)
developer_project = create(:project)
master_project = create(:project)
reporter_project.team << [user, :reporter]
developer_project.team << [user, :developer]
master_project.team << [user, :master]
reporter_project = create(:empty_project) { |p| p.add_reporter(user) }
developer_project = create(:empty_project) { |p| p.add_developer(user) }
master_project = create(:empty_project) { |p| p.add_master(user) }
expect(user.projects_where_can_admin_issues.to_a).to eq([master_project, developer_project, reporter_project])
expect(user.can?(:admin_issue, master_project)).to eq(true)
......@@ -1206,10 +1203,8 @@ describe User, models: true do
end
it 'does not include for which the user access level is below reporter' do
project = create(:project)
guest_project = create(:project)
guest_project.team << [user, :guest]
project = create(:empty_project)
guest_project = create(:empty_project) { |p| p.add_guest(user) }
expect(user.projects_where_can_admin_issues.to_a).to be_empty
expect(user.can?(:admin_issue, guest_project)).to eq(false)
......@@ -1217,15 +1212,14 @@ describe User, models: true do
end
it 'does not include archived projects' do
project = create(:project)
project.update_attributes(archived: true)
project = create(:empty_project, :archived)
expect(user.projects_where_can_admin_issues.to_a).to be_empty
expect(user.can?(:admin_issue, project)).to eq(false)
end
it 'does not include projects for which issues are disabled' do
project = create(:project, issues_access_level: ProjectFeature::DISABLED)
project = create(:empty_project, issues_access_level: ProjectFeature::DISABLED)
expect(user.projects_where_can_admin_issues.to_a).to be_empty
expect(user.can?(:admin_issue, project)).to eq(false)
......@@ -1241,7 +1235,7 @@ describe User, models: true do
end
context 'without any projects' do
let(:project) { create(:project) }
let(:project) { create(:empty_project) }
it 'does not load' do
expect(user.ci_authorized_runners).to be_empty
......@@ -1250,7 +1244,7 @@ describe User, models: true do
context 'with personal projects runners' do
let(:namespace) { create(:namespace, owner: user) }
let(:project) { create(:project, namespace: namespace) }
let(:project) { create(:empty_project, namespace: namespace) }
it 'loads' do
expect(user.ci_authorized_runners).to contain_exactly(runner)
......@@ -1281,7 +1275,7 @@ describe User, models: true do
context 'with groups projects runners' do
let(:group) { create(:group) }
let(:project) { create(:project, group: group) }
let(:project) { create(:empty_project, group: group) }
def add_user(access)
group.add_user(user, access)
......@@ -1291,7 +1285,7 @@ describe User, models: true do
end
context 'with other projects runners' do
let(:project) { create(:project) }
let(:project) { create(:empty_project) }
def add_user(access)
project.team << [user, access]
......@@ -1321,8 +1315,8 @@ describe User, models: true do
end
describe '#projects_with_reporter_access_limited_to' do
let(:project1) { create(:project) }
let(:project2) { create(:project) }
let(:project1) { create(:empty_project) }
let(:project2) { create(:empty_project) }
let(:user) { create(:user) }
before do
......
......@@ -9,7 +9,7 @@ describe API::AccessRequests, api: true do
let(:stranger) { create(:user) }
let(:project) do
create(:project, :public, :access_requestable, creator_id: master.id, namespace: master.namespace) do |project|
create(:empty_project, :public, :access_requestable, creator_id: master.id, namespace: master.namespace) do |project|
project.team << [developer, :developer]
project.team << [master, :master]
project.request_access(access_requester)
......
......@@ -8,7 +8,7 @@ describe API::Boards, api: true do
let(:non_member) { create(:user) }
let(:guest) { create(:user) }
let(:admin) { create(:user, :admin) }
let!(:project) { create(:project, :public, creator_id: user.id, namespace: user.namespace ) }
let!(:project) { create(:empty_project, :public, creator_id: user.id, namespace: user.namespace ) }
let!(:dev_label) do
create(:label, title: 'Development', color: '#FFAABB', project: project)
......@@ -188,7 +188,7 @@ describe API::Boards, api: true do
context "when the user is project owner" do
let(:owner) { create(:user) }
let(:project) { create(:project, namespace: owner.namespace) }
let(:project) { create(:empty_project, namespace: owner.namespace) }
it "deletes the list if an admin requests it" do
delete api("#{base_url}/#{dev_list.id}", owner)
......
......@@ -5,8 +5,8 @@ describe API::DeployKeys, api: true do
let(:user) { create(:user) }
let(:admin) { create(:admin) }
let(:project) { create(:project, creator_id: user.id) }
let(:project2) { create(:project, creator_id: user.id) }
let(:project) { create(:empty_project, creator_id: user.id) }
let(:project2) { create(:empty_project, creator_id: user.id) }
let(:deploy_key) { create(:deploy_key, public: true) }
let!(:deploy_keys_project) do
......
......@@ -5,7 +5,7 @@ describe API::Environments, api: true do
let(:user) { create(:user) }
let(:non_member) { create(:user) }
let(:project) { create(:project, :private, namespace: user.namespace) }
let(:project) { create(:empty_project, :private, namespace: user.namespace) }
let!(:environment) { create(:environment, project: project) }
before do
......
require 'spec_helper'
describe API::API, api: true do
describe API::Projects, api: true do
include ApiHelpers
let(:user) { create(:user) }
let(:user2) { create(:user) }
......
......@@ -10,9 +10,9 @@ describe API::Groups, api: true do
let(:admin) { create(:admin) }
let!(:group1) { create(:group, avatar: File.open(uploaded_image_temp_path)) }
let!(:group2) { create(:group, :private) }
let!(:project1) { create(:project, namespace: group1) }
let!(:project2) { create(:project, namespace: group2) }
let!(:project3) { create(:project, namespace: group1, path: 'test', visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
let!(:project1) { create(:empty_project, namespace: group1) }
let!(:project2) { create(:empty_project, namespace: group2) }
let!(:project3) { create(:empty_project, namespace: group1, path: 'test', visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
before do
group1.add_owner(user1)
......@@ -163,7 +163,7 @@ describe API::Groups, api: true do
describe "GET /groups/:id" do
context "when authenticated as user" do
it "returns one of user1's groups" do
project = create(:project, namespace: group2, path: 'Foo')
project = create(:empty_project, namespace: group2, path: 'Foo')
create(:project_group_link, project: project, group: group1)
get api("/groups/#{group1.id}", user1)
......@@ -287,7 +287,7 @@ describe API::Groups, api: true do
expect(json_response.length).to eq(2)
project_names = json_response.map { |proj| proj['name' ] }
expect(project_names).to match_array([project1.name, project3.name])
expect(json_response.first['default_branch']).to be_present
expect(json_response.first['visibility_level']).to be_present
end
it "returns the group's projects with simple representation" do
......@@ -297,11 +297,11 @@ describe API::Groups, api: true do
expect(json_response.length).to eq(2)
project_names = json_response.map { |proj| proj['name' ] }
expect(project_names).to match_array([project1.name, project3.name])
expect(json_response.first['default_branch']).not_to be_present
expect(json_response.first['visibility_level']).not_to be_present
end
it 'filters the groups projects' do
public_project = create(:project, :public, path: 'test1', group: group1)
public_project = create(:empty_project, :public, path: 'test1', group: group1)
get api("/groups/#{group1.id}/projects", user1), visibility: 'public'
......@@ -462,7 +462,7 @@ describe API::Groups, api: true do
end
describe "POST /groups/:id/projects/:project_id" do
let(:project) { create(:project) }
let(:project) { create(:empty_project) }
let(:project_path) { "#{project.namespace.path}%2F#{project.path}" }
before(:each) do
......
......@@ -12,6 +12,7 @@ describe API::Helpers, api: true do
let(:params) { {} }
let(:env) { { 'REQUEST_METHOD' => 'GET' } }
let(:request) { Rack::Request.new(env) }
let(:header) { }
def set_env(user_or_token, identifier)
clear_env
......@@ -46,7 +47,7 @@ describe API::Helpers, api: true do
allow_any_instance_of(self.class).to receive(:doorkeeper_guard){ value }
end
def error!(message, status)
def error!(message, status, header)
raise Exception.new("#{status} - #{message}")
end
......
......@@ -239,7 +239,7 @@ describe API::Internal, api: true do
end
context "blocked user" do
let(:personal_project) { create(:project, namespace: user.namespace) }
let(:personal_project) { create(:empty_project, namespace: user.namespace) }
before do
user.block
......@@ -265,7 +265,7 @@ describe API::Internal, api: true do
end
context "archived project" do
let(:personal_project) { create(:project, namespace: user.namespace) }
let(:personal_project) { create(:empty_project, namespace: user.namespace) }
before do
project.team << [user, :developer]
......@@ -337,8 +337,7 @@ describe API::Internal, api: true do
context 'ssh access has been disabled' do
before do
settings = ::ApplicationSetting.create_from_defaults
settings.update_attribute(:enabled_git_access_protocol, 'http')
stub_application_setting(enabled_git_access_protocol: 'http')
end
it 'rejects the SSH push' do
......@@ -360,8 +359,7 @@ describe API::Internal, api: true do
context 'http access has been disabled' do
before do
settings = ::ApplicationSetting.create_from_defaults
settings.update_attribute(:enabled_git_access_protocol, 'ssh')
stub_application_setting(enabled_git_access_protocol: 'ssh')
end
it 'rejects the HTTP push' do
......@@ -383,8 +381,7 @@ describe API::Internal, api: true do
context 'web actions are always allowed' do
it 'allows WEB push' do
settings = ::ApplicationSetting.create_from_defaults
settings.update_attribute(:enabled_git_access_protocol, 'ssh')
stub_application_setting(enabled_git_access_protocol: 'ssh')
project.team << [user, :developer]
push(key, project, 'web')
......
......@@ -11,7 +11,7 @@ describe API::Issues, api: true do
let(:author) { create(:author) }
let(:assignee) { create(:assignee) }
let(:admin) { create(:user, :admin) }
let!(:project) { create(:project, :public, creator_id: user.id, namespace: user.namespace ) }
let!(:project) { create(:empty_project, :public, creator_id: user.id, namespace: user.namespace ) }
let!(:closed_issue) do
create :closed_issue,
author: user,
......@@ -224,7 +224,7 @@ describe API::Issues, api: true do
describe "GET /groups/:id/issues" do
let!(:group) { create(:group) }
let!(:group_project) { create(:project, :public, creator_id: user.id, namespace: group) }
let!(:group_project) { create(:empty_project, :public, creator_id: user.id, namespace: group) }
let!(:group_closed_issue) do
create :closed_issue,
author: user,
......@@ -1052,7 +1052,7 @@ describe API::Issues, api: true do
context "when the user is project owner" do
let(:owner) { create(:user) }
let(:project) { create(:project, namespace: owner.namespace) }
let(:project) { create(:empty_project, namespace: owner.namespace) }
it "deletes the issue if an admin requests it" do
delete api("/projects/#{project.id}/issues/#{issue.id}", owner)
......@@ -1071,8 +1071,8 @@ describe API::Issues, api: true do
end
describe '/projects/:id/issues/:issue_id/move' do
let!(:target_project) { create(:project, path: 'project2', creator_id: user.id, namespace: user.namespace ) }
let!(:target_project2) { create(:project, creator_id: non_member.id, namespace: non_member.namespace ) }
let!(:target_project) { create(:empty_project, path: 'project2', creator_id: user.id, namespace: user.namespace ) }
let!(:target_project2) { create(:empty_project, creator_id: non_member.id, namespace: non_member.namespace ) }
it 'moves an issue' do
post api("/projects/#{project.id}/issues/#{issue.id}/move", user),
......
......@@ -4,7 +4,7 @@ describe API::Labels, api: true do
include ApiHelpers
let(:user) { create(:user) }
let(:project) { create(:project, creator_id: user.id, namespace: user.namespace) }
let(:project) { create(:empty_project, creator_id: user.id, namespace: user.namespace) }
let!(:label1) { create(:label, title: 'label1', project: project) }
let!(:priority_label) { create(:label, title: 'bug', project: project, priority: 3) }
......
......@@ -9,7 +9,7 @@ describe API::Members, api: true do
let(:stranger) { create(:user) }
let(:project) do
create(:project, :public, :access_requestable, creator_id: master.id, namespace: master.namespace) do |project|
create(:empty_project, :public, :access_requestable, creator_id: master.id, namespace: master.namespace) do |project|
project.team << [developer, :developer]
project.team << [master, :master]
project.request_access(access_requester)
......
......@@ -308,8 +308,8 @@ describe API::MergeRequests, api: true do
context 'forked projects' do
let!(:user2) { create(:user) }
let!(:fork_project) { create(:project, forked_from_project: project, namespace: user2.namespace, creator_id: user2.id) }
let!(:unrelated_project) { create(:project, namespace: create(:user).namespace, creator_id: user2.id) }
let!(:fork_project) { create(:empty_project, forked_from_project: project, namespace: user2.namespace, creator_id: user2.id) }
let!(:unrelated_project) { create(:empty_project, namespace: create(:user).namespace, creator_id: user2.id) }
before :each do |each|
fork_project.team << [user2, :reporter]
......
......@@ -3,7 +3,7 @@ require 'spec_helper'
describe API::Notes, api: true do
include ApiHelpers
let(:user) { create(:user) }
let!(:project) { create(:project, :public, namespace: user.namespace) }
let!(:project) { create(:empty_project, :public, namespace: user.namespace) }
let!(:issue) { create(:issue, project: project, author: user) }
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: user) }
let!(:snippet) { create(:project_snippet, project: project, author: user) }
......@@ -14,12 +14,12 @@ describe API::Notes, api: true do
# For testing the cross-reference of a private issue in a public issue
let(:private_user) { create(:user) }
let(:private_project) do
create(:project, namespace: private_user.namespace).
create(:empty_project, namespace: private_user.namespace).
tap { |p| p.team << [private_user, :master] }
end
let(:private_issue) { create(:issue, project: private_project) }
let(:ext_proj) { create(:project, :public) }
let(:ext_proj) { create(:empty_project, :public) }
let(:ext_issue) { create(:issue, project: ext_proj) }
let!(:cross_reference_note) do
......@@ -265,7 +265,7 @@ describe API::Notes, api: true do
end
context 'when user does not have access to create noteable' do
let(:private_issue) { create(:issue, project: create(:project, :private)) }
let(:private_issue) { create(:issue, project: create(:empty_project, :private)) }
##
# We are posting to project user has access to, but we use issue id
......
......@@ -5,7 +5,7 @@ describe API::NotificationSettings, api: true do
let(:user) { create(:user) }
let!(:group) { create(:group) }
let!(:project) { create(:project, :public, creator_id: user.id, namespace: group) }
let!(:project) { create(:empty_project, :public, creator_id: user.id, namespace: group) }
describe "GET /notification_settings" do
it "returns global notification settings for the current user" do
......
......@@ -4,7 +4,7 @@ describe API::ProjectHooks, 'ProjectHooks', api: true do
include ApiHelpers
let(:user) { create(:user) }
let(:user3) { create(:user) }
let!(:project) { create(:project, creator_id: user.id, namespace: user.namespace) }
let!(:project) { create(:empty_project, creator_id: user.id, namespace: user.namespace) }
let!(:hook) do
create(:project_hook,
:all_events_enabled,
......@@ -204,7 +204,7 @@ describe API::ProjectHooks, 'ProjectHooks', api: true do
it "returns a 404 if a user attempts to delete project hooks he/she does not own" do
test_user = create(:user)
other_project = create(:project)
other_project = create(:empty_project)
other_project.team << [test_user, :master]
delete api("/projects/#{other_project.id}/hooks/#{hook.id}", test_user)
......
......@@ -8,8 +8,8 @@ describe API::Projects, api: true do
let(:user2) { create(:user) }
let(:user3) { create(:user) }
let(:admin) { create(:admin) }
let(:project) { create(:project, creator_id: user.id, namespace: user.namespace) }
let(:project2) { create(:project, path: 'project2', creator_id: user.id, namespace: user.namespace) }
let(:project) { create(:empty_project, creator_id: user.id, namespace: user.namespace) }
let(:project2) { create(:empty_project, path: 'project2', creator_id: user.id, namespace: user.namespace) }
let(:snippet) { create(:project_snippet, :public, author: user, project: project, title: 'example') }
let(:project_member) { create(:project_member, :master, user: user, project: project) }
let(:project_member2) { create(:project_member, :developer, user: user3, project: project) }
......@@ -32,7 +32,7 @@ describe API::Projects, api: true do
access_level: ProjectMember::MASTER)
end
let(:project4) do
create(:project,
create(:empty_project,
name: 'third_project',
path: 'third_project',
creator_id: user4.id,
......@@ -252,7 +252,7 @@ describe API::Projects, api: true do
end
end
let!(:public_project) { create(:project, :public) }
let!(:public_project) { create(:empty_project, :public) }
before do
project
project2
......@@ -283,7 +283,7 @@ describe API::Projects, api: true do
end
describe 'GET /projects/starred' do
let(:public_project) { create(:project, :public) }
let(:public_project) { create(:empty_project, :public) }
before do
project_member2
......@@ -583,7 +583,7 @@ describe API::Projects, api: true do
describe 'GET /projects/:id' do
context 'when unauthenticated' do
it 'returns the public projects' do
public_project = create(:project, :public)
public_project = create(:empty_project, :public)
get api("/projects/#{public_project.id}")
......@@ -665,7 +665,7 @@ describe API::Projects, api: true do
it 'handles users with dots' do
dot_user = create(:user, username: 'dot.user')
project = create(:project, creator_id: dot_user.id, namespace: dot_user.namespace)
project = create(:empty_project, creator_id: dot_user.id, namespace: dot_user.namespace)
get api("/projects/#{dot_user.namespace.name}%2F#{project.path}", dot_user)
expect(response).to have_http_status(200)
......@@ -711,7 +711,7 @@ describe API::Projects, api: true do
end
context 'group project' do
let(:project2) { create(:project, group: create(:group)) }
let(:project2) { create(:empty_project, group: create(:group)) }
before { project2.group.add_owner(user) }
......@@ -756,7 +756,7 @@ describe API::Projects, api: true do
context 'when unauthenticated' do
it_behaves_like 'project events response' do
let(:project) { create(:project, :public) }
let(:project) { create(:empty_project, :public) }
let(:current_user) { nil }
end
end
......@@ -807,7 +807,7 @@ describe API::Projects, api: true do
context 'when unauthenticated' do
it_behaves_like 'project users response' do
let(:project) { create(:project, :public) }
let(:project) { create(:empty_project, :public) }
let(:current_user) { nil }
end
end
......@@ -921,11 +921,11 @@ describe API::Projects, api: true do
end
describe :fork_admin do
let(:project_fork_target) { create(:project) }
let(:project_fork_source) { create(:project, :public) }
let(:project_fork_target) { create(:empty_project) }
let(:project_fork_source) { create(:empty_project, :public) }
describe 'POST /projects/:id/fork/:forked_from_id' do
let(:new_project_fork_source) { create(:project, :public) }
let(:new_project_fork_source) { create(:empty_project, :public) }
it "is not available for non admin users" do
post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user)
......@@ -966,7 +966,7 @@ describe API::Projects, api: true do
end
context 'when users belong to project group' do
let(:project_fork_target) { create(:project, group: create(:group)) }
let(:project_fork_target) { create(:empty_project, group: create(:group)) }
before do
project_fork_target.group.add_owner user
......@@ -1121,7 +1121,6 @@ describe API::Projects, api: true do
it_behaves_like 'project search response', query: 'one.dot.two', results: 1 do
let(:current_user) { user }
end
end
context 'when authenticated as a different user' do
......
......@@ -7,8 +7,8 @@ describe API::Runners, api: true do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:project) { create(:project, creator_id: user.id) }
let(:project2) { create(:project, creator_id: user.id) }
let(:project) { create(:empty_project, creator_id: user.id) }
let(:project2) { create(:empty_project, creator_id: user.id) }
let!(:shared_runner) { create(:ci_runner, :shared) }
let!(:unused_specific_runner) { create(:ci_runner) }
......
......@@ -3,8 +3,8 @@ require 'spec_helper'
describe API::Todos, api: true do
include ApiHelpers
let(:project_1) { create(:project) }
let(:project_2) { create(:project) }
let(:project_1) { create(:empty_project) }
let(:project_2) { create(:empty_project) }
let(:author_1) { create(:user) }
let(:author_2) { create(:user) }
let(:john_doe) { create(:user, username: 'john_doe') }
......
......@@ -15,7 +15,7 @@ describe API::Triggers do
let!(:trigger_request) { create(:ci_trigger_request, trigger: trigger, created_at: '2015-01-01 12:13:14') }
describe 'POST /projects/:project_id/trigger' do
let!(:project2) { create(:empty_project) }
let!(:project2) { create(:project) }
let(:options) do
{
token: trigger_token
......
......@@ -5,7 +5,7 @@ describe API::Variables, api: true do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let!(:project) { create(:project, creator_id: user.id) }
let!(:project) { create(:empty_project, creator_id: user.id) }
let!(:master) { create(:project_member, :master, user: user, project: project) }
let!(:developer) { create(:project_member, :developer, user: user2, project: project) }
let!(:variable) { create(:ci_variable, project: project) }
......
......@@ -4,7 +4,8 @@ describe Ci::API::Builds do
include ApiHelpers
let(:runner) { FactoryGirl.create(:ci_runner, tag_list: ["mysql", "ruby"]) }
let(:project) { FactoryGirl.create(:empty_project) }
let(:project) { FactoryGirl.create(:empty_project, shared_runners_enabled: false) }
let(:last_update) { nil }
describe "Builds API for runners" do
let(:pipeline) { create(:ci_pipeline_without_jobs, project: project, ref: 'master') }
......@@ -16,6 +17,8 @@ describe Ci::API::Builds do
describe "POST /builds/register" do
let!(:build) { create(:ci_build, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) }
let(:user_agent) { 'gitlab-ci-multi-runner 1.5.2 (1-5-stable; go1.6.3; linux/amd64)' }
let!(:last_update) { }
let!(:new_update) { }
before do
stub_container_registry_config(enabled: false)
......@@ -24,7 +27,31 @@ describe Ci::API::Builds do
shared_examples 'no builds available' do
context 'when runner sends version in User-Agent' do
context 'for stable version' do
it { expect(response).to have_http_status(204) }
it 'gives 204 and set X-GitLab-Last-Update' do
expect(response).to have_http_status(204)
expect(response.header).to have_key('X-GitLab-Last-Update')
end
end
context 'when last_update is up-to-date' do
let(:last_update) { runner.ensure_runner_queue_value }
it 'gives 204 and set the same X-GitLab-Last-Update' do
expect(response).to have_http_status(204)
expect(response.header['X-GitLab-Last-Update'])
.to eq(last_update)
end
end
context 'when last_update is outdated' do
let(:last_update) { runner.ensure_runner_queue_value }
let(:new_update) { runner.tick_runner_queue }
it 'gives 204 and set a new X-GitLab-Last-Update' do
expect(response).to have_http_status(204)
expect(response.header['X-GitLab-Last-Update'])
.to eq(new_update)
end
end
context 'for beta version' do
......@@ -49,6 +76,7 @@ describe Ci::API::Builds do
register_builds info: { platform: :darwin }
expect(response).to have_http_status(201)
expect(response.headers).not_to have_key('X-GitLab-Last-Update')
expect(json_response['sha']).to eq(build.sha)
expect(runner.reload.platform).to eq("darwin")
expect(json_response["options"]).to eq({ "image" => "ruby:2.1", "services" => ["postgres"] })
......@@ -119,10 +147,10 @@ describe Ci::API::Builds do
end
context 'for shared runner' do
let(:shared_runner) { create(:ci_runner, token: "SharedRunner") }
let!(:runner) { create(:ci_runner, :shared, token: "SharedRunner") }
before do
register_builds shared_runner.token
register_builds(runner.token)
end
it_behaves_like 'no builds available'
......@@ -224,7 +252,9 @@ describe Ci::API::Builds do
end
def register_builds(token = runner.token, **params)
post ci_api("/builds/register"), params.merge(token: token), { 'User-Agent' => user_agent }
new_params = params.merge(token: token, last_update: last_update)
post ci_api("/builds/register"), new_params, { 'User-Agent' => user_agent }
end
end
......
require 'spec_helper'
describe Ci::UpdateBuildQueueService, :services do
let(:project) { create(:project) }
let(:build) { create(:ci_build, pipeline: pipeline) }
let(:pipeline) { create(:ci_pipeline, project: project) }
context 'when updating specific runners' do
let(:runner) { create(:ci_runner) }
context 'when there are runner that can pick build' do
before { build.project.runners << runner }
it 'ticks runner queue value' do
expect { subject.execute(build) }
.to change { runner.ensure_runner_queue_value }
end
end
context 'when there are no runners that can pick build' do
it 'does not tick runner queue value' do
expect { subject.execute(build) }
.not_to change { runner.ensure_runner_queue_value }
end
end
end
context 'when updating shared runners' do
let(:runner) { create(:ci_runner, :shared) }
context 'when there are runner that can pick build' do
it 'ticks runner queue value' do
expect { subject.execute(build) }
.to change { runner.ensure_runner_queue_value }
end
end
context 'when there are no runners that can pick build' do
before { build.tag_list = [:docker] }
it 'does not tick runner queue value' do
expect { subject.execute(build) }
.not_to change { runner.ensure_runner_queue_value }
end
end
end
end
......@@ -2,6 +2,7 @@ require './spec/simplecov_env'
SimpleCovEnv.start!
ENV["RAILS_ENV"] ||= 'test'
ENV["IN_MEMORY_APPLICATION_SETTINGS"] = 'true'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment