a cabal implementation in erlang
% SPDX-FileCopyrightText: 2023 Henry Bubert
%
% SPDX-License-Identifier: LGPL-2.1-or-later

-module(cabal_database_suite).

-include_lib("eunit/include/eunit.hrl").

-define(current_function_name(),
    atom_to_list(element(2, element(2, process_info(self(), current_function))))
).

open_close_test() ->
    TestName = ?current_function_name(),
    Db = open_db(TestName),
    cabal_db:close(Db).

%% quick sanity test
post_text_test() ->
    [_, {binary, Bin}, _] = examples:post_text(),
    Decoded = cabal_posts:decode(Bin),
    [Header, Body] = Decoded,
    Hash = proplists:get_value(hash, Header),
    Db = open_db(?current_function_name()),
    {ok, Id, Hash} = cabal_db:save_post(Db, Decoded, Bin),
    ?assertEqual(1, Id),
    {ok, [Header, Body, Bin]} = cabal_db:load_post(Db, Id),
    {ok, [Header, Body, Bin]} = cabal_db:load_post(Db, Hash),
    ?assertEqual(true, cabal_db:has_post(Db, Hash)),
    Rand = crypto:strong_rand_bytes(32),
    ?assertEqual(false, cabal_db:has_post(Db, Rand)),
    cabal_db:close(Db).

all_posts_test() ->
    Examples = [
        examples:post_text(),
        examples:post_delete(),
        examples:post_info(),
        examples:post_topic(),
        examples:post_join(),
        examples:post_leave()
    ],
    Db = open_db(?current_function_name()),
    Test = fun(Ex) ->
        [_, {binary, Bin}, _] = Ex,
        Decoded = cabal_posts:decode(Bin),
        [Header, Body] = Decoded,

        {ok, Id, Hash} = cabal_db:save_post(Db, Decoded, Bin),
        ?assert(is_integer(Id)),

        {ok, [LoadedHeader, LoadedBody, LoadedBin]} = cabal_db:load_post(Db, Id),
        ?assertEqual(Header, LoadedHeader),
        ?assertEqual(Body, LoadedBody),
        ?assertEqual(Bin, LoadedBin),

        Hash = proplists:get_value(hash, Header),
        {ok, [LoadedHeader, LoadedBody, LoadedBin]} = cabal_db:load_post(Db, Hash),

        ?assert(cabal_db:has_post(Db, Hash))
    end,
    lists:foreach(Test, Examples),
    cabal_db:close(Db).

channel_time_range_test() ->
    Ex = examples:post_text(),
    Db = open_db(?current_function_name()),
    [_, {binary, Bin}, _] = Ex,
    Decoded = cabal_posts:decode(Bin),

    {ok, _Id, Hash} = cabal_db:save_post(Db, Bin),
    [Header, _Body] = Decoded,
    Ts = proplists:get_value(timestamp, Header),
    {ok, [GotHash]} = cabal_db:get_text_hashes_by_time_range(Db, "default", Ts - 10, Ts + 10, 1),
    ?assertEqual(Hash, GotHash).

get_oldest_timestamp_test() ->
    Ex = examples:post_text(),
    Db = open_db(?current_function_name()),
    [_, {binary, Bin}, _] = Ex,
    Decoded = cabal_posts:decode(Bin),

    {ok, _Id, _Hash} = cabal_db:save_post(Db, Bin),
    [Header, _Body] = Decoded,
    ExpectedTs = proplists:get_value(timestamp, Header),

    %% Test that we can get the oldest timestamp for the channel
    {ok, OldestTs} = cabal_db:get_oldest_timestamp(Db, "default"),
    ?assertEqual(ExpectedTs, OldestTs),

    cabal_db:close(Db).

% Helpers
%%%%%%%%%

open_db(Name) ->
    %% TODO: cleanup on success
    TestDb = string:chomp(os:cmd("mktemp --tmpdir  -d caberl-" ++ Name ++ "-XXXXX")),
    %?debugFmt("~nUsing DB Path: ~p~n", [TestDb]),
    {ok, Db} = cabal_db:open(TestDb),
    Db.