def test_check_no_init() -> None: """Validates that check fails when no configuration file""" runner = CliRunner() Context(INTEGRATION).cache.wipe() # No .bento.yml exists in this directory result = runner.invoke(check, obj=Context(base_path=INTEGRATION)) assert result.exit_code == 3
def test_install_ignore_in_repo() -> None: """Validates that bento installs an ignore file if none exists""" context = Context(base_path=SIMPLE, is_init=True) command = InitCommand(context) with mod_file(context.ignore_file_path): context.ignore_file_path.unlink() command._install_ignore_if_not_exists() context = Context(base_path=SIMPLE, is_init=True) assert context.ignore_file_path.exists()
def test_install_ignore_in_repo() -> None: """Validates that bento installs an ignore file if none exists""" context = Context(base_path=SIMPLE, is_init=True) command = InitCommand(context) with util.mod_file(context.ignore_file_path): context.ignore_file_path.unlink() command._install_ignore_if_not_exists() context = Context(base_path=SIMPLE, is_init=True) ig = context.file_ignores assert "node_modules/" in ig.patterns
def test_install_ignore_no_repo(tmp_path: Path, monkeypatch: MonkeyPatch) -> None: """Validates that bento installs extra ignore items when not in a git repo""" monkeypatch.chdir(tmp_path) context = Context(base_path=tmp_path, is_init=True) command = InitCommand(context) command._install_ignore_if_not_exists() context = Context(base_path=tmp_path, is_init=True) ig = context.file_ignores assert "node_modules/" in ig.patterns
def test_check_compare_to_head_no_diffs() -> None: """Validates that check shows no issues with no diffs with --comparison head""" runner = CliRunner(mix_stderr=False) Context(SIMPLE).cache.wipe() result = runner.invoke( check, ["--formatter", "json", str(SIMPLE)], obj=Context(base_path=SIMPLE)) parsed = json.loads(result.stdout) assert len(parsed) == 0
def test_check_happy_path() -> None: """Validates that check discovers issues in normal usage""" runner = CliRunner(mix_stderr=False) Context(SIMPLE).cache.wipe() result = runner.invoke( check, ["--formatter", "json"], obj=Context(base_path=SIMPLE) ) parsed = json.loads(result.stdout) assert len(parsed) == 4
def test_check_show_all() -> None: """Validates that check displays archived issues with --show-all""" runner = CliRunner(mix_stderr=False) Context(SIMPLE).cache.wipe() result = runner.invoke( check, ["--formatter", "json", "--show-all"], obj=Context(base_path=SIMPLE) ) parsed = json.loads(result.stdout) assert len(parsed) == 5
def test_register_email_from_env(monkeypatch: MonkeyPatch, tmp_path: Path) -> None: monkeypatch.setenv("BENTO_EMAIL", "*****@*****.**") ctx = click.Context(cli) context = Context(base_path=INTEGRATION / "simple") context.is_init = True ctx.obj = context registrar = Registrar(click_context=ctx, agree=False) assert registrar.email == "*****@*****.**"
def test_disable_tool() -> None: """Validates that disable tool correctly modifies config""" runner = CliRunner() context = Context(base_path=SIMPLE) with mod_file(context.config_path): runner.invoke(disable, ["tool", "eslint"], obj=context) config = context.config assert config["tools"]["eslint"]["run"] is False # Check persists to file (Context reads from file) persisted_config = Context(base_path=SIMPLE).config assert persisted_config["tools"]["eslint"]["run"] is False
def test_check_specified_paths() -> None: """Validates that check discovers issues in specified paths""" runner = CliRunner(mix_stderr=False) Context(SIMPLE).cache.wipe() result = runner.invoke( check, ["--formatter", "json", "init.js", "foo.py"], obj=Context(base_path=SIMPLE), ) parsed = json.loads(result.stdout) assert len(parsed) == 3
def test_check_tool_error() -> None: expectation = "✘ Error while running r2c.foo: test" with patch.object( bento.tool_runner.Runner, "parallel_results", return_value=[("r2c.foo", Exception("test"))], ): runner = CliRunner(mix_stderr=False) Context(SIMPLE).cache.wipe() result = runner.invoke(check, obj=Context(base_path=SIMPLE)) assert result.exit_code == 3 assert expectation in result.stderr.splitlines()
def test_check_compare_archive() -> None: """Validates that check discovers issues in tech debt mode""" runner = CliRunner(mix_stderr=False) Context(SIMPLE).cache.wipe() result = runner.invoke( check, ["--formatter", "json", "--all", str(SIMPLE)], obj=Context(base_path=SIMPLE), ) parsed = json.loads(result.stdout) assert len(parsed) == 4
def test_check_no_archive() -> None: """Validates that check operates without an archive file""" runner = CliRunner(mix_stderr=False) context = Context(base_path=SIMPLE) context.cache.wipe() with util.mod_file(context.baseline_file_path): context.baseline_file_path.unlink() result = runner.invoke( check, ["--formatter", "json"], obj=Context(base_path=SIMPLE) ) parsed = json.loads(result.stdout) assert len(parsed) == 5 # Archive contains a single whitelisted finding
def cli(ctx: click.Context, base_path: Optional[str], agree: bool) -> None: _setup_logging() is_init = ctx.invoked_subcommand == "init" ctx.help_option_names = ["-h", "--help"] if base_path is None: ctx.obj = Context(is_init=is_init) else: ctx.obj = Context(base_path=base_path, is_init=is_init) if not _is_running_supported_python3(): raise OutdatedPythonException() registrar = register.Registrar(ctx, agree) if not registrar.verify(): raise InvalidRegistrationException()
def tmp_config(tmp_path: Path) -> Iterator[click.Context]: click_context = click.Context(cli) context = Context(base_path=INTEGRATION / "simple") context.is_init = True click_context.obj = context memo = os.environ.get("SHELL") with patch("bento.constants.GLOBAL_CONFIG_PATH", tmp_path / "config.yml"): try: if memo: del os.environ["SHELL"] yield (click_context) finally: if memo: os.environ["SHELL"] = memo
def test_check_specified_paths_and_staged() -> None: """Validates that check errors when --staged-only used with paths""" runner = CliRunner(mix_stderr=False) Context(SIMPLE).cache.wipe() pytest.raises( Exception, runner.invoke( check, ["--staged-only", "init.js", "foo.py"], obj=Context(base_path=SIMPLE), catch_exceptions=False, ), )
def test_check_no_diff_noop() -> None: """Validates that bare check with no diffs is noop""" runner = CliRunner(mix_stderr=False) Context(SIMPLE).cache.wipe() result = runner.invoke(check, ["--formatter", "json"], obj=Context(base_path=SIMPLE)) parsed = json.loads(result.stdout) assert len(parsed) == 0 assert ( "Nothing to check or archive. Please confirm that changes are staged and not" in result.stderr)
def update_tool_run(context: Context, tool: str, run: bool) -> None: """Sets run field of tool to RUN. Default to no ignore if tool not in config """ config = context.config tool_config = config["tools"] all_tools = ", ".join(f"'{k}'" for k in tool_config.keys()) # Check that TOOL is valid if tool not in {t.tool_id() for t in bento.extra.TOOLS}: raise InvalidToolException(tool, all_tools) if tool not in tool_config: # Read default ignore from default config file with (open( os.path.join(os.path.dirname(__file__), "configs/default.yml"))) as template: yml = yaml.safe_load(template) default_ignore: List[str] = [] if tool in yml["tools"]: default_ignore = yml["tools"][tool]["ignore"] tool_config[tool] = {"ignore": default_ignore} tool_config[tool]["run"] = run context.config = config
def install_ci(context: Context) -> None: """ Configures Bento to run in CI. """ _raise_if_unsupported(context.base_path) pretty_path = context.pretty_path(context.gh_actions_file_path) config_directory = pretty_path.parts[0] if not context.is_init: # confirmation was handled as part of init content.Install.banner.echo() if is_ci_configured(context): content.Overwrite.warn.echo(str(pretty_path).strip()) is_overwrite_confirmed = content.Overwrite.confirm.echo() content.Overwrite.after_confirm.echo() if not is_overwrite_confirmed: sys.exit() on_done = content.Install.progress.echo(config_directory) _write_gh_actions_config(context.gh_actions_file_path, _get_user_email()) if not context.is_init: on_done() content.Install.after_progress.echo() content.Install.finalize_ci.echo()
def test_archive_updates_whitelist() -> None: """Validates that archive updates the whitelist file""" runner = CliRunner() context = Context(INTEGRATION / "simple") with mod_file(context.baseline_file_path) as whitelist: runner.invoke(archive, obj=context, args=["--all", str(context.base_path)]) yml = bento.result.json_to_violation_hashes(whitelist) expectation = { "bandit": { "6f77d9d773cc5248ae20b83f80a7b26a", "e540c501c568dad8d9e2e00abba5740f", }, "eslint": {"6daebd293be00a3d97e19de4a1a39fa5"}, "flake8": { "23d898269aae05ed6897cf56dfbd3cde", "b849b45f8a969cc5eb46e6ea76d7e809", }, } assert yml == expectation
def update_tool_run(context: Context, tool: str, run: bool) -> None: """Sets run field of tool to RUN. Default to no ignore if tool not in config """ # Check that TOOL is valid if tool not in {t.tool_id() for t in bento.extra.TOOLS}: echo_error( f"No tool named '{tool}'. See help text for list of available tools." ) sys.exit(3) config = context.config tool_config = config["tools"] if tool not in tool_config: # Read default ignore from default config file with (open( os.path.join(os.path.dirname(__file__), "configs/default.yml"))) as template: yml = yaml.safe_load(template) default_ignore: List[str] = [] if tool in yml["tools"]: default_ignore = yml["tools"][tool]["ignore"] tool_config[tool] = {"ignore": default_ignore} tool_config[tool]["run"] = run context.config = config
def test_archive_no_init() -> None: """Validates that archive fails when no configuration file""" runner = CliRunner() # No .bento.yml exists in this directory result = runner.invoke(archive, obj=Context(base_path=INTEGRATION)) assert result.exit_code == 3
def test_version() -> None: """Validates that version string is printed""" runner = CliRunner() context = Context(base_path=SIMPLE) result = runner.invoke(cli, ["--version"], obj=context) assert result.output.strip() == f"bento/{__version__}"
def test_enable_tool() -> None: """Validates that enable tool works with no run""" runner = CliRunner() context = Context(base_path=SIMPLE) with mod_file(context.config_path): config = context.config assert "run" not in config["tools"]["eslint"] runner.invoke(enable, ["tool", "eslint"], obj=context) context = Context(base_path=SIMPLE) assert config["tools"]["eslint"]["run"] # Check persists to file (Context reads from file) persisted_config = Context(base_path=SIMPLE).config assert persisted_config["tools"]["eslint"]["run"]
def test_enable_tool_not_found() -> None: """Validates that enabling an unconfigured tool causes run failure""" runner = CliRunner() context = Context(base_path=PY_ONLY) result = runner.invoke(enable, ["check", "eslint", "foo"], obj=context) assert result.exception.code == 3
def test_disable_then_enable_tool() -> None: """Validates that enable tool works after previously disabling""" runner = CliRunner() context = Context(base_path=SIMPLE) with mod_file(context.config_path): config = context.config runner.invoke(disable, ["tool", "eslint"], obj=context) assert config["tools"]["eslint"]["run"] is False runner.invoke(enable, ["tool", "eslint"], obj=context) assert config["tools"]["eslint"]["run"] # Check persists to file (Context reads from file) persisted_config = Context(base_path=SIMPLE).config assert persisted_config["tools"]["eslint"]["run"]
def test_no_install_empty_project() -> None: """Validates that bento does installs a config on an empty project""" context = Context(base_path=INTEGRATION / "none", is_init=True) command = InitCommand(context) with mod_file(context.config_path): context.config_path.unlink() assert not context.config_path.exists() command._install_config_if_not_exists() assert len(context.config["tools"]) == 0
def test_enable_default_ignores() -> None: """Validates that enable tool not in config uses default ignores""" runner = CliRunner() context = Context(base_path=PY_ONLY) with mod_file(context.config_path): config = context.config # Test is meaningless if tool is already in config assert "eslint" not in config["tools"] runner.invoke(enable, ["tool", "eslint"], obj=context) assert config["tools"]["eslint"]["run"] assert len(config["tools"]["eslint"]["ignore"]) > 0 # Check persists to file (Context reads from file) persisted_config = Context(base_path=PY_ONLY).config assert persisted_config["tools"]["eslint"]["run"] assert len(persisted_config["tools"]["eslint"]["ignore"]) > 0
def test_init_py_only() -> None: context = Context(base_path=INTEGRATION / "py-only", is_init=True) with mod_file(context.config_path): context.config_path.unlink() CliRunner(mix_stderr=False).invoke(init, obj=context) config = context.config assert "eslint" not in config["tools"] assert "flake8" in config["tools"] assert "bandit" in config["tools"]
def test_enable_tool_found() -> None: """Validates that enabling a check updates ignores in config""" runner = CliRunner() context = Context(base_path=SIMPLE) with mod_file(context.config_path): runner.invoke(enable, ["check", "eslint", "curly"], obj=context) config = context.config assert "curly" not in config["tools"]["eslint"]["ignore"]