%%%------------------------------------------------------------------- %%% @copyright (C) 2026, QPQ AG %%% @doc %%% EUnit tests for gmser_schema_export. %%% @end %%%------------------------------------------------------------------- -module(gmser_schema_export_tests). -include_lib("eunit/include/eunit.hrl"). %% Spend template as in aec_spend_tx (kept local so tests do not %% depend on aecore). spend_template() -> [ {sender_id, id} , {recipient_id, id} , {amount, int} , {gas_price, int} , {gas, int} , {ttl, int} , {nonce, int} , {payload, binary} ]. signed_tx_template() -> [ {signatures, [binary]} , {transaction, binary} ]. name_update_v1_template() -> [ {account_id, id} , {nonce, int} , {name_id, id} , {name_ttl, int} , {pointers, [{binary, id}]} , {client_ttl, int} , {gas_price, int} , {gas, int} , {ttl, int} ]. match(Bin, Re) when is_binary(Bin) -> re:run(Bin, Re, [{capture, none}]). %%%=================================================================== %%% Naming %%%=================================================================== type_name_from_registered_tag_test() -> Tag = gmser_chain_objects:tag(spend_tx), ?assertEqual(12, Tag), ?assertEqual("SpendTxV1", gmser_schema_export:type_name(Tag, 1)). type_name_from_atom_test() -> ?assertEqual("SpendTxV1", gmser_schema_export:type_name(spend_tx, 12, 1)), ?assertEqual("SignedTxV1", gmser_schema_export:type_name(<<"signed_tx">>, 11, 1)). type_name_unknown_tag_test() -> ?assertEqual("Object9999V2", gmser_schema_export:type_name(9999, 2)). %%%=================================================================== %%% Single object export %%%=================================================================== spend_object_export_test() -> Tag = gmser_chain_objects:tag(spend_tx), {ok, "SpendTxV1", Defs} = gmser_schema_export:object_to_asn1(Tag, 1, spend_template()), Text = iolist_to_binary(Defs), ?assertEqual(match, match(Text, <<"SpendTxV1 ::= SEQUENCE \\{">>)), ?assertEqual(match, match(Text, <<"tag INTEGER \\(12\\)">>)), ?assertEqual(match, match(Text, <<"vsn INTEGER \\(1\\)">>)), ?assertEqual(match, match(Text, <<"senderId Id">>)), ?assertEqual(match, match(Text, <<"recipientId Id">>)), ?assertEqual(match, match(Text, <<"amount BigInt">>)), ?assertEqual(match, match(Text, <<"gasPrice BigInt">>)), ?assertEqual(match, match(Text, <<"payload OCTET STRING">>)), %% last field must not have a trailing comma before closing brace ?assertEqual(match, match(Text, <<"payload OCTET STRING\n}">>)). spend_without_tag_vsn_fields_test() -> Tag = gmser_chain_objects:tag(spend_tx), {ok, "SpendTxV1", Defs} = gmser_schema_export:object_to_asn1( Tag, 1, spend_template(), #{include_tag_vsn => false}), Text = iolist_to_binary(Defs), ?assertEqual(nomatch, match(Text, <<"tag INTEGER">>)), ?assertEqual(match, match(Text, <<"senderId Id">>)). explicit_type_name_test() -> {ok, "MySpendV1", Defs} = gmser_schema_export:object_to_asn1( 12, 1, spend_template(), #{type_name => "MySpend"}), Text = iolist_to_binary(Defs), ?assertEqual(match, match(Text, <<"MySpendV1 ::= SEQUENCE">>)). %%%=================================================================== %%% Nested types %%%=================================================================== list_and_tuple_export_test() -> Tag = gmser_chain_objects:tag(name_update_tx), {ok, TypeName, Defs} = gmser_schema_export:object_to_asn1(Tag, 1, name_update_v1_template()), ?assertEqual("NameUpdateTxV1", TypeName), Text = iolist_to_binary(Defs), ?assertEqual(match, match(Text, <<"pointers SEQUENCE OF SEQUENCE \\{">>)), ?assertEqual(match, match(Text, <<"c1 OCTET STRING">>)), ?assertEqual(match, match(Text, <<"c2 Id">>)). signed_tx_list_field_test() -> Tag = gmser_chain_objects:tag(signed_tx), {ok, "SignedTxV1", Defs} = gmser_schema_export:object_to_asn1(Tag, 1, signed_tx_template()), Text = iolist_to_binary(Defs), ?assertEqual(match, match(Text, <<"signatures SEQUENCE OF OCTET STRING">>)), ?assertEqual(match, match(Text, <<"transaction OCTET STRING">>)). map_items_export_test() -> Template = [{body, #{items => [{foo, int}, {bar, binary}]}}], {ok, _, Defs} = gmser_schema_export:object_to_asn1( 10, 1, Template, #{type_name => account, include_tag_vsn => false}), Text = iolist_to_binary(Defs), ?assertEqual(match, match(Text, <<"body SEQUENCE \\{">>)), ?assertEqual(match, match(Text, <<"foo BigInt">>)), ?assertEqual(match, match(Text, <<"bar OCTET STRING">>)). uint_types_export_test() -> Template = [{a, uint8}, {b, uint16}, {c, uint32}, {d, uint64}, {e, uint128}, {f, bool}], {ok, _, Defs} = gmser_schema_export:object_to_asn1( 1, 1, Template, #{type_name => "Uints", include_tag_vsn => false}), Text = iolist_to_binary(Defs), ?assertEqual(match, match(Text, <<"a Uint8">>)), ?assertEqual(match, match(Text, <<"b Uint16">>)), ?assertEqual(match, match(Text, <<"c Uint32">>)), ?assertEqual(match, match(Text, <<"d Uint64">>)), ?assertEqual(match, match(Text, <<"e Uint128">>)), ?assertEqual(match, match(Text, <<"f BOOLEAN">>)). %%%=================================================================== %%% Full module %%%=================================================================== module_export_test() -> Objects = [ {spend_tx, gmser_chain_objects:tag(spend_tx), 1, spend_template()} , {gmser_chain_objects:tag(signed_tx), 1, signed_tx_template()} ], {ok, Iodata} = gmser_schema_export:module_to_asn1('GajumaruChainObjects', Objects), Text = iolist_to_binary(Iodata), ?assertEqual(match, match(Text, <<"GajumaruChainObjects DEFINITIONS">>)), ?assertEqual(match, match(Text, <<"Id ::= SEQUENCE">>)), ?assertEqual(match, match(Text, <<"BigInt ::= INTEGER">>)), ?assertEqual(match, match(Text, <<"SpendTxV1 ::= SEQUENCE">>)), ?assertEqual(match, match(Text, <<"SignedTxV1 ::= SEQUENCE">>)), ?assertEqual(match, match(Text, <<"tag 12 vsn 1 -> SpendTxV1">>)), ?assertEqual(match, match(Text, <<"tag 11 vsn 1 -> SignedTxV1">>)), ?assertEqual(match, re:run(Text, <<"^END">>, [{capture, none}, multiline])). write_module_test() -> Tmp = filename:join( filename:basedir(user_cache, "gmser_schema_export"), "gmser_schema_export_test.asn"), ok = filelib:ensure_dir(Tmp), Objects = [{gmser_chain_objects:tag(spend_tx), 1, spend_template()}], ?assertEqual(ok, gmser_schema_export:write_module( Tmp, 'GajumaruChainObjects', Objects)), {ok, Bin} = file:read_file(Tmp), ?assertEqual(match, match(Bin, <<"SpendTxV1 ::= SEQUENCE">>)), ok = file:delete(Tmp). bad_template_test() -> ?assertError({unsupported_template_type, float}, gmser_schema_export:object_to_asn1( 1, 1, [{x, float}])).