Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
a293ae1ab5
commit
4ecd816dcb
|
|
@ -2,17 +2,6 @@ include:
|
|||
- local: .gitlab/ci/qa-common/main.gitlab-ci.yml
|
||||
- template: Verify/Browser-Performance.gitlab-ci.yml
|
||||
|
||||
.test-variables:
|
||||
variables:
|
||||
QA_GENERATE_ALLURE_REPORT: "true"
|
||||
QA_CAN_TEST_PRAEFECT: "false"
|
||||
GITLAB_USERNAME: "root"
|
||||
GITLAB_PASSWORD: "${REVIEW_APPS_ROOT_PASSWORD}"
|
||||
GITLAB_ADMIN_USERNAME: "root"
|
||||
GITLAB_ADMIN_PASSWORD: "${REVIEW_APPS_ROOT_PASSWORD}"
|
||||
GITLAB_QA_ADMIN_ACCESS_TOKEN: "${REVIEW_APPS_ROOT_TOKEN}"
|
||||
GITHUB_ACCESS_TOKEN: "${QA_GITHUB_ACCESS_TOKEN}"
|
||||
|
||||
.bundle-base:
|
||||
extends:
|
||||
- .qa-cache
|
||||
|
|
@ -20,30 +9,34 @@ include:
|
|||
before_script:
|
||||
- cd qa && bundle install
|
||||
|
||||
.review-qa-base:
|
||||
image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/debian-${DEBIAN_VERSION}-ruby-${RUBY_VERSION}:bundler-2.3-git-2.36-lfs-2.9-chrome-${CHROME_VERSION}-docker-${DOCKER_VERSION}-gcloud-383-kubectl-1.23
|
||||
review-qa-smoke:
|
||||
extends:
|
||||
- .use-docker-in-docker
|
||||
- .bundle-base
|
||||
- .test-variables
|
||||
- .default-retry
|
||||
- .rules:qa-smoke
|
||||
image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/debian-${DEBIAN_VERSION}-ruby-${RUBY_VERSION}:bundler-2.3-git-2.36-lfs-2.9-chrome-${CHROME_VERSION}-docker-${DOCKER_VERSION}-gcloud-383-kubectl-1.23
|
||||
stage: qa
|
||||
needs:
|
||||
- review-deploy
|
||||
- download-knapsack-report
|
||||
- pipeline: $PARENT_PIPELINE_ID
|
||||
job: retrieve-tests-metadata
|
||||
needs: [review-deploy]
|
||||
variables:
|
||||
GIT_LFS_SKIP_SMUDGE: 1
|
||||
WD_INSTALL_DIR: /usr/local/bin
|
||||
RSPEC_REPORT_OPTS: --force-color --order random --format documentation --format RspecJunitFormatter --out tmp/rspec-${CI_JOB_ID}.xml
|
||||
GITLAB_USERNAME: "root"
|
||||
GITLAB_PASSWORD: "${REVIEW_APPS_ROOT_PASSWORD}"
|
||||
GITLAB_ADMIN_USERNAME: "root"
|
||||
GITLAB_ADMIN_PASSWORD: "${REVIEW_APPS_ROOT_PASSWORD}"
|
||||
GITLAB_QA_ADMIN_ACCESS_TOKEN: "${REVIEW_APPS_ROOT_TOKEN}"
|
||||
GITHUB_ACCESS_TOKEN: "${QA_GITHUB_ACCESS_TOKEN}"
|
||||
COLORIZED_LOGS: "true"
|
||||
QA_GENERATE_ALLURE_REPORT: "true"
|
||||
QA_CAN_TEST_PRAEFECT: "false"
|
||||
script:
|
||||
- QA_COMMAND="bundle exec bin/qa ${QA_SCENARIO} ${QA_GITLAB_URL} -- ${QA_TESTS} ${RSPEC_REPORT_OPTS}"
|
||||
- QA_COMMAND="bundle exec bin/qa Test::Instance::Smoke ${QA_GITLAB_URL} -- ${QA_TESTS} ${RSPEC_REPORT_OPTS}"
|
||||
- echo "Running - '${QA_COMMAND}'"
|
||||
- eval "$QA_COMMAND"
|
||||
after_script:
|
||||
- |
|
||||
echo "Sentry errors for the current review-app test run can be found via following url:"
|
||||
echo "https://sentry.gitlab.net/gitlab/gitlab-review-apps/releases/$(echo "${CI_COMMIT_SHA}" | cut -c1-11)/all-events/."
|
||||
echo "https://new-sentry.gitlab.net/organizations/gitlab/releases/$(echo "${CI_COMMIT_SHA}" | cut -c1-11)/?environment=review&issuesType=all&project=19"
|
||||
artifacts:
|
||||
paths:
|
||||
- qa/tmp
|
||||
|
|
@ -52,61 +45,18 @@ include:
|
|||
expire_in: 7 days
|
||||
when: always
|
||||
|
||||
# Store knapsack report as artifact so the same report is reused across all jobs
|
||||
download-knapsack-report:
|
||||
extends:
|
||||
- .bundle-base
|
||||
- .rules:prepare-report
|
||||
stage: prepare
|
||||
script:
|
||||
- bundle exec rake "knapsack:download[qa]"
|
||||
allow_failure: true
|
||||
artifacts:
|
||||
paths:
|
||||
- qa/knapsack/review-qa-*.json
|
||||
expire_in: 1 day
|
||||
|
||||
review-qa-smoke:
|
||||
extends:
|
||||
- .review-qa-base
|
||||
- .rules:qa-smoke
|
||||
variables:
|
||||
QA_SCENARIO: Test::Instance::Smoke
|
||||
|
||||
review-qa-blocking:
|
||||
extends:
|
||||
- .review-qa-base
|
||||
- .rules:qa-blocking
|
||||
variables:
|
||||
QA_SCENARIO: Test::Instance::ReviewBlocking
|
||||
retry: 1
|
||||
review-qa-blocking-parallel:
|
||||
extends:
|
||||
- review-qa-blocking
|
||||
- .rules:qa-blocking-parallel
|
||||
parallel: 10
|
||||
|
||||
review-qa-non-blocking:
|
||||
extends:
|
||||
- .review-qa-base
|
||||
- .rules:qa-non-blocking
|
||||
variables:
|
||||
QA_SCENARIO: Test::Instance::ReviewNonBlocking
|
||||
when: manual
|
||||
allow_failure: true
|
||||
review-qa-non-blocking-parallel:
|
||||
extends:
|
||||
- review-qa-non-blocking
|
||||
- .rules:qa-non-blocking-parallel
|
||||
parallel: 5
|
||||
|
||||
browser_performance:
|
||||
extends:
|
||||
- .default-retry
|
||||
- .review:rules:review-performance
|
||||
image: ${GITLAB_DEPENDENCY_PROXY_ADDRESS}docker:${DOCKER_VERSION}-git
|
||||
services:
|
||||
- docker:${DOCKER_VERSION}-dind
|
||||
stage: qa
|
||||
needs: ["review-deploy"]
|
||||
variables:
|
||||
DOCKER_HOST: tcp://docker:2375
|
||||
DOCKER_TLS_CERTDIR: ""
|
||||
URL: environment_url.txt
|
||||
|
||||
e2e-test-report:
|
||||
|
|
@ -115,26 +65,6 @@ e2e-test-report:
|
|||
variables:
|
||||
ALLURE_RESULTS_GLOB: "qa/tmp/allure-results"
|
||||
|
||||
upload-knapsack-report:
|
||||
extends:
|
||||
- .generate-knapsack-report-base
|
||||
- .bundle-base
|
||||
stage: report
|
||||
variables:
|
||||
QA_KNAPSACK_REPORT_FILE_PATTERN: $CI_PROJECT_DIR/qa/tmp/knapsack/*/*.json
|
||||
|
||||
delete-test-resources:
|
||||
extends:
|
||||
- .bundle-base
|
||||
- .rules:prepare-report
|
||||
stage: report
|
||||
variables:
|
||||
GITLAB_QA_ACCESS_TOKEN: $REVIEW_APPS_ROOT_TOKEN
|
||||
script:
|
||||
- export GITLAB_ADDRESS="$QA_GITLAB_URL"
|
||||
- bundle exec rake "test_resources:delete[$CI_PROJECT_DIR/qa/tmp/test-resources-*.json]"
|
||||
allow_failure: true
|
||||
|
||||
notify-slack:
|
||||
extends:
|
||||
- .notify-slack
|
||||
|
|
|
|||
|
|
@ -1,19 +1,6 @@
|
|||
# ------------------------------------------
|
||||
# Conditions
|
||||
# ------------------------------------------
|
||||
# Specific specs passed
|
||||
.specific-specs: &specific-specs
|
||||
if: $QA_TESTS != ""
|
||||
|
||||
# No specific specs passed
|
||||
.all-specs: &all-specs
|
||||
if: $QA_TESTS == ""
|
||||
|
||||
# No specific specs in mr pipeline
|
||||
.all-specs-mr: &all-specs-mr
|
||||
if: '($CI_MERGE_REQUEST_EVENT_TYPE == "merged_result" || $CI_MERGE_REQUEST_EVENT_TYPE == "detached") && $QA_TESTS == ""'
|
||||
when: manual
|
||||
|
||||
# Triggered by change pattern
|
||||
.app-changes: &app-changes
|
||||
if: $APP_CHANGE_TRIGGER == "true"
|
||||
|
|
@ -47,29 +34,6 @@
|
|||
- "scripts/trigger-build.rb"
|
||||
- "{,ee/,jh/}{bin,config}/**/*.rb"
|
||||
|
||||
# ------------------------------------------
|
||||
# Conditions set
|
||||
# ------------------------------------------
|
||||
.qa-manual: &qa-manual
|
||||
when: manual
|
||||
allow_failure: true
|
||||
variables:
|
||||
QA_TESTS: ""
|
||||
|
||||
.never-when-qa-run-all-tests-or-no-specific-specs:
|
||||
- <<: *qa-run-all-tests
|
||||
when: never
|
||||
- <<: *all-specs
|
||||
when: never
|
||||
|
||||
.never-when-specific-specs-always-when-qa-run-all-tests:
|
||||
- *qa-run-all-tests
|
||||
- <<: *specific-specs
|
||||
when: manual
|
||||
allow_failure: true
|
||||
variables:
|
||||
QA_TESTS: ""
|
||||
|
||||
# ------------------------------------------
|
||||
# Prepare
|
||||
# ------------------------------------------
|
||||
|
|
@ -112,32 +76,11 @@
|
|||
QA_TESTS: "" # unset QA_TESTS even if specific tests were inferred from stage label
|
||||
- *qa-run-all-tests
|
||||
- if: $QA_SUITES =~ /Test::Instance::Smoke/
|
||||
- *qa-manual
|
||||
|
||||
.rules:qa-blocking:
|
||||
rules:
|
||||
- <<: *app-changes
|
||||
when: never
|
||||
- !reference [.never-when-qa-run-all-tests-or-no-specific-specs]
|
||||
- if: $QA_SUITES =~ /Test::Instance::ReviewBlocking/
|
||||
.rules:qa-blocking-parallel:
|
||||
rules:
|
||||
# always trigger blocking suite if review pipeline got triggered by specific changes in application code
|
||||
- <<: *app-changes
|
||||
# keep option to trigger tests manually even if no rules match
|
||||
- when: manual
|
||||
allow_failure: true
|
||||
variables:
|
||||
QA_TESTS: "" # unset QA_TESTS even if specific tests were inferred from stage label
|
||||
- !reference [.never-when-specific-specs-always-when-qa-run-all-tests]
|
||||
- if: $QA_SUITES =~ /Test::Instance::ReviewBlocking/
|
||||
|
||||
.rules:qa-non-blocking:
|
||||
rules:
|
||||
- !reference [.never-when-qa-run-all-tests-or-no-specific-specs]
|
||||
- if: $QA_SUITES =~ /Test::Instance::ReviewNonBlocking/
|
||||
.rules:qa-non-blocking-parallel:
|
||||
rules:
|
||||
- !reference [.never-when-specific-specs-always-when-qa-run-all-tests]
|
||||
- *all-specs-mr # set full suite to manual when no specific specs passed in mr
|
||||
- if: $QA_SUITES =~ /Test::Instance::ReviewNonBlocking/
|
||||
QA_TESTS: ""
|
||||
|
||||
.review:rules:review-performance:
|
||||
rules:
|
||||
|
|
|
|||
|
|
@ -2675,7 +2675,6 @@
|
|||
- <<: *if-dot-com-gitlab-org-schedule
|
||||
allow_failure: true
|
||||
variables:
|
||||
KNAPSACK_GENERATE_REPORT: "true"
|
||||
QA_SAVE_TEST_METRICS: "true"
|
||||
QA_EXPORT_TEST_METRICS: "false" # on main runs, metrics are exported to separate bucket via rake task for better consistency
|
||||
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@ class Group < Namespace
|
|||
|
||||
has_many :all_group_members, -> { non_request }, dependent: :destroy, as: :source, class_name: 'GroupMember' # rubocop:disable Cop/ActiveRecordDependent
|
||||
has_many :all_owner_members, -> { non_request.all_owners }, as: :source, class_name: 'GroupMember'
|
||||
has_many :group_members, -> { non_request.where.not(members: { access_level: Gitlab::Access::MINIMAL_ACCESS }) }, dependent: :destroy, as: :source # rubocop:disable Cop/ActiveRecordDependent
|
||||
has_many :namespace_members, -> { non_request.where.not(members: { access_level: Gitlab::Access::MINIMAL_ACCESS }).unscope(where: %i[source_id source_type]) },
|
||||
has_many :group_members, -> { non_request.non_minimal_access }, dependent: :destroy, as: :source # rubocop:disable Cop/ActiveRecordDependent
|
||||
has_many :namespace_members, -> { non_request.non_minimal_access.unscope(where: %i[source_id source_type]) },
|
||||
foreign_key: :member_namespace_id, inverse_of: :group, class_name: 'GroupMember'
|
||||
alias_method :members, :group_members
|
||||
|
||||
|
|
@ -434,7 +434,7 @@ class Group < Namespace
|
|||
end
|
||||
|
||||
def owned_by?(user)
|
||||
owners.include?(user)
|
||||
all_owner_members.exists?(user: user)
|
||||
end
|
||||
|
||||
def add_members(users, access_level, current_user: nil, expires_at: nil)
|
||||
|
|
@ -593,6 +593,12 @@ class Group < Namespace
|
|||
end
|
||||
end
|
||||
|
||||
# Only for direct and not requested members with higher access level than MIMIMAL_ACCESS
|
||||
# It returns true for non-active users
|
||||
def has_user?(user)
|
||||
group_members.exists?(user: user)
|
||||
end
|
||||
|
||||
def direct_members
|
||||
GroupMember.active_without_invites_and_requests
|
||||
.non_minimal_access
|
||||
|
|
|
|||
|
|
@ -334,7 +334,7 @@ class Project < ApplicationRecord
|
|||
has_many :authorized_users, -> { allow_cross_joins_across_databases(url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/422045') },
|
||||
through: :project_authorizations, source: :user, class_name: 'User'
|
||||
|
||||
has_many :project_members, -> { where(requested_at: nil) },
|
||||
has_many :project_members, -> { non_request },
|
||||
as: :source, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
|
||||
alias_method :members, :project_members
|
||||
has_many :namespace_members, ->(project) { where(requested_at: nil).unscope(where: %i[source_id source_type]) },
|
||||
|
|
@ -508,6 +508,7 @@ class Project < ApplicationRecord
|
|||
delegate :members, prefix: true
|
||||
delegate :add_member, :add_members, :member?
|
||||
delegate :add_guest, :add_reporter, :add_developer, :add_maintainer, :add_owner, :add_role
|
||||
delegate :has_user?
|
||||
end
|
||||
|
||||
with_options to: :namespace do
|
||||
|
|
|
|||
|
|
@ -172,6 +172,11 @@ class ProjectTeam
|
|||
max_member_access(user.id) >= min_access_level
|
||||
end
|
||||
|
||||
# Only for direct and not invited members
|
||||
def has_user?(user)
|
||||
project.project_members.exists?(user: user)
|
||||
end
|
||||
|
||||
def human_max_access(user_id)
|
||||
Gitlab::Access.human_access(max_member_access(user_id))
|
||||
end
|
||||
|
|
|
|||
|
|
@ -13,14 +13,12 @@ module Organizations
|
|||
|
||||
rule { admin }.policy do
|
||||
enable :admin_organization
|
||||
enable :create_group
|
||||
enable :read_organization
|
||||
enable :read_organization_user
|
||||
end
|
||||
|
||||
rule { organization_user }.policy do
|
||||
enable :admin_organization
|
||||
enable :create_group
|
||||
enable :read_organization
|
||||
enable :read_organization_user
|
||||
end
|
||||
|
|
|
|||
|
|
@ -92,16 +92,6 @@ module Groups
|
|||
end
|
||||
end
|
||||
|
||||
if @group.organization && !can?(current_user, :create_group, @group.organization)
|
||||
# We are unsetting this here to match behavior of invalid parent_id above and protect against possible
|
||||
# committing to the database of a value that isn't allowed.
|
||||
@group.organization = nil
|
||||
message = s_("CreateGroup|You don't have permission to create a group in the provided organization.")
|
||||
@group.errors.add(:organization_id, message)
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ module Groups
|
|||
|
||||
def ensure_ownership
|
||||
return if @new_parent_group
|
||||
return unless @group.owners.empty?
|
||||
return unless @group.all_owner_members.empty?
|
||||
|
||||
add_owner_on_transferred_group
|
||||
end
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
%span.gl-ml-5.has-tooltip{ title: _('Users') }
|
||||
= sprite_icon('users', css_class: 'gl-vertical-align-text-bottom')
|
||||
= number_with_delimiter(group.users.count)
|
||||
= number_with_delimiter(group.group_members.non_invite.count)
|
||||
|
||||
%span.gl-ml-5.visibility-icon.has-tooltip{ data: { container: 'body', placement: 'left' }, title: visibility_icon_description(group) }
|
||||
= visibility_level_icon(group.visibility_level)
|
||||
|
|
|
|||
|
|
@ -816,31 +816,30 @@ POST /groups
|
|||
|
||||
Parameters:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|---------------------------------------------------------| ------- | -------- |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `name` | string | yes | The name of the group. |
|
||||
| `path` | string | yes | The path of the group. |
|
||||
| `auto_devops_enabled` | boolean | no | Default to Auto DevOps pipeline for all projects within this group. |
|
||||
| `avatar` | mixed | no | Image file for avatar of the group. [Introduced in GitLab 12.9](https://gitlab.com/gitlab-org/gitlab/-/issues/36681) |
|
||||
| `default_branch_protection` | integer | no | See [Options for `default_branch_protection`](#options-for-default_branch_protection). Default to the global level default branch protection setting. |
|
||||
| `description` | string | no | The group's description. |
|
||||
| `emails_disabled` | boolean | no | _([Deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/127899) in GitLab 16.5.)_ Disable email notifications. Use `emails_enabled` instead. |
|
||||
| `emails_enabled` | boolean | no | Enable email notifications. |
|
||||
| `lfs_enabled` | boolean | no | Enable/disable Large File Storage (LFS) for the projects in this group. |
|
||||
| `mentions_disabled` | boolean | no | Disable the capability of a group from getting mentioned. |
|
||||
| `organization_id` | integer | no | The organization ID for the group. |
|
||||
| `parent_id` | integer | no | The parent group ID for creating nested group. |
|
||||
| Attribute | Type | Required | Description |
|
||||
| ------------------------------------------------------- | ------- | -------- | ----------- |
|
||||
| `name` | string | yes | The name of the group. |
|
||||
| `path` | string | yes | The path of the group. |
|
||||
| `auto_devops_enabled` | boolean | no | Default to Auto DevOps pipeline for all projects within this group. |
|
||||
| `avatar` | mixed | no | Image file for avatar of the group. [Introduced in GitLab 12.9](https://gitlab.com/gitlab-org/gitlab/-/issues/36681) |
|
||||
| `default_branch_protection` | integer | no | See [Options for `default_branch_protection`](#options-for-default_branch_protection). Default to the global level default branch protection setting. |
|
||||
| `description` | string | no | The group's description. |
|
||||
| `emails_disabled` | boolean | no | _([Deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/127899) in GitLab 16.5.)_ Disable email notifications. Use `emails_enabled` instead. |
|
||||
| `emails_enabled` | boolean | no | Enable email notifications. |
|
||||
| `lfs_enabled` | boolean | no | Enable/disable Large File Storage (LFS) for the projects in this group. |
|
||||
| `mentions_disabled` | boolean | no | Disable the capability of a group from getting mentioned. |
|
||||
| `parent_id` | integer | no | The parent group ID for creating nested group. |
|
||||
| `project_creation_level` | string | no | Determine if developers can create projects in the group. Can be `noone` (No one), `maintainer` (users with the Maintainer role), or `developer` (users with the Developer or Maintainer role). |
|
||||
| `request_access_enabled` | boolean | no | Allow users to request member access. |
|
||||
| `require_two_factor_authentication` | boolean | no | Require all users in this group to setup Two-factor authentication. |
|
||||
| `share_with_group_lock` | boolean | no | Prevent sharing a project with another group within this group. |
|
||||
| `subgroup_creation_level` | string | no | Allowed to [create subgroups](../user/group/subgroups/index.md#create-a-subgroup). Can be `owner` (Owners), or `maintainer` (users with the Maintainer role). |
|
||||
| `two_factor_grace_period` | integer | no | Time before Two-factor authentication is enforced (in hours). |
|
||||
| `visibility` | string | no | The group's visibility. Can be `private`, `internal`, or `public`. |
|
||||
| `membership_lock` **(PREMIUM ALL)** | boolean | no | Users cannot be added to projects in this group. |
|
||||
| `extra_shared_runners_minutes_limit` **(PREMIUM SELF)** | integer | no | Can be set by administrators only. Additional compute minutes for this group. |
|
||||
| `shared_runners_minutes_limit` **(PREMIUM SELF)** | integer | no | Can be set by administrators only. Maximum number of monthly compute minutes for this group. Can be `nil` (default; inherit system default), `0` (unlimited), or `> 0`. |
|
||||
| `wiki_access_level` **(PREMIUM ALL)** | string | no | The wiki access level. Can be `disabled`, `private`, or `enabled`. |
|
||||
| `request_access_enabled` | boolean | no | Allow users to request member access. |
|
||||
| `require_two_factor_authentication` | boolean | no | Require all users in this group to setup Two-factor authentication. |
|
||||
| `share_with_group_lock` | boolean | no | Prevent sharing a project with another group within this group. |
|
||||
| `subgroup_creation_level` | string | no | Allowed to [create subgroups](../user/group/subgroups/index.md#create-a-subgroup). Can be `owner` (Owners), or `maintainer` (users with the Maintainer role). |
|
||||
| `two_factor_grace_period` | integer | no | Time before Two-factor authentication is enforced (in hours). |
|
||||
| `visibility` | string | no | The group's visibility. Can be `private`, `internal`, or `public`. |
|
||||
| `membership_lock` **(PREMIUM ALL)** | boolean | no | Users cannot be added to projects in this group. |
|
||||
| `extra_shared_runners_minutes_limit` **(PREMIUM SELF)** | integer | no | Can be set by administrators only. Additional compute minutes for this group. |
|
||||
| `shared_runners_minutes_limit` **(PREMIUM SELF)** | integer | no | Can be set by administrators only. Maximum number of monthly compute minutes for this group. Can be `nil` (default; inherit system default), `0` (unlimited), or `> 0`. |
|
||||
| `wiki_access_level` **(PREMIUM ALL)** | string | no | The wiki access level. Can be `disabled`, `private`, or `enabled`. |
|
||||
|
||||
### Options for `default_branch_protection`
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ module API
|
|||
expose :full_name, :full_path
|
||||
expose :created_at
|
||||
expose :parent_id
|
||||
expose :organization_id
|
||||
expose :shared_runners_setting
|
||||
|
||||
expose :custom_attributes, using: 'API::Entities::CustomAttribute', if: :with_custom_attributes
|
||||
|
|
|
|||
|
|
@ -213,15 +213,11 @@ module API
|
|||
requires :name, type: String, desc: 'The name of the group'
|
||||
requires :path, type: String, desc: 'The path of the group'
|
||||
optional :parent_id, type: Integer, desc: 'The parent group id for creating nested group'
|
||||
optional :organization_id, type: Integer, desc: 'The organization id for the group'
|
||||
|
||||
use :optional_params
|
||||
end
|
||||
post feature_category: :groups_and_projects, urgency: :low do
|
||||
organization = find_organization!(params[:organization_id]) if params[:organization_id].present?
|
||||
authorize! :create_group, organization if organization
|
||||
|
||||
parent_group = find_group!(params[:parent_id], organization: organization) if params[:parent_id].present?
|
||||
parent_group = find_group!(params[:parent_id]) if params[:parent_id].present?
|
||||
if parent_group
|
||||
authorize! :create_subgroup, parent_group
|
||||
else
|
||||
|
|
|
|||
|
|
@ -211,25 +211,18 @@ module API
|
|||
not_found!('Pipeline')
|
||||
end
|
||||
|
||||
def find_organization!(id)
|
||||
organization = Organizations::Organization.find_by_id(id)
|
||||
check_organization_access(organization)
|
||||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def find_group(id, organization: nil)
|
||||
collection = organization.present? ? Group.in_organization(organization) : Group.all
|
||||
|
||||
def find_group(id)
|
||||
if id.to_s =~ INTEGER_ID_REGEX
|
||||
collection.find_by(id: id)
|
||||
Group.find_by(id: id)
|
||||
else
|
||||
collection.find_by_full_path(id)
|
||||
Group.find_by_full_path(id)
|
||||
end
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
def find_group!(id, organization: nil)
|
||||
group = find_group(id, organization: organization)
|
||||
def find_group!(id)
|
||||
group = find_group(id)
|
||||
check_group_access(group)
|
||||
end
|
||||
|
||||
|
|
@ -842,12 +835,6 @@ module API
|
|||
@sudo_identifier ||= params[SUDO_PARAM] || env[SUDO_HEADER]
|
||||
end
|
||||
|
||||
def check_organization_access(organization)
|
||||
return organization if can?(current_user, :read_organization, organization)
|
||||
|
||||
not_found!('Organization')
|
||||
end
|
||||
|
||||
def secret_token
|
||||
Gitlab::Shell.secret_token
|
||||
end
|
||||
|
|
|
|||
|
|
@ -14539,9 +14539,6 @@ msgstr ""
|
|||
msgid "CreateGitTag|Set tag message"
|
||||
msgstr ""
|
||||
|
||||
msgid "CreateGroup|You don't have permission to create a group in the provided organization."
|
||||
msgstr ""
|
||||
|
||||
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe API::Entities::Group, feature_category: :groups_and_projects do
|
||||
let_it_be(:group) do
|
||||
base_group = create(:group) { |g| create(:project_statistics, namespace_id: g.id) }
|
||||
Group.with_statistics.find(base_group.id)
|
||||
end
|
||||
|
||||
subject(:json) { described_class.new(group, { with_custom_attributes: true, statistics: true }).as_json }
|
||||
|
||||
it 'returns expected data' do
|
||||
expect(json.keys).to(
|
||||
include(
|
||||
:organization_id, :path, :description, :visibility, :share_with_group_lock, :require_two_factor_authentication,
|
||||
:two_factor_grace_period, :project_creation_level, :auto_devops_enabled,
|
||||
:subgroup_creation_level, :emails_disabled, :emails_enabled, :lfs_enabled, :default_branch_protection,
|
||||
:default_branch_protection_defaults, :avatar_url, :request_access_enabled, :full_name, :full_path, :created_at,
|
||||
:parent_id, :organization_id, :shared_runners_setting, :custom_attributes, :statistics
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
@ -406,37 +406,6 @@ RSpec.describe API::Helpers, feature_category: :shared do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#find_organization!' do
|
||||
let_it_be(:organization) { create(:organization) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
allow(helper).to receive(:current_user).and_return(user)
|
||||
allow(helper).to receive(:initial_current_user).and_return(user)
|
||||
end
|
||||
|
||||
context 'when user is authenticated' do
|
||||
it 'returns requested organization' do
|
||||
expect(helper.find_organization!(organization.id)).to eq(organization)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user is not authenticated' do
|
||||
let(:user) { nil }
|
||||
|
||||
it 'returns requested organization' do
|
||||
expect(helper.find_organization!(organization.id)).to eq(organization)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when organization does not exist' do
|
||||
it 'returns nil' do
|
||||
expect(helper).to receive(:render_api_error!).with('404 Organization Not Found', 404)
|
||||
expect(helper.find_organization!(non_existing_record_id)).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#find_group!' do
|
||||
let_it_be(:group) { create(:group, :public) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
|
@ -488,7 +457,7 @@ RSpec.describe API::Helpers, feature_category: :shared do
|
|||
end
|
||||
end
|
||||
|
||||
context 'with support for IDs and paths as arguments' do
|
||||
context 'support for IDs and paths as arguments' do
|
||||
let_it_be(:group) { create(:group) }
|
||||
|
||||
let(:user) { group.first_owner }
|
||||
|
|
@ -534,34 +503,6 @@ RSpec.describe API::Helpers, feature_category: :shared do
|
|||
it_behaves_like 'group finder'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with support for organization as an argument' do
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:organization) { create(:organization) }
|
||||
|
||||
before do
|
||||
allow(helper).to receive(:current_user).and_return(group.first_owner)
|
||||
allow(helper).to receive(:job_token_authentication?).and_return(false)
|
||||
allow(helper).to receive(:authenticate_non_public?).and_return(false)
|
||||
end
|
||||
|
||||
subject { helper.find_group!(group.id, organization: organization) }
|
||||
|
||||
context 'when group exists in the organization' do
|
||||
before do
|
||||
group.update!(organization: organization)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(group) }
|
||||
end
|
||||
|
||||
context 'when group does not exist in the organization' do
|
||||
it 'returns nil' do
|
||||
expect(helper).to receive(:render_api_error!).with('404 Group Not Found', 404)
|
||||
is_expected.to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#find_group_by_full_path!' do
|
||||
|
|
|
|||
|
|
@ -1642,6 +1642,46 @@ RSpec.describe Group, feature_category: :groups_and_projects do
|
|||
it { expect(subject.parent).to be_kind_of(described_class) }
|
||||
end
|
||||
|
||||
describe '#has_user?' do
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:subgroup) { create(:group, parent: group) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:user2) { create(:user) }
|
||||
|
||||
subject { group.has_user?(user) }
|
||||
|
||||
context 'when the user is a member' do
|
||||
before_all do
|
||||
group.add_developer(user)
|
||||
end
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
it { expect(group.has_user?(user2)).to be_falsey }
|
||||
|
||||
it 'returns false for subgroup' do
|
||||
expect(subgroup.has_user?(user)).to be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the user is a member with minimal access' do
|
||||
before_all do
|
||||
group.add_member(user, GroupMember::MINIMAL_ACCESS)
|
||||
end
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
|
||||
context 'when the user has requested membership' do
|
||||
before_all do
|
||||
create(:group_member, :developer, :access_request, user: user, source: group)
|
||||
end
|
||||
|
||||
it 'returns false' do
|
||||
expect(subject).to be_falsey
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#member?' do
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
|
|
|||
|
|
@ -1121,6 +1121,8 @@ RSpec.describe Project, factory_default: :keep, feature_category: :groups_and_pr
|
|||
end
|
||||
|
||||
it { is_expected.to delegate_method(:members).to(:team).with_prefix(true) }
|
||||
it { is_expected.to delegate_method(:has_user?).to(:team) }
|
||||
it { is_expected.to delegate_method(:member?).to(:team) }
|
||||
it { is_expected.to delegate_method(:name).to(:owner).with_prefix(true).allow_nil }
|
||||
it { is_expected.to delegate_method(:root_ancestor).to(:namespace).allow_nil }
|
||||
it { is_expected.to delegate_method(:certificate_based_clusters_enabled?).to(:namespace).allow_nil }
|
||||
|
|
|
|||
|
|
@ -341,22 +341,52 @@ RSpec.describe ProjectTeam, feature_category: :groups_and_projects do
|
|||
end
|
||||
end
|
||||
|
||||
describe "#human_max_access" do
|
||||
it 'returns Maintainer role' do
|
||||
user = create(:user)
|
||||
group = create(:group)
|
||||
project = create(:project, namespace: group)
|
||||
describe '#has_user?' do
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:project) { create(:project, namespace: group) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:user2) { create(:user) }
|
||||
|
||||
subject { project.team.has_user?(user) }
|
||||
|
||||
context 'when the user is a member' do
|
||||
before_all do
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
it { expect(group.has_user?(user2)).to be_falsey }
|
||||
end
|
||||
|
||||
context 'when user is a member with minimal access' do
|
||||
before_all do
|
||||
project.add_member(user, GroupMember::MINIMAL_ACCESS)
|
||||
end
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
|
||||
context 'when user is not a direct member of the project' do
|
||||
before_all do
|
||||
create(:group_member, :developer, user: user, source: group)
|
||||
end
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
end
|
||||
|
||||
describe "#human_max_access" do
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:project) { create(:project, namespace: group) }
|
||||
|
||||
it 'returns Maintainer role' do
|
||||
group.add_maintainer(user)
|
||||
|
||||
expect(project.team.human_max_access(user.id)).to eq 'Maintainer'
|
||||
end
|
||||
|
||||
it 'returns Owner role' do
|
||||
user = create(:user)
|
||||
group = create(:group)
|
||||
project = create(:project, namespace: group)
|
||||
|
||||
group.add_owner(user)
|
||||
|
||||
expect(project.team.human_max_access(user.id)).to eq 'Owner'
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ RSpec.describe Organizations::OrganizationPolicy, feature_category: :cell do
|
|||
|
||||
context 'when admin mode is enabled', :enable_admin_mode do
|
||||
it { is_expected.to be_allowed(:admin_organization) }
|
||||
it { is_expected.to be_allowed(:create_group) }
|
||||
it { is_expected.to be_allowed(:read_organization) }
|
||||
it { is_expected.to be_allowed(:read_organization_user) }
|
||||
end
|
||||
|
|
@ -37,14 +36,12 @@ RSpec.describe Organizations::OrganizationPolicy, feature_category: :cell do
|
|||
end
|
||||
|
||||
it { is_expected.to be_allowed(:admin_organization) }
|
||||
it { is_expected.to be_allowed(:create_group) }
|
||||
it { is_expected.to be_allowed(:read_organization) }
|
||||
it { is_expected.to be_allowed(:read_organization_user) }
|
||||
end
|
||||
|
||||
context 'when the user is not part of the organization' do
|
||||
it { is_expected.to be_disallowed(:admin_organization) }
|
||||
it { is_expected.to be_disallowed(:create_group) }
|
||||
it { is_expected.to be_disallowed(:read_organization_user) }
|
||||
# All organizations are currently public, and hence they are allowed to be read
|
||||
# even if the user is not a part of the organization.
|
||||
|
|
|
|||
|
|
@ -1937,59 +1937,6 @@ RSpec.describe API::Groups, feature_category: :groups_and_projects do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when group is within a provided organization' do
|
||||
let_it_be(:organization) { create(:organization) }
|
||||
|
||||
context 'when user is an organization user' do
|
||||
before_all do
|
||||
create(:organization_user, user: user3, organization: organization)
|
||||
end
|
||||
|
||||
it 'creates group within organization' do
|
||||
post api('/groups', user3), params: attributes_for_group_api(organization_id: organization.id)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:created)
|
||||
expect(json_response['organization_id']).to eq(organization.id)
|
||||
end
|
||||
|
||||
context 'when parent_group is not part of the organization' do
|
||||
it 'does not create the group with not_found' do
|
||||
post(
|
||||
api('/groups', user3),
|
||||
params: attributes_for_group_api(parent_id: group2.id, organization_id: organization.id)
|
||||
)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when organization does not exist' do
|
||||
it 'does not create the group with not_found' do
|
||||
post api('/groups', user3), params: attributes_for_group_api(organization_id: non_existing_record_id)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user is not an organization user' do
|
||||
it 'does not create the group' do
|
||||
post api('/groups', user3), params: attributes_for_group_api(organization_id: organization.id)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user is an admin' do
|
||||
it 'creates group within organization' do
|
||||
post api('/groups', admin, admin_mode: true), params: attributes_for_group_api(organization_id: organization.id)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:created)
|
||||
expect(json_response['organization_id']).to eq(organization.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when authenticated as user with group permissions" do
|
||||
it "creates group", :aggregate_failures do
|
||||
group = attributes_for_group_api request_access_enabled: false
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ RSpec.describe Groups::CreateService, '#execute', feature_category: :groups_and_
|
|||
let!(:user) { create(:user) }
|
||||
let!(:group_params) { { path: "group_path", visibility_level: Gitlab::VisibilityLevel::PUBLIC } }
|
||||
|
||||
subject(:execute) { service.execute }
|
||||
subject { service.execute }
|
||||
|
||||
shared_examples 'has sync-ed traversal_ids' do
|
||||
specify { expect(subject.reload.traversal_ids).to eq([subject.parent&.traversal_ids, subject.id].flatten.compact) }
|
||||
|
|
@ -119,37 +119,6 @@ RSpec.describe Groups::CreateService, '#execute', feature_category: :groups_and_
|
|||
end
|
||||
end
|
||||
|
||||
describe 'creating a group within a provided organization' do
|
||||
let_it_be(:organization) { create(:organization) }
|
||||
|
||||
let(:current_user) { user }
|
||||
let(:params) { group_params.merge(organization_id: organization.id) }
|
||||
let(:service) { described_class.new(current_user, params) }
|
||||
|
||||
context 'when user can create the group' do
|
||||
before do
|
||||
create(:organization_user, user: user, organization: organization)
|
||||
end
|
||||
|
||||
it { is_expected.to be_persisted }
|
||||
end
|
||||
|
||||
context 'when user is an admin', :enable_admin_mode do
|
||||
let(:current_user) { create(:admin) }
|
||||
|
||||
it { is_expected.to be_persisted }
|
||||
end
|
||||
|
||||
context 'when user can not create the group' do
|
||||
it 'does not save group and returns an error' do
|
||||
expect(execute).not_to be_persisted
|
||||
expect(execute.errors[:organization_id].first)
|
||||
.to eq(s_("CreateGroup|You don't have permission to create a group in the provided organization."))
|
||||
expect(execute.organization_id).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'creating subgroup' do
|
||||
let!(:group) { create(:group) }
|
||||
let!(:service) { described_class.new(user, group_params.merge(parent_id: group.id)) }
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ RSpec.describe Members::CreateService, :aggregate_failures, :clean_gitlab_redis_
|
|||
|
||||
it 'adds a user to members' do
|
||||
expect(execute_service[:status]).to eq(:success)
|
||||
expect(source.users).to include member
|
||||
expect(source).to have_user(member)
|
||||
expect(Onboarding::Progress.completed?(source, :user_added)).to be(true)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ RSpec.describe ResourceAccessTokens::RevokeService, feature_category: :system_ac
|
|||
it 'removes membership of bot user' do
|
||||
subject
|
||||
|
||||
expect(resource.reload.users).not_to include(resource_bot)
|
||||
expect(resource.reload).not_to have_user(resource_bot)
|
||||
end
|
||||
|
||||
it 'initiates user removal' do
|
||||
|
|
@ -56,7 +56,7 @@ RSpec.describe ResourceAccessTokens::RevokeService, feature_category: :system_ac
|
|||
it 'does not remove bot from member list' do
|
||||
subject
|
||||
|
||||
expect(resource.reload.users).to include(resource_bot)
|
||||
expect(resource.reload).to have_user(resource_bot)
|
||||
end
|
||||
|
||||
it 'does not transfer issuables of bot user to ghost user' do
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec::Matchers.define :have_user do |user|
|
||||
match do |resource|
|
||||
raise ArgumentError, 'Unknown resource type' unless resource.is_a?(Group) || resource.is_a?(Project)
|
||||
|
||||
expect(resource.has_user?(user)).to be_truthy
|
||||
end
|
||||
|
||||
failure_message do |group|
|
||||
"Expected #{group} to have the user #{user} among its members"
|
||||
end
|
||||
end
|
||||
|
|
@ -108,7 +108,7 @@ RSpec.shared_examples_for "member creation" do
|
|||
it 'does not update the member' do
|
||||
member = described_class.add_member(source, project_bot, :maintainer, current_user: user)
|
||||
|
||||
expect(source.users.reload).to include(project_bot)
|
||||
expect(source.reload).to have_user(project_bot)
|
||||
expect(member).to be_persisted
|
||||
expect(member.access_level).to eq(Gitlab::Access::DEVELOPER)
|
||||
expect(member.errors.full_messages).to include(/not authorized to update member/)
|
||||
|
|
@ -119,7 +119,7 @@ RSpec.shared_examples_for "member creation" do
|
|||
it 'adds the member' do
|
||||
member = described_class.add_member(source, project_bot, :maintainer, current_user: user)
|
||||
|
||||
expect(source.users.reload).to include(project_bot)
|
||||
expect(source.reload).to have_user(project_bot)
|
||||
expect(member).to be_persisted
|
||||
end
|
||||
end
|
||||
|
|
@ -130,7 +130,7 @@ RSpec.shared_examples_for "member creation" do
|
|||
member = described_class.add_member(source, user, :maintainer, current_user: admin)
|
||||
|
||||
expect(member).to be_persisted
|
||||
expect(source.users.reload).to include(user)
|
||||
expect(source.reload).to have_user(user)
|
||||
expect(member.created_by).to eq(admin)
|
||||
end
|
||||
end
|
||||
|
|
@ -140,7 +140,7 @@ RSpec.shared_examples_for "member creation" do
|
|||
member = described_class.add_member(source, user, :maintainer, current_user: admin)
|
||||
|
||||
expect(member).not_to be_persisted
|
||||
expect(source.users.reload).not_to include(user)
|
||||
expect(source).not_to have_user(user)
|
||||
expect(member.errors.full_messages).to include(/not authorized to create member/)
|
||||
end
|
||||
end
|
||||
|
|
@ -153,52 +153,52 @@ RSpec.shared_examples_for "member creation" do
|
|||
|
||||
described_class.access_levels.each do |sym_key, int_access_level|
|
||||
it "accepts the :#{sym_key} symbol as access level", :aggregate_failures do
|
||||
expect(source.users).not_to include(user)
|
||||
expect(source).not_to have_user(user)
|
||||
|
||||
member = described_class.add_member(source, user.id, sym_key)
|
||||
|
||||
expect(member.access_level).to eq(int_access_level)
|
||||
expect(source.users.reload).to include(user)
|
||||
expect(source.reload).to have_user(user)
|
||||
end
|
||||
|
||||
it "accepts the #{int_access_level} integer as access level", :aggregate_failures do
|
||||
expect(source.users).not_to include(user)
|
||||
expect(source).not_to have_user(user)
|
||||
|
||||
member = described_class.add_member(source, user.id, int_access_level)
|
||||
|
||||
expect(member.access_level).to eq(int_access_level)
|
||||
expect(source.users.reload).to include(user)
|
||||
expect(source.reload).to have_user(user)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with no current_user' do
|
||||
context 'when called with a known user id' do
|
||||
it 'adds the user as a member' do
|
||||
expect(source.users).not_to include(user)
|
||||
expect(source).not_to have_user(user)
|
||||
|
||||
described_class.add_member(source, user.id, :maintainer)
|
||||
|
||||
expect(source.users.reload).to include(user)
|
||||
expect(source.reload).to have_user(user)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when called with an unknown user id' do
|
||||
it 'does not add the user as a member' do
|
||||
expect(source.users).not_to include(user)
|
||||
expect(source).not_to have_user(user)
|
||||
|
||||
described_class.add_member(source, non_existing_record_id, :maintainer)
|
||||
|
||||
expect(source.users.reload).not_to include(user)
|
||||
expect(source.reload).not_to have_user(user)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when called with a user object' do
|
||||
it 'adds the user as a member' do
|
||||
expect(source.users).not_to include(user)
|
||||
expect(source).not_to have_user(user)
|
||||
|
||||
described_class.add_member(source, user, :maintainer)
|
||||
|
||||
expect(source.users.reload).to include(user)
|
||||
expect(source.reload).to have_user(user)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -208,29 +208,29 @@ RSpec.shared_examples_for "member creation" do
|
|||
end
|
||||
|
||||
it 'adds the requester as a member', :aggregate_failures do
|
||||
expect(source.users).not_to include(user)
|
||||
expect(source.reload).not_to have_user(user)
|
||||
expect(source.requesters.exists?(user_id: user)).to eq(true)
|
||||
|
||||
described_class.add_member(source, user, :maintainer)
|
||||
|
||||
expect(source.users.reload).to include(user)
|
||||
expect(source.requesters.reload.exists?(user_id: user)).to eq(false)
|
||||
expect(source.reload).to have_user(user)
|
||||
expect(source.requesters.exists?(user_id: user)).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when called with a known user email' do
|
||||
it 'adds the user as a member' do
|
||||
expect(source.users).not_to include(user)
|
||||
expect(source).not_to have_user(user)
|
||||
|
||||
described_class.add_member(source, user.email, :maintainer)
|
||||
|
||||
expect(source.users.reload).to include(user)
|
||||
expect(source.reload).to have_user(user)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when called with an unknown user email' do
|
||||
it 'creates an invited member' do
|
||||
expect(source.users).not_to include(user)
|
||||
expect(source).not_to have_user(user)
|
||||
|
||||
described_class.add_member(source, 'user@example.com', :maintainer)
|
||||
|
||||
|
|
@ -245,18 +245,18 @@ RSpec.shared_examples_for "member creation" do
|
|||
described_class.add_member(source, email_starting_with_number, :maintainer)
|
||||
|
||||
expect(source.members.invite.pluck(:invite_email)).to include(email_starting_with_number)
|
||||
expect(source.users.reload).not_to include(user)
|
||||
expect(source.reload).not_to have_user(user)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when current_user can update member', :enable_admin_mode do
|
||||
it 'creates the member' do
|
||||
expect(source.users).not_to include(user)
|
||||
expect(source).not_to have_user(user)
|
||||
|
||||
described_class.add_member(source, user, :maintainer, current_user: admin)
|
||||
|
||||
expect(source.users.reload).to include(user)
|
||||
expect(source.reload).to have_user(user)
|
||||
end
|
||||
|
||||
context 'when called with a requester user object' do
|
||||
|
|
@ -265,12 +265,12 @@ RSpec.shared_examples_for "member creation" do
|
|||
end
|
||||
|
||||
it 'adds the requester as a member', :aggregate_failures do
|
||||
expect(source.users).not_to include(user)
|
||||
expect(source).not_to have_user(user)
|
||||
expect(source.requesters.exists?(user_id: user)).to be_truthy
|
||||
|
||||
described_class.add_member(source, user, :maintainer, current_user: admin)
|
||||
|
||||
expect(source.users.reload).to include(user)
|
||||
expect(source.reload).to have_user(user)
|
||||
expect(source.requesters.reload.exists?(user_id: user)).to be_falsy
|
||||
end
|
||||
end
|
||||
|
|
@ -278,11 +278,11 @@ RSpec.shared_examples_for "member creation" do
|
|||
|
||||
context 'when current_user cannot update member' do
|
||||
it 'does not create the member', :aggregate_failures do
|
||||
expect(source.users).not_to include(user)
|
||||
expect(source).not_to have_user(user)
|
||||
|
||||
member = described_class.add_member(source, user, :maintainer, current_user: user)
|
||||
|
||||
expect(source.users.reload).not_to include(user)
|
||||
expect(source.reload).not_to have_user(user)
|
||||
expect(member).not_to be_persisted
|
||||
end
|
||||
|
||||
|
|
@ -292,12 +292,12 @@ RSpec.shared_examples_for "member creation" do
|
|||
end
|
||||
|
||||
it 'does not destroy the requester', :aggregate_failures do
|
||||
expect(source.users).not_to include(user)
|
||||
expect(source).not_to have_user(user)
|
||||
expect(source.requesters.exists?(user_id: user)).to be_truthy
|
||||
|
||||
described_class.add_member(source, user, :maintainer, current_user: user)
|
||||
|
||||
expect(source.users.reload).not_to include(user)
|
||||
expect(source.reload).not_to have_user(user)
|
||||
expect(source.requesters.exists?(user_id: user)).to be_truthy
|
||||
end
|
||||
end
|
||||
|
|
@ -311,7 +311,7 @@ RSpec.shared_examples_for "member creation" do
|
|||
|
||||
context 'with no current_user' do
|
||||
it 'updates the member' do
|
||||
expect(source.users).to include(user)
|
||||
expect(source).to have_user(user)
|
||||
|
||||
described_class.add_member(source, user, :maintainer)
|
||||
|
||||
|
|
@ -321,7 +321,7 @@ RSpec.shared_examples_for "member creation" do
|
|||
|
||||
context 'when current_user can update member', :enable_admin_mode do
|
||||
it 'updates the member' do
|
||||
expect(source.users).to include(user)
|
||||
expect(source).to have_user(user)
|
||||
|
||||
described_class.add_member(source, user, :maintainer, current_user: admin)
|
||||
|
||||
|
|
@ -331,7 +331,7 @@ RSpec.shared_examples_for "member creation" do
|
|||
|
||||
context 'when current_user cannot update member' do
|
||||
it 'does not update the member' do
|
||||
expect(source.users).to include(user)
|
||||
expect(source).to have_user(user)
|
||||
|
||||
described_class.add_member(source, user, :maintainer, current_user: user)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue