def test_incremental_config_dialog(prepare_config_file, monkeypatch, capsys): config, config_text = prepare_config_file new_value = "new_value" setup_input(monkeypatch, [new_value, "Y"]) assert config_dialog(config, attributes=["new_path"], incremental=True) captured = capsys.readouterr() assert ("Current content" not in captured.out ), "incremental should suppress printing file altogether" with pytest.raises(Exception): config_dialog(config, attributes=["update_node"], incremental=True)
def test_single_dialog_new_config(tmp_path, monkeypatch, save_agree): """Tests single run of config_dialog by populating a blank file""" path: Path = tmp_path / "config.toml" # File will be created as a result of the test file_created = save_agree or save_agree is None if save_agree: save_agree = "Y" elif save_agree is None: save_agree = "" else: save_agree = "N" # The monkey patched test cli_input. Should be the same length as the list of params fed to config_dialog later # Note: click provides validations for confirmation prompts, no need to test for garbage cli_input test_input = [ "author name", "https://confluence.local", "page title", save_agree ] setup_input(monkeypatch, test_input) # In this scenario file should be created when and only when the function returned true assert (config_dialog( Path(path), ["author", "auth.url", "pages.page1.page_title"]) == path.exists()) if file_created: assert (path.read_text() == """author = "author name" [auth] url = "https://confluence.local" [pages] [pages.page1] page_title = "page title" """)
def test_single_dialog_existing_file_one_update(user_input, prepare_config_file, monkeypatch): """This test checks that key update in the config works: * User input: * 'n' -> no update * 'y' -> update * '' -> update * If update happens - key is really updated """ config, config_text = prepare_config_file new_value = "new_value" test_input = [ "Y", # overwrite file user_input, # whether the value should be updated new_value, # new value for the parameter "Y", # save the file ] setup_input(monkeypatch, test_input) assert config_dialog(config, ["update_node"]) if user_input in ["Y", ""]: assert (parse(config.read_text())["update_node"] == new_value ), "Existing value was not updated" else: assert config.read_text() == config_text
def test_sensitive_parameter_file_mode(tmp_path, monkeypatch, capsys): """Checks that if there is a parameter - the wizard will create file with 600 permissions and alert the user""" monkeypatch.setattr("getpass.getpass", lambda x: "password") setup_input(monkeypatch, ("password", "Y")) config_path: Path = tmp_path / "config.toml" assert config_dialog( config_path, attributes=[DialogParameter("hidden param", hide_input=True)]) captured = capsys.readouterr() assert "sensitive parameter was passed" in captured.out assert config_path.stat().st_mode == 33152
def test_single_dialog_existing_file_base(mode, user_agrees_to_overwrite_file, prepare_config_file, monkeypatch): config, config_text = prepare_config_file new_value = "new_value" if user_agrees_to_overwrite_file: user_input = ["Y", new_value, "Y"] else: user_input = ["N"] setup_input(monkeypatch, user_input) if user_agrees_to_overwrite_file: node_path = "new_node" assert config_dialog(config, [node_path]) assert (parse(config.read_text())[node_path] == new_value ), "Value was not set to the new one" else: assert config_dialog(config, ["update"]) is None assert config.read_text() == config_text
def test_dialog_converts_filename_to_path(tmp_path, monkeypatch): """Makes sure the dialog accepts both Path and strings for config file""" path_as_path: Path = tmp_path / "config_path.toml" path_as_string: str = str(tmp_path / "config_string.toml") # In this scenario file should be created when and only when the function returned true for tested_type in [path_as_path, path_as_string]: # Taken from previous test test_input = [ "author name", "https://confluence.local", "page title", "Y" ] setup_input(monkeypatch, test_input) assert config_dialog(tested_type, ["author", "auth.url", "pages.page1.page_title"]) assert Path(tested_type).exists()
def test_single_dialog_existing_file_multiple_updates(user_updates_values, prepare_config_file, monkeypatch): """In a scenario where there are more than 1 keys to update - make sure that permutations of user input are handled correctly""" new_value = "new_value" update_attrs = ["update_node", "parent.parent_update_node"] answer_1, answer_2 = user_updates_values config, config_text = prepare_config_file test_input = ["Y"] for user_answer in [answer_1, answer_2]: if user_answer: test_input += ["Y", new_value] else: test_input += ["N"] test_input += ["Y"] setup_input(monkeypatch, test_input) assert config_dialog(config, update_attrs) for user_answer, attr in zip([answer_1, answer_2], update_attrs): # The value should be updated <=> user said yes assert user_answer == (get_attribute_by_path( attribute_path=attr, config=parse(config.read_text())) == new_value)
def create_config( local_only: Optional[bool] = typer.Option( False, "--local-only", show_default=False, help="Create config only in the local folder.", ), home_only: Optional[bool] = typer.Option( False, "--home-only", show_default=False, help="Create config only in the $XDG_CONFIG_HOME.", ), ): """Runs configuration wizard. The wizard guides through setting up values for configuration file.""" import xdg.BaseDirectory from confluence_poster.config_wizard import ( config_dialog, get_filled_attributes_from_file, print_config_with_hidden_attrs, page_add_dialog, ) from functools import partial echo = state.print_function confirm = state.confirm_function prompt = state.prompt_function home_config_location = (Path(xdg.BaseDirectory.xdg_config_home) / "confluence_poster/config.toml") all_params = ( DialogParameter( "author", comment= "If the page was not updated by the username specified here, throw an error." "\nIf this setting is omitted - username from auth section " "is used for checks", required=False, ), # auth: DialogParameter("auth.confluence_url", comment="URL of confluence instance"), DialogParameter("auth.username", comment="Username for authentication in Confluence"), DialogParameter( "auth.password", comment= "Password for authentication. May be supplied through runtime option or " "environment", required=False, hide_input=True, ), DialogParameter( "auth.is_cloud", comment="Whether the confluence instance is a cloud one", type=bool, ), # pages: DialogParameter( "pages.default.page_space", comment= "Space key (e.g. LOC for 'local-dev' space). If defined here - will be used " "if a page does not redefine it", required=False, ), ) + generate_page_dialog_params(1) home_only_params = ( "author", "auth.confluence_url", "auth.username", "auth.password", "auth.is_cloud", ) # To hide password in prompts _print_config_file = partial(print_config_with_hidden_attrs, hidden_attributes=["auth.password"]) config_dialog = partial(config_dialog, config_print_function=_print_config_file) page_add_dialog = partial(page_add_dialog, config_print_function=_print_config_file) # Initial prompt echo("Starting config wizard.") echo( "This wizard will guide you through creating the configuration files.") answer = "" if not any([local_only, home_only]): echo( "Since neither '--local-only' nor '--home-only' were specified, wizard will guide you through creating " f"config files in {home_config_location.parent}(XDG_CONFIG_HOME) and {Path.cwd()}(local directory)" ) answer = prompt( f"Create config in {home_config_location.parent}? [Y/n/q]" "\n* 'n' skips to config in the local directory" "\n* 'q' will exit the wizard\n", type=str, default="y", ).lower() if answer == "q": raise typer.Exit() if (answer == "y" and not local_only) or home_only: # Create config in home while True: dialog_result = config_dialog( filename=home_config_location, attributes=[_ for _ in all_params if _ in home_only_params], ) if dialog_result is None or dialog_result: # None means the user does not want to overwrite the file break if home_only: # If --home-only is specified - no need to create another one in local folder echo( "--home-only specified, not attempting to create any more configs." ) raise typer.Exit() if not local_only: # If local-only is passed - no need to ask for confirmation of creating a local only config local_answer = confirm(f"Proceed to create config in {Path.cwd()}?", default=True) if not local_answer: echo("Exiting.") raise typer.Exit() # Create config in current working directory echo("Creating config in local directory.") local_config_name = prompt("Please provide the name of local config", type=str, default=default_config_name) home_parameters = get_filled_attributes_from_file(home_config_location) local_config_parameters = [ _ for _ in all_params if _ not in home_parameters ] while True: dialog_result = config_dialog(filename=Path.cwd() / local_config_name, attributes=local_config_parameters) if dialog_result is None or dialog_result: # None means the user does not want to overwrite the file break while confirm("Add more pages?", default=False): page_add_dialog(Path.cwd() / local_config_name) echo( "Configuration wizard finished. Consider running the `validate` command to check the generated config" )