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.
This commit is contained in:
+7
-1
@@ -20,7 +20,13 @@ ADMIN_MODER_PASSWORD=замените-сильный-пароль-модерат
|
|||||||
ADMIN_SUPPORT_EMAIL=support@eventhub.local
|
ADMIN_SUPPORT_EMAIL=support@eventhub.local
|
||||||
ADMIN_SUPPORT_PASSWORD=замените-сильный-пароль-поддержки
|
ADMIN_SUPPORT_PASSWORD=замените-сильный-пароль-поддержки
|
||||||
|
|
||||||
# SMTP (пусто SMTP_HOST = только лог; пароль не коммитить)
|
# Email: EMAIL_TRANSPORT=auto|smtp|http_api|log (auto: API key → http_api, else SMTP_HOST → smtp, else log)
|
||||||
|
EMAIL_TRANSPORT=auto
|
||||||
|
# HTTP API (HTTPS — when outbound SMTP ports blocked). Canon: Resend (Brevo optional).
|
||||||
|
EMAIL_API_KEY=
|
||||||
|
EMAIL_API_PROVIDER=resend
|
||||||
|
# EMAIL_API_URL=https://api.resend.com/emails
|
||||||
|
# SMTP (пусто SMTP_HOST + нет API key = только лог; пароль не коммитить)
|
||||||
SMTP_HOST=
|
SMTP_HOST=
|
||||||
SMTP_PORT=587
|
SMTP_PORT=587
|
||||||
SMTP_USER=
|
SMTP_USER=
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ services:
|
|||||||
- ADMIN_SUPPORT_PASSWORD=${ADMIN_SUPPORT_PASSWORD}
|
- ADMIN_SUPPORT_PASSWORD=${ADMIN_SUPPORT_PASSWORD}
|
||||||
- CLUSTER_MODE=true
|
- CLUSTER_MODE=true
|
||||||
- DNS_NAME=eventhub-node
|
- DNS_NAME=eventhub-node
|
||||||
|
- EMAIL_TRANSPORT=${EMAIL_TRANSPORT:-auto}
|
||||||
|
- EMAIL_API_KEY=${EMAIL_API_KEY:-}
|
||||||
|
- EMAIL_API_PROVIDER=${EMAIL_API_PROVIDER:-resend}
|
||||||
|
- EMAIL_API_URL=${EMAIL_API_URL:-}
|
||||||
- SMTP_HOST=${SMTP_HOST:-}
|
- SMTP_HOST=${SMTP_HOST:-}
|
||||||
- SMTP_PORT=${SMTP_PORT:-587}
|
- SMTP_PORT=${SMTP_PORT:-587}
|
||||||
- SMTP_USER=${SMTP_USER:-}
|
- SMTP_USER=${SMTP_USER:-}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
trails,
|
trails,
|
||||||
cowboy_swagger,
|
cowboy_swagger,
|
||||||
ssl,
|
ssl,
|
||||||
|
inets,
|
||||||
gen_smtp
|
gen_smtp
|
||||||
]},
|
]},
|
||||||
{env, [
|
{env, [
|
||||||
|
|||||||
+186
-12
@@ -2,11 +2,13 @@
|
|||||||
-export([send_verification_email/2, send_specialist_invite/2, send_password_reset/2,
|
-export([send_verification_email/2, send_specialist_invite/2, send_password_reset/2,
|
||||||
send_booking_reminder/4, send_waitlist_promoted/3]).
|
send_booking_reminder/4, send_waitlist_promoted/3]).
|
||||||
|
|
||||||
%% SMTP via env (variant A / Back#68):
|
%% Email transport (Spec#8 / stage outbound :25 blocked):
|
||||||
%% SMTP_HOST empty → log-only (local/tests)
|
%% EMAIL_TRANSPORT = smtp | http_api | log | auto (default auto)
|
||||||
%% SMTP_PORT (default 587), SMTP_USER, SMTP_PASS, SMTP_FROM, SMTP_TLS
|
%% auto: EMAIL_API_KEY set → http_api; else SMTP_HOST set → smtp; else log
|
||||||
%% PUBLIC_APP_URL — base for deep links (default https://stage.calentiq.com)
|
%% SMTP: SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS, SMTP_FROM, SMTP_TLS
|
||||||
%% Tests: application:set_env(eventhub, smtp_deliver, Fun)
|
%% HTTP API: EMAIL_API_KEY, EMAIL_API_PROVIDER=resend|brevo (default resend),
|
||||||
|
%% optional EMAIL_API_URL; SMTP_FROM as sender
|
||||||
|
%% Tests: smtp_deliver / email_http_deliver app env mocks
|
||||||
|
|
||||||
-spec send_verification_email(binary(), binary()) -> ok.
|
-spec send_verification_email(binary(), binary()) -> ok.
|
||||||
send_verification_email(Email, Token) ->
|
send_verification_email(Email, Token) ->
|
||||||
@@ -20,7 +22,6 @@ send_specialist_invite(Email, Token) ->
|
|||||||
send_password_reset(Email, Token) ->
|
send_password_reset(Email, Token) ->
|
||||||
send_kind(reset, Email, abs_url(<<"/reset-password?token=">>, Token)).
|
send_kind(reset, Email, abs_url(<<"/reset-password?token=">>, Token)).
|
||||||
|
|
||||||
%% Reminder before event start (Back#70).
|
|
||||||
-spec send_booking_reminder(binary(), binary(), calendar:datetime(), binary()) -> ok.
|
-spec send_booking_reminder(binary(), binary(), calendar:datetime(), binary()) -> ok.
|
||||||
send_booking_reminder(Email, EventTitle, StartTime, EventPath) ->
|
send_booking_reminder(Email, EventTitle, StartTime, EventPath) ->
|
||||||
Url = abs_path(EventPath),
|
Url = abs_path(EventPath),
|
||||||
@@ -41,6 +42,55 @@ send_kind(Kind, To0, Url) ->
|
|||||||
send(to_bin(To0), Subject, Plain, Html).
|
send(to_bin(To0), Subject, Plain, Html).
|
||||||
|
|
||||||
send(To, Subject, Plain, Html) ->
|
send(To, Subject, Plain, Html) ->
|
||||||
|
case transport() of
|
||||||
|
log ->
|
||||||
|
logger:info(#{what => email_log_only, to => To, subject => Subject}),
|
||||||
|
ok;
|
||||||
|
smtp ->
|
||||||
|
send_smtp(To, Subject, Plain, Html);
|
||||||
|
http_api ->
|
||||||
|
send_http_api(To, Subject, Plain, Html)
|
||||||
|
end.
|
||||||
|
|
||||||
|
transport() ->
|
||||||
|
case env_bin("EMAIL_TRANSPORT", <<>>) of
|
||||||
|
<<"smtp">> -> smtp;
|
||||||
|
<<"http_api">> -> http_api;
|
||||||
|
<<"log">> -> log;
|
||||||
|
<<"auto">> -> transport_auto();
|
||||||
|
<<>> ->
|
||||||
|
case application:get_env(eventhub, email_transport) of
|
||||||
|
{ok, smtp} -> smtp;
|
||||||
|
{ok, http_api} -> http_api;
|
||||||
|
{ok, log} -> log;
|
||||||
|
_ -> transport_auto()
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
transport_auto()
|
||||||
|
end.
|
||||||
|
|
||||||
|
transport_auto() ->
|
||||||
|
case application:get_env(eventhub, email_transport) of
|
||||||
|
{ok, smtp} -> smtp;
|
||||||
|
{ok, http_api} -> http_api;
|
||||||
|
{ok, log} -> log;
|
||||||
|
_ ->
|
||||||
|
case api_key() of
|
||||||
|
<<>> ->
|
||||||
|
case smtp_relay() of
|
||||||
|
undefined -> log;
|
||||||
|
_ -> smtp
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
http_api
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% SMTP
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
|
||||||
|
send_smtp(To, Subject, Plain, Html) ->
|
||||||
case smtp_relay() of
|
case smtp_relay() of
|
||||||
undefined ->
|
undefined ->
|
||||||
logger:info(#{what => email_log_only, to => To, subject => Subject}),
|
logger:info(#{what => email_log_only, to => To, subject => Subject}),
|
||||||
@@ -49,11 +99,11 @@ send(To, Subject, Plain, Html) ->
|
|||||||
From = from_addr(),
|
From = from_addr(),
|
||||||
Raw = rfc822(From, To, Subject, Plain, Html),
|
Raw = rfc822(From, To, Subject, Plain, Html),
|
||||||
Opts = smtp_opts(Relay),
|
Opts = smtp_opts(Relay),
|
||||||
safe_deliver(From, To, Raw, Opts)
|
safe_smtp_deliver(From, To, Raw, Opts)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
safe_deliver(From, To, Raw, Opts) ->
|
safe_smtp_deliver(From, To, Raw, Opts) ->
|
||||||
Fun = deliver_fun(),
|
Fun = smtp_deliver_fun(),
|
||||||
try Fun(From, To, Raw, Opts) of
|
try Fun(From, To, Raw, Opts) of
|
||||||
{ok, _} -> ok;
|
{ok, _} -> ok;
|
||||||
ok -> ok;
|
ok -> ok;
|
||||||
@@ -70,13 +120,13 @@ safe_deliver(From, To, Raw, Opts) ->
|
|||||||
ok
|
ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
deliver_fun() ->
|
smtp_deliver_fun() ->
|
||||||
case application:get_env(eventhub, smtp_deliver) of
|
case application:get_env(eventhub, smtp_deliver) of
|
||||||
{ok, Fun} when is_function(Fun, 4) -> Fun;
|
{ok, Fun} when is_function(Fun, 4) -> Fun;
|
||||||
_ -> fun default_deliver/4
|
_ -> fun default_smtp_deliver/4
|
||||||
end.
|
end.
|
||||||
|
|
||||||
default_deliver(From, To, Raw, Opts) ->
|
default_smtp_deliver(From, To, Raw, Opts) ->
|
||||||
gen_smtp_client:send({From, [To], Raw}, Opts).
|
gen_smtp_client:send({From, [To], Raw}, Opts).
|
||||||
|
|
||||||
smtp_relay() ->
|
smtp_relay() ->
|
||||||
@@ -108,6 +158,130 @@ tls_mode(<<"always">>) -> always;
|
|||||||
tls_mode(<<"never">>) -> never;
|
tls_mode(<<"never">>) -> never;
|
||||||
tls_mode(_) -> if_available.
|
tls_mode(_) -> if_available.
|
||||||
|
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% HTTP API (HTTPS transactional — no outbound :25/:587)
|
||||||
|
%%% Providers: resend (default/canon), brevo (optional)
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
|
||||||
|
send_http_api(To, Subject, Plain, Html) ->
|
||||||
|
case api_key() of
|
||||||
|
<<>> ->
|
||||||
|
logger:warning(#{what => email_api_missing_key, to => To}),
|
||||||
|
ok;
|
||||||
|
Key ->
|
||||||
|
From = from_addr(),
|
||||||
|
safe_http_deliver(Key, From, To, Subject, Plain, Html)
|
||||||
|
end.
|
||||||
|
|
||||||
|
safe_http_deliver(Key, From, To, Subject, Plain, Html) ->
|
||||||
|
Fun = http_deliver_fun(),
|
||||||
|
try Fun(Key, From, To, Subject, Plain, Html) of
|
||||||
|
ok -> ok;
|
||||||
|
{ok, _} -> ok;
|
||||||
|
{error, Reason} ->
|
||||||
|
logger:warning(#{what => email_api_error, to => To, reason => Reason}),
|
||||||
|
ok;
|
||||||
|
Other ->
|
||||||
|
logger:warning(#{what => email_api_unexpected, to => To, result => Other}),
|
||||||
|
ok
|
||||||
|
catch
|
||||||
|
Class:Reason:Stack ->
|
||||||
|
logger:warning(#{what => email_api_crash, to => To,
|
||||||
|
class => Class, reason => Reason, stack => Stack}),
|
||||||
|
ok
|
||||||
|
end.
|
||||||
|
|
||||||
|
http_deliver_fun() ->
|
||||||
|
case application:get_env(eventhub, email_http_deliver) of
|
||||||
|
{ok, Fun} when is_function(Fun, 6) -> Fun;
|
||||||
|
_ -> fun default_http_deliver/6
|
||||||
|
end.
|
||||||
|
|
||||||
|
default_http_deliver(Key, From, To, Subject, Plain, Html) ->
|
||||||
|
_ = application:ensure_all_started(inets),
|
||||||
|
_ = application:ensure_all_started(ssl),
|
||||||
|
Provider = api_provider(),
|
||||||
|
{Url, Headers, Body} = http_api_request(Provider, Key, From, To, Subject, Plain, Html),
|
||||||
|
Request = {binary_to_list(Url), Headers, "application/json", Body},
|
||||||
|
HttpOpts = [{timeout, 15000}, {ssl, [{verify, verify_peer},
|
||||||
|
{cacerts, public_key:cacerts_get()}]}],
|
||||||
|
case httpc:request(post, Request, HttpOpts, [{body_format, binary}]) of
|
||||||
|
{ok, {{_, Code, _}, _, _Resp}} when Code >= 200, Code < 300 ->
|
||||||
|
ok;
|
||||||
|
{ok, {{_, Code, _}, _, Resp}} ->
|
||||||
|
{error, {http_status, Code, Resp}};
|
||||||
|
{error, Reason} ->
|
||||||
|
{error, Reason}
|
||||||
|
end.
|
||||||
|
|
||||||
|
http_api_request(resend, Key, From, To, Subject, Plain, Html) ->
|
||||||
|
Url = case env_bin("EMAIL_API_URL", <<>>) of
|
||||||
|
<<>> -> <<"https://api.resend.com/emails">>;
|
||||||
|
U -> U
|
||||||
|
end,
|
||||||
|
FromLine = <<"CalenTIQ <", From/binary, ">">>,
|
||||||
|
Body = jsx:encode(#{
|
||||||
|
<<"from">> => FromLine,
|
||||||
|
<<"to">> => [To],
|
||||||
|
<<"subject">> => Subject,
|
||||||
|
<<"html">> => Html,
|
||||||
|
<<"text">> => Plain
|
||||||
|
}),
|
||||||
|
Headers = [
|
||||||
|
{"authorization", "Bearer " ++ binary_to_list(Key)},
|
||||||
|
{"accept", "application/json"},
|
||||||
|
{"content-type", "application/json"}
|
||||||
|
],
|
||||||
|
{Url, Headers, Body};
|
||||||
|
http_api_request(brevo, Key, From, To, Subject, Plain, Html) ->
|
||||||
|
Url = case env_bin("EMAIL_API_URL", <<>>) of
|
||||||
|
<<>> -> <<"https://api.brevo.com/v3/smtp/email">>;
|
||||||
|
U -> U
|
||||||
|
end,
|
||||||
|
Body = jsx:encode(#{
|
||||||
|
<<"sender">> => #{
|
||||||
|
<<"name">> => <<"CalenTIQ">>,
|
||||||
|
<<"email">> => From
|
||||||
|
},
|
||||||
|
<<"to">> => [#{<<"email">> => To}],
|
||||||
|
<<"subject">> => Subject,
|
||||||
|
<<"htmlContent">> => Html,
|
||||||
|
<<"textContent">> => Plain
|
||||||
|
}),
|
||||||
|
Headers = [
|
||||||
|
{"api-key", binary_to_list(Key)},
|
||||||
|
{"accept", "application/json"},
|
||||||
|
{"content-type", "application/json"}
|
||||||
|
],
|
||||||
|
{Url, Headers, Body}.
|
||||||
|
|
||||||
|
api_provider() ->
|
||||||
|
case env_bin("EMAIL_API_PROVIDER", <<"resend">>) of
|
||||||
|
<<"resend">> -> resend;
|
||||||
|
<<"brevo">> -> brevo;
|
||||||
|
<<"sendinblue">> -> brevo;
|
||||||
|
Other ->
|
||||||
|
case application:get_env(eventhub, email_api_provider) of
|
||||||
|
{ok, resend} -> resend;
|
||||||
|
{ok, brevo} -> brevo;
|
||||||
|
_ when Other =:= <<>> -> resend;
|
||||||
|
_ ->
|
||||||
|
logger:warning(#{what => email_api_unknown_provider, provider => Other}),
|
||||||
|
resend
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
api_key() ->
|
||||||
|
case application:get_env(eventhub, email_api_key) of
|
||||||
|
{ok, K} when is_binary(K), K =/= <<>> -> K;
|
||||||
|
{ok, K} when is_list(K), K =/= "" -> unicode:characters_to_binary(K);
|
||||||
|
_ -> env_bin("EMAIL_API_KEY", <<>>)
|
||||||
|
end.
|
||||||
|
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% Shared
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
|
||||||
from_addr() ->
|
from_addr() ->
|
||||||
env_bin("SMTP_FROM", <<"noreply@calentiq.com">>).
|
env_bin("SMTP_FROM", <<"noreply@calentiq.com">>).
|
||||||
|
|
||||||
|
|||||||
@@ -8,25 +8,35 @@ logic_email_test_() ->
|
|||||||
{"invite and reset urls", fun test_invite_reset/0},
|
{"invite and reset urls", fun test_invite_reset/0},
|
||||||
{"deliver error still ok", fun test_deliver_error/0},
|
{"deliver error still ok", fun test_deliver_error/0},
|
||||||
{"html template placeholders filled", fun test_template_fill/0},
|
{"html template placeholders filled", fun test_template_fill/0},
|
||||||
{"booking reminder deliver", fun test_reminder_deliver/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() ->
|
setup() ->
|
||||||
application:unset_env(eventhub, smtp_host),
|
application:unset_env(eventhub, smtp_host),
|
||||||
application:unset_env(eventhub, smtp_deliver),
|
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.
|
ok.
|
||||||
|
|
||||||
cleanup(_) ->
|
cleanup(_) ->
|
||||||
application:unset_env(eventhub, smtp_host),
|
application:unset_env(eventhub, smtp_host),
|
||||||
application:unset_env(eventhub, smtp_deliver),
|
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.
|
ok.
|
||||||
|
|
||||||
test_log_only() ->
|
test_log_only() ->
|
||||||
application:unset_env(eventhub, smtp_host),
|
application:unset_env(eventhub, smtp_host),
|
||||||
|
application:set_env(eventhub, email_transport, log),
|
||||||
?assertEqual(ok, logic_email:send_verification_email(<<"a@b.c">>, <<"tok">>)).
|
?assertEqual(ok, logic_email:send_verification_email(<<"a@b.c">>, <<"tok">>)).
|
||||||
|
|
||||||
test_verify_deliver() ->
|
test_verify_deliver() ->
|
||||||
Self = self(),
|
Self = self(),
|
||||||
|
application:set_env(eventhub, email_transport, smtp),
|
||||||
application:set_env(eventhub, smtp_host, "smtp.test"),
|
application:set_env(eventhub, smtp_host, "smtp.test"),
|
||||||
application:set_env(eventhub, smtp_deliver,
|
application:set_env(eventhub, smtp_deliver,
|
||||||
fun(From, To, Raw, Opts) ->
|
fun(From, To, Raw, Opts) ->
|
||||||
@@ -50,6 +60,7 @@ test_verify_deliver() ->
|
|||||||
|
|
||||||
test_invite_reset() ->
|
test_invite_reset() ->
|
||||||
Self = self(),
|
Self = self(),
|
||||||
|
application:set_env(eventhub, email_transport, smtp),
|
||||||
application:set_env(eventhub, smtp_host, <<"smtp.test">>),
|
application:set_env(eventhub, smtp_host, <<"smtp.test">>),
|
||||||
application:set_env(eventhub, smtp_deliver,
|
application:set_env(eventhub, smtp_deliver,
|
||||||
fun(_From, To, Raw, _Opts) ->
|
fun(_From, To, Raw, _Opts) ->
|
||||||
@@ -70,6 +81,7 @@ test_invite_reset() ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
test_deliver_error() ->
|
test_deliver_error() ->
|
||||||
|
application:set_env(eventhub, email_transport, smtp),
|
||||||
application:set_env(eventhub, smtp_host, "smtp.test"),
|
application:set_env(eventhub, smtp_host, "smtp.test"),
|
||||||
application:set_env(eventhub, smtp_deliver, fun(_, _, _, _) -> {error, boom} end),
|
application:set_env(eventhub, smtp_deliver, fun(_, _, _, _) -> {error, boom} end),
|
||||||
?assertEqual(ok, logic_email:send_verification_email(<<"a@b.c">>, <<"t">>)),
|
?assertEqual(ok, logic_email:send_verification_email(<<"a@b.c">>, <<"t">>)),
|
||||||
@@ -85,6 +97,7 @@ test_template_fill() ->
|
|||||||
|
|
||||||
test_reminder_deliver() ->
|
test_reminder_deliver() ->
|
||||||
Self = self(),
|
Self = self(),
|
||||||
|
application:set_env(eventhub, email_transport, smtp),
|
||||||
application:set_env(eventhub, smtp_host, "smtp.test"),
|
application:set_env(eventhub, smtp_host, "smtp.test"),
|
||||||
application:set_env(eventhub, smtp_deliver,
|
application:set_env(eventhub, smtp_deliver,
|
||||||
fun(_From, To, Raw, _Opts) ->
|
fun(_From, To, Raw, _Opts) ->
|
||||||
@@ -102,3 +115,33 @@ test_reminder_deliver() ->
|
|||||||
after 1000 ->
|
after 1000 ->
|
||||||
error(timeout)
|
error(timeout)
|
||||||
end.
|
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">>)).
|
||||||
|
|||||||
Reference in New Issue
Block a user