scalebox-sdk 0.1.25__py3-none-any.whl → 1.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- scalebox/__init__.py +2 -2
- scalebox/api/__init__.py +3 -1
- scalebox/api/client/api/sandboxes/get_sandboxes.py +1 -1
- scalebox/api/client/api/sandboxes/post_sandboxes_sandbox_id_connect.py +193 -0
- scalebox/api/client/models/connect_sandbox.py +59 -0
- scalebox/api/client/models/error.py +2 -2
- scalebox/api/client/models/listed_sandbox.py +24 -3
- scalebox/api/client/models/new_sandbox.py +10 -0
- scalebox/api/client/models/sandbox.py +13 -0
- scalebox/api/client/models/sandbox_detail.py +24 -0
- scalebox/cli.py +125 -125
- scalebox/client/aclient.py +57 -57
- scalebox/client/client.py +102 -102
- scalebox/code_interpreter/__init__.py +12 -12
- scalebox/code_interpreter/charts.py +230 -230
- scalebox/code_interpreter/code_interpreter_async.py +3 -1
- scalebox/code_interpreter/code_interpreter_sync.py +3 -1
- scalebox/code_interpreter/constants.py +3 -3
- scalebox/code_interpreter/exceptions.py +13 -13
- scalebox/code_interpreter/models.py +485 -485
- scalebox/connection_config.py +36 -1
- scalebox/csx_connect/__init__.py +1 -1
- scalebox/csx_connect/client.py +485 -485
- scalebox/csx_desktop/main.py +651 -651
- scalebox/exceptions.py +83 -83
- scalebox/generated/api.py +61 -61
- scalebox/generated/api_pb2.py +203 -203
- scalebox/generated/api_pb2.pyi +956 -956
- scalebox/generated/api_pb2_connect.py +1407 -1407
- scalebox/generated/rpc.py +50 -50
- scalebox/sandbox/main.py +146 -139
- scalebox/sandbox/sandbox_api.py +105 -91
- scalebox/sandbox/signature.py +40 -40
- scalebox/sandbox/utils.py +34 -34
- scalebox/sandbox_async/main.py +226 -44
- scalebox/sandbox_async/sandbox_api.py +124 -3
- scalebox/sandbox_sync/main.py +205 -130
- scalebox/sandbox_sync/sandbox_api.py +119 -3
- scalebox/test/CODE_INTERPRETER_TESTS_READY.md +323 -323
- scalebox/test/README.md +329 -329
- scalebox/test/bedrock_openai_adapter.py +73 -0
- scalebox/test/code_interpreter_test.py +34 -34
- scalebox/test/code_interpreter_test_sync.py +34 -34
- scalebox/test/run_stress_code_interpreter_sync.py +178 -0
- scalebox/test/simple_upload_example.py +131 -0
- scalebox/test/stabitiy_test.py +323 -0
- scalebox/test/test_browser_use.py +27 -0
- scalebox/test/test_browser_use_scalebox.py +62 -0
- scalebox/test/test_code_interpreter_execcode.py +289 -211
- scalebox/test/test_code_interpreter_sync_comprehensive.py +116 -69
- scalebox/test/test_connect_pause_async.py +300 -0
- scalebox/test/test_connect_pause_sync.py +300 -0
- scalebox/test/test_csx_desktop_examples.py +3 -3
- scalebox/test/test_desktop_sandbox_sf.py +112 -0
- scalebox/test/test_download_url.py +41 -0
- scalebox/test/test_existing_sandbox.py +1037 -0
- scalebox/test/test_sandbox_async_comprehensive.py +5 -3
- scalebox/test/test_sandbox_object_storage_example.py +151 -0
- scalebox/test/test_sandbox_object_storage_example_async.py +159 -0
- scalebox/test/test_sandbox_sync_comprehensive.py +1 -1
- scalebox/test/test_sf.py +141 -0
- scalebox/test/test_watch_dir_async.py +58 -0
- scalebox/test/testacreate.py +1 -1
- scalebox/test/testagetinfo.py +1 -3
- scalebox/test/testcomputeuse.py +243 -243
- scalebox/test/testsandbox_api.py +5 -5
- scalebox/test/testsandbox_async.py +17 -47
- scalebox/test/testsandbox_sync.py +19 -15
- scalebox/test/upload_100mb_example.py +377 -0
- scalebox/utils/httpcoreclient.py +297 -297
- scalebox/utils/httpxclient.py +403 -403
- scalebox/version.py +2 -2
- {scalebox_sdk-0.1.25.dist-info → scalebox_sdk-1.0.2.dist-info}/METADATA +1 -1
- {scalebox_sdk-0.1.25.dist-info → scalebox_sdk-1.0.2.dist-info}/RECORD +78 -60
- {scalebox_sdk-0.1.25.dist-info → scalebox_sdk-1.0.2.dist-info}/WHEEL +1 -1
- {scalebox_sdk-0.1.25.dist-info → scalebox_sdk-1.0.2.dist-info}/entry_points.txt +0 -0
- {scalebox_sdk-0.1.25.dist-info → scalebox_sdk-1.0.2.dist-info}/licenses/LICENSE +0 -0
- {scalebox_sdk-0.1.25.dist-info → scalebox_sdk-1.0.2.dist-info}/top_level.txt +0 -0
|
@@ -106,8 +106,9 @@ result = {"x": x, "y": y}
|
|
|
106
106
|
print(result)
|
|
107
107
|
"""
|
|
108
108
|
|
|
109
|
-
execution = self.sandbox.run_code(code,
|
|
109
|
+
execution = self.sandbox.run_code(code, envs={"CI_TEST": "sync_test1"})
|
|
110
110
|
print(execution.to_json())
|
|
111
|
+
# time.sleep(1000)
|
|
111
112
|
assert isinstance(execution, Execution)
|
|
112
113
|
assert execution.error is None
|
|
113
114
|
assert len(execution.logs.stdout) > 0
|
|
@@ -147,7 +148,7 @@ print(f"标准差: {std_val:.3f}")
|
|
|
147
148
|
}
|
|
148
149
|
"""
|
|
149
150
|
|
|
150
|
-
execution = self.sandbox.run_code(code
|
|
151
|
+
execution = self.sandbox.run_code(code)
|
|
151
152
|
print(execution.to_json())
|
|
152
153
|
assert execution.error is None
|
|
153
154
|
assert any("圆的面积" in line for line in execution.logs.stdout)
|
|
@@ -197,7 +198,7 @@ result = {
|
|
|
197
198
|
print(f"\\n处理结果: {json.dumps(result, indent=2)}")
|
|
198
199
|
"""
|
|
199
200
|
|
|
200
|
-
execution = self.sandbox.run_code(code
|
|
201
|
+
execution = self.sandbox.run_code(code)
|
|
201
202
|
print(execution.to_json())
|
|
202
203
|
assert execution.error is None
|
|
203
204
|
assert any("原始数据" in line for line in execution.logs.stdout)
|
|
@@ -365,10 +366,7 @@ print(f"结果: {result}")
|
|
|
365
366
|
assert self.sandbox is not None
|
|
366
367
|
|
|
367
368
|
# 创建Python上下文
|
|
368
|
-
python_context = self.sandbox.create_code_context(
|
|
369
|
-
language="python",
|
|
370
|
-
cwd="/tmp"
|
|
371
|
-
)
|
|
369
|
+
python_context = self.sandbox.create_code_context(language="python", cwd="/tmp")
|
|
372
370
|
print(python_context)
|
|
373
371
|
assert isinstance(python_context, Context)
|
|
374
372
|
assert python_context.id is not None
|
|
@@ -799,6 +797,55 @@ print("生成HTML格式结果")
|
|
|
799
797
|
from IPython.display import HTML
|
|
800
798
|
HTML(html_content)
|
|
801
799
|
"""
|
|
800
|
+
# code = """
|
|
801
|
+
# import sys
|
|
802
|
+
# try:
|
|
803
|
+
# import matplotlib
|
|
804
|
+
# matplotlib.use('Agg') # Non-interactive backend
|
|
805
|
+
# import matplotlib.pyplot as plt
|
|
806
|
+
# import numpy as np
|
|
807
|
+
# from IPython.display import display
|
|
808
|
+
#
|
|
809
|
+
# print("✅ All imports successful")
|
|
810
|
+
#
|
|
811
|
+
# # Generate a simple plot
|
|
812
|
+
# x = np.linspace(0, 2 * np.pi, 100)
|
|
813
|
+
# y = np.sin(x)
|
|
814
|
+
#
|
|
815
|
+
# fig, ax = plt.subplots(figsize=(8, 6))
|
|
816
|
+
# ax.plot(x, y, 'b-', linewidth=2, label='sin(x)')
|
|
817
|
+
# ax.set_xlabel('X axis')
|
|
818
|
+
# ax.set_ylabel('Y axis')
|
|
819
|
+
# ax.set_title('Sine Wave - Test Plot')
|
|
820
|
+
# ax.legend()
|
|
821
|
+
# ax.grid(True, alpha=0.3)
|
|
822
|
+
#
|
|
823
|
+
# plt.tight_layout()
|
|
824
|
+
#
|
|
825
|
+
# # 🎯 KEY: Save to bytes and display as Image
|
|
826
|
+
# import io
|
|
827
|
+
# from IPython.display import Image
|
|
828
|
+
#
|
|
829
|
+
# buf = io.BytesIO()
|
|
830
|
+
# plt.savefig(buf, format='png', bbox_inches='tight', dpi=100)
|
|
831
|
+
# buf.seek(0)
|
|
832
|
+
# plt.close(fig)
|
|
833
|
+
#
|
|
834
|
+
# # Display the image data
|
|
835
|
+
# display(Image(data=buf.getvalue()))
|
|
836
|
+
# #
|
|
837
|
+
# # print("SUCCESS: Plot generated and displayed")
|
|
838
|
+
# # "plot_created"
|
|
839
|
+
#
|
|
840
|
+
# except ImportError as e:
|
|
841
|
+
# print(f"SKIP: matplotlib not available - {e}")
|
|
842
|
+
# "matplotlib_not_installed"
|
|
843
|
+
# except Exception as e:
|
|
844
|
+
# print(f"ERROR: {e}")
|
|
845
|
+
# import traceback
|
|
846
|
+
# traceback.print_exc()
|
|
847
|
+
# "error_occurred"
|
|
848
|
+
# """
|
|
802
849
|
|
|
803
850
|
execution = self.sandbox.run_code(code, language="python")
|
|
804
851
|
print(execution.to_json())
|
|
@@ -3292,68 +3339,68 @@ print(f"\\n完成 {len(results)} 个API调用")
|
|
|
3292
3339
|
self.test_file_operations_simulation, "File Operations Simulation"
|
|
3293
3340
|
)
|
|
3294
3341
|
|
|
3295
|
-
# 性能测试
|
|
3296
|
-
self.run_test(
|
|
3297
|
-
|
|
3298
|
-
)
|
|
3299
|
-
self.run_test(
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
)
|
|
3303
|
-
|
|
3304
|
-
# 结果格式测试
|
|
3305
|
-
self.run_test(self.test_text_result, "Text Result Format")
|
|
3306
|
-
self.run_test(self.test_html_result, "HTML Result Format")
|
|
3307
|
-
self.run_test(self.test_markdown_result, "Markdown Result Format")
|
|
3308
|
-
self.run_test(self.test_svg_result, "SVG Result Format")
|
|
3309
|
-
self.run_test(self.test_image_results, "Image Result Formats (PNG/JPEG)")
|
|
3310
|
-
self.run_test(self.test_latex_result, "LaTeX Result Format")
|
|
3311
|
-
self.run_test(self.test_json_data_result, "JSON Data Result Format")
|
|
3312
|
-
self.run_test(self.test_javascript_result, "JavaScript Result Format")
|
|
3313
|
-
self.run_test(self.test_chart_data_result, "Chart Data Result Format")
|
|
3314
|
-
self.run_test(self.test_mixed_format_result, "Mixed Format Result")
|
|
3315
|
-
|
|
3316
|
-
# R语言测试
|
|
3317
|
-
self.run_test(
|
|
3318
|
-
|
|
3319
|
-
)
|
|
3320
|
-
self.run_test(self.test_r_language_data_analysis, "R Language Data Analysis")
|
|
3321
|
-
self.run_test(self.test_r_language_visualization, "R Language Visualization")
|
|
3322
|
-
self.run_test(self.test_r_language_statistics, "R Language Statistics")
|
|
3323
|
-
self.run_test(
|
|
3324
|
-
|
|
3325
|
-
)
|
|
3326
|
-
|
|
3327
|
-
# Node.js/JavaScript 测试
|
|
3328
|
-
self.run_test(self.test_nodejs_basic_execution, "Node.js Basic Execution")
|
|
3329
|
-
self.run_test(self.test_nodejs_async_promises, "Node.js Async Promises")
|
|
3330
|
-
self.run_test(self.test_nodejs_data_processing, "Node.js Data Processing")
|
|
3331
|
-
self.run_test(self.test_nodejs_chart_data, "Node.js Chart Data Generation")
|
|
3332
|
-
self.run_test(self.test_nodejs_context_management, "Node.js Context Management")
|
|
3333
|
-
|
|
3334
|
-
# Bash 测试
|
|
3335
|
-
self.run_test(self.test_bash_basic_execution, "Bash Basic Execution")
|
|
3336
|
-
self.run_test(self.test_bash_file_operations, "Bash File Operations")
|
|
3337
|
-
self.run_test(self.test_bash_pipelines_and_grep, "Bash Pipelines and Grep")
|
|
3338
|
-
# self.run_test(self.test_bash_env_and_exit_codes, "Bash Env and Exit Codes")
|
|
3339
|
-
self.run_test(self.test_bash_context_management, "Bash Context Management")
|
|
3340
|
-
|
|
3341
|
-
# IJAVA 测试
|
|
3342
|
-
self.run_test(self.test_ijava_basic_execution, "IJAVA Basic Execution")
|
|
3343
|
-
self.run_test(self.test_ijava_oop_features, "IJAVA OOP Features")
|
|
3344
|
-
self.run_test(self.test_ijava_collections, "IJAVA Collections")
|
|
3345
|
-
self.run_test(self.test_ijava_file_io, "IJAVA File I/O")
|
|
3346
|
-
self.run_test(self.test_ijava_context_management, "IJAVA Context Management")
|
|
3347
|
-
|
|
3348
|
-
# Deno 测试
|
|
3349
|
-
self.run_test(self.test_deno_basic_execution, "Deno Basic Execution")
|
|
3350
|
-
self.run_test(self.test_deno_typescript_features, "Deno TypeScript Features")
|
|
3351
|
-
self.run_test(self.test_deno_async_await, "Deno Async/Await")
|
|
3352
|
-
self.run_test(self.test_deno_file_operations, "Deno File Operations")
|
|
3353
|
-
self.run_test(self.test_deno_context_management, "Deno Context Management")
|
|
3354
|
-
|
|
3355
|
-
# 高级功能测试
|
|
3356
|
-
self.run_test(self.test_web_request_simulation, "Web Request Simulation")
|
|
3342
|
+
# # 性能测试
|
|
3343
|
+
# self.run_test(
|
|
3344
|
+
# self.test_performance_simple_calculations, "Performance Simple Calculations"
|
|
3345
|
+
# )
|
|
3346
|
+
# self.run_test(
|
|
3347
|
+
# self.test_performance_concurrent_simulation,
|
|
3348
|
+
# "Performance Concurrent Simulation",
|
|
3349
|
+
# )
|
|
3350
|
+
|
|
3351
|
+
# # 结果格式测试
|
|
3352
|
+
# self.run_test(self.test_text_result, "Text Result Format")
|
|
3353
|
+
# self.run_test(self.test_html_result, "HTML Result Format")
|
|
3354
|
+
# self.run_test(self.test_markdown_result, "Markdown Result Format")
|
|
3355
|
+
# self.run_test(self.test_svg_result, "SVG Result Format")
|
|
3356
|
+
# self.run_test(self.test_image_results, "Image Result Formats (PNG/JPEG)")
|
|
3357
|
+
# self.run_test(self.test_latex_result, "LaTeX Result Format")
|
|
3358
|
+
# self.run_test(self.test_json_data_result, "JSON Data Result Format")
|
|
3359
|
+
# self.run_test(self.test_javascript_result, "JavaScript Result Format")
|
|
3360
|
+
# self.run_test(self.test_chart_data_result, "Chart Data Result Format")
|
|
3361
|
+
# self.run_test(self.test_mixed_format_result, "Mixed Format Result")
|
|
3362
|
+
#
|
|
3363
|
+
# # R语言测试
|
|
3364
|
+
# self.run_test(
|
|
3365
|
+
# self.test_r_language_basic_execution, "R Language Basic Execution"
|
|
3366
|
+
# )
|
|
3367
|
+
# self.run_test(self.test_r_language_data_analysis, "R Language Data Analysis")
|
|
3368
|
+
# self.run_test(self.test_r_language_visualization, "R Language Visualization")
|
|
3369
|
+
# self.run_test(self.test_r_language_statistics, "R Language Statistics")
|
|
3370
|
+
# self.run_test(
|
|
3371
|
+
# self.test_r_language_context_management, "R Language Context Management"
|
|
3372
|
+
# )
|
|
3373
|
+
#
|
|
3374
|
+
# # Node.js/JavaScript 测试
|
|
3375
|
+
# self.run_test(self.test_nodejs_basic_execution, "Node.js Basic Execution")
|
|
3376
|
+
# self.run_test(self.test_nodejs_async_promises, "Node.js Async Promises")
|
|
3377
|
+
# self.run_test(self.test_nodejs_data_processing, "Node.js Data Processing")
|
|
3378
|
+
# self.run_test(self.test_nodejs_chart_data, "Node.js Chart Data Generation")
|
|
3379
|
+
# self.run_test(self.test_nodejs_context_management, "Node.js Context Management")
|
|
3380
|
+
#
|
|
3381
|
+
# # Bash 测试
|
|
3382
|
+
# self.run_test(self.test_bash_basic_execution, "Bash Basic Execution")
|
|
3383
|
+
# self.run_test(self.test_bash_file_operations, "Bash File Operations")
|
|
3384
|
+
# self.run_test(self.test_bash_pipelines_and_grep, "Bash Pipelines and Grep")
|
|
3385
|
+
# # self.run_test(self.test_bash_env_and_exit_codes, "Bash Env and Exit Codes")
|
|
3386
|
+
# self.run_test(self.test_bash_context_management, "Bash Context Management")
|
|
3387
|
+
#
|
|
3388
|
+
# # IJAVA 测试
|
|
3389
|
+
# self.run_test(self.test_ijava_basic_execution, "IJAVA Basic Execution")
|
|
3390
|
+
# self.run_test(self.test_ijava_oop_features, "IJAVA OOP Features")
|
|
3391
|
+
# self.run_test(self.test_ijava_collections, "IJAVA Collections")
|
|
3392
|
+
# self.run_test(self.test_ijava_file_io, "IJAVA File I/O")
|
|
3393
|
+
# self.run_test(self.test_ijava_context_management, "IJAVA Context Management")
|
|
3394
|
+
#
|
|
3395
|
+
# # Deno 测试
|
|
3396
|
+
# self.run_test(self.test_deno_basic_execution, "Deno Basic Execution")
|
|
3397
|
+
# # self.run_test(self.test_deno_typescript_features, "Deno TypeScript Features")
|
|
3398
|
+
# self.run_test(self.test_deno_async_await, "Deno Async/Await")
|
|
3399
|
+
# self.run_test(self.test_deno_file_operations, "Deno File Operations")
|
|
3400
|
+
# self.run_test(self.test_deno_context_management, "Deno Context Management")
|
|
3401
|
+
#
|
|
3402
|
+
# # 高级功能测试
|
|
3403
|
+
# self.run_test(self.test_web_request_simulation, "Web Request Simulation")
|
|
3357
3404
|
|
|
3358
3405
|
def cleanup(self):
|
|
3359
3406
|
"""清理资源"""
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Test cases for connect and pause functionality (asynchronous version).
|
|
4
|
+
|
|
5
|
+
This test suite validates:
|
|
6
|
+
- Creating a sandbox
|
|
7
|
+
- Adding and deleting files before pause
|
|
8
|
+
- Pausing the sandbox
|
|
9
|
+
- Connecting to the paused sandbox
|
|
10
|
+
- Verifying file operations after connect
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import asyncio
|
|
14
|
+
import time
|
|
15
|
+
|
|
16
|
+
from scalebox.sandbox.filesystem.filesystem import WriteInfo, FileType
|
|
17
|
+
from scalebox.sandbox_async.main import AsyncSandbox
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ConnectPauseAsyncTest:
|
|
21
|
+
"""Test suite for connect and pause functionality (async)."""
|
|
22
|
+
|
|
23
|
+
def __init__(self):
|
|
24
|
+
self.sandbox = None
|
|
25
|
+
self.sandbox_id = None
|
|
26
|
+
self.test_results = []
|
|
27
|
+
|
|
28
|
+
def log_test_result(self, test_name: str, success: bool, message: str = ""):
|
|
29
|
+
"""记录测试结果"""
|
|
30
|
+
status = "✅ PASS" if success else "❌ FAIL"
|
|
31
|
+
result = {
|
|
32
|
+
"test": test_name,
|
|
33
|
+
"success": success,
|
|
34
|
+
"message": message,
|
|
35
|
+
}
|
|
36
|
+
self.test_results.append(result)
|
|
37
|
+
print(f"{status} {test_name}: {message}")
|
|
38
|
+
|
|
39
|
+
async def test_connect_pause_with_files(self):
|
|
40
|
+
"""测试connect和pause功能,包含文件操作"""
|
|
41
|
+
try:
|
|
42
|
+
# 1. 创建沙箱
|
|
43
|
+
print("Step 1: Creating sandbox...")
|
|
44
|
+
self.sandbox = await AsyncSandbox.create(
|
|
45
|
+
template="base",
|
|
46
|
+
timeout=3600,
|
|
47
|
+
metadata={"test": "connect_pause_async"},
|
|
48
|
+
envs={"TEST_ENV": "async_test"},
|
|
49
|
+
)
|
|
50
|
+
self.sandbox_id = self.sandbox.sandbox_id
|
|
51
|
+
assert self.sandbox is not None
|
|
52
|
+
assert self.sandbox_id is not None
|
|
53
|
+
print(f"Created sandbox with ID: {self.sandbox_id}")
|
|
54
|
+
|
|
55
|
+
# 2. 在pause之前增加文件
|
|
56
|
+
print("Step 2: Adding files before pause...")
|
|
57
|
+
test_files = {
|
|
58
|
+
"/tmp/test_file1.txt": "Content of test file 1",
|
|
59
|
+
"/tmp/test_file2.txt": "Content of test file 2",
|
|
60
|
+
"/tmp/test_file3.bin": b"Binary content for test file 3",
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
for file_path, content in test_files.items():
|
|
64
|
+
result = await self.sandbox.files.write(file_path, content)
|
|
65
|
+
assert isinstance(result, WriteInfo)
|
|
66
|
+
exists = await self.sandbox.files.exists(file_path)
|
|
67
|
+
assert exists
|
|
68
|
+
print(f"Created file: {file_path}")
|
|
69
|
+
|
|
70
|
+
# 3. 在pause之前删除一个文件
|
|
71
|
+
print("Step 3: Deleting a file before pause...")
|
|
72
|
+
file_to_delete = "/tmp/test_file2.txt"
|
|
73
|
+
await self.sandbox.files.remove(file_to_delete)
|
|
74
|
+
exists = await self.sandbox.files.exists(file_to_delete)
|
|
75
|
+
assert not exists
|
|
76
|
+
print(f"Deleted file: {file_to_delete}")
|
|
77
|
+
|
|
78
|
+
# 4. 创建目录和文件
|
|
79
|
+
print("Step 4: Creating directory and nested files...")
|
|
80
|
+
await self.sandbox.files.make_dir("/tmp/test_dir")
|
|
81
|
+
await self.sandbox.files.write(
|
|
82
|
+
"/tmp/test_dir/nested_file.txt", "Nested file content"
|
|
83
|
+
)
|
|
84
|
+
assert await self.sandbox.files.exists("/tmp/test_dir")
|
|
85
|
+
assert await self.sandbox.files.exists("/tmp/test_dir/nested_file.txt")
|
|
86
|
+
print("Created directory and nested file")
|
|
87
|
+
|
|
88
|
+
# 5. Pause沙箱
|
|
89
|
+
print("Step 5: Pausing sandbox...")
|
|
90
|
+
await self.sandbox.beta_pause()
|
|
91
|
+
print("Sandbox paused successfully")
|
|
92
|
+
|
|
93
|
+
# 等待一小段时间确保pause完成
|
|
94
|
+
await asyncio.sleep(2)
|
|
95
|
+
|
|
96
|
+
# 6. Connect到暂停的沙箱
|
|
97
|
+
print("Step 6: Connecting to paused sandbox...")
|
|
98
|
+
connected_sandbox = await AsyncSandbox.connect(self.sandbox_id)
|
|
99
|
+
assert connected_sandbox.sandbox_id == self.sandbox_id
|
|
100
|
+
print("Connected to sandbox successfully")
|
|
101
|
+
|
|
102
|
+
# 等待一小段时间确保connect完成
|
|
103
|
+
await asyncio.sleep(2)
|
|
104
|
+
|
|
105
|
+
# 7. 在connect之后校验文件操作结果
|
|
106
|
+
print("Step 7: Verifying file operations after connect...")
|
|
107
|
+
|
|
108
|
+
# 校验应该存在的文件
|
|
109
|
+
exists = await connected_sandbox.files.exists("/tmp/test_file1.txt")
|
|
110
|
+
assert exists, "test_file1.txt should exist"
|
|
111
|
+
file1_content = await connected_sandbox.files.read(
|
|
112
|
+
"/tmp/test_file1.txt", format="text"
|
|
113
|
+
)
|
|
114
|
+
assert "Content of test file 1" in file1_content
|
|
115
|
+
print("✅ Verified test_file1.txt exists and content is correct")
|
|
116
|
+
|
|
117
|
+
exists = await connected_sandbox.files.exists("/tmp/test_file3.bin")
|
|
118
|
+
assert exists, "test_file3.bin should exist"
|
|
119
|
+
file3_content = await connected_sandbox.files.read(
|
|
120
|
+
"/tmp/test_file3.bin", format="bytes"
|
|
121
|
+
)
|
|
122
|
+
assert bytes(file3_content) == b"Binary content for test file 3"
|
|
123
|
+
print("✅ Verified test_file3.bin exists and content is correct")
|
|
124
|
+
|
|
125
|
+
# 校验应该不存在的文件(被删除的)
|
|
126
|
+
exists = await connected_sandbox.files.exists("/tmp/test_file2.txt")
|
|
127
|
+
assert not exists, "test_file2.txt should not exist"
|
|
128
|
+
print("✅ Verified test_file2.txt was deleted correctly")
|
|
129
|
+
|
|
130
|
+
# 校验目录和嵌套文件
|
|
131
|
+
exists = await connected_sandbox.files.exists("/tmp/test_dir")
|
|
132
|
+
assert exists, "test_dir should exist"
|
|
133
|
+
exists = await connected_sandbox.files.exists(
|
|
134
|
+
"/tmp/test_dir/nested_file.txt"
|
|
135
|
+
)
|
|
136
|
+
assert exists, "nested_file.txt should exist"
|
|
137
|
+
nested_content = await connected_sandbox.files.read(
|
|
138
|
+
"/tmp/test_dir/nested_file.txt", format="text"
|
|
139
|
+
)
|
|
140
|
+
assert "Nested file content" in nested_content
|
|
141
|
+
print("✅ Verified directory and nested file exist and content is correct")
|
|
142
|
+
|
|
143
|
+
# 8. 在connect后的沙箱中执行一些操作验证功能正常
|
|
144
|
+
print("Step 8: Verifying sandbox functionality after connect...")
|
|
145
|
+
result = await connected_sandbox.commands.run(
|
|
146
|
+
"echo 'Test command after connect'"
|
|
147
|
+
)
|
|
148
|
+
assert result.exit_code == 0
|
|
149
|
+
assert "Test command after connect" in result.stdout
|
|
150
|
+
print("✅ Verified sandbox commands work after connect")
|
|
151
|
+
|
|
152
|
+
# 9. 创建新文件验证写入功能正常
|
|
153
|
+
print("Step 9: Testing file write after connect...")
|
|
154
|
+
new_file_path = "/tmp/new_file_after_connect.txt"
|
|
155
|
+
await connected_sandbox.files.write(
|
|
156
|
+
new_file_path, "New file created after connect"
|
|
157
|
+
)
|
|
158
|
+
exists = await connected_sandbox.files.exists(new_file_path)
|
|
159
|
+
assert exists
|
|
160
|
+
new_content = await connected_sandbox.files.read(
|
|
161
|
+
new_file_path, format="text"
|
|
162
|
+
)
|
|
163
|
+
assert "New file created after connect" in new_content
|
|
164
|
+
print("✅ Verified file write works after connect")
|
|
165
|
+
|
|
166
|
+
self.log_test_result(
|
|
167
|
+
"Connect and Pause with Files", True, "All verifications passed"
|
|
168
|
+
)
|
|
169
|
+
return True
|
|
170
|
+
|
|
171
|
+
except Exception as e:
|
|
172
|
+
self.log_test_result("Connect and Pause with Files", False, str(e))
|
|
173
|
+
print(f"Test failed with error: {e}")
|
|
174
|
+
import traceback
|
|
175
|
+
|
|
176
|
+
traceback.print_exc()
|
|
177
|
+
return False
|
|
178
|
+
|
|
179
|
+
async def test_connect_pause_multiple_operations(self):
|
|
180
|
+
"""测试多次pause和connect操作"""
|
|
181
|
+
try:
|
|
182
|
+
# 1. 创建沙箱
|
|
183
|
+
print("Test 2: Creating sandbox for multiple operations test...")
|
|
184
|
+
sandbox = await AsyncSandbox.create(
|
|
185
|
+
template="base",
|
|
186
|
+
timeout=3600,
|
|
187
|
+
metadata={"test": "connect_pause_multiple_async"},
|
|
188
|
+
)
|
|
189
|
+
sandbox_id = sandbox.sandbox_id
|
|
190
|
+
print(f"Created sandbox with ID: {sandbox_id}")
|
|
191
|
+
|
|
192
|
+
# 2. 第一次操作:创建文件
|
|
193
|
+
print("First operation: Creating files...")
|
|
194
|
+
await sandbox.files.write("/tmp/multi_test1.txt", "First batch")
|
|
195
|
+
await sandbox.files.write("/tmp/multi_test2.txt", "First batch")
|
|
196
|
+
|
|
197
|
+
# 3. 第一次pause
|
|
198
|
+
print("First pause...")
|
|
199
|
+
await sandbox.beta_pause()
|
|
200
|
+
await asyncio.sleep(2)
|
|
201
|
+
|
|
202
|
+
# 4. 第一次connect
|
|
203
|
+
print("First connect...")
|
|
204
|
+
sandbox = await AsyncSandbox.connect(sandbox_id)
|
|
205
|
+
await asyncio.sleep(2)
|
|
206
|
+
|
|
207
|
+
# 5. 验证第一次的文件存在
|
|
208
|
+
assert await sandbox.files.exists("/tmp/multi_test1.txt")
|
|
209
|
+
assert await sandbox.files.exists("/tmp/multi_test2.txt")
|
|
210
|
+
|
|
211
|
+
# 6. 第二次操作:删除一个文件,创建新文件
|
|
212
|
+
print("Second operation: Deleting and creating files...")
|
|
213
|
+
await sandbox.files.remove("/tmp/multi_test1.txt")
|
|
214
|
+
await sandbox.files.write("/tmp/multi_test3.txt", "Second batch")
|
|
215
|
+
|
|
216
|
+
# 7. 第二次pause
|
|
217
|
+
print("Second pause...")
|
|
218
|
+
await sandbox.beta_pause()
|
|
219
|
+
await asyncio.sleep(2)
|
|
220
|
+
|
|
221
|
+
# 8. 第二次connect
|
|
222
|
+
print("Second connect...")
|
|
223
|
+
sandbox = await AsyncSandbox.connect(sandbox_id)
|
|
224
|
+
await asyncio.sleep(2)
|
|
225
|
+
|
|
226
|
+
# 9. 验证第二次操作的结果
|
|
227
|
+
exists = await sandbox.files.exists("/tmp/multi_test1.txt")
|
|
228
|
+
assert not exists, "multi_test1.txt should be deleted"
|
|
229
|
+
exists = await sandbox.files.exists("/tmp/multi_test2.txt")
|
|
230
|
+
assert exists, "multi_test2.txt should exist"
|
|
231
|
+
exists = await sandbox.files.exists("/tmp/multi_test3.txt")
|
|
232
|
+
assert exists, "multi_test3.txt should exist"
|
|
233
|
+
content = await sandbox.files.read("/tmp/multi_test3.txt", format="text")
|
|
234
|
+
assert "Second batch" in content
|
|
235
|
+
|
|
236
|
+
# 清理
|
|
237
|
+
await sandbox.kill()
|
|
238
|
+
|
|
239
|
+
self.log_test_result(
|
|
240
|
+
"Multiple Connect and Pause Operations",
|
|
241
|
+
True,
|
|
242
|
+
"All verifications passed",
|
|
243
|
+
)
|
|
244
|
+
return True
|
|
245
|
+
|
|
246
|
+
except Exception as e:
|
|
247
|
+
self.log_test_result("Multiple Connect and Pause Operations", False, str(e))
|
|
248
|
+
print(f"Test failed with error: {e}")
|
|
249
|
+
import traceback
|
|
250
|
+
|
|
251
|
+
traceback.print_exc()
|
|
252
|
+
return False
|
|
253
|
+
|
|
254
|
+
async def cleanup(self):
|
|
255
|
+
"""清理资源"""
|
|
256
|
+
if self.sandbox:
|
|
257
|
+
try:
|
|
258
|
+
await self.sandbox.kill()
|
|
259
|
+
print("Sandbox cleaned up successfully")
|
|
260
|
+
except Exception as e:
|
|
261
|
+
print(f"Error cleaning up sandbox: {e}")
|
|
262
|
+
|
|
263
|
+
def print_summary(self):
|
|
264
|
+
"""打印测试摘要"""
|
|
265
|
+
total_tests = len(self.test_results)
|
|
266
|
+
passed_tests = sum(1 for r in self.test_results if r["success"])
|
|
267
|
+
failed_tests = total_tests - passed_tests
|
|
268
|
+
|
|
269
|
+
print("\n" + "=" * 60)
|
|
270
|
+
print("Connect and Pause Test Report (Async)")
|
|
271
|
+
print("=" * 60)
|
|
272
|
+
print(f"总测试数: {total_tests}")
|
|
273
|
+
print(f"通过数: {passed_tests}")
|
|
274
|
+
print(f"失败数: {failed_tests}")
|
|
275
|
+
print(f"成功率: {(passed_tests/total_tests*100):.1f}%")
|
|
276
|
+
|
|
277
|
+
if failed_tests > 0:
|
|
278
|
+
print(f"\n失败的测试:")
|
|
279
|
+
for result in self.test_results:
|
|
280
|
+
if not result["success"]:
|
|
281
|
+
print(f" ❌ {result['test']}: {result['message']}")
|
|
282
|
+
|
|
283
|
+
print("=" * 60)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
async def main():
|
|
287
|
+
"""主函数"""
|
|
288
|
+
test_suite = ConnectPauseAsyncTest()
|
|
289
|
+
|
|
290
|
+
try:
|
|
291
|
+
# 运行测试
|
|
292
|
+
await test_suite.test_connect_pause_with_files()
|
|
293
|
+
await test_suite.test_connect_pause_multiple_operations()
|
|
294
|
+
finally:
|
|
295
|
+
await test_suite.cleanup()
|
|
296
|
+
test_suite.print_summary()
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
if __name__ == "__main__":
|
|
300
|
+
asyncio.run(main())
|