diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..2c78ef7 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,50 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + +permissions: + contents: read + +jobs: + test: + name: OTP ${{ matrix.otp }} + runs-on: ubuntu-latest + container: + image: erlang:${{ matrix.otp }} + strategy: + fail-fast: false + matrix: + # 26 is what chef-server currently pins; 27 and 28 are tested so + # regressions show up before the pin moves. + otp: ['26', '27', '28'] + steps: + - uses: actions/checkout@v4 + + - name: Compile + run: rebar3 compile + + - name: EUnit + # The network-dependent suites are opt-in via + # OPSCODERL_HTTPC_NETWORK_TESTS; see test/oc_httpc_tests.erl. + run: rebar3 eunit + + dialyzer: + name: Dialyzer (informational) + runs-on: ubuntu-latest + container: + image: erlang:26 + # Not a merge gate yet. Four warnings are outstanding on main: + # - three from lager's parse transform expanding into lager:do_log/10, + # lager:md/0 and lager_config:get/2, which dialyzer cannot resolve. + # Removing lager clears all three. + # - one for pooler:time_spec/0, which pooler uses internally but never + # exports, so oc_httpc's spec refers to a type that is not public. + # Once both are resolved this should drop continue-on-error and gate. + continue-on-error: true + steps: + - uses: actions/checkout@v4 + - name: Dialyzer + run: rebar3 dialyzer diff --git a/.gitignore b/.gitignore index bd1643e..f137977 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,7 @@ rel/opscoderl_httpc .concrete/DEV_MODE deps.plt .eunit + +# rebar3 +_build/ +rebar.lock diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index c254898..0000000 --- a/.travis.yml +++ /dev/null @@ -1,16 +0,0 @@ -language: erlang -otp_release: - - 21.0 - - 20.3 - - 19.3 - - 18.3 - -install: true -script: rebar3 eunit - -branches: - only: - - master - - - diff --git a/rebar.config b/rebar.config index 8eeef61..a9499c9 100644 --- a/rebar.config +++ b/rebar.config @@ -20,9 +20,7 @@ [ {meck, ".*", {git, "https://github.com/eproxus/meck", {ref, "06192a984750070ace33b60a492ca27ec9bc6806"}}}, {observer_cli, ".*", - {git, "https://github.com/zhongwencool/observer_cli", {ref, "baa70569bccc5508e9839e20768540ef3cdca016"}}}, - {eper, ".*", - {git, "https://github.com/massemanet/eper", {ref, "17b0f97ea8287b72e8ebbe7132214db182ff1a1d"}}} + {git, "https://github.com/zhongwencool/observer_cli", {ref, "baa70569bccc5508e9839e20768540ef3cdca016"}}} ]}]} ]}. diff --git a/test/oc_httpc_tests.erl b/test/oc_httpc_tests.erl index a387fd1..21c2377 100644 --- a/test/oc_httpc_tests.erl +++ b/test/oc_httpc_tests.erl @@ -22,7 +22,22 @@ -include_lib("eunit/include/eunit.hrl"). +%% The suites below make live requests to a dozen third-party sites +%% (google.com, sun.com, yaws.hyber.org, jigsaw.w3.org and others) and assert +%% HTTP 200 from them. They are integration tests, not unit tests: they fail +%% whenever one of those hosts is slow, moved or gone, which makes them useless +%% as a merge gate. They stay available and are opted into by setting +%% OPSCODERL_HTTPC_NETWORK_TESTS=1; CI runs the deterministic suites only. +network_tests_enabled() -> + os:getenv("OPSCODERL_HTTPC_NETWORK_TESTS") =/= false. + opscoderl_ibrowse_test_() -> + case network_tests_enabled() of + false -> []; + true -> opscoderl_ibrowse_tests() + end. + +opscoderl_ibrowse_tests() -> {setup, fun() -> application:ensure_all_started(ssl), @@ -79,7 +94,13 @@ assert_200_req(RootUrl, Endpoint, Method, OptionsInput) -> ?assertMatch({ok, _, _, _}, Result) end}. -multi_request_test() -> +multi_request_test_() -> + case network_tests_enabled() of + false -> []; + true -> {timeout, 120, fun multi_request/0} + end. + +multi_request() -> RootUrl = "http://jigsaw.w3.org/", CallbackFun = fun(RequestFun) -> Paths = ["HTTP/ChunkedScript", @@ -108,7 +129,13 @@ multi_request_test() -> ?assertEqual(15, length(Results)), [?assertMatch({ok, _,_,_}, Result) || Result <- Results]. -process_leak_test() -> +process_leak_test_() -> + case network_tests_enabled() of + false -> []; + true -> {timeout, 120, fun process_leak/0} + end. + +process_leak() -> Options = [{connect_timeout, 5000}], PoolConfig = [{root_url, "http://google.com"}, {init_count, 3}, {max_count, 3}, {ibrowse_options, Options}, {max_connection_requests, 1}], @@ -151,7 +178,13 @@ request_error_test_() -> {foreach, fun() -> error_logger:tty(false), + %% pooler has to be running before add_pool/2. This used to be + %% started by opscoderl_ibrowse_test_'s setup, so these cases only + %% passed when that suite ran first; they now stand alone. + {ok, _} = application:ensure_all_started(pooler), application:start(ibrowse), + %% no request reaches this host: ibrowse is mecked below, so the + %% URL is only pool configuration. RootUrl = "http://jigsaw.w3.org/", Options = [{connect_timeout, 5000}], PoolConfig = [{root_url, RootUrl}, {init_count, 50}, {max_count, 250},