From 2d8abe495570f5b96e1b879b28543c0ce99a893c Mon Sep 17 00:00:00 2001 From: Melony Franchini Date: Sun, 22 Oct 2023 16:42:43 -0600 Subject: [PATCH 1/2] Fix: Update Loan#create & #update --- .../api/v1/users/loans_controller.rb | 39 ++++-- config/routes.rb | 2 +- .../api/v1/users/loans_request_spec.rb | 123 ++++++++++++++---- 3 files changed, 129 insertions(+), 35 deletions(-) diff --git a/app/controllers/api/v1/users/loans_controller.rb b/app/controllers/api/v1/users/loans_controller.rb index 8e0f508..9093726 100644 --- a/app/controllers/api/v1/users/loans_controller.rb +++ b/app/controllers/api/v1/users/loans_controller.rb @@ -6,6 +6,7 @@ def create loan = Loan.new(owner_id: @owner.id, puzzle_id: @puzzle.id, borrower_id: @borrower.id) if loan.save + loan.puzzle.update(status: 1) render json: LoanSerializer.new(loan), status: :created #201 else render json: { error: "Unable to create loan" }, status: :unprocessable_entity #422 @@ -13,20 +14,38 @@ def create end def update - user = User.find(params[:id]) loan = Loan.find(params[:loan_id]) - if params[:action_type] == 'deny' || params[:action_type] == 'withdraw' - if loan.update(status: 2) - render json: LoanSerializer.new(loan), status: 200 - else - render json: { error: "Unable to update loan status" }, status: 422 - end - elsif params[:action_type] == 'invalid_action' - render json: { error: "Invalid action" }, status: 422 + if params[:action_type] == 'accept' + loan.update(status: 1) + loan.puzzle.update(status: 2) + render json: LoanSerializer.new(loan), status: 200 + + elsif params[:action_type] == 'withdraw' + loan.update(status: 2) + loan.puzzle.update(status: 0) + render json: LoanSerializer.new(loan), status: 200 + + elsif params[:action_type] == 'deny' + loan.update(status: 2) + loan.puzzle.update(status: 2) + render json: LoanSerializer.new(loan), status: 200 + + elsif params[:action_type] == 'close' + loan.update(status: 3) + loan.puzzle.update(status: 2) + render json: LoanSerializer.new(loan), status: 200 + else - render json: { error: "Unable to update loan status" }, status: 422 + render json: { error: "Unable to update loan status" }, status: 422 end + + + # elsif params[:action_type] == 'invalid_action' + # render json: { error: "Invalid action" }, status: 422 + # else + # render json: { error: "Unable to update loan status" }, status: 422 + # end end private diff --git a/config/routes.rb b/config/routes.rb index ce95d73..a525af2 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -21,7 +21,7 @@ patch '/users/:user_id/puzzles/:puzzle_id', to: 'users/puzzles#update' post '/users/:user_id/loans', to: 'users/loans#create' - patch '/users/:id/loans/:loan_id', to: 'users/loans#update' + patch '/users/:user_id/loans/:loan_id', to: 'users/loans#update' end end end diff --git a/spec/requests/api/v1/users/loans_request_spec.rb b/spec/requests/api/v1/users/loans_request_spec.rb index 41a272e..e3df7cc 100644 --- a/spec/requests/api/v1/users/loans_request_spec.rb +++ b/spec/requests/api/v1/users/loans_request_spec.rb @@ -2,23 +2,29 @@ RSpec.describe 'Users/LoansController' do describe '#create' do - let(:user_1) { create(:user, id: 1) } - let(:user_2) { create(:user, id: 2) } - let(:puzzle_1) { create(:puzzle, user: user_1) } - let(:loan_1) { create(:loan, owner: user_1, borrower: user_2) } + # let(:user_1) { create(:user, id: 1) } + # let(:user_2) { create(:user, id: 2) } + # let(:puzzle_1) { create(:puzzle, user: user_1) } + # let(:loan_1) { create(:loan, owner: user_1, borrower: user_2) } context "when successful" do it 'creates a new loan' do user_1 = create(:user, id: 1) user_2 = create(:user, id: 2) puzzle_1 = create(:puzzle, user: user_1) - - post "/api/v1/users/#{user_1.id}/loans", params: { + + expect(puzzle_1.status).to eq("Available") + + loan_request = { puzzle_id: puzzle_1.id, borrower_id: user_2.id } + headers = { 'CONTENT_TYPE' => 'application/json' } + post "/api/v1/users/#{user_1.id}/loans", headers:, params: JSON.generate(loan_request) + expect(response).to have_http_status(201) + expect(puzzle_1.status).to eq("Pending") parsed_data = JSON.parse(response.body, symbolize_names: true) @@ -106,13 +112,36 @@ let(:loan_2) { create(:loan, owner: user_1, borrower: user_2, puzzle: puzzle_1) } context "when successful" do - it 'updates loan status to 2 when status is deny' do - patch "/api/v1/users/#{user_1.id}/loans/#{loan_2.id}", params: { - id: user_1.id, loan_id: loan_2.id, action_type: 'deny' + it 'updates loan status to Accepted when loan action type is Accept' do + # FE will send us an new param called action_type to know which button the user clicked + # action types: "accept," "deny", "withdraw", or "close" + loan_update = { + action_type: "accept" + } + + expect(puzzle_1.status).to eq("Pending") + + headers = { 'CONTENT_TYPE' => 'application/json' } + patch "/api/v1/users/#{user_1.id}/loans/#{loan_2.id}", headers:, params: JSON.generate(loan_update) + + expect(response).to have_http_status(200) + expect(puzzle_1.status).to eq("Not Available") + + parsed_data = JSON.parse(response.body, symbolize_names: true) + expect(parsed_data[:data][:attributes][:status]).to eq("Accepted") + end + + it 'updates loan status to Cancelled when loan action type is Withdraw' do + # FE will send us an new param called action_type to know which button the user clicked + # action types: "accept," "deny", "withdraw", or "close" + loan_update = { + action_type: "withdraw", } + headers = { 'CONTENT_TYPE' => 'application/json' } + patch "/api/v1/users/#{user_1.id}/loans/#{loan_2.id}", headers:, params: JSON.generate(loan_update) + expect(response).to have_http_status(200) - expect(loan_2.reload.status).to eq('Cancelled') parsed_data = JSON.parse(response.body, symbolize_names: true) @@ -123,19 +152,44 @@ expect(parsed_data[:data][:attributes]).to be_a(Hash) expect(parsed_data[:data][:attributes].keys).to eq([:owner_id, :borrower_id, :puzzle_id, :status]) - expect(parsed_data[:data][:attributes][:owner_id]).to eq(loan_2.owner_id) - expect(parsed_data[:data][:attributes][:borrower_id]).to eq(loan_2.borrower_id) - expect(parsed_data[:data][:attributes][:puzzle_id]).to eq(loan_2.puzzle_id) - expect(parsed_data[:data][:attributes][:status]).to eq(loan_2.status) + expect(parsed_data[:data][:attributes][:status]).to eq("Cancelled") end - it 'updates loan status to 2 when status is withdraw' do - patch "/api/v1/users/#{user_1.id}/loans/#{loan_2.id}", params: { - id: user_1.id, loan_id: loan_2.id, action_type: 'withdraw' + it 'updates loan status to Cancelled when loan action type is Deny' do + # FE will send us an new param called action_type to know which button the user clicked + # action types: "accept," "deny", "withdraw", or "close" + loan_update = { + action_type: "deny", + } + + headers = { 'CONTENT_TYPE' => 'application/json' } + patch "/api/v1/users/#{user_1.id}/loans/#{loan_2.id}", headers:, params: JSON.generate(loan_update) + + expect(response).to have_http_status(200) + + parsed_data = JSON.parse(response.body, symbolize_names: true) + + expect(parsed_data).to be_a(Hash) + expect(parsed_data.keys).to eq([:data]) + expect(parsed_data[:data]).to be_an(Hash) + expect(parsed_data[:data].keys).to eq([:id, :type, :attributes]) + + expect(parsed_data[:data][:attributes]).to be_a(Hash) + expect(parsed_data[:data][:attributes].keys).to eq([:owner_id, :borrower_id, :puzzle_id, :status]) + expect(parsed_data[:data][:attributes][:status]).to eq("Cancelled") + end + + it 'updates loan status to Closed when loan action type is Close' do + # FE will send us an new param called action_type to know which button the user clicked + # action types: "accept," "deny", "withdraw", or "close" + loan_update = { + action_type: "close", } + headers = { 'CONTENT_TYPE' => 'application/json' } + patch "/api/v1/users/#{user_1.id}/loans/#{loan_2.id}", headers:, params: JSON.generate(loan_update) + expect(response).to have_http_status(200) - expect(loan_2.reload.status).to eq('Cancelled') parsed_data = JSON.parse(response.body, symbolize_names: true) @@ -146,15 +200,36 @@ expect(parsed_data[:data][:attributes]).to be_a(Hash) expect(parsed_data[:data][:attributes].keys).to eq([:owner_id, :borrower_id, :puzzle_id, :status]) - expect(parsed_data[:data][:attributes][:owner_id]).to eq(loan_2.owner_id) - expect(parsed_data[:data][:attributes][:borrower_id]).to eq(loan_2.borrower_id) - expect(parsed_data[:data][:attributes][:puzzle_id]).to eq(loan_2.puzzle_id) - expect(parsed_data[:data][:attributes][:status]).to eq(loan_2.status) + expect(parsed_data[:data][:attributes][:status]).to eq("Closed") end + + + # it 'updates loan status to 2 when status is withdraw' do + # patch "/api/v1/users/#{user_1.id}/loans/#{loan_2.id}", params: { + # id: user_1.id, loan_id: loan_2.id, action_type: 'withdraw' + # } + + # expect(response).to have_http_status(200) + # expect(loan_2.reload.status).to eq('Cancelled') + + # parsed_data = JSON.parse(response.body, symbolize_names: true) + + # expect(parsed_data).to be_a(Hash) + # expect(parsed_data.keys).to eq([:data]) + # expect(parsed_data[:data]).to be_an(Hash) + # expect(parsed_data[:data].keys).to eq([:id, :type, :attributes]) + + # expect(parsed_data[:data][:attributes]).to be_a(Hash) + # expect(parsed_data[:data][:attributes].keys).to eq([:owner_id, :borrower_id, :puzzle_id, :status]) + # expect(parsed_data[:data][:attributes][:owner_id]).to eq(loan_2.owner_id) + # expect(parsed_data[:data][:attributes][:borrower_id]).to eq(loan_2.borrower_id) + # expect(parsed_data[:data][:attributes][:puzzle_id]).to eq(loan_2.puzzle_id) + # expect(parsed_data[:data][:attributes][:status]).to eq(loan_2.status) + # end end context "when NOT successful" do - it 'returns an error message when an action type is invalid' do + xit 'returns an error message when an action type is invalid' do patch "/api/v1/users/#{user_1.id}/loans/#{loan_2.id}", params: { id: user_1.id, loan_id: loan_2.id, action_type: 'invalid_action' } @@ -172,7 +247,7 @@ allow_any_instance_of(Loan).to receive(:update).and_return(false) end - it 'returns an error message when loan update fails' do + xit 'returns an error message when loan update fails' do patch "/api/v1/users/#{user_1.id}/loans/#{loan_2.id}", params: { id: user_1.id, loan_id: loan_2.id, action_type: 'deny' } From cc153f4bd493b0a939795414397ec08f44f75d0b Mon Sep 17 00:00:00 2001 From: Melony Franchini Date: Sun, 22 Oct 2023 17:26:18 -0600 Subject: [PATCH 2/2] fix: Update UserLoans#create & #update tests & actions --- .../api/v1/users/loans_controller.rb | 17 +- .../api/v1/users/loans_request_spec.rb | 166 +++++++----------- 2 files changed, 67 insertions(+), 116 deletions(-) diff --git a/app/controllers/api/v1/users/loans_controller.rb b/app/controllers/api/v1/users/loans_controller.rb index 9093726..c49ea68 100644 --- a/app/controllers/api/v1/users/loans_controller.rb +++ b/app/controllers/api/v1/users/loans_controller.rb @@ -4,7 +4,7 @@ class Api::V1::Users::LoansController < ApplicationController def create loan = Loan.new(owner_id: @owner.id, puzzle_id: @puzzle.id, borrower_id: @borrower.id) - + if loan.save loan.puzzle.update(status: 1) render json: LoanSerializer.new(loan), status: :created #201 @@ -20,32 +20,21 @@ def update loan.update(status: 1) loan.puzzle.update(status: 2) render json: LoanSerializer.new(loan), status: 200 - elsif params[:action_type] == 'withdraw' loan.update(status: 2) loan.puzzle.update(status: 0) render json: LoanSerializer.new(loan), status: 200 - elsif params[:action_type] == 'deny' loan.update(status: 2) loan.puzzle.update(status: 2) render json: LoanSerializer.new(loan), status: 200 - elsif params[:action_type] == 'close' loan.update(status: 3) - loan.puzzle.update(status: 2) + loan.puzzle.update(status: 0) render json: LoanSerializer.new(loan), status: 200 - else - render json: { error: "Unable to update loan status" }, status: 422 + render json: { error: "Unable to update loan status" }, status: 422 end - - - # elsif params[:action_type] == 'invalid_action' - # render json: { error: "Invalid action" }, status: 422 - # else - # render json: { error: "Unable to update loan status" }, status: 422 - # end end private diff --git a/spec/requests/api/v1/users/loans_request_spec.rb b/spec/requests/api/v1/users/loans_request_spec.rb index e3df7cc..882c049 100644 --- a/spec/requests/api/v1/users/loans_request_spec.rb +++ b/spec/requests/api/v1/users/loans_request_spec.rb @@ -2,11 +2,6 @@ RSpec.describe 'Users/LoansController' do describe '#create' do - # let(:user_1) { create(:user, id: 1) } - # let(:user_2) { create(:user, id: 2) } - # let(:puzzle_1) { create(:puzzle, user: user_1) } - # let(:loan_1) { create(:loan, owner: user_1, borrower: user_2) } - context "when successful" do it 'creates a new loan' do user_1 = create(:user, id: 1) @@ -23,8 +18,8 @@ headers = { 'CONTENT_TYPE' => 'application/json' } post "/api/v1/users/#{user_1.id}/loans", headers:, params: JSON.generate(loan_request) + expect(Loan.last.puzzle.status).to eq("Pending") expect(response).to have_http_status(201) - expect(puzzle_1.status).to eq("Pending") parsed_data = JSON.parse(response.body, symbolize_names: true) @@ -106,156 +101,123 @@ end describe '#update' do - let(:user_1) { create(:user) } - let(:user_2) { create(:user) } - let(:puzzle_1) { create(:puzzle) } - let(:loan_2) { create(:loan, owner: user_1, borrower: user_2, puzzle: puzzle_1) } + before(:each) do + @user_1 = create(:user, id: 1) + @user_2 = create(:user, id: 2) + @puzzle_1 = create(:puzzle, user: @user_1) + + loan_request = { + puzzle_id: @puzzle_1.id, + borrower_id: @user_2.id + } + + headers = { 'CONTENT_TYPE' => 'application/json' } + post "/api/v1/users/#{@user_1.id}/loans", headers:, params: JSON.generate(loan_request) + + @current_loan = Loan.last + end context "when successful" do + #REFACTOR: the following tests test both the puzzle status and the loan status changes + it 'updates loan status to Accepted when loan action type is Accept' do - # FE will send us an new param called action_type to know which button the user clicked - # action types: "accept," "deny", "withdraw", or "close" - loan_update = { - action_type: "accept" - } + expect(@current_loan.puzzle.status).to eq("Pending") - expect(puzzle_1.status).to eq("Pending") + loan_update = { action_type: "accept" } headers = { 'CONTENT_TYPE' => 'application/json' } - patch "/api/v1/users/#{user_1.id}/loans/#{loan_2.id}", headers:, params: JSON.generate(loan_update) + patch "/api/v1/users/#{@user_1.id}/loans/#{@current_loan.id}", headers:, params: JSON.generate(loan_update) - expect(response).to have_http_status(200) - expect(puzzle_1.status).to eq("Not Available") + @current_loan.reload + expect(@current_loan.puzzle.status).to eq("Not Available") + expect(response).to have_http_status(200) parsed_data = JSON.parse(response.body, symbolize_names: true) expect(parsed_data[:data][:attributes][:status]).to eq("Accepted") end it 'updates loan status to Cancelled when loan action type is Withdraw' do - # FE will send us an new param called action_type to know which button the user clicked - # action types: "accept," "deny", "withdraw", or "close" - loan_update = { - action_type: "withdraw", - } + expect(@current_loan.puzzle.status).to eq("Pending") + + loan_update = { action_type: "withdraw" } headers = { 'CONTENT_TYPE' => 'application/json' } - patch "/api/v1/users/#{user_1.id}/loans/#{loan_2.id}", headers:, params: JSON.generate(loan_update) + patch "/api/v1/users/#{@user_1.id}/loans/#{@current_loan.id}", headers:, params: JSON.generate(loan_update) - expect(response).to have_http_status(200) + @current_loan.reload + expect(@current_loan.puzzle.status).to eq("Available") + expect(response).to have_http_status(200) parsed_data = JSON.parse(response.body, symbolize_names: true) - - expect(parsed_data).to be_a(Hash) - expect(parsed_data.keys).to eq([:data]) - expect(parsed_data[:data]).to be_an(Hash) - expect(parsed_data[:data].keys).to eq([:id, :type, :attributes]) - - expect(parsed_data[:data][:attributes]).to be_a(Hash) - expect(parsed_data[:data][:attributes].keys).to eq([:owner_id, :borrower_id, :puzzle_id, :status]) expect(parsed_data[:data][:attributes][:status]).to eq("Cancelled") end it 'updates loan status to Cancelled when loan action type is Deny' do - # FE will send us an new param called action_type to know which button the user clicked - # action types: "accept," "deny", "withdraw", or "close" - loan_update = { - action_type: "deny", - } + expect(@current_loan.puzzle.status).to eq("Pending") + + loan_update = { action_type: "deny" } headers = { 'CONTENT_TYPE' => 'application/json' } - patch "/api/v1/users/#{user_1.id}/loans/#{loan_2.id}", headers:, params: JSON.generate(loan_update) + patch "/api/v1/users/#{@user_1.id}/loans/#{@current_loan.id}", headers:, params: JSON.generate(loan_update) - expect(response).to have_http_status(200) + @current_loan.reload + expect(@current_loan.puzzle.status).to eq("Not Available") + expect(response).to have_http_status(200) parsed_data = JSON.parse(response.body, symbolize_names: true) - - expect(parsed_data).to be_a(Hash) - expect(parsed_data.keys).to eq([:data]) - expect(parsed_data[:data]).to be_an(Hash) - expect(parsed_data[:data].keys).to eq([:id, :type, :attributes]) - - expect(parsed_data[:data][:attributes]).to be_a(Hash) - expect(parsed_data[:data][:attributes].keys).to eq([:owner_id, :borrower_id, :puzzle_id, :status]) expect(parsed_data[:data][:attributes][:status]).to eq("Cancelled") end it 'updates loan status to Closed when loan action type is Close' do - # FE will send us an new param called action_type to know which button the user clicked - # action types: "accept," "deny", "withdraw", or "close" - loan_update = { - action_type: "close", - } + expect(@current_loan.puzzle.status).to eq("Pending") + + loan_update = { action_type: "close" } headers = { 'CONTENT_TYPE' => 'application/json' } - patch "/api/v1/users/#{user_1.id}/loans/#{loan_2.id}", headers:, params: JSON.generate(loan_update) + patch "/api/v1/users/#{@user_1.id}/loans/#{@current_loan.id}", headers:, params: JSON.generate(loan_update) - expect(response).to have_http_status(200) + @current_loan.reload + expect(@current_loan.puzzle.status).to eq("Available") + expect(response).to have_http_status(200) parsed_data = JSON.parse(response.body, symbolize_names: true) - - expect(parsed_data).to be_a(Hash) - expect(parsed_data.keys).to eq([:data]) - expect(parsed_data[:data]).to be_an(Hash) - expect(parsed_data[:data].keys).to eq([:id, :type, :attributes]) - - expect(parsed_data[:data][:attributes]).to be_a(Hash) - expect(parsed_data[:data][:attributes].keys).to eq([:owner_id, :borrower_id, :puzzle_id, :status]) expect(parsed_data[:data][:attributes][:status]).to eq("Closed") end + end + context "when NOT successful" do + it 'returns an error message when an action type is invalid' do + expect(@current_loan.puzzle.status).to eq("Pending") - # it 'updates loan status to 2 when status is withdraw' do - # patch "/api/v1/users/#{user_1.id}/loans/#{loan_2.id}", params: { - # id: user_1.id, loan_id: loan_2.id, action_type: 'withdraw' - # } - - # expect(response).to have_http_status(200) - # expect(loan_2.reload.status).to eq('Cancelled') - - # parsed_data = JSON.parse(response.body, symbolize_names: true) - - # expect(parsed_data).to be_a(Hash) - # expect(parsed_data.keys).to eq([:data]) - # expect(parsed_data[:data]).to be_an(Hash) - # expect(parsed_data[:data].keys).to eq([:id, :type, :attributes]) + loan_update = { action_type: "puzzled" } - # expect(parsed_data[:data][:attributes]).to be_a(Hash) - # expect(parsed_data[:data][:attributes].keys).to eq([:owner_id, :borrower_id, :puzzle_id, :status]) - # expect(parsed_data[:data][:attributes][:owner_id]).to eq(loan_2.owner_id) - # expect(parsed_data[:data][:attributes][:borrower_id]).to eq(loan_2.borrower_id) - # expect(parsed_data[:data][:attributes][:puzzle_id]).to eq(loan_2.puzzle_id) - # expect(parsed_data[:data][:attributes][:status]).to eq(loan_2.status) - # end - end + headers = { 'CONTENT_TYPE' => 'application/json' } + patch "/api/v1/users/#{@user_1.id}/loans/#{@current_loan.id}", headers:, params: JSON.generate(loan_update) - context "when NOT successful" do - xit 'returns an error message when an action type is invalid' do - patch "/api/v1/users/#{user_1.id}/loans/#{loan_2.id}", params: { - id: user_1.id, loan_id: loan_2.id, action_type: 'invalid_action' - } + @current_loan.reload + expect(@current_loan.puzzle.status).to eq("Pending") expect(response).to have_http_status(422) - parsed_error_data = JSON.parse(response.body, symbolize_names: true) - expect(parsed_error_data).to be_a(Hash) expect(parsed_error_data.keys).to eq([:error]) - expect(parsed_error_data[:error]).to eq("Invalid action") + expect(parsed_error_data[:error]).to eq("Unable to update loan status") end - before do + it 'returns an error message when loan update fails' do allow_any_instance_of(Loan).to receive(:update).and_return(false) - end + expect(@current_loan.puzzle.status).to eq("Pending") - xit 'returns an error message when loan update fails' do - patch "/api/v1/users/#{user_1.id}/loans/#{loan_2.id}", params: { - id: user_1.id, loan_id: loan_2.id, action_type: 'deny' - } + loan_update = { action_type: "accepted" } - expect(response).to have_http_status(422) + headers = { 'CONTENT_TYPE' => 'application/json' } + patch "/api/v1/users/#{@user_1.id}/loans/#{@current_loan.id}", headers:, params: JSON.generate(loan_update) - parsed_error_data = JSON.parse(response.body, symbolize_names: true) + @current_loan.reload + expect(@current_loan.puzzle.status).to eq("Pending") + expect(response).to have_http_status(422) + parsed_error_data = JSON.parse(response.body, symbolize_names: true) expect(parsed_error_data).to be_a(Hash) expect(parsed_error_data.keys).to eq([:error]) expect(parsed_error_data[:error]).to eq("Unable to update loan status")