%% Test suite for the handling hexpm repo configurations
|
|
-module(rebar_pkg_repos_SUITE).
|
|
|
|
-compile(export_all).
|
|
|
|
-include_lib("common_test/include/ct.hrl").
|
|
-include_lib("eunit/include/eunit.hrl").
|
|
-include("rebar.hrl").
|
|
|
|
all() ->
|
|
[default_repo, repo_merging, repo_replacing,
|
|
auth_read_write_read, auth_remove_from_config, auth_merging, auth_config_errors, organization_merging,
|
|
{group, resolve_version}].
|
|
|
|
groups() ->
|
|
[{resolve_version, [use_first_repo_match, use_exact_with_hash, fail_repo_update,
|
|
ignore_match_in_excluded_repo, optional_prereleases]}].
|
|
|
|
init_per_group(resolve_version, Config) ->
|
|
Repo1 = <<"test-repo-1">>,
|
|
Repo2 = <<"test-repo-2">>,
|
|
Repo3 = <<"test-repo-3">>,
|
|
Hexpm = <<"hexpm">>,
|
|
Repos = [Repo1, Repo2, Repo3, Hexpm],
|
|
|
|
Deps = [{"A", "0.1.1", <<"inner checksum">>, <<"good outer checksum">>, Repo1, false},
|
|
{"A", "0.1.1", <<"inner checksum">>, <<"good outer checksum">>, Repo2, false},
|
|
{"B", "1.0.0", Repo1, false},
|
|
{"B", "2.0.0", Repo2, false},
|
|
{"B", "1.4.0", Repo3, false},
|
|
{"B", "1.4.3", Hexpm, false},
|
|
{"B", "1.4.6", Hexpm, #{reason => 'RETIRED_INVALID'}},
|
|
{"B", "1.5.0", Hexpm, false},
|
|
{"B", "1.5.6-rc.0", Hexpm, true},
|
|
{"C", "1.3.1", <<"inner checksum">>, <<"bad outer checksum">>, Repo1, false},
|
|
{"C", "1.3.1", <<"inner checksum">>, <<"good outer checksum">>, Repo2, false}],
|
|
[{deps, Deps}, {repos, Repos} | Config];
|
|
init_per_group(_, Config) ->
|
|
Config.
|
|
|
|
end_per_group(_, _) ->
|
|
ok.
|
|
|
|
init_per_testcase(use_first_repo_match, Config) ->
|
|
Deps = ?config(deps, Config),
|
|
Repos = ?config(repos, Config),
|
|
State = setup_deps_and_repos(Deps, Repos),
|
|
|
|
meck:new(rebar_packages, [passthrough, no_link]),
|
|
|
|
%% fail when the first repo is updated since it doesn't have a matching package
|
|
%% should continue anyway
|
|
meck:expect(rebar_packages, update_package,
|
|
fun(_, _, _State) -> ok end),
|
|
meck:expect(rebar_packages, verify_table,
|
|
fun(_State) -> true end),
|
|
|
|
[{state, State} | Config];
|
|
init_per_testcase(use_exact_with_hash, Config) ->
|
|
Deps = ?config(deps, Config),
|
|
Repos = ?config(repos, Config),
|
|
State = setup_deps_and_repos(Deps, Repos),
|
|
|
|
meck:new(rebar_packages, [passthrough, no_link]),
|
|
|
|
%% fail when the first repo is updated since it doesn't have a matching package
|
|
%% should continue anyway
|
|
meck:expect(rebar_packages, update_package,
|
|
fun(_, _, _State) -> ok end),
|
|
meck:expect(rebar_packages, verify_table,
|
|
fun(_State) -> true end),
|
|
|
|
[{state, State} | Config];
|
|
init_per_testcase(fail_repo_update, Config) ->
|
|
Deps = ?config(deps, Config),
|
|
Repos = ?config(repos, Config),
|
|
State = setup_deps_and_repos(Deps, Repos),
|
|
|
|
meck:new(rebar_packages, [passthrough, no_link]),
|
|
|
|
%% fail when the first repo is updated since it doesn't have a matching package
|
|
%% should continue anyway
|
|
[Repo1 | _] = Repos,
|
|
meck:expect(rebar_packages, update_package,
|
|
fun(_, #{name := Repo}, _State) when Repo =:= Repo1 -> fail;
|
|
(_, _, _State) -> ok end),
|
|
meck:expect(rebar_packages, verify_table,
|
|
fun(_State) -> true end),
|
|
|
|
[{state, State} | Config];
|
|
init_per_testcase(ignore_match_in_excluded_repo, Config) ->
|
|
Deps = ?config(deps, Config),
|
|
Repos = [Repo1, _, Repo3 | _] = ?config(repos, Config),
|
|
|
|
%% drop repo1 and repo2 from the repos to be used by the pkg resource
|
|
State = setup_deps_and_repos(Deps, [R || R <- Repos, R =/= Repo3, R =/= Repo1]),
|
|
|
|
meck:new(rebar_packages, [passthrough, no_link]),
|
|
|
|
%% fail when the first repo is updated since it doesn't have a matching package
|
|
%% should continue anyway
|
|
[_, _, Repo3 | _] = Repos,
|
|
meck:expect(rebar_packages, update_package,
|
|
fun(_, _, _State) -> ok end),
|
|
meck:expect(rebar_packages, verify_table,
|
|
fun(_State) -> true end),
|
|
|
|
[{state, State} | Config];
|
|
init_per_testcase(optional_prereleases, Config) ->
|
|
Deps = ?config(deps, Config),
|
|
Repos = ?config(repos, Config),
|
|
|
|
State = setup_deps_and_repos(Deps, Repos),
|
|
|
|
meck:new(rebar_packages, [passthrough, no_link]),
|
|
|
|
meck:expect(rebar_packages, update_package,
|
|
fun(_, _, _State) -> ok end),
|
|
meck:expect(rebar_packages, verify_table,
|
|
fun(_State) -> true end),
|
|
|
|
[{state, State} | Config];
|
|
init_per_testcase(Case, Config) when Case =:= auth_merging ;
|
|
Case =:= auth_config_errors ;
|
|
Case =:= auth_remove_from_config ;
|
|
Case =:= auth_read_write_read ->
|
|
meck:new(file, [passthrough, no_link, unstick]),
|
|
meck:new(rebar_packages, [passthrough, no_link]),
|
|
Config;
|
|
init_per_testcase(organization_merging, Config) ->
|
|
meck:new(file, [passthrough, no_link, unstick]),
|
|
meck:new(rebar_packages, [passthrough, no_link]),
|
|
Config;
|
|
init_per_testcase(_, Config) ->
|
|
Config.
|
|
|
|
end_per_testcase(Case, _Config) when Case =:= auth_merging ;
|
|
Case =:= auth_config_errors ;
|
|
Case =:= organization_merging ;
|
|
Case =:= auth_remove_from_config ;
|
|
Case =:= auth_read_write_read ->
|
|
meck:unload(file),
|
|
meck:unload(rebar_packages);
|
|
end_per_testcase(Case, _Config) when Case =:= use_first_repo_match ;
|
|
Case =:= use_exact_with_hash ;
|
|
Case =:= fail_repo_update ;
|
|
Case =:= ignore_match_in_excluded_repo ;
|
|
Case =:= optional_prereleases ->
|
|
meck:unload(rebar_packages);
|
|
end_per_testcase(_, _) ->
|
|
ok.
|
|
|
|
|
|
default_repo(_Config) ->
|
|
Repo1 = #{name => <<"hexpm">>,
|
|
api_key => <<"asdf">>},
|
|
|
|
MergedRepos = rebar_hex_repos:repos([{repos, [Repo1]}]),
|
|
|
|
?assertMatch([#{name := <<"hexpm">>,
|
|
api_key := <<"asdf">>,
|
|
api_url := <<"https://hex.pm/api">>}], MergedRepos).
|
|
|
|
|
|
repo_merging(_Config) ->
|
|
Repo1 = #{name => <<"repo-1">>,
|
|
api_url => <<"repo-1/api">>},
|
|
Repo2 = #{name => <<"repo-2">>,
|
|
repo_url => <<"repo-2/repo">>,
|
|
repo_verify => false},
|
|
Result = rebar_hex_repos:merge_repos([Repo1, Repo2,
|
|
#{name => <<"repo-2">>,
|
|
api_url => <<"repo-2/api">>,
|
|
repo_url => <<"bad url">>,
|
|
repo_verify => true},
|
|
#{name => <<"repo-1">>,
|
|
api_url => <<"bad url">>,
|
|
repo_verify => true},
|
|
#{name => <<"repo-2">>,
|
|
api_url => <<"repo-2/api-2">>,
|
|
repo_url => <<"other/repo">>}]),
|
|
?assertMatch([#{name := <<"repo-1">>,
|
|
api_url := <<"repo-1/api">>,
|
|
repo_verify := true},
|
|
#{name := <<"repo-2">>,
|
|
api_url := <<"repo-2/api">>,
|
|
repo_url := <<"repo-2/repo">>,
|
|
repo_verify := false}], Result).
|
|
|
|
repo_replacing(_Config) ->
|
|
Repo1 = #{name => <<"repo-1">>,
|
|
repo_name => <<"repo-1">>,
|
|
api_url => <<"repo-1/api">>},
|
|
Repo2 = #{name => <<"repo-2">>,
|
|
repo_name => <<"repo-2">>,
|
|
repo_url => <<"repo-2/repo">>,
|
|
repo_verify => false},
|
|
|
|
?assertMatch([Repo1, Repo2, #{name := <<"hexpm">>}],
|
|
rebar_hex_repos:repos([{repos, [Repo1]},
|
|
{repos, [Repo2]}])),
|
|
|
|
%% use of replace is ignored if found in later entries than the first
|
|
?assertMatch([Repo1, Repo2, #{name := <<"hexpm">>}],
|
|
rebar_hex_repos:repos([{repos, [Repo1]},
|
|
{repos, replace, [Repo2]}])),
|
|
|
|
?assertMatch([Repo1],
|
|
rebar_hex_repos:repos([{repos, replace, [Repo1]},
|
|
{repos, [Repo2]}])).
|
|
|
|
auth_merging(_Config) ->
|
|
Repo1 = #{name => <<"repo-1">>,
|
|
api_url => <<"repo-1/api">>},
|
|
Repo2 = #{name => <<"repo-2">>,
|
|
repo_url => <<"repo-2/repo">>,
|
|
repo_verify => false},
|
|
|
|
State = rebar_state:new([{hex, [{repos, [Repo1, Repo2]}]}]),
|
|
meck:expect(file, consult,
|
|
fun(_) ->
|
|
{ok, [#{<<"repo-1">> => #{read_key => <<"read key">>,
|
|
write_key => <<"write key">>},
|
|
<<"repo-2">> => #{read_key => <<"read key 2">>,
|
|
repos_key => <<"repos key 2">>,
|
|
write_key => <<"write key 2">>},
|
|
<<"hexpm">> => #{write_key => <<"write key hexpm">>}}]}
|
|
end),
|
|
|
|
?assertMatch({ok,
|
|
#resource{state=#{repos := [#{name := <<"repo-1">>,
|
|
read_key := <<"read key">>,
|
|
write_key := <<"write key">>},
|
|
#{name := <<"repo-2">>,
|
|
read_key := <<"read key 2">>,
|
|
repos_key := <<"repos key 2">>,
|
|
write_key := <<"write key 2">>},
|
|
#{name := <<"hexpm">>,
|
|
write_key := <<"write key hexpm">>}]}}},
|
|
rebar_pkg_resource:init(pkg, State)),
|
|
|
|
ok.
|
|
|
|
auth_config_errors(_Config) ->
|
|
Repo1 = #{name => <<"repo-1">>,
|
|
api_url => <<"repo-1/api">>},
|
|
Repo2 = #{name => <<"repo-2">>,
|
|
repo_url => <<"repo-2/repo">>,
|
|
repo_verify => false},
|
|
|
|
State = rebar_state:new([{hex, [{repos, [Repo1, Repo2]}]}]),
|
|
meck:expect(file, consult,
|
|
fun(_) ->
|
|
{error, {3,erl_parse,["syntax error before: ","'=>'"]}}
|
|
end),
|
|
|
|
?assertThrow(rebar_abort, rebar_pkg_resource:init(pkg, State)),
|
|
meck:expect(file, consult,
|
|
fun(_) ->
|
|
{error, enoent}
|
|
end),
|
|
|
|
|
|
{ok, #resource{state=#{ repos := [
|
|
UpdatedRepo1,
|
|
UpdatedRepo2,
|
|
DefaultRepo
|
|
]}}} = rebar_pkg_resource:init(pkg, State),
|
|
|
|
?assertEqual(undefined, maps:get(write_key, UpdatedRepo1, undefined)),
|
|
?assertEqual(undefined, maps:get(read_key, UpdatedRepo1, undefined)),
|
|
?assertEqual(undefined, maps:get(repos_key, UpdatedRepo1, undefined)),
|
|
?assertEqual(undefined, maps:get(write_key, UpdatedRepo2, undefined)),
|
|
?assertEqual(undefined, maps:get(repos_key, UpdatedRepo2, undefined)),
|
|
?assertEqual(undefined, maps:get(read_key, UpdatedRepo2, undefined)),
|
|
?assertEqual(undefined, maps:get(write_key, DefaultRepo, undefined)),
|
|
ok.
|
|
|
|
auth_read_write_read(_Config) ->
|
|
Repo1 = #{name => <<"hexpm:repo-1">>,
|
|
api_url => <<"repo-1/api">>},
|
|
Repo2 = #{name => <<"hexpm:repo-2">>,
|
|
repo_url => <<"repo-2/repo">>,
|
|
repo_verify => false},
|
|
State = rebar_state:new([{hex, [{repos, [Repo1, Repo2]}]}]),
|
|
|
|
meck:expect(file, consult,
|
|
fun(_) ->
|
|
{ok, [#{<<"hexpm:repo-1">> => #{read_key => <<"read key">>},
|
|
<<"hexpm:repo-2">> => #{read_key => <<"read key 2">>,
|
|
repos_key => <<"repos key 2">>,
|
|
write_key => <<"write key 2">>}}]}
|
|
end),
|
|
|
|
meck:expect(file, write_file,
|
|
fun(_File, CfgBin, _) ->
|
|
case binary:match(CfgBin, <<"foo">>) of
|
|
nomatch ->
|
|
Err = "expected auth config binary with key <<\"foo\">>",
|
|
meck:exception(error, {expected, Err});
|
|
_ ->
|
|
ok
|
|
end
|
|
end),
|
|
|
|
rebar_hex_repos:update_auth_config(#{<<"foo">> => <<200>>}, State).
|
|
|
|
auth_remove_from_config(_Config) ->
|
|
Repo1 = #{name => <<"hexpm:repo-1">>,
|
|
api_url => <<"repo-1/api">>},
|
|
Repo2 = #{name => <<"hexpm:repo-2">>,
|
|
repo_url => <<"repo-2/repo">>,
|
|
repo_verify => false},
|
|
State = rebar_state:new([{hex, [{repos, [Repo1, Repo2]}]}]),
|
|
|
|
meck:expect(file, consult,
|
|
fun(_) ->
|
|
{ok, [#{<<"hexpm:repo-1">> => #{read_key => <<"read key">>},
|
|
<<"hexpm:repo-2">> => #{read_key => <<"read key 2">>,
|
|
repos_key => <<"repos key 2">>,
|
|
write_key => <<"write key 2">>}}]}
|
|
end),
|
|
|
|
meck:expect(file, write_file,
|
|
fun(_File, CfgBin, _) ->
|
|
case binary:match(CfgBin, <<"hexpm:repo-1">>) of
|
|
nomatch ->
|
|
ok;
|
|
_ ->
|
|
Err = "expected auth config binary without key <<\"hexpm:repo-1\">>",
|
|
meck:exception(error, {expected, Err})
|
|
end
|
|
end),
|
|
|
|
rebar_hex_repos:remove_from_auth_config(<<"hexpm:repo-1">>, State).
|
|
|
|
organization_merging(_Config) ->
|
|
Repo1 = #{name => <<"hexpm:repo-1">>,
|
|
api_url => <<"repo-1/api">>},
|
|
Repo2 = #{name => <<"hexpm:repo-2">>,
|
|
repo_url => <<"repo-2/repo">>,
|
|
repo_verify => false},
|
|
|
|
State = rebar_state:new([{hex, [{repos, [Repo1, Repo2]}]}]),
|
|
meck:expect(file, consult,
|
|
fun(_) ->
|
|
{ok, [#{<<"hexpm:repo-1">> => #{read_key => <<"read key">>},
|
|
<<"hexpm:repo-2">> => #{read_key => <<"read key 2">>,
|
|
repos_key => <<"repos key 2">>,
|
|
write_key => <<"write key 2">>},
|
|
<<"hexpm">> => #{write_key => <<"write key hexpm">>}}]}
|
|
end),
|
|
|
|
?assertMatch({ok,
|
|
#resource{state=#{repos := [#{name := <<"hexpm:repo-1">>,
|
|
parent := <<"hexpm">>,
|
|
repo_name := <<"repo-1">>,
|
|
api_repository := <<"repo-1">>,
|
|
repo_organization := <<"repo-1">>,
|
|
read_key := <<"read key">>,
|
|
write_key := <<"write key hexpm">>},
|
|
#{name := <<"hexpm:repo-2">>,
|
|
parent := <<"hexpm">>,
|
|
repo_name := <<"repo-2">>,
|
|
api_repository := <<"repo-2">>,
|
|
repo_organization := <<"repo-2">>,
|
|
read_key := <<"read key 2">>,
|
|
repos_key := <<"repos key 2">>,
|
|
write_key := <<"write key 2">>},
|
|
#{name := <<"hexpm">>,
|
|
repo_name := <<"hexpm">>,
|
|
api_repository := undefined,
|
|
repo_organization := undefined,
|
|
write_key := <<"write key hexpm">>}]}}},
|
|
rebar_pkg_resource:init(pkg, State)),
|
|
|
|
ok.
|
|
|
|
use_first_repo_match(Config) ->
|
|
State = ?config(state, Config),
|
|
|
|
?assertMatch({ok,{package,{<<"B">>, {{2,0,0}, {[],[]}}, Repo2},
|
|
<<"inner checksum">>,<<"outer checksum">>, false, []},
|
|
#{name := Repo2,
|
|
http_adapter := {r3_hex_http_httpc, #{profile := rebar}}}},
|
|
rebar_packages:resolve_version(<<"B">>, <<"> 1.4.0">>, undefined, undefined,
|
|
?PACKAGE_TABLE, State)),
|
|
|
|
?assertMatch({ok,{package,{<<"B">>, {{1,4,0}, {[],[]}}, Repo3},
|
|
<<"inner checksum">>,<<"outer checksum">>, false, []},
|
|
#{name := Repo3,
|
|
http_adapter := {r3_hex_http_httpc, #{profile := rebar}}}},
|
|
rebar_packages:resolve_version(<<"B">>, <<"~> 1.4.0">>, undefined, undefined,
|
|
?PACKAGE_TABLE, State)).
|
|
|
|
%% tests that even though an easier repo has C-1.3.1 it doesn't use it since its hash is different
|
|
use_exact_with_hash(Config) ->
|
|
State = ?config(state, Config),
|
|
|
|
?assertMatch({ok,{package,{<<"C">>, {{1,3,1}, {[],[]}}, Repo2},
|
|
<<"inner checksum">>, <<"good outer checksum">>, false, []},
|
|
#{name := Repo2,
|
|
http_adapter := {r3_hex_http_httpc, #{profile := rebar}}}},
|
|
rebar_packages:resolve_version(<<"C">>, <<"1.3.1">>, <<"inner checksum">>, <<"good outer checksum">>,
|
|
?PACKAGE_TABLE, State)).
|
|
|
|
fail_repo_update(Config) ->
|
|
State = ?config(state, Config),
|
|
|
|
?assertMatch({ok,{package,{<<"B">>, {{1,4,0}, {[],[]}}, Repo3},
|
|
<<"inner checksum">>,<<"outer checksum">>, false, []},
|
|
#{name := Repo3,
|
|
http_adapter := {r3_hex_http_httpc, #{profile := rebar}}}},
|
|
rebar_packages:resolve_version(<<"B">>, <<"~> 1.4.0">>, undefined, undefined,
|
|
?PACKAGE_TABLE, State)).
|
|
|
|
ignore_match_in_excluded_repo(Config) ->
|
|
State = ?config(state, Config),
|
|
Repos = ?config(repos, Config),
|
|
|
|
?assertMatch({ok,{package,{<<"B">>, {{1,4,6}, {[],[]}}, Hexpm},
|
|
<<"inner checksum">>,<<"outer checksum">>, #{reason := 'RETIRED_INVALID'}, []},
|
|
#{name := Hexpm,
|
|
http_adapter := {r3_hex_http_httpc, #{profile := rebar}}}},
|
|
rebar_packages:resolve_version(<<"B">>, <<"~> 1.4.0">>, undefined, undefined,
|
|
?PACKAGE_TABLE, State)),
|
|
|
|
[_, Repo2 | _] = Repos,
|
|
?assertMatch({ok,{package,{<<"A">>, {{0,1,1}, {[],[]}}, Repo2},
|
|
<<"inner checksum">>, <<"good outer checksum">>, false, []},
|
|
#{name := Repo2,
|
|
http_adapter := {r3_hex_http_httpc, #{profile := rebar}}}},
|
|
rebar_packages:resolve_version(<<"A">>, <<"0.1.1">>, <<"inner checksum">>, <<"good outer checksum">>,
|
|
?PACKAGE_TABLE, State)).
|
|
|
|
optional_prereleases(Config) ->
|
|
State = ?config(state, Config),
|
|
|
|
?assertMatch({ok,{package,{<<"B">>, {{1,5,0}, {[],[]}}, Hexpm},
|
|
<<"inner checksum">>,<<"outer checksum">>, false, []},
|
|
#{name := Hexpm,
|
|
http_adapter := {r3_hex_http_httpc, #{profile := rebar}}}},
|
|
rebar_packages:resolve_version(<<"B">>, <<"~> 1.5.0">>, undefined, undefined,
|
|
?PACKAGE_TABLE, State)),
|
|
|
|
?assertMatch({ok,{package,{<<"B">>, {{1,5,6}, {[<<"rc">>,0],[]}}, Hexpm},
|
|
<<"inner checksum">>,<<"outer checksum">>, true, []},
|
|
#{name := Hexpm,
|
|
http_adapter := {r3_hex_http_httpc, #{profile := rebar}}}},
|
|
rebar_packages:resolve_version(<<"B">>, <<"1.5.6-rc.0">>, <<"inner checksum">>, <<"outer checksum">>,
|
|
?PACKAGE_TABLE, State)),
|
|
|
|
%% allow prerelease through configuration
|
|
State1 = rebar_state:set(State, deps_allow_prerelease, true),
|
|
?assertMatch({ok,{package,{<<"B">>, {{1,5,6}, {[<<"rc">>,0],[]}}, Hexpm},
|
|
<<"inner checksum">>,<<"outer checksum">>, true, []},
|
|
#{name := Hexpm,
|
|
http_adapter := {r3_hex_http_httpc, #{profile := rebar}}}},
|
|
rebar_packages:resolve_version(<<"B">>, <<"~> 1.5.0">>, <<"inner checksum">>, <<"outer checksum">>,
|
|
?PACKAGE_TABLE, State1)).
|
|
|
|
%%
|
|
|
|
setup_deps_and_repos(Deps, Repos) ->
|
|
catch ets:delete(?PACKAGE_TABLE),
|
|
true = rebar_packages:new_package_table(),
|
|
insert_deps(Deps),
|
|
State = rebar_state:new([{hex, [{repos, [#{name => R} || R <- Repos]}]}]),
|
|
rebar_state:create_resources([{pkg, rebar_pkg_resource}], State).
|
|
|
|
|
|
insert_deps(Deps) ->
|
|
lists:foreach(fun({Name, Version, Repo, Retired}) ->
|
|
ets:insert(?PACKAGE_TABLE, #package{key={rebar_utils:to_binary(Name),
|
|
ec_semver:parse(Version),
|
|
rebar_utils:to_binary(Repo)},
|
|
dependencies=[],
|
|
retired=Retired,
|
|
inner_checksum = <<"inner checksum">>,
|
|
outer_checksum = <<"outer checksum">>});
|
|
({Name, Version, InnerChecksum, OuterChecksum, Repo, Retired}) ->
|
|
ets:insert(?PACKAGE_TABLE, #package{key={rebar_utils:to_binary(Name),
|
|
ec_semver:parse(Version),
|
|
rebar_utils:to_binary(Repo)},
|
|
dependencies=[],
|
|
retired=Retired,
|
|
inner_checksum = InnerChecksum,
|
|
outer_checksum = OuterChecksum})
|
|
end, Deps).
|