diff --git a/.pryrc b/.pryrc new file mode 100644 index 000000000..ca997213f --- /dev/null +++ b/.pryrc @@ -0,0 +1,7 @@ +# frozen_string_literal: true +if defined?(PryByebug) + Pry.commands.alias_command 's', 'step' + Pry.commands.alias_command 'n', 'next' + Pry.commands.alias_command 'f', 'finish' + Pry.commands.alias_command 'c', 'continue' +end diff --git a/.release-version b/.release-version index 6621aa3bc..9b57023ab 100644 --- a/.release-version +++ b/.release-version @@ -1 +1 @@ -3.61.1 +3.62.0 diff --git a/Gemfile.lock b/Gemfile.lock index 12080ecf5..d6c0e887d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -136,7 +136,7 @@ GEM diff-lcs (1.5.1) docile (1.4.0) drb (2.2.1) - dry-cli (1.1.0) + dry-cli (1.2.0) erubi (1.13.0) exception_notification (4.5.0) actionmailer (>= 5.2, < 8) @@ -192,10 +192,10 @@ GEM tilt hashdiff (1.1.0) hashie (5.0.0) - i18n (1.14.5) + i18n (1.14.6) concurrent-ruby (~> 1.0) io-console (0.7.2) - irb (1.14.0) + irb (1.14.1) rdoc (>= 4.0.0) reline (>= 0.4.2) json (2.7.2) @@ -264,7 +264,7 @@ GEM puma (6.4.3) nio4r (~> 2.0) racc (1.8.1) - rack (3.1.7) + rack (3.1.8) rack-mini-profiler (3.3.1) rack (>= 1.2.0) rack-proxy (0.7.7) @@ -319,7 +319,7 @@ GEM rdoc (6.7.0) psych (>= 4.0.0) regexp_parser (2.9.2) - reline (0.5.9) + reline (0.5.10) io-console (~> 0.5) rexml (3.3.6) strscan @@ -404,7 +404,7 @@ GEM rbs syntax_tree (>= 2.0.1) temple (0.10.3) - thor (1.3.1) + thor (1.3.2) tilt (2.4.0) timeout (0.4.1) tzinfo (2.0.6) @@ -413,8 +413,9 @@ GEM vite_rails (3.0.17) railties (>= 5.1, < 8) vite_ruby (~> 3.0, >= 3.2.2) - vite_ruby (3.6.0) + vite_ruby (3.9.0) dry-cli (>= 0.7, < 2) + logger (~> 1.6) rack-proxy (~> 0.6, >= 0.6.1) zeitwerk (~> 2.2) web-console (4.2.1) @@ -434,7 +435,7 @@ GEM xpath (3.2.0) nokogiri (~> 1.8) yard (0.9.36) - zeitwerk (2.6.17) + zeitwerk (2.6.18) PLATFORMS ruby diff --git a/app/frontend/entrypoints/application.js b/app/frontend/entrypoints/application.js index dfc9cb98e..e4c83293f 100644 --- a/app/frontend/entrypoints/application.js +++ b/app/frontend/entrypoints/application.js @@ -29,6 +29,15 @@ console.log('Visit the guide for more information: ', 'https://vite-ruby.netlify // ^^^ Template from Vite. Below is custom code for Limber ^^^ +// Import Rails UJS as in Sequencescape +import Rails from '@rails/ujs' + +try { + Rails.start() +} catch { + // Nothing +} + // Import Libraries import 'bootstrap' import 'popper.js' diff --git a/app/frontend/javascript/custom-tagged-plate/components/CustomTaggedPlate.spec.js b/app/frontend/javascript/custom-tagged-plate/components/CustomTaggedPlate.spec.js index 037afeb9c..954bfc22e 100644 --- a/app/frontend/javascript/custom-tagged-plate/components/CustomTaggedPlate.spec.js +++ b/app/frontend/javascript/custom-tagged-plate/components/CustomTaggedPlate.spec.js @@ -833,8 +833,8 @@ describe('CustomTaggedPlate', () => { purpose_uuid: 'purpose-uuid', parent_uuid: 'parent-plate-uuid', tag_layout: { - tag_group: 'tag-1-group-uuid', - tag2_group: 'tag-2-group-uuid', + tag_group_uuid: 'tag-1-group-uuid', + tag2_group_uuid: 'tag-2-group-uuid', direction: 'column', walking_by: 'manual by plate', initial_tag: 1, diff --git a/app/frontend/javascript/custom-tagged-plate/components/CustomTaggedPlate.vue b/app/frontend/javascript/custom-tagged-plate/components/CustomTaggedPlate.vue index ec11ba7db..8f2f2a123 100644 --- a/app/frontend/javascript/custom-tagged-plate/components/CustomTaggedPlate.vue +++ b/app/frontend/javascript/custom-tagged-plate/components/CustomTaggedPlate.vue @@ -619,8 +619,8 @@ export default { purpose_uuid: this.purposeUuid, parent_uuid: this.parentUuid, tag_layout: { - tag_group: this.tag1GroupUuid, - tag2_group: this.tag2GroupUuid, + tag_group_uuid: this.tag1GroupUuid, + tag2_group_uuid: this.tag2GroupUuid, direction: this.direction, walking_by: this.walkingBy, initial_tag: initialTag, diff --git a/app/frontend/javascript/legacy_scripts_a.js b/app/frontend/javascript/legacy_scripts_a.js index 392a1766b..47237c542 100644 --- a/app/frontend/javascript/legacy_scripts_a.js +++ b/app/frontend/javascript/legacy_scripts_a.js @@ -59,9 +59,12 @@ let limberPlateView = function (defaultTab) { control.find('a[href="' + defaultTab + '"]').tab('show') plateElement.on('click', '.aliquot', function (event) { + control.find('a[data-plate-view="pools-view"]').tab('show') + let pool = $(event.currentTarget).data('pool') - control.find('a[data-plate-view="pools-view"]').tab('show') + // Handle cases where pool is not defined to prevent errors + if (pool === undefined || pool === '') return plateElement .find('.aliquot[data-pool!=' + pool + ']') diff --git a/app/frontend/javascript/pipeline-graph/index.js b/app/frontend/javascript/pipeline-graph/index.js index 9b3d477f8..9e4b0cdeb 100644 --- a/app/frontend/javascript/pipeline-graph/index.js +++ b/app/frontend/javascript/pipeline-graph/index.js @@ -226,6 +226,8 @@ const applyMouseEvents = function () { // when an edge is clicked, filter the graph to show only that pipeline core.on('click', 'edge', (event) => { const pipeline = event.target.data('pipeline') - applyFilter(pipeline) + const group = event.target.data('group') + const pipelineOrGroup = pipeline || group + applyFilter(pipelineOrGroup) }) } diff --git a/app/helpers/page_helper.rb b/app/helpers/page_helper.rb index 49f774667..11e5ba153 100644 --- a/app/helpers/page_helper.rb +++ b/app/helpers/page_helper.rb @@ -52,8 +52,8 @@ def jumbotron(jumbotron_id = nil, options = {}, &) # eg. state_badge('pending') # Pending - def state_badge(state) - tag.span(state.titleize, class: "state-badge #{state}", title: 'Labware State', data: { toggle: 'tooltip' }) + def state_badge(state, title: 'Labware State') + tag.span(state.titleize, class: "state-badge #{state}", title: title, data: { toggle: 'tooltip' }) end # eg. count_badge(0) diff --git a/app/models/labware_creators/baited_plate.rb b/app/models/labware_creators/baited_plate.rb index b4a49ccc6..ebddd05c7 100644 --- a/app/models/labware_creators/baited_plate.rb +++ b/app/models/labware_creators/baited_plate.rb @@ -25,12 +25,13 @@ def plate end def bait_library_layout_preview - @bait_library_layout_preview ||= api.bait_library_layout.preview!(plate: parent_uuid, user: user_uuid).layout + @bait_library_layout_preview ||= + Sequencescape::Api::V2::BaitLibraryLayout.preview(plate_uuid: parent_uuid, user_uuid: user_uuid).first.layout end def create_labware! create_plate_with_standard_transfer! do |child| - api.bait_library_layout.create!(plate: child.uuid, user: user_uuid) + Sequencescape::Api::V2::BaitLibraryLayout.create!(plate_uuid: child.uuid, user_uuid: user_uuid) end end diff --git a/app/models/labware_creators/custom_tagged_plate.rb b/app/models/labware_creators/custom_tagged_plate.rb index 3f6401b13..d7d161c94 100644 --- a/app/models/labware_creators/custom_tagged_plate.rb +++ b/app/models/labware_creators/custom_tagged_plate.rb @@ -18,10 +18,10 @@ class CustomTaggedPlate < Base { tag_plate: %i[asset_uuid template_uuid state], tag_layout: [ - :user, - :plate, - :tag_group, - :tag2_group, + :user_uuid, + :plate_uuid, + :tag_group_uuid, + :tag2_group_uuid, :direction, :walking_by, :initial_tag, @@ -47,14 +47,11 @@ def initialize(*args, &) def create_plate! # rubocop:todo Metrics/AbcSize @child = - api - .pooled_plate_creation - .create!( - child_purpose: purpose_uuid, - user: user_uuid, - parents: [parent_uuid, tag_plate.asset_uuid].compact_blank - ) - .child + Sequencescape::Api::V2::PooledPlateCreation.create!( + child_purpose_uuid: purpose_uuid, + parent_uuids: [parent_uuid, tag_plate.asset_uuid].compact_blank, + user_uuid: user_uuid + ).child transfer_material_from_parent!(@child.uuid) @@ -107,7 +104,7 @@ def tag_layout_attributes def create_labware! create_plate! do |plate_uuid| - api.tag_layout.create!(tag_layout_attributes.merge(plate: plate_uuid, user: user_uuid)) + Sequencescape::Api::V2::TagLayout.create!(tag_layout_attributes.merge(plate_uuid:, user_uuid:)) end end end diff --git a/app/models/labware_creators/merged_plate.rb b/app/models/labware_creators/merged_plate.rb index d6bd17bfb..8f1be6c3f 100644 --- a/app/models/labware_creators/merged_plate.rb +++ b/app/models/labware_creators/merged_plate.rb @@ -51,10 +51,10 @@ def barcodes=(barcodes) private def create_plate_from_parent! - api.pooled_plate_creation.create!( - child_purpose: purpose_uuid, - user: user_uuid, - parents: source_plates.map(&:uuid) + Sequencescape::Api::V2::PooledPlateCreation.create!( + child_purpose_uuid: purpose_uuid, + parent_uuids: source_plates.map(&:uuid), + user_uuid: user_uuid ) end diff --git a/app/models/labware_creators/multi_plate_pool.rb b/app/models/labware_creators/multi_plate_pool.rb index 6beeaacc5..597447a01 100644 --- a/app/models/labware_creators/multi_plate_pool.rb +++ b/app/models/labware_creators/multi_plate_pool.rb @@ -17,10 +17,12 @@ class MultiPlatePool < Base private def create_labware! - plate_creation = - api.pooled_plate_creation.create!(parents: transfers.keys, child_purpose: purpose_uuid, user: user_uuid) - - @child = plate_creation.child + @child = + Sequencescape::Api::V2::PooledPlateCreation.create!( + child_purpose_uuid: purpose_uuid, + parent_uuids: transfers.keys, + user_uuid: user_uuid + ).child api.bulk_transfer.create!(user: user_uuid, well_transfers: well_transfers) diff --git a/app/models/labware_creators/multi_stamp.rb b/app/models/labware_creators/multi_stamp.rb index 6cb8a9810..04d09ccbc 100644 --- a/app/models/labware_creators/multi_stamp.rb +++ b/app/models/labware_creators/multi_stamp.rb @@ -30,10 +30,12 @@ class MultiStamp < Base # rubocop:todo Style/Documentation private def create_labware! - plate_creation = - api.pooled_plate_creation.create!(parents: parent_uuids, child_purpose: purpose_uuid, user: user_uuid) - - @child = plate_creation.child + @child = + Sequencescape::Api::V2::PooledPlateCreation.create!( + child_purpose_uuid: purpose_uuid, + parent_uuids: parent_uuids, + user_uuid: user_uuid + ).child transfer_material_from_parent!(@child.uuid) diff --git a/app/models/labware_creators/multi_stamp_tubes.rb b/app/models/labware_creators/multi_stamp_tubes.rb index aa3b153d0..c596f399a 100644 --- a/app/models/labware_creators/multi_stamp_tubes.rb +++ b/app/models/labware_creators/multi_stamp_tubes.rb @@ -41,13 +41,14 @@ def create_labware! create_and_build_submission return if errors.size.positive? - plate_creation = - api.pooled_plate_creation.create!(parents: parent_uuids, child_purpose: purpose_uuid, user: user_uuid) + @child = + Sequencescape::Api::V2::PooledPlateCreation.create!( + child_purpose_uuid: purpose_uuid, + parent_uuids: parent_uuids, + user_uuid: user_uuid + ).child - @child = plate_creation.child - child_v2 = Sequencescape::Api::V2.plate_with_wells(@child.uuid) - - transfer_material_from_parent!(child_v2) + transfer_material_from_parent! yield(@child) if block_given? true @@ -87,15 +88,12 @@ def parent_tubes Sequencescape::Api::V2::Tube.find_all(uuid: parent_uuids, includes: 'receptacle,aliquots,aliquots.study') end - def transfer_material_from_parent!(child_plate) - api.transfer_request_collection.create!( - user: user_uuid, - transfer_requests: transfer_request_attributes(child_plate) - ) + def transfer_material_from_parent! + api.transfer_request_collection.create!(user: user_uuid, transfer_requests: transfer_request_attributes) end - def transfer_request_attributes(child_plate) - transfers.map { |transfer| request_hash(transfer, child_plate) } + def transfer_request_attributes + transfers.map { |transfer| request_hash(transfer) } end def source_tube_outer_request_uuid(tube) @@ -108,13 +106,13 @@ def source_tube_outer_request_uuid(tube) pending_reqs.first.uuid || nil end - def request_hash(transfer, child_plate) + def request_hash(transfer) tube = Sequencescape::Api::V2::Tube.find_by(uuid: transfer[:source_tube]) { 'source_asset' => transfer[:source_asset], 'target_asset' => - child_plate.wells.detect { |child_well| child_well.location == transfer.dig(:new_target, :location) }&.uuid, + @child.wells.detect { |child_well| child_well.location == transfer.dig(:new_target, :location) }&.uuid, 'outer_request' => source_tube_outer_request_uuid(tube) } end diff --git a/app/models/labware_creators/multi_stamp_tubes_using_tube_rack_scan.rb b/app/models/labware_creators/multi_stamp_tubes_using_tube_rack_scan.rb index d640a3ca6..471889ac4 100644 --- a/app/models/labware_creators/multi_stamp_tubes_using_tube_rack_scan.rb +++ b/app/models/labware_creators/multi_stamp_tubes_using_tube_rack_scan.rb @@ -65,13 +65,14 @@ def save # # @return [Boolean] true if the child plate was created successfully. def create_labware! - plate_creation = - api.pooled_plate_creation.create!(parents: parent_tube_uuids, child_purpose: purpose_uuid, user: user_uuid) + @child = + Sequencescape::Api::V2::PooledPlateCreation.create!( + child_purpose_uuid: purpose_uuid, + parent_uuids: parent_tube_uuids, + user_uuid: user_uuid + ).child - @child = plate_creation.child - child_v2 = Sequencescape::Api::V2.plate_with_wells(@child.uuid) - - transfer_material_from_parent!(child_v2) + transfer_material_from_parent! yield(@child) if block_given? true @@ -232,23 +233,18 @@ def tube_has_expected_active_request?(tube_in_db) end # Transfers material from the parent tubes to the given child plate. - # @param child_plate [Sequencescape::Api::V2::Plate] The plate to transfer material to. - def transfer_material_from_parent!(child_plate) - api.transfer_request_collection.create!( - user: user_uuid, - transfer_requests: transfer_request_attributes(child_plate) - ) + def transfer_material_from_parent! + api.transfer_request_collection.create!(user: user_uuid, transfer_requests: transfer_request_attributes) end # Returns an array of hashes representing the transfer requests for the given child plate. # Each hash includes the UUIDs of the parent tube and child well, and the UUID of the outer request. - # @param child_plate [Sequencescape::Api::V2::Plate] The plate to get the transfer requests for. # @return [Array] An array of hashes representing the transfer requests. - def transfer_request_attributes(child_plate) + def transfer_request_attributes parent_tubes.each_with_object([]) do |(foreign_barcode, parent_tube), tube_transfers| tube_transfers << request_hash( parent_tube.uuid, - child_plate + @child .wells .detect { |child_well| child_well.location == csv_file.location_by_barcode_details[foreign_barcode] } &.uuid, diff --git a/app/models/labware_creators/plate_split_to_tube_racks.rb b/app/models/labware_creators/plate_split_to_tube_racks.rb index c69c8300e..ca7208100 100644 --- a/app/models/labware_creators/plate_split_to_tube_racks.rb +++ b/app/models/labware_creators/plate_split_to_tube_racks.rb @@ -408,13 +408,12 @@ def parent_wells_for_contingency # @param tube_attributes [Hash] A hash of attributes to use for the created tubes. # @return [Hash] A hash of the created tubes indexed by name. def create_tubes(tube_purpose_uuid, number_of_tubes, tube_attributes) - api - .specific_tube_creation + Sequencescape::Api::V2::SpecificTubeCreation .create!( - user: user_uuid, - parent: parent_uuid, - child_purposes: [tube_purpose_uuid] * number_of_tubes, - tube_attributes: tube_attributes + child_purpose_uuids: [tube_purpose_uuid] * number_of_tubes, + parent_uuids: [parent_uuid], + tube_attributes: tube_attributes, + user_uuid: user_uuid ) .children .index_by(&:name) diff --git a/app/models/labware_creators/pooled_tubes_base.rb b/app/models/labware_creators/pooled_tubes_base.rb index f7dbe4bfb..abc4a7d10 100644 --- a/app/models/labware_creators/pooled_tubes_base.rb +++ b/app/models/labware_creators/pooled_tubes_base.rb @@ -20,13 +20,12 @@ def create_labware! end def create_child_stock_tubes - api - .specific_tube_creation + Sequencescape::Api::V2::SpecificTubeCreation .create!( - user: user_uuid, - parent: parent_uuid, - child_purposes: [purpose_uuid] * pool_uuids.length, - tube_attributes: tube_attributes + child_purpose_uuids: [purpose_uuid] * pool_uuids.length, + parent_uuids: [parent_uuid], + tube_attributes: tube_attributes, + user_uuid: user_uuid ) .children .index_by(&:name) diff --git a/app/models/labware_creators/pooled_tubes_by_sample.rb b/app/models/labware_creators/pooled_tubes_by_sample.rb index 3eafcf7dc..f4c00f7ce 100644 --- a/app/models/labware_creators/pooled_tubes_by_sample.rb +++ b/app/models/labware_creators/pooled_tubes_by_sample.rb @@ -47,13 +47,12 @@ def parent_v1 # Have we made this class so cardinal-specific (e.g. lookup of ancestor vac tubes) that it cannot be re-used? def create_child_stock_tubes - api - .specific_tube_creation + Sequencescape::Api::V2::SpecificTubeCreation .create!( - user: user_uuid, - parent: parent_uuid, - child_purposes: [purpose_uuid] * pool_uuids.length, - tube_attributes: tube_attributes + child_purpose_uuids: [purpose_uuid] * pool_uuids.length, + parent_uuids: [parent_uuid], + tube_attributes: tube_attributes, + user_uuid: user_uuid ) .children .index_by(&:name) diff --git a/app/models/labware_creators/pooled_tubes_by_submission_with_phi_x.rb b/app/models/labware_creators/pooled_tubes_by_submission_with_phi_x.rb index f0d6103f0..cef106449 100644 --- a/app/models/labware_creators/pooled_tubes_by_submission_with_phi_x.rb +++ b/app/models/labware_creators/pooled_tubes_by_submission_with_phi_x.rb @@ -15,13 +15,12 @@ class PooledTubesBySubmissionWithPhiX < PooledTubesBySubmission attr_accessor :spikedbuffer_tube_barcode def create_child_stock_tubes - api - .specific_tube_creation + Sequencescape::Api::V2::SpecificTubeCreation .create!( - user: user_uuid, - parents: parents, - child_purposes: [purpose_uuid] * pool_uuids.length, - tube_attributes: tube_attributes + child_purpose_uuids: [purpose_uuid] * pool_uuids.length, + parent_uuids: parents, + tube_attributes: tube_attributes, + user_uuid: user_uuid ) .children .index_by(&:name) diff --git a/app/models/labware_creators/pooled_tubes_from_whole_plates.rb b/app/models/labware_creators/pooled_tubes_from_whole_plates.rb index 41a09f490..937e23569 100644 --- a/app/models/labware_creators/pooled_tubes_from_whole_plates.rb +++ b/app/models/labware_creators/pooled_tubes_from_whole_plates.rb @@ -15,15 +15,14 @@ class PooledTubesFromWholePlates < Base def create_labware! # Create a single tube - # TODO: This should link to multiple parents in production + # TODO: {Y24-190} See if we can do all the transfers as part of the SpecificTubeCreation instead of separately. @child = - api - .specific_tube_creation + Sequencescape::Api::V2::SpecificTubeCreation .create!( - user: user_uuid, - parent: parents.first.uuid, - child_purposes: [purpose_uuid], - tube_attributes: [{ name: "#{stock_plate_barcode}+" }] + child_purpose_uuids: [purpose_uuid], + parent_uuids: [parents.first.uuid], + tube_attributes: [{ name: "#{stock_plate_barcode}+" }], + user_uuid: user_uuid ) .children .first @@ -40,6 +39,10 @@ def stock_plate_barcode "#{parents.first.stock_plate.barcode.prefix}#{parents.first.stock_plate.barcode.number}" end + def redirection_target + TubeProxy.new(@child.uuid) + end + # TODO: This should probably be asynchronous def available_plates @search_options = OngoingPlate.new(purposes: [parent.plate_purpose.uuid], include_used: false, states: ['passed']) diff --git a/app/sequencescape/sequencescape/api/v2/bait_library_layout.rb b/app/sequencescape/sequencescape/api/v2/bait_library_layout.rb new file mode 100644 index 000000000..bb5bb251d --- /dev/null +++ b/app/sequencescape/sequencescape/api/v2/bait_library_layout.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +# bait library layout resource +class Sequencescape::Api::V2::BaitLibraryLayout < Sequencescape::Api::V2::Base + custom_endpoint :preview, on: :collection, request_method: :post +end diff --git a/app/sequencescape/sequencescape/api/v2/pooled_plate_creation.rb b/app/sequencescape/sequencescape/api/v2/pooled_plate_creation.rb new file mode 100644 index 000000000..ef2d1d631 --- /dev/null +++ b/app/sequencescape/sequencescape/api/v2/pooled_plate_creation.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# Represents a pooled plate creation in Limber via the Sequencescape API +class Sequencescape::Api::V2::PooledPlateCreation < Sequencescape::Api::V2::Base + has_one :child, class_name: 'Sequencescape::Api::V2::Plate' + has_many :parents, class_name: 'Sequencescape::Api::V2::Labware' + has_one :user, class_name: 'Sequencescape::Api::V2::User' +end diff --git a/app/sequencescape/sequencescape/api/v2/specific_tube_creation.rb b/app/sequencescape/sequencescape/api/v2/specific_tube_creation.rb new file mode 100644 index 000000000..4b3235552 --- /dev/null +++ b/app/sequencescape/sequencescape/api/v2/specific_tube_creation.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# Represents a specific tube creation in Limber via the Sequencescape API +class Sequencescape::Api::V2::SpecificTubeCreation < Sequencescape::Api::V2::Base + has_many :children, class_name: 'Sequencescape::Api::V2::Tube' + has_many :parents, class_name: 'Sequencescape::Api::V2::Labware' + has_one :user, class_name: 'Sequencescape::Api::V2::User' +end diff --git a/app/sequencescape/sequencescape/api/v2/tag_layout.rb b/app/sequencescape/sequencescape/api/v2/tag_layout.rb new file mode 100644 index 000000000..2e5f92fda --- /dev/null +++ b/app/sequencescape/sequencescape/api/v2/tag_layout.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +# tag layout resource +class Sequencescape::Api::V2::TagLayout < Sequencescape::Api::V2::Base +end diff --git a/app/views/application/_submission_item.html.erb b/app/views/application/_submission_item.html.erb index b6145c676..f09a8da9b 100644 --- a/app/views/application/_submission_item.html.erb +++ b/app/views/application/_submission_item.html.erb @@ -1,3 +1,3 @@
  • - <%= state_badge(submission_item.state) %> <%= submission_item.name %> + <%= state_badge(submission_item.state, title: 'Submission State') %> <%= submission_item.name %>
  • diff --git a/config/pipelines/bespoke_pcr.yml b/config/pipelines/bespoke_pcr.yml index 20668475f..e75c64975 100644 --- a/config/pipelines/bespoke_pcr.yml +++ b/config/pipelines/bespoke_pcr.yml @@ -35,9 +35,11 @@ Bespoke PCR: - SGE Library v0.2 library_pass: LBB Lib PCR-XP relationships: + Stock Plate: LBB Ligation LBB Cherrypick: LBB Ligation LBC Cherrypick: LBB Ligation LBB Ligation: LBB Lib PCR-XP + Bespoke PCR BCR: filters: request_type_key: limber_pcr_bespoke @@ -45,6 +47,7 @@ Bespoke PCR BCR: - Manual Chromium single cell BCR library_pass: LBB Lib PCR-XP relationships: + Stock Plate: LBB Enriched BCR LBB Cherrypick: LBB Enriched BCR LBC Cherrypick: LBB Enriched BCR LBB Enriched BCR: LBB Ligation @@ -56,6 +59,7 @@ Bespoke PCR BCR HT: - Manual Chromium single cell BCR HT library_pass: LBB Lib PCR-XP relationships: + Stock Plate: LBB Enriched BCR HT LBB Cherrypick: LBB Enriched BCR HT LBC Cherrypick: LBB Enriched BCR HT LBB Enriched BCR HT: LBB Ligation @@ -67,6 +71,7 @@ Bespoke PCR TCR: - Manual Chromium single cell TCR library_pass: LBB Lib PCR-XP relationships: + Stock Plate: LBB Enriched TCR LBB Cherrypick: LBB Enriched TCR LBC Cherrypick: LBB Enriched TCR LBB Enriched TCR: LBB Ligation @@ -78,6 +83,7 @@ Bespoke PCR TCR HT: - Manual Chromium single cell TCR HT library_pass: LBB Lib PCR-XP relationships: + Stock Plate: LBB Enriched TCR HT LBB Cherrypick: LBB Enriched TCR HT LBC Cherrypick: LBB Enriched TCR HT LBB Enriched TCR HT: LBB Ligation diff --git a/config/purposes/bespoke.yml b/config/purposes/bespoke.yml index 5a4411904..fc21a0aeb 100644 --- a/config/purposes/bespoke.yml +++ b/config/purposes/bespoke.yml @@ -43,3 +43,8 @@ LBB Enriched TCR: LBB Enriched TCR HT: :asset_type: plate :creator_class: LabwareCreators::PartialStampedPlateWithoutDilution +Stock Plate: + :asset_type: plate + :stock_plate: true + :input_plate: true + :presenter_class: Presenters::StockPlatePresenter diff --git a/package.json b/package.json index 2201fdcf9..7b7c3a0d2 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,9 @@ { "dependencies": { + "@rails/ujs": "^7.1.3", "axios": "^0.28.1", "bootstrap-vue": "^2.23.1", - "cytoscape": "^3.30.0", + "cytoscape": "^3.30.2", "cytoscape-elk": "^2.2.0", "cytoscape-popper": "^2.0.0", "devour-client": "^2.1.2", @@ -33,8 +34,8 @@ "prettier": "^3.0.0", "sass": "^1.77.8", "typescript": "^4.6.3", - "vite": "^5.4.7", - "vite-plugin-ruby": "^5.0.0", + "vite": "^5.0.0", + "vite-plugin-ruby": "^5.1.0", "vitest": "^2.0.5", "vue-template-compiler": "^2.7.0" }, diff --git a/spec/factories/bait_library_layout_factories.rb b/spec/factories/bait_library_layout_factories.rb index 9301e066e..f5fc1bc58 100644 --- a/spec/factories/bait_library_layout_factories.rb +++ b/spec/factories/bait_library_layout_factories.rb @@ -3,11 +3,9 @@ require_relative '../support/factory_bot_extensions' FactoryBot.define do - # Generates a BaitLibraryLayout (API V1) - factory :bait_library_layout, class: Sequencescape::BaitLibraryLayout, traits: [:api_object] do - json_root { 'bait_library_layout' } - - with_belongs_to_associations 'plate' + # Generates an incomplete, but sufficient for testing, V2 BaitLibraryLayout + factory :bait_library_layout, class: Sequencescape::Api::V2::BaitLibraryLayout do + skip_create transient do # Provide an array of pools, describing the number of wells in each diff --git a/spec/factory_outputs/bait_library_layout_factory_spec.rb b/spec/factory_outputs/bait_library_layout_factory_spec.rb deleted file mode 100644 index 784db8853..000000000 --- a/spec/factory_outputs/bait_library_layout_factory_spec.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe 'bait library layout factory' do - subject { json(:bait_library_layout, plate_uuid: 'plate-uuid', uuid: 'bait-library-layout-uuid') } - - let(:json_content) do - '{ - "bait_library_layout": { - "actions": {"read": "http://example.com:3000/bait-library-layout-uuid"}, - "plate": { - "actions": { - "read": "http://example.com:3000/plate-uuid" - }, - "uuid": "plate-uuid" - }, - "uuid": "bait-library-layout-uuid", - "layout": { - "A1": "Human all exon 50MB", - "B1": "Human all exon 50MB", - "C1": "Mouse all exon", - "D1": "Mouse all exon" - } - } - }' - end - - it 'should match the expected json' do - expect(JSON.parse(subject)['bait_library_layout']).to eq JSON.parse(json_content)['bait_library_layout'] - end -end diff --git a/spec/features/creating_plate_with_bait_spec.rb b/spec/features/creating_plate_with_bait_spec.rb index d7625dd66..99aa56faa 100644 --- a/spec/features/creating_plate_with_bait_spec.rb +++ b/spec/features/creating_plate_with_bait_spec.rb @@ -21,6 +21,8 @@ let(:transfer_template) { json :transfer_template, uuid: transfer_template_uuid } let(:expected_transfers) { WellHelpers.stamp_hash(96) } + let(:bait_library_layout) { create :bait_library_layout } + let(:transfer_requests) do WellHelpers.column_order(96)[0, 6].map do |well_name| { 'source_asset' => "2-well-#{well_name}", 'target_asset' => "3-well-#{well_name}", 'submission_id' => '2' } @@ -44,17 +46,7 @@ # end of stubs for plate show page # These stubs are required to render plate_creation baiting page - stub_api_post( - 'bait_library_layouts', - 'preview', - body: json(:bait_library_layout), - payload: { - bait_library_layout: { - plate: plate_uuid, - user: user_uuid - } - } - ) + expect_api_v2_posts('BaitLibraryLayout', [{ plate_uuid:, user_uuid: }], [[bait_library_layout]], method: :preview) # end of stubs for plate_creation baiting page @@ -80,16 +72,8 @@ }, body: '{}' ) - stub_api_post( - 'bait_library_layouts', - body: json(:bait_library_layout), - payload: { - bait_library_layout: { - plate: 'child-uuid', - user: user_uuid - } - } - ) + + expect_api_v2_posts('BaitLibraryLayout', [{ plate_uuid: 'child-uuid', user_uuid: user_uuid }]) # end of stubs for creating a new plate with baits # Stub the requests for the next plate page diff --git a/spec/features/pooling_multiple_plates_into_one_tube_spec.rb b/spec/features/pooling_multiple_plates_into_one_tube_spec.rb index 73937a3d7..1e2487b1a 100644 --- a/spec/features/pooling_multiple_plates_into_one_tube_spec.rb +++ b/spec/features/pooling_multiple_plates_into_one_tube_spec.rb @@ -57,32 +57,13 @@ end let(:example_plate_3_listed) { associated(*example_plate3_args) } - let(:child_tube_uuid) { 'tube-0' } - let(:child_tube) do - create :v2_tube, purpose_uuid: 'child-purpose-0', purpose_name: 'Pool tube', uuid: child_tube_uuid - end + let(:child_tube) { create :v2_tube, purpose_uuid: 'child-purpose-0', purpose_name: 'Pool tube' } - let(:tube_creation_request_uuid) { SecureRandom.uuid } - - let!(:tube_creation_request) do - # TODO: In reality we want to link in all four parents. - stub_api_post( - 'specific_tube_creations', - payload: { - specific_tube_creation: { - user: user_uuid, - parent: plate_uuid, - child_purposes: ['child-purpose-0'], - tube_attributes: [{ name: 'DN2+' }] - } - }, - body: json(:specific_tube_creation, uuid: tube_creation_request_uuid, children_count: 1) - ) - end + let(:specific_tube_creation) do + response = double + allow(response).to receive(:children).and_return([child_tube]) - # Find out what tubes we've just made! - let!(:tube_creation_children_request) do - stub_api_get(tube_creation_request_uuid, 'children', body: json(:tube_collection, names: ['DN1+'])) + response end # Used to fetch the pools. This is the kind of thing we could pass through from a custom form @@ -119,8 +100,6 @@ stub_v2_plate(example_plate_new_api) - stub_v2_plate(example_plate_new_api) - stub_api_get(plate_uuid, body: example_plate) stub_api_get(plate_uuid, 'wells', body: well_set_a) @@ -132,13 +111,25 @@ scenario 'creates multiple plates' do stub_v2_plate(example_plate_2) + expect_api_v2_posts( + 'SpecificTubeCreation', + [ + { + child_purpose_uuids: ['child-purpose-0'], + parent_uuids: [plate_uuid], + tube_attributes: [{ name: 'DN2+' }], + user_uuid: user_uuid + } + ], + [specific_tube_creation] + ) expect_api_v2_posts( 'Transfer', [plate_uuid, plate_uuid_2].map do |source_uuid| { user_uuid: user_uuid, source_uuid: source_uuid, - destination_uuid: 'tube-0', + destination_uuid: child_tube.uuid, transfer_template_uuid: 'whole-plate-to-tube' } end @@ -156,15 +147,11 @@ click_on('Make Pool') expect(page).to have_text('New empty labware added to the system') expect(page).to have_text('Pool tube') - - # This isn't strictly speaking correct to test. But there isn't a great way - # of confirming that the right information got passed to the back end otherwise. - # (Although you expect it to fail on an incorrect request) - expect(tube_creation_request).to have_been_made end scenario 'detects tag clash' do stub_v2_plate(example_plate_3) + fill_in_swipecard_and_barcode(user_swipecard, plate_barcode_1) plate_title = find('#plate-title') expect(plate_title).to have_text('example-purpose') diff --git a/spec/features/pooling_multiple_plates_spec.rb b/spec/features/pooling_multiple_plates_spec.rb index b7a49849f..e86f00458 100644 --- a/spec/features/pooling_multiple_plates_spec.rb +++ b/spec/features/pooling_multiple_plates_spec.rb @@ -34,29 +34,6 @@ purpose_uuid: 'stock-plate-purpose-uuid' end - let(:child_plate_uuid) { SecureRandom.uuid } - let(:child_plate) do - create :v2_plate, - purpose_uuid: 'child-purpose-0', - purpose_name: 'Pool Plate', - uuid: child_plate_uuid, - barcode_number: 3 - end - - let!(:pooled_plate_creation_request) do - stub_api_post( - 'pooled_plate_creations', - payload: { - pooled_plate_creation: { - user: user_uuid, - child_purpose: 'child-purpose-0', - parents: [plate_uuid, plate_uuid_2] - } - }, - body: json(:plate_creation, child_uuid: child_plate_uuid) - ) - end - let!(:bulk_transfer_request) do stub_api_post( 'bulk_transfers', @@ -67,25 +44,44 @@ { 'source_uuid' => plate_uuid, 'source_location' => 'A1', - 'destination_uuid' => child_plate_uuid, + 'destination_uuid' => child_plate.uuid, 'destination_location' => 'A1' }, { 'source_uuid' => plate_uuid_2, 'source_location' => 'A1', - 'destination_uuid' => child_plate_uuid, + 'destination_uuid' => child_plate.uuid, 'destination_location' => 'B1' }, { 'source_uuid' => plate_uuid_2, 'source_location' => 'B1', - 'destination_uuid' => child_plate_uuid, + 'destination_uuid' => child_plate.uuid, 'destination_location' => 'B1' } ] } }, - body: json(:plate_creation, child_uuid: child_plate_uuid) + body: json(:plate_creation, child_uuid: child_plate.uuid) + ) + end + + let(:child_plate) { create :v2_plate, purpose_name: 'Pool Plate', barcode_number: 3 } + + let(:pooled_plate_creation) do + response = double + allow(response).to receive(:child).and_return(child_plate) + + response + end + + def expect_pooled_plate_creation + expect_api_v2_posts( + 'PooledPlateCreation', + [ + { child_purpose_uuid: child_plate.purpose.uuid, parent_uuids: [plate_uuid, plate_uuid_2], user_uuid: user_uuid } + ], + [pooled_plate_creation] ) end @@ -94,7 +90,7 @@ create :purpose_config, creator_class: 'LabwareCreators::MultiPlatePool', name: 'Pool Plate', - uuid: 'child-purpose-0' + uuid: child_plate.purpose.uuid create :pipeline, relationships: { 'Pooled example' => 'Pool Plate' } # We look up the user @@ -108,6 +104,8 @@ end scenario 'creates multiple plates' do + expect_pooled_plate_creation + fill_in_swipecard_and_barcode(user_swipecard, plate_barcode_1) plate_title = find('#plate-title') expect(plate_title).to have_text('Pooled example') @@ -119,7 +117,6 @@ expect(page).to have_content('DN2: A1, B1') click_on('Make Pre-Cap pool Plate') expect(page).to have_text('New empty labware added to the system') - expect(pooled_plate_creation_request).to have_been_made expect(bulk_transfer_request).to have_been_made expect(page).to have_text('Pool Plate') end diff --git a/spec/helpers/page_helper_spec.rb b/spec/helpers/page_helper_spec.rb index 9bc98a951..5c8f2d44e 100644 --- a/spec/helpers/page_helper_spec.rb +++ b/spec/helpers/page_helper_spec.rb @@ -22,4 +22,18 @@ expect(count_badge(0, 'test')).to eq('0') end end + + describe '::state_badge' do + it 'returns a badge with the given state and default title' do + expect(state_badge('pending')).to eq( + 'Pending' + ) + end + + it 'returns a badge with the given state and title' do + expect(state_badge('passed', title: 'Submission State')).to eq( + 'Passed' + ) + end + end end diff --git a/spec/models/labware_creators/baited_plate_spec.rb b/spec/models/labware_creators/baited_plate_spec.rb index 48c035bf4..df6761193 100644 --- a/spec/models/labware_creators/baited_plate_spec.rb +++ b/spec/models/labware_creators/baited_plate_spec.rb @@ -23,6 +23,8 @@ let(:form_attributes) { { user_uuid:, purpose_uuid:, parent_uuid: } } + let(:bait_library_layout) { create :bait_library_layout } + let(:transfer_requests) do WellHelpers.column_order(96)[0, 6].map do |well_name| { 'source_asset' => "2-well-#{well_name}", 'target_asset' => "3-well-#{well_name}", 'submission_id' => '2' } @@ -36,32 +38,6 @@ context 'create plate' do has_a_working_api - let!(:bait_library_layout_preview_request) do - stub_api_post( - 'bait_library_layouts/preview', - payload: { - bait_library_layout: { - plate: parent_uuid, - user: user_uuid - } - }, - body: json(:bait_library_layout) - ) - end - - let!(:bait_library_layout_request) do - stub_api_post( - 'bait_library_layouts', - payload: { - bait_library_layout: { - plate: 'child-uuid', - user: user_uuid - } - }, - body: json(:bait_library_layout) - ) - end - let!(:plate_creation_request) do stub_api_post( 'plate_creations', @@ -79,6 +55,9 @@ before do stub_v2_plate(parent, stub_search: false) stub_v2_plate(child, stub_search: false) + + stub_api_v2_post('BaitLibraryLayout') + stub_api_v2_post('BaitLibraryLayout', [bait_library_layout], method: :preview) end let!(:transfer_creation_request) do diff --git a/spec/models/labware_creators/custom_pooled_tubes_spec.rb b/spec/models/labware_creators/custom_pooled_tubes_spec.rb index 2f6b027f9..b8fff5277 100644 --- a/spec/models/labware_creators/custom_pooled_tubes_spec.rb +++ b/spec/models/labware_creators/custom_pooled_tubes_spec.rb @@ -71,29 +71,28 @@ ) end - let(:tube_creation_request_uuid) { SecureRandom.uuid } - - let(:tube_creation_request) do - stub_api_post( - 'specific_tube_creations', - payload: { - specific_tube_creation: { - user: user_uuid, - parent: parent_uuid, - child_purposes: [purpose_uuid, purpose_uuid], - tube_attributes: [{ name: 'DN5 A1:B2' }, { name: 'DN5 C1:G2' }] + def expect_specific_tube_creation + child_tubes = [ + create(:v2_tube, name: 'DN5 A1:B2', uuid: 'tube-0'), + create(:v2_tube, name: 'DN5 C1:G2', uuid: 'tube-1') + ] + + # Create a mock for the specific tube creation. + specific_tube_creation = double + allow(specific_tube_creation).to receive(:children).and_return(child_tubes) + + # Expect the post request and return the mock. + expect_api_v2_posts( + 'SpecificTubeCreation', + [ + { + child_purpose_uuids: [purpose_uuid, purpose_uuid], + parent_uuids: [parent_uuid], + tube_attributes: child_tubes.map { |tube| { name: tube.name } }, + user_uuid: user_uuid } - }, - body: json(:specific_tube_creation, uuid: tube_creation_request_uuid, children_count: 2) - ) - end - - # Find out what tubes we've just made! - let(:tube_creation_children_request) do - stub_api_get( - tube_creation_request_uuid, - 'children', - body: json(:tube_collection, names: ['DN5 A1:B2', 'DN5 C1:G2']) + ], + [specific_tube_creation] ) end @@ -136,8 +135,6 @@ before do stub_parent_request stub_qc_file_creation - tube_creation_children_request - tube_creation_request transfer_creation_request end @@ -147,9 +144,10 @@ end it 'pools according to the file' do + expect_specific_tube_creation + expect(subject.save).to be_truthy expect(stub_qc_file_creation).to have_been_made.once - expect(tube_creation_request).to have_been_made.once expect(transfer_creation_request).to have_been_made.once end end diff --git a/spec/models/labware_creators/custom_tagged_plate_spec.rb b/spec/models/labware_creators/custom_tagged_plate_spec.rb index 1ae27edd2..5a1ecd5a9 100644 --- a/spec/models/labware_creators/custom_tagged_plate_spec.rb +++ b/spec/models/labware_creators/custom_tagged_plate_spec.rb @@ -95,49 +95,69 @@ context 'On create' do let(:tag_plate_uuid) { 'tag-plate' } let(:tag_template_uuid) { 'tag-layout-template' } - let(:child_plate_uuid) { SecureRandom.uuid } let(:parents) { [plate_uuid, tag_plate_uuid] } - let!(:plate_creation_request) do - stub_api_post( - 'pooled_plate_creations', - payload: { - pooled_plate_creation: { - parents: parents, - child_purpose: child_purpose_uuid, - user: user_uuid - } - }, - body: json(:plate_creation, child_uuid: child_plate_uuid) + let(:expected_transfers) { WellHelpers.stamp_hash(96) } + + let(:child_plate) { create :v2_plate } + + let(:pooled_plate_creation) do + response = double + allow(response).to receive(:child).and_return(child_plate) + + response + end + + def expect_pooled_plate_creation + expect_api_v2_posts( + 'PooledPlateCreation', + [{ child_purpose_uuid: child_purpose_uuid, parent_uuids: parents, user_uuid: user_uuid }], + [pooled_plate_creation] ) end - let(:expected_transfers) { WellHelpers.stamp_hash(96) } + def expect_state_change_creation + expect_api_v2_posts( + 'StateChange', + [ + { + reason: 'Used in Library creation', + target_uuid: tag_plate_uuid, + target_state: 'exhausted', + user_uuid: user_uuid + } + ] + ) + end - def expect_transfer_creation + def expect_tag_layout_creation expect_api_v2_posts( - 'Transfer', + 'TagLayout', [ { user_uuid: user_uuid, - source_uuid: plate_uuid, - destination_uuid: child_plate_uuid, - transfer_template_uuid: transfer_template_uuid, - transfers: expected_transfers + plate_uuid: child_plate.uuid, + tag_group_uuid: 'tag-group-uuid', + tag2_group_uuid: 'tag2-group-uuid', + direction: 'column', + walking_by: 'manual by plate', + initial_tag: '1', + tags_per_well: 1 } ] ) end - def expect_state_change_creation + def expect_transfer_creation expect_api_v2_posts( - 'StateChange', + 'Transfer', [ { - reason: 'Used in Library creation', - target_uuid: tag_plate_uuid, - target_state: 'exhausted', - user_uuid: user_uuid + user_uuid: user_uuid, + source_uuid: plate_uuid, + destination_uuid: child_plate.uuid, + transfer_template_uuid: transfer_template_uuid, + transfers: expected_transfers } ] ) @@ -157,9 +177,9 @@ def expect_state_change_creation state: tag_plate_state }, tag_layout: { - user: 'user-uuid', - tag_group: 'tag-group-uuid', - tag2_group: 'tag2-group-uuid', + user_uuid: 'user-uuid', + tag_group_uuid: 'tag-group-uuid', + tag2_group_uuid: 'tag2-group-uuid', direction: 'column', walking_by: 'manual by plate', initial_tag: '1', @@ -177,54 +197,38 @@ def expect_state_change_creation it_behaves_like 'it has a custom page', 'custom_tagged_plate' context 'on save' do - let!(:custom_tag_layout_creation_request) do - stub_api_post( - 'tag_layouts', - payload: { - tag_layout: { - user: 'user-uuid', - plate: child_plate_uuid, - tag_group: 'tag-group-uuid', - tag2_group: 'tag2-group-uuid', - direction: 'column', - walking_by: 'manual by plate', - initial_tag: '1', - tags_per_well: 1 - } - } - ) - end - context 'with an available tag plate' do let(:tag_plate_state) { 'available' } it 'creates a tag plate' do - expect_transfer_creation + expect_pooled_plate_creation expect_state_change_creation + expect_tag_layout_creation + expect_transfer_creation expect(subject.save).to be true - expect(plate_creation_request).to have_been_made.once - expect(custom_tag_layout_creation_request).to have_been_made.once end it 'has the correct child (and uuid)' do + stub_api_v2_post('PooledPlateCreation', pooled_plate_creation) + stub_api_v2_post('TagLayout') stub_api_v2_post('Transfer') stub_api_v2_post('StateChange') expect(subject.save).to be true # This will be our new plate - expect(subject.child.uuid).to eq(child_plate_uuid) + expect(subject.child.uuid).to eq(child_plate.uuid) end context 'when a user has exhausted the plate in another tab' do it 'creates a tag plate' do - expect_transfer_creation + expect_pooled_plate_creation expect_state_change_creation + expect_tag_layout_creation + expect_transfer_creation expect(subject.save).to be true - expect(plate_creation_request).to have_been_made.once - expect(custom_tag_layout_creation_request).to have_been_made.once end end end @@ -233,23 +237,25 @@ def expect_state_change_creation let(:tag_plate_state) { 'exhausted' } it 'creates a tagged plate' do + # This one will be VERY different + expect_tag_layout_creation + + expect_pooled_plate_creation expect_transfer_creation expect(Sequencescape::Api::V2::StateChange).not_to receive(:create!) expect(subject.save).to be true - expect(plate_creation_request).to have_been_made.once - - # This one will be VERY different - expect(custom_tag_layout_creation_request).to have_been_made.once end it 'has the correct child (and uuid)' do + stub_api_v2_post('PooledPlateCreation', pooled_plate_creation) + stub_api_v2_post('TagLayout') stub_api_v2_post('Transfer') expect(subject.save).to be true # This will be our new plate - expect(subject.child.uuid).to eq(child_plate_uuid) + expect(subject.child.uuid).to eq(child_plate.uuid) end end @@ -259,19 +265,21 @@ def expect_state_change_creation let(:parents) { [plate_uuid] } it 'creates a tag plate' do + expect_pooled_plate_creation + expect_tag_layout_creation expect_transfer_creation expect(Sequencescape::Api::V2::StateChange).not_to receive(:create!) expect(subject.save).to be true - expect(plate_creation_request).to have_been_made.once - expect(custom_tag_layout_creation_request).to have_been_made.once end it 'has the correct child (and uuid)' do + stub_api_v2_post('PooledPlateCreation', pooled_plate_creation) + stub_api_v2_post('TagLayout') stub_api_v2_post('Transfer') expect(subject.save).to be true - expect(subject.child.uuid).to eq(child_plate_uuid) + expect(subject.child.uuid).to eq(child_plate.uuid) end end end diff --git a/spec/models/labware_creators/merged_plate_spec.rb b/spec/models/labware_creators/merged_plate_spec.rb index 62bfa79ae..f67c4e403 100644 --- a/spec/models/labware_creators/merged_plate_spec.rb +++ b/spec/models/labware_creators/merged_plate_spec.rb @@ -54,15 +54,6 @@ creator_class: 'LabwareCreators::MergedPlate' end - let(:child_plate) do - create :v2_plate, - uuid: 'child-uuid', - barcode_number: '4', - size: plate_size, - outer_requests: requests, - purpose: child_purpose - end - let(:requests) do Array.new(plate_size) { |i| create :library_request, state: 'started', uuid: "request-#{i}", submission_id: 1 } end @@ -70,7 +61,6 @@ let(:user_uuid) { 'user-uuid' } before do - stub_v2_plate(child_plate, stub_search: false) stub_v2_plate(source_plate_1, stub_search: false) stub_v2_plate(source_plate_2, stub_search: false) end @@ -86,20 +76,8 @@ includes: plate_includes ).and_return([source_plate_1, source_plate_2]) ) - end - let!(:plate_creation_request) do - stub_api_post( - 'pooled_plate_creations', - payload: { - pooled_plate_creation: { - user: user_uuid, - child_purpose: child_purpose_uuid, - parents: [source_plate_1.uuid, source_plate_2.uuid] - } - }, - body: json(:plate_creation, child_uuid: child_plate.uuid) - ) + stub_v2_plate(child_plate, stub_search: false) end let!(:transfer_creation_request) do @@ -115,10 +93,42 @@ ) end + let(:child_plate) do + create :v2_plate, + uuid: 'child-uuid', + barcode_number: '4', + size: plate_size, + outer_requests: requests, + purpose: child_purpose + end + + let(:pooled_plate_creation) do + response = double + allow(response).to receive(:child).and_return(child_plate) + + response + end + + def expect_pooled_plate_creation + expect_api_v2_posts( + 'PooledPlateCreation', + [ + { + child_purpose_uuid: child_purpose_uuid, + parent_uuids: [source_plate_1.uuid, source_plate_2.uuid], + user_uuid: user_uuid + } + ], + [pooled_plate_creation] + ) + end + it 'makes the expected requests' do + expect_pooled_plate_creation + expect(subject).to be_valid expect(subject.save!).to eq true - expect(plate_creation_request).to have_been_made + expect(transfer_creation_request).to have_been_made end end diff --git a/spec/models/labware_creators/multi_plate_pool_spec.rb b/spec/models/labware_creators/multi_plate_pool_spec.rb index f08bc3943..0f9c6ac2e 100644 --- a/spec/models/labware_creators/multi_plate_pool_spec.rb +++ b/spec/models/labware_creators/multi_plate_pool_spec.rb @@ -77,18 +77,13 @@ } end - let!(:pooled_plate_creation_request) do - stub_api_post( - 'pooled_plate_creations', - payload: { - pooled_plate_creation: { - user: user_uuid, - child_purpose: child_purpose_uuid, - parents: [plate_uuid, plate_b_uuid] - } - }, - body: json(:plate_creation, child_uuid: child_plate_uuid) - ) + let(:child_plate) { create :v2_plate } + + let(:pooled_plate_creation) do + response = double + allow(response).to receive(:child).and_return(child_plate) + + response end let!(:bulk_transfer_request) do @@ -101,38 +96,48 @@ { 'source_uuid' => plate_uuid, 'source_location' => 'A1', - 'destination_uuid' => child_plate_uuid, + 'destination_uuid' => child_plate.uuid, 'destination_location' => 'A1' }, { 'source_uuid' => plate_uuid, 'source_location' => 'B1', - 'destination_uuid' => child_plate_uuid, + 'destination_uuid' => child_plate.uuid, 'destination_location' => 'A1' }, { 'source_uuid' => plate_b_uuid, 'source_location' => 'A1', - 'destination_uuid' => child_plate_uuid, + 'destination_uuid' => child_plate.uuid, 'destination_location' => 'B1' }, { 'source_uuid' => plate_b_uuid, 'source_location' => 'B1', - 'destination_uuid' => child_plate_uuid, + 'destination_uuid' => child_plate.uuid, 'destination_location' => 'B1' } ] } }, - body: json(:plate_creation, child_uuid: child_plate_uuid) + body: json(:plate_creation, child_uuid: child_plate.uuid) + ) + end + + def expect_pooled_plate_creation + expect_api_v2_posts( + 'PooledPlateCreation', + [{ child_purpose_uuid: child_purpose_uuid, parent_uuids: [plate_uuid, plate_b_uuid], user_uuid: user_uuid }], + [pooled_plate_creation] ) end context '#save!' do it 'creates a plate!' do + expect_pooled_plate_creation + subject.save! - expect(pooled_plate_creation_request).to have_been_made.once + expect(bulk_transfer_request).to have_been_made.once end end diff --git a/spec/models/labware_creators/multi_stamp_spec.rb b/spec/models/labware_creators/multi_stamp_spec.rb index 5f8591cc6..e1da3803d 100644 --- a/spec/models/labware_creators/multi_stamp_spec.rb +++ b/spec/models/labware_creators/multi_stamp_spec.rb @@ -11,7 +11,6 @@ let(:parent1_uuid) { 'parent1-plate-uuid' } let(:parent2_uuid) { 'parent2-plate-uuid' } - let(:child_uuid) { 'child-uuid' } let(:requests_parent1) { Array.new(24) { |i| create :library_request, state: 'started', uuid: "request-p1-#{i}" } } let(:requests_parent2) { Array.new(24) { |i| create :library_request, state: 'started', uuid: "request-p2-#{i}" } } @@ -41,21 +40,19 @@ stock_plate: stock_plate2 ) end - let(:child_plate_v2) { create :v2_plate, uuid: child_uuid, barcode_number: '5', size: 96 } - let(:child_plate_v1) { json :stock_plate_with_metadata, stock_plate: { barcode: '5', uuid: child_uuid } } + let(:child_plate) { create :v2_plate, barcode_number: '5', size: 96 } let(:child_purpose_uuid) { 'child-purpose' } let(:child_purpose_name) { 'Child Purpose' } let(:user_uuid) { 'user-uuid' } - let(:user) { json :v1_user, uuid: user_uuid } before do create :purpose_config, name: child_purpose_name, uuid: child_purpose_uuid stub_v2_plate(parent1, stub_search: false) stub_v2_plate(parent2, stub_search: false) - stub_v2_plate(child_plate_v2, stub_search: false, custom_query: [:plate_with_wells, child_plate_v2.uuid]) + stub_v2_plate(child_plate, stub_search: false, custom_query: [:plate_with_wells, child_plate.uuid]) end context 'on new' do @@ -484,7 +481,7 @@ parents: [parent1_uuid, parent2_uuid] } }, - body: json(:plate_creation, child_uuid:) + body: json(:plate_creation, child_uuid: child_plate.uuid) ) end @@ -554,19 +551,33 @@ ) end + let(:pooled_plate_creation) do + response = double + allow(response).to receive(:child).and_return(child_plate) + + response + end + + def expect_pooled_plate_creation + expect_api_v2_posts( + 'PooledPlateCreation', + [{ child_purpose_uuid: child_purpose_uuid, parent_uuids: [parent1_uuid, parent2_uuid], user_uuid: user_uuid }], + [pooled_plate_creation] + ) + end + context '#save!' do setup do - stub_api_get(child_plate_v2.uuid, body: child_plate_v1) - - stub_api_get('user-uuid', body: user) - stub_api_get('asset-uuid', body: child_plate_v1) + # stub_api_get('user-uuid', body: user) end it 'creates a plate!' do + expect_pooled_plate_creation + subject.save! - expect(pooled_plate_creation_request).to have_been_made.once + expect(transfer_creation_request).to have_been_made.once - expect(subject.child.uuid).to eq(child_uuid) + expect(subject.child.uuid).to eq(child_plate.uuid) expect(subject).to be_valid expect(subject.errors.messages).to be_empty end diff --git a/spec/models/labware_creators/multi_stamp_tubes_spec.rb b/spec/models/labware_creators/multi_stamp_tubes_spec.rb index 2e3c3e840..5d0df5a90 100644 --- a/spec/models/labware_creators/multi_stamp_tubes_spec.rb +++ b/spec/models/labware_creators/multi_stamp_tubes_spec.rb @@ -32,13 +32,8 @@ receptacle: parent2_receptacle end - let(:child_uuid) { 'child-uuid' } let(:child_purpose_uuid) { 'child-purpose' } let(:child_purpose_name) { 'Child Purpose' } - let(:child_plate_v2) do - create :v2_plate_for_submission, uuid: child_uuid, purpose_name: child_purpose_name, barcode_number: '5', size: 96 - end - let(:child_plate_v1) { json :stock_plate_with_metadata, stock_plate: { barcode: '5', uuid: child_uuid } } let(:user_uuid) { 'user-uuid' } let(:user) { json :v1_user, uuid: user_uuid } @@ -51,7 +46,6 @@ Settings.submission_templates = { 'example' => example_template_uuid } stub_v2_tube(parent1, stub_search: false) stub_v2_tube(parent2, stub_search: false) - stub_v2_plate(child_plate_v2, stub_search: false, custom_includes: 'wells,wells.aliquots,wells.aliquots.study') end context 'on new' do @@ -86,20 +80,6 @@ } end - let!(:ms_plate_creation_request) do - stub_api_post( - 'pooled_plate_creations', - payload: { - pooled_plate_creation: { - user: user_uuid, - child_purpose: child_purpose_uuid, - parents: [parent1_tube_uuid, parent2_tube_uuid] - } - }, - body: json(:plate_creation, child_uuid:) - ) - end - let(:transfer_requests) do [ { source_asset: 'tube1', target_asset: '5-well-A1', outer_request: 'outer-request-1' }, @@ -119,6 +99,32 @@ body: '{}' ) end + + let(:child_plate) do + create :v2_plate_for_submission, purpose_name: child_purpose_name, barcode_number: '5', size: 96 + end + + let(:pooled_plate_creation) do + response = double + allow(response).to receive(:child).and_return(child_plate) + + response + end + + def expect_pooled_plate_creation + expect_api_v2_posts( + 'PooledPlateCreation', + [ + { + child_purpose_uuid: child_purpose_uuid, + parent_uuids: [parent1_tube_uuid, parent2_tube_uuid], + user_uuid: user_uuid + } + ], + [pooled_plate_creation] + ) + end + context 'when the submission is created' do describe 'internal methods' do it 'determines the configuration for the submission' do @@ -185,31 +191,7 @@ context '#save!' do setup do - stub_api_get(child_plate_v2.uuid, body: child_plate_v1) - stub_api_get( - 'custom_metadatum_collection-uuid', - body: json(:v1_custom_metadatum_collection, uuid: 'custom_metadatum_collection-uuid') - ) - stub_api_get('user-uuid', body: user) - stub_api_get('asset-uuid', body: child_plate_v1) - expect(subject).to receive(:parent_tubes).and_return([parent1, parent2]) - - metadata = attributes_for(:v1_custom_metadatum_collection).fetch(:metadata, {}) - - stub_api_put( - 'custom_metadatum_collection-uuid', - payload: { - custom_metadatum_collection: { - metadata: - } - }, - body: json(:v1_custom_metadatum_collection) - ) - - expect('Sequencescape::Api::V2'.constantize).to receive(:plate_with_wells).with(child_uuid).and_return( - child_plate_v2 - ) expect(subject).to receive(:source_tube_outer_request_uuid).with(parent1).and_return('outer-request-1') expect(subject).to receive(:source_tube_outer_request_uuid).with(parent2).and_return('outer-request-2') end @@ -242,19 +224,6 @@ aliquots: [aliquot2] end - let(:child_aliquot1) { create :v2_aliquot, study_id: 1 } - let(:child_aliquot2) { create :v2_aliquot, study_id: 2 } - let(:child_well1) { create :v2_stock_well, location: 'A1', uuid: '5-well-A1', aliquots: [child_aliquot1] } - let(:child_well2) { create :v2_stock_well, location: 'B1', uuid: '5-well-B1', aliquots: [child_aliquot2] } - let(:child_plate_v2) do - create :v2_plate_for_submission, - uuid: child_uuid, - purpose_name: child_purpose_name, - barcode_number: '5', - size: 96, - wells: [child_well1, child_well2] - end - let!(:order_request) do stub_api_get(example_template_uuid, body: json(:submission_template, uuid: example_template_uuid)) stub_api_post( @@ -291,8 +260,10 @@ end it 'creates a plate!' do + expect_pooled_plate_creation + subject.save! - expect(ms_plate_creation_request).to have_been_made.once + expect(transfer_creation_request).to have_been_made.once expect(order_request).to have_been_made.once expect(submission_request).to have_been_made.once diff --git a/spec/models/labware_creators/multi_stamp_tubes_using_tube_rack_scan_spec.rb b/spec/models/labware_creators/multi_stamp_tubes_using_tube_rack_scan_spec.rb index 6bd6e2752..6c4d584fe 100644 --- a/spec/models/labware_creators/multi_stamp_tubes_using_tube_rack_scan_spec.rb +++ b/spec/models/labware_creators/multi_stamp_tubes_using_tube_rack_scan_spec.rb @@ -84,30 +84,10 @@ [:purpose, 'receptacle.aliquots.request.request_type', 'receptacle.requests_as_source.request_type'] end - # child aliquots - let(:child_aliquot1) { create :v2_aliquot } - let(:child_aliquot2) { create :v2_aliquot } - - # child wells - let(:child_well1) { create :v2_well, location: 'A1', uuid: '5-well-A1', aliquots: [child_aliquot1] } - let(:child_well2) { create :v2_well, location: 'B1', uuid: '5-well-B1', aliquots: [child_aliquot2] } - # child plate - let(:child_plate_uuid) { 'child-uuid' } let(:child_plate_purpose_uuid) { 'child-purpose' } let(:child_plate_purpose_name) { 'Child Purpose' } - let(:child_plate_v2) do - create :v2_plate, - uuid: child_plate_uuid, - purpose_name: child_plate_purpose_name, - barcode_number: '5', - size: 96, - wells: [child_well1, child_well2] - end - - let(:child_plate_v2) do - create :v2_plate, uuid: child_plate_uuid, purpose_name: child_plate_purpose_name, barcode_number: '5', size: 96 - end + let(:child_plate) { create :v2_plate, purpose_name: child_plate_purpose_name, barcode_number: '5', size: 96 } let(:user_uuid) { 'user-uuid' } let(:user) { json :v1_user, uuid: user_uuid } @@ -132,7 +112,7 @@ end let(:stub_upload_file_creation) do - stub_request(:post, api_url_for(child_plate_uuid, 'qc_files')).with( + stub_request(:post, api_url_for(child_plate.uuid, 'qc_files')).with( body: file_content, headers: { 'Content-Type' => 'sequencescape/qc_file', @@ -149,7 +129,7 @@ let(:child_plate_v1) do # qc_files are created through the API V1. The actions attribute for qcfiles is required by the API V1. - json :plate, uuid: child_plate_uuid, purpose_uuid: child_plate_purpose_uuid, qc_files_actions: %w[read create] + json :plate, uuid: child_plate.uuid, purpose_uuid: child_plate_purpose_uuid, qc_files_actions: %w[read create] end before do @@ -162,9 +142,7 @@ includes: tube_includes ).and_return(parent_tube_2) - stub_v2_plate(child_plate_v2, stub_search: false, custom_query: [:plate_with_wells, child_plate_v2.uuid]) - - stub_api_get(child_plate_uuid, body: child_plate_v1) + stub_api_get(child_plate.uuid, body: child_plate_v1) stub_upload_file_creation @@ -194,20 +172,6 @@ { user_uuid: user_uuid, purpose_uuid: child_plate_purpose_uuid, parent_uuid: parent_tube_1_uuid, file: file } end - let!(:ms_plate_creation_request) do - stub_api_post( - 'pooled_plate_creations', - payload: { - pooled_plate_creation: { - user: user_uuid, - child_purpose: child_plate_purpose_uuid, - parents: [parent_tube_1_uuid, parent_tube_2_uuid] - } - }, - body: json(:plate_creation, child_plate_uuid:) - ) - end - let(:transfer_requests) do [ { source_asset: 'tube-1-uuid', target_asset: '5-well-A1', outer_request: 'request-1' }, @@ -228,6 +192,27 @@ ) end + let(:pooled_plate_creation) do + response = double + allow(response).to receive(:child).and_return(child_plate) + + response + end + + def expect_pooled_plate_creation + expect_api_v2_posts( + 'PooledPlateCreation', + [ + { + child_purpose_uuid: child_plate_purpose_uuid, + parent_uuids: [parent_tube_1_uuid, parent_tube_2_uuid], + user_uuid: user_uuid + } + ], + [pooled_plate_creation] + ) + end + subject { LabwareCreators::MultiStampTubesUsingTubeRackScan.new(api, form_attributes) } it 'creates a plate!' do @@ -235,10 +220,11 @@ subject.labware.barcode.machine = 'AB10000001' subject.labware.barcode.ean13 = nil + expect_pooled_plate_creation + subject.save - expect(subject.errors.full_messages).to be_empty - expect(ms_plate_creation_request).to have_been_made.once + expect(subject.errors.full_messages).to be_empty expect(transfer_creation_request).to have_been_made.once end end diff --git a/spec/models/labware_creators/plate_split_to_tube_racks_spec.rb b/spec/models/labware_creators/plate_split_to_tube_racks_spec.rb index 4f7f83f9a..0e88bd6af 100644 --- a/spec/models/labware_creators/plate_split_to_tube_racks_spec.rb +++ b/spec/models/labware_creators/plate_split_to_tube_racks_spec.rb @@ -116,89 +116,68 @@ { user_uuid: user_uuid, purpose_uuid: child_sequencing_tube_purpose_uuid, parent_uuid: parent_uuid } end - # child tubes for lookup after creation - let(:child_tube_1_uuid) { SecureRandom.uuid } - let(:child_tube_1_aliquot) { create(:v2_aliquot, sample: sample1) } - let(:child_tube_1_v2) do - create( - :v2_stock_tube, - state: 'passed', - purpose_name: child_sequencing_tube_purpose_name, - aliquots: [child_tube_1_aliquot], - barcode_prefix: 'FX', - barcode_number: 1, - uuid: child_tube_1_uuid + let(:sequencing_file) do + fixture_file_upload( + 'spec/fixtures/files/scrna_core/scrna_core_sequencing_tube_rack_scan.csv', + 'sequencescape/qc_file' ) end - let(:child_tube_2_uuid) { SecureRandom.uuid } - let(:child_tube_2_aliquot) { create(:v2_aliquot, sample: sample2) } - let(:child_tube_2_v2) do - create( - :v2_stock_tube, - state: 'passed', - purpose_name: child_sequencing_tube_purpose_name, - aliquots: [child_tube_2_aliquot], - barcode_prefix: 'FX', - barcode_number: 2, - uuid: child_tube_2_uuid + let(:contingency_file) do + fixture_file_upload( + 'spec/fixtures/files/scrna_core/scrna_core_contingency_tube_rack_scan.csv', + 'sequencescape/qc_file' ) end - let(:child_tube_3_uuid) { SecureRandom.uuid } - let(:child_tube_3_aliquot) { create(:v2_aliquot, sample: sample1) } - let(:child_tube_3_v2) do - create( - :v2_stock_tube, - state: 'passed', - purpose_name: child_contingency_tube_purpose_name, - aliquots: [child_tube_3_aliquot], - barcode_prefix: 'FX', - barcode_number: 11, - uuid: child_tube_3_uuid - ) - end + def prepare_created_child_tubes(tube_attributes) + # Prepare child tubes and stub their lookups. + child_tubes = + tube_attributes.map { |attrs| create(:v2_tube, name: attrs[:name], foreign_barcode: attrs[:foreign_barcode]) } + child_tubes.each { |child_tube| stub_v2_labware(child_tube) } - let(:child_tube_4_uuid) { SecureRandom.uuid } - let(:child_tube_4_aliquot) { create(:v2_aliquot, sample: sample1) } - let(:child_tube_4_v2) do - create( - :v2_stock_tube, - state: 'passed', - purpose_name: child_contingency_tube_purpose_name, - aliquots: [child_tube_4_aliquot], - barcode_prefix: 'FX', - barcode_number: 12, - uuid: child_tube_4_uuid - ) + child_tubes end - let(:child_tube_5_uuid) { SecureRandom.uuid } - let(:child_tube_5_aliquot) { create(:v2_aliquot, sample: sample2) } - let(:child_tube_5_v2) do - create( - :v2_stock_tube, - state: 'passed', - purpose_name: child_contingency_tube_purpose_name, - aliquots: [child_tube_5_aliquot], - barcode_prefix: 'FX', - barcode_number: 13, - uuid: child_tube_5_uuid - ) - end + def expect_specific_tube_creation(child_purpose_uuid, child_tubes) + # Create a mock for the specific tube creation. + specific_tube_creation = double + allow(specific_tube_creation).to receive(:children).and_return(child_tubes) - let(:sequencing_file) do - fixture_file_upload( - 'spec/fixtures/files/scrna_core/scrna_core_sequencing_tube_rack_scan.csv', - 'sequencescape/qc_file' + # Expect the post request and return the mock. + expect_api_v2_posts( + 'SpecificTubeCreation', + [ + { + child_purpose_uuids: [child_purpose_uuid] * child_tubes.size, + parent_uuids: [parent_uuid], + tube_attributes: child_tubes.map { |tube| { name: tube.name, foreign_barcode: tube.foreign_barcode } }, + user_uuid: user_uuid + } + ], + [specific_tube_creation] ) end - let(:contingency_file) do - fixture_file_upload( - 'spec/fixtures/files/scrna_core/scrna_core_contingency_tube_rack_scan.csv', - 'sequencescape/qc_file' - ) + # tubes_hash should be a hash with tube rack barcodes as keys and arrays of tubes as values. + def expect_custom_metadatum_collection_posts(tubes_hash) + # Prepare the expected call arguments. + expected_call_args = + tubes_hash.flat_map do |tube_rack_barcode, tubes| + tubes.map do |tube| + { + user_id: user.id, + asset_id: tube.id, + metadata: { + tube_rack_barcode: tube_rack_barcode, + tube_rack_position: tube.name.split(':').last + } + } + end + end + + # Expect the post requests. + expect_api_v2_posts('CustomMetadatumCollection', expected_call_args) end before do @@ -221,12 +200,14 @@ stub_v2_tube(ancestor_tube_1_v2, stub_search: false) stub_v2_tube(ancestor_tube_2_v2, stub_search: false) - # child tube lookups - stub_v2_tube(child_tube_1_v2, stub_search: false) - stub_v2_tube(child_tube_2_v2, stub_search: false) - stub_v2_tube(child_tube_3_v2, stub_search: false) - stub_v2_tube(child_tube_4_v2, stub_search: false) - stub_v2_tube(child_tube_5_v2, stub_search: false) + # Block finding tubes by given barcodes. + allow(Sequencescape::Api::V2::Tube).to receive(:find_by).with(barcode: 'FX00000001').and_return(nil) + allow(Sequencescape::Api::V2::Tube).to receive(:find_by).with(barcode: 'FX00000002').and_return(nil) + allow(Sequencescape::Api::V2::Tube).to receive(:find_by).with(barcode: 'FX00000011').and_return(nil) + allow(Sequencescape::Api::V2::Tube).to receive(:find_by).with(barcode: 'FX00000012').and_return(nil) + allow(Sequencescape::Api::V2::Tube).to receive(:find_by).with(barcode: 'FX00000013').and_return(nil) + allow(Sequencescape::Api::V2::Tube).to receive(:find_by).with(barcode: 'FX00000014').and_return(nil) + allow(Sequencescape::Api::V2::Tube).to receive(:find_by).with(barcode: 'FX00000015').and_return(nil) end context 'on new' do @@ -373,13 +354,6 @@ 'wells.aliquots,wells.aliquots.sample,wells.downstream_tubes,' \ 'wells.downstream_tubes.custom_metadatum_collection' ) - allow(Sequencescape::Api::V2::Tube).to receive(:find_by).with(barcode: 'FX00000001').and_return(nil) - allow(Sequencescape::Api::V2::Tube).to receive(:find_by).with(barcode: 'FX00000002').and_return(nil) - allow(Sequencescape::Api::V2::Tube).to receive(:find_by).with(barcode: 'FX00000011').and_return(nil) - allow(Sequencescape::Api::V2::Tube).to receive(:find_by).with(barcode: 'FX00000012').and_return(nil) - allow(Sequencescape::Api::V2::Tube).to receive(:find_by).with(barcode: 'FX00000013').and_return(nil) - allow(Sequencescape::Api::V2::Tube).to receive(:find_by).with(barcode: 'FX00000014').and_return(nil) - allow(Sequencescape::Api::V2::Tube).to receive(:find_by).with(barcode: 'FX00000015').and_return(nil) end context 'when files are not present' do @@ -498,13 +472,6 @@ 'wells.aliquots,wells.aliquots.sample,wells.downstream_tubes,' \ 'wells.downstream_tubes.custom_metadatum_collection' ) - allow(Sequencescape::Api::V2::Tube).to receive(:find_by).with(barcode: 'FX00000001').and_return(nil) - allow(Sequencescape::Api::V2::Tube).to receive(:find_by).with(barcode: 'FX00000002').and_return(nil) - allow(Sequencescape::Api::V2::Tube).to receive(:find_by).with(barcode: 'FX00000011').and_return(nil) - allow(Sequencescape::Api::V2::Tube).to receive(:find_by).with(barcode: 'FX00000012').and_return(nil) - allow(Sequencescape::Api::V2::Tube).to receive(:find_by).with(barcode: 'FX00000013').and_return(nil) - allow(Sequencescape::Api::V2::Tube).to receive(:find_by).with(barcode: 'FX00000014').and_return(nil) - allow(Sequencescape::Api::V2::Tube).to receive(:find_by).with(barcode: 'FX00000015').and_return(nil) end context 'when files are not present' do @@ -635,7 +602,42 @@ end context '#save' do - let!(:stub_sequencing_tube_creation_request_uuid) { SecureRandom.uuid } + # body for stubbing the contingency file upload + let(:contingency_file_content) do + content = contingency_file.read + contingency_file.rewind + content + end + + # stub the contingency file upload + let!(:stub_contingency_file_upload) do + stub_request(:post, api_url_for(parent_uuid, 'qc_files')).with( + body: contingency_file_content, + headers: { + 'Content-Type' => 'sequencescape/qc_file', + 'Content-Disposition' => 'form-data; filename="scrna_core_contingency_tube_rack_scan.csv"' + } + ).to_return( + status: 201, + body: json(:qc_file, filename: 'scrna_core_contingency_tube_rack_scan.csv'), + headers: { + 'content-type' => 'application/json' + } + ) + end + + let(:contingency_tubes) do + prepare_created_child_tubes( + [ + # sample 1 from well A2 to contingency tube 1 in A1 + { name: 'SPR:NT1O:A1', foreign_barcode: 'FX00000011' }, + # sample 2 from well B2 to contingency tube 2 in B1 + { name: 'SPR:NT2P:B1', foreign_barcode: 'FX00000012' }, + # sample 1 from well A3 to contingency tube 3 in C1 + { name: 'SPR:NT1O:C1', foreign_barcode: 'FX00000013' } + ] + ) + end before do stub_v2_plate( @@ -646,13 +648,6 @@ 'wells.downstream_tubes.custom_metadatum_collection' ) stub_api_get(parent_uuid, body: parent_v1) - allow(Sequencescape::Api::V2::Tube).to receive(:find_by).with(barcode: 'FX00000001').and_return(nil) - allow(Sequencescape::Api::V2::Tube).to receive(:find_by).with(barcode: 'FX00000002').and_return(nil) - allow(Sequencescape::Api::V2::Tube).to receive(:find_by).with(barcode: 'FX00000011').and_return(nil) - allow(Sequencescape::Api::V2::Tube).to receive(:find_by).with(barcode: 'FX00000012').and_return(nil) - allow(Sequencescape::Api::V2::Tube).to receive(:find_by).with(barcode: 'FX00000013').and_return(nil) - allow(Sequencescape::Api::V2::Tube).to receive(:find_by).with(barcode: 'FX00000014').and_return(nil) - allow(Sequencescape::Api::V2::Tube).to receive(:find_by).with(barcode: 'FX00000015').and_return(nil) end context 'with both sequencing and contingency files' do @@ -666,51 +661,6 @@ } end - # stub the contingency tube creation - let!(:stub_contingency_tube_creation_request_uuid) { SecureRandom.uuid } - let!(:stub_contingency_tube_creation_request) do - stub_api_post( - 'specific_tube_creations', - payload: { - specific_tube_creation: { - child_purposes: [ - child_contingency_tube_purpose_uuid, - child_contingency_tube_purpose_uuid, - child_contingency_tube_purpose_uuid - ], - tube_attributes: [ - # sample 1 from well A2 to contingency tube 1 in A1 - { name: 'SPR:NT1O:A1', foreign_barcode: 'FX00000011' }, - # sample 2 from well B2 to contingency tube 2 in B1 - { name: 'SPR:NT2P:B1', foreign_barcode: 'FX00000012' }, - # sample 1 from well A3 to contingency tube 3 in C1 - { name: 'SPR:NT1O:C1', foreign_barcode: 'FX00000013' } - ], - user: user_uuid, - parent: parent_uuid - } - }, - body: json(:specific_tube_creation, uuid: stub_contingency_tube_creation_request_uuid, children_count: 3) - ) - end - - # stub what contingency tubes were just made - let!(:stub_contingency_tube_creation_children_request) do - stub_api_get( - stub_contingency_tube_creation_request_uuid, - 'children', - body: - json( - :tube_collection_with_barcodes_specified, - size: 3, - names: %w[SPR:NT1O:A1 SPR:NT2P:B1 SPR:NT1O:C1], - barcode_prefix: 'FX', - barcode_numbers: [11, 12, 13], - uuid_index_offset: 2 - ) - ) - end - # body for stubbing the contingency file upload let(:contingency_file_content) do content = contingency_file.read @@ -742,6 +692,17 @@ content end + let(:sequencing_tubes) do + prepare_created_child_tubes( + [ + # sample 1 in well A1 to seq tube 1 in A1 + { name: 'SEQ:NT1O:A1', foreign_barcode: 'FX00000001' }, + # sample 2 in well B1 to seq tube 2 in B1 + { name: 'SEQ:NT2P:B1', foreign_barcode: 'FX00000002' } + ] + ) + end + # stub the sequencing file upload let!(:stub_sequencing_file_upload) do stub_request(:post, api_url_for(parent_uuid, 'qc_files')).with( @@ -759,87 +720,26 @@ ) end - # stub the sequencing tube creation - let!(:stub_sequencing_tube_creation_request_uuid) { SecureRandom.uuid } - let!(:stub_sequencing_tube_creation_request) do - stub_api_post( - 'specific_tube_creations', - payload: { - specific_tube_creation: { - child_purposes: [child_sequencing_tube_purpose_uuid, child_sequencing_tube_purpose_uuid], - tube_attributes: [ - # sample 1 in well A1 to seq tube 1 in A1 - { name: 'SEQ:NT1O:A1', foreign_barcode: 'FX00000001' }, - # sample 2 in well B1 to seq tube 2 in B1 - { name: 'SEQ:NT2P:B1', foreign_barcode: 'FX00000002' } - ], - user: user_uuid, - parent: parent_uuid - } - }, - body: json(:specific_tube_creation, uuid: stub_sequencing_tube_creation_request_uuid, children_count: 2) - ) - end - - # stub what sequencing tubes were just made - let!(:stub_sequencing_tube_creation_children_request) do - stub_api_get( - stub_sequencing_tube_creation_request_uuid, - 'children', - body: - json( - :tube_collection_with_barcodes_specified, - size: 2, - names: %w[SEQ:NT1O:A1 SEQ:NT2P:B1], - barcode_prefix: 'FX', - barcode_numbers: [1, 2] - ) - ) - end - # stub the transfer creation let!(:stub_transfer_creation_request) do + parent_wells = [parent_well_a1, parent_well_b1, parent_well_a2, parent_well_b2, parent_well_a3] + target_tubes = sequencing_tubes + contingency_tubes + transfer_requests = + parent_wells.map.with_index do |parent_well, index| + { 'submission_id' => '2', 'source_asset' => parent_well.uuid, 'target_asset' => target_tubes[index].uuid } + end stub_api_post( 'transfer_request_collections', payload: { transfer_request_collection: { user: user_uuid, - transfer_requests: [ - { 'submission_id' => '2', 'source_asset' => parent_well_a1.uuid, 'target_asset' => 'tube-0' }, - { 'submission_id' => '2', 'source_asset' => parent_well_b1.uuid, 'target_asset' => 'tube-1' }, - { 'submission_id' => '2', 'source_asset' => parent_well_a2.uuid, 'target_asset' => 'tube-2' }, - { 'submission_id' => '2', 'source_asset' => parent_well_b2.uuid, 'target_asset' => 'tube-3' }, - { 'submission_id' => '2', 'source_asset' => parent_well_a3.uuid, 'target_asset' => 'tube-4' } - ] + transfer_requests: transfer_requests } }, body: '{}' ) end - # need api v1 versions of child tubes - let(:child_tube_1_v1) { json :tube, uuid: child_tube_1_uuid, barcode_prefix: 'FX', barcode_number: 1 } - let(:child_tube_2_v1) { json :tube, uuid: child_tube_2_uuid, barcode_prefix: 'FX', barcode_number: 2 } - let(:child_tube_3_v1) { json :tube, uuid: child_tube_3_uuid, barcode_prefix: 'FX', barcode_number: 11 } - let(:child_tube_4_v1) { json :tube, uuid: child_tube_4_uuid, barcode_prefix: 'FX', barcode_number: 12 } - let(:child_tube_5_v1) { json :tube, uuid: child_tube_5_uuid, barcode_prefix: 'FX', barcode_number: 13 } - - # Metadata expected to be sent in POST requests - let!(:metadata_for_tube_1) { { tube_rack_barcode: 'TR00000001', tube_rack_position: 'A1' } } - let(:tube_1_create_args) { { user_id: user.id, asset_id: child_tube_1_v2.id, metadata: metadata_for_tube_1 } } - - let!(:metadata_for_tube_2) { { tube_rack_barcode: 'TR00000001', tube_rack_position: 'B1' } } - let(:tube_2_create_args) { { user_id: user.id, asset_id: child_tube_2_v2.id, metadata: metadata_for_tube_2 } } - - let!(:metadata_for_tube_3) { { tube_rack_barcode: 'TR00000002', tube_rack_position: 'A1' } } - let(:tube_3_create_args) { { user_id: user.id, asset_id: child_tube_3_v2.id, metadata: metadata_for_tube_3 } } - - let!(:metadata_for_tube_4) { { tube_rack_barcode: 'TR00000002', tube_rack_position: 'B1' } } - let(:tube_4_create_args) { { user_id: user.id, asset_id: child_tube_4_v2.id, metadata: metadata_for_tube_4 } } - - let!(:metadata_for_tube_5) { { tube_rack_barcode: 'TR00000002', tube_rack_position: 'C1' } } - let(:tube_5_create_args) { { user_id: user.id, asset_id: child_tube_5_v2.id, metadata: metadata_for_tube_5 } } - let(:contingency_file) do fixture_file_upload( 'spec/fixtures/files/scrna_core/scrna_core_contingency_tube_rack_scan_3_tubes.csv', @@ -847,28 +747,20 @@ ) end - before do - stub_v2_user(user) - - stub_v2_labware(child_tube_1_v2) - stub_v2_labware(child_tube_2_v2) - stub_v2_labware(child_tube_3_v2) - stub_v2_labware(child_tube_4_v2) - stub_v2_labware(child_tube_5_v2) - end + before { stub_v2_user(user) } it 'creates the child tubes' do - expect_api_v2_posts( - 'CustomMetadatumCollection', - [tube_1_create_args, tube_2_create_args, tube_3_create_args, tube_4_create_args, tube_5_create_args] + expect_specific_tube_creation(child_sequencing_tube_purpose_uuid, sequencing_tubes) + expect_specific_tube_creation(child_contingency_tube_purpose_uuid, contingency_tubes) + + expect_custom_metadatum_collection_posts( + { 'TR00000001' => sequencing_tubes, 'TR00000002' => contingency_tubes } ) expect(subject.valid?).to be_truthy expect(subject.save).to be_truthy expect(stub_sequencing_file_upload).to have_been_made.once - expect(stub_sequencing_tube_creation_request).to have_been_made.once expect(stub_contingency_file_upload).to have_been_made.once - expect(stub_contingency_tube_creation_request).to have_been_made.once expect(stub_transfer_creation_request).to have_been_made.once end @@ -878,94 +770,20 @@ create(:v2_well, location: 'A1', aliquots: [parent_aliquot_sample1_aliquot1], state: 'failed') end - # as A1 is failed order of samples is changed - let!(:stub_sequencing_tube_creation_request) do - stub_api_post( - 'specific_tube_creations', - payload: { - specific_tube_creation: { - child_purposes: [child_sequencing_tube_purpose_uuid, child_sequencing_tube_purpose_uuid], - tube_attributes: [ - # sample 2 in well B1 to seq tube 1 in A1 - { name: 'SEQ:NT2P:A1', foreign_barcode: 'FX00000001' }, - # sample 1 in well A2 to seq tube 2 in B1 - { name: 'SEQ:NT1O:B1', foreign_barcode: 'FX00000002' } - ], - user: user_uuid, - parent: parent_uuid - } - }, - body: json(:specific_tube_creation, uuid: stub_sequencing_tube_creation_request_uuid, children_count: 2) - ) - end - - # stub what sequencing tubes were just made (order changed) - let!(:stub_sequencing_tube_creation_children_request) do - stub_api_get( - stub_sequencing_tube_creation_request_uuid, - 'children', - body: - json( - :tube_collection_with_barcodes_specified, - size: 2, - names: %w[SEQ:NT2P:A1 SEQ:NT1O:B1], - barcode_prefix: 'FX', - barcode_numbers: [1, 2] - ) - ) - end - - # only 2 contingency tubes will be needed - let!(:stub_contingency_tube_creation_request) do - stub_api_post( - 'specific_tube_creations', - payload: { - specific_tube_creation: { - child_purposes: [child_contingency_tube_purpose_uuid, child_contingency_tube_purpose_uuid], - tube_attributes: [ - # sample 2 from well B2 to contingency tube 1 in A1 - { name: 'SPR:NT2P:A1', foreign_barcode: 'FX00000011' }, - # sample 1 from well A3 to contingency tube 2 in B1 - { name: 'SPR:NT1O:B1', foreign_barcode: 'FX00000012' } - ], - user: user_uuid, - parent: parent_uuid - } - }, - body: json(:specific_tube_creation, uuid: stub_contingency_tube_creation_request_uuid, children_count: 3) - ) - end - - # stub what contingency tubes were just made (just 2) - let!(:stub_contingency_tube_creation_children_request) do - stub_api_get( - stub_contingency_tube_creation_request_uuid, - 'children', - body: - json( - :tube_collection_with_barcodes_specified, - size: 2, - names: %w[SPR:NT2P:A1 SPR:NT1O:B1], - barcode_prefix: 'FX', - barcode_numbers: [11, 12], - uuid_index_offset: 2 - ) - ) - end - # one fewer transfer request let!(:stub_transfer_creation_request) do + parent_wells = [parent_well_b1, parent_well_a2, parent_well_b2, parent_well_a3] + target_tubes = sequencing_tubes + contingency_tubes + transfer_requests = + parent_wells.map.with_index do |parent_well, index| + { 'submission_id' => '2', 'source_asset' => parent_well.uuid, 'target_asset' => target_tubes[index].uuid } + end stub_api_post( 'transfer_request_collections', payload: { transfer_request_collection: { user: user_uuid, - transfer_requests: [ - { 'submission_id' => '2', 'source_asset' => parent_well_b1.uuid, 'target_asset' => 'tube-0' }, - { 'submission_id' => '2', 'source_asset' => parent_well_a2.uuid, 'target_asset' => 'tube-1' }, - { 'submission_id' => '2', 'source_asset' => parent_well_b2.uuid, 'target_asset' => 'tube-2' }, - { 'submission_id' => '2', 'source_asset' => parent_well_a3.uuid, 'target_asset' => 'tube-3' } - ] + transfer_requests: transfer_requests } }, body: '{}' @@ -979,30 +797,40 @@ ) end - before do - stub_get_labware_metadata(child_tube_1_v2.barcode.machine, child_tube_1_v1) - stub_get_labware_metadata(child_tube_2_v2.barcode.machine, child_tube_2_v1) - stub_get_labware_metadata(child_tube_3_v2.barcode.machine, child_tube_3_v1) - stub_get_labware_metadata(child_tube_4_v2.barcode.machine, child_tube_4_v1) + let(:sequencing_tubes) do + prepare_created_child_tubes( + [ + # sample 2 in well B1 to seq tube 1 in A1 + { name: 'SEQ:NT2P:A1', foreign_barcode: 'FX00000001' }, + # sample 1 in well A2 to seq tube 2 in B1 + { name: 'SEQ:NT1O:B1', foreign_barcode: 'FX00000002' } + ] + ) + end - stub_asset_search(child_tube_1_v2.barcode.machine, child_tube_1_v1) - stub_asset_search(child_tube_2_v2.barcode.machine, child_tube_2_v1) - stub_asset_search(child_tube_3_v2.barcode.machine, child_tube_3_v1) - stub_asset_search(child_tube_4_v2.barcode.machine, child_tube_4_v1) + let(:contingency_tubes) do + prepare_created_child_tubes( + [ + # sample 2 from well B2 to contingency tube 1 in A1 + { name: 'SPR:NT2P:A1', foreign_barcode: 'FX00000011' }, + # sample 1 from well A3 to contingency tube 2 in B1 + { name: 'SPR:NT1O:B1', foreign_barcode: 'FX00000012' } + ] + ) end it 'does not create a tube for the failed well' do - expect_api_v2_posts( - 'CustomMetadatumCollection', - [tube_1_create_args, tube_2_create_args, tube_3_create_args, tube_4_create_args] # no tube 5 + expect_specific_tube_creation(child_sequencing_tube_purpose_uuid, sequencing_tubes) + expect_specific_tube_creation(child_contingency_tube_purpose_uuid, contingency_tubes) + + expect_custom_metadatum_collection_posts( + { 'TR00000001' => sequencing_tubes, 'TR00000002' => contingency_tubes } ) expect(subject.valid?).to be_truthy expect(subject.save).to be_truthy expect(stub_sequencing_file_upload).to have_been_made.once - expect(stub_sequencing_tube_creation_request).to have_been_made.once expect(stub_contingency_file_upload).to have_been_made.once - expect(stub_contingency_tube_creation_request).to have_been_made.once expect(stub_transfer_creation_request).to have_been_made.once end end @@ -1042,86 +870,50 @@ ) end - # stub the sequencing tube creation - let!(:stub_sequencing_tube_creation_request_uuid) { SecureRandom.uuid } - let!(:stub_sequencing_tube_creation_request) do - stub_api_post( - 'specific_tube_creations', - payload: { - specific_tube_creation: { - child_purposes: [child_sequencing_tube_purpose_uuid, child_sequencing_tube_purpose_uuid], - tube_attributes: [ - # sample 1 in well A1 to seq tube 1 in A1 - { name: 'SEQ:NT1O:A1', foreign_barcode: 'FX00000001' }, - # sample 2 in well B1 to seq tube 2 in B1 - { name: 'SEQ:NT2P:B1', foreign_barcode: 'FX00000002' } - ], - user: user_uuid, - parent: parent_uuid - } - }, - body: json(:specific_tube_creation, uuid: stub_sequencing_tube_creation_request_uuid, children_count: 2) - ) - end - - # stub what sequencing tubes were just made - let!(:stub_sequencing_tube_creation_children_request) do - stub_api_get( - stub_sequencing_tube_creation_request_uuid, - 'children', - body: - json( - :tube_collection_with_barcodes_specified, - size: 2, - names: %w[SEQ:NT1O:A1 SEQ:NT2P:B1], - barcode_prefix: 'FX', - barcode_numbers: [1, 2] - ) + let(:sequencing_tubes) do + prepare_created_child_tubes( + [ + # sample 1 from well A1 to sequencing tube 1 in A1 + { name: 'SEQ:NT1O:A1', foreign_barcode: 'FX00000001' }, + # sample 2 from well B1 to sequencing tube 2 in B1 + { name: 'SEQ:NT2P:B1', foreign_barcode: 'FX00000002' } + ] ) end # stub the transfer creation let!(:stub_transfer_creation_request) do + parent_wells = [parent_well_a1, parent_well_b1] + transfer_requests = + parent_wells.map.with_index do |parent_well, index| + { + 'submission_id' => '2', + 'source_asset' => parent_well.uuid, + 'target_asset' => sequencing_tubes[index].uuid + } + end stub_api_post( 'transfer_request_collections', payload: { transfer_request_collection: { user: user_uuid, - transfer_requests: [ - { 'submission_id' => '2', 'source_asset' => parent_well_a1.uuid, 'target_asset' => 'tube-0' }, - { 'submission_id' => '2', 'source_asset' => parent_well_b1.uuid, 'target_asset' => 'tube-1' } - ] + transfer_requests: transfer_requests } }, body: '{}' ) end - # need api v1 versions of child tubes - let(:child_tube_1_v1) { json :tube, uuid: child_tube_1_uuid, barcode_prefix: 'FX', barcode_number: 1 } - let(:child_tube_2_v1) { json :tube, uuid: child_tube_2_uuid, barcode_prefix: 'FX', barcode_number: 2 } - - # need to stub the creation of the tube metadata - let!(:metadata_for_tube_1) { { tube_rack_barcode: 'TR00000001', tube_rack_position: 'A1' } } - let(:tube_1_create_args) { { user_id: user.id, asset_id: child_tube_1_v2.id, metadata: metadata_for_tube_1 } } - - let!(:metadata_for_tube_2) { { tube_rack_barcode: 'TR00000001', tube_rack_position: 'B1' } } - let(:tube_2_create_args) { { user_id: user.id, asset_id: child_tube_2_v2.id, metadata: metadata_for_tube_2 } } - - before do - stub_v2_user(user) - - stub_v2_labware(child_tube_1_v2) - stub_v2_labware(child_tube_2_v2) - end + before { stub_v2_user(user) } it 'creates the child tubes' do - expect_api_v2_posts('CustomMetadatumCollection', [tube_1_create_args, tube_2_create_args]) + # Contingency tubes creation + expect_specific_tube_creation(child_sequencing_tube_purpose_uuid, sequencing_tubes) + expect_custom_metadatum_collection_posts({ 'TR00000001' => sequencing_tubes }) expect(subject.valid?).to be_truthy expect(subject.save).to be_truthy expect(stub_sequencing_file_upload).to have_been_made.once - expect(stub_sequencing_tube_creation_request).to have_been_made.once expect(stub_transfer_creation_request).to have_been_made.once end end diff --git a/spec/models/labware_creators/pooled_tubes_by_submission_spec.rb b/spec/models/labware_creators/pooled_tubes_by_submission_spec.rb index a15f483ef..48cf5ae39 100644 --- a/spec/models/labware_creators/pooled_tubes_by_submission_spec.rb +++ b/spec/models/labware_creators/pooled_tubes_by_submission_spec.rb @@ -11,6 +11,8 @@ RSpec.describe LabwareCreators::PooledTubesBySubmission do include FeatureHelpers + has_a_working_api + it_behaves_like 'it only allows creation from charged and passed plates with defined downstream pools' subject { LabwareCreators::PooledTubesBySubmission.new(api, form_attributes) } @@ -31,10 +33,25 @@ before { stub_v2_plate(source_plate, stub_search: false) } context '#save!' do - has_a_working_api - - let(:child_1_name) { 'DN5 A1:C1' } - let(:child_2_name) { 'DN5 D1:A2' } + def expect_specific_tube_creation(child_tubes) + # Create a mock for the specific tube creation. + specific_tube_creation = double + allow(specific_tube_creation).to receive(:children).and_return(child_tubes) + + # Expect the post request and return the mock. + expect_api_v2_posts( + 'SpecificTubeCreation', + [ + { + child_purpose_uuids: [purpose_uuid] * child_tubes.size, + parent_uuids: [parent_uuid], + tube_attributes: child_tubes.map { |tube| { name: tube.name } }, + user_uuid: user_uuid + } + ], + [specific_tube_creation] + ) + end # Used to fetch the pools. This is the kind of thing we could pass through from a custom form let!(:parent_request) do @@ -42,40 +59,20 @@ stub_api_get(parent_uuid, 'wells', body: wells_json) end - let(:creation_payload) do - { - user: user_uuid, - parent: parent_uuid, - child_purposes: [purpose_uuid, purpose_uuid], - tube_attributes: [{ name: child_1_name }, { name: child_2_name }] - } - end + let(:child_1_name) { 'DN5 A1:C1' } + let(:child_2_name) { 'DN5 D1:A2' } - let(:tube_creation_request_uuid) { SecureRandom.uuid } + let(:tube_attributes) { [{ name: child_1_name }, { name: child_2_name }] } - let!(:tube_creation_request) do - stub_api_post( - 'specific_tube_creations', - payload: { - specific_tube_creation: creation_payload - }, - body: - json( - :specific_tube_creation, - uuid: tube_creation_request_uuid, - children_count: 2, - names: [child_1_name, child_2_name] - ) - ) - end + let(:child_tubes) do + # Prepare child tubes and stub their lookups. + child_tubes = + tube_attributes.each_with_index.map do |attrs, index| + create(:v2_tube, name: attrs[:name], uuid: "tube-#{index}") + end + child_tubes.each { |child_tube| stub_v2_labware(child_tube) } - # Find out what tubes we've just made! - let!(:tube_creation_children_request) do - stub_api_get( - tube_creation_request_uuid, - 'children', - body: json(:tube_collection, names: [child_1_name, child_2_name]) - ) + child_tubes end let(:transfer_requests) do @@ -106,9 +103,10 @@ end context 'without parent metadata' do + before { expect_specific_tube_creation(child_tubes) } + it 'pools by submission' do expect(subject.save!).to be_truthy - expect(tube_creation_request).to have_been_made.once expect(transfer_creation_request).to have_been_made.once end @@ -133,24 +131,11 @@ for_multiplexing: true end - setup do - stub_get_labware_metadata('DN10', parent, metadata: { stock_barcode: 'DN6' }) - stub_api_post( - 'specific_tube_creations', - payload: { - specific_tube_creation: creation_payload - }, - body: - json( - :specific_tube_creation, - uuid: tube_creation_request_uuid, - children_count: 2, - names: [child_1_name, child_2_name] - ) - ) - end + setup { stub_get_labware_metadata('DN10', parent, metadata: { stock_barcode: 'DN6' }) } it 'sets the correct tube name' do + expect_specific_tube_creation(child_tubes) + expect(subject.save!).to be_truthy expect(subject.child_stock_tubes.length).to eq(2) expect(subject.child_stock_tubes).to have_key(child_1_name) @@ -172,6 +157,9 @@ { 'source_asset' => 'example-well-uuid-7', 'target_asset' => 'tube-1', 'submission' => 'pool-2-uuid' } ] end + + before { expect_specific_tube_creation(child_tubes) } + it 'pools by submission' do expect(subject.save!).to be_truthy expect(transfer_creation_request).to have_been_made.once @@ -191,14 +179,9 @@ ] end - let(:creation_payload) do - { - user: user_uuid, - parent: parent_uuid, - child_purposes: [purpose_uuid], - tube_attributes: [{ name: child_1_name }] - } - end + let(:tube_attributes) { [{ name: child_1_name }] } + + before { expect_specific_tube_creation(child_tubes) } it 'pools by submission' do expect(subject.save!).to be_truthy diff --git a/spec/models/labware_creators/pooled_tubes_from_whole_plates_spec.rb b/spec/models/labware_creators/pooled_tubes_from_whole_plates_spec.rb index 90ca78426..1ee701ebc 100644 --- a/spec/models/labware_creators/pooled_tubes_from_whole_plates_spec.rb +++ b/spec/models/labware_creators/pooled_tubes_from_whole_plates_spec.rb @@ -51,54 +51,43 @@ let(:form_attributes) { { user_uuid:, purpose_uuid:, parent_uuid:, barcodes: } } - let(:tube_creation_request_uuid) { SecureRandom.uuid } + let(:child_tube) { create :v2_tube } + let(:specific_tube_creation) do + response = double + allow(response).to receive(:children).and_return([child_tube]) - let(:tube_creation_request) do - # TODO: In reality we want to link in all four parents. - stub_api_post( - 'specific_tube_creations', - payload: { - specific_tube_creation: { - user: user_uuid, - parent: parent_uuid, - child_purposes: [purpose_uuid], - tube_attributes: [{ name: 'DN2+' }] - } - }, - body: json(:specific_tube_creation, uuid: tube_creation_request_uuid, children_count: 1) - ) + response end - # Find out what tubes we've just made! - let(:tube_creation_children_request) do - stub_api_get(tube_creation_request_uuid, 'children', body: json(:tube_collection, names: ['DN2+'])) - end - - # Used to fetch the pools. This is the kind of thing we could pass through from a custom form - let(:stub_barcode_searches) { stub_asset_search(barcodes, [parent, parent2, parent3, parent4]) } - - before do - stub_barcode_searches - tube_creation_children_request - tube_creation_request - end + before { stub_asset_search(barcodes, [parent, parent2, parent3, parent4]) } context 'with compatible plates' do it 'pools from all the plates' do + expect_api_v2_posts( + 'SpecificTubeCreation', + [ + { + child_purpose_uuids: [purpose_uuid], + parent_uuids: [parent_uuid], + tube_attributes: [{ name: 'DN2+' }], + user_uuid: user_uuid + } + ], + [specific_tube_creation] + ) expect_api_v2_posts( 'Transfer', [parent_uuid, parent2_uuid, parent3_uuid, parent4_uuid].map do |source_uuid| { user_uuid: user_uuid, source_uuid: source_uuid, - destination_uuid: 'tube-0', + destination_uuid: child_tube.uuid, transfer_template_uuid: 'whole-plate-to-tube' } end ) expect(subject.save!).to be_truthy - expect(tube_creation_request).to have_been_made.once end end end diff --git a/spec/models/labware_creators/quadrant_stamp_primer_panel_spec.rb b/spec/models/labware_creators/quadrant_stamp_primer_panel_spec.rb index 6c5abbe77..488f372c8 100644 --- a/spec/models/labware_creators/quadrant_stamp_primer_panel_spec.rb +++ b/spec/models/labware_creators/quadrant_stamp_primer_panel_spec.rb @@ -13,7 +13,6 @@ let(:parent1_uuid) { 'example-plate-uuid' } let(:parent2_uuid) { 'example-plate2-uuid' } - let(:child_uuid) { 'child-uuid' } let(:requests) { Array.new(96) { |i| create :gbs_library_request, state: 'started', uuid: "request-#{i}" } } let(:requests2) { Array.new(96) { |i| create :gbs_library_request, state: 'started', uuid: "request-#{i}" } } let(:stock_plate1) { create :v2_stock_plate_for_plate, barcode_number: '1' } @@ -40,20 +39,18 @@ stock_plate: stock_plate2 ) end - let(:child_plate_v2) { create :v2_plate, uuid: child_uuid, barcode_number: '5', size: 384 } - let(:child_plate_v1) { json :stock_plate_with_metadata, stock_plate: { barcode: '5', uuid: child_uuid } } + let(:child_plate) { create :v2_plate, barcode_number: '5', size: 384 } let(:child_purpose_uuid) { 'child-purpose' } let(:child_purpose_name) { 'Child Purpose' } - let(:user_uuid) { 'user-uuid' } - let(:v1_user) { json :v1_user, uuid: user_uuid } - let(:user) { create :user, uuid: user_uuid } + let(:user) { create :user } before do create :purpose_config, name: child_purpose_name + stub_v2_user(user) stub_v2_plate(parent1, stub_search: false) stub_v2_plate(parent2, stub_search: false) - stub_v2_plate(child_plate_v2, stub_search: false, custom_query: [:plate_with_wells, child_plate_v2.uuid]) + stub_v2_plate(child_plate, stub_search: false, custom_query: [:plate_with_wells, child_plate.uuid]) end context 'on new' do @@ -75,7 +72,7 @@ end context 'on create' do - subject { LabwareCreators::QuadrantStampPrimerPanel.new(api, form_attributes.merge(user_uuid:)) } + subject { LabwareCreators::QuadrantStampPrimerPanel.new(api, form_attributes.merge(user_uuid: user.uuid)) } let(:form_attributes) do { @@ -246,20 +243,6 @@ } end - let!(:pooled_plate_creation_request) do - stub_api_post( - 'pooled_plate_creations', - payload: { - pooled_plate_creation: { - user: user_uuid, - child_purpose: child_purpose_uuid, - parents: [parent1_uuid, parent2_uuid] - } - }, - body: json(:plate_creation, child_uuid:) - ) - end - let(:transfer_requests) do [ { source_asset: '3-well-A1', outer_request: 'request-0', target_asset: '5-well-A1' }, @@ -290,7 +273,7 @@ 'transfer_request_collections', payload: { transfer_request_collection: { - user: user_uuid, + user: user.uuid, transfer_requests: transfer_requests } }, @@ -298,37 +281,44 @@ ) end - context '#save!' do - setup do - stub_api_get(child_plate_v2.uuid, body: child_plate_v1) - stub_api_get( - 'custom_metadatum_collection-uuid', - body: json(:v1_custom_metadatum_collection, uuid: 'custom_metadatum_collection-uuid') - ) - stub_api_get('user-uuid', body: v1_user) - stub_v2_user(user) - stub_api_get('asset-uuid', body: child_plate_v1) + let(:pooled_plate_creation) do + response = double + allow(response).to receive(:child).and_return(child_plate) - metadata = - attributes_for(:v1_custom_metadatum_collection).fetch(:metadata, {}).merge( - stock_barcode_q0: stock_plate1.barcode.human, - stock_barcode_q1: stock_plate2.barcode.human - ) + response + end - stub_api_put( - 'custom_metadatum_collection-uuid', - payload: { - custom_metadatum_collection: { - metadata: - } - }, - body: json(:v1_custom_metadatum_collection) - ) - end + def expect_pooled_plate_creation + expect_api_v2_posts( + 'PooledPlateCreation', + [{ child_purpose_uuid: child_purpose_uuid, parent_uuids: [parent1_uuid, parent2_uuid], user_uuid: user.uuid }], + [pooled_plate_creation] + ) + end + def expect_custom_metadatum_collection_creation + expect_api_v2_posts( + 'CustomMetadatumCollection', + [ + { + asset_id: child_plate.id, + metadata: { + stock_barcode_q0: stock_plate1.barcode.human, + stock_barcode_q1: stock_plate2.barcode.human + }, + user_id: user.id + } + ] + ) + end + + context '#save!' do it 'creates a plate!' do + expect_pooled_plate_creation + expect_custom_metadatum_collection_creation + subject.save! - expect(pooled_plate_creation_request).to have_been_made.once + expect(transfer_creation_request).to have_been_made.once end end diff --git a/spec/support/api_url_helper.rb b/spec/support/api_url_helper.rb index 2e720069e..38bfc39ba 100644 --- a/spec/support/api_url_helper.rb +++ b/spec/support/api_url_helper.rb @@ -92,16 +92,16 @@ def stub_api_v2_save(klass) allow_any_instance_of(receiving_class).to receive(:save).and_return(true) end - def stub_api_v2_post(klass, return_value = nil) - # intercepts the 'create!' method for any class beginning with - # 'Sequencescape::Api::V2::' and returns the given value or else true. + def stub_api_v2_post(klass, return_value = nil, method: :create!) + # intercepts the specified `method` for any class beginning with + # 'Sequencescape::Api::V2::' and returns the given `return_value`, or else `true`. receiving_class = "Sequencescape::Api::V2::#{klass}".constantize return_value ||= true - allow(receiving_class).to receive(:create!).and_return(return_value) + allow(receiving_class).to receive(method).and_return(return_value) end - def expect_api_v2_posts(klass, args_list, return_values = []) - # Expects the 'create!' method for any class beginning with + def expect_api_v2_posts(klass, args_list, return_values = [], method: :create!) + # Expects the specified `method` for any class beginning with # 'Sequencescape::Api::V2::' to be called with given arguments, in sequence, and returns the given values. # If return_values is empty, it will return true. receiving_class = "Sequencescape::Api::V2::#{klass}".constantize @@ -109,7 +109,7 @@ def expect_api_v2_posts(klass, args_list, return_values = []) .zip(return_values) .each do |args, ret| ret ||= true - expect(receiving_class).to receive(:create!).with(args).and_return(ret) + expect(receiving_class).to receive(method).with(args).and_return(ret) end end diff --git a/yarn.lock b/yarn.lock index 9e8731f84..a2d8a360a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -620,6 +620,11 @@ resolved "https://registry.yarnpkg.com/@prettier/plugin-ruby/-/plugin-ruby-4.0.4.tgz#73d85fc2a1731a3f62b57ac3116cf1c234027cb6" integrity sha512-lCpvfS/dQU5WrwN3AQ5vR8qrvj2h5gE41X08NNzAAXvHdM4zwwGRcP2sHSxfu6n6No+ljWCVx95NvJPFTTjCTg== +"@rails/ujs@^7.1.3": + version "7.1.402" + resolved "https://registry.yarnpkg.com/@rails/ujs/-/ujs-7.1.402.tgz#5d4e7e5d1e2e22df081bf5693fc75f0725635130" + integrity sha512-q9dDlIR+anDtuGcV56rLnqHAxtWo8vkSnvfFt7juthvHc+97NEtGlnM++uhvnlDbR+7EGkX8nGqQIF8R93oWMQ== + "@rollup/rollup-android-arm-eabi@4.22.4": version "4.22.4" resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz#8b613b9725e8f9479d142970b106b6ae878610d5" @@ -1230,10 +1235,10 @@ cytoscape-popper@^2.0.0: dependencies: "@popperjs/core" "^2.0.0" -cytoscape@^3.30.0: - version "3.30.0" - resolved "https://registry.yarnpkg.com/cytoscape/-/cytoscape-3.30.0.tgz#5b0c13f7bb305481e2c70414d4c5f149d92eda82" - integrity sha512-l590mjTHT6/Cbxp13dGPC2Y7VXdgc+rUeF8AnF/JPzhjNevbDJfObnJgaSjlldOgBQZbue+X6IUZ7r5GAgvauQ== +cytoscape@^3.30.2: + version "3.30.2" + resolved "https://registry.yarnpkg.com/cytoscape/-/cytoscape-3.30.2.tgz#94149707fb6547a55e3b44f03ffe232706212161" + integrity sha512-oICxQsjW8uSaRmn4UK/jkczKOqTrVqt5/1WL0POiJUT2EKNc9STM4hYFHv917yu55aTBMFNRzymlJhVAiWPCxw== data-urls@^5.0.0: version "5.0.0" @@ -3058,16 +3063,8 @@ std-env@^3.7.0: resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.7.0.tgz#c9f7386ced6ecf13360b6c6c55b8aaa4ef7481d2" integrity sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg== -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^4.1.0: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0: + name string-width-cjs version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -3085,14 +3082,8 @@ string-width@^5.0.1, string-width@^5.1.2: emoji-regex "^9.2.2" strip-ansi "^7.0.1" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: + name strip-ansi-cjs version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -3274,15 +3265,15 @@ vite-node@2.0.5: tinyrainbow "^1.2.0" vite "^5.0.0" -vite-plugin-ruby@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/vite-plugin-ruby/-/vite-plugin-ruby-5.0.0.tgz#cd891198a7672f2e8402439f53ab9d2b08f6502d" - integrity sha512-c8PjTp21Ah/ttgnNUyu0qvCXZI08Jr9I24oUKg3TRIRhF5GcOZ++6wtlTCrNFd9COEQbpXHxlRIXd/MEg0iZJw== +vite-plugin-ruby@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/vite-plugin-ruby/-/vite-plugin-ruby-5.1.0.tgz#489f2071ea6d86b5db735ea1fbbbe10e50e8e9bc" + integrity sha512-Dgd/dCvb/8GYxZx2NEgxygEoUpHz9vfad7itlO4fXqYwdfAEwFwwOohC630yf/+kxIGBZXI5yk+Y3WkL9VkwcA== dependencies: debug "^4.3.4" fast-glob "^3.3.2" -vite@^5.0.0, vite@^5.4.7: +vite@^5.0.0: version "5.4.7" resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.7.tgz#d226f57c08b61379e955f3836253ed3efb2dcf00" integrity sha512-5l2zxqMEPVENgvzTuBpHer2awaetimj2BGkhBPdnwKbPNOlHsODU+oiazEZzLK7KhAnOrO+XGYJYn4ZlUhDtDQ==