2019-04-15 06:17:05 -04:00
# frozen_string_literal: true
2017-09-13 11:16:30 -04:00
require 'spec_helper'
2016-01-23 19:08:15 -05:00
2020-09-24 08:09:37 -04:00
RSpec . describe GroupsController , factory_default : :keep do
2019-04-09 11:38:58 -04:00
include ExternalAuthorizationServiceHelpers
2021-03-09 13:09:41 -05:00
include AdminModeHelper
2019-04-09 11:38:58 -04:00
2020-09-24 08:09:37 -04:00
let_it_be_with_refind ( :group ) { create_default ( :group , :public ) }
let_it_be_with_refind ( :project ) { create ( :project , namespace : group ) }
let_it_be ( :user ) { create ( :user ) }
2021-03-09 13:09:41 -05:00
let_it_be ( :admin_with_admin_mode ) { create ( :admin ) }
let_it_be ( :admin_without_admin_mode ) { create ( :admin ) }
2020-09-24 08:09:37 -04:00
let_it_be ( :group_member ) { create ( :group_member , group : group , user : user ) }
let_it_be ( :owner ) { group . add_owner ( create ( :user ) ) . user }
let_it_be ( :maintainer ) { group . add_maintainer ( create ( :user ) ) . user }
let_it_be ( :developer ) { group . add_developer ( create ( :user ) ) . user }
let_it_be ( :guest ) { group . add_guest ( create ( :user ) ) . user }
2017-09-07 14:35:45 -04:00
2021-03-09 13:09:41 -05:00
before do
enable_admin_mode! ( admin_with_admin_mode )
end
2017-09-07 14:35:45 -04:00
shared_examples 'member with ability to create subgroups' do
it 'renders the new page' do
sign_in ( member )
2018-12-17 17:52:17 -05:00
get :new , params : { parent_id : group . id }
2017-09-07 14:35:45 -04:00
expect ( response ) . to render_template ( :new )
end
end
shared_examples 'member without ability to create subgroups' do
it 'renders the 404 page' do
sign_in ( member )
2018-12-17 17:52:17 -05:00
get :new , params : { parent_id : group . id }
2017-09-07 14:35:45 -04:00
expect ( response ) . not_to render_template ( :new )
2020-03-31 17:08:05 -04:00
expect ( response ) . to have_gitlab_http_status ( :not_found )
2017-09-07 14:35:45 -04:00
end
end
2019-03-14 08:55:46 -04:00
shared_examples 'details view' do
2020-06-10 20:08:35 -04:00
let ( :namespace ) { group }
2019-03-14 08:55:46 -04:00
it { is_expected . to render_template ( 'groups/show' ) }
context 'as atom' do
let! ( :event ) { create ( :event , project : project ) }
let ( :format ) { :atom }
it { is_expected . to render_template ( 'groups/show' ) }
2019-10-23 05:06:03 -04:00
it 'assigns events for all the projects in the group' , :sidekiq_might_not_need_inline do
2019-03-14 08:55:46 -04:00
subject
2020-01-30 16:08:47 -05:00
expect ( assigns ( :events ) . map ( & :id ) ) . to contain_exactly ( event . id )
2019-03-14 08:55:46 -04:00
end
end
end
2017-10-20 09:39:15 -04:00
describe 'GET #show' do
before do
sign_in ( user )
end
2019-03-14 08:55:46 -04:00
let ( :format ) { :html }
2017-10-20 09:39:15 -04:00
2019-03-14 08:55:46 -04:00
subject { get :show , params : { id : group . to_param } , format : format }
2017-10-20 09:39:15 -04:00
2020-06-14 20:08:43 -04:00
context 'when the group is not importing' do
it_behaves_like 'details view'
end
context 'when the group is importing' do
before do
create ( :group_import_state , group : group )
end
it 'redirects to the import status page' do
expect ( subject ) . to redirect_to group_import_path ( group )
end
end
2019-03-14 08:55:46 -04:00
end
describe 'GET #details' do
before do
sign_in ( user )
2017-10-20 09:39:15 -04:00
end
2019-03-14 08:55:46 -04:00
let ( :format ) { :html }
subject { get :details , params : { id : group . to_param } , format : format }
it_behaves_like 'details view'
2017-10-20 09:39:15 -04:00
end
2018-09-03 09:16:23 -04:00
describe 'GET edit' do
it 'sets the badge API endpoint' do
sign_in ( owner )
2018-12-17 17:52:17 -05:00
get :edit , params : { id : group . to_param }
2018-09-03 09:16:23 -04:00
expect ( assigns ( :badge_api_endpoint ) ) . not_to be_nil
end
end
2017-09-07 14:35:45 -04:00
describe 'GET #new' do
2019-07-24 05:20:54 -04:00
context 'when creating subgroups' do
2017-09-07 14:35:45 -04:00
[ true , false ] . each do | can_create_group_status |
context " and can_create_group is #{ can_create_group_status } " do
before do
2021-03-09 13:09:41 -05:00
User . where ( id : [ admin_with_admin_mode , admin_without_admin_mode , owner , maintainer , developer , guest ] ) . update_all ( can_create_group : can_create_group_status )
2017-09-07 14:35:45 -04:00
end
2021-03-09 13:09:41 -05:00
[ :admin_with_admin_mode , :owner , :maintainer ] . each do | member_type |
2017-09-07 14:35:45 -04:00
context " and logged in as #{ member_type . capitalize } " do
it_behaves_like 'member with ability to create subgroups' do
let ( :member ) { send ( member_type ) }
end
end
end
2021-03-09 13:09:41 -05:00
[ :guest , :developer , :admin_without_admin_mode ] . each do | member_type |
2017-09-07 14:35:45 -04:00
context " and logged in as #{ member_type . capitalize } " do
it_behaves_like 'member without ability to create subgroups' do
let ( :member ) { send ( member_type ) }
end
end
end
end
end
end
end
2018-01-30 18:45:23 -05:00
describe 'GET #activity' do
context 'as json' do
2020-01-30 16:08:47 -05:00
before do
sign_in ( user )
end
2019-10-23 05:06:03 -04:00
it 'includes events from all projects in group and subgroups' , :sidekiq_might_not_need_inline do
2019-07-01 07:17:47 -04:00
2 . times do
2018-01-30 18:45:23 -05:00
project = create ( :project , group : group )
create ( :event , project : project )
end
2019-07-01 07:17:47 -04:00
subgroup = create ( :group , parent : group )
project = create ( :project , group : subgroup )
create ( :event , project : project )
2018-01-30 18:45:23 -05:00
2018-12-17 17:52:17 -05:00
get :activity , params : { id : group . to_param } , format : :json
2018-01-30 18:45:23 -05:00
2020-01-27 07:08:35 -05:00
expect ( response ) . to have_gitlab_http_status ( :ok )
2018-01-30 18:45:23 -05:00
expect ( json_response [ 'count' ] ) . to eq ( 3 )
expect ( assigns ( :projects ) . limit_value ) . to be_nil
end
end
2020-01-30 16:08:47 -05:00
context 'when user has no permission to see the event' do
let ( :project_with_restricted_access ) do
create ( :project , :public , issues_access_level : ProjectFeature :: PRIVATE , group : group )
end
before do
create ( :event , project : project )
create ( :event , :created , project : project_with_restricted_access , target : create ( :issue ) )
group . add_guest ( user )
sign_in ( user )
end
it 'filters out invisible event' do
get :activity , params : { id : group . to_param } , format : :json
expect ( json_response [ 'count' ] ) . to eq ( 1 )
end
end
2018-01-30 18:45:23 -05:00
end
2017-09-07 14:35:45 -04:00
describe 'POST #create' do
2019-06-03 03:32:12 -04:00
it 'allows creating a group' do
sign_in ( user )
expect do
post :create , params : { group : { name : 'new_group' , path : " new_group " } }
end . to change { Group . count } . by ( 1 )
2020-01-27 07:08:35 -05:00
expect ( response ) . to have_gitlab_http_status ( :found )
2019-06-03 03:32:12 -04:00
end
context 'authorization' do
it 'allows an admin to create a group' do
sign_in ( create ( :admin ) )
expect do
post :create , params : { group : { name : 'new_group' , path : " new_group " } }
end . to change { Group . count } . by ( 1 )
2020-01-27 07:08:35 -05:00
expect ( response ) . to have_gitlab_http_status ( :found )
2019-06-03 03:32:12 -04:00
end
end
2019-07-24 05:20:54 -04:00
context 'when creating subgroups' do
2017-09-07 14:35:45 -04:00
[ true , false ] . each do | can_create_group_status |
context " and can_create_group is #{ can_create_group_status } " do
context 'and logged in as Owner' do
it 'creates the subgroup' do
owner . update_attribute ( :can_create_group , can_create_group_status )
sign_in ( owner )
2018-12-17 17:52:17 -05:00
post :create , params : { group : { parent_id : group . id , path : 'subgroup' } }
2017-09-07 14:35:45 -04:00
expect ( response ) . to be_redirect
expect ( response . body ) . to match ( %r{ http://test.host/ #{ group . path } /subgroup } )
end
end
context 'and logged in as Developer' do
it 'renders the new template' do
developer . update_attribute ( :can_create_group , can_create_group_status )
sign_in ( developer )
previous_group_count = Group . count
2018-12-17 17:52:17 -05:00
post :create , params : { group : { parent_id : group . id , path : 'subgroup' } }
2017-09-07 14:35:45 -04:00
expect ( response ) . to render_template ( :new )
expect ( Group . count ) . to eq ( previous_group_count )
end
end
end
end
end
context 'when creating a top level group' do
before do
sign_in ( developer )
end
context 'and can_create_group is enabled' do
before do
developer . update_attribute ( :can_create_group , true )
end
it 'creates the Group' do
original_group_count = Group . count
2018-12-17 17:52:17 -05:00
post :create , params : { group : { path : 'subgroup' } }
2017-09-07 14:35:45 -04:00
expect ( Group . count ) . to eq ( original_group_count + 1 )
expect ( response ) . to be_redirect
end
end
context 'and can_create_group is disabled' do
before do
developer . update_attribute ( :can_create_group , false )
end
it 'does not create the Group' do
original_group_count = Group . count
2018-12-17 17:52:17 -05:00
post :create , params : { group : { path : 'subgroup' } }
2017-09-07 14:35:45 -04:00
expect ( Group . count ) . to eq ( original_group_count )
expect ( response ) . to render_template ( :new )
end
end
end
2020-03-26 14:08:03 -04:00
context " malicious group name " do
subject { post :create , params : { group : { name : " <script>alert('Mayday!');</script> " , path : " invalid_group_url " } } }
before do
sign_in ( user )
end
it { expect { subject } . not_to change { Group . count } }
it { expect ( subject ) . to render_template ( :new ) }
end
2020-04-24 05:09:44 -04:00
context 'when creating a group with `default_branch_protection` attribute' do
before do
sign_in ( user )
end
subject do
post :create , params : { group : { name : 'new_group' , path : 'new_group' , default_branch_protection : Gitlab :: Access :: PROTECTION_NONE } }
end
context 'for users who have the ability to create a group with `default_branch_protection`' do
it 'creates group with the specified branch protection level' do
subject
expect ( response ) . to have_gitlab_http_status ( :found )
expect ( Group . last . default_branch_protection ) . to eq ( Gitlab :: Access :: PROTECTION_NONE )
end
end
context 'for users who do not have the ability to create a group with `default_branch_protection`' do
it 'does not create the group with the specified branch protection level' do
allow ( Ability ) . to receive ( :allowed? ) . and_call_original
allow ( Ability ) . to receive ( :allowed? ) . with ( user , :create_group_with_default_branch_protection ) { false }
subject
expect ( response ) . to have_gitlab_http_status ( :found )
expect ( Group . last . default_branch_protection ) . not_to eq ( Gitlab :: Access :: PROTECTION_NONE )
end
end
end
2021-03-19 05:08:53 -04:00
context 'when creating a group with captcha protection' do
before do
sign_in ( user )
stub_application_setting ( recaptcha_enabled : true )
end
after do
# Avoid test ordering issue and ensure `verify_recaptcha` returns true
unless Recaptcha . configuration . skip_verify_env . include? ( 'test' )
Recaptcha . configuration . skip_verify_env << 'test'
end
end
it 'displays an error when the reCAPTCHA is not solved' do
allow ( controller ) . to receive ( :verify_recaptcha ) . and_return ( false )
post :create , params : { group : { name : 'new_group' , path : " new_group " } }
expect ( response ) . to render_template ( :new )
expect ( flash [ :alert ] ) . to eq ( _ ( 'There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.' ) )
end
it 'allows creating a group when the reCAPTCHA is solved' do
expect do
post :create , params : { group : { name : 'new_group' , path : " new_group " } }
end . to change { Group . count } . by ( 1 )
expect ( response ) . to have_gitlab_http_status ( :found )
end
it 'allows creating a sub-group without checking the captcha' do
expect ( controller ) . not_to receive ( :verify_recaptcha )
expect do
post :create , params : { group : { name : 'new_group' , path : " new_group " , parent_id : group . id } }
end . to change { Group . count } . by ( 1 )
expect ( response ) . to have_gitlab_http_status ( :found )
end
context 'with feature flag switched off' do
before do
stub_feature_flags ( recaptcha_on_top_level_group_creation : false )
end
it 'allows creating a group without the reCAPTCHA' do
expect ( controller ) . not_to receive ( :verify_recaptcha )
expect do
post :create , params : { group : { name : 'new_group' , path : " new_group " } }
end . to change { Group . count } . by ( 1 )
expect ( response ) . to have_gitlab_http_status ( :found )
end
end
end
2017-09-07 14:35:45 -04:00
end
2016-03-21 16:01:38 -04:00
describe 'GET #index' do
2016-01-23 19:08:15 -05:00
context 'as a user' do
it 'redirects to Groups Dashboard' do
2016-03-21 16:01:38 -04:00
sign_in ( user )
2016-01-23 19:08:15 -05:00
get :index
expect ( response ) . to redirect_to ( dashboard_groups_path )
end
end
context 'as a guest' do
it 'redirects to Explore Groups' do
get :index
expect ( response ) . to redirect_to ( explore_groups_path )
end
end
end
2017-05-03 19:51:25 -04:00
2019-10-23 05:06:03 -04:00
describe 'GET #issues' , :sidekiq_might_not_need_inline do
2020-10-02 11:08:13 -04:00
let_it_be ( :issue_1 ) { create ( :issue , project : project , title : 'foo' ) }
let_it_be ( :issue_2 ) { create ( :issue , project : project , title : 'bar' ) }
2016-03-21 16:01:38 -04:00
before do
2016-04-16 15:09:08 -04:00
create_list ( :award_emoji , 3 , awardable : issue_2 )
create_list ( :award_emoji , 2 , awardable : issue_1 )
2017-05-03 07:27:17 -04:00
create_list ( :award_emoji , 2 , :downvote , awardable : issue_2 )
2016-03-21 16:01:38 -04:00
sign_in ( user )
end
context 'sorting by votes' do
it 'sorts most popular issues' do
2018-12-17 17:52:17 -05:00
get :issues , params : { id : group . to_param , sort : 'upvotes_desc' }
2016-03-21 16:01:38 -04:00
expect ( assigns ( :issues ) ) . to eq [ issue_2 , issue_1 ]
end
it 'sorts least popular issues' do
2018-12-17 17:52:17 -05:00
get :issues , params : { id : group . to_param , sort : 'downvotes_desc' }
2016-03-21 16:01:38 -04:00
expect ( assigns ( :issues ) ) . to eq [ issue_2 , issue_1 ]
end
end
2018-09-05 07:10:36 -04:00
context 'searching' do
it 'works with popularity sort' do
2018-12-17 17:52:17 -05:00
get :issues , params : { id : group . to_param , search : 'foo' , sort : 'popularity' }
2018-09-05 07:10:36 -04:00
expect ( assigns ( :issues ) ) . to eq ( [ issue_1 ] )
end
it 'works with priority sort' do
2018-12-17 17:52:17 -05:00
get :issues , params : { id : group . to_param , search : 'foo' , sort : 'priority' }
2018-09-05 07:10:36 -04:00
expect ( assigns ( :issues ) ) . to eq ( [ issue_1 ] )
end
it 'works with label priority sort' do
2018-12-17 17:52:17 -05:00
get :issues , params : { id : group . to_param , search : 'foo' , sort : 'label_priority' }
2018-09-05 07:10:36 -04:00
expect ( assigns ( :issues ) ) . to eq ( [ issue_1 ] )
end
end
2016-03-21 16:01:38 -04:00
end
2019-10-23 05:06:03 -04:00
describe 'GET #merge_requests' , :sidekiq_might_not_need_inline do
2016-03-21 16:01:38 -04:00
let ( :merge_request_1 ) { create ( :merge_request , source_project : project ) }
let ( :merge_request_2 ) { create ( :merge_request , :simple , source_project : project ) }
before do
2016-04-16 15:09:08 -04:00
create_list ( :award_emoji , 3 , awardable : merge_request_2 )
create_list ( :award_emoji , 2 , awardable : merge_request_1 )
2016-06-01 05:23:09 -04:00
create_list ( :award_emoji , 2 , :downvote , awardable : merge_request_2 )
2016-03-21 16:01:38 -04:00
sign_in ( user )
end
context 'sorting by votes' do
it 'sorts most popular merge requests' do
2018-12-17 17:52:17 -05:00
get :merge_requests , params : { id : group . to_param , sort : 'upvotes_desc' }
2016-03-21 16:01:38 -04:00
expect ( assigns ( :merge_requests ) ) . to eq [ merge_request_2 , merge_request_1 ]
end
it 'sorts least popular merge requests' do
2018-12-17 17:52:17 -05:00
get :merge_requests , params : { id : group . to_param , sort : 'downvotes_desc' }
2016-03-21 16:01:38 -04:00
expect ( assigns ( :merge_requests ) ) . to eq [ merge_request_2 , merge_request_1 ]
end
end
end
2016-05-28 22:54:17 -04:00
describe 'DELETE #destroy' do
context 'as another user' do
it 'returns 404' do
sign_in ( create ( :user ) )
2018-12-17 17:52:17 -05:00
delete :destroy , params : { id : group . to_param }
2016-05-28 22:54:17 -04:00
2020-03-31 17:08:05 -04:00
expect ( response ) . to have_gitlab_http_status ( :not_found )
2016-05-28 22:54:17 -04:00
end
end
context 'as the group owner' do
before do
sign_in ( user )
end
it 'schedules a group destroy' do
2016-08-15 17:26:40 -04:00
Sidekiq :: Testing . fake! do
2018-12-17 17:52:17 -05:00
expect { delete :destroy , params : { id : group . to_param } } . to change ( GroupDestroyWorker . jobs , :size ) . by ( 1 )
2016-08-15 17:26:40 -04:00
end
2016-05-28 22:54:17 -04:00
end
it 'redirects to the root path' do
2018-12-17 17:52:17 -05:00
delete :destroy , params : { id : group . to_param }
2016-05-28 22:54:17 -04:00
expect ( response ) . to redirect_to ( root_path )
end
end
end
2016-12-20 11:52:27 -05:00
describe 'PUT update' do
before do
sign_in ( user )
end
2017-05-01 16:46:30 -04:00
it 'updates the path successfully' do
2018-12-17 17:52:17 -05:00
post :update , params : { id : group . to_param , group : { path : 'new_path' } }
2016-12-20 11:52:27 -05:00
2020-01-27 07:08:35 -05:00
expect ( response ) . to have_gitlab_http_status ( :found )
2016-12-20 11:52:27 -05:00
expect ( controller ) . to set_flash [ :notice ]
end
it 'does not update the path on error' do
allow_any_instance_of ( Group ) . to receive ( :move_dir ) . and_raise ( Gitlab :: UpdatePathError )
2018-12-17 17:52:17 -05:00
post :update , params : { id : group . to_param , group : { path : 'new_path' } }
2016-12-20 11:52:27 -05:00
2016-12-21 07:29:27 -05:00
expect ( assigns ( :group ) . errors ) . not_to be_empty
expect ( assigns ( :group ) . path ) . not_to eq ( 'new_path' )
2016-12-20 11:52:27 -05:00
end
2019-04-05 14:49:46 -04:00
it 'updates the project_creation_level successfully' do
post :update , params : { id : group . to_param , group : { project_creation_level : :: Gitlab :: Access :: MAINTAINER_PROJECT_ACCESS } }
2020-01-27 07:08:35 -05:00
expect ( response ) . to have_gitlab_http_status ( :found )
2019-04-05 14:49:46 -04:00
expect ( group . reload . project_creation_level ) . to eq ( :: Gitlab :: Access :: MAINTAINER_PROJECT_ACCESS )
end
2019-10-16 05:07:51 -04:00
2020-04-24 05:09:44 -04:00
context 'updating default_branch_protection' do
subject do
put :update , params : { id : group . to_param , group : { default_branch_protection : :: Gitlab :: Access :: PROTECTION_DEV_CAN_MERGE } }
end
2020-03-02 07:07:57 -05:00
2020-04-24 05:09:44 -04:00
context 'for users who have the ability to update default_branch_protection' do
it 'updates the attribute' do
subject
expect ( response ) . to have_gitlab_http_status ( :found )
expect ( group . reload . default_branch_protection ) . to eq ( :: Gitlab :: Access :: PROTECTION_DEV_CAN_MERGE )
end
end
context 'for users who do not have the ability to update default_branch_protection' do
it 'does not update the attribute' do
allow ( Ability ) . to receive ( :allowed? ) . and_call_original
allow ( Ability ) . to receive ( :allowed? ) . with ( user , :update_default_branch_protection , group ) { false }
subject
expect ( response ) . to have_gitlab_http_status ( :found )
expect ( group . reload . default_branch_protection ) . not_to eq ( :: Gitlab :: Access :: PROTECTION_DEV_CAN_MERGE )
end
end
2020-03-02 07:07:57 -05:00
end
2020-10-08 20:08:41 -04:00
context " updating default_branch_name " do
let ( :example_branch_name ) { " example_branch_name " }
subject ( :update_action ) do
put :update ,
params : {
id : group . to_param ,
group : { default_branch_name : example_branch_name }
}
end
it " updates the attribute " do
expect { subject }
. to change { group . namespace_settings . reload . default_branch_name }
. from ( nil )
. to ( example_branch_name )
expect ( response ) . to have_gitlab_http_status ( :found )
end
context " to empty string " do
let ( :example_branch_name ) { '' }
it " does not update the attribute " do
subject
expect ( group . namespace_settings . reload . default_branch_name ) . not_to eq ( '' )
end
end
end
2020-08-25 23:10:11 -04:00
context 'when there is a conflicting group path' do
let! ( :conflict_group ) { create ( :group , path : SecureRandom . hex ( 12 ) ) }
let! ( :old_name ) { group . name }
it 'does not render references to the conflicting group' do
put :update , params : { id : group . to_param , group : { path : conflict_group . path } }
expect ( response ) . to have_gitlab_http_status ( :ok )
expect ( group . reload . name ) . to eq ( old_name )
expect ( response . body ) . not_to include ( conflict_group . path )
end
end
2019-10-16 05:07:51 -04:00
context 'when a project inside the group has container repositories' do
before do
stub_container_registry_config ( enabled : true )
stub_container_registry_tags ( repository : / image / , tags : %w[ rc1 ] )
create ( :container_repository , project : project , name : :image )
end
it 'does allow the group to be renamed' do
post :update , params : { id : group . to_param , group : { name : 'new_name' } }
expect ( controller ) . to set_flash [ :notice ]
2020-01-27 07:08:35 -05:00
expect ( response ) . to have_gitlab_http_status ( :found )
2019-10-16 05:07:51 -04:00
expect ( group . reload . name ) . to eq ( 'new_name' )
end
it 'does not allow to path of the group to be changed' do
post :update , params : { id : group . to_param , group : { path : 'new_path' } }
expect ( assigns ( :group ) . errors [ :base ] . first ) . to match ( / Docker images in their Container Registry / )
2020-01-27 07:08:35 -05:00
expect ( response ) . to have_gitlab_http_status ( :ok )
2019-10-16 05:07:51 -04:00
end
end
2017-05-18 15:56:39 -04:00
end
2021-04-05 23:09:02 -04:00
context " updating :resource_access_token_creation_allowed " do
subject do
put :update ,
params : {
id : group . to_param ,
group : { resource_access_token_creation_allowed : false }
}
end
context 'when user is a group owner' do
before do
group . add_owner ( user )
sign_in ( user )
end
it " updates the attribute " do
expect { subject }
. to change { group . namespace_settings . reload . resource_access_token_creation_allowed }
. from ( true )
. to ( false )
expect ( response ) . to have_gitlab_http_status ( :found )
end
end
context 'when not a group owner' do
before do
group . add_developer ( user )
sign_in ( user )
end
it " does not update the attribute " do
expect { subject } . not_to change { group . namespace_settings . reload . resource_access_token_creation_allowed }
end
end
end
2017-05-18 15:56:39 -04:00
describe '#ensure_canonical_path' do
before do
sign_in ( user )
end
context 'for a GET request' do
context 'when requesting groups at the root path' do
before do
allow ( request ) . to receive ( :original_fullpath ) . and_return ( " / #{ group_full_path } " )
2018-12-17 17:52:17 -05:00
get :show , params : { id : group_full_path }
2017-05-18 15:56:39 -04:00
end
2017-05-01 16:46:30 -04:00
2017-05-18 15:56:39 -04:00
context 'when requesting the canonical path with different casing' do
let ( :group_full_path ) { group . to_param . upcase }
2017-05-01 16:46:30 -04:00
2017-05-18 15:56:39 -04:00
it 'redirects to the correct casing' do
expect ( response ) . to redirect_to ( group )
expect ( controller ) . not_to set_flash [ :notice ]
end
end
context 'when requesting a redirected path' do
2021-04-04 02:09:06 -04:00
let ( :redirect_route ) { group . redirect_routes . create! ( path : 'old-path' ) }
2017-05-18 15:56:39 -04:00
let ( :group_full_path ) { redirect_route . path }
it 'redirects to the canonical path' do
expect ( response ) . to redirect_to ( group )
expect ( controller ) . to set_flash [ :notice ] . to ( group_moved_message ( redirect_route , group ) )
end
context 'when the old group path is a substring of the scheme or host' do
2021-04-04 02:09:06 -04:00
let ( :redirect_route ) { group . redirect_routes . create! ( path : 'http' ) }
2017-05-18 15:56:39 -04:00
it 'does not modify the requested host' do
expect ( response ) . to redirect_to ( group )
expect ( controller ) . to set_flash [ :notice ] . to ( group_moved_message ( redirect_route , group ) )
end
end
context 'when the old group path is substring of groups' do
# I.e. /groups/oups should not become /grfoo/oups
2021-04-04 02:09:06 -04:00
let ( :redirect_route ) { group . redirect_routes . create! ( path : 'oups' ) }
2017-05-18 15:56:39 -04:00
it 'does not modify the /groups part of the path' do
expect ( response ) . to redirect_to ( group )
expect ( controller ) . to set_flash [ :notice ] . to ( group_moved_message ( redirect_route , group ) )
end
end
end
2017-05-01 16:46:30 -04:00
end
2017-05-18 15:56:39 -04:00
context 'when requesting groups under the /groups path' do
2017-05-18 19:23:05 -04:00
context 'when requesting the canonical path' do
context 'non-show path' do
context 'with exactly matching casing' do
it 'does not redirect' do
2018-12-17 17:52:17 -05:00
get :issues , params : { id : group . to_param }
2017-05-18 19:23:05 -04:00
2020-01-27 07:08:35 -05:00
expect ( response ) . not_to have_gitlab_http_status ( :moved_permanently )
2017-05-18 19:23:05 -04:00
end
end
2017-05-01 16:46:30 -04:00
2017-05-18 19:23:05 -04:00
context 'with different casing' do
it 'redirects to the correct casing' do
2018-12-17 17:52:17 -05:00
get :issues , params : { id : group . to_param . upcase }
2017-05-18 19:23:05 -04:00
expect ( response ) . to redirect_to ( issues_group_path ( group . to_param ) )
expect ( controller ) . not_to set_flash [ :notice ]
end
end
end
context 'show path' do
context 'with exactly matching casing' do
it 'does not redirect' do
2018-12-17 17:52:17 -05:00
get :show , params : { id : group . to_param }
2017-05-18 19:23:05 -04:00
2020-01-27 07:08:35 -05:00
expect ( response ) . not_to have_gitlab_http_status ( :moved_permanently )
2017-05-18 19:23:05 -04:00
end
end
context 'with different casing' do
it 'redirects to the correct casing at the root path' do
2018-12-17 17:52:17 -05:00
get :show , params : { id : group . to_param . upcase }
2017-05-18 19:23:05 -04:00
expect ( response ) . to redirect_to ( group )
expect ( controller ) . not_to set_flash [ :notice ]
end
end
2017-05-18 15:56:39 -04:00
end
end
context 'when requesting a redirected path' do
2021-04-04 02:09:06 -04:00
let ( :redirect_route ) { group . redirect_routes . create! ( path : 'old-path' ) }
2017-05-18 15:56:39 -04:00
it 'redirects to the canonical path' do
2018-12-17 17:52:17 -05:00
get :issues , params : { id : redirect_route . path }
2017-05-18 15:56:39 -04:00
expect ( response ) . to redirect_to ( issues_group_path ( group . to_param ) )
expect ( controller ) . to set_flash [ :notice ] . to ( group_moved_message ( redirect_route , group ) )
end
context 'when the old group path is a substring of the scheme or host' do
2021-04-04 02:09:06 -04:00
let ( :redirect_route ) { group . redirect_routes . create! ( path : 'http' ) }
2017-05-18 15:56:39 -04:00
it 'does not modify the requested host' do
2018-12-17 17:52:17 -05:00
get :issues , params : { id : redirect_route . path }
2017-05-18 15:56:39 -04:00
expect ( response ) . to redirect_to ( issues_group_path ( group . to_param ) )
expect ( controller ) . to set_flash [ :notice ] . to ( group_moved_message ( redirect_route , group ) )
end
end
context 'when the old group path is substring of groups' do
# I.e. /groups/oups should not become /grfoo/oups
2021-04-04 02:09:06 -04:00
let ( :redirect_route ) { group . redirect_routes . create! ( path : 'oups' ) }
2017-05-18 15:56:39 -04:00
it 'does not modify the /groups part of the path' do
2018-12-17 17:52:17 -05:00
get :issues , params : { id : redirect_route . path }
2017-05-18 15:56:39 -04:00
expect ( response ) . to redirect_to ( issues_group_path ( group . to_param ) )
expect ( controller ) . to set_flash [ :notice ] . to ( group_moved_message ( redirect_route , group ) )
end
end
context 'when the old group path is substring of groups plus the new path' do
# I.e. /groups/oups/oup should not become /grfoos
2021-04-04 02:09:06 -04:00
let ( :redirect_route ) { group . redirect_routes . create! ( path : 'oups/oup' ) }
2017-05-18 15:56:39 -04:00
it 'does not modify the /groups part of the path' do
2018-12-17 17:52:17 -05:00
get :issues , params : { id : redirect_route . path }
2017-05-18 15:56:39 -04:00
expect ( response ) . to redirect_to ( issues_group_path ( group . to_param ) )
expect ( controller ) . to set_flash [ :notice ] . to ( group_moved_message ( redirect_route , group ) )
end
end
end
2017-05-01 16:46:30 -04:00
end
2017-09-05 11:27:55 -04:00
2017-09-13 11:16:30 -04:00
context 'for a POST request' do
context 'when requesting the canonical path with different casing' do
it 'does not 404' do
2018-12-17 17:52:17 -05:00
post :update , params : { id : group . to_param . upcase , group : { path : 'new_path' } }
2017-05-18 15:56:39 -04:00
2020-01-27 07:08:35 -05:00
expect ( response ) . not_to have_gitlab_http_status ( :not_found )
2017-09-13 11:16:30 -04:00
end
2017-05-18 15:56:39 -04:00
2017-09-13 11:16:30 -04:00
it 'does not redirect to the correct casing' do
2018-12-17 17:52:17 -05:00
post :update , params : { id : group . to_param . upcase , group : { path : 'new_path' } }
2017-05-18 15:56:39 -04:00
2020-01-27 07:08:35 -05:00
expect ( response ) . not_to have_gitlab_http_status ( :moved_permanently )
2017-09-13 11:16:30 -04:00
end
2017-05-18 15:56:39 -04:00
end
2017-09-13 11:16:30 -04:00
context 'when requesting a redirected path' do
2021-04-04 02:09:06 -04:00
let ( :redirect_route ) { group . redirect_routes . create! ( path : 'old-path' ) }
2017-05-01 16:46:30 -04:00
2017-09-13 11:16:30 -04:00
it 'returns not found' do
2018-12-17 17:52:17 -05:00
post :update , params : { id : redirect_route . path , group : { path : 'new_path' } }
2017-05-01 16:46:30 -04:00
2020-01-27 07:08:35 -05:00
expect ( response ) . to have_gitlab_http_status ( :not_found )
2017-09-13 11:16:30 -04:00
end
2017-05-18 15:56:39 -04:00
end
2017-05-01 16:46:30 -04:00
end
2017-05-11 16:57:03 -04:00
2017-09-13 11:16:30 -04:00
context 'for a DELETE request' do
context 'when requesting the canonical path with different casing' do
it 'does not 404' do
2018-12-17 17:52:17 -05:00
delete :destroy , params : { id : group . to_param . upcase }
2017-05-18 13:30:17 -04:00
2020-01-27 07:08:35 -05:00
expect ( response ) . not_to have_gitlab_http_status ( :not_found )
2017-09-13 11:16:30 -04:00
end
2017-05-18 13:30:17 -04:00
2017-09-13 11:16:30 -04:00
it 'does not redirect to the correct casing' do
2018-12-17 17:52:17 -05:00
delete :destroy , params : { id : group . to_param . upcase }
2017-05-18 15:56:39 -04:00
2020-01-27 07:08:35 -05:00
expect ( response ) . not_to have_gitlab_http_status ( :moved_permanently )
2017-09-13 11:16:30 -04:00
end
2017-05-18 15:56:39 -04:00
end
2017-09-13 11:16:30 -04:00
context 'when requesting a redirected path' do
2021-04-04 02:09:06 -04:00
let ( :redirect_route ) { group . redirect_routes . create! ( path : 'old-path' ) }
2017-05-18 15:56:39 -04:00
2017-09-13 11:16:30 -04:00
it 'returns not found' do
2018-12-17 17:52:17 -05:00
delete :destroy , params : { id : redirect_route . path }
2017-05-18 15:56:39 -04:00
2020-01-27 07:08:35 -05:00
expect ( response ) . to have_gitlab_http_status ( :not_found )
2017-09-13 11:16:30 -04:00
end
2017-05-18 15:56:39 -04:00
end
2017-05-18 13:30:17 -04:00
end
end
2017-09-13 11:16:30 -04:00
def group_moved_message ( redirect_route , group )
" Group ' #{ redirect_route . path } ' was moved to ' #{ group . full_path } '. Please update any links and bookmarks that may still have the old path. "
end
2017-05-11 16:57:03 -04:00
end
2018-02-05 19:10:58 -05:00
2019-07-24 05:20:54 -04:00
describe 'PUT transfer' do
2018-02-05 19:10:58 -05:00
before do
sign_in ( user )
end
2018-10-30 06:53:01 -04:00
context 'when transferring to a subgroup goes right' do
2018-02-05 19:10:58 -05:00
let ( :new_parent_group ) { create ( :group , :public ) }
2020-09-24 08:09:37 -04:00
let ( :group ) { create ( :group , :public ) }
2018-02-05 19:10:58 -05:00
let! ( :group_member ) { create ( :group_member , :owner , group : group , user : user ) }
let! ( :new_parent_group_member ) { create ( :group_member , :owner , group : new_parent_group , user : user ) }
before do
put :transfer ,
2018-12-17 17:52:17 -05:00
params : {
id : group . to_param ,
new_parent_group_id : new_parent_group . id
}
2018-02-05 19:10:58 -05:00
end
2020-09-24 08:09:37 -04:00
it 'returns a notice and redirects to the new path' do
2018-02-05 19:10:58 -05:00
expect ( flash [ :notice ] ) . to eq ( " Group ' #{ group . name } ' was successfully transferred. " )
expect ( response ) . to redirect_to ( " / #{ new_parent_group . path } / #{ group . path } " )
end
end
context 'when converting to a root group goes right' do
let ( :group ) { create ( :group , :public , :nested ) }
let! ( :group_member ) { create ( :group_member , :owner , group : group , user : user ) }
before do
put :transfer ,
2018-12-17 17:52:17 -05:00
params : {
id : group . to_param ,
new_parent_group_id : ''
}
2018-02-05 19:10:58 -05:00
end
2020-09-24 08:09:37 -04:00
it 'returns a notice and redirects to the new path' do
2018-02-05 19:10:58 -05:00
expect ( flash [ :notice ] ) . to eq ( " Group ' #{ group . name } ' was successfully transferred. " )
expect ( response ) . to redirect_to ( " / #{ group . path } " )
end
end
context 'When the transfer goes wrong' do
let ( :new_parent_group ) { create ( :group , :public ) }
2020-09-24 08:09:37 -04:00
let ( :group ) { create ( :group , :public ) }
2018-02-05 19:10:58 -05:00
let! ( :group_member ) { create ( :group_member , :owner , group : group , user : user ) }
let! ( :new_parent_group_member ) { create ( :group_member , :owner , group : new_parent_group , user : user ) }
before do
allow_any_instance_of ( :: Groups :: TransferService ) . to receive ( :proceed_to_transfer ) . and_raise ( Gitlab :: UpdatePathError , 'namespace directory cannot be moved' )
put :transfer ,
2018-12-17 17:52:17 -05:00
params : {
id : group . to_param ,
new_parent_group_id : new_parent_group . id
}
2018-02-05 19:10:58 -05:00
end
2020-09-24 08:09:37 -04:00
it 'returns an alert and redirects to the current path' do
2018-02-05 19:10:58 -05:00
expect ( flash [ :alert ] ) . to eq " Transfer failed: namespace directory cannot be moved "
2019-04-01 21:14:19 -04:00
expect ( response ) . to redirect_to ( edit_group_path ( group ) )
2018-02-05 19:10:58 -05:00
end
end
context 'when the user is not allowed to transfer the group' do
let ( :new_parent_group ) { create ( :group , :public ) }
2020-09-24 08:09:37 -04:00
let ( :group ) { create ( :group , :public ) }
2018-02-05 19:10:58 -05:00
let! ( :group_member ) { create ( :group_member , :guest , group : group , user : user ) }
let! ( :new_parent_group_member ) { create ( :group_member , :guest , group : new_parent_group , user : user ) }
before do
put :transfer ,
2018-12-17 17:52:17 -05:00
params : {
id : group . to_param ,
new_parent_group_id : new_parent_group . id
}
2018-02-05 19:10:58 -05:00
end
2019-04-05 04:43:27 -04:00
it 'is denied' do
2020-01-27 07:08:35 -05:00
expect ( response ) . to have_gitlab_http_status ( :not_found )
2018-02-05 19:10:58 -05:00
end
end
2019-10-16 05:07:51 -04:00
context 'transferring when a project has container images' do
let ( :group ) { create ( :group , :public , :nested ) }
2020-09-24 08:09:37 -04:00
let ( :project ) { create ( :project , namespace : group ) }
2019-10-16 05:07:51 -04:00
let! ( :group_member ) { create ( :group_member , :owner , group : group , user : user ) }
before do
stub_container_registry_config ( enabled : true )
stub_container_registry_tags ( repository : / image / , tags : %w[ rc1 ] )
create ( :container_repository , project : project , name : :image )
put :transfer ,
params : {
id : group . to_param ,
new_parent_group_id : ''
}
end
it 'does not allow the group to be transferred' do
expect ( controller ) . to set_flash [ :alert ] . to match ( / Docker images in their Container Registry / )
expect ( response ) . to redirect_to ( edit_group_path ( group ) )
end
end
2018-02-05 19:10:58 -05:00
end
2018-11-28 14:06:02 -05:00
2020-04-09 08:09:24 -04:00
describe 'POST #export' do
2021-03-09 13:09:41 -05:00
let ( :admin ) { create ( :admin ) }
before do
enable_admin_mode! ( admin )
end
2020-04-09 08:09:24 -04:00
context 'when the group export feature flag is not enabled' do
before do
sign_in ( admin )
stub_feature_flags ( group_import_export : false )
end
it 'returns a not found error' do
post :export , params : { id : group . to_param }
expect ( response ) . to have_gitlab_http_status ( :not_found )
end
end
context 'when the user does not have permission to export the group' do
before do
sign_in ( guest )
end
it 'returns an error' do
post :export , params : { id : group . to_param }
expect ( response ) . to have_gitlab_http_status ( :not_found )
end
end
context 'when supplied valid params' do
before do
sign_in ( admin )
end
it 'triggers the export job' do
expect ( GroupExportWorker ) . to receive ( :perform_async ) . with ( admin . id , group . id , { } )
post :export , params : { id : group . to_param }
end
it 'redirects to the edit page' do
post :export , params : { id : group . to_param }
expect ( response ) . to have_gitlab_http_status ( :found )
end
end
context 'when the endpoint receives requests above the rate limit' do
before do
sign_in ( admin )
2020-06-03 08:08:21 -04:00
allow ( Gitlab :: ApplicationRateLimiter )
. to receive ( :increment )
2020-07-07 11:08:49 -04:00
. and_return ( Gitlab :: ApplicationRateLimiter . rate_limits [ :group_export ] [ :threshold ] . call + 1 )
2020-04-09 08:09:24 -04:00
end
it 'throttles the endpoint' do
post :export , params : { id : group . to_param }
2020-06-03 08:08:21 -04:00
expect ( response . body ) . to eq ( 'This endpoint has been requested too many times. Try again later.' )
expect ( response ) . to have_gitlab_http_status :too_many_requests
2020-04-09 08:09:24 -04:00
end
end
end
describe 'GET #download_export' do
2021-03-09 13:09:41 -05:00
let ( :admin ) { create ( :admin ) }
before do
enable_admin_mode! ( admin )
end
2020-04-09 08:09:24 -04:00
context 'when there is a file available to download' do
let ( :export_file ) { fixture_file_upload ( 'spec/fixtures/group_export.tar.gz' ) }
before do
sign_in ( admin )
create ( :import_export_upload , group : group , export_file : export_file )
end
it 'sends the file' do
get :download_export , params : { id : group . to_param }
expect ( response . body ) . to eq export_file . tempfile . read
end
end
context 'when there is no file available to download' do
before do
sign_in ( admin )
end
it 'returns not found' do
get :download_export , params : { id : group . to_param }
expect ( flash [ :alert ] )
. to eq 'Group export link has expired. Please generate a new export from your group settings.'
expect ( response ) . to redirect_to ( edit_group_path ( group ) )
end
end
context 'when the group export feature flag is not enabled' do
before do
sign_in ( admin )
stub_feature_flags ( group_import_export : false )
end
it 'returns a not found error' do
post :export , params : { id : group . to_param }
expect ( response ) . to have_gitlab_http_status ( :not_found )
end
end
context 'when the user does not have the required permissions' do
before do
sign_in ( guest )
end
it 'returns not_found' do
get :download_export , params : { id : group . to_param }
expect ( response ) . to have_gitlab_http_status ( :not_found )
end
end
context 'when the endpoint receives requests above the rate limit' do
before do
sign_in ( admin )
2020-06-03 08:08:21 -04:00
allow ( Gitlab :: ApplicationRateLimiter )
. to receive ( :increment )
2020-07-07 11:08:49 -04:00
. and_return ( Gitlab :: ApplicationRateLimiter . rate_limits [ :group_download_export ] [ :threshold ] . call + 1 )
2020-04-09 08:09:24 -04:00
end
it 'throttles the endpoint' do
get :download_export , params : { id : group . to_param }
2020-06-03 08:08:21 -04:00
expect ( response . body ) . to eq ( 'This endpoint has been requested too many times. Try again later.' )
expect ( response ) . to have_gitlab_http_status :too_many_requests
2020-04-09 08:09:24 -04:00
end
end
end
2018-11-28 14:06:02 -05:00
context 'token authentication' do
it_behaves_like 'authenticates sessionless user' , :show , :atom , public : true do
before do
default_params . merge! ( id : group )
end
end
it_behaves_like 'authenticates sessionless user' , :issues , :atom , public : true do
before do
default_params . merge! ( id : group , author_id : user . id )
end
end
it_behaves_like 'authenticates sessionless user' , :issues_calendar , :ics , public : true do
before do
default_params . merge! ( id : group )
end
end
end
2019-04-09 11:38:58 -04:00
describe 'external authorization' do
before do
group . add_owner ( user )
sign_in ( user )
end
context 'with external authorization service enabled' do
before do
enable_external_authorization_service_check
end
describe 'GET #show' do
it 'is successful' do
get :show , params : { id : group . to_param }
2020-01-27 07:08:35 -05:00
expect ( response ) . to have_gitlab_http_status ( :ok )
2019-04-09 11:38:58 -04:00
end
it 'does not allow other formats' do
get :show , params : { id : group . to_param } , format : :atom
2020-01-27 07:08:35 -05:00
expect ( response ) . to have_gitlab_http_status ( :forbidden )
2019-04-09 11:38:58 -04:00
end
end
describe 'GET #edit' do
it 'is successful' do
get :edit , params : { id : group . to_param }
2020-01-27 07:08:35 -05:00
expect ( response ) . to have_gitlab_http_status ( :ok )
2019-04-09 11:38:58 -04:00
end
end
describe 'GET #new' do
it 'is successful' do
get :new
2020-01-27 07:08:35 -05:00
expect ( response ) . to have_gitlab_http_status ( :ok )
2019-04-09 11:38:58 -04:00
end
end
describe 'GET #index' do
it 'is successful' do
get :index
# Redirects to the dashboard
2020-01-27 07:08:35 -05:00
expect ( response ) . to have_gitlab_http_status ( :found )
2019-04-09 11:38:58 -04:00
end
end
describe 'POST #create' do
it 'creates a group' do
expect do
post :create , params : { group : { name : 'a name' , path : 'a-name' } }
end . to change { Group . count } . by ( 1 )
end
end
describe 'PUT #update' do
it 'updates a group' do
expect do
put :update , params : { id : group . to_param , group : { name : 'world' } }
end . to change { group . reload . name }
end
2020-03-26 14:08:03 -04:00
context " malicious group name " do
subject { put :update , params : { id : group . to_param , group : { name : " <script>alert('Attack!');</script> " } } }
it { is_expected . to render_template ( :edit ) }
it 'does not update name' do
expect { subject } . not_to change { group . reload . name }
end
end
2019-04-09 11:38:58 -04:00
end
describe 'DELETE #destroy' do
it 'deletes the group' do
delete :destroy , params : { id : group . to_param }
2020-01-27 07:08:35 -05:00
expect ( response ) . to have_gitlab_http_status ( :found )
2019-04-09 11:38:58 -04:00
end
end
end
describe 'GET #activity' do
subject { get :activity , params : { id : group . to_param } }
it_behaves_like 'disabled when using an external authorization service'
end
2020-06-19 14:08:39 -04:00
describe " GET # activity as JSON " do
include DesignManagementTestHelpers
let ( :other_project ) { create ( :project , :public , group : group ) }
def get_activity
get :activity , params : { format : :json , id : group . to_param }
end
before do
enable_design_management
issue = create ( :issue , project : project )
create ( :event , :created , project : project , target : issue )
create ( :design_event , project : project )
create ( :design_event , project : other_project )
sign_in ( user )
request . cookies [ :event_filter ] = 'all'
end
it 'returns count' do
get_activity
expect ( json_response [ 'count' ] ) . to eq ( 3 )
end
end
2019-04-09 11:38:58 -04:00
describe 'GET #issues' do
subject { get :issues , params : { id : group . to_param } }
it_behaves_like 'disabled when using an external authorization service'
end
describe 'GET #merge_requests' do
subject { get :merge_requests , params : { id : group . to_param } }
it_behaves_like 'disabled when using an external authorization service'
end
end
2020-10-21 23:08:25 -04:00
describe 'GET #unfoldered_environment_names' do
it 'shows the environment names of a public project to an anonymous user' do
public_project = create ( :project , :public , namespace : group )
create ( :environment , project : public_project , name : 'foo' )
get (
:unfoldered_environment_names ,
params : { id : group , format : :json }
)
expect ( response ) . to have_gitlab_http_status ( :ok )
expect ( json_response ) . to eq ( %w[ foo ] )
end
it 'does not show environment names of private projects to anonymous users' do
create ( :environment , project : project , name : 'foo' )
get (
:unfoldered_environment_names ,
params : { id : group , format : :json }
)
expect ( response ) . to have_gitlab_http_status ( :ok )
expect ( json_response ) . to be_empty
end
it 'shows environment names of a private project to a group member' do
create ( :environment , project : project , name : 'foo' )
sign_in ( developer )
get (
:unfoldered_environment_names ,
params : { id : group , format : :json }
)
expect ( response ) . to have_gitlab_http_status ( :ok )
expect ( json_response ) . to eq ( %w[ foo ] )
end
it 'does not show environment names of private projects to a logged-in non-member' do
alice = create ( :user )
create ( :environment , project : project , name : 'foo' )
sign_in ( alice )
get (
:unfoldered_environment_names ,
params : { id : group , format : :json }
)
expect ( response ) . to have_gitlab_http_status ( :ok )
expect ( json_response ) . to be_empty
end
end
2016-01-23 19:08:15 -05:00
end