Skip to content

Commit

Permalink
test(Predictions): Added the mox for predictions (#2036)
Browse files Browse the repository at this point in the history
* test(Predictions): Added the mox for predictions

* Added behaviour

* Configured predictions repo, and updated predictions controller tests

* Updated the prediction schedule and corresponding test

* Updated realtime schedules to use prediction repo

* Added updated the tests for transit near me

* Updated the vehicle marker channel to use new predictions repo

* Updated the finder_api to use the predictions repo pattern

* Added new repo model to green

* Added test pattern to trip_info

* Removed function injection from green

* Removed predictions injection

* Alphabatized the configuration

* Cleaned up predictions_test file

* Fixed spacing

* Moved repo mocks to own section

* Added prediction factory

* Added missing file

* Added faker to predictions test

* Added more factory usage for predictions and some random data

* Remove redundant defaults
  • Loading branch information
kotva006 committed May 15, 2024
1 parent c25b58d commit 5885848
Show file tree
Hide file tree
Showing 19 changed files with 416 additions and 323 deletions.
5 changes: 4 additions & 1 deletion config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ config :dotcom, :cms_api_module, CMS.Api
config :dotcom, :httpoison, HTTPoison

config :dotcom, :mbta_api_module, MBTA.Api
config :dotcom, :repo_modules, route_patterns: RoutePatterns.Repo

config :dotcom, :repo_modules,
predictions: Predictions.Repo,
route_patterns: RoutePatterns.Repo

config :dotcom, :redis, Dotcom.Cache.Multilevel.Redis
config :dotcom, :redix, Redix
Expand Down
5 changes: 4 additions & 1 deletion config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ config :dotcom, :httpoison, HTTPoison.Mock

config :dotcom, :cms_api_module, CMS.Api.Static
config :dotcom, :mbta_api_module, MBTA.Api.Mock
config :dotcom, :repo_modules, route_patterns: RoutePatterns.Repo.Mock

config :dotcom, :repo_modules,
predictions: Predictions.Repo.Mock,
route_patterns: RoutePatterns.Repo.Mock

config :dotcom, :redis, Dotcom.Redis.Mock
config :dotcom, :redix, Dotcom.Redix.Mock
Expand Down
19 changes: 9 additions & 10 deletions lib/dotcom/realtime_schedule.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ defmodule Dotcom.RealtimeSchedule do
alias Dotcom.JsonHelpers
alias Dotcom.TransitNearMe
alias Predictions.Prediction
alias Predictions.Repo, as: PredictionsRepo
alias RoutePatterns.RoutePattern
alias Routes.Repo, as: RoutesRepo
alias Routes.Route
Expand All @@ -25,10 +24,11 @@ defmodule Dotcom.RealtimeSchedule do

@predicted_schedules_per_stop 2

@predictions_repo Application.compile_env!(:dotcom, :repo_modules)[:predictions]

@default_opts [
stops_fn: &StopsRepo.get/1,
routes_fn: &RoutesRepo.by_stop_with_route_pattern/1,
predictions_fn: &PredictionsRepo.all_no_cache/1,
schedules_fn: &SchedulesRepo.by_route_ids/2,
alerts_fn: &Alerts.Repo.by_route_ids/2
]
Expand All @@ -52,7 +52,6 @@ defmodule Dotcom.RealtimeSchedule do
opts = Keyword.merge(@default_opts, opts)
stops_fn = Keyword.fetch!(opts, :stops_fn)
routes_fn = Keyword.fetch!(opts, :routes_fn)
predictions_fn = Keyword.fetch!(opts, :predictions_fn)
schedules_fn = Keyword.fetch!(opts, :schedules_fn)
alerts_fn = Keyword.fetch!(opts, :alerts_fn)

Expand All @@ -62,7 +61,7 @@ defmodule Dotcom.RealtimeSchedule do

# stage 2, get stops, predictions, schedules, and alerts
stops_task = Task.async(fn -> get_stops(stop_ids, stops_fn) end)
predictions_task = Task.async(fn -> get_predictions(route_with_patterns, predictions_fn) end)
predictions_task = Task.async(fn -> get_predictions(route_with_patterns) end)
schedules_task = Task.async(fn -> get_schedules(route_with_patterns, now, schedules_fn) end)

alerts_task = Task.async(fn -> get_alerts(route_with_patterns, now, alerts_fn) end)
Expand Down Expand Up @@ -128,12 +127,12 @@ defmodule Dotcom.RealtimeSchedule do
|> json_safe_alerts(now)
end

@spec get_predictions([route_with_patterns_t], fun()) :: map
defp get_predictions(route_with_patterns, predictions_fn) do
@spec get_predictions([route_with_patterns_t]) :: map
defp get_predictions(route_with_patterns) do
route_with_patterns
|> Enum.map(fn {stop_id, _route, route_patterns} ->
Task.async(fn ->
do_get_predictions(stop_id, route_patterns, predictions_fn)
do_get_predictions(stop_id, route_patterns)
end)
end)
|> Enum.flat_map(&Task.await(&1, @long_timeout))
Expand Down Expand Up @@ -161,13 +160,13 @@ defmodule Dotcom.RealtimeSchedule do
end
end

@spec do_get_predictions(Stop.id_t(), [RoutePattern.t()], fun()) :: [
@spec do_get_predictions(Stop.id_t(), [RoutePattern.t()]) :: [
{
route_pattern_name_t,
[Prediction.t()]
}
]
defp do_get_predictions(stop_id, route_patterns, predictions_fn) do
defp do_get_predictions(stop_id, route_patterns) do
route_patterns
|> Enum.map(fn route_pattern ->
key = route_pattern_key(route_pattern, stop_id)
Expand All @@ -180,7 +179,7 @@ defmodule Dotcom.RealtimeSchedule do
sort: "time",
"page[limit]": @predicted_schedules_per_stop
]
|> predictions_fn.()
|> @predictions_repo.all_no_cache()
|> Enum.filter(& &1.time)

{key, next_two_predictions}
Expand Down
5 changes: 3 additions & 2 deletions lib/dotcom/transit_near_me.ex
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ defmodule Dotcom.TransitNearMe do
"place-hsmnl"
]

@predictions_repo Application.compile_env!(:dotcom, :repo_modules)[:predictions]

@spec build(Address.t(), Keyword.t()) :: stops_with_distances
def build(%Address{} = location, opts) do
opts = Keyword.merge(@default_opts, opts)
Expand Down Expand Up @@ -382,10 +384,9 @@ defmodule Dotcom.TransitNearMe do
PredictedSchedule.t()
]
defp get_predicted_schedules(schedules, params, opts) do
predictions_fn = Keyword.get(opts, :predictions_fn, &Predictions.Repo.all/1)
now = Keyword.fetch!(opts, :now)

predictions = predictions_fn.(params)
predictions = @predictions_repo.all(params)

if predictions == [] do
Logger.warning("#{__MODULE__} no.predictions.for.schedule #{inspect(params)}")
Expand Down
4 changes: 3 additions & 1 deletion lib/dotcom_web/channels/vehicle_map_marker_channel.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ defmodule DotcomWeb.VehicleMapMarkerChannel do
alias Leaflet.MapData.Marker
alias Vehicles.Vehicle

@predictions_repo Application.compile_env!(:dotcom, :repo_modules)[:predictions]

intercept(["reset", "add", "update", "remove"])

@impl Phoenix.Channel
Expand Down Expand Up @@ -46,7 +48,7 @@ defmodule DotcomWeb.VehicleMapMarkerChannel do
trip = Schedules.Repo.trip(vehicle.trip_id)

prediction =
Predictions.Repo.all(
@predictions_repo.all(
route: vehicle.route_id,
direction_id: vehicle.direction_id
)
Expand Down
5 changes: 2 additions & 3 deletions lib/dotcom_web/controllers/schedule/finder_api.ex
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ defmodule DotcomWeb.ScheduleController.FinderApi do
require Logger

@route_patterns_repo Application.compile_env!(:dotcom, :repo_modules)[:route_patterns]
@predictions_repo Application.compile_env!(:dotcom, :repo_modules)[:predictions]

@type react_keys :: :date | :direction | :is_current
@type react_strings :: [{react_keys, String.t()}]
Expand Down Expand Up @@ -217,11 +218,9 @@ defmodule DotcomWeb.ScheduleController.FinderApi do
direction_id: direction_id
]

predictions_fn = Map.get(conn.assigns, :predictions_fn, &Predictions.Repo.all/1)

predictions =
if current_service?,
do: predictions_fn.(prediction_opts) |> Enum.filter(&(&1.stop.id == stop_id)),
do: @predictions_repo.all(prediction_opts) |> Enum.filter(&(&1.stop.id == stop_id)),
else: []

{schedules, predictions}
Expand Down
8 changes: 3 additions & 5 deletions lib/dotcom_web/controllers/schedule/green.ex
Original file line number Diff line number Diff line change
Expand Up @@ -72,24 +72,22 @@ defmodule DotcomWeb.ScheduleController.Green do
assign(conn, :stops_on_routes, GreenLine.stops_on_routes(direction_id, date))
end

def predictions(conn, opts) do
def predictions(conn, _opts) do
{predictions, vehicle_predictions} =
if DotcomWeb.ScheduleController.Predictions.should_fetch_predictions?(conn) do
predictions_fn = opts[:predictions_fn] || (&Predictions.Repo.all/1)

predictions_stream =
conn
|> conn_with_branches
|> Task.async_stream(
fn conn ->
DotcomWeb.ScheduleController.Predictions.predictions(conn, predictions_fn)
DotcomWeb.ScheduleController.Predictions.predictions(conn)
end,
timeout: @task_timeout,
on_timeout: :kill_task
)

vehicle_predictions =
DotcomWeb.ScheduleController.Predictions.vehicle_predictions(conn, predictions_fn)
DotcomWeb.ScheduleController.Predictions.vehicle_predictions(conn)

{flat_map_results(predictions_stream), vehicle_predictions}
else
Expand Down
2 changes: 1 addition & 1 deletion lib/dotcom_web/controllers/schedule/line_api.ex
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ defmodule DotcomWeb.ScheduleController.LineApi do
conn
|> DateInRating.call(opts)
|> Green.vehicle_locations(VehicleLocations.init(opts))
|> Green.predictions(Predictions.init(opts))
|> Green.predictions(opts)
|> VehicleTooltips.call(opts)
end

Expand Down
53 changes: 22 additions & 31 deletions lib/dotcom_web/controllers/schedule/predictions.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,20 @@ defmodule DotcomWeb.ScheduleController.Predictions do
alias Predictions.Prediction
alias Util.AsyncAssign

@default_opts [
predictions_fn: &Predictions.Repo.all/1
]

@typep predictions_fn :: (Keyword.t() -> [Prediction.t()] | {:error, any})
@predictions_repo Application.compile_env!(:dotcom, :repo_modules)[:predictions]

@impl true
def init(opts) do
Keyword.merge(@default_opts, opts)
end
def init(opts \\ []), do: opts

@impl true
def call(conn, opts) do
Util.log_duration(__MODULE__, :do_call, [conn, opts])
def call(conn, _opts \\ []) do
Util.log_duration(__MODULE__, :do_call, [conn])
end

def do_call(conn, opts) do
def do_call(conn) do
if should_fetch_predictions?(conn) do
predictions_task = fn -> predictions(conn, opts[:predictions_fn]) end
vehicle_predictions_task = fn -> vehicle_predictions(conn, opts[:predictions_fn]) end
predictions_task = fn -> predictions(conn) end
vehicle_predictions_task = fn -> vehicle_predictions(conn) end

conn
|> AsyncAssign.async_assign_default(:predictions, predictions_task, [])
Expand All @@ -54,18 +48,15 @@ defmodule DotcomWeb.ScheduleController.Predictions do
Date.compare(assigns.date, Util.service_date(assigns.date_time)) == :eq
end

@spec predictions(Plug.Conn.t(), predictions_fn) :: [Prediction.t()]
def predictions(
%{
assigns: %{
origin: origin,
destination: destination,
route: %{id: route_id},
direction_id: direction_id
}
},
predictions_fn
)
@spec predictions(Plug.Conn.t()) :: [Prediction.t()]
def predictions(%{
assigns: %{
origin: origin,
destination: destination,
route: %{id: route_id},
direction_id: direction_id
}
})
when not is_nil(origin) do
destination_id = if destination, do: Map.get(destination, :id)

Expand All @@ -76,7 +67,7 @@ defmodule DotcomWeb.ScheduleController.Predictions do
[route: route_id, direction_id: direction_id]
end

predictions_fn.(opts)
@predictions_repo.all(opts)
|> case do
{:error, error} ->
Logger.error("predictions for opts #{inspect(opts)}: #{inspect(error)}")
Expand All @@ -93,7 +84,7 @@ defmodule DotcomWeb.ScheduleController.Predictions do
end
end

def predictions(_conn, _) do
def predictions(_conn) do
[]
end

Expand All @@ -114,16 +105,16 @@ defmodule DotcomWeb.ScheduleController.Predictions do
end)
end

@spec vehicle_predictions(Plug.Conn.t(), predictions_fn) :: [Prediction.t()]
def vehicle_predictions(%{assigns: %{vehicle_locations: vehicle_locations}}, predictions_fn) do
@spec vehicle_predictions(Plug.Conn.t()) :: [Prediction.t()]
def vehicle_predictions(%{assigns: %{vehicle_locations: vehicle_locations}}) do
{trip_ids, stop_ids} =
vehicle_locations
|> Map.keys()
|> Enum.unzip()

trip_ids = trip_ids |> Enum.reject(&is_nil/1) |> Enum.join(",")

case predictions_fn.(trip: trip_ids) do
case @predictions_repo.all(trip: trip_ids) do
{:error, error} ->
Logger.error("predictions for trips #{inspect(trip_ids)}: #{inspect(error)}")

Expand All @@ -135,7 +126,7 @@ defmodule DotcomWeb.ScheduleController.Predictions do
end
end

def vehicle_predictions(_conn, _) do
def vehicle_predictions(_conn) do
[]
end
end
17 changes: 9 additions & 8 deletions lib/dotcom_web/controllers/schedule/trip_info.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ defmodule DotcomWeb.ScheduleController.TripInfo do
alias Routes.Route
alias DotcomWeb.ScheduleController.VehicleLocations

@predictions_repo Application.compile_env!(:dotcom, :repo_modules)[:predictions]

@default_opts [
trip_fn: &Schedules.Repo.schedule_for_trip/2,
vehicle_fn: &Vehicles.Repo.trip/1,
prediction_fn: &Predictions.Repo.all/1
vehicle_fn: &Vehicles.Repo.trip/1
]

@impl true
Expand Down Expand Up @@ -96,7 +97,7 @@ defmodule DotcomWeb.ScheduleController.TripInfo do
case opts[:trip_fn].(trip_id, date: conn.assigns.date) do
trips when is_list(trips) ->
trips
|> build_trip_times(conn.assigns, trip_id, opts[:prediction_fn])
|> build_trip_times(conn.assigns, trip_id)
|> TripInfo.from_list(
vehicle: opts[:vehicle_fn].(trip_id),
vehicle_stop_name: active_stop,
Expand Down Expand Up @@ -148,19 +149,19 @@ defmodule DotcomWeb.ScheduleController.TripInfo do
NaiveDateTime.compare(a, b)
end

defp build_trip_times(schedules, %{date_time: date_time} = assigns, trip_id, prediction_fn) do
defp build_trip_times(schedules, %{date_time: date_time} = assigns, trip_id) do
assigns
|> get_trip_predictions(Util.service_date(date_time), trip_id, prediction_fn)
|> get_trip_predictions(Util.service_date(date_time), trip_id)
|> PredictedSchedule.group(schedules)
end

defp get_trip_predictions(%{date: date}, service_date, _, _prediction_fn)
defp get_trip_predictions(%{date: date}, service_date, _trip_id)
when date != service_date do
[]
end

defp get_trip_predictions(_, _, trip_id, prediction_fn) do
prediction_fn.(trip: trip_id)
defp get_trip_predictions(_, _, trip_id) do
@predictions_repo.all(trip: trip_id)
end

@spec show_trips?(DateTime.t(), DateTime.t(), integer, String.t()) :: boolean
Expand Down
6 changes: 4 additions & 2 deletions lib/predicted_schedule.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ defmodule PredictedSchedule do
prediction: Prediction.t() | nil
}

@predictions_repo Application.compile_env!(:dotcom, :repo_modules)[:predictions]

def get(route_id, stop_id, opts \\ []) do
schedules_fn = Keyword.get(opts, :schedules_fn, &Schedules.Repo.by_route_ids/2)
predictions_fn = Keyword.get(opts, :predictions_fn, &Predictions.Repo.all/1)

now = Keyword.get(opts, :now, Util.now())
direction_id = Keyword.get(opts, :direction_id)
sort_fn = Keyword.get(opts, :sort_fn, &sort_predicted_schedules/1)
Expand Down Expand Up @@ -53,7 +55,7 @@ defmodule PredictedSchedule do

predicted_schedules =
[route: route_id, direction_id: direction_id]
|> predictions_fn.()
|> @predictions_repo.all()
|> Enum.filter(&(&1.stop.id == stop_id))
|> PredictedSchedule.group(schedules, sort_fn: sort_fn)
|> filter_predicted_schedules(now)
Expand Down
15 changes: 15 additions & 0 deletions lib/predictions/repo/behaviour.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
defmodule Predictions.Repo.Behaviour do
@moduledoc """
Behavior for an API client for fetching prediction data.
"""

@doc """
Return predictions for given params
"""
@callback all(Keyword.t()) :: [Predictions.Prediction.t()] | []

@doc """
Return predictions for give prarams ignoring the cache
"""
@callback all_no_cache(Keyword.t()) :: [Predictions.Prediction.t()] | []
end

0 comments on commit 5885848

Please sign in to comment.