Write the Erlang device with inline eunit tests (HyperBEAM convention — device and tests live in the same file):
Write $HB_DIR/src/dev_{name}.erl:
-module(dev_{name}).
-export([info/3, init/3, compute/3]).
-include("include/hb.hrl").
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-endif.
%% Exports: list available keys
info(_Msg1, _Msg2, Opts) ->
{ok, #{
<<"exports">> => [<<"init">>, <<"compute">>]
}}.
%% Initialize device state
init(Msg1, _Msg2, Opts) ->
{ok, hb_maps:put(<<"initialized">>, true, Msg1)}.
%% Main compute — route by Action tag
compute(Msg1, Msg2, Opts) ->
Action = hb_ao:get(<<"Action">>, Msg2, Opts),
handle_action(Action, Msg1, Msg2, Opts).
handle_action(<<"MyAction">>, Msg1, Msg2, Opts) ->
%% Implementation here
{ok, Msg1};
handle_action(_, Msg1, _Msg2, _Opts) ->
{ok, hb_maps:put(<<"Error">>, <<"Unknown action">>, Msg1)}.
%%%===================================================================
%%% EUnit Tests (inline — HyperBEAM convention)
%%%===================================================================
-ifdef(TEST).
info_test() ->
{ok, Info} = info(#{}, #{}, #{}),
?assertMatch(#{<<"exports">> := _}, Info).
compute_test() ->
{ok, State} = init(#{}, #{}, #{}),
Msg = #{<<"Action">> => <<"MyAction">>},
{ok, Result} = compute(State, Msg, #{}),
?assertMatch(#{}, Result).
-endif.
Key patterns:
- Device + tests in same file — use
-ifdef(TEST). / -endif. guards
- Tests call local functions directly (no
module:function prefix needed)
- Use
hb_maps for state (not plain maps)
- Use
hb_private for secrets
- Use
ar_wallet for auth/signing
- Route actions via
hb_ao:get(<<"Action">>, Msg2, Opts)
- Return
{ok, UpdatedMsg} or {error, Reason}
Do NOT create separate test files in $HB_DIR/test/. All eunit tests go inline in the device source file.
Compile (use the $HB_DIR path):
cd $HB_DIR && rebar3 as genesis_wasm compile
Verify the .beam file was actually created:
ls $HB_DIR/_build/genesis_wasm/lib/hb/ebin/dev_{name}.beam && echo "COMPILED" || echo "NOT FOUND"
If the beam file is missing, compilation failed silently. Read the full output and fix.