-module(stats_tops_tests). -include_lib("eunit/include/eunit.hrl"). -include("records.hrl"). -define(TABLES, [event, calendar, review, report, stats_counter, stats_daily]). setup() -> eh_test_support:start_mnesia(), eh_test_support:ensure_tables(?TABLES), {ok, _} = stats_collector:start_link(), ok = stats_collector:subscribe(), ok. cleanup(_) -> case whereis(stats_collector) of undefined -> ok; Pid -> unlink(Pid), exit(Pid, kill), wait_dead(Pid, 50) end, eh_test_support:delete_tables(?TABLES), eh_test_support:stop_mnesia(), ok. wait_dead(_Pid, 0) -> ok; wait_dead(Pid, N) -> case is_process_alive(Pid) of true -> timer:sleep(20), wait_dead(Pid, N - 1); false -> ok end. stats_tops_test_() -> {foreach, fun setup/0, fun cleanup/1, [ {"top events by rating order", fun test_event_rating_top/0}, {"top calendars by rating and reviews", fun test_calendar_tops/0}, {"rating update reorders top", fun test_rating_update/0}, {"review target tops all/pos/neg", fun test_review_target_tops/0}, {"report target tops", fun test_report_target_tops/0}, {"calendar pos/neg from reviews", fun test_calendar_pos_neg/0} ]}. test_event_rating_top() -> Now = calendar:universal_time(), ok = mnesia:dirty_write(make_event(<<"e_low">>, 2.0, Now)), ok = mnesia:dirty_write(make_event(<<"e_high">>, 5.0, Now)), ok = mnesia:dirty_write(make_event(<<"e_mid">>, 3.5, Now)), wait_top_event(<<"e_high">>, 30), Top = core_event:get_top_events_by_rating(2), Ids = [E#event.id || E <- Top], ?assertEqual([<<"e_high">>, <<"e_mid">>], Ids). test_calendar_tops() -> Now = calendar:universal_time(), ok = mnesia:dirty_write(make_calendar(<<"c1">>, 4.0, 10, Now)), ok = mnesia:dirty_write(make_calendar(<<"c2">>, 5.0, 2, Now)), ok = mnesia:dirty_write(make_calendar(<<"c3">>, 3.0, 20, Now)), wait_top_cal_rating(<<"c2">>, 30), ByRating = [C#calendar.id || C <- core_calendar:get_top_calendars_by_rating(2)], ?assertEqual([<<"c2">>, <<"c1">>], ByRating), ByReviews = [C#calendar.id || C <- core_calendar:get_top_calendars_by_reviews(2)], ?assertEqual([<<"c3">>, <<"c1">>], ByReviews). test_rating_update() -> Now = calendar:universal_time(), E1 = make_event(<<"eu1">>, 4.0, Now), E2 = make_event(<<"eu2">>, 3.0, Now), ok = mnesia:dirty_write(E1), ok = mnesia:dirty_write(E2), wait_top_event(<<"eu1">>, 30), ok = mnesia:dirty_write(E2#event{rating_avg = 4.5}), wait_top_event(<<"eu2">>, 30), Top = core_event:get_top_events_by_rating(1), ?assertEqual([<<"eu2">>], [E#event.id || E <- Top]). test_review_target_tops() -> Now = calendar:universal_time(), ok = mnesia:dirty_write(make_review(<<"r1">>, calendar, <<"cA">>, 5, Now)), ok = mnesia:dirty_write(make_review(<<"r2">>, calendar, <<"cA">>, 5, Now)), ok = mnesia:dirty_write(make_review(<<"r3">>, event, <<"eA">>, 1, Now)), ok = mnesia:dirty_write(make_review(<<"r4">>, calendar, <<"cB">>, 2, Now)), wait_review_top({calendar, <<"cA">>}, 30), All = core_review:get_top_targets_by_reviews(3), ?assertMatch([{calendar, <<"cA">>, 2} | _], All), AllKeys = [{T, I} || {T, I, _} <- All], ?assert(lists:member({event, <<"eA">>}, AllKeys)), ?assert(lists:member({calendar, <<"cB">>}, AllKeys)), Pos = core_review:get_top_targets_by_positive_reviews(1), ?assertEqual([{calendar, <<"cA">>, 2}], Pos), Neg = core_review:get_top_targets_by_negative_reviews(2), NegIds = [{T, I} || {T, I, _} <- Neg], ?assert(lists:member({event, <<"eA">>}, NegIds)), ?assert(lists:member({calendar, <<"cB">>}, NegIds)). test_report_target_tops() -> Now = calendar:universal_time(), ok = mnesia:dirty_write(make_report(<<"p1">>, calendar, <<"cX">>, Now)), ok = mnesia:dirty_write(make_report(<<"p2">>, calendar, <<"cX">>, Now)), ok = mnesia:dirty_write(make_report(<<"p3">>, event, <<"eX">>, Now)), wait_report_top({calendar, <<"cX">>}, 30), Top = core_report:get_top_targets_by_reports(2), ?assertEqual([{calendar, <<"cX">>, 2}, {event, <<"eX">>, 1}], Top). test_calendar_pos_neg() -> Now = calendar:universal_time(), ok = mnesia:dirty_write(make_calendar(<<"cp1">>, 0.0, 0, Now)), ok = mnesia:dirty_write(make_calendar(<<"cp2">>, 0.0, 0, Now)), ok = mnesia:dirty_write(make_review(<<"rp1">>, calendar, <<"cp1">>, 5, Now)), ok = mnesia:dirty_write(make_review(<<"rp2">>, calendar, <<"cp1">>, 4, Now)), ok = mnesia:dirty_write(make_review(<<"rn1">>, calendar, <<"cp2">>, 1, Now)), wait_cal_pos(<<"cp1">>, 30), Pos = [C#calendar.id || C <- core_calendar:get_top_calendars_by_positive_reviews(1)], ?assertEqual([<<"cp1">>], Pos), Neg = [C#calendar.id || C <- core_calendar:get_top_calendars_by_negative_reviews(1)], ?assertEqual([<<"cp2">>], Neg). make_review(Id, Type, TargetId, Rating, Now) -> #review{ id = Id, user_id = <<"u1">>, target_type = Type, target_id = TargetId, rating = Rating, comment = <<>>, status = visible, reason = <<>>, likes = 0, dislikes = 0, created_at = Now, updated_at = Now }. make_report(Id, Type, TargetId, Now) -> #report{ id = Id, reporter_id = <<"u1">>, target_type = Type, target_id = TargetId, reason = <<"spam">>, status = pending, created_at = Now, resolved_at = undefined, resolved_by = <<>> }. make_event(Id, Avg, Now) -> #event{ id = Id, calendar_id = <<"cal">>, title = Id, description = <<>>, event_type = single, start_time = Now, duration = 60, recurrence_rule = <<>>, master_id = <<>>, is_instance = false, specialist_id = <<>>, location = undefined, tags = [], capacity = 10, online_link = <<>>, status = active, reason = <<>>, rating_avg = Avg, rating_count = 1, attachments = [], edit_history = [], created_at = Now, updated_at = Now }. make_calendar(Id, Avg, Count, Now) -> #calendar{ id = Id, owner_id = <<"u1">>, title = Id, description = <<>>, short_name = Id, category = <<>>, color = <<>>, image_url = <<>>, settings = #{}, tags = [], type = personal, confirmation = auto, rating_avg = Avg, rating_count = Count, status = active, reason = <<>>, created_at = Now, updated_at = Now }. wait_top_event(ExpectedId, 0) -> Top = core_event:get_top_events_by_rating(1), ?assertEqual([ExpectedId], [E#event.id || E <- Top]); wait_top_event(ExpectedId, N) -> case core_event:get_top_events_by_rating(1) of [#event{id = ExpectedId}] -> ok; _ -> timer:sleep(25), wait_top_event(ExpectedId, N - 1) end. wait_top_cal_rating(ExpectedId, 0) -> Top = core_calendar:get_top_calendars_by_rating(1), ?assertEqual([ExpectedId], [C#calendar.id || C <- Top]); wait_top_cal_rating(ExpectedId, N) -> case core_calendar:get_top_calendars_by_rating(1) of [#calendar{id = ExpectedId}] -> ok; _ -> timer:sleep(25), wait_top_cal_rating(ExpectedId, N - 1) end. wait_review_top(Expected, 0) -> case core_review:get_top_targets_by_reviews(1) of [{T, I, _} | _] -> ?assertEqual(Expected, {T, I}); Other -> ?assertEqual(Expected, Other) end; wait_review_top(Expected = {T, I}, N) -> case core_review:get_top_targets_by_reviews(1) of [{T, I, _} | _] -> ok; _ -> timer:sleep(25), wait_review_top(Expected, N - 1) end. wait_report_top(Expected, 0) -> case core_report:get_top_targets_by_reports(1) of [{T, I, _} | _] -> ?assertEqual(Expected, {T, I}); Other -> ?assertEqual(Expected, Other) end; wait_report_top(Expected = {T, I}, N) -> case core_report:get_top_targets_by_reports(1) of [{T, I, _} | _] -> ok; _ -> timer:sleep(25), wait_report_top(Expected, N - 1) end. wait_cal_pos(ExpectedId, 0) -> Top = core_calendar:get_top_calendars_by_positive_reviews(1), ?assertEqual([ExpectedId], [C#calendar.id || C <- Top]); wait_cal_pos(ExpectedId, N) -> case core_calendar:get_top_calendars_by_positive_reviews(1) of [#calendar{id = ExpectedId}] -> ok; _ -> timer:sleep(25), wait_cal_pos(ExpectedId, N - 1) end.