Files
archived-teslamate/test/teslamate/vehicles/vehicle_test.exs
2021-09-24 22:39:26 +02:00

499 lines
16 KiB
Elixir

defmodule TeslaMate.Vehicles.VehicleTest do
use TeslaMate.VehicleCase, async: true
alias TeslaMate.Vehicles.Vehicle.Summary
alias TeslaMate.Vehicles.Vehicle
alias TeslaMate.Log.{Car, Update}
describe "starting" do
@tag :capture_log
test "handles unknown and faulty states", %{test: name} do
events = [
{:ok, %TeslaApi.Vehicle{state: "unknown"}},
{:error, %TeslaApi.Error{reason: :boom, message: "boom"}}
]
:ok = start_vehicle(name, events)
assert_receive {:pubsub, {:broadcast, _, _, %Summary{state: :unavailable, healthy: true}}}
assert_receive {:pubsub, {:broadcast, _, _, %Summary{healthy: false, state: :unavailable}}}
refute_receive _
end
test "handles online state", %{test: name} do
events = [
{:ok, online_event()}
]
:ok = start_vehicle(name, events)
assert_receive {:start_state, car, :online, date: _}
assert_receive {ApiMock, {:stream, 1000, _}}
assert_receive {:insert_position, ^car, %{}}
assert_receive {:pubsub, {:broadcast, _, _, %Summary{state: :online}}}
refute_receive _
end
test "handles offline state", %{test: name} do
events = [
{:ok, %TeslaApi.Vehicle{state: "offline"}}
]
:ok = start_vehicle(name, events)
assert_receive {:start_state, _car, :offline, []}
assert_receive {:pubsub, {:broadcast, _server, _topic, %Summary{state: :offline}}}
refute_receive _
end
test "handles asleep state", %{test: name} do
events = [
{:ok, %TeslaApi.Vehicle{state: "asleep"}}
]
:ok = start_vehicle(name, events)
assert_receive {:start_state, _car, :asleep, []}
assert_receive {:pubsub, {:broadcast, _server, _topic, %Summary{state: :asleep}}}
refute_receive _
end
end
describe "resume_logging/1" do
alias TeslaMate.Vehicles.Vehicle
test "does nothing of already online", %{test: name} do
events = [
{:ok, online_event()}
]
:ok = start_vehicle(name, events)
assert_receive {:start_state, car, :online, date: _}, 100
assert_receive {ApiMock, {:stream, 1000, _}}
assert_receive {:insert_position, ^car, %{}}
assert_receive {:pubsub, {:broadcast, _, _, %Summary{state: :online}}}
assert :ok = Vehicle.resume_logging(name)
refute_receive _
end
test "increases polling frequency if asleep", %{test: name} do
events = [
{:ok, %TeslaApi.Vehicle{state: "asleep"}},
{:ok, %TeslaApi.Vehicle{state: "asleep"}},
{:ok, %TeslaApi.Vehicle{state: "asleep"}},
{:ok, %TeslaApi.Vehicle{state: "asleep"}},
{:ok, %TeslaApi.Vehicle{state: "asleep"}},
{:ok, %TeslaApi.Vehicle{state: "asleep"}},
{:ok, %TeslaApi.Vehicle{state: "asleep"}},
{:ok, online_event()}
]
:ok = start_vehicle(name, events)
assert_receive {:start_state, car, :asleep, []}
assert_receive {:pubsub, {:broadcast, _server, _topic, %Summary{state: :asleep}}}
assert :ok = Vehicle.resume_logging(name)
assert_receive {:start_state, ^car, :online, date: _}, 100
assert_receive {ApiMock, {:stream, 1000, _}}
assert_receive {:insert_position, ^car, %{}}
assert_receive {:pubsub, {:broadcast, _, _, %Summary{state: :online}}}
refute_receive _
end
test "increases polling frequency if offline", %{test: name} do
events = [
{:ok, %TeslaApi.Vehicle{state: "offline"}},
{:ok, %TeslaApi.Vehicle{state: "offline"}},
{:ok, %TeslaApi.Vehicle{state: "offline"}},
{:ok, %TeslaApi.Vehicle{state: "offline"}},
{:ok, %TeslaApi.Vehicle{state: "offline"}},
{:ok, %TeslaApi.Vehicle{state: "offline"}},
{:ok, %TeslaApi.Vehicle{state: "offline"}},
{:ok, online_event()}
]
:ok = start_vehicle(name, events)
assert_receive {:start_state, car, :offline, []}
assert_receive {:pubsub, {:broadcast, _server, _topic, %Summary{state: :offline}}}
assert :ok = Vehicle.resume_logging(name)
assert_receive {:start_state, ^car, :online, date: _}, 100
assert_receive {ApiMock, {:stream, 1000, _}}
assert_receive {:insert_position, ^car, %{}}
assert_receive {:pubsub, {:broadcast, _, _, %Summary{state: :online}}}
refute_receive _
end
end
describe "settings change" do
alias TeslaMate.Vehicles.Vehicle
alias TeslaMate.Settings.CarSettings
test "applies new sleep settings", %{test: name} do
events = [
{:ok, online_event()}
]
:ok =
start_vehicle(name, events,
settings: %{
suspend_after_idle_min: 999_999_999,
suspend_min: 10_000,
use_streaming_api: false
}
)
# Online
assert_receive {:start_state, %Car{id: car_id} = car, :online, date: _}
assert_receive {:insert_position, ^car, %{}}
assert_receive {:pubsub, {:broadcast, _server, _topic, %Summary{state: :online}}}
refute_receive _, 500
# Reduce suspend_after_idle_min
send(name, %CarSettings{
suspend_after_idle_min: 1,
suspend_min: 10_000,
use_streaming_api: false
})
assert_receive {:pubsub, {:broadcast, _server, _topic, %Summary{state: :suspended}}}
assert_receive {:insert_position,
%Car{
id: ^car_id,
settings: %CarSettings{suspend_after_idle_min: 1, suspend_min: 10_000}
}, %{}}
refute_receive _
end
end
describe "error handling" do
@tag :capture_log
test "restarts if the eid changed", %{test: name} do
events = [
{:ok, online_event()},
{:ok, online_event()},
{:error, :vehicle_not_found}
]
:ok = start_vehicle(name, events)
# Online
assert_receive {:start_state, car, :online, date: _}
assert_receive {ApiMock, {:stream, 1000, _}}
assert_receive {:insert_position, ^car, %{}}
assert_receive {:pubsub, {:broadcast, _, _, %Summary{state: :online, healthy: true}}}
# Too many errors
assert_receive {:pubsub, {:broadcast, _, _, %Summary{state: :online, healthy: false}}}
# Killed
assert_receive {VehiclesMock, :kill}
end
@tag :capture_log
test "reports the health status", %{test: name} do
events = [
{:ok, online_event()},
{:ok, online_event()},
{:error, :unknown}
]
:ok = start_vehicle(name, events)
assert_receive {:start_state, car, :online, date: _}
assert_receive {ApiMock, {:stream, 1000, _}}
assert_receive {:insert_position, ^car, %{}}
assert_receive {:pubsub, {:broadcast, _, _, %Summary{state: :online, healthy: true}}}
# ...
assert_receive {:pubsub, {:broadcast, _, _, %Summary{state: :online, healthy: false}}}, 1000
end
@tag :capture_log
test "handles timeout errors", %{test: name} do
events = [
{:ok, online_event()},
{:ok, online_event()},
{:error, :timeout},
{:error, :timeout},
{:error, :timeout},
{:error, :timeout},
{:ok, online_event()},
{:ok, online_event()},
{:error, :timeout},
{:error, :timeout},
{:error, :closed},
{:error, :closed},
{:error, :timeout},
{:ok, online_event()},
{:ok, online_event()}
]
:ok = start_vehicle(name, events)
# Online
assert_receive {:start_state, car, :online, date: _}
assert_receive {ApiMock, {:stream, 1000, _}}
assert_receive {:insert_position, ^car, %{}}
assert_receive {:pubsub, {:broadcast, _, _, %Summary{state: :online, healthy: true}}}
refute_receive _, 100
end
@tag :capture_log
test "notices if vehicle is in service ", %{test: name} do
events = [
{:ok, online_event()},
{:ok, online_event()},
{:error, :vehicle_in_service},
{:error, :vehicle_in_service},
{:ok, online_event()},
fn -> Process.sleep(10_000) end
]
:ok = start_vehicle(name, events, settings: %{use_streaming_api: false})
# Online
assert_receive {:start_state, car, :online, date: _}
assert_receive {:insert_position, ^car, %{}}
assert_receive {:pubsub, {:broadcast, _, _, %Summary{state: :online, healthy: true}}}
refute_receive _, 400
end
test "ends a drive if vehicle is in service", %{test: name} do
now_ts = DateTime.utc_now() |> DateTime.to_unix(:millisecond)
events = [
{:ok, online_event()},
{:ok, drive_event(now_ts + 0, "D", 5)},
{:ok, drive_event(now_ts + 1, "D", 50)},
{:error, :vehicle_in_service},
fn -> Process.sleep(10_000) end
]
:ok = start_vehicle(name, events)
assert_receive {:start_state, car, :online, date: _}
assert_receive {ApiMock, {:stream, 1000, _}}
assert_receive {:insert_position, ^car, %{}}
assert_receive {:pubsub, {:broadcast, _, _, %Summary{state: :online}}}
assert_receive {:pubsub, {:broadcast, _, _, %Summary{state: :driving}}}
assert_receive {:start_drive, ^car}
assert_receive {:insert_position, drive, %{longitude: 0.1, speed: 8}}
assert_receive {:pubsub, {:broadcast, _, _, %Summary{state: :driving}}}
assert_receive {:insert_position, ^drive, %{longitude: 0.1, speed: 80}}
assert_receive {:close_drive, ^drive, []}
assert_receive {:"$websockex_cast", :disconnect}
assert_receive {:pubsub, {:broadcast, _, _, %Summary{state: :start}}}
refute_receive _
end
@tag :capture_log
test "stops polling if signed out", %{test: name} do
events = [
{:ok, online_event()},
{:ok, online_event()},
{:error, :not_signed_in},
# next events shall just show that polling stops
{:ok, online_event()},
{:ok, online_event()}
]
:ok = start_vehicle(name, events)
# Online
assert_receive {:start_state, car, :online, date: _}
assert_receive {ApiMock, {:stream, 1000, _}}
assert_receive {:insert_position, ^car, %{}}
assert_receive {:pubsub, {:broadcast, _, _, %Summary{state: :online, healthy: true}}}
assert_receive {:pubsub, {:broadcast, _, _, %Summary{state: :start, healthy: false}}}
refute_receive _
end
@tag :capture_log
test "broadcasts the summary when the health check succeeds again", %{test: name} do
events = [
{:ok, %TeslaApi.Vehicle{state: "asleep"}}
]
:ok = start_vehicle(name, events)
fuse_name =
TestHelper.eventually(
fn ->
assert %Summary{state: :asleep, healthy: true, car: %Car{id: id}} =
Vehicle.summary(name)
:"#{Vehicle}_#{id}_api_error"
end,
delay: 10
)
assert_receive {:start_state, _car, :asleep, []}
assert_receive {:pubsub, {:broadcast, _, _, %Summary{state: :asleep, healthy: true}}}
:ok = :fuse.circuit_disable(fuse_name)
assert_receive {:pubsub, {:broadcast, _, _, %Summary{state: :asleep, healthy: false}}}
:ok = :fuse.circuit_enable(fuse_name)
assert_receive {:pubsub, {:broadcast, _, _, %Summary{state: :asleep, healthy: true}}}
refute_receive _
end
end
describe "summary" do
test "returns the summary if no api request was completed yet", %{test: name} do
events = [
fn ->
Process.sleep(10_000)
{:ok, online_event()}
end
]
:ok = start_vehicle(name, events)
for _ <- 1..10 do
assert %Summary{state: :unavailable, healthy: true} = Vehicle.summary(name)
end
end
test "returns the summary even if the api call is blocked", %{test: name} do
events = [
{:ok, online_event()},
{:ok, online_event()},
fn ->
Process.sleep(10_000)
{:ok, online_event()}
end
]
:ok = start_vehicle(name, events)
assert_receive {:start_state, _car, :online, date: _}
assert_receive {ApiMock, {:stream, 1000, _}}
for _ <- 1..10 do
assert %Summary{state: :online, healthy: true} = Vehicle.summary(name)
end
end
end
describe "updates" do
test "logs the current software version at first startup", %{test: name} do
now_ts = DateTime.utc_now() |> DateTime.to_unix(:millisecond)
events = [
{:ok,
online_event(vehicle_state: %{timestamp: now_ts, car_version: "42.42.42.0 b2ab650"})}
]
:ok = start_vehicle(name, events, last_update: nil)
date = DateTime.from_unix!(now_ts, :millisecond)
assert_receive {:start_state, car, :online, date: _}
assert_receive {ApiMock, {:stream, 1000, _}}
assert_receive {:insert_position, ^car, %{}}
assert_receive {:insert_missed_update, ^car, "42.42.42.0 b2ab650", date: ^date}
assert_receive {:pubsub, {:broadcast, _, _, %Summary{state: :online}}}
refute_receive _
end
test "logs missing updates", %{test: name} do
now_ts = DateTime.utc_now() |> DateTime.to_unix(:millisecond)
events = [
{:ok,
online_event(vehicle_state: %{timestamp: now_ts, car_version: "2020.12.10 e0ccfda3d911"})}
]
:ok = start_vehicle(name, events, last_update: %Update{version: "2020.12.5 e2179e0650f0"})
date = DateTime.from_unix!(now_ts, :millisecond)
assert_receive {:start_state, car, :online, date: _}
assert_receive {ApiMock, {:stream, 1000, _}}
assert_receive {:insert_position, ^car, %{}}
assert_receive {:insert_missed_update, ^car, "2020.12.10 e0ccfda3d911", date: ^date}
assert_receive {:pubsub, {:broadcast, _, _, %Summary{state: :online}}}
refute_receive _
end
test "does not log updates <= current version", %{test: name} do
events = [
{:ok, online_event()},
{:ok, online_event(vehicle_state: %{car_version: "2019.40.10.7 ad132c7b057e"})},
{:ok, %TeslaApi.Vehicle{state: "asleep"}},
{:ok, %TeslaApi.Vehicle{state: "asleep"}},
{:ok, online_event()},
{:ok, online_event(vehicle_state: %{car_version: "2019.40.10.6"})},
{:ok, %TeslaApi.Vehicle{state: "asleep"}},
{:ok, %TeslaApi.Vehicle{state: "asleep"}},
{:ok, online_event()},
{:ok, online_event(vehicle_state: %{car_version: "2019.40.2.6"})},
{:ok, %TeslaApi.Vehicle{state: "asleep"}},
{:ok, %TeslaApi.Vehicle{state: "asleep"}},
{:ok, online_event()},
{:ok, online_event(vehicle_state: %{car_version: "2019.40.9"})},
{:ok, %TeslaApi.Vehicle{state: "asleep"}},
{:ok, %TeslaApi.Vehicle{state: "asleep"}}
]
:ok =
start_vehicle(name, events, last_update: %Update{version: "2019.40.10.7 ad132c7b057e"})
for _ <- 1..4 do
assert_receive {:start_state, car, :online, date: _}
assert_receive {ApiMock, {:stream, 1000, _}}
assert_receive {:insert_position, ^car, %{}}
assert_receive {:pubsub, {:broadcast, _, _, %Summary{state: :online}}}
assert_receive {:start_state, ^car, :asleep, []}
assert_receive {:pubsub, {:broadcast, _, _, %Summary{state: :asleep}}}
assert_receive {:"$websockex_cast", :disconnect}
end
refute_receive _
end
@tag :capture_log
test "handles unexpected :car_version's", %{test: name} do
events = [
{:ok, online_event(vehicle_state: %{car_version: nil})}
]
:ok = start_vehicle(name, events, last_update: nil)
assert_receive {:start_state, car, :online, date: _}
assert_receive {ApiMock, {:stream, 1000, _}}
assert_receive {:insert_position, ^car, %{}}
assert_receive {:pubsub, {:broadcast, _, _, %Summary{state: :online}}}
refute_receive _
end
end
end