1: -module(graphql_session_SUITE). 2: 3: -include_lib("common_test/include/ct.hrl"). 4: -include_lib("eunit/include/eunit.hrl"). 5: -include_lib("exml/include/exml.hrl"). 6: 7: -compile([export_all, nowarn_export_all]). 8: 9: -import(distributed_helper, [mim/0, require_rpc_nodes/1, rpc/4]). 10: -import(graphql_helper, [execute/3, execute_auth/2, get_listener_port/1, 11: get_listener_config/1, get_ok_value/2, get_err_msg/1]). 12: 13: suite() -> 14: require_rpc_nodes([mim]) ++ escalus:suite(). 15: 16: all() -> 17: [{group, user_session}, 18: {group, admin_session}]. 19: 20: groups() -> 21: [{user_session, [parallel], user_session_handler()}, 22: {admin_session, [], admin_session_handler()}]. 23: 24: user_session_handler() -> 25: [user_list_resources, 26: user_count_resources, 27: user_sessions_info]. 28: 29: admin_session_handler() -> 30: [admin_list_sessions, 31: admin_count_sessions, 32: admin_list_user_sessions, 33: admin_count_user_resources, 34: admin_get_user_resource, 35: admin_list_users_with_status, 36: admin_count_users_with_status, 37: admin_kick_session, 38: admin_set_presence, 39: admin_set_presence_away, 40: admin_set_presence_unavailable]. 41: 42: init_per_suite(Config) -> 43: Config2 = escalus:init_per_suite(Config), 44: dynamic_modules:save_modules(domain_helper:host_type(), Config2). 45: 46: end_per_suite(Config) -> 47: dynamic_modules:restore_modules(Config), 48: escalus:end_per_suite(Config). 49: 50: init_per_group(admin_session, Config) -> 51: Config1 = escalus:create_users(Config, escalus:get_users([alice, alice_bis, bob])), 52: graphql_helper:init_admin_handler(Config1); 53: init_per_group(user_session, Config) -> 54: [{schema_endpoint, user} | Config]. 55: 56: end_per_group(admin_session, Config) -> 57: escalus_fresh:clean(), 58: escalus:delete_users(Config, escalus:get_users([alice, alice_bis, bob])); 59: end_per_group(user_session, _Config) -> 60: escalus_fresh:clean(). 61: 62: init_per_testcase(CaseName, Config) -> 63: escalus:init_per_testcase(CaseName, Config). 64: 65: end_per_testcase(CaseName, Config) -> 66: escalus:end_per_testcase(CaseName, Config). 67: 68: %% Test cases 69: 70: user_list_resources(Config) -> 71: escalus:fresh_story_with_config(Config, [{alice, 2}], fun user_list_resources_story/3). 72: 73: user_list_resources_story(Config, Alice, Alice2) -> 74: Ep = ?config(schema_endpoint, Config), 75: Creds = graphql_helper:make_creds(Alice), 76: 77: Query = <<"query{ session { listResources } }">>, 78: Result = execute(Ep, #{query => Query}, Creds), 79: 80: Path = [data, session, listResources], 81: ExpectedRes = [escalus_client:resource(Alice), escalus_client:resource(Alice2)], 82: ?assertMatch(ExpectedRes, lists:sort(get_ok_value(Path, Result))). 83: 84: user_count_resources(Config) -> 85: escalus:fresh_story_with_config(Config, [{alice, 3}], fun user_count_resources_story/4). 86: 87: user_count_resources_story(Config, Alice, _Alice2, _Alice3) -> 88: Ep = ?config(schema_endpoint, Config), 89: Creds = graphql_helper:make_creds(Alice), 90: 91: Query = <<"query{ session { countResources } }">>, 92: Result = execute(Ep, #{query => Query}, Creds), 93: 94: Path = [data, session, countResources], 95: ?assertEqual(3, get_ok_value(Path, Result)). 96: 97: user_sessions_info(Config) -> 98: escalus:fresh_story_with_config(Config, [{alice, 1}], fun user_sessions_info_story/2). 99: 100: user_sessions_info_story(Config, Alice) -> 101: Ep = ?config(schema_endpoint, Config), 102: Creds = graphql_helper:make_creds(Alice), 103: 104: Query = <<"query{ session { listSessions { user } } }">>, 105: Result = execute(Ep, #{query => Query}, Creds), 106: ExpectedUser = escalus_utils:jid_to_lower(escalus_client:full_jid(Alice)), 107: 108: Path = [data, session, listSessions], 109: ?assertMatch([#{<<"user">> := ExpectedUser}], get_ok_value(Path, Result)). 110: 111: admin_list_sessions(Config) -> 112: escalus:fresh_story_with_config(Config, [{alice, 1}, {alice_bis, 1}, {bob, 1}], 113: fun admin_list_sessions_story/4). 114: 115: admin_list_sessions_story(Config, _Alice, AliceB, _Bob) -> 116: BisDomain = escalus_client:server(AliceB), 117: Path = [data, session, listSessions], 118: % List all sessions 119: Res = execute_auth(list_sessions_body(null), Config), 120: Sessions = get_ok_value(Path, Res), 121: ?assertEqual(3, length(Sessions)), 122: % List sessions for a domain 123: Res2 = execute_auth(list_sessions_body(BisDomain), Config), 124: Sessions2 = get_ok_value(Path, Res2), 125: ?assertEqual(1, length(Sessions2)). 126: 127: admin_count_sessions(Config) -> 128: escalus:fresh_story_with_config(Config, [{alice, 1}, {alice_bis, 1}, {bob, 1}], 129: fun admin_count_sessions_story/4). 130: 131: admin_count_sessions_story(Config, _Alice, AliceB, _Bob) -> 132: BisDomain = escalus_client:server(AliceB), 133: Path = [data, session, countSessions], 134: % Count all sessions 135: Res = execute_auth(count_sessions_body(null), Config), 136: Number = get_ok_value(Path, Res), 137: ?assertEqual(3, Number), 138: % Count sessions for a domain 139: Res2 = execute_auth(count_sessions_body(BisDomain), Config), 140: Number2 = get_ok_value(Path, Res2), 141: ?assertEqual(1, Number2). 142: 143: admin_list_user_sessions(Config) -> 144: escalus:fresh_story_with_config(Config, [{alice, 2}, {bob, 1}], 145: fun admin_list_user_sessions_story/4). 146: 147: admin_list_user_sessions_story(Config, Alice, Alice2, _Bob) -> 148: S1JID = escalus_client:full_jid(Alice), 149: S2JID = escalus_client:full_jid(Alice2), 150: Path = [data, session, listUserSessions], 151: Res = execute_auth(list_user_sessions_body(S1JID), Config), 152: ExpectedRes = lists:map(fun escalus_utils:jid_to_lower/1, [S1JID, S2JID]), 153: Sessions = get_ok_value(Path, Res), 154: ?assertEqual(2, length(Sessions)), 155: ?assert(users_match(ExpectedRes, Sessions)). 156: 157: admin_count_user_resources(Config) -> 158: escalus:fresh_story_with_config(Config, [{alice, 3}], fun admin_count_user_resources_story/4). 159: 160: admin_count_user_resources_story(Config, Alice, _Alice2, _Alice3) -> 161: Path = [data, session, countUserResources], 162: JID = escalus_client:full_jid(Alice), 163: Res = execute_auth(count_user_resources_body(JID), Config), 164: ?assertEqual(3, get_ok_value(Path, Res)). 165: 166: admin_get_user_resource(Config) -> 167: escalus:fresh_story_with_config(Config, [{alice, 3}], fun admin_get_user_resource_story/4). 168: 169: admin_get_user_resource_story(Config, Alice, Alice2, _Alice3) -> 170: Path = [data, session, getUserResource], 171: JID = escalus_client:short_jid(Alice), 172: % Provide a correct resource number 173: Res = execute_auth(get_user_resource_body(JID, 2), Config), 174: ?assertEqual(escalus_client:resource(Alice2), get_ok_value(Path, Res)), 175: % Provide a wrong resource number 176: Res2 = execute_auth(get_user_resource_body(JID, 4), Config), 177: ?assertNotEqual(nomatch, binary:match(get_err_msg(Res2), <<"Wrong resource number">>)). 178: 179: admin_count_users_with_status(Config) -> 180: escalus:fresh_story_with_config(Config, [{alice, 1}, {alice_bis, 1}], 181: fun admin_count_users_with_status_story/3). 182: 183: admin_count_users_with_status_story(Config, Alice, AliceB) -> 184: Path = [data, session, countUsersWithStatus], 185: AwayStatus = <<"away">>, 186: AwayPresence = escalus_stanza:presence_show(AwayStatus), 187: DndStatus = <<"dnd">>, 188: DndPresence = escalus_stanza:presence_show(DndStatus), 189: % Count users with away status globally 190: escalus_client:send(Alice, AwayPresence), 191: escalus_client:send(AliceB, AwayPresence), 192: Res = execute_auth(count_users_with_status_body(null, AwayStatus), Config), 193: ?assertEqual(2, get_ok_value(Path, Res)), 194: % Count users with away status for a domain 195: Res2 = execute_auth(count_users_with_status_body(domain_helper:domain(), AwayStatus), Config), 196: ?assertEqual(1, get_ok_value(Path, Res2)), 197: % Count users with dnd status globally 198: escalus_client:send(AliceB, DndPresence), 199: Res3 = execute_auth(count_users_with_status_body(null, DndStatus), Config), 200: ?assertEqual(1, get_ok_value(Path, Res3)). 201: 202: admin_list_users_with_status(Config) -> 203: escalus:fresh_story_with_config(Config, [{alice, 1}, {alice_bis, 1}], 204: fun admin_list_users_with_status_story/3). 205: 206: admin_list_users_with_status_story(Config, Alice, AliceB) -> 207: AliceJID = escalus_client:full_jid(Alice), 208: AliceBJID = escalus_client:full_jid(AliceB), 209: Path = [data, session, listUsersWithStatus], 210: AwayStatus = <<"away">>, 211: AwayPresence = escalus_stanza:presence_show(AwayStatus), 212: DndStatus = <<"dnd">>, 213: DndPresence = escalus_stanza:presence_show(DndStatus), 214: % List users with away status globally 215: escalus_client:send(Alice, AwayPresence), 216: escalus_client:send(AliceB, AwayPresence), 217: Res = execute_auth(list_users_with_status_body(null, AwayStatus), Config), 218: StatusUsers = get_ok_value(Path, Res), 219: ?assertEqual(2, length(StatusUsers)), 220: ?assert(users_match([AliceJID, AliceBJID], StatusUsers)), 221: % List users with away status for a domain 222: Res2 = execute_auth(list_users_with_status_body(domain_helper:domain(), AwayStatus), Config), 223: StatusUsers2 = get_ok_value(Path, Res2), 224: ?assertEqual(1, length(StatusUsers2)), 225: ?assert(users_match([AliceJID], StatusUsers2)), 226: % List users with dnd status globally 227: escalus_client:send(AliceB, DndPresence), 228: Res3 = execute_auth(list_users_with_status_body(null, DndStatus), Config), 229: StatusUsers3 = get_ok_value(Path, Res3), 230: ?assertEqual(1, length(StatusUsers3)), 231: ?assert(users_match([AliceBJID], StatusUsers3)). 232: 233: admin_kick_session(Config) -> 234: escalus:fresh_story_with_config(Config, [{alice, 2}], fun admin_kick_session_story/3). 235: 236: admin_kick_session_story(Config, Alice1, Alice2) -> 237: JIDA1 = escalus_client:full_jid(Alice1), 238: JIDA2 = escalus_client:full_jid(Alice2), 239: Reason = <<"Test kick">>, 240: Path = [data, session, kickUser, message], 241: Res = execute_auth(kick_session_body(JIDA1, Reason), Config), 242: % Kick an active session 243: ?assertNotEqual(nomatch, binary:match(get_ok_value(Path, Res), <<"kicked">>)), 244: ?assertEqual(JIDA1, get_ok_value([data, session, kickUser, jid], Res)), 245: % Try to kick an offline session 246: Res2 = execute_auth(kick_session_body(JIDA1, Reason), Config), 247: ?assertNotEqual(nomatch, binary:match(get_err_msg(Res2), <<"No active session">>)), 248: % Try to kick a session with JID without a resource 249: Res3 = execute_auth(kick_session_body(escalus_client:short_jid(Alice2), Reason), Config), 250: ?assertNotEqual(nomatch, binary:match(get_err_msg(Res3), <<"No active session">>)), 251: % Kick another active session 252: Res4 = execute_auth(kick_session_body(JIDA2, Reason), Config), 253: ?assertNotEqual(nomatch, binary:match(get_ok_value(Path, Res4), <<"kicked">>)). 254: 255: admin_set_presence(Config) -> 256: escalus:fresh_story_with_config(Config, [{alice, 1}], fun admin_set_presence_story/2). 257: 258: admin_set_presence_story(Config, Alice) -> 259: JID = escalus_client:full_jid(Alice), 260: ShortJID = escalus_client:short_jid(Alice), 261: Type = <<"AVAILABLE">>, 262: Show = <<"ONLINE">>, 263: Status = <<"Be right back">>, 264: Priority = 1, 265: % Send short JID 266: Res = execute_auth(set_presence_body(ShortJID, Type, Show, Status, Priority), Config), 267: ?assertNotEqual(nomatch, binary:match(get_err_msg(Res), <<"resource is empty">>)), 268: % Send full JID 269: Path = [data, session, setPresence, message], 270: Res2 = execute_auth(set_presence_body(JID, Type, Show, Status, Priority), Config), 271: ?assertNotEqual(nomatch, binary:match(get_ok_value(Path, Res2), <<"set successfully">>)), 272: Presence = escalus:wait_for_stanza(Alice), 273: ?assertNot(escalus_pred:is_presence_with_show(<<"online">>, Presence)), 274: escalus:assert(is_presence_with_type, [<<"available">>], Presence), 275: escalus:assert(is_presence_with_status, [Status], Presence), 276: escalus:assert(is_presence_with_priority, [<<"1">>], Presence). 277: 278: admin_set_presence_away(Config) -> 279: escalus:fresh_story_with_config(Config, [{alice, 1}], fun admin_set_presence_away_story/2). 280: 281: admin_set_presence_away_story(Config, Alice) -> 282: JID = escalus_client:full_jid(Alice), 283: Type = <<"AVAILABLE">>, 284: Show = <<"AWAY">>, 285: Path = [data, session, setPresence, message], 286: Res2 = execute_auth(set_presence_body(JID, Type, Show, null, null), Config), 287: ?assertNotEqual(nomatch, binary:match(get_ok_value(Path, Res2), <<"set successfully">>)), 288: Presence = escalus:wait_for_stanza(Alice), 289: escalus:assert(is_presence_with_type, [<<"available">>], Presence), 290: escalus:assert(is_presence_with_show, [<<"away">>], Presence). 291: 292: admin_set_presence_unavailable(Config) -> 293: escalus:fresh_story_with_config(Config, [{alice, 2}], fun admin_set_presence_unavailable_story/3). 294: 295: admin_set_presence_unavailable_story(Config, Alice, Alice2) -> 296: JID = escalus_client:full_jid(Alice), 297: Type = <<"UNAVAILABLE">>, 298: Status = <<"I'm sleeping">>, 299: Path = [data, session, setPresence, message], 300: Res2 = execute_auth(set_presence_body(JID, Type, null, Status, null), Config), 301: ?assertNotEqual(nomatch, binary:match(get_ok_value(Path, Res2), <<"set successfully">>)), 302: Presence = escalus:wait_for_stanza(Alice2), 303: escalus:assert(is_presence_with_type, [<<"unavailable">>], Presence), 304: escalus:assert(is_presence_with_status, [Status], Presence). 305: 306: %% Helpers 307: 308: -spec users_match([jid:literal_jid()], [#{user := jid:literal_jid()}]) -> boolean(). 309: users_match(Expected, Actual) -> 310: lists:all(fun(#{<<"user">> := JID}) -> lists:member(JID, Expected) end, Actual). 311: 312: %% Request bodies 313: 314: list_sessions_body(Domain) -> 315: Query = <<"query Q1($domain: String) 316: { session { listSessions(domain: $domain) { user } } }">>, 317: OpName = <<"Q1">>, 318: Vars = #{<<"domain">> => Domain}, 319: #{query => Query, operationName => OpName, variables => Vars}. 320: 321: count_sessions_body(Domain) -> 322: Query = <<"query Q1($domain: String) 323: { session { countSessions(domain: $domain) } }">>, 324: OpName = <<"Q1">>, 325: Vars = #{<<"domain">> => Domain}, 326: #{query => Query, operationName => OpName, variables => Vars}. 327: 328: list_user_sessions_body(JID) -> 329: Query = <<"query Q1($user: JID!) 330: { session { listUserSessions(user: $user) { user } } }">>, 331: OpName = <<"Q1">>, 332: Vars = #{<<"user">> => JID}, 333: #{query => Query, operationName => OpName, variables => Vars}. 334: 335: count_user_resources_body(JID) -> 336: Query = <<"query Q1($user: JID!) 337: { session { countUserResources(user: $user) } }">>, 338: OpName = <<"Q1">>, 339: Vars = #{<<"user">> => JID}, 340: #{query => Query, operationName => OpName, variables => Vars}. 341: 342: get_user_resource_body(JID, Number) -> 343: Query = <<"query Q1($user: JID!, $number: Int!) 344: { session { getUserResource(user: $user, number: $number) } }">>, 345: OpName = <<"Q1">>, 346: Vars = #{<<"user">> => JID, <<"number">> => Number}, 347: #{query => Query, operationName => OpName, variables => Vars}. 348: 349: list_users_with_status_body(Domain, Status) -> 350: Query = <<"query Q1($domain: String, $status: String!) 351: { session { listUsersWithStatus(domain: $domain, status: $status) { user } } }">>, 352: OpName = <<"Q1">>, 353: Vars = #{<<"domain">> => Domain, <<"status">> => Status}, 354: #{query => Query, operationName => OpName, variables => Vars}. 355: 356: count_users_with_status_body(Domain, Status) -> 357: Query = <<"query Q1($domain: String, $status: String!) 358: { session { countUsersWithStatus(domain: $domain, status: $status) } }">>, 359: OpName = <<"Q1">>, 360: Vars = #{<<"domain">> => Domain, <<"status">> => Status}, 361: #{query => Query, operationName => OpName, variables => Vars}. 362: 363: kick_session_body(JID, Reason) -> 364: Query = <<"mutation M1($user: JID!, $reason: String!) 365: { session { kickUser(user: $user, reason: $reason) { jid message }} }">>, 366: OpName = <<"M1">>, 367: Vars = #{<<"user">> => JID, <<"reason">> => Reason}, 368: #{query => Query, operationName => OpName, variables => Vars}. 369: 370: set_presence_body(JID, Type, Show, Status, Priority) -> 371: Query = <<"mutation M1($user: JID!, $type: PresenceType!, $show: PresenceShow, $status: String, $priority: Int) 372: { session { setPresence(user: $user, type: $type, show: $show, status: $status, priority: $priority) 373: { message jid } } }">>, 374: OpName = <<"M1">>, 375: Vars = #{<<"user">> => JID, <<"type">> => Type, <<"show">> => Show, <<"status">> => Status, <<"priority">> => Priority}, 376: #{query => Query, operationName => OpName, variables => Vars}.