aboutsummaryrefslogtreecommitdiff
path: root/server/test/jchat_perf_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_perf_SUITE.erl
i vibe coded itHEADmaster
Diffstat (limited to 'server/test/jchat_perf_SUITE.erl')
-rw-r--r--server/test/jchat_perf_SUITE.erl185
1 files changed, 185 insertions, 0 deletions
diff --git a/server/test/jchat_perf_SUITE.erl b/server/test/jchat_perf_SUITE.erl
new file mode 100644
index 0000000..5feccbb
--- /dev/null
+++ b/server/test/jchat_perf_SUITE.erl
@@ -0,0 +1,185 @@
+-module(jchat_perf_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]).
+
+%% Test cases
+-export([test_message_creation_throughput/1,
+ test_conversation_query_performance/1,
+ test_concurrent_requests/1,
+ test_large_conversation/1]).
+
+all() ->
+ [test_message_creation_throughput,
+ test_conversation_query_performance,
+ test_concurrent_requests,
+ test_large_conversation].
+
+init_per_suite(Config) ->
+ application:ensure_all_started(jchat),
+ timer:sleep(1000),
+ Config.
+
+end_per_suite(_Config) ->
+ application:stop(jchat),
+ ok.
+
+%% Performance tests
+
+test_message_creation_throughput(_Config) ->
+ NumMessages = 1000,
+ ConvId = <<"perf-conv-1">>,
+
+ % Setup
+ {ok, _} = jchat_db:create_conversation(ConvId, #{
+ title => <<"Performance Test">>,
+ participant_ids => [<<"user1">>]
+ }),
+
+ % Measure throughput
+ StartTime = erlang:monotonic_time(millisecond),
+
+ lists:foreach(fun(N) ->
+ MsgId = list_to_binary(io_lib:format("msg-~p", [N])),
+ {ok, _} = jchat_db:create_message(MsgId, #{
+ conversation_id => ConvId,
+ sender_id => <<"user1">>,
+ body => <<"Performance test message">>
+ })
+ end, lists:seq(1, NumMessages)),
+
+ EndTime = erlang:monotonic_time(millisecond),
+ Duration = EndTime - StartTime,
+ Throughput = NumMessages * 1000 / Duration,
+
+ ct:pal("Created ~p messages in ~p ms (~.2f msg/sec)",
+ [NumMessages, Duration, Throughput]),
+
+ % Should be able to create at least 100 messages/second
+ true = Throughput > 100.0.
+
+test_conversation_query_performance(_Config) ->
+ NumConversations = 100,
+ NumQueries = 1000,
+
+ % Setup - create conversations
+ lists:foreach(fun(N) ->
+ ConvId = list_to_binary(io_lib:format("query-conv-~p", [N])),
+ {ok, _} = jchat_db:create_conversation(ConvId, #{
+ title => list_to_binary(io_lib:format("Conversation ~p", [N])),
+ participant_ids => [<<"user1">>]
+ })
+ end, lists:seq(1, NumConversations)),
+
+ % Measure query performance
+ StartTime = erlang:monotonic_time(millisecond),
+
+ lists:foreach(fun(_) ->
+ {ok, _Conversations} = jchat_db:query_conversations(<<"user1">>, #{})
+ end, lists:seq(1, NumQueries)),
+
+ EndTime = erlang:monotonic_time(millisecond),
+ Duration = EndTime - StartTime,
+ AvgQueryTime = Duration / NumQueries,
+
+ ct:pal("Executed ~p queries in ~p ms (~.2f ms/query)",
+ [NumQueries, Duration, AvgQueryTime]),
+
+ % Each query should take less than 10ms on average
+ true = AvgQueryTime < 10.0.
+
+test_concurrent_requests(_Config) ->
+ NumWorkers = 10,
+ RequestsPerWorker = 100,
+ ConvId = <<"concurrent-conv">>,
+
+ % Setup
+ {ok, _} = jchat_db:create_conversation(ConvId, #{
+ title => <<"Concurrent Test">>,
+ participant_ids => [<<"user1">>]
+ }),
+
+ Parent = self(),
+ StartTime = erlang:monotonic_time(millisecond),
+
+ % Spawn workers
+ Workers = [spawn_link(fun() ->
+ worker_loop(ConvId, RequestsPerWorker, Parent)
+ end) || _ <- lists:seq(1, NumWorkers)],
+
+ % Wait for all workers to complete
+ lists:foreach(fun(Worker) ->
+ receive
+ {Worker, done} -> ok
+ after 30000 ->
+ error(timeout)
+ end
+ end, Workers),
+
+ EndTime = erlang:monotonic_time(millisecond),
+ Duration = EndTime - StartTime,
+ TotalRequests = NumWorkers * RequestsPerWorker,
+ Throughput = TotalRequests * 1000 / Duration,
+
+ ct:pal("Completed ~p concurrent requests in ~p ms (~.2f req/sec)",
+ [TotalRequests, Duration, Throughput]),
+
+ % Should handle at least 50 concurrent req/sec
+ true = Throughput > 50.0.
+
+test_large_conversation(_Config) ->
+ NumMessages = 10000,
+ ConvId = <<"large-conv">>,
+
+ % Setup
+ {ok, _} = jchat_db:create_conversation(ConvId, #{
+ title => <<"Large Conversation">>,
+ participant_ids => [<<"user1">>]
+ }),
+
+ % Create many messages
+ StartTime = erlang:monotonic_time(millisecond),
+
+ lists:foreach(fun(N) ->
+ MsgId = list_to_binary(io_lib:format("large-msg-~p", [N])),
+ Body = list_to_binary(io_lib:format("Message ~p in large conversation", [N])),
+ {ok, _} = jchat_db:create_message(MsgId, #{
+ conversation_id => ConvId,
+ sender_id => <<"user1">>,
+ body => Body
+ })
+ end, lists:seq(1, NumMessages)),
+
+ EndTime = erlang:monotonic_time(millisecond),
+ Duration = EndTime - StartTime,
+
+ ct:pal("Created large conversation with ~p messages in ~p ms",
+ [NumMessages, Duration]),
+
+ % Test querying the large conversation
+ QueryStart = erlang:monotonic_time(millisecond),
+ {ok, Messages} = jchat_db:query_messages(#{in_conversation => ConvId}, #{sort => sent_at}),
+ QueryEnd = erlang:monotonic_time(millisecond),
+ QueryDuration = QueryEnd - QueryStart,
+
+ ct:pal("Queried ~p messages in ~p ms", [length(Messages), QueryDuration]),
+
+ % Query should complete reasonably fast even for large conversations
+ true = QueryDuration < 1000. % Less than 1 second
+
+%% Helper functions
+
+worker_loop(ConvId, 0, Parent) ->
+ Parent ! {self(), done};
+worker_loop(ConvId, N, Parent) ->
+ % Create a message
+ MsgId = list_to_binary(io_lib:format("worker-~p-msg-~p", [self(), N])),
+ {ok, _} = jchat_db:create_message(MsgId, #{
+ conversation_id => ConvId,
+ sender_id => <<"user1">>,
+ body => <<"Concurrent test message">>
+ }),
+ worker_loop(ConvId, N - 1, Parent).