mirror of
https://github.com/d9pouces/id_configurator.git
synced 2026-11-09 01:47:21 +01:00
bf56cdfcd2
Signed-off-by: d9pouces <github@19pouces.net>
210 lines
6.2 KiB
Python
210 lines
6.2 KiB
Python
import json
|
|
from unittest.mock import MagicMock
|
|
|
|
from typer.testing import CliRunner
|
|
|
|
from id_configurator import cli
|
|
|
|
runner = CliRunner()
|
|
|
|
MINIMAL_CONFIG = {
|
|
"uuid": "12345678-1234-5678-1234-567812345678",
|
|
"organisation": "InterDiode",
|
|
}
|
|
CONFIG_WITH_CERT = {
|
|
**MINIMAL_CONFIG,
|
|
"configurations": {"certificates": [{"name": "my-ca", "content": "fake"}]},
|
|
}
|
|
CONFIG_WITH_GO_REPO = {
|
|
**MINIMAL_CONFIG,
|
|
"configurations": {
|
|
"go": {"repositories": [{"name": "demo", "url": "https://example.invalid/"}]}
|
|
},
|
|
}
|
|
|
|
|
|
def write_config(tmp_path, data) -> str:
|
|
path = tmp_path / "config.json"
|
|
path.write_text(json.dumps(data), encoding="utf-8")
|
|
return str(path)
|
|
|
|
|
|
def test_validate_valid_config(tmp_path) -> None:
|
|
config_path = write_config(tmp_path, MINIMAL_CONFIG)
|
|
result = runner.invoke(cli.app, ["validate", "--config", config_path])
|
|
assert result.exit_code == 0
|
|
assert "is valid" in result.stdout
|
|
|
|
|
|
def test_validate_without_uuid_or_organisation(tmp_path) -> None:
|
|
config_path = write_config(tmp_path, {})
|
|
result = runner.invoke(cli.app, ["validate", "--config", config_path])
|
|
assert result.exit_code == 0
|
|
assert "<no uuid>" in result.stdout
|
|
assert "<no organisation>" in result.stdout
|
|
|
|
|
|
def test_validate_without_config_fails() -> None:
|
|
result = runner.invoke(cli.app, ["validate"])
|
|
assert result.exit_code != 0
|
|
assert "either --config, --config-env" in result.output
|
|
|
|
|
|
def _mock_urlopen(content: bytes) -> MagicMock:
|
|
response = MagicMock()
|
|
response.read.return_value = content
|
|
response.__enter__.return_value = response
|
|
response.__exit__.return_value = False
|
|
return MagicMock(return_value=response)
|
|
|
|
|
|
def test_validate_from_config_url(monkeypatch) -> None:
|
|
monkeypatch.setattr(
|
|
"id_configurator.config.urllib.request.urlopen",
|
|
_mock_urlopen(json.dumps(MINIMAL_CONFIG).encode("utf-8")),
|
|
)
|
|
result = runner.invoke(
|
|
cli.app, ["validate", "--config-url", "https://example.invalid/config.json"]
|
|
)
|
|
assert result.exit_code == 0
|
|
assert "is valid" in result.stdout
|
|
|
|
|
|
def test_validate_from_config_url_insecure(monkeypatch) -> None:
|
|
mock_urlopen = _mock_urlopen(json.dumps(MINIMAL_CONFIG).encode("utf-8"))
|
|
monkeypatch.setattr("id_configurator.config.urllib.request.urlopen", mock_urlopen)
|
|
result = runner.invoke(
|
|
cli.app,
|
|
[
|
|
"validate",
|
|
"--config-url",
|
|
"https://example.invalid/config.json",
|
|
"--config-url-insecure",
|
|
],
|
|
)
|
|
assert result.exit_code == 0
|
|
assert "is valid" in result.stdout
|
|
_, kwargs = mock_urlopen.call_args
|
|
assert kwargs["context"].verify_mode.name == "CERT_NONE"
|
|
|
|
|
|
def test_list_modules() -> None:
|
|
result = runner.invoke(cli.app, ["list-modules"])
|
|
assert result.exit_code == 0
|
|
assert set(result.stdout.splitlines()) == {
|
|
"apk",
|
|
"apt",
|
|
"certificates",
|
|
"go",
|
|
"java",
|
|
"npm",
|
|
"nuget",
|
|
"php",
|
|
"python",
|
|
"ruby",
|
|
"rust",
|
|
"yum",
|
|
}
|
|
|
|
|
|
def test_apply_skips_irrelevant_modules(tmp_path, caplog) -> None:
|
|
config_path = write_config(tmp_path, MINIMAL_CONFIG)
|
|
dst = tmp_path / "dst"
|
|
with caplog.at_level("DEBUG", logger="id_configurator"):
|
|
result = runner.invoke(
|
|
cli.app, ["apply", "--config", config_path, "--dst", str(dst), "-v"]
|
|
)
|
|
assert result.exit_code == 0
|
|
assert "skipping module apk" in caplog.text
|
|
assert "skipping module java" in caplog.text
|
|
assert "skipping module apt" in caplog.text
|
|
assert "skipping module certificates" in caplog.text
|
|
assert "skipping module yum" in caplog.text
|
|
assert "skipping module python" in caplog.text
|
|
|
|
|
|
def test_apply_runs_relevant_module(tmp_path) -> None:
|
|
config_path = write_config(tmp_path, CONFIG_WITH_CERT)
|
|
dst = tmp_path / "dst"
|
|
result = runner.invoke(
|
|
cli.app, ["apply", "--config", config_path, "--dst", str(dst)]
|
|
)
|
|
|
|
assert result.exit_code == 0
|
|
assert (dst / "usr/local/share/ca-certificates/my-ca.crt").read_bytes() == b"fake"
|
|
assert (dst / "etc/ssl/certs/ca-certificates.crt").read_bytes() == b"fake\n"
|
|
|
|
|
|
def test_apply_with_user_option_writes_per_user_config(tmp_path) -> None:
|
|
config_path = write_config(tmp_path, CONFIG_WITH_GO_REPO)
|
|
dst = tmp_path / "dst"
|
|
result = runner.invoke(
|
|
cli.app,
|
|
[
|
|
"apply",
|
|
"--config",
|
|
config_path,
|
|
"--dst",
|
|
str(dst),
|
|
"--user",
|
|
"alice",
|
|
"--user",
|
|
"root",
|
|
],
|
|
)
|
|
|
|
assert result.exit_code == 0
|
|
assert (dst / "home/alice/.config/go/env").read_text(encoding="utf-8") == (
|
|
"GOPROXY=https://example.invalid/\nGOSUMDB=off\n"
|
|
)
|
|
assert (dst / "root/.config/go/env").read_text(encoding="utf-8") == (
|
|
"GOPROXY=https://example.invalid/\nGOSUMDB=off\n"
|
|
)
|
|
|
|
|
|
def test_apply_with_auto_users_reads_passwd_from_src(tmp_path) -> None:
|
|
config_path = write_config(tmp_path, CONFIG_WITH_GO_REPO)
|
|
src = tmp_path / "src"
|
|
(src / "etc").mkdir(parents=True)
|
|
(src / "etc/passwd").write_text(
|
|
"root:x:0:0:root:/root:/bin/bash\n"
|
|
"alice:x:1000:1000:Alice:/home/alice:/bin/bash\n"
|
|
"daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin\n",
|
|
encoding="utf-8",
|
|
)
|
|
dst = tmp_path / "dst"
|
|
result = runner.invoke(
|
|
cli.app,
|
|
[
|
|
"apply",
|
|
"--config",
|
|
config_path,
|
|
"--dst",
|
|
str(dst),
|
|
"--src",
|
|
str(src),
|
|
"--auto-users",
|
|
],
|
|
)
|
|
|
|
assert result.exit_code == 0
|
|
# root is always included despite its uid 0; daemon (nologin shell) is
|
|
# a system account, excluded.
|
|
assert (dst / "root/.config/go/env").exists()
|
|
assert (dst / "home/alice/.config/go/env").exists()
|
|
assert not (dst / "usr/sbin/.config/go/env").exists()
|
|
|
|
|
|
def test_main_default_name_calls_app(monkeypatch) -> None:
|
|
mock_app = MagicMock()
|
|
monkeypatch.setattr(cli, "app", mock_app)
|
|
cli.main()
|
|
mock_app.assert_called_once()
|
|
|
|
|
|
def test_main_other_name_does_not_call_app(monkeypatch) -> None:
|
|
mock_app = MagicMock()
|
|
monkeypatch.setattr(cli, "app", mock_app)
|
|
cli.main("id_configurator.__main__")
|
|
mock_app.assert_not_called()
|