-module(jchat_SUITE). -include_lib("common_test/include/ct.hrl"). -include("../src/jchat.hrl"). %% CT callbacks -export([all/0, init_per_suite/1, end_per_suite/1, init_per_testcase/2, end_per_testcase/2]). %% Test cases -export([test_session_endpoint/1, test_core_echo/1, test_conversation_create/1, test_conversation_get/1, test_message_create/1, test_message_get/1, test_invalid_method/1, test_invalid_json/1, test_unknown_capability/1]). all() -> [test_session_endpoint, test_core_echo, test_conversation_create, test_conversation_get, test_message_create, test_message_get, test_invalid_method, test_invalid_json, test_unknown_capability]. init_per_suite(Config) -> % Start the application application:ensure_all_started(jchat), timer:sleep(1000), % Allow startup Config. end_per_suite(_Config) -> application:stop(jchat), ok. init_per_testcase(_TestCase, Config) -> Config. end_per_testcase(_TestCase, _Config) -> ok. %% Test cases test_session_endpoint(_Config) -> URL = "http://localhost:8080/jmap/session", {ok, {{_, 200, _}, Headers, Body}} = httpc:request(get, {URL, []}, [], []), % Check content type {"content-type", "application/json"} = lists:keyfind("content-type", 1, Headers), % Parse and validate session object {ok, Session} = jchat_utils:json_decode(list_to_binary(Body)), % Validate required fields true = maps:is_key(<<"capabilities">>, Session), true = maps:is_key(<<"accounts">>, Session), true = maps:is_key(<<"apiUrl">>, Session), % Check chat capability Capabilities = maps:get(<<"capabilities">>, Session), true = maps:is_key(<<"urn:ietf:params:jmap:chat">>, Capabilities). test_core_echo(_Config) -> Request = #{ <<"using">> => [<<"urn:ietf:params:jmap:core">>], <<"methodCalls">> => [ [<<"Core/echo">>, #{<<"hello">> => <<"world">>}, <<"c1">>] ] }, Response = make_jmap_request(Request), MethodResponses = maps:get(<<"methodResponses">>, Response), [EchoResponse] = MethodResponses, [<<"Core/echo">>, Args, <<"c1">>] = EchoResponse, <<"world">> = maps:get(<<"hello">>, Args). test_conversation_create(_Config) -> Request = #{ <<"using">> => [<<"urn:ietf:params:jmap:core">>, <<"urn:ietf:params:jmap:chat">>], <<"methodCalls">> => [ [<<"Conversation/set">>, #{ <<"accountId">> => <<"default">>, <<"create">> => #{ <<"conv1">> => #{ <<"title">> => <<"Test Conversation">>, <<"participantIds">> => [<<"user1">>, <<"user2">>] } } }, <<"c1">>] ] }, Response = make_jmap_request(Request), MethodResponses = maps:get(<<"methodResponses">>, Response), [SetResponse] = MethodResponses, [<<"Conversation/set">>, Args, <<"c1">>] = SetResponse, Created = maps:get(<<"created">>, Args), true = maps:is_key(<<"conv1">>, Created). test_conversation_get(_Config) -> % First create a conversation {ok, Conv} = jchat_db:create_conversation(<<"test-conv-1">>, #{ title => <<"Test Conversation">>, participant_ids => [<<"user1">>] }), Request = #{ <<"using">> => [<<"urn:ietf:params:jmap:core">>, <<"urn:ietf:params:jmap:chat">>], <<"methodCalls">> => [ [<<"Conversation/get">>, #{ <<"accountId">> => <<"default">>, <<"ids">> => [<<"test-conv-1">>] }, <<"c1">>] ] }, Response = make_jmap_request(Request), MethodResponses = maps:get(<<"methodResponses">>, Response), [GetResponse] = MethodResponses, [<<"Conversation/get">>, Args, <<"c1">>] = GetResponse, List = maps:get(<<"list">>, Args), [ConvData] = List, <<"test-conv-1">> = maps:get(<<"id">>, ConvData). test_message_create(_Config) -> % First create a conversation {ok, _Conv} = jchat_db:create_conversation(<<"test-conv-2">>, #{ title => <<"Message Test Conversation">>, participant_ids => [<<"user1">>] }), Request = #{ <<"using">> => [<<"urn:ietf:params:jmap:core">>, <<"urn:ietf:params:jmap:chat">>], <<"methodCalls">> => [ [<<"Message/set">>, #{ <<"accountId">> => <<"default">>, <<"create">> => #{ <<"msg1">> => #{ <<"conversationId">> => <<"test-conv-2">>, <<"body">> => <<"Hello, World!">>, <<"senderId">> => <<"user1">> } } }, <<"c1">>] ] }, Response = make_jmap_request(Request), MethodResponses = maps:get(<<"methodResponses">>, Response), [SetResponse] = MethodResponses, [<<"Message/set">>, Args, <<"c1">>] = SetResponse, Created = maps:get(<<"created">>, Args), true = maps:is_key(<<"msg1">>, Created). test_message_get(_Config) -> % Create conversation and message {ok, _Conv} = jchat_db:create_conversation(<<"test-conv-3">>, #{ title => <<"Get Test Conversation">>, participant_ids => [<<"user1">>] }), {ok, _Msg} = jchat_db:create_message(<<"test-msg-1">>, #{ conversation_id => <<"test-conv-3">>, sender_id => <<"user1">>, body => <<"Test message">> }), Request = #{ <<"using">> => [<<"urn:ietf:params:jmap:core">>, <<"urn:ietf:params:jmap:chat">>], <<"methodCalls">> => [ [<<"Message/get">>, #{ <<"accountId">> => <<"default">>, <<"ids">> => [<<"test-msg-1">>] }, <<"c1">>] ] }, Response = make_jmap_request(Request), MethodResponses = maps:get(<<"methodResponses">>, Response), [GetResponse] = MethodResponses, [<<"Message/get">>, Args, <<"c1">>] = GetResponse, List = maps:get(<<"list">>, Args), [MsgData] = List, <<"test-msg-1">> = maps:get(<<"id">>, MsgData). test_invalid_method(_Config) -> Request = #{ <<"using">> => [<<"urn:ietf:params:jmap:core">>], <<"methodCalls">> => [ [<<"Invalid/method">>, #{}, <<"c1">>] ] }, Response = make_jmap_request(Request), MethodResponses = maps:get(<<"methodResponses">>, Response), [ErrorResponse] = MethodResponses, [<<"error">>, ErrorArgs, <<"c1">>] = ErrorResponse, <<"unknownMethod">> = maps:get(<<"type">>, ErrorArgs). test_invalid_json(_Config) -> URL = "http://localhost:8080/jmap/api", InvalidJSON = "{ invalid json", {ok, {{_, 400, _}, _Headers, Body}} = httpc:request(post, {URL, [], "application/json", InvalidJSON}, [], []), {ok, ErrorResponse} = jchat_utils:json_decode(list_to_binary(Body)), <<"urn:ietf:params:jmap:error:notJSON">> = maps:get(<<"type">>, ErrorResponse). test_unknown_capability(_Config) -> Request = #{ <<"using">> => [<<"unknown:capability">>], <<"methodCalls">> => [] }, RequestJSON = jchat_utils:json_encode(Request), URL = "http://localhost:8080/jmap/api", {ok, {{_, 400, _}, _Headers, Body}} = httpc:request(post, {URL, [], "application/json", RequestJSON}, [], []), {ok, ErrorResponse} = jchat_utils:json_decode(list_to_binary(Body)), <<"urn:ietf:params:jmap:error:unknownCapability">> = maps:get(<<"type">>, ErrorResponse). %% Helper functions make_jmap_request(Request) -> RequestJSON = jchat_utils:json_encode(Request), URL = "http://localhost:8080/jmap/api", {ok, {{_, 200, _}, _Headers, Body}} = httpc:request(post, {URL, [], "application/json", RequestJSON}, [], []), {ok, Response} = jchat_utils:json_decode(list_to_binary(Body)), Response.