diff --git a/pyproject.toml b/pyproject.toml index 17ac9ecf..a6e022b4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,7 +58,7 @@ dependencies = [ ] [project.optional-dependencies] test = [ - "black", + "black>=22,<23", "mock", "mypy", "pytest>=7.0", diff --git a/tabcmd/__main__.py b/tabcmd/__main__.py index 50eace8f..5bd5fb1f 100644 --- a/tabcmd/__main__.py +++ b/tabcmd/__main__.py @@ -3,9 +3,8 @@ try: from tabcmd.tabcmd import main except ImportError as e: - print(sys.stderr, e) - print(sys.stderr, "Tabcmd needs to be run as a module, it cannot be run as a script") - print(sys.stderr, "Try running python -m tabcmd") + print("Exception thrown running program: `{}`, `{}`".format(e, e.__context__), file=sys.stderr) + print("[Possible cause: Tabcmd needs to be run as a module, try running `python -m tabcmd`]", file=sys.stderr) sys.exit(1) if __name__ == "__main__": diff --git a/tabcmd/commands/auth/session.py b/tabcmd/commands/auth/session.py index 1dcf34a7..87101791 100644 --- a/tabcmd/commands/auth/session.py +++ b/tabcmd/commands/auth/session.py @@ -43,7 +43,7 @@ def __init__(self): self.no_prompt = False self.certificate = None self.no_certcheck = False - self.no_proxy = True + self.no_proxy = False self.proxy = None self.timeout = None @@ -73,10 +73,10 @@ def _update_session_data(self, args): self.token_name = args.token_name or self.token_name self.token_value = args.token_value or self.token_value - self.no_prompt = args.no_prompt or self.no_prompt + self.no_prompt = args.no_prompt # have to set this on every call? self.certificate = args.certificate or self.certificate - self.no_certcheck = args.no_certcheck or self.no_certcheck - self.no_proxy = args.no_proxy or self.no_proxy + self.no_certcheck = args.no_certcheck # have to set this on every call? + self.no_proxy = args.no_proxy # have to set this on every call? self.proxy = args.proxy or self.proxy self.timeout = self.timeout_as_integer(self.logger, args.timeout, self.timeout) @@ -150,28 +150,45 @@ def _create_new_token_credential(self): else: Errors.exit_with_error(self.logger, _("session.errors.missing_arguments").format("token name")) - def _set_connection_options(self) -> TSC.Server: + def _open_connection_with_opts(self) -> TSC.Server: self.logger.debug("Setting up request options") - # args still to be handled here: - # proxy, --no-proxy, - # cert http_options: Dict[str, Any] = {"headers": {"User-Agent": "Tabcmd/{}".format(version)}} + if self.no_certcheck: http_options["verify"] = False urllib3.disable_warnings(category=InsecureRequestWarning) + + """ + Do we want to do the same format check as old tabcmd? + For now I think we can trust requests to handle a bad proxy + Pattern pattern = Pattern.compile("([^:]*):([0-9]*)"); + if not matches: + throw new ReportableException(m_i18n.getString("sessionoptions.errors.bad_proxy_format", proxyArg)); + """ if self.proxy: - # do we catch this error? "sessionoptions.errors.bad_proxy_format" - self.logger.debug("Setting proxy: ", self.proxy) + self.logger.debug("Setting http proxy: {}".format(self.proxy)) + proxies = {"http": self.proxy} + http_options["proxies"] = proxies + if self.no_proxy: + # override any proxy that was set + http_options["proxies"] = None + if self.timeout: http_options["timeout"] = self.timeout + + if self.certificate: + http_options["cert"] = self.certificate + try: self.logger.debug(http_options) + # this is the only place we open a connection to the server + # so the request options are all set for the session now tableau_server = TSC.Server(self.server_url, http_options=http_options) except Exception as e: self.logger.debug( - "Connection args: server {}, site {}, proxy {}, cert {}".format( - self.server_url, self.site_name, self.proxy, self.certificate + "Connection args: server {}, site {}, proxy {}/no-proxy {}, cert {}".format( + self.server_url, self.site_name, self.proxy, self.no_proxy, self.certificate ) ) Errors.exit_with_error(self.logger, "Failed to connect to server", e) @@ -196,11 +213,10 @@ def _verify_server_connection_unauthed(self): Errors.exit_with_error(self.logger, exception=e) def _create_new_connection(self) -> TSC.Server: - self.logger.info(_("session.new_session")) self._print_server_info() self.logger.info(_("session.connecting")) try: - self.tableau_server = self._set_connection_options() + self.tableau_server = self._open_connection_with_opts() except Exception as e: Errors.exit_with_error(self.logger, "Failed to connect to server", e) return self.tableau_server @@ -211,22 +227,33 @@ def _read_existing_state(self): def _print_server_info(self): self.logger.info("===== Server: {}".format(self.server_url)) + if self.proxy: + self.logger.info("===== Proxy: {}".format(self.proxy)) if self.username: self.logger.info("===== Username: {}".format(self.username)) + if self.certificate: + self.logger.info("===== Certificate: {}".format(self.certificate)) else: self.logger.info("===== Token Name: {}".format(self.token_name)) site_display_name = self.site_name or "Default Site" self.logger.info(_("dataconnections.classes.tableau_server_site") + ": {}".format(site_display_name)) + # side-effect: sets self.username def _validate_existing_signin(self): - self.logger.info(_("session.continuing_session")) # when do these two messages show up? self.logger.info(_("session.auto_site_login")) try: if self.tableau_server and self.tableau_server.is_signed_in(): - response = self.tableau_server.users.get_by_id(self.user_id) - self.logger.debug(response) - if response.status_code.startswith("200"): - return self.tableau_server + server_user = self.tableau_server.users.get_by_id(self.user_id).name + if not self.username: + self.username = server_user + if not self.username == server_user: + Errors.exit_with_error( + self.logger, + message="Local username `{}` does not match server username `{}`".format( + self.username, server_user + ), + ) + return self.tableau_server except TSC.ServerResponseError as e: self.logger.info(_("publish.errors.unexpected_server_response"), e) except Exception as e: @@ -245,12 +272,14 @@ def _sign_in(self, tableau_auth) -> TSC.Server: self.site_id = self.tableau_server.site_id self.user_id = self.tableau_server.user_id self.auth_token = self.tableau_server._auth_token - if not self.username: - self.username = self.tableau_server.users.get_by_id(self.user_id).name - self.logger.info(_("common.output.succeeded")) + success = self._validate_existing_signin() except Exception as e: Errors.exit_with_error(self.logger, exception=e) - self.logger.debug("Signed into {0}{1} as {2}".format(self.server_url, self.site_name, self.username)) + if success: + self.logger.info(_("common.output.succeeded")) + else: + Errors.exit_with_error(self.logger, message="Sign in failed") + return self.tableau_server def _get_saved_credentials(self): @@ -290,6 +319,7 @@ def create_session(self, args, logger): else: # no login arguments given - look for saved info # maybe we're already signed in! if self.tableau_server: + self.logger.info(_("session.continuing_session")) signed_in_object = self._validate_existing_signin() self.logger.debug(signed_in_object) # or maybe we at least have the credentials saved @@ -297,7 +327,7 @@ def create_session(self, args, logger): credentials = self._get_saved_credentials() if credentials and not signed_in_object: - # logging in, not using an existing session + self.logger.debug("We are not logged in yet but we have credentials to log in with") self.tableau_server = self._create_new_connection() self._verify_server_connection_unauthed() signed_in_object = self._sign_in(credentials) @@ -358,7 +388,12 @@ def _read_from_json(self): try: with open(str(file_path), "r") as file_contents: data = json.load(file_contents) + if data is None or data == {}: + return content = data["tableau_auth"] + if content is None: + return + self._save_data_from_json(content) except json.JSONDecodeError as e: self._wipe_bad_json(e, "Error reading data from session file") except IOError as e: @@ -368,8 +403,11 @@ def _read_from_json(self): except Exception as e: self._wipe_bad_json(e, "Unexpected error reading session details from file") + def _save_data_from_json(self, content): try: auth = content[0] + if auth is None: + self._wipe_bad_json(ValueError(), "Empty session file") self.auth_token = auth["auth_token"] self.server_url = auth["server"] self.site_name = auth["site_name"] diff --git a/tabcmd/execution/parent_parser.py b/tabcmd/execution/parent_parser.py index ba379619..bc81f59c 100644 --- a/tabcmd/execution/parent_parser.py +++ b/tabcmd/execution/parent_parser.py @@ -85,7 +85,7 @@ def parent_parser_with_global_options(): ) proxy_group.add_argument( "--no-proxy", - action="store_false", + action="store_true", # default to false help=_("session.options.no-proxy"), ) diff --git a/tests/commands/test_session.py b/tests/commands/test_session.py index 5f4c8297..dfecc3a3 100644 --- a/tests/commands/test_session.py +++ b/tests/commands/test_session.py @@ -2,10 +2,9 @@ from argparse import Namespace import unittest from unittest import mock -from unittest.mock import patch, mock_open +from unittest.mock import MagicMock from tabcmd.commands.auth.session import Session -import os args_to_mock = Namespace( username=None, @@ -50,9 +49,9 @@ logger = logging.getLogger("tests") -def _set_mocks_for_json_file_saved_username(mock_json_load, auth_token, username): +def _set_mocks_for_json_file_saved_username(mock_json_lib, auth_token, username): mock_auth = vars(mock_data_from_json) - mock_json_load.return_value = {"tableau_auth": [mock_auth]} + mock_json_lib.load.return_value = {"tableau_auth": [mock_auth]} mock_auth["auth_token"] = auth_token mock_auth["username"] = username mock_auth["server"] = fakeserver @@ -60,27 +59,28 @@ def _set_mocks_for_json_file_saved_username(mock_json_load, auth_token, username mock_auth["no_certcheck"] = True -def _set_mocks_for_json_file_exists(mock_path, does_it_exist=True): - os.path = mock_path - mock_path.expanduser.return_value = "" - mock_path.join.return_value = "" - mock_path.exists.return_value = does_it_exist - return mock_path +def _set_mocks_for_json_file_exists(mock_path, mock_json_lib, does_it_exist=True): + mock_json_lib.JSONDecodeError = ValueError + path = mock_path() + path.expanduser.return_value = "" + path.join.return_value = "" + path.exists.return_value = does_it_exist - -def _set_mock_file_content(mock_load, expected_content): - mock_load.return_value = expected_content - return mock_load + mock_auth = vars(mock_data_from_json) + if does_it_exist: + mock_json_lib.load.return_value = [mock_auth] + else: + mock_json_lib.load.return_value = None + return path -@mock.patch("json.dump") -@mock.patch("json.load") +@mock.patch("tabcmd.commands.auth.session.json") @mock.patch("os.path") @mock.patch("builtins.open") class JsonTests(unittest.TestCase): - def test_read_session_from_json(self, mock_open, mock_path, mock_load, mock_dump): - _set_mocks_for_json_file_exists(mock_path) - _set_mocks_for_json_file_saved_username(mock_load, "AUTHTOKEN", "USERNAME") + def test_read_session_from_json(self, mock_open, mock_path, mock_json): + _set_mocks_for_json_file_exists(mock_path, mock_json) + _set_mocks_for_json_file_saved_username(mock_json, "AUTHTOKEN", "USERNAME") test_session = Session() test_session._read_from_json() assert hasattr(test_session.auth_token, "AUTHTOKEN") is False, test_session @@ -88,16 +88,16 @@ def test_read_session_from_json(self, mock_open, mock_path, mock_load, mock_dump assert test_session.username == "USERNAME" assert test_session.server_url == fakeserver, test_session.server_url - def test_save_session_to_json(self, mock_open, mock_path, mock_load, mock_dump): - _set_mocks_for_json_file_exists(mock_path) + def test_save_session_to_json(self, mock_open, mock_path, mock_json): + _set_mocks_for_json_file_exists(mock_path, mock_json) test_session = Session() test_session.username = "USN" test_session.server = "SRVR" test_session._save_session_to_json() - assert mock_dump.was_called() + assert mock_json.dump.was_called() - def clear_session(self, mock_open, mock_path, mock_load, mock_dump): - _set_mocks_for_json_file_exists(mock_path) + def clear_session(self, mock_open, mock_path, mock_json): + _set_mocks_for_json_file_exists(mock_path, mock_json) test_session = Session() test_session.username = "USN" test_session.server = "SRVR" @@ -105,13 +105,13 @@ def clear_session(self, mock_open, mock_path, mock_load, mock_dump): assert test_session.username is None assert test_session.server is None - def test_json_not_present(self, mock_open, mock_path, mock_load, mock_dump): - _set_mocks_for_json_file_exists(mock_path, False) + def test_json_not_present(self, mock_open, mock_path, mock_json): + _set_mocks_for_json_file_exists(mock_path, mock_json, does_it_exist=False) assert mock_open.was_not_called() - def test_json_invalid(self, mock_open, mock_path, mock_load, mock_dump): - _set_mocks_for_json_file_exists(mock_path) - _set_mock_file_content(mock_load, "just a string") + def test_json_invalid(self, mock_open, mock_path, mock_json): + _set_mocks_for_json_file_exists(mock_path, mock_json) + mock_json.load = "just a string" test_session = Session() assert test_session.username is None @@ -215,34 +215,30 @@ def test_dont_show_prompt_if_user_said_no(self): """ -@mock.patch("json.dump") -@mock.patch("json.load") +@mock.patch("tabcmd.commands.auth.session.json") @mock.patch("os.path") @mock.patch("builtins.open") @mock.patch("getpass.getpass") class CreateSessionTests(unittest.TestCase): @mock.patch("tableauserverclient.Server") - def test_create_session_first_time_no_args( - self, mock_tsc, mock_pass, mock_file, mock_path, mock_json_load, mock_json_dump - ): - mock_path = _set_mocks_for_json_file_exists(mock_path, False) - assert mock_path.exists("anything") is False + def test_create_session_first_time_no_args(self, mock_tsc, mock_pass, mock_file, mock_path, mock_json): + _set_mocks_for_json_file_exists(mock_path, mock_json, does_it_exist=False) + test_args = Namespace(**vars(args_to_mock)) - mock_tsc().users.get_by_id.return_value = None new_session = Session() with self.assertRaises(SystemExit): auth = new_session.create_session(test_args, None) @mock.patch("tableauserverclient.Server") - def test_create_session_first_time_with_token_arg( - self, mock_tsc, mock_pass, mock_file, mock_path, mock_json_load, mock_json_dump - ): - mock_path = _set_mocks_for_json_file_exists(mock_path, False) - assert mock_path.exists("anything") is False + def test_create_session_first_time_with_token_arg(self, mock_tsc, mock_pass, mock_file, mock_path, mock_json): + _set_mocks_for_json_file_exists(mock_path, mock_json, does_it_exist=False) + new_session = Session() + new_session.tableau_server = mock_tsc() + _set_mock_signin_validation_succeeds(new_session.tableau_server, "u") + test_args = Namespace(**vars(args_to_mock)) test_args.token_name = "tn" test_args.token_value = "foo" - new_session = Session() auth = new_session.create_session(test_args, None) assert auth is not None, auth assert auth.auth_token is not None, auth.auth_token @@ -251,46 +247,52 @@ def test_create_session_first_time_with_token_arg( assert new_session.token_name == "tn", new_session @mock.patch("tableauserverclient.Server") - def test_create_session_first_time_with_password_arg( - self, mock_tsc, mock_pass, mock_file, mock_path, mock_json_load, mock_json_dump - ): - mock_path = _set_mocks_for_json_file_exists(mock_path, False) + def test_create_session_first_time_with_password_arg(self, mock_tsc, mock_pass, mock_file, mock_path, mock_json): + name = "uuuu" + new_session = Session() + new_session.tableau_server = mock_tsc() + _set_mocks_for_json_file_exists(mock_path, mock_json, does_it_exist=False) + _set_mock_signin_validation_succeeds(new_session.tableau_server, name) + test_args = Namespace(**vars(args_to_mock)) - test_args.username = "uuuu" + test_args.username = name test_args.password = "pppp" - new_session = Session() auth = new_session.create_session(test_args, None) assert auth is not None, auth assert auth.auth_token is not None, auth.auth_token - assert new_session.username == "uuuu", new_session + assert new_session.username == name, new_session assert mock_tsc.has_been_called() @mock.patch("tableauserverclient.Server") def test_create_session_first_time_with_password_file_as_password( - self, mock_tsc, mock_pass, mock_file, mock_path, mock_json_load, mock_json_dump + self, mock_tsc, mock_pass, mock_file, mock_path, mock_json ): - mock_path = _set_mocks_for_json_file_exists(mock_path, False) + username = "uuuu" + _set_mocks_for_json_file_exists(mock_path, mock_json, does_it_exist=False) + new_session = Session() + _set_mock_signin_validation_succeeds(mock_tsc(), username) test_args = Namespace(**vars(args_to_mock)) - test_args.username = "uuuu" + test_args.username = username # filename = os.path.join(os.path.dirname(__file__),"test_credential_file.txt") # test_args.password_file = os.getcwd()+"/test_credential_file.txt" test_args.password_file = "filename" with mock.patch("builtins.open", mock.mock_open(read_data="my_password")): - new_session = Session() auth = new_session.create_session(test_args, None) assert auth is not None, auth assert auth.auth_token is not None, auth.auth_token - assert new_session.username == "uuuu", new_session + assert new_session.username == username, new_session assert new_session.password_file == "filename", new_session assert mock_tsc.has_been_called() @mock.patch("tableauserverclient.Server") def test_create_session_first_time_with_password_file_as_token( - self, mock_tsc, mock_pass, mock_file, mock_path, mock_json_load, mock_json_dump + self, mock_tsc, mock_pass, mock_file, mock_path, mock_json ): - mock_path = _set_mocks_for_json_file_exists(mock_path, False) + _set_mocks_for_json_file_exists(mock_path, mock_json, does_it_exist=False) + server = mock_tsc() + _set_mock_signin_validation_succeeds(server, "testing") test_args = Namespace(**vars(args_to_mock)) test_args.token_name = "mytoken" test_args.password_file = "filename" @@ -304,9 +306,9 @@ def test_create_session_first_time_with_password_file_as_token( assert mock_tsc.has_been_called() @mock.patch("tableauserverclient.Server") - def test_load_saved_session_data(self, mock_tsc, mock_pass, mock_file, mock_path, mock_json_load, mock_json_dump): - _set_mocks_for_json_file_exists(mock_path, True) - _set_mocks_for_json_file_saved_username(mock_json_load, "auth_token", "username") + def test_load_saved_session_data(self, mock_tsc, mock_pass, mock_file, mock_path, mock_json): + _set_mocks_for_json_file_exists(mock_path, mock_json) + _set_mocks_for_json_file_saved_username(mock_json, "auth_token", "username") test_args = Namespace(**vars(args_to_mock)) new_session = Session() new_session._read_existing_state() @@ -317,11 +319,9 @@ def test_load_saved_session_data(self, mock_tsc, mock_pass, mock_file, mock_path assert mock_tsc.has_been_called() @mock.patch("tableauserverclient.Server") - def test_create_session_with_active_session_saved( - self, mock_tsc, mock_pass, mock_file, mock_path, mock_json_load, mock_json_dump - ): - _set_mocks_for_json_file_exists(mock_path, True) - _set_mocks_for_json_file_saved_username(mock_json_load, "auth_token", None) + def test_create_session_with_active_session_saved(self, mock_tsc, mock_pass, mock_file, mock_path, mock_json): + _set_mocks_for_json_file_exists(mock_path, mock_json) + _set_mocks_for_json_file_saved_username(mock_json, "auth_token", None) test_args = Namespace(**vars(args_to_mock)) test_args.token_value = "tn" test_args.token_name = "tnnnn" @@ -335,12 +335,14 @@ def test_create_session_with_active_session_saved( @mock.patch("tableauserverclient.Server") def test_create_session_with_saved_expired_username_session( - self, mock_tsc, mock_pass, mock_file, mock_path, mock_json_load, mock_json_dump + self, mock_tsc, mock_pass, mock_file, mock_path, mock_json ): - _set_mocks_for_json_file_saved_username(mock_json_load, "auth_token", "username") - _set_mocks_for_json_file_exists(mock_path, True) - tsc_under_test = CreateSessionTests._set_mock_tsc_not_signed_in(mock_tsc) - CreateSessionTests._set_mock_tsc_sign_in_succeeds(tsc_under_test) + test_username = "monster" + server = mock_tsc() + _set_mocks_for_json_file_exists(mock_path, mock_json) + _set_mocks_for_json_file_saved_username(mock_json, "auth_token", test_username) + _set_mock_tsc_sign_in_succeeds(server) + _set_mock_signin_validation_succeeds(server, test_username) test_args = Namespace(**vars(args_to_mock)) mock_pass.getpass.return_value = "success" test_args.password = "eqweqwe" @@ -350,24 +352,31 @@ def test_create_session_with_saved_expired_username_session( assert auth is not None, auth assert auth.auth_token is not None, auth.auth_token assert auth.auth_token == "cookiieeeee" - assert new_session.username == "username", new_session + assert new_session.username == test_username, new_session assert mock_tsc.use_server_version.has_been_called() - @staticmethod - def _set_mock_tsc_not_signed_in(mock_tsc): - tsc_in_test = mock.MagicMock(name="manually mocking tsc") - mock_tsc.return_value = tsc_in_test - tsc_in_test.is_signed_in.return_value = False # CreateSessionTests.return_False - tsc_in_test.server_info.get.return_value = Exception - return tsc_in_test - @staticmethod - def _set_mock_tsc_sign_in_succeeds(mock_tsc): - tscauth_mock = mock.MagicMock(name="tsc.auth") - mock_tsc.auth = tscauth_mock - mock_tsc.auth_token = "cookiieeeee" - mock_tsc.site_id = "1" - mock_tsc.user_id = "0" +def _set_mock_tsc_not_signed_in(mock_tsc): + tsc_in_test = mock.MagicMock(name="manually mocking tsc") + tsc_in_test.is_signed_in.return_value = False # CreateSessionTests.return_False + tsc_in_test.server_info.get.return_value = Exception + return tsc_in_test + + +def _set_mock_tsc_sign_in_succeeds(mock_tsc): + tscauth_mock = mock.MagicMock(name="tsc.auth") + mock_tsc.auth = tscauth_mock + mock_tsc.auth_token = "cookiieeeee" + mock_tsc.site_id = "1" + mock_tsc.user_id = "0" + + +def _set_mock_signin_validation_succeeds(mock_tsc, username): + mock_u_factory = MagicMock("user") + mock_u = mock_u_factory() + mock_tsc.users.get_by_id.return_value = mock_u + mock_u.name = username + mock_tsc.is_signed_in.return_value = True class TimeoutArgTests(unittest.TestCase): @@ -392,7 +401,7 @@ class TimeoutIntegrationTest(unittest.TestCase): def test_connection_times_out(self): test_args = Namespace(**vars(args_to_mock)) new_session = Session() - test_args.timeout = 10 + test_args.timeout = 2 test_args.username = "u" test_args.password = "p" @@ -403,36 +412,51 @@ def test_connection_times_out(self): # should test connection doesn't time out? -@mock.patch("tableauserverclient.Server") class ConnectionOptionsTest(unittest.TestCase): - def test_certcheck_on(self, mock_tsc): - mock_tsc.add_http_options = mock.MagicMock() - mock_session = mock.MagicMock() + def test_user_agent(self): + mock_session = Session() + mock_session.server_url = "fakehost" + connection = mock_session._open_connection_with_opts() + assert connection._http_options["headers"]["User-Agent"].startswith("Tabcmd/") + + def test_no_certcheck(self): + mock_session = Session() + mock_session.server_url = "fakehost" mock_session.no_certcheck = True mock_session.site_id = "s" mock_session.user_id = "u" - server = "anything" - mock_session._set_connection_options() - assert mock_tsc.add_http_options.has_been_called() + connection = mock_session._open_connection_with_opts() + assert connection._http_options["verify"] == False - def test_certcheck_off(self, mock_tsc): - mock_session = mock.MagicMock() - server = "anything" + def test_cert(self): + mock_session = Session() + mock_session.server_url = "fakehost" mock_session.site_id = "s" mock_session.user_id = "u" - mock_session._set_connection_options() - mock_tsc.add_http_options.assert_not_called() + mock_session.certificate = "my-cert-info" + connection = mock_session._open_connection_with_opts() + assert connection._http_options["cert"] == mock_session.certificate - """ - def test_cert(self, mock_tsc): - assert False, 'feature not implemented' + def test_proxy_stuff(self): + mock_session = Session() + mock_session.server_url = "fakehost" + mock_session.site_id = "s" + mock_session.user_id = "u" + mock_session.proxy = "proxy:port" + connection = mock_session._open_connection_with_opts() + assert connection._http_options["proxies"] == {"http": mock_session.proxy} - def test_proxy_stuff(self, mock_tsc): - assert False, 'feature not implemented' + def test_timeout(self): + mock_session = Session() + mock_session.server_url = "fakehost" + mock_session.site_id = "s" + mock_session.user_id = "u" + mock_session.timeout = 10 + connection = mock_session._open_connection_with_opts() + assert connection._http_options["timeout"] == 10 - def test_timeout(self, mock_tsc): - assert False, 'feature not implemented' +""" class CookieTests(unittest.TestCase): def test_no_file_if_no_cookie(self):