234 lines
5.7 KiB
Elixir
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
|