From db24c6a6da39080022eab9190f7aecee1a5ad35f Mon Sep 17 00:00:00 2001 From: smaximov Date: Mon, 5 Mar 2018 16:15:55 +0300 Subject: [PATCH 1/3] Fix transaction in Blog.Accounts.create_user --- lib/blog/accounts/accounts.ex | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/blog/accounts/accounts.ex b/lib/blog/accounts/accounts.ex index 69e5c52..5b4b529 100644 --- a/lib/blog/accounts/accounts.ex +++ b/lib/blog/accounts/accounts.ex @@ -13,6 +13,9 @@ defmodule Blog.Accounts do with {:ok, contact} <- create_contact(contact_attrs), {:ok, user} <- do_create_user(user_attrs, contact) do %{user | contacts: [contact]} + else + {:error, changeset} -> + Repo.rollback(changeset) end end @@ -21,14 +24,14 @@ defmodule Blog.Accounts do def create_contact(attrs) do attrs |> Accounts.Contact.changeset - |> Blog.Repo.insert + |> Repo.insert end defp do_create_user(attrs, contact) do attrs |> Map.put(:contact_id, contact.id) |> Accounts.User.changeset - |> Blog.Repo.insert + |> Repo.insert end end From d58b8ff9998753b75cb208e65cf6ca4712ee6fe4 Mon Sep 17 00:00:00 2001 From: smaximov Date: Mon, 5 Mar 2018 17:14:57 +0300 Subject: [PATCH 2/3] Transaction tests --- test/blog/accounts/accounts_test.exs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 test/blog/accounts/accounts_test.exs diff --git a/test/blog/accounts/accounts_test.exs b/test/blog/accounts/accounts_test.exs new file mode 100644 index 0000000..84a0d79 --- /dev/null +++ b/test/blog/accounts/accounts_test.exs @@ -0,0 +1,18 @@ +defmodule Blog.AccountsTest do + use Blog.DataCase + + alias Blog.Accounts + + test "create_user/1 returns {:error, changeset} and rolles back transaction on error" do + attrs = %{ + contact: %{type: "phone", value: "+1 000 000 0000"}, + name: "", + password: "password" + } + + contact_count = Repo.aggregate(Accounts.Contact, :count, :id) + + assert {:error, %Ecto.Changeset{}} = Accounts.create_user(attrs) + assert ^contact_count = Repo.aggregate(Accounts.Contact, :count, :id) + end +end From ec357ca061e1a38eac6eab50a122780ebfbd2e33 Mon Sep 17 00:00:00 2001 From: smaximov Date: Mon, 5 Mar 2018 17:24:49 +0300 Subject: [PATCH 3/3] Validate uniqueness on (type, value) columns of the `contacts` table --- lib/blog/accounts/contact.ex | 1 + test/blog/accounts/accounts_test.exs | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/lib/blog/accounts/contact.ex b/lib/blog/accounts/contact.ex index 9779055..c598b9f 100644 --- a/lib/blog/accounts/contact.ex +++ b/lib/blog/accounts/contact.ex @@ -24,6 +24,7 @@ defmodule Blog.Accounts.Contact do contact |> cast(attrs, [:type, :value]) |> validate_required([:type, :value]) + |> unique_constraint(:value, name: :contacts_type_value_index) end end diff --git a/test/blog/accounts/accounts_test.exs b/test/blog/accounts/accounts_test.exs index 84a0d79..b677efb 100644 --- a/test/blog/accounts/accounts_test.exs +++ b/test/blog/accounts/accounts_test.exs @@ -15,4 +15,13 @@ defmodule Blog.AccountsTest do assert {:error, %Ecto.Changeset{}} = Accounts.create_user(attrs) assert ^contact_count = Repo.aggregate(Accounts.Contact, :count, :id) end + + test "create_user/1 discards duplicate contacts" do + contact = %{type: "phone", value: "+1 000 000 0000"} + + assert {:ok, _} = Accounts.create_user(%{contact: contact, name: "name", password: "password"}) + + assert {:error, changeset} = Accounts.create_user(%{contact: contact, name: "other", password: "password"}) + assert "has already been taken" in errors_on(changeset).value + end end