diff --git a/app/controllers/publications_controller.rb b/app/controllers/publications_controller.rb index e6fb96a3..deec866b 100644 --- a/app/controllers/publications_controller.rb +++ b/app/controllers/publications_controller.rb @@ -111,7 +111,14 @@ def index end def show - @submitter = helpers.find_submitter(instance_variable_get("@#{controller_name.singularize}").id) if session[:admin] + return unless session[:admin] + + resource = controller_name.singularize + resource_instance = instance_variable_get("@#{resource}") + + raise ActiveRecord::RecordNotFound unless resource_instance + + @submitter = helpers.find_submitter(resource_instance.id) end def new diff --git a/spec/controllers/publications_controller/publications_controller_access_control_spec.rb b/spec/controllers/publications_controller/publications_controller_access_control_spec.rb new file mode 100644 index 00000000..7121b382 --- /dev/null +++ b/spec/controllers/publications_controller/publications_controller_access_control_spec.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +# The PublicationsController is responsible for handling the requests for all +# publications. Each model inherits its CRUD actions from this controller. +# We do not actually have a Publications model, so for testing purposes +# we will use the Book model, which is a child of the Publication model. +# +require 'rails_helper' + +# We are calling BooksController, a "child" of PublicationsController, so we +# need to define it here. +RSpec.describe BooksController, type: :controller do + it_behaves_like 'restricts non-logged-in users', { + 'index' => :get, + 'show' => :get, + 'new' => :get, + 'edit' => :get, + 'create' => :post, + 'update' => :put, + 'destroy' => :delete + } +end diff --git a/spec/controllers/publications_controller/publications_controller_create_spec.rb b/spec/controllers/publications_controller/publications_controller_create_spec.rb new file mode 100644 index 00000000..8d23721b --- /dev/null +++ b/spec/controllers/publications_controller/publications_controller_create_spec.rb @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +# The PublicationsController is responsible for handling the requests for all +# publications. Each model inherits its CRUD actions from this controller. +# We do not actually have a Publications model, so for testing purposes +# we will use the Book model, which is a child of the Publication model. +# +require 'rails_helper' + +# We are calling BooksController, a "child" of PublicationsController, so we +# need to define it here. +RSpec.describe BooksController, type: :controller do + let(:submitter) { FactoryBot.create(:submitter) } + let(:another_submitter) { FactoryBot.create(:submitter) } + + let(:submitter_id) { submitter.id.to_s } + let(:another_submitter_id) { another_submitter.id.to_s } + + let(:valid_attributes) do + { 'author_first_name' => %w[Test Person], + 'author_last_name' => %w[Case 2], + 'college_ids' => ['', '1', '2'], + 'uc_department' => 'Dept', + 'work_title' => 'WT', + 'other_title' => 'OT', + 'publisher' => 'Pub', + 'city' => 'City', + 'publication_date' => 'Today', + 'url' => 'www.fake.com', + 'doi' => 'doi:' } + end + + let(:invalid_attributes) do + { 'author_first_name' => ['Bad'], + 'author_last_name' => [''], + 'college_ids' => [''], + 'uc_department' => '', + 'work_title' => '', + 'other_title' => '', + 'publisher' => '', + 'city' => '', + 'publication_date' => '', + 'url' => '', + 'doi' => '' } + end + + let(:already_created_book_by_submitter) { FactoryBot.create(:book, valid_attributes.merge(submitter_id:)) } + let(:already_created_book_by_another_submitter) { FactoryBot.create(:book, valid_attributes.merge(submitter_id: another_submitter_id)) } + + describe 'POST #create' do + before do + already_created_book_by_submitter + login_as_submitter_of(already_created_book_by_submitter) + end + + context 'with valid params' do + it 'creates a new Book' do + expect do + post :create, params: { book: valid_attributes } + end.to change(Book, :count).by(1) + end + + it 'redirects to the publications path after creation' do + post :create, params: { book: valid_attributes } + expect(response).to redirect_to(publications_path) + end + end + + context 'with invalid params' do + it 'does not create a new Book' do + expect do + post :create, params: { book: invalid_attributes } + end.not_to change(Book, :count) + end + + it "redirects to the 'new' template with status 'unprocessable_entity'" do + post :create, params: { book: invalid_attributes } + expect(response).to render_template(:new) + expect(response.status).to eql 422 + end + end + end +end diff --git a/spec/controllers/publications_controller/publications_controller_destroy_spec.rb b/spec/controllers/publications_controller/publications_controller_destroy_spec.rb new file mode 100644 index 00000000..78cbb414 --- /dev/null +++ b/spec/controllers/publications_controller/publications_controller_destroy_spec.rb @@ -0,0 +1,115 @@ +# frozen_string_literal: true + +# The PublicationsController is responsible for handling the requests for all +# publications. Each model inherits its CRUD actions from this controller. +# We do not actually have a Publications model, so for testing purposes +# we will use the Book model, which is a child of the Publication model. +# +require 'rails_helper' + +# We are calling BooksController, a "child" of PublicationsController, so we +# need to define it here. +RSpec.describe BooksController, type: :controller do + let(:submitter) { FactoryBot.create(:submitter) } + let(:another_submitter) { FactoryBot.create(:submitter) } + + let(:submitter_id) { submitter.id.to_s } + let(:another_submitter_id) { another_submitter.id.to_s } + + let(:valid_attributes) do + { 'author_first_name' => %w[Test Person], + 'author_last_name' => %w[Case 2], + 'college_ids' => ['', '1', '2'], + 'uc_department' => 'Dept', + 'work_title' => 'WT', + 'other_title' => 'OT', + 'publisher' => 'Pub', + 'city' => 'City', + 'publication_date' => 'Today', + 'url' => 'www.fake.com', + 'doi' => 'doi:' } + end + + let(:already_created_book_by_submitter) { FactoryBot.create(:book, valid_attributes.merge(submitter_id:)) } + let(:already_created_book_by_another_submitter) { FactoryBot.create(:book, valid_attributes.merge(submitter_id: another_submitter_id)) } + + describe 'DELETE #destroy' do + before do + already_created_book_by_submitter + already_created_book_by_another_submitter + end + + context 'when attempting to delete books as a submitter' do + before do + login_as_submitter_of(already_created_book_by_submitter) + end + + context 'and the book belongs to the submitter' do + it 'destroys only the requested book' do + expect { Book.find(already_created_book_by_submitter.id) }.not_to raise_error + expect do + delete :destroy, params: { id: already_created_book_by_submitter.id } + end.to change(Book, :count).by(-1) + expect { Book.find(already_created_book_by_submitter.id) }.to raise_error(ActiveRecord::RecordNotFound) + end + + it 'redirects to the publications_path' do + delete :destroy, params: { id: already_created_book_by_submitter.id } + expect(response).to redirect_to(publications_path) + end + + it 'displays a success flash notice' do + delete :destroy, params: { id: already_created_book_by_submitter.id } + expect(flash[:warning]).to eql 'Book was successfully destroyed.' + end + end + + context 'and the book does not belong to the submitter' do + it 'raises a 404 error and does not destroy the requested book' do + expect { Book.find(already_created_book_by_another_submitter.id) }.not_to raise_error + initial_book_count = Book.count + expect do + delete :destroy, params: { id: already_created_book_by_another_submitter.id } + end.to raise_error(ActiveRecord::RecordNotFound) + expect(Book.count).to eql initial_book_count + expect { Book.find(already_created_book_by_another_submitter.id) }.not_to raise_error + end + end + end + + context 'when attempting to delete books as an admin' do + before do + login_as_admin_unit_test + end + + it 'destroys the requested books by either submitter' do + expect { Book.find(already_created_book_by_submitter.id) }.not_to raise_error + expect { Book.find(already_created_book_by_another_submitter.id) }.not_to raise_error + + expect do + delete :destroy, params: { id: already_created_book_by_submitter.id } + end.to change(Book, :count).by(-1) + expect do + delete :destroy, params: { id: already_created_book_by_another_submitter.id } + end.to change(Book, :count).by(-1) + + expect do + Book.find(already_created_book_by_submitter.id) + end.to raise_error(ActiveRecord::RecordNotFound) + expect do + Book.find(already_created_book_by_another_submitter.id) + end.to raise_error(ActiveRecord::RecordNotFound) + end + + it 'redirects to the publications_path' do + delete :destroy, params: { id: already_created_book_by_submitter.id } + expect(response).to redirect_to(publications_path) + end + + it 'displays a success flash notice' do + delete :destroy, params: { id: already_created_book_by_submitter.id } + expect(flash[:warning]).to eql 'Book was successfully destroyed.' + end + end + end +end diff --git a/spec/controllers/publications_controller/publications_controller_index_spec.rb b/spec/controllers/publications_controller/publications_controller_index_spec.rb new file mode 100644 index 00000000..6efaf9e8 --- /dev/null +++ b/spec/controllers/publications_controller/publications_controller_index_spec.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +# The PublicationsController is responsible for handling the requests for all +# publications. Each model inherits its CRUD actions from this controller. +# We do not actually have a Publications model, so for testing purposes +# we will use the Book model, which is a child of the Publication model. +# +require 'rails_helper' + +# We are calling BooksController, a "child" of PublicationsController, so we +# need to define it here. +RSpec.describe BooksController, type: :controller do + let(:submitter) { FactoryBot.create(:submitter) } + let(:another_submitter) { FactoryBot.create(:submitter) } + + let(:submitter_id) { submitter.id.to_s } + let(:another_submitter_id) { another_submitter.id.to_s } + + let(:valid_attributes) do + { 'author_first_name' => %w[Test Person], + 'author_last_name' => %w[Case 2], + 'college_ids' => ['', '1', '2'], + 'uc_department' => 'Dept', + 'work_title' => 'WT', + 'other_title' => 'OT', + 'publisher' => 'Pub', + 'city' => 'City', + 'publication_date' => 'Today', + 'url' => 'www.fake.com', + 'doi' => 'doi:' } + end + + let(:already_created_book_by_submitter) { FactoryBot.create(:book, valid_attributes.merge(submitter_id:)) } + let(:already_created_book_by_another_submitter) { FactoryBot.create(:book, valid_attributes.merge(submitter_id: another_submitter_id)) } + + # We have some custom behavior for the index action. Firstly, it redirects + # the user to the publications path. Then it calls a series of partials + # that are indexes of each of the publication types. + describe 'GET #index' do + before do + already_created_book_by_submitter + already_created_book_by_another_submitter + end + + context 'when a user is logged in as an admin' do + before do + login_as_admin_unit_test + get :index + end + + it 'assigns all books to @books' do + expect(assigns(:books)).to include(already_created_book_by_submitter) + expect(assigns(:books)).to include(already_created_book_by_another_submitter) + end + + it 'redirects to publications path' do + expect(response).to redirect_to(publications_path) + end + end + + context 'when a user is logged in as a submitter' do + before do + login_as_submitter_of(already_created_book_by_submitter) + get :index + end + + it 'assigns only the submitter\'s books to @books' do + expect(assigns(:books)).to eq([already_created_book_by_submitter]) + expect(assigns(:books)).not_to include(already_created_book_by_another_submitter) + end + + it 'redirects to publications path' do + expect(response).to redirect_to(publications_path) + end + end + end +end diff --git a/spec/controllers/publications_controller/publications_controller_show_spec.rb b/spec/controllers/publications_controller/publications_controller_show_spec.rb new file mode 100644 index 00000000..5dba89cd --- /dev/null +++ b/spec/controllers/publications_controller/publications_controller_show_spec.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +# The PublicationsController is responsible for handling the requests for all +# publications. Each model inherits its CRUD actions from this controller. +# We do not actually have a Publications model, so for testing purposes +# we will use the Book model, which is a child of the Publication model. +# +require 'rails_helper' + +# We are calling BooksController, a "child" of PublicationsController, so we +# need to define it here. +RSpec.describe BooksController, type: :controller do + let(:submitter) { FactoryBot.create(:submitter) } + let(:another_submitter) { FactoryBot.create(:submitter) } + let(:book) { FactoryBot.create(:book, submitter_id: submitter.id.to_s) } + let(:another_book) { FactoryBot.create(:book, submitter_id: another_submitter.id.to_s) } + + before do + book + end + + describe 'GET #show' do + context 'as an admin user' do + before do + session[:admin] = true + get :show, params: { id: book.id } + end + + it 'sets @submitter correctly' do + expect(assigns(:submitter).id.to_s).to eq(book.submitter_id) + end + + it 'renders the show template' do + expect(response).to render_template(:show) + end + + context 'when the book does not exist' do + before do + session[:admin] = true + end + + it 'responds with a 404 not found' do + expect { get :show, params: { id: 'nonexistent' } }.to raise_error(ActiveRecord::RecordNotFound) + end + end + end + + context 'as a submitter' do + before do + session[:admin] = nil + book + login_as_submitter_of(book) + end + + context 'who owns the book' do + it 'does not set @submitter' do + get :show, params: { id: book.id } + expect(assigns(:submitter)).to be_nil + end + + it 'renders the show template' do + get :show, params: { id: book.id } + expect(response).to render_template(:show) + end + end + + context 'who does not own the book' do + it 'raises a 404 not found error and does not set submitter' do + expect { get :show, params: { id: another_book.id } }.to raise_error(ActiveRecord::RecordNotFound) + expect(assigns(:submitter)).to be_nil + end + end + end + end +end diff --git a/spec/controllers/publications_controller/publications_controller_update_spec.rb b/spec/controllers/publications_controller/publications_controller_update_spec.rb new file mode 100644 index 00000000..8083d18d --- /dev/null +++ b/spec/controllers/publications_controller/publications_controller_update_spec.rb @@ -0,0 +1,130 @@ +# frozen_string_literal: true + +# The PublicationsController is responsible for handling the requests for all +# publications. Each model inherits its CRUD actions from this controller. +# We do not actually have a Publications model, so for testing purposes +# we will use the Book model, which is a child of the Publication model. +# +require 'rails_helper' + +# We are calling BooksController, a "child" of PublicationsController, so we +# need to define it here. +RSpec.describe BooksController, type: :controller do + let(:submitter) { FactoryBot.create(:submitter) } + let(:another_submitter) { FactoryBot.create(:submitter) } + + let(:submitter_id) { submitter.id.to_s } + let(:another_submitter_id) { another_submitter.id.to_s } + + let(:valid_attributes) do + { 'author_first_name' => %w[Test Person], + 'author_last_name' => %w[Case 2], + 'college_ids' => ['', '1', '2'], + 'uc_department' => 'Dept', + 'work_title' => 'WT', + 'other_title' => 'OT', + 'publisher' => 'Pub', + 'city' => 'City', + 'publication_date' => 'Today', + 'url' => 'www.fake.com', + 'doi' => 'doi:' } + end + + let(:new_attributes) do + { 'author_first_name' => %w[New Person], + 'author_last_name' => %w[Person 2] } + end + + let(:invalid_attributes) do + { 'author_first_name' => ['Bad'], + 'author_last_name' => [''] } + end + + let(:already_created_book_by_submitter) { FactoryBot.create(:book, valid_attributes.merge(submitter_id:)) } + let(:already_created_book_by_another_submitter) { FactoryBot.create(:book, valid_attributes.merge(submitter_id: another_submitter_id)) } + + describe 'PUT #update' do + before do + already_created_book_by_submitter + already_created_book_by_another_submitter + end + + context 'when attempting to update a book as a submitter' do + before do + login_as_submitter_of(already_created_book_by_submitter) + end + + context 'and the book belongs to the submitter' do + context 'and updating with valid params' do + it 'updates the requested book' do + put :update, params: { id: already_created_book_by_submitter.id, book: new_attributes } + already_created_book_by_submitter.reload + expect(already_created_book_by_submitter.author_first_name).to eql %w[New Person] + expect(already_created_book_by_submitter.author_last_name).to eql %w[Person 2] + end + + it 'redirects to the book' do + put :update, params: { id: already_created_book_by_submitter.id, book: valid_attributes } + expect(response).to redirect_to(already_created_book_by_submitter) + end + + it 'displays a success flash notice' do + put :update, params: { id: already_created_book_by_submitter.to_param, book: valid_attributes } + expect(flash[:success]).to eql 'Book was successfully updated.' + end + end + + context 'and updating with invalid params' do + it 'does not update the requested book' do + put :update, params: { id: already_created_book_by_submitter.id, book: invalid_attributes } + already_created_book_by_submitter.reload + expect(already_created_book_by_submitter.author_first_name).to eql %w[Test Person] + expect(already_created_book_by_submitter.author_last_name).to eql %w[Case 2] + end + + it "redirects to the 'edit' template with status 'unprocessable_entity'" do + put :update, params: { id: already_created_book_by_submitter.to_param, book: invalid_attributes } + expect(response).to render_template(:edit) + expect(response.status).to eql 422 + end + end + end + + context 'and the book does not belong to the submitter' do + it 'does not update the requested book and raises a 404 error' do + expect do + put :update, params: { id: already_created_book_by_another_submitter.id, book: new_attributes } + end + .to raise_error(ActiveRecord::RecordNotFound) + + already_created_book_by_another_submitter.reload + + expect(already_created_book_by_another_submitter.author_first_name).to eql %w[Test Person] + expect(already_created_book_by_another_submitter.author_last_name).to eql %w[Case 2] + end + end + end + + context 'when attempting to update a book as an admin' do + before do + login_as_admin_unit_test + end + it 'updates the requested book' do + put :update, params: { id: already_created_book_by_submitter.id, book: new_attributes } + already_created_book_by_submitter.reload + expect(already_created_book_by_submitter.author_first_name).to eql %w[New Person] + expect(already_created_book_by_submitter.author_last_name).to eql %w[Person 2] + end + + it 'redirects to the book' do + put :update, params: { id: already_created_book_by_submitter.to_param, book: valid_attributes } + expect(response).to redirect_to(already_created_book_by_submitter) + end + + it 'displays a success flash notice' do + put :update, params: { id: already_created_book_by_submitter.to_param, book: valid_attributes } + expect(flash[:success]).to eql 'Book was successfully updated.' + end + end + end +end diff --git a/spec/controllers/publications_controller_spec.rb b/spec/controllers/publications_controller_spec.rb deleted file mode 100644 index a2938ba0..00000000 --- a/spec/controllers/publications_controller_spec.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe PublicationsController, type: :controller do - let(:valid_session) { { submitter_id: 1 } } - - describe 'GET #index' do - before do - Submitter.create!( - first_name: 'First Name', - last_name: 'Last Name', - college: 2, - department: 'Department', - mailing_address: 'Mailing Address', - phone_number: '111-111-1111', - email_address: 'test@mail.uc.edu' - ) - end - - it 'returns a success response' do - get :index, session: valid_session - expect(response).to be_successful - end - end -end diff --git a/spec/features/specific_page_access/book_page_access_spec.rb b/spec/features/specific_page_access/book_page_access_spec.rb index 96d3cd18..f9a3fc2d 100644 --- a/spec/features/specific_page_access/book_page_access_spec.rb +++ b/spec/features/specific_page_access/book_page_access_spec.rb @@ -18,7 +18,7 @@ context 'when admin is logged in' do before do - login_as_admin + login_as_admin_feature_test create_book_as_new_submitter click_on("I'm Finished") end diff --git a/spec/features/specific_page_access/submitter_page_access_spec.rb b/spec/features/specific_page_access/submitter_page_access_spec.rb index c2c8d5e8..dcbcee97 100644 --- a/spec/features/specific_page_access/submitter_page_access_spec.rb +++ b/spec/features/specific_page_access/submitter_page_access_spec.rb @@ -18,7 +18,7 @@ context 'when admin is logged in' do before do - login_as_admin + login_as_admin_feature_test end it 'allows access to the submitter profile page' do diff --git a/spec/support/helpers/access_authorization_for_feature_tests.rb b/spec/support/helpers/access_authorization_for_feature_tests.rb index 48b7c85a..1fd44114 100644 --- a/spec/support/helpers/access_authorization_for_feature_tests.rb +++ b/spec/support/helpers/access_authorization_for_feature_tests.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -def login_as_admin +def login_as_admin_feature_test visit manage_path fill_in('username', with: ENV.fetch('ADMIN_USERNAME', nil)) fill_in('password', with: ENV.fetch('ADMIN_PASSWORD', nil)) diff --git a/spec/support/helpers/login_helpers_for_unit_tests.rb b/spec/support/helpers/login_helpers_for_unit_tests.rb index daf3caff..57f3241c 100644 --- a/spec/support/helpers/login_helpers_for_unit_tests.rb +++ b/spec/support/helpers/login_helpers_for_unit_tests.rb @@ -23,4 +23,10 @@ def extract_submitter_id_from(resource) def log_in_submitter(submitter_id) session[:submitter_id] = submitter_id + session.delete(:admin) +end + +def login_as_admin_unit_test + session[:admin] = true + session.delete(:submitter_id) end