- <%# ----------- Title and tooltip ---------------------------------%>
- <%= render partial: "modules/tooltip", locals: { form: form, field: access_object, tooltip: t("access_control.#{access_object}"), options: {display_label: t("access_control.#{access_object}label").html_safe} } %>
-
- <%#------------ Add access item text field and submit button ------------ %>
diff --git a/app/views/playlists/_current_item.html.erb b/app/views/playlists/_current_item.html.erb
new file mode 100644
index 0000000000..d368b698f1
--- /dev/null
+++ b/app/views/playlists/_current_item.html.erb
@@ -0,0 +1,55 @@
+<%#
+Copyright 2011-2015, The Trustees of Indiana University and Northwestern
+ University. Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software distributed
+ under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations under the License.
+--- END LICENSE_HEADER BLOCK ---
+%>
+
diff --git a/app/views/playlists/_current_masterfile.html.erb b/app/views/playlists/_current_masterfile.html.erb
new file mode 100644
index 0000000000..f8de5e9973
--- /dev/null
+++ b/app/views/playlists/_current_masterfile.html.erb
@@ -0,0 +1,82 @@
+<%#
+Copyright 2011-2015, The Trustees of Indiana University and Northwestern
+ University. Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software distributed
+ under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations under the License.
+--- END LICENSE_HEADER BLOCK ---
+%>
+
+
+
+
+<% content_for :page_scripts do %>
+
+<% end %>
diff --git a/app/views/playlists/_edit_form.html.erb b/app/views/playlists/_edit_form.html.erb
new file mode 100644
index 0000000000..a94007a31e
--- /dev/null
+++ b/app/views/playlists/_edit_form.html.erb
@@ -0,0 +1,255 @@
+
+
+
+ <%= link_to "View Playlist", @playlist, { class: 'btn btn-xs btn-primary'} %>
+ <% if can?(:destroy, @playlist) %>
+ <%= link_to "Delete Playlist", @playlist, method: :delete, class: 'btn btn-xs btn-danger btn-confirmation', data: {placement: 'bottom'} %>
+ <% end %>
+
+
+
+
+
+
Name
+
<%= @playlist.title %>
+
+
+
Description
+
<%= @playlist.comment %>
+
+
+
<%= t("blacklight/folders/folder.visibility", scope: "helpers.label") %>
+
+ <% if @playlist.visibility==Playlist::PUBLIC %>
+ <%= human_friendly_visibility Playlist::PUBLIC %>
+ <% else %>
+ <%= human_friendly_visibility Playlist::PRIVATE %>
+ <% end %>
+
+
+
+
+ <%= render 'form' %>
+
+
+ <% if @playlist.items.empty? %>
+
There are currently no items in this playlist.
+ <% else %>
+ <%= form_for(@playlist,url: { action: "update_multiple" }, html: { class: 'form-horizontal playlist_actions' }) do |f| %>
+
+
+
Playlist Items
+
+
+
+
+ <%= check_box_tag 'select_all', "Select All", false, form:"edit_playlist_#{@playlist.id}" %>
+ Select All
+
+
+
+ <% end #form_for update_multiple %>
+
+ <%= form_for(@playlist, html: { id: 'playlist_sort_form', class: 'form-horizontal playlist_actions' }) do |fs| %>
+
+
+ <% @playlist.items.each_with_index do |i, index| %>
+
+
+
+
+ <%= text_field_tag "playlist[items_attributes[#{index}[position]]]", i.position, class: 'form-control position-input', form: 'playlist_sort_form' %>
+ <%= hidden_field_tag "playlist[items_attributes[#{index}[id]]]", i.id, form: 'playlist_sort_form' %>
+
+ <% if can? :read, i.annotation.master_file %>
+
+ <%= link_to i.annotation.title, i.annotation.mediafragment_uri, id: "playlist_item_title_label_#{i.id}" %>
+
+
+
+
+ <%= check_box_tag 'annotation_ids[]', i.id, false, form:"edit_playlist_#{@playlist.id}", class:"playlist_item_select" %>
+ Select
+
+
+ <% else %>
+
+
+ [inaccessible item] <%= i.annotation.master_file.mediaobject.pid %>
+
+
+
+ <%= check_box_tag 'annotation_ids[]', i.id, false, form:"edit_playlist_#{@playlist.id}", class:"playlist_item_select" %>
+ Select
+
+
+ <% end %>
+
+
+
+ <%= bootstrap_form_for i, remote: true, html: { id: "playlist_item_form_#{i.id}", class: "playlist_item_edit_form" }, format: 'json' do |pif| %>
+ <%= hidden_field_tag "playlist_id", @playlist.id, form: "playlist_item_form_#{i.id}" %>
+
+
+
+
+ <% end #bootstrap_form_for playlist_item_edit %>
+
+
+
+ <% end #playlist.items.each %>
+
+
+ <%= fs.submit class: 'btn btn-primary btn-xs', value: 'Save Changes', form: 'playlist_sort_form', style: 'visibility:hidden' %>
+ <% end #form_for playlist_sort_form %>
+ <% end #playlist empty else%>
+
+
+<% content_for :page_scripts do %>
+
+
+<% end %>
diff --git a/app/views/playlists/_form.html.erb b/app/views/playlists/_form.html.erb
new file mode 100644
index 0000000000..8535f34f26
--- /dev/null
+++ b/app/views/playlists/_form.html.erb
@@ -0,0 +1,35 @@
+
+ <%= form_for(@playlist, html: { id: 'playlist_form', class: 'form-horizontal playlist_actions' }) do |f| %>
+
+
+
+
+
+
+ <%= f.submit class: 'btn btn-primary btn-xs', value: t("playlist.#{params[:action]}.action") %>
+ <% if params[:action] == "edit" || params[:action] == "update" %>
+
Cancel
+ <% end %>
+
+
+
+ <% end # form_for playlist_form%>
+
diff --git a/app/views/playlists/_item_list.html.erb b/app/views/playlists/_item_list.html.erb
new file mode 100644
index 0000000000..d9e0a99b7a
--- /dev/null
+++ b/app/views/playlists/_item_list.html.erb
@@ -0,0 +1,40 @@
+<%#
+Copyright 2011-2015, The Trustees of Indiana University and Northwestern
+ University. Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software distributed
+ under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations under the License.
+--- END LICENSE_HEADER BLOCK ---
+%>
+<% if current_ability.can? :edit, @playlist %>
+
+ <%= link_to edit_playlist_path(@playlist) do %>
+
+ Edit Playlist
+
+ <% end %>
+
+<% end %>
+
+ <% if @playlist.visibility==Playlist::PRIVATE %>
+
+ <% else %>
+
+ <% end %>
+ <%= @playlist.title %>
+
+<% if @playlist.comment.present? %>
+
+ <%= @playlist.comment %>
+
+<% end %>
+
+ <%= render partial: 'playlist_item', collection: @playlist.items, locals: { annotations: @playlist.annotations } %>
+
diff --git a/app/views/playlists/_player.html.erb b/app/views/playlists/_player.html.erb
new file mode 100644
index 0000000000..40b1cfd383
--- /dev/null
+++ b/app/views/playlists/_player.html.erb
@@ -0,0 +1,111 @@
+<%#
+Copyright 2011-2015, The Trustees of Indiana University and Northwestern
+ University. Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software distributed
+ under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations under the License.
+--- END LICENSE_HEADER BLOCK ---
+%>
+
+<% f_start = @current_annotation.start_time / 1000.0 %>
+<% f_end = @current_annotation.end_time / 1000.0 %>
+<% @currentStream = @current_masterfile %>
+<% @token = StreamToken.find_or_create_session_token(session, @currentStream.pid) %>
+<% @currentStreamInfo = @currentStream.stream_details(@token, ApplicationController.default_url_options[:host]) %>
+<% @currentStreamInfo['t'] = "#{f_start},#{f_end}" %>
+<% if can? :read, @current_masterfile %>
+
+ <% if @currentStream.present? and @currentStream.derivatives.present? %>
+ <%= stylesheet_link_tag "mediaelement_rails/mediaelementplayer" =%>
+
+ <% if @currentStreamInfo[:is_video] %>
+ <%= master_file_meta_properties(@currentStream) do %>
+
+ <% @currentStreamInfo[:stream_flash].each do |flash| %>
+
+ <% end %>
+ <% @currentStreamInfo[:stream_hls].each do |hls| %>
+
+ <% end %>
+ <% if @currentStreamInfo[:captions_path].present? %>
+
+ <% end %>
+
+ <% end %>
+ <% else %>
+ <%= master_file_meta_properties(@currentStream) do %>
+
+ <% @currentStreamInfo[:stream_flash].each do |flash| %>
+
+ <% end %>
+ <% @currentStreamInfo[:stream_hls].each do |hls| %>
+
+ <% end %>
+
+ <% end %>
+ <% end %>
+ <% end %>
+
+<% end %>
+<% unless can? :read, @current_masterfile %>
+
Restricted Access
+
You do not have permission to playback item <%= @current_masterfile.mediaobject_id %>
+<% end %>
+<% content_for :page_scripts do %>
+ <%= javascript_include_tag "mediaelement_rails/mediaelement-and-player" =%>
+ <%= javascript_include_tag 'android_pre_play' %>
+ <%= javascript_include_tag 'mediaelement-qualityselector/mep-feature-qualities' %>
+ <%= javascript_include_tag 'me-thumb-selector' %>
+ <%= javascript_include_tag 'mediaelement-skin-avalon/mep-feature-responsive' %>
+ <%= javascript_include_tag 'avalon_player' %>
+
+ <%= stylesheet_link_tag "mediaelement-qualityselector/mep-feature-qualities" =%>
+ <%= stylesheet_link_tag 'me-thumb-selector' %>
+ <%= stylesheet_link_tag "mediaelement-skin-avalon/mejs-skin-avalon" =%>
+
+ <% if @currentStream.present? and @currentStream.derivatives.present? %>
+
+ <% end %>
+<% end %>
diff --git a/app/views/playlists/_playlist_item.html.erb b/app/views/playlists/_playlist_item.html.erb
new file mode 100644
index 0000000000..e79c3e04d7
--- /dev/null
+++ b/app/views/playlists/_playlist_item.html.erb
@@ -0,0 +1,26 @@
+<% annotation = AvalonAnnotation.find(@playlist.items[playlist_item_counter].annotation_id) %>
+<% item = @playlist.items[playlist_item_counter] %>
+<% item_class = (item == @current_playlist_item)? 'now_playing' : 'queue' %>
+<% item_type = (annotation.master_file.file_format=='Moving image')? 'fa-film' : 'fa-music' %>
+<% if can? :read, item %>
+
+ <% if item_class == 'now_playing' %>
+
+ <% end %>
+ <%= link_to playlist_path(@playlist, position: item.position) do %>
+ <%= annotation.title %>
+ <% end %>
+ <%= annotation.duration %>
+
+<% else %>
+ <% denied_class = item_class %>
+ <% denied_class = 'denied_item' unless denied_class == 'now_playing'%>
+
+ <% if item_class == 'now_playing' %>
+
+ <% end %>
+
+ [Inaccessible Item] <%=annotation.master_file.mediaobject_id%>
+ <%= annotation.duration %>
+
+<% end %>
diff --git a/app/views/playlists/_related_items.html.erb b/app/views/playlists/_related_items.html.erb
new file mode 100644
index 0000000000..d1b9d1f537
--- /dev/null
+++ b/app/views/playlists/_related_items.html.erb
@@ -0,0 +1,34 @@
+<%#
+Copyright 2011-2015, The Trustees of Indiana University and Northwestern
+ University. Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software distributed
+ under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations under the License.
+--- END LICENSE_HEADER BLOCK ---
+%>
+
diff --git a/app/views/playlists/edit.html.erb b/app/views/playlists/edit.html.erb
new file mode 100644
index 0000000000..9b65b16697
--- /dev/null
+++ b/app/views/playlists/edit.html.erb
@@ -0,0 +1,9 @@
+
+
Editing playlist
+
+<%= render 'edit_form' %>
+
+
+<% content_for :page_scripts do %>
+ <%= javascript_include_tag "avalon_playlists/playlist_items" =%>
+<% end %>
diff --git a/app/views/playlists/index.html.erb b/app/views/playlists/index.html.erb
new file mode 100644
index 0000000000..0efa3c2fcf
--- /dev/null
+++ b/app/views/playlists/index.html.erb
@@ -0,0 +1,93 @@
+<%#
+Copyright 2011-2015, The Trustees of Indiana University and Northwestern
+ University. Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software distributed
+ under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations under the License.
+--- END LICENSE_HEADER BLOCK ---
+%>
+
+<% if current_user.nil? %>
+<%= link_to 'Please login to view your playlists.', new_user_session_path %>
+<% end %>
+<% unless current_user.nil? %>
+ <% playlists = Playlist.where(user_id: current_user.id) %>
+
+
+
+
+
+
+
Playlists (<%= playlists.size %> total)
+ <% unless playlists.empty? %>
+
+
+
+ Name
+ Size
+ Visibility
+ Created
+ Updated
+ Actions
+
+
+
+ <% playlists.each do |playlist| %>
+
+
+ <%= link_to(playlist.title, playlist_path(playlist), title: playlist.comment)%>
+
+
+ <%= PlaylistItem.where(playlist_id: playlist.id).size %> items
+
+
+ <% if playlist.visibility =='private' %>
+ Only me
+ <% end %>
+ <% if playlist.visibility !='private' %>
+ Public
+ <% end %>
+
+
+ ><%= time_ago_in_words(playlist.created_at)%>
+
+
+ ><%= time_ago_in_words(playlist.updated_at)%>
+
+
+ <%= link_to(edit_playlist_path(playlist), class: 'btn btn-default btn-xs') do %>
+ Edit
+ <% end %>
+ <%= link_to(playlist_path(playlist), method: :delete, class: 'btn btn-xs btn-danger btn-confirmation', data: {placement: 'bottom'}) do %>
+ Delete
+ <% end %>
+
+
+ <%end%>
+
+
+
+ <% end %>
+
+ <%= link_to(new_playlist_path) do %>
+
+ Create New Playlist
+
+ <% end %>
+
+
+
+
+
+
+
+ <% unless playlists.empty? %>
+ <% end %>
+<% end %>
diff --git a/app/views/playlists/new.html.erb b/app/views/playlists/new.html.erb
new file mode 100644
index 0000000000..2ddfcc1994
--- /dev/null
+++ b/app/views/playlists/new.html.erb
@@ -0,0 +1,5 @@
+
New playlist
+
+
+ <%= render 'form' %>
+
diff --git a/app/views/playlists/show.html.erb b/app/views/playlists/show.html.erb
new file mode 100644
index 0000000000..970d8ca5e5
--- /dev/null
+++ b/app/views/playlists/show.html.erb
@@ -0,0 +1,82 @@
+<%#
+Copyright 2011-2015, The Trustees of Indiana University and Northwestern
+ University. Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software distributed
+ under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations under the License.
+--- END LICENSE_HEADER BLOCK ---
+%>
+<% @page_title = t('media_objects.show.title', :media_object_title => @playlist.title, :application_name => application_name) %>
+<% position = params.has_key?(:position)? params[:position].to_i : 1 %>
+
+
+
+<% if @playlist.items.empty? %>
+
+ <%= render 'item_list' %>
+ This playlist currently has no items.
+
+<% else %>
+ <% @current_playlist_item = @playlist.items.where(position: position).first %>
+ <% @current_annotation = AvalonAnnotation.find(@current_playlist_item.annotation_id) %>
+ <% @current_masterfile = MasterFile.find(@current_playlist_item.annotation.source.split('/').last) %>
+ <% @current_mediaobject = MediaObject.find(@current_masterfile.mediaobject_id) %>
+
+ <%= render 'player' %>
+ <% if can? :read, @current_masterfile %>
+
+
+
+
+ <%= @current_annotation.title %>
+ [<%= @current_annotation.duration %>]
+
+
+
+ <%= @current_annotation.comment %>
+
+
+
+
+
+ <%= render 'current_item' %>
+
+
+ <% @related_annotations = @playlist.related_annotations(@current_playlist_item)%>
+ <% unless @related_annotations.empty? %>
+
+
+
+ <%= render 'related_items' %>
+
+
+ <% end %>
+
+
+ <% end %>
+
+
+ <%= render 'item_list' %>
+
+<% end %>
+
+
+
+
diff --git a/config/application.rb b/config/application.rb
index e584db735b..7501138fe8 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -11,7 +11,7 @@
end
module Avalon
- VERSION = '4.0.1'
+ VERSION = '5.0'
class MissingUserId < Exception; end
class Application < Rails::Application
diff --git a/config/authentication.yml.example b/config/authentication.yml.example
index 969673b09a..0ffcb13ca8 100644
--- a/config/authentication.yml.example
+++ b/config/authentication.yml.example
@@ -5,6 +5,13 @@
:fields:
- :email
+#- :name: Avalon JSON Api
+# :id: :api
+# :tokens:
+# token:
+# :username: system_account_name
+# :email: system@example.edu
+#
#- :name: Avalon Lti OAuth
# :provider: :lti
# :hidden: true
diff --git a/config/initializers/active_fedora.rb b/config/initializers/active_fedora.rb
index d83af3b3fb..4367528845 100644
--- a/config/initializers/active_fedora.rb
+++ b/config/initializers/active_fedora.rb
@@ -1,3 +1,22 @@
ActiveFedora::Base.class_eval do
has_metadata name: 'DC', type: DublinCoreDocument
end
+
+#Added for Kaminari paging
+ActiveFedora::QueryMethods.module_eval do
+ def extending(*modules, &block)
+ if modules.any? || block
+ spawn.extending!(*modules, &block)
+ else
+ self
+ end
+ end
+end
+
+#Cherry-picked 9cf316b71e78e65d95bfe1fa8ce93373da1a7305
+ActiveFedora::Associations::CollectionProxy.class_eval do
+ def scope
+ @association.scope
+ end
+ alias spawn scope
+end
diff --git a/config/initializers/authn_providers.rb b/config/initializers/authn_providers.rb
index bc412d66e9..81d97c0231 100644
--- a/config/initializers/authn_providers.rb
+++ b/config/initializers/authn_providers.rb
@@ -1,7 +1,8 @@
module Avalon
- module Authentication
- Providers = YAML.load(File.read(File.expand_path('../../authentication.yml',__FILE__)))
- VisibleProviders = Providers.reject {|provider| provider[:hidden]}
+ module Authentication
+ Config = YAML.load(File.read(File.expand_path('../../authentication.yml',__FILE__)))
+ Providers = Config.reject {|provider| provider[:provider].blank? }
+ VisibleProviders = Providers.reject {|provider| provider[:hidden]}
HiddenProviders = Providers - VisibleProviders
end
end
diff --git a/config/initializers/hydra_config.rb b/config/initializers/hydra_config.rb
index ccf79ee96b..2786a4a151 100644
--- a/config/initializers/hydra_config.rb
+++ b/config/initializers/hydra_config.rb
@@ -1,4 +1,6 @@
require 'hydra/head' unless defined? Hydra
+require 'hydra/multiple_policy_aware_access_controls_enforcement'
+require 'hydra/multiple_policy_aware_ability'
Hydra.configure do |config|
@@ -32,6 +34,6 @@
config.permissions.inheritable.read.individual = ActiveFedora::SolrService.solr_name("inheritable_read_access_person", :symbol)
config.permissions.inheritable.edit.group = ActiveFedora::SolrService.solr_name("inheritable_edit_access_group", :symbol)
config.permissions.inheritable.edit.individual = ActiveFedora::SolrService.solr_name("inheritable_edit_access_person", :symbol)
- config.permissions.policy_class = Admin::Collection
-
+ #config.permissions.policy_class = Admin::Collection
+ config.permissions.policy_class = {Admin::Collection => {}, Lease => {clause: " AND begin_time_dti:[* TO NOW] AND end_time_dti:[NOW TO *]"}}
end
diff --git a/config/initializers/kaminari_active_fedora_extension.rb b/config/initializers/kaminari_active_fedora_extension.rb
new file mode 100644
index 0000000000..8a6676b1ed
--- /dev/null
+++ b/config/initializers/kaminari_active_fedora_extension.rb
@@ -0,0 +1,118 @@
+module Kaminari
+# module ActiveFedoraExtension
+# extend ActiveSupport::Concern
+#
+# module ClassMethods
+# # Future subclasses will pick up the model extension
+# def inherited(kls) #:nodoc:
+# super
+# kls.send(:include, Kaminari::ActiveFedoraModelExtension) if kls.superclass == ::ActiveFedora::Base
+# end
+# end
+#
+# included do
+# # Existing subclasses pick up the model extension as well
+# self.descendants.each do |kls|
+# kls.send(:include, Kaminari::ActiveFedoraModelExtension) if kls.superclass == ::ActiveFedora::Base
+# end
+# end
+# end
+#
+ module ActiveFedoraModelExtension
+ extend ActiveSupport::Concern
+
+ included do
+ self.send(:include, Kaminari::ConfigurationMethods)
+
+ # Fetch the values at the specified page number
+ # Model.page(5)
+ eval <<-RUBY
+ def self.#{Kaminari.config.page_method_name}(num = nil)
+ limit(default_per_page).offset(default_per_page * ((num = num.to_i - 1) < 0 ? 0 : num)).extending do
+ include Kaminari::ActiveFedoraRelationMethods
+ include Kaminari::PageScopeMethods
+ end
+ end
+ RUBY
+ end
+ end
+
+ module ActiveFedoraRelationMethods
+ def entry_name
+ model_name.human.downcase
+ end
+
+ def reset #:nodoc:
+ @total_count = nil
+ super
+ end
+
+ def total_count(column_name = :all, options = {}) #:nodoc:
+ # #count overrides the #select which could include generated columns referenced in #order, so skip #order here, where it's irrelevant to the result anyway
+ @total_count ||= begin
+# c = except(:offset, :limit, :order)
+
+ # Remove includes only if they are irrelevant
+ # c = c.except(:includes) unless references_eager_loaded_tables?
+
+ # Rails 4.1 removes the `options` argument from AR::Relation#count
+ # args = [column_name]
+ args = []
+ args << options #if ActiveRecord::VERSION::STRING < '4.1.0'
+
+ # .group returns an OrderdHash that responds to #count
+ c = count(*args)
+ if c.is_a?(Hash) || c.is_a?(ActiveSupport::OrderedHash)
+ c.count
+ else
+ c.respond_to?(:count) ? c.count(*args) : c
+ end
+ end
+ end
+ end
+
+ module PageScopeMethods
+ # Specify the
per_page value for the preceding
page scope
+ # Model.page(3).per(10)
+ def per(num)
+ if (n = num.to_i) < 0 || !(/^\d/ =~ num.to_s)
+ self
+ elsif n.zero?
+ limit(n)
+ elsif Kaminari.config.max_per_page && Kaminari.config.max_per_page < n
+ limit(Kaminari.config.max_per_page).offset(offset_value / limit_value * Kaminari.config.max_per_page)
+ else
+ limit(n).offset(offset_value / limit_value * n)
+ end
+ end
+
+ # Total number of pages
+ def total_pages
+ count_without_padding = total_count
+ count_without_padding -= @_padding if defined?(@_padding) && @_padding
+ count_without_padding = 0 if count_without_padding < 0
+
+ total_pages_count = (count_without_padding.to_f / limit_value).ceil
+ if Kaminari.config.max_pages.present? && Kaminari.config.max_pages < total_pages_count
+ Kaminari.config.max_pages
+ else
+ total_pages_count
+ end
+ rescue FloatDomainError
+ raise ZeroPerPageOperation, "The number of total pages was incalculable. Perhaps you called .per(0)?"
+ end
+ end
+end
+
+ActiveFedora::Relation.class_eval do
+ include Kaminari::ConfigurationMethods
+
+ def page(num = nil)
+ limit(Kaminari.config.default_per_page).offset(Kaminari.config.default_per_page * ((num = num.to_i - 1) < 0 ? 0 : num)).extending do
+ include Kaminari::ActiveFedoraRelationMethods
+ include Kaminari::PageScopeMethods
+ end
+ end
+
+ delegate :to_json, to: :to_a
+end
diff --git a/config/initializers/uri_parser.rb b/config/initializers/uri_parser.rb
new file mode 100644
index 0000000000..c775522fbe
--- /dev/null
+++ b/config/initializers/uri_parser.rb
@@ -0,0 +1,12 @@
+require 'addressable/uri'
+
+class URI::Parser
+ def split url
+ begin
+ a = Addressable::URI::parse url
+ [a.scheme, a.userinfo, a.host, a.port, nil, a.path, nil, a.query, a.fragment]
+ rescue Addressable::URI::InvalidURIError => err
+ raise URI::InvalidURIError, err.message
+ end
+ end
+end
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 773c365f3d..03dbe12cc0 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -10,8 +10,8 @@ en:
release_label: "Release"
errors:
lti_auth_error: |
- If you are an instructor, you cannot view these items in student mode.
- Please switch back to instructor mode to view. If you are a student and
+ If you are an instructor, you cannot view these items in student mode.
+ Please switch back to instructor mode to view. If you are a student and
receiving this message, please contact us at
%s .
messages:
blank: "field is required."
@@ -23,16 +23,16 @@ en:
empty_share_section_permalink_notice: "After processing has started the section link will be available."
metadata_tip:
abstract: |
- Summary provides a space for describing the contents of the item. Examples
- include liner notes, contents list, or an opera scene abstract. This field
- is not meant for cataloger's descriptions but for descriptions that
- accompany the item. Depending on the length of the summary it may be
+ Summary provides a space for describing the contents of the item. Examples
+ include liner notes, contents list, or an opera scene abstract. This field
+ is not meant for cataloger's descriptions but for descriptions that
+ accompany the item. Depending on the length of the summary it may be
truncated in search results.
collection: |
- Collection defines the source or host of the item (for example, the source
- of the original record or the collection responsible for entering
- information about the item). Collections will be aggregated for browsing
- access. Examples: Variations, Indiana University Libraries Film Archive,
+ Collection defines the source or host of the item (for example, the source
+ of the original record or the collection responsible for entering
+ information about the item). Collections will be aggregated for browsing
+ access. Examples: Variations, Indiana University Libraries Film Archive,
IUCAT, NUcat.
contributor: |
Contributors are persons or bodies associated with the item but not considered
@@ -42,7 +42,7 @@ en:
possible, use the
Library of Congress Name Authority File .
creator: |
-
Required field. Main contributors are the primary persons
+ Main contributors are the primary persons
or bodies associated with the creation of the content. Main contributors
will be included in search results display and aggregated for browsing
access. At this time there is no ability to specify a main contributor as
@@ -100,13 +100,13 @@ en:
letters within the field will bring up permitted matches. Only terms or
codes from the
MARC Code List for Languages may be used. This is an
- optional field.
+ optional field.
terms_of_use: |
- Terms of Use describes the conditions under which content may be used. This is an optional field.
+ Terms of Use describes the conditions under which content may be used. This is an optional field.
physical_description: |
- Physical Description is an optional field that will display information about the original resource being described.
+ Physical Description is an optional field that will display information about the original resource being described.
related_item_url: |
- The label is the text that will be displayed in the item record and will link to the URL entered. Related Item is an optional field.
+ The label is the text that will be displayed in the item record and will link to the URL entered. Related Item is an optional field.
note: |
Note is used to describe aspects of the resource not accounted for in any of the other fields, such as creation or production credits, performers, venue, historical or biographical information, language details, awards given to the performance or the work performed. Recommended use is to provide a separate Contributor field for each person or body associated with the creation of the content and to use a Note to provide more information about such contributions or to provide information about secondary persons or bodies associated with the creation of the content. Type specifies the type of note and is used as a label in the user interface.
Statement of Responsibility is used to provide information about primary persons or bodies associated with the creation of the content, along with details about their roles. This information can be transcribed from the credits listed in the resource itself or on its packaging. Recommended use is to provide a separate Contributor field for each person or body listed in the Statement of Responsibility. Statement of Responsibility may be left empty if the use of Contributor fields alone is preferred. Statement of Responsibility is displayed in the user interface appended to the Title field, following a ' / '.
other_identifier: |
@@ -119,6 +119,8 @@ en:
The Section Label will be used in an item's listing to identify each file associated with the item.
permalink: |
The section Permalink is a URL that links to an individual section of an Item. In addition, the entire item will have a distinct permalink that can be assigned in the Resource Description page. If configured to do so, Avalon will automatically generate a permalink if this field is left blank.
+ datedigitized: |
+ The Date Digitized field reflects when the original media was initially digitized, not ingested into Avalon. This date automatically will be set to the date the ingest process completes unless a date is provided.
thumbnail: |
The Thumbnail is a still image that will be displayed in search results. Thumbnails can be grabbed from a video during playback by clicking the 'Create thumbnail' button in the player or by entering a time offset value here in the format 'hh:mm:ss.sss'.
skip_transcoding: |
@@ -130,29 +132,35 @@ en:
userlabel: "Avalon User"
grouplabel: "Avalon Group"
classlabel: "External Group"
+ ipaddresslabel: "IP Address or Range"
depositor: |
- Depositors add media to the collection and describe it with metadata.
- They can publish items but not unpublish. They can only modify or
+ Depositors add media to the collection and describe it with metadata.
+ They can publish items but not unpublish. They can only modify or
delete unpublished items.
editor: |
- Editors have supervisory responsibility for the collection building — the
- ingest and description process. They can assign depositor roles, change
- the name or description of the collection, and can modify the access
- controls for individual items in the collection. Editors can also do
+ Editors have supervisory responsibility for the collection building — the
+ ingest and description process. They can assign depositor roles, change
+ the name or description of the collection, and can modify the access
+ controls for individual items in the collection. Editors can also do
anything a depositor can do.
manager: |
- Managers have overall accountability for the collection. Managers can
- create collections and assign editor and depositor roles for those
- collections. They set the default access controls for items added to
- the collection, and they also step in when a published item needs
- revising or deleting. Managers can also do anything an editor or
+ Managers have overall accountability for the collection. Managers can
+ create collections and assign editor and depositor roles for those
+ collections. They set the default access controls for items added to
+ the collection, and they also step in when a published item needs
+ revising or deleting. Managers can also do anything an editor or
depositor can do.
user: |
- Enter a username to grant them access to an item or collection.
+ Enter a username to grant them access to an item or collection. Start and end dates are not required; if included, access for the specified user will start at the beginning of the start date and end at the beginning of the end date. Otherwise, access will be open ended for the specified user.
group: |
- Select an Avalon group from the dropdown list below to grant it access to an item or collection.
+ Select an Avalon group from the dropdown list below to grant it access to an item or collection. Start and end dates are not required; if included, access for the specified group will start at the beginning of the start date and end at the beginning of the end date. Otherwise, access will be open ended for the specified group.
class: |
- Enter an external group to grant it access to an item or collection. This group might represent students in a course or members of an institution-specific group.
+ Enter an external group to grant it access to an item or collection. This group might represent students in a course or members of an institution-specific group. Start and end dates are not required; if included, access for the specified group will start at the beginning of the start date and end at the beginning of the end date. Otherwise, access will be open ended for the specified group.
+ ipaddress: |
+ An IP Address or subnet. Eg. 255.0.1.10 or 255.0.1.10/21 or 255.0.1.10/255.255.255.0
+ or ffaa:aaff:bbcc:ddee:1122:3344:5566:7777
+ or ffaa:aaff:bbcc:ddee:1122:3344:5566:7777/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00
+ Start and end dates are not required; if included, access for the specified IP Address(es) will start at the beginning of the start date and end at the beginning of the end date. Otherwise, access will be open ended for the specified IP Address(es).
contact:
title: 'Contact Us - %{application_name}'
@@ -172,3 +180,26 @@ en:
title: '%{step_title} - %{media_object_title} - %{application_name}'
show:
title: '%{media_object_title} - %{application_name}'
+ player:
+ customError: 'Your browser requires Adobe Flash Player in order to play this item. For more information, see
Adobe Flash Player Help .'
+
+ playlist:
+ ago: "%{time} ago"
+ lockAltText: "This playlist is private."
+ unlockAltText: "This playlist is public."
+ lockText: "Private"
+ unlockText: "Public"
+ new:
+ action: "Create"
+ create:
+ action: "Create"
+ edit:
+ action: "Save Changes"
+ update:
+ action: "Save Changes"
+
+ activerecord:
+ attributes:
+ playlist:
+ title: "Name"
+ comment: "Description"
diff --git a/config/routes.rb b/config/routes.rb
index 38e8e6ccf0..f7604164d8 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,4 +1,5 @@
Avalon::Application.routes.draw do
+
mount BrowseEverything::Engine => '/browse'
# HydraHead.add_routes(self)
@@ -21,7 +22,7 @@
root :to => "catalog#index"
devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }, format: false
- devise_scope :user do
+ devise_scope :user do
match '/users/sign_in', :to => "users/sessions#new", :as => :new_user_session, via: [:get]
match '/users/sign_out', :to => "users/sessions#destroy", :as => :destroy_user_session, via: [:get]
end
@@ -31,8 +32,14 @@
match "/oembed", to: 'master_files#oembed', via: [:get]
match "object/:id", to: 'object#show', via: [:get], :as => :object
- resources :media_objects, except: [:create] do
+
+ resources :vocabulary, except: [:create, :destroy, :new, :edit]
+
+ resources :media_objects, except: [:create, :update] do
member do
+ put :update, action: :update, defaults: { format: 'html' }, constraints: { format: 'html' }
+ put :update, action: :json_update, constraints: { format: 'json' }
+ patch :update, action: :update, defaults: { format: 'html' }, constraints: { format: 'html' }
put :update_status
get :progress, :action => :show_progress
get 'content/:datastream', :action => :deliver_content, :as => :inspect
@@ -42,12 +49,11 @@
get :confirm_remove
end
collection do
+ post :create, action: :create, constraints: { format: 'json' }
get :confirm_remove
put :update_status
# 'delete' has special signifigance so use 'remove' for now
delete :remove, :action => :destroy
- get :confirm_reassign_collection
- put :reassign_collection
end
end
resources :master_files, except: [:new, :index, :update] do
@@ -60,21 +66,34 @@
post 'still', :to => 'master_files#set_frame', :defaults => { :format => 'html' }
get :embed
post 'attach_structure'
+ post 'attach_captions'
+ get :captions
end
end
match '/media_objects/:media_object_id/section/:id/embed' => 'master_files#embed', via: [:get]
resources :derivatives, only: [:create]
-
+ resources :playlists do
+ resources :playlist_items, path: 'items', only: [:create, :update]
+ member do
+ patch 'update_multiple'
+ delete 'update_multiple'
+ end
+ end
+
+ resources :avalon_annotation, only: [:create, :show, :update, :destroy]
+
resources :comments, only: [:index, :create]
+ resources :playlist_items, only: [:update], :constraints => {:format => /(js|json)/}
+
#match 'search/index' => 'search#index'
#match 'search/facet/:id' => 'search#facet'
namespace :admin do
- resources :groups, except: [:show] do
- collection do
+ resources :groups, except: [:show] do
+ collection do
put 'update_multiple'
end
member do
@@ -85,6 +104,7 @@
member do
get 'edit'
get 'remove'
+ get 'items'
end
end
end
@@ -96,7 +116,7 @@
end
mount AboutPage::Engine => '/about(.:format)', :as => 'about_page'
-
+
# The priority is based upon order of creation:
# first created -> highest priority.
diff --git a/db/migrate/20151201164326_add_date_digitized_to_master_file.rb b/db/migrate/20151201164326_add_date_digitized_to_master_file.rb
new file mode 100644
index 0000000000..b15fd7c76a
--- /dev/null
+++ b/db/migrate/20151201164326_add_date_digitized_to_master_file.rb
@@ -0,0 +1,19 @@
+class AddDateDigitizedToMasterFile < ActiveRecord::Migration
+
+ def up
+ MasterFile.find_each({},{batch_size:5}) do |mf|
+ encode = ActiveEncode::Base.find(mf.workflow_id)
+ next unless encode.present?
+ mf.date_digitized = encode.finished_at
+ mf.save(validate: false)
+ end
+ end
+
+ def down
+ MasterFile.find_each({},{batch_size:5}) do |mf|
+ mf.date_digitized = nil
+ mf.save(validate: false)
+ end
+ end
+
+end
diff --git a/db/migrate/20160105181819_derivative_add_managed.rb b/db/migrate/20160105181819_derivative_add_managed.rb
new file mode 100644
index 0000000000..a95697f833
--- /dev/null
+++ b/db/migrate/20160105181819_derivative_add_managed.rb
@@ -0,0 +1,21 @@
+class DerivativeAddManaged < ActiveRecord::Migration
+
+ def up
+ say_with_time("Add Derivative.managed") do
+ Derivative.find_each({},{batch_size:5}) do |d|
+ d.managed ||= true;
+ d.save_as_version('R5');
+ end
+ end
+ end
+
+ def down
+ say_with_time("Remove Derivative.managed") do
+ Derivative.find_each({},{batch_size:5}) do |d|
+ d.managed = nil;
+ d.save_as_version('R4');
+ end
+ end
+ end
+
+end
diff --git a/db/migrate/20160122203634_add_resource_types_to_display_metadata.rb b/db/migrate/20160122203634_add_resource_types_to_display_metadata.rb
new file mode 100644
index 0000000000..81476132c7
--- /dev/null
+++ b/db/migrate/20160122203634_add_resource_types_to_display_metadata.rb
@@ -0,0 +1,16 @@
+class AddResourceTypesToDisplayMetadata < ActiveRecord::Migration
+
+ def up
+ say_with_time("Add resource types to displaymetadata") do
+ MediaObject.find_each({},{batch_size:5}) do |mo|
+ mo.set_resource_types!
+ mo.save
+ end
+ end
+ end
+
+ def down
+ raise ActiveRecord::IrreversibleMigration
+ end
+
+end
diff --git a/db/migrate/20160427165749_create_playlists.rb b/db/migrate/20160427165749_create_playlists.rb
new file mode 100644
index 0000000000..68ca2bd45b
--- /dev/null
+++ b/db/migrate/20160427165749_create_playlists.rb
@@ -0,0 +1,12 @@
+class CreatePlaylists < ActiveRecord::Migration
+ def change
+ create_table :playlists do |t|
+ t.string :title
+ t.references :user, null: false, index: true
+ t.string :comment
+ t.string :visibility
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20160428133859_create_playlist_items.rb b/db/migrate/20160428133859_create_playlist_items.rb
new file mode 100644
index 0000000000..f3c76a39ea
--- /dev/null
+++ b/db/migrate/20160428133859_create_playlist_items.rb
@@ -0,0 +1,11 @@
+class CreatePlaylistItems < ActiveRecord::Migration
+ def change
+ create_table :playlist_items do |t|
+ t.references :playlist, null: false, index: true
+ t.references :annotation, null: false, index: true
+ t.integer :position
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20160511155417_create_annotations.active_annotations.rb b/db/migrate/20160511155417_create_annotations.active_annotations.rb
new file mode 100644
index 0000000000..863a45cafc
--- /dev/null
+++ b/db/migrate/20160511155417_create_annotations.active_annotations.rb
@@ -0,0 +1,10 @@
+# This migration comes from active_annotations (originally 20160422052041)
+class CreateAnnotations < ActiveRecord::Migration
+ def change
+ create_table :annotations do |t|
+ t.string :uuid
+ t.string :source_uri
+ t.text :annotation
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index da1831013e..a22603f6cd 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,13 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20150625121750) do
+ActiveRecord::Schema.define(version: 20160511155417) do
+
+ create_table "annotations", force: true do |t|
+ t.string "uuid"
+ t.string "source_uri"
+ t.text "annotation"
+ end
create_table "bookmarks", force: true do |t|
t.integer "user_id", null: false
@@ -66,6 +72,28 @@
t.string "name", limit: 50
end
+ create_table "playlist_items", force: true do |t|
+ t.integer "playlist_id", null: false
+ t.integer "annotation_id", null: false
+ t.integer "position"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
+ add_index "playlist_items", ["annotation_id"], name: "index_playlist_items_on_annotation_id"
+ add_index "playlist_items", ["playlist_id"], name: "index_playlist_items_on_playlist_id"
+
+ create_table "playlists", force: true do |t|
+ t.string "title"
+ t.integer "user_id", null: false
+ t.string "comment"
+ t.string "visibility"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
+ add_index "playlists", ["user_id"], name: "index_playlists_on_user_id"
+
create_table "role_maps", force: true do |t|
t.string "entry"
t.integer "parent_id"
@@ -82,8 +110,8 @@
add_index "searches", ["user_id"], name: "index_searches_on_user_id"
create_table "sessions", force: true do |t|
- t.string "session_id", null: false
- t.text "data"
+ t.string "session_id", null: false
+ t.text "data", limit: 16777215
t.datetime "created_at"
t.datetime "updated_at"
end
diff --git a/felix b/felix
index b34b990852..b5f720c64c 160000
--- a/felix
+++ b/felix
@@ -1 +1 @@
-Subproject commit b34b9908529bcb7b7b2db9833d7f3ea134d064d1
+Subproject commit b5f720c64cf73985a3ca7bfd22eda55e27496ea0
diff --git a/lib/avalon/batch/entry.rb b/lib/avalon/batch/entry.rb
index 8f25538889..549d414c96 100644
--- a/lib/avalon/batch/entry.rb
+++ b/lib/avalon/batch/entry.rb
@@ -67,6 +67,15 @@ def valid?
def file_valid?(file_spec)
valid = true
+ # Check date_digitized for valid format
+ if file_spec[:date_digitized].present?
+ begin
+ DateTime.parse(file_spec[:date_digitized])
+ rescue ArgumentError
+ @errors.add(:date_digitized, "Invalid date_digitized: #{file_spec[:date_digitized]}. Recommended format: yyyy-mm-dd.")
+ valid = false
+ end
+ end
# Check file offsets for valid format
if file_spec[:offset].present? && !Avalon::Batch::Entry.offset_valid?(file_spec[:offset])
@errors.add(:offset, "Invalid offset: #{file_spec[:offset]}")
@@ -110,11 +119,17 @@ def self.offset_valid?( offset )
true
end
- def self.attach_structure_to_master_file( master_file, filename )
+ def self.attach_datastreams_to_master_file( master_file, filename )
structural_file = "#{filename}.structure.xml"
if File.exists? structural_file
master_file.structuralMetadata.content=File.open(structural_file)
end
+ captions_file = "#{filename}.vtt"
+ if File.exists? captions_file
+ master_file.captions.content=File.open(captions_file)
+ master_file.captions.mimeType='text/vtt'
+ master_file.captions.dsLabel=captions_file
+ end
end
def process!
@@ -125,12 +140,13 @@ def process!
master_file.save(validate: false) #required: need pid before setting mediaobject
master_file.mediaobject = media_object
files = self.class.gatherFiles(file_spec[:file])
- self.class.attach_structure_to_master_file(master_file, file_spec[:file])
+ self.class.attach_datastreams_to_master_file(master_file, file_spec[:file])
master_file.setContent(files)
master_file.absolute_location = file_spec[:absolute_location] if file_spec[:absolute_location].present?
master_file.label = file_spec[:label] if file_spec[:label].present?
master_file.poster_offset = file_spec[:offset] if file_spec[:offset].present?
-
+ master_file.date_digitized = DateTime.parse(file_spec[:date_digitized]).to_time.utc.iso8601 if file_spec[:date_digitized].present?
+
#Make sure to set content before setting the workflow
master_file.set_workflow(file_spec[:skip_transcoding] ? 'skip_transcoding' : nil)
if master_file.save
diff --git a/lib/avalon/batch/manifest.rb b/lib/avalon/batch/manifest.rb
index 8ab8a39a5f..a5b27960b6 100644
--- a/lib/avalon/batch/manifest.rb
+++ b/lib/avalon/batch/manifest.rb
@@ -21,7 +21,7 @@ class Manifest
extend Forwardable
EXTENSIONS = ['csv','xls','xlsx','ods']
- FILE_FIELDS = [:file,:label,:offset,:skip_transcoding,:absolute_location]
+ FILE_FIELDS = [:file,:label,:offset,:skip_transcoding,:absolute_location,:date_digitized]
SKIP_FIELDS = [:collection]
def_delegators :@entries, :each
@@ -166,7 +166,6 @@ def create_entries!
opts[opt] = val
end
}
-
entries << Entry.new(fields.select { |f| !FILE_FIELDS.include?(f) }, content, opts, index, self)
end
end
diff --git a/lib/avalon/bib_retriever/MARC21slim2MODS3-5-avalon.xsl b/lib/avalon/bib_retriever/MARC21slim2MODS3-5-avalon.xsl
index 492373a640..e82ddc9040 100644
--- a/lib/avalon/bib_retriever/MARC21slim2MODS3-5-avalon.xsl
+++ b/lib/avalon/bib_retriever/MARC21slim2MODS3-5-avalon.xsl
@@ -46,6 +46,8 @@ Removed 041 subfields except for $d and $j. kdm 20150429
Replaced use of 003 value for recordInfo\recordIdentifer@source with "local". kdm 20150430
Added type="other" for 024 ind1=8. kdm 20150501
Removed identifiers except
to . kdm 20150514
+Added check that controlField008-35-37 variable is not set to 'N/A' from old cataloging practices. jlh 20151109
+Convert unknown dates (uuuu) to EDTF (unknown/unknown). bwk 20160223
-->
+
-
+
+
+ unknown/unknown
+
+
+
+
+
-
+
+
+ unknown/unknown
+
+
+
+
+
@@ -1118,7 +1135,14 @@ Revision 1.02 - Added Log Comment 2003/03/24 19:37:42 ckeith
-
+
+
+ unknown/unknown
+
+
+
+
+
@@ -1413,7 +1437,7 @@ Revision 1.02 - Added Log Comment 2003/03/24 19:37:42 ckeith
+ select="normalize-space(translate(substring($controlField008,36,3),'|#','')) != 'N/A'"/>
@@ -2095,11 +2119,11 @@ Revision 1.02 - Added Log Comment 2003/03/24 19:37:42 ckeith
-
+
id
- all_text_timv
+ title_tesi^10.0
+ section_label_tesim^5.0
+ creator_ssim^3.0
+ date_sim^3.0
+ all_text_timv^1.0
active_fedora_model_ssi
object_type_si
- all_text_timv^10
+ title_tesi^10.0
+ section_label_tesim^5.0
+ creator_ssim^3.0
+ date_sim^3.0
+ all_text_timv^1.0
diff --git a/spec/config/authentication.yml b/spec/config/authentication.yml
index 96fac0985f..7e67c70532 100644
--- a/spec/config/authentication.yml
+++ b/spec/config/authentication.yml
@@ -10,3 +10,9 @@
:params:
:oauth_credentials:
key: 'secret'
+- :name: Avalon JSON Api
+ :id: :api
+ :tokens:
+ 'secret_token':
+ :username: system_account_name
+ :email: system@example.edu
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index e0f770f458..812a9c6dfc 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -15,5 +15,23 @@
require 'spec_helper'
describe ApplicationController do
+ controller do
+ def create
+ render nothing: true
+ end
+ end
+ context "normal auth" do
+ it "should check for authenticity token" do
+ expect(controller).to receive(:verify_authenticity_token)
+ post :create
+ end
+ end
+ context "ingest API" do
+ it "should not check for authenticity token for API requests" do
+ request.headers['Avalon-Api-Key'] = 'secret_token'
+ expect(controller).not_to receive(:verify_authenticity_token)
+ post :create
+ end
+ end
end
diff --git a/spec/controllers/avalon_annotation_controller_spec.rb b/spec/controllers/avalon_annotation_controller_spec.rb
new file mode 100644
index 0000000000..51659eda06
--- /dev/null
+++ b/spec/controllers/avalon_annotation_controller_spec.rb
@@ -0,0 +1,111 @@
+# Copyright 2011-2015, The Trustees of Indiana University and Northwestern
+# University. Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+#
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed
+# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+# CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+# --- END LICENSE_HEADER BLOCK ---
+
+require 'spec_helper'
+
+describe AvalonAnnotationController do
+ subject(:video_master_file) { FactoryGirl.create(:master_file_with_derivative) }
+ let(:annotation) { AvalonAnnotation.new(master_file: video_master_file) }
+
+ before :all do
+ @controller = AvalonAnnotationController.new
+ end
+
+ describe 'creating an annotation and displaying it' do
+ it 'can create an annotation and display it as JSON' do
+ allow(MasterFile).to receive(:find).and_return(video_master_file)
+ post 'create', master_file: video_master_file.id
+ expect { JSON.parse(response.body) }.not_to raise_error
+ end
+ it 'raises an ArgumentError error when the master file is not supplied' do
+ expect { post 'create' }.to raise_error(ArgumentError, 'Master File Not Supplied')
+ end
+ it 'raises an error when the master fille cannot be found' do
+ expect { post 'create', master_file: 'OC' }.to raise_error(ActionController::RoutingError, 'Master File Not Found')
+ end
+ end
+ describe 'updating an annotation' do
+ it 'can update an annotation and display it as JSON' do
+ annotation.save!
+ put 'update', id: annotation.uuid, start_time: '60', end_time: '90', title: '30 Seconds of Fun', comment: 'Are we having fun yet?'
+ expect { JSON.parse(response.body) }.not_to raise_error
+ end
+ it 'raises an error when the annotation cannot be found' do
+ expect { put 'update', id: 'OC' }.to raise_error(ActionController::RoutingError, 'Annotation Not Found')
+ end
+ end
+
+ describe 'displaying an annotation' do
+ it 'can display an annotation as JSON' do
+ annotation.save!
+ get 'show', id: annotation.uuid
+ expect { JSON.parse(response.body) }.not_to raise_error
+ end
+ it 'raises an error when it cannot find the annotation' do
+ expect { get 'show', id: 'OC' }.to raise_error(ActionController::RoutingError, 'Annotation Not Found')
+ end
+ end
+ describe 'destroying an annotation' do
+ it 'can destroy an annotation and returns the result as JSON' do
+ annotation.save!
+ delete 'destroy', id: annotation.uuid
+ resp = JSON.parse(response.body)
+ expect(resp['action']).to match('destroy')
+ expect(resp['id']).to match(annotation.uuid)
+ expect(resp['success']).to be_truthy
+ end
+ it 'raises an error when the annotation is not found to destroy' do
+ expect { delete 'destroy', id: 'OC' }.to raise_error(ActionController::RoutingError, 'Annotation Not Found')
+ end
+ end
+
+ describe 'selected key updates' do
+ it 'updates multiple fields' do
+ annotation.save!
+ @controller.stub(:params).and_return(id: annotation.uuid, start_time: '17', end_time: '17', title: 'Detroit', comment: 'The founding')
+ @controller.lookup_annotation
+ expect(@controller.instance_variable_get(:@annotation)).to receive(:update).once
+ expect { @controller.selected_key_updates }.not_to raise_error
+ end
+ it 'does not update the annotation when no valid keys are passed' do
+ annotation.save!
+ @controller.stub(:params).and_return(id: annotation.uuid, s_time: '17', e_time: '17', name: 'Detroit', stuff: 'The founding')
+ @controller.lookup_annotation
+ expect(@controller.instance_variable_get(:@annotation)).not_to receive(:update)
+ expect { @controller.selected_key_updates }.not_to raise_error
+ end
+ end
+ describe 'looking up annotations' do
+ it 'raises an error when it cannot find an annotation' do
+ @controller.stub(:params).and_return(id: '1817')
+ expect { @controller.lookup_annotation }.to raise_error
+ end
+ it 'sets the annotation class variable when it finds an annotation' do
+ annotation.save!
+ @controller.stub(:params).and_return(id: annotation.uuid)
+ expect { @controller.lookup_annotation }.not_to raise_error
+ end
+ end
+ describe 'raising errors' do
+ it 'raises ActionController::RoutingError referencing annotation by default' do
+ expect { @controller.not_found }.to raise_error(ActionController::RoutingError, 'Annotation Not Found')
+ end
+ it 'raises ActionController::RoutingError referencing annotation when passed :annotation' do
+ expect { @controller.not_found(item: :annotation) }.to raise_error(ActionController::RoutingError, 'Annotation Not Found')
+ end
+ it 'raises ActionController::RoutingError referencing master_file when passed :master_file' do
+ expect { @controller.not_found(item: :master_file) }.to raise_error(ActionController::RoutingError, 'Master File Not Found')
+ end
+ end
+end
diff --git a/spec/controllers/bookmarks_controller_spec.rb b/spec/controllers/bookmarks_controller_spec.rb
index 2be114a55c..64eebdd246 100644
--- a/spec/controllers/bookmarks_controller_spec.rb
+++ b/spec/controllers/bookmarks_controller_spec.rb
@@ -150,6 +150,17 @@
expect(mo.read_users).to include 'cjcolvar'
end
end
+ it 'adds a time-based user to the selected items' do
+ post 'update_access_control', submit_add_user: 'Add', add_user_begin: Date.yesterday, add_user_end: Date.today, user: 'cjcolvar'
+ expect(flash[:success]).to eq( I18n.t("blacklight.update_access_control.success", count: 3))
+ media_objects.each do |mo|
+ mo.reload
+ expect(mo.governing_policies[1].read_users).to include 'cjcolvar'
+ expect(mo.governing_policies[1].begin_time).to eq DateTime.parse(Date.yesterday.to_s).utc.beginning_of_day.iso8601
+ expect(mo.governing_policies[1].end_time).to eq DateTime.parse(Date.today.to_s).utc.end_of_day.iso8601
+ end
+ end
+
it 'removes a user from the selected items' do
media_objects.each do |mo|
mo.read_users += ["john.doe"]
@@ -163,6 +174,19 @@
expect(mo.read_users).not_to include 'john.doe'
end
end
+ it 'removes a time-based user from the selected items' do
+ media_objects.each do |mo|
+ mo.governing_policies += [Lease.create(begin_time: Date.today-2.day, end_time: Date.yesterday, read_users: ['jane.doe'])]
+ mo.save
+ mo.reload
+ end
+ post 'update_access_control', submit_remove_user: 'Remove', user: 'john.doe'
+ expect(flash[:success]).to eq( I18n.t("blacklight.update_access_control.success", count: 3))
+ media_objects.each do |mo|
+ mo.reload
+ expect(mo.governing_policies.collect{|p|p.read_users}.flatten.uniq.compact).not_to include 'john.doe'
+ end
+ end
end
context 'groups' do
it 'adds a group to the selected items' do
@@ -173,6 +197,16 @@
expect(mo.read_groups).to include 'students'
end
end
+ it 'adds a time-based group to the selected items' do
+ post 'update_access_control', submit_add_group: 'Add', add_group_begin: Date.yesterday, add_group_end: Date.today, group: 'students'
+ expect(flash[:success]).to eq( I18n.t("blacklight.update_access_control.success", count: 3))
+ media_objects.each do |mo|
+ mo.reload
+ expect(mo.governing_policies[1].read_groups).to include 'students'
+ expect(mo.governing_policies[1].begin_time).to eq DateTime.parse(Date.yesterday.to_s).utc.beginning_of_day.iso8601
+ expect(mo.governing_policies[1].end_time).to eq DateTime.parse(Date.today.to_s).utc.end_of_day.iso8601
+ end
+ end
it 'removes a group from the selected items' do
media_objects.each do |mo|
mo.read_groups += ["test-group"]
@@ -186,6 +220,19 @@
expect(mo.read_groups).not_to include 'test-group'
end
end
+ it 'removes a time-based group from the selected items' do
+ media_objects.each do |mo|
+ mo.governing_policies += [Lease.create(begin_time: Date.today-2.day, end_time: Date.yesterday, read_groups: ['test-group'])]
+ mo.save
+ mo.reload
+ end
+ post 'update_access_control', submit_remove_group: 'Remove', group: 'test-group'
+ expect(flash[:success]).to eq( I18n.t("blacklight.update_access_control.success", count: 3))
+ media_objects.each do |mo|
+ mo.reload
+ expect(mo.governing_policies.collect{|p|p.read_groups}.flatten.uniq.compact).not_to include 'test-group'
+ end
+ end
end
context 'external groups' do
it 'adds an external group to the selected items' do
@@ -196,6 +243,16 @@
expect(mo.read_groups).to include 'ECON-101'
end
end
+ it 'adds a time-based external group to the selected items' do
+ post 'update_access_control', submit_add_class: 'Add', add_class_begin: Date.yesterday, add_class_end: Date.today, class: 'ECON-101'
+ expect(flash[:success]).to eq( I18n.t("blacklight.update_access_control.success", count: 3))
+ media_objects.each do |mo|
+ mo.reload
+ expect(mo.governing_policies[1].read_groups).to include 'ECON-101'
+ expect(mo.governing_policies[1].begin_time).to eq DateTime.parse(Date.yesterday.to_s).utc.beginning_of_day.iso8601
+ expect(mo.governing_policies[1].end_time).to eq DateTime.parse(Date.today.to_s).utc.end_of_day.iso8601
+ end
+ end
it 'removes an external group from the selected items' do
media_objects.each do |mo|
mo.read_groups += ["MUSIC-101"]
@@ -209,6 +266,65 @@
expect(mo.read_groups).not_to include 'MUSIC-101'
end
end
+ it 'removes a time-based external group from the selected items' do
+ media_objects.each do |mo|
+ mo.governing_policies += [Lease.create(begin_time: Date.today-2.day, end_time: Date.yesterday, read_groups: ['MUSIC-101'])]
+ mo.save
+ mo.reload
+ end
+ post 'update_access_control', submit_remove_class: 'Remove', class: 'MUSIC-101'
+ expect(flash[:success]).to eq( I18n.t("blacklight.update_access_control.success", count: 3))
+ media_objects.each do |mo|
+ mo.reload
+ expect(mo.governing_policies.collect{|p|p.read_groups}.flatten.uniq.compact).not_to include 'MUSIC-101'
+ end
+ end
+ end
+ context 'ip groups' do
+ it 'adds an ip group to the selected items' do
+ post 'update_access_control', submit_add_ipaddress: 'Add', ipaddress: '127.0.0.127'
+ expect(flash[:success]).to eq( I18n.t("blacklight.update_access_control.success", count: 3))
+ media_objects.each do |mo|
+ mo.reload
+ expect(mo.read_groups).to include '127.0.0.127'
+ end
+ end
+ it 'adds a time-based ip group to the selected items' do
+ post 'update_access_control', submit_add_ipaddress: 'Add', add_ipaddress_begin: Date.yesterday, add_ipaddress_end: Date.today, ipaddress: '127.0.0.127'
+ expect(flash[:success]).to eq( I18n.t("blacklight.update_access_control.success", count: 3))
+ media_objects.each do |mo|
+ mo.reload
+ expect(mo.governing_policies[1].read_groups).to include '127.0.0.127'
+ expect(mo.governing_policies[1].begin_time).to eq DateTime.parse(Date.yesterday.to_s).utc.beginning_of_day.iso8601
+ expect(mo.governing_policies[1].end_time).to eq DateTime.parse(Date.today.to_s).utc.end_of_day.iso8601
+ end
+ end
+ it 'removes an ip group from the selected items' do
+ media_objects.each do |mo|
+ mo.read_groups += ["127.0.0.127"]
+ mo.save
+ mo.reload
+ end
+ post 'update_access_control', submit_remove_ipaddress: 'Remove', ipaddress: '127.0.0.127'
+ expect(flash[:success]).to eq( I18n.t("blacklight.update_access_control.success", count: 3))
+ media_objects.each do |mo|
+ mo.reload
+ expect(mo.read_groups).not_to include '127.0.0.127'
+ end
+ end
+ it 'removes a time-based ip group from the selected items' do
+ media_objects.each do |mo|
+ mo.governing_policies += [Lease.create(begin_time: Date.today-2.day, end_time: Date.yesterday, read_groups: ['127.0.0.127'])]
+ mo.save
+ mo.reload
+ end
+ post 'update_access_control', submit_remove_ipaddress: 'Remove', ipaddress: '127.0.0.127'
+ expect(flash[:success]).to eq( I18n.t("blacklight.update_access_control.success", count: 3))
+ media_objects.each do |mo|
+ mo.reload
+ expect(mo.governing_policies.collect{|p|p.read_groups}.flatten.uniq.compact).not_to include '127.0.0.127'
+ end
+ end
end
end
end
diff --git a/spec/controllers/catalog_controller_spec.rb b/spec/controllers/catalog_controller_spec.rb
index 5e16ae43ca..dd8e85e6e5 100644
--- a/spec/controllers/catalog_controller_spec.rb
+++ b/spec/controllers/catalog_controller_spec.rb
@@ -20,24 +20,24 @@
it "should show results for items that are public and published" do
mo = FactoryGirl.create(:published_media_object, visibility: 'public')
get 'index', :q => ""
- response.should be_success
- response.should render_template('catalog/index')
- assigns(:document_list).count.should eql(1)
- assigns(:document_list).map(&:id).should == [mo.id]
+ expect(response).to be_success
+ expect(response).to render_template('catalog/index')
+ expect(assigns(:document_list).count).to eql(1)
+ expect(assigns(:document_list).map(&:id)).to eq([mo.id])
end
it "should not show results for items that are not public" do
mo = FactoryGirl.create(:published_media_object, visibility: 'restricted')
get 'index', :q => ""
- response.should be_success
- response.should render_template('catalog/index')
- assigns(:document_list).count.should eql(0)
+ expect(response).to be_success
+ expect(response).to render_template('catalog/index')
+ expect(assigns(:document_list).count).to eql(0)
end
it "should not show results for items that are not published" do
mo = FactoryGirl.create(:media_object, visibility: 'public')
get 'index', :q => ""
- response.should be_success
- response.should render_template('catalog/index')
- assigns(:document_list).count.should eql(0)
+ expect(response).to be_success
+ expect(response).to render_template('catalog/index')
+ expect(assigns(:document_list).count).to eql(0)
end
end
describe "as an authenticated user" do
@@ -47,24 +47,24 @@
it "should show results for items that are published and available to registered users" do
mo = FactoryGirl.create(:published_media_object, visibility: 'restricted')
get 'index', :q => ""
- response.should be_success
- response.should render_template('catalog/index')
- assigns(:document_list).count.should eql(1)
- assigns(:document_list).map(&:id).should == [mo.id]
+ expect(response).to be_success
+ expect(response).to render_template('catalog/index')
+ expect(assigns(:document_list).count).to eql(1)
+ expect(assigns(:document_list).map(&:id)).to eq([mo.id])
end
it "should not show results for items that are not public or available to registered users" do
mo = FactoryGirl.create(:published_media_object, visibility: 'private')
get 'index', :q => ""
- response.should be_success
- response.should render_template('catalog/index')
- assigns(:document_list).count.should eql(0)
+ expect(response).to be_success
+ expect(response).to render_template('catalog/index')
+ expect(assigns(:document_list).count).to eql(0)
end
it "should not show results for items that are not published" do
mo = FactoryGirl.create(:media_object, visibility: 'public')
get 'index', :q => ""
- response.should be_success
- response.should render_template('catalog/index')
- assigns(:document_list).count.should eql(0)
+ expect(response).to be_success
+ expect(response).to render_template('catalog/index')
+ expect(assigns(:document_list).count).to eql(0)
end
end
describe "as a manager" do
@@ -74,47 +74,106 @@
it "should show results for items that are unpublished, private, and belong to one of my collections" do
mo = FactoryGirl.create(:media_object, visibility: 'private', collection: collection)
get 'index', :q => ""
- response.should be_success
- response.should render_template('catalog/index')
- assigns(:document_list).count.should eql(1)
- assigns(:document_list).map(&:id).should == [mo.id]
+ expect(response).to be_success
+ expect(response).to render_template('catalog/index')
+ expect(assigns(:document_list).count).to eql(1)
+ expect(assigns(:document_list).map(&:id)).to eq([mo.id])
+ end
+ it "should show results for items that are hidden and belong to one of my collections" do
+ mo = FactoryGirl.create(:media_object, hidden: true, visibility: 'private', collection: collection)
+ get 'index', :q => ""
+ expect(response).to be_success
+ expect(response).to render_template('catalog/index')
+ expect(assigns(:document_list).count).to eql(1)
+ expect(assigns(:document_list).map(&:id)).to eq([mo.id])
+ end
+ it "should show results for items that are not hidden and do not belong to one of my collections along with hidden items that belong to my collections" do
+ mo = FactoryGirl.create(:media_object, hidden: true, visibility: 'private', collection: collection)
+ mo2 = FactoryGirl.create(:fully_searchable_media_object)
+ get 'index', :q => ""
+ expect(response).to be_success
+ expect(response).to render_template('catalog/index')
+ expect(assigns(:document_list).count).to eql(2)
+ expect(assigns(:document_list).map(&:id)).to match_array([mo.id, mo2.id])
end
it "should not show results for items that do not belong to one of my collections" do
mo = FactoryGirl.create(:media_object, visibility: 'private')
get 'index', :q => ""
- response.should be_success
- response.should render_template('catalog/index')
- assigns(:document_list).count.should eql(0)
+ expect(response).to be_success
+ expect(response).to render_template('catalog/index')
+ expect(assigns(:document_list).count).to eql(0)
+ end
+ it "should not show results for hidden items that do not belong to one of my collections" do
+ mo = FactoryGirl.create(:media_object, hidden: true, visibility: 'private', read_users: [manager.username])
+ get 'index', :q => ""
+ expect(response).to be_success
+ expect(response).to render_template('catalog/index')
+ expect(assigns(:document_list).count).to eql(0)
end
end
- describe "as an administrator" do
- let!(:administrator) {login_as(:administrator)}
+ describe "as an administrator" do
+ let!(:administrator) {login_as(:administrator)}
- it "should show results for all items" do
+ it "should show results for all items" do
mo = FactoryGirl.create(:media_object, visibility: 'private')
get 'index', :q => ""
- response.should be_success
- response.should render_template('catalog/index')
- assigns(:document_list).count.should eql(1)
- assigns(:document_list).map(&:id).should == [mo.id]
+ expect(response).to be_success
+ expect(response).to render_template('catalog/index')
+ expect(assigns(:document_list).count).to eql(1)
+ expect(assigns(:document_list).map(&:id)).to eq([mo.id])
end
- end
+ end
describe "as an lti user" do
let!(:user) { login_lti 'student' }
let!(:lti_group) { @controller.user_session[:virtual_groups].first }
it "should show results for items visible to the lti virtual group" do
mo = FactoryGirl.create(:published_media_object, visibility: 'private', read_groups: [lti_group])
get 'index', :q => "read_access_virtual_group_ssim:#{lti_group}"
- response.should be_success
- response.should render_template('catalog/index')
- assigns(:document_list).count.should eql(1)
- assigns(:document_list).map(&:id).should == [mo.id]
+ expect(response).to be_success
+ expect(response).to render_template('catalog/index')
+ expect(assigns(:document_list).count).to eql(1)
+ expect(assigns(:document_list).map(&:id)).to eq([mo.id])
+ end
+ end
+
+ describe "as an unauthenticated user with a specific IP address" do
+ before(:each) do
+ @user = login_as 'public'
+ @ip_address1 = Faker::Internet.ip_v4_address
+ @mo = FactoryGirl.create(:published_media_object, visibility: 'private', read_groups: [@ip_address1])
+ end
+ it "should show no results when no items are visible to the user's IP address" do
+ get 'index', :q => ""
+ expect(assigns(:document_list).count).to eq 0
+ end
+ it "should show results for items visible to the the user's IP address" do
+ allow_any_instance_of(ActionDispatch::Request).to receive(:remote_ip).and_return(@ip_address1)
+ get 'index', :q => ""
+ expect(assigns(:document_list).count).to eq 1
+ expect(assigns(:document_list).map(&:id)).to include @mo.id
+ end
+ it "should show results for items visible to the the user's IPv4 subnet" do
+ ip_address2 = Faker::Internet.ip_v4_address
+ allow_any_instance_of(ActionDispatch::Request).to receive(:remote_ip).and_return(ip_address2)
+ mo2 = FactoryGirl.create(:published_media_object, visibility: 'private', read_groups: [ip_address2+'/30'])
+ get 'index', :q => ""
+ expect(assigns(:document_list).count).to be >= 1
+ expect(assigns(:document_list).map(&:id)).to include mo2.id
+ end
+ it "should show results for items visible to the the user's IPv6 subnet" do
+ ip_address3 = Faker::Internet.ip_v6_address
+ allow_any_instance_of(ActionDispatch::Request).to receive(:remote_ip).and_return(ip_address3)
+ mo3 = FactoryGirl.create(:published_media_object, visibility: 'private', read_groups: [ip_address3+'/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00'])
+ get 'index', :q => ""
+ expect(assigns(:document_list).count).to be >= 1
+ expect(assigns(:document_list).map(&:id)).to include mo3.id
end
end
describe "search fields" do
let(:media_object) { FactoryGirl.create(:fully_searchable_media_object) }
- ["title_tesi", "creator_ssim", "contributor_sim", "unit_ssim", "collection_ssim", "summary_ssi", "publisher_sim", "subject_topic_sim", "subject_geographic_sim", "subject_temporal_sim", "genre_sim", "physical_description_si", "language_sim", "date_sim", "notes_sim", "table_of_contents_sim", "other_identifier_sim" ].each do |field|
+# ["title_tesi", "creator_ssim", "contributor_sim", "unit_ssim", "collection_ssim", "summary_ssi", "publisher_sim", "subject_topic_sim", "subject_geographic_sim", "subject_temporal_sim", "genre_sim", "physical_description_sim", "language_sim", "date_sim", "notes_sim", "table_of_contents_sim", "other_identifier_sim", "date_ingested_sim" ].each do |field|
+ ["title_tesi", "creator_ssim", "contributor_sim", "unit_ssim", "collection_ssim", "summary_ssi", "publisher_sim", "subject_topic_sim", "subject_geographic_sim", "subject_temporal_sim", "genre_sim", "physical_description_sim", "language_sim", "date_sim", "notes_sim", "table_of_contents_sim", "other_identifier_sim" ].each do |field|
it "should find results based upon #{field}" do
query = Array(media_object.to_solr[field]).first
#split on ' ' and only search on the first word of a multiword field value
@@ -128,6 +187,30 @@
end
end
end
+ describe "search structure" do
+ before(:each) do
+ @media_object = FactoryGirl.create(:fully_searchable_media_object)
+ @master_file = FactoryGirl.create(:master_file_with_structure, mediaobject_id: @media_object.pid, label: 'Test Label')
+ @media_object.parts += [@master_file]
+ @media_object.save!
+ end
+ it "should find results based upon structure" do
+ get 'index', q: 'CD 1'
+ expect(assigns(:document_list).count).to eq 1
+ expect(assigns(:document_list).collect(&:id)). to eq [@media_object.id]
+ end
+ it 'should find results based upon section labels' do
+ get 'index', q: 'Test Label'
+ expect(assigns(:document_list).count).to eq 1
+ expect(assigns(:document_list).collect(&:id)). to eq [@media_object.id]
+ end
+ it 'should find items in correct relevancy order' do
+ media_object_1 = FactoryGirl.create(:fully_searchable_media_object, title: 'Test Label')
+ get 'index', q: 'Test Label'
+ expect(assigns(:document_list).count).to eq 2
+ expect(assigns(:document_list).collect(&:id)).to eq [media_object_1.id, @media_object.id]
+ end
+ end
describe "sort fields" do
let!(:m1) { FactoryGirl.create(:published_media_object, title: 'Yabba', date_issued: '1960', creator: ['Fred'], visibility: 'public') }
diff --git a/spec/controllers/collections_controller_spec.rb b/spec/controllers/collections_controller_spec.rb
index 343a30ca05..3cc041c7e4 100644
--- a/spec/controllers/collections_controller_spec.rb
+++ b/spec/controllers/collections_controller_spec.rb
@@ -17,38 +17,78 @@
describe Admin::CollectionsController, type: :controller do
render_views
+ describe 'security' do
+ let(:collection) { FactoryGirl.create(:collection) }
+ describe 'ingest api' do
+ it "all routes should return 401 when no token is present" do
+ expect(get :index, format: 'json').to have_http_status(401)
+ expect(get :show, id: collection.id, format: 'json').to have_http_status(401)
+ expect(get :items, id: collection.id, format: 'json').to have_http_status(401)
+ expect(post :create, format: 'json').to have_http_status(401)
+ expect(put :update, id: collection.id, format: 'json').to have_http_status(401)
+ end
+ it "all routes should return 403 when a bad token in present" do
+ request.headers['Avalon-Api-Key'] = 'badtoken'
+ expect(get :index, format: 'json').to have_http_status(403)
+ expect(get :show, id: collection.id, format: 'json').to have_http_status(403)
+ expect(get :items, id: collection.id, format: 'json').to have_http_status(403)
+ expect(post :create, format: 'json').to have_http_status(403)
+ expect(put :update, id: collection.id, format: 'json').to have_http_status(403)
+ end
+ end
+ describe 'normal auth' do
+ context 'with end-user' do
+ before do
+ login_as :user
+ end
+ #New is isolated here due to issues caused by the controller instance not being regenerated
+ it "should redirect to /" do
+ expect(get :new).to redirect_to(root_path)
+ end
+ it "all routes should redirect to /" do
+ expect(get :index).to redirect_to(root_path)
+ expect(get :show, id: collection.id).to redirect_to(root_path)
+ expect(get :edit, id: collection.id).to redirect_to(root_path)
+ expect(get :remove, id: collection.id).to redirect_to(root_path)
+ expect(post :create).to redirect_to(root_path)
+ expect(put :update, id: collection.id).to redirect_to(root_path)
+ expect(patch :update, id: collection.id).to redirect_to(root_path)
+ expect(delete :destroy, id: collection.id).to redirect_to(root_path)
+ end
+ end
+ end
+ end
+
describe "#manage" do
let!(:collection) { FactoryGirl.create(:collection) }
before(:each) do
request.env["HTTP_REFERER"] = '/'
+ login_as(:administrator)
end
it "should add users to manager role" do
- login_as(:administrator)
manager = FactoryGirl.create(:manager)
put 'update', id: collection.id, submit_add_manager: 'Add', add_manager: manager.username
collection.reload
- manager.should be_in(collection.managers)
+ expect(manager).to be_in(collection.managers)
end
it "should not add users to manager role" do
- login_as(:administrator)
user = FactoryGirl.create(:user)
put 'update', id: collection.id, submit_add_manager: 'Add', add_manager: user.username
collection.reload
- user.should_not be_in(collection.managers)
- flash[:notice].should_not be_empty
+ expect(user).not_to be_in(collection.managers)
+ expect(flash[:notice]).not_to be_empty
end
it "should remove users from manager role" do
- login_as(:administrator)
#initial_manager = FactoryGirl.create(:manager).username
collection.managers += [FactoryGirl.create(:manager).username]
collection.save!
manager = User.where(username: collection.managers.first).first
put 'update', id: collection.id, remove_manager: manager.username
collection.reload
- manager.should_not be_in(collection.managers)
+ expect(manager).not_to be_in(collection.managers)
end
end
@@ -63,7 +103,7 @@
editor = FactoryGirl.build(:user)
put 'update', id: collection.id, submit_add_editor: 'Add', add_editor: editor.username
collection.reload
- editor.should be_in(collection.editors)
+ expect(editor).to be_in(collection.editors)
end
it "should remove users from editor role" do
@@ -71,7 +111,7 @@
editor = User.where(username: collection.editors.first).first
put 'update', id: collection.id, remove_editor: editor.username
collection.reload
- editor.should_not be_in(collection.editors)
+ expect(editor).not_to be_in(collection.editors)
end
end
@@ -86,7 +126,7 @@
depositor = FactoryGirl.build(:user)
put 'update', id: collection.id, submit_add_depositor: 'Add', add_depositor: depositor.username
collection.reload
- depositor.should be_in(collection.depositors)
+ expect(depositor).to be_in(collection.depositors)
end
it "should remove users from depositor role" do
@@ -94,7 +134,48 @@
depositor = User.where(username: collection.depositors.first).first
put 'update', id: collection.id, remove_depositor: depositor.username
collection.reload
- depositor.should_not be_in(collection.depositors)
+ expect(depositor).not_to be_in(collection.depositors)
+ end
+ end
+
+ describe "#index" do
+ let!(:collection) { FactoryGirl.create(:collection) }
+ subject(:json) { JSON.parse(response.body) }
+
+ it "should return list of collections" do
+ request.headers['Avalon-Api-Key'] = 'secret_token'
+ get 'index', format:'json'
+ expect(json.count).to eq(1)
+ expect(json.first['id']).to eq(collection.pid)
+ expect(json.first['name']).to eq(collection.name)
+ expect(json.first['unit']).to eq(collection.unit)
+ expect(json.first['description']).to eq(collection.description)
+ expect(json.first['object_count']['total']).to eq(collection.media_objects.count)
+ expect(json.first['object_count']['published']).to eq(collection.media_objects.reject{|mo| !mo.published?}.count)
+ expect(json.first['object_count']['unpublished']).to eq(collection.media_objects.reject{|mo| mo.published?}.count)
+ expect(json.first['roles']['managers']).to eq(collection.managers)
+ expect(json.first['roles']['editors']).to eq(collection.editors)
+ expect(json.first['roles']['depositors']).to eq(collection.depositors)
+ end
+ end
+
+ describe 'pagination' do
+ subject(:json) { JSON.parse(response.body) }
+ it 'should paginate index' do
+ 5.times { FactoryGirl.create(:collection) }
+ request.headers['Avalon-Api-Key'] = 'secret_token'
+ get 'index', format:'json', per_page: '2'
+ expect(json.count).to eq(2)
+ expect(response.headers['Per-Page']).to eq('2')
+ expect(response.headers['Total']).to eq('5')
+ end
+ it 'should paginate collection/items' do
+ collection = FactoryGirl.create(:collection, items: 5)
+ request.headers['Avalon-Api-Key'] = 'secret_token'
+ get 'items', id: collection.pid, format: 'json', per_page: '2'
+ expect(json.count).to eq(2)
+ expect(response.headers['Per-Page']).to eq('2')
+ expect(response.headers['Total']).to eq('5')
end
end
@@ -104,43 +185,112 @@
it "should allow access to managers" do
login_user(collection.managers.first)
get 'show', id: collection.id
- response.should be_ok
+ expect(response).to be_ok
end
- it "should redirect to collections index when manager doesn't have access" do
- login_as(:manager)
- get 'show', id: collection.id
- response.should redirect_to(admin_collections_path)
+ context "with json format" do
+ subject(:json) { JSON.parse(response.body) }
+
+ it "should return json for specific collection" do
+ request.headers['Avalon-Api-Key'] = 'secret_token'
+ get 'show', id: collection.pid, format:'json'
+ expect(json['id']).to eq(collection.pid)
+ expect(json['name']).to eq(collection.name)
+ expect(json['unit']).to eq(collection.unit)
+ expect(json['description']).to eq(collection.description)
+ expect(json['object_count']['total']).to eq(collection.media_objects.count)
+ expect(json['object_count']['published']).to eq(collection.media_objects.reject{|mo| !mo.published?}.count)
+ expect(json['object_count']['unpublished']).to eq(collection.media_objects.reject{|mo| mo.published?}.count)
+ expect(json['roles']['managers']).to eq(collection.managers)
+ expect(json['roles']['editors']).to eq(collection.editors)
+ expect(json['roles']['depositors']).to eq(collection.depositors)
+ end
+
+ it "should return 404 if requested collection not present" do
+ request.headers['Avalon-Api-Key'] = 'secret_token'
+ get 'show', id: 'avalon:doesnt_exist', format: 'json'
+ expect(response.status).to eq(404)
+ expect(JSON.parse(response.body)["errors"].class).to eq Array
+ expect(JSON.parse(response.body)["errors"].first.class).to eq String
+ end
+ end
+ end
+
+ describe "#items" do
+ let!(:collection) { FactoryGirl.create(:collection, items: 2) }
+
+ it "should return json for specific collection's media objects" do
+ request.headers['Avalon-Api-Key'] = 'secret_token'
+ get 'items', id: collection.pid, format: 'json'
+ expect(JSON.parse(response.body)).to include(collection.media_objects[0].pid,collection.media_objects[1].pid)
+ #TODO add check that mediaobject is serialized to json properly
end
+
end
describe "#create" do
+ let!(:collection) { FactoryGirl.build(:collection) }
+
it "should notify administrators" do
- login_as(:administrator) #Login as admin so there will be at least one administrator to get an email
+ login_as(:administrator) #otherwise, there are no administrators to mail
mock_delay = double('mock_delay').as_null_object
- NotificationsMailer.stub(:delay).and_return(mock_delay)
- mock_delay.should_receive(:new_collection)
- @collection = FactoryGirl.build(:collection)
- post 'create', admin_collection: {name: @collection.name, description: @collection.description, unit: @collection.unit}
+ allow(NotificationsMailer).to receive(:delay).and_return(mock_delay)
+ expect(mock_delay).to receive(:new_collection)
+ request.headers['Avalon-Api-Key'] = 'secret_token'
+ post 'create', format:'json', admin_collection: {name: collection.name, description: collection.description, unit: collection.unit, managers: collection.managers}
end
+ it "should create a new collection" do
+ request.headers['Avalon-Api-Key'] = 'secret_token'
+ post 'create', format:'json', admin_collection: {name: collection.name, description: collection.description, unit: collection.unit, managers: collection.managers}
+ expect(JSON.parse(response.body)['id'].class).to eq String
+ expect(JSON.parse(response.body)).not_to include('errors')
+ end
+ it "should return 422 if collection creation failed" do
+ request.headers['Avalon-Api-Key'] = 'secret_token'
+ post 'create', format:'json', admin_collection: {name: collection.name, description: collection.description, unit: collection.unit}
+ expect(response.status).to eq(422)
+ expect(JSON.parse(response.body)).to include('errors')
+ expect(JSON.parse(response.body)["errors"].class).to eq Array
+ expect(JSON.parse(response.body)["errors"].first.class).to eq String
+ end
+
end
describe "#update" do
it "should notify administrators if name changed" do
- login_as(:administrator) #Login as admin so there will be at least one administrator to get an email
+ login_as(:administrator) #otherwise, there are no administrators to mail
mock_delay = double('mock_delay').as_null_object
- NotificationsMailer.stub(:delay).and_return(mock_delay)
- mock_delay.should_receive(:update_collection)
+ allow(NotificationsMailer).to receive(:delay).and_return(mock_delay)
+ expect(mock_delay).to receive(:update_collection)
@collection = FactoryGirl.create(:collection)
put 'update', id: @collection.pid, admin_collection: {name: "#{@collection.name}-new", description: @collection.description, unit: @collection.unit}
end
- context "access controls" do
+ context "update REST API" do
let!(:collection) { FactoryGirl.create(:collection)}
- before(:each) do
- login_as(:administrator)
- end
+ it "should update a collection via API" do
+ old_description = collection.description
+ request.headers['Avalon-Api-Key'] = 'secret_token'
+ put 'update', format: 'json', id: collection.pid, admin_collection: {description: collection.description+'new'}
+ expect(JSON.parse(response.body)['id'].class).to eq String
+ expect(JSON.parse(response.body)).not_to include('errors')
+ collection.reload
+ expect(collection.description).to eq old_description+'new'
+ end
+ it "should return 422 if collection update via API failed" do
+ allow_any_instance_of(Admin::Collection).to receive(:save).and_return false
+ request.headers['Avalon-Api-Key'] = 'secret_token'
+ put 'update', format: 'json', id: collection.pid, admin_collection: {description: collection.description+'new'}
+ expect(response.status).to eq(422)
+ expect(JSON.parse(response.body)).to include('errors')
+ expect(JSON.parse(response.body)["errors"].class).to eq Array
+ expect(JSON.parse(response.body)["errors"].first.class).to eq String
+ end
+ end
+
+ context "access controls" do
+ let!(:collection) { FactoryGirl.create(:collection)}
it "should not allow empty user" do
expect{ put 'update', id: collection.pid, submit_add_user: "Add", add_user: "", add_user_display: ""}.not_to change{ collection.reload.default_read_users.size }
diff --git a/spec/controllers/dropbox_controller_spec.rb b/spec/controllers/dropbox_controller_spec.rb
index acf7be61f2..e35f31c90a 100644
--- a/spec/controllers/dropbox_controller_spec.rb
+++ b/spec/controllers/dropbox_controller_spec.rb
@@ -26,18 +26,18 @@
@collection = FactoryGirl.create(:collection)
@temp_files = (0..20).map{|index| { name: "a_movie_#{index}.mov" } }
@dropbox = double(Avalon::Dropbox)
- @dropbox.stub(:all).and_return @temp_files
- Avalon::Dropbox.stub(:new).and_return(@dropbox)
+ allow(@dropbox).to receive(:all).and_return @temp_files
+ allow(Avalon::Dropbox).to receive(:new).and_return(@dropbox)
end
it 'deletes video/audio files' do
- @dropbox.should_receive(:delete).exactly(@temp_files.count).times
+ expect(@dropbox).to receive(:delete).exactly(@temp_files.count).times
delete :bulk_delete, { :collection_id => @collection.pid, :filenames => @temp_files.map{|f| f[:name] } }
end
it "should allow the collection manager to delete" do
login_user @collection.managers.first
- @dropbox.should_receive(:delete).exactly(@temp_files.count).times
+ expect(@dropbox).to receive(:delete).exactly(@temp_files.count).times
delete :bulk_delete, {:collection_id => @collection.pid, :filenames => @temp_files.map{|f| f[:name]}}
expect(response.status).to be(200)
end
diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb
index b9267987a2..7b3365748c 100644
--- a/spec/controllers/groups_controller_spec.rb
+++ b/spec/controllers/groups_controller_spec.rb
@@ -23,8 +23,8 @@
describe "creating a new group" do
it "should redirect to sign in page with a notice when unauthenticated" do
expect { get 'new' }.not_to change { Admin::Group.all.count }
- flash[:notice].should_not be_nil
- response.should redirect_to(new_user_session_path)
+ expect(flash[:notice]).not_to be_nil
+ expect(response).to redirect_to(new_user_session_path)
end
it "should redirect to home page with a notice when authenticated but unauthorized" do
@@ -38,8 +38,8 @@
group = FactoryGirl.create(:group)
login_as('policy_editor')
expect { post 'create', admin_group: group.name }.not_to change {Admin::Group.all.count }
- flash[:error].should_not be_nil
- response.should redirect_to(admin_groups_path)
+ expect(flash[:error]).not_to be_nil
+ expect(response).to redirect_to(admin_groups_path)
end
context "Default permissions should be applied" do
@@ -47,8 +47,8 @@
login_as('policy_editor')
expect { post 'create', admin_group: test_group }.to change { Admin::Group.all.count }
g = Admin::Group.find(test_group)
- g.should_not be_nil
- response.should redirect_to(edit_admin_group_path(g))
+ expect(g).not_to be_nil
+ expect(response).to redirect_to(edit_admin_group_path(g))
end
end
@@ -64,16 +64,16 @@
context "editing a group" do
it "should redirect to sign in page with a notice on when unauthenticated" do
get 'edit', id: group.name
- flash[:notice].should_not be_nil
- response.should redirect_to(new_user_session_path)
+ expect(flash[:notice]).not_to be_nil
+ expect(response).to redirect_to(new_user_session_path)
end
it "should redirect to home page with a notice when authenticated but unauthorized" do
login_as('student')
get 'edit', id: group.name
- flash[:notice].should_not be_nil
- response.should redirect_to(root_path)
+ expect(flash[:notice]).not_to be_nil
+ expect(response).to redirect_to(root_path)
end
it "should be able to change group users when authenticated and authorized" do
@@ -84,9 +84,9 @@
group_name = group.name
group = Admin::Group.find(group_name)
- group.users.should include(new_user)
- flash[:notice].should_not be_nil
- response.should redirect_to(edit_admin_group_path(Admin::Group.find(group.name)))
+ expect(group.users).to include(new_user)
+ expect(flash[:notice]).not_to be_nil
+ expect(response).to redirect_to(edit_admin_group_path(Admin::Group.find(group.name)))
end
it "should be able to change group name when authenticated and authorized" do
@@ -96,18 +96,18 @@
put 'update', group_name: new_group_name, id: group.name
new_group = Admin::Group.find(new_group_name)
- new_group.should_not be_nil
- new_group.users.should == group.users
- flash[:notice].should_not be_nil
- response.should redirect_to(edit_admin_group_path(new_group))
+ expect(new_group).not_to be_nil
+ expect(new_group.users).to eq(group.users)
+ expect(flash[:notice]).not_to be_nil
+ expect(response).to redirect_to(edit_admin_group_path(new_group))
end
it "should not be able to rename system groups" do
login_as('administrator')
put 'update', group_name: Faker::Lorem.word, id: 'manager'
- Admin::Group.find('manager').should_not be_nil
- flash[:error].should_not be_nil
+ expect(Admin::Group.find('manager')).not_to be_nil
+ expect(flash[:error]).not_to be_nil
end
it "should be able to remove users from a group" do
@@ -116,8 +116,8 @@
put 'update_users', id: group.name, user_ids: Admin::Group.find(group.name).users
- Admin::Group.find(group.name).users.should be_empty
- flash[:error].should be_nil
+ expect(Admin::Group.find(group.name).users).to be_empty
+ expect(flash[:error]).to be_nil
end
it "should not remove users from the manager group if they are sole managers of a collection" do
@@ -129,7 +129,7 @@
put 'update_users', id: 'manager', user_ids: [manager_name]
expect(Admin::Group.find('manager').users).to include(manager_name)
- flash[:error].should_not be_nil
+ expect(flash[:error]).not_to be_nil
end
['administrator','group_manager'].each do |g|
@@ -139,9 +139,9 @@
put 'update', id: g, new_user: new_user
group = Admin::Group.find(g)
- group.users.should include(new_user)
- flash[:notice].should_not be_nil
- response.should redirect_to(edit_admin_group_path(Admin::Group.find(group.name)))
+ expect(group.users).to include(new_user)
+ expect(flash[:notice]).not_to be_nil
+ expect(response).to redirect_to(edit_admin_group_path(Admin::Group.find(group.name)))
end
it "should not be able to manage #{g} group as a group_manager" do
@@ -150,9 +150,9 @@
put 'update', id: g, new_user: new_user
group = Admin::Group.find(g)
- group.users.should_not include(new_user)
- flash[:error].should_not be_nil
- response.should redirect_to(admin_groups_path)
+ expect(group.users).not_to include(new_user)
+ expect(flash[:error]).not_to be_nil
+ expect(response).to redirect_to(admin_groups_path)
end
end
end
@@ -161,24 +161,24 @@
it "should redirect to sign in page with a notice on when unauthenticated" do
expect { put 'update_multiple', group_ids: [group.name] }.not_to change { RoleControls.users(group.name) }
- flash[:notice].should_not be_nil
- response.should redirect_to(new_user_session_path)
+ expect(flash[:notice]).not_to be_nil
+ expect(response).to redirect_to(new_user_session_path)
end
it "should redirect to home page with a notice when authenticated but unauthorized" do
login_as('student')
expect { put 'update_multiple', group_ids: [group.name] }.not_to change { RoleControls.users(group.name) }
- flash[:notice].should_not be_nil
- response.should redirect_to(root_path)
+ expect(flash[:notice]).not_to be_nil
+ expect(response).to redirect_to(root_path)
end
it "should be able to change group users when authenticated and authorized" do
login_as('policy_editor')
expect { put 'update_multiple', group_ids: [group.name] }.to change { RoleControls.users(group.name) }
- flash[:notice].should_not be_nil
- response.should redirect_to(admin_groups_path)
+ expect(flash[:notice]).not_to be_nil
+ expect(response).to redirect_to(admin_groups_path)
end
end
end
diff --git a/spec/controllers/master_files_controller_spec.rb b/spec/controllers/master_files_controller_spec.rb
index a05438f858..3ae91bb949 100644
--- a/spec/controllers/master_files_controller_spec.rb
+++ b/spec/controllers/master_files_controller_spec.rb
@@ -33,11 +33,11 @@
request.env["HTTP_REFERER"] = "/"
@file = fixture_file_upload('/videoshort.mp4', 'video/mp4')
- @file.stub(:size).and_return(MasterFile::MAXIMUM_UPLOAD_SIZE + 2^21)
+ allow(@file).to receive(:size).and_return(MasterFile::MAXIMUM_UPLOAD_SIZE + 2^21)
expect { post :create, Filedata: [@file], original: 'any', container_id: media_object.pid}.not_to change { MasterFile.count }
- flash[:error].should_not be_nil
+ expect(flash[:error]).not_to be_nil
end
end
@@ -50,9 +50,9 @@
container_id: media_object.pid
master_file = media_object.reload.parts.first
- master_file.file_format.should eq "Moving image"
+ expect(master_file.file_format).to eq "Moving image"
- flash[:errors].should be_nil
+ expect(flash[:errors]).to be_nil
end
it "should recognize an audio format" do
@@ -63,7 +63,7 @@
container_id: media_object.pid
master_file = media_object.reload.parts.first
- master_file.file_format.should eq "Sound"
+ expect(master_file.file_format).to eq "Sound"
end
it "should reject non audio/video format" do
@@ -73,7 +73,7 @@
expect { post :create, Filedata: [@file], original: 'any', container_id: media_object.pid }.not_to change { MasterFile.count }
- flash[:error].should_not be_nil
+ expect(flash[:error]).not_to be_nil
end
it "should recognize audio/video based on extension when MIMETYPE is of unknown format" do
@@ -84,9 +84,9 @@
original: 'any',
container_id: media_object.pid
master_file = MasterFile.all.last
- master_file.file_format.should eq "Moving image"
+ expect(master_file.file_format).to eq "Moving image"
- flash[:errors].should be_nil
+ expect(flash[:errors]).to be_nil
end
end
@@ -101,22 +101,39 @@ class << @file
post :create, Filedata: [@file], original: 'any', container_id: media_object.pid
master_file = MasterFile.all.last
- media_object.reload.parts.should include master_file
- master_file.mediaobject.pid.should eq(media_object.pid)
+ expect(media_object.reload.parts).to include master_file
+ expect(master_file.mediaobject.pid).to eq(media_object.pid)
- flash[:errors].should be_nil
+ expect(flash[:errors]).to be_nil
end
it "should associate a dropbox file" do
skip
- Avalon::Dropbox.any_instance.stub(:find).and_return "spec/fixtures/videoshort.mp4"
+ allow_any_instance_of(Avalon::Dropbox).to receive(:find).and_return "spec/fixtures/videoshort.mp4"
post :create, dropbox: [{id: 1}], original: 'any', container_id: media_object.pid
master_file = MasterFile.all.last
media_object.reload
- media_object.parts.should include master_file
- master_file.mediaobject.pid.should eq(media_object.pid)
+ expect(media_object.parts).to include master_file
+ expect(master_file.mediaobject.pid).to eq(media_object.pid)
- flash[:errors].should be_nil
+ expect(flash[:errors]).to be_nil
+ end
+ it "should not fail when associating with a published mediaobject" do
+ media_object = FactoryGirl.create(:published_media_object)
+ login_user media_object.collection.managers.first
+ @file = fixture_file_upload('/videoshort.mp4', 'video/mp4')
+ #Work-around for a Rails bug
+ class << @file
+ attr_reader :tempfile
+ end
+
+ post :create, Filedata: [@file], original: 'any', container_id: media_object.pid
+
+ master_file = MasterFile.all.last
+ expect(media_object.reload.parts).to include master_file
+ expect(master_file.mediaobject.pid).to eq(media_object.pid)
+
+ expect(flash[:errors]).to be_nil
end
end
end
@@ -137,7 +154,7 @@ class << @file
context "should no longer be associated with its parent object" do
it "should create then remove a file from a video object" do
expect { post :destroy, id: master_file.pid }.to change { MasterFile.count }.by(-1)
- master_file.mediaobject.reload.parts.should_not include master_file
+ expect(master_file.mediaobject.reload.parts).not_to include master_file
end
end
end
@@ -146,7 +163,7 @@ class << @file
let!(:master_file) {FactoryGirl.create(:master_file)}
it "should redirect you to the media object page with the correct section" do
get :show, id: master_file.pid, t:'10'
- response.should redirect_to("#{pid_section_media_object_path(master_file.mediaobject.pid, master_file.pid)}?t=10")
+ expect(response).to redirect_to("#{pid_section_media_object_path(master_file.mediaobject.pid, master_file.pid)}?t=10")
end
end
@@ -238,5 +255,34 @@ class << @file
expect(flash[:notice]).to be_nil
end
end
+ describe "#attach_captions" do
+ let!(:media_object) {FactoryGirl.create(:media_object_with_master_file)}
+ let!(:content_provider) {login_user media_object.collection.managers.first}
+ let!(:master_file) {media_object.parts.first}
+
+ before(:each) do
+ login_user media_object.collection.managers.first
+ end
+
+ it "should populate captions datastream with text" do
+ # populate the captions datastream with an uploaded vtt file
+ file = fixture_file_upload('/dropbox/example_batch_ingest/assets/sheephead_mountain.mov.vtt', 'text/vtt')
+ post 'attach_captions', master_file: {captions: file}, id: master_file.id
+ master_file.reload
+ expect(master_file.captions.has_content?).to be_truthy
+ expect(master_file.captions.label).to eq('sheephead_mountain.mov.vtt')
+ expect(master_file.captions.mimeType).to eq('text/vtt')
+ expect(flash[:errors]).to be_nil
+ expect(flash[:notice]).to be_nil
+ end
+ it "should remove contents of captions datastream" do
+ # remove the contents of the datastream
+ post 'attach_captions', id: master_file.id
+ master_file.reload
+ expect(master_file.captions.empty?).to be true
+ expect(flash[:errors]).to be_nil
+ expect(flash[:notice]).to be_nil
+ end
+ end
end
diff --git a/spec/controllers/media_objects_controller_spec.rb b/spec/controllers/media_objects_controller_spec.rb
index 3c25beb734..199a9be065 100644
--- a/spec/controllers/media_objects_controller_spec.rb
+++ b/spec/controllers/media_objects_controller_spec.rb
@@ -1,14 +1,14 @@
# Copyright 2011-2015, The Trustees of Indiana University and Northwestern
# University. Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
-#
+#
# You may obtain a copy of the License at
-#
+#
# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software distributed
+#
+# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
-# CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
# --- END LICENSE_HEADER BLOCK ---
@@ -17,24 +17,311 @@
describe MediaObjectsController, type: :controller do
render_views
- describe "#new" do
- let!(:collection) { FactoryGirl.create(:collection) }
- before(:each) do
- request.env["HTTP_REFERER"] = '/'
+ before(:each) do
+ request.env["HTTP_REFERER"] = '/'
+ end
+
+ describe 'security' do
+ let(:media_object) { FactoryGirl.create(:media_object) }
+ let(:collection) { FactoryGirl.create(:collection) }
+ describe 'ingest api' do
+ it "all routes should return 401 when no token is present" do
+ expect(get :index, format: 'json').to have_http_status(401)
+ expect(get :show, id: media_object.id, format: 'json').to have_http_status(401)
+ expect(post :create, format: 'json').to have_http_status(401)
+ expect(put :update, id: media_object.id, format: 'json').to have_http_status(401)
+ end
+ it "all routes should return 403 when a bad token in present" do
+ request.headers['Avalon-Api-Key'] = 'badtoken'
+ expect(get :index, format: 'json').to have_http_status(403)
+ expect(get :show, id: media_object.id, format: 'json').to have_http_status(403)
+ expect(post :create, format: 'json').to have_http_status(403)
+ expect(put :update, id: media_object.id, format: 'json').to have_http_status(403)
+ end
end
+ describe 'normal auth' do
+ context 'with unauthenticated user' do
+ #New is isolated here due to issues caused by the controller instance not being regenerated
+ it "should redirect to sign in" do
+ expect(get :new).to redirect_to(new_user_session_path)
+ end
+ it "all routes should redirect to sign in" do
+ expect(get :show, id: media_object.id).to redirect_to(new_user_session_path)
+ expect(get :show_progress, id: media_object.id, format: 'json').to have_http_status(401)
+ expect(get :edit, id: media_object.id).to redirect_to(new_user_session_path)
+ expect(get :confirm_remove, id: media_object.id).to redirect_to(new_user_session_path)
+ expect(post :create).to redirect_to(new_user_session_path)
+ expect(put :update, id: media_object.id).to redirect_to(new_user_session_path)
+ expect(put :update_status, id: media_object.id).to redirect_to(new_user_session_path)
+ expect(get :tree, id: media_object.id).to redirect_to(new_user_session_path)
+ expect(get :deliver_content, id: media_object.id, datastream: 'descMetadata').to redirect_to(new_user_session_path)
+ expect(delete :destroy, id: media_object.id).to redirect_to(new_user_session_path)
+ end
+ end
+ context 'with end-user' do
+ before do
+ login_as :user
+ end
+ #New is isolated here due to issues caused by the controller instance not being regenerated
+ it "should redirect to /" do
+ expect(get :new).to redirect_to(root_path)
+ end
+ it "all routes should redirect to /" do
+ expect(get :show, id: media_object.id).to redirect_to(root_path)
+ expect(get :show_progress, id: media_object.id, format: 'json').to redirect_to(root_path)
+ expect(get :edit, id: media_object.id).to redirect_to(root_path)
+ expect(get :confirm_remove, id: media_object.id).to redirect_to(root_path)
+ expect(post :create).to redirect_to(root_path)
+ expect(put :update, id: media_object.id).to redirect_to(root_path)
+ expect(put :update_status, id: media_object.id).to redirect_to(root_path)
+ expect(get :tree, id: media_object.id).to redirect_to(root_path)
+ expect(get :deliver_content, id: media_object.id, datastream: 'descMetadata').to redirect_to(root_path)
+ expect(delete :destroy, id: media_object.id).to redirect_to(root_path)
+ end
+ end
+ end
+ end
- it "should redirect to sign in page with a notice when unauthenticated" do
- expect { get 'new', collection_id: collection.pid }.not_to change { MediaObject.count }
- flash[:notice].should_not be_nil
- response.should redirect_to(new_user_session_path)
+ context "JSON API methods" do
+ let!(:collection) { FactoryGirl.create(:collection) }
+ let!(:testdir) {'spec/fixtures/'}
+ let!(:filename) {'videoshort.high.mp4'}
+ let!(:absolute_location) {Rails.root.join(File.join(testdir, filename)).to_s}
+ let!(:structure) {File.read(File.join(testdir, 'structure.xml'))}
+ let!(:captions) {File.read(File.join(testdir, 'dropbox/example_batch_ingest/assets/sheephead_mountain.mov.vtt'))}
+ let!(:bib_id) { '7763100' }
+ let!(:sru_url) { "http://zgate.example.edu:9000/db?version=1.1&operation=searchRetrieve&maximumRecords=1&recordSchema=marcxml&query=rec.id=#{bib_id}" }
+ let!(:sru_response) { File.read(File.expand_path("../../fixtures/#{bib_id}.xml",__FILE__)) }
+ let!(:masterfile) {{
+ file_location: absolute_location,
+ label: "Part 1",
+ files: [{label: 'quality-high',
+ id: 'track-1',
+ url: absolute_location,
+ duration: "6315",
+ mime_type: "video/mp4",
+ audio_bitrate: "127716.0",
+ audio_codec: "AAC",
+ video_bitrate: "1000000.0",
+ video_codec: "AVC",
+ width: "640",
+ height: "480" },
+ {label: 'quality-medium',
+ id: 'track-2',
+ url: absolute_location,
+ duration: "6315",
+ mime_type: "video/mp4",
+ audio_bitrate: "127716.0",
+ audio_codec: "AAC",
+ video_bitrate: "1000000.0",
+ video_codec: "AVC",
+ width: "640",
+ height: "480" }
+ ],
+ file_location: absolute_location,
+ file_checksum: "7ae24368ccb7a6c6422a14ff73f33c9a",
+ file_size: "199160",
+ duration: "6315",
+ display_aspect_ratio: "1.7777777777777777",
+ original_frame_size: "640x480",
+ file_format: "Moving image",
+ poster_offset: "0:02",
+ thumbnail_offset: "0:02",
+ date_digitized: "2015-12-31",
+ workflow_name: "avalon",
+ percent_complete: "100.0",
+ percent_succeeded: "100.0",
+ percent_failed: "0",
+ status_code: "COMPLETED",
+ other_identifier: '40000000045312',
+ structure: structure,
+ captions: captions,
+ captions_type: 'text/vtt'
+ }}
+ let!(:descMetadata_fields) {[
+ :title,
+ :alternative_title,
+ :translated_title,
+ :uniform_title,
+ :statement_of_responsibility,
+ :creator,
+ :date_created,
+ :date_issued,
+ :copyright_date,
+ :abstract,
+ :note,
+ :format,
+ :resource_type,
+ :contributor,
+ :publisher,
+ :genre,
+ :subject,
+ :related_item_url,
+ :geographic_subject,
+ :temporal_subject,
+ :topical_subject,
+ :bibliographic_id,
+ :language,
+ :terms_of_use,
+ :table_of_contents,
+ :physical_description,
+ :other_identifier
+ ]}
+ describe "#create" do
+ context 'using api' do
+ before do
+ request.headers['Avalon-Api-Key'] = 'secret_token'
+ end
+ it "should respond with 422 if collection not found" do
+ post 'create', format: 'json', collection_id: "avalon:doesnt_exist"
+ expect(response.status).to eq(422)
+ expect(JSON.parse(response.body)).to include('errors')
+ expect(JSON.parse(response.body)["errors"].class).to eq Array
+ expect(JSON.parse(response.body)["errors"].first.class).to eq String
+ end
+ it "should create a new mediaobject" do
+ media_object = FactoryGirl.create(:multiple_entries)
+ fields = media_object.attributes.select {|k,v| descMetadata_fields.include? k.to_sym }
+ post 'create', format: 'json', fields: fields, files: [masterfile], collection_id: collection.pid
+ expect(response.status).to eq(200)
+ new_media_object = MediaObject.find(JSON.parse(response.body)['id'])
+ expect(new_media_object.title).to eq media_object.title
+ expect(new_media_object.creator).to eq media_object.creator
+ expect(new_media_object.date_issued).to eq media_object.date_issued
+ expect(new_media_object.parts_with_order).to eq new_media_object.parts
+ expect(new_media_object.duration).to eq '6315'
+ expect(new_media_object.format).to eq 'video/mp4'
+ expect(new_media_object.avalon_resource_type).to eq ['moving image']
+ expect(new_media_object.parts.first.date_digitized).to eq('2015-12-31T00:00:00Z')
+ expect(new_media_object.parts.first.DC.identifier).to include('40000000045312')
+ expect(new_media_object.parts.first.structuralMetadata.has_content?).to be_truthy
+ expect(new_media_object.parts.first.captions.has_content?).to be_truthy
+ expect(new_media_object.parts.first.captions.label).to eq('ingest.api')
+ expect(new_media_object.parts.first.captions.mimeType).to eq('text/vtt')
+ expect(new_media_object.parts.first.derivatives.count).to eq(2)
+ expect(new_media_object.parts.first.derivatives.first.location_url).to eq(absolute_location)
+ expect(new_media_object.workflow.last_completed_step).to eq([HYDRANT_STEPS.last.step])
+ end
+ it "should create a new published mediaobject" do
+ media_object = FactoryGirl.create(:multiple_entries)
+ fields = media_object.attributes.select {|k,v| descMetadata_fields.include? k.to_sym }
+ post 'create', format: 'json', fields: fields, files: [masterfile], collection_id: collection.pid, publish: true
+ expect(response.status).to eq(200)
+ new_media_object = MediaObject.find(JSON.parse(response.body)['id'])
+ expect(new_media_object.published?).to be_truthy
+ expect(new_media_object.workflow.last_completed_step).to eq([HYDRANT_STEPS.last.step])
+ end
+ it "should create a new mediaobject with successful bib import" do
+ Avalon::Configuration['bib_retriever'] = { 'protocol' => 'sru', 'url' => 'http://zgate.example.edu:9000/db' }
+ FakeWeb.register_uri :get, sru_url, body: sru_response
+ fields = { bibliographic_id: bib_id }
+ post 'create', format: 'json', import_bib_record: true, fields: fields, files: [masterfile], collection_id: collection.pid
+ expect(response.status).to eq(200)
+ new_media_object = MediaObject.find(JSON.parse(response.body)['id'])
+ expect(new_media_object.bibliographic_id).to eq(['local', bib_id])
+ expect(new_media_object.title).to eq('245 A : B F G K N P S')
+ end
+ it "should create a new mediaobject with supplied fields when bib import fails" do
+ Avalon::Configuration['bib_retriever'] = { 'protocol' => 'sru', 'url' => 'http://zgate.example.edu:9000/db' }
+ FakeWeb.register_uri :get, sru_url, body: nil
+ media_object = FactoryGirl.create(:media_object)
+ fields = media_object.attributes.select {|k,v| descMetadata_fields.include? k.to_sym }
+ fields = fields.merge({ bibliographic_id: bib_id })
+ post 'create', format: 'json', import_bib_record: true, fields: fields, files: [masterfile], collection_id: collection.pid
+ expect(response.status).to eq(200)
+ new_media_object = MediaObject.find(JSON.parse(response.body)['id'])
+ expect(new_media_object.bibliographic_id).to eq(['local', bib_id])
+ expect(new_media_object.title).to eq media_object.title
+ expect(new_media_object.creator).to eq [] #creator no longer required, so supplied value won't be used
+ expect(new_media_object.date_issued).to eq media_object.date_issued
+ end
+ it "should create a new mediaobject, removing invalid data for non-required fields" do
+ media_object = FactoryGirl.create(:multiple_entries)
+ fields = media_object.attributes.select {|k,v| descMetadata_fields.include? k.to_sym }
+ fields[:language] = ['???']
+ fields[:related_item_url] = ['???']
+ fields[:note] = ['note']
+ fields[:note_type] = ['???']
+ fields[:date_created] = '???'
+ fields[:copyright_date] = '???'
+ post 'create', format: 'json', fields: fields, files: [masterfile], collection_id: collection.pid
+ expect(response.status).to eq(200)
+ new_media_object = MediaObject.find(JSON.parse(response.body)['id'])
+ expect(new_media_object.title).to eq media_object.title
+ expect(new_media_object.language).to eq []
+ expect(new_media_object.related_item_url).to eq []
+ expect(new_media_object.note).to eq nil
+ expect(new_media_object.date_created).to eq nil
+ expect(new_media_object.copyright_date).to eq nil
+ end
+ it "should merge supplied other identifiers after bib import" do
+ Avalon::Configuration['bib_retriever'] = { 'protocol' => 'sru', 'url' => 'http://zgate.example.edu:9000/db' }
+ FakeWeb.register_uri :get, sru_url, body: sru_response
+ fields = { bibliographic_id: bib_id, other_identifier_type: ['other'], other_identifier: ['12345'] }
+ post 'create', format: 'json', import_bib_record: true, fields: fields, files: [masterfile], collection_id: collection.pid
+ expect(response.status).to eq(200)
+ new_media_object = MediaObject.find(JSON.parse(response.body)['id'])
+ expect(new_media_object.bibliographic_id).to eq(['local', bib_id])
+ expect(new_media_object.other_identifier.find {|id_pair| id_pair[0] == 'other'}).not_to be nil
+ expect(new_media_object.other_identifier.find {|id_pair| id_pair[0] == 'other'}[1]).to eq('12345')
+ end
+ end
end
-
- it "should redirect to home page with a notice when authenticated but unauthorized" do
- login_as :user
- expect { get 'new', collection_id: collection.pid }.not_to change { MediaObject.count }
- flash[:notice].should_not be_nil
- response.should redirect_to(root_path)
+ describe "#update" do
+ context 'using api' do
+ before do
+ request.headers['Avalon-Api-Key'] = 'secret_token'
+ end
+ let!(:media_object) { FactoryGirl.create(:media_object_with_master_file) }
+ it "should route json format to #json_update" do
+ assert_routing({ path: 'media_objects/avalon:1.json', method: :put },
+ { controller: 'media_objects', action: 'json_update', id: 'avalon:1', format: 'json' })
+ end
+ it "should route unspecified format to #update" do
+ assert_routing({ path: 'media_objects/avalon:1', method: :put },
+ { controller: 'media_objects', action: 'update', id: 'avalon:1', format: 'html' })
+ end
+ it "should update a mediaobject's metadata" do
+ old_title = media_object.title
+ put 'json_update', format: 'json', id: media_object.pid, fields: {title: old_title+'new'}, collection_id: media_object.collection_id
+ expect(JSON.parse(response.body)['id'].class).to eq String
+ expect(JSON.parse(response.body)).not_to include('errors')
+ media_object.reload
+ expect(media_object.title).to eq old_title+'new'
+ end
+ it "should add a masterfile to a mediaobject" do
+ put 'json_update', format: 'json', id: media_object.pid, files: [masterfile], collection_id: media_object.collection_id
+ expect(JSON.parse(response.body)['id'].class).to eq String
+ expect(JSON.parse(response.body)).not_to include('errors')
+ media_object.reload
+ expect(media_object.parts.count).to eq 2
+ end
+ it "should delete existing masterfiles and add a new masterfile to a mediaobject" do
+ put 'json_update', format: 'json', id: media_object.pid, files: [masterfile], collection_id: media_object.collection_id, replace_masterfiles: true
+ expect(JSON.parse(response.body)['id'].class).to eq String
+ expect(JSON.parse(response.body)).not_to include('errors')
+ media_object.reload
+ expect(media_object.parts.count).to eq 1
+ end
+ it "should return 404 if media object doesn't exist" do
+ allow_any_instance_of(MediaObject).to receive(:save).and_return false
+ put 'json_update', format: 'json', id: 'avalon:doesnt_exist', fields: {}, collection_id: media_object.collection_id
+ expect(response.status).to eq(404)
+ end
+ it "should return 422 if media object update failed" do
+ allow_any_instance_of(MediaObject).to receive(:save).and_return false
+ put 'json_update', format: 'json', id: media_object.pid, fields: {}, collection_id: media_object.collection_id
+ expect(response.status).to eq(422)
+ expect(JSON.parse(response.body)).to include('errors')
+ expect(JSON.parse(response.body)["errors"].class).to eq Array
+ expect(JSON.parse(response.body)["errors"].first.class).to eq String
+ end
+ end
end
+ end
+
+ describe "#new" do
+ let!(:collection) { FactoryGirl.create(:collection) }
it "should not let manager of other collections create an item in this collection" do
skip
@@ -45,14 +332,14 @@
login_user collection.managers.first
expect { get 'new', collection_id: collection.pid }.to change { MediaObject.count }
pid = MediaObject.all.last.pid
- response.should redirect_to(edit_media_object_path(id: pid))
+ expect(response).to redirect_to(edit_media_object_path(id: pid))
end
it "should copy default permissions from its owning collection" do
login_user collection.depositors.first
- get 'new', collection_id: collection.pid
-
+ get 'new', collection_id: collection.pid
+
#MediaObject.all.last.edit_users.should include(collection.managers)
#MediaObject.all.last.edit_users.should include(collection.depositors)
end
@@ -63,28 +350,14 @@
describe "#edit" do
let!(:media_object) { FactoryGirl.create(:media_object) }
- it "should redirect to sign in page with a notice when unauthenticated" do
- get 'edit', id: media_object.pid
- flash[:notice].should_not be_nil
- response.should redirect_to(new_user_session_path)
- end
-
- it "should redirect to show page with a notice when authenticated but unauthorized" do
- login_as :user
-
- get 'edit', id: media_object.pid
- flash[:notice].should_not be_nil
- response.should redirect_to(media_object_path(media_object.pid) )
- end
-
it "should redirect to first workflow step if authorized to edit" do
login_user media_object.collection.managers.first
get 'edit', id: media_object.pid
- response.should be_success
- response.should render_template "_#{HYDRANT_STEPS.first.template}"
+ expect(response).to be_success
+ expect(response).to render_template "_#{HYDRANT_STEPS.first.template}"
end
-
+
it "should not default to the Access Control page" do
skip "[VOV-1165] Wait for product owner feedback on which step to default to"
end
@@ -95,11 +368,11 @@
it "should be able to retrieve an existing record from Fedora" do
media_object.workflow.last_completed_step = 'resource-description'
media_object.save
-
+
# Set the task so that it can get to the resource-description step
login_user media_object.collection.managers.first
get :edit, {id: media_object.pid, step: 'resource-description'}
- response.response_code.should == 200
+ expect(response.response_code).to eq(200)
end
end
@@ -107,16 +380,16 @@
before(:each) { login_user mo.collection.managers.first }
context "Persisting Permalinks on unpublished mediaobject" do
subject(:mo) { media_object }
- it "should persist new permalink on unpublished media_object" do
- expect { put 'update', id: mo.pid, step: 'resource-description',
- media_object: { permalink: 'newpermalink', title: 'newtitle',
+ it "should persist new permalink on unpublished media_object" do
+ expect { put 'update', id: mo.pid, step: 'resource-description',
+ media_object: { permalink: 'newpermalink', title: 'newtitle',
creator: 'newcreator', date_issued: 'newdateissued' }}
- .to change { MediaObject.find(mo.pid).permalink }
+ .to change { MediaObject.find(mo.pid).permalink }
.to('newpermalink')
end
- it "should persist new permalink on unpublished media_object part" do
+ it "should persist new permalink on unpublished media_object part" do
part1 = FactoryGirl.create(:master_file, mediaobject: mo)
- expect {put 'update', id: mo.pid, step: 'file-upload',
+ expect {put 'update', id: mo.pid, step: 'file-upload',
parts: { part1.pid => { permalink: 'newpermalinkpart' }}}
.to change { MasterFile.find(part1.pid).permalink }
.to('newpermalinkpart')
@@ -125,21 +398,67 @@
context "Persisting Permalinks on published mediaobject" do
subject(:mo) { FactoryGirl.create(:published_media_object, permalink: 'oldpermalink') }
it "should persist updated permalink on published media_object" do
- expect { put 'update', id: mo.pid, step: 'resource-description',
- media_object: { permalink: 'newpermalink', title: mo.title,
+ expect { put 'update', id: mo.pid, step: 'resource-description',
+ media_object: { permalink: 'newpermalink', title: mo.title,
creator: mo.creator, date_issued: mo.date_issued }}
.to change { MediaObject.find(mo.pid).permalink }
.to('newpermalink')
end
it "should persist updated permalink on published media_object part" do
part1 = FactoryGirl.create(:master_file, permalink: 'oldpermalinkpart1', mediaobject: mo)
- expect { put 'update', id: mo.pid, step: 'file-upload',
+ expect { put 'update', id: mo.pid, step: 'file-upload',
parts: { part1.pid => { permalink: 'newpermalinkpart' }}}
.to change { MasterFile.find(part1.pid).permalink }
.to('newpermalinkpart')
end
end
end
+
+ context "Hidden Items" do
+ subject(:mo) { FactoryGirl.create(:media_object_with_completed_workflow, hidden: true) }
+ let!(:user) { Faker::Internet.email }
+ before(:each) { login_user mo.collection.managers.first }
+
+ it "should retain the hidden status of an object when other access control settings change" do
+ expect { put 'update', id: mo.pid, step: 'access-control', donot_advance: 'true',
+ add_user: user, add_user_display: user, submit_add_user: 'Add' }
+ .not_to change { MediaObject.find(mo.pid).hidden? }
+ end
+ end
+ end
+
+ describe "#index" do
+ let!(:media_object) { FactoryGirl.create(:published_media_object, visibility: 'public') }
+ subject(:json) { JSON.parse(response.body) }
+
+ it "should return list of media_objects" do
+ request.headers['Avalon-Api-Key'] = 'secret_token'
+ get 'index', format:'json'
+ expect(json.count).to eq(1)
+ expect(json.first['id']).to eq(media_object.pid)
+ expect(json.first['title']).to eq(media_object.title)
+ expect(json.first['collection']).to eq(media_object.collection.name)
+ expect(json.first['main_contributors']).to eq(media_object.creator)
+ expect(json.first['publication_date']).to eq(media_object.date_created)
+ expect(json.first['published_by']).to eq(media_object.avalon_publisher)
+ expect(json.first['published']).to eq(media_object.published?)
+ expect(json.first['summary']).to eq(media_object.abstract)
+ end
+ end
+
+ describe 'pagination' do
+ let(:collection) { FactoryGirl.create(:collection) }
+ subject(:json) { JSON.parse(response.body) }
+ before do
+ 5.times { FactoryGirl.create(:published_media_object, visibility: 'public', collection: collection) }
+ request.headers['Avalon-Api-Key'] = 'secret_token'
+ get 'index', format:'json', per_page: '2'
+ end
+ it 'should paginate' do
+ expect(json.count).to eq(2)
+ expect(response.headers['Per-Page']).to eq('2')
+ expect(response.headers['Total']).to eq('5')
+ end
end
describe "#show" do
@@ -148,19 +467,19 @@
context "Known items should be retrievable" do
it "should be accesible by its PID" do
get :show, id: media_object.pid
- response.response_code.should == 200
+ expect(response.response_code).to eq(200)
end
it "should return an error if the PID does not exist" do
expect(MediaObject).to receive(:find).with('no-such-object') { raise ActiveFedora::ObjectNotFoundError }
get :show, id: 'no-such-object'
- response.response_code.should == 404
+ expect(response.response_code).to eq(404)
end
it "should be available to a manager when unpublished" do
login_user media_object.collection.managers.first
get 'show', id: media_object.pid
- response.should_not redirect_to new_user_session_path
+ expect(response).not_to redirect_to new_user_session_path
end
it "should provide a JSON stream description to the client" do
@@ -171,13 +490,32 @@
mopid = media_object.pid
media_object = MediaObject.find(mopid)
- media_object.parts.collect { |part|
- get 'show', id: media_object.pid, format: 'json', content: part.pid
+ media_object.parts.collect { |part|
+ get 'show', id: media_object.pid, format: 'js', content: part.pid
json_obj = JSON.parse(response.body)
- json_obj['is_video'].should == part.is_video?
+ expect(json_obj['is_video']).to eq(part.is_video?)
}
end
end
+ context "Test lease access control" do
+ let!(:media_object) { FactoryGirl.create(:published_media_object, visibility: 'private') }
+ let!(:user) { FactoryGirl.create(:user) }
+ before :each do
+ login_user user.username
+ end
+ it "should not be available to a user on an inactive lease" do
+ media_object.governing_policies+=[Lease.create(begin_time: Date.today-2.day, end_time: Date.yesterday, read_users: [user.username])]
+ media_object.save!
+ get 'show', id: media_object.pid
+ expect(response.response_code).not_to eq(200)
+ end
+ it "should be available to a user on an active lease" do
+ media_object.governing_policies+=[Lease.create(begin_time: Date.yesterday, end_time: Date.tomorrow, read_users: [user.username])]
+ media_object.save!
+ get 'show', id: media_object.pid
+ expect(response.response_code).to eq(200)
+ end
+ end
context "Conditional Share partials should be rendered" do
context "Normal login" do
@@ -212,18 +550,18 @@
end
context "LTI login" do
it "administrators/managers/editors: should include lti, embed, and share" do
- user = login_lti 'administrator'
+ login_lti 'administrator'
lti_group = @controller.user_session[:virtual_groups].first
- mo = FactoryGirl.create(:published_media_object, visibility: 'private', read_groups: [lti_group])
+ FactoryGirl.create(:published_media_object, visibility: 'private', read_groups: [lti_group])
get :show, id: media_object.pid
expect(response).to render_template(:_share_resource)
expect(response).to render_template(:_embed_resource)
expect(response).to render_template(:_lti_url)
end
it "others: should include only lti" do
- user = login_lti 'student'
+ login_lti 'student'
lti_group = @controller.user_session[:virtual_groups].first
- mo = FactoryGirl.create(:published_media_object, visibility: 'private', read_groups: [lti_group])
+ FactoryGirl.create(:published_media_object, visibility: 'private', read_groups: [lti_group])
get :show, id: media_object.pid
expect(response).to_not render_template(:_share_resource)
expect(response).to_not render_template(:_embed_resource)
@@ -232,7 +570,7 @@
end
context "No share tabs rendered" do
it "should not render Share button" do
- @controller.stub(:evaluate_if_unless_configuration).and_return false
+ allow(@controller).to receive(:evaluate_if_unless_configuration).and_return false
expect(response).to_not render_template(:_share)
end
end
@@ -255,16 +593,16 @@
context "correctly handle unfound streams/sections" do
subject(:mo){FactoryGirl.create(:media_object_with_master_file)}
- before do
+ before do
mo.save(validate: false)
- login_user mo.collection.managers.first
+ login_user mo.collection.managers.first
end
it "redirects to first stream when currentStream is nil" do
- expect(get 'show', id: mo.pid, content: 'foo').to redirect_to(media_object_path(id: mo.pid))
+ expect(get 'show', id: mo.pid, content: 'foo').to redirect_to(media_object_path(id: mo.pid))
end
it "responds with 404 when non-existant section is requested" do
get 'show', id: mo.pid, part: 100
- expect(response.code).to eq('404')
+ expect(response.code).to eq('404')
end
end
@@ -274,12 +612,12 @@
context 'Before sign in' do
it 'persists the current url on the session' do
get 'show', id: media_object.pid
- session[:previous_url].should eql media_object_path(media_object)
+ expect(session[:previous_url]).to eql media_object_path(media_object)
end
end
context 'After sign in' do
- before do
+ before do
@user = FactoryGirl.create(:user)
@media_object = FactoryGirl.create(:media_object, visibility: 'private', read_users: [@user.username] )
end
@@ -289,31 +627,61 @@
end
end
end
-
+
context "Items should not be available to unauthorized users" do
it "should redirect to sign in when not logged in and item is unpublished" do
media_object.publish!(nil)
- media_object.should_not be_published
+ expect(media_object).not_to be_published
get 'show', id: media_object.pid
- response.should redirect_to new_user_session_path
+ expect(response).to redirect_to new_user_session_path
end
it "should redirect to home page when logged in and item is unpublished" do
media_object.publish!(nil)
- media_object.should_not be_published
+ expect(media_object).not_to be_published
login_as :user
get 'show', id: media_object.pid
- response.should redirect_to root_path
+ expect(response).to redirect_to root_path
end
end
+
+ context "with json format" do
+ subject(:json) { JSON.parse(response.body) }
+ let!(:media_object) { FactoryGirl.create(:media_object) }
+
+ before do
+ request.headers['Avalon-Api-Key'] = 'secret_token'
+ end
+
+ it "should return json for specific media_object" do
+ get 'show', id: media_object.pid, format:'json'
+ expect(json['id']).to eq(media_object.pid)
+ expect(json['title']).to eq(media_object.title)
+ expect(json['collection']).to eq(media_object.collection.name)
+ expect(json['main_contributors']).to eq(media_object.creator)
+ expect(json['publication_date']).to eq(media_object.date_created)
+ expect(json['published_by']).to eq(media_object.avalon_publisher)
+ expect(json['published']).to eq(media_object.published?)
+ expect(json['summary']).to eq(media_object.abstract)
+ end
+
+ it "should return 404 if requested media_object not present" do
+ login_as(:administrator)
+ get 'show', id: 'avalon:doesnt_exist', format: 'json'
+ expect(response.status).to eq(404)
+ expect(JSON.parse(response.body)["errors"].class).to eq Array
+ expect(JSON.parse(response.body)["errors"].first.class).to eq String
+ end
+ end
+
end
-
+
describe "#destroy" do
let!(:collection) { FactoryGirl.create(:collection) }
- before(:each) do
+ before(:each) do
login_user collection.managers.first
end
-
+
it "should remove the MediaObject and MasterFiles from the system" do
media_object = FactoryGirl.create(:media_object_with_master_file, collection: collection)
delete :destroy, id: media_object.pid
@@ -327,12 +695,6 @@
expect(response.code).to eq '404'
end
- it "should fail if user is not authorized" do
- media_object = FactoryGirl.create(:media_object)
- expect(delete :destroy, id: media_object.id).to redirect_to root_path
- expect(flash[:notice]).to include("permission denied")
- end
-
it "should remove multiple items" do
media_objects = []
3.times { media_objects << FactoryGirl.create(:media_object, collection: collection) }
@@ -343,6 +705,9 @@
end
describe "#update_status" do
+ before { Delayed::Worker.delay_jobs = false }
+ after { Delayed::Worker.delay_jobs = true }
+
let!(:collection) { FactoryGirl.create(:collection) }
before(:each) do
login_user collection.managers.first
@@ -354,7 +719,7 @@
Permalink.on_generate { |obj| "http://example.edu/permalink" }
end
it 'publishes media object' do
- media_object = FactoryGirl.create(:media_object, collection: collection)
+ media_object = FactoryGirl.create(:media_object, collection: collection)
get 'update_status', id: media_object.pid, status: 'publish'
media_object.reload
expect(media_object).to be_published
@@ -362,25 +727,19 @@
end
it "should fail when id doesn't exist" do
- get 'update_status', id: 'avalon:this-pid-is-fake', status: 'publish'
- expect(response.code).to eq '404'
- end
-
- it "should fail if user is not authorized" do
- media_object = FactoryGirl.create(:media_object)
- expect(get 'update_status', id: media_object.pid, status: 'publish').to be_redirect
- expect(flash[:notice]).to include("permission denied")
+ get 'update_status', id: 'avalon:this-pid-is-fake', status: 'publish'
+ expect(response.code).to eq '404'
end
it "should publish multiple items" do
- media_objects = []
- 3.times { media_objects << FactoryGirl.create(:media_object, collection: collection) }
+ media_objects = []
+ 3.times { media_objects << FactoryGirl.create(:media_object, collection: collection) }
get 'update_status', id: media_objects.map(&:id), status: 'publish'
- expect(flash[:notice]).to include('3 media objects')
+ expect(flash[:notice]).to include('3 media objects')
media_objects.each do |mo|
mo.reload
- expect(mo).to be_published
- expect(mo.permalink).to be_present
+ expect(mo).to be_published
+ expect(mo.permalink).to be_present
end
end
end
@@ -394,44 +753,54 @@
end
it "should fail when id doesn't exist" do
- get 'update_status', id: 'avalon:this-pid-is-fake', status: 'unpublish'
- expect(response.code).to eq '404'
- end
-
- it "should fail if user is not authorized" do
- media_object = FactoryGirl.create(:media_object)
- expect(get 'update_status', id: media_object.pid, status: 'unpublish').to be_redirect
- expect(flash[:notice]).to include("permission denied")
+ get 'update_status', id: 'avalon:this-pid-is-fake', status: 'unpublish'
+ expect(response.code).to eq '404'
end
it "should unpublish multiple items" do
- media_objects = []
- 3.times { media_objects << FactoryGirl.create(:published_media_object, collection: collection) }
+ media_objects = []
+ 3.times { media_objects << FactoryGirl.create(:published_media_object, collection: collection) }
get 'update_status', id: media_objects.map(&:id), status: 'unpublish'
- expect(flash[:notice]).to include('3 media objects')
+ expect(flash[:notice]).to include('3 media objects')
media_objects.each do |mo|
mo.reload
- expect(mo).not_to be_published
+ expect(mo).not_to be_published
end
end
end
end
+ describe "#save" do
+ it 'removes bookmarks that are no longer viewable' do
+ media_object = FactoryGirl.create(:published_media_object)
+ user = FactoryGirl.create(:public)
+ bookmark = Bookmark.create(document_id: media_object.pid, user_id: user.id)
+ login_user media_object.collection.managers.first
+ request.env["HTTP_REFERER"] = '/'
+ expect {
+ get 'update_status', id: media_object.pid, status: 'unpublish'
+ }.to change { Bookmark.exists? bookmark }.from( true ).to( false )
+ end
+ end
+
describe "#update" do
it 'updates the order' do
media_object = FactoryGirl.create(:media_object)
- media_object.parts << FactoryGirl.create(:master_file)
- media_object.parts << FactoryGirl.create(:master_file)
+ 2.times do
+ mf = FactoryGirl.create(:master_file)
+ mf.mediaobject = media_object
+ mf.save
+ end
master_file_pids = media_object.parts.map(&:id)
media_object.section_pid = master_file_pids
media_object.save( validate: false )
login_user media_object.collection.managers.first
-
+
put 'update', :id => media_object.pid, :masterfile_ids => master_file_pids.reverse, :step => 'structure'
media_object.reload
- media_object.section_pid.should eq master_file_pids.reverse
+ expect(media_object.section_pid).to eq master_file_pids.reverse
end
it 'sets the MIME type' do
media_object = FactoryGirl.create(:media_object)
@@ -442,5 +811,123 @@
media_object.reload
expect(media_object.descMetadata.media_type).to eq(["video/mp4"])
end
+
+ context 'large objects' do
+ before(:each) do
+ Permalink.on_generate { |obj| sleep(0.5); "http://example.edu/permalink" }
+ end
+
+ let!(:media_object) do
+ mo = FactoryGirl.create(:published_media_object)
+ 10.times { FactoryGirl.create(:master_file_with_derivative, mediaobject: mo) }
+ mo
+ end
+
+ it "should update all the labels" do
+ login_user media_object.collection.managers.first
+ part_params = {}
+ media_object.parts_with_order.each_with_index { |mf,i| part_params[mf.pid] = { pid: mf.pid, label: "Part #{i}", permalink: '', poster_offset: '00:00:00.000' } }
+ params = { id: media_object.pid, parts: part_params, save: 'Save', step: 'file-upload', donot_advance: 'true' }
+ patch 'update', params
+ media_object.reload
+ media_object.parts_with_order.each_with_index do |mf,i|
+ expect(mf.label).to eq "Part #{i}"
+ end
+ end
+ end
+
+ context "access controls" do
+ let!(:media_object) { FactoryGirl.create(:media_object) }
+ let!(:user) { Faker::Internet.email }
+ let!(:group) { Faker::Lorem.word }
+ let!(:classname) { Faker::Lorem.word }
+ let!(:ipaddr) { Faker::Internet.ip_v4_address }
+ before(:each) { login_user media_object.collection.managers.first }
+
+ context "grant and revoke special read access" do
+ it "grants and revokes special read access to users" do
+ expect { put :update, id: media_object.id, step: 'access-control', donot_advance: 'true', add_user: user, submit_add_user: 'Add' }.to change { media_object.reload.read_users }.from([]).to([user])
+ expect {put :update, id: media_object.id, step: 'access-control', donot_advance: 'true', remove_user: user, submit_remove_user: 'Remove' }.to change { media_object.reload.read_users }.from([user]).to([])
+ end
+ it "grants and revokes special read access to groups" do
+ expect { put :update, id: media_object.id, step: 'access-control', donot_advance: 'true', add_group: group, submit_add_group: 'Add' }.to change { media_object.reload.read_groups }.from([]).to([group])
+ expect { put :update, id: media_object.id, step: 'access-control', donot_advance: 'true', remove_group: group, submit_remove_group: 'Remove' }.to change { media_object.reload.read_groups }.from([group]).to([])
+ end
+ it "grants and revokes special read access to external groups" do
+ expect { put :update, id: media_object.id, step: 'access-control', donot_advance: 'true', add_class: classname, submit_add_class: 'Add' }.to change { media_object.reload.read_groups }.from([]).to([classname])
+ expect { put :update, id: media_object.id, step: 'access-control', donot_advance: 'true', remove_class: classname, submit_remove_class: 'Remove' }.to change { media_object.reload.read_groups }.from([classname]).to([])
+ end
+ it "grants and revokes special read access to ips" do
+ expect { put :update, id: media_object.id, step: 'access-control', donot_advance: 'true', add_ipaddress: ipaddr, submit_add_ipaddress: 'Add' }.to change { media_object.reload.read_groups }.from([]).to([ipaddr])
+ expect { put :update, id: media_object.id, step: 'access-control', donot_advance: 'true', remove_ipaddress: ipaddr, submit_remove_ipaddress: 'Remove' }.to change { media_object.reload.read_groups }.from([ipaddr]).to([])
+ end
+ end
+
+ context "grant and revoke time-based special read access" do
+ it "should grant and revoke time-based access for users" do
+ expect {
+ put :update, id: media_object.id, step: 'access-control', donot_advance: 'true', add_user: user, submit_add_user: 'Add', add_user_begin: Date.yesterday, add_user_end: Date.tomorrow
+ media_object.reload
+ }.to change{media_object.governing_policies.count}.by(1)
+ expect(media_object.governing_policies.last.class).to eq(Lease)
+ lease_pid = media_object.reload.governing_policies.last.pid
+ expect {
+ put :update, id: media_object.id, step: 'access-control', donot_advance: 'true', remove_lease: lease_pid
+ media_object.reload
+ }.to change{media_object.governing_policies.count}.by(-1)
+ end
+ end
+
+ context "must validate lease date ranges" do
+ it "should accept valid date range for lease" do
+ expect {
+ put :update, id: media_object.id, step: 'access-control', donot_advance: 'true', add_user: user, submit_add_user: 'Add', add_user_begin: Date.today, add_user_end: Date.tomorrow
+ media_object.reload
+ }.to change{media_object.governing_policies.count}.by(1)
+ end
+ it "should reject reverse date range for lease" do
+ expect {
+ put :update, id: media_object.id, step: 'access-control', donot_advance: 'true', add_user: user, submit_add_user: 'Add', add_user_begin: Date.tomorrow, add_user_end: Date.today
+ media_object.reload
+ }.not_to change{media_object.governing_policies.count}
+ end
+ it "should accept missing begin date and set it to today" do
+ expect {
+ put :update, id: media_object.id, step: 'access-control', donot_advance: 'true', add_user: user, submit_add_user: 'Add', add_user_begin: '', add_user_end: Date.tomorrow
+ media_object.reload
+ }.to change{media_object.governing_policies.count}.by(1)
+ expect(media_object.governing_policies.last.begin_time).to eq(Date.today)
+ end
+ it "should reject missing end date" do
+ expect {
+ put :update, id: media_object.id, step: 'access-control', donot_advance: 'true', add_user: user, submit_add_user: 'Add', add_user_begin: Date.tomorrow, add_user_end: ''
+ media_object.reload
+ }.not_to change{media_object.governing_policies.count}
+ end
+ end
+ end
+ end
+
+ describe "#show_progress" do
+ it "should return information about the processing state of the media object's parts" do
+ media_object = FactoryGirl.create(:media_object_with_master_file)
+ login_as 'administrator'
+ get :show_progress, id: media_object.id, format: 'json'
+ expect(JSON.parse(response.body)["overall"]).to_not be_empty
+ end
+ it "should return something even if the media object has no parts" do
+ media_object = FactoryGirl.create(:media_object)
+ login_as 'administrator'
+ get :show_progress, id: media_object.id, format: 'json'
+ expect(JSON.parse(response.body)["overall"]).to_not be_empty
+ end
+ end
+
+ describe "#set_session_quality" do
+ it "should set the posted quality in the session" do
+ login_as 'administrator'
+ post :set_session_quality, quality: 'crazy_high'
+ expect(@request.session[:quality]).to eq('crazy_high')
+ end
end
end
diff --git a/spec/controllers/object_controller_spec.rb b/spec/controllers/object_controller_spec.rb
index ac7baa18ea..bbda565a54 100644
--- a/spec/controllers/object_controller_spec.rb
+++ b/spec/controllers/object_controller_spec.rb
@@ -18,25 +18,25 @@
describe "#show" do
it "should redirect you to root if no object is found" do
get :show, id: 'avalon:bad-pid'
- response.should redirect_to root_path
+ expect(response).to redirect_to root_path
end
it "should redirect you to the object show page" do
obj = FactoryGirl.create(:media_object)
get :show, id: obj.pid
- response.should redirect_to(media_object_path(obj))
+ expect(response).to redirect_to(media_object_path(obj))
end
it "should pass along request params" do
obj = FactoryGirl.create(:media_object)
get :show, id: obj.pid, foo: 'bar'
- response.should redirect_to(media_object_path(obj, {foo: 'bar'}))
+ expect(response).to redirect_to(media_object_path(obj, {foo: 'bar'}))
end
it "should redirect to appended url" do
obj = FactoryGirl.create(:media_object)
get :show, id: obj.pid, urlappend: 'test'
- response.should redirect_to(media_object_path(obj)+"/test")
+ expect(response).to redirect_to(media_object_path(obj)+"/test")
end
end
diff --git a/spec/controllers/playlist_items_controller_spec.rb b/spec/controllers/playlist_items_controller_spec.rb
new file mode 100644
index 0000000000..5bb6e4e269
--- /dev/null
+++ b/spec/controllers/playlist_items_controller_spec.rb
@@ -0,0 +1,111 @@
+require 'spec_helper'
+
+RSpec.describe PlaylistItemsController, type: :controller do
+ # This should return the minimal set of attributes required to create a valid
+ # PlaylistItem. As you add validations to PlaylistItem, be sure to
+ # adjust the attributes here as well.
+ let(:valid_attributes) do
+ { title: Faker::Lorem.word, start_time: "00:00:00", end_time: "00:01:37", master_file_id: master_file.pid }
+ end
+
+ let(:invalid_attributes) do
+ { title: "", start_time: 'not-a-time', end_time: 'not-a-time' }
+ end
+
+ let(:invalid_times) do
+ { title: Faker::Lorem.word, start_time: 0.0, end_time: 'not-a-time', master_file_id: master_file.pid }
+ end
+
+ # This should return the minimal set of values that should be in the session
+ # in order to pass any filters (e.g. authentication) defined in
+ # PlaylistsController. Be sure to keep this updated too.
+ let(:valid_session) { {} }
+
+ let(:user) { login_as :user }
+ let(:playlist) { FactoryGirl.create(:playlist, user: user) }
+ let(:master_file) { FactoryGirl.create(:master_file, duration: "100000") }
+
+ describe 'security' do
+ let(:playlist) { FactoryGirl.create(:playlist) }
+ let(:playlist_item) { FactoryGirl.create(:playlist_item, playlist: playlist) }
+ context 'with unauthenticated user' do
+ it "all routes should redirect to sign in" do
+ expect(post :create, playlist_id: playlist.to_param, playlist_item: valid_attributes).to redirect_to(new_user_session_path)
+ expect(put :update, playlist_id: playlist.to_param, id: playlist_item.id).to redirect_to(new_user_session_path)
+ end
+ end
+ context 'with end-user' do
+ before do
+ login_as :user
+ end
+ it "all routes should redirect to /" do
+ expect(post :create, playlist_id: playlist.to_param, playlist_item: valid_attributes).to have_http_status(:unauthorized)
+ expect(put :update, playlist_id: playlist.to_param, id: playlist_item.id).to have_http_status(:unauthorized)
+ end
+ end
+ end
+
+
+ describe 'POST #create' do
+
+ context 'with valid params' do
+ it 'creates a new Playlist Item' do
+ expect do
+ post :create, { playlist_id: playlist.to_param, playlist_item: valid_attributes }, valid_session
+ end.to change(PlaylistItem, :count).by(1)
+ end
+
+ it 'creates a new AvalonAnnotation' do
+ expect do
+ post :create, { playlist_id: playlist.to_param, playlist_item: valid_attributes }, valid_session
+ end.to change(AvalonAnnotation, :count).by(1)
+ expect(AvalonAnnotation.last.start_time).to eq (0.0)
+ expect(AvalonAnnotation.last.end_time).to eq (97000.0)
+ end
+
+ it 'adds the Playlist Item to the playlist' do
+ expect do
+ post :create, { playlist_id: playlist.to_param, playlist_item: valid_attributes }, valid_session
+ end.to change { playlist.reload.items.size }.by(1)
+ end
+
+ it 'responds with 201 CREATED status code' do
+ post :create, { playlist_id: playlist.to_param, playlist_item: valid_attributes }, valid_session
+ expect(response).to have_http_status(:created)
+ end
+
+ it 'responds with a flash message with link to playlist' do
+ post :create, { playlist_id: playlist.to_param, playlist_item: valid_attributes }, valid_session
+ expect(JSON.parse(response.body)['message']).to include('Add to playlist was successful.')
+ expect(JSON.parse(response.body)['message']).to include(playlist_url(playlist))
+ end
+ end
+
+ context 'with invalid params' do
+ it 'invalid times respond with a 400 BAD REQUEST' do
+ post :create, { playlist_id: playlist.to_param, playlist_item: invalid_times }, valid_session
+ expect(response).to have_http_status(:bad_request)
+ end
+ end
+ end
+ describe 'PATCH #update' do
+ let!(:video_master_file) { FactoryGirl.create(:master_file, duration: "200000") }
+ let!(:annotation) { AvalonAnnotation.create(master_file: video_master_file, title: Faker::Lorem.word, comment: Faker::Lorem.sentence, start_time: 1000, end_time: 2000) }
+ let!(:playlist_item) { PlaylistItem.create!(playlist_id: playlist.id, annotation_id: annotation.id) }
+
+ context 'with valid params' do
+ it 'updates Playlist Item' do
+ expect do
+ patch :update, { playlist_id: playlist.id, id: playlist_item.id, playlist_item: { title: Faker::Lorem.word, start_time:'00:20', end_time:'1:20' }}, valid_session
+ end.to change{ playlist_item.reload.title }
+ end
+ end
+ context 'with invalid params' do
+ it 'fails to update Playlist Item' do
+ expect do
+ patch :update, { playlist_id: playlist.id, id: playlist_item.id, playlist_item: { title: Faker::Lorem.word, start_time:'00:20', end_time:'not-a-time' }}, valid_session
+ end.not_to change{ playlist_item.reload.title }
+ end
+ end
+ end
+end
diff --git a/spec/controllers/playlists_controller_spec.rb b/spec/controllers/playlists_controller_spec.rb
new file mode 100644
index 0000000000..1f82f85f1a
--- /dev/null
+++ b/spec/controllers/playlists_controller_spec.rb
@@ -0,0 +1,239 @@
+require 'spec_helper'
+
+# This spec was generated by rspec-rails when you ran the scaffold generator.
+# It demonstrates how one might use RSpec to specify the controller code that
+# was generated by Rails when you ran the scaffold generator.
+#
+# It assumes that the implementation code is generated by the rails scaffold
+# generator. If you are using any extension libraries to generate different
+# controller code, this generated spec may or may not pass.
+#
+# It only uses APIs available in rails and/or rspec-rails. There are a number
+# of tools you can use to make these specs even more expressive, but we're
+# sticking to rails and rspec-rails APIs to keep things simple and stable.
+#
+# Compared to earlier versions of this generator, there is very limited use of
+# stubs and message expectations in this spec. Stubs are only used when there
+# is no simpler way to get a handle on the object needed for the example.
+# Message expectations are only used when there is no simpler way to specify
+# that an instance is receiving a specific message.
+
+RSpec.describe PlaylistsController, type: :controller do
+ # This should return the minimal set of attributes required to create a valid
+ # Playlist. As you add validations to Playlist, be sure to
+ # adjust the attributes here as well.
+ let(:valid_attributes) do
+ { title: Faker::Lorem.word, visibility: Playlist::PUBLIC, user: user }
+ end
+
+ let(:invalid_attributes) do
+ { visibility: 'unknown' }
+ end
+
+ # This should return the minimal set of values that should be in the session
+ # in order to pass any filters (e.g. authentication) defined in
+ # PlaylistsController. Be sure to keep this updated too.
+ let(:valid_session) { {} }
+
+ let(:user) { login_as :user }
+
+ describe 'security' do
+ let(:playlist) { FactoryGirl.create(:playlist) }
+ context 'with unauthenticated user' do
+ #New is isolated here due to issues caused by the controller instance not being regenerated
+ it "should redirect to sign in" do
+ expect(get :new).to redirect_to(new_user_session_path)
+ end
+ it "all routes should redirect to sign in" do
+ expect(get :index).to redirect_to(new_user_session_path)
+ expect(get :edit, id: playlist.id).to redirect_to(new_user_session_path)
+ expect(post :create).to redirect_to(new_user_session_path)
+ expect(put :update, id: playlist.id).to redirect_to(new_user_session_path)
+ expect(put :update_multiple, id: playlist.id).to redirect_to(new_user_session_path)
+ expect(delete :destroy, id: playlist.id).to redirect_to(new_user_session_path)
+ end
+ context 'with a public playlist' do
+ let(:playlist) { FactoryGirl.create(:playlist, visibility: Playlist::PUBLIC) }
+ it "should return the playlist view page" do
+ expect(get :show, id: playlist.id).not_to redirect_to(new_user_session_path)
+ end
+ end
+ context 'with a private playlist' do
+ it "should NOT return the playlist view page" do
+ expect(get :show, id: playlist.id).to redirect_to(new_user_session_path)
+ end
+ end
+ end
+ context 'with end-user' do
+ before do
+ login_as :user
+ end
+ it "all routes should redirect to /" do
+ expect(get :edit, id: playlist.id).to redirect_to(root_path)
+ expect(put :update, id: playlist.id).to redirect_to(root_path)
+ expect(put :update_multiple, id: playlist.id).to redirect_to(root_path)
+ expect(delete :destroy, id: playlist.id).to redirect_to(root_path)
+ end
+ context 'with a public playlist' do
+ let(:playlist) { FactoryGirl.create(:playlist, visibility: Playlist::PUBLIC) }
+ it "should return the playlist view page" do
+ expect(get :show, id: playlist.id).not_to redirect_to(root_path)
+ end
+ end
+ context 'with a private playlist' do
+ it "should NOT return the playlist view page" do
+ expect(get :show, id: playlist.id).to redirect_to(root_path)
+ end
+ end
+ end
+ end
+
+ describe 'GET #index' do
+ it 'assigns accessible playlists as @playlists' do
+ # TODO: test non-accessible playlists not appearing
+ playlist = Playlist.create! valid_attributes
+ get :index, {}, valid_session
+ expect(assigns(:playlists)).to eq([playlist])
+ end
+ end
+
+ describe 'GET #show' do
+ it 'assigns the requested playlist as @playlist' do
+ playlist = Playlist.create! valid_attributes
+ get :show, { id: playlist.to_param }, valid_session
+ expect(assigns(:playlist)).to eq(playlist)
+ end
+ # TODO: write tests for public/private playists
+ end
+
+ describe 'GET #new' do
+ before do
+ login_as :user
+ end
+ it 'assigns a new playlist as @playlist' do
+ get :new, {}, valid_session
+ expect(assigns(:playlist)).to be_a_new(Playlist)
+ end
+ end
+
+ describe 'GET #edit' do
+ it 'assigns the requested playlist as @playlist' do
+ playlist = Playlist.create! valid_attributes
+ get :edit, { id: playlist.to_param }, valid_session
+ expect(assigns(:playlist)).to eq(playlist)
+ end
+ end
+
+ describe 'POST #create' do
+ context 'with valid params' do
+ it 'creates a new Playlist' do
+ expect do
+ post :create, { playlist: valid_attributes }, valid_session
+ end.to change(Playlist, :count).by(1)
+ end
+
+ it 'assigns a newly created playlist as @playlist' do
+ post :create, { playlist: valid_attributes }, valid_session
+ expect(assigns(:playlist)).to be_a(Playlist)
+ expect(assigns(:playlist)).to be_persisted
+ end
+
+ it 'redirects to the created playlist' do
+ post :create, { playlist: valid_attributes }, valid_session
+ expect(response).to redirect_to(Playlist.last)
+ end
+ end
+
+ context 'with invalid params' do
+ before do
+ login_as :user
+ end
+ it 'assigns a newly created but unsaved playlist as @playlist' do
+ post :create, { playlist: invalid_attributes }, valid_session
+ expect(assigns(:playlist)).to be_a_new(Playlist)
+ end
+
+ it "re-renders the 'new' template" do
+ post :create, { playlist: invalid_attributes }, valid_session
+ expect(response).to render_template('new')
+ end
+ end
+ end
+
+ describe 'PUT #update' do
+ context 'with valid params' do
+ let(:new_attributes) do
+ { title: Faker::Lorem.word, visibility: Playlist::PUBLIC, comment: Faker::Lorem.sentence }
+ end
+
+ it 'updates the requested playlist' do
+ playlist = Playlist.create! valid_attributes
+ put :update, { id: playlist.to_param, playlist: new_attributes }, valid_session
+ playlist.reload
+ expect(playlist.title).to eq new_attributes[:title]
+ expect(playlist.visibility).to eq new_attributes[:visibility]
+ expect(playlist.comment).to eq new_attributes[:comment]
+ end
+
+ it 'assigns the requested playlist as @playlist' do
+ playlist = Playlist.create! valid_attributes
+ put :update, { id: playlist.to_param, playlist: valid_attributes }, valid_session
+ expect(assigns(:playlist)).to eq(playlist)
+ end
+
+ it 'redirects to edit playlist' do
+ playlist = Playlist.create! valid_attributes
+ put :update, { id: playlist.to_param, playlist: valid_attributes }, valid_session
+ expect(response).to redirect_to(edit_playlist_path(playlist))
+ end
+ end
+
+ context 'with invalid params' do
+ it 'assigns the playlist as @playlist' do
+ playlist = Playlist.create! valid_attributes
+ put :update, { id: playlist.to_param, playlist: invalid_attributes }, valid_session
+ expect(assigns(:playlist)).to eq(playlist)
+ end
+
+ it "re-renders the 'edit' template" do
+ playlist = Playlist.create! valid_attributes
+ put :update, { id: playlist.to_param, playlist: invalid_attributes }, valid_session
+ expect(response).to render_template('edit')
+ end
+ end
+ end
+
+ describe 'PUT #update_multiple' do
+ context 'delete' do
+ it 'redirects to edit playlist' do
+ playlist = Playlist.create! valid_attributes
+ put :update_multiple, { id: playlist.to_param, annotation_ids: [] }, valid_session
+ expect(response).to redirect_to(edit_playlist_path(playlist))
+ end
+ end
+ end
+
+ describe 'DELETE #destroy' do
+ it 'destroys the requested playlist' do
+ playlist = Playlist.create! valid_attributes
+ expect do
+ delete :destroy, { id: playlist.to_param }, valid_session
+ end.to change(Playlist, :count).by(-1)
+ end
+
+ it 'redirects to the playlists list' do
+ playlist = Playlist.create! valid_attributes
+ delete :destroy, { id: playlist.to_param }, valid_session
+ expect(response).to redirect_to(playlists_url)
+ end
+ end
+
+ describe 'GET #edit' do
+ it 'assigns the requested playlist as @playlist' do
+ playlist = Playlist.create! valid_attributes
+ get :edit, { id: playlist.to_param }, valid_session
+ expect(assigns(:playlist)).to eq(playlist)
+ end
+ end
+
+end
diff --git a/spec/controllers/vocabulary_controller_spec.rb b/spec/controllers/vocabulary_controller_spec.rb
new file mode 100644
index 0000000000..cdc20706cf
--- /dev/null
+++ b/spec/controllers/vocabulary_controller_spec.rb
@@ -0,0 +1,111 @@
+# Copyright 2011-2015, The Trustees of Indiana University and Northwestern
+# University. Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+#
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed
+# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+# CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+# --- END LICENSE_HEADER BLOCK ---
+
+require 'spec_helper'
+require 'fileutils'
+
+describe VocabularyController, type: :controller do
+ render_views
+
+ before(:all) {
+ FileUtils.cp_r 'spec/fixtures/controlled_vocabulary.yml', 'spec/fixtures/controlled_vocabulary.yml.tmp'
+ Avalon::ControlledVocabulary.class_variable_set :@@path, Rails.root.join('spec/fixtures/controlled_vocabulary.yml.tmp')
+ }
+ after(:all) {
+ File.delete('spec/fixtures/controlled_vocabulary.yml.tmp')
+ }
+
+ before do
+ request.headers['Avalon-Api-Key'] = 'secret_token'
+ end
+
+ describe 'security' do
+ let(:vocab) { :units }
+ describe 'ingest api' do
+ it "all routes should return 401 when no token is present" do
+ request.headers['Avalon-Api-Key'] = nil
+ expect(get :index, format: 'json').to have_http_status(401)
+ expect(get :show, id: vocab, format: 'json').to have_http_status(401)
+ expect(put :update, id: vocab, format: 'json').to have_http_status(401)
+ expect(patch :update, id: vocab, format: 'json').to have_http_status(401)
+ end
+ it "all routes should return 403 when a bad token in present" do
+ request.headers['Avalon-Api-Key'] = 'badtoken'
+ expect(get :index, format: 'json').to have_http_status(403)
+ expect(get :show, id: vocab, format: 'json').to have_http_status(403)
+ expect(put :update, id: vocab, format: 'json').to have_http_status(403)
+ expect(patch :update, id: vocab, format: 'json').to have_http_status(403)
+ end
+ end
+ describe 'normal auth' do
+ context 'with end-user' do
+ before do
+ request.headers['Avalon-Api-Key'] = nil
+ login_as :user
+ end
+ it "all routes should redirect to /" do
+ expect(get :index, format: 'json').to redirect_to(root_path)
+ expect(get :show, id: vocab, format: 'json').to redirect_to(root_path)
+ expect(put :update, id: vocab, format: 'json').to redirect_to(root_path)
+ expect(patch :update, id: vocab, format: 'json').to redirect_to(root_path)
+ end
+ end
+ end
+ end
+
+ describe "#index" do
+ it "should return vocabulary for entire app" do
+ get 'index', format:'json'
+ expect(JSON.parse(response.body)).to include('units','note_types','identifier_types')
+ end
+ end
+ describe "#show" do
+ it "should return a particular vocabulary" do
+ get 'show', format:'json', id: :units
+ expect(JSON.parse(response.body)).to include('Default Unit')
+ end
+ it "should return 404 if requested vocabulary not present" do
+ get 'show', format:'json', id: :doesnt_exist
+ expect(response.status).to eq(404)
+ expect(JSON.parse(response.body)["errors"].class).to eq Array
+ expect(JSON.parse(response.body)["errors"].first.class).to eq String
+ end
+ end
+ describe "#update" do
+ it "should add unit to controlled vocabulary" do
+ put 'update', format:'json', id: :units, entry: 'New Unit'
+ expect(Avalon::ControlledVocabulary.vocabulary[:units]).to include("New Unit")
+ end
+ it "should return 404 if requested vocabulary not present" do
+ put 'update', format:'json', id: :doesnt_exist, entry: 'test'
+ expect(response.status).to eq(404)
+ expect(JSON.parse(response.body)["errors"].class).to eq Array
+ expect(JSON.parse(response.body)["errors"].first.class).to eq String
+ end
+ it "should return 422 if no new value sent" do
+ put 'update', format:'json', id: :units
+ expect(response.status).to eq(422)
+ expect(JSON.parse(response.body)["errors"].class).to eq Array
+ expect(JSON.parse(response.body)["errors"].first.class).to eq String
+ end
+ it "should return 422 if update fails" do
+ allow(Avalon::ControlledVocabulary).to receive(:vocabulary=).and_return(false)
+ put 'update', format:'json', id: :units
+ expect(response.status).to eq(422)
+ expect(JSON.parse(response.body)["errors"].class).to eq Array
+ expect(JSON.parse(response.body)["errors"].first.class).to eq String
+ end
+ end
+
+end
diff --git a/spec/factories/avalon_annotation.rb b/spec/factories/avalon_annotation.rb
new file mode 100644
index 0000000000..c1a1ed6d07
--- /dev/null
+++ b/spec/factories/avalon_annotation.rb
@@ -0,0 +1,23 @@
+# Copyright 2011-2015, The Trustees of Indiana University and Northwestern
+# University. Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+#
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed
+# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+# CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+# --- END LICENSE_HEADER BLOCK ---
+
+FactoryGirl.define do
+ factory :avalon_annotation do
+ master_file { FactoryGirl.create(:master_file) }
+ title { Faker::Lorem.word }
+ comment { Faker::Lorem.sentence }
+ start_time { 0.0 }
+ end_time { 100.0 }
+ end
+end
diff --git a/spec/factories/collections.rb b/spec/factories/collections.rb
index d97be03dd1..6156cbf128 100644
--- a/spec/factories/collections.rb
+++ b/spec/factories/collections.rb
@@ -25,6 +25,7 @@
transient { items 0 }
after(:create) do |c, env|
1.upto(env.items) { FactoryGirl.create(:media_object, collection: c) }
+ c.reload
end
end
end
diff --git a/spec/factories/lease.rb b/spec/factories/lease.rb
new file mode 100644
index 0000000000..b342df1040
--- /dev/null
+++ b/spec/factories/lease.rb
@@ -0,0 +1,20 @@
+# Copyright 2011-2015, The Trustees of Indiana University and Northwestern
+# University. Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+#
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed
+# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+# CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+# --- END LICENSE_HEADER BLOCK ---
+
+FactoryGirl.define do
+ factory :lease, class: Lease do
+ begin_time { Date.yesterday }
+ end_time { Date.tomorrow }
+ end
+end
diff --git a/spec/factories/master_files.rb b/spec/factories/master_files.rb
index 12c9ebbd90..ce0b44c2ac 100644
--- a/spec/factories/master_files.rb
+++ b/spec/factories/master_files.rb
@@ -1,14 +1,14 @@
# Copyright 2011-2015, The Trustees of Indiana University and Northwestern
# University. Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
-#
+#
# You may obtain a copy of the License at
-#
+#
# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software distributed
+#
+# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
-# CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
# --- END LICENSE_HEADER BLOCK ---
@@ -18,7 +18,8 @@
file_format {'Moving image'}
percent_complete {"#{rand(100)}"}
workflow_name 'avalon'
- mediaobject {FactoryGirl.create(:media_object)}
+ duration {'100'}
+ association :mediaobject, factory: :media_object
factory :master_file_with_derivative do
workflow_name 'avalon'
@@ -35,5 +36,17 @@
mf.save
end
end
+ factory :master_file_with_structure do
+ after(:create) do |mf|
+ mf.structuralMetadata.content = File.read('spec/fixtures/structure.xml')
+ mf.save
+ end
+ end
+ factory :master_file_sound do
+ after(:create) do |mf|
+ mf.file_format = 'Sound'
+ mf.save
+ end
+ end
end
end
diff --git a/spec/factories/media_objects.rb b/spec/factories/media_objects.rb
index 673b863501..cf9ff5de62 100644
--- a/spec/factories/media_objects.rb
+++ b/spec/factories/media_objects.rb
@@ -32,7 +32,7 @@
topical_subject {[Faker::Lorem.word]}
temporal_subject {[Faker::Lorem.word]}
geographic_subject {[Faker::Address.country]}
- physical_description {Faker::Lorem.word}
+ physical_description {[Faker::Lorem.word]}
table_of_contents {[Faker::Lorem.paragraph]}
after(:create) do |mo|
mo.update_datastream(:descMetadata, {
@@ -55,6 +55,12 @@
mo.save
end
end
+ factory :media_object_with_completed_workflow do
+ after(:create) do |mo|
+ mo.workflow.last_completed_step = [HYDRANT_STEPS.last.step]
+ mo.save
+ end
+ end
end
factory :minimal_record, class: MediaObject do
@@ -78,8 +84,9 @@
factory :multiple_entries, class: MediaObject do
title 'Multiple contributors'
creator ['RSpec']
- date_issued '#{Date.today.edtf}'
+ date_issued {"#{Date.today.edtf}"}
abstract 'A record with multiple contributors, publishers, and search terms'
+ collection {FactoryGirl.create(:collection)}
contributor ['Chris Colvard', 'Nathan Rogers', 'Phuong Dinh']
publisher ['Mark Notess', 'Jon Dunn', 'Stu Baker']
diff --git a/spec/factories/playlist.rb b/spec/factories/playlist.rb
new file mode 100644
index 0000000000..1dee32b4ce
--- /dev/null
+++ b/spec/factories/playlist.rb
@@ -0,0 +1,22 @@
+# Copyright 2011-2015, The Trustees of Indiana University and Northwestern
+# University. Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+#
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed
+# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+# CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+# --- END LICENSE_HEADER BLOCK ---
+
+FactoryGirl.define do
+ factory :playlist do
+ user
+ title { Faker::Lorem.word }
+ comment { Faker::Lorem.sentence }
+ visibility { Playlist::PRIVATE }
+ end
+end
diff --git a/spec/factories/playlist_item.rb b/spec/factories/playlist_item.rb
new file mode 100644
index 0000000000..9b82b52124
--- /dev/null
+++ b/spec/factories/playlist_item.rb
@@ -0,0 +1,20 @@
+# Copyright 2011-2015, The Trustees of Indiana University and Northwestern
+# University. Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+#
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed
+# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+# CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+# --- END LICENSE_HEADER BLOCK ---
+
+FactoryGirl.define do
+ factory :playlist_item do
+ playlist
+ association :annotation, factory: :avalon_annotation
+ end
+end
diff --git a/spec/fixtures/7763100-ns.xml b/spec/fixtures/7763100-ns.xml
new file mode 100644
index 0000000000..a5dfea1ec0
--- /dev/null
+++ b/spec/fixtures/7763100-ns.xml
@@ -0,0 +1,271 @@
+
+1.1 1 info:srw/schema/1/marcxml-v1.1 xml
+ 01523cgm a2200433 a 4500
+ 7763100
+ 20150127160330.0
+ aj can|n
+ 150127r19991990ilu--- kneng d
+
+ IEN
+ IEN
+
+
+ eng
+ fre
+ ger
+
+
+ 111 A
+ C
+ D
+ E
+ F
+ G
+ K
+ L
+ N
+ P
+ Q
+ T
+
+
+ 245 A :
+ B
+ F
+ G
+ K
+ N
+ P
+ S /
+ C.
+
+
+ 246
+
+
+ 260 A :
+ 260 B,
+ 2010.
+
+
+ 260 A :
+ 260 B,
+ 2014.
+
+
+ 300 A :
+ 300 B ;
+ 300 C.
+
+
+ 600 (100) A
+ B
+ C
+ D
+ F
+ G
+ K
+ L
+ N
+ P
+ Q
+ T
+
+
+ 610 (110) A
+ B
+ B
+ C
+ D
+ F
+ G
+ K
+ L
+ N
+ P
+ T
+
+
+ 611 (111) A
+ C
+ D
+ E
+ F
+ G
+ K
+ L
+ N
+ P
+ Q
+ T
+
+
+ 500
+
+
+ 520
+
+
+ 530
+
+
+ 540
+
+
+ 600 A
+ B
+ C
+ D
+ F
+ G
+ K
+ L
+ M
+ N
+ O
+ P
+ Q
+ R
+ S
+ T
+ 600 X
+ 600 Y
+ 600 Z
+ 600 V
+
+
+ 610 A
+ B
+ B
+ C
+ D
+ F
+ G
+ K
+ L
+ N
+ P
+ T
+ 610 X
+ 610 Y
+ 610 Z
+ 610 V
+
+
+ 611 A
+ C
+ D
+ E
+ F
+ G
+ K
+ L
+ N
+ P
+ Q
+ S
+ T
+ 611 X
+ 611 Y
+ 611 Z
+ 611 V
+
+
+ 630 A
+ D
+ F
+ H
+ K
+ L
+ O
+ R
+ 630 X
+ 630 Y
+ 630 Z
+ 630 V
+
+
+ 648
+ lcsh.
+
+
+ 648
+
+
+ 650 A
+ 650 X
+ 650 Y
+ 650 Z
+ 650 V
+
+
+ 651 A
+ 651 X
+ 651 Y
+ 651 Z
+ 651 V
+
+
+ 653 A
+ 653 A
+
+
+ 655 A
+
+
+ 700 A
+ B
+ C
+ D
+ F
+ G
+ K
+ L
+ M
+ N
+ O
+ P
+ Q
+ R
+ S
+ T
+
+
+ 710 A
+ B
+ B
+ C
+ D
+ F
+ G
+ K
+ L
+ N
+ P
+ T
+
+
+ 711 A
+ C
+ D
+ E
+ F
+ G
+ K
+ L
+ N
+ P
+ Q
+ S
+ T
+
+
+ 752 A
+ 752 B
+ 752 C
+ 752 D
+
+
+ suppressed record for testing Avalon.
+
+ 1
diff --git a/spec/fixtures/controlled_vocabulary.yml b/spec/fixtures/controlled_vocabulary.yml
new file mode 100644
index 0000000000..37ed03f8d8
--- /dev/null
+++ b/spec/fixtures/controlled_vocabulary.yml
@@ -0,0 +1,22 @@
+---
+:units:
+- Default Unit
+:identifier_types:
+ local: Catalog Key
+ oclc: OCLC
+ lccn: LCCN
+ issue number: Issue Number
+ matrix number: Matrix Number
+ music publisher: Music Publisher/Label
+ videorecording identifier: Videorecording Identifier
+ other: Other
+:note_types:
+ general: General Note
+ awards: Awards
+ biographical/historical: Bibliographical/Historical Note
+ creation/production credits: Creation/Production Credits
+ language: Language Note
+ local: Local Note
+ performers: Performers
+ statement of responsibility: Statement of Responsibility
+ venue: Venue/Event Date
diff --git a/spec/fixtures/dropbox/example_batch_ingest/assets/sheephead_mountain.mov.vtt b/spec/fixtures/dropbox/example_batch_ingest/assets/sheephead_mountain.mov.vtt
new file mode 100644
index 0000000000..0e9a211f7d
--- /dev/null
+++ b/spec/fixtures/dropbox/example_batch_ingest/assets/sheephead_mountain.mov.vtt
@@ -0,0 +1,33 @@
+WEBVTT FILE
+
+1
+00:00:03.500 --> 00:00:05.000 D:vertical A:start
+Everyone wants the most from life
+
+2
+00:00:06.000 --> 00:00:09.000 A:start
+Like internet experiences that are rich and entertaining
+
+3
+00:00:11.000 --> 00:00:14.000 A:end
+Phone conversations where people truly connect
+
+4
+00:00:14.500 --> 00:00:18.000
+Your favourite TV programmes ready to watch at the touch of a button
+
+5
+00:00:19.000 --> 00:00:24.000
+Which is why we are bringing TV, internet and phone together in one super package
+
+6
+00:00:24.500 --> 00:00:26.000
+One simple way to get everything
+
+7
+00:00:26.500 --> 00:00:27.500 L:12%
+UPC
+
+8
+00:00:28.000 --> 00:00:30.000 L:75%
+Simply for everyone
\ No newline at end of file
diff --git a/spec/fixtures/dropbox/example_batch_ingest/batch_manifest.xlsx b/spec/fixtures/dropbox/example_batch_ingest/batch_manifest.xlsx
index f748131aed..57e74e858c 100644
Binary files a/spec/fixtures/dropbox/example_batch_ingest/batch_manifest.xlsx and b/spec/fixtures/dropbox/example_batch_ingest/batch_manifest.xlsx differ
diff --git a/spec/fixtures/dropbox/example_batch_ingest/missing_required_field.xlsx b/spec/fixtures/dropbox/example_batch_ingest/missing_required_field.xlsx
index 83af06efc1..89960b4b09 100644
Binary files a/spec/fixtures/dropbox/example_batch_ingest/missing_required_field.xlsx and b/spec/fixtures/dropbox/example_batch_ingest/missing_required_field.xlsx differ
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index acdaa50bbd..b14fa9a579 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -17,39 +17,39 @@
describe ApplicationHelper do
describe "#active_for_controller" do
before(:each) do
- helper.stub(:params).and_return({controller: 'media_objects'})
+ allow(helper).to receive(:params).and_return({controller: 'media_objects'})
end
it "should return 'active' for matching controller names" do
- helper.active_for_controller('media_objects').should == 'active'
+ expect(helper.active_for_controller('media_objects')).to eq('active')
end
it "should return '' for non-matching controller names" do
- helper.active_for_controller('master_files').should == ''
+ expect(helper.active_for_controller('master_files')).to eq('')
end
it "should handle name-spaced controllers" do
- helper.stub(:params).and_return({controller: 'admin/groups'})
- helper.active_for_controller('admin/groups').should == 'active'
- helper.active_for_controller('groups').should == ''
+ allow(helper).to receive(:params).and_return({controller: 'admin/groups'})
+ expect(helper.active_for_controller('admin/groups')).to eq('active')
+ expect(helper.active_for_controller('groups')).to eq('')
end
end
describe "#stream_label_for" do
it "should return the label first if it is available" do
master_file = FactoryGirl.build(:master_file, label: 'Label')
- master_file.file_location.should_not be_nil
- master_file.label.should_not be_nil
- helper.stream_label_for(master_file).should == 'Label'
+ expect(master_file.file_location).not_to be_nil
+ expect(master_file.label).not_to be_nil
+ expect(helper.stream_label_for(master_file)).to eq('Label')
end
it "should return the filename second if it is available" do
master_file = FactoryGirl.build(:master_file, label: nil)
- master_file.file_location.should_not be_nil
- master_file.label.should be_nil
- helper.stream_label_for(master_file).should == File.basename(master_file.file_location)
+ expect(master_file.file_location).not_to be_nil
+ expect(master_file.label).to be_nil
+ expect(helper.stream_label_for(master_file)).to eq(File.basename(master_file.file_location))
end
it "should handle empty file_locations and labels" do
master_file = FactoryGirl.build(:master_file, file_location: nil, label: nil)
- master_file.file_location.should be_nil
- master_file.label.should be_nil
- helper.stream_label_for(master_file).should == master_file.pid
+ expect(master_file.file_location).to be_nil
+ expect(master_file.label).to be_nil
+ expect(helper.stream_label_for(master_file)).to eq(master_file.pid)
end
end
@@ -104,27 +104,27 @@
describe "#image_for" do
# image_for expects hash keys as labels, not strings
it "should return nil" do
- doc = {"mods_tesim" => [] }
+ doc = {"avalon_resource_type_tesim" => [] }
expect(helper.image_for(doc)).to eq(nil)
end
it "should return audio icon" do
- doc = {"mods_tesim" => ['sound recording 2', 'sound recording 1'] }
+ doc = {"avalon_resource_type_tesim" => ['sound recording 2', 'sound recording 1'] }
expect(helper.image_for(doc)).to eq('/assets/audio_icon.png')
end
it "should return video icon" do
- doc = {"mods_tesim" => ['moving image 1'] }
+ doc = {"avalon_resource_type_tesim" => ['moving image 1'] }
expect(helper.image_for(doc)).to eq('/assets/video_icon.png')
end
it "should return hybrid icon" do
- doc = {"mods_tesim" => ['moving image 1', 'sound recording 1'] }
+ doc = {"avalon_resource_type_tesim" => ['moving image 1', 'sound recording 1'] }
expect(helper.image_for(doc)).to eq('/assets/hybrid_icon.png')
end
it "should return nil when only unprocessed video" do
- doc = {"section_pid_tesim" => ['1'], "mods_tesim" => [] }
+ doc = {"section_pid_tesim" => ['1'], "avalon_resource_type_tesim" => [] }
expect(helper.image_for(doc)).to eq(nil)
end
it "should return thumbnail" do
- doc = {"section_pid_tesim" => ['1'], "mods_tesim" => ['moving image 1'] }
+ doc = {"section_pid_tesim" => ['1'], "avalon_resource_type_tesim" => ['moving image 1'] }
expect(helper.image_for(doc)).to eq('/master_files/1/thumbnail')
end
end
diff --git a/spec/integration/omniauth_callbacks_controller_spec.rb b/spec/integration/omniauth_callbacks_controller_spec.rb
index e1bc796f75..b11a6d0b94 100644
--- a/spec/integration/omniauth_callbacks_controller_spec.rb
+++ b/spec/integration/omniauth_callbacks_controller_spec.rb
@@ -28,7 +28,7 @@
before :each do
hide_const("Avalon::GROUP_LDAP")
- IMS::LTI::ToolProvider.any_instance.stub(:valid_request!) { true }
+ allow_any_instance_of(IMS::LTI::ToolProvider).to receive(:valid_request!) { true }
@old_config = Devise.omniauth_configs[:lti].options[:consumers]
Devise.omniauth_configs[:lti].options[:consumers] = Devise.omniauth_configs[:lti].strategy[:consumers] = lti_config
end
@@ -65,7 +65,7 @@
subject { Hash.new }
before :each do
- Users::OmniauthCallbacksController.any_instance.stub(:user_session) { subject }
+ allow_any_instance_of(Users::OmniauthCallbacksController).to receive(:user_session) { subject }
post '/users/auth/lti/callback', foo_hash
end
@@ -84,7 +84,7 @@
subject { Hash.new }
before :each do
- Users::OmniauthCallbacksController.any_instance.stub(:user_session) { subject }
+ allow_any_instance_of(Users::OmniauthCallbacksController).to receive(:user_session) { subject }
foo_hash.delete('lis_person_sourcedid')
post '/users/auth/lti/callback', foo_hash
end
@@ -102,14 +102,14 @@
subject { post '/users/auth/lti/callback', foo_hash }
let(:user_session) { Hash.new }
before :each do
- Users::OmniauthCallbacksController.any_instance.stub(:user_session) { user_session }
+ allow_any_instance_of(Users::OmniauthCallbacksController).to receive(:user_session) { user_session }
end
it "should redirect to the external group facet applied for the lti group" do
expect(subject).to redirect_to catalog_index_path('f[read_access_virtual_group_ssim][]' => user_session[:lti_group])
end
context 'when there are other external groups' do
before do
- User.any_instance.stub(:ldap_groups) { [Faker::Lorem.word] }
+ allow_any_instance_of(User).to receive(:ldap_groups) { [Faker::Lorem.word] }
end
it "should redirect to the external group facet applied for the lti group" do
expect(subject).to redirect_to catalog_index_path('f[read_access_virtual_group_ssim][]' => user_session[:lti_group])
diff --git a/spec/lib/avalon/batch_entry_spec.rb b/spec/lib/avalon/batch_entry_spec.rb
index f5a5f3d9dc..8f323ccfa3 100644
--- a/spec/lib/avalon/batch_entry_spec.rb
+++ b/spec/lib/avalon/batch_entry_spec.rb
@@ -38,9 +38,9 @@
let(:collection) {FactoryGirl.create(:collection)}
let(:manifest) do
manifest = double()
- manifest.stub_chain(:package, :dir).and_return(testdir)
- manifest.stub_chain(:package, :user, :user_key).and_return('archivist1@example.org')
- manifest.stub_chain(:package, :collection).and_return(collection)
+ allow(manifest).to receive_message_chain(:package, :dir).and_return(testdir)
+ allow(manifest).to receive_message_chain(:package, :user, :user_key).and_return('archivist1@example.org')
+ allow(manifest).to receive_message_chain(:package, :collection).and_return(collection)
manifest
end
let(:entry) do
diff --git a/spec/lib/avalon/batch_ingest_spec.rb b/spec/lib/avalon/batch_ingest_spec.rb
index 5e05b0c8e4..ae7095a882 100644
--- a/spec/lib/avalon/batch_ingest_spec.rb
+++ b/spec/lib/avalon/batch_ingest_spec.rb
@@ -43,7 +43,7 @@
# this is a test environment, we don't want to kick off
# generation jobs if possible
- MasterFile.any_instance.stub(:save).and_return(true)
+ allow_any_instance_of(MasterFile).to receive(:save).and_return(true)
end
describe 'valid manifest' do
@@ -72,8 +72,8 @@
it 'should send email when batch finishes processing' do
mailer = double('mailer').as_null_object
- IngestBatchMailer.should_receive(:batch_ingest_validation_success).with(duck_type(:each)).and_return(mailer)
- mailer.should_receive(:deliver)
+ expect(IngestBatchMailer).to receive(:batch_ingest_validation_success).with(duck_type(:each)).and_return(mailer)
+ expect(mailer).to receive(:deliver)
batch_ingest.ingest
end
@@ -84,21 +84,21 @@
expect { batch_ingest.ingest }.not_to raise_error
expect { batch_ingest.ingest }.not_to change{IngestBatch.count}
error_file = File.join(@dropbox_dir,'example_batch_ingest','bad_manifest.xlsx.error')
- File.exists?(error_file).should be true
- File.read(error_file).should =~ /^Invalid manifest/
+ expect(File.exists?(error_file)).to be true
+ expect(File.read(error_file)).to match(/^Invalid manifest/)
end
it 'should ingest batch with spaces in name' do
space_batch_path = File.join('spec/fixtures/dropbox/example batch ingest', 'batch manifest with spaces.xlsx')
space_batch = Avalon::Batch::Package.new(space_batch_path, collection)
- Avalon::Dropbox.any_instance.stub(:find_new_packages).and_return [space_batch]
+ allow_any_instance_of(Avalon::Dropbox).to receive(:find_new_packages).and_return [space_batch]
expect{batch_ingest.ingest}.to change{IngestBatch.count}.by(1)
end
it 'should ingest batch with skip-transcoding derivatives' do
derivatives_batch_path = File.join('spec/fixtures/dropbox/pretranscoded_batch_ingest', 'batch_manifest_derivatives.xlsx')
derivatives_batch = Avalon::Batch::Package.new(derivatives_batch_path, collection)
- Avalon::Dropbox.any_instance.stub(:find_new_packages).and_return [derivatives_batch]
+ allow_any_instance_of(Avalon::Dropbox).to receive(:find_new_packages).and_return [derivatives_batch]
expect_any_instance_of(MasterFile).to receive(:process).with(hash_including('quality-high', 'quality-medium', 'quality-low'))
expect{batch_ingest.ingest}.to change{IngestBatch.count}.by(1)
end
@@ -111,8 +111,8 @@
batch_ingest.ingest
ingest_batch = IngestBatch.first
media_object = MediaObject.find(ingest_batch.media_object_ids.last)
- media_object.bibliographic_id.should == ['local', bib_id]
- media_object.title.should == '245 A : B F G K N P S'
+ expect(media_object.bibliographic_id).to eq(['local', bib_id])
+ expect(media_object.title).to eq('245 A : B F G K N P S')
end
it 'should ingest structural metadata' do
@@ -120,7 +120,15 @@
ingest_batch = IngestBatch.first
media_object = MediaObject.find(ingest_batch.media_object_ids.first)
master_file = media_object.parts.first
- expect(master_file.structuralMetadata.has_content?).to be_true
+ expect(master_file.structuralMetadata.has_content?).to be_truthy
+ end
+
+ it 'should ingest captions' do
+ batch_ingest.ingest
+ ingest_batch = IngestBatch.first
+ media_object = MediaObject.find(ingest_batch.media_object_ids.first)
+ master_file = media_object.parts.first
+ expect(master_file.captions.has_content?).to be_truthy
end
it 'should set MasterFile details' do
@@ -128,56 +136,57 @@
ingest_batch = IngestBatch.last
media_object = MediaObject.find(ingest_batch.media_object_ids.first)
master_file = media_object.parts.first
- master_file.label.should == 'Quis quo'
- master_file.poster_offset.to_i.should == 500
- master_file.workflow_name.should == 'avalon'
- master_file.absolute_location.should == Avalon::FileResolver.new.path_to(master_file.file_location)
-
+ expect(master_file.label).to eq('Quis quo')
+ expect(master_file.poster_offset.to_i).to eq(500)
+ expect(master_file.workflow_name).to eq('avalon')
+ expect(master_file.absolute_location).to eq(Avalon::FileResolver.new.path_to(master_file.file_location))
+ expect(master_file.date_digitized).to eq('2015-10-30T00:00:00Z')
# if a master file is saved on a media object
# it should have workflow name set
# master_file.workflow_name.should be_nil
master_file = media_object.parts[1]
- master_file.label.should == 'Unde aliquid'
- master_file.poster_offset.to_i.should == 500
- master_file.workflow_name.should == 'avalon-skip-transcoding'
- master_file.absolute_location.should == 'file:///tmp/sheephead_mountain_master.mov'
+ expect(master_file.label).to eq('Unde aliquid')
+ expect(master_file.poster_offset.to_i).to eq(500)
+ expect(master_file.workflow_name).to eq('avalon-skip-transcoding')
+ expect(master_file.absolute_location).to eq('file:///tmp/sheephead_mountain_master.mov')
+ expect(master_file.date_digitized).to eq('2015-10-31T00:00:00Z')
master_file = media_object.parts[2]
- master_file.label.should == 'Audio'
- master_file.workflow_name.should == 'fullaudio'
- master_file.absolute_location.should == Avalon::FileResolver.new.path_to(master_file.file_location)
+ expect(master_file.label).to eq('Audio')
+ expect(master_file.workflow_name).to eq('fullaudio')
+ expect(master_file.absolute_location).to eq(Avalon::FileResolver.new.path_to(master_file.file_location))
end
it 'should set avalon_uploader' do
batch_ingest.ingest
ingest_batch = IngestBatch.last
media_object = MediaObject.find(ingest_batch.media_object_ids.first)
- media_object.avalon_uploader.should == 'frances.dickens@reichel.com'
+ expect(media_object.avalon_uploader).to eq('frances.dickens@reichel.com')
end
it 'should set hidden' do
batch_ingest.ingest
ingest_batch = IngestBatch.last
media_object = MediaObject.find(ingest_batch.media_object_ids.first)
- media_object.should_not be_hidden
+ expect(media_object).not_to be_hidden
media_object = MediaObject.find(ingest_batch.media_object_ids[1])
- media_object.should be_hidden
+ expect(media_object).to be_hidden
end
it 'should correctly set identifiers' do
batch_ingest.ingest
ingest_batch = IngestBatch.last
media_object = MediaObject.find(ingest_batch.media_object_ids.last)
- media_object.bibliographic_id.should eq(["local",bib_id])
+ expect(media_object.bibliographic_id).to eq(["local",bib_id])
end
it 'should correctly set notes' do
batch_ingest.ingest
ingest_batch = IngestBatch.last
media_object = MediaObject.find(ingest_batch.media_object_ids.first)
- media_object.note.first.should eq(["general","This is a test general note"])
+ expect(media_object.note.first).to eq(["general","This is a test general note"])
end
end
@@ -198,65 +207,65 @@
end
it 'does not create an ingest batch object when there are zero packages' do
- Avalon::Dropbox.any_instance.stub(:find_new_packages).and_return []
+ allow_any_instance_of(Avalon::Dropbox).to receive(:find_new_packages).and_return []
#expect(IngestBatchMailer).to receive(:batch_ingest_validation_error).with(anything(), include("Expected error message"))
expect{batch_ingest.ingest}.to_not change{IngestBatch.count}
end
it 'should result in an error if a file is not found' do
batch = Avalon::Batch::Package.new( 'spec/fixtures/dropbox/example_batch_ingest/wrong_filename_manifest.xlsx', collection )
- Avalon::Dropbox.any_instance.stub(:find_new_packages).and_return [batch]
+ allow_any_instance_of(Avalon::Dropbox).to receive(:find_new_packages).and_return [batch]
mailer = double('mailer').as_null_object
- IngestBatchMailer.should_receive(:batch_ingest_validation_error).with(duck_type(:each),duck_type(:each)).and_return(mailer)
- mailer.should_receive(:deliver)
+ expect(IngestBatchMailer).to receive(:batch_ingest_validation_error).with(duck_type(:each),duck_type(:each)).and_return(mailer)
+ expect(mailer).to receive(:deliver)
expect{batch_ingest.ingest}.to_not change{IngestBatch.count}
- batch.errors[3].messages.should have_key(:content)
- batch.errors[3].messages[:content].should eq(["File not found: spec/fixtures/dropbox/example_batch_ingest/assets/sheephead_mountain_wrong.mov"])
+ expect(batch.errors[3].messages).to have_key(:content)
+ expect(batch.errors[3].messages[:content]).to eq(["File not found: spec/fixtures/dropbox/example_batch_ingest/assets/sheephead_mountain_wrong.mov"])
end
it 'does not create an ingest batch object when there are no files' do
batch = Avalon::Batch::Package.new('spec/fixtures/dropbox/example_batch_ingest/no_files.xlsx', collection)
- Avalon::Dropbox.any_instance.stub(:find_new_packages).and_return [batch]
+ allow_any_instance_of(Avalon::Dropbox).to receive(:find_new_packages).and_return [batch]
expect{batch_ingest.ingest}.to_not change{IngestBatch.count}
end
it 'should fail if the manifest specified a non-manager user' do
batch = Avalon::Batch::Package.new('spec/fixtures/dropbox/example_batch_ingest/non_manager_manifest.xlsx', collection)
- Avalon::Dropbox.any_instance.stub(:find_new_packages).and_return [batch]
+ allow_any_instance_of(Avalon::Dropbox).to receive(:find_new_packages).and_return [batch]
mailer = double('mailer').as_null_object
- IngestBatchMailer.should_receive(:batch_ingest_validation_error).with(anything(), include("User jay@krajcik.org does not have permission to add items to collection: Ut minus ut accusantium odio autem odit..")).and_return(mailer)
- mailer.should_receive(:deliver)
+ expect(IngestBatchMailer).to receive(:batch_ingest_validation_error).with(anything(), include("User jay@krajcik.org does not have permission to add items to collection: Ut minus ut accusantium odio autem odit..")).and_return(mailer)
+ expect(mailer).to receive(:deliver)
expect{batch_ingest.ingest}.to_not change{IngestBatch.count}
end
it 'should fail if a bad offset is specified' do
batch = Avalon::Batch::Package.new('spec/fixtures/dropbox/example_batch_ingest/bad_offset_manifest.xlsx', collection)
- Avalon::Dropbox.any_instance.stub(:find_new_packages).and_return [batch]
+ allow_any_instance_of(Avalon::Dropbox).to receive(:find_new_packages).and_return [batch]
mailer = double('mailer').as_null_object
- IngestBatchMailer.should_receive(:batch_ingest_validation_error).with(duck_type(:each),duck_type(:each)).and_return(mailer)
- mailer.should_receive(:deliver)
+ expect(IngestBatchMailer).to receive(:batch_ingest_validation_error).with(duck_type(:each),duck_type(:each)).and_return(mailer)
+ expect(mailer).to receive(:deliver)
expect{batch_ingest.ingest}.to_not change{IngestBatch.count}
- batch.errors[4].messages.should have_key(:offset)
- batch.errors[4].messages[:offset].should eq(['Invalid offset: 5:000'])
+ expect(batch.errors[4].messages).to have_key(:offset)
+ expect(batch.errors[4].messages[:offset]).to eq(['Invalid offset: 5:000'])
end
it 'should fail if missing required field' do
batch = Avalon::Batch::Package.new('spec/fixtures/dropbox/example_batch_ingest/missing_required_field.xlsx', collection)
- Avalon::Dropbox.any_instance.stub(:find_new_packages).and_return [batch]
+ allow_any_instance_of(Avalon::Dropbox).to receive(:find_new_packages).and_return [batch]
mailer = double('mailer').as_null_object
- IngestBatchMailer.should_receive(:batch_ingest_validation_error).with(duck_type(:each),duck_type(:each)).and_return(mailer)
- mailer.should_receive(:deliver)
+ expect(IngestBatchMailer).to receive(:batch_ingest_validation_error).with(duck_type(:each),duck_type(:each)).and_return(mailer)
+ expect(mailer).to receive(:deliver)
expect{batch_ingest.ingest}.to_not change{IngestBatch.count}
- batch.errors[4].messages.should have_key(:creator)
- batch.errors[4].messages[:creator].should eq(['field is required.'])
+ expect(batch.errors[4].messages).to have_key(:title)
+ expect(batch.errors[4].messages[:title]).to eq(['field is required.'])
end
it 'should fail if field is not in accepted metadata field list' do
batch = Avalon::Batch::Package.new('spec/fixtures/dropbox/example_batch_ingest/badColumnName_nonRequired.xlsx', collection)
- Avalon::Dropbox.any_instance.stub(:find_new_packages).and_return [batch]
+ allow_any_instance_of(Avalon::Dropbox).to receive(:find_new_packages).and_return [batch]
mailer = double('mailer').as_null_object
- IngestBatchMailer.should_receive(:batch_ingest_validation_error).with(duck_type(:each),duck_type(:each)).and_return(mailer)
- mailer.should_receive(:deliver)
+ expect(IngestBatchMailer).to receive(:batch_ingest_validation_error).with(duck_type(:each),duck_type(:each)).and_return(mailer)
+ expect(mailer).to receive(:deliver)
expect{batch_ingest.ingest}.to_not change{IngestBatch.count}
expect(batch.errors[4].messages).to have_key(:contributator)
expect(batch.errors[4].messages[:contributator]).to eq(["Metadata attribute 'contributator' not found"])
@@ -264,11 +273,11 @@
it 'should fail if an unknown error occurs' do
batch = Avalon::Batch::Package.new('spec/fixtures/dropbox/example_batch_ingest/badColumnName_nonRequired.xlsx', collection)
- Avalon::Dropbox.any_instance.stub(:find_new_packages).and_return [batch]
+ allow_any_instance_of(Avalon::Dropbox).to receive(:find_new_packages).and_return [batch]
mailer = double('mailer').as_null_object
- IngestBatchMailer.should_receive(:batch_ingest_validation_error).with(batch ,['RuntimeError: Foo']).and_return(mailer)
- mailer.should_receive(:deliver)
- batch_ingest.should_receive(:ingest_package) { raise "Foo" }
+ expect(IngestBatchMailer).to receive(:batch_ingest_validation_error).with(batch ,['RuntimeError: Foo']).and_return(mailer)
+ expect(mailer).to receive(:deliver)
+ expect(batch_ingest).to receive(:ingest_package) { raise "Foo" }
expect { batch_ingest.ingest }.to_not raise_error
end
end
diff --git a/spec/lib/avalon/bib_retriever_spec.rb b/spec/lib/avalon/bib_retriever_spec.rb
index 3cdbe7de5b..082e59cb4d 100644
--- a/spec/lib/avalon/bib_retriever_spec.rb
+++ b/spec/lib/avalon/bib_retriever_spec.rb
@@ -41,26 +41,47 @@
describe 'sru' do
let(:sru_url) { "http://zgate.example.edu:9000/db?version=1.1&operation=searchRetrieve&maximumRecords=1&recordSchema=marcxml&query=rec.id=%5E%25#{bib_id}" }
- let(:sru_response) { File.read(File.expand_path("../../../fixtures/#{bib_id}.xml",__FILE__)) }
-
- before :each do
- Avalon::Configuration['bib_retriever'] = { 'protocol' => 'sru', 'url' => 'http://zgate.example.edu:9000/db' }
- FakeWeb.register_uri :get, sru_url, body: sru_response
- end
- after :each do
- FakeWeb.clean_registry
+ describe 'default namespace' do
+ let(:sru_response) { File.read(File.expand_path("../../../fixtures/#{bib_id}.xml",__FILE__)) }
+
+ before :each do
+ Avalon::Configuration['bib_retriever'] = { 'protocol' => 'sru', 'url' => 'http://zgate.example.edu:9000/db' }
+ FakeWeb.register_uri :get, sru_url, body: sru_response
+ end
+
+ after :each do
+ FakeWeb.clean_registry
+ end
+
+ it 'retrieves proper MODS' do
+ response = Avalon::BibRetriever.instance.get_record("^%#{bib_id}")
+ expect(Nokogiri::XML(response)).to be_equivalent_to(mods)
+ end
end
- it 'retrieves proper MODS' do
- response = Avalon::BibRetriever.instance.get_record("^%#{bib_id}")
- expect(Nokogiri::XML(response)).to be_equivalent_to(mods)
+ describe 'alternate namespace' do
+ let(:sru_response) { File.read(File.expand_path("../../../fixtures/#{bib_id}-ns.xml",__FILE__)) }
+
+ before :each do
+ Avalon::Configuration['bib_retriever'] = { 'protocol' => 'sru', 'url' => 'http://zgate.example.edu:9000/db', 'namespace' => 'http://example.edu/fake/sru/namespace/' }
+ FakeWeb.register_uri :get, sru_url, body: sru_response
+ end
+
+ after :each do
+ FakeWeb.clean_registry
+ end
+
+ it 'retrieves proper MODS' do
+ response = Avalon::BibRetriever.instance.get_record("^%#{bib_id}")
+ expect(Nokogiri::XML(response)).to be_equivalent_to(mods)
+ end
end
end
describe 'zoom' do
it 'retrieves proper MODS' do
- pending "need a reasonable way to mock ruby-zoom"
+ skip "need a reasonable way to mock ruby-zoom"
end
end
end
diff --git a/spec/lib/avalon/controlled_vocabulary_spec.rb b/spec/lib/avalon/controlled_vocabulary_spec.rb
index 7a933be8d6..61befbe19c 100644
--- a/spec/lib/avalon/controlled_vocabulary_spec.rb
+++ b/spec/lib/avalon/controlled_vocabulary_spec.rb
@@ -17,33 +17,33 @@
describe Avalon::ControlledVocabulary do
before do
- File.stub(:read).and_return ''
- File.stub(:file?).and_return true
+ allow(File).to receive(:read).and_return ''
+ allow(File).to receive(:file?).and_return true
end
describe '#vocabulary' do
it 'reads the file directly from disk' do
- File.should_receive(:read).twice
+ expect(File).to receive(:read).twice
Avalon::ControlledVocabulary.vocabulary
Avalon::ControlledVocabulary.vocabulary
end
it 'returns an empty hash when yaml file is empty' do
- Avalon::ControlledVocabulary.vocabulary.should eql({})
+ expect(Avalon::ControlledVocabulary.vocabulary).to eql({})
end
end
describe '#find_by_name' do
before do
- Avalon::ControlledVocabulary.stub(:vocabulary).and_return({ units: ['Archives'] })
+ allow(Avalon::ControlledVocabulary).to receive(:vocabulary).and_return({ units: ['Archives'] })
end
it 'finds a vocabulary by name' do
- Avalon::ControlledVocabulary.find_by_name('units').should eql ['Archives']
+ expect(Avalon::ControlledVocabulary.find_by_name('units')).to eql ['Archives']
end
it 'finds a vocabulary by symbol' do
- Avalon::ControlledVocabulary.find_by_name(:units).should eql ['Archives']
+ expect(Avalon::ControlledVocabulary.find_by_name(:units)).to eql ['Archives']
end
end
diff --git a/spec/lib/avalon/dropbox_spec.rb b/spec/lib/avalon/dropbox_spec.rb
index 0e8ee8ed51..df37e1f33b 100644
--- a/spec/lib/avalon/dropbox_spec.rb
+++ b/spec/lib/avalon/dropbox_spec.rb
@@ -26,12 +26,12 @@
let(:collection) { FactoryGirl.create(:collection, name: 'Ut minus ut accusantium odio autem odit.', managers: ['frances.dickens@reichel.com']) }
subject { Avalon::Dropbox.new(Avalon::Configuration.lookup('dropbox.path'),collection) }
it 'returns true if the file is found' do
- File.stub(:delete).and_return true
+ allow(File).to receive(:delete).and_return true
subject.delete('some_file.mov')
end
it 'returns false if the file is not found' do
- subject.delete('some_file.mov').should be false
+ expect(subject.delete('some_file.mov')).to be false
end
end
diff --git a/spec/lib/avalon/file_resolver_spec.rb b/spec/lib/avalon/file_resolver_spec.rb
index f3b46a14e8..d631b1c6a9 100644
--- a/spec/lib/avalon/file_resolver_spec.rb
+++ b/spec/lib/avalon/file_resolver_spec.rb
@@ -18,23 +18,23 @@
let(:resolver){ Avalon::FileResolver.new }
describe "#path_to" do
it 'returns umodified path when string already has a schema' do
- resolver.path_to('http://example.com').should == 'http://example.com'
+ expect(resolver.path_to('http://example.com')).to eq('http://example.com')
end
it 'returns path with schema' do
- resolver.stub(:mount_map).and_return({'/Volumes/dropbox/'=> 'smb://example.edu/dropbox'})
- resolver.path_to('/Volumes/dropbox/master_files/').should == 'smb://example.edu/dropbox/master_files'
+ allow(resolver).to receive(:mount_map).and_return({'/Volumes/dropbox/'=> 'smb://example.edu/dropbox'})
+ expect(resolver.path_to('/Volumes/dropbox/master_files/')).to eq('smb://example.edu/dropbox/master_files')
end
it 'returns path with file schema when no mounts match' do
- resolver.stub(:mount_map).and_return({})
- resolver.path_to('/storage/master_files/').should == 'file:///storage/master_files/'
+ allow(resolver).to receive(:mount_map).and_return({})
+ expect(resolver.path_to('/storage/master_files/')).to eq('file:///storage/master_files/')
end
end
describe "#mount_map" do
it 'returns a formatted mount' do
- resolver.stub(:overrides).and_return({})
+ allow(resolver).to receive(:overrides).and_return({})
resolver.instance_variable_set(:@mounts, ['//adam@example.edu/dropbox on /Volumes/dropbox (smbfs, nodev, nosuid, mounted by adam)'])
- resolver.mount_map.should == {'/Volumes/dropbox/'=>'smb://example.edu/dropbox'}
+ expect(resolver.mount_map).to eq({'/Volumes/dropbox/'=>'smb://example.edu/dropbox'})
end
end
end
diff --git a/spec/lib/avalon/matterhorn_rtmp_url_spec.rb b/spec/lib/avalon/matterhorn_rtmp_url_spec.rb
index a9c5aa17b1..ea8cdcbb3a 100644
--- a/spec/lib/avalon/matterhorn_rtmp_url_spec.rb
+++ b/spec/lib/avalon/matterhorn_rtmp_url_spec.rb
@@ -18,11 +18,13 @@
describe Avalon::MatterhornRtmpUrl do
subject {Avalon::MatterhornRtmpUrl.parse('rtmp://localhost/avalon/mp4:98285a5b-603a-4a14-acc0-20e37a3514bb/b3d5663d-53f1-4f7d-b7be-b52fd5ca50a3/MVI_0057.mp4')}
- its(:application) {should == 'avalon'}
- its(:prefix) {should == 'mp4'}
- its(:media_id) {should == '98285a5b-603a-4a14-acc0-20e37a3514bb'}
- its(:stream_id) {should == 'b3d5663d-53f1-4f7d-b7be-b52fd5ca50a3'}
- its(:filename) {should == 'MVI_0057'}
- its(:extension) {should == 'mp4'}
- its(:to_path) {should == '98285a5b-603a-4a14-acc0-20e37a3514bb/b3d5663d-53f1-4f7d-b7be-b52fd5ca50a3/MVI_0057.mp4'}
+ it "should have attributes" do
+ expect(subject.application).to eq('avalon')
+ expect(subject.prefix).to eq('mp4')
+ expect(subject.media_id).to eq('98285a5b-603a-4a14-acc0-20e37a3514bb')
+ expect(subject.stream_id).to eq('b3d5663d-53f1-4f7d-b7be-b52fd5ca50a3')
+ expect(subject.filename).to eq('MVI_0057')
+ expect(subject.extension).to eq('mp4')
+ expect(subject.to_path).to eq('98285a5b-603a-4a14-acc0-20e37a3514bb/b3d5663d-53f1-4f7d-b7be-b52fd5ca50a3/MVI_0057.mp4')
+ end
end
diff --git a/spec/lib/avalon/sanitizer_spec.rb b/spec/lib/avalon/sanitizer_spec.rb
index 94b1c42b28..4f35ed1052 100644
--- a/spec/lib/avalon/sanitizer_spec.rb
+++ b/spec/lib/avalon/sanitizer_spec.rb
@@ -18,15 +18,15 @@
describe Avalon::Sanitizer do
describe '#sanitize' do
it 'replaces blacklisted characters' do
- Avalon::Sanitizer.sanitize('abcdefg&',['&','_']).should == 'abcdefg_'
+ expect(Avalon::Sanitizer.sanitize('abcdefg&',['&','_'])).to eq('abcdefg_')
end
it 'replaces multiple blacklisted characters' do
- Avalon::Sanitizer.sanitize('avalon*media&system',['*&','__']).should == 'avalon_media_system'
+ expect(Avalon::Sanitizer.sanitize('avalon*media&system',['*&','__'])).to eq('avalon_media_system')
end
it 'does not modify a string without any blacklisted characters' do
- Avalon::Sanitizer.sanitize('avalon_media_system',['*&','__']).should == 'avalon_media_system'
+ expect(Avalon::Sanitizer.sanitize('avalon_media_system',['*&','__'])).to eq('avalon_media_system')
end
end
end
diff --git a/spec/mailers/ingest_batch_mailer_spec.rb b/spec/mailers/ingest_batch_mailer_spec.rb
index 1e40854ebd..759a872ebd 100644
--- a/spec/mailers/ingest_batch_mailer_spec.rb
+++ b/spec/mailers/ingest_batch_mailer_spec.rb
@@ -23,20 +23,20 @@
it 'has an error if media object has not been set' do
ingest_batch = IngestBatch.create
@email = IngestBatchMailer.status_email( ingest_batch.id )
- @email.should have_body_text('There was an error. It appears no files have been submitted')
+ expect(@email).to have_body_text('There was an error. It appears no files have been submitted')
end
it 'has an error if there are no media objects present' do
@ingest_batch = IngestBatch.create( media_object_ids: [])
@email = IngestBatchMailer.status_email( @ingest_batch.id )
- @email.should have_body_text('There was an error. It appears no files have been submitted')
+ expect(@email).to have_body_text('There was an error. It appears no files have been submitted')
end
it 'shows the title of one media object' do
media_object = FactoryGirl.create(:media_object)
ingest_batch = IngestBatch.create(media_object_ids: [media_object.id])
@email = IngestBatchMailer.status_email(ingest_batch.id)
- @email.should have_body_text(media_object.title)
+ expect(@email).to have_body_text(media_object.title)
end
it 'has the status of the master file in a first row' do
@@ -50,8 +50,8 @@
# Ideally a within block would be nice here
fragment = email_message.find("table > tbody > tr:nth-child(1)")
- fragment.find('.master-file').should have_content(File.basename(master_file.file_location))
- fragment.find('.percent-complete').should have_content(master_file.percent_complete)
- fragment.find('.status-code').should have_content(master_file.status_code.downcase.titleize)
+ expect(fragment.find('.master-file')).to have_content(File.basename(master_file.file_location))
+ expect(fragment.find('.percent-complete')).to have_content(master_file.percent_complete)
+ expect(fragment.find('.status-code')).to have_content(master_file.status_code.downcase.titleize)
end
end
diff --git a/spec/mailers/notifications_mailer_spec.rb b/spec/mailers/notifications_mailer_spec.rb
index adbb16c6fb..31bf2ede8b 100644
--- a/spec/mailers/notifications_mailer_spec.rb
+++ b/spec/mailers/notifications_mailer_spec.rb
@@ -39,38 +39,38 @@
end
it 'has correct e-mail address' do
- @email.should deliver_to(@admin_user.email)
+ expect(@email).to deliver_to(@admin_user.email)
end
context 'subject' do
it 'has collection name' do
- @email.should have_subject(/#{@collection.name}/)
+ expect(@email).to have_subject(/#{@collection.name}/)
end
end
context 'body' do
it 'has link to collection' do
- @email.should have_body_text(admin_collection_url(@collection))
+ expect(@email).to have_body_text(admin_collection_url(@collection))
end
it 'has collection name' do
- @email.should have_body_text(@collection.name)
+ expect(@email).to have_body_text(@collection.name)
end
it 'has old collection name' do
- @email.should have_body_text(@old_name)
+ expect(@email).to have_body_text(@old_name)
end
it 'has updater e-mail' do
- @email.should have_body_text(@updater.email)
+ expect(@email).to have_body_text(@updater.email)
end
it 'has collection description' do
- @email.should have_body_text(@collection.description)
+ expect(@email).to have_body_text(@collection.description)
end
it 'has unit name' do
- @email.should have_body_text(@collection.unit)
+ expect(@email).to have_body_text(@collection.unit)
end
it 'has dropbox absolute path' do
@@ -94,30 +94,30 @@
end
it 'has correct e-mail address' do
- @email.should deliver_to(@admin_user.email)
+ expect(@email).to deliver_to(@admin_user.email)
end
context 'subject' do
it 'has collection name' do
- @email.should have_subject(/#{@collection.name}/)
+ expect(@email).to have_subject(/#{@collection.name}/)
end
end
context 'body' do
it 'has collection name' do
- @email.should have_body_text(@collection.name)
+ expect(@email).to have_body_text(@collection.name)
end
it 'has creator e-mail' do
- @email.should have_body_text(@creator.email)
+ expect(@email).to have_body_text(@creator.email)
end
it 'has collection description' do
- @email.should have_body_text(@collection.description)
+ expect(@email).to have_body_text(@collection.description)
end
it 'has unit name' do
- @email.should have_body_text(@collection.unit)
+ expect(@email).to have_body_text(@collection.unit)
end
it 'has dropbox absolute path' do
diff --git a/spec/migrations/r2_group_migration_spec.rb b/spec/migrations/r2_group_migration_spec.rb
index 922c885d02..4dfb4fc347 100644
--- a/spec/migrations/r2_group_migration_spec.rb
+++ b/spec/migrations/r2_group_migration_spec.rb
@@ -38,11 +38,11 @@
end
it "should create the administrator group" do
- Admin::Group.find('administrator').should be_nil
+ expect(Admin::Group.find('administrator')).to be_nil
R2GroupMigration.new.up
admin_group = Admin::Group.find('administrator')
- admin_group.should_not be_nil
- admin_group.users.should include(Admin::Group.find('group_manager').users.first)
+ expect(admin_group).not_to be_nil
+ expect(admin_group.users).to include(Admin::Group.find('group_manager').users.first)
end
end
diff --git a/spec/models/avalon_annonation_spec.rb b/spec/models/avalon_annonation_spec.rb
new file mode 100644
index 0000000000..372793d50c
--- /dev/null
+++ b/spec/models/avalon_annonation_spec.rb
@@ -0,0 +1,182 @@
+# Copyright 2011-2015, The Trustees of Indiana University and Northwestern
+# University. Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+#
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software distributed
+# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+# CONDITIONS OF ANY KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations under the License.
+# --- END LICENSE_HEADER BLOCK ---
+
+require 'spec_helper'
+
+describe AvalonAnnotation do
+ subject(:video_master_file) { FactoryGirl.create(:master_file, duration: "1") }
+ #subject(:sound_master_file) { FactoryGirl.create(:master_file_sound) }
+ let(:annotation) { AvalonAnnotation.new(master_file: video_master_file) }
+
+ describe 'creating an annotation' do
+ it 'sets the master file when creating the object' do
+ expect(annotation.master_file.pid).to eq(video_master_file.pid)
+ end
+ it 'sets the source uri' do
+ expect(annotation.source).not_to be_nil
+ expect { URI.parse(annotation.source) }.not_to raise_error
+ end
+ it 'sets default end and start times' do
+ expect(annotation.start_time).to eq(0)
+ expect(annotation.end_time).to eq(1)
+ end
+
+ it 'sets the end time to the duration of the master_file by default' do
+ allow(video_master_file).to receive(:duration).and_return(100)
+ annotation_with_duration = AvalonAnnotation.new(master_file: video_master_file)
+ expect(annotation_with_duration.end_time).to eq(100)
+ end
+ it 'sets the title to the master_file label by default' do
+ expect(annotation.title).to match(video_master_file.embed_title)
+ end
+
+ it 'loads the master_file' do
+ annotation.save!
+ new_instance = AvalonAnnotation.find_by(id: annotation.id)
+ expect(new_instance.master_file.pid).to eq(video_master_file.pid)
+ end
+
+ it 'can store start and end times' do
+ annotation.start_time = 0.5
+ annotation.end_time = 0.75
+ annotation.save!
+ annotation.reload
+ expect(annotation.start_time).to eq(0.5)
+ expect(annotation.end_time).to eq(0.75)
+ end
+ end
+ describe 'aliases for Avalon Annotation' do
+ describe 'aliasing content with comment' do
+ it 'sets content using comment=' do
+ content = 'Big Two, Little Eight'
+ annotation.comment = content
+ annotation.save!
+ expect(annotation.reload.content).to match(content)
+ end
+ it 'accesses content using comment' do
+ content = '1817'
+ annotation.content = content
+ annotation.save!
+ expect(annotation.reload.comment).to match(content)
+ end
+ end
+ describe 'aliasing label with title' do
+ it 'sets label using title=' do
+ title = 'Astrolounge'
+ annotation.title = title
+ annotation.save!
+ expect(annotation.reload.label).to match(title)
+ end
+ it 'accesses label using title' do
+ title = 'Defeat You'
+ annotation.label = title
+ annotation.save!
+ expect(annotation.reload.title).to match(title)
+ end
+ end
+ end
+ it 'can load the master_file based on the uri' do
+ mf = annotation.master_file
+ expect(mf.pid).to eq(video_master_file.pid)
+ end
+ describe 'selector' do
+ it 'can create the selector using the start and end time' do
+ video_master_file.stub(:rdf_uri).and_return(RuntimeError, 'htt://www.avalon.edu/obj')
+ expect{ annotation.mediafragment_uri }.not_to raise_error
+ end
+ end
+ describe 'solr' do
+ it 'can create a solr hash' do
+ expect(annotation.to_solr.class).to eq(Hash)
+ end
+ it 'can save the annotation and solrize it' do
+ expect(annotation).to receive(:post_to_solr).once
+ expect { annotation.save }.not_to raise_error
+ end
+ it 'can destroy annotation and remove it from solr' do
+ annotation.save!
+ expect(annotation).to receive(:delete_from_solr).once
+ expect { annotation.destroy }.not_to raise_error
+ end
+ end
+ describe 'time validation' do
+ describe 'negative times' do
+ it 'raises an ArgumentError when start_time is negative' do
+ annotation.start_time = -1
+ expect(annotation).not_to be_valid
+ end
+ it 'raises an ArgumentError when end_time is negative' do
+ annotation.end_time = -1
+ expect(annotation).not_to be_valid
+ end
+ end
+ describe 'start and end time spacing' do
+ it 'raises an error when the end time preceeds the start time' do
+ annotation.end_time = 0
+ annotation.start_time = 1
+ expect(annotation).not_to be_valid
+ end
+ it 'raises an error when the end time equals the start time' do
+ annotation.end_time = 0
+ annotation.start_time = 0
+ expect(annotation).not_to be_valid
+ end
+ end
+ describe 'duration' do
+ subject(:video_master_file) { FactoryGirl.create(:master_file, duration: "8", derivatives: [derivative, derivative2]) }
+ let(:derivative) { FactoryGirl.create(:derivative, duration: "12") }
+ let(:derivative2) { FactoryGirl.create(:derivative, duration: "10") }
+ it 'raises an error when end time exceeds the duration' do
+ annotation.end_time = 60
+ expect(annotation).not_to be_valid
+ end
+ it 'allows end times longer than master_file duration but not longer than the longest derivative' do
+ annotation.end_time = 12
+ expect(annotation).to be_valid
+ end
+ end
+ end
+ describe 'related annotations' do
+ let(:second_annotation) { AvalonAnnotation.new(master_file: video_master_file) }
+ let!(:user) {FactoryGirl.build(:user)}
+ it 'returns nil when there are no related annotations' do
+ allow(PlaylistItem).to receive(:where).and_return([])
+ expect(annotation.playlist_position(1)).to be_nil
+ end
+ it 'returns position when there is a related annotation' do
+ #byebug
+ stub_playlist
+ expect(annotation.playlist_position(@playlist.id)).to eq(1)
+ expect(second_annotation.playlist_position(@playlist.id)).to eq(2)
+ end
+
+ def stub_playlist
+ user.save!
+ @playlist = Playlist.new
+ @playlist.title = 'whatever'
+ @playlist.user_id = User.last.id
+ @playlist.save!
+ pos = 1
+ [annotation, second_annotation].each do |a|
+ a.save!
+ @pi = PlaylistItem.new
+ @pi.playlist_id = @playlist.id
+ @pi.annotation_id = a.id
+ @pi.position = pos
+ @pi.save!
+ pos += 1
+ end
+ end
+ end
+end
diff --git a/spec/models/collection_spec.rb b/spec/models/collection_spec.rb
index daf2baebf2..69d9f8e48a 100644
--- a/spec/models/collection_spec.rb
+++ b/spec/models/collection_spec.rb
@@ -26,7 +26,7 @@
let(:ability){ Ability.new(user) }
let(:user){ FactoryGirl.create(:administrator) }
- it{ should be_able_to(:manage, collection) }
+ it{ is_expected.to be_able_to(:manage, collection) }
end
context 'when manager' do
@@ -34,16 +34,16 @@
let(:ability){ Ability.new(user) }
let(:user){ User.where(username: collection.managers.first).first }
- it{ should be_able_to(:read, Admin::Collection) }
- it{ should be_able_to(:update, collection) }
- it{ should be_able_to(:read, collection) }
- it{ should be_able_to(:update_unit, collection) }
- it{ should be_able_to(:update_managers, collection) }
- it{ should be_able_to(:update_editors, collection) }
- it{ should be_able_to(:update_depositors, collection) }
- it{ should be_able_to(:create, Admin::Collection) }
- it{ should be_able_to(:destroy, collection) }
- it{ should be_able_to(:update_access_control, collection) }
+ it{ is_expected.to be_able_to(:read, Admin::Collection) }
+ it{ is_expected.to be_able_to(:update, collection) }
+ it{ is_expected.to be_able_to(:read, collection) }
+ it{ is_expected.to be_able_to(:update_unit, collection) }
+ it{ is_expected.to be_able_to(:update_managers, collection) }
+ it{ is_expected.to be_able_to(:update_editors, collection) }
+ it{ is_expected.to be_able_to(:update_depositors, collection) }
+ it{ is_expected.to be_able_to(:create, Admin::Collection) }
+ it{ is_expected.to be_able_to(:destroy, collection) }
+ it{ is_expected.to be_able_to(:update_access_control, collection) }
end
context 'when editor' do
@@ -52,16 +52,16 @@
let(:user){ User.where(username: collection.editors.first).first }
#Will need to define new action that covers just the things that an editor is allowed to edit
- it{ should be_able_to(:read, Admin::Collection) }
- it{ should be_able_to(:read, collection) }
- it{ should be_able_to(:update, collection) }
- it{ should_not be_able_to(:update_unit, collection) }
- it{ should_not be_able_to(:update_managers, collection) }
- it{ should_not be_able_to(:update_editors, collection) }
- it{ should be_able_to(:update_depositors, collection) }
- it{ should_not be_able_to(:create, Admin::Collection) }
- it{ should_not be_able_to(:destroy, collection) }
- it{ should_not be_able_to(:update_access_control, collection) }
+ it{ is_expected.to be_able_to(:read, Admin::Collection) }
+ it{ is_expected.to be_able_to(:read, collection) }
+ it{ is_expected.to be_able_to(:update, collection) }
+ it{ is_expected.not_to be_able_to(:update_unit, collection) }
+ it{ is_expected.not_to be_able_to(:update_managers, collection) }
+ it{ is_expected.not_to be_able_to(:update_editors, collection) }
+ it{ is_expected.to be_able_to(:update_depositors, collection) }
+ it{ is_expected.not_to be_able_to(:create, Admin::Collection) }
+ it{ is_expected.not_to be_able_to(:destroy, collection) }
+ it{ is_expected.not_to be_able_to(:update_access_control, collection) }
end
context 'when depositor' do
@@ -69,16 +69,16 @@
let(:ability){ Ability.new(user) }
let(:user){ User.where(username: collection.depositors.first).first }
- it{ should be_able_to(:read, Admin::Collection) }
- it{ should be_able_to(:read, collection) }
- it{ should_not be_able_to(:update_unit, collection) }
- it{ should_not be_able_to(:update_managers, collection) }
- it{ should_not be_able_to(:update_editors, collection) }
- it{ should_not be_able_to(:update_depositors, collection) }
- it{ should_not be_able_to(:create, collection) }
- it{ should_not be_able_to(:update, collection) }
- it{ should_not be_able_to(:destroy, collection) }
- it{ should_not be_able_to(:update_access_control, collection) }
+ it{ is_expected.to be_able_to(:read, Admin::Collection) }
+ it{ is_expected.to be_able_to(:read, collection) }
+ it{ is_expected.not_to be_able_to(:update_unit, collection) }
+ it{ is_expected.not_to be_able_to(:update_managers, collection) }
+ it{ is_expected.not_to be_able_to(:update_editors, collection) }
+ it{ is_expected.not_to be_able_to(:update_depositors, collection) }
+ it{ is_expected.not_to be_able_to(:create, collection) }
+ it{ is_expected.not_to be_able_to(:update, collection) }
+ it{ is_expected.not_to be_able_to(:destroy, collection) }
+ it{ is_expected.not_to be_able_to(:update_access_control, collection) }
end
context 'when end user' do
@@ -86,16 +86,16 @@
let(:ability){ Ability.new(user) }
let(:user){ FactoryGirl.create(:user) }
- it{ should_not be_able_to(:read, Admin::Collection) }
- it{ should_not be_able_to(:read, collection) }
- it{ should_not be_able_to(:update_unit, collection) }
- it{ should_not be_able_to(:update_managers, collection) }
- it{ should_not be_able_to(:update_editors, collection) }
- it{ should_not be_able_to(:update_depositors, collection) }
- it{ should_not be_able_to(:create, collection) }
- it{ should_not be_able_to(:update, collection) }
- it{ should_not be_able_to(:destroy, collection) }
- it{ should_not be_able_to(:update_access_control, collection) }
+ it{ is_expected.not_to be_able_to(:read, Admin::Collection) }
+ it{ is_expected.not_to be_able_to(:read, collection) }
+ it{ is_expected.not_to be_able_to(:update_unit, collection) }
+ it{ is_expected.not_to be_able_to(:update_managers, collection) }
+ it{ is_expected.not_to be_able_to(:update_editors, collection) }
+ it{ is_expected.not_to be_able_to(:update_depositors, collection) }
+ it{ is_expected.not_to be_able_to(:create, collection) }
+ it{ is_expected.not_to be_able_to(:update, collection) }
+ it{ is_expected.not_to be_able_to(:destroy, collection) }
+ it{ is_expected.not_to be_able_to(:update_access_control, collection) }
end
context 'when lti user' do
@@ -103,16 +103,16 @@
let(:ability){ Ability.new(user) }
let(:user){ FactoryGirl.create(:user_lti) }
- it{ should_not be_able_to(:read, Admin::Collection) }
- it{ should_not be_able_to(:read, collection) }
- it{ should_not be_able_to(:update_unit, collection) }
- it{ should_not be_able_to(:update_managers, collection) }
- it{ should_not be_able_to(:update_editors, collection) }
- it{ should_not be_able_to(:update_depositors, collection) }
- it{ should_not be_able_to(:create, collection) }
- it{ should_not be_able_to(:update, collection) }
- it{ should_not be_able_to(:destroy, collection) }
- it{ should_not be_able_to(:update_access_control, collection) }
+ it{ is_expected.not_to be_able_to(:read, Admin::Collection) }
+ it{ is_expected.not_to be_able_to(:read, collection) }
+ it{ is_expected.not_to be_able_to(:update_unit, collection) }
+ it{ is_expected.not_to be_able_to(:update_managers, collection) }
+ it{ is_expected.not_to be_able_to(:update_editors, collection) }
+ it{ is_expected.not_to be_able_to(:update_depositors, collection) }
+ it{ is_expected.not_to be_able_to(:create, collection) }
+ it{ is_expected.not_to be_able_to(:update, collection) }
+ it{ is_expected.not_to be_able_to(:destroy, collection) }
+ it{ is_expected.not_to be_able_to(:update_access_control, collection) }
end
end
@@ -123,34 +123,35 @@
let(:editor) {FactoryGirl.create(:user)}
let(:depositor) {FactoryGirl.create(:user)}
- it {should validate_presence_of(:name)}
- it {should validate_uniqueness_of(:name)}
+ it {is_expected.to validate_presence_of(:name)}
+ it {is_expected.to validate_uniqueness_of(:name)}
it "shouldn't complain about partial name matches" do
FactoryGirl.create(:collection, name: "This little piggy went to market")
expect { FactoryGirl.create(:collection, name: "This little piggy") }.not_to raise_error
end
- it {should validate_presence_of(:unit)}
- it {should ensure_inclusion_of(:unit).in_array(Admin::Collection.units)}
+ it {is_expected.to validate_presence_of(:unit)}
+ it {is_expected.to ensure_inclusion_of(:unit).in_array(Admin::Collection.units)}
it "should ensure length of :managers is_at_least(1)"
- its(:name) {should == "Herman B. Wells Collection"}
- its(:unit) {should == "University Archives"}
- its(:description) {should == "Collection about our 11th university president, 1938-1962"}
- its(:created_at) {should == DateTime.parse(wells_collection.create_date)}
- its(:managers) {should == [manager.username]}
- its(:editors) {should == [editor.username]}
- its(:depositors) {should == [depositor.username]}
-
- its(:rightsMetadata) {should be_kind_of Hydra::Datastream::RightsMetadata}
- its(:inheritedRights) {should be_kind_of Hydra::Datastream::InheritableRightsMetadata}
- its(:defaultRights) {should be_kind_of Hydra::Datastream::NonIndexedRightsMetadata}
+ it "should have attributes" do
+ expect(subject.name).to eq("Herman B. Wells Collection")
+ expect(subject.unit).to eq("University Archives")
+ expect(subject.description).to eq("Collection about our 11th university president, 1938-1962")
+ expect(subject.created_at).to eq(DateTime.parse(wells_collection.create_date))
+ expect(subject.managers).to eq([manager.username])
+ expect(subject.editors).to eq([editor.username])
+ expect(subject.depositors).to eq([depositor.username])
+ expect(subject.rightsMetadata).to be_kind_of Hydra::Datastream::RightsMetadata
+ expect(subject.inheritedRights).to be_kind_of Hydra::Datastream::InheritableRightsMetadata
+ expect(subject.defaultRights).to be_kind_of Hydra::Datastream::NonIndexedRightsMetadata
+ end
end
describe "Admin::Collection.units" do
it "should return an array of units" do
- Admin::Collection.stub(:units).and_return ["University Archives", "Black Film Center/Archive"]
- Admin::Collection.units.should be_an_instance_of Array
- Admin::Collection.units.should == ["University Archives", "Black Film Center/Archive"]
+ allow(Admin::Collection).to receive(:units).and_return ["University Archives", "Black Film Center/Archive"]
+ expect(Admin::Collection.units).to be_an_instance_of Array
+ expect(Admin::Collection.units).to eq(["University Archives", "Black Film Center/Archive"])
end
end
@@ -158,7 +159,7 @@
it "should solrize important information" do
map = Solrizer.default_field_mapper
collection.name = "Herman B. Wells Collection"
- collection.to_solr[ map.solr_name(:name, :stored_searchable, type: :string) ].should == "Herman B. Wells Collection"
+ expect(collection.to_solr[ map.solr_name(:name, :stored_searchable, type: :string) ]).to eq("Herman B. Wells Collection")
end
end
@@ -169,75 +170,75 @@
describe "#managers" do
it "should return the intersection of edit_users and managers role" do
collection.edit_users = [user.username, "pdinh"]
- RoleControls.should_receive("users").with("manager").and_return([user.username, "atomical"])
- RoleControls.should_receive("users").with("administrator").and_return([])
- collection.managers.should == [user.username] #collection.edit_users & RoleControls.users("manager")
+ expect(RoleControls).to receive("users").with("manager").and_return([user.username, "atomical"])
+ expect(RoleControls).to receive("users").with("administrator").and_return([])
+ expect(collection.managers).to eq([user.username]) #collection.edit_users & RoleControls.users("manager")
end
end
describe "#managers=" do
it "should add managers to the collection" do
manager_list = [FactoryGirl.create(:manager).username, FactoryGirl.create(:manager).username]
collection.managers = manager_list
- collection.managers.should == manager_list
+ expect(collection.managers).to eq(manager_list)
end
it "should call add_manager" do
manager_list = [FactoryGirl.create(:manager).username, FactoryGirl.create(:manager).username]
- collection.should_receive("add_manager").with(manager_list[0])
- collection.should_receive("add_manager").with(manager_list[1])
+ expect(collection).to receive("add_manager").with(manager_list[0])
+ expect(collection).to receive("add_manager").with(manager_list[1])
collection.managers = manager_list
end
it "should remove managers from the collection" do
manager_list = [FactoryGirl.create(:manager).username, FactoryGirl.create(:manager).username]
collection.managers = manager_list
- collection.managers.should == manager_list
+ expect(collection.managers).to eq(manager_list)
collection.managers -= manager_list
- collection.managers.should == []
+ expect(collection.managers).to eq([])
end
it "should call remove_manager" do
collection.managers = [user.username]
- collection.should_receive("remove_manager").with(user.username)
+ expect(collection).to receive("remove_manager").with(user.username)
collection.managers = [FactoryGirl.create(:manager).username]
end
end
describe "#add_manager" do
it "should give edit access to the collection" do
collection.add_manager(user.username)
- collection.edit_users.should include(user.username)
- collection.inherited_edit_users.should include(user.username)
- collection.managers.should include(user.username)
+ expect(collection.edit_users).to include(user.username)
+ expect(collection.inherited_edit_users).to include(user.username)
+ expect(collection.managers).to include(user.username)
end
it "should add users who have the administrator role" do
administrator = FactoryGirl.create(:administrator)
collection.add_manager(administrator.username)
- collection.edit_users.should include(administrator.username)
- collection.inherited_edit_users.should include(administrator.username)
- collection.managers.should include(administrator.username)
+ expect(collection.edit_users).to include(administrator.username)
+ expect(collection.inherited_edit_users).to include(administrator.username)
+ expect(collection.managers).to include(administrator.username)
end
it "should not add administrators to editors role" do
administrator = FactoryGirl.create(:administrator)
collection.add_manager(administrator.username)
- collection.editors.should_not include(administrator.username)
+ expect(collection.editors).not_to include(administrator.username)
end
it "should not add users who do not have the manager role" do
not_manager = FactoryGirl.create(:user)
expect {collection.add_manager(not_manager.username)}.to raise_error(ArgumentError)
- collection.managers.should_not include(not_manager.username)
+ expect(collection.managers).not_to include(not_manager.username)
end
end
describe "#remove_manager" do
it "should revoke edit access to the collection" do
collection.remove_manager(user.username)
- collection.edit_users.should_not include(user.username)
- collection.inherited_edit_users.should_not include(user.username)
- collection.managers.should_not include(user.username)
+ expect(collection.edit_users).not_to include(user.username)
+ expect(collection.inherited_edit_users).not_to include(user.username)
+ expect(collection.managers).not_to include(user.username)
end
it "should not remove users who do not have the manager role" do
not_manager = FactoryGirl.create(:user)
collection.edit_users = [not_manager.username]
collection.inherited_edit_users = [not_manager.username]
collection.remove_manager(not_manager.username)
- collection.edit_users.should include(not_manager.username)
- collection.inherited_edit_users.should include(not_manager.username)
+ expect(collection.edit_users).to include(not_manager.username)
+ expect(collection.inherited_edit_users).to include(not_manager.username)
end
end
end
@@ -249,31 +250,31 @@
describe "#editors" do
it "should not return managers" do
collection.edit_users = [user.username, FactoryGirl.create(:manager).username]
- collection.editors.should == [user.username]
+ expect(collection.editors).to eq([user.username])
end
end
describe "#editors=" do
it "should add editors to the collection" do
editor_list = [FactoryGirl.create(:user).username, FactoryGirl.create(:user).username]
collection.editors = editor_list
- collection.editors.should == editor_list
+ expect(collection.editors).to eq(editor_list)
end
it "should call add_editor" do
editor_list = [FactoryGirl.create(:user).username, FactoryGirl.create(:user).username]
- collection.should_receive("add_editor").with(editor_list[0])
- collection.should_receive("add_editor").with(editor_list[1])
+ expect(collection).to receive("add_editor").with(editor_list[0])
+ expect(collection).to receive("add_editor").with(editor_list[1])
collection.editors = editor_list
end
it "should remove editors from the collection" do
name = user.username
collection.editors = [name]
- collection.editors.should == [name]
+ expect(collection.editors).to eq([name])
collection.editors -= [name]
- collection.editors.should == []
+ expect(collection.editors).to eq([])
end
it "should call remove_editor" do
collection.editors = [user.username]
- collection.should_receive("remove_editor").with(user.username)
+ expect(collection).to receive("remove_editor").with(user.username)
collection.editors = [FactoryGirl.create(:user).username]
end
end
@@ -281,26 +282,26 @@
it "should give edit access to the collection" do
not_editor = FactoryGirl.create(:user)
collection.add_editor(not_editor.username)
- collection.edit_users.should include(not_editor.username)
- collection.inherited_edit_users.should include(not_editor.username)
- collection.editors.should include(not_editor.username)
+ expect(collection.edit_users).to include(not_editor.username)
+ expect(collection.inherited_edit_users).to include(not_editor.username)
+ expect(collection.editors).to include(not_editor.username)
end
end
describe "#remove_editor" do
it "should revoke edit access to the collection" do
collection.add_editor(user.username)
collection.remove_editor(user.username)
- collection.edit_users.should_not include(user.username)
- collection.inherited_edit_users.should_not include(user.username)
- collection.editors.should_not include(user.username)
+ expect(collection.edit_users).not_to include(user.username)
+ expect(collection.inherited_edit_users).not_to include(user.username)
+ expect(collection.editors).not_to include(user.username)
end
it "should not remove users who do not have the editor role" do
not_editor = FactoryGirl.create(:manager)
collection.edit_users = [not_editor.username]
collection.inherited_edit_users = [not_editor.username]
collection.remove_editor(not_editor.username)
- collection.edit_users.should include(not_editor.username)
- collection.inherited_edit_users.should_not include(user.username)
+ expect(collection.edit_users).to include(not_editor.username)
+ expect(collection.inherited_edit_users).not_to include(user.username)
end
end
end
@@ -312,31 +313,31 @@
describe "#depositors" do
it "should return the read_users" do
collection.read_users = [user.username]
- collection.depositors.should == [user.username]
+ expect(collection.depositors).to eq([user.username])
end
end
describe "#depositors=" do
it "should add depositors to the collection" do
depositor_list = [FactoryGirl.create(:user).username, FactoryGirl.create(:user).username]
collection.depositors = depositor_list
- collection.depositors.should == depositor_list
+ expect(collection.depositors).to eq(depositor_list)
end
it "should call add_depositor" do
depositor_list = [FactoryGirl.create(:user).username, FactoryGirl.create(:user).username]
- collection.should_receive("add_depositor").with(depositor_list[0])
- collection.should_receive("add_depositor").with(depositor_list[1])
+ expect(collection).to receive("add_depositor").with(depositor_list[0])
+ expect(collection).to receive("add_depositor").with(depositor_list[1])
collection.depositors = depositor_list
end
it "should remove depositors from the collection" do
name = user.username
collection.depositors = [name]
- collection.depositors.should == [name]
+ expect(collection.depositors).to eq([name])
collection.depositors -= [name]
- collection.depositors.should == []
+ expect(collection.depositors).to eq([])
end
it "should call remove_depositor" do
collection.add_depositor(user.username)
- collection.should_receive("remove_depositor").with(user.username)
+ expect(collection).to receive("remove_depositor").with(user.username)
collection.depositors = [FactoryGirl.create(:user).username]
end
end
@@ -344,22 +345,22 @@
it "should give edit access to the collection" do
not_depositor = FactoryGirl.create(:user)
collection.add_depositor(not_depositor.username)
- collection.inherited_edit_users.should include(not_depositor.username)
- collection.depositors.should include(not_depositor.username)
+ expect(collection.inherited_edit_users).to include(not_depositor.username)
+ expect(collection.depositors).to include(not_depositor.username)
end
end
describe "#remove_depositor" do
it "should revoke edit access to the collection" do
collection.add_depositor(user.username)
collection.remove_depositor(user.username)
- collection.inherited_edit_users.should_not include(user.username)
- collection.depositors.should_not include(user.username)
+ expect(collection.inherited_edit_users).not_to include(user.username)
+ expect(collection.depositors).not_to include(user.username)
end
it "should not remove users who do not have the depositor role" do
not_depositor = FactoryGirl.create(:manager)
collection.inherited_edit_users = [not_depositor.username]
collection.remove_depositor(not_depositor.username)
- collection.inherited_edit_users.should include(not_depositor.username)
+ expect(collection.inherited_edit_users).to include(not_depositor.username)
end
end
end
@@ -383,15 +384,15 @@
end
it 'sets the new collection on media_object' do
- @media_objects.each{|m| m.collection.should eql @target_collection }
+ @media_objects.each{|m| expect(m.collection).to eql @target_collection }
end
it 'removes the media object from the source collection' do
- @source_collection.media_objects.should eq []
+ expect(@source_collection.media_objects).to eq []
end
it 'adds the media object to the target collection' do
- @target_collection.media_objects.should eq @media_objects
+ expect(@target_collection.media_objects).to eq @media_objects
end
end
@@ -407,13 +408,13 @@
end
it "should persist assigned #default_read_users" do
- Admin::Collection.find(collection.pid).default_read_users.should == users
+ expect(Admin::Collection.find(collection.pid).default_read_users).to eq(users)
end
it "should persist empty #default_read_users" do
collection.default_read_users = []
collection.save
- Admin::Collection.find(collection.pid).default_read_users.should == []
+ expect(Admin::Collection.find(collection.pid).default_read_users).to eq([])
end
end
@@ -426,13 +427,13 @@
end
it "should persist assigned #default_read_groups" do
- Admin::Collection.find(collection.pid).default_read_groups.should == groups
+ expect(Admin::Collection.find(collection.pid).default_read_groups).to eq(groups)
end
it "should persist empty #default_read_groups" do
collection.default_read_groups = []
collection.save
- Admin::Collection.find(collection.pid).default_read_groups.should == []
+ expect(Admin::Collection.find(collection.pid).default_read_groups).to eq([])
end
end
end
@@ -442,23 +443,23 @@
let!(:collection) {FactoryGirl.create(:collection)}
it 'should call reindex_members if name has changed' do
collection.name = "New name"
- collection.should be_name_changed
- collection.should_receive("reindex_members").and_return(nil)
+ expect(collection).to be_name_changed
+ expect(collection).to receive("reindex_members").and_return(nil)
collection.save
end
it 'should call reindex_members if unit has changed' do
collection.unit = Admin::Collection.units.last
- collection.should be_unit_changed
- collection.should_receive("reindex_members").and_return(nil)
+ expect(collection).to be_unit_changed
+ expect(collection).to receive("reindex_members").and_return(nil)
collection.save
end
it 'should not call reindex_members if name or unit has not been changed' do
collection.description = "A different description"
- collection.should_not be_name_changed
- collection.should_not be_unit_changed
- collection.should_not_receive("reindex_members")
+ expect(collection).not_to be_name_changed
+ expect(collection).not_to be_unit_changed
+ expect(collection).not_to receive("reindex_members")
collection.save
end
end
@@ -466,8 +467,7 @@
describe "reindex_members" do
before do
- @media_objects = (1..3).map{ FactoryGirl.create(:media_object)}
- @collection = FactoryGirl.create(:collection, media_objects: @media_objects)
+ @collection = FactoryGirl.create(:collection, items: 3)
allow(Admin::Collection).to receive(:find).with(@collection.pid).and_return(@collection)
end
it 'should reindex in the background' do
@@ -475,7 +475,7 @@
end
it 'should call update_index on all member objects' do
Delayed::Worker.delay_jobs = false
- @media_objects.each {|mo| mo.should_receive("update_index").and_return(true)}
+ @collection.media_objects.each {|mo| expect(mo).to receive("update_index").and_return(true)}
@collection.reindex_members {}
Delayed::Worker.delay_jobs = true
end
@@ -486,22 +486,22 @@
it 'removes bad characters from collection name' do
collection.name = '../../secret.rb'
- Dir.should_receive(:mkdir).with( File.join(Avalon::Configuration.lookup('dropbox.path'), '______secret_rb') )
- Dir.stub(:mkdir) # stubbing this out in a before(:each) block will effect where mkdir is used elsewhere (i.e. factories)
+ expect(Dir).to receive(:mkdir).with( File.join(Avalon::Configuration.lookup('dropbox.path'), '______secret_rb') )
+ allow(Dir).to receive(:mkdir) # stubbing this out in a before(:each) block will effect where mkdir is used elsewhere (i.e. factories)
collection.send(:create_dropbox_directory!)
end
it 'sets dropbox_directory_name on collection' do
collection.name = 'african art'
- Dir.stub(:mkdir)
+ allow(Dir).to receive(:mkdir)
collection.send(:create_dropbox_directory!)
- collection.dropbox_directory_name.should == 'african_art'
+ expect(collection.dropbox_directory_name).to eq('african_art')
end
it 'uses a different directory name if the directory exists' do
collection.name = 'african art'
FakeFS.activate!
FileUtils.mkdir_p(File.join(Avalon::Configuration.lookup('dropbox.path'), 'african_art'))
FileUtils.mkdir_p(File.join(Avalon::Configuration.lookup('dropbox.path'), 'african_art_2'))
- Dir.should_receive(:mkdir).with(File.join(Avalon::Configuration.lookup('dropbox.path'), 'african_art_3'))
+ expect(Dir).to receive(:mkdir).with(File.join(Avalon::Configuration.lookup('dropbox.path'), 'african_art_3'))
collection.send(:create_dropbox_directory!)
FakeFS.deactivate!
end
@@ -514,8 +514,8 @@
it 'handles Unicode collection names correctly' do
collection.name = collection_name
- Dir.should_receive(:mkdir).with( File.join(Avalon::Configuration.lookup('dropbox.path'), collection_dir) )
- Dir.stub(:mkdir)
+ expect(Dir).to receive(:mkdir).with( File.join(Avalon::Configuration.lookup('dropbox.path'), collection_dir) )
+ allow(Dir).to receive(:mkdir)
collection.send(:create_dropbox_directory!)
end
end
diff --git a/spec/models/comments_spec.rb b/spec/models/comments_spec.rb
index bd90f75064..782b724d58 100644
--- a/spec/models/comments_spec.rb
+++ b/spec/models/comments_spec.rb
@@ -25,36 +25,36 @@
end
it "should validate if all fields are entered correctly" do
- @comment_test.should be_valid
+ expect(@comment_test).to be_valid
end
it "should fail if the name is missing" do
@comment_test.name = nil
- @comment_test.should_not be_valid
+ expect(@comment_test).not_to be_valid
end
describe "Subject" do
it "should fail if there is no subject" do
@comment_test.subject = nil
- @comment_test.should_not be_valid
+ expect(@comment_test).not_to be_valid
end
it "should fail if the subject is not in the list" do
@comment_test.subject = 'Not in the list'
- @comment_test.should_not be_valid
+ expect(@comment_test).not_to be_valid
end
end
describe "Comments" do
it "should fail if there is no comment" do
@comment_test.comment = nil
- @comment_test.should_not be_valid
+ expect(@comment_test).not_to be_valid
end
it "should strip out any unsafe HTML" do
@comment_test.comment =
"But this is safe
"
- @comment_test.comment.should_not match /\