vendredi 29 novembre 2019

WithClauseError, elixir unit tests match with (:ok, value}

I'm trying to write safe functional code in elixir and use unit tests to confirm my code works correctly. Here is the controller code:

def calculate_price(start_time, end_time, zone, payment_type) do
    with( {:ok} <- validate_times(start_time, end_time),
          {:ok} <- validate_zone(zone),
          {:ok} <- validate_payment_type(payment_type)
    ) do
      elapsed_minutes = div(Time.diff(end_time, start_time), 60)
      cond do
        zone == "A" && elapsed_minutes <= 15 -> {:ok, 0}
        zone == "B" && elapsed_minutes <= 90 -> {:ok, 0}
        zone == "A" && elapsed_minutes > 15 && payment_type == "hourly" -> {:ok, calc(elapsed_minutes - 15, 2, 60)}
        zone == "B" && elapsed_minutes > 90 && payment_type == "hourly" -> {:ok, calc(elapsed_minutes - 90, 1, 60)}
        zone == "A" && elapsed_minutes > 15 && payment_type == "real"   -> {:ok, calc(elapsed_minutes - 15, 0.16, 5)}
        zone == "B" && elapsed_minutes > 90 && payment_type == "real"   -> {:ok, calc(elapsed_minutes - 90, 0.08, 5)}
      end
    else
      {:error, error} -> IO.puts error
    end
  end

  defp validate_times(start_time, end_time) when end_time > start_time, do: :ok
  defp validate_times(_start_time, _end_time), do: {:error, "The start/end time is wrong"}

  defp validate_zone(zone) when zone == "A" or zone == "B", do: :ok
  defp validate_zone(_zone), do: {:error, "The zone is wrong"}

  defp validate_payment_type(payment_type) when payment_type == "hourly" or payment_type == "real", do: :ok
  defp validate_payment_type(_payment_type), do: {:error, "The payment type is wrong"}

  defp calc(minutes_to_pay, price_per_minutes, minutes_per_price_increment) do
    cond do
      rem(minutes_to_pay, minutes_per_price_increment) > 0 ->
        (div(minutes_to_pay, minutes_per_price_increment) + 1) * price_per_minutes
      true -> div(minutes_to_pay, minutes_per_price_increment) * price_per_minutes
    end
  end

controller_test code:

test "calculate price; zone: B, paymentType: real" do
    # 4 hours and 30 minute difference
    startTime = ~T[12:00:00.000]
    endTime = ~T[16:30:00.000]
    zone = "B"
    paymentType = "real"

   assert {:ok, 2.88} == FindmyparkingWeb.ReservationController.calculate_price(startTime, endTime, zone, paymentType)

  end

For this code, I'm tring to validate the correct parameters are passed in so that on the happy path of my code I return a result of {:ok, value}. If the parameters are wrong I want to know why the error happened. Currently I am just printing to command line, but eventually I want to return {:error, reason}. Just putting {:error, error} in the else clause caused a different error.

The result of the test case is: ** (WithClauseError) no with clause matching: :ok

What I think this means is that my calculate_price function is returning {:ok}. I don't understand why the value inside the with clause is being returned and not the values in the do or else clause!

My elixir version is 1.9.1.

Aucun commentaire:

Enregistrer un commentaire