diff --git a/Gemfile b/Gemfile index 00f9df8..16e60c3 100755 --- a/Gemfile +++ b/Gemfile @@ -73,6 +73,8 @@ group :test do gem "selenium-webdriver" # For controller testing helpers gem "rails-controller-testing" + # For mocking and stubbing + gem "mocha" end gem "devise", "~> 4.9" diff --git a/test/controllers/orders_controller_test.rb b/test/controllers/orders_controller_test.rb new file mode 100644 index 0000000..f1425a0 --- /dev/null +++ b/test/controllers/orders_controller_test.rb @@ -0,0 +1,329 @@ +require "test_helper" + +class OrdersControllerTest < ActionDispatch::IntegrationTest + def setup + @user = User.create!( + email: "test@example.com", + password: "password123", + password_confirmation: "password123" + ) + + @event = Event.create!( + name: "Test Event", + slug: "test-event", + description: "A valid description for the test event that is long enough", + latitude: 48.8566, + longitude: 2.3522, + venue_name: "Test Venue", + venue_address: "123 Test Street", + user: @user, + start_time: 1.week.from_now, + end_time: 1.week.from_now + 3.hours, + state: :published + ) + + @ticket_type = TicketType.create!( + name: "General Admission", + description: "General admission tickets with full access to the event", + price_cents: 2500, + quantity: 100, + sale_start_at: Time.current, + sale_end_at: @event.start_time - 1.hour, + requires_id: false, + event: @event + ) + + @order = Order.create!( + user: @user, + event: @event, + status: "draft", + total_amount_cents: 2500 + ) + + @ticket = Ticket.create!( + order: @order, + ticket_type: @ticket_type, + status: "draft", + first_name: "John", + last_name: "Doe" + ) + + sign_in @user + end + + # === Authentication Tests === + + test "should require authentication for all actions" do + sign_out @user + + get event_order_new_path(@event.slug, @event.id) + assert_redirected_to new_user_session_path + + post event_order_create_path(@event.slug, @event.id) + assert_redirected_to new_user_session_path + + get order_path(@order) + assert_redirected_to new_user_session_path + + get checkout_order_path(@order) + assert_redirected_to new_user_session_path + end + + # === New Action Tests === + + test "should get new with valid event" do + # Mock session to have cart data + @request.session[:pending_cart] = { + @ticket_type.id.to_s => { "quantity" => "2" } + } + + get event_order_new_path(@event.slug, @event.id) + assert_response :success + + # Should assign tickets_needing_names + tickets_needing_names = assigns(:tickets_needing_names) + assert_not_nil tickets_needing_names + assert_equal 2, tickets_needing_names.size + assert_equal @ticket_type.id, tickets_needing_names.first[:ticket_type_id] + end + + test "new should redirect when cart is empty" do + # Clear any cart data + @request.session[:pending_cart] = {} + + get event_order_new_path(@event.slug, @event.id) + assert_redirected_to event_path(@event.slug, @event) + assert_match /sélectionner vos billets/, flash[:alert] + end + + test "new should redirect when no cart data" do + # No cart data in session + @request.session.delete(:pending_cart) + + get event_order_new_path(@event.slug, @event.id) + assert_redirected_to event_path(@event.slug, @event) + assert_match /sélectionner vos billets/, flash[:alert] + end + + # === Create Action Tests === + + test "should create order with valid ticket data" do + @request.session[:pending_cart] = { + @ticket_type.id.to_s => { "quantity" => "1" } + } + + assert_difference "Order.count", 1 do + assert_difference "Ticket.count", 1 do + post event_order_create_path(@event.slug, @event.id), params: { + tickets_attributes: { + "0" => { + ticket_type_id: @ticket_type.id, + first_name: "Jane", + last_name: "Smith" + } + } + } + end + end + + new_order = Order.last + assert_equal "draft", new_order.status + assert_equal @user, new_order.user + assert_equal @event, new_order.event + assert_equal @ticket_type.price_cents, new_order.total_amount_cents + + assert_redirected_to checkout_order_path(new_order) + assert_equal new_order.id, session[:draft_order_id] + assert_nil session[:pending_cart] + end + + test "create should redirect when cart is empty" do + @request.session[:pending_cart] = {} + + assert_no_difference "Order.count" do + post event_order_create_path(@event.slug, @event.id) + end + + assert_redirected_to event_path(@event.slug, @event) + assert_match /Aucun billet sélectionné/, flash[:alert] + end + + test "create should handle missing ticket names" do + @request.session[:pending_cart] = { + @ticket_type.id.to_s => { "quantity" => "1" } + } + + post event_order_create_path(@event.slug, @event.id), params: { + tickets_attributes: { + "0" => { + ticket_type_id: @ticket_type.id, + first_name: "", + last_name: "" + } + } + } + + # Should redirect back to new order page + assert_redirected_to event_order_new_path(@event.slug, @event.id) + assert_match /Aucun billet valide créé/, flash[:alert] + end + + # === Show Action Tests === + + test "should show order" do + get order_path(@order) + assert_response :success + + order = assigns(:order) + assert_equal @order, order + + tickets = assigns(:tickets) + assert_includes tickets, @ticket + end + + test "should not show other user's order" do + other_user = User.create!( + email: "other@example.com", + password: "password123", + password_confirmation: "password123" + ) + + other_order = Order.create!( + user: other_user, + event: @event, + status: "draft", + total_amount_cents: 2500 + ) + + get order_path(other_order) + # Should redirect to dashboard/root with alert + assert_redirected_to root_path + assert_match /Commande non trouvée/, flash[:alert] + end + + # === Checkout Action Tests === + + test "should show checkout page" do + get checkout_order_path(@order) + assert_response :success + + order = assigns(:order) + assert_equal @order, order + + tickets = assigns(:tickets) + assert_includes tickets, @ticket + + total_amount = assigns(:total_amount) + assert_equal @order.total_amount_cents, total_amount + + expiring_soon = assigns(:expiring_soon) + assert_not_nil expiring_soon + end + + test "checkout should redirect expired order" do + # Make order expired + @order.update!(expires_at: 1.hour.ago) + + get checkout_order_path(@order) + assert_redirected_to event_path(@event.slug, @event) + assert_match /commande a expiré/, flash[:alert] + + @order.reload + assert_equal "expired", @order.status + end + + # === Retry Payment Tests === + + test "should allow retry payment for retryable order" do + post retry_payment_order_path(@order) + assert_redirected_to checkout_order_path(@order) + end + + test "should not allow retry payment for non-retryable order" do + # Make order non-retryable (too many attempts) + @order.update!(payment_attempts: Order::MAX_PAYMENT_ATTEMPTS) + + post retry_payment_order_path(@order) + assert_redirected_to event_path(@event.slug, @event) + assert_match /ne peut plus être payée/, flash[:alert] + end + + # === Increment Payment Attempt Tests === + + test "should increment payment attempt via AJAX" do + initial_attempts = @order.payment_attempts + + post increment_payment_attempt_order_path(@order), xhr: true + + assert_response :success + + response_data = JSON.parse(@response.body) + assert response_data["success"] + assert_equal initial_attempts + 1, response_data["attempts"] + + @order.reload + assert_equal initial_attempts + 1, @order.payment_attempts + assert_not_nil @order.last_payment_attempt_at + end + + # === Payment Success Tests (simplified) === + + test "payment_success should redirect when Stripe not configured" do + # Mock the config to return nil + Rails.application.config.stripe = { secret_key: nil } + + get order_payment_success_path, params: { session_id: "test_session" } + assert_redirected_to root_path + assert_match /système de paiement n'est pas correctement configuré/, flash[:alert] + end + + # === Payment Cancel Tests === + + test "payment_cancel should redirect to checkout if order can retry" do + @request.session[:draft_order_id] = @order.id + + get order_payment_cancel_path + assert_redirected_to checkout_order_path(@order) + assert_match /paiement a été annulé.*réessayer/, flash[:alert] + end + + test "payment_cancel should redirect to root if no order in session" do + @request.session.delete(:draft_order_id) + + get order_payment_cancel_path + assert_redirected_to root_path + assert_match /paiement a été annulé/, flash[:alert] + end + + # === Error Handling Tests === + + test "should handle non-existent event in new" do + get event_order_new_path(@event.slug, 99999) + assert_redirected_to events_path + assert_match /Événement non trouvé/, flash[:alert] + end + + test "should handle non-existent event in create" do + post event_order_create_path(@event.slug, 99999) + assert_redirected_to events_path + assert_match /Événement non trouvé/, flash[:alert] + end + + test "should handle non-existent order" do + get order_path(99999) + assert_redirected_to root_path + assert_match /Commande non trouvée/, flash[:alert] + end + + # === Route Helper Tests === + + test "should have correct route helpers" do + # Test that the route helpers exist and work + assert_not_nil event_order_new_path(@event.slug, @event.id) + assert_not_nil event_order_create_path(@event.slug, @event.id) + assert_not_nil order_path(@order) + assert_not_nil checkout_order_path(@order) + assert_not_nil retry_payment_order_path(@order) + assert_not_nil increment_payment_attempt_order_path(@order) + end +end \ No newline at end of file diff --git a/test/test_helper.rb b/test/test_helper.rb index 9dbf7a4..dd0c2bd 100755 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -2,6 +2,7 @@ ENV["RAILS_ENV"] ||= "test" require_relative "../config/environment" require "rails/test_help" require "minitest/reporters" +require "mocha/minitest" Minitest::Reporters.use! # Minitest::Reporters.use!(Minitest::Reporters::SpecReporter.new, color: true) @@ -18,3 +19,7 @@ module ActiveSupport # Add more helper methods to be used by all tests here... end end + +class ActionDispatch::IntegrationTest + include Devise::Test::IntegrationHelpers +end