Skip to content

Commit f84e525

Browse files
committed
feat: enhance transaction handling in Repo module
- Updated the `transact/2` function to handle `:ok` return values, allowing for successful transactions without a result. - Added a new test suite for `Repo.transact/2` to cover various scenarios including successful commits, rollbacks on errors, and nested transactions.
1 parent cb1a086 commit f84e525

File tree

2 files changed

+150
-0
lines changed

2 files changed

+150
-0
lines changed

lib/algora/repo.ex

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,10 @@ defmodule Algora.Repo do
6161
end
6262

6363
case result do
64+
:ok -> nil
6465
{:ok, result} -> result
6566
{:error, reason} -> repo.rollback(reason)
67+
error -> repo.rollback(error)
6668
end
6769
end,
6870
opts

test/algora/repo_test.exs

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
defmodule Algora.RepoTest do
2+
use Algora.DataCase
3+
4+
alias Algora.Accounts.User
5+
alias Algora.Repo
6+
7+
describe "transact/2" do
8+
test "commits transaction when {:ok, result} is returned" do
9+
user = insert(:user)
10+
original_email = user.email
11+
12+
{:ok, result} =
13+
Repo.transact(fn _repo ->
14+
{:ok, updated_user} = user |> Ecto.Changeset.change(%{email: "[email protected]"}) |> Repo.update()
15+
{:ok, updated_user}
16+
end)
17+
18+
assert result.email == "[email protected]"
19+
assert Repo.get!(User, user.id).email == "[email protected]"
20+
refute Repo.get!(User, user.id).email == original_email
21+
end
22+
23+
test "commits transaction when :ok is returned" do
24+
user = insert(:user)
25+
original_email = user.email
26+
27+
{:ok, result} =
28+
Repo.transact(fn _repo ->
29+
{:ok, _updated_user} = user |> Ecto.Changeset.change(%{email: "[email protected]"}) |> Repo.update()
30+
:ok
31+
end)
32+
33+
assert result == nil
34+
assert Repo.get!(User, user.id).email == "[email protected]"
35+
refute Repo.get!(User, user.id).email == original_email
36+
end
37+
38+
test "rolls back transaction when anything else is returned" do
39+
user = insert(:user)
40+
original_email = user.email
41+
42+
# Test plain error atom
43+
{:error, result} =
44+
Repo.transact(fn _repo ->
45+
{:ok, _updated_user} = user |> Ecto.Changeset.change(%{email: "[email protected]"}) |> Repo.update()
46+
:error
47+
end)
48+
49+
assert result == :error
50+
assert Repo.get!(User, user.id).email == original_email
51+
52+
# Test error tuple
53+
{:error, result} =
54+
Repo.transact(fn _repo ->
55+
{:ok, _updated_user} = user |> Ecto.Changeset.change(%{email: "[email protected]"}) |> Repo.update()
56+
{:error, "reason"}
57+
end)
58+
59+
assert result == "reason"
60+
assert Repo.get!(User, user.id).email == original_email
61+
62+
# Test unexpected return value
63+
{:error, result} =
64+
Repo.transact(fn _repo ->
65+
{:ok, _updated_user} = user |> Ecto.Changeset.change(%{email: "[email protected]"}) |> Repo.update()
66+
"unexpected"
67+
end)
68+
69+
assert result == "unexpected"
70+
assert Repo.get!(User, user.id).email == original_email
71+
end
72+
73+
test "rolls back transaction when an error is raised" do
74+
user = insert(:user)
75+
original_email = user.email
76+
77+
assert_raise RuntimeError, "boom", fn ->
78+
Repo.transact(fn _repo ->
79+
{:ok, _updated_user} = user |> Ecto.Changeset.change(%{email: "[email protected]"}) |> Repo.update()
80+
raise "boom"
81+
end)
82+
end
83+
84+
assert Repo.get!(User, user.id).email == original_email
85+
end
86+
87+
test "commits nested transactions when all succeed" do
88+
user = insert(:user)
89+
original_email = user.email
90+
91+
{:ok, result} =
92+
Repo.transact(fn _repo ->
93+
{:ok, user1} = user |> Ecto.Changeset.change(%{email: "[email protected]"}) |> Repo.update()
94+
95+
{:ok, user2} =
96+
Repo.transact(fn _repo ->
97+
user1 |> Ecto.Changeset.change(%{email: "[email protected]"}) |> Repo.update()
98+
end)
99+
100+
{:ok, user2}
101+
end)
102+
103+
assert result.email == "[email protected]"
104+
assert Repo.get!(User, user.id).email == "[email protected]"
105+
refute Repo.get!(User, user.id).email == original_email
106+
end
107+
108+
test "rolls back nested transactions when inner transaction fails" do
109+
user = insert(:user)
110+
original_email = user.email
111+
112+
{:error, result} =
113+
Repo.transact(fn _repo ->
114+
{:ok, user1} = user |> Ecto.Changeset.change(%{email: "[email protected]"}) |> Repo.update()
115+
116+
Repo.transact(fn _repo ->
117+
{:ok, _user2} = user1 |> Ecto.Changeset.change(%{email: "[email protected]"}) |> Repo.update()
118+
{:error, :inner_failed}
119+
end)
120+
121+
{:ok, user1}
122+
end)
123+
124+
assert result == :rollback
125+
assert Repo.get!(User, user.id).email == original_email
126+
end
127+
128+
test "rolls back nested transactions when outer transaction fails" do
129+
user = insert(:user)
130+
original_email = user.email
131+
132+
{:error, result} =
133+
Repo.transact(fn _repo ->
134+
{:ok, user1} = user |> Ecto.Changeset.change(%{email: "[email protected]"}) |> Repo.update()
135+
136+
{:ok, _user2} =
137+
Repo.transact(fn _repo ->
138+
user1 |> Ecto.Changeset.change(%{email: "[email protected]"}) |> Repo.update()
139+
end)
140+
141+
{:error, :outer_failed}
142+
end)
143+
144+
assert result == :outer_failed
145+
assert Repo.get!(User, user.id).email == original_email
146+
end
147+
end
148+
end

0 commit comments

Comments
 (0)