Add tests

This commit is contained in:
Adrian Kumpf
2019-09-01 22:13:15 +02:00
parent ac913ae632
commit a035f6d31e
8 changed files with 272 additions and 10 deletions
@@ -2,11 +2,12 @@ defmodule TeslaMate.Mqtt.PubSub.VehicleSubscriber do
use GenServer
require Logger
import Core.Dependency, only: [call: 3]
alias TeslaMate.Mqtt.Publisher
alias TeslaMate.Vehicles
defstruct [:car_id, :last_summary]
defstruct [:car_id, :last_summary, :deps]
alias __MODULE__, as: State
def child_spec(arg) do
@@ -23,8 +24,15 @@ defmodule TeslaMate.Mqtt.PubSub.VehicleSubscriber do
@impl true
def init(opts) do
car_id = Keyword.fetch!(opts, :car_id)
:ok = Vehicles.subscribe(car_id)
{:ok, %State{car_id: car_id}}
deps = %{
vehicles: Keyword.get(opts, :deps_vehicles, Vehicles),
publisher: Keyword.get(opts, :deps_publisher, Publisher)
}
:ok = call(deps.vehicles, :subscribe, [car_id])
{:ok, %State{car_id: car_id, deps: deps}}
end
@impl true
@@ -40,10 +48,11 @@ defmodule TeslaMate.Mqtt.PubSub.VehicleSubscriber do
end)
|> Task.async_stream(
fn {key, value} ->
Publisher.publish("teslamate/cars/#{car_id}/#{key}", to_string(value),
retain: true,
qos: 1
)
call(state.deps.publisher, :publish, [
"teslamate/cars/#{car_id}/#{key}",
to_string(value),
[retain: true, qos: 1]
])
end,
max_concurrency: 10,
on_timeout: :kill_task,
@@ -54,6 +63,6 @@ defmodule TeslaMate.Mqtt.PubSub.VehicleSubscriber do
_ok -> nil
end)
{:noreply, state}
{:noreply, %State{state | last_summary: summary}}
end
end
+2 -1
View File
@@ -55,7 +55,8 @@ defmodule TeslaMate.MixProject do
{:mojito, "~> 0.5"},
{:geocalc, "~> 0.5"},
{:srtm, "~> 0.1"},
{:fuse, "~> 2.4"}
{:fuse, "~> 2.4"},
{:mock, "~> 0.3", only: :test}
]
end
+2
View File
@@ -18,10 +18,12 @@
"hackney": {:hex, :hackney, "1.15.1", "9f8f471c844b8ce395f7b6d8398139e26ddca9ebc171a8b91342ee15a19963f4", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
"idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
"jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
"meck": {:hex, :meck, "0.8.13", "ffedb39f99b0b99703b8601c6f17c7f76313ee12de6b646e671e3188401f7866", [:rebar3], [], "hexpm"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"},
"mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm"},
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm"},
"mint": {:hex, :mint, "0.4.0", "b93a10192957624ed4a8b8641eff1819019c36487bdf49e2b505afd2cc9b7911", [:mix], [{:castore, "~> 0.1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm"},
"mock": {:hex, :mock, "0.3.3", "42a433794b1291a9cf1525c6d26b38e039e0d3a360732b5e467bfc77ef26c914", [:mix], [{:meck, "~> 0.8.13", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm"},
"mojito": {:hex, :mojito, "0.5.0", "de003eafc09633d236e487621f1a8d7d55ca0414a5a418e09ab70a595f9c9c12", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: false]}, {:mint, "~> 0.4.0", [hex: :mint, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}], "hexpm"},
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"},
"phoenix": {:hex, :phoenix, "1.4.9", "746d098e10741c334d88143d3c94cab1756435f94387a63441792e66ec0ee974", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
+27
View File
@@ -0,0 +1,27 @@
defmodule MqttPublisherMock do
use GenServer
defstruct [:pid]
alias __MODULE__, as: State
# API
def start_link(opts) do
GenServer.start_link(__MODULE__, opts, name: Keyword.fetch!(opts, :name))
end
def publish(name, topic, msg, opts), do: GenServer.call(name, {:publish, topic, msg, opts})
# Callbacks
@impl true
def init(opts) do
{:ok, %State{pid: Keyword.fetch!(opts, :pid)}}
end
@impl true
def handle_call({:publish, _topic, _msg, _opts} = action, _from, %State{pid: pid} = state) do
send(pid, {MqttPublisherMock, action})
{:reply, :ok, state}
end
end
+7
View File
@@ -12,6 +12,8 @@ defmodule VehiclesMock do
def restart(name), do: GenServer.call(name, :restart)
def subscribe(name, car_id), do: GenServer.call(name, {:subscribe, car_id})
# Callbacks
@impl true
@@ -20,6 +22,11 @@ defmodule VehiclesMock do
end
@impl true
def handle_call({:subscribe, _car_id} = action, _from, %State{pid: pid} = state) do
send(pid, {VehiclesMock, action})
{:reply, :ok, state}
end
def handle_call(:restart, _from, %State{pid: pid} = state) do
send(pid, {VehiclesMock, :restart})
{:reply, :ok, state}
@@ -0,0 +1,65 @@
defmodule TeslaMate.Locations.GeocoderTest do
use ExUnit.Case, async: false
alias TeslaMate.Locations.Geocoder
import Mock
@response {:ok,
%Mojito.Response{
body:
"{\"place_id\":241575531,\"licence\":\"Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright\",\"osm_type\":\"node\",\"osm_id\":5983038298,\"lat\":\"37.8895442\",\"lon\":\"41.1288167\",\"place_rank\":30,\"category\":\"amenity\",\"type\":\"cafe\",\"importance\":0,\"addresstype\":\"amenity\",\"name\":\"Kahve Deryası\",\"display_name\":\"Kahve Deryası, Cihan Kavşağı, Batman, Ziyagökalp Mahallesi, Batman merkez, Batman, Southeastern Anatolia Region, 72060, Turkey\",\"address\":{\"cafe\":\"Kahve Deryası\",\"road\":\"Cihan Kavşağı\",\"residential\":\"Batman\",\"suburb\":\"Ziyagökalp Mahallesi\",\"city\":\"Batman merkez\",\"state\":\"Southeastern Anatolia Region\",\"postcode\":\"72060\",\"country\":\"Turkey\",\"country_code\":\"tr\"},\"extratags\":{},\"namedetails\":{\"name\":\"Kahve Deryası\"},\"boundingbox\":[\"37.8894442\",\"37.8896442\",\"41.1287167\",\"41.1289167\"]}",
complete: true,
headers: [
{"date", "Sun, 01 Sep 2019 21:03:23 GMT"},
{"server", "Apache/2.4.29 (Ubuntu)"},
{"access-control-allow-origin", "*"},
{"access-control-allow-methods", "OPTIONS,GET"},
{"strict-transport-security", "max-age=31536000; includeSubDomains; preload"},
{"expect-ct",
"max-age=0, report-uri=\"https://openstreetmap.report-uri.com/r/d/ct/reportOnly\""},
{"content-type", "application/json; charset=UTF-8"}
],
status_code: 200
}}
test "geocoders coordinates" do
with_mock Mojito,
get:
fn "https://nominatim.openstreetmap.org/reverse?format=jsonv2&addressdetails=1&extratags=1&namedetails=1&zoom=19&lat=37.889602&lon=41.129182",
_headers ->
@response
end do
assert Geocoder.reverse_lookup(37.889602, 41.129182) ==
{:ok,
%{
city: "Batman merkez",
country: "Turkey",
county: nil,
display_name:
"Kahve Deryası, Cihan Kavşağı, Batman, Ziyagökalp Mahallesi, Batman merkez, Batman, Southeastern Anatolia Region, 72060, Turkey",
house_number: nil,
latitude: "37.8895442",
longitude: "41.1288167",
name: "Kahve Deryası",
neighbourhood: "Ziyagökalp Mahallesi",
place_id: 241_575_531,
postcode: "72060",
raw: %{
"cafe" => "Kahve Deryası",
"city" => "Batman merkez",
"country" => "Turkey",
"country_code" => "tr",
"postcode" => "72060",
"residential" => "Batman",
"road" => "Cihan Kavşağı",
"state" => "Southeastern Anatolia Region",
"suburb" => "Ziyagökalp Mahallesi"
},
road: "Cihan Kavşağı",
state: "Southeastern Anatolia Region",
state_district: nil
}}
end
end
end
@@ -0,0 +1,135 @@
defmodule TeslaMate.Mqtt.PubSub.VehicleSubscriberTest do
use TeslaMate.DataCase, async: true
alias TeslaMate.Mqtt.PubSub.VehicleSubscriber
alias TeslaMate.Vehicles.Vehicle.Summary
defp start_subscriber(name, car_id) do
publisher_name = :"mqtt_publisher_#{name}"
vehicles_name = :"vehicles_#{name}"
{:ok, _pid} = start_supervised({MqttPublisherMock, name: publisher_name, pid: self()})
{:ok, _pid} = start_supervised({VehiclesMock, name: vehicles_name, pid: self()})
start_supervised(
{VehicleSubscriber,
[
name: name,
car_id: car_id,
deps_publisher: {MqttPublisherMock, publisher_name},
deps_vehicles: {VehiclesMock, vehicles_name}
]}
)
end
test "", %{test: name} do
{:ok, pid} = start_subscriber(name, 0)
assert_receive {VehiclesMock, {:subscribe, 0}}
summary = %Summary{
display_name: "Foo",
state: :online,
since: DateTime.utc_now(),
battery_level: 60.0,
ideal_battery_range_km: 230.5,
est_battery_range_km: 220.0,
battery_range_km: 230.5,
charge_energy_added: 25,
speed: 40,
outside_temp: 15,
inside_temp: 20.0,
locked: true,
sentry_mode: false,
plugged_in: false,
scheduled_charging_start_time: DateTime.utc_now() |> DateTime.add(60 * 60 * 10, :second),
charge_limit_soc: 90,
charger_power: 50
}
send(pid, summary)
assert_receive {MqttPublisherMock,
{:publish, "teslamate/cars/0/charge_limit_soc", "90", [retain: true, qos: 1]}}
assert_receive {MqttPublisherMock,
{:publish, "teslamate/cars/0/display_name", "Foo", [retain: true, qos: 1]}}
assert_receive {MqttPublisherMock,
{:publish, "teslamate/cars/0/inside_temp", "20.0", [retain: true, qos: 1]}}
assert_receive {MqttPublisherMock,
{:publish, "teslamate/cars/0/locked", "true", [retain: true, qos: 1]}}
assert_receive {MqttPublisherMock,
{:publish, "teslamate/cars/0/outside_temp", "15", [retain: true, qos: 1]}}
assert_receive {MqttPublisherMock,
{:publish, "teslamate/cars/0/plugged_in", "false", [retain: true, qos: 1]}}
scheduled_charging_start_time_str = to_string(summary.scheduled_charging_start_time)
assert_receive {MqttPublisherMock,
{:publish, "teslamate/cars/0/scheduled_charging_start_time",
^scheduled_charging_start_time_str, [retain: true, qos: 1]}}
assert_receive {MqttPublisherMock,
{:publish, "teslamate/cars/0/sentry_mode", "false", [retain: true, qos: 1]}}
assert_receive {MqttPublisherMock,
{:publish, "teslamate/cars/0/speed", "40", [retain: true, qos: 1]}}
assert_receive {MqttPublisherMock,
{:publish, "teslamate/cars/0/state", "online", [retain: true, qos: 1]}}
assert_receive {MqttPublisherMock,
{:publish, "teslamate/cars/0/battery_level", "60.0", [retain: true, qos: 1]}}
assert_receive {MqttPublisherMock,
{:publish, "teslamate/cars/0/battery_range_km", "230.5",
[retain: true, qos: 1]}}
assert_receive {MqttPublisherMock,
{:publish, "teslamate/cars/0/est_battery_range_km", "220.0",
[retain: true, qos: 1]}}
assert_receive {MqttPublisherMock,
{:publish, "teslamate/cars/0/ideal_battery_range_km", "230.5",
[retain: true, qos: 1]}}
assert_receive {MqttPublisherMock,
{:publish, "teslamate/cars/0/charge_energy_added", "25",
[retain: true, qos: 1]}}
assert_receive {MqttPublisherMock,
{:publish, "teslamate/cars/0/charger_power", "50", [retain: true, qos: 1]}}
since_str = to_string(summary.since)
assert_receive {MqttPublisherMock,
{:publish, "teslamate/cars/0/since", ^since_str, [retain: true, qos: 1]}}
# send same summary again
send(pid, summary)
refute_receive _
end
test "send empty string if scheduled_charging_start_time is nil", %{test: name} do
{:ok, pid} = start_subscriber(name, 0)
assert_receive {VehiclesMock, {:subscribe, 0}}
summary = %Summary{
scheduled_charging_start_time: nil
}
send(pid, summary)
assert_receive {MqttPublisherMock,
{:publish, "teslamate/cars/0/scheduled_charging_start_time", "",
[retain: true, qos: 1]}}
refute_receive _
end
end
+17 -1
View File
@@ -1,7 +1,7 @@
defmodule TeslaMateWeb.GeoFenceLiveTest do
use TeslaMateWeb.ConnCase
alias TeslaMate.{Locations, Settings}
alias TeslaMate.{Locations, Settings, Log}
alias TeslaMate.Locations.GeoFence
def geofence_fixture(attrs \\ %{}) do
@@ -151,6 +151,22 @@ defmodule TeslaMateWeb.GeoFenceLiveTest do
end
describe "New" do
test "pre-fills the coordinates with the most recent position", %{conn: conn} do
assert {:ok, car} = Log.create_car(%{efficiency: 0.153, eid: 42, model: "3", vid: 42})
assert {:ok, _} =
Log.insert_position(car.id, %{
date: DateTime.utc_now(),
latitude: 48.067612,
longitude: 12.862226
})
assert {:ok, view, html} = live(conn, "/geo-fences/new")
assert html =~ ~r/<input .*? id="geo_fence_latitude" .*? value="48.067612">/
assert html =~ ~r/<input .*? id="geo_fence_longitude" .*? value="12.862226">/
end
test "validates cahnges when creating a new geo-fence", %{conn: conn} do
assert {:ok, view, html} = live(conn, "/geo-fences/new")