diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index eb6aa50e3..e47792d84 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -1,15 +1,15 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
-# on 2022-02-18 11:32:31 -0500 using RuboCop version 0.52.1.
+# on 2022-05-24 16:48:44 -0400 using RuboCop version 0.52.1.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.
-# Offense count: 203
+# Offense count: 187
# Configuration parameters: CountComments, ExcludedMethods.
Metrics/BlockLength:
- Max: 217
+ Max: 181
# Offense count: 1
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
@@ -17,10 +17,10 @@ Metrics/BlockLength:
Metrics/LineLength:
Max: 215
-# Offense count: 5
+# Offense count: 4
# Configuration parameters: CountComments.
Metrics/MethodLength:
- Max: 30
+ Max: 27
# Offense count: 2
RSpec/AnyInstance:
@@ -33,16 +33,13 @@ RSpec/DescribeClass:
Exclude:
- 'spec/features/hyrax_callbacks_spec.rb'
-# Offense count: 14
+# Offense count: 5
# Configuration parameters: AssignmentOnly.
RSpec/InstanceVariable:
Exclude:
- - 'spec/features/spot_workflow_spec.rb'
- - 'spec/services/spot/mappers/base_mapper_spec.rb'
- - 'spec/services/spot/validators/bag_validator_spec.rb'
+ - 'spec/presenters/spot/iiif_manifest_presenter_spec.rb'
- 'spec/support/shared_examples/indexing/indexes_language_and_label.rb'
- 'spec/support/shared_examples/logs_a_warning.rb'
- - 'spec/support/shared_examples/mappers/language_tagged_titles.rb'
- 'spec/support/shared_examples/spot_workflow_notification.rb'
# Offense count: 1
@@ -55,6 +52,13 @@ RSpec/NamedSubject:
Exclude:
- 'spec/support/shared_examples/logs_a_warning.rb'
+# Offense count: 3
+# Configuration parameters: IgnoreSymbolicNames.
+RSpec/VerifiedDoubles:
+ Exclude:
+ - 'spec/inputs/multi_authority_controlled_vocabulary_input_spec.rb'
+ - 'spec/jobs/characterize_job_spec.rb'
+
# Offense count: 1
# Cop supports --auto-correct.
Security/YAMLLoad:
diff --git a/app/channels/application_cable/channel.rb b/app/channels/application_cable/channel.rb
deleted file mode 100644
index 51e3e936b..000000000
--- a/app/channels/application_cable/channel.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-# frozen_string_literal: true
-module ApplicationCable
- class Channel < ActionCable::Channel::Base
- end
-end
diff --git a/app/channels/application_cable/connection.rb b/app/channels/application_cable/connection.rb
deleted file mode 100644
index fa70319da..000000000
--- a/app/channels/application_cable/connection.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-# frozen_string_literal: true
-module ApplicationCable
- class Connection < ActionCable::Connection::Base
- end
-end
diff --git a/app/controllers/concerns/spot/redirection_helpers.rb b/app/controllers/concerns/spot/redirection_helpers.rb
index 18a95605c..d0d8536df 100644
--- a/app/controllers/concerns/spot/redirection_helpers.rb
+++ b/app/controllers/concerns/spot/redirection_helpers.rb
@@ -7,7 +7,7 @@ module RedirectionHelpers
# collections but not other work types. There's probably something I'm missing, but this will at least
# allow us to resolve Handles and legacy URLs for collections.
def redirect_params_for(solr_document:)
- controller = solr_document['has_model_ssim'].first.downcase.pluralize
+ controller = solr_document['has_model_ssim'].first.underscore.pluralize
params = { controller: "hyrax/#{controller}", action: 'show', id: solr_document['id'] }
return Hyrax::Engine.routes.url_for(**params, only_path: true) if controller == 'collections'
diff --git a/app/controllers/handle_controller.rb b/app/controllers/handle_controller.rb
index e43aca06a..9816bf323 100644
--- a/app/controllers/handle_controller.rb
+++ b/app/controllers/handle_controller.rb
@@ -20,10 +20,6 @@ def show
private
- def id_from_params
- URI.decode(params[:id])
- end
-
# @return [String]
def identifier_solr_field
'identifier_ssim'
diff --git a/app/indexers/concerns/indexes_sortable_date.rb b/app/indexers/concerns/indexes_sortable_date.rb
index a8e965d58..45d65fcb9 100644
--- a/app/indexers/concerns/indexes_sortable_date.rb
+++ b/app/indexers/concerns/indexes_sortable_date.rb
@@ -45,11 +45,6 @@ def generate_solr_document
private
- # @return [String, nil]
- def raw_date_value
- object.send(sortable_date_property).sort.first
- end
-
# Converts whatever our +date_value+ is to a YYYY-MM-DDT00:00:00Z
# string for Solr to use in sorting.
#
diff --git a/app/jobs/characterize_job.rb b/app/jobs/characterize_job.rb
index 2acedad55..677347de7 100644
--- a/app/jobs/characterize_job.rb
+++ b/app/jobs/characterize_job.rb
@@ -23,13 +23,19 @@ def characterize(file_set, _file_id, filepath)
Hydra::Works::CharacterizationService.run(file_set.characterization_proxy, filepath, ch12n_tool: tool)
Rails.logger.debug "Ran characterization on #{file_set.characterization_proxy.id} (#{file_set.characterization_proxy.mime_type})"
- alpha_channels(file_set) if file_set.image? && Hyrax.config.iiif_image_server?
+ alpha_channels(file_set, filepath) if file_set.image? && Hyrax.config.iiif_image_server?
file_set.characterization_proxy.save!
file_set.update_index
file_set.parent&.in_collections&.each(&:update_index)
end
+ def alpha_channels(file_set, filepath)
+ return unless file_set.characterization_proxy.respond_to?(:alpha_channels=)
+
+ file_set.characterization_proxy.alpha_channels = channels(filepath)
+ end
+
def channels(filepath)
ch = MiniMagick::Tool::Identify.new do |cmd|
cmd.format '%[channels]'
@@ -38,12 +44,6 @@ def channels(filepath)
[ch]
end
- def alpha_channels(file_set)
- return unless file_set.characterization_proxy.respond_to?(:alpha_channels=)
-
- file_set.characterization_proxy.alpha_channels = channels(filepath)
- end
-
def tool
ENV['FITS_SERVLET_URL'].present? ? :fits_servlet : :fits
end
diff --git a/app/models/concerns/spot/nested_collection_behavior.rb b/app/models/concerns/spot/nested_collection_behavior.rb
index d291b33cc..88257b59d 100644
--- a/app/models/concerns/spot/nested_collection_behavior.rb
+++ b/app/models/concerns/spot/nested_collection_behavior.rb
@@ -32,7 +32,7 @@ def add_member_objects(new_member_ids)
collections_to_add = gather_collections_to_add
collection_ids_to_add = collections_to_add.map(&:id)
- Array(new_member_ids).collect do |member_id|
+ Array.wrap(new_member_ids).collect do |member_id|
member = member_query_service(member_id)
message = check_multiple_membership(item: member, collection_ids: collection_ids_to_add)
diff --git a/app/presenters/concerns/spot/presents_attributes.rb b/app/presenters/concerns/spot/presents_attributes.rb
index 5f21ebc85..5b8d47f63 100644
--- a/app/presenters/concerns/spot/presents_attributes.rb
+++ b/app/presenters/concerns/spot/presents_attributes.rb
@@ -69,9 +69,9 @@ def find_renderer_class(name)
end
return renderer unless renderer.nil?
- return super if defined?(:super)
- raise NameError, "unknown renderer type: #{name}"
+ # super will check the Hyrax scope, which is necessary for their renderers
+ super
end
end
end
diff --git a/app/services/spot/iiif_service.rb b/app/services/spot/iiif_service.rb
index 45d1e77a4..7798df898 100644
--- a/app/services/spot/iiif_service.rb
+++ b/app/services/spot/iiif_service.rb
@@ -11,6 +11,16 @@ class IiifService
COMPLIANCE_LEVEL_URI = 'http://iiif.io/api/image/2/level2.json'
DEFAULT_SIZE = '600,'
+ # Class method for providing a download url (one where the content-disposition is set to 'attachment')
+ #
+ # @param [String] file_id
+ # @param [String] size
+ # @param [String] filename (must include extension)
+ # @return [String]
+ def self.download_url(file_id:, size:, filename:)
+ new(file_id: file_id).download_url(size: size, filename: filename)
+ end
+
# Class method to be used via Hyrax initializer for generating an image's IIIF URL.
# We're not using the +base_url+ parameter provided and instead relying on
# the default, which is the environment value for 'IIIF_BASE_URL'.
@@ -24,7 +34,7 @@ class IiifService
# @return [String]
# @see config/initializers/hyrax.rb
def self.image_url(file_id, _base_url, size)
- new(file_id: file_id, base_url: ENV['IIIF_BASE_URL']).image_url(size: size)
+ new(file_id: file_id).image_url(size: size)
end
# Class method to be used via Hyrax initializer for generating an info.json URL.
@@ -41,17 +51,7 @@ def self.image_url(file_id, _base_url, size)
# @note this produces a URL _without_ the final 'info.json' of the path.
# Somewhere in the pipeline this is added (possibly by the viewer?)
def self.info_url(file_id, _base_url)
- new(file_id: file_id, base_url: ENV['IIIF_BASE_URL']).info_url
- end
-
- # Class method for providing a download url (one where the content-disposition is set to 'attachment')
- #
- # @param [String] file_id
- # @param [String] size
- # @param [String] filename (must include extension)
- # @return [String]
- def self.download_url(file_id:, size:, filename:)
- new(file_id: file_id, base_url: ENV['IIIF_BASE_URL']).download_url(size: size, filename: filename)
+ new(file_id: file_id).info_url
end
attr_reader :file_id, :base_url
diff --git a/app/validators/spot/required_local_authority_validator.rb b/app/validators/spot/required_local_authority_validator.rb
index 0296f7898..3d7104287 100644
--- a/app/validators/spot/required_local_authority_validator.rb
+++ b/app/validators/spot/required_local_authority_validator.rb
@@ -30,7 +30,7 @@ def validate(record)
def authority_for(name)
# try the name
- return Qa::Authorities::Local::FileBasedAuthority.new(name) if authority_exists?(name)
+ return Qa::Authorities::Local::FileBasedAuthority.new(name.to_s) if authority_exists?(name.to_s)
# otherwise oops!
raise "Authority doesn't exist: #{name}"
diff --git a/config/cable.yml b/config/cable.yml
deleted file mode 100644
index ed2a8da4f..000000000
--- a/config/cable.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-development:
- adapter: async
-
-test:
- adapter: async
-
-production:
- adapter: redis
- url: redis://localhost:6379/1
- channel_prefix: spot_production
diff --git a/config/routes.rb b/config/routes.rb
index bc9c091c1..4de0e7e4a 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -33,7 +33,8 @@
}
# handle uri catching: ldr.lafayette.edu/handle/:id
- resources :handle, only: :show, constraints: { id: %r{[0-9]+/[a-zA-Z0-9]+} }
+ # allows slash encoding ('/' => '%2F')
+ resources :handle, only: :show, constraints: { id: %r{10385/[a-zA-Z0-9%]+} }
##
# routes for engines + hyrax
diff --git a/spec/authorities/qa/authorities/solr_suggest_spec.rb b/spec/authorities/qa/authorities/solr_suggest_spec.rb
index d6b52405b..65b693179 100644
--- a/spec/authorities/qa/authorities/solr_suggest_spec.rb
+++ b/spec/authorities/qa/authorities/solr_suggest_spec.rb
@@ -55,4 +55,36 @@
it { is_expected.to eq [] }
end
+
+ describe '#build_dictionary!' do
+ before do
+ allow(ActiveFedora::SolrService.instance.conn)
+ .to receive(:get)
+ .with('/solr/spot-test/suggest', params: params)
+
+ authority.build_dictionary!
+ end
+
+ context 'when acting on an individual field' do
+ let(:dictionary) { 'keyword' }
+ let(:params) { { 'suggest' => true, 'suggest.dictionary' => dictionary, 'suggest.build' => true } }
+
+ it 'sends individaul dictionary params' do
+ expect(ActiveFedora::SolrService.instance.conn)
+ .to have_received(:get)
+ .with('/solr/spot-test/suggest', params: params)
+ end
+ end
+
+ context 'when building all' do
+ let(:dictionary) { described_class::BUILD_ALL_KEYWORD }
+ let(:params) { { 'suggest' => true, 'suggest.buildAll' => true } }
+
+ it 'sends the build_all params' do
+ expect(ActiveFedora::SolrService.instance.conn)
+ .to have_received(:get)
+ .with('/solr/spot-test/suggest', params: params)
+ end
+ end
+ end
end
diff --git a/spec/controllers/handle_controller_spec.rb b/spec/controllers/handle_controller_spec.rb
index b44e041f5..2d2c97b15 100644
--- a/spec/controllers/handle_controller_spec.rb
+++ b/spec/controllers/handle_controller_spec.rb
@@ -42,8 +42,21 @@
it { is_expected.to redirect_to hyrax_image_path(solr_data[:id]) }
end
- context 'when a Handle exists for a Collection' do
+ context 'when a Handle exists for a StudentWork' do
let(:handle) { '10385/9012' }
+ let(:solr_data) do
+ {
+ id: 'existing-student_work',
+ has_model_ssim: ['StudentWork'],
+ identifier_ssim: ["hdl:#{handle}"]
+ }
+ end
+
+ it { is_expected.to redirect_to hyrax_student_work_path(solr_data[:id]) }
+ end
+
+ context 'when a Handle exists for a Collection' do
+ let(:handle) { '10385/col123' }
let(:solr_data) do
{
id: 'existing-col',
@@ -56,7 +69,7 @@
end
context 'when a handle does not exist for an item' do
- let(:handle) { '1234/nothere' }
+ let(:handle) { '10385/nothere' }
let(:solr_data) { { id: 'unrelated' } }
diff --git a/spec/forms/spot/forms/collection_form_spec.rb b/spec/forms/spot/forms/collection_form_spec.rb
index d2f7fc714..b2a015eb3 100644
--- a/spec/forms/spot/forms/collection_form_spec.rb
+++ b/spec/forms/spot/forms/collection_form_spec.rb
@@ -87,6 +87,14 @@
it { is_expected.to eq [RDF::Literal('A lengthier explanation of a collection', language: :en)] }
end
end
+
+ describe 'stores a "slug" identifier' do
+ subject { attributes[:identifier] }
+
+ let(:params) { { 'slug' => 'a-cool-collection' } }
+
+ it { is_expected.to include 'slug:a-cool-collection' }
+ end
end
describe '#initialize_field' do
diff --git a/spec/helpers/spot/facet_helper_spec.rb b/spec/helpers/spot/facet_helper_spec.rb
index 790412190..2b3e73a40 100644
--- a/spec/helpers/spot/facet_helper_spec.rb
+++ b/spec/helpers/spot/facet_helper_spec.rb
@@ -58,4 +58,54 @@
it { is_expected.to eq 'Public' }
end
+
+ describe '#admin_facets?' do
+ subject { helper.admin_facets? }
+
+ let(:user) { create(:user) }
+ let(:admin_user) { create(:admin_user) }
+ let(:current_user) { admin_user }
+
+ before do
+ allow(helper).to receive(:current_user).and_return(current_user)
+ end
+
+ context 'when current_user is not an admin' do
+ let(:current_user) { user }
+
+ it { is_expected.to be false }
+ end
+
+ context 'when no admin_facets are defined' do
+ before do
+ allow(helper).to receive(:admin_facet_names).and_return []
+ end
+
+ it { is_expected.to be false }
+ end
+
+ context 'when facets are in the request' do
+ let(:facet) { double }
+
+ before do
+ allow(helper).to receive(:facets_from_request).and_return([facet])
+ end
+
+ context 'when any of the facets should be rendered' do
+ before do
+ allow(helper).to receive(:should_render_facet?).with(facet).and_return true
+ end
+
+ it { is_expected.to be true }
+ end
+
+ context 'when none of the facets should be rendered' do
+ before do
+ allow(helper).to receive(:should_render_facet?).with(facet).and_return false
+ end
+
+ it { is_expected.to be false }
+ end
+ end
+ end
end
diff --git a/spec/inputs/multi_authority_controlled_vocabulary_input_spec.rb b/spec/inputs/multi_authority_controlled_vocabulary_input_spec.rb
new file mode 100644
index 000000000..5240d9bfb
--- /dev/null
+++ b/spec/inputs/multi_authority_controlled_vocabulary_input_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+RSpec.describe MultiAuthorityControlledVocabularyInput, type: :input do
+ let(:work) { build(:publication) }
+ let(:form) { Hyrax::PublicationForm.new(work, admin_ability, nil) }
+ let(:admin_ability) { Ability.new(create(:admin_user)) }
+ let(:input) { input_for(form, :location, as: :multi_authority_controlled_vocabulary, authorities: [:geonames, :tgn]) }
+
+ let(:item1) { double('value 1', rdf_label: ['Item 1'], rdf_subject: 'http://example.org/1', node?: false) }
+ let(:item2) { double('value 2', rdf_label: ['Item 2'], rdf_subject: 'http://example.org/2', node?: false) }
+
+ before do
+ allow(form).to receive(:[]).with(:location).and_return([item1, item2])
+ end
+
+ it 'adds dropdowns for each authority' do
+ expect(input).to have_selector('select#publication_location_authority_select_2 option', count: 3)
+ end
+
+ it 'renders label + subject if both are present' do
+ expect(input).to have_selector('input[value="Item 1 (http://example.org/1)"]', visible: false)
+ expect(input).to have_selector('input[value="Item 2 (http://example.org/2)"]', visible: false)
+ end
+end
diff --git a/spec/jobs/characterize_job_spec.rb b/spec/jobs/characterize_job_spec.rb
index da61ff1c4..d3eae225e 100644
--- a/spec/jobs/characterize_job_spec.rb
+++ b/spec/jobs/characterize_job_spec.rb
@@ -16,10 +16,11 @@
allow(f).to receive(:save!)
end
end
+ let(:characterization_proxy) { file }
before do
allow(FileSet).to receive(:find).with(file_set_id).and_return(file_set)
- allow(Hydra::Works::CharacterizationService).to receive(:run).with(file, filename, ch12n_tool: tool)
+ allow(Hydra::Works::CharacterizationService).to receive(:run).with(characterization_proxy, filename, ch12n_tool: tool)
allow(CreateDerivativesJob).to receive(:perform_later).with(file_set, file.id, filename)
allow(Hyrax::WorkingDirectory).to receive(:find_or_retrieve).and_return(filename)
end
@@ -49,10 +50,44 @@
context 'when the characterization proxy content is absent' do
before { allow(file_set).to receive(:characterization_proxy?).and_return(false) }
+
it 'raises an error' do
expect { described_class.perform_now(file_set, file.id) }.to raise_error(StandardError, /original_file was not found/)
end
end
+
+ # I don't think we implement the Alpha channel storage
+ # (at least )
+ context 'when the file_set is an image' do
+ let(:cmd_container) { double }
+ let(:channel_value) { 'alpha value' }
+ let(:characterization_proxy) { double(id: 'CharacterizationProxy', mime_type: 'application/none') }
+
+ before do
+ allow(Hyrax.config).to receive(:iiif_image_server?).and_return true
+ allow(file_set).to receive(:image?).and_return true
+ allow(file_set).to receive(:characterization_proxy).and_return characterization_proxy
+ allow(characterization_proxy).to receive(:alpha_channels=)
+ allow(characterization_proxy).to receive(:save!)
+ allow(MiniMagick::Tool::Identify)
+ .to receive(:new)
+ .and_yield(cmd_container)
+ .and_return(channel_value)
+
+ allow(cmd_container).to receive(:format).with('%[channels]')
+ allow(cmd_container).to receive(:<<).with(filename)
+ end
+
+ it 'parses and stores the alpha channels' do
+ described_class.perform_now(file_set, file.id)
+
+ expect(cmd_container).to have_received(:format).with('%[channels]')
+ expect(cmd_container).to have_received(:<<).with(filename)
+
+ expect(characterization_proxy).to have_received(:alpha_channels=).with([channel_value])
+ expect(characterization_proxy).to have_received(:save!)
+ end
+ end
end
context 'when FITS_SERVLET_URL is defined' do
diff --git a/spec/models/collection_spec.rb b/spec/models/collection_spec.rb
index 577bae659..2b748dbfb 100644
--- a/spec/models/collection_spec.rb
+++ b/spec/models/collection_spec.rb
@@ -122,6 +122,9 @@
# note: this should fail when we update to hyrax@3 because we'll need to stub +Hyrax.query_service+
describe '#add_member_objects' do
+ let(:grandparent_collection) { described_class.new(id: 'grandparent-collection') }
+ let(:parent_collection) { described_class.new(id: 'parent-collection') }
+ let(:child_collection) { described_class.new(id: 'child-collection') }
let(:work) { Publication.new(id: 'publication-to-add-to-collections') }
before do
@@ -139,9 +142,6 @@
end
context 'with a single parent object' do
- let(:parent_collection) { described_class.new(id: 'parent-collection') }
- let(:child_collection) { described_class.new(id: 'child-collection') }
-
before { child_collection.member_of_collections << parent_collection }
it 'adds the work to the collection + parent' do
@@ -151,10 +151,6 @@
end
context 'with a deeper tree' do
- let(:grandparent_collection) { described_class.new(id: 'grandparent-collection') }
- let(:parent_collection) { described_class.new(id: 'parent-collection') }
- let(:child_collection) { described_class.new(id: 'child-collection') }
-
before do
parent_collection.member_of_collections << grandparent_collection
child_collection.member_of_collections << parent_collection
@@ -165,5 +161,43 @@
expect(work.member_of_collections).to eq [child_collection, parent_collection, grandparent_collection]
end
end
+
+ context 'when the Hyrax.query_service is active' do
+ let(:query_service) { double }
+
+ before do
+ allow(Hyrax).to receive(:query_service).and_return(query_service)
+ allow(query_service)
+ .to receive(:find_by_alternate_id)
+ .with(alternate_id: work.id, use_valkyrie: false)
+ .and_return(work)
+ end
+
+ xit 'uses the service' do
+ expect(child_collection.add_member_objects([work.id])).to eq [work]
+ expect(query_service).to have_received(:find_by_alternate_id)
+ end
+ end
+
+ context 'when a work can not be added to the collection' do
+ let(:checker_double) { instance_double(Hyrax::MultipleMembershipChecker) }
+
+ before do
+ allow(Hyrax::MultipleMembershipChecker)
+ .to receive(:new)
+ .with(item: work)
+ .and_return(checker_double)
+
+ allow(checker_double)
+ .to receive(:check)
+ .with(collection_ids: [child_collection.id], include_current_members: true)
+ .and_return('Can not include work into collection')
+ end
+
+ it 'returns works with errors attached' do
+ expect(child_collection.add_member_objects([work.id]).flat_map { |work| work.errors[:collections] })
+ .to eq ['Can not include work into collection']
+ end
+ end
end
end
diff --git a/spec/models/spot/controlled_vocabularies/base_spec.rb b/spec/models/spot/controlled_vocabularies/base_spec.rb
index 8381ce503..7f2d6a984 100644
--- a/spec/models/spot/controlled_vocabularies/base_spec.rb
+++ b/spec/models/spot/controlled_vocabularies/base_spec.rb
@@ -5,9 +5,11 @@
let(:label_en) { RDF::Literal('Horror in art', language: :en) }
let(:label_de) { RDF::Literal('Schrecken ', language: :de) }
let(:labels) { [label_en, label_de] }
+ let(:graph) { RDF::Graph.new.tap { |graph| statements.each { |stmt| graph << stmt } } }
+ let(:statements) { labels.map { |label| RDF::Statement(resource, RDF::Vocab::SKOS.prefLabel, label) } }
before do
- allow(resource).to receive(:rdf_label).and_return(labels)
+ stub_request(:get, uri.to_s).to_return(status: 200, body: graph.dump(:ttl))
end
describe '#default_labels' do
@@ -53,6 +55,24 @@
expect(RdfLabel.count).to be > 1
end
end
+
+ context 'when a label already exists' do
+ before do
+ RdfLabel.create(uri: uri.to_s, value: label_en.to_s)
+ stub_request(:any, uri.to_s)
+
+ resource.fetch
+ end
+
+ after do
+ RdfLabel.find_by(uri: uri.to_s)&.destroy
+ end
+
+ it 'does not make a remote call' do
+ expect(resource.rdf_label).to eq [label_en.to_s]
+ expect(a_request(:get, 'http://id.loc.gov')).not_to have_been_made
+ end
+ end
end
describe '#preferred_label' do
@@ -63,6 +83,8 @@
allow(resource).to receive(:pick_preferred_label)
# need to trigger first_or_create before checking to see if it exists
cache
+
+ resource.fetch
end
after { cache.delete }
@@ -79,6 +101,8 @@
# this is our public method way of testing +Base#pick_preferred_label+
context 'when a label has not been cached' do
+ before { resource.fetch }
+
context 'when an English value exists' do
it { is_expected.to eq label_en.to_s }
end
@@ -96,6 +120,10 @@
let(:generated_label) { "#{label_en}$#{uri}" }
+ before do
+ resource.fetch
+ end
+
it { is_expected.to include uri.to_s }
it { is_expected.to include(label: generated_label) }
diff --git a/spec/presenters/spot/iiif_manifest_presenter_spec.rb b/spec/presenters/spot/iiif_manifest_presenter_spec.rb
new file mode 100644
index 000000000..72a948c28
--- /dev/null
+++ b/spec/presenters/spot/iiif_manifest_presenter_spec.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+RSpec.describe Spot::IiifManifestPresenter do
+ let(:presenter) { described_class.new(document) }
+ let(:document) { SolrDocument.new(solr_attributes) }
+
+ describe '#manifest_metadata' do
+ subject(:manifest_metadata) { presenter.manifest_metadata.first }
+
+ let(:solr_attributes) { { 'title_alternative_tesim' => ['Another Title to the Work'] } }
+
+ describe 'locale handling' do
+ context 'when a field has a locale' do
+ it { is_expected.to include 'label' => 'Alternative Title' }
+ it { is_expected.to include 'value' => ['Another Title to the Work'] }
+ end
+
+ context 'when a field does not have a locale' do
+ before do
+ @old_locale = I18n.locale
+ I18n.locale = :fr # we currently don't support french
+ end
+
+ after do
+ I18n.locale = @old_locale
+ end
+
+ it { is_expected.to include 'label' => 'Title Alternative' }
+ it { is_expected.to include 'value' => ['Another Title to the Work'] }
+ end
+ end
+
+ context 'when a field is empty' do
+ it 'does not appear in the output' do
+ expect(presenter.title).to eq []
+ expect(presenter.manifest_metadata.any? { |m| m['label'] == 'Title' }).to be false
+ end
+ end
+
+ # I'm not entirely sure when this is being used (maybe we're passing work presenters
+ # into this presenter; those return CV tuples), but we should make sure the presenter
+ # knows how to handle a special-case controlled vocabulary value
+ context 'when a field returns a controlled vocabulary tuple' do
+ let(:subject_tuple) { [['http://id.worldcat.org/fast/2004076', 'Little free libraries']] }
+
+ before do
+ allow(Hyrax.config).to receive(:iiif_metadata_fields).and_return([:subject])
+ allow(document).to receive(:subject).and_return(subject_tuple)
+ end
+
+ it { is_expected.to include 'label' => 'Subject' }
+ it { is_expected.to include 'value' => ['Little free libraries'] }
+ end
+ end
+end
diff --git a/spec/services/spot/handle_service_spec.rb b/spec/services/spot/handle_service_spec.rb
index c64e5b6ae..0421d4f20 100644
--- a/spec/services/spot/handle_service_spec.rb
+++ b/spec/services/spot/handle_service_spec.rb
@@ -2,7 +2,7 @@
RSpec.describe Spot::HandleService do
subject(:service) { described_class.new(work) }
- let(:work) { instance_double(Publication, id: 'abc123def', identifier: identifiers) }
+ let(:work) { build(:publication, id: 'abc123def', identifier: identifiers) }
let(:identifiers) { [] }
let(:handle_server_url) { 'http://handle-service:8000' }
let(:handle_prefix) { '10385' }
@@ -53,10 +53,10 @@
stub_env('HANDLE_CLIENT_CERT', cert_path)
stub_env('HANDLE_CLIENT_KEY', key_path)
- allow(service).to receive(:cert_exist?).and_return(true)
- allow(service).to receive(:cert_contents).and_return(:cert_data)
- allow(service).to receive(:key_exist?).and_return(true)
- allow(service).to receive(:key_contents).and_return(:key_data)
+ allow(File).to receive(:exist?).with(cert_path).and_return true
+ allow(File).to receive(:read).with(cert_path).and_return(:cert_data)
+ allow(File).to receive(:exist?).with(key_path).and_return true
+ allow(File).to receive(:read).with(key_path).and_return(:key_data)
allow(OpenSSL::X509::Certificate).to receive(:new).with(:cert_data).and_return(cert_double)
allow(OpenSSL::PKey).to receive(:read).with(:key_data).and_return(key_double)
@@ -80,6 +80,17 @@
expect(work).to have_received(:save!)
end
+ context 'when a work already has an identifier' do
+ let(:identifiers) { ['hdl:10385/abc123def'] }
+
+ it 'does not save the identifier to the record' do
+ service.mint
+
+ expect(work).not_to have_received(:identifier=)
+ expect(work).not_to have_received(:save!)
+ end
+ end
+
context 'when a responseCode != 1 is returned' do
let(:body_content) { { responseCode: 202, handle: "#{handle_prefix}/#{work.id}" } }
diff --git a/spec/services/spot/iiif_service_spec.rb b/spec/services/spot/iiif_service_spec.rb
index c75a5cafc..13d3c60b2 100644
--- a/spec/services/spot/iiif_service_spec.rb
+++ b/spec/services/spot/iiif_service_spec.rb
@@ -8,25 +8,10 @@
let(:file_id) { 'abc123def/files/00000000-0000-0000-0000-000000000000' }
- describe '#info_url' do
- subject { service.info_url }
+ describe '.download_url' do
+ subject { described_class.download_url(file_id: file_id, size: '500,', filename: 'image.jpg') }
- # viewer appends info.json i believe
- it { is_expected.to eq 'http://localhost/iiif/2/abc123def' }
- end
-
- describe '#image_url' do
- context 'without any arguments' do
- subject { service.image_url }
-
- it { is_expected.to eq 'http://localhost/iiif/2/abc123def/full/600,/0/default.jpg' }
- end
-
- context 'with arguments provided' do
- subject { service.image_url(region: '100,100', size: '100,100', rotation: '180', quality: 'gray', format: 'tif') }
-
- it { is_expected.to eq 'http://localhost/iiif/2/abc123def/100,100/100,100/180/gray.tif' }
- end
+ it { is_expected.to eq 'http://localhost/iiif/2/abc123def/full/500,/0/default.jpg?response-content-disposition=attachment%3B%20image.jpg' }
end
describe '#download_url' do
@@ -48,4 +33,31 @@
it { is_expected.to eq 'http://localhost/iiif/2/abc123def/100,100/100,100/180/gray.tif?response-content-disposition=attachment%3B%20gray-work.tif' }
end
end
+
+ describe '.image_url' do
+ subject { described_class.image_url(file_id, 'http://noop.global', '500,') }
+
+ it { is_expected.to eq 'http://localhost/iiif/2/abc123def/full/500,/0/default.jpg' }
+ end
+
+ describe '#image_url' do
+ context 'without any arguments' do
+ subject { service.image_url }
+
+ it { is_expected.to eq 'http://localhost/iiif/2/abc123def/full/600,/0/default.jpg' }
+ end
+
+ context 'with arguments provided' do
+ subject { service.image_url(region: '100,100', size: '100,100', rotation: '180', quality: 'gray', format: 'tif') }
+
+ it { is_expected.to eq 'http://localhost/iiif/2/abc123def/100,100/100,100/180/gray.tif' }
+ end
+ end
+
+ describe '.info_url' do
+ subject { described_class.info_url(file_id, 'http://noop.global/iiif/2') }
+
+ # viewer appends info.json i believe
+ it { is_expected.to eq 'http://localhost/iiif/2/abc123def' }
+ end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 30d496447..b384d8b4c 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -120,6 +120,8 @@
config.include ControllerHelpers, type: :helper
config.include Select2Helpers, type: :feature
config.include Mail::Matchers, type: :mailer
+ config.include InputSupport, type: :input
+ config.include Capybara::RSpecMatchers, type: :input
config.use_transactional_fixtures = false
config.render_views = true
diff --git a/spec/support/input_support.rb b/spec/support/input_support.rb
new file mode 100644
index 000000000..d3ce5cc3f
--- /dev/null
+++ b/spec/support/input_support.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+#
+# Pulled from Hyrax
+# @see https://github.com/samvera/hyrax/blob/v2.9.6/spec/support/input_support.rb
+module InputSupport
+ extend ActiveSupport::Concern
+
+ include RSpec::Rails::HelperExampleGroup
+
+ def input_for(object, attribute_name, options = {})
+ helper.simple_form_for object, url: '' do |f|
+ f.input attribute_name, options
+ end
+ end
+end
diff --git a/spec/support/shared_examples/presenters/humanizes_date_fields.rb b/spec/support/shared_examples/presenters/humanizes_date_fields.rb
index 1093017ac..2e5935260 100644
--- a/spec/support/shared_examples/presenters/humanizes_date_fields.rb
+++ b/spec/support/shared_examples/presenters/humanizes_date_fields.rb
@@ -30,6 +30,12 @@
it { is_expected.to eq ['1986 to 2020'] }
end
+
+ context 'when the value is not EDTF parseable' do
+ let(:original_value) { ['Last Year'] }
+
+ it { is_expected.to eq original_value }
+ end
end
end
end
diff --git a/spec/support/shared_examples/renders_attribute_to_html.rb b/spec/support/shared_examples/presenters/renders_attribute_to_html.rb
similarity index 63%
rename from spec/support/shared_examples/renders_attribute_to_html.rb
rename to spec/support/shared_examples/presenters/renders_attribute_to_html.rb
index d8ed1dad1..33d6ac4f6 100644
--- a/spec/support/shared_examples/renders_attribute_to_html.rb
+++ b/spec/support/shared_examples/presenters/renders_attribute_to_html.rb
@@ -6,21 +6,20 @@
let(:ability) { Ability.new(build(:user)) }
let(:field) { :keyword }
let(:options) { {} }
+ let(:klass) { Spot::Renderers::AttributeRenderer }
+
+ before do
+ allow(klass).to receive(:new).and_return(attribute_double)
+ end
describe '#attribute_to_html' do
- subject(:render_attribute!) { presenter.attribute_to_html(field, options) }
+ subject(:render_attribute) { presenter.attribute_to_html(field, options) }
let(:attribute_double) { instance_double(klass.to_s, render: true) }
- before do
- allow(klass).to receive(:new).and_return(attribute_double)
- end
-
context 'default mode' do
- let(:klass) { Spot::Renderers::AttributeRenderer }
-
it 'calls the AttributeRenderer by default' do
- render_attribute!
+ render_attribute
expect(attribute_double).to have_received(:render)
end
@@ -31,7 +30,7 @@
let(:options) { { render_as: :faceted } }
it 'calls the FacetedAttributeRenderer' do
- render_attribute!
+ render_attribute
expect(attribute_double).to have_received(:render)
end
@@ -42,7 +41,7 @@
let(:options) { { render_as: :external_authority } }
it 'calls the ExternalAuthorityAttributeRenderer' do
- render_attribute!
+ render_attribute
expect(attribute_double).to have_received(:render)
end
@@ -53,10 +52,31 @@
let(:options) { { render_as: :date } }
it 'calls the Hyrax DateAttributeRenderer' do
- render_attribute!
+ render_attribute
expect(attribute_double).to have_received(:render)
end
end
+
+ context 'when a field does not exist' do
+ let(:field) { :nope }
+
+ before do
+ allow(Rails.logger).to receive(:warn)
+ end
+
+ it 'returns nothing and logs a warning' do
+ expect(render_attribute).to be nil
+ expect(Rails.logger).to have_received(:warn).exactly(1).time
+ end
+ end
+
+ context 'when a renderer does not exist' do
+ let(:options) { { render_as: :something_bad } }
+
+ it 'raises a NameError' do
+ expect { render_attribute }.to raise_error(NameError, 'unknown renderer type `something_bad`')
+ end
+ end
end
end
diff --git a/spec/support/shared_examples/spot_presenter.rb b/spec/support/shared_examples/presenters/spot_presenter.rb
similarity index 100%
rename from spec/support/shared_examples/spot_presenter.rb
rename to spec/support/shared_examples/presenters/spot_presenter.rb
diff --git a/spec/support/shared_examples/spot_works_controller.rb b/spec/support/shared_examples/spot_works_controller.rb
index e44fc5a9c..0382b072a 100644
--- a/spec/support/shared_examples/spot_works_controller.rb
+++ b/spec/support/shared_examples/spot_works_controller.rb
@@ -99,4 +99,13 @@
end
end
end
+
+ describe 'IIIF manifests' do
+ it 'returns a JSON manifest' do
+ get :manifest, params: { id: work.id, format: 'json' }
+
+ expect(response).to be_successful
+ expect(response.header.fetch('Content-Type')).to start_with 'application/json;'
+ end
+ end
end
diff --git a/spec/validators/spot/required_local_authority_validator_spec.rb b/spec/validators/spot/required_local_authority_validator_spec.rb
new file mode 100644
index 000000000..b890741a4
--- /dev/null
+++ b/spec/validators/spot/required_local_authority_validator_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+RSpec.describe Spot::RequiredLocalAuthorityValidator do
+ let(:validator) { described_class.new(options) }
+ let(:options) { { field: field, authority: authority } }
+ let(:field) { :resource_type }
+ let(:authority) { 'resource_types' }
+ let(:record) { Publication.new(resource_type: [resource_type_value]) }
+ let(:resource_type_value) { 'Article' }
+
+ describe '#validate' do
+ context 'when the value is valid' do
+ it 'attaches no errors' do
+ validator.validate(record)
+
+ expect(record.errors).to be_empty
+ end
+ end
+
+ context 'when the value is invalid' do
+ let(:resource_type_value) { 'Nothing' }
+
+ it 'adds an error' do
+ validator.validate(record)
+
+ expect(record.errors).not_to be_empty
+ expect(record.errors[field]).to include '"Nothing" is not a valid Resource Type.'
+ end
+ end
+
+ context 'when the authority is not valid' do
+ let(:authority) { 'nonexistent' }
+
+ it 'raises an exception' do
+ expect { validator.validate(record) }
+ .to raise_error(RuntimeError)
+ end
+ end
+ end
+end