epgsql_pool_SUITE.erl 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. -module(epgsql_pool_SUITE).
  2. %% test needs connection to database
  3. %% and database should be inited with ./testdb_schema.sql
  4. -include("epgsql_pool.hrl").
  5. -include_lib("epgsql/include/epgsql.hrl").
  6. -include_lib("common_test/include/ct.hrl").
  7. -include_lib("eunit/include/eunit.hrl").
  8. -export([all/0,
  9. init_per_suite/1, end_per_suite/1, init_per_testcase/2, end_per_testcase/2,
  10. equery_test/1, transaction_test/1, reconnect_test/1, error_handler_test/1
  11. ]).
  12. -define(SELECT_ITEMS_QUERY, "SELECT id, category_id, title, num FROM item ORDER by id ASC").
  13. all() ->
  14. [equery_test,
  15. transaction_test,
  16. reconnect_test,
  17. error_handler_test
  18. ].
  19. init_per_suite(Config) ->
  20. application:ensure_all_started(epgsql_pool),
  21. Config.
  22. end_per_suite(Config) ->
  23. application:stop(epgsql_pool),
  24. Config.
  25. init_per_testcase(_, Config) ->
  26. Params = #epgsql_connection_params{host = "localhost", port = 5432, username = "test", password = "test", database = "testdb"},
  27. epgsql_pool_settings:set_connection_params(my_pool, Params),
  28. {ok, _} = epgsql_pool:start(my_pool, 5, 10),
  29. epgsql_pool:equery(my_pool, "TRUNCATE TABLE item"),
  30. epgsql_pool:equery(my_pool, "TRUNCATE TABLE category CASCADE"),
  31. Config.
  32. end_per_testcase(_, Config) ->
  33. ok = epgsql_pool:stop(my_pool),
  34. Config.
  35. equery_test(Config) ->
  36. {ok, 3, _, Ids} = epgsql_pool:equery(my_pool,
  37. "INSERT INTO category (title) "
  38. "VALUES ('cat 1'), ('cat 2'), ('cat 3') "
  39. "RETURNING id"),
  40. WaitForRows = lists:map(fun({{Id}, Title}) -> {Id, Title} end,
  41. lists:zip(Ids, [<<"cat 1">>, <<"cat 2">>, <<"cat 3">>])),
  42. {ok, _, Rows} = epgsql_pool:equery(my_pool, "SELECT id, title FROM category ORDER by id ASC"),
  43. ct:pal("Rows ~p", [Rows]),
  44. ?assertEqual(WaitForRows, Rows),
  45. ok.
  46. transaction_test(Config) ->
  47. {FirstCatId, CatIds2, ItemIds2} =
  48. epgsql_pool:transaction(my_pool,
  49. fun(Worker) ->
  50. ct:pal("worker:~p", [Worker]),
  51. {ok, 3, _, CatIds0} =
  52. epgsql_pool:equery(Worker,
  53. "INSERT INTO category (title) "
  54. "VALUES ('cat 4'), ('cat 5'), ('cat 6') "
  55. "RETURNING id"),
  56. CatIds1 = lists:map(fun({Cid}) -> Cid end, CatIds0),
  57. CatId = hd(CatIds1),
  58. {ok, 2, _, ItemIds0} =
  59. epgsql_pool:equery(Worker,
  60. "INSERT INTO item (category_id, title, num) "
  61. "VALUES ($1, 'item 1', 5), ($1, 'item 2', 7) "
  62. "RETURNING id", [CatId]),
  63. ItemIds1 = lists:map(fun({Iid}) -> Iid end, ItemIds0),
  64. {CatId, CatIds1, ItemIds1}
  65. end),
  66. WaitForCats = lists:zip(CatIds2, [<<"cat 4">>, <<"cat 5">>, <<"cat 6">>]),
  67. {ok, _, CatRows} = epgsql_pool:equery(my_pool, "SELECT id, title FROM category ORDER by id ASC"),
  68. ct:pal("CatRows ~p", [CatRows]),
  69. ?assertEqual(WaitForCats, CatRows),
  70. WaitForItems = lists:map(fun({ItemId, {Title, Num}}) -> {ItemId, FirstCatId, Title, Num} end,
  71. lists:zip(ItemIds2, [{<<"item 1">>, 5}, {<<"item 2">>, 7}])),
  72. {ok, _, ItemRows} = epgsql_pool:equery(my_pool, ?SELECT_ITEMS_QUERY),
  73. ct:pal("ItemRows ~p", [ItemRows]),
  74. ?assertEqual(WaitForItems, ItemRows),
  75. try
  76. epgsql_pool:transaction(my_pool,
  77. fun(Worker) ->
  78. ct:pal("worker:~p", [Worker]),
  79. {ok, 2} =
  80. epgsql_pool:equery(Worker,
  81. "INSERT INTO item (category_id, title, num) "
  82. "VALUES ($1, 'item 3', 55), ($1, 'item 4', 77) ",
  83. [FirstCatId]),
  84. {ok, _, ItemRows2} = epgsql_pool:equery(Worker, ?SELECT_ITEMS_QUERY),
  85. ct:pal("ItemRows2 ~p", [ItemRows2]),
  86. ?assertMatch([{_, FirstCatId, <<"item 1">>, 5},
  87. {_, FirstCatId, <<"item 2">>, 7},
  88. {_, FirstCatId, <<"item 3">>, 55},
  89. {_, FirstCatId, <<"item 4">>, 77}],
  90. ItemRows2),
  91. throw(cancel_transaction)
  92. end)
  93. catch
  94. throw:cancel_transaction -> ok
  95. end,
  96. %% items not changes after calcelled transaction
  97. {ok, _, ItemRows} = epgsql_pool:equery(my_pool, ?SELECT_ITEMS_QUERY),
  98. ok.
  99. reconnect_test(Config) ->
  100. Worker = pooler:take_member(my_pool, 1000) ,
  101. {state, my_pool, #epgsql_connection{sock = Sock1}} = sys:get_state(Worker),
  102. ct:pal("Worker: ~p, sock: ~p", [Worker, Sock1]),
  103. R1 = epgsql_pool:equery(Worker, ?SELECT_ITEMS_QUERY),
  104. ct:pal("first query ~p", [R1]),
  105. {ok, _, []} = R1,
  106. ct:pal("~p close_connection", [Sock1]),
  107. exit(Sock1, close_connection),
  108. R2 = epgsql_pool:equery(Worker, "select * from item"),
  109. ct:pal("second query goes immediatelly ~p", [R2]),
  110. {error, reconnecting} = R2,
  111. timer:sleep(50),
  112. R3 = epgsql_pool:equery(Worker, "select * from item"),
  113. ct:pal("third query goes after 50 ms ~p", [R3]),
  114. {error, reconnecting} = R3,
  115. timer:sleep(150),
  116. R4 = epgsql_pool:equery(Worker, "select * from item"),
  117. ct:pal("fouth query goes after 200 ms ~p", [R4]),
  118. {ok, _, []} = R4,
  119. {state, my_pool, #epgsql_connection{sock = Sock2}} = sys:get_state(Worker),
  120. ct:pal("Worker: ~p, sock: ~p", [Worker, Sock2]),
  121. ?assertNotEqual(Sock1, Sock2),
  122. ok.
  123. error_handler_test(Config) ->
  124. {error, Error} = epgsql_pool:equery(my_pool, "SELECT id, title FROM some_table"),
  125. ct:pal("Error:~p", [Error]),
  126. ?assertMatch(#error{severity = error, message = <<"relation \"some_table\" does not exist">>}, Error),
  127. Query2 = "SELECT some_field FROM item WHERE id = $1",
  128. ErrorMessage2 = <<"column \"some_field\" does not exist">>,
  129. ErrorHandler = fun(PoolName, Stmt, Params, Error2) ->
  130. ct:pal("ErrorHandler: ~p", [Error2]),
  131. ?assertEqual(my_pool, PoolName),
  132. ?assertEqual(Query2, Stmt),
  133. ?assertEqual([1], Params),
  134. ?assertMatch(#error{severity = error, message = ErrorMessage2}, Error2),
  135. {db_error, Error2#error.message}
  136. end,
  137. Res = epgsql_pool:equery(my_pool, Query2, [1], ErrorHandler),
  138. ct:pal("Res:~p", [Res]),
  139. ?assertEqual({db_error, ErrorMessage2}, Res),
  140. ok.