aboutsummaryrefslogtreecommitdiff
path: root/server/test/jchat_http_SUITE.erl
diff options
context:
space:
mode:
authorCalvin Morrison <calvin@pobox.com>2025-09-03 21:15:36 -0400
committerCalvin Morrison <calvin@pobox.com>2025-09-03 21:15:36 -0400
commit49fa5aa2a127bdf8924d02bf77e5086b39c7a447 (patch)
tree61d86a7705dacc9fddccc29fa79d075d83ab8059 /server/test/jchat_http_SUITE.erl
i vibe coded itHEADmaster
Diffstat (limited to 'server/test/jchat_http_SUITE.erl')
-rw-r--r--server/test/jchat_http_SUITE.erl224
1 files changed, 224 insertions, 0 deletions
diff --git a/server/test/jchat_http_SUITE.erl b/server/test/jchat_http_SUITE.erl
new file mode 100644
index 0000000..5c8804f
--- /dev/null
+++ b/server/test/jchat_http_SUITE.erl
@@ -0,0 +1,224 @@
+-module(jchat_http_SUITE).
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("eunit/include/eunit.hrl").
+
+%%====================================================================
+%% CT Callbacks
+%%====================================================================
+
+suite() ->
+ [{timetrap, {seconds, 30}}].
+
+init_per_suite(Config) ->
+ % Start the application
+ application:ensure_all_started(jchat),
+ % Wait for server to start
+ timer:sleep(1000),
+ [{server_url, "http://localhost:8081"} | Config].
+
+end_per_suite(_Config) ->
+ application:stop(jchat),
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ % Clean up test data
+ mnesia:clear_table(user),
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+all() ->
+ [test_auth_register_endpoint,
+ test_auth_login_endpoint,
+ test_auth_me_endpoint,
+ test_jmap_api_with_auth,
+ test_jmap_api_without_auth,
+ test_cors_headers].
+
+%%====================================================================
+%% Test Cases
+%%====================================================================
+
+test_auth_register_endpoint(Config) ->
+ ServerUrl = ?config(server_url, Config),
+
+ % Prepare registration data
+ Email = "test@example.com",
+ Password = "testpass123",
+ DisplayName = "Test User",
+
+ ReqBody = jsx:encode(#{
+ <<"email">> => list_to_binary(Email),
+ <<"password">> => list_to_binary(Password),
+ <<"displayName">> => list_to_binary(DisplayName)
+ }),
+
+ % Make registration request
+ Url = ServerUrl ++ "/auth/register",
+ Headers = [{"content-type", "application/json"}],
+
+ {ok, {{_Version, 201, _ReasonPhrase}, _Headers, ResponseBody}} =
+ httpc:request(post, {Url, Headers, "application/json", ReqBody}, [], []),
+
+ % Parse response
+ ResponseMap = jsx:decode(list_to_binary(ResponseBody)),
+ ?assert(maps:is_key(<<"token">>, ResponseMap)),
+ ?assert(maps:is_key(<<"user">>, ResponseMap)),
+
+ User = maps:get(<<"user">>, ResponseMap),
+ ?assertEqual(list_to_binary(Email), maps:get(<<"email">>, User)),
+ ?assertEqual(list_to_binary(DisplayName), maps:get(<<"displayName">>, User)).
+
+test_auth_login_endpoint(Config) ->
+ ServerUrl = ?config(server_url, Config),
+
+ % First register a user
+ Email = "login.test@example.com",
+ Password = "logintest123",
+ DisplayName = "Login Test User",
+
+ {ok, _User} = jchat_auth:register_user(
+ list_to_binary(Email),
+ list_to_binary(Password),
+ list_to_binary(DisplayName)
+ ),
+
+ % Now test login
+ ReqBody = jsx:encode(#{
+ <<"email">> => list_to_binary(Email),
+ <<"password">> => list_to_binary(Password)
+ }),
+
+ Url = ServerUrl ++ "/auth/login",
+ Headers = [{"content-type", "application/json"}],
+
+ {ok, {{_Version, 200, _ReasonPhrase}, _Headers, ResponseBody}} =
+ httpc:request(post, {Url, Headers, "application/json", ReqBody}, [], []),
+
+ % Parse response
+ ResponseMap = jsx:decode(list_to_binary(ResponseBody)),
+ ?assert(maps:is_key(<<"token">>, ResponseMap)),
+ ?assert(maps:is_key(<<"user">>, ResponseMap)).
+
+test_auth_me_endpoint(Config) ->
+ ServerUrl = ?config(server_url, Config),
+
+ % Register and login to get token
+ Email = "me.test@example.com",
+ Password = "metest123",
+ DisplayName = "Me Test User",
+
+ {ok, _User} = jchat_auth:register_user(
+ list_to_binary(Email),
+ list_to_binary(Password),
+ list_to_binary(DisplayName)
+ ),
+
+ {ok, {_AuthUser, Token}} = jchat_auth:authenticate_user(
+ list_to_binary(Email),
+ list_to_binary(Password)
+ ),
+
+ % Test /auth/me endpoint
+ Url = ServerUrl ++ "/auth/me",
+ Headers = [{"authorization", "Bearer " ++ binary_to_list(Token)}],
+
+ {ok, {{_Version, 200, _ReasonPhrase}, _Headers, ResponseBody}} =
+ httpc:request(get, {Url, Headers}, [], []),
+
+ % Parse response
+ ResponseMap = jsx:decode(list_to_binary(ResponseBody)),
+ ?assert(maps:is_key(<<"user">>, ResponseMap)),
+
+ User = maps:get(<<"user">>, ResponseMap),
+ ?assertEqual(list_to_binary(Email), maps:get(<<"email">>, User)).
+
+test_jmap_api_with_auth(Config) ->
+ ServerUrl = ?config(server_url, Config),
+
+ % Register and login to get token
+ Email = "jmap.test@example.com",
+ Password = "jmaptest123",
+ DisplayName = "JMAP Test User",
+
+ {ok, _User} = jchat_auth:register_user(
+ list_to_binary(Email),
+ list_to_binary(Password),
+ list_to_binary(DisplayName)
+ ),
+
+ {ok, {_AuthUser, Token}} = jchat_auth:authenticate_user(
+ list_to_binary(Email),
+ list_to_binary(Password)
+ ),
+
+ % Test JMAP API call
+ ReqBody = jsx:encode(#{
+ <<"using">> => [<<"urn:ietf:params:jmap:core">>, <<"https://jmap.io/jchat/">>],
+ <<"methodCalls">> => [
+ [<<"Conversation/query">>, #{
+ <<"accountId">> => <<"default">>,
+ <<"filter">> => #{},
+ <<"sort">> => [#{<<"property">> => <<"lastMessageAt">>, <<"isAscending">> => false}]
+ }, <<"c1">>]
+ ]
+ }),
+
+ Url = ServerUrl ++ "/jmap/api",
+ Headers = [
+ {"content-type", "application/json"},
+ {"authorization", "Bearer " ++ binary_to_list(Token)}
+ ],
+
+ {ok, {{_Version, 200, _ReasonPhrase}, _Headers, ResponseBody}} =
+ httpc:request(post, {Url, Headers, "application/json", ReqBody}, [], []),
+
+ % Parse response
+ ResponseMap = jsx:decode(list_to_binary(ResponseBody)),
+ ?assert(maps:is_key(<<"methodResponses">>, ResponseMap)).
+
+test_jmap_api_without_auth(Config) ->
+ ServerUrl = ?config(server_url, Config),
+
+ % Test JMAP API call without authentication
+ ReqBody = jsx:encode(#{
+ <<"using">> => [<<"urn:ietf:params:jmap:core">>, <<"https://jmap.io/jchat/">>],
+ <<"methodCalls">> => [
+ [<<"Conversation/query">>, #{
+ <<"accountId">> => <<"default">>,
+ <<"filter">> => #{},
+ <<"sort">> => [#{<<"property">> => <<"lastMessageAt">>, <<"isAscending">> => false}]
+ }, <<"c1">>]
+ ]
+ }),
+
+ Url = ServerUrl ++ "/jmap/api",
+ Headers = [{"content-type", "application/json"}],
+
+ {ok, {{_Version, 401, _ReasonPhrase}, _Headers, _ResponseBody}} =
+ httpc:request(post, {Url, Headers, "application/json", ReqBody}, [], []).
+
+test_cors_headers(Config) ->
+ ServerUrl = ?config(server_url, Config),
+
+ % Test CORS preflight
+ Url = ServerUrl ++ "/auth/register",
+ Headers = [
+ {"origin", "http://localhost:3000"},
+ {"access-control-request-method", "POST"},
+ {"access-control-request-headers", "content-type,authorization"}
+ ],
+
+ {ok, {{_Version, StatusCode, _ReasonPhrase}, ResponseHeaders, _ResponseBody}} =
+ httpc:request(options, {Url, Headers}, [], []),
+
+ % Should return 200 or 204 for OPTIONS
+ ?assert(StatusCode =:= 200 orelse StatusCode =:= 204),
+
+ % Check for CORS headers
+ HeadersMap = maps:from_list(ResponseHeaders),
+ ?assert(maps:is_key("access-control-allow-origin", HeadersMap) orelse
+ maps:is_key("Access-Control-Allow-Origin", HeadersMap)).