|
28 | 28 |
|
29 | 29 | from airflow.models import Variable, crypto, variable |
30 | 30 | from airflow.sdk import SecretCache |
| 31 | +from airflow.secrets import BaseSecretsBackend |
31 | 32 | from airflow.secrets.metastore import MetastoreBackend |
32 | 33 |
|
33 | 34 | from tests_common.test_utils import db |
|
41 | 42 | pytestmark = pytest.mark.db_test |
42 | 43 |
|
43 | 44 |
|
| 45 | +class _TeamUnawareVariableBackend(BaseSecretsBackend): |
| 46 | + """A custom backend whose ``get_variable`` override predates the ``team_name`` keyword.""" |
| 47 | + |
| 48 | + def __init__(self): |
| 49 | + self.was_called = False |
| 50 | + |
| 51 | + # The signature mismatch with the base class is the point of this fixture, so mypy's |
| 52 | + # override check has to be waived here rather than fixed. |
| 53 | + def get_variable(self, key: str) -> str | None: # type: ignore[override] |
| 54 | + self.was_called = True |
| 55 | + return "secret_val" |
| 56 | + |
| 57 | + |
| 58 | +class _TeamAwareVariableBackend(BaseSecretsBackend): |
| 59 | + """A custom backend whose ``get_variable`` override accepts ``team_name``.""" |
| 60 | + |
| 61 | + def __init__(self): |
| 62 | + self.received_team_name: str | None = None |
| 63 | + |
| 64 | + def get_variable(self, key: str, team_name: str | None = None) -> str | None: |
| 65 | + self.received_team_name = team_name |
| 66 | + return "secret_val" |
| 67 | + |
| 68 | + |
44 | 69 | class TestVariable: |
45 | 70 | @pytest.fixture(autouse=True) |
46 | 71 | def setup_test_cases(self): |
@@ -192,6 +217,43 @@ def test_variable_set_with_extra_secret_backend(self, mock_ensure_secrets, caplo |
192 | 217 | ) |
193 | 218 | Variable.delete(key="key", session=session) |
194 | 219 |
|
| 220 | + @mock.patch("airflow.models.variable.ensure_secrets_loaded") |
| 221 | + def test_write_conflict_check_forwards_team_name(self, mock_ensure_secrets): |
| 222 | + """The check must resolve against the scope being written, not the global one.""" |
| 223 | + backend = _TeamAwareVariableBackend() |
| 224 | + mock_ensure_secrets.return_value = [backend, MetastoreBackend()] |
| 225 | + |
| 226 | + Variable.check_for_write_conflict(key="key", team_name="team_a") |
| 227 | + |
| 228 | + assert backend.received_team_name == "team_a" |
| 229 | + |
| 230 | + @mock.patch("airflow.models.variable.ensure_secrets_loaded") |
| 231 | + def test_write_conflict_check_tolerates_team_unaware_backend(self, mock_ensure_secrets): |
| 232 | + """A backend whose override predates ``team_name`` must still be consulted, not error out.""" |
| 233 | + backend = _TeamUnawareVariableBackend() |
| 234 | + mock_ensure_secrets.return_value = [backend, MetastoreBackend()] |
| 235 | + |
| 236 | + Variable.check_for_write_conflict(key="key", team_name="team_a") |
| 237 | + |
| 238 | + assert backend.was_called |
| 239 | + |
| 240 | + @conf_vars({("core", "multi_team"): "True"}) |
| 241 | + @mock.patch.object(Variable, "check_for_write_conflict") |
| 242 | + def test_set_forwards_team_name_to_write_conflict_check(self, mock_check, testing_team, session): |
| 243 | + Variable.set(key="key", value="db-value", team_name=testing_team.name, session=session) |
| 244 | + |
| 245 | + assert mock_check.call_args.kwargs["team_name"] == testing_team.name |
| 246 | + |
| 247 | + @conf_vars({("core", "multi_team"): "True"}) |
| 248 | + @mock.patch.object(Variable, "check_for_write_conflict") |
| 249 | + def test_update_forwards_team_name_to_write_conflict_check(self, mock_check, testing_team, session): |
| 250 | + Variable.set(key="key", value="db-value", team_name=testing_team.name, session=session) |
| 251 | + SecretCache.invalidate_variable("key") |
| 252 | + |
| 253 | + Variable.update(key="key", value="new-value", team_name=testing_team.name, session=session) |
| 254 | + |
| 255 | + assert mock_check.call_args.kwargs["team_name"] == testing_team.name |
| 256 | + |
195 | 257 | def test_variable_set_get_round_trip_json(self): |
196 | 258 | value = {"a": 17, "b": 47} |
197 | 259 | Variable.set(key="tested_var_set_id", value=value, serialize_json=True) |
|
0 commit comments