示例#1
0
def test_charm_parameters_requirement(config):
    """The --requirement option implies a set of validations."""
    cmd = PackCommand("group", config)
    parser = ArgumentParser()
    cmd.fill_parser(parser)
    (action,) = [action for action in parser._actions if action.dest == "requirement"]
    assert action.type is useful_filepath
示例#2
0
def test_charm_parameters_entrypoint(config):
    """The --entrypoint option implies a set of validations."""
    cmd = PackCommand("group", config)
    parser = ArgumentParser()
    cmd.fill_parser(parser)
    (action,) = [action for action in parser._actions if action.dest == "entrypoint"]
    assert isinstance(action.type, SingleOptionEnsurer)
    assert action.type.converter is useful_filepath
示例#3
0
def test_resolve_bundle_type(config):
    """The config indicates the project is a bundle."""
    config.set(type="bundle")
    cmd = PackCommand("group", config)

    with patch.object(cmd, "_pack_bundle") as mock:
        cmd.run(noargs)
    mock.assert_called_with()
示例#4
0
def test_resolve_no_config_packs_charm(config):
    """There is no config, so it's decided to pack a charm."""
    config.set(project=Project(config_provided=False))
    cmd = PackCommand("group", config)

    with patch.object(cmd, "_pack_charm") as mock:
        cmd.run(noargs)
    mock.assert_called_with(noargs)
示例#5
0
def test_resolve_no_config_packs_charm(config, tmp_path):
    """There is no config, so it's decided to pack a charm."""
    config.set(project=Project(
        config_provided=False,
        dirpath=tmp_path,
        started_at=datetime.datetime.utcnow(),
    ))
    cmd = PackCommand(config)

    with patch.object(cmd, "_pack_charm") as mock:
        cmd.run(noargs)
    mock.assert_called_with(noargs)
示例#6
0
def test_charm_parameters_validator(config, tmp_path):
    """Check that build.Builder is properly called."""
    args = Namespace(
        destructive_mode=True,
        requirement="test-reqs",
        entrypoint="test-epoint",
        bases_index=[],
        force=True,
    )
    config.set(
        type="charm",
        project=Project(dirpath=tmp_path, started_at=datetime.datetime.utcnow()),
    )
    with patch("charmcraft.commands.build.Validator", autospec=True) as validator_class_mock:
        validator_class_mock.return_value = validator_instance_mock = MagicMock()
        with patch("charmcraft.commands.build.Builder"):
            PackCommand("group", config).run(args)
    validator_instance_mock.process.assert_called_with(
        Namespace(
            **{
                "destructive_mode": True,
                "from": tmp_path,
                "requirement": "test-reqs",
                "entrypoint": "test-epoint",
                "bases_indices": [],
                "force": True,
            }
        )
    )
示例#7
0
def test_missing_bundle_file(tmp_path, config):
    """Can not build a bundle without bundle.yaml."""
    # build without a bundle.yaml!
    with pytest.raises(CommandError) as cm:
        PackCommand('group', config).run(noargs)
    assert str(cm.value) == (
        "Missing or invalid main bundle file: '{}'.".format(tmp_path / 'bundle.yaml'))
示例#8
0
def test_bundle_simple_succesful_build(tmp_path, emitter, bundle_yaml,
                                       bundle_config):
    """A simple happy story."""
    # mandatory files (other thant the automatically provided manifest)
    content = bundle_yaml(name="testbundle")
    bundle_config.set(type="bundle")
    (tmp_path / "README.md").write_text("test readme")

    # build!
    PackCommand(bundle_config).run(noargs)

    # check
    zipname = tmp_path / "testbundle.zip"
    zf = zipfile.ZipFile(zipname)
    assert "charmcraft.yaml" not in [x.filename for x in zf.infolist()]
    assert zf.read("bundle.yaml") == content.encode("ascii")
    assert zf.read("README.md") == b"test readme"

    expected = "Created '{}'.".format(zipname)
    emitter.assert_message(expected)

    # check the manifest is present and with particular values that depend on given info
    manifest = yaml.safe_load(zf.read("manifest.yaml"))
    assert manifest[
        "charmcraft-started-at"] == bundle_config.project.started_at.isoformat(
        ) + "Z"

    # verify that the manifest was not leftover in user's project
    assert not (tmp_path / "manifest.yaml").exists()
示例#9
0
def test_prime_extra_globstar(tmp_path, bundle_yaml, bundle_config):
    """Double star means whatever directories are in the path."""
    bundle_yaml(name="testbundle")
    bundle_config.set(prime=["lib/**/*"])
    srcpaths = (
        ("lib/foo/f1.txt", True),
        ("lib/foo/deep/fx.txt", True),
        ("lib/bar/f2.txt", True),
        ("lib/f3.txt", True),
        ("extra/lib/f.txt", False),
        ("libs/fs.txt", False),
    )

    for srcpath, expected in srcpaths:
        testfile = tmp_path / pathlib.Path(srcpath)
        testfile.parent.mkdir(parents=True, exist_ok=True)
        testfile.touch()

    with patch.object(pack, "MANDATORY_FILES", []):
        PackCommand(bundle_config).run(noargs)

    zf = zipfile.ZipFile(tmp_path / "testbundle.zip")
    zipped_files = [x.filename for x in zf.infolist()]
    for srcpath, expected in srcpaths:
        assert (srcpath in zipped_files) == expected
示例#10
0
def test_resolve_bundle_with_requirement(config):
    """The requirement option is not valid when packing a bundle."""
    config.set(type="bundle")
    args = Namespace(requirement="reqs.txt", entrypoint=None)

    with pytest.raises(CommandError) as cm:
        PackCommand("group", config).run(args)
    assert str(cm.value) == "The -r/--requirement option is valid only when packing a charm"
示例#11
0
def test_bundle_missing_other_mandatory_file(tmp_path, bundle_config, bundle_yaml):
    """Can not build a bundle without any of the mandatory files."""
    bundle_yaml(name="testbundle")
    bundle_config.set(type="bundle")

    # build without a README!
    with pytest.raises(CommandError) as cm:
        PackCommand("group", bundle_config).run(noargs)
    assert str(cm.value) == "Missing mandatory file: {!r}.".format(str(tmp_path / "README.md"))
示例#12
0
def test_missing_bundle_file(tmp_path, charmcraft_yaml):
    """Can not build a bundle without bundle.yaml."""
    # build without a bundle.yaml!
    args = Namespace(from_dir=tmp_path)
    with pytest.raises(CommandError) as cm:
        PackCommand('group').run(args)
    assert str(
        cm.value) == ("Missing or invalid main bundle file: '{}'.".format(
            tmp_path / 'bundle.yaml'))
示例#13
0
def test_specified_directory_not_found(tmp_path):
    """The specified directory is not there."""
    not_there = tmp_path / 'not there'
    args = Namespace(from_dir=not_there)
    with pytest.raises(CommandError) as cm:
        PackCommand('group').run(args)
    assert str(
        cm.value) == "Bundle project directory was not found: '{}'.".format(
            not_there)
示例#14
0
def test_bundle_shell_after(tmp_path, bundle_yaml, bundle_config, mock_parts,
                            mock_launch_shell):
    bundle_yaml(name="testbundle")
    bundle_config.set(type="bundle")
    (tmp_path / "README.md").write_text("test readme")

    PackCommand(bundle_config).run(get_namespace(shell_after=True))

    assert mock_launch_shell.mock_calls == [mock.call()]
示例#15
0
def test_resolve_bundle_with_entrypoint(config):
    """The entrypoint option is not valid when packing a bundle."""
    config.set(type="bundle")
    args = Namespace(requirement=None, entrypoint="mycharm.py")

    with pytest.raises(CommandError) as cm:
        PackCommand(config).run(args)
    assert str(
        cm.value) == "The -e/--entry option is valid only when packing a charm"
示例#16
0
def test_specified_directory_not_a_directory(tmp_path):
    """The specified directory is not really a directory."""
    somefile = tmp_path / 'somefile'
    somefile.touch()
    args = Namespace(from_dir=somefile)
    with pytest.raises(CommandError) as cm:
        PackCommand('group').run(args)
    assert str(cm.value) == (
        "Bundle project directory is not a directory: '{}'.".format(somefile))
示例#17
0
def test_bad_type_in_charmcraft(bundle_yaml, config):
    """The charmcraft.yaml file must have a proper type field."""
    bundle_yaml(name='testbundle')
    config.set(type='charm')

    # build!
    with pytest.raises(CommandError) as cm:
        PackCommand('group', config).run(noargs)
    assert str(cm.value) == (
        "Bad config: 'type' field in charmcraft.yaml must be 'bundle' for this command.")
示例#18
0
def test_missing_name_in_bundle(tmp_path, bundle_yaml, config):
    """Can not build a bundle without name."""
    config.set(type='bundle')

    # build!
    with pytest.raises(CommandError) as cm:
        PackCommand('group', config).run(noargs)
    assert str(cm.value) == (
        "Invalid bundle config; missing a 'name' field indicating the bundle's name in file '{}'."
        .format(tmp_path / 'bundle.yaml'))
示例#19
0
def test_charm_builder_infrastructure_called(config):
    """Check that build.Builder is properly called."""
    config.set(type="charm")
    with patch("charmcraft.commands.build.Validator", autospec=True) as validator_mock:
        validator_mock(config).process.return_value = "processed args"
        with patch("charmcraft.commands.build.Builder") as builder_class_mock:
            builder_class_mock.return_value = builder_instance_mock = MagicMock()
            PackCommand("group", config).run(noargs)
    builder_class_mock.assert_called_with("processed args", config)
    builder_instance_mock.run.assert_called_with([], destructive_mode=False)
示例#20
0
def test_bundle_missing_name_in_bundle(tmp_path, bundle_yaml, bundle_config):
    """Can not build a bundle without name."""
    bundle_config.set(type="bundle")

    # build!
    with pytest.raises(CraftError) as cm:
        PackCommand(bundle_config).run(noargs)
    assert str(cm.value) == (
        "Invalid bundle config; "
        "missing a 'name' field indicating the bundle's name in file '{}'.".
        format(tmp_path / "bundle.yaml"))
示例#21
0
def test_missing_charmcraft_file(tmp_path, bundle_yaml):
    """Can not build a bundle without charmcraft.yaml."""
    bundle_yaml(name='testbundle')

    # build without a charmcraft.yaml!
    args = Namespace(from_dir=tmp_path)
    with pytest.raises(CommandError) as cm:
        PackCommand('group').run(args)
    assert str(
        cm.value) == ("Missing or invalid charmcraft file: '{}'.".format(
            tmp_path / 'charmcraft.yaml'))
示例#22
0
def test_missing_name_in_bundle(tmp_path, bundle_yaml, charmcraft_yaml):
    """Can not build a bundle without name."""
    charmcraft_yaml(type='bundle')

    # build!
    args = Namespace(from_dir=tmp_path)
    with pytest.raises(CommandError) as cm:
        PackCommand('group').run(args)
    assert str(cm.value) == (
        "Invalid bundle config; missing a 'name' field indicating the bundle's name in file '{}'."
        .format(tmp_path / 'bundle.yaml'))
示例#23
0
def test_missing_type_in_charmcraft(tmp_path, bundle_yaml, charmcraft_yaml):
    """The charmcraft.yaml file must have a proper type field."""
    bundle_yaml(name='testbundle')

    # build!
    args = Namespace(from_dir=tmp_path)
    with pytest.raises(CommandError) as cm:
        PackCommand('group').run(args)
    assert str(cm.value) == (
        "Invalid charmcraft config; 'type' must be 'bundle' in file '{}'.".
        format(tmp_path / 'charmcraft.yaml'))
示例#24
0
def test_bundle_debug_with_error(tmp_path, bundle_yaml, bundle_config,
                                 mock_parts, mock_launch_shell):
    mock_parts.PartsLifecycle.return_value.run.side_effect = CraftError("fail")
    bundle_yaml(name="testbundle")
    bundle_config.set(type="bundle")
    (tmp_path / "README.md").write_text("test readme")

    with pytest.raises(CraftError):
        PackCommand(bundle_config).run(get_namespace(debug=True))

    assert mock_launch_shell.mock_calls == [mock.call()]
示例#25
0
def test_prime_extra_wildcards_not_found(tmp_path, bundle_yaml, bundle_config):
    """Use wildcards to specify several files but nothing found."""
    bundle_yaml(name="testbundle")
    bundle_config.set(prime=["*.txt"])

    # non-existent files are not included if using a wildcard
    with patch.object(pack, "MANDATORY_FILES", []):
        PackCommand(bundle_config).run(noargs)

    zf = zipfile.ZipFile(tmp_path / "testbundle.zip")
    zipped_files = [x.filename for x in zf.infolist()]
    assert zipped_files == ["manifest.yaml"]
示例#26
0
def test_prime_extra_missing(tmp_path, bundle_yaml, bundle_config):
    """Extra files were indicated but not found."""
    bundle_yaml(name="testbundle")
    bundle_config.set(prime=["f2.txt", "f1.txt"])
    testfile1 = tmp_path / "f1.txt"
    testfile1.touch()

    with patch.object(pack, "MANDATORY_FILES", []):
        with pytest.raises(CommandError) as err:
            PackCommand(bundle_config).run(noargs)
    assert str(err.value) == (
        "Parts processing error: Failed to copy '{}/build/stage/f2.txt': "
        "no such file or directory.".format(tmp_path))
示例#27
0
def test_prime_extra_long_path(tmp_path, bundle_yaml, bundle_config):
    """An extra file can be deep in directories."""
    bundle_yaml(name="testbundle")
    bundle_config.set(prime=["foo/bar/baz/extra.txt"])
    testfile = tmp_path / "foo" / "bar" / "baz" / "extra.txt"
    testfile.parent.mkdir(parents=True)
    testfile.touch()

    with patch.object(pack, "MANDATORY_FILES", []):
        PackCommand(bundle_config).run(noargs)

    zf = zipfile.ZipFile(tmp_path / "testbundle.zip")
    zipped_files = [x.filename for x in zf.infolist()]
    assert "foo/bar/baz/extra.txt" in zipped_files
示例#28
0
def test_prime_extra_ok(tmp_path, bundle_yaml, bundle_config):
    """Extra files were indicated ok."""
    bundle_yaml(name="testbundle")
    bundle_config.set(prime=["f2.txt", "f1.txt"])
    testfile1 = tmp_path / "f1.txt"
    testfile1.touch()
    testfile2 = tmp_path / "f2.txt"
    testfile2.touch()

    with patch.object(pack, "MANDATORY_FILES", []):
        PackCommand(bundle_config).run(noargs)

    zf = zipfile.ZipFile(tmp_path / "testbundle.zip")
    zipped_files = [x.filename for x in zf.infolist()]
    assert "f1.txt" in zipped_files
    assert "f2.txt" in zipped_files