1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2013-2020. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20 21%% 22%% Tests of events sent as a consequence of diameter:subscribe/1. 23%% Watchdog events are dealt with more extensively in the watchdog 24%% suite. 25%% 26 27-module(diameter_event_SUITE). 28 29-export([suite/0, 30 all/0, 31 init_per_suite/1, 32 end_per_suite/1, 33 init_per_testcase/2, 34 end_per_testcase/2]). 35 36%% testcases 37-export([start/1, 38 start_server/1, 39 up/1, 40 down/1, 41 cea_timeout/1, 42 stop/1]). 43 44-include("diameter.hrl"). 45 46%% =========================================================================== 47 48-define(util, diameter_util). 49 50-define(ADDR, {127,0,0,1}). 51-define(REALM, "REALM"). 52 53-define(SERVER, "SERVER.SERVER-REALM"). 54-define(CLIENT, "CLIENT.CLIENT-REALM"). 55 56-define(DICT_COMMON, ?DIAMETER_DICT_COMMON). 57-define(DICT_ACCT, ?DIAMETER_DICT_ACCOUNTING). 58 59-define(SERVER_CAPX_TMO, 6000). 60 61%% Config for diameter:start_service/2. 62-define(SERVICE(Host, Dicts), 63 [{'Origin-Host', Host}, 64 {'Origin-Realm', realm(Host)}, 65 {'Host-IP-Address', [?ADDR]}, 66 {'Vendor-Id', 12345}, 67 {'Product-Name', "OTP/diameter"}, 68 {'Acct-Application-Id', [D:id() || D <- Dicts]}, 69 {decode_format, map} 70 | [{application, [{dictionary, D}, 71 {module, #diameter_callback{}}]} 72 || D <- Dicts]]). 73 74%% Diameter Result-Code's: 75-define(NO_COMMON_APP, 5010). 76 77%% =========================================================================== 78 79suite() -> 80 [{timetrap, {seconds, 60}}]. 81 82all() -> 83 [start, 84 start_server, 85 up, 86 down, 87 cea_timeout, 88 stop]. 89 90%% Not used, but a convenient place to enable trace. 91init_per_suite(Config) -> 92 Config. 93 94end_per_suite(_Config) -> 95 ok. 96 97init_per_testcase(Name, Config) -> 98 [{name, Name} | Config]. 99 100end_per_testcase(_, _) -> 101 ok. 102 103%% =========================================================================== 104%% start/stop testcases 105 106start(_Config) -> 107 ok = diameter:start(). 108 109start_server(Config) -> 110 diameter:subscribe(?SERVER), 111 ok = diameter:start_service(?SERVER, ?SERVICE(?SERVER, [?DICT_COMMON])), 112 LRef = ?util:listen(?SERVER, tcp, [{capabilities_cb, fun capx_cb/2}, 113 {capx_timeout, ?SERVER_CAPX_TMO}]), 114 [PortNr] = ?util:lport(tcp, LRef), 115 ?util:write_priv(Config, portnr, PortNr), 116 start = event(?SERVER). 117 118%% Connect with matching capabilities and expect the connection to 119%% come up. 120up(Config) -> 121 {Svc, Ref, T} = connect(Config, [{strict_mbit, false}, 122 {connect_timer, 5000}, 123 {watchdog_timer, 15000}]), 124 start = event(Svc), 125 {{up, Ref, {TPid, Caps}, T, #diameter_packet{msg = M}}, _} 126 = {event(Svc), T}, 127 ['CEA' | #{}] = M, %% assert 128 {watchdog, Ref, _, {initial, okay}, _} = event(Svc), 129 %% Kill the transport process and see that the connection is 130 %% reestablished after a watchdog timeout, not after connect_timer 131 %% expiry. 132 exit(TPid, kill), 133 {{down, Ref, {TPid, Caps}, T}, _} = {event(Svc), T}, 134 {watchdog, Ref, _, {okay, down}, _} = event(Svc), 135 {reconnect, Ref, _} = event(Svc, 10000, 20000). 136 137%% Connect with non-matching capabilities and expect CEA from the peer 138%% to indicate as much and then for the transport to be restarted 139%% (after connect_timer). 140down(Config) -> 141 {Svc, Ref, T} = connect(Config, [{capabilities, [{'Acct-Application-Id', 142 [?DICT_ACCT:id()]}]}, 143 {applications, [?DICT_ACCT]}, 144 {connect_timer, 5000}, 145 {watchdog_timer, 20000}]), 146 start = event(Svc), 147 {{closed, Ref, {'CEA', ?NO_COMMON_APP, _, #diameter_packet{msg = M}}, T}, 148 _} 149 = {event(Svc), T}, 150 ['CEA' | #{}] = M, %% assert 151 {reconnect, Ref, _} = event(Svc, 4000, 10000). 152 153%% Connect with matching capabilities but have the server delay its 154%% CEA and cause the client to timeout. 155cea_timeout(Config) -> 156 {Svc, Ref, T} = connect(Config, [{capx_timeout, ?SERVER_CAPX_TMO div 2}, 157 {connect_timer, 2*?SERVER_CAPX_TMO}]), 158 start = event(Svc), 159 {{closed, Ref, {'CEA', timeout}, T}, _} = {event(Svc), T}. 160 161stop(_Config) -> 162 ok = diameter:stop(). 163 164%% ---------------------------------------- 165 166%% Keep the server from sending CEA until the client has timed out. 167capx_cb(_, #diameter_caps{origin_host = {_, "cea_timeout-" ++ _}}) -> 168 receive after ?SERVER_CAPX_TMO -> ok end; 169 170%% Or not. 171capx_cb(_, _Caps) -> 172 ok. 173 174%% ---------------------------------------- 175 176%% Use the testcase name to construct Origin-Host of the client so 177%% that the server can match on it in capx_cb/2. 178connect(Config, Opts) -> 179 Pre = atom_to_list(proplists:get_value(name, Config)), 180 Name = Pre ++ uniq() ++ ?CLIENT, 181 diameter:subscribe(Name), 182 ok = start_service(Name, ?SERVICE(Name, [?DICT_COMMON, ?DICT_ACCT])), 183 {connect, _} = T = opts(Config, Opts), 184 {ok, Ref} = diameter:add_transport(Name, T), 185 {Name, Ref, T}. 186 187uniq() -> 188 "-" ++ diameter_util:unique_string(). 189 190event(Name) -> 191 receive #diameter_event{service = Name, info = T} -> T end. 192 193event(Name, TL, TH) -> 194 T0 = diameter_lib:now(), 195 Event = event(Name), 196 DT = diameter_lib:micro_diff(T0) div 1000, 197 {true, true, DT, Event} = {TL < DT, DT < TH, DT, Event}, 198 Event. 199 200start_service(Name, Opts) -> 201 diameter:start_service(Name, [{monitor, self()} | Opts]). 202 203opts(Config, Opts) -> 204 PortNr = ?util:read_priv(Config, portnr), 205 206 {connect, [{transport_module, diameter_tcp}, 207 {transport_config, [{ip, ?ADDR}, {port, 0}, 208 {raddr, ?ADDR}, {rport, PortNr}]} 209 | Opts]}. 210 211realm(Host) -> 212 tl(lists:dropwhile(fun(C) -> C /= $. end, Host)). 213