Running with gitlab-runner 13.3.0-rc1 (669fc507)
  on docker-auto-scale-com 8a6210b8
section_start:1598369245:prepare_executor
Preparing the "docker+machine" executor
Using Docker executor with image registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.28-lfs-2.9-chrome-84-node-12.x-yarn-1.21-postgresql-11-graphicsmagick-1.3.34 ...
Starting service postgres:11.6 ...
Pulling docker image postgres:11.6 ...
Using docker image sha256:2c963c0eb8c6efa49bb8352ea446f248d208d674cfc34fc9ea275b5f99f8dedd for postgres:11.6 ...
Starting service redis:4.0-alpine ...
Pulling docker image redis:4.0-alpine ...
Using docker image sha256:e3dd0e49bca555d559ca2e97f06a1efa108ebd230fddcb17606723994f18ae3b for redis:4.0-alpine ...
Waiting for services to be up and running...
Authenticating with credentials from job payload (GitLab Registry)
Pulling docker image registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.28-lfs-2.9-chrome-84-node-12.x-yarn-1.21-postgresql-11-graphicsmagick-1.3.34 ...
Using docker image sha256:cd688957c9207f55272d81be6d4908692d78233833e3f09a9c0899b2f24eb3ff for registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.28-lfs-2.9-chrome-84-node-12.x-yarn-1.21-postgresql-11-graphicsmagick-1.3.34 ...
section_end:1598369264:prepare_executor
section_start:1598369264:prepare_script
Preparing environment
Running on runner-8a6210b8-project-278964-concurrent-0 via runner-8a6210b8-gsrm-1598364619-ba2cb037...
section_end:1598369268:prepare_script
section_start:1598369268:get_sources
Getting source from Git repository
$ eval "$CI_PRE_CLONE_SCRIPT"
Downloading archived master...
Connecting to storage.googleapis.com (172.217.204.128:443)
saving to '/tmp/gitlab.tar.gz'
gitlab.tar.gz          3% |*                               | 37.8M  0:00:25 ETA
gitlab.tar.gz         40% |************                    |  402M  0:00:02 ETA
gitlab.tar.gz         74% |***********************         |  749M  0:00:01 ETA
gitlab.tar.gz        100% |********************************| 1004M  0:00:00 ETA
'/tmp/gitlab.tar.gz' saved
Extracting tarball into /builds/gitlab-org/gitlab...
Fetching changes with git depth set to 20...
Reinitialized existing Git repository in /builds/gitlab-org/gitlab/.git/
Created fresh repository.
Checking out dbc1a472 as master...

Skipping Git submodules setup
section_end:1598369341:get_sources
section_start:1598369341:restore_cache
Restoring cache
Checking cache for rails-v2-3...
Downloading cache.zip from https://storage.googleapis.com/gitlab-com-runners-cache/project/278964/rails-v2-3 
Successfully extracted cache
section_end:1598369455:restore_cache
section_start:1598369455:download_artifacts
Downloading artifacts
Downloading artifacts for compile-test-assets (702959600)...
Downloading artifacts from coordinator... ok        id=702959600 responseStatus=200 OK token=efr6uzds
Downloading artifacts for retrieve-tests-metadata (702959602)...
Downloading artifacts from coordinator... ok        id=702959602 responseStatus=200 OK token=VgekGNeS
Downloading artifacts for setup-test-env (702959601)...
Downloading artifacts from coordinator... ok        id=702959601 responseStatus=200 OK token=Fwwkf6XV
WARNING: tmp/tests/gitlab-shell/.gitlab_shell_secret: chmod tmp/tests/gitlab-shell/.gitlab_shell_secret: no such file or directory (suppressing repeats) 
section_end:1598369499:download_artifacts
section_start:1598369499:step_script
Executing "step_script" stage of the job script
$ [ "$FOSS_ONLY" = "1" ] && rm -rf ee/ qa/spec/ee/ qa/qa/specs/features/ee/ qa/qa/ee/ qa/qa/ee.rb
$ export GOPATH=$CI_PROJECT_DIR/.go
$ mkdir -p $GOPATH
$ source scripts/utils.sh
$ source scripts/prepare_build.sh
Bundler version 1.17.2
$ bundle install --clean --without=production --without=development --jobs=2 --path=vendor --retry=3 --quiet
==> 'bundle install --clean --without=production --without=development --jobs=2 --path=vendor --retry=3 --quiet' succeeded in 1 seconds.
$ bundle check
Warning: the running version of Bundler (1.17.2) is older than the version that created the lockfile (1.17.3). We suggest you upgrade to the latest version of Bundler by running `gem install bundler`.
The Gemfile's dependencies are satisfied
==> 'bundle check' succeeded in 1 seconds.
$ bundle pristine pg
Warning: the running version of Bundler (1.17.2) is older than the version that created the lockfile (1.17.3). We suggest you upgrade to the latest version of Bundler by running `gem install bundler`.
Installing pg 1.2.2 with native extensions
==> 'bundle pristine pg' succeeded in 11 seconds.
$ gem install knapsack --no-document
Successfully installed knapsack-1.18.0
1 gem installed
==> 'gem install knapsack --no-document' succeeded in 1 seconds.
$ setup_db_user_only
CREATE ROLE
GRANT
==> 'setup_db_user_only' succeeded in 0 seconds.
$ bundle exec rake db:drop db:create db:structure:load db:migrate gitlab:db:setup_ee
Dropped database 'gitlabhq_test'
Created database 'gitlabhq_test'
Dropped database 'gitlabhq_geo_test'
Created database 'gitlabhq_geo_test'
==> 'bundle exec rake db:drop db:create db:structure:load db:migrate gitlab:db:setup_ee' succeeded in 30 seconds.
$ run_timed_command "scripts/gitaly-test-build"
$ scripts/gitaly-test-build
Settings are listed in order of priority. The top value will be used.
retry
Set for your local app (/builds/gitlab-org/gitlab/tmp/tests/gitaly/ruby/.bundle/config/config): 3

path
Set for your local app (/builds/gitlab-org/gitlab/tmp/tests/gitaly/ruby/.bundle/config/config): "/builds/gitlab-org/gitlab/vendor/gitaly-ruby"

jobs
Set for your local app (/builds/gitlab-org/gitlab/tmp/tests/gitaly/ruby/.bundle/config/config): 4

app_config
Set via BUNDLE_APP_CONFIG: "/builds/gitlab-org/gitlab/tmp/tests/gitaly/ruby/.bundle/config"

gemfile
Set via BUNDLE_GEMFILE: "/builds/gitlab-org/gitlab/tmp/tests/gitaly/ruby/Gemfile"

flags
Set via BUNDLE_FLAGS: "--jobs=4 --retry=3 --quiet --path=/builds/gitlab-org/gitlab/vendor/gitaly-ruby"

silence_root_warning
Set via BUNDLE_SILENCE_ROOT_WARNING: true

go install -ldflags '-X gitlab.com/gitlab-org/gitaly/internal/version.version=13.3.0-rc5 -X gitlab.com/gitlab-org/gitaly/internal/version.buildtime=20200825.153228' -tags "tracer_static,tracer_static_jaeger,continuous_profiler_stackdriver" gitlab.com/gitlab-org/gitaly/cmd/praefect gitlab.com/gitlab-org/gitaly/cmd/gitaly-hooks gitlab.com/gitlab-org/gitaly/cmd/gitaly-blackbox gitlab.com/gitlab-org/gitaly/cmd/gitaly gitlab.com/gitlab-org/gitaly/cmd/gitaly-debug gitlab.com/gitlab-org/gitaly/cmd/gitaly-ssh gitlab.com/gitlab-org/gitaly/cmd/gitaly-wrapper
install /builds/gitlab-org/gitlab/tmp/tests/gitaly/_build/bin/praefect /builds/gitlab-org/gitlab/tmp/tests/gitaly/_build/bin/gitaly-hooks /builds/gitlab-org/gitlab/tmp/tests/gitaly/_build/bin/gitaly-blackbox /builds/gitlab-org/gitlab/tmp/tests/gitaly/_build/bin/gitaly /builds/gitlab-org/gitlab/tmp/tests/gitaly/_build/bin/gitaly-debug /builds/gitlab-org/gitlab/tmp/tests/gitaly/_build/bin/gitaly-ssh /builds/gitlab-org/gitlab/tmp/tests/gitaly/_build/bin/gitaly-wrapper /builds/gitlab-org/gitlab/tmp/tests/gitaly
Checking gitaly-ruby Gemfile...
Checking gitaly-ruby bundle...
Warning: the running version of Bundler (1.17.2) is older than the version that created the lockfile (1.17.3). We suggest you upgrade to the latest version of Bundler by running `gem install bundler`.
The Gemfile's dependencies are satisfied
Trying to connect to gitaly: ........ OK
Trying to connect to praefect: ........ OK
==> 'scripts/gitaly-test-build' succeeded in 46 seconds.
$ run_timed_command "scripts/gitaly-test-spawn"
$ scripts/gitaly-test-spawn
Checking gitaly-ruby Gemfile...
Checking gitaly-ruby bundle...
Warning: the running version of Bundler (1.17.2) is older than the version that created the lockfile (1.17.3). We suggest you upgrade to the latest version of Bundler by running `gem install bundler`.
The Gemfile's dependencies are satisfied
Trying to connect to gitaly: ........ OK
Trying to connect to praefect: ....... OK
==> 'scripts/gitaly-test-spawn' succeeded in 2 seconds.
$ source scripts/rspec_helpers.sh
$ rspec_paralellized_job "--tag ~quarantine --tag ~geo --tag ~level:migration"
KNAPSACK_TEST_FILE_PATTERN: spec/{controllers,mailers,requests}{,/**/}*_spec.rb
RSpec's reporter has already been initialized with #<IO:<STDERR>> as the deprecation stream, so your change to `deprecation_stream` will be ignored. You should configure it earlier for it to take effect, or use the `--deprecation-out` CLI option. (Called from /builds/gitlab-org/gitlab/spec/support/rspec.rb:12:in `block in <top (required)>')
Knapsack report generator started!
Run options:
  include {:focus=>true}
  exclude {:quarantine=>true, :geo=>true, :level=>"migration"}

All examples were filtered out; ignoring {:focus=>true}

==> Setting up gitlab-test bare repository in /builds/gitlab-org/gitlab/tmp/tests/gitlab-test_bare...
    /builds/gitlab-org/gitlab/tmp/tests/gitlab-test_bare set up in 0.042303689 seconds...

API::MergeRequests
  route shadowing
    does not occur
  GET /merge_requests
    when unauthenticated
      returns an array of all merge requests
      returns authentication error without any scope
      returns authentication error  when scope is assigned-to-me
      returns authentication error when scope is assigned_to_me
      returns authentication error  when scope is created-by-me
    when authenticated
      returns an array of all merge requests except unauthorized ones
      returns an array of no merge_requests when wip=yes
      returns an array of no merge_requests when wip=no
      does not return unauthorized merge requests
      returns an array of merge requests created by current user if no scope is given
      returns an array of merge requests assigned to the given user
      returns an array of merge requests with no assignee
      returns an array of merge requests with any assignee
      returns an array of merge requests assigned to me
      returns an array of merge requests assigned to me (kebab-case)
      returns an array of merge requests created by me
      returns an array of merge requests created by me (kebab-case)
      returns merge requests reacted by the authenticated user by the given emoji
      returns merge requests created before a specific date
      returns merge requests created after a specific date
      returns merge requests updated before a specific date
      returns merge requests updated after a specific date
      filter by author
        when only `author_id` is passed
          returns an array of merge requests authored by the given user
        when only `author_username` is passed
          returns an array of merge requests authored by the given user(by `author_username`)
        when both `author_id` and `author_username` are passed
          returns a 400
      source_branch param
        returns merge requests with the given source branch
      target_branch param
        returns merge requests with the given target branch
      search params
        returns merge requests matching given search string for title
        returns merge requests matching given search string for title and scoped in title
        returns an empty array if no merge request matches given search string for description and scoped in title
        returns merge requests for project matching given search string for description
      state param
        returns merge requests with the given state
  GET /projects/:id/merge_requests
    returns 404 for non public projects
    returns an array of no merge_requests when wip=yes
    returns merge_request by "iids" array
    behaves like merge requests list
      when unauthenticated
        returns merge requests for public projects
      when authenticated
        avoids N+1 queries
        returns an array of all merge_requests using simple mode
        returns an array of all merge_requests
        returns an array of open merge_requests
        returns an array of closed merge_requests
        returns an array of merged merge_requests
        matches V4 response schema
        returns an empty array if no issue matches milestone
        returns an empty array if milestone does not exist
        returns an array of merge requests in given milestone
        returns an array of merge requests matching state in milestone
        returns an array of labeled merge requests that are merged for a milestone
        when merge request is unchecked
          with merge status recheck projection
            checks mergeability asynchronously
          without merge status recheck projection
            does not enqueue a merge status recheck
        with labels
          returns an array of all merge_requests
          with labels_details
            returns labels with details
            avoids N+1 queries
        with labels
          returns an array of labeled merge requests
          returns an array of labeled merge requests where all labels match
          returns an empty array if no merge request matches labels
          returns an array of labeled merge requests where all labels match
          returns an array of merge requests with any label when filtering by any label
          returns an array of merge requests with any label when filtering by any label
          returns an array of merge requests with any label when filtering by any label
          returns an array of merge requests without a label when filtering by no label
        with ordering
          returns an array of merge_requests in ascending order
          returns an array of merge_requests in descending order
          returns an array of merge_requests ordered by updated_at
          returns an array of merge_requests ordered by created_at
          2 merge requests with equal created_at
            page breaks first page correctly
            page breaks second page correctly
        NOT params
          returns merge requests without any of the labels given
          returns merge requests without any of the milestones given
          returns merge requests without the author given
          returns merge requests without the assignee given
        source_branch param
          returns merge requests with the given source branch
        target_branch param
          returns merge requests with the given target branch
    a project which enforces all discussions to be resolved
      avoids N+1 queries
  GET /groups/:id/merge_requests
    behaves like merge requests list
      when unauthenticated
        returns merge requests for public projects
      when authenticated
        avoids N+1 queries
        returns an array of all merge_requests using simple mode
        returns an array of all merge_requests
        returns an array of open merge_requests
        returns an array of closed merge_requests
        returns an array of merged merge_requests
        matches V4 response schema
        returns an empty array if no issue matches milestone
        returns an empty array if milestone does not exist
        returns an array of merge requests in given milestone
        returns an array of merge requests matching state in milestone
        returns an array of labeled merge requests that are merged for a milestone
        when merge request is unchecked
          with merge status recheck projection
            checks mergeability asynchronously
          without merge status recheck projection
            does not enqueue a merge status recheck
        with labels
          returns an array of all merge_requests
          with labels_details
            returns labels with details
            avoids N+1 queries
        with labels
          returns an array of labeled merge requests
          returns an array of labeled merge requests where all labels match
          returns an empty array if no merge request matches labels
          returns an array of labeled merge requests where all labels match
          returns an array of merge requests with any label when filtering by any label
          returns an array of merge requests with any label when filtering by any label
          returns an array of merge requests with any label when filtering by any label
          returns an array of merge requests without a label when filtering by no label
        with ordering
          returns an array of merge_requests in ascending order
          returns an array of merge_requests in descending order
          returns an array of merge_requests ordered by updated_at
          returns an array of merge_requests ordered by created_at
          2 merge requests with equal created_at
            page breaks first page correctly
            page breaks second page correctly
        NOT params
          returns merge requests without any of the labels given
          returns merge requests without any of the milestones given
          returns merge requests without the author given
          returns merge requests without the assignee given
        source_branch param
          returns merge requests with the given source branch
        target_branch param
          returns merge requests with the given target branch
    when have subgroups
      behaves like merge requests list
        when unauthenticated
          returns merge requests for public projects
        when authenticated
          avoids N+1 queries
          returns an array of all merge_requests using simple mode
          returns an array of all merge_requests
          returns an array of open merge_requests
          returns an array of closed merge_requests
          returns an array of merged merge_requests
          matches V4 response schema
          returns an empty array if no issue matches milestone
          returns an empty array if milestone does not exist
          returns an array of merge requests in given milestone
          returns an array of merge requests matching state in milestone
          returns an array of labeled merge requests that are merged for a milestone
          when merge request is unchecked
            with merge status recheck projection
              checks mergeability asynchronously
            without merge status recheck projection
              does not enqueue a merge status recheck
          with labels
            returns an array of all merge_requests
            with labels_details
              returns labels with details
              avoids N+1 queries
          with labels
            returns an array of labeled merge requests
            returns an array of labeled merge requests where all labels match
            returns an empty array if no merge request matches labels
            returns an array of labeled merge requests where all labels match
            returns an array of merge requests with any label when filtering by any label
            returns an array of merge requests with any label when filtering by any label
            returns an array of merge requests with any label when filtering by any label
            returns an array of merge requests without a label when filtering by no label
          with ordering
            returns an array of merge_requests in ascending order
            returns an array of merge_requests in descending order
            returns an array of merge_requests ordered by updated_at
            returns an array of merge_requests ordered by created_at
            2 merge requests with equal created_at
              page breaks first page correctly
              page breaks second page correctly
          NOT params
            returns merge requests without any of the labels given
            returns merge requests without any of the milestones given
            returns merge requests without the author given
            returns merge requests without the assignee given
          source_branch param
            returns merge requests with the given source branch
          target_branch param
            returns merge requests with the given target branch
    #to_reference
      exposes reference path in context of group
      referencing from parent group
        exposes reference path in context of parent group
    with archived projects
      returns an array excluding merge_requests from archived projects
      with non_archived param set as false
        returns an array including merge_requests from archived projects
  GET /projects/:id/merge_requests/:merge_request_iid
    matches json schema
    exposes known attributes
    exposes description and title html when render_html is true
    exposes rebase_in_progress when include_rebase_in_progress is true
    returns the commits behind the target branch when include_diverged_commits_count is present
    returns a 404 error if merge_request_iid not found
    returns a 404 error if merge_request `id` is used instead of iid
    indicates if a user cannot merge the MR
    returns `checking` as its merge_status instead of `cannot_be_merged_rechecking`
    when author is not a member without any merged merge requests
      exposes first_contribution as true
    merge_request_metrics
      has fields from merge request metrics
      returns correct values
    head_pipeline
      when user can read the pipeline
        exposes pipeline information
      when user can not read the pipeline
        does not expose pipeline information
    Work in Progress
      returns merge request
    when a merge request has more than the changes limit
      returns a string indicating that more changes were made
    for forked projects
      includes the `allow_collaboration` field
    when merge request is unchecked
      checks mergeability asynchronously
  GET /projects/:id/merge_requests/:merge_request_iid/participants
    behaves like issuable participants endpoint
      returns participants
      returns a 404 when iid does not exist
      returns a 404 when id is used instead of iid
  GET /projects/:id/merge_requests/:merge_request_iid/commits
    returns a 200 when merge request is valid
    returns a 404 when merge_request_iid not found
    returns a 404 when merge_request id is used instead of iid
  GET /projects/:id/merge_requests/:merge_request_iid/:context_commits
    returns a 200 when merge request is valid
    returns a 404 when merge_request_iid not found
  GET /projects/:id/merge_requests/:merge_request_iid/changes
    returns the change information of the merge_request
    returns a 404 when merge_request_iid not found
    returns a 404 when merge_request id is used instead of iid
  GET /projects/:id/merge_requests/:merge_request_iid/pipelines
    when authorized
      returns a paginated array of corresponding pipelines
      exposes basic attributes
      returns 404 if MR does not exist
    when unauthorized
      returns 403
  POST /projects/:id/merge_requests/:merge_request_iid/pipelines
    when authorized
      creates and returns the new Pipeline
    when unauthorized
      responds with a blank 404
    when the merge request does not exist
      responds with a blank 404
    when the .gitlab-ci.yml file is invalid
      creates a failed pipeline
  POST /projects/:id/merge_requests
    support for deprecated assignee_id
      creates a new merge request
      creates a new merge request when assignee_id is empty
      filters assignee_id of unauthorized user
    single assignee restrictions
      creates a new project merge request with no more than one assignee
    between branches projects
      returns 422 when source_branch equals target_branch
      returns 400 when source_branch is missing
      returns 400 when target_branch is missing
      returns 400 when title is missing
      different labels
        creates merge request with special label names
        creates merge request with special label names as array
        empty label param does not add any labels
        empty label param as array does not add any labels, but only explicitly as json
        empty label param as array, does not add any labels
        array with one empty string element does not add labels
        array with multiple empty string elements, does not add labels
        behaves like creates merge request with labels
          returns merge_request
        behaves like creates merge request with labels
          returns merge_request
        behaves like creates merge request with labels
          returns merge_request
      with existing MR
        returns 409 when MR already exists for source/target
      accepts remove_source_branch parameter
        sets force_remove_source_branch to false
        sets force_remove_source_branch to true
    forked projects
      returns merge_request
      does not return 422 when source_branch equals target_branch
      returns 403 when target project has disabled merge requests
      returns 400 when source_branch is missing
      returns 400 when target_branch is missing
      returns 400 when title is missing
      allows setting `allow_collaboration`
      returns 201 when target_branch is specified and for the same project
      when target_branch and target_project_id is specified
        returns 422 if targeting a different fork
        returns 403 if targeting a different fork which user can not access
  PUT /projects/:id/merge_reuests/:merge_request_iid
    behaves like issuable update endpoint
      PUT /projects/:id/issues/:issue_id
        clears labels when labels param is nil
        updates the issuable with labels param as array
  POST /projects/:id/merge_requests/:merge_request_iid/context_commits
    when authenticated
      creates and returns the new context commit
      returns 400 when one or more shas are invalid
      returns 400 when the commits are empty
      returns 400 when params is empty
      returns 403 when creating new context commit for guest role
      returns 403 when creating new context commit for reporter role
      doesnt create when its already created
        returns 400 when the context commit is already created
    when unauthenticated
      returns 401 if user tries to create context commits
  DELETE /projects/:id/merge_requests/:merge_request_iid
    when the user is developer
      denies the deletion of the merge request
    when the user is project owner
      destroys the merge request owners can destroy
      returns 404 for an invalid merge request IID
      returns 404 if the merge request id is used instead of iid
      behaves like 412 response
        for a modified ressource
          returns 412 with a JSON error
        for an unmodified ressource
          returns 204 with an empty body
  DELETE /projects/:id/merge_requests/:merge_request_iid/context_commits
    when authenticated
      deletes context commit
      returns 400 when invalid commit sha is passed
      returns 400 when commits is empty
      returns 400 when no params is passed
      returns 403 when deleting existing context commit for guest role
      returns 403 when deleting existing context commit for reporter role
    when unauthenticated
      returns 401, unauthorised error
  PUT /projects/:id/merge_requests/:merge_request_iid/merge
    returns merge_request in case of success
    returns 406 if branch can't be merged
    returns 405 if merge_request is not open
    returns 405 if merge_request is a work in progress
    returns 405 if the build failed for a merge request that requires success
    returns 401 if user has no permissions to merge
    returns 409 if the SHA parameter doesn't match
    succeeds if the SHA parameter matches
    updates the MR's squash attribute
    does not merge if merge_when_pipeline_succeeds is passed and the pipeline has failed
    merges if the head pipeline already succeeded and `merge_when_pipeline_succeeds` is passed
    enables merge when pipeline succeeds if the pipeline is active
    enables merge when pipeline succeeds if the pipeline is active and only_allow_merge_if_pipeline_succeeds is true
    returns 404 for an invalid merge request IID
    returns 404 if the merge request id is used instead of iid
    the squash_commit_message param
      results in a specific squash commit message when set
      results in a default squash commit message when not set
    the should_remove_source_branch param
      removes the source branch when set
    with a merge request that has force_remove_source_branch enabled
      removes the source branch
      does not remove the source branch
    performing a ff-merge with squash
      records the squash commit SHA and returns it in the response
  GET /projects/:id/merge_requests/:merge_request_iid/merge_ref
    returns the generated ID from the merge service in case of success
    when merge-ref is not synced with merge status
      returns 200 if MR can be merged
      returns 400 if MR cannot be merged
    when user has no access to the MR
      returns 404
    when invalid merge request IID
      returns 404
    when merge request ID is used instead IID
      returns 404
  PUT /projects/:id/merge_requests/:merge_request_iid
    updates title and returns merge_request
    updates description and returns merge_request
    updates milestone_id and returns merge_request
    updates squash and returns merge_request
    returns merge_request with renamed target_branch
    returns merge_request that removes the source branch
    filters assignee_id of unauthorized user
    does not update state when title is empty
    does not update state when target_branch is empty
    returns 404 for an invalid merge request IID
    returns 404 if the merge request id is used instead of iid
    updates force_remove_source_branch properly
      sets to false
      sets to true
      with a merge request across forks
        is true for an authorized user
        is false for an unauthorized user
    to close a MR
      returns merge_request
    when updating labels
      allows special label names
      also accepts labels as an array
      empty label param removes labels
      label param as empty array, but only explicitly as json, removes labels
      empty label as array, removes labels
      array with one empty string element removes labels
      array with multiple empty string elements, removes labels
    with labels
      when adding labels, keeps existing labels and adds new
      when removing labels, only removes those specified
      when removing all labels, keeps no labels
  GET :id/merge_requests/:merge_request_iid/closes_issues
    returns the issue that will be closed on merge
    returns an empty array when there are no issues to be closed
    handles external issues
    returns 403 if the user has no access to the merge request
    returns 404 for an invalid merge request IID
    returns 404 if the merge request id is used instead of iid
  POST :id/merge_requests/:merge_request_iid/subscribe
    subscribes to a merge request
    returns 304 if already subscribed
    returns 404 if the merge request is not found
    returns 404 if the merge request id is used instead of iid
    returns 403 if user has no access to read code
  POST :id/merge_requests/:merge_request_iid/unsubscribe
    unsubscribes from a merge request
    returns 304 if not subscribed
    returns 404 if the merge request is not found
    returns 404 if the merge request id is used instead of iid
    returns 403 if user has no access to read code
  POST :id/merge_requests/:merge_request_iid/cancel_merge_when_pipeline_succeeds
    removes the merge_when_pipeline_succeeds status
    returns 404 if the merge request is not found
    returns 404 if the merge request id is used instead of iid
  PUT :id/merge_requests/:merge_request_iid/rebase
    returns 403 if the user cannot push to the branch
    returns 409 if a rebase is already in progress
    returns 409 if rebase can't lock the row
    when rebase can be performed
      enqueues a rebase of the merge request against the target branch
      when skip_ci parameter is set
        enqueues a rebase of the merge request with skip_ci flag set
  Time tracking
    POST /projects/:id/merge_requests/:merge_request_id/time_estimate
      sets the time estimate for merge_request
      with an unauthorized user
        behaves like an unauthorized API user
          is expected to eq 403
        behaves like API user with insufficient permissions
          with non member that is the author
            behaves like an unauthorized API user
              is expected to eq 403
      updating the current estimate
        when duration has a bad format
          does not modify the original estimate
        with a valid duration
          updates the estimate
    POST /projects/:id/merge_requests/:merge_request_id/reset_time_estimate
      resets the time estimate for merge_request
      with an unauthorized user
        behaves like an unauthorized API user
          is expected to eq 403
        behaves like API user with insufficient permissions
          with non member that is the author
            behaves like an unauthorized API user
              is expected to eq 403
    POST /projects/:id/merge_requests/:merge_request_id/add_spent_time
      add spent time for merge_request
      with an unauthorized user
        behaves like an unauthorized API user
          is expected to eq 403
        behaves like API user with insufficient permissions
          with non member that is the author
            behaves like an unauthorized API user
              is expected to eq 403
      when subtracting time
        subtracts time of the total spent time
      when time to subtract is greater than the total spent time
        does not modify the total time spent
    POST /projects/:id/merge_requests/:merge_request_id/reset_spent_time
      resets spent time for merge_request
      with an unauthorized user
        behaves like an unauthorized API user
          is expected to eq 403
        behaves like API user with insufficient permissions
          with non member that is the author
            behaves like an unauthorized API user
              is expected to eq 403
    GET /projects/:id/merge_requests/:merge_request_id/time_stats
      returns the time stats for merge_request

API::Notes
  when there are cross-reference system notes
    behaves like with cross-reference system notes
      returns only the note that the user should see
      avoids Git calls and N+1 SQL queries
  when noteable is an Issue
    behaves like noteable API
      GET /projects/:id/issues/:noteable_id/notes
        returns an array of notes
        returns a 404 error when noteable id not found
        returns 404 when not authorized
        sorting
          sorts by ascending order when requested
          sorts by updated_at in descending order when requested
          sorts by updated_at in ascending order when requested
          without sort params
            sorts by created_at in descending order by default
            fetches notes using parent path as id paremeter
            2 notes with equal created_at
              page breaks first page correctly
              page breaks second page correctly
      GET /projects/:id/issues/:noteable_id/notes/:note_id
        returns a note by id
        returns a 404 error if note not found
      POST /projects/:id/issues/:noteable_id/notes
        creates a new note
        creates a confidential note if confidential is set to true
        returns a 400 bad request error if body not given
        returns a 401 unauthorized error if user not authenticated
        creates an activity event when a note is created
        setting created_at
          by an admin
            sets the creation time on the new note
          by a project owner
            sets the creation time on the new note
          by a group owner
            sets the creation time on the new note
          by another user
            ignores the given creation time
        when the user is posting an award emoji on a noteable created by someone else
          creates a new note
        when the user is posting an award emoji on their own noteable
          creates a new note
        when user does not have access to read the noteable
          responds with 404
      PUT /projects/:id/issues/:noteable_id/notes/:note_id
        returns a 404 error when note id not found
        returns a 400 bad request error if body is empty
        when eveything is ok
          with multiple params present
            returns modified note
            updates the note
          when only body param is present
            updates only the note text
          when only confidential param is present
            updates only the note text
      DELETE /projects/:id/issues/:noteable_id/notes/:note_id
        deletes a note
        returns a 404 error when note id not found
        behaves like 412 response
          for a modified ressource
            returns 412 with a JSON error
          for an unmodified ressource
            returns 204 with an empty body
    when user does not have access to create noteable
      responds with resource not found error
      does not create new note
    when referencing other project
      GET /projects/:id/noteable/:noteable_id/notes
        current user cannot view the notes
          returns an empty array
          issue is confidential
            returns 404
        current user can view the note
          returns a non-empty array
        activity filters
          when not provided
            returns all the notes
            behaves like a notes request
              is a note array response
          when all_notes provided
            returns all the notes
            behaves like a notes request
              is a note array response
          when provided
            filter: "only_comments", count: 1, system_notable: false
              properly filters the returned notables
              behaves like a notes request
                is a note array response
            filter: "only_activity", count: 1, system_notable: true
              properly filters the returned notables
              behaves like a notes request
                is a note array response
      GET /projects/:id/noteable/:noteable_id/notes/:note_id
        current user cannot view the notes
          returns a 404 error
          when issue is confidential
            returns 404
        current user can view the note
          returns an issue note by id
  when noteable is a Snippet
    behaves like noteable API
      GET /projects/:id/snippets/:noteable_id/notes
        returns an array of notes
        returns a 404 error when noteable id not found
        returns 404 when not authorized
        sorting
          sorts by ascending order when requested
          sorts by updated_at in descending order when requested
          sorts by updated_at in ascending order when requested
          without sort params
            sorts by created_at in descending order by default
            fetches notes using parent path as id paremeter
            2 notes with equal created_at
              page breaks first page correctly
              page breaks second page correctly
      GET /projects/:id/snippets/:noteable_id/notes/:note_id
        returns a note by id
        returns a 404 error if note not found
      POST /projects/:id/snippets/:noteable_id/notes
        creates a new note
        creates a confidential note if confidential is set to true
        returns a 400 bad request error if body not given
        returns a 401 unauthorized error if user not authenticated
        creates an activity event when a note is created
        setting created_at
          by an admin
            sets the creation time on the new note
          by a project owner
            sets the creation time on the new note
          by a group owner
            sets the creation time on the new note
          by another user
            ignores the given creation time
        when the user is posting an award emoji on a noteable created by someone else
          creates a new note
        when the user is posting an award emoji on their own noteable
          creates a new note
        when user does not have access to read the noteable
          responds with 404
      PUT /projects/:id/snippets/:noteable_id/notes/:note_id
        returns a 404 error when note id not found
        returns a 400 bad request error if body is empty
        when eveything is ok
          with multiple params present
            returns modified note
            updates the note
          when only body param is present
            updates only the note text
          when only confidential param is present
            updates only the note text
      DELETE /projects/:id/snippets/:noteable_id/notes/:note_id
        deletes a note
        returns a 404 error when note id not found
        behaves like 412 response
          for a modified ressource
            returns 412 with a JSON error
          for an unmodified ressource
            returns 204 with an empty body
  when noteable is a Merge Request
    behaves like noteable API
      GET /projects/:id/merge_requests/:noteable_id/notes
        returns an array of notes
        returns a 404 error when noteable id not found
        returns 404 when not authorized
        sorting
          sorts by ascending order when requested
          sorts by updated_at in descending order when requested
          sorts by updated_at in ascending order when requested
          without sort params
            sorts by created_at in descending order by default
            fetches notes using parent path as id paremeter
            2 notes with equal created_at
              page breaks first page correctly
              page breaks second page correctly
      GET /projects/:id/merge_requests/:noteable_id/notes/:note_id
        returns a note by id
        returns a 404 error if note not found
      POST /projects/:id/merge_requests/:noteable_id/notes
        creates a new note
        creates a confidential note if confidential is set to true
        returns a 400 bad request error if body not given
        returns a 401 unauthorized error if user not authenticated
        creates an activity event when a note is created
        setting created_at
          by an admin
            sets the creation time on the new note
          by a project owner
            sets the creation time on the new note
          by a group owner
            sets the creation time on the new note
          by another user
            ignores the given creation time
        when the user is posting an award emoji on a noteable created by someone else
          creates a new note
        when the user is posting an award emoji on their own noteable
          creates a new note
        when user does not have access to read the noteable
          responds with 404
      PUT /projects/:id/merge_requests/:noteable_id/notes/:note_id
        returns a 404 error when note id not found
        returns a 400 bad request error if body is empty
        when eveything is ok
          with multiple params present
            returns modified note
            updates the note
          when only body param is present
            updates only the note text
          when only confidential param is present
            updates only the note text
      DELETE /projects/:id/merge_requests/:noteable_id/notes/:note_id
        deletes a note
        returns a 404 error when note id not found
        behaves like 412 response
          for a modified ressource
            returns 412 with a JSON error
          for an unmodified ressource
            returns 204 with an empty body
    a command only note
      returns 202 Accepted status
      does not actually create a new note
      does however create a system note about the change
      applies the commands
      reports the changes
    when the merge request discussion is locked
      when a user is a team member
        returns 200 status
        creates a new note
      when a user is not a team member
        returns 403 status
        does not create a new note

API::Releases
  GET /projects/:id/releases
    returns an upcoming_release status for a future release
    returns an upcoming_release status for a past release
    avoids N+1 queries
    when there are two releases
      returns 200 HTTP status
      returns releases ordered by released_at
      matches response schema
      returns rendered helper paths
      returns the merge requests and issues links, with correct query
    when tag does not exist in git repository
      returns the tag
    when tag contains a slash
      returns 200 HTTP status
    when user is a guest
      responds 200 OK
      does not expose tag, commit, source code or helper paths
      when project is public
        responds 200 OK
        exposes tag, commit, source code and helper paths
    when user is not a project member
      cannot find the project
      when project is public
        allows the request
  GET /projects/:id/releases/:tag_name
    when there is a release
      returns 200 HTTP status
      returns a release entry
      matches response schema
      contains source information as assets
      with evidence
        returns the evidence
        #collected_at
      when release has link asset
        contains link information as assets
        when link is internal
          has external false
      when user is a guest
        responds 403 Forbidden
        when project is public
          responds 200 OK
          exposes tag and commit
    when specified tag is not found in the project
      cannot find the release entry
    when user is not a project member
      cannot find the project
      when project is public
        allows the request
        when release is associated to a milestone
          matches schema
          exposes milestones
          returns issue stats for milestone
          when project restricts visibility of issues and merge requests
            does not expose milestones
          when project restricts visibility of issues
            exposes milestones
  POST /projects/:id/releases
    accepts the request
    creates a new release
    creates a new release without description
    sets the released_at to the current time if the released_at parameter is not provided
    sets the released_at to the value in the parameters if specified
    assumes the utc timezone for released_at if the timezone is not provided
    allows specifying a released_at with a local time zone
    matches response schema
    does not create a new tag
    when user is a reporter
      forbids the request
    when user is not a project member
      forbids the request
      when project is public
        forbids the request
      when create assets altogether
        when create one asset
          accepts the request
          creates an asset with specified parameters
          matches response schema
        when create two assets
          creates two assets with specified parameters
          when link names are duplicates
            recognizes as a bad request
    when using JOB-TOKEN auth
      when no token is provided
        returns a :not_found error
      when an invalid token is provided
        returns an :unauthorized error
      when a valid token is provided
        creates the release
    when tag does not exist in git repository
      creates a new tag
      creates a new release
      when tag name is HEAD
        returns a 400 error as failure on tag creation
      when tag name is empty
        returns a 400 error as failure on tag creation
    when release already exists
      returns an error as conflicted request
  PUT /projects/:id/releases/:tag_name
    accepts the request
    updates the description
    does not change other attributes
    matches response schema
    updates released_at
    when user tries to update sha
      does not allow the request
    when params is empty
      does not allow the request
    when there are no corresponding releases
      forbids the request
    when user is a reporter
      forbids the request
    when user is not a project member
      forbids the request
      when project is public
        forbids the request
  DELETE /projects/:id/releases/:tag_name
    accepts the request
    destroys the release
    does not remove a tag in repository
    matches response schema
    when there are no corresponding releases
      forbids the request
    when user is a reporter
      forbids the request
    when user is not a project member
      forbids the request
      when project is public
        forbids the request

ProjectsController
  updates Service Desk attributes
  GET new
    with an authenticated user
      when namespace_id param is present
        when user has access to the namespace
          renders the template
        when user does not have access to the namespace
          responds with status 404
      with the new_create_project_ui experiment enabled and the user is part of the control group
        passes the right tracking parameters to the frontend
  GET index
    as a user
      redirects to root page
    as a guest
      redirects to Explore page
  GET #activity as JSON
    when user has permission to see the event
      returns count
      design events are visible
        returns correct count
    when user has no permission to see the event
      filters out invisible event
      filters out invisible event when calculating the count
  GET show
    user not project member
      user does not have access to project
        does not initialize notification setting
      user has access to project
        and does not have notification setting
          initializes notification as disabled
        and has notification setting
          shows current notification setting
      when project repository is disabled
        shows wiki homepage
        shows issues list page if wiki is disabled
        shows customize workflow page if wiki and issues are disabled
        shows activity if enabled by user
    when the storage is not available
DEPRECATION WARNING: render file: should be given the absolute path to a file (called from render at /builds/gitlab-org/gitlab/app/controllers/application_controller.rb:134)
      renders a 503
    project with empty repo
      with readme view set
        renders the empty project view
      with activity view set
        renders the empty project view
      with files view set
        renders the empty project view
    project with broken repo
      with readme view set
        renders the empty project view
      with activity view set
        renders the empty project view
      with files view set
        renders the empty project view
    rendering default project view
      renders the activity view
      renders the files view
      renders the readme view
    when the url contains .atom
      expects an error creating the project
    when the project is pending deletions
      renders a 404 error
    redirection from http://someproject.git
      redirects to project page (format.html)
    when the project is forked and has a repository
      does not increase the number of queries when the project is forked
  GET edit
    allows an admin user to access the page
    sets the badge API endpoint
  POST #archive
    for a user with the ability to archive a project
      archives the project
      redirects to projects path
    for a user that does not have the ability to archive a project
      does not archive the project
      returns 404
  POST #unarchive
    for a user with the ability to unarchive a project
      unarchives the project
      redirects to projects path
    for a user that does not have the ability to unarchive a project
      does not unarchive the project
      returns 404
  #housekeeping
    when authenticated as owner
      forces a full garbage collection
    when authenticated as developer
      does not execute housekeeping
  #update
    hashed storage
      behaves like updating a project
        updates Fast Forward Merge attributes
        does not update namespace
        when only renaming a project path
          sets the repository to the right path after a rename
        when project has container repositories with tags
          does not allow to rename the project
    legacy storage
      behaves like updating a project
        updates Fast Forward Merge attributes
        does not update namespace
        when only renaming a project path
          sets the repository to the right path after a rename
        when project has container repositories with tags
          does not allow to rename the project
    as maintainer
      behaves like unauthorized when external service denies access
        allows access when the authorization service allows it
        allows access when the authorization service denies it
        updates when the service allows access
        does not update when the service rejects access
  #transfer
    updates namespace
    when new namespace is empty
      project namespace is not changed
  #destroy
    redirects to the dashboard
    when the project is forked
      closes all related merge requests
  PUT #new_issuable_address for issue
    has http status 200
    changes the user incoming email token
    changes projects new issue address
  PUT #new_issuable_address for merge request
    has http status 200
    changes the user incoming email token
    changes projects new merge request address
  POST #toggle_star
    toggles star if user is signed in
    does nothing if user is not signed in
  DELETE remove_fork
    does nothing if user is not signed in
    when signed in
      with forked project
        removes fork from project
      when project not forked
        does nothing if project was not forked
  GET refs
    gets a list of branches and tags
    gets a list of branches, tags and commits
    when preferred language is Japanese
      gets a list of branches, tags and commits
    when private project
      as a guest
        renders forbidden
  POST #preview_markdown
    renders json in a correct format
    when not authorized
      returns 404
    state filter on references
      renders JSON body with state filter for issues
      renders JSON body with state filter for MRs
    when path parameter is provided
      renders JSON body with image links expanded
    when path and ref parameters are provided
      renders JSON body with image links expanded
  #ensure_canonical_path
    for a GET request
      when requesting the canonical path
        with exactly matching casing
          loads the project
        with different casing
          redirects to the normalized path
      when requesting a redirected path
        redirects to the canonical path
        redirects to the canonical path (testing non-show action)
    for a POST request
      when requesting the canonical path with different casing
        does not 404
        does not redirect to the correct casing
      when requesting a redirected path
        returns not found
    for a DELETE request
      when requesting the canonical path with different casing
        does not 404
        does not redirect to the correct casing
      when requesting a redirected path
        returns not found
  project export
    #export
      when project export is enabled
        returns 302
      when project export is disabled
        returns 404
      when the endpoint receives requests above the limit
        prevents requesting project export
    #download_export
      object storage enabled
        when project export is enabled
          returns 302
        when project export is disabled
          returns 404
        when the endpoint receives requests above the limit
          prevents requesting project export
    #remove_export
      when project export is enabled
        returns 302
      when project export is disabled
        returns 404
    #generate_new_export
      when project export is enabled
        returns 302
      when project export is disabled
        returns 404
      when the endpoint receives requests above the limit
        prevents requesting project export
  private project with token authentication
    behaves like authenticates sessionless user
      doesn't log the user in otherwise
      when the 'personal_access_token' param is populated with the personal access token
        logs the user in
      when the personal access token has no api scope
        does not log the user in
      when the 'PERSONAL_ACCESS_TOKEN' header is populated with the personal access token
        logs the user in
  public project with token authentication
    behaves like authenticates sessionless user
      when the 'personal_access_token' param is populated with the personal access token
        logs the user in
        does not log the user in if page is public
      when the 'PERSONAL_ACCESS_TOKEN' header is populated with the personal access token
        logs the user in
  GET resolve
    with an authenticated user
      gives 404 for non-existing project
      when user has access to the project
        behaves like resolvable endpoint
          redirects to the project page
      when user has no access to the project
        gives 404 for existing project
    non authenticated user
      gives 404 for private project
      with a public project
        behaves like resolvable endpoint
          redirects to the project page

API::Files
  http headers
    converts value into string
    raises exception if value is an Enumerable
  HEAD /projects/:id/repository/files/:file_path
    when unauthenticated and project is public
      behaves like repository files
        returns 400 when file path is invalid
        returns file attributes in headers
        returns file by commit sha
        behaves like when path is absolute
          returns 400 when file path is absolute
        when mandatory params are not given
          responds with a 400 status
        when file_path does not exist
          responds with a 404 status
        when file_path does not exist
          responds with a 403 status
    when unauthenticated and project is private
      responds with a 404 status
    when PATs are used
      behaves like repository files
        returns 400 when file path is invalid
        returns file attributes in headers
        returns file by commit sha
        behaves like when path is absolute
          returns 400 when file path is absolute
        when mandatory params are not given
          responds with a 400 status
        when file_path does not exist
          responds with a 404 status
        when file_path does not exist
          responds with a 403 status
    when authenticated as a developer
      behaves like repository files
        returns 400 when file path is invalid
        returns file attributes in headers
        returns file by commit sha
        behaves like when path is absolute
          returns 400 when file path is absolute
        when mandatory params are not given
          responds with a 400 status
        when file_path does not exist
          responds with a 404 status
        when file_path does not exist
          responds with a 403 status
    when authenticated as a guest
      behaves like 403 response
        returns 403
  GET /projects/:id/repository/files/:file_path
    when unauthenticated and project is public
      behaves like repository files
        returns 400 for invalid file path
        returns file attributes as json
DEPRECATION WARNING: Rails 6.1 will return Content-Type header without modification. If you want just the MIME type, please use `#media_type` instead. (called from block (4 levels) in <top (required)> at /builds/gitlab-org/gitlab/spec/requests/api/files_spec.rb:207)
        returns json when file has txt extension
        returns file by commit sha
        returns raw file info
        returns blame file info
        sets inline content disposition by default
        behaves like when path is absolute
          returns 400 when file path is absolute
        with filename with pathspec characters
          returns JSON wth commit SHA
        when mandatory params are not given
          behaves like 400 response
            returns 400
        when file_path does not exist
          behaves like 404 response
            returns 404
        when repository is disabled
          behaves like 403 response
            returns 403
    when PATs are used
      behaves like repository files
        returns 400 for invalid file path
        returns file attributes as json
DEPRECATION WARNING: Rails 6.1 will return Content-Type header without modification. If you want just the MIME type, please use `#media_type` instead. (called from block (4 levels) in <top (required)> at /builds/gitlab-org/gitlab/spec/requests/api/files_spec.rb:207)
        returns json when file has txt extension
        returns file by commit sha
        returns raw file info
        returns blame file info
        sets inline content disposition by default
        behaves like when path is absolute
          returns 400 when file path is absolute
        with filename with pathspec characters
          returns JSON wth commit SHA
        when mandatory params are not given
          behaves like 400 response
            returns 400
        when file_path does not exist
          behaves like 404 response
            returns 404
        when repository is disabled
          behaves like 403 response
            returns 403
    when unauthenticated and project is private
      behaves like 404 response
        returns 404
    when authenticated as a developer
      behaves like repository files
        returns 400 for invalid file path
        returns file attributes as json
DEPRECATION WARNING: Rails 6.1 will return Content-Type header without modification. If you want just the MIME type, please use `#media_type` instead. (called from block (4 levels) in <top (required)> at /builds/gitlab-org/gitlab/spec/requests/api/files_spec.rb:207)
        returns json when file has txt extension
        returns file by commit sha
        returns raw file info
        returns blame file info
        sets inline content disposition by default
        behaves like when path is absolute
          returns 400 when file path is absolute
        with filename with pathspec characters
          returns JSON wth commit SHA
        when mandatory params are not given
          behaves like 400 response
            returns 400
        when file_path does not exist
          behaves like 404 response
            returns 404
        when repository is disabled
          behaves like 403 response
            returns 403
    when authenticated as a guest
      behaves like 403 response
        returns 403
  GET /projects/:id/repository/files/:file_path/blame
    when unauthenticated and project is public
      behaves like repository blame files
        returns file attributes in headers
        returns 400 when file path is invalid
        returns blame file attributes as json
        returns blame file info for files with dots
        returns file by commit sha
        behaves like when path is absolute
          returns 400 when file path is absolute
        when mandatory params are not given
          behaves like 400 response
            returns 400
        when file_path does not exist
          behaves like 404 response
            returns 404
        when commit does not exist
          behaves like 404 response
            returns 404
        when repository is disabled
          behaves like 403 response
            returns 403
    when unauthenticated and project is private
      behaves like 404 response
        returns 404
    when authenticated as a developer
      behaves like repository blame files
        returns file attributes in headers
        returns 400 when file path is invalid
        returns blame file attributes as json
        returns blame file info for files with dots
        returns file by commit sha
        behaves like when path is absolute
          returns 400 when file path is absolute
        when mandatory params are not given
          behaves like 400 response
            returns 400
        when file_path does not exist
          behaves like 404 response
            returns 404
        when commit does not exist
          behaves like 404 response
            returns 404
        when repository is disabled
          behaves like 403 response
            returns 403
    when authenticated as a guest
      behaves like 403 response
        returns 403
    when PATs are used
      returns blame file by commit sha
  GET /projects/:id/repository/files/:file_path/raw
    when unauthenticated and project is public
      behaves like repository raw files
        returns 400 when file path is invalid
        returns raw file info
        returns raw file info for files with dots
        returns file by commit sha
        sets no-cache headers
        behaves like when path is absolute
          returns 400 when file path is absolute
        when mandatory params are not given
          behaves like 400 response
            returns 400
        when file_path does not exist
          behaves like 404 response
            returns 404
        when repository is disabled
          behaves like 403 response
            returns 403
    when unauthenticated and project is private
      behaves like 404 response
        returns 404
    when authenticated as a developer
      behaves like repository raw files
        returns 400 when file path is invalid
        returns raw file info
        returns raw file info for files with dots
        returns file by commit sha
        sets no-cache headers
        behaves like when path is absolute
          returns 400 when file path is absolute
        when mandatory params are not given
          behaves like 400 response
            returns 400
        when file_path does not exist
          behaves like 404 response
            returns 404
        when repository is disabled
          behaves like 403 response
            returns 403
    when authenticated as a guest
      behaves like 403 response
        returns 403
    when PATs are used
      returns file by commit sha
  POST /projects/:id/repository/files/:file_path
    returns 400 when file path is invalid
    creates a new file in project repo
    returns a 400 bad request if no mandatory params given
    returns a 400 bad request if the commit message is empty
    returns a 400 if editor fails to create file
    behaves like when path is absolute
      returns 400 when file path is absolute
    with PATs
      returns 403 with `read_repository` scope
      returns 201 with `api` scope
    when specifying an author
DEPRECATION WARNING: Rails 6.1 will return Content-Type header without modification. If you want just the MIME type, please use `#media_type` instead. (called from block (4 levels) in <top (required)> at /builds/gitlab-org/gitlab/spec/requests/api/files_spec.rb:693)
      creates a new file with the specified author
    when the repo is empty
      creates a new file in project repo
  PUT /projects/:id/repository/files
    updates existing file in project repo
    returns a 400 bad request if the commit message is empty
    returns a 400 bad request if update existing file with stale last commit id
    updates existing file in project repo with accepts correct last commit id
    returns 400 when file path is invalid
    returns a 400 bad request if no params given
    behaves like when path is absolute
      returns 400 when file path is absolute
    when specifying an author
      updates a file with the specified author
  DELETE /projects/:id/repository/files
    returns 400 when file path is invalid
    deletes existing file in project repo
    returns a 400 bad request if no params given
    returns a 400 bad request if the commit message is empty
    returns a 400 if fails to delete file
    behaves like when path is absolute
      returns 400 when file path is absolute
    when specifying an author
      removes a file with the specified author
  POST /projects/:id/repository/files with binary file
    remains unchanged

API::Jobs
  GET /projects/:id/jobs
    authorized user
      returns project jobs
      returns correct values
      returns pipeline data
      avoids N+1 queries
      without artifacts and trace
        returns no artifacts nor trace data
      behaves like a job with artifacts and trace
        with artifacts and trace
          returns artifacts and trace data
      filter project with one scope element
        is expected to be a kind of Array
      filter project with array of scope elements
        is expected to be a kind of Array
      respond 400 when scope contains invalid state
        is expected to respond with numeric status code bad_request
    unauthorized user
      when user is not logged in
        does not return project jobs
      when user is guest
        does not return project jobs
  GET /projects/:id/pipelines/:pipeline_id/jobs
    authorized user
      returns pipeline jobs
      returns correct values
      returns pipeline data
      avoids N+1 queries
      behaves like a job with artifacts and trace
        with artifacts and trace
          returns artifacts and trace data
      filter jobs with one scope element
        is expected to be a kind of Array
      filter jobs with array of scope elements
        is expected to be a kind of Array
      respond 400 when scope contains invalid state
        is expected to respond with numeric status code bad_request
      jobs in different pipelines
        excludes jobs from other pipelines
      when config source not ci
        returns the specified pipeline
    unauthorized user
      when user is not logged in
        does not return jobs
      when user is guest
        does not return jobs
  GET /projects/:id/pipelines/:pipeline_id/bridges
    authorized user
      returns pipeline bridges
      returns correct values
      returns pipeline data
      returns downstream pipeline data
      avoids N+1 queries
      filter bridges
        with one scope element
          skip_before_request
        with array of scope elements
          skip_before_request
      respond 400 when scope contains invalid state
        is expected to respond with numeric status code bad_request
      bridges in different pipelines
        excludes bridges from other pipelines
    unauthorized user
      when user is not logged in
        does not return bridges
      when user is guest
        does not return bridges
      when user has no read access for pipeline
        does not return bridges
      when user has no read_build access for project
        does not return bridges
  GET /projects/:id/jobs/:job_id
    authorized user
      returns specific job data
      returns pipeline data
      behaves like a job with artifacts and trace
        with artifacts and trace
          returns artifacts and trace data
    unauthorized user
      does not return specific job data
  DELETE /projects/:id/jobs/:job_id/artifacts
    when user is anonymous
      does not delete artifacts
      returns status 401 (unauthorized)
    with developer
      does not delete artifacts
      returns status 403 (forbidden)
    with authorized user
      deletes artifacts
      returns status 204 (no content)
  GET /projects/:id/jobs/:job_id/artifacts/:artifact_path
    when job has artifacts
      when user is anonymous
        when project is public
          allows to access artifacts
        when project is public with builds access disabled
          rejects access to artifacts
        when project is private
          rejects access and hides existence of artifacts
      when user is authorized
        returns a specific artifact file for a valid path
    when job does not have artifacts
      does not return job artifact file
  GET /projects/:id/jobs/:job_id/artifacts
    normal authentication
      job with artifacts
        does not return job artifacts if not uploaded
        when artifacts are stored locally
          authorized user
            behaves like downloads artifact
              returns specific job artifacts
          unauthorized user
            does not return specific job artifacts
        when artifacts are stored remotely
          when proxy download is enabled
            responds with the workhorse send-url
          when proxy download is disabled
            returns location redirect
          authorized user
            returns the file remote URL
          unauthorized user
            does not return specific job artifacts
  GET /projects/:id/artifacts/:ref_name/download?job=name
    when not logged in
      does not find a resource in a private project
    when logging as guest
      gives 403
    non-existing job
      has no such ref
        behaves like not found
          is expected to respond with numeric status code not_found
      has no such job
        behaves like not found
          is expected to respond with numeric status code not_found
    find proper job
      with regular branch
        behaves like a valid file
          when artifacts are stored locally
            is expected to respond with numeric status code ok
            is expected to include {"Content-Transfer-Encoding" => "binary", "Content-Disposition" => "attachment; filename=\"ci_build_artifacts.zip\"; filename*=UTF-8''ci_build_artifacts.zip"}
          when artifacts are stored remotely
            returns location redirect
      with branch name containing slash
        behaves like a valid file
          when artifacts are stored locally
            is expected to respond with numeric status code ok
            is expected to include {"Content-Transfer-Encoding" => "binary", "Content-Disposition" => "attachment; filename=\"ci_build_artifacts.zip\"; filename*=UTF-8''ci_build_artifacts.zip"}
          when artifacts are stored remotely
            returns location redirect
  GET id/jobs/artifacts/:ref_name/raw/*artifact_path?job=name
    when job has artifacts
      when user is anonymous
        when project is public
          allows to access artifacts
        when project is public with builds access disabled
          rejects access to artifacts
        when project is private
          rejects access and hides existence of artifacts
      when user is authorized
        returns a specific artifact file for a valid path
      with branch name containing slash
        returns a specific artifact file for a valid path
      non-existing job
        has no such ref
          behaves like not found
            is expected to respond with numeric status code not_found
        has no such job
          behaves like not found
            is expected to respond with numeric status code not_found
    when job does not have artifacts
      does not return job artifact file
  GET /projects/:id/jobs/:job_id/trace
    authorized user
      when trace is in ObjectStorage
        returns specific job trace
      when trace is artifact
        returns specific job trace
      when trace is file
        returns specific job trace
    unauthorized user
      does not return specific job trace
  POST /projects/:id/jobs/:job_id/cancel
    authorized user
      user with :update_build persmission
        cancels running or pending job
      user without :update_build permission
        does not cancel job
    unauthorized user
      does not cancel job
  POST /projects/:id/jobs/:job_id/retry
    authorized user
      user with :update_build permission
        retries non-running job
      user without :update_build permission
        does not retry job
    unauthorized user
      does not retry job
  POST /projects/:id/jobs/:job_id/erase
    job is erasable
      erases job content
      updates job
    job is not erasable
      responds with forbidden
    when a developer erases a build
      when the build was created by the developer
        is expected to respond with numeric status code created
      when the build was created by the other
        is expected to respond with numeric status code forbidden
  POST /projects/:id/jobs/:job_id/artifacts/keep
    artifacts did not expire
      keeps artifacts
    no artifacts
      responds with not found
  POST /projects/:id/jobs/:job_id/play
    on an playable job
      when user is authorized to trigger a manual action
        plays the job
      when user is not authorized to trigger a manual action
        when user does not have access to the project
          does not trigger a manual action
        when user is not allowed to trigger the manual action
          does not trigger a manual action
    on a non-playable job
      returns a status code 400, Bad Request

API::Labels
  GET /projects/:id/labels
    returns all available labels to the project
    when the with_counts parameter is set
      includes counts in the response
    when the include_ancestor_groups parameter is not set
      returns all available labels for the project, parent group and ancestor groups
    when the include_ancestor_groups parameter is set to false
      returns all available labels for the project and the parent group only
  POST /projects/:id/labels
    returns created label when all params
    returns created label when only required params
    creates a prioritized label
    returns a 400 bad request if name not given
    returns a 400 bad request if color not given
    returns 400 for invalid color
    returns 400 for too long color code
    returns 400 for invalid name
    returns 409 if label already exists in group
    returns 400 for invalid priority
    returns 409 if label already exists in project
  DELETE /projects/:id/labels
    returns 404 for non existing label
    returns 400 for wrong parameters
    fails if label_id and name are given in params
    behaves like label delete API
      returns 204 for existing label (deprecated route)
      returns 204 for existing label (rest route)
    behaves like label delete API
      returns 204 for existing label (deprecated route)
      returns 204 for existing label (rest route)
    behaves like 412 response
      for a modified ressource
        returns 412 with a JSON error
      for an unmodified ressource
        returns 204 with an empty body
  PUT /projects/:id/labels
    returns 404 if label does not exist
    returns 404 if label by id does not exist
    returns 400 if no label name and id is given
    fails if label_id and name are given in params
    when using name
      behaves like label update API
        returns 200 if name is changed (deprecated route)
        returns 200 if colors is changed (deprecated route)
        returns 200 if a priority is added (deprecated route)
        returns 400 if no new parameters given (deprecated route)
        returns 400 when color code is too short (deprecated route)
        returns 400 for too long color code (deprecated route)
        returns 400 for invalid priority (deprecated route)
        returns 200 if name and colors and description are changed (deprecated route)
        returns 400 for invalid name (deprecated route)
        returns 200 if description is changed (deprecated route)
        returns 200 if priority is changed (deprecated route)
        returns 200 if name is changed (rest route)
        returns 200 if colors is changed (rest route)
        returns 200 if a priority is added (rest route)
        returns 400 if no new parameters given (rest route)
        returns 400 when color code is too short (rest route)
        returns 400 for too long color code (rest route)
        returns 400 for invalid priority (rest route)
        returns 200 if name and colors and description are changed (rest route)
        returns 400 for invalid name (rest route)
        returns 200 if description is changed (rest route)
        returns 200 if priority is changed (rest route)
        returns 200 if a priority is removed (deprecated route)
        returns 200 if a priority is removed (rest route)
    when using label_id
      behaves like label update API
        returns 200 if name is changed (deprecated route)
        returns 200 if colors is changed (deprecated route)
        returns 200 if a priority is added (deprecated route)
        returns 400 if no new parameters given (deprecated route)
        returns 400 when color code is too short (deprecated route)
        returns 400 for too long color code (deprecated route)
        returns 400 for invalid priority (deprecated route)
        returns 200 if name and colors and description are changed (deprecated route)
        returns 400 for invalid name (deprecated route)
        returns 200 if description is changed (deprecated route)
        returns 200 if priority is changed (deprecated route)
        returns 200 if name is changed (rest route)
        returns 200 if colors is changed (rest route)
        returns 200 if a priority is added (rest route)
        returns 400 if no new parameters given (rest route)
        returns 400 when color code is too short (rest route)
        returns 400 for too long color code (rest route)
        returns 400 for invalid priority (rest route)
        returns 200 if name and colors and description are changed (rest route)
        returns 400 for invalid name (rest route)
        returns 200 if description is changed (rest route)
        returns 200 if priority is changed (rest route)
        returns 200 if a priority is removed (deprecated route)
        returns 200 if a priority is removed (rest route)
  PUT /projects/:id/labels/promote
    returns 200 if label is promoted
    returns 200 if group label already exists
    returns 403 if guest promotes label
    returns 404 if label does not exist
    returns 400 if no label name given
  POST /projects/:id/labels/:label_id/subscribe
    when label_id is a label title
      subscribes to the label
    when label_id is a label ID
      subscribes to the label
    when user is already subscribed to label
      returns 304
    when label ID is not found
      returns 404 error
  POST /projects/:id/labels/:label_id/unsubscribe
    when label_id is a label title
      unsubscribes from the label
    when label_id is a label ID
      unsubscribes from the label
    when user is already unsubscribed from label
      returns 304
    when label ID is not found
      returns 404 error

API::Services
  GET /projects/:id/services
    returns authentication error when unauthenticated
    returns error when authenticated but user is not a project owner
    project with services
      returns a list of all active services
  PUT /projects/:id/services/alerts
    updates alerts settings
    returns if required fields missing
  DELETE /projects/:id/services/alerts
    deletes alerts
  GET /projects/:id/services/alerts
    returns authentication error when unauthenticated
    returns all properties of service alerts
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/asana
    updates asana settings
    returns if required fields missing
  DELETE /projects/:id/services/asana
    deletes asana
  GET /projects/:id/services/asana
    returns authentication error when unauthenticated
    returns all properties of service asana
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/assembla
    updates assembla settings
    returns if required fields missing
  DELETE /projects/:id/services/assembla
    deletes assembla
  GET /projects/:id/services/assembla
    returns authentication error when unauthenticated
    returns all properties of service assembla
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/bamboo
    updates bamboo settings
    returns if required fields missing
  DELETE /projects/:id/services/bamboo
    deletes bamboo
  GET /projects/:id/services/bamboo
    returns authentication error when unauthenticated
    returns all properties of service bamboo
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/bugzilla
    updates bugzilla settings
    returns if required fields missing
  DELETE /projects/:id/services/bugzilla
    deletes bugzilla
  GET /projects/:id/services/bugzilla
    returns authentication error when unauthenticated
    returns all properties of service bugzilla
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/buildkite
    updates buildkite settings
    returns if required fields missing
  DELETE /projects/:id/services/buildkite
    deletes buildkite
  GET /projects/:id/services/buildkite
    returns authentication error when unauthenticated
    returns all properties of service buildkite
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/campfire
    updates campfire settings
    returns if required fields missing
  DELETE /projects/:id/services/campfire
    deletes campfire
  GET /projects/:id/services/campfire
    returns authentication error when unauthenticated
    returns all properties of service campfire
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/confluence
    updates confluence settings
    returns if required fields missing
  DELETE /projects/:id/services/confluence
    deletes confluence
  GET /projects/:id/services/confluence
    returns authentication error when unauthenticated
    returns all properties of service confluence
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/custom-issue-tracker
    updates custom_issue_tracker settings
    returns if required fields missing
  DELETE /projects/:id/services/custom-issue-tracker
    deletes custom_issue_tracker
  GET /projects/:id/services/custom-issue-tracker
    returns authentication error when unauthenticated
    returns all properties of service custom_issue_tracker
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/discord
    updates discord settings
    returns if required fields missing
  DELETE /projects/:id/services/discord
    deletes discord
  GET /projects/:id/services/discord
    returns authentication error when unauthenticated
    returns all properties of service discord
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/drone-ci
    updates drone_ci settings
    returns if required fields missing
  DELETE /projects/:id/services/drone-ci
    deletes drone_ci
  GET /projects/:id/services/drone-ci
    returns authentication error when unauthenticated
    returns all properties of service drone_ci
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/emails-on-push
    updates emails_on_push settings
    returns if required fields missing
  DELETE /projects/:id/services/emails-on-push
    deletes emails_on_push
  GET /projects/:id/services/emails-on-push
    returns authentication error when unauthenticated
    returns all properties of service emails_on_push
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/external-wiki
    updates external_wiki settings
    returns if required fields missing
  DELETE /projects/:id/services/external-wiki
    deletes external_wiki
  GET /projects/:id/services/external-wiki
    returns authentication error when unauthenticated
    returns all properties of service external_wiki
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/flowdock
    updates flowdock settings
    returns if required fields missing
  DELETE /projects/:id/services/flowdock
    deletes flowdock
  GET /projects/:id/services/flowdock
    returns authentication error when unauthenticated
    returns all properties of service flowdock
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/github
    updates github settings
    returns if required fields missing
  DELETE /projects/:id/services/github
    deletes github
  GET /projects/:id/services/github
    returns authentication error when unauthenticated
    returns all properties of service github
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/hangouts-chat
    updates hangouts_chat settings
    returns if required fields missing
  DELETE /projects/:id/services/hangouts-chat
    deletes hangouts_chat
  GET /projects/:id/services/hangouts-chat
    returns authentication error when unauthenticated
    returns all properties of service hangouts_chat
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/hipchat
    updates hipchat settings
    returns if required fields missing
  DELETE /projects/:id/services/hipchat
    deletes hipchat
  GET /projects/:id/services/hipchat
    returns authentication error when unauthenticated
    returns all properties of service hipchat
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/irker
    updates irker settings
    returns if required fields missing
  DELETE /projects/:id/services/irker
    deletes irker
  GET /projects/:id/services/irker
    returns authentication error when unauthenticated
    returns all properties of service irker
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/jenkins
    updates jenkins settings
    returns if required fields missing
  DELETE /projects/:id/services/jenkins
    deletes jenkins
  GET /projects/:id/services/jenkins
    returns authentication error when unauthenticated
    returns all properties of service jenkins
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/jira
    updates jira settings
    returns if required fields missing
  DELETE /projects/:id/services/jira
    deletes jira
  GET /projects/:id/services/jira
    returns authentication error when unauthenticated
    returns all properties of service jira
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/mattermost
    updates mattermost settings
    returns if required fields missing
  DELETE /projects/:id/services/mattermost
    deletes mattermost
  GET /projects/:id/services/mattermost
    returns authentication error when unauthenticated
    returns all properties of service mattermost
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/mattermost-slash-commands
    updates mattermost_slash_commands settings
    returns if required fields missing
  DELETE /projects/:id/services/mattermost-slash-commands
    deletes mattermost_slash_commands
  GET /projects/:id/services/mattermost-slash-commands
    returns authentication error when unauthenticated
    returns all properties of service mattermost_slash_commands
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/microsoft-teams
    updates microsoft_teams settings
    returns if required fields missing
  DELETE /projects/:id/services/microsoft-teams
    deletes microsoft_teams
  GET /projects/:id/services/microsoft-teams
    returns authentication error when unauthenticated
    returns all properties of service microsoft_teams
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/packagist
    updates packagist settings
    returns if required fields missing
  DELETE /projects/:id/services/packagist
    deletes packagist
  GET /projects/:id/services/packagist
    returns authentication error when unauthenticated
    returns all properties of service packagist
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/pipelines-email
    updates pipelines_email settings
    returns if required fields missing
  DELETE /projects/:id/services/pipelines-email
    deletes pipelines_email
  GET /projects/:id/services/pipelines-email
    returns authentication error when unauthenticated
    returns all properties of service pipelines_email
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/pivotaltracker
    updates pivotaltracker settings
    returns if required fields missing
  DELETE /projects/:id/services/pivotaltracker
    deletes pivotaltracker
  GET /projects/:id/services/pivotaltracker
    returns authentication error when unauthenticated
    returns all properties of service pivotaltracker
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/prometheus
    updates prometheus settings
    returns if required fields missing
  DELETE /projects/:id/services/prometheus
    deletes prometheus
  GET /projects/:id/services/prometheus
    returns authentication error when unauthenticated
    returns all properties of service prometheus
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/pushover
    updates pushover settings
    returns if required fields missing
  DELETE /projects/:id/services/pushover
    deletes pushover
  GET /projects/:id/services/pushover
    returns authentication error when unauthenticated
    returns all properties of service pushover
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/redmine
    updates redmine settings
    returns if required fields missing
  DELETE /projects/:id/services/redmine
    deletes redmine
  GET /projects/:id/services/redmine
    returns authentication error when unauthenticated
    returns all properties of service redmine
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/slack
    updates slack settings
    returns if required fields missing
  DELETE /projects/:id/services/slack
    deletes slack
  GET /projects/:id/services/slack
    returns authentication error when unauthenticated
    returns all properties of service slack
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/slack-slash-commands
    updates slack_slash_commands settings
    returns if required fields missing
  DELETE /projects/:id/services/slack-slash-commands
    deletes slack_slash_commands
  GET /projects/:id/services/slack-slash-commands
    returns authentication error when unauthenticated
    returns all properties of service slack_slash_commands
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/teamcity
    updates teamcity settings
    returns if required fields missing
  DELETE /projects/:id/services/teamcity
    deletes teamcity
  GET /projects/:id/services/teamcity
    returns authentication error when unauthenticated
    returns all properties of service teamcity
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/unify-circuit
    updates unify_circuit settings
    returns if required fields missing
  DELETE /projects/:id/services/unify-circuit
    deletes unify_circuit
  GET /projects/:id/services/unify-circuit
    returns authentication error when unauthenticated
    returns all properties of service unify_circuit
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/webex-teams
    updates webex_teams settings
    returns if required fields missing
  DELETE /projects/:id/services/webex-teams
    deletes webex_teams
  GET /projects/:id/services/webex-teams
    returns authentication error when unauthenticated
    returns all properties of service webex_teams
    returns error when authenticated but not a project owner
  PUT /projects/:id/services/youtrack
    updates youtrack settings
    returns if required fields missing
  DELETE /projects/:id/services/youtrack
    deletes youtrack
  GET /projects/:id/services/youtrack
    returns authentication error when unauthenticated
    returns all properties of service youtrack
    returns error when authenticated but not a project owner
  POST /projects/:id/services/:slug/trigger
    Mattermost Service
      no service is available
        returns a not found message
      the service exists
        the service is not active
          when the service is inactive
        the service is active
          returns status 200
        when the project can not be found
          returns a generic 404
    Slack Service
      returns status 200
  Mattermost service
    accepts a username for update
  Microsoft Teams service
    accepts branches_to_be_notified for update
    accepts notify_only_broken_pipelines for update

API::ProjectSnippets
  GET /projects/:project_id/snippets/:id/user_agent_detail
    exposes known attributes
    respects project scoping
    returns unauthorized for non-admin users
    with snippets disabled
      behaves like 403 response
        returns 403
  GET /projects/:project_id/snippets/
    returns all snippets available to team member
    hides private snippets from regular user
    with snippets disabled
      behaves like 403 response
        returns 403
  GET /projects/:project_id/snippets/:id
    returns snippet json
    returns 404 for invalid snippet id
    with snippets disabled
      behaves like 403 response
        returns 403
    behaves like snippet_multiple_files feature disabled
      does not return files attributes
  POST /projects/:project_id/snippets/
    with an external user
      that belongs to the project
        creates a new snippet
      that does not belong to the project
        does not create a new snippet
    with a regular user
      creates a new snippet
      behaves like project snippet repository actions
        creates repository
        commit the files to the repository
    with an admin
      creates a new snippet
      returns 400 for missing parameters
      returns 400 if title is blank
      behaves like project snippet repository actions
        creates repository
        commit the files to the repository
      behaves like snippet creation with files parameter
        returns 400 if both files and content are provided
        returns 400 when neither files or content are provided
        path: ".gitattributes", content: "file content", status: :created, error: nil
          responds correctly
        path: "valid/path/file.rb", content: "file content", status: :created, error: nil
          responds correctly
        path: ".gitattributes", content: nil, status: :bad_request, error: "files[0][content] is empty"
          responds correctly
        path: ".gitattributes", content: "", status: :bad_request, error: "files[0][content] is empty"
          responds correctly
        path: "", content: "file content", status: :bad_request, error: "files[0][file_path] is empty"
          responds correctly
        path: nil, content: "file content", status: :bad_request, error: "files[0][file_path] should be a valid file path, files[0][file_path] is empty"
          responds correctly
        path: "../../etc/passwd", content: "file content", status: :bad_request, error: "files[0][file_path] should be a valid file path"
          responds correctly
      behaves like snippet creation without files parameter
        allows file_name and content parameters
        returns 400 if file_name and content are not both provided
        returns 400 if content is blank
    when save fails because the repository could not be created
      returns 400
    when the snippet is spam
      when the snippet is private
        creates the snippet
      when the snippet is public
        rejects the snippet
        creates a spam log
    with snippets disabled
      behaves like 403 response
        returns 403
  PUT /projects/:project_id/snippets/:id/
    updates snippet
    updates snippet with content parameter
    returns 404 for invalid snippet id
    returns 400 for missing parameters
    returns 400 if content is blank
    returns 400 if title is blank
    behaves like update with repository actions
      when the repository exists
        commits the changes to the repository
      when the repository does not exist
        when update attributes does not include file_name or content
          does not create the repository
        when update attributes include file_name or content
          creates the repository
          commits the file to the repository
          when save fails due to a repository creation error
            returns 400
            does not save the changes to the snippet object
    when the snippet is spam
      when the snippet is private
        creates the snippet
      when the snippet is public
        rejects the snippet
        creates a spam log
      when the private snippet is made public
        rejects the snippet
        creates a spam log
    with snippets disabled
      behaves like 403 response
        returns 403
  DELETE /projects/:project_id/snippets/:id/
    deletes snippet
    returns 404 for invalid snippet id
    behaves like 412 response
      for a modified ressource
        returns 412 with a JSON error
      for an unmodified ressource
        returns 204 with an empty body
    with snippets disabled
      behaves like 403 response
        returns 403
  GET /projects/:project_id/snippets/:id/raw
    returns raw text
    returns 404 for invalid snippet id
    with snippets disabled
      behaves like 403 response
        returns 403
    behaves like snippet blob content
      returns content from repository
      when snippet repository is empty
        returns content from database
  GET /projects/:project_id/snippets/:id/files/:ref/:file_path/raw
    behaves like raw snippet files
      with no user
        requires authentication
      when not authorized
        behaves like not found
          returns 404
      with an invalid snippet ID
        behaves like not found
          returns 404
      with valid params
        returns the raw file info
      with invalid params
        file_path: "%2Egitattributes", ref: "invalid-ref", status: :not_found, key: "message", message: "404 Reference Not Found"
          is expected to respond with numeric status code not_found
          is expected to eq "404 Reference Not Found"
        file_path: "%2Egitattributes", ref: nil, status: :not_found, key: "error", message: "404 Not Found"
          is expected to respond with numeric status code not_found
          is expected to eq "404 Not Found"
        file_path: "%2Egitattributes", ref: "", status: :not_found, key: "error", message: "404 Not Found"
          is expected to respond with numeric status code not_found
          is expected to eq "404 Not Found"
        file_path: "doesnotexist.rb", ref: "master", status: :not_found, key: "message", message: "404 File Not Found"
          is expected to respond with numeric status code not_found
          is expected to eq "404 File Not Found"
        file_path: "/does/not/exist.rb", ref: "master", status: :not_found, key: "error", message: "404 Not Found"
          is expected to respond with numeric status code not_found
          is expected to eq "404 Not Found"
        file_path: "%2E%2E%2Fetc%2Fpasswd", ref: "master", status: :bad_request, key: "error", message: "file_path should be a valid file path"
          is expected to respond with numeric status code bad_request
          is expected to eq "file_path should be a valid file path"
        file_path: "%2Fetc%2Fpasswd", ref: "master", status: :bad_request, key: "error", message: "file_path should be a valid file path"
          is expected to respond with numeric status code bad_request
          is expected to eq "file_path should be a valid file path"
        file_path: "../../etc/passwd", ref: "master", status: :not_found, key: "error", message: "404 Not Found"
          is expected to respond with numeric status code not_found
          is expected to eq "404 Not Found"

getting projects
  behaves like a graphql namespace
    includes the packages size if the user can read the statistics
    behaves like a working graphql query
      returns a successful response
    with no user
      finds only public projects
  when the namespace is a user
    behaves like a graphql namespace
      includes the packages size if the user can read the statistics
      behaves like a working graphql query
        returns a successful response
      with no user
        finds only public projects
  when not including subgroups
    behaves like a graphql namespace
      includes the packages size if the user can read the statistics
      behaves like a working graphql query
        returns a successful response
      with no user
        finds only public projects

Groups::ChildrenController
  GET #index
    for projects
      as a user
        shows all children
        being member of private subgroup
          shows public and private children the user is member of
      as a guest
        shows the public children
    for subgroups
      as a user
        shows all children
        being member of private subgroup
          shows public and private children the user is member of
      as a guest
        shows the public children
      filtering children
        expands the tree for matching projects
        expands the tree for matching subgroups
        merges the trees correctly
        expands the tree upto a specified parent
        returns an array with one element when only one result is matched
        returns an empty array when there are no search results
        succeeds if multiple pages contain matching subgroups
        includes pagination headers
      queries per rendered element
        queries the expected amount for a group row
        queries the expected amount for a project row
        when rendering hierarchies
          queries the expected amount when nested rows are increased for a group
          queries the expected amount when a new group match is added
          queries the expected amount when nested rows are increased for a project
    pagination
      with only projects
        has projects on the first page
        has projects on the second page
      with subgroups and projects
        contains all subgroups
        contains the project and group on the second page
        with a mixed first page
          correctly calculates the counts
    external authorization
      works when external authorization service is enabled

API::ProjectClusters
  GET /projects/:id/clusters
    non-authorized user
      responds with 403
    authorized user
      includes pagination headers
      only includes authorized clusters
  GET /projects/:id/clusters/:cluster_id
    non-authorized user
      responds with 403
    authorized user
      returns specific cluster
      returns cluster information
      returns project information
      returns kubernetes platform information
      returns user information
      returns GCP provider information
      when cluster has no provider
        does not include GCP provider info
      with non-existing cluster
        returns 404
  POST /projects/:id/clusters/user
    non-authorized user
      responds with 403
    authorized user
      with valid params
        creates a new Cluster::Cluster
      when user does not indicate authorization type
        defaults to RBAC
      when user sets authorization type as ABAC
        creates an ABAC cluster
      current user does not have access to management_project_id
        responds with 400
        returns validation errors
      with invalid params
        does not create a new Clusters::Cluster
    non-authorized user
      responds with 403
  POST /projects/:id/clusters/user with multiple clusters
    when another cluster exists
      responds with 201
      allows multiple clusters to be associated to project
  PUT /projects/:id/clusters/:cluster_id
    non-authorized user
      responds with 403
    authorized user
      with valid params
        updates cluster attributes
      with invalid params
        does not update cluster attributes
        returns validation errors
      current user does not have access to management_project_id
        returns validation errors
      with a GCP cluster
        when user tries to change GCP specific fields
          returns validation error
        when user tries to change namespace
          responds with 200
      with an user cluster
        updates platform kubernetes attributes
      with a cluster that does not belong to user
        responds with 404
  DELETE /projects/:id/clusters/:cluster_id
    non-authorized user
      responds with 403
    authorized user
      deletes the cluster
      with a cluster that does not belong to user
        responds with 404

Projects::EnvironmentsController
  GET index
    when a request for the HTML is made
      responds with status code 200
      expires etag cache to force reload environments list
    when requesting JSON response for folders
      with default parameters
        responds with a flat payload describing available environments
        sets the polling interval header
      when a folder-based nested structure is requested
        responds with a payload containing the latest environment for each folder
      when requesting available environments scope
        responds with a payload describing available environments
        contains values describing environment scopes sizes
      when requesting stopped environments scope
        responds with a payload describing stopped environments
        contains values describing environment scopes sizes
  GET folder
    when using default format
      responds with HTML
    when using JSON format
      sorts the subfolders lexicographically
  GET show
    with valid id
      responds with a status code 200
    with invalid id
      responds with a status code 404
  GET edit
    responds with a status code 200
  PATCH #update
    responds with a 302
  PATCH #stop
    when env not available
      returns 404
    when stop action
      returns action url
    when no stop action
      returns env url
  POST #cancel_auto_stop
    when environment is set as auto-stop
      behaves like successful response for #cancel_auto_stop
        when request is html
          redirects to show page
          expires etag caching
        when request is js
          responds as ok
          expires etag caching
      when user is reporter
        shows NOT Found
    when environment is not set as auto-stop
      behaves like failed response for #cancel_auto_stop
        when request is html
          redirects to show page
        when request is js
          responds as unprocessable entity
  GET #terminal
    with valid id
      responds with a status code 200
      loads the terminals for the environment
    with invalid id
      responds with a status code 404
  GET #terminal_websocket_authorize
    with valid workhorse signature
      and valid id
        returns the first terminal for the environment
      and invalid id
        returns 404
    with invalid workhorse signature
      aborts with an exception
  GET #metrics_redirect
    redirects to metrics dashboard page
  GET #metrics
    when environment has no metrics
      redirects to metrics dashboard page
      when requesting metrics as JSON
        returns a metrics JSON document
    when environment has some metrics
      returns a metrics JSON document
    permissions
      checks :metrics_dashboard ability
    with anonymous user and public dashboard visibility
      redirects to metrics dashboard page
  GET #additional_metrics
    when environment has no metrics
      when requesting metrics as JSON
        returns a metrics JSON document
    when environment has some metrics
      returns a metrics JSON document
    when time params are missing
      raises an error when window params are missing
    when only one time param is provided
      raises an error when start is missing
      raises an error when end is missing
    permissions
      checks :metrics_dashboard ability
    with anonymous user and public dashboard visibility
      does not fail
  GET #metrics_dashboard
    behaves like the default dashboard
      behaves like includes all dashboards
        includes info for all findable dashboard
      behaves like GET #metrics_dashboard for dashboard
        returns correct dashboard
        behaves like GET #metrics_dashboard correctly formatted response
          returns a json object with the correct keys
    behaves like dashboard can be specified
      when dashboard is specified
        behaves like error response
          behaves like GET #metrics_dashboard correctly formatted response
            returns a json object with the correct keys
        when the project dashboard is available
          behaves like the specified dashboard
            behaves like includes all dashboards
              includes info for all findable dashboard
            behaves like GET #metrics_dashboard for dashboard
              returns correct dashboard
              behaves like GET #metrics_dashboard correctly formatted response
                returns a json object with the correct keys
            when the dashboard cannot not be processed
              behaves like error response
                behaves like GET #metrics_dashboard correctly formatted response
                  returns a json object with the correct keys
        when the specified dashboard is the default dashboard
          behaves like the default dashboard
            behaves like includes all dashboards
              includes info for all findable dashboard
            behaves like GET #metrics_dashboard for dashboard
              returns correct dashboard
              behaves like GET #metrics_dashboard correctly formatted response
                returns a json object with the correct keys
    behaves like dashboard can be embedded
      when the embedded flag is included
        behaves like the default dynamic dashboard
          behaves like specified dashboard embed
            contains only the specified charts
            behaves like 200 response
              behaves like GET #metrics_dashboard correctly formatted response
                returns a json object with the correct keys
        when incomplete dashboard params are provided
          behaves like the default dynamic dashboard
            behaves like specified dashboard embed
              contains only the specified charts
              behaves like 200 response
                behaves like GET #metrics_dashboard correctly formatted response
                  returns a json object with the correct keys
        when invalid params are provided
          behaves like the default dynamic dashboard
            behaves like specified dashboard embed
              contains only the specified charts
              behaves like 200 response
                behaves like GET #metrics_dashboard correctly formatted response
                  returns a json object with the correct keys
        when the dashboard is correctly specified
          behaves like error response
            behaves like GET #metrics_dashboard correctly formatted response
              returns a json object with the correct keys
          and exists
            behaves like specified dashboard embed
              contains only the specified charts
              behaves like 200 response
                behaves like GET #metrics_dashboard correctly formatted response
                  returns a json object with the correct keys
    with anonymous user and public dashboard visibility
      behaves like the default dashboard
        behaves like includes all dashboards
          includes info for all findable dashboard
        behaves like GET #metrics_dashboard for dashboard
          returns correct dashboard
          behaves like GET #metrics_dashboard correctly formatted response
            returns a json object with the correct keys
    permissions
      checks :metrics_dashboard ability
  GET #search
    responds with status code 200
    returns matched results
    when query is review
      returns matched results
    when query is empty
      returns matched results
    when query is review/patch-3
      responds with status code 204
    when query is partially matched in the middle of environment name
      responds with status code 204
    when query contains a wildcard character
      prevents wildcard injection
    when query matches case insensitively
      returns matched results

Projects::DeployKeysController
  GET index
    when html requested
      redirects to project settings with the correct anchor
    when json requested
      when user has access to all projects where deploy keys are used
        returns json in a correct format
      when user has no access to all projects where deploy keys are used
        returns json in a correct format
  POST create
    creates a new deploy key for the project
    redirects to project settings with the correct anchor
    when the deploy key is invalid
      shows an alert with the validations errors
  /enable/:id
    with anonymous user
      redirects to login
    with user with no permission
      returns 404
    with user with permission
      returns 302
      returns 404
    with admin
      returns 302
  /disable/:id
    with anonymous user
      redirects to login
    with user with no permission
      returns 404
    with user with permission
      returns 302
      returns 404
    with admin
      returns 302
  PUT update
    with project maintainer
      public deploy key attached to project
        does not update the title of the deploy key
        updates can_push of deploy_keys_project
    with admin
      public deploy key attached to project
        updates the title of the deploy key
        updates can_push of deploy_keys_project
      when a different deploy key id param is injected
        and that deploy key id exists
          does not update the can_push attribute
        and that deploy key id does not exist
          returns 404
    with admin as project maintainer
      public deploy key attached to project
        updates can_push of deploy_keys_project
        admin mode disabled
          does not update the title of the deploy key
        admin mode enabled
          updates the title of the deploy key

Import::GiteaController
  GET new
    behaves like a GitHub-ish import controller: GET new
      redirects to status if we already have a token
      renders the :new page if no token is present in session
  POST personal_access_token
    behaves like a GitHub-ish import controller: POST personal_access_token
      updates access token
      strips access token with spaces
  GET status
    behaves like a GitHub-ish import controller: GET status
      returns variables for json request
      does not show already added project
      touches the etag cache store
      handles an invalid access token
      does not produce N+1 database queries
      requests provider repos list
      when filtering
        filters list of repositories by name
        when user input contains html
          sanitizes user input
      when host url is local or not http
        denies network request
        denies network request
        denies network request
  POST create
    behaves like a GitHub-ish import controller: POST create
      returns 200 response when the project is imported successfully
      returns 422 response with the base error when the project could not be imported
      touches the etag cache store
      when the repository owner is the provider user
        when the provider user and GitLab user's usernames match
          takes the current user's namespace
        when the provider user and GitLab user's usernames don't match
          takes the current user's namespace
      when the repository owner is not the provider user
        when a namespace with the provider user's username already exists
          when the namespace is owned by the GitLab user
            takes the existing namespace
          when the namespace is not owned by the GitLab user
            creates a project using user's namespace
        when a namespace with the provider user's username doesn't exist
          when current user can create namespaces
            creates the namespace
            takes the new namespace
          when current user can't create namespaces
            doesn't create the namespace
            takes the current user's namespace
        user has chosen a namespace and name for the project
          takes the selected namespace and name
          takes the selected name and default namespace
        user has chosen an existing nested namespace and name for the project
          takes the selected namespace and name
        user has chosen a non-existent nested namespaces and name for the project
          takes the selected namespace and name
          creates the namespaces
          new namespace has the right parent
        user has chosen existent and non-existent nested namespaces and name for the project
          takes the selected namespace and name
          creates the namespaces
          does not create a new namespace under the user namespace
        user cannot create a subgroup inside a group is not a member of
          does not take the selected namespace and name
          does not create the namespaces
        user can use a group without having permissions to create a group
          takes the selected namespace and name
        when user can not create projects in the chosen namespace
          returns 422 response
    behaves like project import rate limiter
      when limit exceeds
        notifies and redirects user
  GET realtime_changes
    behaves like a GitHub-ish import controller: GET realtime_changes
      sets a Poll-Interval header

Updating the container expiration policy
  post graphql mutation
    with existing container expiration policy
      user_role: :maintainer, shared_examples_name: "accepting the mutation request updating the container expiration policy"
        behaves like accepting the mutation request updating the container expiration policy
          behaves like updating the container expiration policy attributes
            updates the container expiration policy
            behaves like not creating the container expiration policy
              doesn't create the container expiration policy
          behaves like returning a success
            returns the updated container expiration policy
            behaves like returning response status
              returns success
          behaves like rejecting invalid regex for
            for field name_regex
              returns an error
              behaves like returning response status
                returns success
              behaves like not creating the container expiration policy
                doesn't create the container expiration policy
          behaves like rejecting invalid regex for
            for field name_regex_keep
              returns an error
              behaves like returning response status
                returns success
              behaves like not creating the container expiration policy
                doesn't create the container expiration policy
      user_role: :developer, shared_examples_name: "accepting the mutation request updating the container expiration policy"
        behaves like accepting the mutation request updating the container expiration policy
          behaves like updating the container expiration policy attributes
            updates the container expiration policy
            behaves like not creating the container expiration policy
              doesn't create the container expiration policy
          behaves like returning a success
            returns the updated container expiration policy
            behaves like returning response status
              returns success
          behaves like rejecting invalid regex for
            for field name_regex
              returns an error
              behaves like returning response status
                returns success
              behaves like not creating the container expiration policy
                doesn't create the container expiration policy
          behaves like rejecting invalid regex for
            for field name_regex_keep
              returns an error
              behaves like returning response status
                returns success
              behaves like not creating the container expiration policy
                doesn't create the container expiration policy
      user_role: :reporter, shared_examples_name: "denying the mutation request"
        behaves like denying the mutation request
          returns no response
          behaves like not creating the container expiration policy
            doesn't create the container expiration policy
          behaves like returning response status
            returns success
      user_role: :guest, shared_examples_name: "denying the mutation request"
        behaves like denying the mutation request
          returns no response
          behaves like not creating the container expiration policy
            doesn't create the container expiration policy
          behaves like returning response status
            returns success
      user_role: :anonymous, shared_examples_name: "denying the mutation request"
        behaves like denying the mutation request
          returns no response
          behaves like not creating the container expiration policy
            doesn't create the container expiration policy
          behaves like returning response status
            returns success
    without existing container expiration policy
      user_role: :maintainer, shared_examples_name: "accepting the mutation request creating the container expiration policy"
        behaves like accepting the mutation request creating the container expiration policy
          behaves like creating the container expiration policy
            behaves like updating the container expiration policy attributes
              creates a new container expiration policy
              updates the container expiration policy
            behaves like returning a success
              returns the updated container expiration policy
              behaves like returning response status
                returns success
          behaves like returning a success
            returns the updated container expiration policy
            behaves like returning response status
              returns success
          behaves like rejecting invalid regex for
            for field name_regex
              returns an error
              behaves like returning response status
                returns success
              behaves like not creating the container expiration policy
                doesn't create the container expiration policy
          behaves like rejecting invalid regex for
            for field name_regex_keep
              returns an error
              behaves like returning response status
                returns success
              behaves like not creating the container expiration policy
                doesn't create the container expiration policy
      user_role: :developer, shared_examples_name: "accepting the mutation request creating the container expiration policy"
        behaves like accepting the mutation request creating the container expiration policy
          behaves like creating the container expiration policy
            behaves like updating the container expiration policy attributes
              creates a new container expiration policy
              updates the container expiration policy
            behaves like returning a success
              returns the updated container expiration policy
              behaves like returning response status
                returns success
          behaves like returning a success
            returns the updated container expiration policy
            behaves like returning response status
              returns success
          behaves like rejecting invalid regex for
            for field name_regex
              returns an error
              behaves like returning response status
                returns success
              behaves like not creating the container expiration policy
                doesn't create the container expiration policy
          behaves like rejecting invalid regex for
            for field name_regex_keep
              returns an error
              behaves like returning response status
                returns success
              behaves like not creating the container expiration policy
                doesn't create the container expiration policy
      user_role: :reporter, shared_examples_name: "denying the mutation request"
        behaves like denying the mutation request
          returns no response
          behaves like not creating the container expiration policy
            doesn't create the container expiration policy
          behaves like returning response status
            returns success
      user_role: :guest, shared_examples_name: "denying the mutation request"
        behaves like denying the mutation request
          returns no response
          behaves like not creating the container expiration policy
            doesn't create the container expiration policy
          behaves like returning response status
            returns success
      user_role: :anonymous, shared_examples_name: "denying the mutation request"
        behaves like denying the mutation request
          returns no response
          behaves like not creating the container expiration policy
            doesn't create the container expiration policy
          behaves like returning response status
            returns success

API::GroupLabels
  GET :id/labels
    returns all available labels for the group
    when the with_counts parameter is set
      includes counts in the response
  GET :subgroup_id/labels
    when the include_ancestor_groups parameter is not set
      returns all available labels for the group and ancestor groups
    when the include_ancestor_groups parameter is set to false
      returns all available labels for the group but not for ancestor groups
  GET :id/labels/:label_id
    returns a single label for the group
  POST /groups/:id/labels
    returns created label when all params are given
    returns created label when only required params are given
    returns a 400 bad request if name not given
    returns a 400 bad request if color is not given
    returns 409 if label already exists
  DELETE /groups/:id/labels (deprecated)
    returns 204 for existing label
    returns 404 for non existing label
    returns 400 for wrong parameters
    does not delete parent's group labels
    behaves like 412 response
      for a modified ressource
        returns 412 with a JSON error
      for an unmodified ressource
        returns 204 with an empty body
  DELETE /groups/:id/labels/:label_id
    returns 204 for existing label
    returns 404 for non existing label
    does not delete parent's group labels
    behaves like 412 response
      for a modified ressource
        returns 412 with a JSON error
      for an unmodified ressource
        returns 204 with an empty body
  PUT /groups/:id/labels (deprecated)
    returns 200 if name and colors and description are changed
    does not update parent's group label
    returns 404 if label does not exist
    returns 400 if no label name given
    returns 400 if no new parameters given
  PUT /groups/:id/labels/:label_id
    returns 200 if name and colors and description are changed
    does not update parent's group label
    returns 404 if label does not exist
    returns 400 if no new parameters given
  POST /groups/:id/labels/:label_id/subscribe
    when label_id is a label title
      subscribes to the label
    when label_id is a label ID
      subscribes to the label
    when user is already subscribed to label
      returns 304
    when label ID is not found
      returns 404 error
  POST /groups/:id/labels/:label_id/unsubscribe
    when label_id is a label title
      unsubscribes from the label
    when label_id is a label ID
      unsubscribes from the label
    when user is already unsubscribed from label
      returns 304
    when label ID is not found
      returns 404 error

API::Environments
  GET /projects/:id/environments
    as member of the project
      returns project environments
      when filtering
        returns environment by name
        returns no environment by non-existent name
        returns environments by name_like
        returns no environment by non-existent name_like
    as non member
      returns a 404 status code
  POST /projects/:id/environments
    as a member
      creates a environment with valid params
      requires name to be passed
      returns a 400 if environment already exists
      returns a 400 if slug is specified
    a non member
      rejects the request
      returns a 400 when the required params are missing
  PUT /projects/:id/environments/:environment_id
    returns a 200 if name and external_url are changed
    won't allow slug to be changed
    won't update the external_url if only the name is passed
    returns a 404 if the environment does not exist
  DELETE /projects/:id/environments/:environment_id
    as a maintainer
      rejects the requests in environment isn't stopped
      returns a 200 for stopped environment
      returns a 404 for non existing id
      behaves like 412 response
        for a modified ressource
          returns 412 with a JSON error
        for an unmodified ressource
          returns 204 with an empty body
    a non member
      rejects the request
  POST /projects/:id/environments/:environment_id/stop
    as a maintainer
      returns a 404 for non existing id
      with a stoppable environment
        returns a 200
        actually stops the environment
    a non member
      rejects the request
  GET /projects/:id/environments/:environment_id
    as member of the project
      returns project environments
    as non member
      returns a 404 status code

API::PagesDomains
  GET /pages/domains
    when pages is disabled
      behaves like 404 response
        returns 404
    when pages is enabled
      when authenticated as an admin
        returns paginated all pages domains
      when authenticated as a non-member
        behaves like 403 response
          returns 403
  GET /projects/:project_id/pages/domains
    when pages is disabled
      behaves like 404 response
        returns 404
    when user is a maintainer
      behaves like get pages domains
        returns paginated pages domains
    when user is a developer
      behaves like 403 response
        returns 403
    when user is a reporter
      behaves like 403 response
        returns 403
    when user is a guest
      behaves like 403 response
        returns 403
    when user is not a member
      behaves like 404 response
        returns 404
  GET /projects/:project_id/pages/domains/:domain
    when domain is vacant
      behaves like 404 response
        returns 404
    when user is a maintainer
      behaves like get pages domain
        returns pages domain
        returns pages domain with project path
        returns pages domain with a certificate
        returns pages domain with an expired certificate
        returns pages domain with letsencrypt
    when user is a developer
      behaves like 403 response
        returns 403
    when user is a reporter
      behaves like 403 response
        returns 403
    when user is a guest
      behaves like 403 response
        returns 403
    when user is not a member
      behaves like 404 response
        returns 404
  POST /projects/:project_id/pages/domains
    when user is a maintainer
      behaves like post pages domains
        creates a new pages domain
        creates a new secure pages domain
        creates domain with letsencrypt enabled
        creates domain with letsencrypt enabled and provided certificate
        fails to create pages domain without key
        fails to create pages domain with key missmatch
    when user is a developer
      behaves like 403 response
        returns 403
    when user is a reporter
      behaves like 403 response
        returns 403
    when user is a guest
      behaves like 403 response
        returns 403
    when user is not a member
      behaves like 404 response
        returns 404
  PUT /projects/:project_id/pages/domains/:domain
    when domain is vacant
      behaves like 404 response
        returns 404
    when user is a maintainer
      behaves like put pages domain
        updates pages domain removing certificate
        updates pages domain adding certificate
        updates pages domain adding certificate with letsencrypt
        updates pages domain enabling letsencrypt
        updates pages domain disabling letsencrypt while preserving the certificate
        updates pages domain with expired certificate
        updates pages domain with expired certificate not updating key
        updates certificate source to user_provided if is changed
        fails to update pages domain adding certificate without key
        fails to update pages domain adding certificate with missing chain
        fails to update pages domain with key missmatch
    when user is a developer
      behaves like 403 response
        returns 403
    when user is a reporter
      behaves like 403 response
        returns 403
    when user is a guest
      behaves like 403 response
        returns 403
    when user is not a member
      behaves like 404 response
        returns 404
  DELETE /projects/:project_id/pages/domains/:domain
    when domain is vacant
      behaves like 404 response
        returns 404
    when user is a maintainer
      behaves like delete pages domain
        deletes a pages domain
    when user is a developer
      behaves like 403 response
        returns 403
    when user is a reporter
      behaves like 403 response
        returns 403
    when user is a guest
      behaves like 403 response
        returns 403
    when user is not a member
      behaves like 404 response
        returns 404

Projects::StarrersController
  GET index
    N+1 queries
      avoids N+1s loading users
    when project is public
      when no user is logged in
        with no searching
          only users with public profiles are visible
          starrers counts are correct
        when searching by user
          only users with public profiles are visible
          starrers counts are correct
      when public user is logged in
        with no searching
          their star is also visible
          starrers counts are correct
        when searching by user
          only users with public profiles are visible
          starrers counts are correct
      when private user is logged in
        with no searching
          their star is also visible
          starrers counts are correct
        when searching by user
          only users with public profiles are visible
          starrers counts are correct
      when admin is logged in
        with no searching
          all users are visible
          starrers counts are correct
        when searching by user
          public and private starrers are visible
          starrers counts are correct
    when project is private
      starrers are not visible for non logged in users
      when user is logged in
        only users with public profiles are visible
        starrers counts are correct

Projects::Ci::LintsController
  GET #show
    with enough privileges
      is expected to be successful
      renders show page
      retrieves project
    without enough privileges
      responds with 404
  POST #create
    with a valid gitlab-ci.yml
      using legacy validation (YamlProcessor)
        runs validations through YamlProcessor
        behaves like returns a successful validation
          returns successfully
          render show page
          retrieves project
      using dry_run mode
        runs validations through Ci::CreatePipelineService
        behaves like returns a successful validation
          returns successfully
          render show page
          retrieves project
        when dry_run feature flag is disabled
          runs validations through YamlProcessor
          behaves like returns a successful validation
            returns successfully
            render show page
            retrieves project
    with an invalid gitlab-ci.yml
      assigns result with errors
      with dry_run mode
        assigns result with errors
    without enough privileges
      responds with 404

Dashboard::MilestonesController
  #index
    returns group and project milestones to which the user belongs
    returns closed group and project milestones to which the user belongs
    searches legacy project milestones by title when search_title is given
    searches group milestones by title when search_title is given
    shows counts of open and closed group and project milestones to which the user belongs to
    external authorization
      behaves like disabled when using an external authorization service
        works when the feature is not enabled
        renders a 404 with a message when the feature is enabled

Projects::PerformanceMonitoring::DashboardsController
  POST #create
    authenticated user
      project with repository feature
        with rights to push to the repository
          valid parameters
            delegates cloning to ::Metrics::Dashboard::CloneDashboardService
            request format json
              returns services response
              Metrics::Dashboard::CloneDashboardService failure
                returns json with failure message
              param commit_message is missing
                responds with bad request status and error message
              param file_name is missing
                responds with bad request status and error message
              param dashboard is missing
                responds with bad request status and error message
              param branch_name is missing
                responds with bad request status and error message
        without rights to push to repository
          responds with :forbidden status code
      project without repository feature
        responds with :not_found status code
  PUT #update
    authenticated user
      project with repository feature
        with rights to push to the repository
          valid parameters
            request format json
              returns path to new file
              UpdateDashboardService failure
                returns json with failure message
          missing branch
            raises responds with :bad_request status code and error message
        without rights to push to repository
          responds with :forbidden status code
      project without repository feature
        responds with :not_found status code

Projects::Environments::SampleMetricsController
  GET #query
    when the file is not found
      returns a 404
    when the sample data is found
      returns JSON with a message and a 200 status code
Knapsack report was generated. Preview:
{
  "spec/requests/api/merge_requests_spec.rb": 334.3573615550995,
  "spec/requests/api/notes_spec.rb": 95.78141021728516,
  "spec/requests/api/releases_spec.rb": 77.6811888217926,
  "spec/controllers/projects_controller_spec.rb": 68.437251329422,
  "spec/requests/api/files_spec.rb": 56.65901064872742,
  "spec/requests/api/jobs_spec.rb": 46.59989857673645,
  "spec/requests/api/labels_spec.rb": 37.35848093032837,
  "spec/requests/api/services_spec.rb": 33.73446488380432,
  "spec/requests/api/project_snippets_spec.rb": 28.235632181167603,
  "spec/requests/api/graphql/namespace/projects_spec.rb": 19.815821170806885,
  "spec/controllers/groups/children_controller_spec.rb": 22.735734224319458,
  "spec/requests/api/project_clusters_spec.rb": 16.715394973754883,
  "spec/controllers/projects/environments_controller_spec.rb": 18.011045932769775,
  "spec/controllers/projects/deploy_keys_controller_spec.rb": 16.541812896728516,
  "spec/controllers/import/gitea_controller_spec.rb": 14.014161109924316,
  "spec/requests/api/graphql/mutations/container_expiration_policy/update_spec.rb": 11.524455785751343,
  "spec/requests/api/group_labels_spec.rb": 12.472500085830688,
  "spec/requests/api/environments_spec.rb": 11.942697048187256,
  "spec/requests/api/pages_domains_spec.rb": 8.484442949295044,
  "spec/controllers/projects/starrers_controller_spec.rb": 10.079819679260254,
  "spec/controllers/projects/ci/lints_controller_spec.rb": 9.388967752456665,
  "spec/controllers/dashboard/milestones_controller_spec.rb": 9.078088760375977,
  "spec/controllers/projects/performance_monitoring/dashboards_controller_spec.rb": 4.264218330383301,
  "spec/controllers/projects/environments/sample_metrics_controller_spec.rb": 0.28141212463378906
}

Knapsack global time execution for tests: 16m 04s

Finished in 20 minutes 44 seconds (files took 56.95 seconds to load)
1738 examples, 0 failures

Tue Aug 25 15:55:08 UTC 2020
section_end:1598370909:step_script
section_start:1598370909:archive_cache
Saving cache
Not uploading cache rails-v2-3 due to policy
section_end:1598370913:archive_cache
section_start:1598370913:upload_artifacts_on_success
Uploading artifacts for successful job
Uploading artifacts...
coverage/: found 5 matching files and directories  
knapsack/: found 3 matching files and directories  
rspec_flaky/: found 4 matching files and directories 
rspec_profiling/: found 2 matching files and directories 
WARNING: tmp/capybara/: no matching files          
tmp/memory_test/: found 2 matching files and directories 
log/*.log: found 15 matching files and directories 
Uploading artifacts as "archive" to coordinator... ok  id=702959679 responseStatus=201 Created token=2MhX2uVs
Uploading artifacts...
junit_rspec.xml: found 1 matching files and directories 
Uploading artifacts as "junit" to coordinator... ok  id=702959679 responseStatus=201 Created token=2MhX2uVs
section_end:1598370920:upload_artifacts_on_success
Job succeeded