Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactoring test suite using StreamData #919

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/astarte_appengine_api/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ defmodule Astarte.AppEngine.API.Mixfile do
end

# Specifies which paths to compile per environment.
defp elixirc_paths(:test), do: ["test/support", "lib"]
defp elixirc_paths(:test), do: ["test/support", "test/support_v2", "lib"]
defp elixirc_paths(_), do: ["lib"]

defp dialyzer_cache_directory(:ci) do
Expand Down
9 changes: 6 additions & 3 deletions apps/astarte_appengine_api/mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,18 @@
"cqex": {:hex, :cqex, "1.0.1", "bc9980ac3b82d039879f8d6ca589deab799fe08f80ff449d60ad709f2524718f", [:mix], [{:cqerl, "~> 2.0.1", [hex: :cqerl, repo: "hexpm", optional: false]}], "hexpm", "1bbf2079c044cbf0f747f60dcf0409a951eaa8f1a2447cd6d80d6ff1b7c4dc6b"},
"credentials_obfuscation": {:hex, :credentials_obfuscation, "3.4.0", "34e18b126b3aefd6e8143776fbe1ceceea6792307c99ac5ee8687911f048cfd7", [:rebar3], [], "hexpm", "738ace0ed5545d2710d3f7383906fc6f6b582d019036e5269c4dbd85dbced566"},
"cyanide": {:hex, :cyanide, "2.0.0", "f97b700b87f9b0679ae812f0c4b7fe35ea6541a4121a096cf10287941b7a6d55", [:mix], [], "hexpm", "7f9748251804c2a2115b539202568e1117ab2f0ae09875853fb89cc94ae19dd1"},
"db_connection": {:hex, :db_connection, "2.3.1", "4c9f3ed1ef37471cbdd2762d6655be11e38193904d9c5c1c9389f1b891a3088e", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm", "abaab61780dde30301d840417890bd9f74131041afd02174cf4e10635b3a63f5"},
"decimal": {:hex, :decimal, "1.9.0", "83e8daf59631d632b171faabafb4a9f4242c514b0a06ba3df493951c08f64d07", [:mix], [], "hexpm", "b1f2343568eed6928f3e751cf2dffde95bfaa19dd95d09e8a9ea92ccfd6f7d85"},
"db_connection": {:hex, :db_connection, "2.6.0", "77d835c472b5b67fc4f29556dee74bf511bbafecdcaf98c27d27fa5918152086", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c2f992d15725e721ec7fbc1189d4ecdb8afef76648c746a8e1cad35e3b8a35f3"},
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
"dialyxir": {:hex, :dialyxir, "1.4.2", "764a6e8e7a354f0ba95d58418178d486065ead1f69ad89782817c296d0d746a5", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "516603d8067b2fd585319e4b13d3674ad4f314a5902ba8130cd97dc902ce6bbd"},
"dialyzex": {:git, "https://github.com/Comcast/dialyzex.git", "cdc7cf71fe6df0ce4cf59e3f497579697a05c989", []},
"ecto": {:hex, :ecto, "3.10.3", "eb2ae2eecd210b4eb8bece1217b297ad4ff824b4384c0e3fdd28aaf96edd6135", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "44bec74e2364d491d70f7e42cd0d690922659d329f6465e89feb8a34e8cd3433"},
"ecto_morph": {:hex, :ecto_morph, "0.1.28", "073c3faf4ff7c496fc2ae8352ea7d8c9b89ae7b2e6995da3027454d44cb547fb", [:mix], [{:ecto, ">= 3.0.3", [hex: :ecto, repo: "hexpm", optional: false]}], "hexpm", "ce39a1252a5b7d58c601beb702eed4303b11016705f02fbae9b78f8fb971b0bc"},
"ecto_sql": {:hex, :ecto_sql, "3.10.2", "6b98b46534b5c2f8b8b5f03f126e75e2a73c64f3c071149d32987a5378b0fdbd", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.10.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "68c018debca57cb9235e3889affdaec7a10616a4e3a80c99fa1d01fdafaa9007"},
"elixir_uuid": {:hex, :elixir_uuid, "1.2.1", "dce506597acb7e6b0daeaff52ff6a9043f5919a4c3315abb4143f0b00378c097", [:mix], [], "hexpm", "f7eba2ea6c3555cea09706492716b0d87397b88946e6380898c2889d68585752"},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
"ex_json_schema": {:hex, :ex_json_schema, "0.7.4", "09eb5b0c8184e5702bc89625a9d0c05c7a0a845d382e9f6f406a0fc1c9a8cc3f", [:mix], [], "hexpm", "45c67fa840f0d719a2b5578126dc29bcdc1f92499c0f61bcb8a3bcb5935f9684"},
"ex_lttb": {:hex, :ex_lttb, "0.3.0", "aec7aab96be6535c4c8f143c2b5f2191a9d1a6f512690ec6d6f4f6bce0223b0f", [:mix], [], "hexpm", "6937bf70307d85781200912c3dcf5e32efdcbdf958e9107ee0a61d4fefc1fddb"},
"exandra": {:hex, :exandra, "0.10.2", "e95dca77501df9ae48f23854224e91712e64d65cd7157e2fe46232ea97918ec6", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.10", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.10", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:xandra, "~> 0.18.0", [hex: :xandra, repo: "hexpm", optional: false]}], "hexpm", "334616b170233828f2acac0b060c3c6c91051848972218d2b159e3a455b07c84"},
"excoveralls": {:hex, :excoveralls, "0.15.0", "ac941bf85f9f201a9626cc42b2232b251ad8738da993cf406a4290cacf562ea4", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "9631912006b27eca30a2f3c93562bc7ae15980afb014ceb8147dc5cdd8f376f1"},
"expo": {:hex, :expo, "0.5.2", "beba786aab8e3c5431813d7a44b828e7b922bfa431d6bfbada0904535342efe2", [:mix], [], "hexpm", "8c9bfa06ca017c9cb4020fabe980bc7fdb1aaec059fd004c2ab3bff03b1c599c"},
"gettext": {:hex, :gettext, "0.24.0", "6f4d90ac5f3111673cbefc4ebee96fe5f37a114861ab8c7b7d5b30a1108ce6d8", [:mix], [{:expo, "~> 0.5.1", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "bdf75cdfcbe9e4622dd18e034b227d77dd17f0f133853a1c73b97b3d6c770e8b"},
Expand All @@ -41,6 +43,7 @@
"mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"},
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
"mox": {:hex, :mox, "0.5.2", "55a0a5ba9ccc671518d068c8dddd20eeb436909ea79d1799e2209df7eaa98b6c", [:mix], [], "hexpm", "df4310628cd628ee181df93f50ddfd07be3e5ecc30232d3b6aadf30bdfe6092b"},
"nimble_options": {:hex, :nimble_options, "1.1.0", "3b31a57ede9cb1502071fade751ab0c7b8dbe75a9a4c2b5bbb0943a690b63172", [:mix], [], "hexpm", "8bbbb3941af3ca9acc7835f5655ea062111c9c27bcac53e004460dfd19008a99"},
"observer_cli": {:hex, :observer_cli, "1.6.1", "d176f967c978ab8b8a29c35c12524f78b7bb36fd4e9b8276dd75c9cb56e07e42", [:mix, :rebar3], [{:recon, "~>2.5.1", [hex: :recon, repo: "hexpm", optional: false]}], "hexpm", "3418e319764b9dff1f469e43cbdffd7fd54ea47cbf765027c557abd146a19fb3"},
"parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"},
"phoenix": {:hex, :phoenix, "1.7.2", "c375ffb482beb4e3d20894f84dd7920442884f5f5b70b9f4528cbe0cedefec63", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.4", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "1ebca94b32b4d0e097ab2444a9742ed8ff3361acad17365e4e6b2e79b4792159"},
Expand Down Expand Up @@ -74,5 +77,5 @@
"uuid": {:hex, :uuid_erl, "2.0.1", "1fd9079c544d521063897887a1c5b3302dca98f9bb06aadcdc6fb0663f256797", [:rebar3], [{:quickrand, "~> 2.0.1", [hex: :quickrand, repo: "hexpm", optional: false]}], "hexpm", "ab57caccd51f170011e5f444ce865f84b41605e483a9efcc468c1afaec87553b"},
"websock": {:hex, :websock, "0.5.1", "c496036ce95bc26d08ba086b2a827b212c67e7cabaa1c06473cd26b40ed8cf10", [:mix], [], "hexpm", "b9f785108b81cd457b06e5f5dabe5f65453d86a99118b2c0a515e1e296dc2d2c"},
"websock_adapter": {:hex, :websock_adapter, "0.5.1", "292e6c56724e3457e808e525af0e9bcfa088cc7b9c798218e78658c7f9b85066", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "8e2e1544bfde5f9d0442f9cec2f5235398b224f75c9e06b60557debf64248ec1"},
"xandra": {:hex, :xandra, "0.13.1", "f82866e6c47527f74f35dd3007b5311121852dd861a29ed1613e27ccfaba0102", [:mix], [{:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.7", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "a2efdb8921e3b694bf3505e40c5ec9d353d8fa3755cec946be7c18b8236d7230"},
"xandra": {:hex, :xandra, "0.18.1", "6ac8794161f69a5ada6e8c197e5e3472f44c94f7b3add208cd3abc8ee135f852", [:mix], [{:decimal, "~> 1.7 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}, {:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "25d74d8101ca303b7be102da14a37629ae94c1bd21827f9d199a27f5e89b785f"},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#
# This file is part of Astarte.
#
# Copyright 2024 SECO Mind Srl
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

defmodule Astarte.AppEngine.API.V2DeviceTest do
use ExUnit.Case, async: true
use ExUnitProperties
use Astarte.Test.Cases.Device
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This use hides the setup step(s) needed to populate the db, so it is unclear where e.g. devices selected at line 39 do come from. It should be possible to clearly identify what is being created, what operation is being performed and what the expected output would be by just looking at a single test.
I find that explicitly implementing setup_all and setup in the test case helps doing so.

The same goes for the other tests on groups and interfaces.


alias Ecto.Changeset
alias StreamData
alias Astarte.Core.Mapping
alias Astarte.Core.Interface
alias Astarte.AppEngine.API.Stats
alias Astarte.AppEngine.API.Stats.DevicesStats
alias Astarte.Test.Setups.Database, as: DatabaseSetup
alias Astarte.Test.Setups.Interface, as: InterfaceSetup
alias Astarte.Test.Generators.String, as: StringGenerator
alias Astarte.Test.Generators.Interface, as: InterfaceGenerator
alias Astarte.Test.Generators.Mapping, as: MappingGenerator
alias Astarte.Test.Generators.Device, as: DeviceGenerator
alias Astarte.Test.Helpers.Database, as: DatabaseHelper

@moduletag :v2
@moduletag :device
@moduletag interface_count: 10
@moduletag device_count: 100

describe "device generator testing" do
property "validate device with pre-generated interfaces", %{interfaces: interfaces} do
check all device <- DeviceGenerator.device(interfaces: interfaces) do
:ok
end
end
end

describe "devices fixtures testing" do
test "validate inserted devices", %{
cluster: cluster,
keyspace: keyspace,
devices: devices
} do
list = DatabaseHelper.select!(:device, cluster, keyspace, devices)
fields = [:device_id, :encoded_id]

for field <- fields do
f = fn l -> Enum.map(l, fn d -> d[field] end) end
devices_ids_a = f.(devices)
devices_ids_b = f.(list)
assert [] === devices_ids_a -- devices_ids_b
assert [] === devices_ids_b -- devices_ids_a
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
#
# This file is part of Astarte.
#
# Copyright 2024 SECO Mind Srl
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

defmodule Astarte.AppEngine.API.V2GroupTest do
use ExUnit.Case, async: true
use ExUnitProperties
use Astarte.Test.Cases.Group
use Astarte.Test.Cases.Conn

alias Astarte.AppEngine.API.Device
alias Astarte.AppEngine.API.Device.DevicesList
alias Astarte.AppEngine.API.Device.DeviceStatus
alias Astarte.Test.Generators.Group, as: GroupGenerator
alias Astarte.Test.Generators.Device, as: DeviceGenerator

@moduletag :v2
@moduletag :group
@moduletag interface_count: 10
@moduletag device_count: 100
@moduletag group_count: 2

describe "create" do
@tag :unit
property "fails when group name is not valid", %{auth_conn: auth_conn, keyspace: keyspace} do
check all {{group_name, error}, devices} <-
tuple({
bind(GroupGenerator.name(), fn name ->
bind(integer(0..2), fn num ->
constant(
case num do
0 -> {"", "can't be blank"}
1 -> {"~" <> name, "is not valid"}
2 -> {"@" <> name, "is not valid"}
end
)
end)
end),
list_of(DeviceGenerator.encoded_id(), min_length: 0, max_length: 1)
}) do
params = %{
"group_name" => group_name,
"devices" => devices
}

response = post(auth_conn, groups_path(auth_conn, :create, keyspace), data: params)
assert [error] === json_response(response, 422)["errors"]["group_name"]
end
end

property "fails when devices list empty", %{auth_conn: auth_conn, keyspace: keyspace} do
@tag :unit
check all group_name <- GroupGenerator.name() do
params = %{
"group_name" => group_name,
"devices" => []
}

response = post(auth_conn, groups_path(auth_conn, :create, keyspace), data: params)

assert ["should have at least 1 item(s)"] ===
json_response(response, 422)["errors"]["devices"]
end
end

property "fails when device does not exist", %{auth_conn: auth_conn, keyspace: keyspace} do
check all group_name <- GroupGenerator.name(),
devices <- list_of(DeviceGenerator.encoded_id(), min_length: 1) do
params = %{
"group_name" => group_name,
"devices" => devices
}

response = post(auth_conn, groups_path(auth_conn, :create, keyspace), data: params)

assert ["must exist (#{Enum.at(devices, 0)} not found)"] ===
json_response(response, 422)["errors"]["devices"]
end
end

test "fails when the group already exists", %{
auth_conn: auth_conn,
cluster: cluster,
keyspace: keyspace,
interfaces: interfaces,
devices: devices,
groups: groups
} do
device_ids = Enum.map(devices, & &1.encoded_id)
existing_group_name = Enum.at(groups, 0).name

params = %{
"group_name" => existing_group_name,
"devices" => device_ids
}

response = post(auth_conn, groups_path(auth_conn, :create, keyspace), data: params)
assert "Group already exists" === json_response(response, 409)["errors"]["detail"]
end

property "success creates groups with valid parameters", %{
auth_conn: auth_conn,
cluster: cluster,
keyspace: keyspace,
interfaces: interfaces,
devices: devices,
groups: groups
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make explicit that these are old groups

Suggested change
groups: groups
groups: old_groups

} do
device_ids = Enum.map(devices, & &1.encoded_id)
old_group_names = Enum.map(groups, & &1.name)

check all group_name <-
filter(GroupGenerator.name(), fn name -> name not in old_group_names end) do
params = %{
"group_name" => group_name,
"devices" => device_ids
}

response = post(auth_conn, groups_path(auth_conn, :create, keyspace), data: params)
assert params === json_response(response, 201)["data"]
response = get(auth_conn, groups_path(auth_conn, :show, keyspace, group_name))
assert group_name === json_response(response, 200)["data"]["group_name"]

for device <- device_ids do
{:ok, %DeviceStatus{groups: groups}} = Device.get_device_status!(keyspace, device)
assert group_name in groups
# TODO right way
# assert [group_name] === groups -- old_group_names
Comment on lines +138 to +142
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're just testing that a group is being created, why checking that every device in the group has it in its status? This is probably a (slightly) different property that should be checked in another test

end
end
end
end
end
Loading
Loading