def test_get_owner_info_multiline_data(local_gcp, write_gridmap, auth_client): write_gridmap("\n".join([ f'"/C=US/O=Globus Consortium/OU=Globus Connect User/CN=sirosen{x}" sirosen{x}' # noqa: E501 for x in ["", "2", "3"] ]) + "\n") info = local_gcp.get_owner_info() assert isinstance(info, globus_sdk.GlobusConnectPersonalOwnerInfo) assert info.username == "*****@*****.**" register_api_route( "auth", "/v2/api/identities", json={ "identities": [{ "email": "*****@*****.**", "id": "ae332d86-d274-11e5-b885-b31714a110e9", "identity_provider": "41143743-f3c8-4d60-bbdb-eeecaba85bd9", "identity_type": "login", "name": "Stephen Rosen", "organization": "Globus Team", "status": "used", "username": "******", }] }, ) data = local_gcp.get_owner_info(auth_client) assert isinstance(data, dict) assert data["id"] == "ae332d86-d274-11e5-b885-b31714a110e9"
def test_endpoint_search_noresults(client): register_api_route("transfer", "/endpoint_search", body=EMPTY_SEARCH_RESULT) res = client.endpoint_search("search query!") assert res.data == []
def test_get_owner_info_b32_mode(local_gcp, write_gridmap, auth_client): write_gridmap( f'"/C=US/O=Globus Consortium/OU=Globus Connect User/CN={BASE32_ID}" sirosen\n' ) info = local_gcp.get_owner_info() assert isinstance(info, globus_sdk.GlobusConnectPersonalOwnerInfo) assert info.username is None assert info.id == "ae341a98-d274-11e5-b888-dbae3a8ba545" register_api_route( "auth", "/v2/api/identities", json={ "identities": [{ "email": "*****@*****.**", "id": "ae341a98-d274-11e5-b888-dbae3a8ba545", "identity_provider": "927d7238-f917-4eb2-9ace-c523fa9ba34e", "identity_type": "login", "name": "Stephen Rosen", "organization": "Globus Team", "status": "used", "username": "******", }] }, ) data = local_gcp.get_owner_info(auth_client) assert isinstance(data, dict) assert data["id"] == "ae341a98-d274-11e5-b888-dbae3a8ba545"
def test_http_methods(method, allows_body, base_client): """ BaseClient.{get, delete, post, put, patch} on a path does "the right thing" Sends a text body or JSON body as requested Raises a GlobusAPIError if the response is not a 200 NOTE: tests sending request bodies even on GET (which *shouldn't* have bodies but *may* have them in reality). """ methodname = method.upper() resolved_method = getattr(base_client, method) path = "/madeuppath/objectname" register_api_route("transfer", path, method=methodname, json={"x": "y"}) # request with no body res = resolved_method(path) req = get_last_request() assert req.method == methodname assert req.body is None assert "x" in res assert res["x"] == "y" if allows_body: jsonbody = {"foo": "bar"} res = resolved_method(path, data=jsonbody) req = get_last_request() assert req.method == methodname assert req.body == json.dumps(jsonbody).encode("utf-8") assert "x" in res assert res["x"] == "y" res = resolved_method(path, data="abc") req = get_last_request() assert req.method == methodname assert req.body == "abc" assert "x" in res assert res["x"] == "y" # send "bad" request for status in ERROR_STATUS_CODES: register_api_route( "transfer", "/madeuppath/objectname", method=methodname, status=status, json={"x": "y", "code": "ErrorCode", "message": "foo"}, replace=True, ) with pytest.raises(globus_sdk.GlobusAPIError) as excinfo: resolved_method("/madeuppath/objectname") assert excinfo.value.http_status == status assert excinfo.value.raw_json["x"] == "y" assert excinfo.value.code == "ErrorCode" assert excinfo.value.message == "foo"
def test_identity_map_get_with_default(client): register_api_route("auth", "/v2/api/identities", body=IDENTITIES_SINGLE_RESPONSE) magic = object() # sentinel value idmap = globus_sdk.IdentityMap(client) # a name which doesn't come back, if looked up with `get()` should return the # default assert idmap.get("*****@*****.**", magic) is magic
def test_endpoint_search_reduced(client): register_api_route( "transfer", "/endpoint_search", body=json.dumps(SINGLE_PAGE_SEARCH_RESULT) ) # result has 100 items -- num_results caps it even if the API # returns more res = client.endpoint_search("search query!", num_results=10) assert len(list(res)) == 10
def test_endpoint_search_reduced(client): register_api_route("transfer", "/endpoint_search", body=json.dumps(SINGLE_PAGE_SEARCH_RESULT)) # result has 100 items -- num_results caps it even if the API # returns more res = client.endpoint_search("search query!", num_results=10) assert len(list(res)) == 10
def test_identity_map_add_after_lookup(client): register_api_route("auth", "/v2/api/identities", body=IDENTITIES_SINGLE_RESPONSE) idmap = globus_sdk.IdentityMap(client) x = idmap["*****@*****.**"]["id"] # this is the key: adding it will indicate that we've already seen this ID, perhaps # "unintuitively", and that's part of the value of `add()` returning a boolean value assert idmap.add(x) is False assert idmap[x] == idmap["*****@*****.**"]
def test_endpoint_search_one_page(client): register_api_route("transfer", "/endpoint_search", json=SINGLE_PAGE_SEARCH_RESULT) # without calling the paginated version, we only get one page res = client.endpoint_search("search query!") assert len(list(res)) == 100 assert res["DATA_TYPE"] == "endpoint_list" for res_obj in res: assert res_obj["DATA_TYPE"] == "endpoint"
def test_paginated_method_multipage(client, method, api_methodname, paged_data): if api_methodname == "endpoint_search": route = "/endpoint_search" client_method = client.endpoint_search paginated_method = client.paginated.endpoint_search call_args = ("search_query", ) wrapper_type = "endpoint_list" data_type = "endpoint" elif api_methodname == "task_list": route = "/task_list" client_method = client.task_list paginated_method = client.paginated.task_list call_args = () wrapper_type = "task_list" data_type = "task" else: raise NotImplementedError # add each page for page in paged_data: register_api_route("transfer", route, json=page) # unpaginated, we'll only get one page res = list(client_method(*call_args)) assert len(res) == 100 # reset and reapply responses responses.reset() for page in paged_data: register_api_route("transfer", route, json=page) # setup the paginator and either point at `pages()` or directly at the paginator's # `__iter__` paginator = paginated_method(*call_args) if method == "pages": iterator = paginator.pages() elif method == "__iter__": iterator = paginator else: raise NotImplementedError # paginated calls gets all pages count_pages = 0 count_objects = 0 for page in iterator: count_pages += 1 assert page["DATA_TYPE"] == wrapper_type for res_obj in page: count_objects += 1 assert res_obj["DATA_TYPE"] == data_type assert count_pages == len(paged_data) assert count_objects == sum(len(x["DATA"]) for x in paged_data)
def test_task_skipped_errors_pagination(client, paging_variant): task_id = str(uuid.uuid1()) # add each page (10 pages) for page_number in range(10): page_data = [] for item_number in range(100): page_data.append({ "DATA_TYPE": "skipped_error", "checksum_algorithm": None, "destination_path": f"/~/{page_number}-{item_number}.txt", "error_code": "PERMISSION_DENIED", "error_details": "Error bad stuff happened", "error_time": "2022-02-18T19:06:05+00:00", "external_checksum": None, "is_delete_destination_extra": False, "is_directory": False, "is_symlink": False, "source_path": f"/~/{page_number}-{item_number}.txt", }) register_api_route( "transfer", f"/task/{task_id}/skipped_errors", json={ "DATA_TYPE": "skipped_errors", "next_marker": f"mark{page_number}" if page_number < 9 else None, "DATA": page_data, }, ) # paginator items() call gets an iterator of individual page items if paging_variant == "attr": paginator = client.paginated.task_skipped_errors(task_id) elif paging_variant == "wrap": paginator = Paginator.wrap(client.task_skipped_errors)(task_id) else: raise NotImplementedError count = 0 for item in paginator.items(): count += 1 assert item["DATA_TYPE"] == "skipped_error" assert count == 1000
def test_decode_id_token(token_response): register_api_route( "auth", "/.well-known/openid-configuration", method="GET", body=json.dumps(OIDC_CONFIG), ) register_api_route("auth", "/jwk.json", method="GET", body=json.dumps(JWK)) decoded = token_response.decode_id_token(jwt_params={"verify_exp": False}) assert decoded["preferred_username"] == "*****@*****.**"
def test_get_owner_info_no_auth_data(local_gcp, write_gridmap, auth_client): write_gridmap( '"/C=US/O=Globus Consortium/OU=Globus Connect User/CN=sirosen" sirosen\n' ) info = local_gcp.get_owner_info() assert isinstance(info, globus_sdk.GlobusConnectPersonalOwnerInfo) assert info.username == "*****@*****.**" register_api_route("auth", "/v2/api/identities", json={"identities": []}) data = local_gcp.get_owner_info(auth_client) assert data is None
def test_identity_map_keyerror(client): register_api_route("auth", "/v2/api/identities", body=IDENTITIES_SINGLE_RESPONSE) idmap = globus_sdk.IdentityMap(client) # a name which doesn't come back, indicating that it was not found, will KeyError with pytest.raises(KeyError): idmap["*****@*****.**"] last_req = httpretty.last_request() assert last_req.querystring["usernames"] == ["*****@*****.**"] assert last_req.querystring["provision"] == ["false"]
def test_shared_endpoint_list_non_paginated(client): # add each page for page in SHARED_ENDPOINT_RESULTS: register_api_route("transfer", "/endpoint/endpoint_id/shared_endpoint_list", json=page) # without calling the paginated version, we only get one page res = client.get_shared_endpoint_list("endpoint_id") assert len(list(res)) == 1000 for item in res: assert "id" in item
def test_endpoint_search_multipage_iter_items(client): # add each page for page in MULTIPAGE_SEARCH_RESULTS: register_api_route("transfer", "/endpoint_search", json=page) # paginator items() call gets an iterator of individual page items paginator = client.paginated.endpoint_search("search_query") count_objects = 0 for item in paginator.items(): count_objects += 1 assert item["DATA_TYPE"] == "endpoint" assert count_objects == sum( len(x["DATA"]) for x in MULTIPAGE_SEARCH_RESULTS)
def test_identity_map_del(client): register_api_route("auth", "/v2/api/identities", body=IDENTITIES_SINGLE_RESPONSE) idmap = globus_sdk.IdentityMap(client) identity_id = idmap["*****@*****.**"]["id"] del idmap[identity_id] assert idmap.get("*****@*****.**")["id"] == identity_id # we've only made one request so far assert len(responses.calls) == 1 # but a lookup by ID after a del is going to trigger another request because we've # invalidated the cached ID data and are asking the IDMap to look it up again assert idmap.get(identity_id)["username"] == "*****@*****.**" assert len(responses.calls) == 2
def test_transfer_submit_failure(client, transfer_data): register_api_route( "transfer", "/transfer", method="POST", status=400, body=TRANSFER_SUBMISSION_NODATA_ERROR, ) with pytest.raises(globus_sdk.TransferAPIError) as excinfo: client.submit_transfer(transfer_data()) assert excinfo.value.http_status == 400 assert excinfo.value.request_id == "oUAA6Sq2P" assert excinfo.value.code == "ClientError.BadRequest.NoTransferItems"
def test_get_identities_unauthorized(client): register_api_route( "auth", "/v2/api/[email protected]", body=UNAUTHORIZED_RESPONSE_BODY, status=401, ) with pytest.raises(globus_sdk.AuthAPIError) as excinfo: client.get_identities(usernames="*****@*****.**") err = excinfo.value assert err.code == "UNAUTHORIZED" assert err.raw_text == UNAUTHORIZED_RESPONSE_BODY assert err.raw_json == json.loads(UNAUTHORIZED_RESPONSE_BODY)
def test_delete_submit_success(client, delete_data): register_api_route( "transfer", "/delete", method="POST", body=DELETE_SUBMISSION_SUCCESS ) ddata = delete_data(label="mytask", deadline="2018-06-01", custom_param="foo") assert ddata["custom_param"] == "foo" ddata.add_item("/path/to/foo") res = client.submit_delete(ddata) assert res assert res["submission_id"] == "foosubmitid" assert res["task_id"] == "b5370336-ad79-11e8-823c-0a3b7ca8ce66"
def test_identity_map(client): register_api_route("auth", "/v2/api/identities", body=IDENTITIES_SINGLE_RESPONSE) idmap = globus_sdk.IdentityMap(client, ["*****@*****.**"]) assert idmap["*****@*****.**"]["organization"] == "Globus Team" # lookup by ID also works assert (idmap["ae341a98-d274-11e5-b888-dbae3a8ba545"]["organization"] == "Globus Team") # the last (only) API call was the one by username last_req = httpretty.last_request() assert "ids" not in last_req.querystring assert last_req.querystring["usernames"] == ["*****@*****.**"] assert last_req.querystring["provision"] == ["false"]
def test_endpoint_search_multipage(client): pages = [json.dumps(x) for x in MULTIPAGE_SEARCH_RESULTS] responses = [httpretty.Response(p) for p in pages] register_api_route("transfer", "/endpoint_search", responses=responses) # without cranking up num_results, we'll only get 25 res = list(client.endpoint_search("search_query")) assert len(res) == 25 # reapply (resets responses) register_api_route("transfer", "/endpoint_search", responses=responses) # num_results=None -> no limit res = list(client.endpoint_search("search_query", num_results=None)) assert res[-1]["display_name"] == "SDK Test Stub 299" assert len(res) == sum(len(x["DATA"]) for x in MULTIPAGE_SEARCH_RESULTS)
def test_identity_map_multiple(client): register_api_route("auth", ("/v2/api/identities"), body=IDENTITIES_MULTIPLE_RESPONSE) idmap = globus_sdk.IdentityMap(client, ["*****@*****.**", "*****@*****.**"]) assert idmap["*****@*****.**"]["organization"] == "Globus Team" assert idmap["*****@*****.**"]["organization"] is None last_req = get_last_request() # order doesn't matter, but it should be just these two # if IdentityMap doesn't deduplicate correctly, it could send # `[email protected],[email protected],[email protected]` on the first lookup assert last_req.params["usernames"] in [ "[email protected],[email protected]", "[email protected],[email protected]", ] assert last_req.params["provision"] == "false"
def test_delete_submit_success(client, delete_data): register_api_route( "transfer", "/delete", method="POST", body=DELETE_SUBMISSION_SUCCESS ) ddata = delete_data( label="mytask", deadline="2018-06-01", additional_fields={"custom_param": "foo"} ) assert ddata["custom_param"] == "foo" ddata.add_item("/path/to/foo") res = client.submit_delete(ddata) assert res assert res["submission_id"] == "foosubmitid" assert res["task_id"] == "b5370336-ad79-11e8-823c-0a3b7ca8ce66"
def test_endpoint_search_multipage(client): # add each page for page in MULTIPAGE_SEARCH_RESULTS: register_api_route("transfer", "/endpoint_search", json=page) # without cranking up num_results, we'll only get 25 res = list(client.endpoint_search("search_query")) assert len(res) == 25 # reset and reapply responses responses.reset() for page in MULTIPAGE_SEARCH_RESULTS: register_api_route("transfer", "/endpoint_search", json=page) # num_results=None -> no limit res = list(client.endpoint_search("search_query", num_results=None)) assert res[-1]["display_name"] == "SDK Test Stub 299" assert len(res) == sum(len(x["DATA"]) for x in MULTIPAGE_SEARCH_RESULTS)
def test_transfer_submit_success(client, transfer_data): register_api_route( "transfer", "/transfer", method="POST", body=TRANSFER_SUBMISSION_SUCCESS ) tdata = transfer_data( label="mytask", sync_level="exists", deadline="2018-06-01", custom_param="foo" ) assert tdata["custom_param"] == "foo" assert tdata["sync_level"] == 0 tdata.add_item(six.b("/path/to/foo"), six.u("/path/to/bar")) tdata.add_symlink_item("linkfoo", "linkbar") res = client.submit_transfer(tdata) assert res assert res["submission_id"] == "foosubmitid" assert res["task_id"] == "f51bdaea-ad78-11e8-823c-0a3b7ca8ce66"
def test_server_list(client): epid = "epid" register_api_route( "transfer", "/endpoint/{}/server_list".format(epid), body=SERVER_LIST_TEXT ) res = client.endpoint_server_list(epid) # it should still be a subclass of GlobusResponse assert isinstance(res, globus_sdk.GlobusResponse) # fetch top-level attrs assert res["DATA_TYPE"] == "endpoint_server_list" assert res["endpoint"] == "go#ep1" # intentionally access twice -- unlike PaginatedResource, this is allowed # and works assert len(list(res)) == 1 assert len(list(res)) == 1 assert list(res)[0]["DATA_TYPE"] == "server"
def test_server_list(client): epid = "epid" register_api_route("transfer", "/endpoint/{}/server_list".format(epid), body=SERVER_LIST_TEXT) res = client.endpoint_server_list(epid) # it should still be a subclass of GlobusResponse assert isinstance(res, globus_sdk.GlobusResponse) # fetch top-level attrs assert res["DATA_TYPE"] == "endpoint_server_list" assert res["endpoint"] == "go#ep1" # intentionally access twice -- unlike PaginatedResource, this is allowed # and works assert len(list(res)) == 1 assert len(list(res)) == 1 assert list(res)[0]["DATA_TYPE"] == "server"
def test_oauth2_exchange_code_for_tokens_native(client): """ Starts a NativeAppFlowManager, Confirms invalid code raises 401 Further testing cannot be done without user login credentials """ register_api_route( "auth", "/v2/oauth2/token", method="POST", body=INVALID_GRANT_RESPONSE_BODY, status=401, ) flow_manager = globus_sdk.services.auth.GlobusNativeAppFlowManager(client) client.current_oauth2_flow_manager = flow_manager with pytest.raises(globus_sdk.AuthAPIError) as excinfo: client.oauth2_exchange_code_for_tokens("invalid_code") assert excinfo.value.http_status == 401 assert excinfo.value.code == "Error"
def test_endpoint_search_one_page(client): register_api_route( "transfer", "/endpoint_search", body=json.dumps(SINGLE_PAGE_SEARCH_RESULT) ) # without cranking up num_results, we'll only get 25 res = client.endpoint_search("search query!") assert len(list(res)) == 25 # paginated results don't have __getitem__ ! # attempting to __getitem__ on a response object defaults to a TypeError with pytest.raises(TypeError): res["DATA_TYPE"] # second fetch is empty assert list(res) == [] # reload res = client.endpoint_search("search query!") for res_obj in res: assert res_obj["DATA_TYPE"] == "endpoint"
def test_transfer_submit_success(client, transfer_data): register_api_route("transfer", "/transfer", method="POST", body=TRANSFER_SUBMISSION_SUCCESS) tdata = transfer_data(label="mytask", sync_level="exists", deadline="2018-06-01", custom_param="foo") assert tdata["custom_param"] == "foo" assert tdata["sync_level"] == 0 tdata.add_item(six.b("/path/to/foo"), six.u("/path/to/bar")) tdata.add_symlink_item("linkfoo", "linkbar") res = client.submit_transfer(tdata) assert res assert res["submission_id"] == "foosubmitid" assert res["task_id"] == "f51bdaea-ad78-11e8-823c-0a3b7ca8ce66"
def test_oauth2_exchange_code_for_tokens_confidential(): """ Starts an AuthorizationCodeFlowManager, Confirms bad code raises 401 Further testing cannot be done without user login credentials """ register_api_route( "auth", "/v2/oauth2/token", method="POST", body=INVALID_GRANT_RESPONSE_BODY, status=401, ) ac = globus_sdk.AuthClient(client_id=CLIENT_ID) flow_manager = globus_sdk.auth.GlobusAuthorizationCodeFlowManager(ac, "uri") ac.current_oauth2_flow_manager = flow_manager with pytest.raises(globus_sdk.AuthAPIError) as excinfo: ac.oauth2_exchange_code_for_tokens("invalid_code") assert excinfo.value.http_status == 401 assert excinfo.value.code == "Error"
def test_shared_endpoint_list_iter_items(client, paging_variant): # add each page for page in SHARED_ENDPOINT_RESULTS: register_api_route("transfer", "/endpoint/endpoint_id/shared_endpoint_list", json=page) # paginator items() call gets an iterator of individual page items if paging_variant == "attr": paginator = client.paginated.get_shared_endpoint_list("endpoint_id") elif paging_variant == "wrap": paginator = Paginator.wrap( client.get_shared_endpoint_list)("endpoint_id") else: raise NotImplementedError count = 0 for item in paginator.items(): count += 1 assert "id" in item assert count == 2100
def test_endpoint_search_one_page(client): register_api_route("transfer", "/endpoint_search", body=json.dumps(SINGLE_PAGE_SEARCH_RESULT)) # without cranking up num_results, we'll only get 25 res = client.endpoint_search("search query!") assert len(list(res)) == 25 # paginated results don't have __getitem__ ! # attempting to __getitem__ on a response object defaults to a TypeError with pytest.raises(TypeError): res["DATA_TYPE"] # second fetch is empty assert list(res) == [] # reload res = client.endpoint_search("search query!") for res_obj in res: assert res_obj["DATA_TYPE"] == "endpoint"
def test_oauth2_exchange_code_for_tokens_confidential(): """ Starts an AuthorizationCodeFlowManager, Confirms bad code raises 401 Further testing cannot be done without user login credentials """ register_api_route( "auth", "/v2/oauth2/token", method="POST", body=INVALID_GRANT_RESPONSE_BODY, status=401, ) ac = globus_sdk.AuthClient(client_id=CLIENT_ID) flow_manager = globus_sdk.auth.GlobusAuthorizationCodeFlowManager( ac, "uri") ac.current_oauth2_flow_manager = flow_manager with pytest.raises(globus_sdk.AuthAPIError) as excinfo: ac.oauth2_exchange_code_for_tokens("invalid_code") assert excinfo.value.http_status == 401 assert excinfo.value.code == "Error"
def identities_multiple_response(): register_api_route( "auth", "/v2/api/[email protected]", body=IDENTITIES_MULTIPLE_RESPONSE, )
def test_http_methods(method, allows_body, base_client): """ BaseClient.{get, delete, post, put, patch} on a path does "the right thing" Sends a text body or JSON body as requested Raises a GlobusAPIError if the response is not a 200 NOTE: tests sending request bodies even on GET (which *shouldn't* have bodies but *may* have them in reality). """ methodname = method.upper() resolved_method = getattr(base_client, method) register_api_route( "transfer", "/madeuppath/objectname", method=methodname, body='{"x": "y"}' ) # client should be able to compose the path itself path = base_client.qjoin_path("madeuppath", "objectname") # request with no body res = resolved_method(path) req = httpretty.last_request() assert req.method == methodname assert req.body == six.b("") assert "x" in res assert res["x"] == "y" if allows_body: jsonbody = {"foo": "bar"} res = resolved_method(path, json_body=jsonbody) req = httpretty.last_request() assert req.method == methodname assert req.body == six.b(json.dumps(jsonbody)) assert "x" in res assert res["x"] == "y" res = resolved_method(path, text_body="abc") req = httpretty.last_request() assert req.method == methodname assert req.body == six.b("abc") assert "x" in res assert res["x"] == "y" # send "bad" request for status in ERROR_STATUS_CODES: register_api_route( "transfer", "/madeuppath/objectname", method=methodname, status=status, body='{"x": "y", "code": "ErrorCode", "message": "foo"}', ) with pytest.raises(globus_sdk.GlobusAPIError) as excinfo: resolved_method("/madeuppath/objectname") assert excinfo.value.http_status == status assert excinfo.value.raw_json["x"] == "y" assert excinfo.value.code == "ErrorCode" assert excinfo.value.message == "foo"
def mock_submission_id(): register_api_route("transfer", "/submission_id", body='{"value": "foosubmitid"}')