3 class Arvados::V1::CollectionsControllerTest < ActionController::TestCase
6 PERM_TOKEN_RE = /\+A[[:xdigit:]]+@[[:xdigit:]]{8}\b/
8 def permit_unsigned_manifests isok=true
9 # Set security model for the life of a test.
10 Rails.configuration.permit_create_collection_with_unsigned_manifest = isok
13 def assert_signed_manifest manifest_text, label=''
14 assert_not_nil manifest_text, "#{label} manifest_text was nil"
15 manifest_text.scan(/ [[:xdigit:]]{32}\S*/) do |tok|
16 assert_match(PERM_TOKEN_RE, tok,
17 "Locator in #{label} manifest_text was not signed")
21 def assert_unsigned_manifest resp, label=''
22 txt = resp['unsigned_manifest_text']
23 assert_not_nil(txt, "#{label} unsigned_manifest_text was nil")
25 txt.scan(/ [[:xdigit:]]{32}\S*/) do |tok|
27 refute_match(PERM_TOKEN_RE, tok,
28 "Locator in #{label} unsigned_manifest_text was signed: #{tok}")
33 test "should get index" do
34 authorize_with :active
36 assert_response :success
37 assert(assigns(:objects).andand.any?, "no Collections returned in index")
38 refute(json_response["items"].any? { |c| c.has_key?("manifest_text") },
39 "basic Collections index included manifest_text")
42 test "collections.get returns signed locators, and no unsigned_manifest_text" do
43 permit_unsigned_manifests
44 authorize_with :active
45 get :show, {id: collections(:foo_file).uuid}
46 assert_response :success
47 assert_signed_manifest json_response['manifest_text'], 'foo_file'
48 refute_includes json_response, 'unsigned_manifest_text'
51 test "index with manifest_text selected returns signed locators" do
52 columns = %w(uuid owner_uuid manifest_text)
53 authorize_with :active
54 get :index, select: columns
55 assert_response :success
56 assert(assigns(:objects).andand.any?,
57 "no Collections returned for index with columns selected")
58 json_response["items"].each do |coll|
59 assert_equal(coll.keys - ['kind'], columns,
60 "Collections index did not respect selected columns")
61 assert_signed_manifest coll['manifest_text'], coll['uuid']
65 test "index with unsigned_manifest_text selected returns only unsigned locators" do
66 authorize_with :active
67 get :index, select: ['unsigned_manifest_text']
68 assert_response :success
69 assert_operator json_response["items"].count, :>, 0
71 json_response["items"].each do |coll|
72 assert_equal(coll.keys - ['kind'], ['unsigned_manifest_text'],
73 "Collections index did not respect selected columns")
74 locs += assert_unsigned_manifest coll, coll['uuid']
76 assert_operator locs, :>, 0, "no locators found in any manifests"
79 test 'index without select returns everything except manifest' do
80 authorize_with :active
82 assert_response :success
83 assert json_response['items'].any?
84 json_response['items'].each do |coll|
85 assert_includes(coll.keys, 'uuid')
86 assert_includes(coll.keys, 'name')
87 assert_includes(coll.keys, 'created_at')
88 refute_includes(coll.keys, 'manifest_text')
92 ['', nil, false, 'null'].each do |select|
93 test "index with select=#{select.inspect} returns everything except manifest" do
94 authorize_with :active
95 get :index, select: select
96 assert_response :success
97 assert json_response['items'].any?
98 json_response['items'].each do |coll|
99 assert_includes(coll.keys, 'uuid')
100 assert_includes(coll.keys, 'name')
101 assert_includes(coll.keys, 'created_at')
102 refute_includes(coll.keys, 'manifest_text')
108 ["uuid", "manifest_text"],
110 '["uuid", "manifest_text"]'].each do |select|
111 test "index with select=#{select.inspect} returns no name" do
112 authorize_with :active
113 get :index, select: select
114 assert_response :success
115 assert json_response['items'].any?
116 json_response['items'].each do |coll|
117 refute_includes(coll.keys, 'name')
122 [0,1,2].each do |limit|
123 test "get index with limit=#{limit}" do
124 authorize_with :active
125 get :index, limit: limit
126 assert_response :success
127 assert_equal limit, assigns(:objects).count
128 resp = JSON.parse(@response.body)
129 assert_equal limit, resp['limit']
133 test "items.count == items_available" do
134 authorize_with :active
135 get :index, limit: 100000
136 assert_response :success
137 resp = JSON.parse(@response.body)
138 assert_equal resp['items_available'], assigns(:objects).length
139 assert_equal resp['items_available'], resp['items'].count
140 unique_uuids = resp['items'].collect { |i| i['uuid'] }.compact.uniq
141 assert_equal unique_uuids.count, resp['items'].count
144 test "items.count == items_available with filters" do
145 authorize_with :active
148 filters: [['uuid','=',collections(:foo_file).uuid]]
150 assert_response :success
151 assert_equal 1, assigns(:objects).length
152 assert_equal 1, json_response['items_available']
153 assert_equal 1, json_response['items'].count
156 test "get index with limit=2 offset=99999" do
157 # Assume there are not that many test fixtures.
158 authorize_with :active
159 get :index, limit: 2, offset: 99999
160 assert_response :success
161 assert_equal 0, assigns(:objects).count
162 resp = JSON.parse(@response.body)
163 assert_equal 2, resp['limit']
164 assert_equal 99999, resp['offset']
167 def request_capped_index(params={})
168 authorize_with :user1_with_load
169 coll1 = collections(:collection_1_of_201)
170 Rails.configuration.max_index_database_read =
171 yield(coll1.manifest_text.size)
173 select: %w(uuid manifest_text),
174 filters: [["owner_uuid", "=", coll1.owner_uuid]],
179 test "index with manifest_text limited by max_index_database_read returns non-empty" do
180 request_capped_index() { |_| 1 }
181 assert_response :success
182 assert_equal(1, json_response["items"].size)
183 assert_equal(1, json_response["limit"])
184 assert_equal(201, json_response["items_available"])
187 test "max_index_database_read size check follows same order as real query" do
188 authorize_with :user1_with_load
189 txt = '.' + ' d41d8cd98f00b204e9800998ecf8427e+0'*1000 + " 0:0:empty.txt\n"
190 c = Collection.create! manifest_text: txt, name: '0000000000000000000'
191 request_capped_index(select: %w(uuid manifest_text name),
193 filters: [['name','>=',c.name]]) do |_|
196 assert_response :success
197 assert_equal(1, json_response["items"].size)
198 assert_equal(1, json_response["limit"])
199 assert_equal(c.uuid, json_response["items"][0]["uuid"])
200 # The effectiveness of the test depends on >1 item matching the filters.
201 assert_operator(1, :<, json_response["items_available"])
204 test "index with manifest_text limited by max_index_database_read" do
205 request_capped_index() { |size| (size * 3) + 1 }
206 assert_response :success
207 assert_equal(3, json_response["items"].size)
208 assert_equal(3, json_response["limit"])
209 assert_equal(201, json_response["items_available"])
212 test "max_index_database_read does not interfere with limit" do
213 request_capped_index(limit: 5) { |size| size * 20 }
214 assert_response :success
215 assert_equal(5, json_response["items"].size)
216 assert_equal(5, json_response["limit"])
217 assert_equal(201, json_response["items_available"])
220 test "max_index_database_read does not interfere with order" do
221 request_capped_index(select: %w(uuid manifest_text name),
222 order: "name DESC") { |size| (size * 11) + 1 }
223 assert_response :success
224 assert_equal(11, json_response["items"].size)
225 assert_empty(json_response["items"].reject do |coll|
226 coll["name"] =~ /^Collection_9/
228 assert_equal(11, json_response["limit"])
229 assert_equal(201, json_response["items_available"])
232 test "admin can create collection with unsigned manifest" do
233 authorize_with :admin
235 manifest_text: <<-EOS
236 . d41d8cd98f00b204e9800998ecf8427e+0 0:0:foo.txt
237 . acbd18db4cc2f85cedef654fccc4a4d8+3 0:3:bar.txt
238 . acbd18db4cc2f85cedef654fccc4a4d8+3 0:3:bar.txt
239 ./baz acbd18db4cc2f85cedef654fccc4a4d8+3 0:3:bar.txt
242 test_collection[:portable_data_hash] =
243 Digest::MD5.hexdigest(test_collection[:manifest_text]) +
245 test_collection[:manifest_text].length.to_s
247 # post :create will modify test_collection in place, so we save a copy first.
248 # Hash.deep_dup is not sufficient as it preserves references of strings (??!?)
249 post_collection = Marshal.load(Marshal.dump(test_collection))
251 collection: post_collection
254 assert_response :success
255 assert_nil assigns(:objects)
257 response_collection = assigns(:object)
259 stored_collection = Collection.select([:uuid, :portable_data_hash, :manifest_text]).
260 where(portable_data_hash: response_collection['portable_data_hash']).first
262 assert_equal test_collection[:portable_data_hash], stored_collection['portable_data_hash']
264 # The manifest in the response will have had permission hints added.
265 # Remove any permission hints in the response before comparing it to the source.
266 stripped_manifest = stored_collection['manifest_text'].gsub(/\+A[A-Za-z0-9@_-]+/, '')
267 assert_equal test_collection[:manifest_text], stripped_manifest
269 # TBD: create action should add permission signatures to manifest_text in the response,
270 # and we need to check those permission signatures here.
273 [:admin, :active].each do |user|
274 test "#{user} can get collection using portable data hash" do
277 foo_collection = collections(:foo_file)
279 # Get foo_file using its portable data hash
281 id: foo_collection[:portable_data_hash]
283 assert_response :success
284 assert_not_nil assigns(:object)
285 resp = assigns(:object)
286 assert_equal foo_collection[:portable_data_hash], resp['portable_data_hash']
287 assert_signed_manifest resp['manifest_text']
289 # The manifest in the response will have had permission hints added.
290 # Remove any permission hints in the response before comparing it to the source.
291 stripped_manifest = resp['manifest_text'].gsub(/\+A[A-Za-z0-9@_-]+/, '')
292 assert_equal foo_collection[:manifest_text], stripped_manifest
296 test "create with owner_uuid set to owned group" do
297 permit_unsigned_manifests
298 authorize_with :active
299 manifest_text = ". d41d8cd98f00b204e9800998ecf8427e 0:0:foo.txt\n"
302 owner_uuid: 'zzzzz-j7d0g-rew6elm53kancon',
303 manifest_text: manifest_text,
304 portable_data_hash: "d30fe8ae534397864cb96c544f4cf102+47"
307 assert_response :success
308 resp = JSON.parse(@response.body)
309 assert_equal 'zzzzz-j7d0g-rew6elm53kancon', resp['owner_uuid']
312 test "create fails with duplicate name" do
313 permit_unsigned_manifests
314 authorize_with :admin
315 manifest_text = ". d41d8cd98f00b204e9800998ecf8427e 0:0:foo.txt\n"
318 owner_uuid: 'zzzzz-tpzed-000000000000000',
319 manifest_text: manifest_text,
320 portable_data_hash: "d30fe8ae534397864cb96c544f4cf102+47",
325 response_errors = json_response['errors']
326 assert_not_nil response_errors, 'Expected error in response'
327 assert(response_errors.first.include?('duplicate key'),
328 "Expected 'duplicate key' error in #{response_errors.first}")
331 [false, true].each do |unsigned|
332 test "create with duplicate name, ensure_unique_name, unsigned=#{unsigned}" do
333 permit_unsigned_manifests unsigned
334 authorize_with :active
335 manifest_text = ". acbd18db4cc2f85cedef654fccc4a4d8+3 0:0:foo.txt\n"
337 manifest_text = Collection.sign_manifest manifest_text, api_token(:active)
341 owner_uuid: users(:active).uuid,
342 manifest_text: manifest_text,
343 name: "owned_by_active"
345 ensure_unique_name: true
347 assert_response :success
348 assert_match /^owned_by_active \(\d{4}-\d\d-\d\d.*?Z\)$/, json_response['name']
352 test "create with owner_uuid set to group i can_manage" do
353 permit_unsigned_manifests
354 authorize_with :active
355 manifest_text = ". d41d8cd98f00b204e9800998ecf8427e 0:0:foo.txt\n"
358 owner_uuid: groups(:active_user_has_can_manage).uuid,
359 manifest_text: manifest_text,
360 portable_data_hash: "d30fe8ae534397864cb96c544f4cf102+47"
363 assert_response :success
364 resp = JSON.parse(@response.body)
365 assert_equal groups(:active_user_has_can_manage).uuid, resp['owner_uuid']
368 test "create with owner_uuid fails on group with only can_read permission" do
369 permit_unsigned_manifests
370 authorize_with :active
371 manifest_text = ". d41d8cd98f00b204e9800998ecf8427e 0:0:foo.txt\n"
374 owner_uuid: groups(:all_users).uuid,
375 manifest_text: manifest_text,
376 portable_data_hash: "d30fe8ae534397864cb96c544f4cf102+47"
382 test "create with owner_uuid fails on group with no permission" do
383 permit_unsigned_manifests
384 authorize_with :active
385 manifest_text = ". d41d8cd98f00b204e9800998ecf8427e 0:0:foo.txt\n"
388 owner_uuid: groups(:public).uuid,
389 manifest_text: manifest_text,
390 portable_data_hash: "d30fe8ae534397864cb96c544f4cf102+47"
396 test "admin create with owner_uuid set to group with no permission" do
397 permit_unsigned_manifests
398 authorize_with :admin
399 manifest_text = ". d41d8cd98f00b204e9800998ecf8427e 0:0:foo.txt\n"
402 owner_uuid: 'zzzzz-j7d0g-it30l961gq3t0oi',
403 manifest_text: manifest_text,
404 portable_data_hash: "d30fe8ae534397864cb96c544f4cf102+47"
407 assert_response :success
410 test "should create with collection passed as json" do
411 permit_unsigned_manifests
412 authorize_with :active
416 "manifest_text":". d41d8cd98f00b204e9800998ecf8427e 0:0:foo.txt\n",\
417 "portable_data_hash":"d30fe8ae534397864cb96c544f4cf102+47"\
421 assert_response :success
424 test "should fail to create with checksum mismatch" do
425 permit_unsigned_manifests
426 authorize_with :active
430 "manifest_text":". d41d8cd98f00b204e9800998ecf8427e 0:0:bar.txt\n",\
431 "portable_data_hash":"d30fe8ae534397864cb96c544f4cf102+47"\
438 test "collection UUID is normalized when created" do
439 permit_unsigned_manifests
440 authorize_with :active
443 manifest_text: ". d41d8cd98f00b204e9800998ecf8427e 0:0:foo.txt\n",
444 portable_data_hash: "d30fe8ae534397864cb96c544f4cf102+47+Khint+Xhint+Zhint"
447 assert_response :success
448 assert_not_nil assigns(:object)
449 resp = JSON.parse(@response.body)
450 assert_equal "d30fe8ae534397864cb96c544f4cf102+47", resp['portable_data_hash']
453 test "get full provenance for baz file" do
454 authorize_with :active
455 get :provenance, id: 'ea10d51bcf88862dbcc36eb292017dfd+45'
456 assert_response :success
457 resp = JSON.parse(@response.body)
458 assert_not_nil resp['ea10d51bcf88862dbcc36eb292017dfd+45'] # baz
459 assert_not_nil resp['fa7aeb5140e2848d39b416daeef4ffc5+45'] # bar
460 assert_not_nil resp['1f4b0bc7583c2a7f9102c395f4ffc5e3+45'] # foo
461 assert_not_nil resp['zzzzz-8i9sb-cjs4pklxxjykyuq'] # bar->baz
462 assert_not_nil resp['zzzzz-8i9sb-aceg2bnq7jt7kon'] # foo->bar
465 test "get no provenance for foo file" do
466 # spectator user cannot even see baz collection
467 authorize_with :spectator
468 get :provenance, id: '1f4b0bc7583c2a7f9102c395f4ffc5e3+45'
472 test "get partial provenance for baz file" do
473 # spectator user can see bar->baz job, but not foo->bar job
474 authorize_with :spectator
475 get :provenance, id: 'ea10d51bcf88862dbcc36eb292017dfd+45'
476 assert_response :success
477 resp = JSON.parse(@response.body)
478 assert_not_nil resp['ea10d51bcf88862dbcc36eb292017dfd+45'] # baz
479 assert_not_nil resp['fa7aeb5140e2848d39b416daeef4ffc5+45'] # bar
480 assert_not_nil resp['zzzzz-8i9sb-cjs4pklxxjykyuq'] # bar->baz
481 assert_nil resp['zzzzz-8i9sb-aceg2bnq7jt7kon'] # foo->bar
482 assert_nil resp['1f4b0bc7583c2a7f9102c395f4ffc5e3+45'] # foo
485 test "search collections with 'any' operator" do
486 expect_pdh = collections(:docker_image).portable_data_hash
487 authorize_with :active
489 where: { any: ['contains', expect_pdh[5..25]] }
491 assert_response :success
492 found = assigns(:objects)
493 assert_equal 1, found.count
494 assert_equal expect_pdh, found.first.portable_data_hash
497 [false, true].each do |permit_unsigned|
498 test "create collection with signed manifest, permit_unsigned=#{permit_unsigned}" do
499 permit_unsigned_manifests permit_unsigned
500 authorize_with :active
502 d41d8cd98f00b204e9800998ecf8427e+0
503 acbd18db4cc2f85cedef654fccc4a4d8+3
504 ea10d51bcf88862dbcc36eb292017dfd+45)
506 unsigned_manifest = locators.map { |loc|
507 ". " + loc + " 0:0:foo.txt\n"
509 manifest_uuid = Digest::MD5.hexdigest(unsigned_manifest) +
511 unsigned_manifest.length.to_s
513 # Build a manifest with both signed and unsigned locators.
515 key: Rails.configuration.blob_signing_key,
516 api_token: api_token(:active),
518 signed_locators = locators.collect do |x|
519 Blob.sign_locator x, signing_opts
522 # Leave a non-empty blob unsigned.
523 signed_locators[1] = locators[1]
525 # Leave the empty blob unsigned. This should still be allowed.
526 signed_locators[0] = locators[0]
529 ". " + signed_locators[0] + " 0:0:foo.txt\n" +
530 ". " + signed_locators[1] + " 0:0:foo.txt\n" +
531 ". " + signed_locators[2] + " 0:0:foo.txt\n"
535 manifest_text: signed_manifest,
536 portable_data_hash: manifest_uuid,
539 assert_response :success
540 assert_not_nil assigns(:object)
541 resp = JSON.parse(@response.body)
542 assert_equal manifest_uuid, resp['portable_data_hash']
543 # All of the locators in the output must be signed.
544 resp['manifest_text'].lines.each do |entry|
545 m = /([[:xdigit:]]{32}\+\S+)/.match(entry)
547 assert Blob.verify_signature m[0], signing_opts
553 test "create collection with signed manifest and explicit TTL" do
554 authorize_with :active
556 d41d8cd98f00b204e9800998ecf8427e+0
557 acbd18db4cc2f85cedef654fccc4a4d8+3
558 ea10d51bcf88862dbcc36eb292017dfd+45)
560 unsigned_manifest = locators.map { |loc|
561 ". " + loc + " 0:0:foo.txt\n"
563 manifest_uuid = Digest::MD5.hexdigest(unsigned_manifest) +
565 unsigned_manifest.length.to_s
567 # build a manifest with both signed and unsigned locators.
568 # TODO(twp): in phase 4, all locators will need to be signed, so
569 # this test should break and will need to be rewritten. Issue #2755.
571 key: Rails.configuration.blob_signing_key,
572 api_token: api_token(:active),
576 ". " + locators[0] + " 0:0:foo.txt\n" +
577 ". " + Blob.sign_locator(locators[1], signing_opts) + " 0:0:foo.txt\n" +
578 ". " + Blob.sign_locator(locators[2], signing_opts) + " 0:0:foo.txt\n"
582 manifest_text: signed_manifest,
583 portable_data_hash: manifest_uuid,
586 assert_response :success
587 assert_not_nil assigns(:object)
588 resp = JSON.parse(@response.body)
589 assert_equal manifest_uuid, resp['portable_data_hash']
590 # All of the locators in the output must be signed.
591 resp['manifest_text'].lines.each do |entry|
592 m = /([[:xdigit:]]{32}\+\S+)/.match(entry)
594 assert Blob.verify_signature m[0], signing_opts
599 test "create fails with invalid signature" do
600 authorize_with :active
602 key: Rails.configuration.blob_signing_key,
603 api_token: api_token(:active),
606 # Generate a locator with a bad signature.
607 unsigned_locator = "acbd18db4cc2f85cedef654fccc4a4d8+3"
608 bad_locator = unsigned_locator + "+Affffffffffffffffffffffffffffffffffffffff@ffffffff"
609 assert !Blob.verify_signature(bad_locator, signing_opts)
611 # Creating a collection with this locator should
612 # produce 403 Permission denied.
613 unsigned_manifest = ". #{unsigned_locator} 0:0:foo.txt\n"
614 manifest_uuid = Digest::MD5.hexdigest(unsigned_manifest) +
616 unsigned_manifest.length.to_s
618 bad_manifest = ". #{bad_locator} 0:0:foo.txt\n"
621 manifest_text: bad_manifest,
622 portable_data_hash: manifest_uuid
629 test "create fails with uuid of signed manifest" do
630 authorize_with :active
632 key: Rails.configuration.blob_signing_key,
633 api_token: api_token(:active),
636 unsigned_locator = "d41d8cd98f00b204e9800998ecf8427e+0"
637 signed_locator = Blob.sign_locator(unsigned_locator, signing_opts)
638 signed_manifest = ". #{signed_locator} 0:0:foo.txt\n"
639 manifest_uuid = Digest::MD5.hexdigest(signed_manifest) +
641 signed_manifest.length.to_s
645 manifest_text: signed_manifest,
646 portable_data_hash: manifest_uuid
653 test "reject manifest with unsigned block as stream name" do
654 authorize_with :active
657 manifest_text: "00000000000000000000000000000000+1234 d41d8cd98f00b204e9800998ecf8427e+0 0:0:foo.txt\n"
660 assert_includes [422, 403], response.code.to_i
663 test "multiple locators per line" do
664 permit_unsigned_manifests
665 authorize_with :active
667 d41d8cd98f00b204e9800998ecf8427e+0
668 acbd18db4cc2f85cedef654fccc4a4d8+3
669 ea10d51bcf88862dbcc36eb292017dfd+45)
671 manifest_text = [".", *locators, "0:0:foo.txt\n"].join(" ")
672 manifest_uuid = Digest::MD5.hexdigest(manifest_text) +
674 manifest_text.length.to_s
677 manifest_text: manifest_text,
678 portable_data_hash: manifest_uuid,
680 post_collection = Marshal.load(Marshal.dump(test_collection))
682 collection: post_collection
684 assert_response :success
685 assert_not_nil assigns(:object)
686 resp = JSON.parse(@response.body)
687 assert_equal manifest_uuid, resp['portable_data_hash']
689 # The manifest in the response will have had permission hints added.
690 # Remove any permission hints in the response before comparing it to the source.
691 stripped_manifest = resp['manifest_text'].gsub(/\+A[A-Za-z0-9@_-]+/, '')
692 assert_equal manifest_text, stripped_manifest
695 test "multiple signed locators per line" do
696 permit_unsigned_manifests
697 authorize_with :active
699 d41d8cd98f00b204e9800998ecf8427e+0
700 acbd18db4cc2f85cedef654fccc4a4d8+3
701 ea10d51bcf88862dbcc36eb292017dfd+45)
704 key: Rails.configuration.blob_signing_key,
705 api_token: api_token(:active),
708 unsigned_manifest = [".", *locators, "0:0:foo.txt\n"].join(" ")
709 manifest_uuid = Digest::MD5.hexdigest(unsigned_manifest) +
711 unsigned_manifest.length.to_s
713 signed_locators = locators.map { |loc| Blob.sign_locator loc, signing_opts }
714 signed_manifest = [".", *signed_locators, "0:0:foo.txt\n"].join(" ")
718 manifest_text: signed_manifest,
719 portable_data_hash: manifest_uuid,
722 assert_response :success
723 assert_not_nil assigns(:object)
724 resp = JSON.parse(@response.body)
725 assert_equal manifest_uuid, resp['portable_data_hash']
726 # All of the locators in the output must be signed.
727 # Each line is of the form "path locator locator ... 0:0:file.txt"
728 # entry.split[1..-2] will yield just the tokens in the middle of the line
729 returned_locator_count = 0
730 resp['manifest_text'].lines.each do |entry|
731 entry.split[1..-2].each do |tok|
732 returned_locator_count += 1
733 assert Blob.verify_signature tok, signing_opts
736 assert_equal locators.count, returned_locator_count
739 test 'Reject manifest with unsigned blob' do
740 permit_unsigned_manifests false
741 authorize_with :active
742 unsigned_manifest = ". 0cc175b9c0f1b6a831c399e269772661+1 0:1:a.txt\n"
743 manifest_uuid = Digest::MD5.hexdigest(unsigned_manifest)
746 manifest_text: unsigned_manifest,
747 portable_data_hash: manifest_uuid,
751 "Creating a collection with unsigned blobs should respond 403"
752 assert_empty Collection.where('uuid like ?', manifest_uuid+'%'),
753 "Collection should not exist in database after failed create"
756 test 'List expired collection returns empty list' do
757 authorize_with :active
759 where: {name: 'expired_collection'},
761 assert_response :success
762 found = assigns(:objects)
763 assert_equal 0, found.count
766 test 'Show expired collection returns 404' do
767 authorize_with :active
769 id: 'zzzzz-4zz18-mto52zx1s7sn3ih',
774 test 'Update expired collection returns 404' do
775 authorize_with :active
777 id: 'zzzzz-4zz18-mto52zx1s7sn3ih',
779 name: "still expired"
785 test 'List collection with future expiration time succeeds' do
786 authorize_with :active
788 where: {name: 'collection_expires_in_future'},
790 found = assigns(:objects)
791 assert_equal 1, found.count
795 test 'Show collection with future expiration time succeeds' do
796 authorize_with :active
798 id: 'zzzzz-4zz18-padkqo7yb8d9i3j',
800 assert_response :success
803 test 'Update collection with future expiration time succeeds' do
804 authorize_with :active
806 id: 'zzzzz-4zz18-padkqo7yb8d9i3j',
808 name: "still not expired"
811 assert_response :success
814 test "get collection and verify that file_names is not included" do
815 authorize_with :active
816 get :show, {id: collections(:foo_file).uuid}
817 assert_response :success
818 assert_equal collections(:foo_file).uuid, json_response['uuid']
819 assert_nil json_response['file_names']
820 assert json_response['manifest_text']
826 ].each do |description_size, expected_response|
827 # Descriptions are not part of search indexes. Skip until
828 # full-text search is implemented, at which point replace with a
829 # search in description.
830 skip "create collection with description size #{description_size}
831 and expect response #{expected_response}" do
832 authorize_with :active
834 description = 'here is a collection with a very large description'
835 while description.length < description_size
836 description = description + description
839 post :create, collection: {
840 manifest_text: ". d41d8cd98f00b204e9800998ecf8427e+0 0:0:foo.txt\n",
841 description: description,
844 assert_response expected_response
848 [1, 5, nil].each do |ask|
849 test "Set replication_desired=#{ask.inspect}" do
850 Rails.configuration.default_collection_replication = 2
851 authorize_with :active
853 id: collections(:replication_undesired_unconfirmed).uuid,
855 replication_desired: ask,
858 assert_response :success
859 assert_equal ask, json_response['replication_desired']
863 test "get collection with properties" do
864 authorize_with :active
865 get :show, {id: collections(:collection_with_one_property).uuid}
866 assert_response :success
867 assert_not_nil json_response['uuid']
868 assert_equal 'value1', json_response['properties']['property1']
871 test "create collection with properties" do
872 authorize_with :active
873 manifest_text = ". d41d8cd98f00b204e9800998ecf8427e 0:0:foo.txt\n"
876 manifest_text: manifest_text,
877 portable_data_hash: "d30fe8ae534397864cb96c544f4cf102+47",
878 properties: {'property_1' => 'value_1'}
881 assert_response :success
882 assert_not_nil json_response['uuid']
883 assert_equal 'value_1', json_response['properties']['property_1']
888 ". d41d8cd98f00b204e9800998ecf8427e foo.txt",
889 "d41d8cd98f00b204e9800998ecf8427e 0:0:foo.txt",
890 ". d41d8cd98f00b204e9800998ecf8427e 0:0:foo.txt",
891 ].each do |manifest_text|
892 test "create collection with invalid manifest #{manifest_text} and expect error" do
893 authorize_with :active
896 manifest_text: manifest_text,
897 portable_data_hash: "d41d8cd98f00b204e9800998ecf8427e+0"
901 response_errors = json_response['errors']
902 assert_not_nil response_errors, 'Expected error in response'
903 assert(response_errors.first.include?('Invalid manifest'),
904 "Expected 'Invalid manifest' error in #{response_errors.first}")
909 [nil, "d41d8cd98f00b204e9800998ecf8427e+0"],
910 ["", "d41d8cd98f00b204e9800998ecf8427e+0"],
911 [". d41d8cd98f00b204e9800998ecf8427e 0:0:foo.txt\n", "d30fe8ae534397864cb96c544f4cf102+47"],
912 ].each do |manifest_text, pdh|
913 test "create collection with valid manifest #{manifest_text.inspect} and expect success" do
914 authorize_with :active
917 manifest_text: manifest_text,
918 portable_data_hash: pdh
927 ". d41d8cd98f00b204e9800998ecf8427e foo.txt",
928 "d41d8cd98f00b204e9800998ecf8427e 0:0:foo.txt",
929 ". d41d8cd98f00b204e9800998ecf8427e 0:0:foo.txt",
930 ].each do |manifest_text|
931 test "update collection with invalid manifest #{manifest_text} and expect error" do
932 authorize_with :active
934 id: 'zzzzz-4zz18-bv31uwvy3neko21',
936 manifest_text: manifest_text,
940 response_errors = json_response['errors']
941 assert_not_nil response_errors, 'Expected error in response'
942 assert(response_errors.first.include?('Invalid manifest'),
943 "Expected 'Invalid manifest' error in #{response_errors.first}")
950 ". d41d8cd98f00b204e9800998ecf8427e 0:0:foo.txt\n",
951 ].each do |manifest_text|
952 test "update collection with valid manifest #{manifest_text.inspect} and expect success" do
953 authorize_with :active
955 id: 'zzzzz-4zz18-bv31uwvy3neko21',
957 manifest_text: manifest_text,
964 test 'get trashed collection with include_trash' do
965 uuid = 'zzzzz-4zz18-mto52zx1s7sn3ih' # expired_collection
966 authorize_with :active
974 test 'get trashed collection without include_trash' do
975 uuid = 'zzzzz-4zz18-mto52zx1s7sn3ih' # expired_collection
976 authorize_with :active
983 test 'trash collection using http DELETE verb' do
984 uuid = collections(:collection_owned_by_active).uuid
985 authorize_with :active
990 c = Collection.unscoped.find_by_uuid(uuid)
991 assert_operator c.trash_at, :<, db_current_time
992 assert_equal c.delete_at, c.trash_at + Rails.configuration.blob_signature_ttl
995 test 'delete long-trashed collection immediately using http DELETE verb' do
996 uuid = 'zzzzz-4zz18-mto52zx1s7sn3ih' # expired_collection
997 authorize_with :active
1002 c = Collection.unscoped.find_by_uuid(uuid)
1003 assert_operator c.trash_at, :<, db_current_time
1004 assert_operator c.delete_at, :<, db_current_time
1007 ['zzzzz-4zz18-mto52zx1s7sn3ih', # expired_collection
1008 :empty_collection_name_in_active_user_home_project,
1010 test "trash collection #{fixture} via trash action with grace period" do
1011 if fixture.is_a? String
1014 uuid = collections(fixture).uuid
1016 authorize_with :active
1017 time_before_trashing = db_current_time
1022 c = Collection.unscoped.find_by_uuid(uuid)
1023 assert_operator c.trash_at, :<, db_current_time
1024 assert_operator c.delete_at, :>=, time_before_trashing + Rails.configuration.default_trash_lifetime