Fosi Fossil Service
Small teams. Open source. Built on Fossil.

CaaS / Lamina

Unified Management for Fossil Repositories

Public | Owner: CaaS | Default branch: trunk | Last update: 2026-10-25 00:41

Artifact 41bd2275524f1b5be60a0be1dd15290b237f90efe585bd170ba175308f49e358:


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

-define(SETUP, fun() -> setup() end).
-define(CLEANUP, fun(S) -> cleanup(S) end).

setup() ->
    Prev = nova_test_helper:setup_nova_env(),
    application:set_env(nova, dispatch_backend, persistent_term),
    persistent_term:put(nova_plugins, []),
    {ok, Pid} = nova_plugin_manager:start_link(),
    %% Set up mock plugin module
    meck:new(test_plugin, [non_strict, no_link]),
    meck:expect(test_plugin, plugin_info, fun() ->
        #{title => <<"Test Plugin">>, version => <<"1.0.0">>}
    end),
    meck:expect(test_plugin, init, fun() -> #{initialized => true} end),
    meck:expect(test_plugin, stop, fun() -> ok end),
    %% Plugin without init
    meck:new(test_plugin_no_init, [non_strict, no_link]),
    meck:expect(test_plugin_no_init, plugin_info, fun() ->
        #{title => <<"No Init Plugin">>, version => <<"0.1.0">>}
    end),
    %% Plugin with old tuple format for plugin_info
    meck:new(test_plugin_old, [non_strict, no_link]),
    meck:expect(test_plugin_old, plugin_info, fun() ->
        {<<"Old Plugin">>, <<"2.0.0">>, <<"Author">>, <<"Desc">>, <<"URL">>}
    end),
    {Prev, Pid}.

cleanup({Prev, Pid}) ->
    gen_server:stop(Pid),
    meck:unload(test_plugin),
    meck:unload(test_plugin_no_init),
    meck:unload(test_plugin_old),
    persistent_term:erase(nova_plugins),
    nova_test_helper:cleanup_nova_env(Prev).

%%====================================================================
%% add_plugin/1 with module
%%====================================================================

add_plugin_module_test_() ->
    {setup, ?SETUP, ?CLEANUP, fun() ->
        ok = nova_plugin_manager:add_plugin(test_plugin),
        timer:sleep(50),
        {ok, State} = nova_plugin_manager:get_state(test_plugin),
        ?assertEqual(#{initialized => true}, State)
    end}.

add_plugin_no_init_test_() ->
    {setup, ?SETUP, ?CLEANUP, fun() ->
        ok = nova_plugin_manager:add_plugin(test_plugin_no_init),
        timer:sleep(50),
        {ok, State} = nova_plugin_manager:get_state(test_plugin_no_init),
        ?assertEqual(#{}, State)
    end}.

add_plugin_old_format_test_() ->
    {setup, ?SETUP, ?CLEANUP, fun() ->
        ok = nova_plugin_manager:add_plugin(test_plugin_old),
        timer:sleep(50),
        ?assertMatch({ok, _}, nova_plugin_manager:get_state(test_plugin_old))
    end}.

add_plugin_duplicate_test_() ->
    {setup, ?SETUP, ?CLEANUP, fun() ->
        ok = nova_plugin_manager:add_plugin(test_plugin),
        timer:sleep(50),
        %% Adding again should return ok (already initialized)
        ok = nova_plugin_manager:add_plugin(test_plugin)
    end}.

%%====================================================================
%% add_plugin/1 with tuple
%%====================================================================

add_plugin_tuple_test_() ->
    {setup, ?SETUP, ?CLEANUP, fun() ->
        ok = nova_plugin_manager:add_plugin({pre_request, test_plugin, #{}}),
        timer:sleep(50),
        ?assertMatch({ok, _}, nova_plugin_manager:get_state(test_plugin))
    end}.

%%====================================================================
%% add_plugin/1 with function
%%====================================================================

add_plugin_function_test_() ->
    {setup, ?SETUP, ?CLEANUP, fun() ->
        ok = nova_plugin_manager:add_plugin(fun test_plugin:plugin_info/0),
        timer:sleep(50),
        ?assertMatch({ok, _}, nova_plugin_manager:get_state(test_plugin))
    end}.

%%====================================================================
%% get_state/1
%%====================================================================

get_state_not_found_test_() ->
    {setup, ?SETUP, ?CLEANUP, fun() ->
        ?assertEqual({error, not_found}, nova_plugin_manager:get_state(nonexistent_plugin))
    end}.

get_state_by_function_test_() ->
    {setup, ?SETUP, ?CLEANUP, fun() ->
        ok = nova_plugin_manager:add_plugin(test_plugin),
        timer:sleep(50),
        {ok, State} = nova_plugin_manager:get_state(fun test_plugin:plugin_info/0),
        ?assertEqual(#{initialized => true}, State)
    end}.

%%====================================================================
%% set_state/2
%%====================================================================

set_state_test_() ->
    {setup, ?SETUP, ?CLEANUP, fun() ->
        ok = nova_plugin_manager:add_plugin(test_plugin),
        timer:sleep(50),
        ok = nova_plugin_manager:set_state(test_plugin, #{new_state => true}),
        {ok, State} = nova_plugin_manager:get_state(test_plugin),
        ?assertEqual(#{new_state => true}, State)
    end}.

set_state_not_found_test_() ->
    {setup, ?SETUP, ?CLEANUP, fun() ->
        ?assertEqual({error, not_found}, nova_plugin_manager:set_state(nonexistent_plugin, #{}))
    end}.

set_state_by_function_test_() ->
    {setup, ?SETUP, ?CLEANUP, fun() ->
        ok = nova_plugin_manager:add_plugin(test_plugin),
        timer:sleep(50),
        ok = nova_plugin_manager:set_state(fun test_plugin:plugin_info/0, #{via_fun => true}),
        {ok, State} = nova_plugin_manager:get_state(test_plugin),
        ?assertEqual(#{via_fun => true}, State)
    end}.

%%====================================================================
%% add_plugin/3
%%====================================================================

add_plugin_3_test_() ->
    {setup, ?SETUP, ?CLEANUP, fun() ->
        ok = nova_plugin_manager:add_plugin(test_plugin, <<"Custom Name">>, <<"3.0.0">>),
        timer:sleep(50),
        ?assertMatch({ok, _}, nova_plugin_manager:get_state(test_plugin))
    end}.

%%====================================================================
%% code_change/3
%%====================================================================

code_change_test_() ->
    {setup, ?SETUP, ?CLEANUP, fun() ->
        ?assertEqual({ok, my_state}, nova_plugin_manager:code_change("1.0", my_state, []))
    end}.

Clone

You can clone this repository with the following command:

fossil clone https://fosi.dev/CaaS/Lamina

Contribute

The best way to contribute is through e-mail. See the project documentation for more details.