Files
2026-07-13 13:22:34 +08:00

151 lines
4.8 KiB
Python

from unittest import mock
from unittest.mock import patch
import pytest
from mlflow import get_tracking_uri
from mlflow.environment_variables import MLFLOW_TRACKING_PASSWORD, MLFLOW_TRACKING_USERNAME
from mlflow.exceptions import MlflowException
from mlflow.utils.credentials import login, read_mlflow_creds
def test_read_mlflow_creds_file(tmp_path, monkeypatch):
monkeypatch.delenv(MLFLOW_TRACKING_USERNAME.name, raising=False)
monkeypatch.delenv(MLFLOW_TRACKING_PASSWORD.name, raising=False)
creds_file = tmp_path.joinpath("credentials")
with mock.patch("mlflow.utils.credentials._get_credentials_path", return_value=str(creds_file)):
# credentials file does not exist
creds = read_mlflow_creds()
assert creds.username is None
assert creds.password is None
# credentials file is empty
creds = read_mlflow_creds()
assert creds.username is None
assert creds.password is None
# password is missing
creds_file.write_text(
"""
[mlflow]
mlflow_tracking_username = username
"""
)
creds = read_mlflow_creds()
assert creds.username == "username"
assert creds.password is None
# username is missing
creds_file.write_text(
"""
[mlflow]
mlflow_tracking_password = password
"""
)
creds = read_mlflow_creds()
assert creds.username is None
assert creds.password == "password"
# valid credentials
creds_file.write_text(
"""
[mlflow]
mlflow_tracking_username = username
mlflow_tracking_password = password
"""
)
creds = read_mlflow_creds()
assert creds is not None
assert creds.username == "username"
assert creds.password == "password"
@pytest.mark.parametrize(
("username", "password"),
[
("username", "password"),
("username", None),
(None, "password"),
(None, None),
],
)
def test_read_mlflow_creds_env(username, password, monkeypatch):
if username is None:
monkeypatch.delenv(MLFLOW_TRACKING_USERNAME.name, raising=False)
else:
monkeypatch.setenv(MLFLOW_TRACKING_USERNAME.name, username)
if password is None:
monkeypatch.delenv(MLFLOW_TRACKING_PASSWORD.name, raising=False)
else:
monkeypatch.setenv(MLFLOW_TRACKING_PASSWORD.name, password)
creds = read_mlflow_creds()
assert creds.username == username
assert creds.password == password
def test_read_mlflow_creds_env_takes_precedence_over_file(tmp_path, monkeypatch):
monkeypatch.setenv(MLFLOW_TRACKING_USERNAME.name, "username_env")
monkeypatch.setenv(MLFLOW_TRACKING_PASSWORD.name, "password_env")
creds_file = tmp_path.joinpath("credentials")
with mock.patch("mlflow.utils.credentials._get_credentials_path", return_value=str(creds_file)):
creds_file.write_text(
"""
[mlflow]
mlflow_tracking_username = username_file
mlflow_tracking_password = password_file
"""
)
creds = read_mlflow_creds()
assert creds.username == "username_env"
assert creds.password == "password_env"
def test_mlflow_login(tmp_path, monkeypatch):
# Mock `input()` and `getpass()` to return host, username and password in order.
with (
patch(
"builtins.input",
side_effect=["https://community.cloud.databricks.com/", "dummyusername"],
),
patch("getpass.getpass", side_effect=["dummypassword"]),
):
file_name = f"{tmp_path}/.databrickscfg"
profile = "TEST"
monkeypatch.setenv("DATABRICKS_CONFIG_FILE", file_name)
monkeypatch.setenv("DATABRICKS_CONFIG_PROFILE", profile)
def success():
return
with patch(
"mlflow.utils.credentials._validate_databricks_auth",
side_effect=[MlflowException("Invalid databricks credentials."), success()],
):
login("databricks")
with open(file_name) as f:
lines = f.readlines()
assert lines[0] == "[TEST]\n"
assert lines[1] == "host = https://community.cloud.databricks.com/\n"
assert lines[2] == "username = dummyusername\n"
assert lines[3] == "password = dummypassword\n"
# Assert that the tracking URI is set to the databricks.
assert get_tracking_uri() == "databricks"
def test_mlflow_login_noninteractive():
# Forces mlflow.utils.credentials._validate_databricks_auth to raise `MlflowException()`
with patch(
"mlflow.utils.credentials._validate_databricks_auth",
side_effect=MlflowException("Failed to validate databricks credentials."),
):
with pytest.raises(
MlflowException,
match="No valid Databricks credentials found while running in non-interactive mode",
):
login(backend="databricks", interactive=False)