Copilot commented on code in PR #73808:
URL: https://github.com/apache/airflow/pull/73808#discussion_r4152210496


##########
airflow-ctl/tests/airflow_ctl/api/test_client.py:
##########
@@ -23,6 +23,7 @@
 import subprocess
 import sys
 import tempfile
+from urllib.parse import urlparse
 from unittest.mock import MagicMock, patch

Review Comment:
   Keep these standard-library imports in the repository's sorted order: 
`unittest` precedes `urllib` (for example, 
`task-sdk/tests/task_sdk/definitions/test_connection.py:21-22`). This ordering 
should otherwise fail the import-sorting check.



##########
airflow-ctl/tests/airflow_ctl/api/test_client.py:
##########
@@ -609,3 +610,59 @@ def test_zero_wait_is_allowed(self):
         assert result.returncode == 0, result.stderr
         assert result.stdout.strip() == "0"
         assert result.stderr == ""
+
+
+class TestGetClientEnvironment:
+    """Regression coverage for GH#70519.
+
+    ``get_client`` (and therefore every ``@provide_api_client``-decorated
+    command) must resolve the config file for the requested environment
+    instead of silently using production credentials for every command except
+    ``auth login``.
+    """
+
+    @staticmethod
+    def _write_environment_config(airflow_home, environment: str, api_url: 
str) -> None:
+        (airflow_home / 
f"{environment}.json").write_text(json.dumps({"api_url": api_url}), 
encoding="utf-8")
+
+    @pytest.fixture(autouse=True)
+    def environments(self, tmp_path):
+        """The module fixture already clears the environment; pin AIRFLOW_HOME 
to tmp_path."""
+        os.environ["AIRFLOW_HOME"] = str(tmp_path)
+        self._write_environment_config(tmp_path, "production", 
"https://prod.example.com";)
+        self._write_environment_config(tmp_path, "staging", 
"https://staging.example.com";)
+        yield tmp_path
+        del os.environ["AIRFLOW_HOME"]
+
+    def test_defaults_to_production(self):
+        with get_client(kind=ClientKind.CLI, api_token="TOKEN") as client:
+            assert urlparse(str(client.base_url)).hostname == 
"prod.example.com"
+
+    def test_uses_the_requested_environment(self):

Review Comment:
   This test still passes when the PR's source changes are reverted because 
`get_client` already defaulted to production. Repository testing standards 
require each added test to exercise changed behavior and fail without the fix, 
so remove this padding test.



##########
airflow-ctl/tests/airflow_ctl/ctl/commands/test_version_command.py:
##########
@@ -52,13 +52,20 @@ def test_ctl_version_remote(self, mock_client):
             assert "version" in stdout.getvalue()
             assert "git_version" in stdout.getvalue()
             assert "airflowctl_version" in stdout.getvalue()
-            mock_get_client.assert_called_once_with(kind=ClientKind.NO_AUTH)
+            mock_get_client.assert_called_once_with(kind=ClientKind.NO_AUTH, 
api_environment="production")
 
     def test_ctl_version_remote_with_api_token(self, mock_client):
         with mock.patch("airflowctl.ctl.commands.version_command.get_client") 
as mock_get_client:
             mock_get_client.return_value.__enter__.return_value = mock_client
             version_info(self.parser.parse_args(["version", "--remote", 
"--api-token", "TOKEN"]))
-            mock_get_client.assert_called_once_with(kind=ClientKind.NO_AUTH)
+            mock_get_client.assert_called_once_with(kind=ClientKind.NO_AUTH, 
api_environment="production")
+
+    def test_ctl_version_remote_with_env(self, mock_client):
+        """``--env`` selects which environment's credentials the remote call 
uses."""
+        with mock.patch("airflowctl.ctl.commands.version_command.get_client") 
as mock_get_client:

Review Comment:
   This docstring only restates the test name and invocation, so it adds no 
context that the code does not already convey. Remove it per the repository's 
non-narrating comment guidance.



##########
airflow-ctl/tests/airflow_ctl/api/test_client.py:
##########
@@ -609,3 +610,59 @@ def test_zero_wait_is_allowed(self):
         assert result.returncode == 0, result.stderr
         assert result.stdout.strip() == "0"
         assert result.stderr == ""
+
+
+class TestGetClientEnvironment:
+    """Regression coverage for GH#70519.
+
+    ``get_client`` (and therefore every ``@provide_api_client``-decorated
+    command) must resolve the config file for the requested environment
+    instead of silently using production credentials for every command except
+    ``auth login``.
+    """
+
+    @staticmethod
+    def _write_environment_config(airflow_home, environment: str, api_url: 
str) -> None:
+        (airflow_home / 
f"{environment}.json").write_text(json.dumps({"api_url": api_url}), 
encoding="utf-8")
+
+    @pytest.fixture(autouse=True)
+    def environments(self, tmp_path):
+        """The module fixture already clears the environment; pin AIRFLOW_HOME 
to tmp_path."""
+        os.environ["AIRFLOW_HOME"] = str(tmp_path)
+        self._write_environment_config(tmp_path, "production", 
"https://prod.example.com";)
+        self._write_environment_config(tmp_path, "staging", 
"https://staging.example.com";)
+        yield tmp_path
+        del os.environ["AIRFLOW_HOME"]
+
+    def test_defaults_to_production(self):
+        with get_client(kind=ClientKind.CLI, api_token="TOKEN") as client:
+            assert urlparse(str(client.base_url)).hostname == 
"prod.example.com"
+
+    def test_uses_the_requested_environment(self):
+        with get_client(kind=ClientKind.CLI, api_token="TOKEN", 
api_environment="staging") as client:
+            assert urlparse(str(client.base_url)).hostname == 
"staging.example.com"
+
+    def test_no_auth_uses_the_requested_environment(self):
+        with get_client(kind=ClientKind.NO_AUTH, api_environment="staging") as 
client:
+            assert urlparse(str(client.base_url)).hostname == 
"staging.example.com"
+
+    def test_environment_variable_beats_the_explicit_argument(self, 
monkeypatch):
+        """AIRFLOW_CLI_ENVIRONMENT keeps precedence, matching Credentials' own 
contract."""
+        monkeypatch.setenv("AIRFLOW_CLI_ENVIRONMENT", "staging")
+        with get_client(kind=ClientKind.CLI, api_token="TOKEN", 
api_environment="production") as client:
+            assert urlparse(str(client.base_url)).hostname == 
"staging.example.com"
+
+    def test_decorator_forwards_the_env_argument(self):
+        """``@provide_api_client`` reads ``--env`` off the parsed args 
namespace."""
+        from types import SimpleNamespace

Review Comment:
   This docstring merely repeats the test name and the assertion below. Remove 
it to follow the repository's rule against narrating comments.



##########
airflow-ctl/tests/airflow_ctl/api/test_client.py:
##########
@@ -609,3 +610,59 @@ def test_zero_wait_is_allowed(self):
         assert result.returncode == 0, result.stderr
         assert result.stdout.strip() == "0"
         assert result.stderr == ""
+
+
+class TestGetClientEnvironment:
+    """Regression coverage for GH#70519.
+
+    ``get_client`` (and therefore every ``@provide_api_client``-decorated
+    command) must resolve the config file for the requested environment
+    instead of silently using production credentials for every command except
+    ``auth login``.
+    """

Review Comment:
   Test docstrings must describe behavior rather than track issue numbers. 
Remove the `GH#70519` reference; the remaining prose only repeats the class and 
test names, so the whole class docstring can be removed.



##########
airflow-ctl/tests/airflow_ctl/api/test_client.py:
##########
@@ -609,3 +610,59 @@ def test_zero_wait_is_allowed(self):
         assert result.returncode == 0, result.stderr
         assert result.stdout.strip() == "0"
         assert result.stderr == ""
+
+
+class TestGetClientEnvironment:
+    """Regression coverage for GH#70519.
+
+    ``get_client`` (and therefore every ``@provide_api_client``-decorated
+    command) must resolve the config file for the requested environment
+    instead of silently using production credentials for every command except
+    ``auth login``.
+    """
+
+    @staticmethod
+    def _write_environment_config(airflow_home, environment: str, api_url: 
str) -> None:
+        (airflow_home / 
f"{environment}.json").write_text(json.dumps({"api_url": api_url}), 
encoding="utf-8")
+
+    @pytest.fixture(autouse=True)
+    def environments(self, tmp_path):
+        """The module fixture already clears the environment; pin AIRFLOW_HOME 
to tmp_path."""
+        os.environ["AIRFLOW_HOME"] = str(tmp_path)
+        self._write_environment_config(tmp_path, "production", 
"https://prod.example.com";)
+        self._write_environment_config(tmp_path, "staging", 
"https://staging.example.com";)
+        yield tmp_path
+        del os.environ["AIRFLOW_HOME"]
+
+    def test_defaults_to_production(self):
+        with get_client(kind=ClientKind.CLI, api_token="TOKEN") as client:
+            assert urlparse(str(client.base_url)).hostname == 
"prod.example.com"
+
+    def test_uses_the_requested_environment(self):
+        with get_client(kind=ClientKind.CLI, api_token="TOKEN", 
api_environment="staging") as client:
+            assert urlparse(str(client.base_url)).hostname == 
"staging.example.com"
+
+    def test_no_auth_uses_the_requested_environment(self):
+        with get_client(kind=ClientKind.NO_AUTH, api_environment="staging") as 
client:
+            assert urlparse(str(client.base_url)).hostname == 
"staging.example.com"
+
+    def test_environment_variable_beats_the_explicit_argument(self, 
monkeypatch):
+        """AIRFLOW_CLI_ENVIRONMENT keeps precedence, matching Credentials' own 
contract."""
+        monkeypatch.setenv("AIRFLOW_CLI_ENVIRONMENT", "staging")
+        with get_client(kind=ClientKind.CLI, api_token="TOKEN", 
api_environment="production") as client:
+            assert urlparse(str(client.base_url)).hostname == 
"staging.example.com"
+
+    def test_decorator_forwards_the_env_argument(self):
+        """``@provide_api_client`` reads ``--env`` off the parsed args 
namespace."""
+        from types import SimpleNamespace
+
+        from airflowctl.api.client import provide_api_client

Review Comment:
   These imports are unconditional and have no circular-import or lazy-loading 
requirement, so placing them inside the test violates the repository's 
top-level-import rule. Move `SimpleNamespace` and `provide_api_client` into the 
module import block.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to