mirror of
https://github.com/teslamate-org/teslamate.git
synced 2026-01-24 21:06:08 +08:00
499 lines
16 KiB
Elixir
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
|