diff --git a/lib/dotcom/alerts/disruptions/subway.ex b/lib/dotcom/alerts/disruptions/subway.ex index 3d8bbbd0f5..6d035bf127 100644 --- a/lib/dotcom/alerts/disruptions/subway.ex +++ b/lib/dotcom/alerts/disruptions/subway.ex @@ -5,7 +5,7 @@ defmodule Dotcom.Alerts.Disruptions.Subway do import Dotcom.Alerts, only: [service_impacting_alert?: 1] import Dotcom.Routes, only: [subway_route_ids: 0] - import Dotcom.Utils.ServiceDateTime, only: [service_range: 1] + import Dotcom.Utils.ServiceDateTime, only: [all_service_ranges: 0, service_range: 1] alias Alerts.Alert alias Dotcom.Utils @@ -50,14 +50,24 @@ defmodule Dotcom.Alerts.Disruptions.Subway do defp group_alerts(alert, groups) do alert |> Map.get(:active_period) - |> Enum.map(fn {start, stop} -> [service_range(start), service_range(stop)] end) - |> List.flatten() + |> Enum.flat_map(&service_range_range/1) |> Enum.uniq() |> Enum.reduce(groups, fn service_range, groups -> Map.update(groups, service_range, [alert], &(&1 ++ [alert])) end) end + # An active period can span many ranges from start to stop + # e.g. [:before_today, :today, :later_this_week] + defp service_range_range({start, stop}) do + start_index = Enum.find_index(all_service_ranges(), &(&1 == service_range(start))) + stop_index = Enum.find_index(all_service_ranges(), &(&1 == service_range(stop))) + + all_service_ranges() + |> Enum.with_index(&if(&2 in start_index..stop_index, do: &1)) + |> Enum.reject(&is_nil/1) + end + # Sorts alerts by the start time of the first active period. defp sort_alerts_by_start_time(alerts) do alerts diff --git a/lib/dotcom/utils/service_date_time.ex b/lib/dotcom/utils/service_date_time.ex index c7fb1c8f37..3fce376600 100644 --- a/lib/dotcom/utils/service_date_time.ex +++ b/lib/dotcom/utils/service_date_time.ex @@ -25,6 +25,12 @@ defmodule Dotcom.Utils.ServiceDateTime do @service_rollover_time Application.compile_env!(:dotcom, :service_rollover_time) @timezone Application.compile_env!(:dotcom, :timezone) + @doc """ + Returns an ordered list of service ranges, from earliest to latest. + """ + def all_service_ranges, + do: [:before_today, :today, :later_this_week, :next_week, :after_next_week] + @doc """ Returns the time at which service rolls over from 'today' to 'tomorrow'. """ diff --git a/test/dotcom/alerts/disruptions/subway_test.exs b/test/dotcom/alerts/disruptions/subway_test.exs index e10dd07162..505c5dd828 100644 --- a/test/dotcom/alerts/disruptions/subway_test.exs +++ b/test/dotcom/alerts/disruptions/subway_test.exs @@ -57,6 +57,43 @@ defmodule Dotcom.Alerts.Disruptions.SubwayTest do after_next_week: [^alert_after_next_week] } = future_disruptions() end + + test "handles single active_period spanning many service ranges" do + # Setup + {alert_today_start, _} = service_range_day() + {alert_after_next_week_start, _} = service_range_after_next_week() + + long_alert = + [{alert_today_start, Timex.shift(alert_after_next_week_start, days: 1)}] + |> disruption_alert() + + expect(Alerts.Repo.Mock, :by_route_ids, fn _route_ids, _now -> + [long_alert] + end) + + # Exercise/Verify + assert %{ + later_this_week: [^long_alert], + next_week: [^long_alert], + after_next_week: [^long_alert] + } = future_disruptions() + end + + test "handles alert with more than one active_period" do + # Setup + long_alert = + [service_range_later_this_week(), service_range_next_week()] |> disruption_alert() + + expect(Alerts.Repo.Mock, :by_route_ids, fn _route_ids, _now -> + [long_alert] + end) + + # Exercise/Verify + assert %{ + later_this_week: [^long_alert], + next_week: [^long_alert] + } = future_disruptions() + end end describe "todays_disruptions/0" do @@ -82,6 +119,30 @@ defmodule Dotcom.Alerts.Disruptions.SubwayTest do assert %{today: [^alert_today]} = todays_disruptions() end + test "returns alerts for today when applicable to other service ranges" do + # Setup + {start, _} = service_range_day() + {_, stop} = service_range_next_week() + alert_today_and_beyond = disruption_alert({start, stop}) + + alert_today_and_other_dates = + [ + service_range_day(), + service_range_later_this_week(), + service_range_next_week() + ] + |> disruption_alert() + + expect(Alerts.Repo.Mock, :by_route_ids, fn _route_ids, _now -> + [alert_today_and_beyond, alert_today_and_other_dates] + end) + + # Exercise/Verify + assert %{ + today: [^alert_today_and_beyond, ^alert_today_and_other_dates] + } = todays_disruptions() + end + test "sorts alerts by start time" do # Setup {start, stop} = service_range_day() @@ -100,8 +161,10 @@ defmodule Dotcom.Alerts.Disruptions.SubwayTest do end defp disruption_alert(active_period) do + active_period = if(is_list(active_period), do: active_period, else: [active_period]) + Factories.Alerts.Alert.build(:alert, - active_period: [active_period], + active_period: active_period, effect: service_impacting_effects() |> Faker.Util.pick() ) end