diff --git a/app/services/hyrax/lease_manager.rb b/app/services/hyrax/lease_manager.rb index e95bfef42f..05692a5b80 100644 --- a/app/services/hyrax/lease_manager.rb +++ b/app/services/hyrax/lease_manager.rb @@ -1,6 +1,9 @@ # frozen_string_literal: true module Hyrax + ## + # Provides utilities for managing the lifecycle of an `Hyrax::Lease` on a + # `Hyrax::Resource`. class LeaseManager ## # @!attribute [rw] resource @@ -31,6 +34,12 @@ def lease_for(resource:, query_service: Hyrax.query_service) end end + ## + # Copies and applies the lease to a new (target) resource. + # + # @param target [Hyrax::Resource] + # + # @return [Boolean] def copy_lease_to(target:) return false unless under_lease? @@ -47,7 +56,14 @@ def apply end ## - # @return [Valkyrie::Resource] + # @return [void] + # @raise [NotEnforcableError] when trying to apply an lease that isn't active + def apply! + apply || raise(NotEnforcableError) + end + + ## + # @return [Hyrax::Lease] def lease resource.lease || Lease.new end @@ -58,6 +74,9 @@ def under_lease? lease.active? end + class NotEnforcableError < RuntimeError; end + class NotReleasableError < RuntimeError; end + private def clone_attributes diff --git a/spec/services/hyrax/lease_manager_spec.rb b/spec/services/hyrax/lease_manager_spec.rb new file mode 100644 index 0000000000..a5e2c22e75 --- /dev/null +++ b/spec/services/hyrax/lease_manager_spec.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +RSpec.describe Hyrax::LeaseManager do + subject(:manager) { described_class.new(resource: resource) } + let(:resource) { Hyrax::Resource.new } + + shared_context 'when under lease' do + let(:resource) { FactoryBot.build(:hyrax_resource, :under_lease) } + end + + shared_context 'with expired lease' do + let(:resource) { FactoryBot.build(:hyrax_resource, lease: lease) } + let(:lease) { FactoryBot.build(:hyrax_lease, :expired) } + end + + describe '#apply' do + context 'with no lease' do + it 'is a no-op' do + expect { manager.apply } + .not_to change { resource.visibility } + end + end + + context 'with expired lease' do + include_context 'with expired lease' + + it 'is a no-op' do + expect { manager.apply } + .not_to change { resource.visibility } + end + end + + context 'when under lease' do + include_context 'when under lease' + + before { resource.visibility = 'restricted' } + + it 'applies the active lease visibility' do + expect { manager.apply } + .to change { resource.visibility } + .from('restricted') + .to 'open' + end + end + end + + describe '#lease' do + it 'gives an inactive lease' do + expect(manager.lease).not_to be_active + end + + context 'when under lease' do + include_context 'when under lease' + + it 'gives an active lease' do + expect(manager.lease).to be_active + end + + it 'has lease attributes' do + expect(manager.lease) + .to have_attributes visibility_after_lease: 'authenticated', + visibility_during_lease: 'open', + lease_expiration_date: an_instance_of(Date), + lease_history: be_empty + end + end + end + + describe '#under_lease?' do + it { is_expected.not_to be_under_lease } + + context 'when under lease' do + include_context 'when under lease' + + it { is_expected.to be_under_lease } + end + + context 'with expired lease' do + include_context 'with expired lease' + + it { is_expected.not_to be_under_lease } + end + end +end