diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index ee158f255..36964eaba 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -143,6 +143,13 @@ def skip_authentication? .include?(params[:action]) && !guests_disabled?) end + def set_user + @user = User.find(params[:user_id]) + return unless @user.role == 'banned' + flash[:error] = 'This user is banned and cannot check out equipment.' + params[:banned] = true + end + #-------- end before_filter methods --------# def update_cart # rubocop:disable MethodLength, AbcSize diff --git a/app/controllers/manage_controller.rb b/app/controllers/manage_controller.rb new file mode 100644 index 000000000..05f3e5283 --- /dev/null +++ b/app/controllers/manage_controller.rb @@ -0,0 +1,165 @@ +# frozen_string_literal: true +# rubocop:disable ClassLength +class ManageController < ApplicationController + load_and_authorize_resource + before_action :set_user, only: [:show, :checkout] + before_action :set_reservation, only: [:send_receipt] + + private + + def set_reservation + @reservation = Reservation.find(params[:receipt_id]) + end + + def check_for_banned_user + if @user.role == 'banned' + flash[:error] = 'Banned users cannot check out equipment.' + redirect_to(root_path) && return + end + true + end + + def check_terms_of_service + unless @user.terms_of_service_accepted || + params[:terms_of_service_accepted].present? + flash[:error] = 'You must confirm that the user accepts the Terms of '\ + 'Service.' + redirect_to(:back) && return + end + true + end + + def handle_overdue_reservations + if @user.overdue_reservations? + if can? :override, :checkout_errors + # Admins can ignore this + flash[:notice] = 'Admin Override: Equipment has been checked out '\ + 'successfully, even though the reserver has overdue equipment.' + else + # Everyone else is redirected + flash[:error] = 'Could not check out the equipment, because the '\ + 'reserver has reservations that are overdue.' + redirect_to(:back) && return + end + end + true + end + + def approve_checkout + # check for banned user + return unless check_for_banned_user + + # check terms of service + return unless check_terms_of_service + + # Overdue validation + return unless handle_overdue_reservations + true + end + + def check_nonemptiness_of(checked_out_reservations) + if checked_out_reservations.empty? + flash[:error] = 'No reservation selected.' + redirect_to(:back) && return + end + true + end + + def check_validity_of(checked_in_reservations) + unless checked_in_reservations + flash[:error] = 'One of the items you tried to check in has already '\ + 'been checked in.' + redirect_to(:back) && return + end + true + end + + def check_uniqueness_of(checked_out_reservations) + unless Reservation.unique_equipment_items?(checked_out_reservations) + flash[:error] = 'The same equipment item cannot be simultaneously '\ + 'checked out in multiple reservations.' + redirect_to(:back) && return + end + true + end + + def prep_receipt_page(check_in_set:, check_out_set:, user: nil) + @check_in_set = check_in_set + @check_out_set = check_out_set + @user = user if user + render('receipt', layout: 'application_with_search_sidebar') && return + end + + public + + def show # initializer + @check_out_set = @user.due_for_checkout.includes(:equipment_model) + @check_in_set = @user.due_for_checkin.includes(:equipment_model) + + render :show, layout: 'application' + end + + def checkout + return unless approve_checkout + + checked_out_reservations = + CheckoutHelper.preprocess_checkout(params[:reservations], + @user, current_user) + + return unless check_nonemptiness_of(checked_out_reservations) + return unless check_uniqueness_of(checked_out_reservations) + + ## Save reservations + Reservation.transaction do + begin + checked_out_reservations.each do |r| + CheckoutHelper.checkout_reservation(r, params[:reservations]) + end + rescue ActiveRecord::RecordNotSaved, ActiveRecord::RecordInvalid => e + flash[:error] = "Checking out your reservation failed: #{e.message}" + redirect_to manage_reservations_for_user_path(@user) + raise ActiveRecord::Rollback + end + end + + CheckoutHelper.update_tos(@user) + CheckoutHelper.send_checkout_receipts(checked_out_reservations) + prep_receipt_page(check_in_set: [], check_out_set: checked_out_reservations) + end + + def checkin + # see comments for checkout, this method proceeds in a similar way + checked_in_reservations = + CheckoutHelper.preproccess_checkins(params[:reservations], current_user) + + return unless check_validity_of(checked_in_reservations) + + return unless check_nonemptiness_of(checked_in_reservations) + ## Save reservations + Reservation.transaction do + begin + checked_in_reservations.each do |r| + CheckoutHelper.checkin_reservation(r, params[:reservations]) + end + rescue ActiveRecord::RecordNotSaved, ActiveRecord::RecordInvalid => e + flash[:error] = "Checking in your reservation failed: #{e.message}" + redirect_to :back + raise ActiveRecord::Rollback + end + end + + prep_receipt_page(check_in_set: checked_in_reservations, check_out_set: [], + user: checked_in_reservations.first.reserver) + end + + def send_receipt + if UserMailer.reservation_status_update(@reservation, 'checked out') + .deliver_now + flash[:notice] = 'Successfully delivered receipt email.' + else + flash[:error] = 'Unable to deliver receipt email. Please contact '\ + 'administrator for more support.' + end + redirect_to @reservation + end +end diff --git a/app/controllers/reservations_controller.rb b/app/controllers/reservations_controller.rb index 1e3337131..391e8471f 100644 --- a/app/controllers/reservations_controller.rb +++ b/app/controllers/reservations_controller.rb @@ -3,23 +3,12 @@ class ReservationsController < ApplicationController load_and_authorize_resource - before_action :set_reservation, - only: [:show, :edit, :update, :destroy, :checkout_email, - :checkin_email, :renew, :review, :approve_request, - :deny_request] - before_action :set_user, only: [:manage, :current, :checkout] + before_action :set_user, only: [:current, :checkout] private - def set_user - @user = User.find(params[:user_id]) - return unless @user.role == 'banned' - flash[:error] = 'This user is banned and cannot check out equipment.' - params[:banned] = true - end - - def set_reservation - @reservation = Reservation.find(params[:id]) + def reservation + @reservation ||= Reservation.find(params[:id]) end def set_index_dates @@ -181,12 +170,13 @@ def create # rubocop:disable all def edit @option_array = - @reservation.equipment_model.equipment_items - .collect { |e| [e.name, e.id] } + reservation.equipment_model.equipment_items + .collect { |e| [e.name, e.id] } end # for editing reservations; not for checkout or check-in def update # rubocop:disable all + @reservation = Reservation.find(params[:id]) message = 'Successfully edited reservation.' res = reservation_params # add new equipment item id to hash if it's being changed and save old @@ -195,19 +185,19 @@ def update # rubocop:disable all res[:equipment_item_id] = params[:equipment_item] new_item = EquipmentItem.find(params[:equipment_item]) old_item = - EquipmentItem.find_by id: @reservation.equipment_item_id + EquipmentItem.find_by id: reservation.equipment_item_id # check to see if new item is available unless new_item.available? r = new_item.current_reservation r.update(current_user, - { equipment_item_id: @reservation.equipment_item_id }, + { equipment_item_id: reservation.equipment_item_id }, '') end end # save changes to database - @reservation.update(current_user, res, params[:new_notes]) - if @reservation.save + reservation.update(current_user, res, params[:new_notes]) + if reservation.save # code for switching equipment items unless params[:equipment_item].blank? # if the item was previously assigned to a different reservation @@ -219,163 +209,23 @@ def update # rubocop:disable all end # update the item history / histories - old_item.make_switch_notes(@reservation, r, current_user) if old_item + old_item&.make_switch_notes(reservation, r, current_user) - new_item.make_switch_notes(r, @reservation, current_user) + new_item.make_switch_notes(r, reservation, current_user) end # flash success and exit flash[:notice] = message - redirect_to @reservation + redirect_to reservation else flash[:error] = "Unable to update reservation:\n"\ - "#{@reservation.errors.full_messages.to_sentence}" - redirect_to edit_reservation_path(@reservation) - end - end - - def checkout # rubocop:disable all - # convert all the reservations that are being checked out into an array - # of Reservation objects. only select the ones who are selected, eg - # they have an equipment item id set. - - ## Basic-logic checks, only need to be done once - - # check for banned user - if @user.role == 'banned' - flash[:error] = 'Banned users cannot check out equipment.' - redirect_to(root_path) && return - end - - # check terms of service - unless @user.terms_of_service_accepted || - params[:terms_of_service_accepted].present? - flash[:error] = 'You must confirm that the user accepts the Terms of '\ - 'Service.' - redirect_to(:back) && return - end - - # Overdue validation - if @user.overdue_reservations? - if can? :override, :checkout_errors - # Admins can ignore this - flash[:notice] = 'Admin Override: Equipment has been checked out '\ - 'successfully, even though the reserver has overdue equipment.' - else - # Everyone else is redirected - flash[:error] = 'Could not check out the equipment, because the '\ - 'reserver has reservations that are overdue.' - redirect_to(:back) && return - end - end - - checked_out_reservations = [] - params[:reservations].each do |r_id, r_attrs| - next if r_attrs[:equipment_item_id].blank? - r = Reservation.includes(:reserver).find(r_id) - # check that we don't somehow checkout a reservation that doesn't belong - # to the @user we're checking out for (params hacking?) - next if r.reserver != @user - checked_out_reservations << - r.checkout(r_attrs[:equipment_item_id], current_user, - r_attrs[:checkout_procedures], r_attrs[:notes]) - end - - if checked_out_reservations.empty? - flash[:error] = 'No reservation selected.' - redirect_to(:back) && return - end - - unless Reservation.unique_equipment_items?(checked_out_reservations) - flash[:error] = 'The same equipment item cannot be simultaneously '\ - 'checked out in multiple reservations.' - redirect_to(:back) && return - end - - ## Save reservations - Reservation.transaction do - begin - checked_out_reservations.each do |r| - r.save! - # update equipment item notes - new_notes = params[:reservations][r.id.to_s][:notes] - r.equipment_item.make_reservation_notes('checked out', r, - r.checkout_handler, - new_notes, r.checked_out) - end - rescue ActiveRecord::RecordNotSaved, ActiveRecord::RecordInvalid => e - flash[:error] = "Checking out your reservation failed: #{e.message}" - redirect_to manage_reservations_for_user_path(@user) - raise ActiveRecord::Rollback - end - end - - # update user with terms of service acceptance now that checkout worked - unless @user.terms_of_service_accepted - @user.update_attributes(terms_of_service_accepted: true) + "#{reservation.errors.full_messages.to_sentence}" + redirect_to edit_reservation_path(reservation) end - - # Send checkout receipts - checked_out_reservations.each do |res| - UserMailer.reservation_status_update(res, 'checked out').deliver_now - end - - # prep for receipt page and exit - @check_in_set = [] - @check_out_set = checked_out_reservations - render('receipt', layout: 'application_with_search_sidebar') && return - end - - def checkin # rubocop:disable all - # see comments for checkout, this method proceeds in a similar way - - checked_in_reservations = [] - params[:reservations].each do |r_id, r_attrs| - next if r_attrs[:checkin?].blank? - r = Reservation.find(r_id) - if r.checked_in - flash[:error] = 'One of the items you tried to check in has already '\ - 'been checked in.' - redirect_to(:back) && return # rubocop:disable NonLocalExitFromIterator - end - - checked_in_reservations << r.checkin(current_user, - r_attrs[:checkin_procedures], - r_attrs[:notes]) - end - - if checked_in_reservations.empty? - flash[:error] = 'No reservation selected!' - redirect_to(:back) && return - end - - ## Save reservations - Reservation.transaction do - begin - checked_in_reservations.each do |r| - r.save! - # update equipment item notes - new_notes = params[:reservations][r.id.to_s][:notes] - r.equipment_item.make_reservation_notes('checked in', r, - r.checkin_handler, new_notes, - r.checked_in) - end - rescue ActiveRecord::RecordNotSaved, ActiveRecord::RecordInvalid => e - flash[:error] = "Checking in your reservation failed: #{e.message}" - redirect_to :back - raise ActiveRecord::Rollback - end - end - - # prep for receipt page and exit - @user = checked_in_reservations.first.reserver - @check_in_set = checked_in_reservations - @check_out_set = [] - render('receipt', layout: 'application_with_search_sidebar') && return end def destroy - @reservation.destroy + reservation.destroy flash[:notice] = 'Successfully destroyed reservation.' redirect_to reservations_url end @@ -384,13 +234,6 @@ def upcoming @reservations_set = [Reservation.upcoming].delete_if(&:empty?) end - def manage # initializer - @check_out_set = @user.due_for_checkout.includes(:equipment_model) - @check_in_set = @user.due_for_checkin.includes(:equipment_model) - - render :manage, layout: 'application' - end - def current if params[:banned] && current_user.view_mode != 'superuser' redirect_to(root_path) && return @@ -408,67 +251,56 @@ def current render 'current_reservations' end - def send_receipt - if UserMailer.reservation_status_update(@reservation, 'checked out') - .deliver_now - flash[:notice] = 'Successfully delivered receipt email.' - else - flash[:error] = 'Unable to deliver receipt email. Please contact '\ - 'administrator for more support.' - end - redirect_to @reservation - end - def renew - message = @reservation.renew(current_user) + message = reservation.renew(current_user) if message flash[:error] = message - redirect_to(@reservation) && return + redirect_to(reservation) && return else flash[:notice] = 'Your reservation has been renewed until '\ - "#{@reservation.due_date.to_s(:long)}." - redirect_to @reservation + "#{reservation.due_date.to_s(:long)}." + redirect_to reservation end end def review @all_current_requests_by_user = - @reservation.reserver.reservations.requested.reject do |res| - res.id == @reservation.id + reservation.reserver.reservations.requested.reject do |res| + res.id == reservation.id end - @errors = @reservation.validate + @errors = reservation.validate end def approve_request - @reservation.status = 'reserved' - @reservation.notes = @reservation.notes.to_s # in case of nil - @reservation.notes += "\n\n### Approved on #{Time.zone.now.to_s(:long)} "\ + reservation.status = 'reserved' + reservation.notes = @reservation.notes.to_s # in case of nil + reservation.notes += "\n\n### Approved on #{Time.zone.now.to_s(:long)} "\ "by #{current_user.md_link}" - if @reservation.save + if reservation.save flash[:notice] = 'Request successfully approved' - UserMailer.reservation_status_update(@reservation, + UserMailer.reservation_status_update(reservation, 'request approved').deliver_now redirect_to reservations_path(requested: true) else flash[:error] = 'Oops! Something went wrong. Unable to approve '\ 'reservation.' - redirect_to @reservation + redirect_to reservation end end def deny_request - @reservation.status = 'denied' - @reservation.notes = @reservation.notes.to_s # in case of nil - @reservation.notes += "\n\n### Denied on #{Time.zone.now.to_s(:long)} by "\ + reservation.status = 'denied' + reservation.notes = reservation.notes.to_s # in case of nil + reservation.notes += "\n\n### Denied on #{Time.zone.now.to_s(:long)} by "\ "#{current_user.md_link}" - if @reservation.save + if reservation.save flash[:notice] = 'Request successfully denied' - UserMailer.reservation_status_update(@reservation).deliver_now + UserMailer.reservation_status_update(reservation).deliver_now redirect_to reservations_path(requested: true) else flash[:error] = 'Oops! Something went wrong. Unable to deny '\ 'reservation. We\'re not sure what that\'s all about.' - redirect_to @reservation + redirect_to reservation end end @@ -480,25 +312,24 @@ def archive # rubocop:disable all flash[:error] = 'Reason for archiving cannot be empty.' redirect_to(:back) && return end - set_reservation - if @reservation.checked_in + if reservation.checked_in flash[:error] = 'Cannot archive checked-in reservation.' redirect_to(:back) && return end begin - @reservation.archive(current_user, params[:archive_note]) - .save(validate: false) + reservation.archive(current_user, params[:archive_note]) + .save(validate: false) # archive equipment item if checked out - if @reservation.equipment_item - @reservation.equipment_item - .make_reservation_notes('archived', - @reservation, current_user, - params[:archive_note], - @reservation.checked_in) + if reservation.equipment_item + reservation.equipment_item + .make_reservation_notes('archived', + reservation, current_user, + params[:archive_note], + reservation.checked_in) if AppConfig.check(:autodeactivate_on_archive) - @reservation.equipment_item.deactivate(user: current_user, - reason: params[:archive_note]) + reservation.equipment_item.deactivate(user: current_user, + reason: params[:archive_note]) flash_end = ' The equipment item has been automatically deactivated.' end end diff --git a/app/models/ability.rb b/app/models/ability.rb index 131d3f878..9d0bc77be 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -35,6 +35,7 @@ def admin end def checkout + can :manage, Manage can :manage, Reservation cannot :archive, Reservation cannot :renew, Reservation unless AppConfig.check(:enable_renewals) diff --git a/app/models/manage.rb b/app/models/manage.rb new file mode 100644 index 000000000..0288a73f4 --- /dev/null +++ b/app/models/manage.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true +class Manage < ActiveRecord::Base +end diff --git a/app/models/reservation.rb b/app/models/reservation.rb index 89c49676e..c9a97eb93 100644 --- a/app/models/reservation.rb +++ b/app/models/reservation.rb @@ -361,7 +361,6 @@ def checkout(eq_item, checkout_handler, procedures, new_notes) # notes from the checkout. # # Returns the unsaved, checked out reservation - self.checkout_handler = checkout_handler self.checked_out = Time.zone.now self.equipment_item_id = eq_item diff --git a/app/views/reservations/_check_in_form.html.erb b/app/views/manage/_check_in_form.html.erb similarity index 100% rename from app/views/reservations/_check_in_form.html.erb rename to app/views/manage/_check_in_form.html.erb diff --git a/app/views/reservations/_check_out_form.html.erb b/app/views/manage/_check_out_form.html.erb similarity index 100% rename from app/views/reservations/_check_out_form.html.erb rename to app/views/manage/_check_out_form.html.erb diff --git a/app/views/reservations/receipt.html.erb b/app/views/manage/receipt.html.erb similarity index 87% rename from app/views/reservations/receipt.html.erb rename to app/views/manage/receipt.html.erb index 68db47cd8..e98cf9148 100644 --- a/app/views/reservations/receipt.html.erb +++ b/app/views/manage/receipt.html.erb @@ -1,5 +1,5 @@
- <%= render partial: 'top_buttons', locals: { reserver: @user } %> + <%= render partial: 'reservations/top_buttons', locals: { reserver: @user } %>

@@ -18,7 +18,7 @@

<%= link_to "Reservation ##{reservation.id}", reservation %>


- <%= render partial: 'reservation_details', locals: { :@reservation => reservation } %> + <%= render partial: 'reservations/reservation_details', locals: { :@reservation => reservation } %>
@@ -40,7 +40,7 @@

<%= link_to "Reservation ##{reservation.id}", reservation %>


- <%= render partial: 'reservation_details', locals: { :@reservation => reservation } %> + <%= render partial: 'reservations/reservation_details', locals: { :@reservation => reservation } %>
diff --git a/app/views/reservations/manage.html.erb b/app/views/manage/show.html.erb similarity index 96% rename from app/views/reservations/manage.html.erb rename to app/views/manage/show.html.erb index 24fefb959..01486b5be 100644 --- a/app/views/reservations/manage.html.erb +++ b/app/views/manage/show.html.erb @@ -2,7 +2,7 @@
- <%= render partial: 'top_buttons', locals: {reserver: @user} %> + <%= render partial: '/reservations/top_buttons', locals: {reserver: @user} %>
<% unless @check_in_set.empty? %> diff --git a/config/routes.rb b/config/routes.rb index 002c4fb60..c73750cdc 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -72,15 +72,12 @@ resources :reservations do member do - get :send_receipt put :renew put :archive end end # reservations views - get '/reservations/manage/:user_id', to: 'reservations#manage', - as: :manage_reservations_for_user get '/reservations/current/:user_id', to: 'reservations#current', as: :current_reservations_for_user @@ -90,12 +87,6 @@ as: :approve_request put '/reservations/deny/:id', to: 'reservations#deny_request', as: :deny_request - - # reservation checkout / check-in actions - put '/reservations/checkout/:user_id', to: 'reservations#checkout', - as: :checkout - put '/reservations/check-in/:user_id', to: 'reservations#checkin', - as: :checkin get '/blackouts/flash_message', to: 'blackouts#flash_message', as: :flash_message get '/blackouts/new_recurring', to: 'blackouts#new_recurring', @@ -106,6 +97,16 @@ put '/reservation/view_all_dates', to: 'reservations#view_all_dates', as: :view_all_dates + get 'manage/:user_id', to: 'manage#show', + as: :manage_reservations_for_user + + put 'manage/checkout/:user_id', to: 'manage#checkout', + as: :checkout + put 'manage/check-in/:user_id', to: 'manage#checkin', + as: :checkin + + put 'manage/send_receipt/:receipt_id', to: 'manage#send_receipt', + as: :send_receipt resources :blackouts do collection do post :create_recurring diff --git a/lib/checkout_helper.rb b/lib/checkout_helper.rb new file mode 100644 index 000000000..92dc63e6c --- /dev/null +++ b/lib/checkout_helper.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true +class CheckoutHelper + def self.checkout_reservation(r, reservations) + check_reservation(r, reservations, 'checked out', + r.checkout_handler, r.checked_out) + end + + def self.checkin_reservation(r, reservations) + check_reservation(r, reservations, 'checked in', + r.checkin_handler, r.checked_in) + end + + def self.check_reservation(r, reservations, message, handler, time) + r.save! + new_notes = reservations[r.id.to_s][:notes] + r.equipment_item.make_reservation_notes(message, r, + handler, + new_notes, time) + end + + def self.reservation_for(r_id, r_attrs, user) + return if r_attrs[:equipment_item_id].blank? + r = Reservation.includes(:reserver).find(r_id) + # check that we don't somehow checkout a reservation that doesn't belong + # to the @user we're checking out for (params hacking?) + return if r.reserver != user + r + end + + def self.preprocess_checkout(reservations, user, checkout_handler) + checked_out_reservations = [] + reservations.each do |r_id, r_attrs| + r = CheckoutHelper.reservation_for(r_id, r_attrs, user) + next if r.nil? + checked_out_reservations << r.checkout(r_attrs[:equipment_item_id], + checkout_handler, + r_attrs[:checkout_procedures], + r_attrs[:notes]) + end + checked_out_reservations + end + + def self.send_checkout_receipts(checked_out_reservations) + checked_out_reservations.each do |res| + UserMailer.reservation_status_update(res, 'checked out').deliver_now + end + end + + def self.update_tos(user) + # update user with terms of service acceptance now that checkout worked + return if user.terms_of_service_accepted + user.update_attributes(terms_of_service_accepted: true) + end + + def self.preproccess_checkins(reservations, user) + checked_in_reservations = [] + reservations.each do |r_id, r_attrs| + next if r_attrs[:checkin?].blank? + r = Reservation.find(r_id) + return nil if r.checked_in + checked_in_reservations << r.checkin(user, + r_attrs[:checkin_procedures], + r_attrs[:notes]) + end + checked_in_reservations + end +end diff --git a/spec/controllers/manage_controller_spec.rb b/spec/controllers/manage_controller_spec.rb new file mode 100644 index 000000000..cbdfc5190 --- /dev/null +++ b/spec/controllers/manage_controller_spec.rb @@ -0,0 +1,526 @@ +# frozen_string_literal: true +require 'spec_helper' + +describe ManageController, type: :controller do + AC_DEFAULTS = { disable_user_emails: false, + override_on_create: false, + override_at_checkout: false, + res_exp_time: false, + admin_email: 'admin@email.com' }.freeze + + before(:each) { mock_app_config(AC_DEFAULTS) } + + shared_examples 'inaccessible by banned user' do + before { mock_user_sign_in(FactoryGirl.build_stubbed(:banned)) } + it_behaves_like 'redirected request' + end + describe '#show (GET /manage/:user_id)' do + # Access: admins and checkout persons + # Functionality: + # - assigns @user, @check_out_set and @check_in_set + # - renders :show + + shared_examples 'can access #show' do + let!(:user) { UserMock.new(traits: [:findable]) } + before(:each) do + allow(user).to receive(:due_for_checkout) + .and_return(instance_spy('ActiveRecord::Relation')) + allow(user).to receive(:due_for_checkin) + .and_return(instance_spy('ActiveRecord::Relation')) + get :show, user_id: user.id + end + it { expect(response).to be_success } + it { is_expected.to render_template(:show) } + it 'assigns @user correctly' do + expect(assigns(:user)).to eq(user) + end + it 'assigns @check_out_set correctly' do + expect(assigns(:check_out_set)).to eq(user.due_for_checkout) + end + it 'assigns @check_in_set correctly' do + expect(assigns(:check_in_set)).to eq(user.due_for_checkin) + end + end + + context 'when accessed by admin' do + before(:each) { mock_user_sign_in(UserMock.new(:admin)) } + include_examples 'can access #show' + end + + context 'when accessed by checkout person' do + before(:each) { mock_user_sign_in(UserMock.new(:checkout_person)) } + include_examples 'can access #show' + end + context 'when accessed by patron' do + before(:each) do + user = UserMock.new + mock_user_sign_in(user) + get :show, user_id: user.id + end + include_examples 'redirected request' + end + end + describe '#checkout (PUT /manage/checkout/:user_id)' do + # Access: Admins, checkout persons. + # Functionality: very complicated (almost 100 lines) + # - pass TOS (if not, redirect) + # - params[:reservations] contains hash of + # {reservation_id => {equipment_item_id: int, notes: str, + # checkout_precedures: {checkout_procedure_id => int}}} + # - stops checkout if user has overdue reservations + # - stops checkout if no reservations are selected + # - overrides errors if you can and if there are some, otherwise + # redirects away + # - also prevents checkout if reserver is banned + + # Effects if successful: + # - sets empty @check_in_set, populates @check_out_set with the + # reservations + # - processes all reservations in params[:reservations] -- adds + # checkout_handler, checked_out (time), equipment_item; updates + # notes + # - renders :receipt template + # - sets reservation status to 'checked_out' + + # Note: Many of these can be cross-applied to #checkin as well + + before(:all) do + @user = FactoryGirl.create(:user) + @checkout_person = FactoryGirl.create(:checkout_person) + @admin = FactoryGirl.create(:admin) + @banned = FactoryGirl.create(:banned) + end + + after(:all) { User.destroy_all } + + before(:each) do + sign_in @user + @reservation = FactoryGirl.create(:valid_reservation, reserver: @user) + end + + shared_examples 'has successful checkout' do + before(:each) do + @item = + FactoryGirl.create(:equipment_item, + equipment_model: @reservation.equipment_model) + reservations_params = + { @reservation.id.to_s => { notes: '', + equipment_item_id: @item.id } } + ActionMailer::Base.deliveries = [] + put :checkout, user_id: @user.id, reservations: reservations_params + end + + it { expect(response).to be_success } + it { is_expected.to render_template(:receipt) } + + it 'assigns empty @check_in_set' do + expect(assigns(:check_in_set)).to be_empty + end + + it 'populates @check_out_set' do + expect(assigns(:check_out_set)).to eq [@reservation] + end + + it 'updates the reservation' do + expect(@reservation.checkout_handler).to be_nil + expect(@reservation.checked_out).to be_nil + expect(@reservation.equipment_item).to be_nil + expect(@reservation.reserved?).to be_truthy + @reservation.reload + expect(@reservation.checkout_handler).to be_a(User) + expect(@reservation.checked_out).to_not be_nil + expect(@reservation.equipment_item).to eq @item + expect(@reservation.checked_out).to be_truthy + end + + it 'updates the equipment item history' do + expect { @item.reload }.to change(@item, :notes) + end + + it 'updates the reservation notes' do + expect { @reservation.reload }.to change(@reservation, :notes) + end + + it 'sends checkout receipts' do + expect(ActionMailer::Base.deliveries.count).to eq(1) + end + end + + context 'when accessed by admin' do + before(:each) do + sign_in @admin + end + + include_examples 'has successful checkout' + end + + context 'when accessed by checkout person' do + before(:each) do + sign_in @checkout_person + end + + include_examples 'has successful checkout' + end + + context 'when accessed by patron' do + before(:each) do + sign_in @user + put :checkout, user_id: @user.id + end + + include_examples 'redirected request' + end + + it_behaves_like 'inaccessible by banned user' do + before { put :checkout, user_id: @banned.id } + end + + context 'when tos not accepted and not checked off' do + before do + request.env['HTTP_REFERER'] = 'where_i_came_from' + sign_in @admin + @user.update_attributes(terms_of_service_accepted: false) + put :checkout, user_id: @user.id, reservations: {} + end + it { expect(response).to redirect_to 'where_i_came_from' } + end + + context 'when tos accepted' do + before do + sign_in @admin + @user.update_attributes(terms_of_service_accepted: false) + @item = + FactoryGirl.create(:equipment_item, + equipment_model: @reservation.equipment_model) + reservations_params = + { @reservation.id.to_s => { notes: '', + equipment_item_id: @item.id } } + put :checkout, user_id: @user.id, reservations: reservations_params, + terms_of_service_accepted: true + end + + it { expect(response).to be_success } + end + + context 'with duplicate equipment item selection' do + before do + request.env['HTTP_REFERER'] = 'where_i_came_from' + sign_in @admin + @item = + FactoryGirl.create :equipment_item, + equipment_model: @reservation.equipment_model + FactoryGirl.create :equipment_item, + equipment_model: @reservation.equipment_model + @res2 = + FactoryGirl.create :valid_reservation, + reserver: @user, + equipment_model: @reservation.equipment_model + res_params = { notes: '', equipment_item_id: @item.id } + reservations_params = { @reservation.id.to_s => res_params, + @res2.id.to_s => res_params } + put :checkout, user_id: @user.id, reservations: reservations_params + end + + it { expect(response).to redirect_to 'where_i_came_from' } + + it 'does not update the equipment item history' do + expect { @item.reload }.not_to change(@item, :notes) + end + end + + context 'when not all procedures are filled out' do + before do + sign_in @admin + @item = + FactoryGirl.create(:equipment_item, + equipment_model: @reservation.equipment_model) + @procedure = + FactoryGirl.create(:checkout_procedure, + equipment_model: @reservation.equipment_model) + reservations_params = + { @reservation.id.to_s => { notes: '', + equipment_item_id: @item.id, + checkout_procedures: {} } } + put :checkout, user_id: @user.id, reservations: reservations_params + end + + it { expect(response).to be_success } + + it { is_expected.to render_template(:receipt) } + + it 'assigns empty @check_in_set' do + expect(assigns(:check_in_set)).to be_empty + end + + it 'populates @check_out_set' do + expect(assigns(:check_out_set)).to eq [@reservation] + end + + it 'updates the reservation' do + expect(@reservation.checkout_handler).to be_nil + expect(@reservation.checked_out).to be_nil + expect(@reservation.equipment_item).to be_nil + @reservation.reload + expect(@reservation.checkout_handler).to be_a(User) + expect(@reservation.checked_out).to_not be_nil + expect(@reservation.equipment_item).to eq @item + expect(@reservation.notes).to include(@procedure.step) + end + end + + context 'no reservations selected' do + before do + reservations_params = {} + request.env['HTTP_REFERER'] = 'where_i_came_from' + sign_in @checkout_person + put :checkout, user_id: @user.id, reservations: reservations_params + end + it { is_expected.to set_flash } + it { expect(response).to redirect_to 'where_i_came_from' } + end + + context 'reserver has overdue reservations' do + context 'can override reservations?' do + before do + sign_in @admin + @item = + FactoryGirl.create(:equipment_item, + equipment_model: @reservation.equipment_model) + reservations_params = + { @reservation.id.to_s => { notes: '', + equipment_item_id: @item.id } } + overdue = + FactoryGirl.build(:overdue_reservation, reserver_id: @user.id) + overdue.save(validate: false) + put :checkout, user_id: @user.id, reservations: reservations_params + end + it { expect(response).to be_success } + it { is_expected.to render_template(:receipt) } + end + + context 'cannot override' do + before do + request.env['HTTP_REFERER'] = 'where_i_came_from' + sign_in @checkout_person + @item = + FactoryGirl.create(:equipment_item, + equipment_model: @reservation.equipment_model) + reservations_params = + { @reservation.id.to_s => { notes: '', + equipment_item_id: @item.id } } + overdue = + FactoryGirl.build(:overdue_reservation, reserver_id: @user.id) + overdue.save(validate: false) + put :checkout, user_id: @user.id, reservations: reservations_params + end + it { is_expected.to set_flash } + it { expect(response).to redirect_to 'where_i_came_from' } + end + end + + context 'with banned reserver' do + before(:each) do + @reservation.update_attribute(:reserver_id, @banned.id) + request.env['HTTP_REFERER'] = 'where_i_came_from' + sign_in @checkout_person + @obj = + FactoryGirl.create(:equipment_item, + equipment_model: @reservation.equipment_model) + reservations_params = + { @reservation.id.to_s => { notes: '', + equipment_item_id: @obj.id } } + put :checkout, user_id: @banned.id, reservations: reservations_params + end + + it { is_expected.to set_flash } + it { expect(response).to redirect_to root_path } + end + end + + describe '#checkin (PUT /manage/check-in/:user_id)' do + # Access: Admins, checkout persons. + # Functionality: very complicated (almost 80 lines) + # - params[:reservations] contains a hash of + # {reservation_id => {checkin?: int, notes: str, + # (nil?) checkin_procedures: {checkin_procedure_id => int}}} + # - processes all reservations in params[:reservations] -- adds + # checkin_handler, checked_in (time); updates notes + # - stops checkin if no reservations are selected + # - overrides errors if you can and if there are some, otherwise + # redirects away + # - renders :receipt template + + before(:all) do + @user = FactoryGirl.create(:user) + @checkout_person = FactoryGirl.create(:checkout_person) + @admin = FactoryGirl.create(:admin) + @banned = FactoryGirl.create(:banned) + end + + after(:all) { User.destroy_all } + + before(:each) do + sign_in @user + @reservation = FactoryGirl.create(:valid_reservation, reserver: @user) + end + + shared_examples 'has successful checkin' do + before(:each) do + @reservation = + FactoryGirl.create(:checked_out_reservation, reserver: @user) + @item = @reservation.equipment_item + reservations_params = + { @reservation.id.to_s => { notes: '', checkin?: '1' } } + put :checkin, user_id: @user.id, reservations: reservations_params + end + + it { expect(response).to be_success } + it { is_expected.to render_template(:receipt) } + + it 'assigns empty @check_out_set' do + expect(assigns(:check_out_set)).to be_empty + end + + it 'populates @check_in_set' do + expect(assigns(:check_in_set)).to eq [@reservation] + end + + it 'updates the reservation' do + expect(@reservation.checkin_handler).to be_nil + expect(@reservation.checked_in).to be_nil + @reservation.reload + expect(@reservation.checkin_handler).to be_a(User) + expect(@reservation.checked_in).to_not be_nil + end + + it 'updates the equipment item history' do + expect { @item.reload }.to change(@item, :notes) + end + + it 'updates the reservation notes' do + expect { @reservation.reload }.to change(@reservation, :notes) + end + end + + context 'when accessed by admin' do + before(:each) do + sign_in @admin + end + + include_examples 'has successful checkin' + end + + context 'when accessed by checkout person' do + before(:each) do + sign_in @checkout_person + end + + include_examples 'has successful checkin' + end + + context 'when accessed by patron' do + before(:each) do + sign_in @user + put :checkin, user_id: @user.id + end + + include_examples 'redirected request' + end + + it_behaves_like 'inaccessible by banned user' do + before { put :checkin, user_id: @banned.id } + end + + context 'items have already been checked in' do + before do + sign_in @admin + request.env['HTTP_REFERER'] = 'where_i_came_from' + @reservation = + FactoryGirl.build(:checked_in_reservation, reserver: @user) + @reservation.save(validate: false) + reservations_params = + { @reservation.id.to_s => { notes: '', checkin?: '1' } } + put :checkin, user_id: @user.id, reservations: reservations_params + end + + it { is_expected.to set_flash } + it { expect(response).to redirect_to 'where_i_came_from' } + end + + context 'no reservations to check in' do + before do + request.env['HTTP_REFERER'] = 'where_i_came_from' + sign_in @admin + put :checkin, user_id: @user.id, reservations: {} + end + it { is_expected.to set_flash } + it { expect(response).to redirect_to 'where_i_came_from' } + end + + context 'when not all procedures are filled out' do + before do + sign_in @admin + @reservation = + FactoryGirl.create(:checked_out_reservation, reserver: @user) + @procedure = + FactoryGirl.create(:checkin_procedure, + equipment_model: @reservation.equipment_model) + reservations_params = + { @reservation.id.to_s => { notes: '', checkin?: '1', + checkin_procedures: {} } } + put :checkin, user_id: @user.id, reservations: reservations_params + end + + it { expect(response).to be_success } + it { is_expected.to render_template(:receipt) } + + it 'assigns empty @check_out_set' do + expect(assigns(:check_out_set)).to be_empty + end + + it 'populates @check_in_set' do + expect(assigns(:check_in_set)).to eq [@reservation] + end + + it 'updates the reservation' do + expect(@reservation.checkin_handler).to be_nil + expect(@reservation.checked_in).to be_nil + @reservation.reload + expect(@reservation.checkin_handler).to be_a(User) + expect(@reservation.checked_in).to_not be_nil + expect(@reservation.notes).to include(@procedure.step) + end + end + end + + describe '#send_receipt (PUT /manage/send_receipt/:receipt_id)' do + before { mock_user_sign_in(UserMock.new(:checkout_person)) } + let!(:res) do + FactoryGirl.build_stubbed(:valid_reservation).tap do |r| + allow(Reservation).to receive(:find).with(r.id.to_s).and_return(r) + end + end + + context 'successfully emails' do + before do + allow(UserMailer).to \ + receive_message_chain(:reservation_status_update, :deliver_now) + .and_return(true) + put :send_receipt, receipt_id: res.id + end + it { is_expected.to redirect_to(res) } + it { is_expected.to set_flash[:notice] } + end + + context 'fails to send email' do + before do + allow(UserMailer).to \ + receive_message_chain(:reservation_status_update, :deliver_now) + .and_return(false) + put :send_receipt, receipt_id: res.id + end + it { is_expected.to redirect_to(res) } + it { is_expected.to set_flash[:error] } + end + end +end diff --git a/spec/controllers/reservations_controller_spec.rb b/spec/controllers/reservations_controller_spec.rb index 320038827..777ebd0aa 100644 --- a/spec/controllers/reservations_controller_spec.rb +++ b/spec/controllers/reservations_controller_spec.rb @@ -748,54 +748,6 @@ end end - describe '#manage (GET /reservations/manage/:user_id)' do - # Access: admins and checkout persons - # Functionality: - # - assigns @user, @check_out_set and @check_in_set - # - renders :manage - - shared_examples 'can access #manage' do - let!(:user) { UserMock.new(traits: [:findable]) } - before(:each) do - allow(user).to receive(:due_for_checkout) - .and_return(instance_spy('ActiveRecord::Relation')) - allow(user).to receive(:due_for_checkin) - .and_return(instance_spy('ActiveRecord::Relation')) - get :manage, user_id: user.id - end - it { expect(response).to be_success } - it { is_expected.to render_template(:manage) } - it 'assigns @user correctly' do - expect(assigns(:user)).to eq(user) - end - it 'assigns @check_out_set correctly' do - expect(assigns(:check_out_set)).to eq(user.due_for_checkout) - end - it 'assigns @check_in_set correctly' do - expect(assigns(:check_in_set)).to eq(user.due_for_checkin) - end - end - - context 'when accessed by admin' do - before(:each) { mock_user_sign_in(UserMock.new(:admin)) } - include_examples 'can access #manage' - end - - context 'when accessed by checkout person' do - before(:each) { mock_user_sign_in(UserMock.new(:checkout_person)) } - include_examples 'can access #manage' - end - - context 'when accessed by patron' do - before(:each) do - user = UserMock.new - mock_user_sign_in(user) - get :manage, user_id: user.id - end - include_examples 'redirected request' - end - end - describe '#current (GET /reservations/current/:user_id)' do # not particularily messy but the method is written in a way that # makes mocking + stubbing difficult @@ -888,439 +840,6 @@ end end - describe '#checkout (PUT /reservations/checkout/:user_id)' do - # Access: Admins, checkout persons. - # Functionality: very complicated (almost 100 lines) - # - pass TOS (if not, redirect) - # - params[:reservations] contains hash of - # {reservation_id => {equipment_item_id: int, notes: str, - # checkout_precedures: {checkout_procedure_id => int}}} - # - stops checkout if user has overdue reservations - # - stops checkout if no reservations are selected - # - overrides errors if you can and if there are some, otherwise - # redirects away - # - also prevents checkout if reserver is banned - - # Effects if successful: - # - sets empty @check_in_set, populates @check_out_set with the - # reservations - # - processes all reservations in params[:reservations] -- adds - # checkout_handler, checked_out (time), equipment_item; updates - # notes - # - renders :receipt template - # - sets reservation status to 'checked_out' - - # Note: Many of these can be cross-applied to #checkin as well - - before(:all) do - @user = FactoryGirl.create(:user) - @checkout_person = FactoryGirl.create(:checkout_person) - @admin = FactoryGirl.create(:admin) - @banned = FactoryGirl.create(:banned) - end - - after(:all) { User.destroy_all } - - before(:each) do - sign_in @user - @reservation = FactoryGirl.create(:valid_reservation, reserver: @user) - end - - shared_examples 'has successful checkout' do - before(:each) do - @item = - FactoryGirl.create(:equipment_item, - equipment_model: @reservation.equipment_model) - reservations_params = - { @reservation.id.to_s => { notes: '', - equipment_item_id: @item.id } } - ActionMailer::Base.deliveries = [] - put :checkout, user_id: @user.id, reservations: reservations_params - end - - it { expect(response).to be_success } - it { is_expected.to render_template(:receipt) } - - it 'assigns empty @check_in_set' do - expect(assigns(:check_in_set)).to be_empty - end - - it 'populates @check_out_set' do - expect(assigns(:check_out_set)).to eq [@reservation] - end - - it 'updates the reservation' do - expect(@reservation.checkout_handler).to be_nil - expect(@reservation.checked_out).to be_nil - expect(@reservation.equipment_item).to be_nil - expect(@reservation.reserved?).to be_truthy - @reservation.reload - expect(@reservation.checkout_handler).to be_a(User) - expect(@reservation.checked_out).to_not be_nil - expect(@reservation.equipment_item).to eq @item - expect(@reservation.checked_out).to be_truthy - end - - it 'updates the equipment item history' do - expect { @item.reload }.to change(@item, :notes) - end - - it 'updates the reservation notes' do - expect { @reservation.reload }.to change(@reservation, :notes) - end - - it 'sends checkout receipts' do - expect(ActionMailer::Base.deliveries.count).to eq(1) - end - end - - context 'when accessed by admin' do - before(:each) do - sign_in @admin - end - - include_examples 'has successful checkout' - end - - context 'when accessed by checkout person' do - before(:each) do - sign_in @checkout_person - end - - include_examples 'has successful checkout' - end - - context 'when accessed by patron' do - before(:each) do - sign_in @user - put :checkout, user_id: @user.id - end - - include_examples 'redirected request' - end - - it_behaves_like 'inaccessible by banned user' do - before { put :checkout, user_id: @banned.id } - end - - context 'when tos not accepted and not checked off' do - before do - request.env['HTTP_REFERER'] = 'where_i_came_from' - sign_in @admin - @user.update_attributes(terms_of_service_accepted: false) - put :checkout, user_id: @user.id, reservations: {} - end - it { expect(response).to redirect_to 'where_i_came_from' } - end - - context 'when tos accepted' do - before do - sign_in @admin - @user.update_attributes(terms_of_service_accepted: false) - @item = - FactoryGirl.create(:equipment_item, - equipment_model: @reservation.equipment_model) - reservations_params = - { @reservation.id.to_s => { notes: '', - equipment_item_id: @item.id } } - put :checkout, user_id: @user.id, reservations: reservations_params, - terms_of_service_accepted: true - end - - it { expect(response).to be_success } - end - - context 'with duplicate equipment item selection' do - before do - request.env['HTTP_REFERER'] = 'where_i_came_from' - sign_in @admin - @item = - FactoryGirl.create :equipment_item, - equipment_model: @reservation.equipment_model - FactoryGirl.create :equipment_item, - equipment_model: @reservation.equipment_model - @res2 = - FactoryGirl.create :valid_reservation, - reserver: @user, - equipment_model: @reservation.equipment_model - res_params = { notes: '', equipment_item_id: @item.id } - reservations_params = { @reservation.id.to_s => res_params, - @res2.id.to_s => res_params } - put :checkout, user_id: @user.id, reservations: reservations_params - end - - it { expect(response).to redirect_to 'where_i_came_from' } - - it 'does not update the equipment item history' do - expect { @item.reload }.not_to change(@item, :notes) - end - end - - context 'when not all procedures are filled out' do - before do - sign_in @admin - @item = - FactoryGirl.create(:equipment_item, - equipment_model: @reservation.equipment_model) - @procedure = - FactoryGirl.create(:checkout_procedure, - equipment_model: @reservation.equipment_model) - reservations_params = - { @reservation.id.to_s => { notes: '', - equipment_item_id: @item.id, - checkout_procedures: {} } } - put :checkout, user_id: @user.id, reservations: reservations_params - end - - it { expect(response).to be_success } - - it { is_expected.to render_template(:receipt) } - - it 'assigns empty @check_in_set' do - expect(assigns(:check_in_set)).to be_empty - end - - it 'populates @check_out_set' do - expect(assigns(:check_out_set)).to eq [@reservation] - end - - it 'updates the reservation' do - expect(@reservation.checkout_handler).to be_nil - expect(@reservation.checked_out).to be_nil - expect(@reservation.equipment_item).to be_nil - @reservation.reload - expect(@reservation.checkout_handler).to be_a(User) - expect(@reservation.checked_out).to_not be_nil - expect(@reservation.equipment_item).to eq @item - expect(@reservation.notes).to include(@procedure.step) - end - end - - context 'no reservations selected' do - before do - reservations_params = {} - request.env['HTTP_REFERER'] = 'where_i_came_from' - sign_in @checkout_person - put :checkout, user_id: @user.id, reservations: reservations_params - end - it { is_expected.to set_flash } - it { expect(response).to redirect_to 'where_i_came_from' } - end - - context 'reserver has overdue reservations' do - context 'can override reservations?' do - before do - sign_in @admin - @item = - FactoryGirl.create(:equipment_item, - equipment_model: @reservation.equipment_model) - reservations_params = - { @reservation.id.to_s => { notes: '', - equipment_item_id: @item.id } } - overdue = - FactoryGirl.build(:overdue_reservation, reserver_id: @user.id) - overdue.save(validate: false) - put :checkout, user_id: @user.id, reservations: reservations_params - end - it { expect(response).to be_success } - it { is_expected.to render_template(:receipt) } - end - - context 'cannot override' do - before do - request.env['HTTP_REFERER'] = 'where_i_came_from' - sign_in @checkout_person - @item = - FactoryGirl.create(:equipment_item, - equipment_model: @reservation.equipment_model) - reservations_params = - { @reservation.id.to_s => { notes: '', - equipment_item_id: @item.id } } - overdue = - FactoryGirl.build(:overdue_reservation, reserver_id: @user.id) - overdue.save(validate: false) - put :checkout, user_id: @user.id, reservations: reservations_params - end - it { is_expected.to set_flash } - it { expect(response).to redirect_to 'where_i_came_from' } - end - end - - context 'with banned reserver' do - before(:each) do - @reservation.update_attribute(:reserver_id, @banned.id) - request.env['HTTP_REFERER'] = 'where_i_came_from' - sign_in @checkout_person - @obj = - FactoryGirl.create(:equipment_item, - equipment_model: @reservation.equipment_model) - reservations_params = - { @reservation.id.to_s => { notes: '', - equipment_item_id: @obj.id } } - put :checkout, user_id: @banned.id, reservations: reservations_params - end - - it { is_expected.to set_flash } - it { expect(response).to redirect_to root_path } - end - end - - describe '#checkin (PUT /reservations/check-in/:user_id)' do - # Access: Admins, checkout persons. - # Functionality: very complicated (almost 80 lines) - # - params[:reservations] contains a hash of - # {reservation_id => {checkin?: int, notes: str, - # (nil?) checkin_procedures: {checkin_procedure_id => int}}} - # - processes all reservations in params[:reservations] -- adds - # checkin_handler, checked_in (time); updates notes - # - stops checkin if no reservations are selected - # - overrides errors if you can and if there are some, otherwise - # redirects away - # - renders :receipt template - - before(:all) do - @user = FactoryGirl.create(:user) - @checkout_person = FactoryGirl.create(:checkout_person) - @admin = FactoryGirl.create(:admin) - @banned = FactoryGirl.create(:banned) - end - - after(:all) { User.destroy_all } - - before(:each) do - sign_in @user - @reservation = FactoryGirl.create(:valid_reservation, reserver: @user) - end - - shared_examples 'has successful checkin' do - before(:each) do - @reservation = - FactoryGirl.create(:checked_out_reservation, reserver: @user) - @item = @reservation.equipment_item - reservations_params = - { @reservation.id.to_s => { notes: '', checkin?: '1' } } - put :checkin, user_id: @user.id, reservations: reservations_params - end - - it { expect(response).to be_success } - it { is_expected.to render_template(:receipt) } - - it 'assigns empty @check_out_set' do - expect(assigns(:check_out_set)).to be_empty - end - - it 'populates @check_in_set' do - expect(assigns(:check_in_set)).to eq [@reservation] - end - - it 'updates the reservation' do - expect(@reservation.checkin_handler).to be_nil - expect(@reservation.checked_in).to be_nil - @reservation.reload - expect(@reservation.checkin_handler).to be_a(User) - expect(@reservation.checked_in).to_not be_nil - end - - it 'updates the equipment item history' do - expect { @item.reload }.to change(@item, :notes) - end - - it 'updates the reservation notes' do - expect { @reservation.reload }.to change(@reservation, :notes) - end - end - - context 'when accessed by admin' do - before(:each) do - sign_in @admin - end - - include_examples 'has successful checkin' - end - - context 'when accessed by checkout person' do - before(:each) do - sign_in @checkout_person - end - - include_examples 'has successful checkin' - end - - context 'when accessed by patron' do - before(:each) do - sign_in @user - put :checkin, user_id: @user.id - end - - include_examples 'redirected request' - end - - it_behaves_like 'inaccessible by banned user' do - before { put :checkin, user_id: @banned.id } - end - - context 'items have already been checked in' do - before do - sign_in @admin - request.env['HTTP_REFERER'] = 'where_i_came_from' - @reservation = - FactoryGirl.build(:checked_in_reservation, reserver: @user) - @reservation.save(validate: false) - reservations_params = - { @reservation.id.to_s => { notes: '', checkin?: '1' } } - put :checkin, user_id: @user.id, reservations: reservations_params - end - - it { is_expected.to set_flash } - it { expect(response).to redirect_to 'where_i_came_from' } - end - - context 'no reservations to check in' do - before do - request.env['HTTP_REFERER'] = 'where_i_came_from' - sign_in @admin - put :checkin, user_id: @user.id, reservations: {} - end - it { is_expected.to set_flash } - it { expect(response).to redirect_to 'where_i_came_from' } - end - - context 'when not all procedures are filled out' do - before do - sign_in @admin - @reservation = - FactoryGirl.create(:checked_out_reservation, reserver: @user) - @procedure = - FactoryGirl.create(:checkin_procedure, - equipment_model: @reservation.equipment_model) - reservations_params = - { @reservation.id.to_s => { notes: '', checkin?: '1', - checkin_procedures: {} } } - put :checkin, user_id: @user.id, reservations: reservations_params - end - - it { expect(response).to be_success } - it { is_expected.to render_template(:receipt) } - - it 'assigns empty @check_out_set' do - expect(assigns(:check_out_set)).to be_empty - end - - it 'populates @check_in_set' do - expect(assigns(:check_in_set)).to eq [@reservation] - end - - it 'updates the reservation' do - expect(@reservation.checkin_handler).to be_nil - expect(@reservation.checked_in).to be_nil - @reservation.reload - expect(@reservation.checkin_handler).to be_a(User) - expect(@reservation.checked_in).to_not be_nil - expect(@reservation.notes).to include(@procedure.step) - end - end - end - describe '#renew (PUT /reservations/:id/renew)' do # SMELL: this mostly tests permissions shared_examples 'can renew reservation' do @@ -1524,37 +1043,6 @@ end end - describe '#send_receipt (GET /reservations/:id/send_receipt)' do - before { mock_user_sign_in(UserMock.new(:checkout_person)) } - let!(:res) do - FactoryGirl.build_stubbed(:valid_reservation).tap do |r| - allow(Reservation).to receive(:find).with(r.id.to_s).and_return(r) - end - end - - context 'successfully emails' do - before do - allow(UserMailer).to \ - receive_message_chain(:reservation_status_update, :deliver_now) - .and_return(true) - get :send_receipt, id: res.id - end - it { is_expected.to redirect_to(res) } - it { is_expected.to set_flash[:notice] } - end - - context 'fails to send email' do - before do - allow(UserMailer).to \ - receive_message_chain(:reservation_status_update, :deliver_now) - .and_return(false) - get :send_receipt, id: res.id - end - it { is_expected.to redirect_to(res) } - it { is_expected.to set_flash[:error] } - end - end - describe '#review GET' do let!(:res) { ReservationMock.new(traits: [:findable]) } context 'as admin' do diff --git a/spec/factories/equipment_models.rb b/spec/factories/equipment_models.rb index e927042f1..26201dc0a 100644 --- a/spec/factories/equipment_models.rb +++ b/spec/factories/equipment_models.rb @@ -3,6 +3,7 @@ FactoryGirl.define do sequence(:unique_id) { |n| n } sequence(:ordering) { |n| n + 1 } + factory :equipment_model do name description 'This is a model' @@ -14,6 +15,7 @@ max_renewal_length 10 renewal_days_before_due 10 ordering + factory :restricted_equipment_model do category { FactoryGirl.create(:category, max_per_user: 1) } end diff --git a/spec/features/reservations_spec.rb b/spec/features/reservations_spec.rb index d4e9fbbf0..76e4fba6a 100644 --- a/spec/features/reservations_spec.rb +++ b/spec/features/reservations_spec.rb @@ -384,7 +384,6 @@ expect(page.current_url).to \ eq(manage_reservations_for_user_url(@user)) end - it 'succeeds when the box is checked off' do check 'terms_of_service_accepted' select @eq_model.equipment_items.first.name.to_s, diff --git a/spec/models/equipment_model_spec.rb b/spec/models/equipment_model_spec.rb index 0db92171b..fc3b3a81c 100644 --- a/spec/models/equipment_model_spec.rb +++ b/spec/models/equipment_model_spec.rb @@ -23,7 +23,6 @@ def mock_eq_model(**attrs) it { is_expected.to accept_nested_attributes_for(:checkout_procedures) } it { is_expected.to have_and_belong_to_many(:associated_equipment_models) } it { is_expected.to validate_presence_of(:name) } - it { is_expected.to validate_uniqueness_of(:name) } it { is_expected.to validate_presence_of(:description) } it { is_expected.to belong_to(:category) } it { is_expected.to validate_presence_of(:ordering) } @@ -34,6 +33,11 @@ def mock_eq_model(**attrs) end end + describe 'validations requiring persistance' do + subject { FactoryGirl.build(:equipment_model) } + it { is_expected.to validate_uniqueness_of(:name) } + end + describe 'attribute-specific validations' do shared_examples 'integer attribute' do |attr| it 'is valid with an integer value' do