ash_cubdb/test/ash_cub_db/data_layer_test.exs

234 lines
5.7 KiB
Elixir

defmodule AshCubDB.DataLayerTest do
@moduledoc false
use ExUnit.Case, async: true
alias Ash.{Error.Query.NotFound, Query}
alias AshCubDB.Info
alias Support.{Author, Domain, Post}
import Support.Factory
require Query
setup do
on_exit(fn ->
AshCubDB.clear(Post)
AshCubDB.clear(Author)
end)
end
describe "transformer" do
test "it correctly infers the data directory" do
assert {:ok, path} = Info.cubdb_directory(Post)
assert path =~ ~r/ash_cubdb\/priv\/cubdb\/post$/
end
end
describe "create" do
test "it creates a record" do
params = params!(Post)
assert {:ok, post} = Post.create(params)
assert [{key, value}] = dump(Post)
assert key == {Ecto.UUID.dump!(post.id)}
assert value == {nil, post.body, post.title}
end
test "it honours context multitenancy" do
insert!(Author, count: 3)
assert {:ok, author} =
Author
|> params!()
|> Author.create(tenant: :tenant)
keys =
dump(Author)
|> Enum.map(&elem(&1, 0))
assert {:tenant, {Ecto.UUID.dump!(author.id)}} in keys
assert Enum.count(keys, &(elem(&1, 0) == nil)) == 3
end
test "it doesn't allow IDs to conflict" do
uuid = Ash.UUID.generate()
params =
params!(Post)
|> Map.put(:id, uuid)
assert {:ok, %{id: ^uuid}} = Post.create(params)
assert {:error, invalid} = Post.create(params)
assert Exception.message(invalid) =~ "id: has already been taken"
end
end
describe "upsert" do
test "it creates a record" do
params = params!(Post)
assert {:ok, post} = Post.create(params, upsert?: true)
assert [{key, value}] = dump(Post)
assert key == {Ecto.UUID.dump!(post.id)}
assert value == {nil, post.body, post.title}
end
test "it updates an existing record" do
params = params!(Post)
assert {:ok, post} = Post.create(params)
params =
params
|> Map.put(:title, Faker.Lorem.sentence())
|> Map.put(:id, post.id)
assert {:ok, updated} = Post.create(params, upsert?: true)
assert updated.id == post.id
assert updated.title == params[:title]
assert updated.title != post.title
end
end
describe "read" do
test "non-tenant scoped read" do
expected = insert!(Post, count: 3)
assert {:ok, actual} = Post.read()
assert Enum.all?(actual, &is_struct(&1, Post))
for post <- expected do
assert post.id in Enum.map(actual, & &1.id)
end
end
test "tenant scoped read" do
insert!(Author, count: 3)
expected =
Post
|> params!(count: 3)
|> Enum.map(&Post.create!(&1, tenant: :wat))
assert {:ok, actual} = Post.read(tenant: :wat)
expected_ids = expected |> Enum.map(& &1.id) |> Enum.sort()
actual_ids = actual |> Enum.map(& &1.id) |> Enum.sort()
assert expected_ids == actual_ids
end
test "filters work" do
expected = insert!(Author, attrs: %{name: "Marty McFly"})
insert!(Author, count: 3)
[actual] =
Author
|> Query.filter(name: "Marty McFly")
|> Ash.read!()
assert expected.id == actual.id
end
test "sorting" do
insert!(Author, attrs: %{name: "Alice"})
insert!(Author, attrs: %{name: "Mallory"})
insert!(Author, attrs: %{name: "Bob"})
sorted =
Author
|> Query.sort(name: :desc)
|> Ash.read!()
assert Enum.map(sorted, &to_string(&1.name)) == ["Mallory", "Bob", "Alice"]
end
test "limit" do
insert!(Author, count: 3)
assert [_] =
Author
|> Query.limit(1)
|> Ash.read!()
end
test "offset" do
insert(Author, count: 3)
assert [_, _] =
Author
|> Query.offset(1)
|> Ash.read!()
end
test "distinct" do
author = insert!(Author)
insert!(Author, count: 3, attrs: %{name: author.name})
assert [selected] =
Author
|> Query.distinct(:name)
|> Ash.read!()
assert selected.name == author.name
end
test "distinct sort" do
post = insert!(Post, attrs: %{body: "Alice is cool"})
insert!(Post, attrs: %{title: post.title, body: "Bob is cool"})
insert!(Post, attrs: %{title: post.title, body: "Mallory is cool"})
assert [selected] =
Post
|> Query.distinct(:title)
|> Query.distinct_sort(body: :desc)
|> Ash.read!()
assert selected.title == post.title
assert selected.body == "Mallory is cool"
end
end
describe "update" do
test "records can be updated" do
post = insert!(Post)
params = Post |> params!() |> Map.take([:title])
assert {:ok, updated} = Post.update(post, params)
assert updated.id == post.id
assert updated.title == params.title
assert {:ok, updated} = Post.get(post.id)
assert updated.id == post.id
assert updated.title == params.title
end
end
describe "destroy" do
test "records can be destroyed" do
post = insert!(Post)
assert :ok = Post.destroy(post)
assert {:error, %NotFound{}} = Post.get(post.id)
end
end
describe "calculations" do
test "can be loaded" do
post = insert!(Post)
{:ok, post} = Post.get(post.id, load: :all_text)
assert post.all_text == post.title <> post.body
end
end
defp dump(resource) do
resource
|> via()
|> CubDB.select()
|> Enum.reject(&(elem(&1, 0) == :__metadata__))
|> Enum.to_list()
end
defp via(resource), do: {:via, Registry, {AshCubDB.Registry, resource}}
end