Добавить автомодерацию: политики, порог жалоб, settings/hits API и тесты.
CI / test (push) Failing after 37m9s
CI / deploy-ift (push) Has been skipped
CI / e2e-ift (push) Has been skipped
CI / deploy-stage (push) Has been skipped
CI / e2e-stage (push) Has been skipped

Refs EventHub/EventHubBack#37
Refs EventHub/EventHubBack#38
Refs EventHub/EventHubBack#39
Refs EventHub/EventHubBack#40
This commit is contained in:
2026-07-17 18:09:31 +03:00
parent 4fa802702f
commit e9d1a13900
27 changed files with 1779 additions and 85 deletions
+152
View File
@@ -0,0 +1,152 @@
%%%-------------------------------------------------------------------
%%% @doc User-facing automod API: content reject + report threshold + review reports.
%%%-------------------------------------------------------------------
-module(user_automod_tests).
-include_lib("eunit/include/eunit.hrl").
-export([test/0]).
-spec test() -> ok.
test() ->
ct:pal("=== User Automod Tests ==="),
Admin = api_test_runner:get_admin_token(),
Unique = integer_to_binary(erlang:system_time()),
BadWord = <<"uam_bad_", Unique/binary>>,
_ = api_test_runner:admin_post(<<"/v1/admin/banned-words">>, Admin, #{<<"word">> => BadWord}),
test_reject_event_create(Admin, BadWord),
test_reject_calendar_create(Admin, BadWord),
test_report_threshold_freezes_event(Admin),
test_report_on_review(Admin),
test_dismissed_reports_do_not_freeze(Admin),
%% restore defaults
_ = api_test_runner:admin_put(<<"/v1/admin/automod/settings">>, Admin, #{
<<"keyword_action">> => <<"flag">>,
<<"match_mode">> => <<"word_boundary">>,
<<"report_threshold">> => 3
}),
ct:pal("=== All user automod tests passed ==="),
ok.
test_reject_event_create(Admin, BadWord) ->
ct:pal(" TEST: reject policy returns 400 on event create"),
_ = api_test_runner:admin_put(<<"/v1/admin/automod/settings">>, Admin, #{
<<"keyword_action">> => <<"reject">>
}),
User = api_test_runner:get_user_token(),
CalId = api_test_runner:create_calendar(User, #{title => <<"CleanCal">>}),
{ok, Status, _, Body} = api_test_runner:client_request(post,
<<"/v1/calendars/", CalId/binary, "/events">>, User,
jsx:encode(#{
title => <<"Has ", BadWord/binary>>,
start_time => api_test_runner:future_date_iso8601(),
duration => 30
})),
?assertEqual(400, Status),
?assert(binary:match(list_to_binary(Body), <<"banned">>) =/= nomatch),
ct:pal(" OK").
test_reject_calendar_create(Admin, BadWord) ->
ct:pal(" TEST: reject policy returns 400 on calendar create"),
_ = api_test_runner:admin_put(<<"/v1/admin/automod/settings">>, Admin, #{
<<"keyword_action">> => <<"reject">>
}),
User = api_test_runner:get_user_token(),
{ok, Status, _, _} = api_test_runner:client_request(post, <<"/v1/calendars">>, User,
jsx:encode(#{title => <<"Cal ", BadWord/binary>>, description => <<"x">>})),
?assertEqual(400, Status),
ct:pal(" OK").
test_report_threshold_freezes_event(Admin) ->
ct:pal(" TEST: 3 pending reports freeze event"),
_ = api_test_runner:admin_put(<<"/v1/admin/automod/settings">>, Admin, #{
<<"keyword_action">> => <<"flag">>,
<<"report_threshold">> => 3
}),
Owner = api_test_runner:get_user_token(),
CalId = api_test_runner:create_calendar(Owner, #{title => <<"RepThrCal">>}),
#{<<"id">> := EventId} = api_test_runner:client_post(
<<"/v1/calendars/", CalId/binary, "/events">>, Owner,
#{title => <<"Clean event">>,
start_time => api_test_runner:future_date_iso8601(),
duration => 60}),
lists:foreach(fun(_) ->
Tok = api_test_runner:get_user_token(),
{ok, 201, _, _} = api_test_runner:client_request(post, <<"/v1/reports">>, Tok,
jsx:encode(#{
target_type => <<"event">>,
target_id => EventId,
reason => <<"spam">>
}))
end, [1, 2, 3]),
Ev = api_test_runner:admin_get(<<"/v1/admin/events/", EventId/binary>>, Admin),
?assertEqual(<<"frozen">>, maps:get(<<"status">>, Ev)),
Hits = api_test_runner:admin_get(<<"/v1/admin/automod/hits?trigger=report_threshold">>, Admin),
?assert(lists:any(fun(H) -> maps:get(<<"entity_id">>, H) =:= EventId end, Hits)),
ct:pal(" OK").
test_report_on_review(Admin) ->
ct:pal(" TEST: report on review + threshold hides"),
_ = api_test_runner:admin_put(<<"/v1/admin/automod/settings">>, Admin, #{
<<"report_threshold">> => 3
}),
Owner = api_test_runner:get_user_token(),
CalId = api_test_runner:create_calendar(Owner, #{title => <<"RevRepCal">>}),
Reviewer = api_test_runner:get_user_token(),
{ok, 201, _, RevBody} = api_test_runner:client_request(post, <<"/v1/reviews">>, Reviewer,
jsx:encode(#{
target_type => <<"calendar">>,
target_id => CalId,
rating => 2,
comment => <<"meh">>
})),
#{<<"id">> := ReviewId} = jsx:decode(list_to_binary(RevBody), [return_maps]),
lists:foreach(fun(_) ->
Tok = api_test_runner:get_user_token(),
{ok, 201, _, _} = api_test_runner:client_request(post, <<"/v1/reports">>, Tok,
jsx:encode(#{
target_type => <<"review">>,
target_id => ReviewId,
reason => <<"abuse">>
}))
end, [1, 2, 3]),
Rev = api_test_runner:admin_get(<<"/v1/admin/reviews/", ReviewId/binary>>, Admin),
?assertEqual(<<"hidden">>, maps:get(<<"status">>, Rev)),
ct:pal(" OK").
test_dismissed_reports_do_not_freeze(Admin) ->
ct:pal(" TEST: dismissed reports do not freeze"),
_ = api_test_runner:admin_put(<<"/v1/admin/automod/settings">>, Admin, #{
<<"report_threshold">> => 3
}),
Owner = api_test_runner:get_user_token(),
CalId = api_test_runner:create_calendar(Owner, #{title => <<"DismissCal">>}),
#{<<"id">> := EventId} = api_test_runner:client_post(
<<"/v1/calendars/", CalId/binary, "/events">>, Owner,
#{title => <<"Still clean">>,
start_time => api_test_runner:future_date_iso8601(),
duration => 60}),
ReportIds = lists:map(fun(_) ->
Tok = api_test_runner:get_user_token(),
{ok, 201, _, B} = api_test_runner:client_request(post, <<"/v1/reports">>, Tok,
jsx:encode(#{
target_type => <<"event">>,
target_id => EventId,
reason => <<"noise">>
})),
maps:get(<<"id">>, jsx:decode(list_to_binary(B), [return_maps]))
end, [1, 2]),
lists:foreach(fun(Rid) ->
_ = api_test_runner:admin_put(<<"/v1/admin/reports/", Rid/binary>>, Admin,
#{<<"status">> => <<"dismissed">>})
end, ReportIds),
Tok3 = api_test_runner:get_user_token(),
{ok, 201, _, _} = api_test_runner:client_request(post, <<"/v1/reports">>, Tok3,
jsx:encode(#{
target_type => <<"event">>,
target_id => EventId,
reason => <<"one pending">>
})),
Ev = api_test_runner:admin_get(<<"/v1/admin/events/", EventId/binary>>, Admin),
?assertEqual(<<"active">>, maps:get(<<"status">>, Ev)),
ct:pal(" OK").