1: -module(service_domain_db_SUITE). 2: 3: -include_lib("common_test/include/ct.hrl"). 4: -include_lib("eunit/include/eunit.hrl"). 5: 6: -compile([export_all, nowarn_export_all]). 7: -import(distributed_helper, [mim/0, mim2/0, mim3/0, require_rpc_nodes/1, rpc/4, 8: remove_node_from_cluster/2]). 9: -import(graphql_helper, [execute_command/4]). 10: 11: -import(domain_rest_helper, 12: [set_invalid_creds/1, 13: set_no_creds/1, 14: set_valid_creds/1]). 15: 16: -import(domain_rest_helper, 17: [rest_patch_enabled/3, 18: rest_put_domain/3, 19: putt_domain_with_custom_body/2, 20: rest_select_domain/2, 21: rest_delete_domain/3, 22: delete_custom/4, 23: patch_custom/4]). 24: 25: -import(domain_helper, [domain/0]). 26: -import(config_parser_helper, [config/2]). 27: 28: suite() -> 29: require_rpc_nodes([mim, mim2, mim3]). 30: 31: all() -> 32: [ 33: {group, no_db}, 34: {group, db} 35: ]. 36: 37: groups() -> 38: ParallelConfig = parallel_config(), 39: [ 40: {no_db, ParallelConfig, no_db_cases()}, 41: {db, [], [ 42: {group, plain_db}, 43: {group, plain_db_sequential}, 44: {group, rest_with_auth}, 45: {group, rest_without_auth} 46: ]}, 47: {rest_with_auth, [], [ 48: {group, rest_with_auth_parallel}, 49: {group, rest_service_disabled}, 50: {group, rest_db_fails} 51: ]}, 52: {rest_without_auth, [], [ 53: {group, rest_without_auth_parallel}, 54: {group, rest_service_disabled}, 55: {group, rest_db_fails} 56: ]}, 57: {plain_db, ParallelConfig, db_cases()}, 58: {plain_db_sequential, [], db_sequential_cases()}, 59: {rest_with_auth_parallel, ParallelConfig, rest_cases() ++ rest_auth_cases(true)}, 60: {rest_without_auth_parallel, ParallelConfig, rest_cases() ++ rest_auth_cases(false)}, 61: {rest_service_disabled, ParallelConfig, rest_service_disabled_cases()}, 62: {rest_db_fails, [], rest_db_fails_cases()} 63: ]. 64: 65: parallel_config() -> 66: %% These could be parallel but it seems like mssql CI can't handle the load 67: case distributed_helper:rpc( 68: distributed_helper:mim(), mongoose_rdbms, db_engine, [domain_helper:host_type()]) of 69: odbc -> []; 70: _ -> [parallel] 71: end. 72: 73: no_db_cases() -> [ 74: api_lookup_works, 75: api_lookup_not_found, 76: api_cannot_insert_static, 77: api_cannot_disable_static, 78: api_cannot_enable_static, 79: api_get_all_static, 80: api_get_domains_by_host_type 81: ]. 82: 83: db_cases() -> [ 84: db_get_all_static, 85: db_get_all_dynamic, 86: db_inserted_domain_is_in_db, 87: db_inserted_domain_is_in_core, 88: db_deleted_domain_from_db, 89: db_deleted_domain_fails_with_wrong_host_type, 90: db_deleted_domain_from_core, 91: db_disabled_domain_is_in_db, 92: db_disabled_domain_not_in_core, 93: db_reenabled_domain_is_in_db, 94: db_reenabled_domain_is_in_core, 95: db_cannot_insert_domain_twice_with_the_same_host_type, 96: db_cannot_insert_domain_twice_with_another_host_type, 97: db_cannot_insert_domain_with_unknown_host_type, 98: db_cannot_delete_domain_with_unknown_host_type, 99: db_cannot_enable_domain_with_unknown_host_type, 100: db_cannot_disable_domain_with_unknown_host_type, 101: db_domains_with_unknown_host_type_are_ignored_by_core, 102: db_can_insert_update_delete_dynamic_domain_password, 103: db_cannot_set_password_for_unknown_domain, 104: db_can_check_domain_password, 105: db_cannot_check_password_for_unknown_domain, 106: db_deleting_domain_deletes_domain_admin, 107: sql_select_from, 108: db_could_sync_between_nodes, 109: db_gaps_are_getting_filled_automatically 110: ]. 111: 112: db_sequential_cases() -> [ 113: sql_find_gaps_between, 114: db_records_are_restored_on_mim_restart, 115: db_record_is_ignored_if_domain_static, 116: db_events_table_gets_truncated, 117: db_deleted_from_one_node_while_service_disabled_on_another, 118: db_inserted_from_one_node_while_service_disabled_on_another, 119: db_reinserted_from_one_node_while_service_disabled_on_another, 120: db_crash_on_initial_load_restarts_service, 121: db_out_of_sync_restarts_service, 122: db_restarts_properly, 123: db_keeps_syncing_after_cluster_join, 124: db_event_could_appear_with_lower_id, 125: db_can_insert_update_delete_static_domain_password, 126: rest_cannot_enable_deleting 127: ]. 128: 129: rest_cases() ->[ 130: rest_can_insert_domain, 131: rest_can_disable_domain, 132: rest_request_can_delete_domain, 133: rest_can_delete_domain, 134: rest_cannot_delete_domain_without_correct_type, 135: rest_cannot_delete_missing_domain, 136: rest_cannot_enable_missing_domain, 137: rest_cannot_insert_domain_twice_with_another_host_type, 138: rest_cannot_insert_domain_with_unknown_host_type, 139: rest_cannot_delete_domain_with_unknown_host_type, 140: rest_cannot_disable_missing_domain, 141: rest_can_enable_domain, 142: rest_can_select_domain, 143: rest_cannot_select_domain_if_domain_not_found, 144: rest_cannot_select_domain_when_it_is_static, 145: rest_cannot_put_domain_without_host_type, 146: rest_cannot_put_domain_without_body, 147: rest_cannot_put_domain_with_invalid_json, 148: rest_cannot_put_domain_with_invalid_name, 149: rest_cannot_put_domain_when_it_is_static, 150: rest_cannot_delete_domain_without_host_type, 151: rest_cannot_delete_domain_without_body, 152: rest_cannot_delete_domain_with_invalid_json, 153: rest_cannot_delete_domain_when_it_is_static, 154: rest_cannot_patch_domain_without_enabled_field, 155: rest_cannot_patch_domain_without_body, 156: rest_cannot_patch_domain_with_invalid_json, 157: rest_cannot_enable_domain_when_it_is_static, 158: rest_delete_domain_cleans_data_from_mam 159: ]. 160: 161: rest_auth_cases(false) -> 162: %% auth provided but not configured: 163: [rest_cannot_insert_domain_if_auth_provided_but_not_configured, 164: rest_cannot_delete_domain_if_auth_provided_but_not_configured, 165: rest_cannot_enable_domain_if_auth_provided_but_not_configured, 166: rest_cannot_disable_domain_if_auth_provided_but_not_configured, 167: rest_cannot_select_domain_if_auth_provided_but_not_configured]; 168: rest_auth_cases(true) -> 169: %% basic auth, but wrong pass: 170: [rest_cannot_insert_domain_with_wrong_pass, 171: rest_cannot_delete_domain_with_wrong_pass, 172: rest_cannot_enable_domain_with_wrong_pass, 173: rest_cannot_disable_domain_with_wrong_pass, 174: rest_cannot_select_domain_with_wrong_pass, 175: %% no basic auth: 176: rest_cannot_insert_domain_without_auth, 177: rest_cannot_delete_domain_without_auth, 178: rest_cannot_enable_domain_without_auth, 179: rest_cannot_disable_domain_without_auth, 180: rest_cannot_select_domain_without_auth]. 181: 182: rest_db_fails_cases() -> [ 183: rest_insert_domain_fails_if_db_fails, 184: rest_delete_domain_fails_if_db_fails, 185: rest_enable_domain_fails_if_db_fails 186: ]. 187: 188: rest_service_disabled_cases() -> [ 189: rest_insert_domain_fails_if_service_disabled, 190: rest_delete_domain_fails_if_service_disabled, 191: rest_enable_domain_fails_if_service_disabled 192: ]. 193: 194: %%-------------------------------------------------------------------- 195: %% Suite configuration 196: %%-------------------------------------------------------------------- 197: init_per_suite(Config) -> 198: Config0 = dynamic_services:save_services(all_nodes(), Config), 199: Config1 = dynamic_modules:save_modules(dummy_auth_host_type(), Config0), 200: ensure_nodes_know_each_other(), 201: service_disabled(mim()), 202: service_disabled(mim2()), 203: service_disabled(mim3()), 204: prepare_test_queries(mim()), 205: prepare_test_queries(mim2()), 206: erase_database(mim()), 207: Config2 = ejabberd_node_utils:init(mim(), Config1), 208: mongoose_helper:inject_module(?MODULE), 209: escalus:init_per_suite([{service_setup, per_testcase} | Config2]). 210: 211: end_per_suite(Config) -> 212: [restart_domain_core(Node) || Node <- all_nodes()], 213: dynamic_services:restore_services(Config), 214: domain_helper:insert_configured_domains(), 215: dynamic_modules:restore_modules(Config), 216: escalus_fresh:clean(), 217: escalus:end_per_suite(Config). 218: 219: all_nodes() -> 220: [mim(), mim2(), mim3()]. 221: 222: %%-------------------------------------------------------------------- 223: %% Init & teardown 224: %%-------------------------------------------------------------------- 225: init_per_group(db, Config) -> 226: case mongoose_helper:is_rdbms_enabled(dummy_auth_host_type()) of 227: true -> [{service, true}|Config]; 228: false -> {skip, require_rdbms} 229: end; 230: init_per_group(rest_with_auth, Config) -> 231: rest_helper:change_admin_creds({<<"admin">>, <<"secret">>}), 232: [{auth_creds, valid}|Config]; 233: init_per_group(rest_service_disabled, Config) -> 234: service_disabled(mim()), 235: % setting this option prevents from enabling service in init_per_testase 236: lists:keystore(service_setup, 1, Config, {service_setup, per_group}); 237: init_per_group(GroupName, Config) -> 238: Config1 = save_service_setup_option(GroupName, Config), 239: case ?config(service_setup, Config1) of 240: per_group -> setup_service(#{}, Config1); 241: per_testcase -> ok 242: end, 243: Config1. 244: 245: end_per_group(rest_with_auth, _Config) -> 246: rest_helper:change_admin_creds(any); 247: end_per_group(_GroupName, Config) -> 248: case ?config(service_setup, Config) of 249: per_group -> teardown_service(); 250: per_testcase -> ok 251: end. 252: 253: init_per_testcase(rest_cannot_enable_deleting, Config) -> 254: HostType = <<"type1">>, 255: Server = start_domain_removal_hook(HostType), 256: init_per_testcase(generic, [{server, Server}, {host_type, HostType} | Config]); 257: init_per_testcase(db_crash_on_initial_load_restarts_service, Config) -> 258: maybe_setup_meck(db_crash_on_initial_load_restarts_service), 259: restart_domain_core(mim(), [], []), 260: Config; 261: init_per_testcase(TestcaseName, Config) -> 262: maybe_setup_meck(TestcaseName), 263: case ?config(service_setup, Config) of 264: per_group -> ok; 265: per_testcase -> setup_service(service_opts(TestcaseName), Config) 266: end, 267: init_per_testcase2(TestcaseName, Config). 268: 269: service_opts(db_events_table_gets_truncated) -> 270: #{event_cleaning_interval => 1, event_max_age => 3}; 271: service_opts(_) -> 272: #{}. 273: 274: end_per_testcase(rest_cannot_enable_deleting, Config) -> 275: Server = ?config(server, Config), 276: HostType = ?config(host_type, Config), 277: stop_domain_removal_hook(HostType, Server), 278: exit(Server, normal); 279: end_per_testcase(TestcaseName, Config) -> 280: end_per_testcase2(TestcaseName, Config), 281: case TestcaseName of 282: db_out_of_sync_restarts_service -> 283: rpc(mim(), sys, resume, [service_domain_db]); 284: _ -> ok 285: end, 286: maybe_teardown_meck(TestcaseName), 287: case ?config(service_setup, Config) of 288: per_group -> ok; 289: per_testcase -> teardown_service() 290: end. 291: 292: init_per_testcase2(TestcaseName, Config) 293: when TestcaseName =:= rest_delete_domain_cleans_data_from_mam -> 294: HostType = dummy_auth_host_type(), 295: Mods = [{mod_mam, mam_helper:config_opts(#{pm => #{}})}], 296: dynamic_modules:ensure_modules(HostType, Mods), 297: escalus:init_per_testcase(TestcaseName, Config); 298: init_per_testcase2(db_keeps_syncing_after_cluster_join, Config) -> 299: graphql_helper:init_admin_cli(Config); 300: init_per_testcase2(_, Config) -> 301: Config. 302: 303: end_per_testcase2(TestcaseName, Config) 304: when TestcaseName =:= rest_delete_domain_cleans_data_from_mam -> 305: escalus:end_per_testcase(TestcaseName, Config); 306: end_per_testcase2(db_keeps_syncing_after_cluster_join, _Config) -> 307: graphql_helper:clean(); 308: end_per_testcase2(_, Config) -> 309: Config. 310: 311: setup_service(Opts, Config) -> 312: ServiceEnabled = proplists:get_value(service, Config, false), 313: Pairs1 = [{<<"example.cfg">>, <<"type1">>}, 314: {<<"erlang-solutions.com">>, <<"type2">>}, 315: {<<"erlang-solutions.local">>, <<"type2">>}], 316: restart_domain_core(mim(), Pairs1, host_types_for_mim()), 317: restart_domain_core(mim2(), [], host_types_for_mim2()), 318: case ServiceEnabled of 319: true -> 320: service_enabled(mim(), Opts), 321: service_enabled(mim2(), #{}); 322: false -> 323: ok 324: end. 325: 326: host_types_for_mim() -> 327: [<<"type1">>, <<"type2">>, dummy_auth_host_type(), 328: <<"dbgroup">>, <<"dbgroup2">>, <<"cfggroup">>]. 329: 330: host_types_for_mim2() -> 331: [<<"mim2only">> | host_types_for_mim()]. 332: 333: teardown_service() -> 334: service_disabled(mim()), 335: service_disabled(mim2()), 336: erase_database(mim()). 337: 338: save_service_setup_option(GroupName, Config) -> 339: Value = case is_parallel_group(GroupName) of 340: true -> per_group; 341: false -> per_testcase 342: end, 343: lists:keystore(service_setup, 1, Config, {service_setup, Value}). 344: 345: is_parallel_group(GroupName) -> 346: case lists:keyfind(GroupName, 1, groups()) of 347: {_, Opts, _Cases} -> lists:member(parallel, Opts); 348: _ -> false 349: end. 350: 351: %%-------------------------------------------------------------------- 352: %% Tests 353: %%-------------------------------------------------------------------- 354: 355: % no_db cases 356: 357: api_lookup_works(_) -> 358: {ok, <<"type1">>} = get_host_type(mim(), <<"example.cfg">>). 359: 360: api_lookup_not_found(_) -> 361: {error, not_found} = get_host_type(mim(), <<"example.missing">>). 362: 363: api_cannot_insert_static(_) -> 364: {static, <<"Domain is static">>} = insert_domain(mim(), <<"example.cfg">>, <<"type1">>). 365: 366: api_cannot_disable_static(_) -> 367: {static, <<"Domain is static">>} = disable_domain(mim(), <<"example.cfg">>). 368: 369: api_cannot_enable_static(_) -> 370: {static, <<"Domain is static">>} = enable_domain(mim(), <<"example.cfg">>). 371: 372: %% See also db_get_all_static 373: api_get_all_static(_) -> 374: %% Could be in any order 375: [{<<"erlang-solutions.com">>, <<"type2">>}, 376: {<<"erlang-solutions.local">>, <<"type2">>}, 377: {<<"example.cfg">>, <<"type1">>}] = 378: lists:sort(get_all_static(mim())). 379: 380: api_get_domains_by_host_type(_) -> 381: [<<"erlang-solutions.com">>, <<"erlang-solutions.local">>] = 382: lists:sort(get_domains_by_host_type(mim(), <<"type2">>)), 383: [<<"example.cfg">>] = get_domains_by_host_type(mim(), <<"type1">>), 384: [] = get_domains_by_host_type(mim(), <<"type6">>). 385: 386: % plain_db cases 387: 388: %% Similar to as api_get_all_static, just with DB service enabled 389: db_get_all_static(_) -> 390: Domain = random_domain_name(), 391: {ok, _} = insert_domain(mim(), Domain, <<"type1">>), 392: sync(), 393: %% Could be in any order 394: [{<<"erlang-solutions.com">>, <<"type2">>}, 395: {<<"erlang-solutions.local">>, <<"type2">>}, 396: {<<"example.cfg">>, <<"type1">>}] = 397: lists:sort(get_all_static(mim())). 398: 399: db_get_all_dynamic(_) -> 400: Domain1 = random_domain_name(), 401: Domain2 = random_domain_name(), 402: {ok, _} = insert_domain(mim(), Domain1, <<"type1">>), 403: {ok, _} = insert_domain(mim(), Domain2, <<"type1">>), 404: sync(), 405: ExpectedResult = sets:from_list([{Domain1, <<"type1">>}, {Domain2, <<"type1">>}]), 406: Result = sets:from_list(get_all_dynamic(mim())), 407: % Other test cases can insert their own domains thus we test is_subset 408: true = sets:is_subset(ExpectedResult, Result). 409: 410: db_inserted_domain_is_in_db(_) -> 411: Domain = random_domain_name(), 412: {ok, _} = insert_domain(mim(), Domain, <<"type1">>), 413: {ok, #{host_type := <<"type1">>, status := enabled}} = 414: select_domain(mim(), Domain). 415: 416: db_inserted_domain_is_in_core(_) -> 417: Domain = random_domain_name(), 418: {ok, _} = insert_domain(mim(), Domain, <<"type1">>), 419: sync(), 420: {ok, <<"type1">>} = get_host_type(mim(), Domain). 421: 422: db_deleted_domain_from_db(_) -> 423: Domain = random_domain_name(), 424: {ok, _} = insert_domain(mim(), Domain, <<"type1">>), 425: {ok, _} = delete_domain(mim(), Domain, <<"type1">>), 426: {error, not_found} = select_domain(mim(), Domain). 427: 428: db_deleted_domain_fails_with_wrong_host_type(_) -> 429: Domain = random_domain_name(), 430: {ok, _} = insert_domain(mim(), Domain, <<"type1">>), 431: {wrong_host_type, _} = 432: delete_domain(mim(), Domain, <<"type2">>), 433: {ok, #{host_type := <<"type1">>, status := enabled}} = 434: select_domain(mim(), Domain). 435: 436: db_deleted_domain_from_core(_) -> 437: Domain = random_domain_name(), 438: {ok, _} = insert_domain(mim(), Domain, <<"type1">>), 439: sync(), 440: {ok, _} = delete_domain(mim(), Domain, <<"type1">>), 441: sync(), 442: {error, not_found} = get_host_type(mim(), Domain). 443: 444: db_disabled_domain_is_in_db(_) -> 445: Domain = random_domain_name(), 446: {ok, _} = insert_domain(mim(), Domain, <<"type1">>), 447: {ok, _} = disable_domain(mim(), Domain), 448: {ok, #{host_type := <<"type1">>, status := disabled}} = 449: select_domain(mim(), Domain). 450: 451: db_disabled_domain_not_in_core(_) -> 452: Domain = random_domain_name(), 453: {ok, _} = insert_domain(mim(), Domain, <<"type1">>), 454: {ok, _} = disable_domain(mim(), Domain), 455: sync(), 456: {error, not_found} = get_host_type(mim(), Domain). 457: 458: db_reenabled_domain_is_in_db(_) -> 459: Domain = random_domain_name(), 460: {ok, _} = insert_domain(mim(), Domain, <<"type1">>), 461: {ok, _} = disable_domain(mim(), Domain), 462: {ok, _} = enable_domain(mim(), Domain), 463: {ok, #{host_type := <<"type1">>, status := enabled}} = 464: select_domain(mim(), Domain). 465: 466: db_reenabled_domain_is_in_core(_) -> 467: Domain = random_domain_name(), 468: {ok, _} = insert_domain(mim(), Domain, <<"type1">>), 469: {ok, _} = disable_domain(mim(), Domain), 470: {ok, _} = enable_domain(mim(), Domain), 471: sync(), 472: {ok, <<"type1">>} = get_host_type(mim(), Domain). 473: 474: db_cannot_insert_domain_twice_with_the_same_host_type(_) -> 475: Domain = random_domain_name(), 476: {ok, _} = insert_domain(mim(), Domain, <<"type1">>), 477: {duplicate, _} = insert_domain(mim(), Domain, <<"type1">>). 478: 479: db_cannot_insert_domain_twice_with_another_host_type(_) -> 480: Domain = random_domain_name(), 481: {ok, _} = insert_domain(mim(), Domain, <<"type1">>), 482: {duplicate, _} = insert_domain(mim(), Domain, <<"type2">>). 483: 484: db_cannot_insert_domain_with_unknown_host_type(_) -> 485: Domain = random_domain_name(), 486: {unknown_host_type, _} = insert_domain(mim(), Domain, <<"type6">>). 487: 488: db_cannot_delete_domain_with_unknown_host_type(_) -> 489: Domain = random_domain_name(), 490: {ok, _} = insert_domain(mim2(), Domain, <<"mim2only">>), 491: sync(), 492: {unknown_host_type, _} = delete_domain(mim(), Domain, <<"mim2only">>). 493: 494: db_cannot_enable_domain_with_unknown_host_type(_) -> 495: Domain = random_domain_name(), 496: {ok, _} = insert_domain(mim2(), Domain, <<"mim2only">>), 497: {ok, _} = disable_domain(mim2(), Domain), 498: sync(), 499: {unknown_host_type, _} = enable_domain(mim(), Domain). 500: 501: db_cannot_disable_domain_with_unknown_host_type(_) -> 502: Domain = random_domain_name(), 503: {ok, _} = insert_domain(mim2(), Domain, <<"mim2only">>), 504: sync(), 505: {unknown_host_type, _} = disable_domain(mim(), Domain). 506: 507: db_domains_with_unknown_host_type_are_ignored_by_core(_) -> 508: Domain1 = random_domain_name(), 509: Domain2 = random_domain_name(), 510: {ok, _} = insert_domain(mim2(), Domain1, <<"mim2only">>), 511: {ok, _} = insert_domain(mim2(), Domain2, <<"type1">>), 512: sync(), 513: {ok, <<"type1">>} = get_host_type(mim(), Domain2), %% Counter-case 514: {error, not_found} = get_host_type(mim(), Domain1). 515: 516: db_can_insert_update_delete_dynamic_domain_password(_) -> 517: Domain = <<"password-example.com">>, 518: {ok, _} = insert_domain(mim(), Domain, <<"type1">>), 519: sync(), 520: {ok, _} = set_domain_password(mim(), Domain, <<"rocky1">>), 521: ok = check_domain_password(mim(), Domain, <<"rocky1">>), 522: {ok, _} = set_domain_password(mim(), Domain, <<"rocky2">>), 523: ok = check_domain_password(mim(), Domain, <<"rocky2">>), 524: {ok, _} = delete_domain_password(mim(), Domain), 525: {error, not_found} = select_domain_admin(mim(), Domain). 526: 527: db_cannot_set_password_for_unknown_domain(_) -> 528: {not_found, _} = set_domain_password(mim(), <<"unknown_domain">>, <<>>). 529: 530: db_can_check_domain_password(_) -> 531: StaticDomain = <<"example.cfg">>, 532: {ok, _} = set_domain_password(mim(), StaticDomain, <<"myrock">>), 533: ok = check_domain_password(mim(), StaticDomain, <<"myrock">>), 534: {error, wrong_password} = check_domain_password(mim(), StaticDomain, <<"wrongrock">>). 535: 536: db_cannot_check_password_for_unknown_domain(_) -> 537: {error, not_found} = check_domain_password(mim(), <<"unknown_domain">>, <<>>). 538: 539: db_deleting_domain_deletes_domain_admin(_) -> 540: Domain = <<"password-del-example.db">>, 541: {ok, _} = insert_domain(mim(), Domain, <<"type1">>), 542: sync(), 543: {ok, _} = set_domain_password(mim(), Domain, <<"deleteme">>), 544: {ok, _} = delete_domain(mim(), Domain, <<"type1">>), 545: {error, not_found} = select_domain_admin(mim(), Domain). 546: 547: sql_select_from(_) -> 548: Domain = random_domain_name(), 549: {ok, _} = insert_domain(mim(), Domain, <<"type1">>), 550: Result = rpc(mim(), mongoose_domain_sql, select_from, [0, 100]), 551: % Other test cases insert their own domains thus we check for existence in list 552: [_|_] = lists:filter(fun({_, D, _}) -> D == Domain end, Result). 553: 554: db_could_sync_between_nodes(_) -> 555: {ok, _} = insert_domain(mim(), <<"example.com">>, <<"dbgroup">>), 556: sync(), 557: {ok, <<"dbgroup">>} = get_host_type(mim2(), <<"example.com">>). 558: 559: db_gaps_are_getting_filled_automatically(_Config) -> 560: Domain = random_domain_name(), 561: {ok, _} = insert_domain(mim(), Domain, <<"type1">>), 562: sync(), 563: Max = get_max_event_id(mim()), 564: %% Create a gap in events by manually adding an event 565: GapSize = 10, 566: {updated, 1} = insert_full_event(mim(), Max + GapSize, <<"something.else">>), 567: force_check_for_updates(mim()), 568: sync_local(mim()), 569: F = fun() -> get_event_ids_between(mim(), Max, Max + GapSize) end, 570: mongoose_helper:wait_until(F, lists:seq(Max, Max + GapSize), 571: #{time_left => timer:seconds(15)}). 572: 573: % plain_db_sequential cases 574: % these tests are hard to parallelise due to runtime changes to service 575: 576: sql_find_gaps_between(_) -> 577: with_service_suspended(fun() -> 578: %% Check several ranges 579: check_gap_finder(20, 22), 580: check_gap_finder(10, 15), 581: check_gap_finder(123, 321), 582: check_gap_finder(1000, 1300) 583: end). 584: 585: check_gap_finder(From, To) -> 586: {updated, 1} = insert_full_event(mim(), From, <<"gap_start">>), 587: {updated, 1} = insert_full_event(mim(), To, <<"gap_end">>), 588: Expected = lists:seq(From + 1, To - 1), 589: Expected = find_gaps_between(From, To). 590: 591: find_gaps_between(From, To) -> 592: rpc(mim(), mongoose_domain_loader, find_gaps_between, [From, To]). 593: 594: db_records_are_restored_on_mim_restart(_) -> 595: {ok, _} = insert_domain(mim(), <<"example.com">>, <<"type1">>), 596: %% Simulate MIM restart 597: service_disabled(mim()), 598: restart_domain_core(mim(), [], [<<"type1">>]), 599: {error, not_found} = get_host_type(mim(), <<"example.com">>), 600: service_enabled(mim()), 601: %% DB still contains data 602: {ok, #{host_type := <<"type1">>, status := enabled}} = 603: select_domain(mim(), <<"example.com">>), 604: %% Restored 605: {ok, <<"type1">>} = get_host_type(mim(), <<"example.com">>). 606: 607: db_record_is_ignored_if_domain_static(_) -> 608: {ok, _} = insert_domain(mim(), <<"example.com">>, <<"dbgroup">>), 609: {ok, _} = insert_domain(mim(), <<"example.net">>, <<"dbgroup">>), 610: %% Simulate MIM restart 611: service_disabled(mim()), 612: %% Only one domain is static 613: restart_domain_core(mim(), [{<<"example.com">>, <<"cfggroup">>}], [<<"dbgroup">>, <<"cfggroup">>]), 614: service_enabled(mim()), 615: %% DB still contains data 616: {ok, #{host_type := <<"dbgroup">>, status := enabled}} = 617: select_domain(mim(), <<"example.com">>), 618: {ok, #{host_type := <<"dbgroup">>, status := enabled}} = 619: select_domain(mim(), <<"example.net">>), 620: %% Static DB records are ignored 621: {ok, <<"cfggroup">>} = get_host_type(mim(), <<"example.com">>), 622: {ok, <<"dbgroup">>} = get_host_type(mim(), <<"example.net">>). 623: 624: db_events_table_gets_truncated(_) -> 625: %% Configure service with a very short interval 626: {ok, _} = insert_domain(mim(), <<"example.com">>, <<"dbgroup">>), 627: {ok, _} = insert_domain(mim(), <<"example.net">>, <<"dbgroup">>), 628: {ok, _} = insert_domain(mim(), <<"example.org">>, <<"dbgroup">>), 629: {ok, _} = insert_domain(mim(), <<"example.beta">>, <<"dbgroup">>), 630: Max = get_max_event_id(mim()), 631: true = is_integer(Max), 632: true = Max > 0, 633: %% The events table is not empty and the size of 1, eventually. 634: F = fun() -> get_min_event_id(mim()) end, 635: mongoose_helper:wait_until(F, Max, #{time_left => timer:seconds(15)}). 636: 637: db_deleted_from_one_node_while_service_disabled_on_another(_) -> 638: {ok, _} = insert_domain(mim(), <<"example.com">>, <<"dbgroup">>), 639: sync(), 640: {ok, <<"dbgroup">>} = get_host_type(mim2(), <<"example.com">>), 641: %% Service is disable on the second node 642: service_disabled(mim2()), 643: %% Removed from the first node 644: {ok, _} = delete_domain(mim(), <<"example.com">>, <<"dbgroup">>), 645: sync_local(mim()), 646: {error, not_found} = get_host_type(mim(), <<"example.com">>), 647: {ok, <<"dbgroup">>} = get_host_type(mim2(), <<"example.com">>), 648: %% Sync is working again 649: service_enabled(mim2()), 650: {error, not_found} = get_host_type(mim2(), <<"example.com">>). 651: 652: db_inserted_from_one_node_while_service_disabled_on_another(_) -> 653: %% Service is disable on the second node 654: service_disabled(mim2()), 655: {ok, _} = insert_domain(mim(), <<"example.com">>, <<"dbgroup">>), 656: %% Sync is working again 657: service_enabled(mim2()), 658: {ok, <<"dbgroup">>} = get_host_type(mim2(), <<"example.com">>). 659: 660: db_reinserted_from_one_node_while_service_disabled_on_another(_) -> 661: %% This test shows the behaviour when someone 662: %% reinserts a domain with a different host type. 663: {ok, _} = insert_domain(mim(), <<"example.com">>, <<"dbgroup">>), 664: sync(), 665: {ok, <<"dbgroup">>} = get_host_type(mim2(), <<"example.com">>), 666: %% Service is disable on the second node 667: service_disabled(mim2()), 668: %% Removed from the first node 669: {ok, _} = delete_domain(mim(), <<"example.com">>, <<"dbgroup">>), 670: sync_local(mim()), 671: {ok, _} = insert_domain(mim(), <<"example.com">>, <<"dbgroup2">>), 672: sync_local(mim()), 673: %% Sync is working again 674: service_enabled(mim2()), 675: sync(), 676: %% A corner case: domain name is reinserted with different host type 677: %% while service was down on mim2. check that mim2 is updated 678: {ok, <<"dbgroup2">>} = get_host_type(mim(), <<"example.com">>), 679: {ok, <<"dbgroup2">>} = get_host_type(mim2(), <<"example.com">>), 680: %% check deleting 681: {ok, _} = delete_domain(mim2(), <<"example.com">>, <<"dbgroup2">>), 682: sync(), 683: {error, not_found} = get_host_type(mim(), <<"example.com">>), 684: {error, not_found} = get_host_type(mim2(), <<"example.com">>). 685: 686: db_crash_on_initial_load_restarts_service(_) -> 687: service_enabled(mim()), 688: %% service is restarted 689: true = rpc(mim(), meck, wait, [service_domain_db, restart, 0, timer:seconds(1)]) > 0, 690: ok. 691: 692: db_out_of_sync_restarts_service(_) -> 693: {ok, _} = insert_domain(mim2(), <<"example1.com">>, <<"type1">>), 694: {ok, _} = insert_domain(mim2(), <<"example2.com">>, <<"type1">>), 695: sync(), 696: %% Pause processing events on one node 697: suspend_service(mim()), 698: {ok, _} = insert_domain(mim2(), <<"example3.com">>, <<"type1">>), 699: {ok, _} = insert_domain(mim2(), <<"example4.com">>, <<"type1">>), 700: sync_local(mim2()), 701: %% Truncate events table, keep only one event 702: MaxId = get_max_event_id(mim2()), 703: {updated, _} = delete_events_older_than(mim2(), MaxId), 704: {error, not_found} = get_host_type(mim(), <<"example3.com">>), 705: %% The size of the table is 1 706: MaxId = get_min_event_id(mim2()), 707: %% Resume processing events on one node 708: resume_service(mim()), 709: sync(), 710: %% Out of sync detected and service is restarted 711: true = rpc(mim(), meck, num_calls, [service_domain_db, restart, 0]) > 0, 712: ok. 713: 714: db_restarts_properly(_) -> 715: PID = rpc(mim(), erlang, whereis, [service_domain_db]), 716: ok = rpc(mim(), service_domain_db, restart, []), 717: F = fun() -> 718: PID2 = rpc(mim(), erlang, whereis, [service_domain_db]), 719: PID2 =/= PID 720: end, 721: mongoose_helper:wait_until(F, true, #{time_left => timer:seconds(15)}). 722: 723: db_keeps_syncing_after_cluster_join(Config) -> 724: HostType = dummy_auth_host_type(), 725: %% GIVING mim1 and mim2 are not clustered. 726: %% Ask mim1 to join mim2's cluster 727: %% (and mongooseim application gets restarted on mim1) 728: leave_cluster(Config), 729: service_enabled(mim()), 730: {ok, _} = insert_domain(mim(), <<"example1.com">>, HostType), 731: {ok, _} = insert_domain(mim2(), <<"example2.com">>, HostType), 732: sync(), 733: %% Nodes don't have to be clustered to sync the domains. 734: assert_domains_are_equal(HostType), 735: %% WHEN Adding mim1 joins into a cluster 736: %% (and mongooseim application gets restarted on mim1) 737: join_cluster(Config), 738: service_enabled(mim()), 739: %% THEN Sync is successful 740: {ok, _} = insert_domain(mim(), <<"example3.com">>, HostType), 741: {ok, _} = insert_domain(mim2(), <<"example4.com">>, HostType), 742: sync(), 743: assert_domains_are_equal(HostType). 744: 745: db_event_could_appear_with_lower_id(_Config) -> 746: %% We use 40 and 50 as event ids 747: %% We check two things: 748: %% - that "lazydom" actually gets loaded 749: %% - that gaps get filled 750: %% Starting with an empty DB 751: null = get_max_event_id(mim()), 752: {updated, 1} = insert_domain_settings_without_event(mim(), <<"fastdom">>, <<"type1">>), 753: {updated, 1} = insert_full_event(mim(), 50, <<"fastdom">>), 754: force_check_for_updates(mim()), 755: sync_local(mim()), 756: {ok, <<"type1">>} = get_host_type(mim(), <<"fastdom">>), 757: %% At this point we've completed the initial load and the DB sync 758: %% Now create an event before the first known event 759: {updated, 1} = insert_domain_settings_without_event(mim(), <<"lazydom">>, <<"type1">>), 760: {updated, 1} = insert_full_event(mim(), 40, <<"lazydom">>), 761: force_check_for_updates(mim()), 762: sync_local(mim()), 763: 40 = get_min_event_id(mim()), 764: 50 = get_max_event_id(mim()), 765: %% lazydom gets loaded 766: {ok, <<"type1">>} = get_host_type(mim(), <<"lazydom">>), 767: %% Check gaps 768: F = fun() -> get_event_ids_between(mim(), 40, 50) end, 769: mongoose_helper:wait_until(F, lists:seq(40, 50), 770: #{time_left => timer:seconds(15)}). 771: 772: db_can_insert_update_delete_static_domain_password(_) -> 773: StaticDomain = <<"example.cfg">>, 774: {ok, _} = set_domain_password(mim(), StaticDomain, <<"rocky1">>), 775: ok = check_domain_password(mim(), StaticDomain, <<"rocky1">>), 776: {ok, _} = set_domain_password(mim(), StaticDomain, <<"rocky2">>), 777: ok = check_domain_password(mim(), StaticDomain, <<"rocky2">>), 778: {ok, _} = delete_domain_password(mim(), StaticDomain), 779: {error, not_found} = select_domain_admin(mim(), StaticDomain). 780: 781: rest_cannot_enable_deleting(Config) -> 782: HostType = ?config(host_type, Config), 783: Domain = random_domain_name(), 784: {ok, _} = insert_domain(mim(), Domain, HostType), 785: {ok, #{status := enabled}} = select_domain(mim(), Domain), 786: {ok, _} = request_delete_domain(mim(), Domain, HostType), 787: {ok, #{status := deleting}} = select_domain(mim(), Domain), 788: {deleted, _} = enable_domain(mim(), Domain), 789: Server = ?config(server, Config), 790: Server ! continue, 791: F = fun () -> select_domain(mim(), Domain) end, 792: mongoose_helper:wait_until(F, {error, not_found}, #{time_left => timer:seconds(15)}). 793: 794: % rest cases 795: 796: rest_can_insert_domain(Config) -> 797: Domain = random_domain_name(), 798: {{<<"204">>, _}, _} = 799: rest_put_domain(Config, Domain, <<"type1">>), 800: {ok, #{host_type := <<"type1">>, status := enabled}} = 801: select_domain(mim(), Domain). 802: 803: rest_can_disable_domain(Config) -> 804: Domain = random_domain_name(), 805: rest_put_domain(Config, Domain, <<"type1">>), 806: rest_patch_enabled(Config, Domain, false), 807: {ok, #{host_type := <<"type1">>, status := disabled}} = 808: select_domain(mim(), Domain). 809: 810: rest_request_can_delete_domain(Config) -> 811: Domain = random_domain_name(), 812: %% Put a new domain to delete later 813: rest_put_domain(Config, Domain, <<"type1">>), 814: %% Request delete domain 815: {{<<"202">>, _}, _} = domain_rest_helper:request_delete_domain(Config, Domain, <<"type1">>), 816: %% Wait until it is not found anymore 817: Return = {{<<"404">>, <<"Not Found">>}, <<"Given domain does not exist">>}, 818: F1 = fun() -> rest_select_domain(Config, Domain) end, 819: mongoose_helper:wait_until(F1, Return, #{time_left => timer:seconds(15)}), 820: %% Double-check 821: F2 = fun() -> select_domain(mim(), Domain) end, 822: mongoose_helper:wait_until(F2, {error, not_found}, #{time_left => timer:seconds(5)}). 823: 824: rest_can_delete_domain(Config) -> 825: Domain = random_domain_name(), 826: rest_put_domain(Config, Domain, <<"type1">>), 827: {{<<"204">>, _}, _} = 828: rest_delete_domain(Config, Domain, <<"type1">>), 829: {error, not_found} = select_domain(mim(), Domain). 830: 831: rest_cannot_delete_domain_without_correct_type(Config) -> 832: Domain = random_domain_name(), 833: rest_put_domain(Config, Domain, <<"type1">>), 834: {{<<"403">>, <<"Forbidden">>}, <<"Wrong host type was provided">>} = 835: rest_delete_domain(Config, Domain, <<"type2">>), 836: {ok, _} = select_domain(mim(), Domain). 837: 838: rest_cannot_delete_missing_domain(Config) -> 839: Domain = random_domain_name(), 840: {{<<"404">>, <<"Not Found">>}, <<"Given domain does not exist">>} = 841: rest_delete_domain(Config, Domain, <<"type1">>), 842: {{<<"404">>, <<"Not Found">>}, <<"Given domain does not exist">>} = 843: domain_rest_helper:request_delete_domain(Config, Domain, <<"type1">>). 844: 845: rest_cannot_enable_missing_domain(Config) -> 846: {{<<"404">>, <<"Not Found">>}, <<"Given domain does not exist">>} = 847: rest_patch_enabled(Config, <<"example.db">>, true). 848: 849: rest_cannot_insert_domain_twice_with_another_host_type(Config) -> 850: Domain = random_domain_name(), 851: rest_put_domain(Config, Domain, <<"type1">>), 852: {{<<"409">>, <<"Conflict">>}, <<"Domain already exists">>} = 853: rest_put_domain(Config, Domain, <<"type2">>). 854: 855: rest_cannot_insert_domain_with_unknown_host_type(Config) -> 856: Domain = random_domain_name(), 857: {{<<"403">>,<<"Forbidden">>}, <<"Unknown host type">>} = 858: rest_put_domain(Config, Domain, <<"type6">>). 859: 860: rest_cannot_delete_domain_with_unknown_host_type(Config) -> 861: Domain = random_domain_name(), 862: {{<<"403">>,<<"Forbidden">>}, <<"Unknown host type">>} = 863: rest_delete_domain(Config, Domain, <<"type6">>). 864: 865: rest_cannot_disable_missing_domain(Config) -> 866: {{<<"404">>, <<"Not Found">>}, <<"Given domain does not exist">>} = 867: rest_patch_enabled(Config, <<"example.db">>, false). 868: 869: rest_can_enable_domain(Config) -> 870: Domain = random_domain_name(), 871: rest_put_domain(Config, Domain, <<"type1">>), 872: rest_patch_enabled(Config, Domain, false), 873: rest_patch_enabled(Config, Domain, true), 874: {ok, #{host_type := <<"type1">>, status := enabled}} = 875: select_domain(mim(), Domain). 876: 877: rest_can_select_domain(Config) -> 878: Domain = random_domain_name(), 879: rest_put_domain(Config, Domain, <<"type1">>), 880: {HttpStatus, {Info}} = rest_select_domain(Config, Domain), 881: SortedResult = {HttpStatus, {lists:sort(Info)}}, 882: {{<<"200">>, <<"OK">>}, 883: {[ {<<"host_type">>, <<"type1">>}, {<<"status">>, <<"enabled">>} ]}} = 884: SortedResult. 885: 886: rest_cannot_select_domain_if_domain_not_found(Config) -> 887: {{<<"404">>, <<"Not Found">>}, <<"Given domain does not exist">>} = 888: rest_select_domain(Config, <<"example.db">>). 889: 890: rest_cannot_select_domain_when_it_is_static(Config) -> 891: {{<<"403">>, <<"Forbidden">>}, <<"Domain is static">>} = 892: rest_select_domain(Config, <<"example.cfg">>). 893: 894: rest_cannot_put_domain_without_host_type(Config) -> 895: {{<<"400">>, <<"Bad Request">>}, <<"'host_type' field is missing">>} = 896: putt_domain_with_custom_body(Config, #{}). 897: 898: rest_cannot_put_domain_without_body(Config) -> 899: {{<<"400">>, <<"Bad Request">>}, <<"Invalid request body">>} = 900: putt_domain_with_custom_body(Config, <<>>). 901: 902: rest_cannot_put_domain_with_invalid_json(Config) -> 903: {{<<"400">>, <<"Bad Request">>}, <<"Invalid request body">>} = 904: putt_domain_with_custom_body(Config, <<"{kek">>). 905: 906: rest_cannot_put_domain_with_invalid_name(Config) -> 907: {{<<"400">>, <<"Bad Request">>}, <<"Invalid domain name">>} = 908: rest_put_domain(Config, <<"%f3">>, <<"type1">>). % nameprep fails for ASCII code 243 909: 910: rest_cannot_put_domain_when_it_is_static(Config) -> 911: {{<<"403">>, <<"Forbidden">>}, <<"Domain is static">>} = 912: rest_put_domain(Config, <<"example.cfg">>, <<"type1">>). 913: 914: rest_cannot_delete_domain_without_host_type(Config) -> 915: {{<<"400">>, <<"Bad Request">>}, <<"'host_type' field is missing">>} = 916: delete_custom(Config, admin, <<"/domains/example.db">>, #{}). 917: 918: rest_cannot_delete_domain_without_body(Config) -> 919: {{<<"400">>, <<"Bad Request">>}, <<"Invalid request body">>} = 920: delete_custom(Config, admin, <<"/domains/example.db">>, <<>>). 921: 922: rest_cannot_delete_domain_with_invalid_json(Config) -> 923: {{<<"400">>, <<"Bad Request">>}, <<"Invalid request body">>} = 924: delete_custom(Config, admin, <<"/domains/example.db">>, <<"{kek">>). 925: 926: rest_cannot_delete_domain_when_it_is_static(Config) -> 927: {{<<"403">>, <<"Forbidden">>}, <<"Domain is static">>} = 928: rest_delete_domain(Config, <<"example.cfg">>, <<"type1">>), 929: {{<<"403">>, <<"Forbidden">>}, <<"Domain is static">>} = 930: domain_rest_helper:request_delete_domain(Config, <<"example.cfg">>, <<"type1">>). 931: 932: rest_cannot_patch_domain_without_enabled_field(Config) -> 933: {{<<"400">>, <<"Bad Request">>}, <<"'enabled' field is missing">>} = 934: patch_custom(Config, admin, <<"/domains/example.db">>, #{}). 935: 936: rest_cannot_patch_domain_without_body(Config) -> 937: {{<<"400">>,<<"Bad Request">>}, <<"Invalid request body">>} = 938: patch_custom(Config, admin, <<"/domains/example.db">>, <<>>). 939: 940: rest_cannot_patch_domain_with_invalid_json(Config) -> 941: {{<<"400">>,<<"Bad Request">>}, <<"Invalid request body">>} = 942: patch_custom(Config, admin, <<"/domains/example.db">>, <<"{kek">>). 943: 944: rest_cannot_enable_domain_when_it_is_static(Config) -> 945: {{<<"403">>, <<"Forbidden">>}, <<"Domain is static">>} = 946: rest_patch_enabled(Config, <<"example.cfg">>, true). 947: 948: rest_delete_domain_cleans_data_from_mam(Config) -> 949: HostType = dummy_auth_host_type(), 950: rest_put_domain(Config, <<"example.com">>, HostType), %% alice3 951: rest_put_domain(Config, <<"example.org">>, HostType), %% bob3 952: sync(), 953: %% Alice and Bob use example.com 954: F = fun(FreshConfig, Alice, Bob) -> 955: escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"OH, HAI!">>)), 956: escalus:wait_for_stanza(Bob), 957: mam_helper:wait_for_archive_size_with_host_type(HostType, Alice, 1), 958: mam_helper:wait_for_archive_size_with_host_type(HostType, Bob, 1), 959: {{<<"204">>, _}, _} = 960: rest_delete_domain(Config, <<"example.com">>, HostType), 961: {{<<"204">>, _}, _} = 962: rest_delete_domain(Config, <<"example.org">>, HostType), 963: sync(), 964: %% At this point MIM cannot resolve a domain to a host type, 965: %% so we have to pass it 966: mam_helper:wait_for_archive_size_with_host_type(HostType, Alice, 0), 967: mam_helper:wait_for_archive_size_with_host_type(HostType, Bob, 0), 968: %% Already cleaned at this point 969: escalus_cleaner:remove_client(FreshConfig, Alice), 970: escalus_cleaner:remove_client(FreshConfig, Bob) 971: end, 972: escalus:fresh_story_with_config(Config, [{alice3, 1}, {bob3, 1}], F). 973: 974: % rest_auth cases 975: %% auth provided, but not configured: 976: rest_cannot_insert_domain_if_auth_provided_but_not_configured(Config) -> 977: {{<<"401">>, <<"Unauthorized">>}, _} = 978: rest_put_domain(set_valid_creds(Config), <<"example.db">>, <<"type1">>). 979: 980: rest_cannot_delete_domain_if_auth_provided_but_not_configured(Config) -> 981: {{<<"401">>, <<"Unauthorized">>}, _} = 982: rest_delete_domain(set_valid_creds(Config), <<"example.db">>, <<"type1">>). 983: 984: rest_cannot_enable_domain_if_auth_provided_but_not_configured(Config) -> 985: {{<<"401">>, <<"Unauthorized">>}, _} = 986: rest_patch_enabled(set_valid_creds(Config), <<"example.db">>, false). 987: 988: rest_cannot_disable_domain_if_auth_provided_but_not_configured(Config) -> 989: {{<<"401">>, <<"Unauthorized">>}, _} = 990: rest_patch_enabled(set_valid_creds(Config), <<"example.db">>, false). 991: 992: rest_cannot_select_domain_if_auth_provided_but_not_configured(Config) -> 993: {{<<"401">>, <<"Unauthorized">>}, _} = 994: rest_select_domain(set_valid_creds(Config), <<"example.db">>). 995: 996: %% with wrong pass: 997: rest_cannot_insert_domain_with_wrong_pass(Config) -> 998: {{<<"401">>, <<"Unauthorized">>}, _} = 999: rest_put_domain(set_invalid_creds(Config), <<"example.db">>, <<"type1">>). 1000: 1001: rest_cannot_delete_domain_with_wrong_pass(Config) -> 1002: {{<<"401">>, <<"Unauthorized">>}, _} = 1003: rest_delete_domain(set_invalid_creds(Config), <<"example.db">>, <<"type1">>). 1004: 1005: rest_cannot_enable_domain_with_wrong_pass(Config) -> 1006: {{<<"401">>, <<"Unauthorized">>}, _} = 1007: rest_patch_enabled(set_invalid_creds(Config), <<"example.db">>, true). 1008: 1009: rest_cannot_disable_domain_with_wrong_pass(Config) -> 1010: {{<<"401">>, <<"Unauthorized">>}, _} = 1011: rest_patch_enabled(set_invalid_creds(Config), <<"example.db">>, false). 1012: 1013: rest_cannot_select_domain_with_wrong_pass(Config) -> 1014: {{<<"401">>, <<"Unauthorized">>}, _} = 1015: rest_select_domain(set_invalid_creds(Config), <<"example.db">>). 1016: 1017: %% without auth: 1018: rest_cannot_insert_domain_without_auth(Config) -> 1019: {{<<"401">>, <<"Unauthorized">>}, _} = 1020: rest_put_domain(set_no_creds(Config), <<"example.db">>, <<"type1">>). 1021: 1022: rest_cannot_delete_domain_without_auth(Config) -> 1023: {{<<"401">>, <<"Unauthorized">>}, _} = 1024: rest_delete_domain(set_no_creds(Config), <<"example.db">>, <<"type1">>). 1025: 1026: rest_cannot_enable_domain_without_auth(Config) -> 1027: {{<<"401">>, <<"Unauthorized">>}, _} = 1028: rest_patch_enabled(set_no_creds(Config), <<"example.db">>, true). 1029: 1030: rest_cannot_disable_domain_without_auth(Config) -> 1031: {{<<"401">>, <<"Unauthorized">>}, _} = 1032: rest_patch_enabled(set_no_creds(Config), <<"example.db">>, false). 1033: 1034: rest_cannot_select_domain_without_auth(Config) -> 1035: {{<<"401">>, <<"Unauthorized">>}, _} = 1036: rest_select_domain(set_no_creds(Config), <<"example.db">>). 1037: 1038: % rest_db_fails cases, SQL query is mocked to fail 1039: rest_insert_domain_fails_if_db_fails(Config) -> 1040: assert_rest_db_error(rest_put_domain(Config, <<"example.db">>, <<"type1">>)). 1041: 1042: rest_delete_domain_fails_if_db_fails(Config) -> 1043: {ok, _} = insert_domain(mim(), <<"example.db">>, <<"type1">>), 1044: assert_rest_db_error(rest_delete_domain(Config, <<"example.db">>, <<"type1">>)). 1045: 1046: rest_enable_domain_fails_if_db_fails(Config) -> 1047: assert_rest_db_error(rest_patch_enabled(Config, <<"example.db">>, true)). 1048: 1049: % rest_service_disabled cases, service is disabled for the whole group 1050: rest_insert_domain_fails_if_service_disabled(Config) -> 1051: {{<<"403">>, <<"Forbidden">>}, <<"Dynamic domains service is disabled">>} = 1052: rest_put_domain(Config, <<"example.db">>, <<"type1">>). 1053: 1054: rest_delete_domain_fails_if_service_disabled(Config) -> 1055: {{<<"403">>, <<"Forbidden">>}, <<"Dynamic domains service is disabled">>} = 1056: rest_delete_domain(Config, <<"example.db">>, <<"type1">>). 1057: 1058: rest_enable_domain_fails_if_service_disabled(Config) -> 1059: {{<<"403">>, <<"Forbidden">>}, <<"Dynamic domains service is disabled">>} = 1060: rest_patch_enabled(Config, <<"example.db">>, true). 1061: 1062: %%-------------------------------------------------------------------- 1063: %% Helpers 1064: %%-------------------------------------------------------------------- 1065: 1066: service_enabled(Node) -> 1067: service_enabled(Node, #{}), 1068: sync_local(Node). 1069: 1070: service_enabled(Node, ExtraOpts) -> 1071: Opts = config([services, service_domain_db], ExtraOpts), 1072: dynamic_services:ensure_started(Node, service_domain_db, Opts), 1073: true = rpc(Node, service_domain_db, enabled, []). 1074: 1075: service_disabled(Node) -> 1076: dynamic_services:ensure_stopped(Node, service_domain_db), 1077: false = rpc(Node, service_domain_db, enabled, []). 1078: 1079: restart_domain_core(Node, Pairs, AllowedHostTypes) -> 1080: domain_helper:restart_domain_core(Node, Pairs, AllowedHostTypes). 1081: 1082: restart_domain_core(Node) -> 1083: domain_helper:restart_domain_core(Node). 1084: 1085: insert_domain(Node, Domain, HostType) -> 1086: rpc(Node, mongoose_domain_api, insert_domain, [Domain, HostType]). 1087: 1088: delete_domain(Node, Domain, HostType) -> 1089: rpc(Node, mongoose_domain_api, delete_domain, [Domain, HostType]). 1090: 1091: request_delete_domain(Node, Domain, HostType) -> 1092: rpc(Node, mongoose_domain_api, request_delete_domain, [Domain, HostType]). 1093: 1094: select_domain(Node, Domain) -> 1095: rpc(Node, mongoose_domain_sql, select_domain, [Domain]). 1096: 1097: check_domain_password(Node, Domain, Password) -> 1098: rpc(Node, mongoose_domain_api, check_domain_password, [Domain, Password]). 1099: 1100: set_domain_password(Node, Domain, Password) -> 1101: rpc(Node, mongoose_domain_api, set_domain_password, [Domain, Password]). 1102: 1103: delete_domain_password(Node, Domain) -> 1104: rpc(Node, mongoose_domain_api, delete_domain_password, [Domain]). 1105: 1106: select_domain_admin(Node, Domain) -> 1107: rpc(Node, mongoose_domain_sql, select_domain_admin, [Domain]). 1108: 1109: insert_full_event(Node, EventId, Domain) -> 1110: rpc(Node, mongoose_domain_sql, insert_full_event, [EventId, Domain]). 1111: 1112: insert_domain_settings_without_event(Node, Domain, HostType) -> 1113: rpc(Node, mongoose_domain_sql, insert_domain_settings_without_event, 1114: [Domain, HostType]). 1115: 1116: get_event_ids_between(Node, Min, Max) -> 1117: rpc(Node, mongoose_domain_sql, get_event_ids_between, [Min, Max]). 1118: 1119: erase_database(Node) -> 1120: case mongoose_helper:is_rdbms_enabled(domain()) of 1121: true -> 1122: prepare_test_queries(Node), 1123: rpc(Node, mongoose_domain_sql, erase_database, [global]); 1124: false -> ok 1125: end. 1126: 1127: prepare_test_queries(Node) -> 1128: case mongoose_helper:is_rdbms_enabled(domain()) of 1129: true -> rpc(Node, mongoose_domain_sql, prepare_test_queries, []); 1130: false -> ok 1131: end. 1132: 1133: get_min_event_id(Node) -> 1134: {Min, _} = rpc(Node, mongoose_domain_sql, get_minmax_event_id, []), 1135: Min. 1136: 1137: get_max_event_id(Node) -> 1138: {_, Max} = rpc(Node, mongoose_domain_sql, get_minmax_event_id, []), 1139: Max. 1140: 1141: delete_events_older_than(Node, Id) -> 1142: rpc(Node, mongoose_domain_sql, delete_events_older_than, [Id]). 1143: 1144: get_host_type(Node, Domain) -> 1145: rpc(Node, mongoose_domain_api, get_host_type, [Domain]). 1146: 1147: get_domains_by_host_type(Node, HostType) -> 1148: rpc(Node, mongoose_domain_api, get_domains_by_host_type, [HostType]). 1149: 1150: get_all_static(Node) -> 1151: rpc(Node, mongoose_domain_api, get_all_static, []). 1152: 1153: get_all_dynamic(Node) -> 1154: rpc(Node, mongoose_domain_api, get_all_dynamic, []). 1155: 1156: disable_domain(Node, Domain) -> 1157: rpc(Node, mongoose_domain_api, disable_domain, [Domain]). 1158: 1159: enable_domain(Node, Domain) -> 1160: rpc(Node, mongoose_domain_api, enable_domain, [Domain]). 1161: 1162: start_domain_removal_hook(HostType) -> 1163: Server = spawn(fun stopper/0), 1164: rpc(mim(), gen_hook, add_handler, 1165: [ remove_domain, HostType, fun ?MODULE:domain_removal_hook_fn/3, 1166: #{server => Server}, 30]), %% Priority is so that it comes before muclight and mam 1167: Server. 1168: 1169: stop_domain_removal_hook(HostType, Server) -> 1170: rpc(mim(), gen_hook, delete_handler, 1171: [ remove_domain, HostType, fun ?MODULE:domain_removal_hook_fn/3, 1172: #{server => Server}, 30]). 1173: 1174: domain_removal_hook_fn(Acc, _Params, #{server := Server}) -> 1175: Server ! {wait, self()}, 1176: receive continue -> ok end, 1177: {ok, Acc}. 1178: 1179: stopper() -> 1180: receive 1181: {wait, From} -> 1182: receive continue -> ok end, 1183: From ! continue 1184: end. 1185: 1186: %% force_check_for_updates is already sent by insert or delete commands. 1187: %% But it is async. 1188: %% So, the only thing is left to sync is to call ping to the gen_server 1189: %% to ensure we've finished the check. 1190: sync() -> 1191: sync_local(mim()), 1192: sync_local(mim2()), 1193: ok. 1194: 1195: with_service_suspended(F) -> 1196: suspend_service(mim()), 1197: suspend_service(mim2()), 1198: try 1199: F() 1200: after 1201: resume_service(mim()), 1202: resume_service(mim2()) 1203: end. 1204: 1205: suspend_service(Node) -> 1206: ok = rpc(Node, sys, suspend, [service_domain_db]). 1207: 1208: resume_service(Node) -> 1209: ok = rpc(Node, sys, resume, [service_domain_db]). 1210: 1211: sync_local(Node) -> 1212: pong = rpc(Node, service_domain_db, sync_local, []). 1213: 1214: force_check_for_updates(Node) -> 1215: ok = rpc(Node, service_domain_db, force_check_for_updates, []). 1216: 1217: %% Needed for pg2 group to work 1218: %% So, multiple node tests work 1219: ensure_nodes_know_each_other() -> 1220: pong = rpc(mim2(), net_adm, ping, [maps:get(node, mim())]). 1221: 1222: maybe_setup_meck(rest_insert_domain_fails_if_db_fails) -> 1223: ok = rpc(mim(), meck, new, [mongoose_domain_sql, [passthrough, no_link]]), 1224: ok = rpc(mim(), meck, expect, [mongoose_domain_sql, insert_domain, 2, 1225: {error, {db_error, simulated_db_error}}]); 1226: maybe_setup_meck(rest_delete_domain_fails_if_db_fails) -> 1227: ok = rpc(mim(), meck, new, [mongoose_domain_sql, [passthrough, no_link]]), 1228: ok = rpc(mim(), meck, expect, [mongoose_domain_sql, delete_domain, 2, 1229: {error, {db_error, simulated_db_error}}]); 1230: maybe_setup_meck(rest_enable_domain_fails_if_db_fails) -> 1231: ok = rpc(mim(), meck, new, [mongoose_domain_sql, [passthrough, no_link]]), 1232: ok = rpc(mim(), meck, expect, [mongoose_domain_sql, set_status, 2, 1233: {error, {db_error, simulated_db_error}}]); 1234: maybe_setup_meck(db_crash_on_initial_load_restarts_service) -> 1235: ok = rpc(mim(), meck, new, [mongoose_domain_sql, [passthrough, no_link]]), 1236: ok = rpc(mim(), meck, expect, [mongoose_domain_sql, select_from, 2, something_strange]), 1237: ok = rpc(mim(), meck, new, [service_domain_db, [passthrough, no_link]]), 1238: ok = rpc(mim(), meck, expect, [service_domain_db, restart, 0, ok]); 1239: maybe_setup_meck(db_out_of_sync_restarts_service) -> 1240: ok = rpc(mim(), meck, new, [service_domain_db, [passthrough, no_link]]), 1241: ok = rpc(mim(), meck, expect, [service_domain_db, restart, 0, ok]); 1242: maybe_setup_meck(_TestCase) -> 1243: ok. 1244: 1245: maybe_teardown_meck(_) -> 1246: %% running unload meck makes no harm even if nothing is mocked 1247: rpc(mim(), meck, unload, []). 1248: 1249: leave_cluster(Config) -> 1250: execute_command(<<"server">>, <<"leaveCluster">>, #{}, Config). 1251: 1252: join_cluster(Config) -> 1253: #{node := Node2} = distributed_helper:mim2(), 1254: execute_command(<<"server">>, <<"joinCluster">>, #{<<"node">> => Node2}, Config). 1255: 1256: assert_domains_are_equal(HostType) -> 1257: Domains1 = lists:sort(get_domains_by_host_type(mim(), HostType)), 1258: Domains2 = lists:sort(get_domains_by_host_type(mim2(), HostType)), 1259: case Domains1 == Domains2 of 1260: true -> ok; 1261: false -> ct:fail({Domains1, Domains2}) 1262: end. 1263: 1264: assert_rest_db_error({Result, Msg}) -> 1265: ?assertEqual({<<"500">>, <<"Internal Server Error">>}, Result), 1266: ?assertEqual(<<>>, Msg). % shouldn't leak out the DB error, it's in the logs anyway 1267: 1268: dummy_auth_host_type() -> 1269: <<"dummy auth">>. %% specified in the TOML config file 1270: 1271: random_domain_name() -> 1272: Prefix = integer_to_binary(erlang:unique_integer([positive])), 1273: <<Prefix/binary, ".example.db">>.