Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-09-10 18:11:12 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-09-10 18:11:12 +0300
commit6a6824a5ce46273248944d1859591d3a811aa18e (patch)
tree77cc4f04ff7d59fc518fba04e3a226dc0949ed37
parent8fd149139d3d64b102f51455180f03adbc80f469 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/additional_metadata.vue19
-rw-r--r--app/models/hooks/web_hook.rb2
-rw-r--r--app/models/work_item/type.rb8
-rw-r--r--app/services/issues/base_service.rb7
-rw-r--r--app/services/issues/build_service.rb13
-rw-r--r--app/services/issues/update_service.rb10
-rw-r--r--doc/administration/geo/setup/database.md73
-rw-r--r--doc/development/i18n/externalization.md22
-rw-r--r--doc/integration/elasticsearch.md23
-rw-r--r--doc/integration/jira/jira_cloud_configuration.md6
-rw-r--r--lib/gitlab/issuables_count_for_state.rb2
-rw-r--r--lib/gitlab/saas.rb4
-rw-r--r--locale/gitlab.pot3
-rw-r--r--spec/controllers/boards/issues_controller_spec.rb10
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb10
-rw-r--r--spec/factories/issues.rb2
-rw-r--r--spec/factories/work_item/work_item_types.rb11
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/details/additional_metadata_spec.js21
-rw-r--r--spec/frontend/packages_and_registries/package_registry/mock_data.js4
-rw-r--r--spec/lib/gitlab/issuables_count_for_state_spec.rb2
-rw-r--r--spec/migrations/20210804150320_create_base_work_item_types_spec.rb10
-rw-r--r--spec/migrations/20210831203408_upsert_base_work_item_types_spec.rb14
-rw-r--r--spec/models/hooks/web_hook_spec.rb30
-rw-r--r--spec/models/work_item/type_spec.rb6
-rw-r--r--spec/rake_helper.rb2
-rw-r--r--spec/requests/api/graphql/mutations/issues/create_spec.rb5
-rw-r--r--spec/requests/api/graphql/mutations/issues/update_spec.rb13
-rw-r--r--spec/services/issues/build_service_spec.rb52
-rw-r--r--spec/services/issues/create_service_spec.rb9
-rw-r--r--spec/services/issues/update_service_spec.rb22
-rw-r--r--spec/spec_helper.rb2
-rw-r--r--spec/support/database_cleaner.rb4
-rw-r--r--spec/support/db_cleaner.rb2
-rw-r--r--spec/support/helpers/test_env.rb4
-rw-r--r--spec/support/shared_examples/services/incident_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/work_item_base_types_importer.rb3
36 files changed, 364 insertions, 68 deletions
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/additional_metadata.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/additional_metadata.vue
index 059f038b6e7..3595f83baf0 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/components/details/additional_metadata.vue
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/additional_metadata.vue
@@ -6,6 +6,7 @@ import {
PACKAGE_TYPE_CONAN,
PACKAGE_TYPE_MAVEN,
PACKAGE_TYPE_COMPOSER,
+ PACKAGE_TYPE_PYPI,
} from '~/packages_and_registries/package_registry/constants';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import DetailsRow from '~/vue_shared/components/registry/details_row.vue';
@@ -22,6 +23,7 @@ export default {
composerJson: s__(
'PackageRegistry|Composer.json with license: %{license} and version: %{version}',
),
+ requiredPython: s__('PackageRegistry|Required Python: %{pythonVersion}'),
},
components: {
DetailsRow,
@@ -43,6 +45,7 @@ export default {
PACKAGE_TYPE_CONAN,
PACKAGE_TYPE_MAVEN,
PACKAGE_TYPE_COMPOSER,
+ PACKAGE_TYPE_PYPI,
].includes(this.packageEntity.packageType) && this.packageEntity.metadata
);
},
@@ -58,6 +61,9 @@ export default {
showComposerMetadata() {
return this.packageEntity.packageType === PACKAGE_TYPE_COMPOSER;
},
+ showPypiMetadata() {
+ return this.packageEntity.packageType === PACKAGE_TYPE_PYPI;
+ },
},
};
</script>
@@ -141,6 +147,19 @@ export default {
</gl-sprintf>
</details-row>
</template>
+
+ <details-row
+ v-else-if="showPypiMetadata"
+ icon="information-o"
+ padding="gl-p-4"
+ data-testid="pypi-required-python"
+ >
+ <gl-sprintf :message="$options.i18n.requiredPython">
+ <template #pythonVersion>
+ <strong>{{ packageEntity.metadata.requiredPython }}</strong>
+ </template>
+ </gl-sprintf>
+ </details-row>
</div>
</div>
</template>
diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb
index 9a78fe3971c..cb5c1ac48cd 100644
--- a/app/models/hooks/web_hook.rb
+++ b/app/models/hooks/web_hook.rb
@@ -80,6 +80,8 @@ class WebHook < ApplicationRecord
end
def backoff!
+ return if backoff_count >= MAX_FAILURES && disabled_until && disabled_until > Time.current
+
assign_attributes(disabled_until: next_backoff.from_now, backoff_count: backoff_count.succ.clamp(0, MAX_FAILURES))
save(validate: false)
end
diff --git a/app/models/work_item/type.rb b/app/models/work_item/type.rb
index 03badf95908..7038beadd62 100644
--- a/app/models/work_item/type.rb
+++ b/app/models/work_item/type.rb
@@ -34,6 +34,14 @@ class WorkItem::Type < ApplicationRecord
validates :name, length: { maximum: 255 }
validates :icon_name, length: { maximum: 255 }
+ def self.default_by_type(type)
+ find_by(namespace_id: nil, base_type: type)
+ end
+
+ def self.default_issue_type
+ default_by_type(:issue)
+ end
+
private
def strip_whitespace
diff --git a/app/services/issues/base_service.rb b/app/services/issues/base_service.rb
index 525447e1377..6dce9fd6e73 100644
--- a/app/services/issues/base_service.rb
+++ b/app/services/issues/base_service.rb
@@ -34,6 +34,13 @@ module Issues
private
+ def find_work_item_type_id(issue_type)
+ work_item_type = WorkItem::Type.default_by_type(issue_type)
+ work_item_type ||= WorkItem::Type.default_issue_type
+
+ work_item_type.id
+ end
+
def filter_params(issue)
super
diff --git a/app/services/issues/build_service.rb b/app/services/issues/build_service.rb
index 5cb138946d7..7fdc8daf15c 100644
--- a/app/services/issues/build_service.rb
+++ b/app/services/issues/build_service.rb
@@ -6,6 +6,7 @@ module Issues
def execute
filter_resolve_discussion_params
+
@issue = project.issues.new(issue_params).tap do |issue|
ensure_milestone_available(issue)
end
@@ -60,6 +61,13 @@ module Issues
def issue_params
@issue_params ||= build_issue_params
+
+ # If :issue_type is nil then params[:issue_type] was either nil
+ # or not permitted. Either way, the :issue_type will default
+ # to the column default of `issue`. And that means we need to
+ # ensure the work_item_type_id is set
+ @issue_params[:work_item_type_id] = get_work_item_type_id(@issue_params[:issue_type])
+ @issue_params
end
private
@@ -81,6 +89,11 @@ module Issues
{ author: current_user }
.merge(issue_params_with_info_from_discussions)
.merge(allowed_issue_params)
+ .with_indifferent_access
+ end
+
+ def get_work_item_type_id(issue_type = :issue)
+ find_work_item_type_id(issue_type)
end
end
end
diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb
index 6ee94e014d8..d120b007af2 100644
--- a/app/services/issues/update_service.rb
+++ b/app/services/issues/update_service.rb
@@ -26,6 +26,8 @@ module Issues
end
def before_update(issue, skip_spam_check: false)
+ change_work_item_type(issue)
+
return if skip_spam_check
Spam::SpamActionService.new(
@@ -36,6 +38,14 @@ module Issues
).execute
end
+ def change_work_item_type(issue)
+ return unless issue.changed_attributes['issue_type']
+
+ type_id = find_work_item_type_id(issue.issue_type)
+
+ issue.work_item_type_id = type_id
+ end
+
def handle_changes(issue, options)
super
old_associations = options.fetch(:old_associations, {})
diff --git a/doc/administration/geo/setup/database.md b/doc/administration/geo/setup/database.md
index 05aa026eca1..d72bb0737ae 100644
--- a/doc/administration/geo/setup/database.md
+++ b/doc/administration/geo/setup/database.md
@@ -501,6 +501,79 @@ two other clusters of nodes supporting a Geo **secondary** site. One for the
main database and the other for the tracking database. For more information,
see [High Availability with Omnibus GitLab](../../postgresql/replication_and_failover.md).
+### Changing the replication password
+
+To change the password for the [replication user](https://wiki.postgresql.org/wiki/Streaming_Replication)
+when using Omnibus-managed PostgreSQL instances:
+
+On the GitLab Geo **primary** server:
+
+1. The default value for the replication user is `gitlab_replicator`, but if you've set a custom replication
+ user in your `/etc/gitlab/gitlab.rb` under the `postgresql['sql_replication_user']` setting, make sure to
+ adapt the following instructions for your own user.
+
+ Generate an MD5 hash of the desired password:
+
+ ```shell
+ sudo gitlab-ctl pg-password-md5 gitlab_replicator
+ # Enter password: <your_password_here>
+ # Confirm password: <your_password_here>
+ # 950233c0dfc2f39c64cf30457c3b7f1e
+ ```
+
+ Edit `/etc/gitlab/gitlab.rb`:
+
+ ```ruby
+ # Fill with the hash generated by `gitlab-ctl pg-password-md5 gitlab_replicator`
+ postgresql['sql_replication_password'] = '<md5_hash_of_your_password>'
+ ```
+
+1. Save the file and reconfigure GitLab to change the replication user's password in PostgreSQL:
+
+ ```shell
+ sudo gitlab-ctl reconfigure
+ ```
+
+1. Restart PostgreSQL for the replication password change to take effect:
+
+ ```shell
+ sudo gitlab-ctl restart postgresql
+ ```
+
+Until the password is updated on any **secondary** servers, the [PostgreSQL log](../../logs.md#postgresql-logs) on
+the secondaries will report the following error message:
+
+```console
+FATAL: could not connect to the primary server: FATAL: password authentication failed for user "gitlab_replicator"
+```
+
+On all GitLab Geo **secondary** servers:
+
+1. The first step isn't necessary from a configuration perspective, since the hashed `'sql_replication_password'`
+ is not used on the GitLab Geo **secondary**. However in the event that **secondary** needs to be promoted
+ to the GitLab Geo **primary**, make sure to match the `'sql_replication_password'` in the secondary
+ server configuration.
+
+ Edit `/etc/gitlab/gitlab.rb`:
+
+ ```ruby
+ # Fill with the hash generated by `gitlab-ctl pg-password-md5 gitlab_replicator` on the Geo primary
+ postgresql['sql_replication_password'] = '<md5_hash_of_your_password>'
+ ```
+
+1. During the initial replication setup, the `gitlab-ctl replicate-geo-database` command writes the plaintext
+ password for the replication user account to two locations:
+
+ - `gitlab-geo.conf`: Used by the PostgreSQL replication process, written to the PostgreSQL data
+ directory, by default at `/var/opt/gitlab/postgresql/data/gitlab-geo.conf`.
+ - `.pgpass`: Used by the `gitlab-psql` user, located by default at `/var/opt/gitlab/postgresql/.pgpass`.
+
+ Update the plaintext password in both of these files, and restart PostgreSQL:
+
+ ```shell
+ sudo gitlab-ctl restart postgresql
+ ```
+
## Multi-node database replication
In GitLab 14.0, Patroni replaced `repmgr` as the supported
diff --git a/doc/development/i18n/externalization.md b/doc/development/i18n/externalization.md
index c383a1cac73..52a7f839286 100644
--- a/doc/development/i18n/externalization.md
+++ b/doc/development/i18n/externalization.md
@@ -790,6 +790,28 @@ translate correctly if you extract individual words from the sentence.
When in doubt, try to follow the best practices described in this [Mozilla Developer documentation](https://developer.mozilla.org/en-US/docs/Mozilla/Localization/Localization_content_best_practices#Splitting).
+### Always pass string literals to the translation helpers
+
+The `bin/rake gettext:regenerate` script parses the codebase and extracts all the strings from the
+[translation helpers](#preparing-a-page-for-translation) ready to be translated.
+
+The script cannot resolve the strings if they are passed as variables or function calls. Therefore,
+make sure to always pass string literals to the helpers.
+
+```javascript
+// Good
+__('Some label');
+s__('Namespace', 'Label');
+s__('Namespace|Label');
+n__('%d apple', '%d apples', appleCount);
+
+// Bad
+__(LABEL);
+s__(getLabel());
+s__(NAMESPACE, LABEL);
+n__(LABEL_SINGULAR, LABEL_PLURAL, appleCount);
+```
+
## Updating the PO files with the new content
Now that the new content is marked for translation, run this command to update the
diff --git a/doc/integration/elasticsearch.md b/doc/integration/elasticsearch.md
index 9842881094b..5a08a1fe7f3 100644
--- a/doc/integration/elasticsearch.md
+++ b/doc/integration/elasticsearch.md
@@ -902,25 +902,20 @@ Elasticsearch::Transport::Transport::Errors::BadRequest([400] {
This is because we changed the index mapping in GitLab 8.12 and the old indexes should be removed and built from scratch again,
see details in the [update guide](../update/upgrading_from_source.md).
-- Exception `Elasticsearch::Transport::Transport::Errors::BadRequest`
+### `Elasticsearch::Transport::Transport::Errors::BadRequest`
- If you have this exception (just like in the case above but the actual message is different) please check if you have the correct Elasticsearch version and you met the other [requirements](#system-requirements).
- There is also an easy way to check it automatically with `sudo gitlab-rake gitlab:check` command.
+If you have this exception (just like in the case above but the actual message is different) please check if you have the correct Elasticsearch version and you met the other [requirements](#system-requirements).
+There is also an easy way to check it automatically with `sudo gitlab-rake gitlab:check` command.
-- Exception `Elasticsearch::Transport::Transport::Errors::RequestEntityTooLarge`
+### `Elasticsearch::Transport::Transport::Errors::RequestEntityTooLarge`
- ```plaintext
- [413] {"Message":"Request size exceeded 10485760 bytes"}
- ```
+```plaintext
+[413] {"Message":"Request size exceeded 10485760 bytes"}
+```
- This exception is seen when your Elasticsearch cluster is configured to reject
- requests above a certain size (10MiB in this case). This corresponds to the
- `http.max_content_length` setting in `elasticsearch.yml`. Increase it to a
- larger size and restart your Elasticsearch cluster.
+This exception is seen when your Elasticsearch cluster is configured to reject requests above a certain size (10MiB in this case). This corresponds to the `http.max_content_length` setting in `elasticsearch.yml`. Increase it to a larger size and restart your Elasticsearch cluster.
- AWS has [fixed limits](https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/aes-limits.html)
- for this setting ("Maximum Size of HTTP Request Payloads"), based on the size of
- the underlying instance.
+AWS has [fixed limits](https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/aes-limits.html) for this setting ("Maximum Size of HTTP Request Payloads"), based on the size of the underlying instance.
### My single node Elasticsearch cluster status never goes from `yellow` to `green` even though everything seems to be running properly
diff --git a/doc/integration/jira/jira_cloud_configuration.md b/doc/integration/jira/jira_cloud_configuration.md
index e42a102e030..0cfffdb8ba4 100644
--- a/doc/integration/jira/jira_cloud_configuration.md
+++ b/doc/integration/jira/jira_cloud_configuration.md
@@ -11,10 +11,10 @@ on Atlassian cloud. To create the API token:
1. Sign in to [`id.atlassian.com`](https://id.atlassian.com/manage-profile/security/api-tokens)
with your email address. Use an account with *write* access to Jira projects.
-1. Go to **Settings > API tokens**.
+1. Go to **Settings > Atlassian account settings > Security > Create and manage API tokens**.
1. Select **Create API token** to display a modal window with an API token.
-1. To copy the API token, select **Copy to clipboard**, or select **View** and write
- down the new API token. You need this value when you
+1. In the dialog, enter a label for your token and select **Create**.
+1. To copy the API token, select **Copy**, then paste the token somewhere safe. You need this value when you
[configure GitLab](configure.md).
You need the newly created token, and the email
diff --git a/lib/gitlab/issuables_count_for_state.rb b/lib/gitlab/issuables_count_for_state.rb
index 660877b964a..f11dd520d2d 100644
--- a/lib/gitlab/issuables_count_for_state.rb
+++ b/lib/gitlab/issuables_count_for_state.rb
@@ -6,7 +6,7 @@ module Gitlab
# The name of the Gitlab::SafeRequestStore cache key.
CACHE_KEY = :issuables_count_for_state
# The expiration time for the Rails cache.
- CACHE_EXPIRES_IN = 10.minutes
+ CACHE_EXPIRES_IN = 1.hour
THRESHOLD = 1000
# The state values that can be safely casted to a Symbol.
diff --git a/lib/gitlab/saas.rb b/lib/gitlab/saas.rb
index 8d9d8415cb1..4066c8a7906 100644
--- a/lib/gitlab/saas.rb
+++ b/lib/gitlab/saas.rb
@@ -20,6 +20,10 @@ module Gitlab
def self.dev_url
'https://dev.gitlab.org'
end
+
+ def self.registry_prefix
+ 'registry.gitlab.com'
+ end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 21e8d2fc0b9..5dbc880fff5 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -24043,6 +24043,9 @@ msgstr ""
msgid "PackageRegistry|Remove package"
msgstr ""
+msgid "PackageRegistry|Required Python: %{pythonVersion}"
+msgstr ""
+
msgid "PackageRegistry|RubyGems"
msgstr ""
diff --git a/spec/controllers/boards/issues_controller_spec.rb b/spec/controllers/boards/issues_controller_spec.rb
index 48000284264..cc60ab16d2e 100644
--- a/spec/controllers/boards/issues_controller_spec.rb
+++ b/spec/controllers/boards/issues_controller_spec.rb
@@ -428,17 +428,21 @@ RSpec.describe Boards::IssuesController do
describe 'POST create' do
context 'with valid params' do
- it 'returns a successful 200 response' do
+ before do
create_issue user: user, board: board, list: list1, title: 'New issue'
+ end
+ it 'returns a successful 200 response' do
expect(response).to have_gitlab_http_status(:ok)
end
it 'returns the created issue' do
- create_issue user: user, board: board, list: list1, title: 'New issue'
-
expect(response).to match_response_schema('entities/issue_board')
end
+
+ it 'sets the default work_item_type' do
+ expect(Issue.last.work_item_type.base_type).to eq('issue')
+ end
end
context 'with invalid params' do
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index 0c29280316a..b3b9941062d 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -1176,12 +1176,22 @@ RSpec.describe Projects::IssuesController do
project.issues.first
end
+ context 'when creating an incident' do
+ it 'sets the correct issue_type' do
+ issue = post_new_issue(issue_type: 'incident')
+
+ expect(issue.issue_type).to eq('incident')
+ expect(issue.work_item_type.base_type).to eq('incident')
+ end
+ end
+
it 'creates the issue successfully', :aggregate_failures do
issue = post_new_issue
expect(issue).to be_a(Issue)
expect(issue.persisted?).to eq(true)
expect(issue.issue_type).to eq('issue')
+ expect(issue.work_item_type.base_type).to eq('issue')
end
context 'resolving discussions in MergeRequest' do
diff --git a/spec/factories/issues.rb b/spec/factories/issues.rb
index 2d52747dece..8b53732a3c1 100644
--- a/spec/factories/issues.rb
+++ b/spec/factories/issues.rb
@@ -8,6 +8,7 @@ FactoryBot.define do
updated_by { author }
relative_position { RelativePositioning::START_POSITION }
issue_type { :issue }
+ association :work_item_type, :default
trait :confidential do
confidential { true }
@@ -59,6 +60,7 @@ FactoryBot.define do
factory :incident do
issue_type { :incident }
+ association :work_item_type, :default, :incident
end
end
end
diff --git a/spec/factories/work_item/work_item_types.rb b/spec/factories/work_item/work_item_types.rb
index 07d6d685c57..1c586aab59b 100644
--- a/spec/factories/work_item/work_item_types.rb
+++ b/spec/factories/work_item/work_item_types.rb
@@ -8,6 +8,17 @@ FactoryBot.define do
base_type { WorkItem::Type.base_types[:issue] }
icon_name { 'issue-type-issue' }
+ initialize_with do
+ type_base_attributes = attributes.with_indifferent_access.slice(:base_type, :namespace, :namespace_id)
+
+ # Expect base_types to exist on the DB
+ if type_base_attributes.slice(:namespace, :namespace_id).compact.empty?
+ WorkItem::Type.find_or_initialize_by(type_base_attributes).tap { |type| type.assign_attributes(attributes) }
+ else
+ WorkItem::Type.new(attributes)
+ end
+ end
+
trait :default do
namespace { nil }
end
diff --git a/spec/frontend/packages_and_registries/package_registry/components/details/additional_metadata_spec.js b/spec/frontend/packages_and_registries/package_registry/components/details/additional_metadata_spec.js
index e5147b0b6b1..dda0b5bef8d 100644
--- a/spec/frontend/packages_and_registries/package_registry/components/details/additional_metadata_spec.js
+++ b/spec/frontend/packages_and_registries/package_registry/components/details/additional_metadata_spec.js
@@ -6,6 +6,7 @@ import {
nugetMetadata,
packageData,
composerMetadata,
+ pypiMetadata,
} from 'jest/packages_and_registries/package_registry/mock_data';
import component from '~/packages_and_registries/package_registry/components/details/additional_metadata.vue';
import {
@@ -14,6 +15,7 @@ import {
PACKAGE_TYPE_MAVEN,
PACKAGE_TYPE_NPM,
PACKAGE_TYPE_COMPOSER,
+ PACKAGE_TYPE_PYPI,
} from '~/packages_and_registries/package_registry/constants';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import DetailsRow from '~/vue_shared/components/registry/details_row.vue';
@@ -22,6 +24,7 @@ const mavenPackage = { packageType: PACKAGE_TYPE_MAVEN, metadata: mavenMetadata(
const conanPackage = { packageType: PACKAGE_TYPE_CONAN, metadata: conanMetadata() };
const nugetPackage = { packageType: PACKAGE_TYPE_NUGET, metadata: nugetMetadata() };
const composerPackage = { packageType: PACKAGE_TYPE_COMPOSER, metadata: composerMetadata() };
+const pypiPackage = { packageType: PACKAGE_TYPE_PYPI, metadata: pypiMetadata() };
const npmPackage = { packageType: PACKAGE_TYPE_NPM, metadata: {} };
describe('Package Additional Metadata', () => {
@@ -58,6 +61,7 @@ describe('Package Additional Metadata', () => {
const findComposerTargetSha = () => wrapper.findByTestId('composer-target-sha');
const findComposerTargetShaCopyButton = () => wrapper.findComponent(ClipboardButton);
const findComposerJson = () => wrapper.findByTestId('composer-json');
+ const findPypiRequiredPython = () => wrapper.findByTestId('pypi-required-python');
it('has the correct title', () => {
mountComponent();
@@ -74,6 +78,7 @@ describe('Package Additional Metadata', () => {
${conanPackage} | ${true} | ${PACKAGE_TYPE_CONAN}
${nugetPackage} | ${true} | ${PACKAGE_TYPE_NUGET}
${composerPackage} | ${true} | ${PACKAGE_TYPE_COMPOSER}
+ ${pypiPackage} | ${true} | ${PACKAGE_TYPE_PYPI}
${npmPackage} | ${false} | ${PACKAGE_TYPE_NPM}
`(
`It is $visible that the component is visible when the package is $packageType`,
@@ -160,4 +165,20 @@ describe('Package Additional Metadata', () => {
});
});
});
+
+ describe('pypi metadata', () => {
+ beforeEach(() => {
+ mountComponent({ packageEntity: pypiPackage });
+ });
+
+ it.each`
+ name | finderFunction | text | icon
+ ${'pypi-required-python'} | ${findPypiRequiredPython} | ${'Required Python: 1.0.0'} | ${'information-o'}
+ `('$name element', ({ finderFunction, text, icon }) => {
+ const element = finderFunction();
+ expect(element.exists()).toBe(true);
+ expect(element.text()).toBe(text);
+ expect(element.props('icon')).toBe(icon);
+ });
+ });
});
diff --git a/spec/frontend/packages_and_registries/package_registry/mock_data.js b/spec/frontend/packages_and_registries/package_registry/mock_data.js
index 98ff29ef728..9438a2d2d72 100644
--- a/spec/frontend/packages_and_registries/package_registry/mock_data.js
+++ b/spec/frontend/packages_and_registries/package_registry/mock_data.js
@@ -133,7 +133,7 @@ export const composerMetadata = () => ({
},
});
-export const pypyMetadata = () => ({
+export const pypiMetadata = () => ({
requiredPython: '1.0.0',
});
@@ -157,7 +157,7 @@ export const packageDetailsQuery = (extendPackage) => ({
metadata: {
...conanMetadata(),
...composerMetadata(),
- ...pypyMetadata(),
+ ...pypiMetadata(),
...mavenMetadata(),
...nugetMetadata(),
},
diff --git a/spec/lib/gitlab/issuables_count_for_state_spec.rb b/spec/lib/gitlab/issuables_count_for_state_spec.rb
index e377d1e1dde..cc4ebba863d 100644
--- a/spec/lib/gitlab/issuables_count_for_state_spec.rb
+++ b/spec/lib/gitlab/issuables_count_for_state_spec.rb
@@ -71,7 +71,7 @@ RSpec.describe Gitlab::IssuablesCountForState do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
- let(:cache_options) { { expires_in: 10.minutes } }
+ let(:cache_options) { { expires_in: 1.hour } }
let(:cache_key) { ['group', group.id, 'issues'] }
let(:threshold) { described_class::THRESHOLD }
let(:states_count) { { opened: 1, closed: 1, all: 2 } }
diff --git a/spec/migrations/20210804150320_create_base_work_item_types_spec.rb b/spec/migrations/20210804150320_create_base_work_item_types_spec.rb
index 535472f5931..9ba29637e00 100644
--- a/spec/migrations/20210804150320_create_base_work_item_types_spec.rb
+++ b/spec/migrations/20210804150320_create_base_work_item_types_spec.rb
@@ -6,7 +6,17 @@ require_migration!('create_base_work_item_types')
RSpec.describe CreateBaseWorkItemTypes, :migration do
let!(:work_item_types) { table(:work_item_types) }
+ after(:all) do
+ # Make sure base types are recreated after running the migration
+ # because migration specs are not run in a transaction
+ WorkItem::Type.delete_all
+ Gitlab::DatabaseImporters::WorkItems::BaseTypeImporter.import
+ end
+
it 'creates default data' do
+ # Need to delete all as base types are seeded before entire test suite
+ WorkItem::Type.delete_all
+
reversible_migration do |migration|
migration.before -> {
# Depending on whether the migration has been run before,
diff --git a/spec/migrations/20210831203408_upsert_base_work_item_types_spec.rb b/spec/migrations/20210831203408_upsert_base_work_item_types_spec.rb
index b9cc9de88cc..c23110750c3 100644
--- a/spec/migrations/20210831203408_upsert_base_work_item_types_spec.rb
+++ b/spec/migrations/20210831203408_upsert_base_work_item_types_spec.rb
@@ -6,8 +6,18 @@ require_migration!('upsert_base_work_item_types')
RSpec.describe UpsertBaseWorkItemTypes, :migration do
let!(:work_item_types) { table(:work_item_types) }
+ after(:all) do
+ # Make sure base types are recreated after running the migration
+ # because migration specs are not run in a transaction
+ WorkItem::Type.delete_all
+ Gitlab::DatabaseImporters::WorkItems::BaseTypeImporter.import
+ end
+
context 'when no default types exist' do
it 'creates default data' do
+ # Need to delete all as base types are seeded before entire test suite
+ WorkItem::Type.delete_all
+
expect(work_item_types.count).to eq(0)
reversible_migration do |migration|
@@ -26,10 +36,6 @@ RSpec.describe UpsertBaseWorkItemTypes, :migration do
end
context 'when default types already exist' do
- before do
- Gitlab::DatabaseImporters::WorkItems::BaseTypeImporter.import
- end
-
it 'does not create default types again' do
expect(work_item_types.all.pluck(:base_type)).to match_array(WorkItem::Type.base_types.values)
diff --git a/spec/models/hooks/web_hook_spec.rb b/spec/models/hooks/web_hook_spec.rb
index c68ad3bf0c4..59f4533a6c1 100644
--- a/spec/models/hooks/web_hook_spec.rb
+++ b/spec/models/hooks/web_hook_spec.rb
@@ -10,7 +10,11 @@ RSpec.describe WebHook do
let(:hook) { build(:project_hook, project: project) }
around do |example|
- freeze_time { example.run }
+ if example.metadata[:skip_freeze_time]
+ example.run
+ else
+ freeze_time { example.run }
+ end
end
describe 'associations' do
@@ -326,10 +330,28 @@ RSpec.describe WebHook do
expect { hook.backoff! }.to change(hook, :backoff_count).by(1)
end
- it 'does not let the backoff count exceed the maximum failure count' do
- hook.backoff_count = described_class::MAX_FAILURES
+ context 'when we have backed off MAX_FAILURES times' do
+ before do
+ stub_const("#{described_class}::MAX_FAILURES", 5)
+ 5.times { hook.backoff! }
+ end
+
+ it 'does not let the backoff count exceed the maximum failure count' do
+ expect { hook.backoff! }.not_to change(hook, :backoff_count)
+ end
+
+ it 'does not change disabled_until', :skip_freeze_time do
+ travel_to(hook.disabled_until - 1.minute) do
+ expect { hook.backoff! }.not_to change(hook, :disabled_until)
+ end
+ end
- expect { hook.backoff! }.not_to change(hook, :backoff_count)
+ it 'changes disabled_until when it has elapsed', :skip_freeze_time do
+ travel_to(hook.disabled_until + 1.minute) do
+ expect { hook.backoff! }.to change { hook.disabled_until }
+ expect(hook.backoff_count).to eq(described_class::MAX_FAILURES)
+ end
+ end
end
include_examples 'is tolerant of invalid records' do
diff --git a/spec/models/work_item/type_spec.rb b/spec/models/work_item/type_spec.rb
index 90f551b7d63..dd5324d63a0 100644
--- a/spec/models/work_item/type_spec.rb
+++ b/spec/models/work_item/type_spec.rb
@@ -19,8 +19,10 @@ RSpec.describe WorkItem::Type do
it 'deletes type but not unrelated issues' do
type = create(:work_item_type)
+ expect(WorkItem::Type.count).to eq(5)
+
expect { type.destroy! }.not_to change(Issue, :count)
- expect(WorkItem::Type.count).to eq 0
+ expect(WorkItem::Type.count).to eq(4)
end
end
@@ -28,7 +30,7 @@ RSpec.describe WorkItem::Type do
type = create(:work_item_type, work_items: [work_item])
expect { type.destroy! }.to raise_error(ActiveRecord::InvalidForeignKey)
- expect(Issue.count).to eq 1
+ expect(Issue.count).to eq(1)
end
end
diff --git a/spec/rake_helper.rb b/spec/rake_helper.rb
index 7df1cf7444f..ca5b4d8337c 100644
--- a/spec/rake_helper.rb
+++ b/spec/rake_helper.rb
@@ -12,6 +12,6 @@ RSpec.configure do |config|
end
config.after(:all) do
- delete_from_all_tables!
+ delete_from_all_tables!(except: deletion_except_tables)
end
end
diff --git a/spec/requests/api/graphql/mutations/issues/create_spec.rb b/spec/requests/api/graphql/mutations/issues/create_spec.rb
index 66450f8c604..886f3140086 100644
--- a/spec/requests/api/graphql/mutations/issues/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/issues/create_spec.rb
@@ -39,11 +39,14 @@ RSpec.describe 'Create an issue' do
end
it 'creates the issue' do
- post_graphql_mutation(mutation, current_user: current_user)
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ end.to change(Issue, :count).by(1)
expect(response).to have_gitlab_http_status(:success)
expect(mutation_response['issue']).to include(input)
expect(mutation_response['issue']).to include('discussionLocked' => true)
+ expect(Issue.last.work_item_type.base_type).to eq('issue')
end
end
end
diff --git a/spec/requests/api/graphql/mutations/issues/update_spec.rb b/spec/requests/api/graphql/mutations/issues/update_spec.rb
index c3aaf090703..0f2eeb90894 100644
--- a/spec/requests/api/graphql/mutations/issues/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/issues/update_spec.rb
@@ -44,6 +44,19 @@ RSpec.describe 'Update of an existing issue' do
expect(mutation_response['issue']).to include('discussionLocked' => true)
end
+ context 'when issue_type is updated' do
+ let(:input) { { 'iid' => issue.iid.to_s, 'type' => 'INCIDENT' } }
+
+ it 'updates issue_type and work_item_type' do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ issue.reload
+ end.to change { issue.work_item_type.base_type }.from('issue').to('incident').and(
+ change(issue, :issue_type).from('issue').to('incident')
+ )
+ end
+ end
+
context 'setting labels' do
let(:mutation) do
graphql_mutation(:update_issue, input_params) do
diff --git a/spec/services/issues/build_service_spec.rb b/spec/services/issues/build_service_spec.rb
index a67e634a4ff..b96dd981e0f 100644
--- a/spec/services/issues/build_service_spec.rb
+++ b/spec/services/issues/build_service_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe Issues::BuildService do
+ using RSpec::Parameterized::TableSyntax
+
let_it_be(:project) { create(:project, :repository) }
let_it_be(:developer) { create(:user) }
let_it_be(:guest) { create(:user) }
@@ -144,6 +146,8 @@ RSpec.describe Issues::BuildService do
issue = build_issue(milestone_id: milestone.id)
expect(issue.milestone).to eq(milestone)
+ expect(issue.issue_type).to eq('issue')
+ expect(issue.work_item_type.base_type).to eq('issue')
end
it 'sets milestone to nil if it is not available for the project' do
@@ -152,6 +156,15 @@ RSpec.describe Issues::BuildService do
expect(issue.milestone).to be_nil
end
+
+ context 'when issue_type is incident' do
+ it 'sets the correct issue type' do
+ issue = build_issue(issue_type: 'incident')
+
+ expect(issue.issue_type).to eq('incident')
+ expect(issue.work_item_type.base_type).to eq('incident')
+ end
+ end
end
context 'as guest' do
@@ -165,22 +178,13 @@ RSpec.describe Issues::BuildService do
end
context 'setting issue type' do
- it 'defaults to issue if issue_type not given' do
- issue = build_issue
+ shared_examples 'builds an issue' do
+ specify do
+ issue = build_issue(issue_type: issue_type)
- expect(issue).to be_issue
- end
-
- it 'sets issue' do
- issue = build_issue(issue_type: 'issue')
-
- expect(issue).to be_issue
- end
-
- it 'sets incident' do
- issue = build_issue(issue_type: 'incident')
-
- expect(issue).to be_incident
+ expect(issue.issue_type).to eq(resulting_issue_type)
+ expect(issue.work_item_type_id).to eq(work_item_type_id)
+ end
end
it 'cannot set invalid issue type' do
@@ -188,6 +192,24 @@ RSpec.describe Issues::BuildService do
expect(issue).to be_issue
end
+
+ context 'with a corresponding WorkItem::Type' do
+ let_it_be(:type_issue_id) { WorkItem::Type.default_issue_type.id }
+ let_it_be(:type_incident_id) { WorkItem::Type.default_by_type(:incident).id }
+
+ where(:issue_type, :work_item_type_id, :resulting_issue_type) do
+ nil | ref(:type_issue_id) | 'issue'
+ 'issue' | ref(:type_issue_id) | 'issue'
+ 'incident' | ref(:type_incident_id) | 'incident'
+ 'test_case' | ref(:type_issue_id) | 'issue' # update once support for test_case is enabled
+ 'requirement' | ref(:type_issue_id) | 'issue' # update once support for requirement is enabled
+ 'invalid' | ref(:type_issue_id) | 'issue'
+ end
+
+ with_them do
+ it_behaves_like 'builds an issue'
+ end
+ end
end
end
end
diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb
index d43dd437292..3988069d83a 100644
--- a/spec/services/issues/create_service_spec.rb
+++ b/spec/services/issues/create_service_spec.rb
@@ -43,10 +43,11 @@ RSpec.describe Issues::CreateService do
expect(issue).to be_persisted
expect(issue.title).to eq('Awesome issue')
- expect(issue.assignees).to eq [assignee]
- expect(issue.labels).to match_array labels
- expect(issue.milestone).to eq milestone
- expect(issue.due_date).to eq Date.tomorrow
+ expect(issue.assignees).to eq([assignee])
+ expect(issue.labels).to match_array(labels)
+ expect(issue.milestone).to eq(milestone)
+ expect(issue.due_date).to eq(Date.tomorrow)
+ expect(issue.work_item_type.base_type).to eq('issue')
end
context 'when skip_system_notes is true' do
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index 712b9886152..331cf291f21 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -228,15 +228,19 @@ RSpec.describe Issues::UpdateService, :mailer do
context 'from incident to issue' do
let(:issue) { create(:incident, project: project) }
+ it 'changed from an incident to an issue type' do
+ expect { update_issue(issue_type: 'issue') }
+ .to change(issue, :issue_type).from('incident').to('issue')
+ .and(change { issue.work_item_type.base_type }.from('incident').to('issue'))
+ end
+
context 'for an incident with multiple labels' do
let(:issue) { create(:incident, project: project, labels: [label_1, label_2]) }
- before do
- update_issue(issue_type: 'issue')
- end
-
it 'removes an `incident` label if one exists on the incident' do
- expect(issue.labels).to eq([label_2])
+ expect { update_issue(issue_type: 'issue') }.to change(issue, :label_ids)
+ .from(containing_exactly(label_1.id, label_2.id))
+ .to([label_2.id])
end
end
@@ -244,12 +248,10 @@ RSpec.describe Issues::UpdateService, :mailer do
let(:issue) { create(:incident, project: project, labels: [label_1, label_2]) }
let(:params) { { label_ids: [label_1.id, label_2.id], remove_label_ids: [] } }
- before do
- update_issue(issue_type: 'issue')
- end
-
it 'adds an incident label id to remove_label_ids for it to be removed' do
- expect(issue.label_ids).to contain_exactly(label_2.id)
+ expect { update_issue(issue_type: 'issue') }.to change(issue, :label_ids)
+ .from(containing_exactly(label_1.id, label_2.id))
+ .to([label_2.id])
end
end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 6ccfabca101..216835e4b9f 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -220,6 +220,8 @@ RSpec.configure do |config|
# Enable all features by default for testing
# Reset any changes in after hook.
stub_all_feature_flags
+
+ TestEnv.seed_db
end
config.after(:all) do
diff --git a/spec/support/database_cleaner.rb b/spec/support/database_cleaner.rb
index 01bf390d9e9..b31881e3082 100644
--- a/spec/support/database_cleaner.rb
+++ b/spec/support/database_cleaner.rb
@@ -14,7 +14,7 @@ RSpec.configure do |config|
end
config.append_after(:context, :migration) do
- delete_from_all_tables!
+ delete_from_all_tables!(except: ['work_item_types'])
# Postgres maximum number of columns in a table is 1600 (https://github.com/postgres/postgres/blob/de41869b64d57160f58852eab20a27f248188135/src/include/access/htup_details.h#L23-L47).
# And since:
@@ -61,7 +61,7 @@ RSpec.configure do |config|
example.run
- delete_from_all_tables!
+ delete_from_all_tables!(except: ['work_item_types'])
self.class.use_transactional_tests = true
end
diff --git a/spec/support/db_cleaner.rb b/spec/support/db_cleaner.rb
index 155dc3c17d9..940ff2751d3 100644
--- a/spec/support/db_cleaner.rb
+++ b/spec/support/db_cleaner.rb
@@ -12,7 +12,7 @@ module DbCleaner
end
def deletion_except_tables
- []
+ ['work_item_types']
end
def setup_database_cleaner
diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb
index aa5fcf222f2..badd4e8212c 100644
--- a/spec/support/helpers/test_env.rb
+++ b/spec/support/helpers/test_env.rb
@@ -452,6 +452,10 @@ module TestEnv
example_group
end
+ def seed_db
+ Gitlab::DatabaseImporters::WorkItems::BaseTypeImporter.import
+ end
+
private
# These are directories that should be preserved at cleanup time
diff --git a/spec/support/shared_examples/services/incident_shared_examples.rb b/spec/support/shared_examples/services/incident_shared_examples.rb
index 9fced12b543..0277cce975a 100644
--- a/spec/support/shared_examples/services/incident_shared_examples.rb
+++ b/spec/support/shared_examples/services/incident_shared_examples.rb
@@ -13,6 +13,7 @@
RSpec.shared_examples 'incident issue' do
it 'has incident as issue type' do
expect(issue.issue_type).to eq('incident')
+ expect(issue.work_item_type.base_type).to eq('incident')
end
end
@@ -41,6 +42,7 @@ RSpec.shared_examples 'not an incident issue' do
it 'has not incident as issue type' do
expect(issue.issue_type).not_to eq('incident')
+ expect(issue.work_item_type.base_type).not_to eq('incident')
end
it 'has not an incident label' do
diff --git a/spec/support/shared_examples/work_item_base_types_importer.rb b/spec/support/shared_examples/work_item_base_types_importer.rb
index 2c02b76d49b..7d652be8d05 100644
--- a/spec/support/shared_examples/work_item_base_types_importer.rb
+++ b/spec/support/shared_examples/work_item_base_types_importer.rb
@@ -2,6 +2,9 @@
RSpec.shared_examples 'work item base types importer' do
it 'creates all base work item types' do
+ # Fixtures need to run on a pristine DB, but the test suite preloads the base types before(:suite)
+ WorkItem::Type.delete_all
+
expect { subject }.to change(WorkItem::Type, :count).from(0).to(WorkItem::Type::BASE_TYPES.count)
end
end