diff options
321 files changed, 4728 insertions, 1907 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 219077d79b8..ff8aa351226 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -18,6 +18,7 @@ variables: SIMPLECOV: "true" USE_DB: "true" USE_BUNDLE_INSTALL: "true" + GIT_DEPTH: "20" before_script: - source ./scripts/prepare_build.sh @@ -134,6 +135,11 @@ spinach 9 10: *spinach-knapsack image: "ruby:2.3" only: - master + cache: + key: "ruby-23" + paths: + - vendor/apt + - vendor/ruby .rspec-knapsack-ruby23: &rspec-knapsack-ruby23 <<: *rspec-knapsack diff --git a/CHANGELOG b/CHANGELOG index 3fd908d30d6..d2dcafc84a0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,8 +1,87 @@ Please view this file on the master branch, on stable branches it's out of date. -v 8.10.0(unreleased) - - Add notifications dropdown for groups +v 8.10.0 (unreleased) + - Fix commit builds API, return all builds for all pipelines for given commit. !4849 + - Replace Haml with Hamlit to make view rendering faster. !3666 + - Wrap code blocks on Activies and Todos page. !4783 (winniehell) + - Display last commit of deleted branch in push events !4699 (winniehell) + - Add Sidekiq queue duration to transaction metrics. + - Let Workhorse serve format-patch diffs + - Make images fit to the size of the viewport !4810 + - Fix check for New Branch button on Issue page !4630 (winniehell) + - Fix MR-auto-close text added to description. !4836 + - Fix pagination when sorting by columns with lots of ties (like priority) + - Exclude email check from the standard health check + - Fix changing issue state columns in milestone view + - Add notification settings dropdown for groups + - Fix user creation with stronger minimum password requirements !4054 (nathan-pmt) + - PipelinesFinder uses git cache data + - Check for conflicts with existing Project's wiki path when creating a new project. + - Remove unused front-end variable -> default_issues_tracker + - Add API endpoint for a group issues !4520 (mahcsig) + - Add Bugzilla integration !4930 (iamtjg) + - Allow [ci skip] to be in any case and allow [skip ci]. !4785 (simon_w) + - Add basic system information like memory and disk usage to the admin panel + +v 8.9.3 (unreleased) + - MergeRequestDiff reload content use update_columns to avoid multiple YAML de/serializations + - Decreased min width of screen to 1280px for pinned sidebar + - Fix encrypted data backwards compatibility after upgrading attr_encrypted gem + - Update mobile button icons to be more inline with typical UI paradigms + - Fixes missing avatar on system notes. !4954 + - Improve performance of obtaining the maximum access of a user in a project + +v 8.9.2 + - Fix visibility of snippets when searching. + - Fix an information disclosure when requesting access to a group containing private projects. + - Update omniauth-saml to 1.6.0 !4951 + +v 8.9.1 + - Refactor labels documentation. !3347 + - Eager load award emoji on notes. !4628 + - Fix some CI wording in documentation. !4660 + - Document `GIT_STRATEGY` and `GIT_DEPTH`. !4720 + - Add documentation for the export & import features. !4732 + - Add some docs for Docker Registry configuration. !4738 + - Ensure we don't send the "access request declined" email to access requesters on project deletion. !4744 + - Display group/project access requesters separately in the admin area. !4798 + - Add documentation and examples for configuring cloud storage for registry images. !4812 + - Clarifies documentation about artifact expiry. !4831 + - Fix the Network graph links. !4832 + - Fix MR-auto-close text added to description. !4836 + - Add documentation for award emoji now that comments can be awarded with emojis. !4839 + - Fix typo in export failure email. !4847 + - Fix header vertical centering. !4170 + - Fix subsequent SAML sign ins. !4718 + - Set button label when picking an option from status dropdown. !4771 + - Prevent invalid URLs from raising exceptions in WikiLink Filter. !4775 + - Handle external issues in IssueReferenceFilter. !4789 + - Support for rendering/redacting multiple documents. !4828 + - Update Todos documentation and screenshots to include new functionality. !4840 + - Hide nav arrows by default. !4843 + - Added bottom padding to label color suggestion link. !4845 + - Use jQuery objects in ref dropdown. !4850 + - Fix GitLab project import issues related to notes and builds. !4855 + - Restrict header logo to 36px so it doesn't overflow. !4861 + - Fix unwanted label unassignment. !4863 + - Fix mobile Safari bug where horizontal nav arrows would flicker on scroll. !4869 + - Restore old behavior around diff notes to outdated discussions. !4870 + - Fix merge requests project settings help link anchor. !4873 + - Fix 404 when accessing pipelines as guest user on public projects. !4881 + - Remove width restriction for logo on sign-in page. !4888 + - Bump gitlab_git to 10.2.3 to fix false truncated warnings with ISO-8559 files. !4884 + - Apply selected value as label. !4886 + - Fix temp file being deleted after the request while importing a GitLab project. !4894 + - Fix pagination when sorting by columns with lots of ties (like priority) + - Implement Subresource Integrity for CSS and JavaScript assets. This prevents malicious assets from loading in the case of a CDN compromise. + - Fix user creation with stronger minimum password requirements !4054 (nathan-pmt) + - Fix a wrong MR status when merge_when_build_succeeds & project.only_allow_merge_if_build_succeeds are true. !4912 + - Add SMTP as default delivery method to match gitlab-org/omnibus-gitlab!826. !4915 + - Remove duplicate 'New Page' button on edit wiki page + +v 8.9.0 v 8.9.0 (unreleased) + - Fix group visibility form layout in application settings - Fix builds API response not including commit data - Fix error when CI job variables key specified but not defined - Fix pipeline status when there are no builds in pipeline @@ -15,7 +94,6 @@ v 8.9.0 (unreleased) - Fix endless redirections when accessing user OAuth applications when they are disabled - Allow enabling wiki page events from Webhook management UI - Bump rouge to 1.11.0 - - Fix MR-auto-close text added to description - Fix issue with arrow keys not working in search autocomplete dropdown - Fix an issue where note polling stopped working if a window was in the background during a refresh. @@ -39,7 +117,6 @@ v 8.9.0 (unreleased) - Implement a fair usage of shared runners - Remove project notification settings associated with deleted projects - Fix 404 page when viewing TODOs that contain milestones or labels in different projects - - Wrap code blocks on Activies and Todos page !4783 (winniehell) - Add a metric for the number of new Redis connections created by a transaction - Fix Error 500 when viewing a blob with binary characters after the 1024-byte mark - Redesign navigation for project pages @@ -100,6 +177,7 @@ v 8.9.0 (unreleased) - Add Application Setting to configure Container Registry token expire delay (default 5min) - Cache assigned issue and merge request counts in sidebar nav - Use Knapsack only in CI environment + - Updated project creation page to match new UI #2542 - Cache project build count in sidebar nav - Add milestone expire date to the right sidebar - Manually mark a issue or merge request as a todo @@ -155,6 +233,10 @@ v 8.9.0 (unreleased) - Add tooltip to pin/unpin navbar - Add new sub nav style to Wiki and Graphs sub navigation +v 8.8.6 + - Fix visibility of snippets when searching. + - Update omniauth-saml to 1.6.0 !4951 + v 8.8.5 - Import GitHub repositories respecting the API rate limit !4166 - Fix todos page throwing errors when you have a project pending deletion !4300 @@ -285,6 +367,10 @@ v 8.8.0 - When creating a .gitignore file a dropdown with templates will be provided - Shows the issue/MR list search/filter form and corrects the mobile styling for guest users. #17562 +v 8.7.8 + - Fix visibility of snippets when searching. + - Update omniauth-saml to 1.6.0 !4951 + v 8.7.7 - Fix import by `Any Git URL` broken if the URL contains a space - Prevent unauthorized access to other projects build traces diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION index 8bd6ba8c5c3..879be8a98fc 100644 --- a/GITLAB_WORKHORSE_VERSION +++ b/GITLAB_WORKHORSE_VERSION @@ -1 +1 @@ -0.7.5 +0.7.7 @@ -30,7 +30,7 @@ gem 'omniauth-github', '~> 1.1.1' gem 'omniauth-gitlab', '~> 1.0.0' gem 'omniauth-google-oauth2', '~> 0.2.0' gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos -gem 'omniauth-saml', '~> 1.5.0' +gem 'omniauth-saml', '~> 1.6.0' gem 'omniauth-shibboleth', '~> 1.2.0' gem 'omniauth-twitter', '~> 1.2.0' gem 'omniauth_crowd', '~> 2.2.0' @@ -76,7 +76,7 @@ gem 'rack-cors', '~> 0.4.0', require: 'rack/cors' gem "kaminari", "~> 0.17.0" # HAML -gem "haml-rails", '~> 0.9.0' +gem 'hamlit', '~> 2.5' # Files attachments gem "carrierwave", '~> 0.10.0' @@ -91,6 +91,7 @@ gem 'fog-core', '~> 1.40' gem 'fog-local', '~> 0.3' gem 'fog-google', '~> 0.3' gem 'fog-openstack', '~> 0.1' +gem 'fog-rackspace', '~> 0.1.1' # for aws storage gem "unf", '~> 0.1.4' @@ -234,7 +235,7 @@ gem 'net-ssh', '~> 3.0.1' gem 'base32', '~> 0.3.0' # Sentry integration -gem 'sentry-raven', '~> 0.15' +gem 'sentry-raven', '~> 1.1.0' gem 'premailer-rails', '~> 1.9.0' @@ -346,3 +347,6 @@ gem "paranoia", "~> 2.0" # Health check gem 'health_check', '~> 1.5.1' + +# System information +gem 'vmstat', '~> 2.1.0' diff --git a/Gemfile.lock b/Gemfile.lock index ba16e4bf337..4c5350ba639 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -243,6 +243,11 @@ GEM fog-core (>= 1.39) fog-json (>= 1.0) ipaddress (>= 0.8) + fog-rackspace (0.1.1) + fog-core (>= 1.35) + fog-json (>= 1.0) + fog-xml (>= 0.1) + ipaddress (>= 0.8) fog-xml (0.1.2) fog-core nokogiri (~> 1.5, >= 1.5.11) @@ -277,7 +282,7 @@ GEM posix-spawn (~> 0.3) gitlab_emoji (0.3.1) gemojione (~> 2.2, >= 2.2.1) - gitlab_git (10.2.0) + gitlab_git (10.2.3) activesupport (~> 4.0) charlock_holmes (~> 0.7.3) github-linguist (~> 4.7.0) @@ -320,14 +325,10 @@ GEM grape-entity (0.4.8) activesupport multi_json (>= 1.3.2) - haml (4.0.7) + hamlit (2.5.0) + temple (~> 0.7.6) + thor tilt - haml-rails (0.9.0) - actionpack (>= 4.0.1) - activesupport (>= 4.0.1) - haml (>= 4.0.6, < 5.0) - html2haml (>= 1.0.1) - railties (>= 4.0.1) hashie (3.4.3) health_check (1.5.1) rails (>= 2.3.0) @@ -337,11 +338,6 @@ GEM html-pipeline (1.11.0) activesupport (>= 2) nokogiri (~> 1.4) - html2haml (2.0.0) - erubis (~> 2.7.0) - haml (~> 4.0.0) - nokogiri (~> 1.6.0) - ruby_parser (~> 3.5) htmlentities (4.3.4) http_parser.rb (0.5.3) httparty (0.13.7) @@ -468,9 +464,9 @@ GEM omniauth-oauth2 (1.3.1) oauth2 (~> 1.0) omniauth (~> 1.2) - omniauth-saml (1.5.0) + omniauth-saml (1.6.0) omniauth (~> 1.3) - ruby-saml (~> 1.1, >= 1.1.1) + ruby-saml (~> 1.3) omniauth-shibboleth (1.2.1) omniauth (>= 1.0.0) omniauth-twitter (1.2.1) @@ -631,9 +627,8 @@ GEM ruby-fogbugz (0.2.1) crack (~> 0.4) ruby-progressbar (1.8.1) - ruby-saml (1.1.2) + ruby-saml (1.3.0) nokogiri (>= 1.5.10) - uuid (~> 2.3) ruby_parser (3.8.2) sexp_processor (~> 4.1) rubyntlm (0.5.2) @@ -665,7 +660,7 @@ GEM activesupport (>= 3.1, < 4.3) select2-rails (3.5.9.3) thor (~> 0.14) - sentry-raven (0.15.6) + sentry-raven (1.1.0) faraday (>= 0.7.6) settingslogic (2.0.9) sexp_processor (4.7.0) @@ -733,6 +728,7 @@ GEM railties (>= 3.2.5, < 6) teaspoon-jasmine (2.2.0) teaspoon (>= 1.0.0) + temple (0.7.7) term-ansicolor (1.3.2) tins (~> 1.0) test_after_commit (0.4.2) @@ -789,6 +785,7 @@ GEM coercible (~> 1.0) descendants_tracker (~> 0.0, >= 0.0.3) equalizer (~> 0.0, >= 0.0.9) + vmstat (2.1.0) warden (1.2.6) rack (>= 1.0) web-console (2.3.0) @@ -866,6 +863,7 @@ DEPENDENCIES fog-google (~> 0.3) fog-local (~> 0.3) fog-openstack (~> 0.1) + fog-rackspace (~> 0.1.1) font-awesome-rails (~> 4.6.1) foreman fuubar (~> 2.0.0) @@ -882,7 +880,7 @@ DEPENDENCIES gon (~> 6.0.1) grape (~> 0.13.0) grape-entity (~> 0.4.2) - haml-rails (~> 0.9.0) + hamlit (~> 2.5) health_check (~> 1.5.1) hipchat (~> 1.5.0) html-pipeline (~> 1.11.0) @@ -920,7 +918,7 @@ DEPENDENCIES omniauth-gitlab (~> 1.0.0) omniauth-google-oauth2 (~> 0.2.0) omniauth-kerberos (~> 0.3.0) - omniauth-saml (~> 1.5.0) + omniauth-saml (~> 1.6.0) omniauth-shibboleth (~> 1.2.0) omniauth-twitter (~> 1.2.0) omniauth_crowd (~> 2.2.0) @@ -960,7 +958,7 @@ DEPENDENCIES sdoc (~> 0.3.20) seed-fu (~> 2.3.5) select2-rails (~> 3.5.9) - sentry-raven (~> 0.15) + sentry-raven (~> 1.1.0) settingslogic (~> 2.0.9) sham_rack shoulda-matchers (~> 2.8.0) @@ -993,6 +991,7 @@ DEPENDENCIES unicorn-worker-killer (~> 0.4.2) version_sorter (~> 2.0.0) virtus (~> 1.0.1) + vmstat (~> 2.1.0) web-console (~> 2.0) webmock (~> 1.21.0) wikicloth (= 0.8.1) @@ -1 +1 @@ -8.9.0-pre +8.10.0-pre diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index 0206db461da..b6dbf2d0cc1 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -50,7 +50,7 @@ #= require_directory ./ci #= require_directory ./commit #= require_directory ./extensions -#= require_directory ./lib +#= require_directory ./lib/utils #= require_directory ./u2f #= require_directory . #= require fuzzaldrin-plus @@ -199,7 +199,6 @@ $ -> $('.header-content .header-logo').toggle() $('.header-content .navbar-collapse').toggle() $('.navbar-toggle').toggleClass('active') - $('.navbar-toggle i').toggleClass("fa-angle-right fa-angle-left") # Show/hide comments on diff $body.on "click", ".js-toggle-diff-comments", (e) -> @@ -261,7 +260,7 @@ $ -> new Aside() # Sidenav pinning - if $window.width() < 1440 and $.cookie('pin_nav') is 'true' + if $window.width() < 1280 and $.cookie('pin_nav') is 'true' $.cookie('pin_nav', 'false', { path: '/' }) $('.page-with-sidebar') .toggleClass('page-sidebar-collapsed page-sidebar-expanded') diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index 030f1564862..37d0adaa625 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -341,7 +341,9 @@ class @AwardsHandler for emoji in frequentlyUsedEmojis $(".emoji-menu-content [data-emoji='#{emoji}']").closest('li').clone().appendTo(ul) - $('input.emoji-search').after(ul).after($('<h5>').text('Frequently used')) + $('.emoji-menu-content') + .prepend(ul) + .prepend($('<h5>').text('Frequently used')) @frequentEmojiBlockRendered = true @@ -356,7 +358,7 @@ class @AwardsHandler if term # Generate a search result block - h5 = $('<h5>').text('Search results').addClass('emoji-search') + h5 = $('<h5>').text('Search results') found_emojis = @searchEmojis(term).show() ul = $('<ul>').addClass('emoji-menu-list emoji-menu-search').append(found_emojis) $('.emoji-menu-content ul, .emoji-menu-content h5').hide() diff --git a/app/assets/javascripts/blob/template_selector.js.coffee b/app/assets/javascripts/blob/template_selector.js.coffee index e76e303189d..40c9169beac 100644 --- a/app/assets/javascripts/blob/template_selector.js.coffee +++ b/app/assets/javascripts/blob/template_selector.js.coffee @@ -19,6 +19,7 @@ class @TemplateSelector data: @data, filterable: true, selectable: true, + toggleLabel: @toggleLabel, search: fields: ['name'] clicked: @onClick @@ -31,6 +32,9 @@ class @TemplateSelector @onFilenameUpdate() ) + toggleLabel: (item) -> + item.name + onFilenameUpdate: -> return unless @$input.length diff --git a/app/assets/javascripts/gl_dropdown.js.coffee b/app/assets/javascripts/gl_dropdown.js.coffee index 2a7bf0bc306..1b0d9f0b1ae 100644 --- a/app/assets/javascripts/gl_dropdown.js.coffee +++ b/app/assets/javascripts/gl_dropdown.js.coffee @@ -186,6 +186,8 @@ class GitLabDropdown @fullData = data @parseData @fullData + + @filter.input.trigger('keyup') if @options.filterable and @filter and @filter.input } # Init filterable @@ -280,7 +282,7 @@ class GitLabDropdown html = @renderData(data) # Render the full menu - full_html = @renderMenu(html.join("")) + full_html = @renderMenu(html) @appendMenu(full_html) @@ -351,7 +353,8 @@ class GitLabDropdown if @options.renderMenu menu_html = @options.renderMenu(html) else - menu_html = "<ul>#{html}</ul>" + menu_html = $('<ul />') + .append(html) return menu_html @@ -360,7 +363,9 @@ class GitLabDropdown selector = '.dropdown-content' if @dropdown.find(".dropdown-toggle-page").length selector = ".dropdown-page-one .dropdown-content" - $(selector, @dropdown).html html + $(selector, @dropdown) + .empty() + .append(html) # Render the row renderItem: (data, group = false, index = false) -> @@ -459,7 +464,7 @@ class GitLabDropdown # Toggle the dropdown label if @options.toggleLabel - @updateLabel() + @updateLabel(selectedObject, el, @) else selectedObject else if el.hasClass(INDETERMINATE_CLASS) @@ -486,7 +491,7 @@ class GitLabDropdown # Toggle the dropdown label if @options.toggleLabel - @updateLabel(selectedObject, el) + @updateLabel(selectedObject, el, @) if value? if !field.length and fieldName @addInput(fieldName, value) @@ -585,8 +590,8 @@ class GitLabDropdown # Scroll the dropdown content up $dropdownContent.scrollTop(listItemTop - dropdownContentTop) - updateLabel: (selected = null, el = null) => - $(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selected, el) + updateLabel: (selected = null, el = null, instance = null) => + $(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selected, el, instance) $.fn.glDropdown = (opts) -> return @.each -> diff --git a/app/assets/javascripts/graphs/application.js.coffee b/app/assets/javascripts/graphs/application.js.coffee index 91f81a5d249..e0f681acf0b 100644 --- a/app/assets/javascripts/graphs/application.js.coffee +++ b/app/assets/javascripts/graphs/application.js.coffee @@ -4,5 +4,4 @@ # It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the # the compiled file. # -#= require Chart #= require_tree . diff --git a/app/assets/javascripts/issuable.js.coffee b/app/assets/javascripts/issuable.js.coffee index d0901be1509..0527c66461c 100644 --- a/app/assets/javascripts/issuable.js.coffee +++ b/app/assets/javascripts/issuable.js.coffee @@ -59,21 +59,23 @@ issuable_created = false filterResults: (form) => formData = form.serialize() - $('.issues-holder, .merge-requests-holder').css('opacity', '0.5') formAction = form.attr('action') issuesUrl = formAction issuesUrl += ("#{if formAction.indexOf('?') < 0 then '?' else '&'}") issuesUrl += formData - Turbolinks.visit(issuesUrl); + Turbolinks.visit(issuesUrl) initChecks: -> + @issuableBulkActions = $('.bulk-update').data('bulkActions') + $('.check_all_issues').off('click').on('click', -> $('.selected_issue').prop('checked', @checked) Issuable.checkChanged() ) - $('.selected_issue').off('change').on('change', Issuable.checkChanged) + $('.selected_issue').off('change').on('change', Issuable.checkChanged.bind(@)) + checkChanged: -> checked_issues = $('.selected_issue:checked') @@ -88,3 +90,6 @@ issuable_created = false $('#update_issues_ids').val [] $('.issues_bulk_update').hide() $('.issues-other-filters').show() + @issuableBulkActions.willUpdateLabels = false + + return true diff --git a/app/assets/javascripts/issue.js.coffee b/app/assets/javascripts/issue.js.coffee index 157361404e0..f446aa49cde 100644 --- a/app/assets/javascripts/issue.js.coffee +++ b/app/assets/javascripts/issue.js.coffee @@ -99,7 +99,7 @@ class @Issue # If the user doesn't have the required permissions the container isn't # rendered at all. - return unless $container + return if $container.length is 0 $.getJSON($container.data('path')) .error -> diff --git a/app/assets/javascripts/issue_status_select.js.coffee b/app/assets/javascripts/issue_status_select.js.coffee index c5740f27ddd..ed50e2e698f 100644 --- a/app/assets/javascripts/issue_status_select.js.coffee +++ b/app/assets/javascripts/issue_status_select.js.coffee @@ -6,6 +6,13 @@ class @IssueStatusSelect $(el).glDropdown( selectable: true fieldName: fieldName + toggleLabel: (selected, el, instance) => + label = 'Author' + $item = instance.dropdown.find('.is-active') + label = $item.text() if $item.length + label + clicked: (item, $el, e)-> + e.preventDefault() id: (obj, el) -> $(el).data("id") ) diff --git a/app/assets/javascripts/issues-bulk-assignment.js.coffee b/app/assets/javascripts/issues-bulk-assignment.js.coffee index b454f9389dd..6b0e69dbae7 100644 --- a/app/assets/javascripts/issues-bulk-assignment.js.coffee +++ b/app/assets/javascripts/issues-bulk-assignment.js.coffee @@ -7,6 +7,11 @@ class @IssuableBulkActions @issues = @getElement('.issues-list .issue') } = opts + # Save instance + @form.data 'bulkActions', @ + + @willUpdateLabels = false + @bindEvents() # Fixes bulk-assign not working when navigating through pages @@ -87,11 +92,12 @@ class @IssuableBulkActions add_label_ids : [] remove_label_ids : [] - @getLabelsToApply().map (id) -> - formData.update.add_label_ids.push id + if @willUpdateLabels + @getLabelsToApply().map (id) -> + formData.update.add_label_ids.push id - @getLabelsToRemove().map (id) -> - formData.update.remove_label_ids.push id + @getLabelsToRemove().map (id) -> + formData.update.remove_label_ids.push id formData diff --git a/app/assets/javascripts/labels_select.js.coffee b/app/assets/javascripts/labels_select.js.coffee index 6a10db10eb1..e95fd96a83f 100644 --- a/app/assets/javascripts/labels_select.js.coffee +++ b/app/assets/javascripts/labels_select.js.coffee @@ -319,6 +319,8 @@ class @LabelsSelect multiSelect: $dropdown.hasClass 'js-multiselect' clicked: (label) -> + _this.enableBulkLabelDropdown() + if $dropdown.hasClass('js-filter-bulk-update') return @@ -377,3 +379,8 @@ class @LabelsSelect label_ids.push $("#issue_#{issue_id}").data('labels') _.intersection.apply _, label_ids + + enableBulkLabelDropdown: -> + if $('.selected_issue:checked').length + issuableBulkActions = $('.bulk-update').data('bulkActions') + issuableBulkActions.willUpdateLabels = true diff --git a/app/assets/javascripts/layout_nav.js.coffee b/app/assets/javascripts/layout_nav.js.coffee index f8f0aea427e..f639f7f5892 100644 --- a/app/assets/javascripts/layout_nav.js.coffee +++ b/app/assets/javascripts/layout_nav.js.coffee @@ -3,11 +3,10 @@ hideEndFade = ($scrollingTabs) -> $this = $(@) $this - .find('.fade-right') - .toggleClass('end-scroll', $this.width() is $this.prop('scrollWidth')) + .siblings('.fade-right') + .toggleClass('scrolling', $this.width() < $this.prop('scrollWidth')) $ -> - $('.fade-left').addClass('end-scroll') hideEndFade($('.scrolling-tabs')) @@ -21,5 +20,5 @@ $ -> currentPosition = $this.scrollLeft() maxPosition = $this.prop('scrollWidth') - $this.outerWidth() - $this.find('.fade-left').toggleClass('end-scroll', currentPosition is 0) - $this.find('.fade-right').toggleClass('end-scroll', currentPosition is maxPosition) + $this.siblings('.fade-left').toggleClass('scrolling', currentPosition > 0) + $this.siblings('.fade-right').toggleClass('scrolling', currentPosition < maxPosition - 1) diff --git a/app/assets/javascripts/lib/chart.js.coffee b/app/assets/javascripts/lib/chart.js.coffee new file mode 100644 index 00000000000..82217fc5107 --- /dev/null +++ b/app/assets/javascripts/lib/chart.js.coffee @@ -0,0 +1 @@ +#= require Chart diff --git a/app/assets/javascripts/lib/d3.js.coffee b/app/assets/javascripts/lib/d3.js.coffee new file mode 100644 index 00000000000..74f0a0bb06a --- /dev/null +++ b/app/assets/javascripts/lib/d3.js.coffee @@ -0,0 +1 @@ +#= require d3 diff --git a/app/assets/javascripts/lib/raphael.js.coffee b/app/assets/javascripts/lib/raphael.js.coffee new file mode 100644 index 00000000000..ab8e5979b87 --- /dev/null +++ b/app/assets/javascripts/lib/raphael.js.coffee @@ -0,0 +1,3 @@ +#= require raphael +#= require g.raphael +#= require g.bar diff --git a/app/assets/javascripts/lib/animate.js.coffee b/app/assets/javascripts/lib/utils/animate.js.coffee index ec3b44d6126..ec3b44d6126 100644 --- a/app/assets/javascripts/lib/animate.js.coffee +++ b/app/assets/javascripts/lib/utils/animate.js.coffee diff --git a/app/assets/javascripts/lib/common_utils.js.coffee b/app/assets/javascripts/lib/utils/common_utils.js.coffee index e39dcb2daa9..e39dcb2daa9 100644 --- a/app/assets/javascripts/lib/common_utils.js.coffee +++ b/app/assets/javascripts/lib/utils/common_utils.js.coffee diff --git a/app/assets/javascripts/lib/datetime_utility.js.coffee b/app/assets/javascripts/lib/utils/datetime_utility.js.coffee index 948d6dbf07e..948d6dbf07e 100644 --- a/app/assets/javascripts/lib/datetime_utility.js.coffee +++ b/app/assets/javascripts/lib/utils/datetime_utility.js.coffee diff --git a/app/assets/javascripts/lib/emoji_aliases.js.coffee.erb b/app/assets/javascripts/lib/utils/emoji_aliases.js.coffee.erb index 80f9936b9c2..80f9936b9c2 100644 --- a/app/assets/javascripts/lib/emoji_aliases.js.coffee.erb +++ b/app/assets/javascripts/lib/utils/emoji_aliases.js.coffee.erb diff --git a/app/assets/javascripts/lib/jquery.timeago.js b/app/assets/javascripts/lib/utils/jquery.timeago.js index cc17aa7d3d1..cc17aa7d3d1 100644 --- a/app/assets/javascripts/lib/jquery.timeago.js +++ b/app/assets/javascripts/lib/utils/jquery.timeago.js diff --git a/app/assets/javascripts/lib/md5.js b/app/assets/javascripts/lib/utils/md5.js index b63716eaad2..b63716eaad2 100644 --- a/app/assets/javascripts/lib/md5.js +++ b/app/assets/javascripts/lib/utils/md5.js diff --git a/app/assets/javascripts/lib/notify.js.coffee b/app/assets/javascripts/lib/utils/notify.js.coffee index 9e28353ac34..9e28353ac34 100644 --- a/app/assets/javascripts/lib/notify.js.coffee +++ b/app/assets/javascripts/lib/utils/notify.js.coffee diff --git a/app/assets/javascripts/lib/text_utility.js.coffee b/app/assets/javascripts/lib/utils/text_utility.js.coffee index bb2772dfed2..7bcb876d056 100644 --- a/app/assets/javascripts/lib/text_utility.js.coffee +++ b/app/assets/javascripts/lib/utils/text_utility.js.coffee @@ -10,17 +10,41 @@ gl.text.selectedText = (text, textarea) -> text.substring(textarea.selectionStart, textarea.selectionEnd) - gl.text.insertText = (textArea, text, tag, selected, wrap) -> + gl.text.lineBefore = (text, textarea) -> + split = text.substring(0, textarea.selectionStart).trim().split('\n') + split[split.length - 1] + + gl.text.lineAfter = (text, textarea) -> + text.substring(textarea.selectionEnd).trim().split('\n')[0] + + gl.text.blockTagText = (text, textArea, blockTag, selected) -> + lineBefore = @lineBefore(text, textArea) + lineAfter = @lineAfter(text, textArea) + + if lineBefore is blockTag and lineAfter is blockTag + # To remove the block tag we have to select the line before & after + if blockTag? + textArea.selectionStart = textArea.selectionStart - (blockTag.length + 1) + textArea.selectionEnd = textArea.selectionEnd + (blockTag.length + 1) + + selected + else + "#{blockTag}\n#{selected}\n#{blockTag}" + + gl.text.insertText = (textArea, text, tag, blockTag, selected, wrap) -> selectedSplit = selected.split('\n') startChar = if not wrap and textArea.selectionStart > 0 then '\n' else '' - if selectedSplit.length > 1 and not wrap - insertText = selectedSplit.map((val) -> - if val.indexOf(tag) is 0 - "#{val.replace(tag, '')}" - else - "#{tag}#{val}" - ).join('\n') + if selectedSplit.length > 1 and (not wrap or blockTag?) + if blockTag? + insertText = @blockTagText(text, textArea, blockTag, selected) + else + insertText = selectedSplit.map((val) -> + if val.indexOf(tag) is 0 + "#{val.replace(tag, '')}" + else + "#{tag}#{val}" + ).join('\n') else insertText = "#{startChar}#{tag}#{selected}#{if wrap then tag else ' '}" @@ -51,7 +75,7 @@ textArea.setSelectionRange pos, pos - gl.text.updateText = (textArea, tag, wrap) -> + gl.text.updateText = (textArea, tag, blockTag, wrap) -> $textArea = $(textArea) oldVal = $textArea.val() textArea = $textArea.get(0) @@ -59,7 +83,7 @@ selected = @selectedText(text, textArea) $textArea.focus() - @insertText(textArea, text, tag, selected, wrap) + @insertText(textArea, text, tag, blockTag, selected, wrap) gl.text.init = (form) -> self = @ @@ -70,6 +94,7 @@ self.updateText( $this.closest('.md-area').find('textarea'), $this.data('md-tag'), + $this.data('md-block'), not $this.data('md-prepend') ) diff --git a/app/assets/javascripts/lib/type_utility.js.coffee b/app/assets/javascripts/lib/utils/type_utility.js.coffee index 957f0d86b36..957f0d86b36 100644 --- a/app/assets/javascripts/lib/type_utility.js.coffee +++ b/app/assets/javascripts/lib/utils/type_utility.js.coffee diff --git a/app/assets/javascripts/lib/url_utility.js.coffee b/app/assets/javascripts/lib/utils/url_utility.js.coffee index e8085e1c2e4..e8085e1c2e4 100644 --- a/app/assets/javascripts/lib/url_utility.js.coffee +++ b/app/assets/javascripts/lib/utils/url_utility.js.coffee diff --git a/app/assets/javascripts/lib/utf8_encode.js b/app/assets/javascripts/lib/utils/utf8_encode.js index 39ffe44dae0..39ffe44dae0 100644 --- a/app/assets/javascripts/lib/utf8_encode.js +++ b/app/assets/javascripts/lib/utils/utf8_encode.js diff --git a/app/assets/javascripts/milestone.js.coffee b/app/assets/javascripts/milestone.js.coffee index 0037a3a21c2..a19e68b39e2 100644 --- a/app/assets/javascripts/milestone.js.coffee +++ b/app/assets/javascripts/milestone.js.coffee @@ -4,18 +4,10 @@ class @Milestone type: "PUT" url: issue_url data: data - success: (data) -> - if data.saved == true - if data.assignee_avatar_url - img_tag = $('<img/>') - img_tag.attr('src', data.assignee_avatar_url) - img_tag.addClass('avatar s16') - $(li).find('.assignee-icon').html(img_tag) - else - $(li).find('.assignee-icon').html('') - $(li).effect 'highlight' - else - new Flash("Issue update failed", 'alert') + success: (_data) => + @successCallback(_data, li) + error: (data) -> + new Flash("Issue update failed", 'alert') dataType: "json" @sortIssues: (data) -> @@ -25,9 +17,10 @@ class @Milestone type: "PUT" url: sort_issues_url data: data - success: (data) -> - if data.saved != true - new Flash("Issues update failed", 'alert') + success: (_data) => + @successCallback(_data) + error: -> + new Flash("Issues update failed", 'alert') dataType: "json" @sortMergeRequests: (data) -> @@ -37,9 +30,10 @@ class @Milestone type: "PUT" url: sort_mr_url data: data - success: (data) -> - if data.saved != true - new Flash("MR update failed", 'alert') + success: (_data) => + @successCallback(_data) + error: (data) -> + new Flash("Issue update failed", 'alert') dataType: "json" @updateMergeRequest: (li, merge_request_url, data) -> @@ -47,20 +41,23 @@ class @Milestone type: "PUT" url: merge_request_url data: data - success: (data) -> - if data.saved == true - if data.assignee_avatar_url - img_tag = $('<img/>') - img_tag.attr('src', data.assignee_avatar_url) - img_tag.addClass('avatar s16') - $(li).find('.assignee-icon').html(img_tag) - else - $(li).find('.assignee-icon').html('') - $(li).effect 'highlight' - else - new Flash("Issue update failed", 'alert') + success: (_data) => + @successCallback(_data, li) + error: (data) -> + new Flash("Issue update failed", 'alert') dataType: "json" + @successCallback: (data, element) => + if data.assignee + img_tag = $('<img/>') + img_tag.attr('src', data.assignee.avatar_url) + img_tag.addClass('avatar s16') + $(element).find('.assignee-icon').html(img_tag) + else + $(element).find('.assignee-icon').html('') + + $(element).effect 'highlight' + constructor: -> oldMouseStart = $.ui.sortable.prototype._mouseStart $.ui.sortable.prototype._mouseStart = (event, overrideHandle, noActivation) -> @@ -81,8 +78,10 @@ class @Milestone stop: (event, ui) -> $(".issues-sortable-list").css "min-height", "0px" update: (event, ui) -> - data = $(this).sortable("serialize") - Milestone.sortIssues(data) + # Prevents sorting from container which element has been removed. + if $(this).find(ui.item).length > 0 + data = $(this).sortable("serialize") + Milestone.sortIssues(data) receive: (event, ui) -> new_state = $(this).data('state') diff --git a/app/assets/javascripts/network/application.js.coffee b/app/assets/javascripts/network/application.js.coffee index cb9eead855b..f75f63869c5 100644 --- a/app/assets/javascripts/network/application.js.coffee +++ b/app/assets/javascripts/network/application.js.coffee @@ -4,9 +4,6 @@ # It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the # the compiled file. # -#= require raphael -#= require g.raphael -#= require g.bar #= require_tree . $ -> diff --git a/app/assets/javascripts/project.js.coffee b/app/assets/javascripts/project.js.coffee index 96e10dd7e8a..3288c801388 100644 --- a/app/assets/javascripts/project.js.coffee +++ b/app/assets/javascripts/project.js.coffee @@ -70,17 +70,20 @@ class @Project fieldName: 'ref' renderRow: (ref) -> if ref.header? - "<li class='dropdown-header'>#{ref.header}</li>" + $('<li />') + .addClass('dropdown-header') + .text(ref.header) else - isActiveClass = if ref is selected then 'is-active' else '' - - "<li> - <a href='#' data-ref='#{escape(ref)}' class='#{isActiveClass}'> - #{ref} - </a> - </li>" + link = $('<a />') + .attr('href', '#') + .addClass(if ref is selected then 'is-active' else '') + .text(ref) + .attr('data-ref', escape(ref)) + + $('<li />') + .append(link) id: (obj, $el) -> - $el.data('ref') + $el.attr('data-ref') toggleLabel: (obj, $el) -> $el.text().trim() clicked: (e) -> diff --git a/app/assets/javascripts/users/application.js.coffee b/app/assets/javascripts/users/application.js.coffee index 647ffbf5f45..91cacfece46 100644 --- a/app/assets/javascripts/users/application.js.coffee +++ b/app/assets/javascripts/users/application.js.coffee @@ -1,8 +1,2 @@ -# This is a manifest file that'll be compiled into including all the files listed below. -# Add new JavaScript/Coffee code in separate files in this directory and they'll automatically -# be included in the compiled file accessible from http://example.com/assets/application.js -# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the -# the compiled file. # -#= require d3 #= require_tree . diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index a7bcb456560..c32ce5195c6 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -26,7 +26,6 @@ header { text-align: center; #tanuki-logo, img { - width: 36px; height: 36px; } } @@ -132,6 +131,10 @@ header { transition-duration: .3s; z-index: 999; + svg, img { + height: 36px; + } + &:hover { cursor: pointer; } diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss index 0281b06d3ba..6e5f216c894 100644 --- a/app/assets/stylesheets/framework/nav.scss +++ b/app/assets/stylesheets/framework/nav.scss @@ -1,6 +1,6 @@ @mixin fade($gradient-direction, $rgba, $gradient-color) { - visibility: visible; - opacity: 1; + visibility: hidden; + opacity: 0; z-index: 2; position: absolute; bottom: 12px; @@ -13,17 +13,16 @@ background: -moz-linear-gradient($gradient-direction, $rgba, $gradient-color 45%); background: linear-gradient($gradient-direction, $rgba, $gradient-color 45%); - &.end-scroll { - visibility: hidden; - opacity: 0; + &.scrolling { + visibility: visible; + opacity: 1; transition-duration: .3s; } .fa { position: relative; - top: 3px; - font-size: 13px; - color: $btn-placeholder-gray; + top: 5px; + font-size: 18px; } } @@ -32,6 +31,7 @@ overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch; + &::-webkit-scrollbar { display: none; } @@ -272,7 +272,7 @@ float: right; padding: 7px 0 0; - @media (max-width: $screen-xs-max) { + @media (max-width: $screen-sm-max) { display: none; } @@ -303,41 +303,9 @@ } .nav-links { - @include scrolling-links(); border-bottom: none; height: 51px; - svg { - position: relative; - top: 2px; - margin-right: 2px; - height: 15px; - width: auto; - - path, - polygon { - fill: $layout-link-gray; - } - } - - .fade-right { - @include fade(left, rgba(250, 250, 250, 0.4), $background-color); - right: 0; - - .fa { - right: -7px; - } - } - - .fade-left { - @include fade(right, rgba(250, 250, 250, 0.4), $background-color); - left: 0; - - .fa { - left: -7px; - } - } - li { a { @@ -373,18 +341,6 @@ } } } - - .nav-control { - - .fade-right { - @media (min-width: $screen-xs-max) { - right: 68px; - } - @media (max-width: $screen-xs-min) { - right: 0; - } - } - } } .scrolling-tabs-container { @@ -392,15 +348,42 @@ .nav-links { @include scrolling-links(); + } + + .fade-right { + @include fade(left, rgba(255, 255, 255, 0.4), $background-color); + right: -5px; + + .fa { + right: -7px; + } + } + + .fade-left { + @include fade(right, rgba(255, 255, 255, 0.4), $background-color); + left: -5px; + + .fa { + left: -7px; + } + } + + &.sub-nav-scroll { .fade-right { - @include fade(left, rgba(255, 255, 255, 0.4), $background-color); right: 0; + + .fa { + right: -23px; + } } .fade-left { - @include fade(right, rgba(255, 255, 255, 0.4), $background-color); left: 0; + + .fa { + left: 10px; + } } } } @@ -413,21 +396,19 @@ .fade-right { @include fade(left, rgba(255, 255, 255, 0.4), $white-light); - right: 0; + right: -5px; + + .fa { + right: -7px; + } } .fade-left { @include fade(right, rgba(255, 255, 255, 0.4), $white-light); - left: 0; - } - - &.event-filter { - .fade-right { - visibility: hidden; + left: -5px; - @media (max-width: $screen-xs-max) { - visibility: visible; - } + .fa { + left: -7px; } } } diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index 98f917ce69b..e8d6a7f2775 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -1,5 +1,6 @@ .page-with-sidebar { padding-top: $header-height; + padding-bottom: 25px; transition: padding $sidebar-transition-duration; .sidebar-wrapper { diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index c37574ca7a1..87f8a17659f 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -7,7 +7,7 @@ $gutter_collapsed_width: 62px; $gutter_width: 290px; $gutter_inner_width: 258px; $sidebar-transition-duration: .15s; -$sidebar-breakpoint: 1440px; +$sidebar-breakpoint: 1280px; /* * UI elements diff --git a/app/assets/stylesheets/pages/awards.scss b/app/assets/stylesheets/pages/awards.scss index 6211f3a52eb..5faedfedd66 100644 --- a/app/assets/stylesheets/pages/awards.scss +++ b/app/assets/stylesheets/pages/awards.scss @@ -8,8 +8,9 @@ .emoji-menu { position: absolute; margin-top: 3px; - z-index: 1000; - min-width: 160px; + padding: $gl-padding; + z-index: 9; + width: 300px; font-size: 14px; background-color: $award-emoji-menu-bg; border: 1px solid $award-emoji-menu-border; @@ -33,20 +34,18 @@ } .emoji-menu-content { - padding: $gl-padding; - width: 300px; height: 300px; overflow-y: scroll; - - input.emoji-search { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAAFu0lEQVRIia1WTahkVxH+quqce7vf6zdvJpHoIlkYJ2SiJiIokmQjgoGgIAaEIYuYXWICgojiwkmC4taFwhjcyIDusogEIwwiSSCKPwsdwzAg0SjJ9Izzk5n3+nXfe8+pqizOvd395scfsJqi6dPnnDr11Vc/NJ1OwUTosqJLCmYCHCAC2mSHs+ojZv6AO46Y+20AhIneJsafhPhXVZSXDk7qi+aOLhtQNuBmQtcarAKjTXpn2+l3u2yPunvZSABRucjcAV/eMZuM48/Go/g1d19kc4wq+e8MZjWkbI/P5t2P3RFFbv7SQdyBlBUx8N8OTuqjMcof+N94yMPrY2DMm/ytnb32J0QrY+6AqsHM4Q64O9SKDmerKDD3Oy/tNL9vk342CC8RuU6n0ymCMHb22scu7zQngtASOjUHE1BX4UUAv4b7Ow6qiXCXuz/UdvogAAweDY943/b4cAz0ZlYHXeMsnT07RVb7wMUr8ykI4H5HVkMd5Rcb4/jNURVOL5qErAaAUUdCCIJ5kx5q2nw8m39ImEAAsjpE6PStB0YfMcd1wqqG3Xn7A3PfZyyKnNjaqD4fmE/fCNKshirIyY1xvI+Av6g5QIAIIWX7cJPssboSiBBEeKmsZne0Sb8kzAUWNYyq8NvbDo0fZ6beqxuLmqOOMr/lwOh+YXpXtbjERGja9JyZ9+HxpXKb9Gj5oywRESbj+Cj1ENG1QViTGBl1FbC1We1tbVRfHWIoQkhqH9xbpE92XUbb6VJZ1R4crjRz1JWcDMJvLdoMcyAEhjuwHo8Bfndg3mbszhOY+adVlMtD3po51OwzIQiEaams7oeJhxRw1FFOVpFRRUYIhMBAFRnjOsC8IFHHUA4TQQhgAqpAiIFfGbxkIqj54ayGbL7UoOqHCniAEKHLNr26l+D9wQJzeUwMAnfHvEnLECzZRwRV++d60ptjW9VLZeolEJG6GwCCE0CFVNB+Ay0NEqoQYG4YYFu7B8IEVRt3uRzy/osIoLV9QZimWXGHUMFdmI6M64DUF2Je88R9VZqCSP+QlcF5k+4tCzSsXaqjINuK6UyE0+s/mk6/qFq8oAIL9pqMLhkGsNrOyoOIlszust3aJv0U9+kFdwjTGwWl1YdF+KWlQSZ0Se/psj8yGVdg5tJyfH96EBWmLtoEMwMzMFt031NzGWLLzKhC+KV7H5ZeeaMOPxemma2x68puc0LN3+/u6LJiePS6MKHvn4wu6cPzJj0hsioeMfDrEvjv5r6W9gBvjKJujuKzQ0URIZj75NylvT+mbHfXQa4rwAMaVRTMm/SFyzvNy0yF6+4AM+1ubcSnqkAIUjQKl1RKSbE5jt+vovx1MBqF0WW7/d1Z80ab9BtmuJ3Xk5cJKds9TZt/uLPXvtiTrQ+dIwqfAejUvM1os6FNikXKUHfQ+ekUsXT5u85enJ0CaBSkkGEo1syUQ+DfMdE/4GA1uzupf9zdbzhOmLsF4efHVXjaHHAzmDtGdQRd/Nc5wAEJjNki3XfhyvwVNz80xANrht3LsENY9cBBdN1L9GUyyvFRFZ42t75sBvCQRykbRlU4tT2pPxoCvzx09d4GmPs200M6wKdWSDGK8mppYSWdhAlt0qeaLv+IadXU9/Evq4FAZ8ej+LmtcTxaRX4NWI0Uag5Vg1p5MYg8BnlhXIdPHDow+vTWZvVMVttXDLqkTzZdPj6Qii6cP1cSvIdl3iQkNYyi9HH0I22y+93tY3DcQkTZgQtM+POoCr8x97eylkmtrgKuztrvXJ21x/aNKuqIkZ/fntRfCdcTfhUTAIhRzoDojJD0aSNLLwMzmpT7+JaLtyf1MwDo6qz9djFaUq3t9MlFmy/c1OCSceY9fMsVaL9mvH9ocXdkdWxv1scAePG0THAhMOaLdOw/Gvxfxb1w4eCapyIENUcV5M3/u8FitAxZ25P6GAHT3UX39Srw+QOb1ZffA98Dl2Wy1BYkAAAAAElFTkSuQmCC"); - background-repeat: no-repeat; - background-position: right 5px center; - background-size: 16px; - } } } +.emoji-search { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAAFu0lEQVRIia1WTahkVxH+quqce7vf6zdvJpHoIlkYJ2SiJiIokmQjgoGgIAaEIYuYXWICgojiwkmC4taFwhjcyIDusogEIwwiSSCKPwsdwzAg0SjJ9Izzk5n3+nXfe8+pqizOvd395scfsJqi6dPnnDr11Vc/NJ1OwUTosqJLCmYCHCAC2mSHs+ojZv6AO46Y+20AhIneJsafhPhXVZSXDk7qi+aOLhtQNuBmQtcarAKjTXpn2+l3u2yPunvZSABRucjcAV/eMZuM48/Go/g1d19kc4wq+e8MZjWkbI/P5t2P3RFFbv7SQdyBlBUx8N8OTuqjMcof+N94yMPrY2DMm/ytnb32J0QrY+6AqsHM4Q64O9SKDmerKDD3Oy/tNL9vk342CC8RuU6n0ymCMHb22scu7zQngtASOjUHE1BX4UUAv4b7Ow6qiXCXuz/UdvogAAweDY943/b4cAz0ZlYHXeMsnT07RVb7wMUr8ykI4H5HVkMd5Rcb4/jNURVOL5qErAaAUUdCCIJ5kx5q2nw8m39ImEAAsjpE6PStB0YfMcd1wqqG3Xn7A3PfZyyKnNjaqD4fmE/fCNKshirIyY1xvI+Av6g5QIAIIWX7cJPssboSiBBEeKmsZne0Sb8kzAUWNYyq8NvbDo0fZ6beqxuLmqOOMr/lwOh+YXpXtbjERGja9JyZ9+HxpXKb9Gj5oywRESbj+Cj1ENG1QViTGBl1FbC1We1tbVRfHWIoQkhqH9xbpE92XUbb6VJZ1R4crjRz1JWcDMJvLdoMcyAEhjuwHo8Bfndg3mbszhOY+adVlMtD3po51OwzIQiEaams7oeJhxRw1FFOVpFRRUYIhMBAFRnjOsC8IFHHUA4TQQhgAqpAiIFfGbxkIqj54ayGbL7UoOqHCniAEKHLNr26l+D9wQJzeUwMAnfHvEnLECzZRwRV++d60ptjW9VLZeolEJG6GwCCE0CFVNB+Ay0NEqoQYG4YYFu7B8IEVRt3uRzy/osIoLV9QZimWXGHUMFdmI6M64DUF2Je88R9VZqCSP+QlcF5k+4tCzSsXaqjINuK6UyE0+s/mk6/qFq8oAIL9pqMLhkGsNrOyoOIlszust3aJv0U9+kFdwjTGwWl1YdF+KWlQSZ0Se/psj8yGVdg5tJyfH96EBWmLtoEMwMzMFt031NzGWLLzKhC+KV7H5ZeeaMOPxemma2x68puc0LN3+/u6LJiePS6MKHvn4wu6cPzJj0hsioeMfDrEvjv5r6W9gBvjKJujuKzQ0URIZj75NylvT+mbHfXQa4rwAMaVRTMm/SFyzvNy0yF6+4AM+1ubcSnqkAIUjQKl1RKSbE5jt+vovx1MBqF0WW7/d1Z80ab9BtmuJ3Xk5cJKds9TZt/uLPXvtiTrQ+dIwqfAejUvM1os6FNikXKUHfQ+ekUsXT5u85enJ0CaBSkkGEo1syUQ+DfMdE/4GA1uzupf9zdbzhOmLsF4efHVXjaHHAzmDtGdQRd/Nc5wAEJjNki3XfhyvwVNz80xANrht3LsENY9cBBdN1L9GUyyvFRFZ42t75sBvCQRykbRlU4tT2pPxoCvzx09d4GmPs200M6wKdWSDGK8mppYSWdhAlt0qeaLv+IadXU9/Evq4FAZ8ej+LmtcTxaRX4NWI0Uag5Vg1p5MYg8BnlhXIdPHDow+vTWZvVMVttXDLqkTzZdPj6Qii6cP1cSvIdl3iQkNYyi9HH0I22y+93tY3DcQkTZgQtM+POoCr8x97eylkmtrgKuztrvXJ21x/aNKuqIkZ/fntRfCdcTfhUTAIhRzoDojJD0aSNLLwMzmpT7+JaLtyf1MwDo6qz9djFaUq3t9MlFmy/c1OCSceY9fMsVaL9mvH9ocXdkdWxv1scAePG0THAhMOaLdOw/Gvxfxb1w4eCapyIENUcV5M3/u8FitAxZ25P6GAHT3UX39Srw+QOb1ZffA98Dl2Wy1BYkAAAAAElFTkSuQmCC"); + background-repeat: no-repeat; + background-position: right 5px center; + background-size: 16px; +} + .emoji-menu-list { list-style: none; padding-left: 0; diff --git a/app/assets/stylesheets/pages/help.scss b/app/assets/stylesheets/pages/help.scss index 0b710ef168b..00ab42bec5c 100644 --- a/app/assets/stylesheets/pages/help.scss +++ b/app/assets/stylesheets/pages/help.scss @@ -63,5 +63,6 @@ border: 1px solid $table-border-gray; padding: 5px; margin: 5px; + max-height: calc(100vh - 100px); } } diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 21ff6ab71f0..542fa244689 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -10,6 +10,7 @@ border: 1px solid $table-border-gray; padding: 5px; margin: 5px; + max-height: calc(100vh - 100px); } } diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index f5f67e2cd84..47bfd144930 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -6,6 +6,7 @@ height: 30px; display: inline-block; margin-right: 10px; + margin-bottom: 10px; } &.suggest-colors-dropdown { diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index aca82f7f7bf..124f4afaa0d 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -264,8 +264,15 @@ margin-bottom: 4px; } + .item-title { + @media (min-width: $screen-sm-min) { + width: 49%; + } + } + .avatar { - margin-left: 0; + left: 0; + top: 2px; } .commit-row-info { diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index ffba3dc5bc6..ac8c02b59dc 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -41,6 +41,10 @@ ul.notes { .timeline-icon { .avatar { visibility: hidden; + + .discussion-body & { + visibility: visible; + } } } } @@ -113,6 +117,7 @@ ul.notes { border: 1px solid $table-border-gray; padding: 5px; margin: 5px 0; + max-height: calc(100vh - 100px); } } } diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index d3e59d7fdb9..89ce1b2df20 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -13,10 +13,53 @@ .new_project, .edit-project { - fieldset.features { - .control-label { + fieldset { + &.features .control-label { font-weight: normal; } + .form-group { + margin-bottom: 5px; + } + &> .form-group { + padding-left: 0; + } + } + .help-block { + margin-bottom: 10px; + } + .project-path { + padding-right: 0; + .form-control { + border-radius: $border-radius-base; + } + } + .input-group > div { + &:last-child { + padding-right: 0; + } + } + @media (max-width: $screen-xs-max) { + .input-group > div { + margin-bottom: 14px; + &:last-child { + margin-bottom: 0; + } + } + fieldset > .form-group:first-child { + padding-right: 0; + } + } + + .input-group-addon { + &.static-namespace { + height: 35px; + border-radius: 3px; + border: 1px solid #e5e5e5; + } + &+ .select2 a { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } } } @@ -365,10 +408,28 @@ a.deploy-project-label { } } -.project-import .btn { - float: left; - margin-bottom: 10px; - margin-right: 10px; +.project-import { + .form-group { + margin-bottom: 0; + } + .import-buttons { + padding-left: 0; + display: -webkit-flex; + display: flex; + -webkit-flex-wrap: wrap; + flex-wrap: wrap; + .btn { + margin-right: 10px; + padding: 8px 12px; + } + &> div { + margin-bottom: 14px; + padding-left: 0; + &:last-child { + margin-bottom: 0; + } + } + } } .project-stats { diff --git a/app/controllers/admin/system_info_controller.rb b/app/controllers/admin/system_info_controller.rb new file mode 100644 index 00000000000..3c67370b667 --- /dev/null +++ b/app/controllers/admin/system_info_controller.rb @@ -0,0 +1,13 @@ +class Admin::SystemInfoController < Admin::ApplicationController + def show + system_info = Vmstat.snapshot + + @cpus = system_info.cpus.length + + @mem_used = system_info.memory.active_bytes + @mem_total = system_info.memory.total_bytes + + @disk_used = system_info.disks[0].used_bytes + @disk_total = system_info.disks[0].total_bytes + end +end diff --git a/app/controllers/dashboard/groups_controller.rb b/app/controllers/dashboard/groups_controller.rb index 71ba6153021..de6bc689bb7 100644 --- a/app/controllers/dashboard/groups_controller.rb +++ b/app/controllers/dashboard/groups_controller.rb @@ -1,5 +1,5 @@ class Dashboard::GroupsController < Dashboard::ApplicationController def index - @group_members = current_user.group_members.page(params[:page]) + @group_members = current_user.group_members.includes(:source).page(params[:page]) end end diff --git a/app/controllers/import/gitlab_projects_controller.rb b/app/controllers/import/gitlab_projects_controller.rb index f99aa490d3e..513348c39af 100644 --- a/app/controllers/import/gitlab_projects_controller.rb +++ b/app/controllers/import/gitlab_projects_controller.rb @@ -12,9 +12,13 @@ class Import::GitlabProjectsController < Import::BaseController return redirect_back_or_default(options: { alert: "You need to upload a GitLab project export archive." }) end + imported_file = project_params[:file].path + "-import" + + FileUtils.copy_entry(project_params[:file].path, imported_file) + @project = Gitlab::ImportExport::ProjectCreator.new(project_params[:namespace_id], current_user, - File.expand_path(project_params[:file].path), + File.expand_path(imported_file), project_params[:path]).execute if @project.saved? diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index cd8b2911674..7599fec3cdf 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -16,6 +16,7 @@ class Projects::BlobController < Projects::ApplicationController before_action :from_merge_request, only: [:edit, :update] before_action :require_branch_head, only: [:edit, :update] before_action :editor_variables, except: [:show, :preview, :diff] + before_action :validate_diff_params, only: :diff def new commit unless @repository.empty? @@ -146,4 +147,10 @@ class Projects::BlobController < Projects::ApplicationController file_content_encoding: params[:encoding] } end + + def validate_diff_params + if [:since, :to, :offset].any? { |key| params[key].blank? } + render nothing: true + end + end end diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index 6751737d15e..d162a5a3165 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -18,9 +18,16 @@ class Projects::CommitController < Projects::ApplicationController apply_diff_view_cookie! @grouped_diff_notes = commit.notes.grouped_diff_notes + @notes = commit.notes.non_diff_notes.fresh + + Banzai::NoteRenderer.render( + @grouped_diff_notes.values.flatten + @notes, + @project, + current_user, + ) @note = @project.build_commit_note(commit) - @notes = commit.notes.non_diff_notes.fresh + @noteable = @commit @comments_target = { noteable_type: 'Commit', diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 4e2d3bebb2e..8b8df680739 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -62,8 +62,12 @@ class Projects::IssuesController < Projects::ApplicationController end def show + raw_notes = @issue.notes_with_associations.fresh + + @notes = Banzai::NoteRenderer. + render(raw_notes, @project, current_user, @path, @project_wiki, @ref) + @note = @project.notes.new(noteable: @issue) - @notes = @issue.notes.with_associations.fresh @noteable = @issue respond_to do |format| @@ -111,6 +115,7 @@ class Projects::IssuesController < Projects::ApplicationController render :edit end end + format.json do render json: @issue.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } }) end diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 851822d805a..dd86b940a08 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -59,7 +59,13 @@ class Projects::MergeRequestsController < Projects::ApplicationController respond_to do |format| format.html format.json { render json: @merge_request } - format.patch { render text: @merge_request.to_patch } + format.patch do + headers.store(*Gitlab::Workhorse.send_git_patch(@project.repository, + @merge_request.diff_base_commit.id, + @merge_request.last_commit.id)) + headers['Content-Disposition'] = 'inline' + head :ok + end format.diff do return render_404 unless @merge_request.diff_refs @@ -85,6 +91,15 @@ class Projects::MergeRequestsController < Projects::ApplicationController @grouped_diff_notes = @merge_request.notes.grouped_diff_notes + Banzai::NoteRenderer.render( + @grouped_diff_notes.values.flatten, + @project, + current_user, + @path, + @project_wiki, + @ref + ) + respond_to do |format| format.html format.json { render json: { html: view_to_html_string("projects/merge_requests/show/_diffs") } } @@ -190,7 +205,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController def merge return access_denied! unless @merge_request.can_be_merged_by?(current_user) - unless @merge_request.mergeable? + # Disable the CI check if merge_when_build_succeeds is enabled since we have + # to wait until CI completes to know + unless @merge_request.mergeable?(skip_ci_check: merge_when_build_succeeds_active?) @status = :failed return end @@ -204,8 +221,13 @@ class Projects::MergeRequestsController < Projects::ApplicationController @merge_request.update(merge_error: nil) - if params[:merge_when_build_succeeds].present? - if @merge_request.pipeline && @merge_request.pipeline.active? + if params[:merge_when_build_succeeds].present? + unless @merge_request.pipeline + @status = :failed + return + end + + if @merge_request.pipeline.active? MergeRequests::MergeWhenBuildSucceedsService.new(@project, current_user, merge_params) .execute(@merge_request) @status = :merge_when_build_succeeds @@ -320,8 +342,21 @@ class Projects::MergeRequestsController < Projects::ApplicationController def define_show_vars # Build a note object for comment form @note = @project.notes.new(noteable: @merge_request) - @notes = @merge_request.mr_and_commit_notes.inc_author.fresh - @discussions = @notes.discussions + + @discussions = @merge_request.mr_and_commit_notes. + inc_author_project_award_emoji. + fresh. + discussions + + @notes = Banzai::NoteRenderer.render( + @discussions.flatten, + @project, + current_user, + @path, + @project_wiki, + @ref + ) + @noteable = @merge_request # Get commits from repository @@ -368,4 +403,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController def ensure_ref_fetched @merge_request.ensure_ref_fetched end + + def merge_when_build_succeeds_active? + params[:merge_when_build_succeeds].present? && + @merge_request.pipeline && @merge_request.pipeline.active? + end end diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 836f79ff080..e14fe26dde7 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -24,6 +24,10 @@ class Projects::NotesController < Projects::ApplicationController def create @note = Notes::CreateService.new(project, current_user, note_params).execute + if @note.is_a?(Note) + Banzai::NoteRenderer.render([@note], @project, current_user) + end + respond_to do |format| format.json { render json: note_json(@note) } format.html { redirect_back_or_default } @@ -33,6 +37,10 @@ class Projects::NotesController < Projects::ApplicationController def update @note = Notes::UpdateService.new(project, current_user, note_params).execute(note) + if @note.is_a?(Note) + Banzai::NoteRenderer.render([@note], @project, current_user) + end + respond_to do |format| format.json { render json: note_json(@note) } format.html { redirect_back_or_default } @@ -118,6 +126,8 @@ class Projects::NotesController < Projects::ApplicationController name: note.name } elsif note.valid? + Banzai::NoteRenderer.render([note], @project, current_user) + { valid: true, id: note.id, diff --git a/app/finders/pipelines_finder.rb b/app/finders/pipelines_finder.rb index c19a795d467..641fbf838f1 100644 --- a/app/finders/pipelines_finder.rb +++ b/app/finders/pipelines_finder.rb @@ -29,10 +29,10 @@ class PipelinesFinder end def branches - project.repository.branches.map(&:name) + project.repository.branch_names end def tags - project.repository.tags.map(&:name) + project.repository.tag_names end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 41859841834..62d13a4b4f3 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -197,7 +197,7 @@ module ApplicationHelper def render_markup(file_name, file_content) if gitlab_markdown?(file_name) - Haml::Helpers.preserve(markdown(file_content)) + Hamlit::RailsHelpers.preserve(markdown(file_content)) elsif asciidoc?(file_name) asciidoc(file_content) elsif plain?(file_name) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 4b4bc3d4276..428a42266d0 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -1,10 +1,10 @@ module BlobHelper - def highlighter(blob_name, blob_content, nowrap: false) - Gitlab::Highlight.new(blob_name, blob_content, nowrap: nowrap) + def highlighter(blob_name, blob_content, repository: nil, nowrap: false) + Gitlab::Highlight.new(blob_name, blob_content, nowrap: nowrap, repository: repository) end - def highlight(blob_name, blob_content, nowrap: false, plain: false) - Gitlab::Highlight.highlight(blob_name, blob_content, nowrap: nowrap, plain: plain) + def highlight(blob_name, blob_content, repository: nil, nowrap: false, plain: false) + Gitlab::Highlight.highlight(blob_name, blob_content, nowrap: nowrap, plain: plain, repository: repository) end def no_highlight_files diff --git a/app/helpers/javascript_helper.rb b/app/helpers/javascript_helper.rb index 91dd91718dc..0e456214d37 100644 --- a/app/helpers/javascript_helper.rb +++ b/app/helpers/javascript_helper.rb @@ -1,7 +1,5 @@ module JavascriptHelper - def page_specific_javascripts(js = nil) - @page_specific_javascripts = js unless js.nil? - - @page_specific_javascripts + def page_specific_javascript_tag(js) + javascript_include_tag asset_path(js), { "data-turbolinks-track" => true } end end diff --git a/app/models/ability.rb b/app/models/ability.rb index 9c58b956007..f5950879ccb 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -196,7 +196,8 @@ class Ability @public_project_rules ||= project_guest_rules + [ :download_code, :fork_project, - :read_commit_status + :read_commit_status, + :read_pipeline ] end diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index ca5a685dd11..10324bf2257 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -163,13 +163,26 @@ module Ci end def skip_ci? - git_commit_message =~ /(\[ci skip\])/ if git_commit_message + git_commit_message =~ /\[(ci skip|skip ci)\]/i if git_commit_message end def environments builds.where.not(environment: nil).success.pluck(:environment).uniq end + # Manually set the notes for a Ci::Pipeline + # There is no ActiveRecord relation between Ci::Pipeline and notes + # as they are related to a commit sha. This method helps importing + # them using the +Gitlab::ImportExport::RelationFactory+ class. + def notes=(notes) + notes.each do |note| + note[:id] = nil + note[:commit_id] = sha + note[:noteable_id] = self['id'] + note.save! + end + end + def notes Note.for_commit_id(sha) end diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb index f8d5d4486fd..c9c47ec7419 100644 --- a/app/models/ci/variable.rb +++ b/app/models/ci/variable.rb @@ -13,6 +13,7 @@ module Ci attr_encrypted :value, mode: :per_attribute_iv_and_salt, + insecure_mode: true, key: Gitlab::Application.secrets.db_key_base, algorithm: 'aes-256-cbc' end diff --git a/app/models/concerns/awardable.rb b/app/models/concerns/awardable.rb index 539c7c31e30..06beff177b1 100644 --- a/app/models/concerns/awardable.rb +++ b/app/models/concerns/awardable.rb @@ -2,10 +2,11 @@ module Awardable extend ActiveSupport::Concern included do - has_many :award_emoji, as: :awardable, dependent: :destroy + has_many :award_emoji, -> { includes(:user) }, as: :awardable, dependent: :destroy if self < Participable - participant :award_emoji_with_associations + # By default we always load award_emoji user association + participant :award_emoji end end @@ -34,12 +35,9 @@ module Awardable end end - def award_emoji_with_associations - award_emoji.includes(:user) - end - def grouped_awards(with_thumbs: true) - awards = award_emoji_with_associations.group_by(&:name) + # By default we always load award_emoji user association + awards = award_emoji.group_by(&:name) if with_thumbs awards[AwardEmoji::UPVOTE_NAME] ||= [] diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 0ccd3474b81..d6f55885dd6 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -19,9 +19,14 @@ module Issuable belongs_to :milestone has_many :notes, as: :noteable, dependent: :destroy do def authors_loaded? - # We check first if we're loaded to not load unnecesarily. + # We check first if we're loaded to not load unnecessarily. loaded? && to_a.all? { |note| note.association(:author).loaded? } end + + def award_emojis_loaded? + # We check first if we're loaded to not load unnecessarily. + loaded? && to_a.all? { |note| note.association(:award_emoji).loaded? } + end end has_many :label_links, as: :target, dependent: :destroy has_many :labels, through: :label_links @@ -49,7 +54,7 @@ module Issuable scope :without_label, -> { joins("LEFT OUTER JOIN label_links ON label_links.target_type = '#{name}' AND label_links.target_id = #{table_name}.id").where(label_links: { id: nil }) } scope :join_project, -> { joins(:project) } - scope :inc_notes_with_associations, -> { includes(notes: :author) } + scope :inc_notes_with_associations, -> { includes(notes: [ :project, :author, :award_emoji ]) } scope :references_project, -> { references(:project) } scope :non_archived, -> { join_project.where(projects: { archived: false }) } @@ -112,15 +117,18 @@ module Issuable end def sort(method, excluded_labels: []) - case method.to_s - when 'milestone_due_asc' then order_milestone_due_asc - when 'milestone_due_desc' then order_milestone_due_desc - when 'downvotes_desc' then order_downvotes_desc - when 'upvotes_desc' then order_upvotes_desc - when 'priority' then order_labels_priority(excluded_labels: excluded_labels) - else - order_by(method) - end + sorted = case method.to_s + when 'milestone_due_asc' then order_milestone_due_asc + when 'milestone_due_desc' then order_milestone_due_desc + when 'downvotes_desc' then order_downvotes_desc + when 'upvotes_desc' then order_upvotes_desc + when 'priority' then order_labels_priority(excluded_labels: excluded_labels) + else + order_by(method) + end + + # Break ties with the ID column for pagination + sorted.order(id: :desc) end def order_labels_priority(excluded_labels: []) @@ -257,7 +265,14 @@ module Issuable # already have their authors loaded (possibly because the scope # `inc_notes_with_associations` was used) and skip the inclusion if that's # the case. - notes.authors_loaded? ? notes : notes.includes(:author) + includes = [] + includes << :author unless notes.authors_loaded? + includes << :award_emoji unless notes.award_emojis_loaded? + if includes.any? + notes.includes(includes) + else + notes + end end def updated_tasks diff --git a/app/models/event.rb b/app/models/event.rb index 716039fb54b..d7d23c7ae6d 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -315,7 +315,7 @@ class Event < ActiveRecord::Base def body? if push? - push_with_commits? + push_with_commits? || rm_ref? elsif note? true else diff --git a/app/models/group.rb b/app/models/group.rb index e66e04371b2..c70c719e338 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -11,7 +11,7 @@ class Group < Namespace has_many :users, -> { where(members: { requested_at: nil }) }, through: :group_members has_many :owners, - -> { where(members: { access_level: Gitlab::Access::OWNER }) }, + -> { where(members: { requested_at: nil, access_level: Gitlab::Access::OWNER }) }, through: :group_members, source: :user diff --git a/app/models/legacy_diff_note.rb b/app/models/legacy_diff_note.rb index 95fd510eb3a..33d2a69ebaf 100644 --- a/app/models/legacy_diff_note.rb +++ b/app/models/legacy_diff_note.rb @@ -20,7 +20,7 @@ class LegacyDiffNote < Note end def discussion_id - @discussion_id ||= self.class.build_discussion_id(noteable_type, noteable_id || commit_id, line_code, active?) + @discussion_id ||= self.class.build_discussion_id(noteable_type, noteable_id || commit_id, line_code) end def diff_file_hash diff --git a/app/models/member.rb b/app/models/member.rb index c74a16367db..57161397e2b 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -32,6 +32,7 @@ class Member < ActiveRecord::Base scope :request, -> { where.not(requested_at: nil) } scope :non_request, -> { where(requested_at: nil) } scope :non_pending, -> { non_request.non_invite } + scope :has_access, -> { where('access_level > 0') } scope :guests, -> { where(access_level: GUEST) } scope :reporters, -> { where(access_level: REPORTER) } diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 36bc98bdb1e..53d9aa588af 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -264,19 +264,19 @@ class MergeRequest < ActiveRecord::Base self.title.sub(WIP_REGEX, "") end - def mergeable? - return false unless mergeable_state? + def mergeable?(skip_ci_check: false) + return false unless mergeable_state?(skip_ci_check: skip_ci_check) check_if_can_be_merged can_be_merged? end - def mergeable_state? + def mergeable_state?(skip_ci_check: false) return false unless open? return false if work_in_progress? return false if broken? - return false unless mergeable_ci_state? + return false unless skip_ci_check || mergeable_ci_state? true end @@ -319,13 +319,6 @@ class MergeRequest < ActiveRecord::Base ) end - # Returns the commit as a series of email patches. - # - # see "git format-patch" - def to_patch - target_project.repository.format_patch(diff_base_commit.sha, source_sha) - end - def hook_attrs attrs = { source: source_project.try(:hook_attrs), diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index aca377cc600..86331a33c05 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -108,44 +108,46 @@ class MergeRequestDiff < ActiveRecord::Base # Reload all commits related to current merge request from repo # and save it as array of hashes in st_commits db field def reload_commits + new_attributes = {} + commit_objects = unmerged_commits if commit_objects.present? - self.st_commits = dump_commits(commit_objects) + new_attributes[:st_commits] = dump_commits(commit_objects) end - save + update_columns_serialized(new_attributes) end # Reload diffs between branches related to current merge request from repo # and save it as array of hashes in st_diffs db field def reload_diffs + new_attributes = {} new_diffs = [] if commits.size.zero? - self.state = :empty + new_attributes[:state] = :empty else diff_collection = unmerged_diffs if diff_collection.overflow? # Set our state to 'overflow' to make the #empty? and #collected? # methods (generated by StateMachine) return false. - self.state = :overflow + new_attributes[:state] = :overflow end - self.real_size = diff_collection.real_size + new_attributes[:real_size] = diff_collection.real_size if diff_collection.any? new_diffs = dump_diffs(diff_collection) - self.state = :collected + new_attributes[:state] = :collected end end - self.st_diffs = new_diffs - - self.base_commit_sha = self.repository.merge_base(self.head, self.base) + new_attributes[:st_diffs] = new_diffs + new_attributes[:base_commit_sha] = self.repository.merge_base(self.head, self.base) - self.save + update_columns_serialized(new_attributes) end # Collect array of Git::Diff objects @@ -190,4 +192,29 @@ class MergeRequestDiff < ActiveRecord::Base ) end end + + private + + # + # #save or #update_attributes providing changes on serialized attributes do a lot of + # serialization and deserialization calls resulting in bad performance. + # Using #update_columns solves the problem with just one YAML.dump per serialized attribute that we provide. + # As a tradeoff we need to reload the current instance to properly manage time objects on those serialized + # attributes. So to keep the same behaviour as the attribute assignment we reload the instance. + # The difference is in the usage of + # #write_attribute= (#update_attributes) and #raw_write_attribute= (#update_columns) + # + # Ex: + # + # new_attributes[:st_commits].first.slice(:committed_date) + # => {:committed_date=>2014-02-27 11:01:38 +0200} + # YAML.load(YAML.dump(new_attributes[:st_commits].first.slice(:committed_date))) + # => {:committed_date=>2014-02-27 10:01:38 +0100} + # + def update_columns_serialized(new_attributes) + return unless new_attributes.any? + + update_columns(new_attributes.merge(updated_at: current_time_from_proper_timezone)) + reload + end end diff --git a/app/models/note.rb b/app/models/note.rb index 8d164647550..8db500a5219 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -6,6 +6,10 @@ class Note < ActiveRecord::Base include Awardable include Importable + # Attribute containing rendered and redacted Markdown as generated by + # Banzai::ObjectRenderer. + attr_accessor :note_html + default_value_for :system, false attr_mentionable :note, pipeline: :note @@ -49,11 +53,13 @@ class Note < ActiveRecord::Base scope :fresh, ->{ order(created_at: :asc, id: :asc) } scope :inc_author_project, ->{ includes(:project, :author) } scope :inc_author, ->{ includes(:author) } + scope :inc_author_project_award_emoji, ->{ includes(:project, :author, :award_emoji) } scope :legacy_diff_notes, ->{ where(type: 'LegacyDiffNote') } scope :non_diff_notes, ->{ where(type: ['Note', nil]) } scope :with_associations, -> do + # FYI noteable cannot be loaded for LegacyDiffNote for commits includes(:author, :noteable, :updated_by, project: [:project_members, { group: [:group_members] }]) end diff --git a/app/models/project.rb b/app/models/project.rb index ca3bc04e2dd..73ded09c162 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -81,6 +81,7 @@ class Project < ActiveRecord::Base has_one :jira_service, dependent: :destroy has_one :redmine_service, dependent: :destroy has_one :custom_issue_tracker_service, dependent: :destroy + has_one :bugzilla_service, dependent: :destroy has_one :gitlab_issue_tracker_service, dependent: :destroy, inverse_of: :project has_one :external_wiki_service, dependent: :destroy @@ -163,6 +164,7 @@ class Project < ActiveRecord::Base validates :avatar, file_size: { maximum: 200.kilobytes.to_i } validate :visibility_level_allowed_by_group validate :visibility_level_allowed_as_fork + validate :check_wiki_path_conflict add_authentication_token_field :runners_token before_save :ensure_runners_token @@ -539,6 +541,16 @@ class Project < ActiveRecord::Base self.errors.add(:visibility_level, "#{level_name} is not allowed since the fork source project has lower visibility.") end + def check_wiki_path_conflict + return if path.blank? + + path_to_check = path.ends_with?('.wiki') ? path.chomp('.wiki') : "#{path}.wiki" + + if Project.where(namespace_id: namespace_id, path: path_to_check).exists? + errors.add(:name, 'has already been taken') + end + end + def to_param path end diff --git a/app/models/project_import_data.rb b/app/models/project_import_data.rb index ca8a9b4217b..331123a5a5b 100644 --- a/app/models/project_import_data.rb +++ b/app/models/project_import_data.rb @@ -7,6 +7,7 @@ class ProjectImportData < ActiveRecord::Base marshal: true, encode: true, mode: :per_attribute_iv_and_salt, + insecure_mode: true, algorithm: 'aes-256-cbc' serialize :data, JSON diff --git a/app/models/project_services/bugzilla_service.rb b/app/models/project_services/bugzilla_service.rb new file mode 100644 index 00000000000..260f6030957 --- /dev/null +++ b/app/models/project_services/bugzilla_service.rb @@ -0,0 +1,25 @@ +class BugzillaService < IssueTrackerService + + prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url + + def title + if self.properties && self.properties['title'].present? + self.properties['title'] + else + 'Bugzilla' + end + end + + def description + if self.properties && self.properties['description'].present? + self.properties['description'] + else + 'Bugzilla issue tracker' + end + end + + def to_param + 'bugzilla' + end + +end diff --git a/app/models/project_services/custom_issue_tracker_service.rb b/app/models/project_services/custom_issue_tracker_service.rb index 6b2b1daa724..8f2db46a7ba 100644 --- a/app/models/project_services/custom_issue_tracker_service.rb +++ b/app/models/project_services/custom_issue_tracker_service.rb @@ -32,7 +32,4 @@ class CustomIssueTrackerService < IssueTrackerService ] end - def initialize_properties - self.properties = {} if properties.nil? - end end diff --git a/app/models/project_team.rb b/app/models/project_team.rb index 73e736820af..0865b979ce0 100644 --- a/app/models/project_team.rb +++ b/app/models/project_team.rb @@ -137,20 +137,10 @@ class ProjectTeam def max_member_access(user_id) access = [] - project.members.non_request.each do |member| - if member.user_id == user_id - access << member.access_field if member.access_field - break - end - end + access += project.members.non_request.where(user_id: user_id).has_access.pluck(:access_level) if group - group.members.non_request.each do |member| - if member.user_id == user_id - access << member.access_field if member.access_field - break - end - end + access += group.members.non_request.where(user_id: user_id).has_access.pluck(:access_level) end if project.invited_groups.any? && project.allowed_to_share_with_group? diff --git a/app/models/repository.rb b/app/models/repository.rb index 221c87164ca..2a6a3b086c2 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -130,7 +130,7 @@ class Repository end def find_tag(name) - raw_repository.tags.find { |tag| tag.name == name } + tags.find { |tag| tag.name == name } end def add_branch(user, branch_name, target) @@ -978,6 +978,10 @@ class Repository raw_repository.ls_files(actual_ref) end + def gitattribute(path, name) + raw_repository.attributes(path)[name] + end + def copy_gitattributes(ref) actual_ref = ref || root_ref begin diff --git a/app/models/service.rb b/app/models/service.rb index 40d39933ad8..d7a32c28267 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -170,6 +170,7 @@ class Service < ActiveRecord::Base bamboo buildkite builds_email + bugzilla campfire custom_issue_tracker drone_ci diff --git a/app/models/snippet.rb b/app/models/snippet.rb index f8034cb5e6b..5ec933601ac 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -20,6 +20,7 @@ class Snippet < ActiveRecord::Base length: { within: 0..255 }, format: { with: Gitlab::Regex.file_name_regex, message: Gitlab::Regex.file_name_regex_message } + validates :content, presence: true validates :visibility_level, inclusion: { in: Gitlab::VisibilityLevel.values } @@ -81,6 +82,11 @@ class Snippet < ActiveRecord::Base 0 end + # alias for compatibility with blobs and highlighting + def path + file_name + end + def name file_name end @@ -135,7 +141,16 @@ class Snippet < ActiveRecord::Base end def accessible_to(user) - where('visibility_level IN (?) OR author_id = ?', [Snippet::INTERNAL, Snippet::PUBLIC], user) + return are_public unless user.present? + return all if user.admin? + + where( + 'visibility_level IN (:visibility_levels) + OR author_id = :author_id + OR project_id IN (:project_ids)', + visibility_levels: [Snippet::PUBLIC, Snippet::INTERNAL], + author_id: user.id, + project_ids: user.authorized_projects.select(:id)) end end end diff --git a/app/models/user.rb b/app/models/user.rb index 876ccc69d8d..767d6366c79 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -25,6 +25,7 @@ class User < ActiveRecord::Base attr_encrypted :otp_secret, key: Gitlab::Application.config.secret_key_base, mode: :per_attribute_iv_and_salt, + insecure_mode: true, algorithm: 'aes-256-cbc' devise :two_factor_authenticatable, @@ -57,7 +58,7 @@ class User < ActiveRecord::Base # Groups has_many :members, dependent: :destroy - has_many :group_members, dependent: :destroy, source: 'GroupMember' + has_many :group_members, -> { where(requested_at: nil) }, dependent: :destroy, source: 'GroupMember' has_many :groups, through: :group_members has_many :owned_groups, -> { where members: { access_level: Gitlab::Access::OWNER } }, through: :group_members, source: :group has_many :masters_groups, -> { where members: { access_level: Gitlab::Access::MASTER } }, through: :group_members, source: :group @@ -65,7 +66,7 @@ class User < ActiveRecord::Base # Projects has_many :groups_projects, through: :groups, source: :projects has_many :personal_projects, through: :namespace, source: :projects - has_many :project_members, dependent: :destroy, class_name: 'ProjectMember' + has_many :project_members, -> { where(requested_at: nil) }, dependent: :destroy, class_name: 'ProjectMember' has_many :projects, through: :project_members has_many :created_projects, foreign_key: :creator_id, class_name: 'Project' has_many :users_star_projects, dependent: :destroy @@ -308,7 +309,7 @@ class User < ActiveRecord::Base def generate_password if self.force_random_password - self.password = self.password_confirmation = Devise.friendly_token.first(8) + self.password = self.password_confirmation = Devise.friendly_token.first(Devise.password_length.min) end end diff --git a/app/services/todo_service.rb b/app/services/todo_service.rb index 540bf54b920..239bd17a035 100644 --- a/app/services/todo_service.rb +++ b/app/services/todo_service.rb @@ -159,8 +159,9 @@ class TodoService def create_todos(users, attributes) Array(users).map do |user| next if pending_todos(user, attributes).exists? - Todo.create(attributes.merge(user_id: user.id)) + todo = Todo.create(attributes.merge(user_id: user.id)) user.update_todos_count_cache + todo end end diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index c883e8f97da..30ab0717164 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -15,7 +15,7 @@ = f.label :default_snippet_visibility, class: 'control-label col-sm-2' .col-sm-10 = render('shared/visibility_radios', model_method: :default_snippet_visibility, form: f, selected_level: @application_setting.default_snippet_visibility, form_model: ProjectSnippet.new) - .form-group.group-visibility-level-holder + .form-group.project-visibility-level-holder = f.label :default_group_visibility, class: 'control-label col-sm-2' .col-sm-10 = render('shared/visibility_radios', model_method: :default_group_visibility, form: f, selected_level: @application_setting.default_group_visibility, form_model: Group.new) diff --git a/app/views/admin/background_jobs/_head.html.haml b/app/views/admin/background_jobs/_head.html.haml index d78682532ed..9d722bd7382 100644 --- a/app/views/admin/background_jobs/_head.html.haml +++ b/app/views/admin/background_jobs/_head.html.haml @@ -1,5 +1,9 @@ .nav-links.sub-nav %ul{ class: (container_class) } + = nav_link(controller: :system_info) do + = link_to admin_system_info_path, title: 'System Info' do + %span + System Info = nav_link(controller: :background_jobs) do = link_to admin_background_jobs_path, title: 'Background Jobs' do %span diff --git a/app/views/admin/system_info/show.html.haml b/app/views/admin/system_info/show.html.haml new file mode 100644 index 00000000000..3ef2f20b589 --- /dev/null +++ b/app/views/admin/system_info/show.html.haml @@ -0,0 +1,22 @@ +- @no_container = true +- page_title "System Info" += render 'admin/background_jobs/head' + +%div{ class: (container_class) } + .prepend-top-default + .row + .col-sm-4 + .light-well + %h4 CPU + .data + %h1= "#{@cpus} cores" + .col-sm-4 + .light-well + %h4 Memory + .data + %h1= "#{number_to_human_size(@mem_used)} / #{number_to_human_size(@mem_total)}" + .col-sm-4 + .light-well + %h4 Disk + .data + %h1= "#{number_to_human_size(@disk_used)} / #{number_to_human_size(@disk_total)}" diff --git a/app/views/admin/users/groups.html.haml b/app/views/admin/users/groups.html.haml index b0a709a568a..8f6d13b881a 100644 --- a/app/views/admin/users/groups.html.haml +++ b/app/views/admin/users/groups.html.haml @@ -1,11 +1,12 @@ - page_title "Groups", @user.name, "Users" = render 'admin/users/head' -- if @user.group_members.present? +- group_members = @user.group_members.includes(:source) +- if group_members.any? .panel.panel-default .panel-heading Groups: %ul.well-list - - @user.group_members.each do |group_member| + - group_members.each do |group_member| - group = group_member.group %li.group_member %span{class: ("list-item-name" unless group_member.owner?)} diff --git a/app/views/ci/errors/show.haml b/app/views/ci/errors/show.haml deleted file mode 100644 index 2788112c835..00000000000 --- a/app/views/ci/errors/show.haml +++ /dev/null @@ -1,2 +0,0 @@ -%h3.error Error -= @error diff --git a/app/views/ci/shared/_guide.html.haml b/app/views/ci/shared/_guide.html.haml deleted file mode 100644 index 09e7e653521..00000000000 --- a/app/views/ci/shared/_guide.html.haml +++ /dev/null @@ -1,13 +0,0 @@ -.bs-callout.help-callout - %h4 How to setup CI for this project - - %ol - %li - Add at least one runner to the project. - Go to #{link_to 'Runners page', runners_path(@project), target: :blank} for instructions. - %li - Put the .gitlab-ci.yml in the root of your repository. Examples can be found in - #{link_to "Configuring project (.gitlab-ci.yml)", "http://doc.gitlab.com/ci/yaml/README.html", target: :blank}. - You can also test your .gitlab-ci.yml in the #{link_to "Lint", ci_lint_path} - %li - Return to this page and refresh it, it should show a new build. diff --git a/app/views/ci/shared/_no_runners.html.haml b/app/views/ci/shared/_no_runners.html.haml deleted file mode 100644 index f56c37d9b37..00000000000 --- a/app/views/ci/shared/_no_runners.html.haml +++ /dev/null @@ -1,7 +0,0 @@ -.alert.alert-danger - %p - Now you need Runners to process your builds. - %span - Checkout the #{link_to 'GitLab Runner section', 'https://about.gitlab.com/gitlab-ci/#gitlab-runner', target: '_blank'} to install it - - diff --git a/app/views/emojis/index.html.haml b/app/views/emojis/index.html.haml index 97401a2e618..8b38b4c2bd4 100644 --- a/app/views/emojis/index.html.haml +++ b/app/views/emojis/index.html.haml @@ -1,6 +1,6 @@ .emoji-menu + = text_field_tag :emoji_search, "", class: "emoji-search search-input form-control", placeholder: "Seach emojis" .emoji-menu-content - = text_field_tag :emoji_search, "", class: "emoji-search search-input form-control" - Gitlab::AwardEmoji.emoji_by_category.each do |category, emojis| %h5.emoji-menu-title = Gitlab::AwardEmoji::CATEGORIES[category] diff --git a/app/views/events/event/_push.html.haml b/app/views/events/event/_push.html.haml index dc4ff17e31a..ea54ef226ec 100644 --- a/app/views/events/event/_push.html.haml +++ b/app/views/events/event/_push.html.haml @@ -1,3 +1,5 @@ +- project = event.project + .event-title %span.author_name= link_to_author event %span.event_label.pushed #{event.action_name} #{event.ref_type} @@ -5,19 +7,18 @@ %strong= event.ref_name - else %strong - = link_to event.ref_name, namespace_project_commits_path(event.project.namespace, event.project, event.ref_name), title: h(event.target_title) + = link_to event.ref_name, namespace_project_commits_path(project.namespace, project, event.ref_name), title: h(event.target_title) at - = link_to_project event.project + = link_to_project project - if event.push_with_commits? - - project = event.project .event-body %ul.well-list.event_commits - few_commits = event.commits[0...2] - few_commits.each do |commit| = render "events/commit", commit: commit, project: project, event: event - - create_mr = event.new_ref? && create_mr_button?(event.project.default_branch, event.ref_name, event.project) + - create_mr = event.new_ref? && create_mr_button?(project.default_branch, event.ref_name, project) - if event.commits_count > 1 %li.commits-stat - if event.commits_count > 2 @@ -27,18 +28,26 @@ - from = event.commit_from - from_label = truncate_sha(from) - else - - from = event.project.default_branch + - from = project.default_branch - from_label = from - = link_to namespace_project_compare_path(event.project.namespace, event.project, from: from, to: event.commit_to) do + = link_to namespace_project_compare_path(project.namespace, project, from: from, to: event.commit_to) do Compare #{from_label}...#{truncate_sha(event.commit_to)} - if create_mr %span{"data-user-is" => event.author_id, "data-display" => "inline"} or - = link_to create_mr_path(event.project.default_branch, event.ref_name, event.project) do + = link_to create_mr_path(project.default_branch, event.ref_name, project) do create a merge request - elsif create_mr %li.commits-stat{"data-user-is" => event.author_id} - = link_to create_mr_path(event.project.default_branch, event.ref_name, event.project) do + = link_to create_mr_path(project.default_branch, event.ref_name, project) do Create Merge Request +- elsif event.rm_ref? + - repository = project.repository + - last_commit = repository.commit(event.commit_from) + - if last_commit + .event-body + %ul.well-list.event_commits + = render "events/commit", commit: last_commit, project: project, event: event + diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index e0ed657919e..757de92d6d4 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -30,8 +30,8 @@ = javascript_include_tag "application" - - if page_specific_javascripts - = javascript_include_tag page_specific_javascripts, {"data-turbolinks-track" => true} + - if content_for?(:page_specific_javascripts) + = yield :page_specific_javascripts = csrf_meta_tags diff --git a/app/views/layouts/ci/_info.html.haml b/app/views/layouts/ci/_info.html.haml deleted file mode 100644 index 24c68a6dbf5..00000000000 --- a/app/views/layouts/ci/_info.html.haml +++ /dev/null @@ -1,2 +0,0 @@ -- if current_user && current_user.is_admin? && Ci::Runner.count.zero? - = render 'ci/shared/no_runners' diff --git a/app/views/layouts/ci/_page.html.haml b/app/views/layouts/ci/_page.html.haml deleted file mode 100644 index 2e56d0ac6a3..00000000000 --- a/app/views/layouts/ci/_page.html.haml +++ /dev/null @@ -1,22 +0,0 @@ -.page-with-sidebar{ class: page_sidebar_class } - = render "layouts/broadcast" - .sidebar-wrapper.nicescroll{ class: nav_sidebar_class } - - - if defined?(sidebar) && sidebar - = render "layouts/ci/#{sidebar}" - - elsif current_user - = render 'layouts/nav/dashboard' - .collapse-nav - = render partial: 'layouts/collapse_button' - - if current_user - = link_to current_user, class: 'sidebar-user', title: "Profile" do - = image_tag avatar_icon(current_user, 60), alt: 'Profile', class: 'avatar avatar s36' - .username - = current_user.username - .content-wrapper - = render "layouts/flash" - = render 'layouts/ci/info' - %div{ class: container_class } - .content - .clearfix - = yield diff --git a/app/views/layouts/ci/notify.html.haml b/app/views/layouts/ci/notify.html.haml deleted file mode 100644 index 270b206df5e..00000000000 --- a/app/views/layouts/ci/notify.html.haml +++ /dev/null @@ -1,19 +0,0 @@ -%html{lang: "en"} - %head - %meta{content: "text/html; charset=utf-8", "http-equiv" => "Content-Type"} - %title - GitLab CI - - %body - = yield :header - - %table{align: "left", border: "0", cellpadding: "0", cellspacing: "0", style: "padding: 10px 0;", width: "100%"} - %tr - %td{align: "left", style: "margin: 0; padding: 10px;"} - = yield - %br - %tr - %td{align: "left", style: "margin: 0; padding: 10px;"} - %p{style: "font-size:small;color:#777"} - - if @project - You're receiving this notification because you are the one who triggered a build on the #{@project.name} project. diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index 40a2c81eebd..1a39572ac3c 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -6,7 +6,7 @@ = icon('bars') %button.navbar-toggle{type: 'button'} %span.sr-only Toggle navigation - = icon('angle-left') + = icon('ellipsis-v') .navbar-collapse.collapse %ul.nav.navbar-nav diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml index 66e5ec1ad1a..5ee8772882e 100644 --- a/app/views/layouts/nav/_admin.html.haml +++ b/app/views/layouts/nav/_admin.html.haml @@ -1,15 +1,16 @@ -%div{ class: nav_control_class } +.scrolling-tabs-container{ class: nav_control_class } = render 'layouts/nav/admin_settings' - + .fade-left + = icon('angle-left') + .fade-right + = icon('angle-right') %ul.nav-links.scrolling-tabs - %li.fade-left - = icon('arrow-left') = nav_link(controller: %w(dashboard admin projects users groups builds runners), html_options: {class: 'home'}) do = link_to admin_root_path, title: 'Overview', class: 'shortcuts-tree' do %span Overview - = nav_link(controller: %w(background_jobs logs health_check)) do - = link_to admin_background_jobs_path, title: 'Monitoring' do + = nav_link(controller: %w(system_info background_jobs logs health_check)) do + = link_to admin_system_info_path, title: 'Monitoring' do %span Monitoring = nav_link(controller: :broadcast_messages) do @@ -37,5 +38,3 @@ = link_to admin_spam_logs_path, title: "Spam Logs" do %span Spam Logs - %li.fade-right - = icon('arrow-right') diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml index f7aa9fab7cf..d7d36c84b6c 100644 --- a/app/views/layouts/nav/_group.html.haml +++ b/app/views/layouts/nav/_group.html.haml @@ -1,9 +1,10 @@ -%div{ class: nav_control_class } +.scrolling-tabs-container{ class: nav_control_class } = render 'layouts/nav/group_settings' - + .fade-left + = icon('angle-left') + .fade-right + = icon('angle-right') %ul.nav-links.scrolling-tabs - %li.fade-left - = icon('arrow-left') = nav_link(path: 'groups#show', html_options: {class: 'home'}) do = link_to group_path(@group), title: 'Home' do %span @@ -32,5 +33,3 @@ = link_to group_group_members_path(@group), title: 'Members' do %span Members - %li.fade-right - = icon('arrow-right') diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml index 44ea939b7e4..96fe62c39c3 100644 --- a/app/views/layouts/nav/_profile.html.haml +++ b/app/views/layouts/nav/_profile.html.haml @@ -1,48 +1,49 @@ -%ul.nav-links.scrolling-tabs - %li.fade-left - = icon('arrow-left') - = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do - = link_to profile_path, title: 'Profile Settings' do - %span - Profile - = nav_link(controller: [:accounts, :two_factor_auths]) do - = link_to profile_account_path, title: 'Account' do - %span - Account - - if current_application_settings.user_oauth_applications? - = nav_link(controller: 'oauth/applications') do - = link_to applications_profile_path, title: 'Applications' do - %span - Applications - = nav_link(controller: :personal_access_tokens) do - = link_to profile_personal_access_tokens_path, title: 'Personal Access Tokens' do - %span - Personal Access Tokens - = nav_link(controller: :emails) do - = link_to profile_emails_path, title: 'Emails' do - %span - Emails - - unless current_user.ldap_user? - = nav_link(controller: :passwords) do - = link_to edit_profile_password_path, title: 'Password' do - %span - Password - = nav_link(controller: :notifications) do - = link_to profile_notifications_path, title: 'Notifications' do - %span - Notifications +.scrolling-tabs-container + .fade-left + = icon('angle-left') + .fade-right + = icon('angle-right') + %ul.nav-links.scrolling-tabs + = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do + = link_to profile_path, title: 'Profile Settings' do + %span + Profile + = nav_link(controller: [:accounts, :two_factor_auths]) do + = link_to profile_account_path, title: 'Account' do + %span + Account + - if current_application_settings.user_oauth_applications? + = nav_link(controller: 'oauth/applications') do + = link_to applications_profile_path, title: 'Applications' do + %span + Applications + = nav_link(controller: :personal_access_tokens) do + = link_to profile_personal_access_tokens_path, title: 'Personal Access Tokens' do + %span + Personal Access Tokens + = nav_link(controller: :emails) do + = link_to profile_emails_path, title: 'Emails' do + %span + Emails + - unless current_user.ldap_user? + = nav_link(controller: :passwords) do + = link_to edit_profile_password_path, title: 'Password' do + %span + Password + = nav_link(controller: :notifications) do + = link_to profile_notifications_path, title: 'Notifications' do + %span + Notifications - = nav_link(controller: :keys) do - = link_to profile_keys_path, title: 'SSH Keys' do - %span - SSH Keys - = nav_link(controller: :preferences) do - = link_to profile_preferences_path, title: 'Preferences' do - %span - Preferences - = nav_link(path: 'profiles#audit_log') do - = link_to audit_log_profile_path, title: 'Audit Log' do - %span - Audit Log - %li.fade-right - = icon('arrow-right') + = nav_link(controller: :keys) do + = link_to profile_keys_path, title: 'SSH Keys' do + %span + SSH Keys + = nav_link(controller: :preferences) do + = link_to profile_preferences_path, title: 'Preferences' do + %span + Preferences + = nav_link(path: 'profiles#audit_log') do + = link_to audit_log_profile_path, title: 'Audit Log' do + %span + Audit Log diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index 27e840df503..dcef427cda3 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -24,10 +24,12 @@ data: { confirm: leave_confirmation_message(@project) }, method: :delete, title: 'Leave project' do Leave Project -%div{ class: nav_control_class } +.scrolling-tabs-container{ class: nav_control_class } + .fade-left + = icon('angle-left') + .fade-right + = icon('angle-right') %ul.nav-links.scrolling-tabs - %li.fade-left - = icon('arrow-left') = nav_link(path: 'projects#show', html_options: {class: 'home'}) do = link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do %span @@ -111,5 +113,3 @@ %li.hidden = link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do Commits - %li.fade-right - = icon('arrow-right') diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml index ca6714ef42b..58d961d93ca 100644 --- a/app/views/projects/_md_preview.html.haml +++ b/app/views/projects/_md_preview.html.haml @@ -12,13 +12,13 @@ %li.confidential-issue-warning = icon('warning') %span This is a confidential issue. Your comment will not be visible to the public. - + %li.pull-right .toolbar-group = markdown_toolbar_button({icon: "bold fw", data: { "md-tag" => "**" }, title: "Add bold text" }) = markdown_toolbar_button({icon: "italic fw", data: { "md-tag" => "*" }, title: "Add italic text" }) = markdown_toolbar_button({icon: "quote-right fw", data: { "md-tag" => "> ", "md-prepend" => true }, title: "Insert a quote" }) - = markdown_toolbar_button({icon: "code fw", data: { "md-tag" => "`" }, title: "Insert code" }) + = markdown_toolbar_button({icon: "code fw", data: { "md-tag" => "`", "md-block" => "```" }, title: "Insert code" }) = markdown_toolbar_button({icon: "list-ul fw", data: { "md-tag" => "* ", "md-prepend" => true }, title: "Add a bullet list" }) = markdown_toolbar_button({icon: "list-ol fw", data: { "md-tag" => "1. ", "md-prepend" => true }, title: "Add a numbered list" }) = markdown_toolbar_button({icon: "check-square-o fw", data: { "md-tag" => "* [ ] ", "md-prepend" => true }, title: "Add a task list" }) diff --git a/app/views/projects/_merge_request_settings.html.haml b/app/views/projects/_merge_request_settings.html.haml index da522b53417..771a2e0df7d 100644 --- a/app/views/projects/_merge_request_settings.html.haml +++ b/app/views/projects/_merge_request_settings.html.haml @@ -8,4 +8,4 @@ %strong Only allow merge requests to be merged if the build succeeds .help-block Builds need to be configured to enable this feature. - = link_to icon('question-circle'), help_page_path('workflow', 'merge_requests#only-allow-merge-requests-to-be-merged-if-the-build-succeeds') + = link_to icon('question-circle'), help_page_path('workflow', 'merge_requests', anchor: 'only-allow-merge-requests-to-be-merged-if-the-build-succeeds') diff --git a/app/views/projects/blob/_text.html.haml b/app/views/projects/blob/_text.html.haml index b1769759dce..58524418a67 100644 --- a/app/views/projects/blob/_text.html.haml +++ b/app/views/projects/blob/_text.html.haml @@ -16,4 +16,4 @@ .file-content.code .nothing-here-block Empty file - else - = render 'shared/file_highlight', blob: blob + = render 'shared/file_highlight', blob: blob, repository: @repository diff --git a/app/views/projects/commits/_head.html.haml b/app/views/projects/commits/_head.html.haml index 54dab4bff07..61152649907 100644 --- a/app/views/projects/commits/_head.html.haml +++ b/app/views/projects/commits/_head.html.haml @@ -1,8 +1,10 @@ -.scrolling-tabs-container +.scrolling-tabs-container.sub-nav-scroll + .fade-left + = icon('angle-left') + .fade-right + = icon('angle-right') .nav-links.sub-nav.scrolling-tabs %ul{ class: (container_class) } - %li.fade-left - = icon('arrow-left') = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do = link_to project_files_path(@project) do Files @@ -26,5 +28,3 @@ = nav_link(controller: [:tags, :releases]) do = link_to namespace_project_tags_path(@project.namespace, @project) do Tags - %li.fade-right - = icon('arrow-right') diff --git a/app/views/projects/graphs/_head.html.haml b/app/views/projects/graphs/_head.html.haml index a388d9a0a61..ca347406dfe 100644 --- a/app/views/projects/graphs/_head.html.haml +++ b/app/views/projects/graphs/_head.html.haml @@ -1,7 +1,9 @@ .nav-links.sub-nav %ul{ class: (container_class) } - - page_specific_javascripts asset_path("graphs/application.js") + - content_for :page_specific_javascripts do + = page_specific_javascript_tag('lib/chart.js') + = page_specific_javascript_tag('graphs/application.js') = nav_link(action: :show) do = link_to 'Contributors', namespace_project_graph_path = nav_link(action: :commits) do diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml index 593af319a47..3ca30b4ba6b 100644 --- a/app/views/projects/network/show.html.haml +++ b/app/views/projects/network/show.html.haml @@ -1,5 +1,7 @@ - page_title "Network", @ref -- page_specific_javascripts asset_path("network/application.js") +- content_for :page_specific_javascripts do + = page_specific_javascript_tag('lib/raphael.js') + = page_specific_javascript_tag('network/application.js') = render "projects/commits/head" = render "head" %div{ class: (container_class) } diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 3c1c6060504..8a73b077357 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -1,110 +1,121 @@ - page_title 'New Project' - header_title "Projects", dashboard_projects_path -%h3.page-title - New Project -%hr - .project-edit-container .project-edit-errors = render 'projects/errors' - .project-edit-content - - = form_for @project, html: { class: 'new_project form-horizontal js-requires-input' } do |f| - .form-group - = f.label :path, class: 'control-label' do - Project owner - .col-sm-10 - = f.select :namespace_id, namespaces_options(:current_user), {}, {class: 'select2 js-select-namespace', tabindex: 1} - - - if current_user.can_create_group? - .help-block - Want to house several dependent projects under the same namespace? - = link_to "Create a group", new_group_path - - .form-group - = f.label :path, class: 'control-label' do - Project name - .col-sm-10 - = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 2, autofocus: true, required: true - - - if import_sources_enabled? - .project-import.js-toggle-container - .form-group - %label.control-label Import project from - .col-sm-10 - - if github_import_enabled? - - if github_import_configured? - = link_to status_import_github_path, class: 'btn import_github' do - %i.fa.fa-github - GitHub - - else - = link_to '#', class: 'how_to_import_link btn import_github' do - %i.fa.fa-github - GitHub - = render 'github_import_modal' - - - if bitbucket_import_enabled? - - if bitbucket_import_configured? - = link_to status_import_bitbucket_path, class: 'btn import_bitbucket', "data-no-turbolink" => "true" do - %i.fa.fa-bitbucket - Bitbucket - - else - = link_to status_import_bitbucket_path, class: 'how_to_import_link btn import_bitbucket', "data-no-turbolink" => "true" do - %i.fa.fa-bitbucket - Bitbucket - = render 'bitbucket_import_modal' - - - if gitlab_import_enabled? - - if gitlab_import_configured? - = link_to status_import_gitlab_path, class: 'btn import_gitlab' do - %i.fa.fa-heart - GitLab.com + .row.prepend-top-default + .col-lg-3.profile-settings-sidebar + %h4.prepend-top-0 + New project + %p + Create or Import your project from popular Git services + .col-lg-9 + = form_for @project, html: { class: 'new_project' } do |f| + %fieldset.append-bottom-0 + .form-group.col-xs-12.col-sm-6 + = f.label :namespace_id, class: 'label-light' do + %span + Project path + .form-group + .input-group + - if current_user.can_select_namespace? + .input-group-addon + = root_url + = f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user, display_path: true), {}, {class: 'select2 js-select-namespace', tabindex: 1} - else - = link_to status_import_gitlab_path, class: 'how_to_import_link btn import_gitlab' do - %i.fa.fa-heart - GitLab.com - = render 'gitlab_import_modal' - - - if gitorious_import_enabled? - = link_to new_import_gitorious_path, class: 'btn import_gitorious' do - %i.icon-gitorious.icon-gitorious-small - Gitorious.org - - - if google_code_import_enabled? - = link_to new_import_google_code_path, class: 'btn import_google_code' do - %i.fa.fa-google - Google Code - - - if fogbugz_import_enabled? - = link_to new_import_fogbugz_path, class: 'btn import_fogbugz' do - %i.fa.fa-bug - Fogbugz - - - if git_import_enabled? - = link_to "#", class: 'btn js-toggle-button import_git' do - %i.fa.fa-git - %span Repo by URL - - - if gitlab_project_import_enabled? - = link_to new_import_gitlab_project_path, class: 'btn import_gitlab_project project-submit' do - %i.fa.fa-gitlab - %span GitLab export - - .js-toggle-content.hide - = render "shared/import_form", f: f - - .prepend-botton-10 - - .form-group - = f.label :description, class: 'control-label' do - Description - %span.light (optional) - .col-sm-10 - = f.text_area :description, class: "form-control", rows: 3, maxlength: 250, tabindex: 3 - = render 'shared/visibility_level', f: f, visibility_level: default_project_visibility, can_change_visibility_level: true, form_model: @project + .input-group-addon.static-namespace + #{root_url}#{current_user.username}/ + .form-group.col-xs-12.col-sm-6.project-path + = f.label :namespace_id, class: 'label-light' do + %span + Project name + = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 2, autofocus: true, required: true + - if current_user.can_create_group? + .help-block + Want to house several dependent projects under the same namespace? + = link_to "Create a group", new_group_path + + - if import_sources_enabled? + .project-import.js-toggle-container + .form-group.clearfix + = f.label :visibility_level, class: 'label-light' do + Import project from + .col-sm-12.import-buttons + %div + - if github_import_enabled? + - if github_import_configured? + = link_to status_import_github_path, class: 'btn import_github' do + %i.fa.fa-github + GitHub + - else + = link_to '#', class: 'how_to_import_link btn import_github' do + %i.fa.fa-github + GitHub + = render 'github_import_modal' + %div + - if bitbucket_import_enabled? + - if bitbucket_import_configured? + = link_to status_import_bitbucket_path, class: 'btn import_bitbucket', "data-no-turbolink" => "true" do + %i.fa.fa-bitbucket + Bitbucket + - else + = link_to status_import_bitbucket_path, class: 'how_to_import_link btn import_bitbucket', "data-no-turbolink" => "true" do + %i.fa.fa-bitbucket + Bitbucket + = render 'bitbucket_import_modal' + %div + - if gitlab_import_enabled? + - if gitlab_import_configured? + = link_to status_import_gitlab_path, class: 'btn import_gitlab' do + %i.fa.fa-heart + GitLab.com + - else + = link_to status_import_gitlab_path, class: 'how_to_import_link btn import_gitlab' do + %i.fa.fa-heart + GitLab.com + = render 'gitlab_import_modal' + %div + - if gitorious_import_enabled? + = link_to new_import_gitorious_path, class: 'btn import_gitorious' do + %i.icon-gitorious.icon-gitorious-small + Gitorious.org + %div + - if google_code_import_enabled? + = link_to new_import_google_code_path, class: 'btn import_google_code' do + %i.fa.fa-google + Google Code + %div + - if fogbugz_import_enabled? + = link_to new_import_fogbugz_path, class: 'btn import_fogbugz' do + %i.fa.fa-bug + Fogbugz + %div + - if git_import_enabled? + = link_to "#", class: 'btn js-toggle-button import_git' do + %i.fa.fa-git + %span Repo by URL + %div + - if gitlab_project_import_enabled? + = link_to new_import_gitlab_project_path, class: 'btn import_gitlab_project project-submit' do + %i.fa.fa-gitlab + %span GitLab export + + .js-toggle-content.hide + = render "shared/import_form", f: f + + .form-group + = f.label :description, class: 'label-light' do + Project description + %span.light (optional) + = f.text_area :description, placeholder: 'Description format', class: "form-control", rows: 3, maxlength: 250 + + .form-group.project-visibility-level-holder + = f.label :visibility_level, class: 'label-light' do + Visibility Level + = link_to "(?)", help_page_path("public_access", "public_access") + = render('shared/visibility_radios', model_method: :visibility_level, form: f, selected_level: @project.visibility_level, form_model: @project) - .form-actions = f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4 = link_to 'Cancel', dashboard_projects_path, class: 'btn btn-cancel' diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index c04d291412c..a5e163b91e9 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -32,7 +32,7 @@ .note-body{class: note_editable ? 'js-task-list-container' : ''} .note-text = preserve do - = markdown(note.note, pipeline: :note, cache_key: [note, "note"], author: note.author) + = note.note_html = edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago', include_author: true) - if note_editable = render 'projects/notes/edit_form', note: note diff --git a/app/views/projects/wikis/edit.html.haml b/app/views/projects/wikis/edit.html.haml index bf5d09d50c2..817bf9b3f69 100644 --- a/app/views/projects/wikis/edit.html.haml +++ b/app/views/projects/wikis/edit.html.haml @@ -15,9 +15,10 @@ Edit Page .nav-controls - - if can?(current_user, :create_wiki, @project) - = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do - New Page + - if !(@page && @page.persisted?) + - if can?(current_user, :create_wiki, @project) + = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do + New Page = render 'main_links' diff --git a/app/views/search/results/_blob.html.haml b/app/views/search/results/_blob.html.haml index 0fe8a3b490a..290743feb4a 100644 --- a/app/views/search/results/_blob.html.haml +++ b/app/views/search/results/_blob.html.haml @@ -2,9 +2,10 @@ .blob-result .file-holder .file-title - = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(blob.ref, blob.filename), :anchor => "L" + blob.startline.to_s) do + - blob_link = namespace_project_blob_path(@project.namespace, @project, tree_join(blob.ref, blob.filename)) + = link_to blob_link do %i.fa.fa-file %strong = blob.filename .file-content.code.term - = render 'shared/file_highlight', blob: blob, first_line_number: blob.startline + = render 'shared/file_highlight', blob: blob, first_line_number: blob.startline, blob_link: blob_link diff --git a/app/views/shared/_event_filter.html.haml b/app/views/shared/_event_filter.html.haml index aa18e6f236f..8824bcc158e 100644 --- a/app/views/shared/_event_filter.html.haml +++ b/app/views/shared/_event_filter.html.haml @@ -1,9 +1,5 @@ %ul.nav-links.event-filter.scrolling-tabs - %li.fade-left - = icon('arrow-left') = event_filter_link EventFilter.push, 'Push events' = event_filter_link EventFilter.merged, 'Merge events' = event_filter_link EventFilter.comments, 'Comments' = event_filter_link EventFilter.team, 'Team' - %li.fade-right - = icon('arrow-right') diff --git a/app/views/shared/_file_highlight.html.haml b/app/views/shared/_file_highlight.html.haml index 37dcf39c062..e26693bf5b9 100644 --- a/app/views/shared/_file_highlight.html.haml +++ b/app/views/shared/_file_highlight.html.haml @@ -1,13 +1,16 @@ +- repository = nil unless local_assigns.key?(:repository) + .file-content.code.js-syntax-highlight .line-numbers - if blob.data.present? - link_icon = icon('link') + - link = blob_link if defined?(blob_link) - blob.data.each_line.each_with_index do |_, index| - offset = defined?(first_line_number) ? first_line_number : 1 - i = index + offset -# We're not using `link_to` because it is too slow once we get to thousands of lines. - %a.diff-line-num{href: "#L#{i}", id: "L#{i}", 'data-line-number' => i} + %a.diff-line-num{href: "#{link}#L#{i}", id: "L#{i}", 'data-line-number' => i} = link_icon = i .blob-content{data: {blob_id: blob.id}} - = highlight(blob.name, blob.data, plain: blob.no_highlighting?) + = highlight(blob.path, blob.data, repository: repository, plain: blob.no_highlighting?) diff --git a/app/views/shared/milestones/_issuable.html.haml b/app/views/shared/milestones/_issuable.html.haml index 47b66d44e43..3c03c220ddd 100644 --- a/app/views/shared/milestones/_issuable.html.haml +++ b/app/views/shared/milestones/_issuable.html.haml @@ -21,7 +21,8 @@ = link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, label_name: label.title, state: 'all' }) do - render_colored_label(label) - - if assignee - = link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, assignee_id: issuable.assignee_id, state: 'all' }), - class: 'has-tooltip', title: "Assigned to #{assignee.name}", data: { container: 'body' } do - - image_tag(avatar_icon(issuable.assignee, 16), class: "avatar s16", alt: '') + %span{ class: "assignee-icon" } + - if assignee + = link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, assignee_id: issuable.assignee_id, state: 'all' }), + class: 'has-tooltip', title: "Assigned to #{assignee.name}", data: { container: 'body' } do + - image_tag(avatar_icon(issuable.assignee, 16), class: "avatar s16", alt: '') diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 92305594a81..68665858c3e 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -1,6 +1,8 @@ - page_title @user.name - page_description @user.bio -- page_specific_javascripts asset_path("users/application.js") +- content_for :page_specific_javascripts do + = page_specific_javascript_tag('lib/d3.js') + = page_specific_javascript_tag('users/application.js') - header_title @user.name, user_path(@user) - @no_container = true diff --git a/config/application.rb b/config/application.rb index 05fec995ed3..2b0595ede2b 100644 --- a/config/application.rb +++ b/config/application.rb @@ -84,6 +84,8 @@ module Gitlab config.assets.precompile << "graphs/application.js" config.assets.precompile << "users/application.js" config.assets.precompile << "network/application.js" + config.assets.precompile << "lib/utils/*.js" + config.assets.precompile << "lib/*.js" # Version of your assets, change this if you want to expire all your assets config.assets.version = '1.0' diff --git a/config/initializers/haml.rb b/config/initializers/haml.rb deleted file mode 100644 index 1516476815a..00000000000 --- a/config/initializers/haml.rb +++ /dev/null @@ -1,7 +0,0 @@ -Haml::Template.options[:ugly] = true - -# Remove the `:coffee` and `:coffeescript` filters -# -# See https://git.io/vztMu and http://stackoverflow.com/a/17571242/223897 -Haml::Filters.remove_filter('coffee') -Haml::Filters.remove_filter('coffeescript') diff --git a/config/initializers/hamlit.rb b/config/initializers/hamlit.rb new file mode 100644 index 00000000000..7b545d8c06c --- /dev/null +++ b/config/initializers/hamlit.rb @@ -0,0 +1,18 @@ +module Hamlit + class TemplateHandler + def call(template) + Engine.new( + generator: Temple::Generators::RailsOutputBuffer, + attr_quote: '"', + ).call(template.source) + end + end +end + +ActionView::Template.register_template_handler( + :haml, + Hamlit::TemplateHandler.new, +) + +Hamlit::Filters.remove_filter('coffee') +Hamlit::Filters.remove_filter('coffeescript') diff --git a/config/initializers/health_check.rb b/config/initializers/health_check.rb index 79e2d23ab2e..6796407d4e6 100644 --- a/config/initializers/health_check.rb +++ b/config/initializers/health_check.rb @@ -1,3 +1,17 @@ +# Email forcibly included in the standard checks, but the email health check +# doesn't support the full range of SMTP options, which can result in failures +# for valid SMTP configurations. +# Overwrite the HealthCheck's detection of whether email is configured +# in order to avoid the email check during standard checks +module HealthCheck + class Utils + def self.mailer_configured? + false + end + end +end + HealthCheck.setup do |config| config.standard_checks = ['database', 'migrations', 'cache'] + config.full_checks = ['database', 'migrations', 'cache'] end diff --git a/config/initializers/metrics.rb b/config/initializers/metrics.rb index d159f4eded2..75f89d524e7 100644 --- a/config/initializers/metrics.rb +++ b/config/initializers/metrics.rb @@ -113,6 +113,10 @@ if Gitlab::Metrics.enabled? config.instrument_methods(Banzai::Renderer) config.instrument_methods(Banzai::Querying) + config.instrument_instance_methods(Banzai::ObjectRenderer) + config.instrument_instance_methods(Banzai::Redactor) + config.instrument_methods(Banzai::NoteRenderer) + [Issuable, Mentionable, Participable].each do |klass| config.instrument_instance_methods(klass) config.instrument_instance_methods(klass::ClassMethods) diff --git a/config/initializers/smtp_settings.rb.sample b/config/initializers/smtp_settings.rb.sample index 2287a76fca7..bd37080b1c8 100644 --- a/config/initializers/smtp_settings.rb.sample +++ b/config/initializers/smtp_settings.rb.sample @@ -10,6 +10,7 @@ if Rails.env.production? Rails.application.config.action_mailer.delivery_method = :smtp + ActionMailer::Base.delivery_method = :smtp ActionMailer::Base.smtp_settings = { address: "email.server.com", port: 465, diff --git a/config/routes.rb b/config/routes.rb index e45293cdf7f..bdfb16a66bf 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -280,6 +280,7 @@ Rails.application.routes.draw do resource :logs, only: [:show] resource :health_check, controller: 'health_check', only: [:show] resource :background_jobs, controller: 'background_jobs', only: [:show] + resource :system_info, controller: 'system_info', only: [:show] resources :namespaces, path: '/projects', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only: [] do root to: 'projects#index', as: :projects diff --git a/doc/README.md b/doc/README.md index f1283cea0ad..be0d17084c7 100644 --- a/doc/README.md +++ b/doc/README.md @@ -44,6 +44,7 @@ - [Housekeeping](administration/housekeeping.md) Keep your Git repository tidy and fast. - [GitLab Performance Monitoring](monitoring/performance/introduction.md) Configure GitLab and InfluxDB for measuring performance metrics. - [Monitoring uptime](monitoring/health_check.md) Check the server status using the health check endpoint. +- [Debugging Tips](administration/troubleshooting/debug.md) Tips to debug problems when things go wrong - [Sidekiq Troubleshooting](administration/troubleshooting/sidekiq.md) Debug when Sidekiq appears hung and is not processing jobs. - [High Availability](administration/high_availability/README.md) Configure multiple servers for scaling or high availability. - [Container Registry](administration/container_registry.md) Configure Docker Registry with GitLab. diff --git a/doc/administration/troubleshooting/debug.md b/doc/administration/troubleshooting/debug.md new file mode 100644 index 00000000000..e5701b86cf3 --- /dev/null +++ b/doc/administration/troubleshooting/debug.md @@ -0,0 +1,120 @@ +# Debugging Tips + +Sometimes things don't work the way they should. Here are some tips on debugging issues out +in production. + +## The GNU Project Debugger (gdb) + +`gdb` is a must-have tool for debugging issues. To install on Ubuntu/Debian: + +``` +sudo apt-get install gdb +``` + +On CentOS: + +``` +sudo yum install gdb +``` + +## Common Problems + +Many of the tips to diagnose issues below apply to many different situations. We'll use one +concrete example to illustrate what you can do to learn what is going wrong. + +### 502 Gateway Timeout after unicorn spins at 100% CPU + +This error occurs when the Web server times out (default: 60 s) after not +hearing back from the unicorn worker. If the CPU spins to 100% while this in +progress, there may be something taking longer than it should. + +To fix this issue, we first need to figure out what is happening. The +following tips are only recommended if you do NOT mind users being affected by +downtime. Otherwise skip to the next section. + +1. Load the problematic URL +1. Run `sudo gdb -p <PID>` to attach to the unicorn process. +1. In the gdb window, type: + + ``` + call (void) rb_backtrace() + ``` + +1. This forces the process to generate a Ruby backtrace. Check + `/var/log/gitlab/unicorn/unicorn_stderr.log` for the backtace. For example, you may see: + + ```ruby + from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:33:in `block in start' + from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:33:in `loop' + from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:36:in `block (2 levels) in start' + from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:44:in `sample' + from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:68:in `sample_objects' + from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:68:in `each_with_object' + from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:68:in `each' + from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:69:in `block in sample_objects' + from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:69:in `name' + ``` + +1. To see the current threads, run: + + ``` + apply all thread bt + ``` + +1. Once you're done debugging with `gdb`, be sure to detach from the process and exit: + + ``` + detach + exit + ``` + +Note that if the unicorn process terminates before you are able to run these +commands, gdb will report an error. To buy more time, you can always raise the +Unicorn timeout. For omnibus users, you can edit `/etc/gitlab/gitlab.rb` and +increase it from 60 seconds to 300: + +```ruby +unicorn['worker_timeout'] = 300 +``` + +For source installations, edit `config/unicorn.rb`. + +[Reconfigure] GitLab for the changes to take effect. + +[Reconfigure]: ../restart_gitlab.md#omnibus-gitlab-reconfigure + +#### Troubleshooting without affecting other users + +The previous section attached to a running unicorn process, and this may have +undesirable effects for users trying to access GitLab during this time. If you +are concerned about affecting others during a production system, you can run a +separate Rails process to debug the issue: + +1. Log in to your GitLab account. +1. Copy the URL that is causing problems (e.g. https://gitlab.com/ABC). +1. Obtain the private token for your user (Profile Settings -> Account). +1. Bring up the GitLab Rails console. For omnibus users, run: + + ```` + sudo gitlab-rails console + ``` + +1. At the Rails console, run: + + ```ruby + [1] pry(main)> app.get '<URL FROM STEP 1>/private_token?<TOKEN FROM STEP 2>' + ``` + + For example: + + ```ruby + [1] pry(main)> app.get 'https://gitlab.com/gitlab-org/gitlab-ce/issues/1?private_token=123456' + ``` + +1. In a new window, run `top`. It should show this ruby process using 100% CPU. Write down the PID. +1. Follow step 2 from the previous section on using gdb. + +# More information + +* [Debugging Stuck Ruby Processes](https://blog.newrelic.com/2013/04/29/debugging-stuck-ruby-processes-what-to-do-before-you-kill-9/) +* [Cheatsheet of using gdb and ruby processes](gdb-stuck-ruby.txt) diff --git a/doc/administration/troubleshooting/gdb-stuck-ruby.txt b/doc/administration/troubleshooting/gdb-stuck-ruby.txt new file mode 100644 index 00000000000..13d5dfcffa4 --- /dev/null +++ b/doc/administration/troubleshooting/gdb-stuck-ruby.txt @@ -0,0 +1,142 @@ +# Here's the script I'll use to demonstrate - it just loops forever: + +$ cat test.rb +#!/usr/bin/env ruby + +loop do + sleep 1 +end + +# Now, I'll start the script in the background, and redirect stdout and stderr +# to /dev/null: + +$ ruby ./test.rb >/dev/null 2>/dev/null & +[1] 1343 + +# Next, I'll grab the PID of the script (1343): + +$ ps aux | grep test.rb +vagrant 1343 0.0 0.4 3884 1652 pts/0 S 14:42 0:00 ruby ./test.rb +vagrant 1345 0.0 0.2 4624 852 pts/0 S+ 14:42 0:00 grep --color=auto test.rb + +# Now I start gdb. Note that I'm using sudo here. This may or may not be +# necessary in your setup. I'd try without sudo first, and fall back to adding +# it if the next step fails: + +$ sudo gdb +GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04 +Copyright (C) 2012 Free Software Foundation, Inc. +License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> +This is free software: you are free to change and redistribute it. +There is NO WARRANTY, to the extent permitted by law. Type "show copying" +and "show warranty" for details. +This GDB was configured as "i686-linux-gnu". +For bug reporting instructions, please see: +<http://bugs.launchpad.net/gdb-linaro/>. + +# OK, now I'm in gdb, and I want to instruct it to attach to our Ruby process. +# I can do that using the 'attach' command, which takes a PID (the one we +# gathered above): + +(gdb) attach 1343 +Attaching to process 1343 +Reading symbols from /opt/vagrant_ruby/bin/ruby...done. +Reading symbols from /lib/i386-linux-gnu/librt.so.1...(no debugging symbols found)...done. +Loaded symbols for /lib/i386-linux-gnu/librt.so.1 +Reading symbols from /lib/i386-linux-gnu/libdl.so.2...(no debugging symbols found)...done. +Loaded symbols for /lib/i386-linux-gnu/libdl.so.2 +Reading symbols from /lib/i386-linux-gnu/libcrypt.so.1...(no debugging symbols found)...done. +Loaded symbols for /lib/i386-linux-gnu/libcrypt.so.1 +Reading symbols from /lib/i386-linux-gnu/libm.so.6...(no debugging symbols found)...done. +Loaded symbols for /lib/i386-linux-gnu/libm.so.6 +Reading symbols from /lib/i386-linux-gnu/libc.so.6...(no debugging symbols found)...done. +Loaded symbols for /lib/i386-linux-gnu/libc.so.6 +Reading symbols from /lib/i386-linux-gnu/libpthread.so.0...(no debugging symbols found)...done. +[Thread debugging using libthread_db enabled] +Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1". +Loaded symbols for /lib/i386-linux-gnu/libpthread.so.0 +Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done. +Loaded symbols for /lib/ld-linux.so.2 +0xb770c424 in __kernel_vsyscall () + +# Great, now gdb is attached to the target process. If the step above fails, try +# going back and running gdb under sudo. The next thing I want to do is gather +# C-level backtraces from all threads in the process. The following command +# stands for 'thread apply all backtrace': + +(gdb) t a a bt + +Thread 1 (Thread 0xb74d76c0 (LWP 1343)): +#0 0xb770c424 in __kernel_vsyscall () +#1 0xb75d7abd in select () from /lib/i386-linux-gnu/libc.so.6 +#2 0x08069c56 in rb_thread_wait_for (time=...) at eval.c:11376 +#3 0x080a20fd in rb_f_sleep (argc=1, argv=0xbf85f490) at process.c:1633 +#4 0x0805e0e2 in call_cfunc (argv=0xbf85f490, argc=1, len=-1, recv=3075299660, func=0x80a20b0 <rb_f_sleep>) + at eval.c:5778 +#5 rb_call0 (klass=3075304600, recv=3075299660, id=9393, oid=9393, argc=1, argv=0xbf85f490, body=0xb74c85a8, flags=2) + at eval.c:5928 +#6 0x0805e35d in rb_call (klass=3075304600, recv=3075299660, mid=9393, argc=1, argv=0xbf85f490, scope=1, + self=<optimized out>) at eval.c:6176 +#7 0x080651ec in rb_eval (self=3075299660, n=0xb74c4e1c) at eval.c:3521 +#8 0x0805c31c in rb_yield_0 (val=6, self=3075299660, klass=<optimized out>, flags=0, avalue=0) at eval.c:5095 +#9 0x0806a1e5 in loop_i () at eval.c:5227 +#10 0x08058dbd in rb_rescue2 (b_proc=0x806a1c0 <loop_i>, data1=0, r_proc=0, data2=0) at eval.c:5491 +#11 0x08058f28 in rb_f_loop () at eval.c:5252 +#12 0x0805e0c1 in call_cfunc (argv=0x0, argc=0, len=0, recv=3075299660, func=0x8058ef0 <rb_f_loop>) at eval.c:5781 +#13 rb_call0 (klass=3075304600, recv=3075299660, id=4121, oid=4121, argc=0, argv=0x0, body=0xb74d4dbc, flags=2) + at eval.c:5928 +#14 0x0805e35d in rb_call (klass=3075304600, recv=3075299660, mid=4121, argc=0, argv=0x0, scope=1, self=<optimized out>) + at eval.c:6176 +#15 0x080651ec in rb_eval (self=3075299660, n=0xb74c4dcc) at eval.c:3521 +#16 0x080662c6 in rb_eval (self=3075299660, n=0xb74c4de0) at eval.c:3236 +#17 0x08068ee4 in ruby_exec_internal () at eval.c:1654 +#18 0x08068f24 in ruby_exec () at eval.c:1674 +#19 0x0806b2cd in ruby_run () at eval.c:1684 +#20 0x08053771 in main (argc=2, argv=0xbf860204, envp=0xbf860210) at main.c:48 + +# C backtraces are sometimes sufficient, but often Ruby backtraces are necessary +# for debugging as well. Ruby has a built-in function called rb_backtrace() that +# we can use to dump out a Ruby backtrace, but it prints to stdout or stderr +# (depending on your Ruby version), which might have been redirected to a file +# or to /dev/null (as in our example) when the process started up. +# +# To get aroundt this, we'll do a little trick and redirect the target process's +# stdout and stderr to the current TTY, so that any output from the process +# will appear directly on our screen. + +# First, let's close the existing file descriptors for stdout and stderr +# (FD 1 and 2, respectively): +(gdb) call (void) close(1) +(gdb) call (void) close(2) + +# Next, we need to figure out the device name for the current TTY: +(gdb) shell tty +/dev/pts/0 + +# OK, now we can pass the device name obtained above to open() and attach +# file descriptors 1 and 2 back to the current TTY with these calls: + +(gdb) call (int) open("/dev/pts/0", 2, 0) +$1 = 1 +(gdb) call (int) open("/dev/pts/0", 2, 0) +$2 = 2 + +# Finally, we call rb_backtrace() in order to dump the Ruby backtrace: + +(gdb) call (void) rb_backtrace() + from ./test.rb:4:in `sleep' + from ./test.rb:4 + from ./test.rb:3:in `loop' + from ./test.rb:3 + +# And here's how we get out of gdb. Once you've quit, you'll probably want to +# clean up the stuck process by killing it. + +(gdb) quit +A debugging session is active. + + Inferior 1 [process 1343] will be detached. + +Quit anyway? (y or n) y +Detaching from program: /opt/vagrant_ruby/bin/ruby, process 1343 +$ diff --git a/doc/api/builds.md b/doc/api/builds.md index de998944352..2adea11247e 100644 --- a/doc/api/builds.md +++ b/doc/api/builds.md @@ -107,6 +107,11 @@ Example of response Get a list of builds for specific commit in a project. +This endpoint will return all builds, from all pipelines for a given commit. +If the commit SHA is not found, it will respond with 404, otherwise it will +return an array of builds (an empty array if there are no builds for this +particular commit). + ``` GET /projects/:id/repository/commits/:sha/builds ``` diff --git a/doc/api/issues.md b/doc/api/issues.md index 0bc82ef9edb..708fc691f67 100644 --- a/doc/api/issues.md +++ b/doc/api/issues.md @@ -28,7 +28,7 @@ GET /issues?labels=foo,bar&state=opened | Attribute | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `state` | string | no | Return all issues or just those that are `opened` or `closed`| -| `labels` | string | no | Comma-separated list of label names | +| `labels` | string | no | Comma-separated list of label names, issues with any of the labels will be returned | | `order_by`| string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` | | `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` | @@ -83,6 +83,82 @@ Example response: ] ``` +## List group issues + +Get a list of a group's issues. + +``` +GET /groups/:id/issues +GET /groups/:id/issues?state=opened +GET /groups/:id/issues?state=closed +GET /groups/:id/issues?labels=foo +GET /groups/:id/issues?labels=foo,bar +GET /groups/:id/issues?labels=foo,bar&state=opened +GET /groups/:id/issues?milestone=1.0.0 +GET /groups/:id/issues?milestone=1.0.0&state=opened +``` + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer | yes | The ID of a group | +| `state` | string | no | Return all issues or just those that are `opened` or `closed`| +| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned | +| `milestone` | string| no | The milestone title | +| `order_by`| string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` | +| `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` | + + +```bash +curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/groups/4/issues +``` + +Example response: + +```json +[ + { + "project_id" : 4, + "milestone" : { + "due_date" : null, + "project_id" : 4, + "state" : "closed", + "description" : "Rerum est voluptatem provident consequuntur molestias similique ipsum dolor.", + "iid" : 3, + "id" : 11, + "title" : "v3.0", + "created_at" : "2016-01-04T15:31:39.788Z", + "updated_at" : "2016-01-04T15:31:39.788Z" + }, + "author" : { + "state" : "active", + "web_url" : "https://gitlab.example.com/u/root", + "avatar_url" : null, + "username" : "root", + "id" : 1, + "name" : "Administrator" + }, + "description" : "Omnis vero earum sunt corporis dolor et placeat.", + "state" : "closed", + "iid" : 1, + "assignee" : { + "avatar_url" : null, + "web_url" : "https://gitlab.example.com/u/lennie", + "state" : "active", + "username" : "lennie", + "id" : 9, + "name" : "Dr. Luella Kovacek" + }, + "labels" : [], + "id" : 41, + "title" : "Ut commodi ullam eos dolores perferendis nihil sunt.", + "updated_at" : "2016-01-04T15:31:46.176Z", + "created_at" : "2016-01-04T15:31:46.176Z", + "subscribed" : false, + "user_notes_count": 1 + } +] +``` + ## List project issues Get a list of a project's issues. @@ -104,7 +180,7 @@ GET /projects/:id/issues?iid=42 | `id` | integer | yes | The ID of a project | | `iid` | integer | no | Return the issue having the given `iid` | | `state` | string | no | Return all issues or just those that are `opened` or `closed`| -| `labels` | string | no | Comma-separated list of label names | +| `labels` | string | no | Comma-separated list of label names, issues with any of the labels will be returned | | `milestone` | string| no | The milestone title | | `order_by`| string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` | | `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` | diff --git a/doc/api/oauth2.md b/doc/api/oauth2.md index d416a826f79..31902e145f6 100644 --- a/doc/api/oauth2.md +++ b/doc/api/oauth2.md @@ -65,6 +65,13 @@ curl -H "Authorization: Bearer OAUTH-TOKEN" https://localhost:3000/api/v3/user ## Resource Owner Password Credentials +## Deprecation Notice + +1. Starting in GitLab 9.0, the Resource Owner Password Credentials will be *disabled* for users with two-factor authentication turned on. +2. These users can access the API using [personal access tokens] instead. + +--- + In this flow, a token is requested in exchange for the resource owner credentials (username and password). The credentials should only be used when there is a high degree of trust between the resource owner and the client (e.g. the client is part of the device operating system or a highly privileged application), and when other authorization grant types are not @@ -100,3 +107,5 @@ client = OAuth2::Client.new('the_client_id', 'the_client_secret', :site => "http access_token = client.password.get_token('user@example.com', 'sekret') puts access_token.token ``` + +[personal access tokens]: ./README.md#personal-access-tokens diff --git a/doc/api/services.md b/doc/api/services.md index ccfc0fccb7f..32d6e2dea78 100644 --- a/doc/api/services.md +++ b/doc/api/services.md @@ -374,40 +374,6 @@ Get Gemnasium service settings for a project. GET /projects/:id/services/gemnasium ``` -## GitLab CI - -Continuous integration server from GitLab - -### Create/Edit GitLab CI service - -Set GitLab CI service for a project. - -``` -PUT /projects/:id/services/gitlab-ci -``` - -Parameters: - -- `token` (**required**) - GitLab CI project specific token -- `project_url` (**required**) - http://ci.gitlabhq.com/projects/3 -- `enable_ssl_verification` (optional) - Enable SSL verification - -### Delete GitLab CI service - -Delete GitLab CI service for a project. - -``` -DELETE /projects/:id/services/gitlab-ci -``` - -### Get GitLab CI service settings - -Get GitLab CI service settings for a project. - -``` -GET /projects/:id/services/gitlab-ci -``` - ## HipChat Private group chat and IM diff --git a/doc/api/session.md b/doc/api/session.md index 71e93d0bb0a..066a055702d 100644 --- a/doc/api/session.md +++ b/doc/api/session.md @@ -1,5 +1,12 @@ # Session +## Deprecation Notice + +1. Starting in GitLab 9.0, this feature will be *disabled* for users with two-factor authentication turned on. +2. These users can access the API using [personal access tokens] instead. + +--- + You can login with both GitLab and LDAP credentials in order to obtain the private token. @@ -45,3 +52,5 @@ Example response: "private_token": "9koXpg98eAheJpvBs5tK" } ``` + +[personal access tokens]: ./README.md#personal-access-tokens diff --git a/doc/api/settings.md b/doc/api/settings.md index 43a0fe35e42..b5152311f28 100644 --- a/doc/api/settings.md +++ b/doc/api/settings.md @@ -56,7 +56,7 @@ PUT /application/settings | `gravatar_enabled` | boolean | no | Enable Gravatar | | `sign_in_text` | string | no | Text on login page | | `home_page_url` | string | no | Redirect to this URL when not logged in | -| `default_branch_protection` | integer | no | Determine if developers can push to master. Can take `0` _(not protected, both developers and masters can push new commits, force push or delete the branch)_, `1` _(partially protected, developers can push new commits, but cannot force push or delete the branch, masters can do anything)_ or `2` _(fully protected, developers cannot push new commits, force push or delete the branch, masters can do anything)_ as a parameter. Default is `1`. | +| `default_branch_protection` | integer | no | Determine if developers can push to master. Can take `0` _(not protected, both developers and masters can push new commits, force push or delete the branch)_, `1` _(partially protected, developers can push new commits, but cannot force push or delete the branch, masters can do anything)_ or `2` _(fully protected, developers cannot push new commits, force push or delete the branch, masters can do anything)_ as a parameter. Default is `2`. | | `restricted_visibility_levels` | array of integers | no | Selected levels cannot be used by non-admin users for projects or snippets. Can take `0` _(Private)_, `1` _(Internal)_ and `2` _(Public)_ as a parameter. Default is null which means there is no restriction. | | `max_attachment_size` | integer | no | Limit attachment size in MB | | `session_expire_delay` | integer | no | Session duration in minutes. GitLab restart is required to apply changes | diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 1892acda29b..d2d1b04f893 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -1034,8 +1034,8 @@ You can find the link under `/ci/lint` of your gitlab instance. ## Skipping builds -If your commit message contains `[ci skip]`, the commit will be created but the -builds will be skipped. +If your commit message contains `[ci skip]` or `[skip ci]`, using any +capitalization, the commit will be created but the builds will be skipped. ## Examples diff --git a/doc/customization/issue_closing.md b/doc/customization/issue_closing.md index 194b8e00299..4620bb2dcde 100644 --- a/doc/customization/issue_closing.md +++ b/doc/customization/issue_closing.md @@ -8,7 +8,7 @@ the matched text will be closed. This happens when the commit is pushed to a pro When not specified, the default `issue_closing_pattern` as shown below will be used: ```bash -((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?))+) +((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing))(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?)|([A-Z][A-Z0-9_]+-\d+))+) ``` Here, `%{issue_ref}` is a complex regular expression defined inside GitLab, that matches a reference to a local issue (`#123`), cross-project issue (`group/project#123`) or a link to an issue (`https://gitlab.example.com/group/project/issues/123`). diff --git a/doc/development/architecture.md b/doc/development/architecture.md index 12e33406cb6..33fd50f4c11 100644 --- a/doc/development/architecture.md +++ b/doc/development/architecture.md @@ -52,7 +52,9 @@ To serve repositories over SSH there's an add-on application called gitlab-shell ### Components -![GitLab Diagram Overview](gitlab_diagram_overview.png) +![GitLab Diagram Overview](gitlab_architecture_diagram.png) + +_[edit diagram (for GitLab team members only)](https://docs.google.com/drawings/d/1fBzAyklyveF-i-2q-OHUIqDkYfjjxC4mq5shwKSZHLs/edit)_ A typical install of GitLab will be on GNU/Linux. It uses Nginx or Apache as a web front end to proxypass the Unicorn web server. By default, communication between Unicorn and the front end is via a Unix domain socket but forwarding requests via TCP is also supported. The web front end accesses `/home/git/gitlab/public` bypassing the Unicorn server to serve static pages, uploads (e.g. avatar images or attachments), and precompiled assets. GitLab serves web pages and a [GitLab API](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/api) using the Unicorn web server. It uses Sidekiq as a job queue which, in turn, uses redis as a non-persistent database backend for job information, meta data, and incoming jobs. diff --git a/doc/development/gitlab_architecture_diagram.png b/doc/development/gitlab_architecture_diagram.png Binary files differnew file mode 100644 index 00000000000..9ab7ffd3c7b --- /dev/null +++ b/doc/development/gitlab_architecture_diagram.png diff --git a/doc/development/gitlab_diagram_overview.png b/doc/development/gitlab_diagram_overview.png Binary files differdeleted file mode 100644 index d9b9eed3d8f..00000000000 --- a/doc/development/gitlab_diagram_overview.png +++ /dev/null diff --git a/doc/development/gotchas.md b/doc/development/gotchas.md index 21078c8d6f9..9d7fe7440d2 100644 --- a/doc/development/gotchas.md +++ b/doc/development/gotchas.md @@ -46,7 +46,7 @@ Rubocop](https://gitlab.com/gitlab-org/gitlab-ce/blob/8-4-stable/.rubocop.yml#L9 Using the inline `:coffee` or `:coffeescript` Haml filters comes with a performance overhead. -_**Note:** We've [removed these two filters](https://gitlab.com/gitlab-org/gitlab-ce/blob/8-5-stable/config/initializers/haml.rb) +_**Note:** We've [removed these two filters](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/config/initializers/hamlit.rb) in an initializer._ ### Further reading diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md index 8a7547e5322..e2ca46504e7 100644 --- a/doc/development/migration_style_guide.md +++ b/doc/development/migration_style_guide.md @@ -37,7 +37,6 @@ First, you need to provide information on whether the migration can be applied: For example: ``` -# rubocop:disable all # Migration type: online without errors (works on previous version and new one) class MyMigration < ActiveRecord::Migration ... diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md index 6d04b9590e6..41685c7ee41 100644 --- a/doc/development/rake_tasks.md +++ b/doc/development/rake_tasks.md @@ -33,3 +33,23 @@ bundle exec rake gitlab:generate_docs ``` bundle exec rake services:doc ``` + +## Updating Emoji Digests + +To update the Emoji digests file (used for Emoji autocomplete) you must run the +following: + +``` +bundle exec rake gemojione:digests +``` + +This will update the file `fixtures/emojis/digests.json` based on the currently +available Emoji. + +## Emoji Sprites + +Generating a sprite file containing all the Emoji can be done by running: + +``` +bundle exec rake gemojione:sprite +``` diff --git a/doc/downgrade_ee_to_ce/README.md b/doc/downgrade_ee_to_ce/README.md index 3625c4191b8..a6d22e5a04a 100644 --- a/doc/downgrade_ee_to_ce/README.md +++ b/doc/downgrade_ee_to_ce/README.md @@ -44,13 +44,13 @@ to avoid getting this error, you need to remove all instances of the **Omnibus Installation** ``` -$ sudo gitlab-rails runner "Service.where(type: 'JenkinsService').delete_all" +$ sudo gitlab-rails runner "Service.where(type: ['JenkinsService', 'JenkinsDeprecatedService']).delete_all" ``` **Source Installation** ``` -$ bundle exec rails runner "Service.where(type: 'JenkinsService').delete_all" production +$ bundle exec rails runner "Service.where(type: ['JenkinsService', 'JenkinsDeprecatedService']).delete_all" production ``` ## Downgrade to CE diff --git a/doc/install/installation.md b/doc/install/installation.md index d9290b1fa76..dc8d9c65535 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -391,6 +391,10 @@ GitLab Shell is an SSH access and repository management software developed speci ### Install gitlab-workhorse +GitLab-Workhorse uses [GNU Make](https://www.gnu.org/software/make/). +If you are not using Linux you may have to run `gmake` instead of +`make` below. + cd /home/git sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git cd gitlab-workhorse diff --git a/doc/install/requirements.md b/doc/install/requirements.md index 09c6211b3ab..a65ac8a5f79 100644 --- a/doc/install/requirements.md +++ b/doc/install/requirements.md @@ -52,7 +52,7 @@ If you have enough RAM memory and a recent CPU the speed of GitLab is mainly lim ### CPU -- 1 core works supports up to 100 users but the application can be a bit slower due to having all workers and background jobs running on the same core +- 1 core supports up to 100 users but the application can be a bit slower due to having all workers and background jobs running on the same core - **2 cores** is the **recommended** number of cores and supports up to 500 users - 4 cores supports up to 2,000 users - 8 cores supports up to 5,000 users diff --git a/doc/integration/external-issue-tracker.md b/doc/integration/external-issue-tracker.md index a2d7e922aad..8d2c6351fb8 100644 --- a/doc/integration/external-issue-tracker.md +++ b/doc/integration/external-issue-tracker.md @@ -1,7 +1,7 @@ # External issue tracker GitLab has a great issue tracker but you can also use an external one such as -Jira or Redmine. Issue trackers are configurable per GitLab project and allow +Jira, Redmine, or Bugzilla. Issue trackers are configurable per GitLab project and allow you to do the following: - the **Issues** link on the GitLab project pages takes you to the appropriate @@ -20,6 +20,7 @@ Visit the links below for details: - [Redmine](../project_services/redmine.md) - [Jira](../project_services/jira.md) +- [Bugzilla](../project_services/bugzilla.md) ### Service Template diff --git a/doc/monitoring/performance/grafana_configuration.md b/doc/monitoring/performance/grafana_configuration.md index 168bd85c26a..7947b0fedc4 100644 --- a/doc/monitoring/performance/grafana_configuration.md +++ b/doc/monitoring/performance/grafana_configuration.md @@ -44,70 +44,32 @@ on a separate server) ## Apply retention policies and create continuous queries -If you intend to import the GitLab provided Grafana dashboards, you will need -to copy and run a set of queries against InfluxDB to create the needed data -sets. +If you intend to import the GitLab provided Grafana dashboards, you will need to +set up the right retention policies and continuous queries. The easiest way of +doing this is by using the [influxdb-management](https://gitlab.com/gitlab-org/influxdb-management) +repository. -On the InfluxDB server, run the following command, substituting your InfluxDB -user and password: +To use this repository you must first clone it: -```bash -influxdb --username admin -password super_secret +``` +git clone https://gitlab.com/gitlab-org/influxdb-management.git +cd influxdb-management ``` -This will drop you in to an InfluxDB interactive session. Copy the entire -contents below and paste it in to the interactive session: +Next you must install the required dependencies: ``` -CREATE RETENTION POLICY default ON gitlab DURATION 1h REPLICATION 1 DEFAULT -CREATE RETENTION POLICY downsampled ON gitlab DURATION 7d REPLICATION 1 -CREATE CONTINUOUS QUERY grape_git_timings_per_action ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.grape_git_timings_per_action FROM gitlab."default".rails_method_calls WHERE (action !~ /.+/ OR action =~ /^Grape#/) AND method =~ /^(Rugged|Gitlab::Git)/ GROUP BY time(1m), action END; -CREATE CONTINUOUS QUERY grape_markdown_render_timings_overall ON gitlab BEGIN SELECT mean(banzai_cached_render_real_time) AS cached_real_mean, percentile(banzai_cached_render_real_time, 95) AS cached_real_95th, percentile(banzai_cached_render_real_time, 99) AS cached_real_99th, mean(banzai_cached_render_cpu_time) AS cached_cpu_mean, percentile(banzai_cached_render_cpu_time, 95) AS cached_cpu_95th, percentile(banzai_cached_render_cpu_time, 99) AS cached_cpu_99th, sum(banzai_cached_render_call_count) AS cached_call_count, mean(banzai_cacheless_render_real_time) AS cacheless_real_mean, percentile(banzai_cacheless_render_real_time, 95) AS cacheless_real_95th, percentile(banzai_cacheless_render_real_time, 99) AS cacheless_real_99th, mean(banzai_cacheless_render_cpu_time) AS cacheless_cpu_mean, percentile(banzai_cacheless_render_cpu_time, 95) AS cacheless_cpu_95th, percentile(banzai_cacheless_render_cpu_time, 99) AS cacheless_cpu_99th, sum(banzai_cacheless_render_call_count) AS cacheless_call_count INTO gitlab.downsampled.grape_markdown_render_timings_overall FROM gitlab."default".rails_transactions WHERE (action !~ /.+/ OR action =~ /^Grape#/) AND (banzai_cached_render_call_count > 0 OR banzai_cacheless_render_call_count > 0) GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY grape_markdown_render_timings_per_action ON gitlab BEGIN SELECT mean(banzai_cached_render_real_time) AS cached_real_mean, percentile(banzai_cached_render_real_time, 95) AS cached_real_95th, percentile(banzai_cached_render_real_time, 99) AS cached_real_99th, mean(banzai_cached_render_cpu_time) AS cached_cpu_mean, percentile(banzai_cached_render_cpu_time, 95) AS cached_cpu_95th, percentile(banzai_cached_render_cpu_time, 99) AS cached_cpu_99th, sum(banzai_cached_render_call_count) AS cached_call_count, mean(banzai_cacheless_render_real_time) AS cacheless_real_mean, percentile(banzai_cacheless_render_real_time, 95) AS cacheless_real_95th, percentile(banzai_cacheless_render_real_time, 99) AS cacheless_real_99th, mean(banzai_cacheless_render_cpu_time) AS cacheless_cpu_mean, percentile(banzai_cacheless_render_cpu_time, 95) AS cacheless_cpu_95th, percentile(banzai_cacheless_render_cpu_time, 99) AS cacheless_cpu_99th, sum(banzai_cacheless_render_call_count) AS cacheless_call_count INTO gitlab.downsampled.grape_markdown_render_timings_per_action FROM gitlab."default".rails_transactions WHERE (action !~ /.+/ OR action =~ /^Grape#/) AND (banzai_cached_render_call_count > 0 OR banzai_cacheless_render_call_count > 0) GROUP BY time(1m), action END; -CREATE CONTINUOUS QUERY grape_markdown_timings_overall ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.grape_markdown_timings_overall FROM gitlab."default".rails_method_calls WHERE (action !~ /.+/ OR action =~ /^Grape#/) AND method =~ /^Banzai/ GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY grape_method_call_timings_per_action_and_method ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.grape_method_call_timings_per_action_and_method FROM gitlab."default".rails_method_calls WHERE action !~ /.+/ OR action =~ /^Grape#/ GROUP BY time(1m), method, action END; -CREATE CONTINUOUS QUERY grape_method_call_timings_per_method ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.grape_method_call_timings_per_method FROM gitlab."default".rails_method_calls WHERE action !~ /.+/ OR action =~ /^Grape#/ GROUP BY time(1m), method END; -CREATE CONTINUOUS QUERY grape_transaction_counts_overall ON gitlab BEGIN SELECT count("duration") AS count INTO gitlab.downsampled.grape_transaction_counts_overall FROM gitlab."default".rails_transactions WHERE action !~ /.+/ OR action =~ /^Grape#/ GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY grape_transaction_counts_per_action ON gitlab BEGIN SELECT count("duration") AS count INTO gitlab.downsampled.grape_transaction_counts_per_action FROM gitlab."default".rails_transactions WHERE action !~ /.+/ OR action =~ /^Grape#/ GROUP BY time(1m), action END; -CREATE CONTINUOUS QUERY grape_transaction_timings_overall ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th, mean(sql_duration) AS sql_duration_mean, percentile(sql_duration, 95) AS sql_duration_95th, percentile(sql_duration, 99) AS sql_duration_99th, mean(view_duration) AS view_duration_mean, percentile(view_duration, 95) AS view_duration_95th, percentile(view_duration, 99) AS view_duration_99th, mean(cache_read_duration) AS cache_read_duration_mean, percentile(cache_read_duration, 99) AS cache_read_duration_99th, percentile(cache_read_duration, 95) AS cache_read_duration_95th, mean(cache_write_duration) AS cache_write_duration_mean, percentile(cache_write_duration, 99) AS cache_write_duration_99th, percentile(cache_write_duration, 95) AS cache_write_duration_95th, mean(cache_delete_duration) AS cache_delete_duration_mean, percentile(cache_delete_duration, 99) AS cache_delete_duration_99th, percentile(cache_delete_duration, 95) AS cache_delete_duration_95th, mean(cache_exists_duration) AS cache_exists_duration_mean, percentile(cache_exists_duration, 99) AS cache_exists_duration_99th, percentile(cache_exists_duration, 95) AS cache_exists_duration_95th, mean(cache_duration) AS cache_duration_mean, percentile(cache_duration, 99) AS cache_duration_99th, percentile(cache_duration, 95) AS cache_duration_95th, mean(method_duration) AS method_duration_mean, percentile(method_duration, 99) AS method_duration_99th, percentile(method_duration, 95) AS method_duration_95th INTO gitlab.downsampled.grape_transaction_timings_overall FROM gitlab."default".rails_transactions WHERE action !~ /.+/ OR action =~ /^Grape#/ GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY grape_transaction_timings_per_action ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th, mean(sql_duration) AS sql_duration_mean, percentile(sql_duration, 95) AS sql_duration_95th, percentile(sql_duration, 99) AS sql_duration_99th, mean(view_duration) AS view_duration_mean, percentile(view_duration, 95) AS view_duration_95th, percentile(view_duration, 99) AS view_duration_99th, mean(cache_read_duration) AS cache_read_duration_mean, percentile(cache_read_duration, 99) AS cache_read_duration_99th, percentile(cache_read_duration, 95) AS cache_read_duration_95th, mean(cache_write_duration) AS cache_write_duration_mean, percentile(cache_write_duration, 99) AS cache_write_duration_99th, percentile(cache_write_duration, 95) AS cache_write_duration_95th, mean(cache_delete_duration) AS cache_delete_duration_mean, percentile(cache_delete_duration, 99) AS cache_delete_duration_99th, percentile(cache_delete_duration, 95) AS cache_delete_duration_95th, mean(cache_exists_duration) AS cache_exists_duration_mean, percentile(cache_exists_duration, 99) AS cache_exists_duration_99th, percentile(cache_exists_duration, 95) AS cache_exists_duration_95th, mean(cache_duration) AS cache_duration_mean, percentile(cache_duration, 99) AS cache_duration_99th, percentile(cache_duration, 95) AS cache_duration_95th, mean(method_duration) AS method_duration_mean, percentile(method_duration, 99) AS method_duration_99th, percentile(method_duration, 95) AS method_duration_95th INTO gitlab.downsampled.grape_transaction_timings_per_action FROM gitlab."default".rails_transactions WHERE action !~ /.+/ OR action =~ /^Grape#/ GROUP BY time(1m), action END; -CREATE CONTINUOUS QUERY rails_file_descriptor_counts ON gitlab BEGIN SELECT sum(value) AS count INTO gitlab.downsampled.rails_file_descriptor_counts FROM gitlab."default".rails_file_descriptors GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY rails_gc_counts ON gitlab BEGIN SELECT sum(count) AS total, sum(minor_gc_count) AS minor, sum(major_gc_count) AS major INTO gitlab.downsampled.rails_gc_counts FROM gitlab."default".rails_gc_statistics GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY rails_gc_timings ON gitlab BEGIN SELECT mean(total_time) AS duration_mean, percentile(total_time, 95) AS duration_95th, percentile(total_time, 99) AS duration_99th INTO gitlab.downsampled.rails_gc_timings FROM gitlab."default".rails_gc_statistics GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY rails_git_timings_per_action ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.rails_git_timings_per_action FROM gitlab."default".rails_method_calls WHERE (action =~ /.+/ AND action !~ /^Grape#/) AND method =~ /^(Rugged|Gitlab::Git)/ GROUP BY time(1m), action END; -CREATE CONTINUOUS QUERY rails_markdown_render_timings_overall ON gitlab BEGIN SELECT mean(banzai_cached_render_real_time) AS cached_real_mean, percentile(banzai_cached_render_real_time, 95) AS cached_real_95th, percentile(banzai_cached_render_real_time, 99) AS cached_real_99th, mean(banzai_cached_render_cpu_time) AS cached_cpu_mean, percentile(banzai_cached_render_cpu_time, 95) AS cached_cpu_95th, percentile(banzai_cached_render_cpu_time, 99) AS cached_cpu_99th, sum(banzai_cached_render_call_count) AS cached_call_count, mean(banzai_cacheless_render_real_time) AS cacheless_real_mean, percentile(banzai_cacheless_render_real_time, 95) AS cacheless_real_95th, percentile(banzai_cacheless_render_real_time, 99) AS cacheless_real_99th, mean(banzai_cacheless_render_cpu_time) AS cacheless_cpu_mean, percentile(banzai_cacheless_render_cpu_time, 95) AS cacheless_cpu_95th, percentile(banzai_cacheless_render_cpu_time, 99) AS cacheless_cpu_99th, sum(banzai_cacheless_render_call_count) AS cacheless_call_count INTO gitlab.downsampled.rails_markdown_render_timings_overall FROM gitlab."default".rails_transactions WHERE (action =~ /.+/ AND action !~ /^Grape#/) AND (banzai_cached_render_call_count > 0 OR banzai_cacheless_render_call_count > 0) GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY rails_markdown_render_timings_per_action ON gitlab BEGIN SELECT mean(banzai_cached_render_real_time) AS cached_real_mean, percentile(banzai_cached_render_real_time, 95) AS cached_real_95th, percentile(banzai_cached_render_real_time, 99) AS cached_real_99th, mean(banzai_cached_render_cpu_time) AS cached_cpu_mean, percentile(banzai_cached_render_cpu_time, 95) AS cached_cpu_95th, percentile(banzai_cached_render_cpu_time, 99) AS cached_cpu_99th, sum(banzai_cached_render_call_count) AS cached_call_count, mean(banzai_cacheless_render_real_time) AS cacheless_real_mean, percentile(banzai_cacheless_render_real_time, 95) AS cacheless_real_95th, percentile(banzai_cacheless_render_real_time, 99) AS cacheless_real_99th, mean(banzai_cacheless_render_cpu_time) AS cacheless_cpu_mean, percentile(banzai_cacheless_render_cpu_time, 95) AS cacheless_cpu_95th, percentile(banzai_cacheless_render_cpu_time, 99) AS cacheless_cpu_99th, sum(banzai_cacheless_render_call_count) AS cacheless_call_count INTO gitlab.downsampled.rails_markdown_render_timings_per_action FROM gitlab."default".rails_transactions WHERE (action =~ /.+/ AND action !~ /^Grape#/) AND (banzai_cached_render_call_count > 0 OR banzai_cacheless_render_call_count > 0) GROUP BY time(1m), action END; -CREATE CONTINUOUS QUERY rails_markdown_timings_overall ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.rails_markdown_timings_overall FROM gitlab."default".rails_method_calls WHERE (action =~ /.+/ AND action !~ /^Grape#/) AND method =~ /^Banzai/ GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY rails_memory_usage_overall ON gitlab BEGIN SELECT mean(value) AS memory_mean, percentile(value, 95) AS memory_95th, percentile(value, 99) AS memory_99th INTO gitlab.downsampled.rails_memory_usage_overall FROM gitlab."default".rails_memory_usage GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY rails_method_call_timings_per_action_and_method ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.rails_method_call_timings_per_action_and_method FROM gitlab."default".rails_method_calls WHERE action =~ /.+/ AND action !~ /^Grape#/ GROUP BY time(1m), method, action END; -CREATE CONTINUOUS QUERY rails_method_call_timings_per_method ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.rails_method_call_timings_per_method FROM gitlab."default".rails_method_calls WHERE action =~ /.+/ AND action !~ /^Grape#/ GROUP BY time(1m), method END; -CREATE CONTINUOUS QUERY rails_object_counts_overall ON gitlab BEGIN SELECT sum(count) AS count INTO gitlab.downsampled.rails_object_counts_overall FROM gitlab."default".rails_object_counts GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY rails_object_counts_per_type ON gitlab BEGIN SELECT sum(count) AS count INTO gitlab.downsampled.rails_object_counts_per_type FROM gitlab."default".rails_object_counts GROUP BY time(1m), type END; -CREATE CONTINUOUS QUERY rails_transaction_counts_overall ON gitlab BEGIN SELECT count("duration") AS count INTO gitlab.downsampled.rails_transaction_counts_overall FROM gitlab."default".rails_transactions WHERE action =~ /.+/ AND action !~ /^Grape#/ GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY rails_transaction_counts_per_action ON gitlab BEGIN SELECT count("duration") AS count INTO gitlab.downsampled.rails_transaction_counts_per_action FROM gitlab."default".rails_transactions WHERE action =~ /.+/ AND action !~ /^Grape#/ GROUP BY time(1m), action END; -CREATE CONTINUOUS QUERY rails_transaction_timings_overall ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th, mean(sql_duration) AS sql_duration_mean, percentile(sql_duration, 95) AS sql_duration_95th, percentile(sql_duration, 99) AS sql_duration_99th, mean(view_duration) AS view_duration_mean, percentile(view_duration, 95) AS view_duration_95th, percentile(view_duration, 99) AS view_duration_99th, mean(cache_read_duration) AS cache_read_duration_mean, percentile(cache_read_duration, 99) AS cache_read_duration_99th, percentile(cache_read_duration, 95) AS cache_read_duration_95th, mean(cache_write_duration) AS cache_write_duration_mean, percentile(cache_write_duration, 99) AS cache_write_duration_99th, percentile(cache_write_duration, 95) AS cache_write_duration_95th, mean(cache_delete_duration) AS cache_delete_duration_mean, percentile(cache_delete_duration, 99) AS cache_delete_duration_99th, percentile(cache_delete_duration, 95) AS cache_delete_duration_95th, mean(cache_exists_duration) AS cache_exists_duration_mean, percentile(cache_exists_duration, 99) AS cache_exists_duration_99th, percentile(cache_exists_duration, 95) AS cache_exists_duration_95th, mean(cache_duration) AS cache_duration_mean, percentile(cache_duration, 99) AS cache_duration_99th, percentile(cache_duration, 95) AS cache_duration_95th, mean(method_duration) AS method_duration_mean, percentile(method_duration, 99) AS method_duration_99th, percentile(method_duration, 95) AS method_duration_95th INTO gitlab.downsampled.rails_transaction_timings_overall FROM gitlab."default".rails_transactions WHERE action =~ /.+/ AND action !~ /^Grape#/ GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY rails_transaction_timings_per_action ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th, mean(sql_duration) AS sql_duration_mean, percentile(sql_duration, 95) AS sql_duration_95th, percentile(sql_duration, 99) AS sql_duration_99th, mean(view_duration) AS view_duration_mean, percentile(view_duration, 95) AS view_duration_95th, percentile(view_duration, 99) AS view_duration_99th, mean(cache_read_duration) AS cache_read_duration_mean, percentile(cache_read_duration, 99) AS cache_read_duration_99th, percentile(cache_read_duration, 95) AS cache_read_duration_95th, mean(cache_write_duration) AS cache_write_duration_mean, percentile(cache_write_duration, 99) AS cache_write_duration_99th, percentile(cache_write_duration, 95) AS cache_write_duration_95th, mean(cache_delete_duration) AS cache_delete_duration_mean, percentile(cache_delete_duration, 99) AS cache_delete_duration_99th, percentile(cache_delete_duration, 95) AS cache_delete_duration_95th, mean(cache_exists_duration) AS cache_exists_duration_mean, percentile(cache_exists_duration, 99) AS cache_exists_duration_99th, percentile(cache_exists_duration, 95) AS cache_exists_duration_95th, mean(cache_duration) AS cache_duration_mean, percentile(cache_duration, 99) AS cache_duration_99th, percentile(cache_duration, 95) AS cache_duration_95th, mean(method_duration) AS method_duration_mean, percentile(method_duration, 99) AS method_duration_99th, percentile(method_duration, 95) AS method_duration_95th INTO gitlab.downsampled.rails_transaction_timings_per_action FROM gitlab."default".rails_transactions WHERE action =~ /.+/ AND action !~ /^Grape#/ GROUP BY time(1m), action END; -CREATE CONTINUOUS QUERY rails_view_timings_per_action_and_view ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.rails_view_timings_per_action_and_view FROM gitlab."default".rails_views WHERE action =~ /.+/ AND action !~ /^Grape#/ GROUP BY time(1m), action, view END; -CREATE CONTINUOUS QUERY sidekiq_file_descriptor_counts ON gitlab BEGIN SELECT sum(value) AS count INTO gitlab.downsampled.sidekiq_file_descriptor_counts FROM gitlab."default".sidekiq_file_descriptors GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY sidekiq_gc_counts ON gitlab BEGIN SELECT sum(count) AS total, sum(minor_gc_count) AS minor, sum(major_gc_count) AS major INTO gitlab.downsampled.sidekiq_gc_counts FROM gitlab."default".sidekiq_gc_statistics GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY sidekiq_gc_timings ON gitlab BEGIN SELECT mean(total_time) AS duration_mean, percentile(total_time, 95) AS duration_95th, percentile(total_time, 99) AS duration_99th INTO gitlab.downsampled.sidekiq_gc_timings FROM gitlab."default".sidekiq_gc_statistics GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY sidekiq_git_timings_per_action ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.sidekiq_git_timings_per_action FROM gitlab."default".sidekiq_method_calls WHERE method =~ /^(Rugged|Gitlab::Git)/ GROUP BY time(1m), action END; -CREATE CONTINUOUS QUERY sidekiq_markdown_render_timings_overall ON gitlab BEGIN SELECT mean(banzai_cached_render_real_time) AS cached_real_mean, percentile(banzai_cached_render_real_time, 95) AS cached_real_95th, percentile(banzai_cached_render_real_time, 99) AS cached_real_99th, mean(banzai_cached_render_cpu_time) AS cached_cpu_mean, percentile(banzai_cached_render_cpu_time, 95) AS cached_cpu_95th, percentile(banzai_cached_render_cpu_time, 99) AS cached_cpu_99th, sum(banzai_cached_render_call_count) AS cached_call_count, mean(banzai_cacheless_render_real_time) AS cacheless_real_mean, percentile(banzai_cacheless_render_real_time, 95) AS cacheless_real_95th, percentile(banzai_cacheless_render_real_time, 99) AS cacheless_real_99th, mean(banzai_cacheless_render_cpu_time) AS cacheless_cpu_mean, percentile(banzai_cacheless_render_cpu_time, 95) AS cacheless_cpu_95th, percentile(banzai_cacheless_render_cpu_time, 99) AS cacheless_cpu_99th, sum(banzai_cacheless_render_call_count) AS cacheless_call_count INTO gitlab.downsampled.sidekiq_markdown_render_timings_overall FROM gitlab."default".sidekiq_transactions WHERE (banzai_cached_render_call_count > 0 OR banzai_cacheless_render_call_count > 0) GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY sidekiq_markdown_render_timings_per_action ON gitlab BEGIN SELECT mean(banzai_cached_render_real_time) AS cached_real_mean, percentile(banzai_cached_render_real_time, 95) AS cached_real_95th, percentile(banzai_cached_render_real_time, 99) AS cached_real_99th, mean(banzai_cached_render_cpu_time) AS cached_cpu_mean, percentile(banzai_cached_render_cpu_time, 95) AS cached_cpu_95th, percentile(banzai_cached_render_cpu_time, 99) AS cached_cpu_99th, sum(banzai_cached_render_call_count) AS cached_call_count, mean(banzai_cacheless_render_real_time) AS cacheless_real_mean, percentile(banzai_cacheless_render_real_time, 95) AS cacheless_real_95th, percentile(banzai_cacheless_render_real_time, 99) AS cacheless_real_99th, mean(banzai_cacheless_render_cpu_time) AS cacheless_cpu_mean, percentile(banzai_cacheless_render_cpu_time, 95) AS cacheless_cpu_95th, percentile(banzai_cacheless_render_cpu_time, 99) AS cacheless_cpu_99th, sum(banzai_cacheless_render_call_count) AS cacheless_call_count INTO gitlab.downsampled.sidekiq_markdown_render_timings_per_action FROM gitlab."default".sidekiq_transactions WHERE (banzai_cached_render_call_count > 0 OR banzai_cacheless_render_call_count > 0) GROUP BY time(1m), action END; -CREATE CONTINUOUS QUERY sidekiq_markdown_timings_overall ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.sidekiq_markdown_timings_overall FROM gitlab."default".sidekiq_method_calls WHERE method =~ /^Banzai/ GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY sidekiq_memory_usage_overall ON gitlab BEGIN SELECT mean(value) AS memory_mean, percentile(value, 95) AS memory_95th, percentile(value, 99) AS memory_99th INTO gitlab.downsampled.sidekiq_memory_usage_overall FROM gitlab."default".sidekiq_memory_usage GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY sidekiq_method_call_timings_per_action_and_method ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.sidekiq_method_call_timings_per_action_and_method FROM gitlab."default".sidekiq_method_calls GROUP BY time(1m), method, action END; -CREATE CONTINUOUS QUERY sidekiq_method_call_timings_per_method ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.sidekiq_method_call_timings_per_method FROM gitlab."default".sidekiq_method_calls GROUP BY time(1m), method END; -CREATE CONTINUOUS QUERY sidekiq_object_counts_overall ON gitlab BEGIN SELECT sum(count) AS count INTO gitlab.downsampled.sidekiq_object_counts_overall FROM gitlab."default".sidekiq_object_counts GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY sidekiq_object_counts_per_type ON gitlab BEGIN SELECT sum(count) AS count INTO gitlab.downsampled.sidekiq_object_counts_per_type FROM gitlab."default".sidekiq_object_counts GROUP BY time(1m), type END; -CREATE CONTINUOUS QUERY sidekiq_transaction_counts_overall ON gitlab BEGIN SELECT count("duration") AS count INTO gitlab.downsampled.sidekiq_transaction_counts_overall FROM gitlab."default".sidekiq_transactions GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY sidekiq_transaction_counts_per_action ON gitlab BEGIN SELECT count("duration") AS count INTO gitlab.downsampled.sidekiq_transaction_counts_per_action FROM gitlab."default".sidekiq_transactions GROUP BY time(1m), action END; -CREATE CONTINUOUS QUERY sidekiq_transaction_timings_overall ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th, mean(sql_duration) AS sql_duration_mean, percentile(sql_duration, 95) AS sql_duration_95th, percentile(sql_duration, 99) AS sql_duration_99th, mean(view_duration) AS view_duration_mean, percentile(view_duration, 95) AS view_duration_95th, percentile(view_duration, 99) AS view_duration_99th, mean(cache_read_duration) AS cache_read_duration_mean, percentile(cache_read_duration, 99) AS cache_read_duration_99th, percentile(cache_read_duration, 95) AS cache_read_duration_95th, mean(cache_write_duration) AS cache_write_duration_mean, percentile(cache_write_duration, 99) AS cache_write_duration_99th, percentile(cache_write_duration, 95) AS cache_write_duration_95th, mean(cache_delete_duration) AS cache_delete_duration_mean, percentile(cache_delete_duration, 99) AS cache_delete_duration_99th, percentile(cache_delete_duration, 95) AS cache_delete_duration_95th, mean(cache_exists_duration) AS cache_exists_duration_mean, percentile(cache_exists_duration, 99) AS cache_exists_duration_99th, percentile(cache_exists_duration, 95) AS cache_exists_duration_95th, mean(cache_duration) AS cache_duration_mean, percentile(cache_duration, 99) AS cache_duration_99th, percentile(cache_duration, 95) AS cache_duration_95th, mean(method_duration) AS method_duration_mean, percentile(method_duration, 99) AS method_duration_99th, percentile(method_duration, 95) AS method_duration_95th INTO gitlab.downsampled.sidekiq_transaction_timings_overall FROM gitlab."default".sidekiq_transactions GROUP BY time(1m) END; -CREATE CONTINUOUS QUERY sidekiq_transaction_timings_per_action ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th, mean(sql_duration) AS sql_duration_mean, percentile(sql_duration, 95) AS sql_duration_95th, percentile(sql_duration, 99) AS sql_duration_99th, mean(view_duration) AS view_duration_mean, percentile(view_duration, 95) AS view_duration_95th, percentile(view_duration, 99) AS view_duration_99th, mean(cache_read_duration) AS cache_read_duration_mean, percentile(cache_read_duration, 99) AS cache_read_duration_99th, percentile(cache_read_duration, 95) AS cache_read_duration_95th, mean(cache_write_duration) AS cache_write_duration_mean, percentile(cache_write_duration, 99) AS cache_write_duration_99th, percentile(cache_write_duration, 95) AS cache_write_duration_95th, mean(cache_delete_duration) AS cache_delete_duration_mean, percentile(cache_delete_duration, 99) AS cache_delete_duration_99th, percentile(cache_delete_duration, 95) AS cache_delete_duration_95th, mean(cache_exists_duration) AS cache_exists_duration_mean, percentile(cache_exists_duration, 99) AS cache_exists_duration_99th, percentile(cache_exists_duration, 95) AS cache_exists_duration_95th, mean(cache_duration) AS cache_duration_mean, percentile(cache_duration, 99) AS cache_duration_99th, percentile(cache_duration, 95) AS cache_duration_95th, mean(method_duration) AS method_duration_mean, percentile(method_duration, 99) AS method_duration_99th, percentile(method_duration, 95) AS method_duration_95th INTO gitlab.downsampled.sidekiq_transaction_timings_per_action FROM gitlab."default".sidekiq_transactions GROUP BY time(1m), action END; -CREATE CONTINUOUS QUERY sidekiq_view_timings_per_action_and_view ON gitlab BEGIN SELECT mean("duration") AS duration_mean, percentile("duration", 95) AS duration_95th, percentile("duration", 99) AS duration_99th INTO gitlab.downsampled.sidekiq_view_timings_per_action_and_view FROM gitlab."default".sidekiq_views GROUP BY time(1m), action, view END; -CREATE CONTINUOUS QUERY web_transaction_counts_overall ON gitlab BEGIN SELECT count("duration") AS count INTO gitlab.downsampled.web_transaction_counts_overall FROM gitlab."default".rails_transactions GROUP BY time(1m) END; +gem install bundler +bundle install ``` +Now you must configure the repository by first copying `.env.example` to `.env` +and then editing the `.env` file to contain the correct InfluxDB settings. Once +configured you can simply run `bundle exec rake` and the InfluxDB database will +be configured for you. + +For more information see the [influxdb-management README](https://gitlab.com/gitlab-org/influxdb-management/blob/master/README.md). + ## Import Dashboards You can now import a set of default dashboards that will give you a good diff --git a/doc/project_services/bugzilla.md b/doc/project_services/bugzilla.md new file mode 100644 index 00000000000..215ed6fe9cc --- /dev/null +++ b/doc/project_services/bugzilla.md @@ -0,0 +1,17 @@ +# Bugzilla Service + +Go to your project's **Settings > Services > Bugzilla** and fill in the required +details as described in the table below. + +| Field | Description | +| ----- | ----------- | +| `description` | A name for the issue tracker (to differentiate between instances, for example) | +| `project_url` | The URL to the project in Bugzilla which is being linked to this GitLab project. Note that the `project_url` requires PRODUCT_NAME to be updated with the product/project name in Bugzilla. | +| `issues_url` | The URL to the issue in Bugzilla project that is linked to this GitLab project. Note that the `issues_url` requires `:id` in the URL. This ID is used by GitLab as a placeholder to replace the issue number. | +| `new_issue_url` | This is the URL to create a new issue in Bugzilla for the project linked to this GitLab project. Note that the `new_issue_url` requires PRODUCT_NAME to be updated with the product/project name in Bugzilla. | + +Once you have configured and enabled Bugzilla: + +- the **Issues** link on the GitLab project pages takes you to the appropriate + Bugzilla product page +- clicking **New issue** on the project dashboard takes you to Bugzilla for entering a new issue diff --git a/doc/project_services/project_services.md b/doc/project_services/project_services.md index f81a035f70b..e15d5db3253 100644 --- a/doc/project_services/project_services.md +++ b/doc/project_services/project_services.md @@ -30,6 +30,7 @@ further configuration instructions and details. Contributions are welcome. | [Atlassian Bamboo CI](bamboo.md) | A continuous integration and build server | | Buildkite | Continuous integration and deployments | | [Builds emails](builds_emails.md) | Email the builds status to a list of recipients | +| [Bugzilla](bugzilla.md) | Bugzilla issue tracker | | Campfire | Simple web-based real-time group chat | | Custom Issue Tracker | Custom issue tracker | | Drone CI | Continuous Integration platform built on Docker, written in Go | diff --git a/doc/update/2.6-to-3.0.md b/doc/update/2.6-to-3.0.md index 4827ef9501a..fb70eaacbc9 100644 --- a/doc/update/2.6-to-3.0.md +++ b/doc/update/2.6-to-3.0.md @@ -13,6 +13,10 @@ git fetch origin git checkout v3.0.3 +# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0 +# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to +# `gem "modernizr-rails", "2.7.1"`` +sudo -u gitlab -H vim Gemfile # Install libs sudo -u gitlab bundle install --without development test postgres diff --git a/doc/update/2.9-to-3.0.md b/doc/update/2.9-to-3.0.md index f4a997a8c5e..ce46b57c09a 100644 --- a/doc/update/2.9-to-3.0.md +++ b/doc/update/2.9-to-3.0.md @@ -13,6 +13,11 @@ sudo -u gitlab -H git fetch origin sudo -u gitlab -H git checkout v3.0.3 +# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0 +# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to +# `gem "modernizr-rails", "2.7.1"`` +sudo -u gitlab -H vim Gemfile + # Install gems sudo -u gitlab -H bundle install --without development test postgres diff --git a/doc/update/3.0-to-3.1.md b/doc/update/3.0-to-3.1.md index a30485c42f7..6ac83f3b60d 100644 --- a/doc/update/3.0-to-3.1.md +++ b/doc/update/3.0-to-3.1.md @@ -25,6 +25,11 @@ sudo -u gitlab -H git checkout v3.1.0 # Install new charlock_holmes sudo gem install charlock_holmes --version '0.6.9' +# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0 +# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to +# `gem "modernizr-rails", "2.7.1"`` +sudo -u gitlab -H vim Gemfile + # Install gems for MySQL sudo -u gitlab -H bundle install --without development test postgres sqlite diff --git a/doc/update/3.1-to-4.0.md b/doc/update/3.1-to-4.0.md index f1ef4df4744..df53ed6de83 100644 --- a/doc/update/3.1-to-4.0.md +++ b/doc/update/3.1-to-4.0.md @@ -26,6 +26,11 @@ I wrote a bash script which will do it automatically for you. Just make sure all sudo -u gitlab -H git fetch sudo -u gitlab -H git checkout 4-0-stable +# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0 +# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to +# `gem "modernizr-rails", "2.7.1"`` +sudo -u gitlab -H vim Gemfile + # Install gems for MySQL sudo -u gitlab -H bundle install --without development test postgres diff --git a/doc/update/4.0-to-4.1.md b/doc/update/4.0-to-4.1.md index d89d5235917..c163bfd348d 100644 --- a/doc/update/4.0-to-4.1.md +++ b/doc/update/4.0-to-4.1.md @@ -22,6 +22,11 @@ cd /home/gitlab/gitlab/ sudo -u gitlab -H git fetch sudo -u gitlab -H git checkout 4-1-stable +# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0 +# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to +# `gem "modernizr-rails", "2.7.1"`` +sudo -u gitlab -H vim Gemfile + # Install gems for MySQL sudo -u gitlab -H bundle install --without development test postgres diff --git a/doc/update/4.1-to-4.2.md b/doc/update/4.1-to-4.2.md index 6fe4412ff90..97367c5f347 100644 --- a/doc/update/4.1-to-4.2.md +++ b/doc/update/4.1-to-4.2.md @@ -17,7 +17,15 @@ sudo -u gitlab -H git fetch sudo -u gitlab -H git checkout 4-2-stable -# Install libs +# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0 +# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to +# `gem "modernizr-rails", "2.7.1"`` +sudo -u gitlab -H vim Gemfile + +# Run a bundle install without deployment to generate the new Gemfile +sudo -u gitlab -H bundle install --without development test postgres --no-deployment + +# Install libs (with deployment this time) sudo -u gitlab -H bundle install --without development test postgres --deployment # update db diff --git a/doc/update/4.2-to-5.0.md b/doc/update/4.2-to-5.0.md index f9faf65f952..ee6de51c923 100644 --- a/doc/update/4.2-to-5.0.md +++ b/doc/update/4.2-to-5.0.md @@ -85,8 +85,17 @@ sudo -u git -H cp config/gitlab.yml.example config/gitlab.yml # edit it sudo -u git -H vim config/gitlab.yml +# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0 +# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to +# `gem "modernizr-rails", "2.7.1"`` +sudo -u git -H vim Gemfile +# Run a bundle install without deployment to generate the new Gemfile +sudo -u git -H bundle install --without development test postgres --no-deployment + +# Install libs (with deployment this time) sudo -u git -H bundle install --without development test postgres --deployment + sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production sudo -u git -H bundle exec rake gitlab:shell:setup RAILS_ENV=production sudo -u git -H bundle exec rake gitlab:shell:build_missing_projects RAILS_ENV=production diff --git a/doc/update/5.0-to-5.1.md b/doc/update/5.0-to-5.1.md index 9fbd1f88515..f0fddcf83af 100644 --- a/doc/update/5.0-to-5.1.md +++ b/doc/update/5.0-to-5.1.md @@ -42,7 +42,17 @@ cd /home/git/gitlab sudo rm tmp/sockets/gitlab.socket sudo -u git -H cp config/puma.rb.example config/puma.rb +# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0 +# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to +# `gem "modernizr-rails", "2.7.1"`` +sudo -u git -H vim Gemfile + +# Run a bundle install without deployment to generate the new Gemfile +sudo -u git -H bundle install --without development test postgres --no-deployment + +# Install libs (with deployment this time) sudo -u git -H bundle install --without development test postgres --deployment + sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production sudo -u git -H bundle exec rake migrate_merge_requests RAILS_ENV=production sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production diff --git a/doc/update/5.1-to-5.2.md b/doc/update/5.1-to-5.2.md index cf9c4e4f770..625fcc33852 100644 --- a/doc/update/5.1-to-5.2.md +++ b/doc/update/5.1-to-5.2.md @@ -40,12 +40,28 @@ sudo -u git -H git checkout v1.4.0 ```bash cd /home/git/gitlab +# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0 +# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to +# `gem "modernizr-rails", "2.7.1"`` +sudo -u git -H vim Gemfile + # MySQL + +# Run a bundle install without deployment to generate the new Gemfile +sudo -u git -H bundle install --without development test postgres --no-deployment + +# Install libs (with deployment this time) sudo -u git -H bundle install --without development test postgres --deployment -#PostgreSQL +# PostgreSQL + +# Run a bundle install without deployment to generate the new Gemfile +sudo -u git -H bundle install --without development test mysql --no-deployment + +# Install libs (with deployment this time) sudo -u git -H bundle install --without development test mysql --deployment +# Both MySQL and PostgreSQL sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production diff --git a/doc/update/5.1-to-5.4.md b/doc/update/5.1-to-5.4.md index 97a98ede070..547d453914c 100644 --- a/doc/update/5.1-to-5.4.md +++ b/doc/update/5.1-to-5.4.md @@ -37,12 +37,28 @@ sudo -u git -H git checkout v1.7.9 # Addresses multiple critical security vulner ```bash cd /home/git/gitlab +# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0 +# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to +# `gem "modernizr-rails", "2.7.1"`` +sudo -u git -H vim Gemfile + # MySQL + +# Run a bundle install without deployment to generate the new Gemfile +sudo -u git -H bundle install --without development test postgres --no-deployment + +# Install libs (with deployment this time) sudo -u git -H bundle install --without development test postgres --deployment -#PostgreSQL +# PostgreSQL + +# Run a bundle install without deployment to generate the new Gemfile +sudo -u git -H bundle install --without development test mysql --no-deployment + +# Install libs (with deployment this time) sudo -u git -H bundle install --without development test mysql --deployment +# Both MySQL and PostgreSQL sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production diff --git a/doc/update/5.1-to-6.0.md b/doc/update/5.1-to-6.0.md index a3fdd92bd2f..c992c69678e 100644 --- a/doc/update/5.1-to-6.0.md +++ b/doc/update/5.1-to-6.0.md @@ -137,12 +137,28 @@ sudo apt-get install python-docutils ```bash cd /home/git/gitlab +# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0 +# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to +# `gem "modernizr-rails", "2.7.1"`` +sudo -u git -H vim Gemfile + # MySQL + +# Run a bundle install without deployment to generate the new Gemfile +sudo -u git -H bundle install --without development test postgres --no-deployment + +# Install libs (with deployment this time) sudo -u git -H bundle install --without development test postgres --deployment -#PostgreSQL +# PostgreSQL + +# Run a bundle install without deployment to generate the new Gemfile +sudo -u git -H bundle install --without development test mysql --no-deployment + +# Install libs (with deployment this time) sudo -u git -H bundle install --without development test mysql --deployment +# Both MySQL and PostgreSQL sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production sudo -u git -H bundle exec rake migrate_groups RAILS_ENV=production sudo -u git -H bundle exec rake migrate_global_projects RAILS_ENV=production diff --git a/doc/update/5.2-to-5.3.md b/doc/update/5.2-to-5.3.md index 27613aeda07..c5254f6fb0c 100644 --- a/doc/update/5.2-to-5.3.md +++ b/doc/update/5.2-to-5.3.md @@ -31,12 +31,28 @@ sudo -u git -H git checkout 5-3-stable ```bash cd /home/git/gitlab +# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0 +# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to +# `gem "modernizr-rails", "2.7.1"`` +sudo -u git -H vim Gemfile + # MySQL + +# Run a bundle install without deployment to generate the new Gemfile +sudo -u git -H bundle install --without development test postgres --no-deployment + +# Install libs (with deployment this time) sudo -u git -H bundle install --without development test postgres --deployment -#PostgreSQL +# PostgreSQL + +# Run a bundle install without deployment to generate the new Gemfile +sudo -u git -H bundle install --without development test mysql --no-deployment + +# Install libs (with deployment this time) sudo -u git -H bundle install --without development test mysql --deployment +# Both MySQL and PostgreSQL sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production diff --git a/doc/update/5.3-to-5.4.md b/doc/update/5.3-to-5.4.md index 577b9a585ff..c4a6146dcda 100644 --- a/doc/update/5.3-to-5.4.md +++ b/doc/update/5.3-to-5.4.md @@ -35,12 +35,28 @@ sudo -u git -H git checkout v1.7.9 # Addresses multiple critical security vulner ```bash cd /home/git/gitlab +# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0 +# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to +# `gem "modernizr-rails", "2.7.1"`` +sudo -u git -H vim Gemfile + # MySQL + +# Run a bundle install without deployment to generate the new Gemfile +sudo -u git -H bundle install --without development test postgres --no-deployment + +# Install libs (with deployment this time) sudo -u git -H bundle install --without development test postgres --deployment -#PostgreSQL +# PostgreSQL + +# Run a bundle install without deployment to generate the new Gemfile +sudo -u git -H bundle install --without development test mysql --no-deployment + +# Install libs (with deployment this time) sudo -u git -H bundle install --without development test mysql --deployment +# Both MySQL and PostgreSQL sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production diff --git a/doc/update/5.4-to-6.0.md b/doc/update/5.4-to-6.0.md index d9c6d9bfb91..f0fee634322 100644 --- a/doc/update/5.4-to-6.0.md +++ b/doc/update/5.4-to-6.0.md @@ -73,12 +73,28 @@ sudo apt-get install python-docutils ```bash cd /home/git/gitlab +# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0 +# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to +# `gem "modernizr-rails", "2.7.1"`` +sudo -u git -H vim Gemfile + # MySQL + +# Run a bundle install without deployment to generate the new Gemfile +sudo -u git -H bundle install --without development test postgres --no-deployment + +# Install libs (with deployment this time) sudo -u git -H bundle install --without development test postgres --deployment # PostgreSQL + +# Run a bundle install without deployment to generate the new Gemfile +sudo -u git -H bundle install --without development test mysql --no-deployment + +# Install libs (with deployment this time) sudo -u git -H bundle install --without development test mysql --deployment +# Both MySQL and PostgreSQL sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production sudo -u git -H bundle exec rake migrate_groups RAILS_ENV=production sudo -u git -H bundle exec rake migrate_global_projects RAILS_ENV=production diff --git a/doc/update/6.0-to-6.1.md b/doc/update/6.0-to-6.1.md index c5eba1c01c4..409faf30902 100644 --- a/doc/update/6.0-to-6.1.md +++ b/doc/update/6.0-to-6.1.md @@ -50,13 +50,28 @@ sudo -u git -H git checkout v1.7.9 ```bash cd /home/git/gitlab +# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0 +# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to +# `gem "modernizr-rails", "2.7.1"`` +sudo -u git -H vim Gemfile + # MySQL + +# Run a bundle install without deployment to generate the new Gemfile +sudo -u git -H bundle install --without development test postgres --no-deployment + +# Install libs (with deployment this time) sudo -u git -H bundle install --without development test postgres --deployment -#PostgreSQL -sudo -u git -H bundle install --without development test mysql --deployment +# PostgreSQL + +# Run a bundle install without deployment to generate the new Gemfile +sudo -u git -H bundle install --without development test mysql --no-deployment +# Install libs (with deployment this time) +sudo -u git -H bundle install --without development test mysql --deployment +# Both MySQL and PostgreSQL sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production sudo -u git -H bundle exec rake migrate_iids RAILS_ENV=production sudo -u git -H bundle exec rake assets:clean RAILS_ENV=production diff --git a/doc/update/6.1-to-6.2.md b/doc/update/6.1-to-6.2.md index a534528108a..150c7ae1c83 100644 --- a/doc/update/6.1-to-6.2.md +++ b/doc/update/6.1-to-6.2.md @@ -45,13 +45,28 @@ sudo apt-get install logrotate ```bash cd /home/git/gitlab +# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0 +# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to +# `gem "modernizr-rails", "2.7.1"`` +sudo -u git -H vim Gemfile + # MySQL + +# Run a bundle install without deployment to generate the new Gemfile +sudo -u git -H bundle install --without development test postgres --no-deployment + +# Install libs (with deployment this time) sudo -u git -H bundle install --without development test postgres --deployment -#PostgreSQL -sudo -u git -H bundle install --without development test mysql --deployment +# PostgreSQL + +# Run a bundle install without deployment to generate the new Gemfile +sudo -u git -H bundle install --without development test mysql --no-deployment +# Install libs (with deployment this time) +sudo -u git -H bundle install --without development test mysql --deployment +# Both MySQL and PostgreSQL sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production sudo -u git -H bundle exec rake assets:clean RAILS_ENV=production sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production diff --git a/doc/update/6.2-to-6.3.md b/doc/update/6.2-to-6.3.md index b08ebde0808..b96dfb8add7 100644 --- a/doc/update/6.2-to-6.3.md +++ b/doc/update/6.2-to-6.3.md @@ -40,13 +40,28 @@ The gitlab-shell config changed recently, so check for config file changes and m ```bash cd /home/git/gitlab +# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0 +# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to +# `gem "modernizr-rails", "2.7.1"`` +sudo -u git -H vim Gemfile + # MySQL + +# Run a bundle install without deployment to generate the new Gemfile +sudo -u git -H bundle install --without development test postgres --no-deployment + +# Install libs (with deployment this time) sudo -u git -H bundle install --without development test postgres --deployment # PostgreSQL -sudo -u git -H bundle install --without development test mysql --deployment +# Run a bundle install without deployment to generate the new Gemfile +sudo -u git -H bundle install --without development test mysql --no-deployment + +# Install libs (with deployment this time) +sudo -u git -H bundle install --without development test mysql --deployment +# Both MySQL and PostgreSQL # Run database migrations sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production diff --git a/doc/update/6.3-to-6.4.md b/doc/update/6.3-to-6.4.md index 951d92dfeb5..37028be055f 100644 --- a/doc/update/6.3-to-6.4.md +++ b/doc/update/6.3-to-6.4.md @@ -36,13 +36,28 @@ sudo -u git -H git checkout v1.8.0 ```bash cd /home/git/gitlab +# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0 +# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to +# `gem "modernizr-rails", "2.7.1"`` +sudo -u git -H vim Gemfile + # MySQL + +# Run a bundle install without deployment to generate the new Gemfile +sudo -u git -H bundle install --without development test postgres --no-deployment + +# Install libs (with deployment this time) sudo -u git -H bundle install --without development test postgres --deployment # PostgreSQL -sudo -u git -H bundle install --without development test mysql --deployment +# Run a bundle install without deployment to generate the new Gemfile +sudo -u git -H bundle install --without development test mysql --no-deployment + +# Install libs (with deployment this time) +sudo -u git -H bundle install --without development test mysql --deployment +# Both MySQL and PostgreSQL # Run database migrations sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production diff --git a/doc/update/6.4-to-6.5.md b/doc/update/6.4-to-6.5.md index 0dae9a9fe59..982381a4db0 100644 --- a/doc/update/6.4-to-6.5.md +++ b/doc/update/6.4-to-6.5.md @@ -46,13 +46,28 @@ sudo -u git -H git checkout v1.8.0 ```bash cd /home/git/gitlab -# MySQL installations (note: the line below states '--without ... postgres') +# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0 +# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to +# `gem "modernizr-rails", "2.7.1"`` +sudo -u git -H vim Gemfile + +# MySQL + +# Run a bundle install without deployment to generate the new Gemfile +sudo -u git -H bundle install --without development test postgres --no-deployment + +# Install libs (with deployment this time) sudo -u git -H bundle install --without development test postgres --deployment -# PostgreSQL installations (note: the line below states '--without ... mysql') -sudo -u git -H bundle install --without development test mysql --deployment +# PostgreSQL + +# Run a bundle install without deployment to generate the new Gemfile +sudo -u git -H bundle install --without development test mysql --no-deployment +# Install libs (with deployment this time) +sudo -u git -H bundle install --without development test mysql --deployment +# Both MySQL and PostgreSQL # Run database migrations sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production diff --git a/doc/update/6.5-to-6.6.md b/doc/update/6.5-to-6.6.md index c24e83eb006..bbed2b30215 100644 --- a/doc/update/6.5-to-6.6.md +++ b/doc/update/6.5-to-6.6.md @@ -46,12 +46,28 @@ sudo -u git -H git checkout v1.8.0 ```bash cd /home/git/gitlab -# MySQL installations (note: the line below states '--without ... postgres') +# The Modernizr gem was yanked from RubyGems. It is required for GitLab >= 2.8.0 +# Edit `Gemfile` and change `gem "modernizr", "2.5.3"` to +# `gem "modernizr-rails", "2.7.1"`` +sudo -u git -H vim Gemfile + +# MySQL + +# Run a bundle install without deployment to generate the new Gemfile +sudo -u git -H bundle install --without development test postgres --no-deployment + +# Install libs (with deployment this time) sudo -u git -H bundle install --without development test postgres --deployment -# PostgreSQL installations (note: the line below states '--without ... mysql') +# PostgreSQL + +# Run a bundle install without deployment to generate the new Gemfile +sudo -u git -H bundle install --without development test mysql --no-deployment + +# Install libs (with deployment this time) sudo -u git -H bundle install --without development test mysql --deployment +# Both MySQL and PostgreSQL # Run database migrations sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production diff --git a/doc/update/6.6-to-6.7.md b/doc/update/6.6-to-6.7.md index b4298c93429..8e82942a1a0 100644 --- a/doc/update/6.6-to-6.7.md +++ b/doc/update/6.6-to-6.7.md @@ -46,13 +46,23 @@ sudo -u git -H git checkout v1.9.1 ```bash cd /home/git/gitlab -# MySQL installations (note: the line below states '--without ... postgres') +# MySQL + +# Run a bundle install without deployment to generate the new Gemfile +sudo -u git -H bundle install --without development test postgres --no-deployment + +# Install libs (with deployment this time) sudo -u git -H bundle install --without development test postgres --deployment -# PostgreSQL installations (note: the line below states '--without ... mysql') -sudo -u git -H bundle install --without development test mysql --deployment +# PostgreSQL +# Run a bundle install without deployment to generate the new Gemfile +sudo -u git -H bundle install --without development test mysql --no-deployment + +# Install libs (with deployment this time) +sudo -u git -H bundle install --without development test mysql --deployment +# Both MySQL and PostgreSQL # Run database migrations sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production diff --git a/doc/update/6.x-or-7.x-to-7.14.md b/doc/update/6.x-or-7.x-to-7.14.md index c45fc9340ea..f170a0021b7 100644 --- a/doc/update/6.x-or-7.x-to-7.14.md +++ b/doc/update/6.x-or-7.x-to-7.14.md @@ -147,12 +147,15 @@ sudo -u git -H bundle install --without development test postgres --deployment # PostgreSQL installations (note: the line below states '--without ... mysql') sudo -u git -H bundle install --without development test mysql --deployment -# Run database migrations -sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production +# Run database migrations from 6.0 to 6.1 +sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production VERSION=20130909132950 # Enable internal issue IDs (introduced in GitLab 6.1) sudo -u git -H bundle exec rake migrate_iids RAILS_ENV=production +# Run left database migrations +sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production + # Clean up assets and cache sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production diff --git a/doc/update/8.8-to-8.9.md b/doc/update/8.8-to-8.9.md index f14046bb4be..423140a92c7 100644 --- a/doc/update/8.8-to-8.9.md +++ b/doc/update/8.8-to-8.9.md @@ -122,6 +122,19 @@ via [/etc/default/gitlab]. [Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache [/etc/default/gitlab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-9-stable/lib/support/init.d/gitlab.default.example#L37 +#### SMTP configuration + +If you're installing from source and use SMTP to deliver mail, you will need to add the following line +to config/initializers/smtp_settings.rb: + +```ruby +ActionMailer::Base.delivery_method = :smtp +``` + +See [smtp_settings.rb.sample] as an example. + +[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/v8.9.0/config/initializers/smtp_settings.rb.sample#L13 + #### Init script Ensure you're still up-to-date with the latest init script changes: diff --git a/doc/user/project/highlighting.md b/doc/user/project/highlighting.md new file mode 100644 index 00000000000..73a2d176b54 --- /dev/null +++ b/doc/user/project/highlighting.md @@ -0,0 +1,31 @@ +[Rouge]: https://rubygems.org/gems/rouge + +# Syntax Highlighting + +GitLab provides syntax highlighting on all files and snippets through the [Rouge][] rubygem. It will try to guess what language to use based on the file extension, which most of the time is sufficient. + +If GitLab is guessing wrong, you can override its choice of language using the `gitlab-language` attribute in `.gitattributes`. For example, if you are working in a Prolog project and using the `.pl` file extension (which would normally be highlighted as Perl), you can add the following to your `.gitattributes` file: + +``` conf +*.pl gitlab-language=prolog +``` + +When you check in and push that change, all `*.pl` files in your project will be highlighted as Prolog. + +The paths here are simply git's builtin [`.gitattributes` interface](https://git-scm.com/docs/gitattributes). So, if you were to invent a file format called a `Nicefile` at the root of your project that used ruby syntax, all you need is: + +``` conf +/Nicefile gitlab-language=ruby +``` + +To disable highlighting entirely, use `gitlab-language=text`. Lots more fun shenanigans are available through CGI options, such as: + +``` conf +# json with erb in it +/my-cool-file gitlab-language=erb?parent=json + +# an entire file of highlighting errors! +/other-file gitlab-language=text?token=Error +``` + +Please note that these configurations will only take effect when the `.gitattributes` file is in your default branch (usually `master`). diff --git a/doc/workflow/img/todo_list_item.png b/doc/workflow/img/todo_list_item.png Binary files differnew file mode 100644 index 00000000000..884ba1d22a3 --- /dev/null +++ b/doc/workflow/img/todo_list_item.png diff --git a/doc/workflow/img/todos_add_todo_sidebar.png b/doc/workflow/img/todos_add_todo_sidebar.png Binary files differnew file mode 100644 index 00000000000..126ecc2c82f --- /dev/null +++ b/doc/workflow/img/todos_add_todo_sidebar.png diff --git a/doc/workflow/img/todos_icon.png b/doc/workflow/img/todos_icon.png Binary files differindex 879b3b51c21..a63bad0c258 100644 --- a/doc/workflow/img/todos_icon.png +++ b/doc/workflow/img/todos_icon.png diff --git a/doc/workflow/img/todos_mark_done_sidebar.png b/doc/workflow/img/todos_mark_done_sidebar.png Binary files differnew file mode 100644 index 00000000000..f449f977dd6 --- /dev/null +++ b/doc/workflow/img/todos_mark_done_sidebar.png diff --git a/doc/workflow/todos.md b/doc/workflow/todos.md index 5f440fdafdd..9524ffd5420 100644 --- a/doc/workflow/todos.md +++ b/doc/workflow/todos.md @@ -1,4 +1,4 @@ -# GitLab ToDos +# GitLab Todos >**Note:** This feature was [introduced][ce-2817] in GitLab 8.5. @@ -14,8 +14,9 @@ in a simple dashboard. --- -You can access quickly your Todos dashboard by clicking the round gray icon -next to the search bar in the upper right corner. +You can quickly access the Todos dashboard using the bell icon next to the +search bar in the upper right corner. The number in blue is the number of Todos +you still have open. ![Todos icon](img/todos_icon.png) @@ -29,45 +30,61 @@ A Todo appears in your Todos dashboard when: >**Note:** Commenting on a commit will _not_ trigger a Todo. -## How a Todo is marked as Done +### Manually creating a Todo + +You can also add an issue or merge request to your Todos dashboard by clicking +the "Add Todo" button in the issue or merge request sidebar. + +![Adding a Todo from the issuable sidebar](img/todos_add_todo_sidebar.png) + +## Marking a Todo as done Any action to the corresponding issue or merge request will mark your Todo as -**Done**. This action can include: +**Done**. Actions that dismiss Todos include: - changing the assignee - changing the milestone - adding/removing a label - commenting on the issue -In case where you think no action is needed, you can manually mark the todo as -done by clicking the corresponding **Done** button, and it will disappear from -your Todos list. If you want to mark all your Todos as done, just click on the -**Mark all as done** button. - --- -In order for a Todo to be marked as done, the action must be coming from you. -So, if you close the related issue or merge the merge request yourself, and you -had a Todo for that, it will automatically get marked as done. On the other -hand, if someone else closes, merges or takes action on the issue or merge -request, your Todo will remain pending. This makes sense because you may need -to give attention to an issue even if it has been resolved. +Todos are personal, and they're only marked as done if the action is coming from +you. If you close the issue or merge request, your Todo will automatically +be marked as done. + +If someone else closes, merges, or takes action on the issue or merge +request, your Todo will remain pending. This prevents other users from closing issues without you being notified. There is just one Todo per issue or merge request, so mentioning a user a hundred times in an issue will only trigger one Todo. +--- + +If no action is needed, you can manually mark the Todo as done by clicking the +corresponding **Done** button, and it will disappear from your Todo list. + +![A Todo in the Todos dashboard](img/todo_list_item.png) + +A Todo can also be marked as done from the issue or merge request sidebar using +the "Mark Done" button. + +![Mark Done from the issuable sidebar](img/todos_mark_done_sidebar.png) + +You can mark all your Todos as done at once by clicking on the **Mark all as +done** button. + ## Filtering your Todos -In general, there are four kinds of filters you can use on your Todos -dashboard: +There are four kinds of filters you can use on your Todos dashboard. -| Filter | Description | -| ------ | ----------- | +| Filter | Description | +| ------- | ----------- | | Project | Filter by project | | Author | Filter by the author that triggered the Todo | | Type | Filter by issue or merge request | | Action | Filter by the action that triggered the Todo (Assigned or Mentioned)| -You can choose more than one filters at the same time. +You can also filter by more than one of these at the same time. [ce-2817]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/2817 diff --git a/features/steps/dashboard/new_project.rb b/features/steps/dashboard/new_project.rb index 29e6b9f1a01..31f8924c38c 100644 --- a/features/steps/dashboard/new_project.rb +++ b/features/steps/dashboard/new_project.rb @@ -10,7 +10,7 @@ class Spinach::Features::NewProject < Spinach::FeatureSteps end step 'I see "New Project" page' do - expect(page).to have_content('Project owner') + expect(page).to have_content('Project path') expect(page).to have_content('Project name') end diff --git a/features/steps/project/issues/award_emoji.rb b/features/steps/project/issues/award_emoji.rb index 1b14659b4df..1498f899cf5 100644 --- a/features/steps/project/issues/award_emoji.rb +++ b/features/steps/project/issues/award_emoji.rb @@ -81,9 +81,7 @@ class Spinach::Features::AwardEmoji < Spinach::FeatureSteps end step 'I search "hand"' do - page.within('.emoji-menu-content') do - fill_in 'emoji_search', with: 'hand' - end + fill_in 'emoji_search', with: 'hand' end step 'I see search result for "hand"' do diff --git a/lib/api/api.rb b/lib/api/api.rb index f8f680a6311..c3fff8b2f8f 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -39,7 +39,7 @@ module API mount ::API::Issues mount ::API::Keys mount ::API::Labels - mount ::API::Licenses + mount ::API::LicenseTemplates mount ::API::MergeRequests mount ::API::Milestones mount ::API::Namespaces diff --git a/lib/api/builds.rb b/lib/api/builds.rb index 979328efe0e..086d8511e8f 100644 --- a/lib/api/builds.rb +++ b/lib/api/builds.rb @@ -33,10 +33,10 @@ module API get ':id/repository/commits/:sha/builds' do authorize_read_builds! - commit = user_project.pipelines.find_by_sha(params[:sha]) - return not_found! unless commit + return not_found! unless user_project.commit(params[:sha]) - builds = commit.builds.order('id DESC') + pipelines = user_project.pipelines.where(sha: params[:sha]) + builds = user_project.builds.where(pipeline: pipelines).order('id DESC') builds = filter_builds(builds, params[:scope]) present paginate(builds), with: Entities::Build, diff --git a/lib/api/issues.rb b/lib/api/issues.rb index 4c43257c48a..8a03a41e9c5 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -59,6 +59,41 @@ module API end end + resource :groups do + # Get a list of group issues + # + # Parameters: + # id (required) - The ID of a group + # state (optional) - Return "opened" or "closed" issues + # labels (optional) - Comma-separated list of label names + # milestone (optional) - Milestone title + # order_by (optional) - Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` + # sort (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc` + # + # Example Requests: + # GET /groups/:id/issues + # GET /groups/:id/issues?state=opened + # GET /groups/:id/issues?state=closed + # GET /groups/:id/issues?labels=foo + # GET /groups/:id/issues?labels=foo,bar + # GET /groups/:id/issues?labels=foo,bar&state=opened + # GET /groups/:id/issues?milestone=1.0.0 + # GET /groups/:id/issues?milestone=1.0.0&state=closed + get ":id/issues" do + group = find_group(params[:id]) + + params[:state] ||= 'opened' + params[:group_id] = group.id + params[:milestone_title] = params.delete(:milestone) + params[:label_name] = params.delete(:labels) + params[:sort] = "#{params.delete(:order_by)}_#{params.delete(:sort)}" if params[:order_by] && params[:sort] + + issues = IssuesFinder.new(current_user, params).execute + + present paginate(issues), with: Entities::Issue, current_user: current_user + end + end + resource :projects do # Get a list of project issues # diff --git a/lib/api/licenses.rb b/lib/api/license_templates.rb index be0e113fbcb..d0552299ed0 100644 --- a/lib/api/licenses.rb +++ b/lib/api/license_templates.rb @@ -1,6 +1,6 @@ module API - # Licenses API - class Licenses < Grape::API + # License Templates API + class LicenseTemplates < Grape::API PROJECT_TEMPLATE_REGEX = /[\<\{\[] (project|description| diff --git a/lib/banzai/filter/image_link_filter.rb b/lib/banzai/filter/image_link_filter.rb index ccd106860bd..8aa6f8f124a 100644 --- a/lib/banzai/filter/image_link_filter.rb +++ b/lib/banzai/filter/image_link_filter.rb @@ -9,6 +9,11 @@ module Banzai def call doc.xpath('descendant-or-self::img[not(ancestor::a)]').each do |img| + div = doc.document.create_element( + 'div', + class: 'image-container' + ) + link = doc.document.create_element( 'a', class: 'no-attachment-icon', @@ -17,7 +22,10 @@ module Banzai ) link.children = img.clone - img.replace(link) + + div.children = link + + img.replace(div) end doc diff --git a/lib/banzai/filter/issue_reference_filter.rb b/lib/banzai/filter/issue_reference_filter.rb index 2614261f9eb..5351272f42d 100644 --- a/lib/banzai/filter/issue_reference_filter.rb +++ b/lib/banzai/filter/issue_reference_filter.rb @@ -31,10 +31,14 @@ module Banzai projects_per_reference.each do |path, project| issue_ids = references_per_project[path] - next unless project.default_issues_tracker? + if project.default_issues_tracker? + issues = project.issues.where(iid: issue_ids.to_a) + else + issues = issue_ids.map { |id| ExternalIssue.new(id, project) } + end - project.issues.where(iid: issue_ids.to_a).each do |issue| - hash[project][issue.iid] = issue + issues.each do |issue| + hash[project][issue.iid.to_i] = issue end end diff --git a/lib/banzai/filter/redactor_filter.rb b/lib/banzai/filter/redactor_filter.rb index c753a84a20d..c59a80dd1c7 100644 --- a/lib/banzai/filter/redactor_filter.rb +++ b/lib/banzai/filter/redactor_filter.rb @@ -7,40 +7,13 @@ module Banzai # class RedactorFilter < HTML::Pipeline::Filter def call - nodes = Querying.css(doc, 'a.gfm[data-reference-type]') - visible = nodes_visible_to_user(nodes) - - nodes.each do |node| - unless visible.include?(node) - # The reference should be replaced by the original text, - # which is not always the same as the rendered text. - text = node.attr('data-original') || node.text - node.replace(text) - end - end + Redactor.new(project, current_user).redact([doc]) doc end private - def nodes_visible_to_user(nodes) - per_type = Hash.new { |h, k| h[k] = [] } - visible = Set.new - - nodes.each do |node| - per_type[node.attr('data-reference-type')] << node - end - - per_type.each do |type, nodes| - parser = Banzai::ReferenceParser[type].new(project, current_user) - - visible.merge(parser.nodes_visible_to_user(current_user, nodes)) - end - - visible - end - def current_user context[:current_user] end diff --git a/lib/banzai/note_renderer.rb b/lib/banzai/note_renderer.rb new file mode 100644 index 00000000000..bab6a9934d1 --- /dev/null +++ b/lib/banzai/note_renderer.rb @@ -0,0 +1,22 @@ +module Banzai + module NoteRenderer + # Renders a collection of Note instances. + # + # notes - The notes to render. + # project - The project to use for rendering/redacting. + # user - The user viewing the notes. + # path - The request path. + # wiki - The project's wiki. + # git_ref - The current Git reference. + def self.render(notes, project, user = nil, path = nil, wiki = nil, git_ref = nil) + renderer = ObjectRenderer.new(project, + user, + requested_path: path, + project_wiki: wiki, + ref: git_ref, + pipeline: :note) + + renderer.render(notes, :note) + end + end +end diff --git a/lib/banzai/object_renderer.rb b/lib/banzai/object_renderer.rb new file mode 100644 index 00000000000..f0e4f28bf12 --- /dev/null +++ b/lib/banzai/object_renderer.rb @@ -0,0 +1,85 @@ +module Banzai + # Class for rendering multiple objects (e.g. Note instances) in a single pass. + # + # Rendered Markdown is stored in an attribute in every object based on the + # name of the attribute containing the Markdown. For example, when the + # attribute `note` is rendered the HTML is stored in `note_html`. + class ObjectRenderer + attr_reader :project, :user + + # Make sure to set the appropriate pipeline in the `raw_context` attribute + # (e.g. `:note` for Note instances). + # + # project - A Project to use for rendering and redacting Markdown. + # user - The user viewing the Markdown/HTML documents, if any. + # context - A Hash containing extra attributes to use in the rendering + # pipeline. + def initialize(project, user = nil, raw_context = {}) + @project = project + @user = user + @raw_context = raw_context + end + + # Renders and redacts an Array of objects. + # + # objects - The objects to render + # attribute - The attribute containing the raw Markdown to render. + # + # Returns the same input objects. + def render(objects, attribute) + documents = render_objects(objects, attribute) + redacted = redact_documents(documents) + + objects.each_with_index do |object, index| + object.__send__("#{attribute}_html=", redacted.fetch(index)) + end + + objects + end + + # Renders the attribute of every given object. + def render_objects(objects, attribute) + objects.map do |object| + render_attribute(object, attribute) + end + end + + # Redacts the list of documents. + # + # Returns an Array containing the redacted documents. + def redact_documents(documents) + redactor = Redactor.new(project, user) + + redactor.redact(documents).map do |document| + document.to_html.html_safe + end + end + + # Returns a Banzai context for the given object and attribute. + def context_for(object, attribute) + context = base_context.merge(cache_key: [object, attribute]) + + if object.respond_to?(:author) + context[:author] = object.author + end + + context + end + + # Renders the attribute of an object. + # + # Returns a `Nokogiri::HTML::Document`. + def render_attribute(object, attribute) + context = context_for(object, attribute) + + string = object.__send__(attribute) + html = Banzai.render(string, context) + + Banzai::Pipeline[:relative_link].to_document(html, context) + end + + def base_context + @base_context ||= @raw_context.merge(current_user: user, project: project) + end + end +end diff --git a/lib/banzai/pipeline/relative_link_pipeline.rb b/lib/banzai/pipeline/relative_link_pipeline.rb new file mode 100644 index 00000000000..270990e7ab4 --- /dev/null +++ b/lib/banzai/pipeline/relative_link_pipeline.rb @@ -0,0 +1,11 @@ +module Banzai + module Pipeline + class RelativeLinkPipeline < BasePipeline + def self.filters + FilterArray[ + Filter::RelativeLinkFilter + ] + end + end + end +end diff --git a/lib/banzai/redactor.rb b/lib/banzai/redactor.rb new file mode 100644 index 00000000000..ffd267d5e9a --- /dev/null +++ b/lib/banzai/redactor.rb @@ -0,0 +1,69 @@ +module Banzai + # Class for removing Markdown references a certain user is not allowed to + # view. + class Redactor + attr_reader :user, :project + + # project - A Project to use for redacting links. + # user - The currently logged in user (if any). + def initialize(project, user = nil) + @project = project + @user = user + end + + # Redacts the references in the given Array of documents. + # + # This method modifies the given documents in-place. + # + # documents - A list of HTML documents containing references to redact. + # + # Returns the documents passed as the first argument. + def redact(documents) + nodes = documents.flat_map do |document| + Querying.css(document, 'a.gfm[data-reference-type]') + end + + redact_nodes(nodes) + + documents + end + + # Redacts the given nodes + # + # nodes - An Array of HTML nodes to redact. + def redact_nodes(nodes) + visible = nodes_visible_to_user(nodes) + + nodes.each do |node| + unless visible.include?(node) + # The reference should be replaced by the original text, + # which is not always the same as the rendered text. + text = node.attr('data-original') || node.text + node.replace(text) + end + end + end + + # Returns the nodes visible to the current user. + # + # nodes - The input nodes to check. + # + # Returns a new Array containing the visible nodes. + def nodes_visible_to_user(nodes) + per_type = Hash.new { |h, k| h[k] = [] } + visible = Set.new + + nodes.each do |node| + per_type[node.attr('data-reference-type')] << node + end + + per_type.each do |type, nodes| + parser = Banzai::ReferenceParser[type].new(project, user) + + visible.merge(parser.nodes_visible_to_user(user, nodes)) + end + + visible + end + end +end diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index ed86de819eb..c52d4d63382 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -2,7 +2,7 @@ module Ci class GitlabCiYamlProcessor class ValidationError < StandardError; end - include Gitlab::Ci::Config::Node::ValidationHelpers + include Gitlab::Ci::Config::Node::LegacyValidationHelpers DEFAULT_STAGES = %w(build test deploy) DEFAULT_STAGE = 'test' diff --git a/lib/gitlab/blame.rb b/lib/gitlab/blame.rb index 997a22779a0..d62bc50ce78 100644 --- a/lib/gitlab/blame.rb +++ b/lib/gitlab/blame.rb @@ -41,7 +41,8 @@ module Gitlab def highlighted_lines @blob.load_all_data!(repository) - @highlighted_lines ||= Gitlab::Highlight.highlight(@blob.name, @blob.data).lines + @highlighted_lines ||= + Gitlab::Highlight.highlight(@blob.path, @blob.data, repository: repository).lines end def project diff --git a/lib/gitlab/ci/config.rb b/lib/gitlab/ci/config.rb index b48d3592f16..adfd097736e 100644 --- a/lib/gitlab/ci/config.rb +++ b/lib/gitlab/ci/config.rb @@ -4,8 +4,6 @@ module Gitlab # Base GitLab CI Configuration facade # class Config - delegate :valid?, :errors, to: :@global - ## # Temporary delegations that should be removed after refactoring # @@ -18,6 +16,14 @@ module Gitlab @global.process! end + def valid? + @global.valid? + end + + def errors + @global.errors + end + def to_hash @config end diff --git a/lib/gitlab/ci/config/node/configurable.rb b/lib/gitlab/ci/config/node/configurable.rb index d60f87f3f94..374ff71d0f5 100644 --- a/lib/gitlab/ci/config/node/configurable.rb +++ b/lib/gitlab/ci/config/node/configurable.rb @@ -15,27 +15,24 @@ module Gitlab # module Configurable extend ActiveSupport::Concern + include Validatable - def allowed_nodes - self.class.allowed_nodes || {} + included do + validations do + validates :config, hash: true + end end private - def prevalidate! - unless @value.is_a?(Hash) - @errors << 'should be a configuration entry with hash value' - end - end - def create_node(key, factory) - factory.with(value: @value[key]) - factory.nullify! unless @value.has_key?(key) + factory.with(value: @config[key], key: key) + factory.nullify! unless @config.has_key?(key) factory.create! end class_methods do - def allowed_nodes + def nodes Hash[@allowed_nodes.map { |key, factory| [key, factory.dup] }] end @@ -47,7 +44,6 @@ module Gitlab define_method(symbol) do raise Entry::InvalidError unless valid? - @nodes[symbol].try(:value) end diff --git a/lib/gitlab/ci/config/node/entry.rb b/lib/gitlab/ci/config/node/entry.rb index 52758a962f3..f044ef965e9 100644 --- a/lib/gitlab/ci/config/node/entry.rb +++ b/lib/gitlab/ci/config/node/entry.rb @@ -8,14 +8,14 @@ module Gitlab class Entry class InvalidError < StandardError; end - attr_accessor :description + attr_reader :config + attr_accessor :key, :description - def initialize(value) - @value = value + def initialize(config) + @config = config @nodes = {} - @errors = [] - - prevalidate! + @validator = self.class.validator.new(self) + @validator.validate end def process! @@ -23,50 +23,54 @@ module Gitlab return unless valid? compose! - - nodes.each(&:process!) - nodes.each(&:validate!) + process_nodes! end def nodes @nodes.values end - def valid? - errors.none? - end - def leaf? - allowed_nodes.none? + self.class.nodes.none? end - def errors - @errors + nodes.map(&:errors).flatten + def key + @key || self.class.name.demodulize.underscore end - def allowed_nodes - {} + def valid? + errors.none? end - def validate! - raise NotImplementedError + def errors + @validator.full_errors + + nodes.map(&:errors).flatten end def value raise NotImplementedError end - private + def self.nodes + {} + end - def prevalidate! + def self.validator + Validator end + private + def compose! - allowed_nodes.each do |key, essence| + self.class.nodes.each do |key, essence| @nodes[key] = create_node(key, essence) end end + def process_nodes! + nodes.each(&:process!) + end + def create_node(key, essence) raise NotImplementedError end diff --git a/lib/gitlab/ci/config/node/factory.rb b/lib/gitlab/ci/config/node/factory.rb index 787ca006f5a..025ae40ef94 100644 --- a/lib/gitlab/ci/config/node/factory.rb +++ b/lib/gitlab/ci/config/node/factory.rb @@ -30,6 +30,7 @@ module Gitlab @entry_class.new(@attributes[:value]).tap do |entry| entry.description = @attributes[:description] + entry.key = @attributes[:key] end end end diff --git a/lib/gitlab/ci/config/node/validation_helpers.rb b/lib/gitlab/ci/config/node/legacy_validation_helpers.rb index 72f648975dc..4d9a508796a 100644 --- a/lib/gitlab/ci/config/node/validation_helpers.rb +++ b/lib/gitlab/ci/config/node/legacy_validation_helpers.rb @@ -2,7 +2,7 @@ module Gitlab module Ci class Config module Node - module ValidationHelpers + module LegacyValidationHelpers private def validate_duration(value) diff --git a/lib/gitlab/ci/config/node/script.rb b/lib/gitlab/ci/config/node/script.rb index 5072bf0db7d..c044f5c5e71 100644 --- a/lib/gitlab/ci/config/node/script.rb +++ b/lib/gitlab/ci/config/node/script.rb @@ -11,16 +11,14 @@ module Gitlab # implementation in Runner. # class Script < Entry - include ValidationHelpers + include Validatable - def value - @value.join("\n") + validations do + validates :config, array_of_strings: true end - def validate! - unless validate_array_of_strings(@value) - @errors << 'before_script should be an array of strings' - end + def value + @config.join("\n") end end end diff --git a/lib/gitlab/ci/config/node/validatable.rb b/lib/gitlab/ci/config/node/validatable.rb new file mode 100644 index 00000000000..f6e2896dfb2 --- /dev/null +++ b/lib/gitlab/ci/config/node/validatable.rb @@ -0,0 +1,29 @@ +module Gitlab + module Ci + class Config + module Node + module Validatable + extend ActiveSupport::Concern + + class_methods do + def validator + validator = Class.new(Node::Validator) + + if defined?(@validations) + @validations.each { |rules| validator.class_eval(&rules) } + end + + validator + end + + private + + def validations(&block) + (@validations ||= []).append(block) + end + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/node/validator.rb b/lib/gitlab/ci/config/node/validator.rb new file mode 100644 index 00000000000..02edc9219c3 --- /dev/null +++ b/lib/gitlab/ci/config/node/validator.rb @@ -0,0 +1,27 @@ +module Gitlab + module Ci + class Config + module Node + class Validator < SimpleDelegator + include ActiveModel::Validations + include Node::Validators + + def initialize(node) + super(node) + @node = node + end + + def full_errors + errors.full_messages.map do |error| + "#{@node.key} #{error}".humanize + end + end + + def self.name + 'Validator' + end + end + end + end + end +end diff --git a/lib/gitlab/ci/config/node/validators.rb b/lib/gitlab/ci/config/node/validators.rb new file mode 100644 index 00000000000..dc9cdb9a220 --- /dev/null +++ b/lib/gitlab/ci/config/node/validators.rb @@ -0,0 +1,27 @@ +module Gitlab + module Ci + class Config + module Node + module Validators + class ArrayOfStringsValidator < ActiveModel::EachValidator + include LegacyValidationHelpers + + def validate_each(record, attribute, value) + unless validate_array_of_strings(value) + record.errors.add(attribute, 'should be an array of strings') + end + end + end + + class HashValidator < ActiveModel::EachValidator + def validate_each(record, attribute, value) + unless value.is_a?(Hash) + record.errors.add(attribute, 'should be a configuration entry hash') + end + end + end + end + end + end + end +end diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb index f751a3a12fd..d4f12cb1df9 100644 --- a/lib/gitlab/gon_helper.rb +++ b/lib/gitlab/gon_helper.rb @@ -3,7 +3,6 @@ module Gitlab def add_gon_variables gon.api_version = API::API.version gon.default_avatar_url = URI::join(Gitlab.config.gitlab.url, ActionController::Base.helpers.image_path('no_avatar.png')).to_s - gon.default_issues_tracker = Project.new.default_issue_tracker.to_param gon.max_file_size = current_application_settings.max_attachment_size gon.relative_url_root = Gitlab.config.gitlab.relative_url_root gon.shortcuts_path = help_shortcuts_path diff --git a/lib/gitlab/highlight.rb b/lib/gitlab/highlight.rb index 280120b0f9e..41296415e35 100644 --- a/lib/gitlab/highlight.rb +++ b/lib/gitlab/highlight.rb @@ -1,7 +1,7 @@ module Gitlab class Highlight - def self.highlight(blob_name, blob_content, nowrap: true, plain: false) - new(blob_name, blob_content, nowrap: nowrap). + def self.highlight(blob_name, blob_content, repository: nil, nowrap: true, plain: false) + new(blob_name, blob_content, nowrap: nowrap, repository: repository). highlight(blob_content, continue: false, plain: plain) end @@ -10,12 +10,21 @@ module Gitlab return [] unless blob blob.load_all_data!(repository) - highlight(file_name, blob.data).lines.map!(&:html_safe) + highlight(file_name, blob.data, repository: repository).lines.map!(&:html_safe) end - def initialize(blob_name, blob_content, nowrap: true) + attr_reader :lexer + def initialize(blob_name, blob_content, repository: nil, nowrap: true) + @blob_name = blob_name + @blob_content = blob_content + @repository = repository @formatter = rouge_formatter(nowrap: nowrap) - @lexer = Rouge::Lexer.guess(filename: blob_name, source: blob_content).new rescue Rouge::Lexers::PlainText + + @lexer = custom_language || begin + Rouge::Lexer.guess(filename: blob_name, source: blob_content).new + rescue Rouge::Lexer::AmbiguousGuess => e + e.alternatives.sort_by(&:tag).first + end end def highlight(text, continue: true, plain: false) @@ -30,6 +39,14 @@ module Gitlab private + def custom_language + language_name = @repository && @repository.gitattribute(@blob_name, 'gitlab-language') + + return nil unless language_name + + Rouge::Lexer.find_fancy(language_name) + end + def rouge_formatter(options = {}) options = options.reverse_merge( nowrap: true, diff --git a/lib/gitlab/import_export/file_importer.rb b/lib/gitlab/import_export/file_importer.rb index 0e70d9282d5..82d1e1805c5 100644 --- a/lib/gitlab/import_export/file_importer.rb +++ b/lib/gitlab/import_export/file_importer.rb @@ -23,7 +23,11 @@ module Gitlab private def decompress_archive - untar_zxf(archive: @archive_file, dir: @shared.export_path) + result = untar_zxf(archive: @archive_file, dir: @shared.export_path) + + raise Projects::ImportService::Error.new("Unable to decompress #{@archive_file} into #{@shared.export_path}") unless result + + true end end end diff --git a/lib/gitlab/import_export/importer.rb b/lib/gitlab/import_export/importer.rb index d209e04f7be..595b20a09bd 100644 --- a/lib/gitlab/import_export/importer.rb +++ b/lib/gitlab/import_export/importer.rb @@ -10,17 +10,22 @@ module Gitlab end def execute - Gitlab::ImportExport::FileImporter.import(archive_file: @archive_file, - shared: @shared) - if check_version! && [project_tree, repo_restorer, wiki_restorer, uploads_restorer].all?(&:restore) + if import_file && check_version! && [project_tree, repo_restorer, wiki_restorer, uploads_restorer].all?(&:restore) project_tree.restored_project else raise Projects::ImportService::Error.new(@shared.errors.join(', ')) end + + remove_import_file end private + def import_file + Gitlab::ImportExport::FileImporter.import(archive_file: @archive_file, + shared: @shared) + end + def check_version! Gitlab::ImportExport::VersionChecker.check!(shared: @shared) end @@ -59,6 +64,10 @@ module Gitlab def wiki_repo_path File.join(@shared.export_path, 'project.wiki.bundle') end + + def remove_import_file + FileUtils.rm_rf(@archive_file) + end end end end diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb index b872780f20a..92bf7e0a2fc 100644 --- a/lib/gitlab/import_export/relation_factory.rb +++ b/lib/gitlab/import_export/relation_factory.rb @@ -12,6 +12,8 @@ module Gitlab USER_REFERENCES = %w[author_id assignee_id updated_by_id user_id].freeze + BUILD_MODELS = %w[Ci::Build commit_status].freeze + def self.create(*args) new(*args).create end @@ -70,7 +72,7 @@ module Gitlab end def generate_imported_object - if @relation_sym == 'commit_status' # call #trace= method after assigning the other attributes + if BUILD_MODELS.include?(@relation_name) # call #trace= method after assigning the other attributes trace = @relation_hash.delete('trace') imported_object do |object| object.trace = trace diff --git a/lib/gitlab/metrics/method_call.rb b/lib/gitlab/metrics/method_call.rb index faf0d9b6318..c048fe20ba7 100644 --- a/lib/gitlab/metrics/method_call.rb +++ b/lib/gitlab/metrics/method_call.rb @@ -18,12 +18,12 @@ module Gitlab # Measures the real and CPU execution time of the supplied block. def measure - start_real = Time.now + start_real = System.monotonic_time start_cpu = System.cpu_time retval = yield - @real_time += (Time.now - start_real) * 1000.0 - @cpu_time += System.cpu_time.to_f - start_cpu + @real_time += System.monotonic_time - start_real + @cpu_time += System.cpu_time - start_cpu @call_count += 1 retval diff --git a/lib/gitlab/metrics/metric.rb b/lib/gitlab/metrics/metric.rb index 1cd1ca30f70..f23d67e1e38 100644 --- a/lib/gitlab/metrics/metric.rb +++ b/lib/gitlab/metrics/metric.rb @@ -4,16 +4,15 @@ module Gitlab class Metric JITTER_RANGE = 0.000001..0.001 - attr_reader :series, :values, :tags, :created_at + attr_reader :series, :values, :tags # series - The name of the series (as a String) to store the metric in. # values - A Hash containing the values to store. # tags - A Hash containing extra tags to add to the metrics. def initialize(series, values, tags = {}) - @values = values - @series = series - @tags = tags - @created_at = Time.now.utc + @values = values + @series = series + @tags = tags end # Returns a Hash in a format that can be directly written to InfluxDB. @@ -27,20 +26,20 @@ module Gitlab # # Due to the way InfluxDB is set up there's no solution to this problem, # all we can do is lower the amount of collisions. We do this by using - # Time#to_f which returns the seconds as a Float providing greater - # accuracy. We then add a small random value that is large enough to - # distinguish most timestamps but small enough to not alter the amount - # of seconds. + # System.real_time which returns the nanoseconds as a Float providing + # greater accuracy. We then add a small random value that is large + # enough to distinguish most timestamps but small enough to not alter + # the timestamp significantly. # # See https://gitlab.com/gitlab-com/operations/issues/175 for more # information. - time = @created_at.to_f + rand(JITTER_RANGE) + time = System.real_time(:nanosecond) + rand(JITTER_RANGE) { series: @series, tags: @tags, values: @values, - timestamp: (time * 1_000_000_000).to_i + timestamp: time.to_i } end end diff --git a/lib/gitlab/metrics/sidekiq_middleware.rb b/lib/gitlab/metrics/sidekiq_middleware.rb index fd98aa3412e..a1240fd33ee 100644 --- a/lib/gitlab/metrics/sidekiq_middleware.rb +++ b/lib/gitlab/metrics/sidekiq_middleware.rb @@ -8,6 +8,8 @@ module Gitlab trans = Transaction.new("#{worker.class.name}#perform") begin + # Old gitlad-shell messages don't provide enqueued_at/created_at attributes + trans.set(:sidekiq_queue_duration, Time.now.to_f - (message['enqueued_at'] || message['created_at'] || 0)) trans.run { yield } ensure trans.finish diff --git a/lib/gitlab/metrics/system.rb b/lib/gitlab/metrics/system.rb index a7d183b2f94..82c18bb108b 100644 --- a/lib/gitlab/metrics/system.rb +++ b/lib/gitlab/metrics/system.rb @@ -34,13 +34,29 @@ module Gitlab # THREAD_CPUTIME is not supported on OS X if Process.const_defined?(:CLOCK_THREAD_CPUTIME_ID) def self.cpu_time - Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :millisecond) + Process. + clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :millisecond).to_f end else def self.cpu_time - Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID, :millisecond) + Process. + clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID, :millisecond).to_f end end + + # Returns the current real time in a given precision. + # + # Returns the time as a Float. + def self.real_time(precision = :millisecond) + Process.clock_gettime(Process::CLOCK_REALTIME, precision).to_f + end + + # Returns the current monotonic clock time in a given precision. + # + # Returns the time as a Float. + def self.monotonic_time(precision = :millisecond) + Process.clock_gettime(Process::CLOCK_MONOTONIC, precision).to_f + end end end end diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb index 4bc5081aa03..bded245da43 100644 --- a/lib/gitlab/metrics/transaction.rb +++ b/lib/gitlab/metrics/transaction.rb @@ -30,7 +30,7 @@ module Gitlab end def duration - @finished_at ? (@finished_at - @started_at) * 1000.0 : 0.0 + @finished_at ? (@finished_at - @started_at) : 0.0 end def allocated_memory @@ -41,12 +41,12 @@ module Gitlab Thread.current[THREAD_KEY] = self @memory_before = System.memory_usage - @started_at = Time.now + @started_at = System.monotonic_time yield ensure @memory_after = System.memory_usage - @finished_at = Time.now + @finished_at = System.monotonic_time Thread.current[THREAD_KEY] = nil end diff --git a/lib/gitlab/o_auth/user.rb b/lib/gitlab/o_auth/user.rb index 78f3ecb4cb4..7af75a9cc4c 100644 --- a/lib/gitlab/o_auth/user.rb +++ b/lib/gitlab/o_auth/user.rb @@ -74,7 +74,7 @@ module Gitlab if user # Case when a LDAP user already exists in Gitlab. Add the OAuth identity to existing account. log.info "LDAP account found for user #{user.username}. Building new #{auth_hash.provider} identity." - user.identities.build(extern_uid: auth_hash.uid, provider: auth_hash.provider) + user.identities.find_or_initialize_by(extern_uid: auth_hash.uid, provider: auth_hash.provider) else log.info "No existing LDAP account was found in GitLab. Checking for #{auth_hash.provider} account." user = find_by_uid_and_provider diff --git a/lib/gitlab/sidekiq_middleware/memory_killer.rb b/lib/gitlab/sidekiq_middleware/memory_killer.rb index ae85b294d31..4831c46c4be 100644 --- a/lib/gitlab/sidekiq_middleware/memory_killer.rb +++ b/lib/gitlab/sidekiq_middleware/memory_killer.rb @@ -25,7 +25,7 @@ module Gitlab Sidekiq.logger.warn "current RSS #{current_rss} exceeds maximum RSS "\ "#{MAX_RSS}" - Sidekiq.logger.warn "this thread will shut down PID #{Process.pid} "\ + Sidekiq.logger.warn "this thread will shut down PID #{Process.pid} - Worker #{worker.class} - JID-#{job['jid']}"\ "in #{GRACE_TIME} seconds" sleep(GRACE_TIME) @@ -36,7 +36,7 @@ module Gitlab "#{SHUTDOWN_SIGNAL} to PID #{Process.pid}" sleep(SHUTDOWN_WAIT) - Sidekiq.logger.warn "sending #{SHUTDOWN_SIGNAL} to PID #{Process.pid}" + Sidekiq.logger.warn "sending #{SHUTDOWN_SIGNAL} to PID #{Process.pid} - Worker #{worker.class} - JID-#{job['jid']}" Process.kill(SHUTDOWN_SIGNAL, Process.pid) end end diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb index 40e8299c36b..ef1241f8600 100644 --- a/lib/gitlab/workhorse.rb +++ b/lib/gitlab/workhorse.rb @@ -52,6 +52,19 @@ module Gitlab ] end + def send_git_patch(repository, from, to) + params = { + 'RepoPath' => repository.path_to_repo, + 'ShaFrom' => from, + 'ShaTo' => to + } + + [ + SEND_DATA_HEADER, + "git-format-patch:#{encode(params)}" + ] + end + protected def encode(hash) diff --git a/spec/controllers/admin/impersonations_controller_spec.rb b/spec/controllers/admin/impersonations_controller_spec.rb index eb82476b179..d5f0b289b5b 100644 --- a/spec/controllers/admin/impersonations_controller_spec.rb +++ b/spec/controllers/admin/impersonations_controller_spec.rb @@ -22,7 +22,7 @@ describe Admin::ImpersonationsController do it "responds with status 404" do delete :destroy - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "doesn't sign us in" do @@ -46,7 +46,7 @@ describe Admin::ImpersonationsController do it "responds with status 404" do delete :destroy - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "doesn't sign us in as the impersonator" do @@ -65,7 +65,7 @@ describe Admin::ImpersonationsController do it "responds with status 404" do delete :destroy - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "doesn't sign us in as the impersonator" do diff --git a/spec/controllers/admin/spam_logs_controller_spec.rb b/spec/controllers/admin/spam_logs_controller_spec.rb index b51b303a714..520a4f6f9c5 100644 --- a/spec/controllers/admin/spam_logs_controller_spec.rb +++ b/spec/controllers/admin/spam_logs_controller_spec.rb @@ -14,7 +14,7 @@ describe Admin::SpamLogsController do it 'lists all spam logs' do get :index - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -22,14 +22,14 @@ describe Admin::SpamLogsController do it 'removes only the spam log when removing log' do expect { delete :destroy, id: first_spam.id }.to change { SpamLog.count }.by(-1) expect(User.find(user.id)).to be_truthy - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'removes user and his spam logs when removing the user' do delete :destroy, id: first_spam.id, remove_user: true expect(flash[:notice]).to eq "User #{user.username} was successfully removed." - expect(response.status).to eq(302) + expect(response).to have_http_status(302) expect(SpamLog.count).to eq(0) expect { User.find(user.id) }.to raise_error(ActiveRecord::RecordNotFound) end diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb index 6caf37ddc2c..ab9aa65f7b9 100644 --- a/spec/controllers/admin/users_controller_spec.rb +++ b/spec/controllers/admin/users_controller_spec.rb @@ -17,7 +17,7 @@ describe Admin::UsersController do it 'deletes user' do delete :destroy, id: user.username, format: :json - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect { User.find(user.id) }.to raise_exception(ActiveRecord::RecordNotFound) end end diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index ff5b3916273..10824c20c87 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -44,7 +44,7 @@ describe ApplicationController do context "when the 'private_token' param is populated with the private token" do it "logs the user in" do get :index, private_token: user.private_token - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(response.body).to eq("authenticated") end end @@ -54,7 +54,7 @@ describe ApplicationController do it "logs the user in" do @request.headers['PRIVATE-TOKEN'] = user.private_token get :index - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(response.body).to eq("authenticated") end end @@ -80,7 +80,7 @@ describe ApplicationController do context "when the 'personal_access_token' param is populated with the personal access token" do it "logs the user in" do get :index, private_token: personal_access_token.token - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(response.body).to eq('authenticated') end end @@ -89,7 +89,7 @@ describe ApplicationController do it "logs the user in" do @request.headers["PRIVATE-TOKEN"] = personal_access_token.token get :index - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(response.body).to eq('authenticated') end end diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb index 28cf804c1b2..60c654f622d 100644 --- a/spec/controllers/autocomplete_controller_spec.rb +++ b/spec/controllers/autocomplete_controller_spec.rb @@ -29,7 +29,7 @@ describe AutocompleteController do get(:users, project_id: 'unknown') end - it { expect(response.status).to eq(404) } + it { expect(response).to have_http_status(404) } end end @@ -58,7 +58,7 @@ describe AutocompleteController do get(:users, group_id: 'unknown') end - it { expect(response.status).to eq(404) } + it { expect(response).to have_http_status(404) } end end @@ -114,7 +114,7 @@ describe AutocompleteController do get(:users, project_id: project.id) end - it { expect(response.status).to eq(404) } + it { expect(response).to have_http_status(404) } end describe 'GET #users with unknown project' do @@ -122,7 +122,7 @@ describe AutocompleteController do get(:users, project_id: 'unknown') end - it { expect(response.status).to eq(404) } + it { expect(response).to have_http_status(404) } end describe 'GET #users with inaccessible group' do @@ -131,7 +131,7 @@ describe AutocompleteController do get(:users, group_id: user.namespace.id) end - it { expect(response.status).to eq(404) } + it { expect(response).to have_http_status(404) } end describe 'GET #users with no project' do diff --git a/spec/controllers/commit_controller_spec.rb b/spec/controllers/commit_controller_spec.rb index cf5c606c723..a3a3309e15e 100644 --- a/spec/controllers/commit_controller_spec.rb +++ b/spec/controllers/commit_controller_spec.rb @@ -155,7 +155,7 @@ describe Projects::CommitController do id: commit.id) expect(response).not_to be_success - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -204,7 +204,7 @@ describe Projects::CommitController do id: master_pickable_commit.id) expect(response).not_to be_success - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end diff --git a/spec/controllers/groups/group_members_controller_spec.rb b/spec/controllers/groups/group_members_controller_spec.rb index c8601341d54..ddc54108a7b 100644 --- a/spec/controllers/groups/group_members_controller_spec.rb +++ b/spec/controllers/groups/group_members_controller_spec.rb @@ -13,7 +13,7 @@ describe Groups::GroupMembersController do it 'renders index with group members' do get :index, group_id: group - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(response).to render_template(:index) end end @@ -26,7 +26,7 @@ describe Groups::GroupMembersController do delete :destroy, group_id: group, id: 42 - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -48,7 +48,7 @@ describe Groups::GroupMembersController do delete :destroy, group_id: group, id: member - expect(response.status).to eq(403) + expect(response).to have_http_status(403) expect(group.users).to include group_user end end @@ -89,7 +89,7 @@ describe Groups::GroupMembersController do it 'returns 403' do delete :leave, group_id: group - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -118,7 +118,7 @@ describe Groups::GroupMembersController do it 'cannot removes himself from the group' do delete :leave, group_id: group - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -166,7 +166,7 @@ describe Groups::GroupMembersController do post :approve_access_request, group_id: group, id: 42 - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -188,7 +188,7 @@ describe Groups::GroupMembersController do post :approve_access_request, group_id: group, id: member - expect(response.status).to eq(403) + expect(response).to have_http_status(403) expect(group.users).not_to include group_requester end end diff --git a/spec/controllers/health_check_controller_spec.rb b/spec/controllers/health_check_controller_spec.rb index 0d8a68bb51a..56ecf2bb644 100644 --- a/spec/controllers/health_check_controller_spec.rb +++ b/spec/controllers/health_check_controller_spec.rb @@ -65,21 +65,21 @@ describe HealthCheckController do it 'supports passing the token in the header' do request.headers['TOKEN'] = token get :index - expect(response.status).to eq(500) + expect(response).to have_http_status(500) expect(response.content_type).to eq 'text/plain' expect(response.body).to include('The server is on fire') end it 'supports failure plaintest response' do get :index, token: token - expect(response.status).to eq(500) + expect(response).to have_http_status(500) expect(response.content_type).to eq 'text/plain' expect(response.body).to include('The server is on fire') end it 'supports failure json response' do get :index, token: token, format: :json - expect(response.status).to eq(500) + expect(response).to have_http_status(500) expect(response.content_type).to eq 'application/json' expect(json_response['healthy']).to be false expect(json_response['message']).to include('The server is on fire') @@ -87,7 +87,7 @@ describe HealthCheckController do it 'supports failure xml response' do get :index, token: token, format: :xml - expect(response.status).to eq(500) + expect(response).to have_http_status(500) expect(response.content_type).to eq 'application/xml' expect(xml_response['healthy']).to be false expect(xml_response['message']).to include('The server is on fire') @@ -95,7 +95,7 @@ describe HealthCheckController do it 'supports failure responses for specific checks' do get :index, token: token, checks: 'email', format: :json - expect(response.status).to eq(500) + expect(response).to have_http_status(500) expect(response.content_type).to eq 'application/json' expect(json_response['healthy']).to be false expect(json_response['message']).to include('Email is on fire') diff --git a/spec/controllers/invites_controller_spec.rb b/spec/controllers/invites_controller_spec.rb index 3c6e54839b5..e478a253b3f 100644 --- a/spec/controllers/invites_controller_spec.rb +++ b/spec/controllers/invites_controller_spec.rb @@ -15,7 +15,7 @@ describe InvitesController do get :accept, id: token member.reload - expect(response.status).to eq(302) + expect(response).to have_http_status(302) expect(member.user).to eq(user) expect(flash[:notice]).to include 'You have been granted' end @@ -26,7 +26,7 @@ describe InvitesController do get :decline, id: token expect{member.reload}.to raise_error ActiveRecord::RecordNotFound - expect(response.status).to eq(302) + expect(response).to have_http_status(302) expect(flash[:notice]).to include 'You have declined the invitation to join' end end diff --git a/spec/controllers/namespaces_controller_spec.rb b/spec/controllers/namespaces_controller_spec.rb index 27e9afe582e..2b334ed1172 100644 --- a/spec/controllers/namespaces_controller_spec.rb +++ b/spec/controllers/namespaces_controller_spec.rb @@ -86,7 +86,7 @@ describe NamespacesController do it "responds with status 404" do get :show, id: group.path - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -102,7 +102,7 @@ describe NamespacesController do it "responds with status 404" do get :show, id: "doesntexist" - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end diff --git a/spec/controllers/notification_settings_controller_spec.rb b/spec/controllers/notification_settings_controller_spec.rb index 07734a2dc19..79b819a1377 100644 --- a/spec/controllers/notification_settings_controller_spec.rb +++ b/spec/controllers/notification_settings_controller_spec.rb @@ -101,7 +101,7 @@ describe NotificationSettingsController do project_id: private_project.id, notification_setting: { level: :participating } - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -159,7 +159,7 @@ describe NotificationSettingsController do id: notification_setting, notification_setting: { level: :participating } - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end diff --git a/spec/controllers/oauth/applications_controller_spec.rb b/spec/controllers/oauth/applications_controller_spec.rb index af378304893..552899eb36c 100644 --- a/spec/controllers/oauth/applications_controller_spec.rb +++ b/spec/controllers/oauth/applications_controller_spec.rb @@ -12,7 +12,7 @@ describe Oauth::ApplicationsController do it 'shows list of applications' do get :index - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'redirects back to profile page if OAuth applications are disabled' do @@ -21,7 +21,7 @@ describe Oauth::ApplicationsController do get :index - expect(response.status).to eq(302) + expect(response).to have_http_status(302) expect(response).to redirect_to(profile_path) end end diff --git a/spec/controllers/profiles/accounts_controller_spec.rb b/spec/controllers/profiles/accounts_controller_spec.rb index 4eafc11abaa..2dc9adfd60c 100644 --- a/spec/controllers/profiles/accounts_controller_spec.rb +++ b/spec/controllers/profiles/accounts_controller_spec.rb @@ -13,7 +13,7 @@ describe Profiles::AccountsController do delete :unlink, provider: 'saml' updated_user = User.find(user.id) - expect(response.status).to eq(302) + expect(response).to have_http_status(302) expect(updated_user.identities.size).to eq(1) expect(updated_user.identities).to include(identity) end diff --git a/spec/controllers/projects/blob_controller_spec.rb b/spec/controllers/projects/blob_controller_spec.rb new file mode 100644 index 00000000000..9444a50b1ce --- /dev/null +++ b/spec/controllers/projects/blob_controller_spec.rb @@ -0,0 +1,40 @@ +require 'rails_helper' + +describe Projects::BlobController do + let(:project) { create(:project) } + let(:user) { create(:user) } + + before do + user = create(:user) + project.team << [user, :master] + + sign_in(user) + end + + describe 'GET diff' do + render_views + + def do_get(opts = {}) + params = { namespace_id: project.namespace.to_param, + project_id: project.to_param, + id: 'master/CHANGELOG' } + get :diff, params.merge(opts) + end + + context 'when essential params are missing' do + it 'renders nothing' do + do_get + + expect(response.body).to be_blank + end + end + + context 'when essential params are present' do + it 'renders the diff content' do + do_get(since: 1, to: 5, offset: 10) + + expect(response.body).to be_present + end + end + end +end diff --git a/spec/controllers/projects/branches_controller_spec.rb b/spec/controllers/projects/branches_controller_spec.rb index c4b4a888b4e..f59d4937157 100644 --- a/spec/controllers/projects/branches_controller_spec.rb +++ b/spec/controllers/projects/branches_controller_spec.rb @@ -103,7 +103,7 @@ describe Projects::BranchesController do namespace_id: project.namespace.to_param, project_id: project.to_param - expect(response.status).to eq(303) + expect(response).to have_http_status(303) end end @@ -121,24 +121,24 @@ describe Projects::BranchesController do context "valid branch name, valid source" do let(:branch) { "feature" } - it { expect(response.status).to eq(200) } + it { expect(response).to have_http_status(200) } end context "valid branch name with unencoded slashes" do let(:branch) { "improve/awesome" } - it { expect(response.status).to eq(200) } + it { expect(response).to have_http_status(200) } end context "valid branch name with encoded slashes" do let(:branch) { "improve%2Fawesome" } - it { expect(response.status).to eq(200) } + it { expect(response).to have_http_status(200) } end context "invalid branch name, valid ref" do let(:branch) { "no-branch" } - it { expect(response.status).to eq(404) } + it { expect(response).to have_http_status(404) } end end end diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index cbaa3e0b7b2..7cf09fa4a4a 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -14,7 +14,7 @@ describe Projects::IssuesController do it "returns index" do get :index, namespace_id: project.namespace.path, project_id: project.path - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "return 301 if request path doesn't match project path" do @@ -28,7 +28,7 @@ describe Projects::IssuesController do project.save get :index, namespace_id: project.namespace.path, project_id: project.path - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "returns 404 when external issue tracker is enabled" do @@ -36,7 +36,7 @@ describe Projects::IssuesController do allow(project).to receive(:default_issues_tracker?).and_return(false) get :index, namespace_id: project.namespace.path, project_id: project.path - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -248,7 +248,7 @@ describe Projects::IssuesController do before { sign_in(user) } it "rejects a developer to destroy an issue" do delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: issue.iid - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -262,7 +262,7 @@ describe Projects::IssuesController do it "deletes the issue" do delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: issue.iid - expect(response.status).to eq(302) + expect(response).to have_http_status(302) expect(controller).to set_flash[:notice].to(/The issue was successfully deleted\./).now end end @@ -280,7 +280,7 @@ describe Projects::IssuesController do project_id: project.path, id: issue.iid, name: "thumbsup") end.to change { issue.award_emoji.count }.by(1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index 4b408c03703..74c050f48f1 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -96,26 +96,14 @@ describe Projects::MergeRequestsController do end describe "as patch" do - include_examples "export merge as", :patch - let(:format) { :patch } - - it "should really be a git email patch with commit" do - get(:show, - namespace_id: project.namespace.to_param, - project_id: project.to_param, - id: merge_request.iid, format: format) - - expect(response.body[0..100]).to start_with("From #{merge_request.commits.last.id}") - end - - it "should contain git diffs" do + it 'triggers workhorse to serve the request' do get(:show, namespace_id: project.namespace.to_param, project_id: project.to_param, id: merge_request.iid, - format: format) + format: :patch) - expect(response.body).to match(/^diff --git/) + expect(response.headers['Gitlab-Workhorse-Send-Data']).to start_with("git-format-patch:") end end end @@ -264,6 +252,18 @@ describe Projects::MergeRequestsController do merge_when_build_succeeds end + + context 'when project.only_allow_merge_if_build_succeeds? is true' do + before do + project.update_column(:only_allow_merge_if_build_succeeds, true) + end + + it 'returns :merge_when_build_succeeds' do + merge_when_build_succeeds + + expect(assigns(:status)).to eq(:merge_when_build_succeeds) + end + end end end end @@ -272,7 +272,7 @@ describe Projects::MergeRequestsController do it "denies access to users unless they're admin or project owner" do delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: merge_request.iid - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end context "when the user is owner" do @@ -285,7 +285,7 @@ describe Projects::MergeRequestsController do it "deletes the merge request" do delete :destroy, namespace_id: project.namespace.path, project_id: project.path, id: merge_request.iid - expect(response.status).to eq(302) + expect(response).to have_http_status(302) expect(controller).to set_flash[:notice].to(/The merge request was successfully deleted\./).now end end diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb index 00bc38b6071..75590c1ed4f 100644 --- a/spec/controllers/projects/notes_controller_spec.rb +++ b/spec/controllers/projects/notes_controller_spec.rb @@ -18,7 +18,7 @@ describe Projects::NotesController do project_id: project.path, id: note.id, name: "thumbsup") end.to change { note.award_emoji.count }.by(1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "removes the already awarded emoji" do @@ -30,7 +30,7 @@ describe Projects::NotesController do project_id: project.path, id: note.id, name: "thumbsup") end.to change { AwardEmoji.count }.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb index e5e750c855f..29aaceb2302 100644 --- a/spec/controllers/projects/project_members_controller_spec.rb +++ b/spec/controllers/projects/project_members_controller_spec.rb @@ -58,7 +58,7 @@ describe Projects::ProjectMembersController do get :index, namespace_id: project.namespace, project_id: project end - it { expect(response.status).to eq(200) } + it { expect(response).to have_http_status(200) } end end @@ -71,7 +71,7 @@ describe Projects::ProjectMembersController do project_id: project, id: 42 - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -94,7 +94,7 @@ describe Projects::ProjectMembersController do project_id: project, id: member - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(project.users).to include team_user end end @@ -139,7 +139,7 @@ describe Projects::ProjectMembersController do delete :leave, namespace_id: project.namespace, project_id: project - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -171,7 +171,7 @@ describe Projects::ProjectMembersController do delete :leave, namespace_id: project.namespace, project_id: project - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -224,7 +224,7 @@ describe Projects::ProjectMembersController do project_id: project, id: 42 - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -247,7 +247,7 @@ describe Projects::ProjectMembersController do project_id: project, id: member - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(project.users).not_to include team_requester end end diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb index 33c35161da3..48f799d8ca1 100644 --- a/spec/controllers/projects/raw_controller_spec.rb +++ b/spec/controllers/projects/raw_controller_spec.rb @@ -13,7 +13,7 @@ describe Projects::RawController do project_id: public_project.to_param, id: id) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8') expect(response.header['Content-Disposition']). to eq("inline") @@ -30,7 +30,7 @@ describe Projects::RawController do project_id: public_project.to_param, id: id) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(response.header['Content-Type']).to eq('image/jpeg') expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with("git-blob:") end @@ -54,7 +54,7 @@ describe Projects::RawController do project_id: public_project.to_param, id: id) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -65,7 +65,7 @@ describe Projects::RawController do project_id: public_project.to_param, id: id) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end diff --git a/spec/controllers/projects/repositories_controller_spec.rb b/spec/controllers/projects/repositories_controller_spec.rb index aad62cf20e3..ee905d11fb2 100644 --- a/spec/controllers/projects/repositories_controller_spec.rb +++ b/spec/controllers/projects/repositories_controller_spec.rb @@ -36,7 +36,7 @@ describe Projects::RepositoriesController do it "renders Not Found" do get :archive, namespace_id: project.namespace.path, project_id: project.path, ref: "master", format: "zip" - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end diff --git a/spec/controllers/projects/snippets_controller_spec.rb b/spec/controllers/projects/snippets_controller_spec.rb index 0f32a30f18b..b8a28f43707 100644 --- a/spec/controllers/projects/snippets_controller_spec.rb +++ b/spec/controllers/projects/snippets_controller_spec.rb @@ -19,7 +19,7 @@ describe Projects::SnippetsController do get :index, namespace_id: project.namespace.path, project_id: project.path expect(assigns(:snippets)).not_to include(project_snippet) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -30,7 +30,7 @@ describe Projects::SnippetsController do get :index, namespace_id: project.namespace.path, project_id: project.path expect(assigns(:snippets)).to include(project_snippet) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -41,7 +41,7 @@ describe Projects::SnippetsController do get :index, namespace_id: project.namespace.path, project_id: project.path expect(assigns(:snippets)).to include(project_snippet) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -56,7 +56,7 @@ describe Projects::SnippetsController do it 'responds with status 404' do get action, namespace_id: project.namespace.path, project_id: project.path, id: project_snippet.to_param - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -67,7 +67,7 @@ describe Projects::SnippetsController do get action, namespace_id: project.namespace.path, project_id: project.path, id: project_snippet.to_param expect(assigns(:snippet)).to eq(project_snippet) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -78,7 +78,7 @@ describe Projects::SnippetsController do get action, namespace_id: project.namespace.path, project_id: project.path, id: project_snippet.to_param expect(assigns(:snippet)).to eq(project_snippet) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -88,7 +88,7 @@ describe Projects::SnippetsController do it 'responds with status 404' do get action, namespace_id: project.namespace.path, project_id: project.path, id: 42 - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -98,7 +98,7 @@ describe Projects::SnippetsController do it 'responds with status 404' do get action, namespace_id: project.namespace.path, project_id: project.path, id: 42 - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end diff --git a/spec/controllers/projects/todo_controller_spec.rb b/spec/controllers/projects/todo_controller_spec.rb index 40a3403b660..5a8bba28594 100644 --- a/spec/controllers/projects/todo_controller_spec.rb +++ b/spec/controllers/projects/todo_controller_spec.rb @@ -22,7 +22,7 @@ describe Projects::TodosController do issuable_type: 'issue') end.to change { user.todos.count }.by(1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -36,7 +36,7 @@ describe Projects::TodosController do issuable_type: 'issue') end.to change { user.todos.count }.by(0) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'should not create todo for issue when user not logged in' do @@ -47,7 +47,7 @@ describe Projects::TodosController do issuable_type: 'issue') end.to change { user.todos.count }.by(0) - expect(response.status).to eq(302) + expect(response).to have_http_status(302) end end end @@ -69,7 +69,7 @@ describe Projects::TodosController do issuable_type: 'merge_request') end.to change { user.todos.count }.by(1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -83,7 +83,7 @@ describe Projects::TodosController do issuable_type: 'merge_request') end.to change { user.todos.count }.by(0) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'should not create todo for merge request user has no access to' do @@ -94,7 +94,7 @@ describe Projects::TodosController do issuable_type: 'merge_request') end.to change { user.todos.count }.by(0) - expect(response.status).to eq(302) + expect(response).to have_http_status(302) end end end diff --git a/spec/controllers/projects/tree_controller_spec.rb b/spec/controllers/projects/tree_controller_spec.rb index e74731c9ed8..4e3a2bdb19e 100644 --- a/spec/controllers/projects/tree_controller_spec.rb +++ b/spec/controllers/projects/tree_controller_spec.rb @@ -64,7 +64,7 @@ describe Projects::TreeController do context "valid SHA commit ID with path" do let(:id) { '6d39438/.gitignore' } - it { expect(response.status).to eq(302) } + it { expect(response).to have_http_status(302) } end end diff --git a/spec/controllers/projects/uploads_controller_spec.rb b/spec/controllers/projects/uploads_controller_spec.rb index 93c4494c660..0893ee89f6a 100644 --- a/spec/controllers/projects/uploads_controller_spec.rb +++ b/spec/controllers/projects/uploads_controller_spec.rb @@ -18,7 +18,7 @@ describe Projects::UploadsController do namespace_id: project.namespace.to_param, project_id: project.to_param, format: :json - expect(response.status).to eq(422) + expect(response).to have_http_status(422) end end @@ -79,7 +79,7 @@ describe Projects::UploadsController do it "responds with status 200" do go - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -87,7 +87,7 @@ describe Projects::UploadsController do it "responds with status 404" do go - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -106,7 +106,7 @@ describe Projects::UploadsController do it "responds with status 200" do go - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -114,7 +114,7 @@ describe Projects::UploadsController do it "responds with status 404" do go - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -140,7 +140,7 @@ describe Projects::UploadsController do it "responds with status 200" do go - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -192,7 +192,7 @@ describe Projects::UploadsController do it "responds with status 200" do go - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -224,7 +224,7 @@ describe Projects::UploadsController do it "responds with status 200" do go - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -232,7 +232,7 @@ describe Projects::UploadsController do it "responds with status 404" do go - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -253,7 +253,7 @@ describe Projects::UploadsController do it "responds with status 200" do go - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -261,7 +261,7 @@ describe Projects::UploadsController do it "responds with status 404" do go - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -270,7 +270,7 @@ describe Projects::UploadsController do it "responds with status 404" do go - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 146b2c2e131..d60579030c0 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -77,7 +77,7 @@ describe ProjectsController do get :show, namespace_id: public_project.namespace.path, id: public_project.path expect(assigns(:project)).to eq(public_project) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -101,7 +101,7 @@ describe ProjectsController do get :show, namespace_id: public_project.namespace.path, id: public_project.path.upcase expect(assigns(:project)).to eq(other_project) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -146,7 +146,7 @@ describe ProjectsController do expect(project.repository.path).to include(new_path) expect(assigns(:repository).path).to eq(project.repository.path) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -161,7 +161,7 @@ describe ProjectsController do delete :destroy, namespace_id: project.namespace.path, id: project.path expect { Project.find(orig_id) }.to raise_error(ActiveRecord::RecordNotFound) - expect(response.status).to eq(302) + expect(response).to have_http_status(302) expect(response).to redirect_to(dashboard_projects_path) end end @@ -234,7 +234,7 @@ describe ProjectsController do delete(:remove_fork, namespace_id: project.namespace.to_param, id: project.to_param, format: :js) - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end diff --git a/spec/controllers/snippets_controller_spec.rb b/spec/controllers/snippets_controller_spec.rb index b3dcb52c500..2a89159c070 100644 --- a/spec/controllers/snippets_controller_spec.rb +++ b/spec/controllers/snippets_controller_spec.rb @@ -19,7 +19,7 @@ describe SnippetsController do it 'responds with status 404' do get :show, id: other_personal_snippet.to_param - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -28,7 +28,7 @@ describe SnippetsController do get :show, id: personal_snippet.to_param expect(assigns(:snippet)).to eq(personal_snippet) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -54,7 +54,7 @@ describe SnippetsController do get :show, id: personal_snippet.to_param expect(assigns(:snippet)).to eq(personal_snippet) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -79,7 +79,7 @@ describe SnippetsController do get :show, id: personal_snippet.to_param expect(assigns(:snippet)).to eq(personal_snippet) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -88,7 +88,7 @@ describe SnippetsController do get :show, id: personal_snippet.to_param expect(assigns(:snippet)).to eq(personal_snippet) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -102,7 +102,7 @@ describe SnippetsController do it 'responds with status 404' do get :show, id: 'doesntexist' - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -110,7 +110,7 @@ describe SnippetsController do it 'responds with status 404' do get :show, id: 'doesntexist' - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -134,7 +134,7 @@ describe SnippetsController do it 'responds with status 404' do get :raw, id: other_personal_snippet.to_param - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -143,7 +143,7 @@ describe SnippetsController do get :raw, id: personal_snippet.to_param expect(assigns(:snippet)).to eq(personal_snippet) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -169,7 +169,7 @@ describe SnippetsController do get :raw, id: personal_snippet.to_param expect(assigns(:snippet)).to eq(personal_snippet) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -194,7 +194,7 @@ describe SnippetsController do get :raw, id: personal_snippet.to_param expect(assigns(:snippet)).to eq(personal_snippet) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -203,7 +203,7 @@ describe SnippetsController do get :raw, id: personal_snippet.to_param expect(assigns(:snippet)).to eq(personal_snippet) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -217,7 +217,7 @@ describe SnippetsController do it 'responds with status 404' do get :raw, id: 'doesntexist' - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -225,7 +225,7 @@ describe SnippetsController do it 'responds with status 404' do get :raw, id: 'doesntexist' - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb index 73858e6f063..69124ab06bf 100644 --- a/spec/controllers/uploads_controller_spec.rb +++ b/spec/controllers/uploads_controller_spec.rb @@ -26,7 +26,7 @@ describe UploadsController do it "responds with status 200" do get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "image.png" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -35,7 +35,7 @@ describe UploadsController do it "responds with status 200" do get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "image.png" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -52,7 +52,7 @@ describe UploadsController do it "responds with status 200" do get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -64,7 +64,7 @@ describe UploadsController do it "responds with status 200" do get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -109,7 +109,7 @@ describe UploadsController do it "responds with status 200" do get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -118,7 +118,7 @@ describe UploadsController do it "responds with status 404" do get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png" - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -133,7 +133,7 @@ describe UploadsController do it "responds with status 200" do get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -145,7 +145,7 @@ describe UploadsController do it "responds with status 200" do get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -181,7 +181,7 @@ describe UploadsController do it "responds with status 200" do get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -190,7 +190,7 @@ describe UploadsController do it "responds with status 404" do get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png" - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -210,7 +210,7 @@ describe UploadsController do it "responds with status 200" do get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -222,7 +222,7 @@ describe UploadsController do it "responds with status 200" do get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -267,7 +267,7 @@ describe UploadsController do it "responds with status 200" do get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -276,7 +276,7 @@ describe UploadsController do it "responds with status 404" do get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png" - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index c61ec174665..8d6f486efdd 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -33,7 +33,7 @@ describe UsersController do it 'renders the show template' do get :show, username: user.username - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(response).to render_template('show') end end @@ -47,7 +47,7 @@ describe UsersController do context 'when logged out' do it 'renders 404' do get :show, username: user.username - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -56,7 +56,7 @@ describe UsersController do it 'renders show' do get :show, username: user.username - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(response).to render_template('show') end end @@ -121,7 +121,7 @@ describe UsersController do context 'format html' do it 'renders snippets page' do get :snippets, username: user.username - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(response).to render_template('show') end end @@ -129,7 +129,7 @@ describe UsersController do context 'format json' do it 'response with snippets json data' do get :snippets, username: user.username, format: :json - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(JSON.parse(response.body)).to have_key('html') end end diff --git a/spec/features/admin/admin_system_info_spec.rb b/spec/features/admin/admin_system_info_spec.rb new file mode 100644 index 00000000000..dbc1d829b67 --- /dev/null +++ b/spec/features/admin/admin_system_info_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe 'Admin System Info' do + before do + login_as :admin + end + + describe 'GET /admin/system_info' do + it 'shows system info page' do + visit admin_system_info_path + + expect(page).to have_content 'CPU' + expect(page).to have_content 'Memory' + expect(page).to have_content 'Disk' + end + end +end diff --git a/spec/features/atom/users_spec.rb b/spec/features/atom/users_spec.rb index de6aed74fb4..91704377a07 100644 --- a/spec/features/atom/users_spec.rb +++ b/spec/features/atom/users_spec.rb @@ -61,7 +61,7 @@ describe "User Feed", feature: true do end it 'should have XHTML summaries in merge request descriptions' do - expect(body).to match /Here is the fix: <a[^>]*><img[^>]*\/><\/a>/ + expect(body).to match /Here is the fix: <\/p><div[^>]*><a[^>]*><img[^>]*\/><\/a><\/div>/ end end end diff --git a/spec/features/groups/members/user_requests_access_spec.rb b/spec/features/groups/members/user_requests_access_spec.rb index 1ea607cbca0..4944301c938 100644 --- a/spec/features/groups/members/user_requests_access_spec.rb +++ b/spec/features/groups/members/user_requests_access_spec.rb @@ -4,6 +4,7 @@ feature 'Groups > Members > User requests access', feature: true do let(:user) { create(:user) } let(:owner) { create(:user) } let(:group) { create(:group, :public) } + let!(:project) { create(:project, :private, namespace: group) } background do group.add_owner(owner) @@ -24,6 +25,20 @@ feature 'Groups > Members > User requests access', feature: true do expect(page).not_to have_content 'Leave Group' end + scenario 'user does not see private projects' do + perform_enqueued_jobs { click_link 'Request Access' } + + expect(page).not_to have_content project.name + end + + scenario 'user does not see group in the Dashboard > Groups page' do + perform_enqueued_jobs { click_link 'Request Access' } + + visit dashboard_groups_path + + expect(page).not_to have_content group.name + end + scenario 'user is not listed in the group members page' do click_link 'Request Access' diff --git a/spec/features/issues/bulk_assignment_labels_spec.rb b/spec/features/issues/bulk_assignment_labels_spec.rb index 7143d0e40f3..afc093cc1f5 100644 --- a/spec/features/issues/bulk_assignment_labels_spec.rb +++ b/spec/features/issues/bulk_assignment_labels_spec.rb @@ -10,7 +10,7 @@ feature 'Issues > Labels bulk assignment', feature: true do let!(:bug) { create(:label, project: project, title: 'bug') } let!(:feature) { create(:label, project: project, title: 'feature') } - context 'as a allowed user', js: true do + context 'as an allowed user', js: true do before do project.team << [user, :master] @@ -164,6 +164,133 @@ feature 'Issues > Labels bulk assignment', feature: true do end end end + + context 'toggling a milestone' do + let!(:milestone) { create(:milestone, project: project, title: 'First Release') } + + context 'setting a milestone' do + before do + issue1.labels << bug + issue2.labels << feature + visit namespace_project_issues_path(project.namespace, project) + end + + it 'labels are kept' do + expect(find("#issue_#{issue1.id}")).to have_content 'bug' + expect(find("#issue_#{issue2.id}")).to have_content 'feature' + + check 'check_all_issues' + open_milestone_dropdown(['First Release']) + update_issues + + expect(find("#issue_#{issue1.id}")).to have_content 'bug' + expect(find("#issue_#{issue1.id}")).to have_content 'First Release' + expect(find("#issue_#{issue2.id}")).to have_content 'feature' + expect(find("#issue_#{issue2.id}")).to have_content 'First Release' + end + end + + context 'setting a milestone and adding another label' do + before do + issue1.labels << bug + + visit namespace_project_issues_path(project.namespace, project) + end + + it 'existing label is kept and new label is present' do + expect(find("#issue_#{issue1.id}")).to have_content 'bug' + + check 'check_all_issues' + open_milestone_dropdown ['First Release'] + open_labels_dropdown ['feature'] + update_issues + + expect(find("#issue_#{issue1.id}")).to have_content 'bug' + expect(find("#issue_#{issue1.id}")).to have_content 'feature' + expect(find("#issue_#{issue1.id}")).to have_content 'First Release' + expect(find("#issue_#{issue2.id}")).to have_content 'feature' + expect(find("#issue_#{issue2.id}")).to have_content 'First Release' + end + end + + context 'setting a milestone and removing existing label' do + before do + issue1.labels << bug + issue1.labels << feature + issue2.labels << feature + + visit namespace_project_issues_path(project.namespace, project) + end + + it 'existing label is kept and new label is present' do + expect(find("#issue_#{issue1.id}")).to have_content 'bug' + expect(find("#issue_#{issue1.id}")).to have_content 'bug' + expect(find("#issue_#{issue2.id}")).to have_content 'feature' + + check 'check_all_issues' + open_milestone_dropdown ['First Release'] + unmark_labels_in_dropdown ['feature'] + update_issues + + expect(find("#issue_#{issue1.id}")).to have_content 'bug' + expect(find("#issue_#{issue1.id}")).not_to have_content 'feature' + expect(find("#issue_#{issue1.id}")).to have_content 'First Release' + expect(find("#issue_#{issue2.id}")).not_to have_content 'feature' + expect(find("#issue_#{issue2.id}")).to have_content 'First Release' + end + end + + context 'unsetting a milestone' do + before do + issue1.milestone = milestone + issue2.milestone = milestone + issue1.save + issue2.save + issue1.labels << bug + issue2.labels << feature + + visit namespace_project_issues_path(project.namespace, project) + end + + it 'labels are kept' do + expect(find("#issue_#{issue1.id}")).to have_content 'bug' + expect(find("#issue_#{issue1.id}")).to have_content 'First Release' + expect(find("#issue_#{issue2.id}")).to have_content 'feature' + expect(find("#issue_#{issue2.id}")).to have_content 'First Release' + + check 'check_all_issues' + open_milestone_dropdown(['No Milestone']) + update_issues + + expect(find("#issue_#{issue1.id}")).to have_content 'bug' + expect(find("#issue_#{issue1.id}")).not_to have_content 'First Release' + expect(find("#issue_#{issue2.id}")).to have_content 'feature' + expect(find("#issue_#{issue2.id}")).not_to have_content 'First Release' + end + end + end + + context 'toggling checked issues' do + before do + issue1.labels << bug + + visit namespace_project_issues_path(project.namespace, project) + end + + it do + expect(find("#issue_#{issue1.id}")).to have_content 'bug' + + check_issue issue1 + open_labels_dropdown ['feature'] + uncheck_issue issue1 + check_issue issue1 + update_issues + sleep 1 # needed + + expect(find("#issue_#{issue1.id}")).to have_content 'bug' + expect(find("#issue_#{issue1.id}")).not_to have_content 'feature' + end + end end context 'as a guest' do @@ -181,6 +308,16 @@ feature 'Issues > Labels bulk assignment', feature: true do end end + def open_milestone_dropdown(items = []) + page.within('.issues_bulk_update') do + click_button 'Milestone' + wait_for_ajax + items.map do |item| + click_link item + end + end + end + def open_labels_dropdown(items = [], unmark = false) page.within('.issues_bulk_update') do click_button 'Label' @@ -201,12 +338,20 @@ feature 'Issues > Labels bulk assignment', feature: true do open_labels_dropdown(items, true) end - def check_issue(issue) + def check_issue(issue, uncheck = false) page.within('.issues-list') do - check "selected_issue_#{issue.id}" + if uncheck + uncheck "selected_issue_#{issue.id}" + else + check "selected_issue_#{issue.id}" + end end end + def uncheck_issue(issue) + check_issue(issue, true) + end + def update_issues click_button 'Update issues' wait_for_ajax diff --git a/spec/features/projects/files/gitignore_dropdown_spec.rb b/spec/features/projects/files/gitignore_dropdown_spec.rb index 073a83b6896..9ebef505b92 100644 --- a/spec/features/projects/files/gitignore_dropdown_spec.rb +++ b/spec/features/projects/files/gitignore_dropdown_spec.rb @@ -24,6 +24,7 @@ feature 'User wants to add a .gitignore file', feature: true do end wait_for_ajax + expect(page).to have_css('.gitignore-selector .dropdown-toggle-text', text: 'Rails') expect(page).to have_content('/.bundle') expect(page).to have_content('# Gemfile.lock, .ruby-version, .ruby-gemset') end diff --git a/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb b/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb index d516e8ce55a..b8c06c383fb 100644 --- a/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb +++ b/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb @@ -24,6 +24,7 @@ feature 'User wants to add a .gitlab-ci.yml file', feature: true do end wait_for_ajax + expect(page).to have_css('.gitlab-ci-yml-selector .dropdown-toggle-text', text: 'jekyll') expect(page).to have_content('This file is a template, and might need editing before it works on your project') expect(page).to have_content('jekyll build -d test') end diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb index 8625ea6bc10..13d980a326f 100644 --- a/spec/features/security/project/internal_access_spec.rb +++ b/spec/features/security/project/internal_access_spec.rb @@ -288,4 +288,142 @@ describe "Internal Project Access", feature: true do it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :visitor } end + + describe "GET /:project_path/pipelines" do + subject { namespace_project_pipelines_path(project.namespace, project) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + describe "GET /:project_path/pipelines/:id" do + let(:pipeline) { create(:ci_pipeline, project: project) } + subject { namespace_project_pipeline_path(project.namespace, project, pipeline) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + describe "GET /:project_path/builds" do + subject { namespace_project_builds_path(project.namespace, project) } + + context "when allowed for public and internal" do + before { project.update(public_builds: true) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + context "when disallowed for public and internal" do + before { project.update(public_builds: false) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + end + + describe "GET /:project_path/builds/:id" do + let(:pipeline) { create(:ci_pipeline, project: project) } + let(:build) { create(:ci_build, pipeline: pipeline) } + subject { namespace_project_build_path(project.namespace, project, build.id) } + + context "when allowed for public and internal" do + before { project.update(public_builds: true) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + context "when disallowed for public and internal" do + before { project.update(public_builds: false) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + end + + describe "GET /:project_path/environments" do + subject { namespace_project_environments_path(project.namespace, project) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + describe "GET /:project_path/environments/:id" do + let(:environment) { create(:environment, project: project) } + subject { namespace_project_environment_path(project.namespace, project, environment) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + describe "GET /:project_path/environments/new" do + subject { new_namespace_project_environment_path(project.namespace, project) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_denied_for reporter } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end end diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb index 544270b4037..ac9690cc127 100644 --- a/spec/features/security/project/private_access_spec.rb +++ b/spec/features/security/project/private_access_spec.rb @@ -260,4 +260,106 @@ describe "Private Project Access", feature: true do it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :visitor } end + + describe "GET /:project_path/pipelines" do + subject { namespace_project_pipelines_path(project.namespace, project) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + describe "GET /:project_path/pipelines/:id" do + let(:pipeline) { create(:ci_pipeline, project: project) } + subject { namespace_project_pipeline_path(project.namespace, project, pipeline) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + describe "GET /:project_path/builds" do + subject { namespace_project_builds_path(project.namespace, project) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + describe "GET /:project_path/builds/:id" do + let(:pipeline) { create(:ci_pipeline, project: project) } + let(:build) { create(:ci_build, pipeline: pipeline) } + subject { namespace_project_build_path(project.namespace, project, build.id) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + describe "GET /:project_path/environments" do + subject { namespace_project_environments_path(project.namespace, project) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + describe "GET /:project_path/environments/:id" do + let(:environment) { create(:environment, project: project) } + subject { namespace_project_environment_path(project.namespace, project, environment) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end + + describe "GET /:project_path/environments/new" do + subject { new_namespace_project_environment_path(project.namespace, project) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_denied_for reporter } + it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :external } + it { is_expected.to be_denied_for :visitor } + end end diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb index f6c6687e162..737897de52b 100644 --- a/spec/features/security/project/public_access_spec.rb +++ b/spec/features/security/project/public_access_spec.rb @@ -109,6 +109,35 @@ describe "Public Project Access", feature: true do it { is_expected.to be_allowed_for :external } end + describe "GET /:project_path/pipelines" do + subject { namespace_project_pipelines_path(project.namespace, project) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :external } + it { is_expected.to be_allowed_for :visitor } + end + + describe "GET /:project_path/pipelines/:id" do + let(:pipeline) { create(:ci_pipeline, project: project) } + subject { namespace_project_pipeline_path(project.namespace, project, pipeline) } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for owner } + it { is_expected.to be_allowed_for master } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :external } + it { is_expected.to be_allowed_for :visitor } + end + describe "GET /:project_path/builds" do subject { namespace_project_builds_path(project.namespace, project) } @@ -191,7 +220,7 @@ describe "Public Project Access", feature: true do describe "GET /:project_path/environments/:id" do let(:environment) { create(:environment, project: project) } - subject { namespace_project_environments_path(project.namespace, project, environment) } + subject { namespace_project_environment_path(project.namespace, project, environment) } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for owner } diff --git a/spec/helpers/visibility_level_helper_spec.rb b/spec/helpers/visibility_level_helper_spec.rb index ff98249570d..5e7594170c5 100644 --- a/spec/helpers/visibility_level_helper_spec.rb +++ b/spec/helpers/visibility_level_helper_spec.rb @@ -1,11 +1,6 @@ require 'spec_helper' describe VisibilityLevelHelper do - include Haml::Helpers - - before :all do - init_haml_helpers - end let(:project) { build(:project) } let(:group) { build(:group) } diff --git a/spec/initializers/settings_spec.rb b/spec/initializers/settings_spec.rb index e58f2c80e95..1bcae8a27db 100644 --- a/spec/initializers/settings_spec.rb +++ b/spec/initializers/settings_spec.rb @@ -1,3 +1,4 @@ +require 'spec_helper' require_relative '../../config/initializers/1_settings' describe Settings, lib: true do diff --git a/spec/javascripts/application_spec.js.coffee b/spec/javascripts/application_spec.js.coffee index 8af39c41f2f..4b6a2bb5440 100644 --- a/spec/javascripts/application_spec.js.coffee +++ b/spec/javascripts/application_spec.js.coffee @@ -1,4 +1,4 @@ -#= require lib/common_utils +#= require lib/utils/common_utils describe 'Application', -> describe 'disable buttons', -> diff --git a/spec/javascripts/awards_handler_spec.js.coffee b/spec/javascripts/awards_handler_spec.js.coffee index ba191199dc7..d7f9c6fc076 100644 --- a/spec/javascripts/awards_handler_spec.js.coffee +++ b/spec/javascripts/awards_handler_spec.js.coffee @@ -160,7 +160,6 @@ describe 'AwardsHandler', -> expect($('[data-emoji=angel]').is(':visible')).toBe no expect($('[data-emoji=anger]').is(':visible')).toBe no expect($('[data-emoji=alien]').is(':visible')).toBe yes - expect($('h5.emoji-search').is(':visible')).toBe yes describe 'emoji menu', -> diff --git a/spec/javascripts/fixtures/emoji_menu.coffee b/spec/javascripts/fixtures/emoji_menu.coffee index e529dd5f1cd..ce1a41390d2 100644 --- a/spec/javascripts/fixtures/emoji_menu.coffee +++ b/spec/javascripts/fixtures/emoji_menu.coffee @@ -1,7 +1,7 @@ window.emojiMenu = """ <div class='emoji-menu'> + <input type="text" name="emoji_search" id="emoji_search" value="" class="emoji-search search-input form-control" /> <div class='emoji-menu-content'> - <input type="text" name="emoji_search" id="emoji_search" value="" class="emoji-search search-input form-control" /> <h5 class='emoji-menu-title'> Emoticons </h5> diff --git a/spec/javascripts/issue_spec.js.coffee b/spec/javascripts/issue_spec.js.coffee index 71f0c1076c5..d84d80f266b 100644 --- a/spec/javascripts/issue_spec.js.coffee +++ b/spec/javascripts/issue_spec.js.coffee @@ -1,4 +1,4 @@ -#= require lib/text_utility +#= require lib/utils/text_utility #= require issue describe 'Issue', -> diff --git a/spec/javascripts/project_title_spec.js.coffee b/spec/javascripts/project_title_spec.js.coffee index 9be29097f4c..f0d26fb5446 100644 --- a/spec/javascripts/project_title_spec.js.coffee +++ b/spec/javascripts/project_title_spec.js.coffee @@ -1,6 +1,6 @@ #= require bootstrap #= require select2 -#= require lib/type_utility +#= require lib/utils/type_utility #= require gl_dropdown #= require api #= require project_select diff --git a/spec/javascripts/search_autocomplete_spec.js.coffee b/spec/javascripts/search_autocomplete_spec.js.coffee index e77177783a7..1c1faca3333 100644 --- a/spec/javascripts/search_autocomplete_spec.js.coffee +++ b/spec/javascripts/search_autocomplete_spec.js.coffee @@ -1,8 +1,8 @@ #= require gl_dropdown #= require search_autocomplete #= require jquery -#= require lib/common_utils -#= require lib/type_utility +#= require lib/utils/common_utils +#= require lib/utils/type_utility #= require fuzzaldrin-plus diff --git a/spec/lib/banzai/filter/image_link_filter_spec.rb b/spec/lib/banzai/filter/image_link_filter_spec.rb index dd5594750c8..a2a1ed58d1b 100644 --- a/spec/lib/banzai/filter/image_link_filter_spec.rb +++ b/spec/lib/banzai/filter/image_link_filter_spec.rb @@ -21,4 +21,9 @@ describe Banzai::Filter::ImageLinkFilter, lib: true do doc = filter(image('https://i.imgur.com/DfssX9C.jpg')) expect(doc.at_css('img')['src']).to eq doc.at_css('a')['href'] end + + it 'wraps the image with a link and a div' do + doc = filter(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')) + expect(doc.to_html).to include('<div class="image-container">') + end end diff --git a/spec/lib/banzai/filter/issue_reference_filter_spec.rb b/spec/lib/banzai/filter/issue_reference_filter_spec.rb index 5b63c946114..8d6ce114aa9 100644 --- a/spec/lib/banzai/filter/issue_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/issue_reference_filter_spec.rb @@ -198,4 +198,40 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do expect(doc.to_html).to match(/\(<a.+>Reference<\/a>\.\)/) end end + + describe '#issues_per_Project' do + context 'using an internal issue tracker' do + it 'returns a Hash containing the issues per project' do + doc = Nokogiri::HTML.fragment('') + filter = described_class.new(doc, project: project) + + expect(filter).to receive(:projects_per_reference). + and_return({ project.path_with_namespace => project }) + + expect(filter).to receive(:references_per_project). + and_return({ project.path_with_namespace => Set.new([issue.iid]) }) + + expect(filter.issues_per_project). + to eq({ project => { issue.iid => issue } }) + end + end + + context 'using an external issue tracker' do + it 'returns a Hash containing the issues per project' do + doc = Nokogiri::HTML.fragment('') + filter = described_class.new(doc, project: project) + + expect(project).to receive(:default_issues_tracker?).and_return(false) + + expect(filter).to receive(:projects_per_reference). + and_return({ project.path_with_namespace => project }) + + expect(filter).to receive(:references_per_project). + and_return({ project.path_with_namespace => Set.new([1]) }) + + expect(filter.issues_per_project[project][1]). + to be_an_instance_of(ExternalIssue) + end + end + end end diff --git a/spec/lib/banzai/note_renderer_spec.rb b/spec/lib/banzai/note_renderer_spec.rb new file mode 100644 index 00000000000..98f76f36fd5 --- /dev/null +++ b/spec/lib/banzai/note_renderer_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper' + +describe Banzai::NoteRenderer do + describe '.render' do + it 'renders a Note' do + note = double(:note) + project = double(:project) + wiki = double(:wiki) + user = double(:user) + + expect(Banzai::ObjectRenderer).to receive(:new). + with(project, user, + requested_path: 'foo', + project_wiki: wiki, + ref: 'bar', + pipeline: :note). + and_call_original + + expect_any_instance_of(Banzai::ObjectRenderer). + to receive(:render).with([note], :note) + + described_class.render([note], project, user, 'foo', wiki, 'bar') + end + end +end diff --git a/spec/lib/banzai/object_renderer_spec.rb b/spec/lib/banzai/object_renderer_spec.rb new file mode 100644 index 00000000000..44256b32bdc --- /dev/null +++ b/spec/lib/banzai/object_renderer_spec.rb @@ -0,0 +1,120 @@ +require 'spec_helper' + +describe Banzai::ObjectRenderer do + let(:project) { create(:empty_project) } + let(:user) { project.owner } + + describe '#render' do + it 'renders and redacts an Array of objects' do + renderer = described_class.new(project, user) + object = double(:object, note: 'hello', note_html: nil) + + expect(renderer).to receive(:render_objects).with([object], :note). + and_call_original + + expect(renderer).to receive(:redact_documents). + with(an_instance_of(Array)). + and_call_original + + expect(object).to receive(:note_html=).with('<p>hello</p>') + + renderer.render([object], :note) + end + end + + describe '#render_objects' do + it 'renders an Array of objects' do + object = double(:object, note: 'hello') + renderer = described_class.new(project, user) + + expect(renderer).to receive(:render_attribute).with(object, :note). + and_call_original + + rendered = renderer.render_objects([object], :note) + + expect(rendered).to be_an_instance_of(Array) + expect(rendered[0]).to be_an_instance_of(Nokogiri::HTML::DocumentFragment) + end + end + + describe '#redact_documents' do + it 'redacts a set of documents and returns them as an Array of Strings' do + doc = Nokogiri::HTML.fragment('<p>hello</p>') + renderer = described_class.new(project, user) + + expect_any_instance_of(Banzai::Redactor).to receive(:redact). + with([doc]). + and_call_original + + redacted = renderer.redact_documents([doc]) + + expect(redacted).to eq(['<p>hello</p>']) + end + end + + describe '#context_for' do + let(:object) { double(:object, note: 'hello') } + let(:renderer) { described_class.new(project, user) } + + it 'returns a Hash' do + expect(renderer.context_for(object, :note)).to be_an_instance_of(Hash) + end + + it 'includes the cache key' do + context = renderer.context_for(object, :note) + + expect(context[:cache_key]).to eq([object, :note]) + end + + context 'when the object responds to "author"' do + it 'includes the author in the context' do + expect(object).to receive(:author).and_return('Alice') + + context = renderer.context_for(object, :note) + + expect(context[:author]).to eq('Alice') + end + end + + context 'when the object does not respond to "author"' do + it 'does not include the author in the context' do + context = renderer.context_for(object, :note) + + expect(context.key?(:author)).to eq(false) + end + end + end + + describe '#render_attribute' do + it 'renders the attribute of an object' do + object = double(:doc, note: 'hello') + renderer = described_class.new(project, user, pipeline: :note) + doc = renderer.render_attribute(object, :note) + + expect(doc).to be_an_instance_of(Nokogiri::HTML::DocumentFragment) + expect(doc.to_html).to eq('<p>hello</p>') + end + end + + describe '#base_context' do + let(:context) do + described_class.new(project, user, pipeline: :note).base_context + end + + it 'returns a Hash' do + expect(context).to be_an_instance_of(Hash) + end + + it 'includes the custom attributes' do + expect(context[:pipeline]).to eq(:note) + end + + it 'includes the current user' do + expect(context[:current_user]).to eq(user) + end + + it 'includes the current project' do + expect(context[:project]).to eq(project) + end + end +end diff --git a/spec/lib/banzai/redactor_spec.rb b/spec/lib/banzai/redactor_spec.rb new file mode 100644 index 00000000000..488f465bcda --- /dev/null +++ b/spec/lib/banzai/redactor_spec.rb @@ -0,0 +1,53 @@ +require 'spec_helper' + +describe Banzai::Redactor do + let(:user) { build(:user) } + let(:project) { build(:empty_project) } + let(:redactor) { described_class.new(project, user) } + + describe '#redact' do + it 'redacts an Array of documents' do + doc1 = Nokogiri::HTML. + fragment('<a class="gfm" data-reference-type="issue">foo</a>') + + doc2 = Nokogiri::HTML. + fragment('<a class="gfm" data-reference-type="issue">bar</a>') + + expect(redactor).to receive(:nodes_visible_to_user).and_return([]) + + expect(redactor.redact([doc1, doc2])).to eq([doc1, doc2]) + + expect(doc1.to_html).to eq('foo') + expect(doc2.to_html).to eq('bar') + end + end + + describe '#redact_nodes' do + it 'redacts an Array of nodes' do + doc = Nokogiri::HTML.fragment('<a href="foo">foo</a>') + node = doc.children[0] + + expect(redactor).to receive(:nodes_visible_to_user). + with([node]). + and_return(Set.new) + + redactor.redact_nodes([node]) + + expect(doc.to_html).to eq('foo') + end + end + + describe '#nodes_visible_to_user' do + it 'returns a Set containing the visible nodes' do + doc = Nokogiri::HTML.fragment('<a data-reference-type="issue"></a>') + node = doc.children[0] + + expect_any_instance_of(Banzai::ReferenceParser::IssueParser). + to receive(:nodes_visible_to_user). + with(user, [node]). + and_return([node]) + + expect(redactor.nodes_visible_to_user([node])).to eq(Set.new([node])) + end + end +end diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index d562d8b25ea..2ca9f554b07 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -951,7 +951,7 @@ EOT config = YAML.dump({ before_script: "bundle update", rspec: { script: "test" } }) expect do GitlabCiYamlProcessor.new(config, path) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "before_script should be an array of strings") + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Before script config should be an array of strings") end it "returns errors if job before_script parameter is not an array of strings" do @@ -1206,5 +1206,17 @@ EOT end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: dependencies parameter should be an array of strings") end end + + describe "Validate configuration templates" do + templates = Dir.glob("#{Rails.root.join('vendor/gitlab-ci-yml')}/**/*.gitlab-ci.yml") + + templates.each do |file| + it "does not return errors for #{file}" do + file = File.read(file) + + expect { GitlabCiYamlProcessor.new(file) }.not_to raise_error + end + end + end end end diff --git a/spec/lib/gitlab/ci/config/node/configurable_spec.rb b/spec/lib/gitlab/ci/config/node/configurable_spec.rb index 47c68f96dc8..9bbda6e7396 100644 --- a/spec/lib/gitlab/ci/config/node/configurable_spec.rb +++ b/spec/lib/gitlab/ci/config/node/configurable_spec.rb @@ -7,26 +7,26 @@ describe Gitlab::Ci::Config::Node::Configurable do node.include(described_class) end - describe 'allowed nodes' do + describe 'configured nodes' do before do node.class_eval do allow_node :object, Object, description: 'test object' end end - describe '#allowed_nodes' do - it 'has valid allowed nodes' do - expect(node.allowed_nodes).to include :object + describe '.nodes' do + it 'has valid nodes' do + expect(node.nodes).to include :object end it 'creates a node factory' do - expect(node.allowed_nodes[:object]) + expect(node.nodes[:object]) .to be_an_instance_of Gitlab::Ci::Config::Node::Factory end it 'returns a duplicated factory object' do - first_factory = node.allowed_nodes[:object] - second_factory = node.allowed_nodes[:object] + first_factory = node.nodes[:object] + second_factory = node.nodes[:object] expect(first_factory).not_to be_equal(second_factory) end diff --git a/spec/lib/gitlab/ci/config/node/factory_spec.rb b/spec/lib/gitlab/ci/config/node/factory_spec.rb index d681aa32456..01a707a6bd4 100644 --- a/spec/lib/gitlab/ci/config/node/factory_spec.rb +++ b/spec/lib/gitlab/ci/config/node/factory_spec.rb @@ -25,6 +25,16 @@ describe Gitlab::Ci::Config::Node::Factory do expect(entry.description).to eq 'test description' end end + + context 'when setting key' do + it 'creates entry with custom key' do + entry = factory + .with(value: ['ls', 'pwd'], key: 'test key') + .create! + + expect(entry.key).to eq 'test key' + end + end end context 'when not setting value' do diff --git a/spec/lib/gitlab/ci/config/node/global_spec.rb b/spec/lib/gitlab/ci/config/node/global_spec.rb index b1972172435..fddd53a2b57 100644 --- a/spec/lib/gitlab/ci/config/node/global_spec.rb +++ b/spec/lib/gitlab/ci/config/node/global_spec.rb @@ -3,13 +3,19 @@ require 'spec_helper' describe Gitlab::Ci::Config::Node::Global do let(:global) { described_class.new(hash) } - describe '#allowed_nodes' do + describe '.nodes' do it 'can contain global config keys' do - expect(global.allowed_nodes).to include :before_script + expect(described_class.nodes).to include :before_script end it 'returns a hash' do - expect(global.allowed_nodes).to be_a Hash + expect(described_class.nodes).to be_a Hash + end + end + + describe '#key' do + it 'returns underscored class name' do + expect(global.key).to eq 'global' end end @@ -79,7 +85,7 @@ describe Gitlab::Ci::Config::Node::Global do describe '#errors' do it 'reports errors from child nodes' do expect(global.errors) - .to include 'before_script should be an array of strings' + .to include 'Before script config should be an array of strings' end end diff --git a/spec/lib/gitlab/ci/config/node/script_spec.rb b/spec/lib/gitlab/ci/config/node/script_spec.rb index e4d6481f8a5..6af6aa15eef 100644 --- a/spec/lib/gitlab/ci/config/node/script_spec.rb +++ b/spec/lib/gitlab/ci/config/node/script_spec.rb @@ -1,13 +1,13 @@ require 'spec_helper' describe Gitlab::Ci::Config::Node::Script do - let(:entry) { described_class.new(value) } + let(:entry) { described_class.new(config) } - describe '#validate!' do - before { entry.validate! } + describe '#process!' do + before { entry.process! } - context 'when entry value is correct' do - let(:value) { ['ls', 'pwd'] } + context 'when entry config value is correct' do + let(:config) { ['ls', 'pwd'] } describe '#value' do it 'returns concatenated command' do @@ -29,12 +29,12 @@ describe Gitlab::Ci::Config::Node::Script do end context 'when entry value is not correct' do - let(:value) { 'ls' } + let(:config) { 'ls' } describe '#errors' do it 'saves errors' do expect(entry.errors) - .to include /should be an array of strings/ + .to include 'Script config should be an array of strings' end end diff --git a/spec/lib/gitlab/ci/config/node/validatable_spec.rb b/spec/lib/gitlab/ci/config/node/validatable_spec.rb new file mode 100644 index 00000000000..10cd01afcd1 --- /dev/null +++ b/spec/lib/gitlab/ci/config/node/validatable_spec.rb @@ -0,0 +1,50 @@ +require 'spec_helper' + +describe Gitlab::Ci::Config::Node::Validatable do + let(:node) { Class.new } + + before do + node.include(described_class) + end + + describe '.validator' do + before do + node.class_eval do + attr_accessor :test_attribute + + validations do + validates :test_attribute, presence: true + end + end + end + + it 'returns validator' do + expect(node.validator.superclass) + .to be Gitlab::Ci::Config::Node::Validator + end + + context 'when validating node instance' do + let(:node_instance) { node.new } + + context 'when attribute is valid' do + before do + node_instance.test_attribute = 'valid' + end + + it 'instance of validator is valid' do + expect(node.validator.new(node_instance)).to be_valid + end + end + + context 'when attribute is not valid' do + before do + node_instance.test_attribute = nil + end + + it 'instance of validator is invalid' do + expect(node.validator.new(node_instance)).to be_invalid + end + end + end + end +end diff --git a/spec/lib/gitlab/ci/config/node/validator_spec.rb b/spec/lib/gitlab/ci/config/node/validator_spec.rb new file mode 100644 index 00000000000..ad875d55384 --- /dev/null +++ b/spec/lib/gitlab/ci/config/node/validator_spec.rb @@ -0,0 +1,67 @@ +require 'spec_helper' + +describe Gitlab::Ci::Config::Node::Validator do + let(:validator) { Class.new(described_class) } + let(:validator_instance) { validator.new(node) } + let(:node) { spy('node') } + + shared_examples 'delegated validator' do + context 'when node is valid' do + before do + allow(node).to receive(:test_attribute).and_return('valid value') + end + + it 'validates attribute in node' do + expect(node).to receive(:test_attribute) + expect(validator_instance).to be_valid + end + + it 'returns no errors' do + validator_instance.validate + + expect(validator_instance.full_errors).to be_empty + end + end + + context 'when node is invalid' do + before do + allow(node).to receive(:test_attribute).and_return(nil) + end + + it 'validates attribute in node' do + expect(node).to receive(:test_attribute) + expect(validator_instance).to be_invalid + end + + it 'returns errors' do + validator_instance.validate + + expect(validator_instance.full_errors).not_to be_empty + end + end + end + + describe 'attributes validations' do + before do + validator.class_eval do + validates :test_attribute, presence: true + end + end + + it_behaves_like 'delegated validator' + end + + describe 'interface validations' do + before do + validator.class_eval do + validate do + unless @node.test_attribute == 'valid value' + errors.add(:test_attribute, 'invalid value') + end + end + end + end + + it_behaves_like 'delegated validator' + end +end diff --git a/spec/lib/gitlab/ci/config_spec.rb b/spec/lib/gitlab/ci/config_spec.rb index 3871d939feb..2a5d132db7b 100644 --- a/spec/lib/gitlab/ci/config_spec.rb +++ b/spec/lib/gitlab/ci/config_spec.rb @@ -67,6 +67,12 @@ describe Gitlab::Ci::Config do expect(config.errors).not_to be_empty end end + + describe '#errors' do + it 'returns an array of strings' do + expect(config.errors).to all(be_an_instance_of(String)) + end + end end end end diff --git a/spec/lib/gitlab/highlight_spec.rb b/spec/lib/gitlab/highlight_spec.rb index 1620eb6c60a..364532e94e3 100644 --- a/spec/lib/gitlab/highlight_spec.rb +++ b/spec/lib/gitlab/highlight_spec.rb @@ -4,6 +4,7 @@ describe Gitlab::Highlight, lib: true do include RepoHelpers let(:project) { create(:project) } + let(:repository) { project.repository } let(:commit) { project.commit(sample_commit.id) } describe '.highlight_lines' do @@ -18,4 +19,30 @@ describe Gitlab::Highlight, lib: true do end end + describe 'custom highlighting from .gitattributes' do + let(:branch) { 'gitattributes' } + let(:blob) { repository.blob_at_branch(branch, path) } + + let(:highlighter) do + Gitlab::Highlight.new(blob.path, blob.data, repository: repository) + end + + before { project.change_head('gitattributes') } + + describe 'basic language selection' do + let(:path) { 'custom-highlighting/test.gitlab-custom' } + it 'highlights as ruby' do + expect(highlighter.lexer.tag).to eq 'ruby' + end + end + + describe 'cgi options' do + let(:path) { 'custom-highlighting/test.gitlab-cgi' } + + it 'highlights as json with erb' do + expect(highlighter.lexer.tag).to eq 'erb' + expect(highlighter.lexer.parent.tag).to eq 'json' + end + end + end end diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json index 400d44ac162..403bd582ef3 100644 --- a/spec/lib/gitlab/import_export/project.json +++ b/spec/lib/gitlab/import_export/project.json @@ -4894,6 +4894,29 @@ "started_at": null, "finished_at": null, "duration": null, + "notes": [ + { + "id": 999, + "note": "Natus rerum qui dolorem dolorum voluptas.", + "noteable_type": "Commit", + "author_id": 1, + "created_at": "2016-03-22T15:19:59.469Z", + "updated_at": "2016-03-22T15:19:59.469Z", + "project_id": 5, + "attachment": { + "url": null + }, + "line_code": null, + "commit_id": "be93687618e4b132087f430a4d8fc3a609c9b77c", + "noteable_id": 36, + "system": false, + "st_diff": null, + "updated_by_id": null, + "author": { + "name": "Administrator" + } + } + ], "statuses": [ { "id": 71, diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index 7a40a43f8ae..23036ab8108 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -18,6 +18,12 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do it 'restores models based on JSON' do expect(restored_project_json).to be true end + + it 'creates a valid pipeline note' do + restored_project_json + + expect(Ci::Pipeline.first.notes).not_to be_empty + end end end end diff --git a/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb b/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb index e520a968999..4d2aa03e722 100644 --- a/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb +++ b/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' describe Gitlab::Metrics::SidekiqMiddleware do let(:middleware) { described_class.new } + let(:message) { { 'args' => ['test'], 'enqueued_at' => Time.new(2016, 6, 23, 6, 59).to_f } } describe '#call' do it 'tracks the transaction' do @@ -11,9 +12,23 @@ describe Gitlab::Metrics::SidekiqMiddleware do with('TestWorker#perform'). and_call_original + expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:set).with(:sidekiq_queue_duration, instance_of(Float)) expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:finish) - middleware.call(worker, 'test', :test) { nil } + middleware.call(worker, message, :test) { nil } + end + + it 'tracks the transaction (for messages without `enqueued_at`)' do + worker = double(:worker, class: double(:class, name: 'TestWorker')) + + expect(Gitlab::Metrics::Transaction).to receive(:new). + with('TestWorker#perform'). + and_call_original + + expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:set).with(:sidekiq_queue_duration, instance_of(Float)) + expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:finish) + + middleware.call(worker, {}, :test) { nil } end end end diff --git a/spec/lib/gitlab/metrics/system_spec.rb b/spec/lib/gitlab/metrics/system_spec.rb index d6ae54e25e8..cf0e282c2fb 100644 --- a/spec/lib/gitlab/metrics/system_spec.rb +++ b/spec/lib/gitlab/metrics/system_spec.rb @@ -28,8 +28,20 @@ describe Gitlab::Metrics::System do end describe '.cpu_time' do - it 'returns a Fixnum' do - expect(described_class.cpu_time).to be_an_instance_of(Fixnum) + it 'returns a Float' do + expect(described_class.cpu_time).to be_an_instance_of(Float) + end + end + + describe '.real_time' do + it 'returns a Float' do + expect(described_class.real_time).to be_an_instance_of(Float) + end + end + + describe '.monotonic_time' do + it 'returns a Float' do + expect(described_class.monotonic_time).to be_an_instance_of(Float) end end end diff --git a/spec/lib/gitlab/saml/user_spec.rb b/spec/lib/gitlab/saml/user_spec.rb index 84c21ceefd9..2753aecc1f4 100644 --- a/spec/lib/gitlab/saml/user_spec.rb +++ b/spec/lib/gitlab/saml/user_spec.rb @@ -164,7 +164,14 @@ describe Gitlab::Saml::User, lib: true do end context 'and LDAP user has an account already' do - let!(:existing_user) { create(:omniauth_user, email: 'john@mail.com', extern_uid: 'uid=user1,ou=People,dc=example', provider: 'ldapmain', username: 'john') } + before do + create(:omniauth_user, + email: 'john@mail.com', + extern_uid: 'uid=user1,ou=People,dc=example', + provider: 'ldapmain', + username: 'john') + end + it 'adds the omniauth identity to the LDAP account' do saml_user.save @@ -177,6 +184,15 @@ describe Gitlab::Saml::User, lib: true do { provider: 'saml', extern_uid: uid } ]) end + + it 'saves successfully on subsequent tries, when both identities are present' do + saml_user.save + local_saml_user = described_class.new(auth_hash) + local_saml_user.save + + expect(local_saml_user.gl_user).to be_valid + expect(local_saml_user.gl_user).to be_persisted + end end context 'user has SAML user, and wants to add their LDAP identity' do diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb index efbcbf72f76..89730ab8eb8 100644 --- a/spec/models/concerns/issuable_spec.rb +++ b/spec/models/concerns/issuable_spec.rb @@ -154,6 +154,20 @@ describe Issue, "Issuable" do expect(issues).to match_array([issue1, issue2, issue, issue3]) end end + + context 'when all of the results are level on the sort key' do + let!(:issues) do + 10.times { create(:issue, project: project) } + end + + it 'has no duplicates across pages' do + sorted_issue_ids = 1.upto(10).map do |i| + project.issues.sort('milestone_due_desc').page(i).per(1).first.id + end + + expect(sorted_issue_ids).to eq(sorted_issue_ids.uniq) + end + end end diff --git a/spec/models/project_services/bugzilla_service_spec.rb b/spec/models/project_services/bugzilla_service_spec.rb new file mode 100644 index 00000000000..a6d9717ccb5 --- /dev/null +++ b/spec/models/project_services/bugzilla_service_spec.rb @@ -0,0 +1,49 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# note_events :boolean default(TRUE), not null +# + +require 'spec_helper' + +describe BugzillaService, models: true do + describe 'Associations' do + it { is_expected.to belong_to :project } + it { is_expected.to have_one :service_hook } + end + + describe 'Validations' do + context 'when service is active' do + before { subject.active = true } + + it { is_expected.to validate_presence_of(:project_url) } + it { is_expected.to validate_presence_of(:issues_url) } + it { is_expected.to validate_presence_of(:new_issue_url) } + it_behaves_like 'issue tracker service URL attribute', :project_url + it_behaves_like 'issue tracker service URL attribute', :issues_url + it_behaves_like 'issue tracker service URL attribute', :new_issue_url + end + + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:project_url) } + it { is_expected.not_to validate_presence_of(:issues_url) } + it { is_expected.not_to validate_presence_of(:new_issue_url) } + end + end +end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 53c8408633c..d305cd9ff1e 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -63,6 +63,27 @@ describe Project, models: true do expect(project2).not_to be_valid expect(project2.errors[:limit_reached].first).to match(/Personal project creation is not allowed/) end + + describe 'wiki path conflict' do + context "when the new path has been used by the wiki of other Project" do + it 'should have an error on the name attribute' do + new_project = build_stubbed(:project, namespace_id: project.namespace_id, path: "#{project.path}.wiki") + + expect(new_project).not_to be_valid + expect(new_project.errors[:name].first).to eq('has already been taken') + end + end + + context "when the new wiki path has been used by the path of other Project" do + it 'should have an error on the name attribute' do + project_with_wiki_suffix = create(:project, path: 'foo.wiki') + new_project = build_stubbed(:project, namespace_id: project_with_wiki_suffix.namespace_id, path: 'foo') + + expect(new_project).not_to be_valid + expect(new_project.errors[:name].first).to eq('has already been taken') + end + end + end end describe 'default_scope' do diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb index 789816bf2c7..0621c6a06ce 100644 --- a/spec/models/snippet_spec.rb +++ b/spec/models/snippet_spec.rb @@ -72,7 +72,7 @@ describe Snippet, models: true do end end - describe '#search_code' do + describe '.search_code' do let(:snippet) { create(:snippet, content: 'class Foo; end') } it 'returns snippets with matching content' do @@ -88,6 +88,46 @@ describe Snippet, models: true do end end + describe '.accessible_to' do + let(:author) { create(:author) } + let(:project) { create(:empty_project) } + + let!(:public_snippet) { create(:snippet, :public) } + let!(:internal_snippet) { create(:snippet, :internal) } + let!(:private_snippet) { create(:snippet, :private, author: author) } + + let!(:project_public_snippet) { create(:snippet, :public, project: project) } + let!(:project_internal_snippet) { create(:snippet, :internal, project: project) } + let!(:project_private_snippet) { create(:snippet, :private, project: project) } + + it 'returns only public snippets when user is blank' do + expect(described_class.accessible_to(nil)).to match_array [public_snippet, project_public_snippet] + end + + it 'returns only public, and internal snippets for regular users' do + user = create(:user) + + expect(described_class.accessible_to(user)).to match_array [public_snippet, internal_snippet, project_public_snippet, project_internal_snippet] + end + + it 'returns public, internal snippets and project private snippets for project members' do + member = create(:user) + project.team << [member, :developer] + + expect(described_class.accessible_to(member)).to match_array [public_snippet, internal_snippet, project_public_snippet, project_internal_snippet, project_private_snippet] + end + + it 'returns private snippets where the user is the author' do + expect(described_class.accessible_to(author)).to match_array [public_snippet, internal_snippet, private_snippet, project_public_snippet, project_internal_snippet] + end + + it 'returns all snippets when for admins' do + admin = create(:admin) + + expect(described_class.accessible_to(admin)).to match_array [public_snippet, internal_snippet, private_snippet, project_public_snippet, project_internal_snippet, project_private_snippet] + end + end + describe '#participants' do let(:project) { create(:project, :public) } let(:snippet) { create(:snippet, content: 'foo', project: project) } diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 73bee535fe3..328254ed56b 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -31,6 +31,26 @@ describe User, models: true do it { is_expected.to have_many(:spam_logs).dependent(:destroy) } it { is_expected.to have_many(:todos).dependent(:destroy) } it { is_expected.to have_many(:award_emoji).dependent(:destroy) } + + describe '#group_members' do + it 'does not include group memberships for which user is a requester' do + user = create(:user) + group = create(:group, :public) + group.request_access(user) + + expect(user.group_members).to be_empty + end + end + + describe '#project_members' do + it 'does not include project memberships for which user is a requester' do + user = create(:user) + project = create(:project, :public) + project.request_access(user) + + expect(user.project_members).to be_empty + end + end end describe 'validations' do diff --git a/spec/requests/api/award_emoji_spec.rb b/spec/requests/api/award_emoji_spec.rb index 2e65e7f1920..ed78d582bd0 100644 --- a/spec/requests/api/award_emoji_spec.rb +++ b/spec/requests/api/award_emoji_spec.rb @@ -17,7 +17,7 @@ describe API::API, api: true do it "returns an array of award_emoji" do get api("/projects/#{project.id}/issues/#{issue.id}/award_emoji", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['name']).to eq(award_emoji.name) end @@ -25,7 +25,7 @@ describe API::API, api: true do it "should return a 404 error when issue id not found" do get api("/projects/#{project.id}/issues/12345/award_emoji", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -33,7 +33,7 @@ describe API::API, api: true do it "returns an array of award_emoji" do get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/award_emoji", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['name']).to eq(downvote.name) end @@ -45,7 +45,7 @@ describe API::API, api: true do get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/award_emoji", user1) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -56,7 +56,7 @@ describe API::API, api: true do it 'returns an array of award emoji' do get api("/projects/#{project.id}/issues/#{issue.id}/notes/#{note.id}/award_emoji", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['name']).to eq(rocket.name) end @@ -68,7 +68,7 @@ describe API::API, api: true do it "returns the award emoji" do get api("/projects/#{project.id}/issues/#{issue.id}/award_emoji/#{award_emoji.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(award_emoji.name) expect(json_response['awardable_id']).to eq(issue.id) expect(json_response['awardable_type']).to eq("Issue") @@ -77,7 +77,7 @@ describe API::API, api: true do it "returns a 404 error if the award is not found" do get api("/projects/#{project.id}/issues/#{issue.id}/award_emoji/12345", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -85,7 +85,7 @@ describe API::API, api: true do it 'returns the award emoji' do get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/award_emoji/#{downvote.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(downvote.name) expect(json_response['awardable_id']).to eq(merge_request.id) expect(json_response['awardable_type']).to eq("MergeRequest") @@ -98,7 +98,7 @@ describe API::API, api: true do get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/award_emoji/#{downvote.id}", user1) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -109,7 +109,7 @@ describe API::API, api: true do it 'returns an award emoji' do get api("/projects/#{project.id}/issues/#{issue.id}/notes/#{note.id}/award_emoji/#{rocket.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).not_to be_an Array expect(json_response['name']).to eq(rocket.name) end @@ -120,7 +120,7 @@ describe API::API, api: true do it "creates a new award emoji" do post api("/projects/#{project.id}/issues/#{issue.id}/award_emoji", user), name: 'blowfish' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['name']).to eq('blowfish') expect(json_response['user']['username']).to eq(user.username) end @@ -128,13 +128,13 @@ describe API::API, api: true do it "should return a 400 bad request error if the name is not given" do post api("/projects/#{project.id}/issues/#{issue.id}/award_emoji", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return a 401 unauthorized error if the user is not authenticated" do post api("/projects/#{project.id}/issues/#{issue.id}/award_emoji"), name: 'thumbsup' - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -145,7 +145,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/issues/#{issue.id}/notes/#{note.id}/award_emoji", user), name: 'rocket' end.to change { note.award_emoji.count }.from(0).to(1) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['user']['username']).to eq(user.username) end end @@ -157,13 +157,13 @@ describe API::API, api: true do delete api("/projects/#{project.id}/issues/#{issue.id}/award_emoji/#{award_emoji.id}", user) end.to change { issue.award_emoji.count }.from(1).to(0) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'returns a 404 error when the award emoji can not be found' do delete api("/projects/#{project.id}/issues/#{issue.id}/award_emoji/12345", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -173,13 +173,13 @@ describe API::API, api: true do delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}/award_emoji/#{downvote.id}", user) end.to change { merge_request.award_emoji.count }.from(1).to(0) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'returns a 404 error when note id not found' do delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}/notes/12345", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -192,7 +192,7 @@ describe API::API, api: true do delete api("/projects/#{project.id}/issues/#{issue.id}/notes/#{note.id}/award_emoji/#{rocket.id}", user) end.to change { note.award_emoji.count }.from(1).to(0) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb index 55582aa53d2..b11ca26ee68 100644 --- a/spec/requests/api/branches_spec.rb +++ b/spec/requests/api/branches_spec.rb @@ -17,7 +17,7 @@ describe API::API, api: true do project.repository.expire_cache get api("/projects/#{project.id}/repository/branches", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array branch_names = json_response.map { |x| x['name'] } expect(branch_names).to match_array(project.repository.branch_names) @@ -27,7 +27,7 @@ describe API::API, api: true do describe "GET /projects/:id/repository/branches/:branch" do it "should return the branch information for a single branch" do get api("/projects/#{project.id}/repository/branches/#{branch_name}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(branch_name) expect(json_response['commit']['id']).to eq(branch_sha) @@ -36,19 +36,19 @@ describe API::API, api: true do it "should return a 403 error if guest" do get api("/projects/#{project.id}/repository/branches", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it "should return a 404 error if branch is not available" do get api("/projects/#{project.id}/repository/branches/unknown", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end describe "PUT /projects/:id/repository/branches/:branch/protect" do it "should protect a single branch" do put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(branch_name) expect(json_response['commit']['id']).to eq(branch_sha) @@ -57,25 +57,25 @@ describe API::API, api: true do it "should return a 404 error if branch not found" do put api("/projects/#{project.id}/repository/branches/unknown/protect", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should return a 403 error if guest" do put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it "should return success when protect branch again" do put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user) put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end describe "PUT /projects/:id/repository/branches/:branch/unprotect" do it "should unprotect a single branch" do put api("/projects/#{project.id}/repository/branches/#{branch_name}/unprotect", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(branch_name) expect(json_response['commit']['id']).to eq(branch_sha) @@ -84,13 +84,13 @@ describe API::API, api: true do it "should return success when unprotect branch" do put api("/projects/#{project.id}/repository/branches/unknown/unprotect", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should return success when unprotect branch again" do put api("/projects/#{project.id}/repository/branches/#{branch_name}/unprotect", user) put api("/projects/#{project.id}/repository/branches/#{branch_name}/unprotect", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -100,7 +100,7 @@ describe API::API, api: true do branch_name: 'feature1', ref: branch_sha - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['name']).to eq('feature1') expect(json_response['commit']['id']).to eq(branch_sha) @@ -110,14 +110,14 @@ describe API::API, api: true do post api("/projects/#{project.id}/repository/branches", user2), branch_name: branch_name, ref: branch_sha - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it 'should return 400 if branch name is invalid' do post api("/projects/#{project.id}/repository/branches", user), branch_name: 'new design', ref: branch_sha - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('Branch name is invalid') end @@ -125,12 +125,12 @@ describe API::API, api: true do post api("/projects/#{project.id}/repository/branches", user), branch_name: 'new_design1', ref: branch_sha - expect(response.status).to eq(201) + expect(response).to have_http_status(201) post api("/projects/#{project.id}/repository/branches", user), branch_name: 'new_design1', ref: branch_sha - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('Branch already exists') end @@ -138,7 +138,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/repository/branches", user), branch_name: 'new_design3', ref: 'foo' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('Invalid reference name') end end @@ -150,25 +150,25 @@ describe API::API, api: true do it "should remove branch" do delete api("/projects/#{project.id}/repository/branches/#{branch_name}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['branch_name']).to eq(branch_name) end it 'should return 404 if branch not exists' do delete api("/projects/#{project.id}/repository/branches/foobar", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should remove protected branch" do project.protected_branches.create(name: branch_name) delete api("/projects/#{project.id}/repository/branches/#{branch_name}", user) - expect(response.status).to eq(405) + expect(response).to have_http_status(405) expect(json_response['message']).to eq('Protected branch cant be removed') end it "should not remove HEAD branch" do delete api("/projects/#{project.id}/repository/branches/master", user) - expect(response.status).to eq(405) + expect(response).to have_http_status(405) expect(json_response['message']).to eq('Cannot remove HEAD branch') end end diff --git a/spec/requests/api/builds_spec.rb b/spec/requests/api/builds_spec.rb index 47e9253a10c..f5b39c3d698 100644 --- a/spec/requests/api/builds_spec.rb +++ b/spec/requests/api/builds_spec.rb @@ -19,7 +19,7 @@ describe API::API, api: true do context 'authorized user' do it 'should return project builds' do - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array end @@ -32,7 +32,7 @@ describe API::API, api: true do let(:query) { 'scope=pending' } it do - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array end end @@ -41,7 +41,7 @@ describe API::API, api: true do let(:query) { 'scope[0]=pending&scope[1]=running' } it do - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array end end @@ -49,7 +49,7 @@ describe API::API, api: true do context 'respond 400 when scope contains invalid state' do let(:query) { 'scope[0]=pending&scope[1]=unknown_status' } - it { expect(response.status).to eq(400) } + it { expect(response).to have_http_status(400) } end end @@ -57,29 +57,66 @@ describe API::API, api: true do let(:api_user) { nil } it 'should not return project builds' do - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end describe 'GET /projects/:id/repository/commits/:sha/builds' do - before do - project.ensure_pipeline(pipeline.sha, 'master') - get api("/projects/#{project.id}/repository/commits/#{pipeline.sha}/builds", api_user) - end + context 'when commit does not exist in repository' do + before do + get api("/projects/#{project.id}/repository/commits/1a271fd1/builds", api_user) + end - context 'authorized user' do - it 'should return project builds for specific commit' do - expect(response.status).to eq(200) - expect(json_response).to be_an Array + it 'responds with 404' do + expect(response).to have_http_status(404) end end - context 'unauthorized user' do - let(:api_user) { nil } + context 'when commit exists in repository' do + context 'when user is authorized' do + context 'when pipeline has builds' do + before do + create(:ci_pipeline, project: project, sha: project.commit.id) + create(:ci_build, pipeline: pipeline) + create(:ci_build) + + get api("/projects/#{project.id}/repository/commits/#{project.commit.id}/builds", api_user) + end + + it 'should return project builds for specific commit' do + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.size).to eq 2 + end + end - it 'should not return project builds' do - expect(response.status).to eq(401) + context 'when pipeline has no builds' do + before do + branch_head = project.commit('feature').id + get api("/projects/#{project.id}/repository/commits/#{branch_head}/builds", api_user) + end + + it 'returns an empty array' do + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response).to be_empty + end + end + end + + context 'when user is not authorized' do + before do + create(:ci_pipeline, project: project, sha: project.commit.id) + create(:ci_build, pipeline: pipeline) + + get api("/projects/#{project.id}/repository/commits/#{project.commit.id}/builds", nil) + end + + it 'should not return project builds' do + expect(response).to have_http_status(401) + expect(json_response.except('message')).to be_empty + end end end end @@ -89,7 +126,7 @@ describe API::API, api: true do context 'authorized user' do it 'should return specific build data' do - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq('test') end end @@ -98,7 +135,7 @@ describe API::API, api: true do let(:api_user) { nil } it 'should not return specific build data' do - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -116,7 +153,7 @@ describe API::API, api: true do end it 'should return specific build artifacts' do - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(response.headers).to include(download_headers) end end @@ -125,13 +162,13 @@ describe API::API, api: true do let(:api_user) { nil } it 'should not return specific build artifacts' do - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end it 'should not return build artifacts if not uploaded' do - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -142,7 +179,7 @@ describe API::API, api: true do context 'authorized user' do it 'should return specific build trace' do - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(response.body).to eq(build.trace) end end @@ -151,7 +188,7 @@ describe API::API, api: true do let(:api_user) { nil } it 'should not return specific build trace' do - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -162,7 +199,7 @@ describe API::API, api: true do context 'authorized user' do context 'user with :update_build persmission' do it 'should cancel running or pending build' do - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(project.builds.first.status).to eq('canceled') end end @@ -171,7 +208,7 @@ describe API::API, api: true do let(:api_user) { user2 } it 'should not cancel build' do - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end end @@ -180,7 +217,7 @@ describe API::API, api: true do let(:api_user) { nil } it 'should not cancel build' do - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -193,7 +230,7 @@ describe API::API, api: true do context 'authorized user' do context 'user with :update_build permission' do it 'should retry non-running build' do - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(project.builds.first.status).to eq('canceled') expect(json_response['status']).to eq('pending') end @@ -203,7 +240,7 @@ describe API::API, api: true do let(:api_user) { user2 } it 'should not retry build' do - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end end @@ -212,7 +249,7 @@ describe API::API, api: true do let(:api_user) { nil } it 'should not retry build' do - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end diff --git a/spec/requests/api/commit_statuses_spec.rb b/spec/requests/api/commit_statuses_spec.rb index 298cdbad329..6668f3543f6 100644 --- a/spec/requests/api/commit_statuses_spec.rb +++ b/spec/requests/api/commit_statuses_spec.rb @@ -41,7 +41,7 @@ describe API::CommitStatuses, api: true do before { get api(get_url, reporter) } it 'returns latest commit statuses' do - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(statuses_id).to contain_exactly(status3.id, status4.id, status5.id, status6.id) @@ -54,7 +54,7 @@ describe API::CommitStatuses, api: true do before { get api(get_url, reporter), all: 1 } it 'returns all commit statuses' do - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(statuses_id).to contain_exactly(status1.id, status2.id, @@ -67,7 +67,7 @@ describe API::CommitStatuses, api: true do before { get api(get_url, reporter), ref: 'develop' } it 'returns latest commit statuses for specific ref' do - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(statuses_id).to contain_exactly(status3.id, status5.id) @@ -78,7 +78,7 @@ describe API::CommitStatuses, api: true do before { get api(get_url, reporter), name: 'coverage' } it 'return latest commit statuses for specific name' do - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(statuses_id).to contain_exactly(status4.id, status5.id) @@ -101,7 +101,7 @@ describe API::CommitStatuses, api: true do before { get api(get_url, guest) } it "should not return project commits" do - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -109,7 +109,7 @@ describe API::CommitStatuses, api: true do before { get api(get_url) } it "should not return project commits" do - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -122,7 +122,7 @@ describe API::CommitStatuses, api: true do before { post api(post_url, developer), state: 'success' } it 'creates commit status' do - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['sha']).to eq(commit.id) expect(json_response['status']).to eq('success') expect(json_response['name']).to eq('default') @@ -141,7 +141,7 @@ describe API::CommitStatuses, api: true do end it 'creates commit status' do - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['sha']).to eq(commit.id) expect(json_response['status']).to eq('success') expect(json_response['name']).to eq('coverage') @@ -155,7 +155,7 @@ describe API::CommitStatuses, api: true do before { post api(post_url, developer), state: 'invalid' } it 'does not create commit status' do - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end @@ -163,7 +163,7 @@ describe API::CommitStatuses, api: true do before { post api(post_url, developer) } it 'does not create commit status' do - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end @@ -172,7 +172,7 @@ describe API::CommitStatuses, api: true do before { post api(post_url, developer), state: 'running' } it 'returns not found error' do - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -181,7 +181,7 @@ describe API::CommitStatuses, api: true do before { post api(post_url, reporter) } it 'should not create commit status' do - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -189,7 +189,7 @@ describe API::CommitStatuses, api: true do before { post api(post_url, guest) } it 'should not create commit status' do - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -197,7 +197,7 @@ describe API::CommitStatuses, api: true do before { post api(post_url) } it 'should not create commit status' do - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index 6fc38f537d3..5219c808791 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -19,7 +19,7 @@ describe API::API, api: true do it "should return project commits" do get api("/projects/#{project.id}/repository/commits", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['id']).to eq(project.repository.commit.id) @@ -29,7 +29,7 @@ describe API::API, api: true do context "unauthorized user" do it "should not return project commits" do get api("/projects/#{project.id}/repository/commits") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end @@ -63,7 +63,7 @@ describe API::API, api: true do it "should return an invalid parameter error message" do get api("/projects/#{project.id}/repository/commits?since=invalid-date", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to include "\"since\" must be a timestamp in ISO 8601 format" end end @@ -73,26 +73,26 @@ describe API::API, api: true do context "authorized user" do it "should return a commit by sha" do get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['id']).to eq(project.repository.commit.id) expect(json_response['title']).to eq(project.repository.commit.title) end it "should return a 404 error if not found" do get api("/projects/#{project.id}/repository/commits/invalid_sha", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should return nil for commit without CI" do get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['status']).to be_nil end it "should return status for CI" do pipeline = project.ensure_pipeline(project.repository.commit.sha, 'master') get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['status']).to eq(pipeline.status) end end @@ -100,7 +100,7 @@ describe API::API, api: true do context "unauthorized user" do it "should not return the selected commit" do get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -111,7 +111,7 @@ describe API::API, api: true do it "should return the diff of the selected commit" do get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/diff", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to be >= 1 @@ -120,14 +120,14 @@ describe API::API, api: true do it "should return a 404 error if invalid commit" do get api("/projects/#{project.id}/repository/commits/invalid_sha/diff", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end context "unauthorized user" do it "should not return the diff of the selected commit" do get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/diff") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -136,7 +136,7 @@ describe API::API, api: true do context 'authorized user' do it 'should return merge_request comments' do get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(2) expect(json_response.first['note']).to eq('a comment on a commit') @@ -145,14 +145,14 @@ describe API::API, api: true do it 'should return a 404 error if merge_request_id not found' do get api("/projects/#{project.id}/repository/commits/1234ab/comments", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end context 'unauthorized user' do it 'should not return the diff of the selected commit' do get api("/projects/#{project.id}/repository/commits/1234ab/comments") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -161,7 +161,7 @@ describe API::API, api: true do context 'authorized user' do it 'should return comment' do post api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user), note: 'My comment' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['note']).to eq('My comment') expect(json_response['path']).to be_nil expect(json_response['line']).to be_nil @@ -170,7 +170,7 @@ describe API::API, api: true do it 'should return the inline comment' do post api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user), note: 'My comment', path: project.repository.commit.diffs.first.new_path, line: 7, line_type: 'new' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['note']).to eq('My comment') expect(json_response['path']).to eq(project.repository.commit.diffs.first.new_path) expect(json_response['line']).to eq(7) @@ -179,19 +179,19 @@ describe API::API, api: true do it 'should return 400 if note is missing' do post api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it 'should return 404 if note is attached to non existent commit' do post api("/projects/#{project.id}/repository/commits/1234ab/comments", user), note: 'My comment' - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end context 'unauthorized user' do it 'should not return the diff of the selected commit' do post api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}/comments") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end diff --git a/spec/requests/api/doorkeeper_access_spec.rb b/spec/requests/api/doorkeeper_access_spec.rb index 0afc3e79339..881b818b5e9 100644 --- a/spec/requests/api/doorkeeper_access_spec.rb +++ b/spec/requests/api/doorkeeper_access_spec.rb @@ -11,21 +11,21 @@ describe API::API, api: true do describe "when unauthenticated" do it "returns authentication success" do get api("/user"), access_token: token.token - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end describe "when token invalid" do it "returns authentication error" do get api("/user"), access_token: "123a" - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end describe "authorization by private token" do it "returns authentication success" do get api("/user", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb index 8efa09f75fd..2e5448143d5 100644 --- a/spec/requests/api/files_spec.rb +++ b/spec/requests/api/files_spec.rb @@ -16,7 +16,7 @@ describe API::API, api: true do } get api("/projects/#{project.id}/repository/files", user), params - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['file_path']).to eq(file_path) expect(json_response['file_name']).to eq('popen.rb') expect(json_response['last_commit_id']).to eq('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') @@ -25,7 +25,7 @@ describe API::API, api: true do it "should return a 400 bad request if no params given" do get api("/projects/#{project.id}/repository/files", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return a 404 if such file does not exist" do @@ -35,7 +35,7 @@ describe API::API, api: true do } get api("/projects/#{project.id}/repository/files", user), params - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -51,13 +51,13 @@ describe API::API, api: true do it "should create a new file in project repo" do post api("/projects/#{project.id}/repository/files", user), valid_params - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['file_path']).to eq('newfile.rb') end it "should return a 400 bad request if no params given" do post api("/projects/#{project.id}/repository/files", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return a 400 if editor fails to create file" do @@ -65,7 +65,7 @@ describe API::API, api: true do and_return(false) post api("/projects/#{project.id}/repository/files", user), valid_params - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end @@ -81,13 +81,13 @@ describe API::API, api: true do it "should update existing file in project repo" do put api("/projects/#{project.id}/repository/files", user), valid_params - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['file_path']).to eq(file_path) end it "should return a 400 bad request if no params given" do put api("/projects/#{project.id}/repository/files", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end @@ -102,20 +102,20 @@ describe API::API, api: true do it "should delete existing file in project repo" do delete api("/projects/#{project.id}/repository/files", user), valid_params - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['file_path']).to eq(file_path) end it "should return a 400 bad request if no params given" do delete api("/projects/#{project.id}/repository/files", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return a 400 if fails to create file" do allow_any_instance_of(Repository).to receive(:remove_file).and_return(false) delete api("/projects/#{project.id}/repository/files", user), valid_params - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end @@ -143,7 +143,7 @@ describe API::API, api: true do it "remains unchanged" do get api("/projects/#{project.id}/repository/files", user), get_params - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['file_path']).to eq(file_path) expect(json_response['file_name']).to eq(file_path) expect(json_response['content']).to eq(put_params[:content]) diff --git a/spec/requests/api/fork_spec.rb b/spec/requests/api/fork_spec.rb index fa94e03ec32..a9f5aa924b7 100644 --- a/spec/requests/api/fork_spec.rb +++ b/spec/requests/api/fork_spec.rb @@ -22,7 +22,7 @@ describe API::API, api: true do context 'when authenticated' do it 'should fork if user has sufficient access to project' do post api("/projects/fork/#{project.id}", user2) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['name']).to eq(project.name) expect(json_response['path']).to eq(project.path) expect(json_response['owner']['id']).to eq(user2.id) @@ -32,7 +32,7 @@ describe API::API, api: true do it 'should fork if user is admin' do post api("/projects/fork/#{project.id}", admin) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['name']).to eq(project.name) expect(json_response['path']).to eq(project.path) expect(json_response['owner']['id']).to eq(admin.id) @@ -42,20 +42,20 @@ describe API::API, api: true do it 'should fail on missing project access for the project to fork' do post api("/projects/fork/#{project.id}", user3) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 Project Not Found') end it 'should fail if forked project exists in the user namespace' do post api("/projects/fork/#{project.id}", user) - expect(response.status).to eq(409) + expect(response).to have_http_status(409) expect(json_response['message']['name']).to eq(['has already been taken']) expect(json_response['message']['path']).to eq(['has already been taken']) end it 'should fail if project to fork from does not exist' do post api('/projects/fork/424242', user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 Project Not Found') end end @@ -63,7 +63,7 @@ describe API::API, api: true do context 'when unauthenticated' do it 'should return authentication error' do post api("/projects/fork/#{project.id}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) expect(json_response['message']).to eq('401 Unauthorized') end end diff --git a/spec/requests/api/group_members_spec.rb b/spec/requests/api/group_members_spec.rb index 02553d0f8e2..52f9e7d4681 100644 --- a/spec/requests/api/group_members_spec.rb +++ b/spec/requests/api/group_members_spec.rb @@ -31,7 +31,7 @@ describe API::API, api: true do it "each user: should return an array of members groups of group3" do [owner, master, developer, reporter, guest].each do |user| get api("/groups/#{group_with_members.id}/members", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.size).to eq(5) expect(json_response.find { |e| e['id'] == owner.id }['access_level']).to eq(GroupMember::OWNER) @@ -45,7 +45,7 @@ describe API::API, api: true do it 'users not part of the group should get access error' do get api("/groups/#{group_with_members.id}/members", stranger) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -54,7 +54,7 @@ describe API::API, api: true do context "when not a member of the group" do it "should not add guest as member of group_no_members when adding being done by person outside the group" do post api("/groups/#{group_no_members.id}/members", reporter), user_id: guest.id, access_level: GroupMember::MASTER - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -66,7 +66,7 @@ describe API::API, api: true do post api("/groups/#{group_no_members.id}/members", owner), user_id: new_user.id, access_level: GroupMember::MASTER end.to change { group_no_members.members.count }.by(1) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['name']).to eq(new_user.name) expect(json_response['access_level']).to eq(GroupMember::MASTER) end @@ -78,27 +78,27 @@ describe API::API, api: true do post api("/groups/#{group_with_members.id}/members", guest), user_id: new_user.id, access_level: GroupMember::MASTER end.not_to change { group_with_members.members.count } - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it "should return error if member already exists" do post api("/groups/#{group_with_members.id}/members", owner), user_id: master.id, access_level: GroupMember::MASTER - expect(response.status).to eq(409) + expect(response).to have_http_status(409) end it "should return a 400 error when user id is not given" do post api("/groups/#{group_no_members.id}/members", owner), access_level: GroupMember::MASTER - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return a 400 error when access level is not given" do post api("/groups/#{group_no_members.id}/members", owner), user_id: master.id - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return a 422 error when access level is not known" do post api("/groups/#{group_no_members.id}/members", owner), user_id: master.id, access_level: 1234 - expect(response.status).to eq(422) + expect(response).to have_http_status(422) end end end @@ -110,7 +110,7 @@ describe API::API, api: true do api("/groups/#{group_no_members.id}/members/#{developer.id}", owner), access_level: GroupMember::MASTER ) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -122,7 +122,7 @@ describe API::API, api: true do access_level: GroupMember::MASTER ) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) get api("/groups/#{group_with_members.id}/members", owner) json_reporter = json_response.find do |e| @@ -139,7 +139,7 @@ describe API::API, api: true do access_level: GroupMember::MASTER ) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) get api("/groups/#{group_with_members.id}/members", owner) json_developer = json_response.find do |e| @@ -153,7 +153,7 @@ describe API::API, api: true do put( api("/groups/#{group_with_members.id}/members/#{master.id}", owner) ) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it 'should return a 422 error when access level is not known' do @@ -161,7 +161,7 @@ describe API::API, api: true do api("/groups/#{group_with_members.id}/members/#{master.id}", owner), access_level: 1234 ) - expect(response.status).to eq(422) + expect(response).to have_http_status(422) end end end @@ -172,7 +172,7 @@ describe API::API, api: true do random_user = create(:user) delete api("/groups/#{group_with_members.id}/members/#{owner.id}", random_user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -182,17 +182,17 @@ describe API::API, api: true do delete api("/groups/#{group_with_members.id}/members/#{guest.id}", owner) end.to change { group_with_members.members.count }.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "should return a 404 error when user id is not known" do delete api("/groups/#{group_with_members.id}/members/1328", owner) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should not allow guest to modify group members" do delete api("/groups/#{group_with_members.id}/members/#{master.id}", guest) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end end diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index 7ecefce80d6..04141a45031 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -23,14 +23,14 @@ describe API::API, api: true do context "when unauthenticated" do it "should return authentication error" do get api("/groups") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end context "when authenticated as user" do it "normal user: should return an array of groups of user1" do get api("/groups", user1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['name']).to eq(group1.name) @@ -40,7 +40,7 @@ describe API::API, api: true do context "when authenticated as admin" do it "admin: should return an array of all groups" do get api("/groups", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(2) end @@ -51,51 +51,51 @@ describe API::API, api: true do context "when authenticated as user" do it "should return one of user1's groups" do get api("/groups/#{group1.id}", user1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) json_response['name'] == group1.name end it "should not return a non existing group" do get api("/groups/1328", user1) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should not return a group not attached to user1" do get api("/groups/#{group2.id}", user1) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end context "when authenticated as admin" do it "should return any existing group" do get api("/groups/#{group2.id}", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(group2.name) end it "should not return a non existing group" do get api("/groups/1328", admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end context 'when using group path in URL' do it 'should return any existing group' do get api("/groups/#{group1.path}", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(group1.name) end it 'should not return a non existing group' do get api('/groups/unknown', admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'should not return a group not attached to user1' do get api("/groups/#{group2.path}", user1) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -107,14 +107,14 @@ describe API::API, api: true do it 'updates the group' do put api("/groups/#{group1.id}", user1), name: new_group_name - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(new_group_name) end it 'returns 404 for a non existing group' do put api('/groups/1328', user1) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -122,7 +122,7 @@ describe API::API, api: true do it 'updates the group' do put api("/groups/#{group1.id}", admin), name: new_group_name - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(new_group_name) end end @@ -131,7 +131,7 @@ describe API::API, api: true do it 'does not updates the group' do put api("/groups/#{group1.id}", user2), name: new_group_name - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -139,7 +139,7 @@ describe API::API, api: true do it 'returns 404 when trying to update the group' do put api("/groups/#{group2.id}", user1), name: new_group_name - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -149,7 +149,7 @@ describe API::API, api: true do it "should return the group's projects" do get api("/groups/#{group1.id}/projects", user1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response.length).to eq(2) project_names = json_response.map { |proj| proj['name' ] } expect(project_names).to match_array([project1.name, project3.name]) @@ -157,13 +157,13 @@ describe API::API, api: true do it "should not return a non existing group" do get api("/groups/1328/projects", user1) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should not return a group not attached to user1" do get api("/groups/#{group2.id}/projects", user1) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should only return projects to which user has access" do @@ -171,7 +171,7 @@ describe API::API, api: true do get api("/groups/#{group1.id}/projects", user3) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response.length).to eq(1) expect(json_response.first['name']).to eq(project3.name) end @@ -180,14 +180,14 @@ describe API::API, api: true do context "when authenticated as admin" do it "should return any existing group" do get api("/groups/#{group2.id}/projects", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response.length).to eq(1) expect(json_response.first['name']).to eq(project2.name) end it "should not return a non existing group" do get api("/groups/1328/projects", admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -195,20 +195,20 @@ describe API::API, api: true do it 'should return any existing group' do get api("/groups/#{group1.path}/projects", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) project_names = json_response.map { |proj| proj['name' ] } expect(project_names).to match_array([project1.name, project3.name]) end it 'should not return a non existing group' do get api('/groups/unknown/projects', admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'should not return a group not attached to user1' do get api("/groups/#{group2.path}/projects", user1) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -217,30 +217,30 @@ describe API::API, api: true do context "when authenticated as user without group permissions" do it "should not create group" do post api("/groups", user1), attributes_for(:group) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end context "when authenticated as user with group permissions" do it "should create group" do post api("/groups", user3), attributes_for(:group) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) end it "should not create group, duplicate" do post api("/groups", user3), { name: 'Duplicate Test', path: group2.path } - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(response.message).to eq("Bad Request") end it "should return 400 bad request error if name not given" do post api("/groups", user3), { path: group2.path } - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return 400 bad request error if path not given" do post api("/groups", user3), { name: 'test' } - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end end @@ -249,37 +249,37 @@ describe API::API, api: true do context "when authenticated as user" do it "should remove group" do delete api("/groups/#{group1.id}", user1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "should not remove a group if not an owner" do user4 = create(:user) group1.add_master(user4) delete api("/groups/#{group1.id}", user3) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it "should not remove a non existing group" do delete api("/groups/1328", user1) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should not remove a group not attached to user1" do delete api("/groups/#{group2.id}", user1) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end context "when authenticated as admin" do it "should remove any existing group" do delete api("/groups/#{group2.id}", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "should not remove a non existing group" do delete api("/groups/1328", admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -295,14 +295,14 @@ describe API::API, api: true do context "when authenticated as user" do it "should not transfer project to group" do post api("/groups/#{group1.id}/projects/#{project.id}", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end context "when authenticated as admin" do it "should transfer project to group" do post api("/groups/#{group1.id}/projects/#{project.id}", admin) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) end end end diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb index 22802dd0e05..437c89c3577 100644 --- a/spec/requests/api/internal_spec.rb +++ b/spec/requests/api/internal_spec.rb @@ -11,7 +11,7 @@ describe API::API, api: true do it do get api("/internal/check"), secret_token: secret_token - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['api_version']).to eq(API::API.version) end end @@ -23,7 +23,7 @@ describe API::API, api: true do it do get api("/internal/broadcast_message"), secret_token: secret_token - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["message"]).to eq(broadcast_message.message) end end @@ -32,7 +32,7 @@ describe API::API, api: true do it do get api("/internal/broadcast_message"), secret_token: secret_token - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_empty end end @@ -42,7 +42,7 @@ describe API::API, api: true do it do get(api("/internal/discover"), key_id: key.id, secret_token: secret_token) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(user.name) end @@ -61,7 +61,7 @@ describe API::API, api: true do push(key, project_wiki) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["status"]).to be_truthy end end @@ -70,7 +70,7 @@ describe API::API, api: true do it do pull(key, project) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["status"]).to be_truthy end end @@ -79,7 +79,7 @@ describe API::API, api: true do it do push(key, project) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["status"]).to be_truthy end end @@ -94,7 +94,7 @@ describe API::API, api: true do it do pull(key, project) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["status"]).to be_falsey end end @@ -103,7 +103,7 @@ describe API::API, api: true do it do push(key, project) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["status"]).to be_falsey end end @@ -120,7 +120,7 @@ describe API::API, api: true do it do pull(key, personal_project) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["status"]).to be_falsey end end @@ -129,7 +129,7 @@ describe API::API, api: true do it do push(key, personal_project) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["status"]).to be_falsey end end @@ -147,7 +147,7 @@ describe API::API, api: true do it do pull(key, project) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["status"]).to be_truthy end end @@ -156,7 +156,7 @@ describe API::API, api: true do it do push(key, project) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["status"]).to be_falsey end end @@ -173,7 +173,7 @@ describe API::API, api: true do it do archive(key, project) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["status"]).to be_truthy end end @@ -182,7 +182,7 @@ describe API::API, api: true do it do archive(key, project) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["status"]).to be_falsey end end @@ -192,7 +192,7 @@ describe API::API, api: true do it do pull(key, OpenStruct.new(path_with_namespace: 'gitlab/notexists')) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["status"]).to be_falsey end end @@ -201,7 +201,7 @@ describe API::API, api: true do it do pull(OpenStruct.new(id: 0), project) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["status"]).to be_falsey end end diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index 59e557c5b2a..2cf130df328 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -51,14 +51,14 @@ describe API::API, api: true do context "when unauthenticated" do it "should return authentication error" do get api("/issues") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end context "when authenticated" do it "should return an array of issues" do get api("/issues", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['title']).to eq(issue.title) end @@ -72,7 +72,7 @@ describe API::API, api: true do it 'should return an array of closed issues' do get api('/issues?state=closed', user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['id']).to eq(closed_issue.id) @@ -80,7 +80,7 @@ describe API::API, api: true do it 'should return an array of opened issues' do get api('/issues?state=opened', user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['id']).to eq(issue.id) @@ -88,7 +88,7 @@ describe API::API, api: true do it 'should return an array of all issues' do get api('/issues?state=all', user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(2) expect(json_response.first['id']).to eq(issue.id) @@ -97,7 +97,7 @@ describe API::API, api: true do it 'should return an array of labeled issues' do get api("/issues?labels=#{label.title}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['labels']).to eq([label.title]) @@ -105,7 +105,7 @@ describe API::API, api: true do it 'should return an array of labeled issues when at least one label matches' do get api("/issues?labels=#{label.title},foo,bar", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['labels']).to eq([label.title]) @@ -113,14 +113,14 @@ describe API::API, api: true do it 'should return an empty array if no issue matches labels' do get api('/issues?labels=foo,bar', user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(0) end it 'should return an array of labeled issues matching given state' do get api("/issues?labels=#{label.title}&state=opened", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['labels']).to eq([label.title]) @@ -129,20 +129,162 @@ describe API::API, api: true do it 'should return an empty array if no issue matches labels and state filters' do get api("/issues?labels=#{label.title}&state=closed", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(0) end end end + describe "GET /groups/:id/issues" do + let!(:group) { create(:group) } + let!(:group_project) { create(:project, :public, creator_id: user.id, namespace: group) } + let!(:group_closed_issue) do + create :closed_issue, + author: user, + assignee: user, + project: group_project, + state: :closed, + milestone: group_milestone + end + let!(:group_confidential_issue) do + create :issue, + :confidential, + project: group_project, + author: author, + assignee: assignee + end + let!(:group_issue) do + create :issue, + author: user, + assignee: user, + project: group_project, + milestone: group_milestone + end + let!(:group_label) do + create(:label, title: 'group_lbl', color: '#FFAABB', project: group_project) + end + let!(:group_label_link) { create(:label_link, label: group_label, target: group_issue) } + let!(:group_milestone) { create(:milestone, title: '3.0.0', project: group_project) } + let!(:group_empty_milestone) do + create(:milestone, title: '4.0.0', project: group_project) + end + let!(:group_note) { create(:note_on_issue, author: user, project: group_project, noteable: group_issue) } + + before do + group_project.team << [user, :reporter] + end + let(:base_url) { "/groups/#{group.id}/issues" } + + it 'returns group issues without confidential issues for non project members' do + get api(base_url, non_member) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['title']).to eq(group_issue.title) + end + + it 'returns group confidential issues for author' do + get api(base_url, author) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(2) + end + + it 'returns group confidential issues for assignee' do + get api(base_url, assignee) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(2) + end + + it 'returns group issues with confidential issues for project members' do + get api(base_url, user) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(2) + end + + it 'returns group confidential issues for admin' do + get api(base_url, admin) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(2) + end + + it 'returns an array of labeled group issues' do + get api("#{base_url}?labels=#{group_label.title}", user) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['labels']).to eq([group_label.title]) + end + + it 'returns an array of labeled group issues where all labels match' do + get api("#{base_url}?labels=#{group_label.title},foo,bar", user) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(0) + end + + it 'returns an empty array if no group issue matches labels' do + get api("#{base_url}?labels=foo,bar", user) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(0) + end + + it 'returns an empty array if no issue matches milestone' do + get api("#{base_url}?milestone=#{group_empty_milestone.title}", user) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(0) + end + + it 'returns an empty array if milestone does not exist' do + get api("#{base_url}?milestone=foo", user) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(0) + end + + it 'returns an array of issues in given milestone' do + get api("#{base_url}?milestone=#{group_milestone.title}", user) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['id']).to eq(group_issue.id) + end + + it 'returns an array of issues matching state in milestone' do + get api("#{base_url}?milestone=#{group_milestone.title}"\ + '&state=closed', user) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.length).to eq(1) + expect(json_response.first['id']).to eq(group_closed_issue.id) + end + end + describe "GET /projects/:id/issues" do let(:base_url) { "/projects/#{project.id}" } let(:title) { milestone.title } it 'should return project issues without confidential issues for non project members' do get api("#{base_url}/issues", non_member) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(2) expect(json_response.first['title']).to eq(issue.title) @@ -150,7 +292,7 @@ describe API::API, api: true do it 'should return project issues without confidential issues for project members with guest role' do get api("#{base_url}/issues", guest) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(2) expect(json_response.first['title']).to eq(issue.title) @@ -158,7 +300,7 @@ describe API::API, api: true do it 'should return project confidential issues for author' do get api("#{base_url}/issues", author) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(3) expect(json_response.first['title']).to eq(issue.title) @@ -166,7 +308,7 @@ describe API::API, api: true do it 'should return project confidential issues for assignee' do get api("#{base_url}/issues", assignee) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(3) expect(json_response.first['title']).to eq(issue.title) @@ -174,7 +316,7 @@ describe API::API, api: true do it 'should return project issues with confidential issues for project members' do get api("#{base_url}/issues", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(3) expect(json_response.first['title']).to eq(issue.title) @@ -182,7 +324,7 @@ describe API::API, api: true do it 'should return project confidential issues for admin' do get api("#{base_url}/issues", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(3) expect(json_response.first['title']).to eq(issue.title) @@ -190,7 +332,7 @@ describe API::API, api: true do it 'should return an array of labeled project issues' do get api("#{base_url}/issues?labels=#{label.title}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['labels']).to eq([label.title]) @@ -198,7 +340,7 @@ describe API::API, api: true do it 'should return an array of labeled project issues when at least one label matches' do get api("#{base_url}/issues?labels=#{label.title},foo,bar", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['labels']).to eq([label.title]) @@ -206,28 +348,28 @@ describe API::API, api: true do it 'should return an empty array if no project issue matches labels' do get api("#{base_url}/issues?labels=foo,bar", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(0) end it 'should return an empty array if no issue matches milestone' do get api("#{base_url}/issues?milestone=#{empty_milestone.title}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(0) end it 'should return an empty array if milestone does not exist' do get api("#{base_url}/issues?milestone=foo", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(0) end it 'should return an array of issues in given milestone' do get api("#{base_url}/issues?milestone=#{title}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(2) expect(json_response.first['id']).to eq(issue.id) @@ -237,7 +379,7 @@ describe API::API, api: true do it 'should return an array of issues matching state in milestone' do get api("#{base_url}/issues?milestone=#{milestone.title}"\ '&state=closed', user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['id']).to eq(closed_issue.id) @@ -248,7 +390,7 @@ describe API::API, api: true do it 'exposes known attributes' do get api("/projects/#{project.id}/issues/#{issue.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['id']).to eq(issue.id) expect(json_response['iid']).to eq(issue.iid) expect(json_response['project_id']).to eq(issue.project.id) @@ -266,7 +408,7 @@ describe API::API, api: true do it "should return a project issue by id" do get api("/projects/#{project.id}/issues/#{issue.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq(issue.title) expect(json_response['iid']).to eq(issue.iid) end @@ -281,44 +423,44 @@ describe API::API, api: true do it "should return 404 if issue id not found" do get api("/projects/#{project.id}/issues/54321", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end context 'confidential issues' do it "should return 404 for non project members" do get api("/projects/#{project.id}/issues/#{confidential_issue.id}", non_member) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should return 404 for project members with guest role" do get api("/projects/#{project.id}/issues/#{confidential_issue.id}", guest) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should return confidential issue for project members" do get api("/projects/#{project.id}/issues/#{confidential_issue.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq(confidential_issue.title) expect(json_response['iid']).to eq(confidential_issue.iid) end it "should return confidential issue for author" do get api("/projects/#{project.id}/issues/#{confidential_issue.id}", author) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq(confidential_issue.title) expect(json_response['iid']).to eq(confidential_issue.iid) end it "should return confidential issue for assignee" do get api("/projects/#{project.id}/issues/#{confidential_issue.id}", assignee) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq(confidential_issue.title) expect(json_response['iid']).to eq(confidential_issue.iid) end it "should return confidential issue for admin" do get api("/projects/#{project.id}/issues/#{confidential_issue.id}", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq(confidential_issue.title) expect(json_response['iid']).to eq(confidential_issue.iid) end @@ -329,7 +471,7 @@ describe API::API, api: true do it "should create a new project issue" do post api("/projects/#{project.id}/issues", user), title: 'new issue', labels: 'label, label2' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['title']).to eq('new issue') expect(json_response['description']).to be_nil expect(json_response['labels']).to eq(['label', 'label2']) @@ -337,21 +479,21 @@ describe API::API, api: true do it "should return a 400 bad request if title not given" do post api("/projects/#{project.id}/issues", user), labels: 'label, label2' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it 'should return 400 on invalid label names' do post api("/projects/#{project.id}/issues", user), title: 'new issue', labels: 'label, ?' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['labels']['?']['title']).to eq(['is invalid']) end it 'should return 400 if title is too long' do post api("/projects/#{project.id}/issues", user), title: 'g' * 256 - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['title']).to eq([ 'is too long (maximum is 255 characters)' ]) @@ -363,7 +505,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/issues", user), title: 'new issue', labels: 'label, label2', created_at: creation_time - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(Time.parse(json_response['created_at'])).to be_within(1.second).of(creation_time) end end @@ -387,7 +529,7 @@ describe API::API, api: true do it "should not create a new project issue" do expect { post api("/projects/#{project.id}/issues", user), params }.not_to change(Issue, :count) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq({ "error" => "Spam detected" }) spam_logs = SpamLog.all @@ -404,7 +546,7 @@ describe API::API, api: true do it "should update a project issue" do put api("/projects/#{project.id}/issues/#{issue.id}", user), title: 'updated title' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq('updated title') end @@ -412,14 +554,14 @@ describe API::API, api: true do it "should return 404 error if issue id not found" do put api("/projects/#{project.id}/issues/44444", user), title: 'updated title' - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'should return 400 on invalid label names' do put api("/projects/#{project.id}/issues/#{issue.id}", user), title: 'updated title', labels: 'label, ?' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['labels']['?']['title']).to eq(['is invalid']) end @@ -427,33 +569,33 @@ describe API::API, api: true do it "should return 403 for non project members" do put api("/projects/#{project.id}/issues/#{confidential_issue.id}", non_member), title: 'updated title' - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it "should return 403 for project members with guest role" do put api("/projects/#{project.id}/issues/#{confidential_issue.id}", guest), title: 'updated title' - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it "should update a confidential issue for project members" do put api("/projects/#{project.id}/issues/#{confidential_issue.id}", user), title: 'updated title' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq('updated title') end it "should update a confidential issue for author" do put api("/projects/#{project.id}/issues/#{confidential_issue.id}", author), title: 'updated title' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq('updated title') end it "should update a confidential issue for admin" do put api("/projects/#{project.id}/issues/#{confidential_issue.id}", admin), title: 'updated title' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq('updated title') end end @@ -466,21 +608,21 @@ describe API::API, api: true do it 'should not update labels if not present' do put api("/projects/#{project.id}/issues/#{issue.id}", user), title: 'updated title' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['labels']).to eq([label.title]) end it 'should remove all labels' do put api("/projects/#{project.id}/issues/#{issue.id}", user), labels: '' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['labels']).to eq([]) end it 'should update labels' do put api("/projects/#{project.id}/issues/#{issue.id}", user), labels: 'foo,bar' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['labels']).to include 'foo' expect(json_response['labels']).to include 'bar' end @@ -488,14 +630,14 @@ describe API::API, api: true do it 'should return 400 on invalid label names' do put api("/projects/#{project.id}/issues/#{issue.id}", user), labels: 'label, ?' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['labels']['?']['title']).to eq(['is invalid']) end it 'should allow special label names' do put api("/projects/#{project.id}/issues/#{issue.id}", user), labels: 'label:foo, label-bar,label_bar,label/bar' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['labels']).to include 'label:foo' expect(json_response['labels']).to include 'label-bar' expect(json_response['labels']).to include 'label_bar' @@ -505,7 +647,7 @@ describe API::API, api: true do it 'should return 400 if title is too long' do put api("/projects/#{project.id}/issues/#{issue.id}", user), title: 'g' * 256 - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['title']).to eq([ 'is too long (maximum is 255 characters)' ]) @@ -516,7 +658,7 @@ describe API::API, api: true do it "should update a project issue" do put api("/projects/#{project.id}/issues/#{issue.id}", user), labels: 'label2', state_event: "close" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['labels']).to include 'label2' expect(json_response['state']).to eq "closed" @@ -527,7 +669,7 @@ describe API::API, api: true do update_time = 2.weeks.ago put api("/projects/#{project.id}/issues/#{issue.id}", user), labels: 'label3', state_event: 'close', updated_at: update_time - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['labels']).to include 'label3' expect(Time.parse(json_response['updated_at'])).to be_within(1.second).of(update_time) @@ -538,12 +680,12 @@ describe API::API, api: true do describe "DELETE /projects/:id/issues/:issue_id" do it "rejects a non member from deleting an issue" do delete api("/projects/#{project.id}/issues/#{issue.id}", non_member) - expect(response.status).to be(403) + expect(response).to have_http_status(403) end it "rejects a developer from deleting an issue" do delete api("/projects/#{project.id}/issues/#{issue.id}", author) - expect(response.status).to be(403) + expect(response).to have_http_status(403) end context "when the user is project owner" do @@ -552,7 +694,7 @@ describe API::API, api: true do it "deletes the issue if an admin requests it" do delete api("/projects/#{project.id}/issues/#{issue.id}", owner) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['state']).to eq 'opened' end end @@ -566,7 +708,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/issues/#{issue.id}/move", user), to_project_id: target_project.id - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['project_id']).to eq(target_project.id) end @@ -575,7 +717,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/issues/#{issue.id}/move", user), to_project_id: project.id - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('Cannot move issue to project it originates from!') end end @@ -585,7 +727,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/issues/#{issue.id}/move", user), to_project_id: target_project2.id - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('Cannot move issue due to insufficient permissions!') end end @@ -594,7 +736,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/issues/#{issue.id}/move", admin), to_project_id: target_project2.id - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['project_id']).to eq(target_project2.id) end @@ -603,7 +745,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/issues/123/move", user), to_project_id: target_project.id - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -612,7 +754,7 @@ describe API::API, api: true do post api("/projects/123/issues/#{issue.id}/move", user), to_project_id: target_project.id - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -621,7 +763,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/issues/#{issue.id}/move", user), to_project_id: 123 - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -630,26 +772,26 @@ describe API::API, api: true do it 'subscribes to an issue' do post api("/projects/#{project.id}/issues/#{issue.id}/subscription", user2) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['subscribed']).to eq(true) end it 'returns 304 if already subscribed' do post api("/projects/#{project.id}/issues/#{issue.id}/subscription", user) - expect(response.status).to eq(304) + expect(response).to have_http_status(304) end it 'returns 404 if the issue is not found' do post api("/projects/#{project.id}/issues/123/subscription", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'returns 404 if the issue is confidential' do post api("/projects/#{project.id}/issues/#{confidential_issue.id}/subscription", non_member) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -657,26 +799,26 @@ describe API::API, api: true do it 'unsubscribes from an issue' do delete api("/projects/#{project.id}/issues/#{issue.id}/subscription", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['subscribed']).to eq(false) end it 'returns 304 if not subscribed' do delete api("/projects/#{project.id}/issues/#{issue.id}/subscription", user2) - expect(response.status).to eq(304) + expect(response).to have_http_status(304) end it 'returns 404 if the issue is not found' do delete api("/projects/#{project.id}/issues/123/subscription", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'returns 404 if the issue is confidential' do delete api("/projects/#{project.id}/issues/#{confidential_issue.id}/subscription", non_member) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end diff --git a/spec/requests/api/keys_spec.rb b/spec/requests/api/keys_spec.rb index d2b87f88712..1861882d59e 100644 --- a/spec/requests/api/keys_spec.rb +++ b/spec/requests/api/keys_spec.rb @@ -14,14 +14,14 @@ describe API::API, api: true do context 'when unauthenticated' do it 'should return authentication error' do get api("/keys/#{key.id}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end context 'when authenticated' do it 'should return 404 for non-existing key' do get api('/keys/999999', admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 Not found') end @@ -29,7 +29,7 @@ describe API::API, api: true do user.keys << key user.save get api("/keys/#{key.id}", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq(key.title) expect(json_response['user']['id']).to eq(user.id) expect(json_response['user']['username']).to eq(user.username) diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb index b2c7f8d9acb..39736779986 100644 --- a/spec/requests/api/labels_spec.rb +++ b/spec/requests/api/labels_spec.rb @@ -15,7 +15,7 @@ describe API::API, api: true do describe 'GET /projects/:id/labels' do it 'should return project labels' do get api("/projects/#{project.id}/labels", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.size).to eq(1) expect(json_response.first['name']).to eq(label1.name) @@ -28,7 +28,7 @@ describe API::API, api: true do name: 'Foo', color: '#FFAABB', description: 'test' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['name']).to eq('Foo') expect(json_response['color']).to eq('#FFAABB') expect(json_response['description']).to eq('test') @@ -38,7 +38,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/labels", user), name: 'Foo', color: '#FFAABB' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['name']).to eq('Foo') expect(json_response['color']).to eq('#FFAABB') expect(json_response['description']).to be_nil @@ -46,19 +46,19 @@ describe API::API, api: true do it 'should return a 400 bad request if name not given' do post api("/projects/#{project.id}/labels", user), color: '#FFAABB' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it 'should return a 400 bad request if color not given' do post api("/projects/#{project.id}/labels", user), name: 'Foobar' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it 'should return 400 for invalid color' do post api("/projects/#{project.id}/labels", user), name: 'Foo', color: '#FFAA' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['color']).to eq(['must be a valid color code']) end @@ -66,7 +66,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/labels", user), name: 'Foo', color: '#FFAAFFFF' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['color']).to eq(['must be a valid color code']) end @@ -74,7 +74,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/labels", user), name: '?', color: '#FFAABB' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['title']).to eq(['is invalid']) end @@ -82,7 +82,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/labels", user), name: 'label1', color: '#FFAABB' - expect(response.status).to eq(409) + expect(response).to have_http_status(409) expect(json_response['message']).to eq('Label already exists') end end @@ -90,18 +90,18 @@ describe API::API, api: true do describe 'DELETE /projects/:id/labels' do it 'should return 200 for existing label' do delete api("/projects/#{project.id}/labels", user), name: 'label1' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'should return 404 for non existing label' do delete api("/projects/#{project.id}/labels", user), name: 'label2' - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 Label Not Found') end it 'should return 400 for wrong parameters' do delete api("/projects/#{project.id}/labels", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end @@ -112,7 +112,7 @@ describe API::API, api: true do new_name: 'New Label', color: '#FFFFFF', description: 'test' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq('New Label') expect(json_response['color']).to eq('#FFFFFF') expect(json_response['description']).to eq('test') @@ -122,7 +122,7 @@ describe API::API, api: true do put api("/projects/#{project.id}/labels", user), name: 'label1', new_name: 'New Label' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq('New Label') expect(json_response['color']).to eq(label1.color) end @@ -131,7 +131,7 @@ describe API::API, api: true do put api("/projects/#{project.id}/labels", user), name: 'label1', color: '#FFFFFF' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(label1.name) expect(json_response['color']).to eq('#FFFFFF') end @@ -140,7 +140,7 @@ describe API::API, api: true do put api("/projects/#{project.id}/labels", user), name: 'label1', description: 'test' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(label1.name) expect(json_response['description']).to eq('test') end @@ -149,18 +149,18 @@ describe API::API, api: true do put api("/projects/#{project.id}/labels", user), name: 'label2', new_name: 'label3' - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'should return 400 if no label name given' do put api("/projects/#{project.id}/labels", user), new_name: 'label2' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('400 (Bad request) "name" not given') end it 'should return 400 if no new parameters given' do put api("/projects/#{project.id}/labels", user), name: 'label1' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('Required parameters '\ '"new_name" or "color" missing') end @@ -170,7 +170,7 @@ describe API::API, api: true do name: 'label1', new_name: '?', color: '#FFFFFF' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['title']).to eq(['is invalid']) end @@ -178,7 +178,7 @@ describe API::API, api: true do put api("/projects/#{project.id}/labels", user), name: 'label1', color: '#FF' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['color']).to eq(['must be a valid color code']) end @@ -186,7 +186,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/labels", user), name: 'Foo', color: '#FFAAFFFF' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['color']).to eq(['must be a valid color code']) end end @@ -196,7 +196,7 @@ describe API::API, api: true do it "should subscribe to the label" do post api("/projects/#{project.id}/labels/#{label1.title}/subscription", user) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response["name"]).to eq(label1.title) expect(json_response["subscribed"]).to be_truthy end @@ -206,7 +206,7 @@ describe API::API, api: true do it "should subscribe to the label" do post api("/projects/#{project.id}/labels/#{label1.id}/subscription", user) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response["name"]).to eq(label1.title) expect(json_response["subscribed"]).to be_truthy end @@ -218,7 +218,7 @@ describe API::API, api: true do it "should return 304" do post api("/projects/#{project.id}/labels/#{label1.id}/subscription", user) - expect(response.status).to eq(304) + expect(response).to have_http_status(304) end end @@ -226,7 +226,7 @@ describe API::API, api: true do it "should a return 404 error" do post api("/projects/#{project.id}/labels/1234/subscription", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -238,7 +238,7 @@ describe API::API, api: true do it "should unsubscribe from the label" do delete api("/projects/#{project.id}/labels/#{label1.title}/subscription", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["name"]).to eq(label1.title) expect(json_response["subscribed"]).to be_falsey end @@ -248,7 +248,7 @@ describe API::API, api: true do it "should unsubscribe from the label" do delete api("/projects/#{project.id}/labels/#{label1.id}/subscription", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["name"]).to eq(label1.title) expect(json_response["subscribed"]).to be_falsey end @@ -260,7 +260,7 @@ describe API::API, api: true do it "should return 304" do delete api("/projects/#{project.id}/labels/#{label1.id}/subscription", user) - expect(response.status).to eq(304) + expect(response).to have_http_status(304) end end @@ -268,7 +268,7 @@ describe API::API, api: true do it "should a return 404 error" do delete api("/projects/#{project.id}/labels/1234/subscription", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end diff --git a/spec/requests/api/licenses_spec.rb b/spec/requests/api/license_templates_spec.rb index 3726b2f5688..9a1894d63a2 100644 --- a/spec/requests/api/licenses_spec.rb +++ b/spec/requests/api/license_templates_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe API::Licenses, api: true do +describe API::API, api: true do include ApiHelpers describe 'Entity' do @@ -23,7 +23,7 @@ describe API::Licenses, api: true do it 'returns a list of available license templates' do get api('/licenses') - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.size).to eq(15) expect(json_response.map { |l| l['key'] }).to include('agpl-3.0') @@ -34,7 +34,7 @@ describe API::Licenses, api: true do it 'returns a list of available popular license templates' do get api('/licenses?popular=1') - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.size).to eq(3) expect(json_response.map { |l| l['key'] }).to include('apache-2.0') @@ -116,7 +116,7 @@ describe API::Licenses, api: true do let(:license_type) { 'muth-over9000' } it 'returns a 404' do - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 5896b93603f..61e897edf87 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -22,14 +22,14 @@ describe API::API, api: true do context "when unauthenticated" do it "should return authentication error" do get api("/projects/#{project.id}/merge_requests") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end context "when authenticated" do it "should return an array of all merge_requests" do get api("/projects/#{project.id}/merge_requests", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(3) expect(json_response.last['title']).to eq(merge_request.title) @@ -37,7 +37,7 @@ describe API::API, api: true do it "should return an array of all merge_requests" do get api("/projects/#{project.id}/merge_requests?state", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(3) expect(json_response.last['title']).to eq(merge_request.title) @@ -45,7 +45,7 @@ describe API::API, api: true do it "should return an array of open merge_requests" do get api("/projects/#{project.id}/merge_requests?state=opened", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.last['title']).to eq(merge_request.title) @@ -53,7 +53,7 @@ describe API::API, api: true do it "should return an array of closed merge_requests" do get api("/projects/#{project.id}/merge_requests?state=closed", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['title']).to eq(merge_request_closed.title) @@ -61,7 +61,7 @@ describe API::API, api: true do it "should return an array of merged merge_requests" do get api("/projects/#{project.id}/merge_requests?state=merged", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['title']).to eq(merge_request_merged.title) @@ -75,7 +75,7 @@ describe API::API, api: true do it "should return an array of merge_requests in ascending order" do get api("/projects/#{project.id}/merge_requests?sort=asc", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(3) response_dates = json_response.map{ |merge_request| merge_request['created_at'] } @@ -84,7 +84,7 @@ describe API::API, api: true do it "should return an array of merge_requests in descending order" do get api("/projects/#{project.id}/merge_requests?sort=desc", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(3) response_dates = json_response.map{ |merge_request| merge_request['created_at'] } @@ -93,7 +93,7 @@ describe API::API, api: true do it "should return an array of merge_requests ordered by updated_at" do get api("/projects/#{project.id}/merge_requests?order_by=updated_at", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(3) response_dates = json_response.map{ |merge_request| merge_request['updated_at'] } @@ -102,7 +102,7 @@ describe API::API, api: true do it "should return an array of merge_requests ordered by created_at" do get api("/projects/#{project.id}/merge_requests?order_by=created_at&sort=asc", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(3) response_dates = json_response.map{ |merge_request| merge_request['created_at'] } @@ -116,7 +116,7 @@ describe API::API, api: true do it 'exposes known attributes' do get api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['id']).to eq(merge_request.id) expect(json_response['iid']).to eq(merge_request.iid) expect(json_response['project_id']).to eq(merge_request.project.id) @@ -142,7 +142,7 @@ describe API::API, api: true do it "should return merge_request" do get api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq(merge_request.title) expect(json_response['iid']).to eq(merge_request.iid) expect(json_response['work_in_progress']).to eq(false) @@ -159,7 +159,7 @@ describe API::API, api: true do it "should return a 404 error if merge_request_id not found" do get api("/projects/#{project.id}/merge_requests/999", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end context 'Work in Progress' do @@ -167,7 +167,7 @@ describe API::API, api: true do it "should return merge_request" do get api("/projects/#{project.id}/merge_requests/#{merge_request_wip.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['work_in_progress']).to eq(true) end end @@ -186,7 +186,7 @@ describe API::API, api: true do it 'returns a 404 when merge_request_id not found' do get api("/projects/#{project.id}/merge_requests/999/commits", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -199,7 +199,7 @@ describe API::API, api: true do it 'returns a 404 when merge_request_id not found' do get api("/projects/#{project.id}/merge_requests/999/changes", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -213,7 +213,7 @@ describe API::API, api: true do author: user, labels: 'label, label2', milestone_id: milestone.id - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['title']).to eq('Test merge_request') expect(json_response['labels']).to eq(['label', 'label2']) expect(json_response['milestone']['id']).to eq(milestone.id) @@ -222,25 +222,25 @@ describe API::API, api: true do it "should return 422 when source_branch equals target_branch" do post api("/projects/#{project.id}/merge_requests", user), title: "Test merge_request", source_branch: "master", target_branch: "master", author: user - expect(response.status).to eq(422) + expect(response).to have_http_status(422) end it "should return 400 when source_branch is missing" do post api("/projects/#{project.id}/merge_requests", user), title: "Test merge_request", target_branch: "master", author: user - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return 400 when target_branch is missing" do post api("/projects/#{project.id}/merge_requests", user), title: "Test merge_request", source_branch: "markdown", author: user - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return 400 when title is missing" do post api("/projects/#{project.id}/merge_requests", user), target_branch: 'master', source_branch: 'markdown' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it 'should return 400 on invalid label names' do @@ -250,7 +250,7 @@ describe API::API, api: true do target_branch: 'master', author: user, labels: 'label, ?' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['labels']['?']['title']).to eq( ['is invalid'] ) @@ -274,7 +274,7 @@ describe API::API, api: true do target_branch: 'master', author: user end.to change { MergeRequest.count }.by(0) - expect(response.status).to eq(409) + expect(response).to have_http_status(409) end end end @@ -292,7 +292,7 @@ describe API::API, api: true do post api("/projects/#{fork_project.id}/merge_requests", user2), title: 'Test merge_request', source_branch: "feature_conflict", target_branch: "master", author: user2, target_project_id: project.id, description: 'Test description for Test merge_request' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['title']).to eq('Test merge_request') expect(json_response['description']).to eq('Test description for Test merge_request') end @@ -303,26 +303,26 @@ describe API::API, api: true do expect(fork_project.forked_from_project).to eq(project) post api("/projects/#{fork_project.id}/merge_requests", user2), title: 'Test merge_request', source_branch: "master", target_branch: "master", author: user2, target_project_id: project.id - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['title']).to eq('Test merge_request') end it "should return 400 when source_branch is missing" do post api("/projects/#{fork_project.id}/merge_requests", user2), title: 'Test merge_request', target_branch: "master", author: user2, target_project_id: project.id - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return 400 when target_branch is missing" do post api("/projects/#{fork_project.id}/merge_requests", user2), title: 'Test merge_request', target_branch: "master", author: user2, target_project_id: project.id - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return 400 when title is missing" do post api("/projects/#{fork_project.id}/merge_requests", user2), target_branch: 'master', source_branch: 'markdown', author: user2, target_project_id: project.id - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end context 'when target_branch is specified' do @@ -333,7 +333,7 @@ describe API::API, api: true do source_branch: 'markdown', author: user, target_project_id: fork_project.id - expect(response.status).to eq(422) + expect(response).to have_http_status(422) end it 'should return 422 if targeting a different fork' do @@ -343,14 +343,14 @@ describe API::API, api: true do source_branch: 'markdown', author: user2, target_project_id: unrelated_project.id - expect(response.status).to eq(422) + expect(response).to have_http_status(422) end end it "should return 201 when target_branch is specified and for the same project" do post api("/projects/#{fork_project.id}/merge_requests", user2), title: 'Test merge_request', target_branch: 'master', source_branch: 'markdown', author: user2, target_project_id: fork_project.id - expect(response.status).to eq(201) + expect(response).to have_http_status(201) end end end @@ -365,7 +365,7 @@ describe API::API, api: true do it "denies the deletion of the merge request" do delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}", developer) - expect(response.status).to be(403) + expect(response).to have_http_status(403) end end @@ -373,7 +373,7 @@ describe API::API, api: true do it "destroys the merge request owners can destroy" do delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -381,7 +381,7 @@ describe API::API, api: true do describe "PUT /projects/:id/merge_requests/:merge_request_id to close MR" do it "should return merge_request" do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), state_event: "close" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['state']).to eq('closed') end end @@ -392,7 +392,7 @@ describe API::API, api: true do it "should return merge_request in case of success" do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "should return 406 if branch can't be merged" do @@ -401,21 +401,21 @@ describe API::API, api: true do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user) - expect(response.status).to eq(406) + expect(response).to have_http_status(406) expect(json_response['message']).to eq('Branch cannot be merged') end it "should return 405 if merge_request is not open" do merge_request.close put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user) - expect(response.status).to eq(405) + expect(response).to have_http_status(405) expect(json_response['message']).to eq('405 Method Not Allowed') end it "should return 405 if merge_request is a work in progress" do merge_request.update_attribute(:title, "WIP: #{merge_request.title}") put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user) - expect(response.status).to eq(405) + expect(response).to have_http_status(405) expect(json_response['message']).to eq('405 Method Not Allowed') end @@ -424,7 +424,7 @@ describe API::API, api: true do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user) - expect(response.status).to eq(405) + expect(response).to have_http_status(405) expect(json_response['message']).to eq('405 Method Not Allowed') end @@ -432,21 +432,21 @@ describe API::API, api: true do user2 = create(:user) project.team << [user2, :reporter] put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user2) - expect(response.status).to eq(401) + expect(response).to have_http_status(401) expect(json_response['message']).to eq('401 Unauthorized') end it "returns 409 if the SHA parameter doesn't match" do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user), sha: merge_request.source_sha.succ - expect(response.status).to eq(409) + expect(response).to have_http_status(409) expect(json_response['message']).to start_with('SHA does not match HEAD of source branch') end it "succeeds if the SHA parameter matches" do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user), sha: merge_request.source_sha - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "enables merge when build succeeds if the ci is active" do @@ -455,7 +455,7 @@ describe API::API, api: true do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user), merge_when_build_succeeds: true - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq('Test') expect(json_response['merge_when_build_succeeds']).to eq(true) end @@ -464,31 +464,31 @@ describe API::API, api: true do describe "PUT /projects/:id/merge_requests/:merge_request_id" do it "updates title and returns merge_request" do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), title: "New title" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq('New title') end it "updates description and returns merge_request" do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), description: "New description" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['description']).to eq('New description') end it "updates milestone_id and returns merge_request" do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), milestone_id: milestone.id - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['milestone']['id']).to eq(milestone.id) end it "should return 400 when source_branch is specified" do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), source_branch: "master", target_branch: "master" - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return merge_request with renamed target_branch" do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), target_branch: "wiki" - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['target_branch']).to eq('wiki') end @@ -497,7 +497,7 @@ describe API::API, api: true do user), title: 'new issue', labels: 'label, ?' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['labels']['?']['title']).to eq(['is invalid']) end end @@ -507,7 +507,7 @@ describe API::API, api: true do original_count = merge_request.notes.size post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/comments", user), note: "My comment" - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['note']).to eq('My comment') expect(json_response['author']['name']).to eq(user.name) expect(json_response['author']['username']).to eq(user.username) @@ -516,20 +516,20 @@ describe API::API, api: true do it "should return 400 if note is missing" do post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/comments", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return 404 if note is attached to non existent merge request" do post api("/projects/#{project.id}/merge_requests/404/comments", user), note: 'My comment' - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end describe "GET :id/merge_requests/:merge_request_id/comments" do it "should return merge_request comments ordered by created_at" do get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/comments", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(2) expect(json_response.first['note']).to eq("a comment on a MR") @@ -539,7 +539,7 @@ describe API::API, api: true do it "should return a 404 error if merge_request_id not found" do get api("/projects/#{project.id}/merge_requests/999/comments", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -551,7 +551,7 @@ describe API::API, api: true do end get api("/projects/#{project.id}/merge_requests/#{mr.id}/closes_issues", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['id']).to eq(issue.id) @@ -559,7 +559,7 @@ describe API::API, api: true do it 'returns an empty array when there are no issues to be closed' do get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/closes_issues", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(0) end @@ -572,7 +572,7 @@ describe API::API, api: true do get api("/projects/#{jira_project.id}/merge_requests/#{merge_request.id}/closes_issues", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['title']).to eq(issue.title) @@ -584,20 +584,20 @@ describe API::API, api: true do it 'subscribes to a merge request' do post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", admin) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['subscribed']).to eq(true) end it 'returns 304 if already subscribed' do post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", user) - expect(response.status).to eq(304) + expect(response).to have_http_status(304) end it 'returns 404 if the merge request is not found' do post api("/projects/#{project.id}/merge_requests/123/subscription", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -605,20 +605,20 @@ describe API::API, api: true do it 'unsubscribes from a merge request' do delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['subscribed']).to eq(false) end it 'returns 304 if not subscribed' do delete api("/projects/#{project.id}/merge_requests/#{merge_request.id}/subscription", admin) - expect(response.status).to eq(304) + expect(response).to have_http_status(304) end it 'returns 404 if the merge request is not found' do post api("/projects/#{project.id}/merge_requests/123/subscription", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end diff --git a/spec/requests/api/milestones_spec.rb b/spec/requests/api/milestones_spec.rb index 0154d1c62cc..0f4e38b2475 100644 --- a/spec/requests/api/milestones_spec.rb +++ b/spec/requests/api/milestones_spec.rb @@ -12,20 +12,20 @@ describe API::API, api: true do describe 'GET /projects/:id/milestones' do it 'should return project milestones' do get api("/projects/#{project.id}/milestones", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['title']).to eq(milestone.title) end it 'should return a 401 error if user not authenticated' do get api("/projects/#{project.id}/milestones") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end it 'returns an array of active milestones' do get api("/projects/#{project.id}/milestones?state=active", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['id']).to eq(milestone.id) @@ -34,7 +34,7 @@ describe API::API, api: true do it 'returns an array of closed milestones' do get api("/projects/#{project.id}/milestones?state=closed", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) expect(json_response.first['id']).to eq(closed_milestone.id) @@ -44,7 +44,7 @@ describe API::API, api: true do describe 'GET /projects/:id/milestones/:milestone_id' do it 'should return a project milestone by id' do get api("/projects/#{project.id}/milestones/#{milestone.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq(milestone.title) expect(json_response['iid']).to eq(milestone.iid) end @@ -60,19 +60,19 @@ describe API::API, api: true do it 'should return 401 error if user not authenticated' do get api("/projects/#{project.id}/milestones/#{milestone.id}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end it 'should return a 404 error if milestone id not found' do get api("/projects/#{project.id}/milestones/1234", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end describe 'POST /projects/:id/milestones' do it 'should create a new project milestone' do post api("/projects/#{project.id}/milestones", user), title: 'new milestone' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['title']).to eq('new milestone') expect(json_response['description']).to be_nil end @@ -80,14 +80,14 @@ describe API::API, api: true do it 'should create a new project milestone with description and due date' do post api("/projects/#{project.id}/milestones", user), title: 'new milestone', description: 'release', due_date: '2013-03-02' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['description']).to eq('release') expect(json_response['due_date']).to eq('2013-03-02') end it 'should return a 400 error if title is missing' do post api("/projects/#{project.id}/milestones", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end @@ -95,14 +95,14 @@ describe API::API, api: true do it 'should update a project milestone' do put api("/projects/#{project.id}/milestones/#{milestone.id}", user), title: 'updated title' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq('updated title') end it 'should return a 404 error if milestone id not found' do put api("/projects/#{project.id}/milestones/1234", user), title: 'updated title' - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -110,7 +110,7 @@ describe API::API, api: true do it 'should update a project milestone' do put api("/projects/#{project.id}/milestones/#{milestone.id}", user), state_event: 'close' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['state']).to eq('closed') end @@ -131,14 +131,14 @@ describe API::API, api: true do end it 'should return project issues for a particular milestone' do get api("/projects/#{project.id}/milestones/#{milestone.id}/issues", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['milestone']['title']).to eq(milestone.title) end it 'should return a 401 error if user not authenticated' do get api("/projects/#{project.id}/milestones/#{milestone.id}/issues") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end describe 'confidential issues' do @@ -155,7 +155,7 @@ describe API::API, api: true do it 'returns confidential issues to team members' do get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.size).to eq(2) expect(json_response.map { |issue| issue['id'] }).to include(issue.id, confidential_issue.id) @@ -167,7 +167,7 @@ describe API::API, api: true do get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", member) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.size).to eq(1) expect(json_response.map { |issue| issue['id'] }).to include(issue.id) @@ -176,7 +176,7 @@ describe API::API, api: true do it 'does not return confidential issues to regular users' do get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", create(:user)) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.size).to eq(1) expect(json_response.map { |issue| issue['id'] }).to include(issue.id) diff --git a/spec/requests/api/namespaces_spec.rb b/spec/requests/api/namespaces_spec.rb index 21787fdd895..237b4b17eb5 100644 --- a/spec/requests/api/namespaces_spec.rb +++ b/spec/requests/api/namespaces_spec.rb @@ -11,14 +11,14 @@ describe API::API, api: true do context "when unauthenticated" do it "should return authentication error" do get api("/namespaces") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end context "when authenticated as admin" do it "admin: should return an array of all namespaces" do get api("/namespaces", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(Namespace.count) @@ -26,7 +26,7 @@ describe API::API, api: true do it "admin: should return an array of matched namespaces" do get api("/namespaces?search=#{group1.name}", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) @@ -36,7 +36,7 @@ describe API::API, api: true do context "when authenticated as a regular user" do it "user: should return an array of namespaces" do get api("/namespaces", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) @@ -44,7 +44,7 @@ describe API::API, api: true do it "admin: should return an array of matched namespaces" do get api("/namespaces?search=#{user.username}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb index beb29a68692..bacd01f8e74 100644 --- a/spec/requests/api/notes_spec.rb +++ b/spec/requests/api/notes_spec.rb @@ -40,7 +40,7 @@ describe API::API, api: true do it "should return an array of issue notes" do get api("/projects/#{project.id}/issues/#{issue.id}/notes", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['body']).to eq(issue_note.note) end @@ -48,14 +48,14 @@ describe API::API, api: true do it "should return a 404 error when issue id not found" do get api("/projects/#{project.id}/issues/12345/notes", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end context "and current user cannot view the notes" do it "should return an empty array" do get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response).to be_empty end @@ -66,7 +66,7 @@ describe API::API, api: true do it "returns 404" do get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -74,7 +74,7 @@ describe API::API, api: true do it "should return an empty array" do get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", private_user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['body']).to eq(cross_reference_note.note) end @@ -86,7 +86,7 @@ describe API::API, api: true do it "should return an array of snippet notes" do get api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['body']).to eq(snippet_note.note) end @@ -94,13 +94,13 @@ describe API::API, api: true do it "should return a 404 error when snippet id not found" do get api("/projects/#{project.id}/snippets/42/notes", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "returns 404 when not authorized" do get api("/projects/#{project.id}/snippets/#{snippet.id}/notes", private_user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -108,7 +108,7 @@ describe API::API, api: true do it "should return an array of merge_requests notes" do get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/notes", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['body']).to eq(merge_request_note.note) end @@ -116,13 +116,13 @@ describe API::API, api: true do it "should return a 404 error if merge request id not found" do get api("/projects/#{project.id}/merge_requests/4444/notes", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "returns 404 when not authorized" do get api("/projects/#{project.id}/merge_requests/4444/notes", private_user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -132,21 +132,21 @@ describe API::API, api: true do it "should return an issue note by id" do get api("/projects/#{project.id}/issues/#{issue.id}/notes/#{issue_note.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['body']).to eq(issue_note.note) end it "should return a 404 error if issue note not found" do get api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end context "and current user cannot view the note" do it "should return a 404 error" do get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes/#{cross_reference_note.id}", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end context "when issue is confidential" do @@ -155,7 +155,7 @@ describe API::API, api: true do it "returns 404" do get api("/projects/#{project.id}/issues/#{issue.id}/notes/#{issue_note.id}", private_user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -164,7 +164,7 @@ describe API::API, api: true do it "should return an issue note by id" do get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes/#{cross_reference_note.id}", private_user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['body']).to eq(cross_reference_note.note) end end @@ -175,14 +175,14 @@ describe API::API, api: true do it "should return a snippet note by id" do get api("/projects/#{project.id}/snippets/#{snippet.id}/notes/#{snippet_note.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['body']).to eq(snippet_note.note) end it "should return a 404 error if snippet note not found" do get api("/projects/#{project.id}/snippets/#{snippet.id}/notes/12345", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -192,7 +192,7 @@ describe API::API, api: true do it "should create a new issue note" do post api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: 'hi!' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['body']).to eq('hi!') expect(json_response['author']['username']).to eq(user.username) end @@ -200,13 +200,13 @@ describe API::API, api: true do it "should return a 400 bad request error if body not given" do post api("/projects/#{project.id}/issues/#{issue.id}/notes", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return a 401 unauthorized error if user not authenticated" do post api("/projects/#{project.id}/issues/#{issue.id}/notes"), body: 'hi!' - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end context 'when an admin or owner makes the request' do @@ -215,7 +215,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: 'hi!', created_at: creation_time - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['body']).to eq('hi!') expect(json_response['author']['username']).to eq(user.username) expect(Time.parse(json_response['created_at'])).to be_within(1.second).of(creation_time) @@ -228,7 +228,7 @@ describe API::API, api: true do it "should create a new snippet note" do post api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user), body: 'hi!' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['body']).to eq('hi!') expect(json_response['author']['username']).to eq(user.username) end @@ -236,13 +236,13 @@ describe API::API, api: true do it "should return a 400 bad request error if body not given" do post api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return a 401 unauthorized error if user not authenticated" do post api("/projects/#{project.id}/snippets/#{snippet.id}/notes"), body: 'hi!' - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end @@ -282,7 +282,7 @@ describe API::API, api: true do put api("/projects/#{project.id}/issues/#{issue.id}/"\ "notes/#{issue_note.id}", user), body: 'Hello!' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['body']).to eq('Hello!') end @@ -290,14 +290,14 @@ describe API::API, api: true do put api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user), body: 'Hello!' - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'should return a 400 bad request error if body not given' do put api("/projects/#{project.id}/issues/#{issue.id}/"\ "notes/#{issue_note.id}", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end @@ -306,7 +306,7 @@ describe API::API, api: true do put api("/projects/#{project.id}/snippets/#{snippet.id}/"\ "notes/#{snippet_note.id}", user), body: 'Hello!' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['body']).to eq('Hello!') end @@ -314,7 +314,7 @@ describe API::API, api: true do put api("/projects/#{project.id}/snippets/#{snippet.id}/"\ "notes/12345", user), body: "Hello!" - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -323,7 +323,7 @@ describe API::API, api: true do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/"\ "notes/#{merge_request_note.id}", user), body: 'Hello!' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['body']).to eq('Hello!') end @@ -331,7 +331,7 @@ describe API::API, api: true do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/"\ "notes/12345", user), body: "Hello!" - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -342,17 +342,17 @@ describe API::API, api: true do delete api("/projects/#{project.id}/issues/#{issue.id}/"\ "notes/#{issue_note.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) # Check if note is really deleted delete api("/projects/#{project.id}/issues/#{issue.id}/"\ "notes/#{issue_note.id}", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'returns a 404 error when note id not found' do delete api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -361,18 +361,18 @@ describe API::API, api: true do delete api("/projects/#{project.id}/snippets/#{snippet.id}/"\ "notes/#{snippet_note.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) # Check if note is really deleted delete api("/projects/#{project.id}/snippets/#{snippet.id}/"\ "notes/#{snippet_note.id}", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'returns a 404 error when note id not found' do delete api("/projects/#{project.id}/snippets/#{snippet.id}/"\ "notes/12345", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -381,18 +381,18 @@ describe API::API, api: true do delete api("/projects/#{project.id}/merge_requests/"\ "#{merge_request.id}/notes/#{merge_request_note.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) # Check if note is really deleted delete api("/projects/#{project.id}/merge_requests/"\ "#{merge_request.id}/notes/#{merge_request_note.id}", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'returns a 404 error when note id not found' do delete api("/projects/#{project.id}/merge_requests/"\ "#{merge_request.id}/notes/12345", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end diff --git a/spec/requests/api/project_hooks_spec.rb b/spec/requests/api/project_hooks_spec.rb index ffb93bbb120..fd1fffa6223 100644 --- a/spec/requests/api/project_hooks_spec.rb +++ b/spec/requests/api/project_hooks_spec.rb @@ -22,7 +22,7 @@ describe API::API, 'ProjectHooks', api: true do context "authorized user" do it "should return project hooks" do get api("/projects/#{project.id}/hooks", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.count).to eq(1) @@ -40,7 +40,7 @@ describe API::API, 'ProjectHooks', api: true do context "unauthorized user" do it "should not access project hooks" do get api("/projects/#{project.id}/hooks", user3) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end end @@ -49,7 +49,7 @@ describe API::API, 'ProjectHooks', api: true do context "authorized user" do it "should return a project hook" do get api("/projects/#{project.id}/hooks/#{hook.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['url']).to eq(hook.url) expect(json_response['issues_events']).to eq(hook.issues_events) expect(json_response['push_events']).to eq(hook.push_events) @@ -61,20 +61,20 @@ describe API::API, 'ProjectHooks', api: true do it "should return a 404 error if hook id is not available" do get api("/projects/#{project.id}/hooks/1234", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end context "unauthorized user" do it "should not access an existing hook" do get api("/projects/#{project.id}/hooks/#{hook.id}", user3) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end it "should return a 404 error if hook id is not available" do get api("/projects/#{project.id}/hooks/1234", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -83,7 +83,7 @@ describe API::API, 'ProjectHooks', api: true do expect do post api("/projects/#{project.id}/hooks", user), url: "http://example.com", issues_events: true end.to change {project.hooks.count}.by(1) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['url']).to eq('http://example.com') expect(json_response['issues_events']).to eq(true) expect(json_response['push_events']).to eq(true) @@ -96,12 +96,12 @@ describe API::API, 'ProjectHooks', api: true do it "should return a 400 error if url not given" do post api("/projects/#{project.id}/hooks", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return a 422 error if url not valid" do post api("/projects/#{project.id}/hooks", user), "url" => "ftp://example.com" - expect(response.status).to eq(422) + expect(response).to have_http_status(422) end end @@ -109,7 +109,7 @@ describe API::API, 'ProjectHooks', api: true do it "should update an existing project hook" do put api("/projects/#{project.id}/hooks/#{hook.id}", user), url: 'http://example.org', push_events: false - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['url']).to eq('http://example.org') expect(json_response['issues_events']).to eq(hook.issues_events) expect(json_response['push_events']).to eq(false) @@ -121,17 +121,17 @@ describe API::API, 'ProjectHooks', api: true do it "should return 404 error if hook id not found" do put api("/projects/#{project.id}/hooks/1234", user), url: 'http://example.org' - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should return 400 error if url is not given" do put api("/projects/#{project.id}/hooks/#{hook.id}", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return a 422 error if url is not valid" do put api("/projects/#{project.id}/hooks/#{hook.id}", user), url: 'ftp://example.com' - expect(response.status).to eq(422) + expect(response).to have_http_status(422) end end @@ -140,22 +140,22 @@ describe API::API, 'ProjectHooks', api: true do expect do delete api("/projects/#{project.id}/hooks/#{hook.id}", user) end.to change {project.hooks.count}.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "should return success when deleting hook" do delete api("/projects/#{project.id}/hooks/#{hook.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "should return a 404 error when deleting non existent hook" do delete api("/projects/#{project.id}/hooks/42", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should return a 405 error if hook id not given" do delete api("/projects/#{project.id}/hooks", user) - expect(response.status).to eq(405) + expect(response).to have_http_status(405) end it "shold return a 404 if a user attempts to delete project hooks he/she does not own" do @@ -164,7 +164,7 @@ describe API::API, 'ProjectHooks', api: true do other_project.team << [test_user, :master] delete api("/projects/#{other_project.id}/hooks/#{hook.id}", test_user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(WebHook.exists?(hook.id)).to be_truthy end end diff --git a/spec/requests/api/project_members_spec.rb b/spec/requests/api/project_members_spec.rb index 44b532b10e1..9a7c1da4401 100644 --- a/spec/requests/api/project_members_spec.rb +++ b/spec/requests/api/project_members_spec.rb @@ -15,7 +15,7 @@ describe API::API, api: true do it "should return project team members" do get api("/projects/#{project.id}/members", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.count).to eq(2) expect(json_response.map { |u| u['username'] }).to include user.username @@ -23,7 +23,7 @@ describe API::API, api: true do it "finds team members with query string" do get api("/projects/#{project.id}/members", user), query: user.username - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.count).to eq(1) expect(json_response.first['username']).to eq(user.username) @@ -31,7 +31,7 @@ describe API::API, api: true do it "should return a 404 error if id not found" do get api("/projects/9999/members", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -40,14 +40,14 @@ describe API::API, api: true do it "should return project team member" do get api("/projects/#{project.id}/members/#{user.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['username']).to eq(user.username) expect(json_response['access_level']).to eq(ProjectMember::MASTER) end it "should return a 404 error if user id not found" do get api("/projects/#{project.id}/members/1234", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -57,7 +57,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/members", user), user_id: user2.id, access_level: ProjectMember::DEVELOPER end.to change { ProjectMember.count }.by(1) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['username']).to eq(user2.username) expect(json_response['access_level']).to eq(ProjectMember::DEVELOPER) end @@ -70,24 +70,24 @@ describe API::API, api: true do post api("/projects/#{project.id}/members", user), user_id: user2.id, access_level: ProjectMember::DEVELOPER end.not_to change { ProjectMember.count } - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['username']).to eq(user2.username) expect(json_response['access_level']).to eq(ProjectMember::DEVELOPER) end it "should return a 400 error when user id is not given" do post api("/projects/#{project.id}/members", user), access_level: ProjectMember::MASTER - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return a 400 error when access level is not given" do post api("/projects/#{project.id}/members", user), user_id: user2.id - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return a 422 error when access level is not known" do post api("/projects/#{project.id}/members", user), user_id: user2.id, access_level: 1234 - expect(response.status).to eq(422) + expect(response).to have_http_status(422) end end @@ -96,24 +96,24 @@ describe API::API, api: true do it "should update project team member" do put api("/projects/#{project.id}/members/#{user3.id}", user), access_level: ProjectMember::MASTER - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['username']).to eq(user3.username) expect(json_response['access_level']).to eq(ProjectMember::MASTER) end it "should return a 404 error if user_id is not found" do put api("/projects/#{project.id}/members/1234", user), access_level: ProjectMember::MASTER - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should return a 400 error when access level is not given" do put api("/projects/#{project.id}/members/#{user3.id}", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should return a 422 error when access level is not known" do put api("/projects/#{project.id}/members/#{user3.id}", user), access_level: 123 - expect(response.status).to eq(422) + expect(response).to have_http_status(422) end end @@ -134,20 +134,20 @@ describe API::API, api: true do expect do delete api("/projects/#{project.id}/members/#{user3.id}", user) end.not_to change { ProjectMember.count } - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "should return 200 if team member already removed" do delete api("/projects/#{project.id}/members/#{user3.id}", user) delete api("/projects/#{project.id}/members/#{user3.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "should return 200 OK when the user was not member" do expect do delete api("/projects/#{project.id}/members/1000000", user) end.to change { ProjectMember.count }.by(0) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['id']).to eq(1000000) expect(json_response['message']).to eq('Access revoked') end @@ -158,7 +158,7 @@ describe API::API, api: true do delete api("/projects/#{project.id}/members/#{user3.id}", user3) end.to change { ProjectMember.count }.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['id']).to eq(project_member2.id) end end diff --git a/spec/requests/api/project_snippets_spec.rb b/spec/requests/api/project_snippets_spec.rb index 9706d060cfa..4ebde201941 100644 --- a/spec/requests/api/project_snippets_spec.rb +++ b/spec/requests/api/project_snippets_spec.rb @@ -27,7 +27,7 @@ describe API::API, api: true do get api("/projects/#{project.id}/snippets/", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response.size).to eq(3) expect(json_response.map{ |snippet| snippet['id']} ).to include(public_snippet.id, internal_snippet.id, private_snippet.id) end @@ -38,7 +38,7 @@ describe API::API, api: true do create(:project_snippet, :private, project: project) get api("/projects/#{project.id}/snippets/", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response.size).to eq(0) end end @@ -56,7 +56,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/snippets/", admin), params - expect(response.status).to eq(201) + expect(response).to have_http_status(201) snippet = ProjectSnippet.find(json_response['id']) expect(snippet.content).to eq(params[:code]) expect(snippet.title).to eq(params[:title]) @@ -73,7 +73,7 @@ describe API::API, api: true do put api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/", admin), code: new_content - expect(response.status).to eq(200) + expect(response).to have_http_status(200) snippet.reload expect(snippet.content).to eq(new_content) end @@ -86,7 +86,7 @@ describe API::API, api: true do delete api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -97,7 +97,7 @@ describe API::API, api: true do get api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/raw", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(response.content_type).to eq 'text/plain' expect(response.body).to eq(snippet.content) end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 01eb4b44b83..41b5ed9bc33 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -45,14 +45,14 @@ describe API::API, api: true do context 'when unauthenticated' do it 'should return authentication error' do get api('/projects') - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end context 'when authenticated' do it 'should return an array of projects' do get api('/projects', user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['name']).to eq(project.name) expect(json_response.first['owner']['username']).to eq(user.username) @@ -84,7 +84,7 @@ describe API::API, api: true do context 'and using search' do it 'should return searched project' do get api('/projects', user), { search: project.name } - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) end @@ -93,21 +93,21 @@ describe API::API, api: true do context 'and using the visibility filter' do it 'should filter based on private visibility param' do get api('/projects', user), { visibility: 'private' } - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(user.namespace.projects.where(visibility_level: Gitlab::VisibilityLevel::PRIVATE).count) end it 'should filter based on internal visibility param' do get api('/projects', user), { visibility: 'internal' } - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(user.namespace.projects.where(visibility_level: Gitlab::VisibilityLevel::INTERNAL).count) end it 'should filter based on public visibility param' do get api('/projects', user), { visibility: 'public' } - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(user.namespace.projects.where(visibility_level: Gitlab::VisibilityLevel::PUBLIC).count) end @@ -121,7 +121,7 @@ describe API::API, api: true do it 'should return the correct order when sorted by id' do get api('/projects', user), { order_by: 'id', sort: 'desc' } - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['id']).to eq(project3.id) end @@ -135,21 +135,21 @@ describe API::API, api: true do context 'when unauthenticated' do it 'should return authentication error' do get api('/projects/all') - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end context 'when authenticated as regular user' do it 'should return authentication error' do get api('/projects/all', user) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end context 'when authenticated as admin' do it 'should return an array of all projects' do get api('/projects/all', admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response).to satisfy do |response| @@ -173,7 +173,7 @@ describe API::API, api: true do it 'should return the starred projects viewable by the user' do get api('/projects/starred', user3) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.map { |project| project['id'] }).to contain_exactly(project.id, public_project.id) end @@ -185,25 +185,25 @@ describe API::API, api: true do allow_any_instance_of(User).to receive(:projects_limit_left).and_return(0) expect { post api('/projects', user2), name: 'foo' }. to change {Project.count}.by(0) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end it 'should create new project without path and return 201' do expect { post api('/projects', user), name: 'foo' }. to change { Project.count }.by(1) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) end it 'should create last project before reaching project limit' do allow_any_instance_of(User).to receive(:projects_limit_left).and_return(1) post api('/projects', user2), name: 'foo' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) end it 'should not create new project without name and return 400' do expect { post api('/projects', user) }.not_to change { Project.count } - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should assign attributes to project" do @@ -273,7 +273,7 @@ describe API::API, api: true do it 'should not allow a non-admin to use a restricted visibility level' do post api('/projects', user), @project - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['visibility_level'].first).to( match('restricted by your GitLab administrator') ) @@ -295,14 +295,14 @@ describe API::API, api: true do it 'should create new project without path and return 201' do expect { post api("/projects/user/#{user.id}", admin), name: 'foo' }.to change {Project.count}.by(1) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) end it 'should respond with 400 on failure and not project' do expect { post api("/projects/user/#{user.id}", admin) }. not_to change { Project.count } - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['name']).to eq([ 'can\'t be blank', 'is too short (minimum is 0 characters)', @@ -380,7 +380,7 @@ describe API::API, api: true do it "uploads the file and returns its info" do post api("/projects/#{project.id}/uploads", user), file: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png") - expect(response.status).to be(201) + expect(response).to have_http_status(201) expect(json_response['alt']).to eq("dk") expect(json_response['url']).to start_with("/uploads/") expect(json_response['url']).to end_with("/dk.png") @@ -394,27 +394,27 @@ describe API::API, api: true do it 'should return a project by id' do get api("/projects/#{project.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(project.name) expect(json_response['owner']['username']).to eq(user.username) end it 'should return a project by path name' do get api("/projects/#{project.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(project.name) end it 'should return a 404 error if not found' do get api('/projects/42', user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 Project Not Found') end it 'should return a 404 error if user is not a member' do other_user = create(:user) get api("/projects/#{project.id}", other_user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'should handle users with dots' do @@ -422,7 +422,7 @@ describe API::API, api: true do project = create(:project, creator_id: dot_user.id, namespace: dot_user.namespace) get api("/projects/#{dot_user.namespace.name}%2F#{project.path}", dot_user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(project.name) end @@ -433,7 +433,7 @@ describe API::API, api: true do it 'contains permission information' do get api("/projects", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response.first['permissions']['project_access']['access_level']). to eq(Gitlab::Access::MASTER) expect(json_response.first['permissions']['group_access']).to be_nil @@ -445,7 +445,7 @@ describe API::API, api: true do project.team << [user, :master] get api("/projects/#{project.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['permissions']['project_access']['access_level']). to eq(Gitlab::Access::MASTER) expect(json_response['permissions']['group_access']).to be_nil @@ -460,7 +460,7 @@ describe API::API, api: true do it 'should set the owner and return 200' do get api("/projects/#{project2.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['permissions']['project_access']).to be_nil expect(json_response['permissions']['group_access']['access_level']). to eq(Gitlab::Access::OWNER) @@ -479,7 +479,7 @@ describe API::API, api: true do get api("/projects/#{project.id}/events", user) end - it { expect(response.status).to eq(200) } + it { expect(response).to have_http_status(200) } context 'joined event' do let(:json_event) { json_response[1] } @@ -500,14 +500,14 @@ describe API::API, api: true do it 'should return a 404 error if not found' do get api('/projects/42/events', user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 Project Not Found') end it 'should return a 404 error if user is not a member' do other_user = create(:user) get api("/projects/#{project.id}/events", other_user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -516,7 +516,7 @@ describe API::API, api: true do it 'should return an array of project snippets' do get api("/projects/#{project.id}/snippets", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['title']).to eq(snippet.title) end @@ -525,13 +525,13 @@ describe API::API, api: true do describe 'GET /projects/:id/snippets/:snippet_id' do it 'should return a project snippet' do get api("/projects/#{project.id}/snippets/#{snippet.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq(snippet.title) end it 'should return a 404 error if snippet id not found' do get api("/projects/#{project.id}/snippets/1234", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -540,7 +540,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/snippets", user), title: 'api test', file_name: 'sample.rb', code: 'test', visibility_level: '0' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['title']).to eq('api test') end @@ -554,7 +554,7 @@ describe API::API, api: true do it 'should update an existing project snippet' do put api("/projects/#{project.id}/snippets/#{snippet.id}", user), code: 'updated code' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq('example') expect(snippet.reload.content).to eq('updated code') end @@ -562,7 +562,7 @@ describe API::API, api: true do it 'should update an existing project snippet with new title' do put api("/projects/#{project.id}/snippets/#{snippet.id}", user), title: 'other api test' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq('other api test') end end @@ -574,24 +574,24 @@ describe API::API, api: true do expect do delete api("/projects/#{project.id}/snippets/#{snippet.id}", user) end.to change { Snippet.count }.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'should return 404 when deleting unknown snippet id' do delete api("/projects/#{project.id}/snippets/1234", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end describe 'GET /projects/:id/snippets/:snippet_id/raw' do it 'should get a raw project snippet' do get api("/projects/#{project.id}/snippets/#{snippet.id}/raw", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'should return a 404 error if raw project snippet not found' do get api("/projects/#{project.id}/snippets/5555/raw", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -604,7 +604,7 @@ describe API::API, api: true do it 'should return array of ssh keys' do get api("/projects/#{project.id}/keys", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['title']).to eq(deploy_key.title) end @@ -613,20 +613,20 @@ describe API::API, api: true do describe 'GET /projects/:id/keys/:key_id' do it 'should return a single key' do get api("/projects/#{project.id}/keys/#{deploy_key.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['title']).to eq(deploy_key.title) end it 'should return 404 Not Found with invalid ID' do get api("/projects/#{project.id}/keys/404", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end describe 'POST /projects/:id/keys' do it 'should not create an invalid ssh key' do post api("/projects/#{project.id}/keys", user), { title: 'invalid key' } - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['key']).to eq([ 'can\'t be blank', 'is too short (minimum is 0 characters)', @@ -636,7 +636,7 @@ describe API::API, api: true do it 'should not create a key without title' do post api("/projects/#{project.id}/keys", user), key: 'some key' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['title']).to eq([ 'can\'t be blank', 'is too short (minimum is 0 characters)' @@ -662,7 +662,7 @@ describe API::API, api: true do it 'should return 404 Not Found with invalid ID' do delete api("/projects/#{project.id}/keys/404", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -676,13 +676,13 @@ describe API::API, api: true do it "shouldn't available for non admin users" do post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it 'should allow project to be forked from an existing project' do expect(project_fork_target.forked?).not_to be_truthy post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) project_fork_target.reload expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id) expect(project_fork_target.forked_project_link).not_to be_nil @@ -691,7 +691,7 @@ describe API::API, api: true do it 'should fail if forked_from project which does not exist' do post api("/projects/#{project_fork_target.id}/fork/9999", admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'should fail with 409 if already forked' do @@ -699,7 +699,7 @@ describe API::API, api: true do project_fork_target.reload expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id) post api("/projects/#{project_fork_target.id}/fork/#{new_project_fork_source.id}", admin) - expect(response.status).to eq(409) + expect(response).to have_http_status(409) project_fork_target.reload expect(project_fork_target.forked_from_project.id).to eq(project_fork_source.id) expect(project_fork_target.forked?).to be_truthy @@ -710,7 +710,7 @@ describe API::API, api: true do it "shouldn't be visible to users outside group" do delete api("/projects/#{project_fork_target.id}/fork", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end context 'when users belong to project group' do @@ -723,7 +723,7 @@ describe API::API, api: true do it 'should be forbidden to non-owner users' do delete api("/projects/#{project_fork_target.id}/fork", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it 'should make forked project unforked' do @@ -732,7 +732,7 @@ describe API::API, api: true do expect(project_fork_target.forked_from_project).not_to be_nil expect(project_fork_target.forked?).to be_truthy delete api("/projects/#{project_fork_target.id}/fork", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) project_fork_target.reload expect(project_fork_target.forked_from_project).to be_nil expect(project_fork_target.forked?).not_to be_truthy @@ -741,7 +741,7 @@ describe API::API, api: true do it 'should be idempotent if not forked' do expect(project_fork_target.forked_from_project).to be_nil delete api("/projects/#{project_fork_target.id}/fork", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(project_fork_target.reload.forked_from_project).to be_nil end end @@ -799,14 +799,14 @@ describe API::API, api: true do context 'when unauthenticated' do it 'should return authentication error' do get api("/projects/search/#{query}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end context 'when authenticated' do it 'should return an array of projects' do get api("/projects/search/#{query}",user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.size).to eq(6) json_response.each {|project| expect(project['name']).to match(/.*query.*/)} @@ -816,7 +816,7 @@ describe API::API, api: true do context 'when authenticated as a different user' do it 'should return matching public projects' do get api("/projects/search/#{query}", user2) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.size).to eq(2) json_response.each {|project| expect(project['name']).to match(/(internal|public) query/)} @@ -838,7 +838,7 @@ describe API::API, api: true do it 'should return authentication error' do project_param = { name: 'bar' } put api("/projects/#{project.id}"), project_param - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end @@ -846,7 +846,7 @@ describe API::API, api: true do it 'should update name' do project_param = { name: 'bar' } put api("/projects/#{project.id}", user), project_param - expect(response.status).to eq(200) + expect(response).to have_http_status(200) project_param.each_pair do |k, v| expect(json_response[k.to_s]).to eq(v) end @@ -855,7 +855,7 @@ describe API::API, api: true do it 'should update visibility_level' do project_param = { visibility_level: 20 } put api("/projects/#{project3.id}", user), project_param - expect(response.status).to eq(200) + expect(response).to have_http_status(200) project_param.each_pair do |k, v| expect(json_response[k.to_s]).to eq(v) end @@ -866,7 +866,7 @@ describe API::API, api: true do project_param = { public: false } put api("/projects/#{project3.id}", user), project_param - expect(response.status).to eq(200) + expect(response).to have_http_status(200) project_param.each_pair do |k, v| expect(json_response[k.to_s]).to eq(v) end @@ -876,14 +876,14 @@ describe API::API, api: true do it 'should not update name to existing name' do project_param = { name: project3.name } put api("/projects/#{project.id}", user), project_param - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['name']).to eq(['has already been taken']) end it 'should update path & name to existing path & name in different namespace' do project_param = { path: project4.path, name: project4.name } put api("/projects/#{project3.id}", user), project_param - expect(response.status).to eq(200) + expect(response).to have_http_status(200) project_param.each_pair do |k, v| expect(json_response[k.to_s]).to eq(v) end @@ -894,7 +894,7 @@ describe API::API, api: true do it 'should update path' do project_param = { path: 'bar' } put api("/projects/#{project3.id}", user4), project_param - expect(response.status).to eq(200) + expect(response).to have_http_status(200) project_param.each_pair do |k, v| expect(json_response[k.to_s]).to eq(v) end @@ -908,7 +908,7 @@ describe API::API, api: true do description: 'new description' } put api("/projects/#{project3.id}", user4), project_param - expect(response.status).to eq(200) + expect(response).to have_http_status(200) project_param.each_pair do |k, v| expect(json_response[k.to_s]).to eq(v) end @@ -917,20 +917,20 @@ describe API::API, api: true do it 'should not update path to existing path' do project_param = { path: project.path } put api("/projects/#{project3.id}", user4), project_param - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['path']).to eq(['has already been taken']) end it 'should not update name' do project_param = { name: 'bar' } put api("/projects/#{project3.id}", user4), project_param - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it 'should not update visibility_level' do project_param = { visibility_level: 20 } put api("/projects/#{project3.id}", user4), project_param - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -943,7 +943,7 @@ describe API::API, api: true do merge_requests_enabled: true, description: 'new description' } put api("/projects/#{project.id}", user3), project_param - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end end @@ -953,7 +953,7 @@ describe API::API, api: true do it 'archives the project' do post api("/projects/#{project.id}/archive", user) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['archived']).to be_truthy end end @@ -966,7 +966,7 @@ describe API::API, api: true do it 'remains archived' do post api("/projects/#{project.id}/archive", user) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['archived']).to be_truthy end end @@ -979,7 +979,7 @@ describe API::API, api: true do it 'rejects the action' do post api("/projects/#{project.id}/archive", user3) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end end @@ -989,7 +989,7 @@ describe API::API, api: true do it 'remains unarchived' do post api("/projects/#{project.id}/unarchive", user) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['archived']).to be_falsey end end @@ -1002,7 +1002,7 @@ describe API::API, api: true do it 'unarchives the project' do post api("/projects/#{project.id}/unarchive", user) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['archived']).to be_falsey end end @@ -1015,7 +1015,7 @@ describe API::API, api: true do it 'rejects the action' do post api("/projects/#{project.id}/unarchive", user3) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end end @@ -1025,7 +1025,7 @@ describe API::API, api: true do it 'stars the project' do expect { post api("/projects/#{project.id}/star", user) }.to change { project.reload.star_count }.by(1) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['star_count']).to eq(1) end end @@ -1039,7 +1039,7 @@ describe API::API, api: true do it 'does not modify the star count' do expect { post api("/projects/#{project.id}/star", user) }.not_to change { project.reload.star_count } - expect(response.status).to eq(304) + expect(response).to have_http_status(304) end end end @@ -1054,7 +1054,7 @@ describe API::API, api: true do it 'unstars the project' do expect { delete api("/projects/#{project.id}/star", user) }.to change { project.reload.star_count }.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['star_count']).to eq(0) end end @@ -1063,7 +1063,7 @@ describe API::API, api: true do it 'does not modify the star count' do expect { delete api("/projects/#{project.id}/star", user) }.not_to change { project.reload.star_count } - expect(response.status).to eq(304) + expect(response).to have_http_status(304) end end end @@ -1072,36 +1072,36 @@ describe API::API, api: true do context 'when authenticated as user' do it 'should remove project' do delete api("/projects/#{project.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'should not remove a project if not an owner' do user3 = create(:user) project.team << [user3, :developer] delete api("/projects/#{project.id}", user3) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it 'should not remove a non existing project' do delete api('/projects/1328', user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'should not remove a project not attached to user' do delete api("/projects/#{project.id}", user2) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end context 'when authenticated as admin' do it 'should remove any existing project' do delete api("/projects/#{project.id}", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'should not remove a non existing project' do delete api('/projects/1328', admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb index 7cf4a01d76b..5890e9c9d3d 100644 --- a/spec/requests/api/repositories_spec.rb +++ b/spec/requests/api/repositories_spec.rb @@ -18,7 +18,7 @@ describe API::API, api: true do it "should return project commits" do get api("/projects/#{project.id}/repository/tree", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['name']).to eq('encoding') @@ -28,7 +28,7 @@ describe API::API, api: true do it 'should return a 404 for unknown ref' do get api("/projects/#{project.id}/repository/tree?ref_name=foo", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response).to be_an Object json_response['message'] == '404 Tree Not Found' @@ -38,7 +38,7 @@ describe API::API, api: true do context "unauthorized user" do it "should not return project commits" do get api("/projects/#{project.id}/repository/tree") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -46,41 +46,41 @@ describe API::API, api: true do describe "GET /projects/:id/repository/blobs/:sha" do it "should get the raw file contents" do get api("/projects/#{project.id}/repository/blobs/master?filepath=README.md", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "should return 404 for invalid branch_name" do get api("/projects/#{project.id}/repository/blobs/invalid_branch_name?filepath=README.md", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should return 404 for invalid file" do get api("/projects/#{project.id}/repository/blobs/master?filepath=README.invalid", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should return a 400 error if filepath is missing" do get api("/projects/#{project.id}/repository/blobs/master", user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end describe "GET /projects/:id/repository/commits/:sha/blob" do it "should get the raw file contents" do get api("/projects/#{project.id}/repository/commits/master/blob?filepath=README.md", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end describe "GET /projects/:id/repository/raw_blobs/:sha" do it "should get the raw file contents" do get api("/projects/#{project.id}/repository/raw_blobs/#{sample_blob.oid}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'should return a 404 for unknown blob' do get api("/projects/#{project.id}/repository/raw_blobs/123456", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response).to be_an Object json_response['message'] == '404 Blob Not Found' @@ -91,7 +91,7 @@ describe API::API, api: true do it "should get the archive" do get api("/projects/#{project.id}/repository/archive", user) repo_name = project.repository.name.gsub("\.git", "") - expect(response.status).to eq(200) + expect(response).to have_http_status(200) type, params = workhorse_send_data expect(type).to eq('git-archive') expect(params['ArchivePath']).to match(/#{repo_name}\-[^\.]+\.tar.gz/) @@ -100,7 +100,7 @@ describe API::API, api: true do it "should get the archive.zip" do get api("/projects/#{project.id}/repository/archive.zip", user) repo_name = project.repository.name.gsub("\.git", "") - expect(response.status).to eq(200) + expect(response).to have_http_status(200) type, params = workhorse_send_data expect(type).to eq('git-archive') expect(params['ArchivePath']).to match(/#{repo_name}\-[^\.]+\.zip/) @@ -109,7 +109,7 @@ describe API::API, api: true do it "should get the archive.tar.bz2" do get api("/projects/#{project.id}/repository/archive.tar.bz2", user) repo_name = project.repository.name.gsub("\.git", "") - expect(response.status).to eq(200) + expect(response).to have_http_status(200) type, params = workhorse_send_data expect(type).to eq('git-archive') expect(params['ArchivePath']).to match(/#{repo_name}\-[^\.]+\.tar.bz2/) @@ -117,28 +117,28 @@ describe API::API, api: true do it "should return 404 for invalid sha" do get api("/projects/#{project.id}/repository/archive/?sha=xxx", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end describe 'GET /projects/:id/repository/compare' do it "should compare branches" do get api("/projects/#{project.id}/repository/compare", user), from: 'master', to: 'feature' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['commits']).to be_present expect(json_response['diffs']).to be_present end it "should compare tags" do get api("/projects/#{project.id}/repository/compare", user), from: 'v1.0.0', to: 'v1.1.0' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['commits']).to be_present expect(json_response['diffs']).to be_present end it "should compare commits" do get api("/projects/#{project.id}/repository/compare", user), from: sample_commit.id, to: sample_commit.parent_id - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['commits']).to be_empty expect(json_response['diffs']).to be_empty expect(json_response['compare_same_ref']).to be_falsey @@ -146,14 +146,14 @@ describe API::API, api: true do it "should compare commits in reverse order" do get api("/projects/#{project.id}/repository/compare", user), from: sample_commit.parent_id, to: sample_commit.id - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['commits']).to be_present expect(json_response['diffs']).to be_present end it "should compare same refs" do get api("/projects/#{project.id}/repository/compare", user), from: 'master', to: 'master' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['commits']).to be_empty expect(json_response['diffs']).to be_empty expect(json_response['compare_same_ref']).to be_truthy @@ -163,7 +163,7 @@ describe API::API, api: true do describe 'GET /projects/:id/repository/contributors' do it 'should return valid data' do get api("/projects/#{project.id}/repository/contributors", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array contributor = json_response.first expect(contributor['email']).to eq('dmitriy.zaporozhets@gmail.com') diff --git a/spec/requests/api/runners_spec.rb b/spec/requests/api/runners_spec.rb index b4c826522a5..00a3c917b6a 100644 --- a/spec/requests/api/runners_spec.rb +++ b/spec/requests/api/runners_spec.rb @@ -39,7 +39,7 @@ describe API::Runners, api: true do get api('/runners', user) shared = json_response.any?{ |r| r['is_shared'] } - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(shared).to be_falsey end @@ -48,14 +48,14 @@ describe API::Runners, api: true do get api('/runners?scope=active', user) shared = json_response.any?{ |r| r['is_shared'] } - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(shared).to be_falsey end it 'should avoid filtering if scope is invalid' do get api('/runners?scope=unknown', user) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end @@ -63,7 +63,7 @@ describe API::Runners, api: true do it 'should not return runners' do get api('/runners') - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -75,7 +75,7 @@ describe API::Runners, api: true do get api('/runners/all', admin) shared = json_response.any?{ |r| r['is_shared'] } - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(shared).to be_truthy end @@ -85,7 +85,7 @@ describe API::Runners, api: true do it 'should not return runners list' do get api('/runners/all', user) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -93,14 +93,14 @@ describe API::Runners, api: true do get api('/runners/all?scope=specific', admin) shared = json_response.any?{ |r| r['is_shared'] } - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(shared).to be_falsey end it 'should avoid filtering if scope is invalid' do get api('/runners?scope=unknown', admin) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end @@ -108,7 +108,7 @@ describe API::Runners, api: true do it 'should not return runners' do get api('/runners') - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -119,7 +119,7 @@ describe API::Runners, api: true do it "should return runner's details" do get api("/runners/#{shared_runner.id}", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['description']).to eq(shared_runner.description) end end @@ -128,7 +128,7 @@ describe API::Runners, api: true do it "should return runner's details" do get api("/runners/#{specific_runner.id}", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['description']).to eq(specific_runner.description) end end @@ -136,7 +136,7 @@ describe API::Runners, api: true do it 'should return 404 if runner does not exists' do get api('/runners/9999', admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -145,7 +145,7 @@ describe API::Runners, api: true do it "should return runner's details" do get api("/runners/#{specific_runner.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['description']).to eq(specific_runner.description) end end @@ -154,7 +154,7 @@ describe API::Runners, api: true do it "should return runner's details" do get api("/runners/#{shared_runner.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['description']).to eq(shared_runner.description) end end @@ -164,7 +164,7 @@ describe API::Runners, api: true do it "should not return runner's details" do get api("/runners/#{specific_runner.id}", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -172,7 +172,7 @@ describe API::Runners, api: true do it "should not return runner's details" do get api("/runners/#{specific_runner.id}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -191,7 +191,7 @@ describe API::Runners, api: true do locked: 'true') shared_runner.reload - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(shared_runner.description).to eq("#{description}_updated") expect(shared_runner.active).to eq(!active) expect(shared_runner.tag_list).to include('ruby2.1', 'pgsql', 'mysql') @@ -206,7 +206,7 @@ describe API::Runners, api: true do update_runner(specific_runner.id, admin, description: 'test') specific_runner.reload - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(specific_runner.description).to eq('test') expect(specific_runner.description).not_to eq(description) end @@ -215,7 +215,7 @@ describe API::Runners, api: true do it 'should return 404 if runner does not exists' do update_runner(9999, admin, description: 'test') - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end def update_runner(id, user, args) @@ -228,7 +228,7 @@ describe API::Runners, api: true do it 'should not update runner' do put api("/runners/#{shared_runner.id}", user) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -236,7 +236,7 @@ describe API::Runners, api: true do it 'should not update runner without access to it' do put api("/runners/#{specific_runner.id}", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it 'should update runner with access to it' do @@ -244,7 +244,7 @@ describe API::Runners, api: true do put api("/runners/#{specific_runner.id}", admin), description: 'test' specific_runner.reload - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(specific_runner.description).to eq('test') expect(specific_runner.description).not_to eq(description) end @@ -255,7 +255,7 @@ describe API::Runners, api: true do it 'should not delete runner' do put api("/runners/#{specific_runner.id}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -267,7 +267,7 @@ describe API::Runners, api: true do expect do delete api("/runners/#{shared_runner.id}", admin) end.to change{ Ci::Runner.shared.count }.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -276,21 +276,21 @@ describe API::Runners, api: true do expect do delete api("/runners/#{unused_specific_runner.id}", admin) end.to change{ Ci::Runner.specific.count }.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'should delete used runner' do expect do delete api("/runners/#{specific_runner.id}", admin) end.to change{ Ci::Runner.specific.count }.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end it 'should return 404 if runner does not exists' do delete api('/runners/9999', admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -298,26 +298,26 @@ describe API::Runners, api: true do context 'when runner is shared' do it 'should not delete runner' do delete api("/runners/#{shared_runner.id}", user) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end context 'when runner is not shared' do it 'should not delete runner without access to it' do delete api("/runners/#{specific_runner.id}", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it 'should not delete runner with more than one associated project' do delete api("/runners/#{two_projects_runner.id}", user) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it 'should delete runner for one owned project' do expect do delete api("/runners/#{specific_runner.id}", user) end.to change{ Ci::Runner.specific.count }.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -326,7 +326,7 @@ describe API::Runners, api: true do it 'should not delete runner' do delete api("/runners/#{specific_runner.id}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -337,7 +337,7 @@ describe API::Runners, api: true do get api("/projects/#{project.id}/runners", user) shared = json_response.any?{ |r| r['is_shared'] } - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(shared).to be_truthy end @@ -347,7 +347,7 @@ describe API::Runners, api: true do it "should not return project's runners" do get api("/projects/#{project.id}/runners", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -355,7 +355,7 @@ describe API::Runners, api: true do it "should not return project's runners" do get api("/projects/#{project.id}/runners") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -372,14 +372,14 @@ describe API::Runners, api: true do expect do post api("/projects/#{project.id}/runners", user), runner_id: specific_runner2.id end.to change{ project.runners.count }.by(+1) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) end it 'should avoid changes when enabling already enabled runner' do expect do post api("/projects/#{project.id}/runners", user), runner_id: specific_runner.id end.to change{ project.runners.count }.by(0) - expect(response.status).to eq(409) + expect(response).to have_http_status(409) end it 'should not enable locked runner' do @@ -389,13 +389,13 @@ describe API::Runners, api: true do post api("/projects/#{project.id}/runners", user), runner_id: specific_runner2.id end.to change{ project.runners.count }.by(0) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it 'should not enable shared runner' do post api("/projects/#{project.id}/runners", user), runner_id: shared_runner.id - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end context 'user is admin' do @@ -403,7 +403,7 @@ describe API::Runners, api: true do expect do post api("/projects/#{project.id}/runners", admin), runner_id: unused_specific_runner.id end.to change{ project.runners.count }.by(+1) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) end end @@ -411,14 +411,14 @@ describe API::Runners, api: true do it 'should not enable runner without access to' do post api("/projects/#{project.id}/runners", user), runner_id: unused_specific_runner.id - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end it 'should raise an error when no runner_id param is provided' do post api("/projects/#{project.id}/runners", admin) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end @@ -426,7 +426,7 @@ describe API::Runners, api: true do it 'should not enable runner' do post api("/projects/#{project.id}/runners", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -434,7 +434,7 @@ describe API::Runners, api: true do it 'should not enable runner' do post api("/projects/#{project.id}/runners") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -446,7 +446,7 @@ describe API::Runners, api: true do expect do delete api("/projects/#{project.id}/runners/#{two_projects_runner.id}", user) end.to change{ project.runners.count }.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -455,14 +455,14 @@ describe API::Runners, api: true do expect do delete api("/projects/#{project.id}/runners/#{specific_runner.id}", user) end.to change{ project.runners.count }.by(0) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end it 'should return 404 is runner is not found' do delete api("/projects/#{project.id}/runners/9999", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -470,7 +470,7 @@ describe API::Runners, api: true do it "should not disable project's runner" do delete api("/projects/#{project.id}/runners/#{specific_runner.id}", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -478,7 +478,7 @@ describe API::Runners, api: true do it "should not disable project's runner" do delete api("/projects/#{project.id}/runners/#{specific_runner.id}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb index fed9ae1949b..bf7eaaaaaed 100644 --- a/spec/requests/api/services_spec.rb +++ b/spec/requests/api/services_spec.rb @@ -14,7 +14,7 @@ describe API::API, api: true do it "should update #{service} settings" do put api("/projects/#{project.id}/services/#{dashed_service}", user), service_attrs - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "should return if required fields missing" do @@ -45,7 +45,7 @@ describe API::API, api: true do it "should delete #{service}" do delete api("/projects/#{project.id}/services/#{dashed_service}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) project.send(service_method).reload expect(project.send(service_method).activated?).to be_falsey end @@ -64,20 +64,20 @@ describe API::API, api: true do it 'should return authentication error when unauthenticated' do get api("/projects/#{project.id}/services/#{dashed_service}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end it "should return all properties of service #{service} when authenticated as admin" do get api("/projects/#{project.id}/services/#{dashed_service}", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['properties'].keys.map(&:to_sym)).to match_array(service_attrs_list.map) end it "should return properties of service #{service} other than passwords when authenticated as project owner" do get api("/projects/#{project.id}/services/#{dashed_service}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['properties'].keys.map(&:to_sym)).to match_array(service_attrs_list_without_passwords) end @@ -85,7 +85,7 @@ describe API::API, api: true do project.team << [user2, :developer] get api("/projects/#{project.id}/services/#{dashed_service}", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end diff --git a/spec/requests/api/session_spec.rb b/spec/requests/api/session_spec.rb index fbd57b34a58..c15b7ff9792 100644 --- a/spec/requests/api/session_spec.rb +++ b/spec/requests/api/session_spec.rb @@ -9,7 +9,7 @@ describe API::API, api: true do context "when valid password" do it "should return private token" do post api("/session"), email: user.email, password: '12345678' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['email']).to eq(user.email) expect(json_response['private_token']).to eq(user.private_token) @@ -48,7 +48,7 @@ describe API::API, api: true do context "when invalid password" do it "should return authentication error" do post api("/session"), email: user.email, password: '123' - expect(response.status).to eq(401) + expect(response).to have_http_status(401) expect(json_response['email']).to be_nil expect(json_response['private_token']).to be_nil @@ -58,7 +58,7 @@ describe API::API, api: true do context "when empty password" do it "should return authentication error" do post api("/session"), email: user.email - expect(response.status).to eq(401) + expect(response).to have_http_status(401) expect(json_response['email']).to be_nil expect(json_response['private_token']).to be_nil @@ -68,7 +68,7 @@ describe API::API, api: true do context "when empty name" do it "should return authentication error" do post api("/session"), password: user.password - expect(response.status).to eq(401) + expect(response).to have_http_status(401) expect(json_response['email']).to be_nil expect(json_response['private_token']).to be_nil diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb index c815a8e1d73..f756101c514 100644 --- a/spec/requests/api/settings_spec.rb +++ b/spec/requests/api/settings_spec.rb @@ -10,7 +10,7 @@ describe API::API, 'Settings', api: true do describe "GET /application/settings" do it "should return application settings" do get api("/application/settings", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Hash expect(json_response['default_projects_limit']).to eq(42) expect(json_response['signin_enabled']).to be_truthy @@ -21,7 +21,7 @@ describe API::API, 'Settings', api: true do it "should update application settings" do put api("/application/settings", admin), default_projects_limit: 3, signin_enabled: false - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['default_projects_limit']).to eq(3) expect(json_response['signin_enabled']).to be_falsey end diff --git a/spec/requests/api/sidekiq_metrics_spec.rb b/spec/requests/api/sidekiq_metrics_spec.rb index 41cbf0c6669..28067f8ca88 100644 --- a/spec/requests/api/sidekiq_metrics_spec.rb +++ b/spec/requests/api/sidekiq_metrics_spec.rb @@ -9,28 +9,28 @@ describe API::SidekiqMetrics, api: true do it 'defines the `queue_metrics` endpoint' do get api('/sidekiq/queue_metrics', admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_a Hash end it 'defines the `process_metrics` endpoint' do get api('/sidekiq/process_metrics', admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['processes']).to be_an Array end it 'defines the `job_stats` endpoint' do get api('/sidekiq/job_stats', admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_a Hash end it 'defines the `compound_metrics` endpoint' do get api('/sidekiq/compound_metrics', admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_a Hash expect(json_response['queues']).to be_a Hash expect(json_response['processes']).to be_an Array diff --git a/spec/requests/api/system_hooks_spec.rb b/spec/requests/api/system_hooks_spec.rb index 94eebc48ec8..cf66f261ade 100644 --- a/spec/requests/api/system_hooks_spec.rb +++ b/spec/requests/api/system_hooks_spec.rb @@ -13,21 +13,21 @@ describe API::API, api: true do context "when no user" do it "should return authentication error" do get api("/hooks") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end context "when not an admin" do it "should return forbidden error" do get api("/hooks", user) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end context "when authenticated as admin" do it "should return an array of hooks" do get api("/hooks", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['url']).to eq(hook.url) end @@ -43,7 +43,7 @@ describe API::API, api: true do it "should respond with 400 if url not given" do post api("/hooks", admin) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it "should not create new hook without url" do @@ -56,13 +56,13 @@ describe API::API, api: true do describe "GET /hooks/:id" do it "should return hook by id" do get api("/hooks/#{hook.id}", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['event_name']).to eq('project_create') end it "should return 404 on failure" do get api("/hooks/404", admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -75,7 +75,7 @@ describe API::API, api: true do it "should return success if hook id not found" do delete api("/hooks/12345", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb index 12e170b232f..fa700ab7343 100644 --- a/spec/requests/api/tags_spec.rb +++ b/spec/requests/api/tags_spec.rb @@ -18,7 +18,7 @@ describe API::API, api: true do context 'without releases' do it "should return an array of project tags" do get api("/projects/#{project.id}/repository/tags", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['name']).to eq(tag_name) end @@ -33,7 +33,7 @@ describe API::API, api: true do it "should return an array of project tags with release info" do get api("/projects/#{project.id}/repository/tags", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['name']).to eq(tag_name) expect(json_response.first['message']).to eq('Version 1.1.0') @@ -48,14 +48,14 @@ describe API::API, api: true do it 'returns a specific tag' do get api("/projects/#{project.id}/repository/tags/#{tag_name}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['name']).to eq(tag_name) end it 'returns 404 for an invalid tag name' do get api("/projects/#{project.id}/repository/tags/foobar", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -66,7 +66,7 @@ describe API::API, api: true do tag_name: 'v7.0.1', ref: 'master' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['name']).to eq('v7.0.1') end end @@ -78,7 +78,7 @@ describe API::API, api: true do ref: 'master', release_description: 'Wow' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['name']).to eq('v7.0.1') expect(json_response['release']['description']).to eq('Wow') end @@ -94,13 +94,13 @@ describe API::API, api: true do context 'delete tag' do it 'should delete an existing tag' do delete api("/projects/#{project.id}/repository/tags/#{tag_name}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['tag_name']).to eq(tag_name) end it 'should raise 404 if the tag does not exist' do delete api("/projects/#{project.id}/repository/tags/foobar", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -117,7 +117,7 @@ describe API::API, api: true do ref: 'master', message: 'Release 7.1.0' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['name']).to eq('v7.1.0') expect(json_response['message']).to eq('Release 7.1.0') end @@ -127,14 +127,14 @@ describe API::API, api: true do post api("/projects/#{project.id}/repository/tags", user2), tag_name: 'v1.9.0', ref: '621491c677087aa243f165eab467bfdfbee00be1' - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it 'should return 400 if tag name is invalid' do post api("/projects/#{project.id}/repository/tags", user), tag_name: 'v 1.0.0', ref: 'master' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('Tag name invalid') end @@ -142,11 +142,11 @@ describe API::API, api: true do post api("/projects/#{project.id}/repository/tags", user), tag_name: 'v8.0.0', ref: 'master' - expect(response.status).to eq(201) + expect(response).to have_http_status(201) post api("/projects/#{project.id}/repository/tags", user), tag_name: 'v8.0.0', ref: 'master' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('Tag v8.0.0 already exists') end @@ -154,7 +154,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/repository/tags", user), tag_name: 'mytag', ref: 'foo' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('Target foo is invalid') end end @@ -167,7 +167,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user), description: description - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['tag_name']).to eq(tag_name) expect(json_response['description']).to eq(description) end @@ -176,7 +176,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/repository/tags/foobar/release", user), description: description - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('Tag does not exist') end @@ -190,7 +190,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user), description: description - expect(response.status).to eq(409) + expect(response).to have_http_status(409) expect(json_response['message']).to eq('Release already exists') end end @@ -211,7 +211,7 @@ describe API::API, api: true do put api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user), description: new_description - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['tag_name']).to eq(tag_name) expect(json_response['description']).to eq(new_description) end @@ -221,7 +221,7 @@ describe API::API, api: true do put api("/projects/#{project.id}/repository/tags/foobar/release", user), description: new_description - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('Tag does not exist') end @@ -229,7 +229,7 @@ describe API::API, api: true do put api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user), description: new_description - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('Release does not exist') end end diff --git a/spec/requests/api/templates_spec.rb b/spec/requests/api/templates_spec.rb index a6d5ade3013..68d0f41b489 100644 --- a/spec/requests/api/templates_spec.rb +++ b/spec/requests/api/templates_spec.rb @@ -22,7 +22,7 @@ describe API::Templates, api: true do it 'returns a list of available gitignore templates' do get api('/gitignores') - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.size).to be > 15 end @@ -34,7 +34,7 @@ describe API::Templates, api: true do it 'returns a list of available gitlab_ci_ymls' do get api('/gitlab_ci_ymls') - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['name']).not_to be_nil end @@ -45,7 +45,7 @@ describe API::Templates, api: true do it 'adds a disclaimer on the top' do get api('/gitlab_ci_ymls/Ruby') - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['content']).to start_with("# This file is a template,") end end diff --git a/spec/requests/api/triggers_spec.rb b/spec/requests/api/triggers_spec.rb index fdd4ec6d761..8992996c30a 100644 --- a/spec/requests/api/triggers_spec.rb +++ b/spec/requests/api/triggers_spec.rb @@ -29,17 +29,17 @@ describe API::API do context 'Handles errors' do it 'should return bad request if token is missing' do post api("/projects/#{project.id}/trigger/builds"), ref: 'master' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it 'should return not found if project is not found' do post api('/projects/0/trigger/builds'), options.merge(ref: 'master') - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'should return unauthorized if token is for different project' do post api("/projects/#{project2.id}/trigger/builds"), options.merge(ref: 'master') - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end @@ -48,14 +48,14 @@ describe API::API do it 'should create builds' do post api("/projects/#{project.id}/trigger/builds"), options.merge(ref: 'master') - expect(response.status).to eq(201) + expect(response).to have_http_status(201) pipeline.builds.reload expect(pipeline.builds.size).to eq(2) end it 'should return bad request with no builds created if there\'s no commit for that ref' do post api("/projects/#{project.id}/trigger/builds"), options.merge(ref: 'other-branch') - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('No builds created') end @@ -66,19 +66,19 @@ describe API::API do it 'should validate variables to be a hash' do post api("/projects/#{project.id}/trigger/builds"), options.merge(variables: 'value', ref: 'master') - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('variables needs to be a hash') end it 'should validate variables needs to be a map of key-valued strings' do post api("/projects/#{project.id}/trigger/builds"), options.merge(variables: { key: %w(1 2) }, ref: 'master') - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('variables needs to be a map of key-valued strings') end it 'create trigger request with variables' do post api("/projects/#{project.id}/trigger/builds"), options.merge(variables: variables, ref: 'master') - expect(response.status).to eq(201) + expect(response).to have_http_status(201) pipeline.builds.reload expect(pipeline.builds.first.trigger_request.variables).to eq(variables) end @@ -91,7 +91,7 @@ describe API::API do it 'should return list of triggers' do get api("/projects/#{project.id}/triggers", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_a(Array) expect(json_response[0]).to have_key('token') end @@ -101,7 +101,7 @@ describe API::API do it 'should not return triggers list' do get api("/projects/#{project.id}/triggers", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -109,7 +109,7 @@ describe API::API do it 'should not return triggers list' do get api("/projects/#{project.id}/triggers") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -119,14 +119,14 @@ describe API::API do it 'should return trigger details' do get api("/projects/#{project.id}/triggers/#{trigger.token}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_a(Hash) end it 'should respond with 404 Not Found if requesting non-existing trigger' do get api("/projects/#{project.id}/triggers/abcdef012345", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -134,7 +134,7 @@ describe API::API do it 'should not return triggers list' do get api("/projects/#{project.id}/triggers/#{trigger.token}", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -142,7 +142,7 @@ describe API::API do it 'should not return triggers list' do get api("/projects/#{project.id}/triggers/#{trigger.token}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -154,7 +154,7 @@ describe API::API do post api("/projects/#{project.id}/triggers", user) end.to change{project.triggers.count}.by(1) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response).to be_a(Hash) end end @@ -163,7 +163,7 @@ describe API::API do it 'should not create trigger' do post api("/projects/#{project.id}/triggers", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -171,7 +171,7 @@ describe API::API do it 'should not create trigger' do post api("/projects/#{project.id}/triggers") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -182,13 +182,13 @@ describe API::API do expect do delete api("/projects/#{project.id}/triggers/#{trigger.token}", user) end.to change{project.triggers.count}.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'should respond with 404 Not Found if requesting non-existing trigger' do delete api("/projects/#{project.id}/triggers/abcdef012345", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -196,7 +196,7 @@ describe API::API do it 'should not delete trigger' do delete api("/projects/#{project.id}/triggers/#{trigger.token}", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -204,7 +204,7 @@ describe API::API do it 'should not delete trigger' do delete api("/projects/#{project.id}/triggers/#{trigger.token}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index a7690f430c4..056256a29f5 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -15,7 +15,7 @@ describe API::API, api: true do context "when unauthenticated" do it "should return authentication error" do get api("/users") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end @@ -29,18 +29,18 @@ describe API::API, api: true do it "renders 403" do get api("/users") - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it "renders 404" do get api("/users/#{user.id}") - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end it "should return an array of users" do get api("/users", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array username = user.username expect(json_response.detect do |user| @@ -50,7 +50,7 @@ describe API::API, api: true do it "should return one user" do get api("/users?username=#{omniauth_user.username}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['username']).to eq(omniauth_user.username) end @@ -59,7 +59,7 @@ describe API::API, api: true do context "when admin" do it "should return an array of users" do get api("/users", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first.keys).to include 'email' expect(json_response.first.keys).to include 'identities' @@ -74,24 +74,24 @@ describe API::API, api: true do describe "GET /users/:id" do it "should return a user by id" do get api("/users/#{user.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['username']).to eq(user.username) end it "should return a 401 if unauthenticated" do get api("/users/9998") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end it "should return a 404 error if user id not found" do get api("/users/9999", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 Not found') end it "should return a 404 if invalid ID" do get api("/users/1ASDF", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -106,7 +106,7 @@ describe API::API, api: true do it "should create user with correct attributes" do post api('/users', admin), attributes_for(:user, admin: true, can_create_group: true) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) user_id = json_response['id'] new_user = User.find(user_id) expect(new_user).not_to eq(nil) @@ -116,7 +116,7 @@ describe API::API, api: true do it "should create non-admin user" do post api('/users', admin), attributes_for(:user, admin: false, can_create_group: false) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) user_id = json_response['id'] new_user = User.find(user_id) expect(new_user).not_to eq(nil) @@ -126,7 +126,7 @@ describe API::API, api: true do it "should create non-admin users by default" do post api('/users', admin), attributes_for(:user) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) user_id = json_response['id'] new_user = User.find(user_id) expect(new_user).not_to eq(nil) @@ -135,12 +135,12 @@ describe API::API, api: true do it "should return 201 Created on success" do post api("/users", admin), attributes_for(:user, projects_limit: 3) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) end it 'creates non-external users by default' do post api("/users", admin), attributes_for(:user) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) user_id = json_response['id'] new_user = User.find(user_id) @@ -150,7 +150,7 @@ describe API::API, api: true do it 'should allow an external user to be created' do post api("/users", admin), attributes_for(:user, external: true) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) user_id = json_response['id'] new_user = User.find(user_id) @@ -163,27 +163,27 @@ describe API::API, api: true do email: 'invalid email', password: 'password', name: 'test' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it 'should return 400 error if name not given' do post api('/users', admin), attributes_for(:user).except(:name) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it 'should return 400 error if password not given' do post api('/users', admin), attributes_for(:user).except(:password) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it 'should return 400 error if email not given' do post api('/users', admin), attributes_for(:user).except(:email) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it 'should return 400 error if username not given' do post api('/users', admin), attributes_for(:user).except(:username) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it 'should return 400 error if user does not validate' do @@ -194,7 +194,7 @@ describe API::API, api: true do name: 'test', bio: 'g' * 256, projects_limit: -1 - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['password']). to eq(['is too short (minimum is 8 characters)']) expect(json_response['message']['bio']). @@ -207,7 +207,7 @@ describe API::API, api: true do it "shouldn't available for non admin users" do post api("/users", user), attributes_for(:user) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end context 'with existing user' do @@ -227,7 +227,7 @@ describe API::API, api: true do password: 'password', username: 'foo' end.to change { User.count }.by(0) - expect(response.status).to eq(409) + expect(response).to have_http_status(409) expect(json_response['message']).to eq('Email has already been taken') end @@ -239,7 +239,7 @@ describe API::API, api: true do password: 'password', username: 'test' end.to change { User.count }.by(0) - expect(response.status).to eq(409) + expect(response).to have_http_status(409) expect(json_response['message']).to eq('Username has already been taken') end end @@ -249,7 +249,7 @@ describe API::API, api: true do it "should redirect to sign in page" do get "/users/sign_up" - expect(response.status).to eq(302) + expect(response).to have_http_status(302) expect(response).to redirect_to(new_user_session_path) end end @@ -261,41 +261,41 @@ describe API::API, api: true do it "should update user with new bio" do put api("/users/#{user.id}", admin), { bio: 'new test bio' } - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['bio']).to eq('new test bio') expect(user.reload.bio).to eq('new test bio') end it 'should update user with his own email' do put api("/users/#{user.id}", admin), email: user.email - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['email']).to eq(user.email) expect(user.reload.email).to eq(user.email) end it 'should update user with his own username' do put api("/users/#{user.id}", admin), username: user.username - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['username']).to eq(user.username) expect(user.reload.username).to eq(user.username) end it "should update user's existing identity" do put api("/users/#{omniauth_user.id}", admin), provider: 'ldapmain', extern_uid: '654321' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(omniauth_user.reload.identities.first.extern_uid).to eq('654321') end it 'should update user with new identity' do put api("/users/#{user.id}", admin), provider: 'github', extern_uid: '67890' - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(user.reload.identities.first.extern_uid).to eq('67890') expect(user.reload.identities.first.provider).to eq('github') end it "should update admin status" do put api("/users/#{user.id}", admin), { admin: true } - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['is_admin']).to eq(true) expect(user.reload.admin).to eq(true) end @@ -309,7 +309,7 @@ describe API::API, api: true do it "should not update admin status" do put api("/users/#{admin_user.id}", admin), { can_create_group: false } - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['is_admin']).to eq(true) expect(admin_user.reload.admin).to eq(true) expect(admin_user.can_create_group).to eq(false) @@ -317,18 +317,18 @@ describe API::API, api: true do it "should not allow invalid update" do put api("/users/#{user.id}", admin), { email: 'invalid email' } - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(user.reload.email).not_to eq('invalid email') end it "shouldn't available for non admin users" do put api("/users/#{user.id}", user), attributes_for(:user) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it "should return 404 for non-existing user" do put api("/users/999999", admin), { bio: 'update should fail' } - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 Not found') end @@ -344,7 +344,7 @@ describe API::API, api: true do name: 'test', bio: 'g' * 256, projects_limit: -1 - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']['password']). to eq(['is too short (minimum is 8 characters)']) expect(json_response['message']['bio']). @@ -364,14 +364,14 @@ describe API::API, api: true do it 'should return 409 conflict error if email address exists' do put api("/users/#{@user.id}", admin), email: 'test@example.com' - expect(response.status).to eq(409) + expect(response).to have_http_status(409) expect(@user.reload.email).to eq(@user.email) end it 'should return 409 conflict error if username taken' do @user_id = User.all.last.id put api("/users/#{@user.id}", admin), username: 'test' - expect(response.status).to eq(409) + expect(response).to have_http_status(409) expect(@user.reload.username).to eq(@user.username) end end @@ -382,13 +382,13 @@ describe API::API, api: true do it "should not create invalid ssh key" do post api("/users/#{user.id}/keys", admin), { title: "invalid key" } - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('400 (Bad request) "key" not given') end it 'should not create key without title' do post api("/users/#{user.id}/keys", admin), key: 'some key' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('400 (Bad request) "title" not given') end @@ -401,7 +401,7 @@ describe API::API, api: true do it "should return 405 for invalid ID" do post api("/users/ASDF/keys", admin) - expect(response.status).to eq(405) + expect(response).to have_http_status(405) end end @@ -411,14 +411,14 @@ describe API::API, api: true do context 'when unauthenticated' do it 'should return authentication error' do get api("/users/#{user.id}/keys") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end context 'when authenticated' do it 'should return 404 for non-existing user' do get api('/users/999999/keys', admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 User Not Found') end @@ -426,14 +426,14 @@ describe API::API, api: true do user.keys << key user.save get api("/users/#{user.id}/keys", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['title']).to eq(key.title) end it "should return 405 for invalid ID" do get api("/users/ASDF/keys", admin) - expect(response.status).to eq(405) + expect(response).to have_http_status(405) end end end @@ -444,7 +444,7 @@ describe API::API, api: true do context 'when unauthenticated' do it 'should return authentication error' do delete api("/users/#{user.id}/keys/42") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end @@ -455,20 +455,20 @@ describe API::API, api: true do expect do delete api("/users/#{user.id}/keys/#{key.id}", admin) end.to change { user.keys.count }.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'should return 404 error if user not found' do user.keys << key user.save delete api("/users/999999/keys/#{key.id}", admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 User Not Found') end it 'should return 404 error if key not foud' do delete api("/users/#{user.id}/keys/42", admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 Key Not Found') end end @@ -479,7 +479,7 @@ describe API::API, api: true do it "should not create invalid email" do post api("/users/#{user.id}/emails", admin), {} - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('400 (Bad request) "email" not given') end @@ -492,7 +492,7 @@ describe API::API, api: true do it "should raise error for invalid ID" do post api("/users/ASDF/emails", admin) - expect(response.status).to eq(405) + expect(response).to have_http_status(405) end end @@ -502,14 +502,14 @@ describe API::API, api: true do context 'when unauthenticated' do it 'should return authentication error' do get api("/users/#{user.id}/emails") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end context 'when authenticated' do it 'should return 404 for non-existing user' do get api('/users/999999/emails', admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 User Not Found') end @@ -517,14 +517,14 @@ describe API::API, api: true do user.emails << email user.save get api("/users/#{user.id}/emails", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['email']).to eq(email.email) end it "should raise error for invalid ID" do put api("/users/ASDF/emails", admin) - expect(response.status).to eq(405) + expect(response).to have_http_status(405) end end end @@ -535,7 +535,7 @@ describe API::API, api: true do context 'when unauthenticated' do it 'should return authentication error' do delete api("/users/#{user.id}/emails/42") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end @@ -546,20 +546,20 @@ describe API::API, api: true do expect do delete api("/users/#{user.id}/emails/#{email.id}", admin) end.to change { user.emails.count }.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'should return 404 error if user not found' do user.emails << email user.save delete api("/users/999999/emails/#{email.id}", admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 User Not Found') end it 'should return 404 error if email not foud' do delete api("/users/#{user.id}/emails/42", admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 Email Not Found') end @@ -574,24 +574,24 @@ describe API::API, api: true do it "should delete user" do delete api("/users/#{user.id}", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect { User.find(user.id) }.to raise_error ActiveRecord::RecordNotFound expect(json_response['email']).to eq(user.email) end it "should not delete for unauthenticated user" do delete api("/users/#{user.id}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end it "shouldn't available for non admin users" do delete api("/users/#{user.id}", user) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end it "should return 404 for non-existing user" do delete api("/users/999999", admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 User Not Found') end @@ -603,7 +603,7 @@ describe API::API, api: true do describe "GET /user" do it "should return current user" do get api("/user", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['email']).to eq(user.email) expect(json_response['is_admin']).to eq(user.is_admin?) expect(json_response['can_create_project']).to eq(user.can_create_project?) @@ -613,7 +613,7 @@ describe API::API, api: true do it "should return 401 error if user is unauthenticated" do get api("/user") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end @@ -621,7 +621,7 @@ describe API::API, api: true do context "when unauthenticated" do it "should return authentication error" do get api("/user/keys") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end @@ -630,7 +630,7 @@ describe API::API, api: true do user.keys << key user.save get api("/user/keys", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first["title"]).to eq(key.title) end @@ -642,13 +642,13 @@ describe API::API, api: true do user.keys << key user.save get api("/user/keys/#{key.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["title"]).to eq(key.title) end it "should return 404 Not Found within invalid ID" do get api("/user/keys/42", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 Not found') end @@ -657,13 +657,13 @@ describe API::API, api: true do user.save admin get api("/user/keys/#{key.id}", admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 Not found') end it "should return 404 for invalid ID" do get api("/users/keys/ASDF", admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -673,29 +673,29 @@ describe API::API, api: true do expect do post api("/user/keys", user), key_attrs end.to change{ user.keys.count }.by(1) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) end it "should return a 401 error if unauthorized" do post api("/user/keys"), title: 'some title', key: 'some key' - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end it "should not create ssh key without key" do post api("/user/keys", user), title: 'title' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('400 (Bad request) "key" not given') end it 'should not create ssh key without title' do post api('/user/keys', user), key: 'some key' - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('400 (Bad request) "title" not given') end it "should not create ssh key without title" do post api("/user/keys", user), key: "somekey" - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end @@ -706,19 +706,19 @@ describe API::API, api: true do expect do delete api("/user/keys/#{key.id}", user) end.to change{user.keys.count}.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "should return success if key ID not found" do delete api("/user/keys/42", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "should return 401 error if unauthorized" do user.keys << key user.save delete api("/user/keys/#{key.id}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end it "should raise error for invalid ID" do @@ -730,7 +730,7 @@ describe API::API, api: true do context "when unauthenticated" do it "should return authentication error" do get api("/user/emails") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end @@ -739,7 +739,7 @@ describe API::API, api: true do user.emails << email user.save get api("/user/emails", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first["email"]).to eq(email.email) end @@ -751,13 +751,13 @@ describe API::API, api: true do user.emails << email user.save get api("/user/emails/#{email.id}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["email"]).to eq(email.email) end it "should return 404 Not Found within invalid ID" do get api("/user/emails/42", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 Not found') end @@ -766,13 +766,13 @@ describe API::API, api: true do user.save admin get api("/user/emails/#{email.id}", admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 Not found') end it "should return 404 for invalid ID" do get api("/users/emails/ASDF", admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -782,17 +782,17 @@ describe API::API, api: true do expect do post api("/user/emails", user), email_attrs end.to change{ user.emails.count }.by(1) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) end it "should return a 401 error if unauthorized" do post api("/user/emails"), email: 'some email' - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end it "should not create email with invalid email" do post api("/user/emails", user), {} - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('400 (Bad request) "email" not given') end end @@ -804,19 +804,19 @@ describe API::API, api: true do expect do delete api("/user/emails/#{email.id}", user) end.to change{user.emails.count}.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "should return success if email ID not found" do delete api("/user/emails/42", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "should return 401 error if unauthorized" do user.emails << email user.save delete api("/user/emails/#{email.id}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end it "should raise error for invalid ID" do @@ -828,25 +828,25 @@ describe API::API, api: true do before { admin } it 'should block existing user' do put api("/users/#{user.id}/block", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(user.reload.state).to eq('blocked') end it 'should not re-block ldap blocked users' do put api("/users/#{ldap_blocked_user.id}/block", admin) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) expect(ldap_blocked_user.reload.state).to eq('ldap_blocked') end it 'should not be available for non admin users' do put api("/users/#{user.id}/block", user) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) expect(user.reload.state).to eq('active') end it 'should return a 404 error if user id not found' do put api('/users/9999/block', admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 User Not Found') end end @@ -857,31 +857,31 @@ describe API::API, api: true do it 'should unblock existing user' do put api("/users/#{user.id}/unblock", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(user.reload.state).to eq('active') end it 'should unblock a blocked user' do put api("/users/#{blocked_user.id}/unblock", admin) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(blocked_user.reload.state).to eq('active') end it 'should not unblock ldap blocked users' do put api("/users/#{ldap_blocked_user.id}/unblock", admin) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) expect(ldap_blocked_user.reload.state).to eq('ldap_blocked') end it 'should not be available for non admin users' do put api("/users/#{user.id}/unblock", user) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) expect(user.reload.state).to eq('active') end it 'should return a 404 error if user id not found' do put api('/users/9999/block', admin) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) expect(json_response['message']).to eq('404 User Not Found') end diff --git a/spec/requests/api/variables_spec.rb b/spec/requests/api/variables_spec.rb index b1e1053d037..ddba18245f8 100644 --- a/spec/requests/api/variables_spec.rb +++ b/spec/requests/api/variables_spec.rb @@ -15,7 +15,7 @@ describe API::API, api: true do it 'should return project variables' do get api("/projects/#{project.id}/variables", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response).to be_a(Array) end end @@ -24,7 +24,7 @@ describe API::API, api: true do it 'should not return project variables' do get api("/projects/#{project.id}/variables", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -32,7 +32,7 @@ describe API::API, api: true do it 'should not return project variables' do get api("/projects/#{project.id}/variables") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -42,14 +42,14 @@ describe API::API, api: true do it 'should return project variable details' do get api("/projects/#{project.id}/variables/#{variable.key}", user) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response['value']).to eq(variable.value) end it 'should respond with 404 Not Found if requesting non-existing variable' do get api("/projects/#{project.id}/variables/non_existing_variable", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -57,7 +57,7 @@ describe API::API, api: true do it 'should not return project variable details' do get api("/projects/#{project.id}/variables/#{variable.key}", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -65,7 +65,7 @@ describe API::API, api: true do it 'should not return project variable details' do get api("/projects/#{project.id}/variables/#{variable.key}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -77,7 +77,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/variables", user), key: 'TEST_VARIABLE_2', value: 'VALUE_2' end.to change{project.variables.count}.by(1) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['key']).to eq('TEST_VARIABLE_2') expect(json_response['value']).to eq('VALUE_2') end @@ -87,7 +87,7 @@ describe API::API, api: true do post api("/projects/#{project.id}/variables", user), key: variable.key, value: 'VALUE_2' end.to change{project.variables.count}.by(0) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end @@ -95,7 +95,7 @@ describe API::API, api: true do it 'should not create variable' do post api("/projects/#{project.id}/variables", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -103,7 +103,7 @@ describe API::API, api: true do it 'should not create variable' do post api("/projects/#{project.id}/variables") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -118,7 +118,7 @@ describe API::API, api: true do updated_variable = project.variables.first - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(value_before).to eq(variable.value) expect(updated_variable.value).to eq('VALUE_1_UP') end @@ -126,7 +126,7 @@ describe API::API, api: true do it 'should responde with 404 Not Found if requesting non-existing variable' do put api("/projects/#{project.id}/variables/non_existing_variable", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -134,7 +134,7 @@ describe API::API, api: true do it 'should not update variable' do put api("/projects/#{project.id}/variables/#{variable.key}", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -142,7 +142,7 @@ describe API::API, api: true do it 'should not update variable' do put api("/projects/#{project.id}/variables/#{variable.key}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -153,13 +153,13 @@ describe API::API, api: true do expect do delete api("/projects/#{project.id}/variables/#{variable.key}", user) end.to change{project.variables.count}.by(-1) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'should responde with 404 Not Found if requesting non-existing variable' do delete api("/projects/#{project.id}/variables/non_existing_variable", user) - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end @@ -167,7 +167,7 @@ describe API::API, api: true do it 'should not delete variable' do delete api("/projects/#{project.id}/variables/#{variable.key}", user2) - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end @@ -175,7 +175,7 @@ describe API::API, api: true do it 'should not delete variable' do delete api("/projects/#{project.id}/variables/#{variable.key}") - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index 7e50bea90d1..1bc51783c3a 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -26,7 +26,7 @@ describe Ci::API::API do post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['sha']).to eq(build.sha) expect(runner.reload.platform).to eq("darwin") end @@ -34,7 +34,7 @@ describe Ci::API::API do it "should return 404 error if no pending build found" do post ci_api("/builds/register"), token: runner.token - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should return 404 error if no builds for specific runner" do @@ -43,7 +43,7 @@ describe Ci::API::API do post ci_api("/builds/register"), token: runner.token - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "should return 404 error if no builds for shared runner" do @@ -52,7 +52,7 @@ describe Ci::API::API do post ci_api("/builds/register"), token: shared_runner.token - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it "returns options" do @@ -61,7 +61,7 @@ describe Ci::API::API do post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response["options"]).to eq({ "image" => "ruby:2.1", "services" => ["postgres"] }) end @@ -72,7 +72,7 @@ describe Ci::API::API do post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response["variables"]).to eq([ { "key" => "CI_BUILD_NAME", "value" => "spinach", "public" => true }, { "key" => "CI_BUILD_STAGE", "value" => "test", "public" => true }, @@ -91,7 +91,7 @@ describe Ci::API::API do post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response["variables"]).to eq([ { "key" => "CI_BUILD_NAME", "value" => "spinach", "public" => true }, { "key" => "CI_BUILD_STAGE", "value" => "test", "public" => true }, @@ -109,7 +109,7 @@ describe Ci::API::API do post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response["depends_on_builds"].count).to eq(2) expect(json_response["depends_on_builds"][0]["name"]).to eq("rspec") end @@ -122,7 +122,7 @@ describe Ci::API::API do it do post ci_api("/builds/register"), token: runner.token, info: { param => value } - expect(response.status).to eq(404) + expect(response).to have_http_status(404) runner.reload is_expected.to eq(value) end @@ -172,7 +172,7 @@ describe Ci::API::API do end it "should update a running build" do - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it 'should not override trace information when no trace is given' do @@ -252,13 +252,13 @@ describe Ci::API::API do context "should authorize posting artifact to running build" do it "using token as parameter" do post authorize_url, { token: build.token }, headers - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["TempPath"]).not_to be_nil end it "using token as header" do post authorize_url, {}, headers_with_token - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_response["TempPath"]).not_to be_nil end end @@ -267,13 +267,13 @@ describe Ci::API::API do it "using token as parameter" do stub_application_setting(max_artifacts_size: 0) post authorize_url, { token: build.token, filesize: 100 }, headers - expect(response.status).to eq(413) + expect(response).to have_http_status(413) end it "using token as header" do stub_application_setting(max_artifacts_size: 0) post authorize_url, { filesize: 100 }, headers_with_token - expect(response.status).to eq(413) + expect(response).to have_http_status(413) end end @@ -281,7 +281,7 @@ describe Ci::API::API do before { post authorize_url, { token: 'invalid', filesize: 100 } } it 'should respond with forbidden' do - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end end @@ -305,20 +305,20 @@ describe Ci::API::API do context "should post artifact to running build" do it "uses regual file post" do upload_artifacts(file_upload, headers_with_token, false) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response["artifacts_file"]["filename"]).to eq(file_upload.original_filename) end it "uses accelerated file post" do upload_artifacts(file_upload, headers_with_token, true) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response["artifacts_file"]["filename"]).to eq(file_upload.original_filename) end it "updates artifact" do upload_artifacts(file_upload, headers_with_token) upload_artifacts(file_upload2, headers_with_token) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response["artifacts_file"]["filename"]).to eq(file_upload2.original_filename) end end @@ -343,7 +343,7 @@ describe Ci::API::API do end it 'stores artifacts and artifacts metadata' do - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(stored_artifacts_file.original_filename).to eq(artifacts.original_filename) expect(stored_metadata_file.original_filename).to eq(metadata.original_filename) end @@ -355,7 +355,7 @@ describe Ci::API::API do end it 'is expected to respond with bad request' do - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it 'does not store metadata' do @@ -382,7 +382,7 @@ describe Ci::API::API do it 'updates when specified' do build.reload - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['artifacts_expire_at']).not_to be_empty expect(build.artifacts_expire_at).to be_within(5.minutes).of(Time.now + 7.days) end @@ -393,7 +393,7 @@ describe Ci::API::API do it 'ignores if not specified' do build.reload - expect(response.status).to eq(201) + expect(response).to have_http_status(201) expect(json_response['artifacts_expire_at']).to be_nil expect(build.artifacts_expire_at).to be_nil end @@ -404,21 +404,21 @@ describe Ci::API::API do it "should fail to post too large artifact" do stub_application_setting(max_artifacts_size: 0) upload_artifacts(file_upload, headers_with_token) - expect(response.status).to eq(413) + expect(response).to have_http_status(413) end end context "artifacts post request does not contain file" do it "should fail to post artifacts without file" do post post_url, {}, headers_with_token - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end context 'GitLab Workhorse is not configured' do it "should fail to post artifacts without GitLab-Workhorse" do post post_url, { token: build.token }, {} - expect(response.status).to eq(403) + expect(response).to have_http_status(403) end end end @@ -437,7 +437,7 @@ describe Ci::API::API do it "should fail to post artifacts for outside of tmp path" do upload_artifacts(file_upload, headers_with_token) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end end @@ -458,7 +458,7 @@ describe Ci::API::API do before { delete delete_url, token: build.token } it 'should remove build artifacts' do - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(build.artifacts_file.exists?).to be_falsy expect(build.artifacts_metadata.exists?).to be_falsy end @@ -475,14 +475,14 @@ describe Ci::API::API do end it 'should download artifact' do - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(response.headers).to include download_headers end end context 'build does not has artifacts' do it 'should respond with not found' do - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end diff --git a/spec/requests/ci/api/triggers_spec.rb b/spec/requests/ci/api/triggers_spec.rb index 72f6a3c981d..f12678e5a8e 100644 --- a/spec/requests/ci/api/triggers_spec.rb +++ b/spec/requests/ci/api/triggers_spec.rb @@ -21,17 +21,17 @@ describe Ci::API::API do context 'Handles errors' do it 'should return bad request if token is missing' do post ci_api("/projects/#{project.ci_id}/refs/master/trigger") - expect(response.status).to eq(400) + expect(response).to have_http_status(400) end it 'should return not found if project is not found' do post ci_api('/projects/0/refs/master/trigger'), options - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end it 'should return unauthorized if token is for different project' do post ci_api("/projects/#{project2.ci_id}/refs/master/trigger"), options - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end @@ -40,14 +40,14 @@ describe Ci::API::API do it 'should create builds' do post ci_api("/projects/#{project.ci_id}/refs/master/trigger"), options - expect(response.status).to eq(201) + expect(response).to have_http_status(201) pipeline.builds.reload expect(pipeline.builds.size).to eq(2) end it 'should return bad request with no builds created if there\'s no commit for that ref' do post ci_api("/projects/#{project.ci_id}/refs/other-branch/trigger"), options - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('No builds created') end @@ -58,19 +58,19 @@ describe Ci::API::API do it 'should validate variables to be a hash' do post ci_api("/projects/#{project.ci_id}/refs/master/trigger"), options.merge(variables: 'value') - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('variables needs to be a hash') end it 'should validate variables needs to be a map of key-valued strings' do post ci_api("/projects/#{project.ci_id}/refs/master/trigger"), options.merge(variables: { key: %w(1 2) }) - expect(response.status).to eq(400) + expect(response).to have_http_status(400) expect(json_response['message']).to eq('variables needs to be a map of key-valued strings') end it 'create trigger request with variables' do post ci_api("/projects/#{project.ci_id}/refs/master/trigger"), options.merge(variables: variables) - expect(response.status).to eq(201) + expect(response).to have_http_status(201) pipeline.builds.reload expect(pipeline.builds.first.trigger_request.variables).to eq(variables) end diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb index fd26ca97818..bae56334be4 100644 --- a/spec/requests/git_http_spec.rb +++ b/spec/requests/git_http_spec.rb @@ -14,7 +14,7 @@ describe 'Git HTTP requests', lib: true do context "when no authentication is provided" do it "responds with status 401 (no project existence information leak)" do download('doesnt/exist.git') do |response| - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -23,7 +23,7 @@ describe 'Git HTTP requests', lib: true do context "when authentication fails" do it "responds with status 401" do download('doesnt/exist.git', user: user.username, password: "nope") do |response| - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -31,7 +31,7 @@ describe 'Git HTTP requests', lib: true do context "when authentication succeeds" do it "responds with status 404" do download('/doesnt/exist.git', user: user.username, password: user.password) do |response| - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -46,7 +46,7 @@ describe 'Git HTTP requests', lib: true do download("/#{wiki.repository.path_with_namespace}.git") do |response| json_body = ActiveSupport::JSON.decode(response.body) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) expect(json_body['RepoPath']).to include(wiki.repository.path_with_namespace) end end @@ -62,13 +62,13 @@ describe 'Git HTTP requests', lib: true do it "downloads get status 200" do download(path, {}) do |response| - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end it "uploads get status 401" do upload(path, {}) do |response| - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end @@ -77,7 +77,7 @@ describe 'Git HTTP requests', lib: true do it "uploads get status 200 (because Git hooks do the real check)" do upload(path, env) do |response| - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -86,7 +86,7 @@ describe 'Git HTTP requests', lib: true do allow(Gitlab.config.gitlab_shell).to receive(:receive_pack).and_return(false) upload(path, env) do |response| - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -97,7 +97,7 @@ describe 'Git HTTP requests', lib: true do allow(Gitlab.config.gitlab_shell).to receive(:upload_pack).and_return(false) download(path, {}) do |response| - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -111,13 +111,13 @@ describe 'Git HTTP requests', lib: true do context "when no authentication is provided" do it "responds with status 401 to downloads" do download(path, {}) do |response| - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end it "responds with status 401 to uploads" do upload(path, {}) do |response| - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -128,7 +128,7 @@ describe 'Git HTTP requests', lib: true do context "when authentication fails" do it "responds with status 401" do download(path, env) do |response| - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end @@ -139,7 +139,7 @@ describe 'Git HTTP requests', lib: true do clone_get(path, env) - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -158,7 +158,7 @@ describe 'Git HTTP requests', lib: true do project.team << [user, :master] download(path, env) do |response| - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end @@ -169,12 +169,12 @@ describe 'Git HTTP requests', lib: true do clone_get(path, env) - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "uploads get status 200" do upload(path, env) do |response| - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -188,13 +188,13 @@ describe 'Git HTTP requests', lib: true do it "downloads get status 200" do clone_get "#{project.path_with_namespace}.git", user: 'oauth2', password: @token.token - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "uploads get status 401 (no project existence information leak)" do push_get "#{project.path_with_namespace}.git", user: 'oauth2', password: @token.token - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end @@ -232,13 +232,13 @@ describe 'Git HTTP requests', lib: true do context "when the user doesn't have access to the project" do it "downloads get status 404" do download(path, user: user.username, password: user.password) do |response| - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end it "uploads get status 200 (because Git hooks do the real check)" do upload(path, user: user.username, password: user.password) do |response| - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end end @@ -256,13 +256,13 @@ describe 'Git HTTP requests', lib: true do it "downloads get status 200" do clone_get "#{project.path_with_namespace}.git", user: 'gitlab-ci-token', password: token - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end it "uploads get status 401 (no project existence information leak)" do push_get "#{project.path_with_namespace}.git", user: 'gitlab-ci-token', password: token - expect(response.status).to eq(401) + expect(response).to have_http_status(401) end end end @@ -336,7 +336,7 @@ describe 'Git HTTP requests', lib: true do end it "returns the file" do - expect(response.status).to eq(200) + expect(response).to have_http_status(200) end end @@ -344,7 +344,7 @@ describe 'Git HTTP requests', lib: true do before { get "/#{project.path_with_namespace}/blob/master/info/refs" } it "returns not found" do - expect(response.status).to eq(404) + expect(response).to have_http_status(404) end end end diff --git a/spec/requests/jwt_controller_spec.rb b/spec/requests/jwt_controller_spec.rb index d2d4a9eca18..c6172b9cc7d 100644 --- a/spec/requests/jwt_controller_spec.rb +++ b/spec/requests/jwt_controller_spec.rb @@ -11,12 +11,12 @@ describe JwtController do context 'existing service' do subject! { get '/jwt/auth', parameters } - it { expect(response.status).to eq(200) } + it { expect(response).to have_http_status(200) } context 'returning custom http code' do let(:service) { double(execute: { http_status: 505 }) } - it { expect(response.status).to eq(505) } + it { expect(response).to have_http_status(505) } end end @@ -36,7 +36,7 @@ describe JwtController do context 'project with disabled CI' do let(:builds_enabled) { false } - it { expect(response.status).to eq(403) } + it { expect(response).to have_http_status(403) } end end @@ -56,14 +56,14 @@ describe JwtController do subject! { get '/jwt/auth', parameters, headers } - it { expect(response.status).to eq(403) } + it { expect(response).to have_http_status(403) } end end context 'unknown service' do subject! { get '/jwt/auth', service: 'unknown' } - it { expect(response.status).to eq(404) } + it { expect(response).to have_http_status(404) } end def credentials(login, password) diff --git a/spec/services/create_commit_builds_service_spec.rb b/spec/services/create_commit_builds_service_spec.rb index deab242f45a..309213bd44c 100644 --- a/spec/services/create_commit_builds_service_spec.rb +++ b/spec/services/create_commit_builds_service_spec.rb @@ -83,6 +83,9 @@ describe CreateCommitBuildsService, services: true do context 'when commit contains a [ci skip] directive' do let(:message) { "some message[ci skip]" } + let(:messageFlip) { "some message[skip ci]" } + let(:capMessage) { "some message[CI SKIP]" } + let(:capMessageFlip) { "some message[SKIP CI]" } before do allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { message } @@ -96,12 +99,55 @@ describe CreateCommitBuildsService, services: true do after: '31das312', commits: commits ) + + expect(pipeline).to be_persisted + expect(pipeline.builds.any?).to be false + expect(pipeline.status).to eq("skipped") + end + + it "skips builds creation if there is [skip ci] tag in commit message" do + commits = [{ message: messageFlip }] + pipeline = service.execute(project, user, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: commits + ) + + expect(pipeline).to be_persisted + expect(pipeline.builds.any?).to be false + expect(pipeline.status).to eq("skipped") + end + + it "skips builds creation if there is [CI SKIP] tag in commit message" do + commits = [{ message: capMessage }] + pipeline = service.execute(project, user, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: commits + ) + + expect(pipeline).to be_persisted + expect(pipeline.builds.any?).to be false + expect(pipeline.status).to eq("skipped") + end + + it "skips builds creation if there is [SKIP CI] tag in commit message" do + commits = [{ message: capMessageFlip }] + pipeline = service.execute(project, user, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: commits + ) + expect(pipeline).to be_persisted expect(pipeline.builds.any?).to be false expect(pipeline.status).to eq("skipped") end - it "does not skips builds creation if there is no [ci skip] tag in commit message" do + it "does not skips builds creation if there is no [ci skip] or [skip ci] tag in commit message" do allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { "some message" } commits = [{ message: "some message" }] diff --git a/spec/services/search/snippet_service_spec.rb b/spec/services/search/snippet_service_spec.rb new file mode 100644 index 00000000000..14f3301d9f4 --- /dev/null +++ b/spec/services/search/snippet_service_spec.rb @@ -0,0 +1,59 @@ +require 'spec_helper' + +describe Search::SnippetService, services: true do + let(:author) { create(:author) } + let(:project) { create(:empty_project) } + + let!(:public_snippet) { create(:snippet, :public, content: 'password: XXX') } + let!(:internal_snippet) { create(:snippet, :internal, content: 'password: XXX') } + let!(:private_snippet) { create(:snippet, :private, content: 'password: XXX', author: author) } + + let!(:project_public_snippet) { create(:snippet, :public, project: project, content: 'password: XXX') } + let!(:project_internal_snippet) { create(:snippet, :internal, project: project, content: 'password: XXX') } + let!(:project_private_snippet) { create(:snippet, :private, project: project, content: 'password: XXX') } + + describe '#execute' do + context 'unauthenticated' do + it 'returns public snippets only' do + search = described_class.new(nil, search: 'password') + results = search.execute + + expect(results.objects('snippet_blobs')).to match_array [public_snippet, project_public_snippet] + end + end + + context 'authenticated' do + it 'returns only public & internal snippets for regular users' do + user = create(:user) + search = described_class.new(user, search: 'password') + results = search.execute + + expect(results.objects('snippet_blobs')).to match_array [public_snippet, internal_snippet, project_public_snippet, project_internal_snippet] + end + + it 'returns public, internal snippets and project private snippets for project members' do + member = create(:user) + project.team << [member, :developer] + search = described_class.new(member, search: 'password') + results = search.execute + + expect(results.objects('snippet_blobs')).to match_array [public_snippet, internal_snippet, project_public_snippet, project_internal_snippet, project_private_snippet] + end + + it 'returns public, internal and private snippets where user is the author' do + search = described_class.new(author, search: 'password') + results = search.execute + + expect(results.objects('snippet_blobs')).to match_array [public_snippet, internal_snippet, private_snippet, project_public_snippet, project_internal_snippet] + end + + it 'returns all snippets when user is admin' do + admin = create(:admin) + search = described_class.new(admin, search: 'password') + results = search.execute + + expect(results.objects('snippet_blobs')).to match_array [public_snippet, internal_snippet, private_snippet, project_public_snippet, project_internal_snippet, project_private_snippet] + end + end + end +end diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb index 498bd4bf800..426bf53f198 100644 --- a/spec/support/test_env.rb +++ b/spec/support/test_env.rb @@ -17,6 +17,7 @@ module TestEnv "'test'" => 'e56497b', 'orphaned-branch' => '45127a9', 'binary-encoding' => '7b1cf43', + 'gitattributes' => '5a62481', } # gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily |