curation_concerns 2.0.0.rc1 → 2.0.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +0 -12
  3. data/.travis.yml +6 -1
  4. data/CONTRIBUTING.md +68 -20
  5. data/app/actors/curation_concerns/actors/add_as_member_of_collections_actor.rb +3 -0
  6. data/app/actors/curation_concerns/actors/add_to_work_actor.rb +16 -4
  7. data/app/actors/curation_concerns/actors/apply_order_actor.rb +16 -5
  8. data/app/actors/curation_concerns/optimistic_lock_validator.rb +28 -0
  9. data/app/assets/javascripts/curation_concerns/file_manager/sorting.es6 +17 -7
  10. data/app/controllers/concerns/curation_concerns/curation_concern_controller.rb +11 -1
  11. data/app/controllers/concerns/curation_concerns/download_behavior.rb +6 -0
  12. data/app/forms/curation_concerns/forms/file_manager_form.rb +27 -0
  13. data/app/forms/curation_concerns/forms/work_form.rb +8 -0
  14. data/app/indexers/curation_concerns/file_set_indexer.rb +2 -2
  15. data/app/jobs/characterize_job.rb +12 -5
  16. data/app/jobs/create_derivatives_job.rb +10 -5
  17. data/app/jobs/ingest_file_job.rb +13 -9
  18. data/app/models/concerns/curation_concerns/ability.rb +1 -0
  19. data/app/models/concerns/curation_concerns/collection_behavior.rb +1 -1
  20. data/app/models/concerns/curation_concerns/work_behavior.rb +6 -0
  21. data/app/presenters/curation_concerns/admin/workflow_role_presenter.rb +4 -0
  22. data/app/presenters/curation_concerns/member_presenter_factory.rb +70 -0
  23. data/app/presenters/curation_concerns/work_show_presenter.rb +7 -47
  24. data/app/services/curation_concerns/actors/actor_factory.rb +2 -1
  25. data/app/services/curation_concerns/workflow/grant_edit_to_depositor.rb +1 -1
  26. data/app/services/curation_concerns/workflow/permission_query.rb +4 -1
  27. data/app/services/curation_concerns/workflow/workflow_importer.rb +1 -0
  28. data/app/services/curation_concerns/workflow/workflow_schema.rb +1 -0
  29. data/app/views/curation_concerns/admin/workflow_roles/index.html.erb +1 -2
  30. data/app/views/curation_concerns/base/_file_manager_member_resource_options.html.erb +2 -2
  31. data/app/views/curation_concerns/base/_file_manager_members.html.erb +13 -5
  32. data/app/views/curation_concerns/base/_file_manager_resource_form.html.erb +1 -1
  33. data/app/views/curation_concerns/base/_form.html.erb +5 -0
  34. data/app/views/curation_concerns/base/_form_in_works_error.html.erb +3 -0
  35. data/app/views/curation_concerns/base/_form_ordered_members_error.html.erb +3 -0
  36. data/app/views/curation_concerns/base/file_manager.html.erb +2 -2
  37. data/app/views/curation_concerns/base/show.json.jbuilder +2 -1
  38. data/config/locales/curation_concerns.en.yml +4 -0
  39. data/curation_concerns.gemspec +1 -1
  40. data/db/migrate/20170308175556_add_allows_access_grant_to_workflow.rb +5 -0
  41. data/lib/curation_concerns/data_migration/collections_migration.rb +16 -0
  42. data/lib/curation_concerns/version.rb +1 -1
  43. data/lib/generators/curation_concerns/templates/workflow.json.erb +1 -0
  44. data/lib/tasks/curation_concerns.rake +12 -0
  45. data/spec/actors/curation_concerns/add_as_member_of_collections_actor_spec.rb +58 -0
  46. data/spec/actors/curation_concerns/apply_order_actor_spec.rb +20 -0
  47. data/spec/actors/curation_concerns/optimistic_lock_validator_spec.rb +50 -0
  48. data/spec/actors/curation_concerns/work_actor_spec.rb +9 -6
  49. data/spec/controllers/curation_concerns/generic_works_controller_spec.rb +2 -1
  50. data/spec/controllers/downloads_controller_spec.rb +5 -6
  51. data/spec/features/create_child_work_spec.rb +16 -2
  52. data/spec/forms/curation_concerns/forms/file_manager_form_spec.rb +19 -0
  53. data/spec/forms/work_form_spec.rb +8 -0
  54. data/spec/lib/curation_concerns/data_migration/collections_migration_spec.rb +34 -0
  55. data/spec/presenters/curation_concerns/member_presenter_factory_spec.rb +25 -0
  56. data/spec/presenters/curation_concerns/work_show_presenter_spec.rb +11 -21
  57. data/spec/services/curation_concerns/workflow/grant_edit_to_depositor_spec.rb +15 -4
  58. data/spec/services/curation_concerns/workflow/permission_query_spec.rb +7 -1
  59. data/spec/services/curation_concerns/workflow/workflow_importer_spec.rb +5 -2
  60. data/spec/services/curation_concerns/workflow/workflow_schema_spec.rb +1 -0
  61. data/spec/views/curation_concerns/admin/workflow_roles/index.html.erb_spec.rb +33 -0
  62. data/spec/views/curation_concerns/base/_form.html.erb_spec.rb +35 -0
  63. data/spec/views/curation_concerns/base/file_manager.html.erb_spec.rb +10 -8
  64. data/spec/views/curation_concerns/base/show.json.jbuilder_spec.rb +3 -1
  65. metadata +26 -5
@@ -4,6 +4,8 @@
4
4
  <div class="alert alert-danger fade in">
5
5
  <strong>Wait don't go!</strong> There was a problem with your submission. Please review the errors below:
6
6
  <a class="close" data-dismiss="alert" href="#">&times;</a>
7
+ <%= render 'form_in_works_error', f: f %>
8
+ <%= render 'form_ordered_members_error', f: f %>
7
9
  </div>
8
10
  <% end %>
9
11
 
@@ -12,6 +14,9 @@
12
14
 
13
15
  <div class="row">
14
16
  <div class="col-md-12 form-actions">
17
+ <%# TODO: If we start using ActionCable, we could listen for object updates and
18
+ alert the user that the object has changed by someone else %>
19
+ <%= f.input CurationConcerns::OptimisticLockValidator.version_field, as: :hidden unless f.object.new_record? %>
15
20
  <%= f.submit class: 'btn btn-primary require-contributor-agreement' %>
16
21
  <% if curation_concern.new_record? %>
17
22
  <%= link_to 'Cancel', main_app.root_path, class: 'btn btn-link' %>
@@ -0,0 +1,3 @@
1
+ <% unless f.object.model.errors[:in_works_ids].empty? %>
2
+ <%= f.full_error(:in_works_ids) %>
3
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <% unless f.object.model.errors[:ordered_member_ids].empty? %>
2
+ <%= f.full_error(:ordered_member_ids) %>
3
+ <% end %>
@@ -1,11 +1,11 @@
1
1
  <h1><%= t("file_manager.link_text") %></h1>
2
2
  <ul class="breadcrumb">
3
3
  <li>
4
- Back to <%= link_to @presenter.to_s, [main_app, @presenter] %>
4
+ Back to <%= link_to @form.to_s, [main_app, @form] %>
5
5
  </li>
6
6
  </ul>
7
7
 
8
- <% if !@presenter.member_presenters.empty? %>
8
+ <% if !@form.member_presenters.empty? %>
9
9
  <div data-action="file-manager">
10
10
  <div class="col-md-3" id="file-manager-tools">
11
11
  <h2>Toolbar</h2>
@@ -1 +1,2 @@
1
- json.extract! @curation_concern, *[:id] + @curation_concern.class.fields.select {|f| ![:has_model].include? f}
1
+ json.extract! @curation_concern, *[:id] + @curation_concern.class.fields.select {|f| ![:has_model].include? f}
2
+ json.version @curation_concern.etag
@@ -1,4 +1,8 @@
1
1
  en:
2
+ activemodel:
3
+ errors:
4
+ messages:
5
+ conflict: "Your changes could not be saved because another user (or background job) updated this %{model} after you began editing. Please make sure all file attachments have completed successfully and try again. This form has refreshed with the most recent saved copy of the %{model}."
2
6
  curation_concerns:
3
7
  admin:
4
8
  workflow_roles:
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_dependency 'hydra-head', '>= 10.3.0', '< 11'
22
- spec.add_dependency 'active-fedora', '>= 10.3.0.rc1'
22
+ spec.add_dependency 'active-fedora', '>= 10.3.0'
23
23
  spec.add_dependency 'blacklight', '~> 6.3'
24
24
  spec.add_dependency 'breadcrumbs_on_rails', '>= 3.0.1', '< 4'
25
25
  spec.add_dependency 'jquery-ui-rails', '~> 5.0.5'
@@ -0,0 +1,5 @@
1
+ class AddAllowsAccessGrantToWorkflow < ActiveRecord::Migration
2
+ def change
3
+ add_column :sipity_workflows, :allows_access_grant, :boolean
4
+ end
5
+ end
@@ -0,0 +1,16 @@
1
+ module CurationConcerns
2
+ module DataMigration
3
+ class CollectionsMigration
4
+ def self.run
5
+ ::Collection.all.each do |collection|
6
+ collection.members.each do |member|
7
+ member.member_of_collections << collection
8
+ member.save
9
+ end
10
+ collection.members = []
11
+ collection.save
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,3 +1,3 @@
1
1
  module CurationConcerns
2
- VERSION = '2.0.0.rc1'.freeze
2
+ VERSION = '2.0.0.rc2'.freeze
3
3
  end
@@ -4,6 +4,7 @@
4
4
  "name": "default",
5
5
  "label": "Default workflow",
6
6
  "description": "A single submission step, default workflow",
7
+ "allows_access_grant": true,
7
8
  "actions": [
8
9
  {
9
10
  "name": "deposit",
@@ -7,4 +7,16 @@ namespace :curation_concerns do
7
7
  puts " #{model}: #{model.count}"
8
8
  end
9
9
  end
10
+ namespace :migrate do
11
+ desc "Migrate collections"
12
+ task collections: :environment do
13
+ CurationConcerns::DataMigration::CollectionsMigration.run
14
+ end
15
+ end
16
+ namespace :solr do
17
+ desc "Enqueue a job to resolrize the repository objects"
18
+ task reindex: :environment do
19
+ ResolrizeJob.perform_later
20
+ end
21
+ end
10
22
  end
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+ describe CurationConcerns::Actors::AddAsMemberOfCollectionsActor do
3
+ let(:user) { create(:user) }
4
+ let(:curation_concern) { GenericWork.new }
5
+ let(:attributes) { {} }
6
+ let(:collection) { FactoryGirl.create(:collection, user: user, title: ['A good title']) }
7
+ subject do
8
+ CurationConcerns::Actors::ActorStack.new(curation_concern,
9
+ user,
10
+ [described_class,
11
+ CurationConcerns::Actors::GenericWorkActor])
12
+ end
13
+ describe 'the next actor' do
14
+ let(:root_actor) { double }
15
+ before do
16
+ allow(CurationConcerns::Actors::RootActor).to receive(:new).and_return(root_actor)
17
+ end
18
+
19
+ let(:attributes) do
20
+ { member_of_collection_ids: [collection.id], title: ['test'] }
21
+ end
22
+
23
+ it 'does not receive the member_of_collection_ids' do
24
+ expect(root_actor).to receive(:create).with(title: ['test'])
25
+ subject.create(attributes)
26
+ end
27
+ end
28
+
29
+ describe 'create' do
30
+ let(:attributes) do
31
+ { member_of_collection_ids: [collection.id], title: ['test'] }
32
+ end
33
+
34
+ it 'adds it to the collection' do
35
+ expect(subject.create(attributes)).to be true
36
+ expect(curation_concern.member_of_collections).to eq [collection]
37
+ end
38
+
39
+ describe "when work is in user's own collection" do
40
+ it "removes the work from that collection" do
41
+ subject.create(attributes)
42
+ expect(subject.create(member_of_collection_ids: [])).to be true
43
+ expect(curation_concern.member_of_collections).to eq []
44
+ end
45
+ end
46
+
47
+ describe "when work is in another user's collection" do
48
+ let(:other_user) { create(:user) }
49
+ let(:collection) { FactoryGirl.create(:collection, user: other_user, title: ['A good title']) }
50
+
51
+ it "doesn't remove the work from that collection" do
52
+ subject.create(attributes)
53
+ expect(subject.create(member_of_collection_ids: [])).to be true
54
+ expect(curation_concern.member_of_collections).to eq [collection]
55
+ end
56
+ end
57
+ end
58
+ end
@@ -78,5 +78,25 @@ describe CurationConcerns::Actors::ApplyOrderActor do
78
78
  expect(curation_concern.ordered_member_ids.size).to eq(1)
79
79
  end
80
80
  end
81
+
82
+ context 'with ordered_member_ids that include a work owned by a different user' do
83
+ # set user not a non-admin for this test to ensure the actor disallows adding the child
84
+ let(:user) { create(:user) }
85
+ let(:other_user) { create(:user) }
86
+ let(:child) { create(:generic_work, user: other_user) }
87
+ let(:attributes) { { ordered_member_ids: [child.id] } }
88
+ let(:root_actor) { double }
89
+
90
+ before do
91
+ allow(CurationConcerns::Actors::RootActor).to receive(:new).and_return(root_actor)
92
+ allow(root_actor).to receive(:update).with({}).and_return(true)
93
+ curation_concern.apply_depositor_metadata(user.user_key)
94
+ curation_concern.save!
95
+ end
96
+
97
+ it "does not attach the work" do
98
+ expect(subject.update(attributes)).to be false
99
+ end
100
+ end
81
101
  end
82
102
  end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe CurationConcerns::OptimisticLockValidator do
4
+ let(:update_actor) do
5
+ double('update actor', update: true,
6
+ curation_concern: work,
7
+ user: depositor)
8
+ end
9
+
10
+ let(:actor) do
11
+ CurationConcerns::Actors::ActorStack.new(work, depositor, [described_class])
12
+ end
13
+
14
+ let(:depositor) { create(:user) }
15
+ let(:work) { create(:generic_work) }
16
+
17
+ describe "update" do
18
+ before do
19
+ allow(CurationConcerns::Actors::RootActor).to receive(:new).and_return(update_actor)
20
+ allow(update_actor).to receive(:update).and_return(true)
21
+ end
22
+
23
+ subject { actor.update(attributes) }
24
+
25
+ context "when version is blank" do
26
+ let(:attributes) { { version: '' } }
27
+ it { is_expected.to be true }
28
+ end
29
+
30
+ context "when version is provided" do
31
+ context "and the version is current" do
32
+ let(:attributes) { { version: work.etag } }
33
+
34
+ it "returns true and calls the next actor without the version attribute" do
35
+ expect(update_actor).to receive(:update).with({}).and_return(true)
36
+ expect(subject).to be true
37
+ end
38
+ end
39
+
40
+ context "and the version is not current" do
41
+ let(:attributes) { { version: "W/\"ab2e8552cb5f7f00f91d2b223eca45849c722301\"" } }
42
+
43
+ it "returns false and sets an error" do
44
+ expect(subject).to be false
45
+ expect(work.errors[:base]).to include "Your changes could not be saved because another user (or background job) updated this Generic work after you began editing. Please make sure all file attachments have completed successfully and try again. This form has refreshed with the most recent saved copy of the Generic work."
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -81,7 +81,7 @@ describe CurationConcerns::Actors::GenericWorkActor do
81
81
  end
82
82
 
83
83
  context 'with in_work_ids' do
84
- let(:parent) { FactoryGirl.create(:generic_work) }
84
+ let(:parent) { create(:generic_work, user: user) }
85
85
  let(:attributes) do
86
86
  FactoryGirl.attributes_for(:generic_work, visibility: visibility).merge(
87
87
  in_works_ids: [parent.id]
@@ -91,6 +91,11 @@ describe CurationConcerns::Actors::GenericWorkActor do
91
91
  expect(subject.create(attributes)).to be true
92
92
  expect(curation_concern.in_works).to eq [parent]
93
93
  end
94
+ it "does not attach the parent" do
95
+ allow(curation_concern).to receive(:depositor).and_return("blahblahblah")
96
+ expect(subject.create(attributes)).to be false
97
+ expect(curation_concern.in_works).to eq []
98
+ end
94
99
  end
95
100
 
96
101
  context 'with a file' do
@@ -190,23 +195,20 @@ describe CurationConcerns::Actors::GenericWorkActor do
190
195
  end
191
196
 
192
197
  context 'with in_works_ids' do
193
- let(:parent) { FactoryGirl.create(:generic_work) }
194
- let(:old_parent) { FactoryGirl.create(:generic_work) }
198
+ let(:parent) { create(:generic_work, user: user) }
199
+ let(:old_parent) { create(:generic_work, user: user) }
195
200
  let(:attributes) do
196
201
  FactoryGirl.attributes_for(:generic_work).merge(
197
202
  in_works_ids: [parent.id]
198
203
  )
199
204
  end
200
205
  before do
201
- curation_concern.apply_depositor_metadata(user.user_key)
202
- curation_concern.save!
203
206
  old_parent.ordered_members << curation_concern
204
207
  old_parent.save!
205
208
  end
206
209
  it "attaches the parent" do
207
210
  expect(subject.update(attributes)).to be true
208
211
  expect(curation_concern.in_works).to eq [parent]
209
-
210
212
  expect(old_parent.reload.members).to eq []
211
213
  end
212
214
  end
@@ -224,6 +226,7 @@ describe CurationConcerns::Actors::GenericWorkActor do
224
226
  old_parent.save!
225
227
  end
226
228
  it "removes the old parent" do
229
+ allow(curation_concern).to receive(:depositor).and_return(old_parent.depositor)
227
230
  expect(subject.update(attributes)).to be true
228
231
  expect(curation_concern.in_works).to eq []
229
232
  expect(old_parent.reload.members).to eq []
@@ -20,6 +20,7 @@ describe CurationConcerns::GenericWorksController do
20
20
  get :show, params: { id: work }
21
21
  expect(response.code).to eq '401'
22
22
  expect(response).to render_template(:unavailable)
23
+ expect(assigns[:presenter]).to be_instance_of CurationConcerns::WorkShowPresenter
23
24
  expect(flash[:notice]).to eq 'The work is not currently available because it has not yet completed the approval process'
24
25
  end
25
26
  end
@@ -340,7 +341,7 @@ describe CurationConcerns::GenericWorksController do
340
341
  it "is successful" do
341
342
  get :file_manager, params: { id: work.id }
342
343
  expect(response).to be_success
343
- expect(assigns(:presenter)).not_to be_blank
344
+ expect(assigns(:form)).not_to be_blank
344
345
  end
345
346
  end
346
347
 
@@ -6,6 +6,7 @@ describe DownloadsController do
6
6
  let(:file_set) do
7
7
  FactoryGirl.create(:file_with_work, user: user, content: File.open(fixture_file_path('files/image.png')))
8
8
  end
9
+ let(:default_image) { ActionController::Base.helpers.image_path 'default.png' }
9
10
  it 'calls render_404 if the object does not exist' do
10
11
  expect(controller).to receive(:render_404) { controller.render body: nil }
11
12
  get :show, params: { id: '8675309' }
@@ -15,18 +16,16 @@ describe DownloadsController do
15
16
  let(:another_user) { FactoryGirl.create(:user) }
16
17
  before { sign_in another_user }
17
18
 
18
- it 'redirects to root' do
19
+ it 'redirects to the default image' do
19
20
  get :show, params: { id: file_set.to_param }
20
- expect(response).to redirect_to root_path
21
- expect(flash['alert']).to eq 'You are not authorized to access this page.'
21
+ expect(response).to redirect_to default_image
22
22
  end
23
23
  end
24
24
 
25
25
  context "when user isn't logged in" do
26
- it 'redirects to sign in' do
26
+ it 'redirects to the default image' do
27
27
  get :show, params: { id: file_set.to_param }
28
- expect(response).to redirect_to new_user_session_path
29
- expect(flash['alert']).to eq 'You are not authorized to access this page.'
28
+ expect(response).to redirect_to default_image
30
29
  end
31
30
 
32
31
  it 'authorizes the resource using only the id' do
@@ -6,12 +6,12 @@ feature 'Creating a new child Work', :workflow do
6
6
  let!(:sipity_entity) do
7
7
  create(:sipity_entity, proxy_for_global_id: parent.to_global_id.to_s)
8
8
  end
9
- let(:redlock_client_stub) { # stub out redis connection
9
+ let(:redlock_client_stub) do # stub out redis connection
10
10
  client = double('redlock client')
11
11
  allow(client).to receive(:lock).and_yield(true)
12
12
  allow(Redlock::Client).to receive(:new).and_return(client)
13
13
  client
14
- }
14
+ end
15
15
  let!(:parent) { create(:generic_work, user: user, title: ["Parent First"]) }
16
16
 
17
17
  before do
@@ -64,5 +64,19 @@ feature 'Creating a new child Work', :workflow do
64
64
 
65
65
  expect(curation_concern.reload.in_works_ids.length).to eq 2
66
66
  end
67
+
68
+ context "with a parent that doesn't belong to this user" do
69
+ let(:new_user) { create(:user) }
70
+ let(:new_parent) { create(:generic_work, user: new_user) }
71
+ it "fails to update" do
72
+ visit "/concern/parent/#{parent.id}/generic_works/#{curation_concern.id}/edit"
73
+ first("input#generic_work_in_works_ids", visible: false).set new_parent.id
74
+ first("input#parent_id", visible: false).set new_parent.id
75
+ click_on "Update Generic work"
76
+
77
+ expect(new_parent.reload.ordered_members.to_a.length).to eq 0
78
+ expect(page).to have_content "Works can only be related to each other if user has ability to edit both."
79
+ end
80
+ end
67
81
  end
68
82
  end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe CurationConcerns::Forms::FileManagerForm do
4
+ let(:work) { create(:work) }
5
+ let(:ability) { instance_double Ability }
6
+ let(:form) { described_class.new(work, ability) }
7
+
8
+ describe "#member_presenters" do
9
+ subject { form.member_presenters }
10
+ let(:factory) { instance_double(CurationConcerns::MemberPresenterFactory, member_presenters: result) }
11
+ let(:result) { double }
12
+ before do
13
+ allow(CurationConcerns::MemberPresenterFactory).to receive(:new).with(work, ability).and_return(factory)
14
+ end
15
+ it "is delegated to the MemberPresenterFactory" do
16
+ expect(subject).to eq result
17
+ end
18
+ end
19
+ end
@@ -22,6 +22,14 @@ describe CurationConcerns::Forms::WorkForm do
22
22
  let(:ability) { nil }
23
23
  let(:form) { PirateShipForm.new(curation_concern, ability) }
24
24
 
25
+ describe "#version" do
26
+ before do
27
+ allow(curation_concern).to receive(:etag).and_return('123456')
28
+ end
29
+ subject { form.version }
30
+ it { is_expected.to eq '123456' }
31
+ end
32
+
25
33
  describe "#select_files" do
26
34
  let(:curation_concern) { create(:work_with_one_file) }
27
35
  let(:title) { curation_concern.file_sets.first.title.first }
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe CurationConcerns::DataMigration::CollectionsMigration do
4
+ let(:collection1) { create(:collection, id: 'alien', title: ['alien movies']) }
5
+ let(:collection2) { create(:collection, id: 'predator', title: ['predator movies']) }
6
+ let(:work1) { create(:work, title: ['alien resurrection']) }
7
+ let(:work2) { create(:work, title: ['the predator']) }
8
+ let(:work3) { create(:work, title: ['alien vs. predator']) }
9
+
10
+ before do
11
+ collection1.members = [work1, work3]
12
+ collection2.members = [work2, work3]
13
+ end
14
+
15
+ describe '.run' do
16
+ it 'moves relationship from collection#members to curation_concern#member_of_collections' do
17
+ expect(collection1.members.to_a.size).to eq 2
18
+ expect(collection2.members.to_a.size).to eq 2
19
+ expect(work1.member_of_collections.to_a.size).to eq 0
20
+ expect(work2.member_of_collections.to_a.size).to eq 0
21
+ expect(work3.member_of_collections.to_a.size).to eq 0
22
+ described_class.run
23
+ expect(collection1.reload.members.to_a.size).to eq 0
24
+ expect(collection2.reload.members.to_a.size).to eq 0
25
+ expect(work1.reload.member_of_collections.to_a.size).to eq 1
26
+ expect(work1.member_of_collection_ids.first).to eq 'alien'
27
+ expect(work2.reload.member_of_collections.to_a.size).to eq 1
28
+ expect(work2.member_of_collection_ids.first).to eq 'predator'
29
+ expect(work3.reload.member_of_collections.to_a.size).to eq 2
30
+ expect(work3.member_of_collection_ids).to include 'predator'
31
+ expect(work3.member_of_collection_ids).to include 'alien'
32
+ end
33
+ end
34
+ end