From 4b85e113f825d4236749cc235cbc0183ed6b8695 Mon Sep 17 00:00:00 2001 From: tangou Date: Thu, 7 Aug 2025 21:01:35 +0800 Subject: [PATCH 1/5] test(vefaas): update Unitest of CloudAgentEngine and CloudApp --- docs/docs/deploy.md | 46 +++++++++++++++- tests/test_cloud.py | 131 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+), 2 deletions(-) create mode 100644 tests/test_cloud.py diff --git a/docs/docs/deploy.md b/docs/docs/deploy.md index 4f41ddfc..46a1661a 100644 --- a/docs/docs/deploy.md +++ b/docs/docs/deploy.md @@ -117,5 +117,47 @@ app = CloudApp(name="veadk-agent", endpoint=ENDPOINT) cloud_app.invoke(user_id=USER_ID, session_id=SESSION_ID, message=...) ``` -- 更新自身代码 -- 删除自身 +## 更新云应用 + +### 通过 CloudAgentEngine 更新 + +当您需要更新已部署的Agent代码时,可以使用`update_function_code`方法: + +```python +from veadk.cloud.cloud_agent_engine import CloudAgentEngine + +engine = CloudAgentEngine() + +# 更新现有应用的代码,保持相同的访问端点 +updated_cloud_app = engine.update_function_code( + application_name="my-agent-app", # 现有应用名称 + path="/my-agent-project" # 本地项目路径 +) + +print(f"应用已更新,访问地址:{updated_cloud_app.vefaas_endpoint}") +``` + +**注意事项:** +- 更新操作会保持相同的访问端点URL +- 确保项目路径包含`agent.py`文件 + + +## 删除云应用 + +### 通过 CloudAgentEngine 删除 + +```python +from veadk.cloud.cloud_agent_engine import CloudAgentEngine + +engine = CloudAgentEngine() + +# 删除指定的云应用 +engine.remove("my-agent-app") +``` + +执行时会提示确认: +``` +Confirm delete cloud app my-agent-app? (y/N): y +``` + +输入`y`确认删除,输入其他任何字符或直接回车则取消删除。 \ No newline at end of file diff --git a/tests/test_cloud.py b/tests/test_cloud.py new file mode 100644 index 00000000..25ff129f --- /dev/null +++ b/tests/test_cloud.py @@ -0,0 +1,131 @@ +# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import tempfile +import pytest +from unittest.mock import Mock, patch, AsyncMock + +from veadk.cloud.cloud_agent_engine import CloudAgentEngine + + +@pytest.mark.asyncio +async def test_cloud(): + app_name = "test-app" + key = "CloudTestIdentifier123" + test_endpoint = "https://test-endpoint.volcengine.com" + test_message = "Hello cloud agent" + + # Create temporary directory with required agent.py file for testing + with tempfile.TemporaryDirectory() as temp_dir: + with open(os.path.join(temp_dir, "agent.py"), "w") as f: + f.write(f"# Test agent implementation with {key}") + + with patch.dict( + os.environ, + { + "VOLCENGINE_ACCESS_KEY": "test_access_key", + "VOLCENGINE_SECRET_KEY": "test_secret_key", + }, + ): + # Mock shutil.copy to avoid template file copying issues + with patch("shutil.copy"): + with patch( + "veadk.cloud.cloud_agent_engine.VeFaaS" + ) as mock_vefaas_class: + # Setup mock VeFaaS service for all operations + mock_vefaas_service = Mock() + mock_vefaas_class.return_value = mock_vefaas_service + + # Mock deploy operation + mock_vefaas_service.deploy.return_value = ( + test_endpoint, + "app-123", + "func-456", + ) + + # Mock update operation + mock_vefaas_service._update_function_code.return_value = ( + test_endpoint, + "app-123", + "func-456", + ) + + # Mock remove operation + mock_vefaas_service.find_app_id_by_name.return_value = "app-123" + mock_vefaas_service.delete.return_value = None + + # Test CloudAgentEngine creation and deploy functionality + engine = CloudAgentEngine() + + # Test deploy operation + cloud_app = engine.deploy(application_name=app_name, path=temp_dir) + + # Verify deployment result contains expected values + assert cloud_app.vefaas_application_name == app_name + assert cloud_app.vefaas_endpoint == test_endpoint + assert cloud_app.vefaas_application_id == "app-123" + + # Test update_function_code operation + updated_app = engine.update_function_code( + application_name=app_name, path=temp_dir + ) + + # Verify update result maintains same endpoint + assert updated_app.vefaas_endpoint == test_endpoint + + # Test remove operation with mocked user input + with patch("builtins.input", return_value="y"): + engine.remove(app_name) + mock_vefaas_service.find_app_id_by_name.assert_called_with( + app_name + ) + mock_vefaas_service.delete.assert_called_with("app-123") + + # Test CloudApp message_send functionality + mock_response = Mock() + mock_message = Mock() + mock_response.root.result = mock_message + + with patch.object(cloud_app, "_get_a2a_client") as mock_get_client: + mock_client = AsyncMock() + mock_client.send_message = AsyncMock(return_value=mock_response) + mock_get_client.return_value = mock_client + + # Test message sending to cloud agent + result = await cloud_app.message_send( + message=test_message, + session_id="session-123", + user_id="user-456", + ) + + # Verify message sending result + assert result == mock_message + mock_client.send_message.assert_called_once() + + # Test CloudApp delete_self functionality + with patch("builtins.input", return_value="y"): + with patch( + "veadk.cli.services.vefaas.vefaas.VeFaaS" + ) as mock_vefaas_in_app: + mock_vefaas_client = Mock() + mock_vefaas_in_app.return_value = mock_vefaas_client + mock_vefaas_client.delete.return_value = None + + cloud_app.delete_self() + mock_vefaas_client.delete.assert_called_with("app-123") + + # Verify all mocks were called as expected + mock_vefaas_service.deploy.assert_called_once() + mock_vefaas_service._update_function_code.assert_called_once() From 23c332a2236b172c2815bdaa9acb063b3cae93d0 Mon Sep 17 00:00:00 2001 From: tangou Date: Thu, 7 Aug 2025 21:14:22 +0800 Subject: [PATCH 2/5] fix(test): test_cloud typer dependency --- tests/test_cloud.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test_cloud.py b/tests/test_cloud.py index 25ff129f..5c17261a 100644 --- a/tests/test_cloud.py +++ b/tests/test_cloud.py @@ -15,9 +15,13 @@ import os import tempfile import pytest +import sys + from unittest.mock import Mock, patch, AsyncMock -from veadk.cloud.cloud_agent_engine import CloudAgentEngine +sys.modules["typer"] = Mock() + +from veadk.cloud.cloud_agent_engine import CloudAgentEngine # noqa: E402 @pytest.mark.asyncio From 7d3400f2d4074bddf4efbdb1b550532d54c09d87 Mon Sep 17 00:00:00 2001 From: tangou Date: Thu, 7 Aug 2025 22:00:35 +0800 Subject: [PATCH 3/5] test(vefaas): volcenginesdkcore dependency, uv sync --all-extras --- .github/workflows/unit-tests.yaml | 2 +- tests/test_cloud.py | 186 ++++++++++++++---------------- 2 files changed, 89 insertions(+), 99 deletions(-) diff --git a/.github/workflows/unit-tests.yaml b/.github/workflows/unit-tests.yaml index 5ccab19c..71e9f6c2 100644 --- a/.github/workflows/unit-tests.yaml +++ b/.github/workflows/unit-tests.yaml @@ -49,7 +49,7 @@ jobs: run: | uv venv .venv source .venv/bin/activate - uv sync + uv sync --all-extras uv pip install -e . - name: Run unit tests with pytest diff --git a/tests/test_cloud.py b/tests/test_cloud.py index 5c17261a..6fe17802 100644 --- a/tests/test_cloud.py +++ b/tests/test_cloud.py @@ -15,13 +15,13 @@ import os import tempfile import pytest -import sys from unittest.mock import Mock, patch, AsyncMock -sys.modules["typer"] = Mock() +os.environ["VOLCENGINE_ACCESS_KEY"] = "test_access_key" +os.environ["VOLCENGINE_SECRET_KEY"] = "test_secret_key" -from veadk.cloud.cloud_agent_engine import CloudAgentEngine # noqa: E402 +from veadk.cloud.cloud_agent_engine import CloudAgentEngine @pytest.mark.asyncio @@ -36,100 +36,90 @@ async def test_cloud(): with open(os.path.join(temp_dir, "agent.py"), "w") as f: f.write(f"# Test agent implementation with {key}") - with patch.dict( - os.environ, - { - "VOLCENGINE_ACCESS_KEY": "test_access_key", - "VOLCENGINE_SECRET_KEY": "test_secret_key", - }, - ): - # Mock shutil.copy to avoid template file copying issues - with patch("shutil.copy"): - with patch( - "veadk.cloud.cloud_agent_engine.VeFaaS" - ) as mock_vefaas_class: - # Setup mock VeFaaS service for all operations - mock_vefaas_service = Mock() - mock_vefaas_class.return_value = mock_vefaas_service - - # Mock deploy operation - mock_vefaas_service.deploy.return_value = ( - test_endpoint, - "app-123", - "func-456", + # 这里不再需要 patch.dict,因为环境变量已经设置 + # Mock shutil.copy to avoid template file copying issues + with patch("shutil.copy"): + with patch("veadk.cloud.cloud_agent_engine.VeFaaS") as mock_vefaas_class: + # Setup mock VeFaaS service for all operations + mock_vefaas_service = Mock() + mock_vefaas_class.return_value = mock_vefaas_service + + # Mock deploy operation + mock_vefaas_service.deploy.return_value = ( + test_endpoint, + "app-123", + "func-456", + ) + + # Mock update operation + mock_vefaas_service._update_function_code.return_value = ( + test_endpoint, + "app-123", + "func-456", + ) + + # Mock remove operation + mock_vefaas_service.find_app_id_by_name.return_value = "app-123" + mock_vefaas_service.delete.return_value = None + + # Test CloudAgentEngine creation and deploy functionality + engine = CloudAgentEngine() + + # Test deploy operation + cloud_app = engine.deploy(application_name=app_name, path=temp_dir) + + # Verify deployment result contains expected values + assert cloud_app.vefaas_application_name == app_name + assert cloud_app.vefaas_endpoint == test_endpoint + assert cloud_app.vefaas_application_id == "app-123" + + # Test update_function_code operation + updated_app = engine.update_function_code( + application_name=app_name, path=temp_dir + ) + + # Verify update result maintains same endpoint + assert updated_app.vefaas_endpoint == test_endpoint + + # Test remove operation with mocked user input + with patch("builtins.input", return_value="y"): + engine.remove(app_name) + mock_vefaas_service.find_app_id_by_name.assert_called_with(app_name) + mock_vefaas_service.delete.assert_called_with("app-123") + + # Test CloudApp message_send functionality + mock_response = Mock() + mock_message = Mock() + mock_response.root.result = mock_message + + with patch.object(cloud_app, "_get_a2a_client") as mock_get_client: + mock_client = AsyncMock() + mock_client.send_message = AsyncMock(return_value=mock_response) + mock_get_client.return_value = mock_client + + # Test message sending to cloud agent + result = await cloud_app.message_send( + message=test_message, + session_id="session-123", + user_id="user-456", ) - # Mock update operation - mock_vefaas_service._update_function_code.return_value = ( - test_endpoint, - "app-123", - "func-456", - ) - - # Mock remove operation - mock_vefaas_service.find_app_id_by_name.return_value = "app-123" - mock_vefaas_service.delete.return_value = None - - # Test CloudAgentEngine creation and deploy functionality - engine = CloudAgentEngine() - - # Test deploy operation - cloud_app = engine.deploy(application_name=app_name, path=temp_dir) - - # Verify deployment result contains expected values - assert cloud_app.vefaas_application_name == app_name - assert cloud_app.vefaas_endpoint == test_endpoint - assert cloud_app.vefaas_application_id == "app-123" - - # Test update_function_code operation - updated_app = engine.update_function_code( - application_name=app_name, path=temp_dir - ) - - # Verify update result maintains same endpoint - assert updated_app.vefaas_endpoint == test_endpoint - - # Test remove operation with mocked user input - with patch("builtins.input", return_value="y"): - engine.remove(app_name) - mock_vefaas_service.find_app_id_by_name.assert_called_with( - app_name - ) - mock_vefaas_service.delete.assert_called_with("app-123") - - # Test CloudApp message_send functionality - mock_response = Mock() - mock_message = Mock() - mock_response.root.result = mock_message - - with patch.object(cloud_app, "_get_a2a_client") as mock_get_client: - mock_client = AsyncMock() - mock_client.send_message = AsyncMock(return_value=mock_response) - mock_get_client.return_value = mock_client - - # Test message sending to cloud agent - result = await cloud_app.message_send( - message=test_message, - session_id="session-123", - user_id="user-456", - ) - - # Verify message sending result - assert result == mock_message - mock_client.send_message.assert_called_once() - - # Test CloudApp delete_self functionality - with patch("builtins.input", return_value="y"): - with patch( - "veadk.cli.services.vefaas.vefaas.VeFaaS" - ) as mock_vefaas_in_app: - mock_vefaas_client = Mock() - mock_vefaas_in_app.return_value = mock_vefaas_client - mock_vefaas_client.delete.return_value = None - - cloud_app.delete_self() - mock_vefaas_client.delete.assert_called_with("app-123") - - # Verify all mocks were called as expected - mock_vefaas_service.deploy.assert_called_once() - mock_vefaas_service._update_function_code.assert_called_once() + # Verify message sending result + assert result == mock_message + mock_client.send_message.assert_called_once() + + # Test CloudApp delete_self functionality + with patch("builtins.input", return_value="y"): + with patch( + "veadk.cli.services.vefaas.vefaas.VeFaaS" + ) as mock_vefaas_in_app: + mock_vefaas_client = Mock() + mock_vefaas_in_app.return_value = mock_vefaas_client + mock_vefaas_client.delete.return_value = None + + cloud_app.delete_self() + mock_vefaas_client.delete.assert_called_with("app-123") + + # Verify all mocks were called as expected + mock_vefaas_service.deploy.assert_called_once() + mock_vefaas_service._update_function_code.assert_called_once() From b63aeb8569eb9b239b49a100ffa05d4d479bb842 Mon Sep 17 00:00:00 2001 From: tangou Date: Fri, 8 Aug 2025 10:12:23 +0800 Subject: [PATCH 4/5] test(vefaas): modify chinese annotation --- tests/test_cloud.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_cloud.py b/tests/test_cloud.py index 6fe17802..0c7b0415 100644 --- a/tests/test_cloud.py +++ b/tests/test_cloud.py @@ -36,7 +36,6 @@ async def test_cloud(): with open(os.path.join(temp_dir, "agent.py"), "w") as f: f.write(f"# Test agent implementation with {key}") - # 这里不再需要 patch.dict,因为环境变量已经设置 # Mock shutil.copy to avoid template file copying issues with patch("shutil.copy"): with patch("veadk.cloud.cloud_agent_engine.VeFaaS") as mock_vefaas_class: From 97d4d0c2353d63b3a037887fd7dd290ceb29bbb0 Mon Sep 17 00:00:00 2001 From: tangou Date: Fri, 8 Aug 2025 10:54:23 +0800 Subject: [PATCH 5/5] test(vefaas): fix deploy docs --- docs/docs/deploy.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/deploy.md b/docs/docs/deploy.md index 46a1661a..79aa7b29 100644 --- a/docs/docs/deploy.md +++ b/docs/docs/deploy.md @@ -134,7 +134,7 @@ updated_cloud_app = engine.update_function_code( path="/my-agent-project" # 本地项目路径 ) -print(f"应用已更新,访问地址:{updated_cloud_app.vefaas_endpoint}") +# 可以使用updated_cloud_app.vefaas_endpoint访问您的项目 ``` **注意事项:** @@ -152,7 +152,7 @@ from veadk.cloud.cloud_agent_engine import CloudAgentEngine engine = CloudAgentEngine() # 删除指定的云应用 -engine.remove("my-agent-app") +engine.remove(app_name="my-agent-app") ``` 执行时会提示确认: