diff --git a/lib/realtime/api.ex b/lib/realtime/api.ex index b94787853..f368089ea 100644 --- a/lib/realtime/api.ex +++ b/lib/realtime/api.ex @@ -25,6 +25,13 @@ defmodule Realtime.Api do |> repo_replica.preload(:extensions) end + @doc """ + Returns list of tenants with filter options: + * order_by + * search external id + * limit + * ordering (desc / asc) + """ def list_tenants(opts) when is_list(opts) do repo_replica = Repo.replica() diff --git a/mix.exs b/mix.exs index f50a48265..062748fc2 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule Realtime.MixProject do def project do [ app: :realtime, - version: "2.20.3", + version: "2.20.4", elixir: "~> 1.14.0", elixirc_paths: elixirc_paths(Mix.env()), start_permanent: Mix.env() == :prod, diff --git a/test/realtime/api_test.exs b/test/realtime/api_test.exs index 74d5ae1d7..52d4010a1 100644 --- a/test/realtime/api_test.exs +++ b/test/realtime/api_test.exs @@ -1,73 +1,90 @@ defmodule Realtime.ApiTest do - use Realtime.DataCase + use Realtime.DataCase, async: false import Mock alias Realtime.Api - alias Realtime.{Api, RateCounter, GenCounter} + alias Realtime.Api.Extensions + alias Realtime.Api.Tenant + alias Realtime.GenCounter + alias Realtime.RateCounter + + @db_conf Application.compile_env(:realtime, Realtime.Repo) + + @valid_attrs %{ + external_id: "external_id", + name: "localhost", + extensions: [ + %{ + "type" => "postgres_cdc_rls", + "settings" => %{ + "db_host" => @db_conf[:hostname], + "db_name" => @db_conf[:database], + "db_user" => @db_conf[:username], + "db_password" => @db_conf[:password], + "db_port" => "5432", + "poll_interval" => 100, + "poll_max_changes" => 100, + "poll_max_record_bytes" => 1_048_576, + "region" => "us-east-1" + } + } + ], + postgres_cdc_default: "postgres_cdc_rls", + jwt_secret: "new secret", + max_concurrent_users: 200, + max_events_per_second: 100 + } + + @update_attrs %{ + external_id: "external_id1", + jwt_secret: "some updated jwt_secret", + name: "some updated name" + } + @invalid_attrs %{external_id: nil, jwt_secret: nil, name: nil} + + setup do + tenants = [ + tenant_fixture(%{external_id: "external_id1", max_concurrent_users: 10}), + tenant_fixture(%{external_id: "external_id2", max_concurrent_users: 5}), + tenant_fixture(%{external_id: "external_id3", max_concurrent_users: 15}), + tenant_fixture(%{external_id: "external_id4", max_concurrent_users: 20}), + tenant_fixture(%{external_id: "external_id5", max_concurrent_users: 25}) + ] + + dev_tenant = Api.list_tenants(search: "dev_tenant") + tenants = tenants ++ dev_tenant + + %{tenants: tenants} + end describe "tenants" do - alias Realtime.Api.{Tenant, Extensions} - db_conf = Application.compile_env(:realtime, Realtime.Repo) - - @valid_attrs %{ - external_id: "external_id", - name: "localhost", - extensions: [ - %{ - "type" => "postgres_cdc_rls", - "settings" => %{ - "db_host" => db_conf[:hostname], - "db_name" => db_conf[:database], - "db_user" => db_conf[:username], - "db_password" => db_conf[:password], - "db_port" => "5432", - "poll_interval" => 100, - "poll_max_changes" => 100, - "poll_max_record_bytes" => 1_048_576, - "region" => "us-east-1" - } - } - ], - postgres_cdc_default: "postgres_cdc_rls", - jwt_secret: "new secret", - max_concurrent_users: 200, - max_events_per_second: 100 - } - - @update_attrs %{ - external_id: "external_id", - jwt_secret: "some updated jwt_secret", - name: "some updated name" - } - @invalid_attrs %{external_id: nil, jwt_secret: nil, name: nil} - - def tenant_fixture(attrs \\ %{}) do - {:ok, tenant} = - attrs - |> Enum.into(@valid_attrs) - |> Api.create_tenant() - - tenant + test "list_tenants/0 returns all tenants", %{tenants: tenants} do + assert Enum.sort(Api.list_tenants()) == Enum.sort(tenants) end - test "list_tenants/0 returns all tenants" do - tenant = tenant_fixture() + test "list_tenants/1 returns filtered tenants", %{tenants: tenants} do + assert hd(Api.list_tenants(search: hd(tenants).external_id)) == hd(tenants) - assert Api.list_tenants() |> Enum.filter(fn e -> e.external_id == "external_id" end) == [ - tenant - ] - end + assert Api.list_tenants(order_by: "max_concurrent_users") == + Enum.sort_by(tenants, & &1.max_concurrent_users, :desc) - test "get_tenant!/1 returns the tenant with given id" do - tenant = tenant_fixture() + assert Api.list_tenants(order_by: "max_concurrent_users", order: "asc") == + Enum.sort_by(tenants, & &1.max_concurrent_users, :asc) - assert Api.get_tenant!(tenant.id) |> Map.delete(:extensions) == - tenant |> Map.delete(:extensions) + assert Api.list_tenants(order_by: "max_concurrent_users", order: "asc", limit: 2) == + tenants |> Enum.sort_by(& &1.max_concurrent_users, :asc) |> Enum.take(2) + end + + test "get_tenant!/1 returns the tenant with given id", %{tenants: [tenant | _]} do + result = Api.get_tenant!(tenant.id) |> Map.delete(:extensions) + expected = tenant |> Map.delete(:extensions) + assert result == expected end test "create_tenant/1 with valid data creates a tenant" do assert {:ok, %Tenant{} = tenant} = Api.create_tenant(@valid_attrs) + assert tenant.external_id == "external_id" assert tenant.jwt_secret == "YIriPuuJO1uerq5hSZ1W5Q==" assert tenant.name == "localhost" @@ -77,73 +94,54 @@ defmodule Realtime.ApiTest do assert {:error, %Ecto.Changeset{}} = Api.create_tenant(@invalid_attrs) end - test "check get_tenant_by_external_id/1" do - tenant_fixture() - + test "check get_tenant_by_external_id/1", %{tenants: [tenant | _]} do %Tenant{extensions: [%Extensions{} = extension]} = - Api.get_tenant_by_external_id("external_id") + Api.get_tenant_by_external_id(tenant.external_id) assert Map.has_key?(extension.settings, "db_password") password = extension.settings["db_password"] assert ^password = "v1QVng3N+pZd/0AEObABwg==" end - test "update_tenant/2 with valid data updates the tenant" do - tenant = tenant_fixture() - assert {:ok, %Tenant{} = tenant} = Api.update_tenant(tenant, @update_attrs) - assert tenant.external_id == "external_id" + test "update_tenant/2 with valid data updates the tenant", %{tenants: [tenant | _]} do + db_enc_key = Application.get_env(:realtime, :db_enc_key) - assert tenant.jwt_secret == - Realtime.Helpers.encrypt!( - "some updated jwt_secret", - Application.get_env(:realtime, :db_enc_key) - ) + assert {:ok, %Tenant{} = tenant} = Api.update_tenant(tenant, @update_attrs) + assert tenant.external_id == "external_id1" + assert tenant.jwt_secret == Realtime.Helpers.encrypt!("some updated jwt_secret", db_enc_key) assert tenant.name == "some updated name" end - test "update_tenant/2 with invalid data returns error changeset" do - tenant = tenant_fixture() + test "update_tenant/2 with invalid data returns error changeset", %{tenants: [tenant | _]} do assert {:error, %Ecto.Changeset{}} = Api.update_tenant(tenant, @invalid_attrs) end - test "delete_tenant/1 deletes the tenant" do - tenant = tenant_fixture() + test "delete_tenant/1 deletes the tenant", %{tenants: [tenant | _]} do assert {:ok, %Tenant{}} = Api.delete_tenant(tenant) assert_raise Ecto.NoResultsError, fn -> Api.get_tenant!(tenant.id) end end - test "delete_tenant_by_external_id/1 deletes the tenant" do - tenant = tenant_fixture() + test "delete_tenant_by_external_id/1 deletes the tenant", %{tenants: [tenant | _]} do assert true == Api.delete_tenant_by_external_id(tenant.external_id) assert false == Api.delete_tenant_by_external_id("undef_tenant") assert_raise Ecto.NoResultsError, fn -> Api.get_tenant!(tenant.id) end end - test "change_tenant/1 returns a tenant changeset" do - tenant = tenant_fixture() + test "change_tenant/1 returns a tenant changeset", %{tenants: [tenant | _]} do assert %Ecto.Changeset{} = Api.change_tenant(tenant) end - test "list_extensions/1 " do - assert length(Api.list_extensions()) == 1 + test "list_extensions/1 ", %{tenants: tenants} do + assert length(Api.list_extensions()) == length(tenants) end - test "preload_counters/1 " do - tenant = tenant_fixture() + test "preload_counters/1 ", %{tenants: [tenant | _]} do assert Api.preload_counters(nil) == nil with_mocks([ - {GenCounter, [], - [ - get: fn _ -> - {:ok, 1} - end - ]}, - {RateCounter, [], - [ - get: fn _ -> {:ok, %RateCounter{avg: 2}} end - ]} + {GenCounter, [], [get: fn _ -> {:ok, 1} end]}, + {RateCounter, [], [get: fn _ -> {:ok, %RateCounter{avg: 2}} end]} ]) do counters = Api.preload_counters(tenant) assert counters.events_per_second_rolling == 2 @@ -153,9 +151,18 @@ defmodule Realtime.ApiTest do assert Api.preload_counters(nil, :any) == nil end - test "rename_settings_field/2" do + test "rename_settings_field/2", %{tenants: [tenant | _]} do Api.rename_settings_field("poll_interval_ms", "poll_interval") - assert %{extensions: [%{settings: %{"poll_interval" => _}}]} = tenant_fixture() + assert %{extensions: [%{settings: %{"poll_interval" => _}}]} = tenant end end + + defp tenant_fixture(attrs) do + {:ok, tenant} = + attrs + |> Enum.into(@valid_attrs) + |> Api.create_tenant() + + tenant + end end