Files
EventHubBack/test/unit/logic_email_tests.erl
T
aleksey 0a1e55e036
CI / test (push) Successful in 7m40s
CI / deploy-ift (push) Successful in 3m29s
CI / e2e-ift (push) Successful in 1m27s
CI / deploy-stage (push) Failing after 3m45s
CI / e2e-stage (push) Has been skipped
feat(email): HTTP API transport via Resend (fallback SMTP).
Outbound SMTP ports are often blocked on VPS; EMAIL_TRANSPORT=http_api
sends via Resend HTTPS (Brevo optional). OpenSMTPd/SMTP remain available.
2026-08-14 23:54:10 +03:00

148 lines
5.9 KiB
Erlang

-module(logic_email_tests).
-include_lib("eunit/include/eunit.hrl").
logic_email_test_() ->
{foreach, fun setup/0, fun cleanup/1, [
{"log-only without host", fun test_log_only/0},
{"mock deliver gets CalenTIQ + verify url", fun test_verify_deliver/0},
{"invite and reset urls", fun test_invite_reset/0},
{"deliver error still ok", fun test_deliver_error/0},
{"html template placeholders filled", fun test_template_fill/0},
{"booking reminder deliver", fun test_reminder_deliver/0},
{"http_api mock deliver", fun test_http_api_deliver/0},
{"http_api error still ok", fun test_http_api_error/0}
]}.
setup() ->
application:unset_env(eventhub, smtp_host),
application:unset_env(eventhub, smtp_deliver),
application:unset_env(eventhub, email_transport),
application:unset_env(eventhub, email_api_key),
application:unset_env(eventhub, email_http_deliver),
ok.
cleanup(_) ->
application:unset_env(eventhub, smtp_host),
application:unset_env(eventhub, smtp_deliver),
application:unset_env(eventhub, email_transport),
application:unset_env(eventhub, email_api_key),
application:unset_env(eventhub, email_http_deliver),
ok.
test_log_only() ->
application:unset_env(eventhub, smtp_host),
application:set_env(eventhub, email_transport, log),
?assertEqual(ok, logic_email:send_verification_email(<<"a@b.c">>, <<"tok">>)).
test_verify_deliver() ->
Self = self(),
application:set_env(eventhub, email_transport, smtp),
application:set_env(eventhub, smtp_host, "smtp.test"),
application:set_env(eventhub, smtp_deliver,
fun(From, To, Raw, Opts) ->
Self ! {smtp, From, To, Raw, Opts},
{ok, mock}
end),
?assertEqual(ok, logic_email:send_verification_email(<<"user@ex.com">>, <<"abc123">>)),
receive
{smtp, From, To, Raw, Opts} ->
?assertEqual(<<"noreply@calentiq.com">>, From),
?assertEqual(<<"user@ex.com">>, To),
?assertEqual("smtp.test", proplists:get_value(relay, Opts)),
?assert(binary:match(Raw, <<"multipart/alternative">>) =/= nomatch),
?assert(binary:match(Raw, <<"text/html">>) =/= nomatch),
?assert(binary:match(Raw, <<"CalenTIQ">>) =/= nomatch),
?assert(binary:match(Raw, <<"/verify?token=abc123">>) =/= nomatch),
?assert(binary:match(Raw, <<"#FF6A3D">>) =/= nomatch)
after 1000 ->
error(timeout)
end.
test_invite_reset() ->
Self = self(),
application:set_env(eventhub, email_transport, smtp),
application:set_env(eventhub, smtp_host, <<"smtp.test">>),
application:set_env(eventhub, smtp_deliver,
fun(_From, To, Raw, _Opts) ->
Self ! {smtp, To, Raw},
ok
end),
ok = logic_email:send_specialist_invite(<<"i@ex.com">>, <<"invtok">>),
receive
{smtp, <<"i@ex.com">>, Raw1} ->
?assert(binary:match(Raw1, <<"/invites?token=invtok">>) =/= nomatch)
after 1000 -> error(timeout)
end,
ok = logic_email:send_password_reset(<<"r@ex.com">>, <<"rst">>),
receive
{smtp, <<"r@ex.com">>, Raw2} ->
?assert(binary:match(Raw2, <<"/reset-password?token=rst">>) =/= nomatch)
after 1000 -> error(timeout)
end.
test_deliver_error() ->
application:set_env(eventhub, email_transport, smtp),
application:set_env(eventhub, smtp_host, "smtp.test"),
application:set_env(eventhub, smtp_deliver, fun(_, _, _, _) -> {error, boom} end),
?assertEqual(ok, logic_email:send_verification_email(<<"a@b.c">>, <<"t">>)),
application:set_env(eventhub, smtp_deliver, fun(_, _, _, _) -> error(crash) end),
?assertEqual(ok, logic_email:send_password_reset(<<"a@b.c">>, <<"t">>)).
test_template_fill() ->
{Sub, Plain, Html} = logic_email_templates:render(verify, <<"https://x/verify?token=z">>),
?assert(binary:match(Sub, <<"CalenTIQ">>) =/= nomatch),
?assert(binary:match(Plain, <<"https://x/verify?token=z">>) =/= nomatch),
?assert(binary:match(Html, <<"{{">>) =:= nomatch),
?assert(binary:match(Html, <<"https://x/verify?token=z">>) =/= nomatch).
test_reminder_deliver() ->
Self = self(),
application:set_env(eventhub, email_transport, smtp),
application:set_env(eventhub, smtp_host, "smtp.test"),
application:set_env(eventhub, smtp_deliver,
fun(_From, To, Raw, _Opts) ->
Self ! {smtp, To, Raw},
{ok, mock}
end),
Start = {{2026, 8, 15}, {10, 30, 0}},
?assertEqual(ok, logic_email:send_booking_reminder(
<<"u@ex.com">>, <<"Yoga hour">>, Start, <<"/c/cal1/e/ev1">>)),
receive
{smtp, <<"u@ex.com">>, Raw} ->
?assert(binary:match(Raw, <<"/c/cal1/e/ev1">>) =/= nomatch),
?assert(binary:match(Raw, <<"Yoga hour">>) =/= nomatch),
?assert(binary:match(Raw, <<"2026-08-15 10:30">>) =/= nomatch)
after 1000 ->
error(timeout)
end.
test_http_api_deliver() ->
Self = self(),
application:set_env(eventhub, email_transport, http_api),
application:set_env(eventhub, email_api_key, <<"test-key">>),
application:set_env(eventhub, email_http_deliver,
fun(Key, From, To, Subject, Plain, Html) ->
Self ! {http, Key, From, To, Subject, Plain, Html},
ok
end),
?assertEqual(ok, logic_email:send_verification_email(<<"user@ex.com">>, <<"abc123">>)),
receive
{http, Key, From, To, Subject, Plain, Html} ->
?assertEqual(<<"test-key">>, Key),
?assertEqual(<<"noreply@calentiq.com">>, From),
?assertEqual(<<"user@ex.com">>, To),
?assert(binary:match(Subject, <<"CalenTIQ">>) =/= nomatch),
?assert(binary:match(Plain, <<"/verify?token=abc123">>) =/= nomatch),
?assert(binary:match(Html, <<"/verify?token=abc123">>) =/= nomatch)
after 1000 ->
error(timeout)
end.
test_http_api_error() ->
application:set_env(eventhub, email_transport, http_api),
application:set_env(eventhub, email_api_key, <<"k">>),
application:set_env(eventhub, email_http_deliver, fun(_, _, _, _, _, _) -> {error, boom} end),
?assertEqual(ok, logic_email:send_verification_email(<<"a@b.c">>, <<"t">>)),
application:set_env(eventhub, email_http_deliver, fun(_, _, _, _, _, _) -> error(crash) end),
?assertEqual(ok, logic_email:send_password_reset(<<"a@b.c">>, <<"t">>)).