%%%------------------------------------------------------------------- %%% @doc Тесты голосования за отзывы (like/dislike). %%% %%% PUT /v1/reviews/:id/vote %%% DELETE /v1/reviews/:id/vote %%% GET /v1/reviews/:id (my_vote) %%% @end %%%------------------------------------------------------------------- -module(user_review_vote_tests). -include_lib("eunit/include/eunit.hrl"). -export([test/0]). -spec test() -> ok. test() -> ct:pal("=== User Review Vote Tests ==="), OwnerToken = api_test_runner:get_user_token(), AuthorEmail = api_test_runner:unique_email(<<"rvauthor">>), AuthorToken = api_test_runner:register_and_login(AuthorEmail, <<"pass">>), VoterEmail = api_test_runner:unique_email(<<"rvvoter">>), VoterToken = api_test_runner:register_and_login(VoterEmail, <<"pass">>), OtherEmail = api_test_runner:unique_email(<<"rvother">>), OtherToken = api_test_runner:register_and_login(OtherEmail, <<"pass">>), CalId = api_test_runner:create_calendar(OwnerToken, #{title => <<"VoteCal">>}), #{<<"id">> := EventId} = api_test_runner:client_post( <<"/v1/calendars/", CalId/binary, "/events">>, OwnerToken, #{title => <<"Event for votes">>, start_time => api_test_runner:future_date_iso8601(), duration => 60}), #{<<"id">> := BookingId} = api_test_runner:client_post( <<"/v1/events/", EventId/binary, "/bookings">>, AuthorToken, #{}), api_test_runner:client_put(<<"/v1/bookings/", BookingId/binary>>, OwnerToken, #{action => <<"confirm">>}), #{<<"id">> := ReviewId} = api_test_runner:client_post( <<"/v1/reviews">>, AuthorToken, #{target_type => <<"event">>, target_id => EventId, rating => 5, comment => <<"Nice event">>}), VotePath = <<"/v1/reviews/", ReviewId/binary, "/vote">>, test_put_like(VoterToken, VotePath), test_put_like_idempotent(VoterToken, VotePath), test_switch_to_dislike(VoterToken, VotePath), test_my_vote_on_get(VoterToken, ReviewId), test_my_vote_on_list(VoterToken, EventId, ReviewId), test_delete_vote(VoterToken, VotePath), test_delete_idempotent(VoterToken, VotePath), test_own_review_forbidden(AuthorToken, VotePath), test_unauthorized(VotePath), test_not_found(VoterToken), test_invalid_body(VoterToken, VotePath), test_second_voter(OtherToken, VotePath), ct:pal("=== All user review vote tests passed ==="), ok. %%%=================================================================== %%% Cases %%%=================================================================== test_put_like(Token, Path) -> ct:pal(" TEST: PUT like"), Resp = api_test_runner:client_put(Path, Token, #{value => <<"like">>}), ?assertEqual(<<"like">>, maps:get(<<"my_vote">>, Resp)), ?assertEqual(1, maps:get(<<"likes">>, Resp)), ?assertEqual(0, maps:get(<<"dislikes">>, Resp)), ct:pal(" OK"). test_put_like_idempotent(Token, Path) -> ct:pal(" TEST: PUT like idempotent"), Resp = api_test_runner:client_put(Path, Token, #{value => <<"like">>}), ?assertEqual(<<"like">>, maps:get(<<"my_vote">>, Resp)), ?assertEqual(1, maps:get(<<"likes">>, Resp)), ?assertEqual(0, maps:get(<<"dislikes">>, Resp)), ct:pal(" OK"). test_switch_to_dislike(Token, Path) -> ct:pal(" TEST: switch like -> dislike"), Resp = api_test_runner:client_put(Path, Token, #{value => <<"dislike">>}), ?assertEqual(<<"dislike">>, maps:get(<<"my_vote">>, Resp)), ?assertEqual(0, maps:get(<<"likes">>, Resp)), ?assertEqual(1, maps:get(<<"dislikes">>, Resp)), ct:pal(" OK"). test_my_vote_on_get(Token, ReviewId) -> ct:pal(" TEST: GET review includes my_vote"), Review = api_test_runner:client_get(<<"/v1/reviews/", ReviewId/binary>>, Token), ?assertEqual(<<"dislike">>, maps:get(<<"my_vote">>, Review)), ?assertEqual(1, maps:get(<<"dislikes">>, Review)), ct:pal(" OK"). test_my_vote_on_list(Token, EventId, ReviewId) -> ct:pal(" TEST: GET reviews list includes my_vote"), Path = <<"/v1/reviews?target_type=event&target_id=", EventId/binary>>, List = api_test_runner:client_get(Path, Token), ?assert(is_list(List)), Match = [R || R <- List, maps:get(<<"id">>, R) =:= ReviewId], ?assertMatch([_], Match), [R] = Match, ?assertEqual(<<"dislike">>, maps:get(<<"my_vote">>, R)), ct:pal(" OK"). test_delete_vote(Token, Path) -> ct:pal(" TEST: DELETE vote"), Resp = api_test_runner:client_delete(Path, Token), ?assertEqual(null, maps:get(<<"my_vote">>, Resp)), ?assertEqual(0, maps:get(<<"likes">>, Resp)), ?assertEqual(0, maps:get(<<"dislikes">>, Resp)), ct:pal(" OK"). test_delete_idempotent(Token, Path) -> ct:pal(" TEST: DELETE vote idempotent"), Resp = api_test_runner:client_delete(Path, Token), ?assertEqual(null, maps:get(<<"my_vote">>, Resp)), ?assertEqual(0, maps:get(<<"likes">>, Resp)), ?assertEqual(0, maps:get(<<"dislikes">>, Resp)), ct:pal(" OK"). test_own_review_forbidden(Token, Path) -> ct:pal(" TEST: vote on own review -> 403"), Body = jsx:encode(#{value => <<"like">>}), Resp = api_test_runner:client_request(put, Path, Token, Body), ?assertMatch({ok, 403, _, _}, Resp), ct:pal(" OK"). test_unauthorized(Path) -> ct:pal(" TEST: unauthorized -> 401"), Body = jsx:encode(#{value => <<"like">>}), Resp = api_test_runner:client_request(put, Path, <<>>, Body), ?assertMatch({ok, 401, _, _}, Resp), ct:pal(" OK"). test_not_found(Token) -> ct:pal(" TEST: vote missing review -> 404"), Path = <<"/v1/reviews/missing-review-id/vote">>, Body = jsx:encode(#{value => <<"like">>}), Resp = api_test_runner:client_request(put, Path, Token, Body), ?assertMatch({ok, 404, _, _}, Resp), ct:pal(" OK"). test_invalid_body(Token, Path) -> ct:pal(" TEST: invalid value -> 400"), Body = jsx:encode(#{value => <<"love">>}), Resp = api_test_runner:client_request(put, Path, Token, Body), ?assertMatch({ok, 400, _, _}, Resp), ct:pal(" OK"). test_second_voter(Token, Path) -> ct:pal(" TEST: second voter like"), Resp = api_test_runner:client_put(Path, Token, #{value => <<"like">>}), ?assertEqual(<<"like">>, maps:get(<<"my_vote">>, Resp)), ?assertEqual(1, maps:get(<<"likes">>, Resp)), ?assertEqual(0, maps:get(<<"dislikes">>, Resp)), ct:pal(" OK").