zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

sandbox.exs (9290B)


      1 defmodule Ecto.Integration.SandboxTest do
      2   use ExUnit.Case
      3 
      4   alias Ecto.Adapters.SQL.Sandbox
      5   alias Ecto.Integration.{PoolRepo, TestRepo}
      6   alias Ecto.Integration.Post
      7 
      8   import ExUnit.CaptureLog
      9 
     10   Application.put_env(:ecto_sql, __MODULE__.DynamicRepo, Application.compile_env(:ecto_sql, TestRepo))
     11 
     12   defmodule DynamicRepo do
     13     use Ecto.Repo, otp_app: :ecto_sql, adapter: TestRepo.__adapter__()
     14   end
     15 
     16   describe "errors" do
     17     test "raises if repo doesn't exist" do
     18       assert_raise UndefinedFunctionError, ~r"function UnknownRepo.get_dynamic_repo/0 is undefined", fn ->
     19         Sandbox.mode(UnknownRepo, :manual)
     20       end
     21     end
     22 
     23     test "raises if repo is not started" do
     24       assert_raise RuntimeError, ~r"could not lookup Ecto repo #{inspect DynamicRepo} because it was not started", fn ->
     25         Sandbox.mode(DynamicRepo, :manual)
     26       end
     27     end
     28 
     29     test "raises if repo is not using sandbox" do
     30       assert_raise RuntimeError, ~r"cannot invoke sandbox operation with pool DBConnection", fn ->
     31         Sandbox.mode(PoolRepo, :manual)
     32       end
     33 
     34       assert_raise RuntimeError, ~r"cannot invoke sandbox operation with pool DBConnection", fn ->
     35         Sandbox.checkout(PoolRepo)
     36       end
     37     end
     38 
     39     test "includes link to SQL sandbox on ownership errors" do
     40       assert_raise DBConnection.OwnershipError,
     41                ~r"See Ecto.Adapters.SQL.Sandbox docs for more information.", fn ->
     42         TestRepo.all(Post)
     43       end
     44     end
     45   end
     46 
     47   describe "mode" do
     48     test "uses the repository when checked out" do
     49       assert_raise DBConnection.OwnershipError, ~r"cannot find ownership process", fn ->
     50         TestRepo.all(Post)
     51       end
     52       Sandbox.checkout(TestRepo)
     53       assert TestRepo.all(Post) == []
     54       Sandbox.checkin(TestRepo)
     55       assert_raise DBConnection.OwnershipError, ~r"cannot find ownership process", fn ->
     56         TestRepo.all(Post)
     57       end
     58     end
     59 
     60     test "uses the repository when allowed from another process" do
     61       assert_raise DBConnection.OwnershipError, ~r"cannot find ownership process", fn ->
     62         TestRepo.all(Post)
     63       end
     64 
     65       parent = self()
     66 
     67       Task.start_link fn ->
     68         Sandbox.checkout(TestRepo)
     69         Sandbox.allow(TestRepo, self(), parent)
     70         send(parent, :allowed)
     71         Process.sleep(:infinity)
     72       end
     73 
     74       assert_receive :allowed
     75       assert TestRepo.all(Post) == []
     76     end
     77 
     78     test "uses the repository when allowed from another process by registered name" do
     79       assert_raise DBConnection.OwnershipError, ~r"cannot find ownership process", fn ->
     80         TestRepo.all(Post)
     81       end
     82 
     83       parent = self()
     84       Process.register(parent, __MODULE__)
     85 
     86       Task.start_link fn ->
     87         Sandbox.checkout(TestRepo)
     88         Sandbox.allow(TestRepo, self(), __MODULE__)
     89         send(parent, :allowed)
     90         Process.sleep(:infinity)
     91       end
     92 
     93       assert_receive :allowed
     94       assert TestRepo.all(Post) == []
     95 
     96       Process.unregister(__MODULE__)
     97     end
     98 
     99     test "uses the repository when shared from another process" do
    100       assert_raise DBConnection.OwnershipError, ~r"cannot find ownership process", fn ->
    101         TestRepo.all(Post)
    102       end
    103 
    104       parent = self()
    105 
    106       Task.start_link(fn ->
    107         Sandbox.checkout(TestRepo)
    108         Sandbox.mode(TestRepo, {:shared, self()})
    109         send(parent, :shared)
    110         Process.sleep(:infinity)
    111       end)
    112 
    113       assert_receive :shared
    114       assert Task.async(fn -> TestRepo.all(Post) end) |> Task.await == []
    115     after
    116       Sandbox.mode(TestRepo, :manual)
    117     end
    118 
    119     test "works with a dynamic repo" do
    120       repo_pid = start_supervised!({DynamicRepo, name: nil})
    121       DynamicRepo.put_dynamic_repo(repo_pid)
    122 
    123       assert Sandbox.mode(DynamicRepo, :manual) == :ok
    124 
    125       assert_raise DBConnection.OwnershipError, ~r"cannot find ownership process", fn ->
    126         DynamicRepo.all(Post)
    127       end
    128 
    129       Sandbox.checkout(DynamicRepo)
    130       assert DynamicRepo.all(Post) == []
    131     end
    132 
    133     test "works with a repo pid" do
    134       repo_pid = start_supervised!({DynamicRepo, name: nil})
    135       DynamicRepo.put_dynamic_repo(repo_pid)
    136 
    137       assert Sandbox.mode(repo_pid, :manual) == :ok
    138 
    139       assert_raise DBConnection.OwnershipError, ~r"cannot find ownership process", fn ->
    140         DynamicRepo.all(Post)
    141       end
    142 
    143       Sandbox.checkout(repo_pid)
    144       assert DynamicRepo.all(Post) == []
    145     end
    146   end
    147 
    148   describe "savepoints" do
    149     test "runs inside a sandbox that is rolled back on checkin" do
    150       Sandbox.checkout(TestRepo)
    151       assert TestRepo.insert(%Post{})
    152       assert TestRepo.all(Post) != []
    153       Sandbox.checkin(TestRepo)
    154       Sandbox.checkout(TestRepo)
    155       assert TestRepo.all(Post) == []
    156       Sandbox.checkin(TestRepo)
    157     end
    158 
    159     test "runs inside a sandbox that may be disabled" do
    160       Sandbox.checkout(TestRepo, sandbox: false)
    161       assert TestRepo.insert(%Post{})
    162       assert TestRepo.all(Post) != []
    163       Sandbox.checkin(TestRepo)
    164 
    165       Sandbox.checkout(TestRepo)
    166       assert {1, _} = TestRepo.delete_all(Post)
    167       Sandbox.checkin(TestRepo)
    168 
    169       Sandbox.checkout(TestRepo, sandbox: false)
    170       assert {1, _} = TestRepo.delete_all(Post)
    171       Sandbox.checkin(TestRepo)
    172     end
    173 
    174     test "runs inside a sandbox with caller data when preloading associations" do
    175       Sandbox.checkout(TestRepo)
    176       assert TestRepo.insert(%Post{})
    177       parent = self()
    178 
    179       Task.start_link fn ->
    180         Sandbox.allow(TestRepo, parent, self())
    181         assert [_] = TestRepo.all(Post) |> TestRepo.preload([:author, :comments])
    182         send parent, :success
    183       end
    184 
    185       assert_receive :success
    186     end
    187 
    188     test "runs inside a sidebox with custom ownership timeout" do
    189       :ok = Sandbox.checkout(TestRepo, ownership_timeout: 200)
    190       parent = self()
    191 
    192       assert capture_log(fn ->
    193         {:ok, pid} =
    194           Task.start(fn ->
    195             Sandbox.allow(TestRepo, parent, self())
    196             TestRepo.transaction(fn -> Process.sleep(500) end)
    197           end)
    198 
    199         ref = Process.monitor(pid)
    200         assert_receive {:DOWN, ^ref, _, ^pid, _}, 1000
    201       end) =~ "it owned the connection for longer than 200ms"
    202     end
    203 
    204     test "does not taint the sandbox on query errors" do
    205       Sandbox.checkout(TestRepo)
    206 
    207       {:ok, _}    = TestRepo.insert(%Post{}, skip_transaction: true)
    208       {:error, _} = TestRepo.query("INVALID")
    209       {:ok, _}    = TestRepo.insert(%Post{}, skip_transaction: true)
    210 
    211       Sandbox.checkin(TestRepo)
    212     end
    213   end
    214 
    215   describe "transactions" do
    216     @tag :transaction_isolation
    217     test "with custom isolation level" do
    218       Sandbox.checkout(TestRepo, isolation: "READ UNCOMMITTED")
    219 
    220       # Setting it to the same level later on works
    221       TestRepo.query!("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED")
    222 
    223       # Even inside a transaction
    224       TestRepo.transaction fn ->
    225         TestRepo.query!("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED")
    226       end
    227     end
    228 
    229     test "disconnects on transaction timeouts" do
    230       Sandbox.checkout(TestRepo)
    231 
    232       assert capture_log(fn ->
    233         {:error, :rollback} =
    234           TestRepo.transaction(fn -> Process.sleep(1000) end, timeout: 100)
    235       end) =~ "timed out"
    236 
    237       Sandbox.checkin(TestRepo)
    238     end
    239   end
    240 
    241   describe "checkouts" do
    242     test "with transaction inside checkout" do
    243       Sandbox.checkout(TestRepo)
    244       refute TestRepo.checked_out?()
    245       refute TestRepo.in_transaction?()
    246 
    247       TestRepo.checkout(fn ->
    248         assert TestRepo.checked_out?()
    249         refute TestRepo.in_transaction?()
    250         TestRepo.transaction(fn ->
    251           assert TestRepo.checked_out?()
    252           assert TestRepo.in_transaction?()
    253         end)
    254         assert TestRepo.checked_out?()
    255         refute TestRepo.in_transaction?()
    256       end)
    257 
    258       refute TestRepo.checked_out?()
    259       refute TestRepo.in_transaction?()
    260     end
    261 
    262     test "with checkout inside transaction" do
    263       Sandbox.checkout(TestRepo)
    264       refute TestRepo.checked_out?()
    265       refute TestRepo.in_transaction?()
    266 
    267       TestRepo.transaction(fn ->
    268         assert TestRepo.checked_out?()
    269         assert TestRepo.in_transaction?()
    270         TestRepo.checkout(fn ->
    271           assert TestRepo.checked_out?()
    272           assert TestRepo.in_transaction?()
    273         end)
    274         assert TestRepo.checked_out?()
    275         assert TestRepo.in_transaction?()
    276       end)
    277 
    278       refute TestRepo.checked_out?()
    279       refute TestRepo.in_transaction?()
    280     end
    281   end
    282 
    283   describe "start_owner!/2" do
    284     test "checks out the connection" do
    285       assert_raise DBConnection.OwnershipError, ~r"cannot find ownership process", fn ->
    286         TestRepo.all(Post)
    287       end
    288 
    289       owner = Sandbox.start_owner!(TestRepo)
    290       assert TestRepo.all(Post) == []
    291 
    292       :ok = Sandbox.stop_owner(owner)
    293       refute Process.alive?(owner)
    294     end
    295 
    296     test "can set shared mode" do
    297       assert_raise DBConnection.OwnershipError, ~r"cannot find ownership process", fn ->
    298         TestRepo.all(Post)
    299       end
    300 
    301       parent = self()
    302 
    303       Task.start_link(fn ->
    304         owner = Sandbox.start_owner!(TestRepo, shared: true)
    305         send(parent, {:owner, owner})
    306         Process.sleep(:infinity)
    307       end)
    308 
    309       assert_receive {:owner, owner}
    310       assert TestRepo.all(Post) == []
    311       :ok = Sandbox.stop_owner(owner)
    312     after
    313       Sandbox.mode(TestRepo, :manual)
    314     end
    315   end
    316 end