Пример #1
0
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
Пример #2
0
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()
Пример #3
0
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
Пример #4
0
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
Пример #5
0
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
Пример #6
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
Пример #7
0
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
Пример #8
0
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 == "*****@*****.**"
Пример #9
0
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
Пример #10
0
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
Пример #11
0
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()
Пример #12
0
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
Пример #13
0
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
Пример #14
0
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()
Пример #15
0
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
Пример #16
0
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,
        ),
    )
Пример #17
0
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)
Пример #18
0
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
Пример #19
0
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()
Пример #20
0
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
Пример #21
0
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
Пример #22
0
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
Пример #23
0
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__}"
Пример #24
0
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"]
Пример #25
0
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
Пример #26
0
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"]
Пример #27
0
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
Пример #28
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
Пример #29
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"]
Пример #30
0
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"]