Commit 60a5ee64 authored by Luke Duncalfe's avatar Luke Duncalfe

Merge branch '2941870-ttl-policy-graphql' into 'master'

GraphQL for dependency proxy TTL policy

See merge request gitlab-org/gitlab!68900
parents 4024dda1 fc7454cc
# frozen_string_literal: true
module Mutations
module DependencyProxy
module ImageTtlGroupPolicy
class Update < Mutations::BaseMutation
include Mutations::ResolvesGroup
graphql_name 'UpdateDependencyProxyImageTtlGroupPolicy'
authorize :admin_dependency_proxy
argument :group_path,
GraphQL::Types::ID,
required: true,
description: 'Group path for the group dependency proxy image TTL policy.'
argument :enabled,
GraphQL::Types::Boolean,
required: false,
description: copy_field_description(Types::DependencyProxy::ImageTtlGroupPolicyType, :enabled)
argument :ttl,
GraphQL::Types::Int,
required: false,
description: copy_field_description(Types::DependencyProxy::ImageTtlGroupPolicyType, :ttl)
field :dependency_proxy_image_ttl_policy,
Types::DependencyProxy::ImageTtlGroupPolicyType,
null: true,
description: 'Group image TTL policy after mutation.'
def resolve(group_path:, **args)
group = authorized_find!(group_path: group_path)
result = ::DependencyProxy::ImageTtlGroupPolicies::UpdateService
.new(container: group, current_user: current_user, params: args)
.execute
{
dependency_proxy_image_ttl_policy: result.payload[:dependency_proxy_image_ttl_policy],
errors: result.errors
}
end
private
def find_object(group_path:)
resolve_group(full_path: group_path)
end
end
end
end
end
# frozen_string_literal: true
module Types
class DependencyProxy::ImageTtlGroupPolicyType < BaseObject
graphql_name 'DependencyProxyImageTtlGroupPolicy'
description 'Group-level Dependency Proxy TTL policy settings'
authorize :read_dependency_proxy
field :enabled, GraphQL::Types::Boolean, null: false, description: 'Indicates whether the policy is enabled or disabled.'
field :ttl, GraphQL::Types::Int, null: true, description: 'Number of days to retain a cached image file.'
field :created_at, Types::TimeType, null: true, description: 'Timestamp of creation.'
field :updated_at, Types::TimeType, null: true, description: 'Timestamp of the most recent update.'
end
end
......@@ -163,6 +163,11 @@ module Types
null: false,
description: 'Prefix for pulling images when using the dependency proxy.'
field :dependency_proxy_image_ttl_policy,
Types::DependencyProxy::ImageTtlGroupPolicyType,
null: true,
description: 'Dependency proxy TTL policy for the group.'
def label(title:)
BatchLoader::GraphQL.for(title).batch(key: group) do |titles, loader, args|
LabelsFinder
......
......@@ -34,6 +34,7 @@ module Types
mount_mutation Mutations::Commits::Create, calls_gitaly: true
mount_mutation Mutations::CustomEmoji::Create, feature_flag: :custom_emoji
mount_mutation Mutations::Discussions::ToggleResolve
mount_mutation Mutations::DependencyProxy::ImageTtlGroupPolicy::Update
mount_mutation Mutations::Environments::CanaryIngress::Update
mount_mutation Mutations::Issues::Create
mount_mutation Mutations::Issues::SetAssignees
......
......@@ -764,6 +764,10 @@ class Group < Namespace
::CustomerRelations::Contact.where(group_id: self.id)
end
def dependency_proxy_image_ttl_policy
super || build_dependency_proxy_image_ttl_policy
end
private
def max_member_access(user_ids)
......
# frozen_string_literal: true
module DependencyProxy
class ImageTtlGroupPolicyPolicy < BasePolicy
delegate { @subject.group }
end
end
......@@ -228,8 +228,9 @@ class GroupPolicy < BasePolicy
rule { dependency_proxy_access_allowed & dependency_proxy_available }
.enable :read_dependency_proxy
rule { developer & dependency_proxy_available }
.enable :admin_dependency_proxy
rule { developer & dependency_proxy_available }.policy do
enable :admin_dependency_proxy
end
rule { can?(:admin_group) & resource_access_token_feature_available }.policy do
enable :read_resource_access_tokens
......
# frozen_string_literal: true
module DependencyProxy
module ImageTtlGroupPolicies
class UpdateService < BaseContainerService
include Gitlab::Utils::StrongMemoize
ALLOWED_ATTRIBUTES = %i[enabled ttl].freeze
def execute
return ServiceResponse.error(message: 'Access Denied', http_status: 403) unless allowed?
return ServiceResponse.error(message: 'Dependency proxy image TTL Policy not found', http_status: 404) unless dependency_proxy_image_ttl_policy
if dependency_proxy_image_ttl_policy.update(dependency_proxy_image_ttl_policy_params)
ServiceResponse.success(payload: { dependency_proxy_image_ttl_policy: dependency_proxy_image_ttl_policy })
else
ServiceResponse.error(
message: dependency_proxy_image_ttl_policy.errors.full_messages.to_sentence || 'Bad request',
http_status: 400
)
end
end
private
def dependency_proxy_image_ttl_policy
strong_memoize(:dependency_proxy_image_ttl_policy) do
container.dependency_proxy_image_ttl_policy
end
end
def allowed?
Ability.allowed?(current_user, :admin_dependency_proxy, container)
end
def dependency_proxy_image_ttl_policy_params
params.slice(*ALLOWED_ATTRIBUTES)
end
end
end
end
......@@ -4180,6 +4180,27 @@ Input type: `UpdateContainerExpirationPolicyInput`
| <a id="mutationupdatecontainerexpirationpolicycontainerexpirationpolicy"></a>`containerExpirationPolicy` | [`ContainerExpirationPolicy`](#containerexpirationpolicy) | Container expiration policy after mutation. |
| <a id="mutationupdatecontainerexpirationpolicyerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
### `Mutation.updateDependencyProxyImageTtlGroupPolicy`
Input type: `UpdateDependencyProxyImageTtlGroupPolicyInput`
#### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationupdatedependencyproxyimagettlgrouppolicyclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationupdatedependencyproxyimagettlgrouppolicyenabled"></a>`enabled` | [`Boolean`](#boolean) | Indicates whether the policy is enabled or disabled. |
| <a id="mutationupdatedependencyproxyimagettlgrouppolicygrouppath"></a>`groupPath` | [`ID!`](#id) | Group path for the group dependency proxy image TTL policy. |
| <a id="mutationupdatedependencyproxyimagettlgrouppolicyttl"></a>`ttl` | [`Int`](#int) | Number of days to retain a cached image file. |
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationupdatedependencyproxyimagettlgrouppolicyclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationupdatedependencyproxyimagettlgrouppolicydependencyproxyimagettlpolicy"></a>`dependencyProxyImageTtlPolicy` | [`DependencyProxyImageTtlGroupPolicy`](#dependencyproxyimagettlgrouppolicy) | Group image TTL policy after mutation. |
| <a id="mutationupdatedependencyproxyimagettlgrouppolicyerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
### `Mutation.updateEpic`
Input type: `UpdateEpicInput`
......@@ -8788,6 +8809,19 @@ Dependency proxy blob.
| <a id="dependencyproxyblobsize"></a>`size` | [`String!`](#string) | Size of the blob file. |
| <a id="dependencyproxyblobupdatedat"></a>`updatedAt` | [`Time!`](#time) | Date of most recent update. |
### `DependencyProxyImageTtlGroupPolicy`
Group-level Dependency Proxy TTL policy settings.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="dependencyproxyimagettlgrouppolicycreatedat"></a>`createdAt` | [`Time`](#time) | Timestamp of creation. |
| <a id="dependencyproxyimagettlgrouppolicyenabled"></a>`enabled` | [`Boolean!`](#boolean) | Indicates whether the policy is enabled or disabled. |
| <a id="dependencyproxyimagettlgrouppolicyttl"></a>`ttl` | [`Int`](#int) | Number of days to retain a cached image file. |
| <a id="dependencyproxyimagettlgrouppolicyupdatedat"></a>`updatedAt` | [`Time`](#time) | Timestamp of the most recent update. |
### `DependencyProxyManifest`
Dependency proxy manifest.
......@@ -9866,6 +9900,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="groupdependencyproxyblobs"></a>`dependencyProxyBlobs` | [`DependencyProxyBlobConnection`](#dependencyproxyblobconnection) | Dependency Proxy blobs. (see [Connections](#connections)) |
| <a id="groupdependencyproxyimagecount"></a>`dependencyProxyImageCount` | [`Int!`](#int) | Number of dependency proxy images cached in the group. |
| <a id="groupdependencyproxyimageprefix"></a>`dependencyProxyImagePrefix` | [`String!`](#string) | Prefix for pulling images when using the dependency proxy. |
| <a id="groupdependencyproxyimagettlpolicy"></a>`dependencyProxyImageTtlPolicy` | [`DependencyProxyImageTtlGroupPolicy`](#dependencyproxyimagettlgrouppolicy) | Dependency proxy TTL policy for the group. |
| <a id="groupdependencyproxymanifests"></a>`dependencyProxyManifests` | [`DependencyProxyManifestConnection`](#dependencyproxymanifestconnection) | Dependency Proxy manifests. (see [Connections](#connections)) |
| <a id="groupdependencyproxysetting"></a>`dependencyProxySetting` | [`DependencyProxySetting`](#dependencyproxysetting) | Dependency Proxy settings for the group. |
| <a id="groupdependencyproxytotalsize"></a>`dependencyProxyTotalSize` | [`String!`](#string) | Total size of the dependency proxy cached images. |
......
# frozen_string_literal: true
FactoryBot.define do
factory :image_ttl_group_policy, class: 'DependencyProxy::ImageTtlGroupPolicy' do
group
enabled { true }
ttl { 90 }
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Mutations::DependencyProxy::ImageTtlGroupPolicy::Update do
using RSpec::Parameterized::TableSyntax
let_it_be_with_reload(:group) { create(:group) }
let_it_be(:user) { create(:user) }
let(:params) { { group_path: group.full_path } }
specify { expect(described_class).to require_graphql_authorizations(:admin_dependency_proxy) }
describe '#resolve' do
subject { described_class.new(object: group, context: { current_user: user }, field: nil).resolve(**params) }
shared_examples 'returning a success' do
it 'returns the dependency proxy image ttl group policy with no errors' do
expect(subject).to eq(
dependency_proxy_image_ttl_policy: ttl_policy,
errors: []
)
end
end
shared_examples 'updating the dependency proxy image ttl policy' do
it_behaves_like 'updating the dependency proxy image ttl policy attributes',
from: { enabled: true, ttl: 90 },
to: { enabled: false, ttl: 2 }
it_behaves_like 'returning a success'
context 'with invalid params' do
let_it_be(:params) { { group_path: group.full_path, enabled: nil } }
it "doesn't create the dependency proxy image ttl policy" do
expect { subject }.not_to change { DependencyProxy::ImageTtlGroupPolicy.count }
end
it 'does not update' do
expect { subject }
.not_to change { ttl_policy.reload.enabled }
end
it 'returns an error' do
expect(subject).to eq(
dependency_proxy_image_ttl_policy: nil,
errors: ['Enabled is not included in the list']
)
end
end
end
shared_examples 'denying access to dependency proxy image ttl policy' do
it 'raises Gitlab::Graphql::Errors::ResourceNotAvailable' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
before do
stub_config(dependency_proxy: { enabled: true })
end
context 'with existing dependency proxy image ttl policy' do
let_it_be(:ttl_policy) { create(:image_ttl_group_policy, group: group) }
let_it_be(:params) do
{ group_path: group.full_path,
enabled: false,
ttl: 2 }
end
where(:user_role, :shared_examples_name) do
:maintainer | 'updating the dependency proxy image ttl policy'
:developer | 'updating the dependency proxy image ttl policy'
:reporter | 'denying access to dependency proxy image ttl policy'
:guest | 'denying access to dependency proxy image ttl policy'
:anonymous | 'denying access to dependency proxy image ttl policy'
end
with_them do
before do
group.send("add_#{user_role}", user) unless user_role == :anonymous
end
it_behaves_like params[:shared_examples_name]
end
end
context 'without existing dependency proxy image ttl policy' do
let_it_be(:ttl_policy) { group.dependency_proxy_image_ttl_policy }
where(:user_role, :shared_examples_name) do
:maintainer | 'creating the dependency proxy image ttl policy'
:developer | 'creating the dependency proxy image ttl policy'
:reporter | 'denying access to dependency proxy image ttl policy'
:guest | 'denying access to dependency proxy image ttl policy'
:anonymous | 'denying access to dependency proxy image ttl policy'
end
with_them do
before do
group.send("add_#{user_role}", user) unless user_role == :anonymous
end
it_behaves_like params[:shared_examples_name]
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['DependencyProxyImageTtlGroupPolicy'] do
it { expect(described_class.graphql_name).to eq('DependencyProxyImageTtlGroupPolicy') }
it { expect(described_class.description).to eq('Group-level Dependency Proxy TTL policy settings') }
it { expect(described_class).to require_graphql_authorizations(:read_dependency_proxy) }
it 'includes dependency proxy image ttl policy fields' do
expected_fields = %w[enabled ttl created_at updated_at]
expect(described_class).to have_graphql_fields(*expected_fields).only
end
end
......@@ -21,8 +21,8 @@ RSpec.describe GitlabSchema.types['Group'] do
packages dependency_proxy_setting dependency_proxy_manifests
dependency_proxy_blobs dependency_proxy_image_count
dependency_proxy_blob_count dependency_proxy_total_size
dependency_proxy_image_prefix shared_runners_setting
timelogs organizations contacts
dependency_proxy_image_prefix dependency_proxy_image_ttl_policy
shared_runners_setting timelogs organizations contacts
]
expect(described_class).to include_graphql_fields(*expected_fields)
......
......@@ -2743,4 +2743,28 @@ RSpec.describe Group do
expect(group.dependency_proxy_image_prefix).not_to include('http')
end
end
describe '#dependency_proxy_image_ttl_policy' do
subject(:ttl_policy) { group.dependency_proxy_image_ttl_policy }
it 'builds a new policy if one does not exist', :aggregate_failures do
expect(ttl_policy.ttl).to eq(90)
expect(ttl_policy.enabled).to eq(false)
expect(ttl_policy.created_at).to be_nil
expect(ttl_policy.updated_at).to be_nil
end
context 'with existing policy' do
before do
group.dependency_proxy_image_ttl_policy.update!(ttl: 30, enabled: true)
end
it 'returns the policy if it already exists', :aggregate_failures do
expect(ttl_policy.ttl).to eq(30)
expect(ttl_policy.enabled).to eq(true)
expect(ttl_policy.created_at).not_to be_nil
expect(ttl_policy.updated_at).not_to be_nil
end
end
end
end
......@@ -893,6 +893,34 @@ RSpec.describe GroupPolicy do
end
end
describe 'dependency proxy' do
context 'feature disabled' do
let(:current_user) { owner }
it { is_expected.to be_disallowed(:read_dependency_proxy) }
it { is_expected.to be_disallowed(:admin_dependency_proxy) }
end
context 'feature enabled' do
before do
stub_config(dependency_proxy: { enabled: true })
group.create_dependency_proxy_setting!(enabled: true)
end
context 'reporter' do
let(:current_user) { reporter }
it { is_expected.to be_disallowed(:admin_dependency_proxy) }
end
context 'developer' do
let(:current_user) { developer }
it { is_expected.to be_allowed(:admin_dependency_proxy) }
end
end
end
context 'deploy token access' do
let!(:group_deploy_token) do
create(:group_deploy_token, group: group, deploy_token: deploy_token)
......@@ -920,6 +948,18 @@ RSpec.describe GroupPolicy do
it { is_expected.to be_allowed(:read_contact) }
it { is_expected.to be_disallowed(:destroy_package) }
end
context 'a deploy token with dependency proxy scopes' do
let_it_be(:deploy_token) { create(:deploy_token, :group, :dependency_proxy_scopes) }
before do
stub_config(dependency_proxy: { enabled: true })
group.create_dependency_proxy_setting!(enabled: true)
end
it { is_expected.to be_allowed(:read_dependency_proxy) }
it { is_expected.to be_disallowed(:admin_dependency_proxy) }
end
end
it_behaves_like 'Self-managed Core resource access tokens'
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'getting dependency proxy image ttl policy for a group' do
using RSpec::Parameterized::TableSyntax
include GraphqlHelpers
let_it_be(:user) { create(:user) }
let_it_be_with_reload(:group) { create(:group) }
let(:dependency_proxy_image_ttl_policy_fields) do
<<~GQL
#{all_graphql_fields_for('dependency_proxy_image_ttl_group_policy'.classify, max_depth: 1)}
GQL
end
let(:fields) do
<<~GQL
#{query_graphql_field('dependency_proxy_image_ttl_policy', {}, dependency_proxy_image_ttl_policy_fields)}
GQL
end
let(:query) do
graphql_query_for(
'group',
{ 'fullPath' => group.full_path },
fields
)
end
let(:variables) { {} }
let(:dependency_proxy_image_ttl_policy_response) { graphql_data.dig('group', 'dependencyProxyImageTtlPolicy') }
before do
stub_config(dependency_proxy: { enabled: true })
end
subject { post_graphql(query, current_user: user, variables: variables) }
it_behaves_like 'a working graphql query' do
before do
subject
end
end
context 'with different permissions' do
where(:group_visibility, :role, :access_granted) do
:private | :maintainer | true
:private | :developer | true
:private | :reporter | true
:private | :guest | true
:private | :anonymous | false
:public | :maintainer | true
:public | :developer | true
:public | :reporter | true
:public | :guest | true
:public | :anonymous | false
end
with_them do
before do
group.update_column(:visibility_level, Gitlab::VisibilityLevel.const_get(group_visibility.to_s.upcase, false))
group.add_user(user, role) unless role == :anonymous
end
it 'return the proper response' do
subject
if access_granted
expect(dependency_proxy_image_ttl_policy_response).to eq("createdAt" => nil, "enabled" => false, "ttl" => 90, "updatedAt" => nil)
else
expect(dependency_proxy_image_ttl_policy_response).to be_blank
end
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Updating the dependency proxy image ttl policy' do
include GraphqlHelpers
using RSpec::Parameterized::TableSyntax
let_it_be(:user) { create(:user) }
let(:params) do
{
group_path: group.full_path,
enabled: false,
ttl: 2
}
end
let(:mutation) do
graphql_mutation(:update_dependency_proxy_image_ttl_group_policy, params) do
<<~QL
dependencyProxyImageTtlPolicy {
enabled
ttl
}
errors
QL
end
end
let(:mutation_response) { graphql_mutation_response(:update_dependency_proxy_image_ttl_group_policy) }
let(:ttl_policy_response) { mutation_response['dependencyProxyImageTtlPolicy'] }
before do
stub_config(dependency_proxy: { enabled: true })
end
describe 'post graphql mutation' do
subject { post_graphql_mutation(mutation, current_user: user) }
let_it_be(:ttl_policy, reload: true) { create(:image_ttl_group_policy) }
let_it_be(:group, reload: true) { ttl_policy.group }
context 'without permission' do
it 'returns no response' do
subject
expect(response).to have_gitlab_http_status(:success)
expect(mutation_response).to be_nil
end
end
context 'with permission' do
before do
group.add_developer(user)
end
it 'returns the updated dependency proxy image ttl policy', :aggregate_failures do
subject
expect(response).to have_gitlab_http_status(:success)
expect(mutation_response['errors']).to be_empty
expect(ttl_policy_response).to include(
'enabled' => params[:enabled],
'ttl' => params[:ttl]
)
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe ::DependencyProxy::ImageTtlGroupPolicies::UpdateService do
using RSpec::Parameterized::TableSyntax
let_it_be_with_reload(:group) { create(:group) }
let_it_be(:user) { create(:user) }
let_it_be(:params) { {} }
describe '#execute' do
subject { described_class.new(container: group, current_user: user, params: params).execute }
shared_examples 'returning a success' do
it 'returns a success' do
result = subject
expect(result.payload[:dependency_proxy_image_ttl_policy]).to be_present
expect(result).to be_success
end
end
shared_examples 'returning an error' do |message, http_status|
it 'returns an error' do
result = subject
expect(result).to have_attributes(
message: message,
status: :error,
http_status: http_status
)
end
end
shared_examples 'updating the dependency proxy image ttl policy' do
it_behaves_like 'updating the dependency proxy image ttl policy attributes',
from: { enabled: true, ttl: 90 },
to: { enabled: false, ttl: 2 }
it_behaves_like 'returning a success'
context 'with invalid params' do
let_it_be(:params) { { enabled: nil } }
it_behaves_like 'not creating the dependency proxy image ttl policy'
it "doesn't update" do
expect { subject }
.not_to change { ttl_policy.reload.enabled }
end
it_behaves_like 'returning an error', 'Enabled is not included in the list', 400
end
end
shared_examples 'denying access to dependency proxy image ttl policy' do
context 'with existing dependency proxy image ttl policy' do
it_behaves_like 'not creating the dependency proxy image ttl policy'
it_behaves_like 'returning an error', 'Access Denied', 403
end
end
before do
stub_config(dependency_proxy: { enabled: true })
end
context 'with existing dependency proxy image ttl policy' do
let_it_be(:ttl_policy) { create(:image_ttl_group_policy, group: group) }
let_it_be(:params) { { enabled: false, ttl: 2 } }
where(:user_role, :shared_examples_name) do
:maintainer | 'updating the dependency proxy image ttl policy'
:developer | 'updating the dependency proxy image ttl policy'
:reporter | 'denying access to dependency proxy image ttl policy'
:guest | 'denying access to dependency proxy image ttl policy'
:anonymous | 'denying access to dependency proxy image ttl policy'
end
with_them do
before do
group.send("add_#{user_role}", user) unless user_role == :anonymous
end
it_behaves_like params[:shared_examples_name]
end
end
context 'without existing dependency proxy image ttl policy' do
let_it_be(:ttl_policy) { group.dependency_proxy_image_ttl_policy }
where(:user_role, :shared_examples_name) do
:maintainer | 'creating the dependency proxy image ttl policy'
:developer | 'creating the dependency proxy image ttl policy'
:reporter | 'denying access to dependency proxy image ttl policy'
:guest | 'denying access to dependency proxy image ttl policy'
:anonymous | 'denying access to dependency proxy image ttl policy'
end
with_them do
before do
group.send("add_#{user_role}", user) unless user_role == :anonymous
end
it_behaves_like params[:shared_examples_name]
end
context 'when the policy is not found' do
before do
group.add_developer(user)
expect(group).to receive(:dependency_proxy_image_ttl_policy).and_return nil
end
it_behaves_like 'returning an error', 'Dependency proxy image TTL Policy not found', 404
end
end
end
end
# frozen_string_literal: true
RSpec.shared_examples 'updating the dependency proxy image ttl policy attributes' do |from: {}, to:|
it_behaves_like 'not creating the dependency proxy image ttl policy'
it 'updates the dependency proxy image ttl policy' do
expect { subject }
.to change { group.dependency_proxy_image_ttl_policy.reload.enabled }.from(from[:enabled]).to(to[:enabled])
.and change { group.dependency_proxy_image_ttl_policy.reload.ttl }.from(from[:ttl]).to(to[:ttl])
end
end
RSpec.shared_examples 'not creating the dependency proxy image ttl policy' do
it "doesn't create the dependency proxy image ttl policy" do
expect { subject }.not_to change { DependencyProxy::ImageTtlGroupPolicy.count }
end
end
RSpec.shared_examples 'creating the dependency proxy image ttl policy' do
it 'creates a new package setting' do
expect { subject }.to change { DependencyProxy::ImageTtlGroupPolicy.count }.by(1)
end
it 'saves the settings' do
subject
expect(group.dependency_proxy_image_ttl_policy).to have_attributes(
enabled: ttl_policy[:enabled],
ttl: ttl_policy[:ttl]
)
end
it_behaves_like 'returning a success'
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment