참고소스 수정본

This commit is contained in:
LASTA_DEV01\lasta
2026-05-12 19:40:31 +09:00
parent 0f34a451fc
commit 2e9204243d
8708 changed files with 3259488 additions and 869 deletions

View File

@@ -0,0 +1,279 @@
from unittest.mock import ANY, MagicMock, call
from typer.testing import CliRunner
from guardrails.cli.hub.install import hub_command
import pytest
class TestInstall:
def test_exits_early_if_uri_is_not_valid(self, mocker):
mock_logger_error = mocker.patch("guardrails.hub.install.cli_logger.error")
runner = CliRunner()
result = runner.invoke(hub_command, ["install", "some-invalid-uri"])
assert result.exit_code == 1
mock_logger_error.assert_called_once_with(
"Invalid URI! The package URI must start with 'hub://'"
)
def test_install_local_models__false(self, mocker):
mock_install = mocker.patch("guardrails.hub.install.install")
runner = CliRunner()
result = runner.invoke(
hub_command,
["install", "hub://guardrails/test-validator", "--no-install-local-models"],
)
mock_install.assert_called_once_with(
"hub://guardrails/test-validator",
install_local_models=False,
quiet=ANY,
upgrade=False,
install_local_models_confirm=ANY,
)
assert result.exit_code == 0
def test_install_local_models__true(self, mocker):
mock_install = mocker.patch("guardrails.hub.install.install")
runner = CliRunner()
result = runner.invoke(
hub_command,
["install", "hub://guardrails/test-validator", "--install-local-models"],
)
mock_install.assert_called_once_with(
"hub://guardrails/test-validator",
install_local_models=True,
quiet=False,
upgrade=False,
install_local_models_confirm=ANY,
)
assert result.exit_code == 0
def test_install_local_models__none(self, mocker):
mock_install = mocker.patch("guardrails.hub.install.install")
runner = CliRunner()
result = runner.invoke(
hub_command,
["install", "hub://guardrails/test-validator"],
)
mock_install.assert_called_once_with(
"hub://guardrails/test-validator",
install_local_models=None,
quiet=False,
upgrade=False,
install_local_models_confirm=ANY,
)
assert result.exit_code == 0
def test_install_quiet(self, mocker):
mock_install = mocker.patch("guardrails.hub.install.install")
runner = CliRunner()
result = runner.invoke(
hub_command, ["install", "hub://guardrails/test-validator", "--quiet"]
)
mock_install.assert_called_once_with(
"hub://guardrails/test-validator",
install_local_models=None,
quiet=True,
upgrade=False,
install_local_models_confirm=ANY,
)
assert result.exit_code == 0
def test_install_multiple_validators(self, mocker):
mock_install_multiple = mocker.patch("guardrails.hub.install.install_multiple")
runner = CliRunner()
result = runner.invoke(
hub_command,
[
"install",
"hub://guardrails/validator1",
"hub://guardrails/validator2",
"--no-install-local-models",
],
)
mock_install_multiple.assert_called_once_with(
["hub://guardrails/validator1", "hub://guardrails/validator2"],
install_local_models=False,
quiet=False,
upgrade=False,
install_local_models_confirm=ANY,
)
assert result.exit_code == 0
def test_install_multiple_validators_with_quiet(self, mocker):
mock_install_multiple = mocker.patch("guardrails.hub.install.install_multiple")
runner = CliRunner()
result = runner.invoke(
hub_command,
[
"install",
"hub://guardrails/validator1",
"hub://guardrails/validator2",
"--quiet",
],
)
mock_install_multiple.assert_called_once_with(
["hub://guardrails/validator1", "hub://guardrails/validator2"],
install_local_models=None,
quiet=True,
upgrade=False,
install_local_models_confirm=ANY,
)
assert result.exit_code == 0
class TestPipProcess:
def test_no_package_string_format(self, mocker):
mocker.patch("guardrails.cli.hub.utils.os.environ", return_value={})
mock_logger_debug = mocker.patch("guardrails.cli.hub.utils.logger.debug")
mock_sys_executable = mocker.patch("guardrails.cli.hub.utils.sys.executable")
mock_subprocess_run = mocker.patch("guardrails.cli.hub.utils.subprocess.run")
subprocess_result_mock = MagicMock()
subprocess_result_mock.stdout = "string output"
mock_subprocess_run.return_value = subprocess_result_mock
from guardrails.cli.hub.utils import pip_process
response = pip_process("inspect", flags=["--path=./install-here"])
assert mock_logger_debug.call_count == 1
debug_calls = [
call("running pip inspect --path=./install-here "),
]
mock_logger_debug.assert_has_calls(debug_calls)
mock_subprocess_run.assert_called_once_with(
[mock_sys_executable, "-m", "pip", "inspect", "--path=./install-here"],
env={},
capture_output=True,
text=True,
check=True,
)
assert response == "string output"
def test_json_format(self, mocker):
mocker.patch("guardrails.cli.hub.utils.os.environ", return_value={})
mock_logger_debug = mocker.patch("guardrails.cli.hub.utils.logger.debug")
mock_sys_executable = mocker.patch("guardrails.cli.hub.utils.sys.executable")
mock_subprocess_run = mocker.patch("guardrails.cli.hub.utils.subprocess.run")
subprocess_result_mock = MagicMock()
subprocess_result_mock.stdout = "json outout"
mock_subprocess_run.return_value = subprocess_result_mock
class MockBytesHeaderParser:
def parsebytes(self, *args):
return {"output": "json"}
mock_bytes_parser = mocker.patch("guardrails.cli.hub.utils.BytesHeaderParser")
mock_bytes_header_parser = MockBytesHeaderParser()
mock_bytes_parser.return_value = mock_bytes_header_parser
from guardrails.cli.hub.utils import pip_process
response = pip_process("show", "pip", format="json")
assert mock_logger_debug.call_count == 2
debug_calls = [
call("running pip show pip"),
call(
"JSON parse exception in decoding output from pip show pip. Falling back to accumulating the byte stream" # noqa
),
]
mock_logger_debug.assert_has_calls(debug_calls)
mock_subprocess_run.assert_called_once_with(
[mock_sys_executable, "-m", "pip", "show", "pip"],
env={},
capture_output=True,
text=True,
check=True,
)
assert response == {"output": "json"}
def test_called_process_error(self, mocker):
mock_logger_error = mocker.patch("guardrails.cli.hub.utils.logger.error")
mock_logger_debug = mocker.patch("guardrails.cli.hub.utils.logger.debug")
mock_sys_executable = mocker.patch("guardrails.cli.hub.utils.sys.executable")
mock_subprocess_check_output = mocker.patch(
"guardrails.cli.hub.utils.subprocess.check_output"
)
from subprocess import CalledProcessError
mock_subprocess_check_output.side_effect = CalledProcessError(1, "something")
from guardrails.cli.hub.utils import pip_process, sys
sys_exit_spy = mocker.spy(sys, "exit")
with pytest.raises(SystemExit):
pip_process("inspect")
mock_logger_debug.assert_called_once_with("running pip inspect ")
mock_subprocess_check_output.assert_called_once_with(
[mock_sys_executable, "-m", "pip", "inspect"]
)
mock_logger_error.assert_called_once_with(
"Failed to inspect \nExit code: 1\nstdout: "
)
sys_exit_spy.assert_called_once_with(1)
def test_other_exception(self, mocker):
error = ValueError("something went wrong")
mock_logger_debug = mocker.patch("guardrails.cli.hub.utils.logger.debug")
mock_logger_debug.side_effect = error
mock_logger_error = mocker.patch("guardrails.cli.hub.utils.logger.error")
from guardrails.cli.hub.utils import pip_process, sys
sys_exit_spy = mocker.spy(sys, "exit")
with pytest.raises(SystemExit):
pip_process("inspect")
mock_logger_debug.assert_called_once_with("running pip inspect ")
mock_logger_error.assert_called_once_with(
"An unexpected exception occurred while try to inspect !", error
)
sys_exit_spy.assert_called_once_with(1)
def test_install_with_upgrade_flag(self, mocker):
mock_install = mocker.patch("guardrails.hub.install.install")
runner = CliRunner()
result = runner.invoke(
hub_command, ["install", "--upgrade", "hub://guardrails/test-validator"]
)
mock_install.assert_called_once_with(
"hub://guardrails/test-validator",
install_local_models=None,
quiet=False,
install_local_models_confirm=ANY,
upgrade=True,
)
assert result.exit_code == 0

View File

@@ -0,0 +1,98 @@
import subprocess
import sys
from unittest.mock import MagicMock
import pytest
from guardrails.cli.hub.utils import installer_process, PipProcessError
class TestInstallerProcess:
def test_pip_installer_builds_correct_command(self, mocker):
mock_run = mocker.patch(
"guardrails.cli.hub.utils.subprocess.run",
return_value=MagicMock(stdout="Success", returncode=0),
)
result = installer_process(
"install", "some-package", ["--upgrade"], installer="pip"
)
assert result == "Success"
mock_run.assert_called_once()
cmd = mock_run.call_args[0][0]
assert cmd[0] == sys.executable
assert cmd[1:3] == ["-m", "pip"]
assert cmd[3] == "install"
assert "--upgrade" in cmd
assert "some-package" in cmd
def test_uv_installer_builds_correct_command(self, mocker):
mock_run = mocker.patch(
"guardrails.cli.hub.utils.subprocess.run",
return_value=MagicMock(stdout="Success", returncode=0),
)
result = installer_process(
"install", "some-package", ["--upgrade"], installer="uv"
)
assert result == "Success"
mock_run.assert_called_once()
cmd = mock_run.call_args[0][0]
assert cmd[0] == "uv"
assert cmd[1] == "pip"
assert cmd[2] == "install"
assert "--upgrade" in cmd
assert "some-package" in cmd
def test_raises_pip_process_error_on_failure(self, mocker):
mocker.patch(
"guardrails.cli.hub.utils.subprocess.run",
side_effect=subprocess.CalledProcessError(
1, "pip", output="out", stderr="error msg"
),
)
with pytest.raises(PipProcessError) as exc_info:
installer_process("install", "bad-package", installer="pip")
assert exc_info.value.action == "install"
assert exc_info.value.package == "bad-package"
def test_handles_none_stderr_on_failure(self, mocker):
exc = subprocess.CalledProcessError(1, "pip", output="out")
exc.stderr = None
mocker.patch(
"guardrails.cli.hub.utils.subprocess.run",
side_effect=exc,
)
with pytest.raises(PipProcessError) as exc_info:
installer_process("install", "bad-package", installer="uv")
assert exc_info.value.stderr == ""
def test_no_color_sets_env_var(self, mocker):
mock_run = mocker.patch(
"guardrails.cli.hub.utils.subprocess.run",
return_value=MagicMock(stdout="Success"),
)
installer_process("install", "some-package", installer="pip", no_color=True)
env = mock_run.call_args[1]["env"]
assert env["NO_COLOR"] == "true"
@pytest.mark.parametrize("installer", ["uv", "pip"])
def test_empty_package_not_appended(self, mocker, installer):
mock_run = mocker.patch(
"guardrails.cli.hub.utils.subprocess.run",
return_value=MagicMock(stdout="Success"),
)
installer_process("install", "", ["--upgrade"], installer=installer)
cmd = mock_run.call_args[0][0]
# Package should not be in command when empty
assert cmd[-1] == "--upgrade"

View File

@@ -0,0 +1,144 @@
import json
from typer.testing import CliRunner
from guardrails.cli.hub.hub import hub_command
runner = CliRunner()
def test_list_from_registry(tmp_path, mocker):
registry_path = tmp_path / ".guardrails" / "hub_registry.json"
registry_path.parent.mkdir(parents=True)
registry = {
"version": 1,
"validators": {
"guardrails/detect-pii": {
"import_path": "guardrails_grhub_detect_pii",
"exports": ["DetectPII"],
"installed_at": "2025-01-01T00:00:00+00:00",
"package_name": "guardrails-grhub-detect-pii",
},
"guardrails/regex-match": {
"import_path": "guardrails_grhub_regex_match",
"exports": ["RegexMatch"],
"installed_at": "2025-01-01T00:00:00+00:00",
"package_name": "guardrails-grhub-regex-match",
},
},
}
registry_path.write_text(json.dumps(registry))
mocker.patch(
"guardrails.hub.registry.get_registry_path",
return_value=registry_path,
)
result = runner.invoke(hub_command, ["list"])
assert result.exit_code == 0
assert "Installed Validators:" in result.output
assert "guardrails/detect-pii (DetectPII)" in result.output
assert "guardrails/regex-match (RegexMatch)" in result.output
def test_list_empty_registry(tmp_path, mocker):
registry_path = tmp_path / ".guardrails" / "hub_registry.json"
registry_path.parent.mkdir(parents=True)
registry_path.write_text(json.dumps({"version": 1, "validators": {}}))
mocker.patch(
"guardrails.hub.registry.get_registry_path",
return_value=registry_path,
)
result = runner.invoke(hub_command, ["list"])
assert result.exit_code == 0
assert "No validators installed." in result.output
def test_list_no_registry_file(tmp_path, mocker):
registry_path = tmp_path / ".guardrails" / "hub_registry.json"
mocker.patch(
"guardrails.hub.registry.get_registry_path",
return_value=registry_path,
)
result = runner.invoke(hub_command, ["list"])
assert result.exit_code == 0
assert "No validators installed." in result.output
def test_list_corrupt_registry(tmp_path, mocker):
registry_path = tmp_path / ".guardrails" / "hub_registry.json"
registry_path.parent.mkdir(parents=True)
registry_path.write_text("not valid json{{{")
mocker.patch(
"guardrails.hub.registry.get_registry_path",
return_value=registry_path,
)
result = runner.invoke(hub_command, ["list"])
assert result.exit_code == 0
assert "No validators installed." in result.output
def test_list_multi_export_validator(tmp_path, mocker):
registry_path = tmp_path / ".guardrails" / "hub_registry.json"
registry_path.parent.mkdir(parents=True)
registry = {
"version": 1,
"validators": {
"guardrails/test-package": {
"import_path": "guardrails_grhub_test_package",
"exports": ["Validator", "Helper"],
"installed_at": "2025-01-01T00:00:00+00:00",
"package_name": "guardrails-grhub-test-package",
},
},
}
registry_path.write_text(json.dumps(registry))
mocker.patch(
"guardrails.hub.registry.get_registry_path",
return_value=registry_path,
)
result = runner.invoke(hub_command, ["list"])
assert result.exit_code == 0
assert "guardrails/test-package (Validator, Helper)" in result.output
def test_list_entry_without_exports_key(tmp_path, mocker):
"""Entry missing exports key should not crash."""
registry_path = tmp_path / ".guardrails" / "hub_registry.json"
registry_path.parent.mkdir(parents=True)
registry = {
"version": 1,
"validators": {
"guardrails/test": {
"import_path": "guardrails_grhub_test",
"installed_at": "2025-01-01T00:00:00+00:00",
"package_name": "guardrails-grhub-test",
"exports": [],
}
},
}
registry_path.write_text(json.dumps(registry))
mocker.patch(
"guardrails.hub.registry.get_registry_path",
return_value=registry_path,
)
result = runner.invoke(hub_command, ["list"])
assert result.exit_code == 0
assert "guardrails/test ()" in result.output

View File

@@ -0,0 +1,38 @@
import pytest
from typer.testing import CliRunner
from guardrails.cli.hub import hub_command
import os
@pytest.fixture
def mock_post_validator_submit(mocker):
return mocker.patch(
"guardrails.cli.hub.submit.post_validator_submit",
new=lambda *args, **kwargs: None,
)
def test_submit_command_success(mock_post_validator_submit):
runner = CliRunner()
package_name = "test_validator"
with runner.isolated_filesystem():
package_path = f"{package_name}.py"
# Create a temporary file with the package name
with open(package_path, "w+") as file:
file.write("# Test validator content")
result = runner.invoke(hub_command, ["submit", package_name])
assert result.exit_code == 0
os.remove(package_path)
def test_submit_command_failure(mock_post_validator_submit):
runner = CliRunner()
package_name = "test_validator"
result = runner.invoke(hub_command, ["submit", package_name])
assert result.exit_code != 0

View File

@@ -0,0 +1,99 @@
from unittest.mock import mock_open, call
import pytest
from guardrails_hub_types import Manifest
from guardrails.cli.hub.uninstall import remove_from_hub_inits
manifest_mock = Manifest.from_dict(
{
"id": "guardrails/test_package",
"name": "test_module",
"author": {"name": "Author Name", "email": "author@example.com"},
"maintainers": [{"name": "Maintainer Name", "email": "maintainer@example.com"}],
"repository": {"url": "https://github.com/example/repo"},
"packageName": "test_package",
"moduleName": "test_module",
"namespace": "guardrails",
"description": "Test module",
"exports": ["Validator", "Helper"],
}
)
def test_remove_from_hub_inits(mocker):
mock_remove_line = mocker.patch("guardrails.cli.hub.uninstall.remove_line")
remove_from_hub_inits(manifest_mock, "/site-packages")
expected_calls = [
call(
"/site-packages/guardrails/hub/__init__.py",
"from guardrails_grhub_test_package import Validator, Helper",
),
]
mock_remove_line.assert_has_calls(expected_calls, any_order=True)
def test_uninstall_invalid_uri(mocker):
with pytest.raises(SystemExit):
mock_logger_error = mocker.patch("guardrails.cli.hub.uninstall.logger.error")
mocker.patch(
"guardrails.cli.hub.uninstall.get_validator_manifest",
return_value=manifest_mock,
)
mocker.patch(
"guardrails.cli.hub.utils.pip_process",
return_value={"Location": "/fake/site-packages"},
)
m_open = mock_open(read_data="import something")
mocker.patch("builtins.open", m_open)
mocker.patch("os.path.exists", return_value=True)
mocker.patch("subprocess.check_call")
from guardrails.cli.hub.uninstall import uninstall
uninstall("not a hub uri")
mock_logger_error.assert_called_once_with("Invalid URI!")
m_open.assert_called()
mock_subprocess_check_call = mocker.patch("subprocess.check_call")
mock_subprocess_check_call.assert_not_called()
def test_uninstall_valid_uri(mocker):
mocker.patch(
"guardrails.cli.hub.uninstall.get_validator_manifest",
return_value=manifest_mock,
)
mock_uninstall_hub_module = mocker.patch(
"guardrails.cli.hub.uninstall.uninstall_hub_module"
)
mocker.patch("guardrails.cli.hub.uninstall.console")
mock_unregister = mocker.patch(
"guardrails.hub.validator_package_service.ValidatorPackageService.unregister_validator"
)
from guardrails.cli.hub.uninstall import uninstall
uninstall("hub://guardrails/test-validator")
mock_uninstall_hub_module.assert_called_once_with(manifest_mock)
mock_unregister.assert_called_once_with("guardrails/test-validator")
def test_uninstall_hub_module(mocker):
mock_pip_process = mocker.patch("guardrails.cli.hub.uninstall.pip_process")
from guardrails.cli.hub.uninstall import uninstall_hub_module
uninstall_hub_module(manifest_mock)
mock_pip_process.assert_called_once_with(
"uninstall", "guardrails-grhub-test-package", flags=["-y"], quiet=True
)