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

-module(cabal_posts_SUITE).

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

%% decode(encode(...))
%%%%%%%%%%%%%%%%%%%%%%

encode_test_test() ->
    TestKp = enacl:sign_keypair(),
    Chan = <<"#foo">>,
    Text = <<"hello, world!😉">>,
    Post = cabal_posts:encode(TestKp, [], {text, Chan, Text}),
    Decoded = cabal_posts:decode(Post),
    [
        [
            {public_key, _Public},
            {links, _Links},
            {type, 0},
            {timestamp, _TimeStamp},
            {hash, _Hash}
        ],
        [
            {channel, GotChan},
            {text, GotText}
        ]
    ] = Decoded,
    ?assertEqual(Text, GotText),
    ?assertEqual(Chan, GotChan).

encode_info_test() ->
    TestKp = enacl:sign_keypair(),
    Name = "元気な子",
    Post = cabal_posts:encode(TestKp, [], {info, {name, Name}}),
    Decoded = cabal_posts:decode(Post),
    [
        [
            {public_key, _Public},
            {links, _Links},
            {type, 2},
            {timestamp, _TimeStamp},
            {hash, _Hash}
        ],
        [{infos, #{<<"name">> := BinName}}]
    ] = Decoded,
    GotName = unicode:characters_to_list(BinName),
    ?assertEqual(Name, GotName).

%% from example tests
%%%%%%%%%%%%%%%%%%%%%

post_text_test() ->
    [{name, _}, {binary, Bin}, {obj, TestObj}] = examples:post_text(),
    Decoded = cabal_posts:decode(Bin),
    [
        [
            {public_key, _PubKey},
            {links, _Links},
            {type, 0},
            {timestamp, _TimeStamp},
            {hash, _Hash}
        ],
        [
            {channel, Channel},
            {text, Text}
        ]
    ] = Decoded,
    ?assertEqual(<<"default">>, Channel),
    ?assertEqual(<<"h€llo world"/utf8>>, Text),
    assert_post(Decoded, TestObj).

post_delete_test() ->
    [{name, _}, {binary, Bin}, {obj, TestObj}] = examples:post_delete(),
    Decoded = cabal_posts:decode(Bin),
    [
        [
            {public_key, _PubKey},
            {links, _Links},
            {type, 1},
            {timestamp, _TimeStamp},
            {hash, _Hash}
        ],
        [{hashes, Hashes}]
    ] = Decoded,
    %% TODO: assert Hash values
    ?assertEqual(length(maps:get(<<"hashes">>, TestObj)), length(Hashes)),
    assert_post(Decoded, TestObj).

post_info_test() ->
    [{name, _}, {binary, Bin}, {obj, TestObj}] = examples:post_info(),
    Decoded = cabal_posts:decode(Bin),
    [
        [
            {public_key, _PubKey},
            {links, _Links},
            {type, 2},
            {timestamp, _TimeStamp},
            {hash, _Hash}
        ],
        [{infos, Infos}]
    ] = Decoded,
    ?assertEqual(#{<<"name">> => <<"cabler">>}, Infos),
    assert_post(Decoded, TestObj).

post_topic_test() ->
    [{name, _}, {binary, Bin}, {obj, TestObj}] = examples:post_topic(),
    Decoded = cabal_posts:decode(Bin),
    [
        [
            {public_key, _PubKey},
            {links, _Links},
            {type, 3},
            {timestamp, _TimeStamp},
            {hash, _Hash}
        ],
        [
            {channel, Channel},
            {topic, Topic}
        ]
    ] = Decoded,
    ?assertEqual(maps:get(<<"channel">>, TestObj), Channel),
    ?assertEqual(maps:get(<<"topic">>, TestObj), Topic),
    assert_post(Decoded, TestObj).

post_join_test() ->
    [{name, _}, {binary, Bin}, {obj, TestObj}] = examples:post_join(),
    Decoded = cabal_posts:decode(Bin),
    [
        [
            {public_key, _PubKey},
            {links, _Links},
            {type, 4},
            {timestamp, _TimeStamp},
            {hash, _Hash}
        ],
        [{channel, Channel}]
    ] = Decoded,
    ?assertEqual(maps:get(<<"channel">>, TestObj), Channel),
    assert_post(Decoded, TestObj).

post_leave_test() ->
    [{name, _}, {binary, Bin}, {obj, TestObj}] = examples:post_leave(),
    Decoded = cabal_posts:decode(Bin),
    [
        [
            {public_key, _PubKey},
            {links, _Links},
            {type, 5},
            {timestamp, _TimeStamp},
            {hash, _Hash}
        ],
        [{channel, Channel}]
    ] = Decoded,
    ?assertEqual(maps:get(<<"channel">>, TestObj), Channel),
    assert_post(Decoded, TestObj).

%% Helpers
%%%%%%%%%%

assert_post([Header, _], TestObj) ->
    ?assertEqual(maps:get(<<"postType">>, TestObj), proplists:get_value(type, Header)),
    ?assertEqual(maps:get(<<"timestamp">>, TestObj), proplists:get_value(timestamp, Header)),
    ?assertEqual(
        length(maps:get(<<"links">>, TestObj)), length(proplists:get_value(links, Header))
    ),
    PubKey = hex:hexstr_to_bin(binary_to_list(maps:get(<<"publicKey">>, TestObj))),
    ?assertEqual(PubKey, proplists:get_value(public_key, Header)).