scalebox-sdk 0.1.24__py3-none-any.whl → 1.0.1__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.
Files changed (87) hide show
  1. scalebox/__init__.py +2 -2
  2. scalebox/api/__init__.py +130 -128
  3. scalebox/api/client/__init__.py +8 -8
  4. scalebox/api/client/api/sandboxes/get_sandboxes_sandbox_id_metrics.py +2 -2
  5. scalebox/api/client/api/sandboxes/post_sandboxes.py +2 -2
  6. scalebox/api/client/api/sandboxes/post_sandboxes_sandbox_id_connect.py +193 -0
  7. scalebox/api/client/client.py +288 -288
  8. scalebox/api/client/models/connect_sandbox.py +59 -0
  9. scalebox/api/client/models/error.py +2 -2
  10. scalebox/api/client/models/listed_sandbox.py +19 -1
  11. scalebox/api/client/models/new_sandbox.py +10 -0
  12. scalebox/api/client/models/sandbox.py +138 -125
  13. scalebox/api/client/models/sandbox_detail.py +24 -0
  14. scalebox/api/client/types.py +46 -46
  15. scalebox/cli.py +125 -125
  16. scalebox/client/aclient.py +57 -57
  17. scalebox/client/client.py +102 -102
  18. scalebox/code_interpreter/__init__.py +12 -12
  19. scalebox/code_interpreter/charts.py +230 -230
  20. scalebox/code_interpreter/constants.py +3 -3
  21. scalebox/code_interpreter/exceptions.py +13 -13
  22. scalebox/code_interpreter/models.py +485 -485
  23. scalebox/connection_config.py +34 -1
  24. scalebox/csx_connect/__init__.py +1 -1
  25. scalebox/csx_connect/client.py +485 -485
  26. scalebox/csx_desktop/main.py +651 -651
  27. scalebox/exceptions.py +83 -83
  28. scalebox/generated/api.py +61 -61
  29. scalebox/generated/api_pb2.py +203 -203
  30. scalebox/generated/api_pb2.pyi +956 -956
  31. scalebox/generated/api_pb2_connect.py +1407 -1407
  32. scalebox/generated/rpc.py +50 -50
  33. scalebox/sandbox/main.py +146 -139
  34. scalebox/sandbox/sandbox_api.py +105 -91
  35. scalebox/sandbox/signature.py +40 -40
  36. scalebox/sandbox/utils.py +34 -34
  37. scalebox/sandbox_async/commands/command.py +307 -307
  38. scalebox/sandbox_async/commands/command_handle.py +187 -187
  39. scalebox/sandbox_async/commands/pty.py +187 -187
  40. scalebox/sandbox_async/filesystem/filesystem.py +557 -557
  41. scalebox/sandbox_async/filesystem/watch_handle.py +61 -61
  42. scalebox/sandbox_async/main.py +228 -46
  43. scalebox/sandbox_async/sandbox_api.py +124 -3
  44. scalebox/sandbox_async/utils.py +7 -7
  45. scalebox/sandbox_sync/__init__.py +2 -2
  46. scalebox/sandbox_sync/commands/command.py +300 -300
  47. scalebox/sandbox_sync/commands/command_handle.py +150 -150
  48. scalebox/sandbox_sync/commands/pty.py +181 -181
  49. scalebox/sandbox_sync/filesystem/filesystem.py +3 -3
  50. scalebox/sandbox_sync/filesystem/watch_handle.py +66 -66
  51. scalebox/sandbox_sync/main.py +208 -133
  52. scalebox/sandbox_sync/sandbox_api.py +119 -3
  53. scalebox/test/CODE_INTERPRETER_TESTS_READY.md +323 -323
  54. scalebox/test/README.md +329 -329
  55. scalebox/test/bedrock_openai_adapter.py +67 -0
  56. scalebox/test/code_interpreter_test.py +34 -34
  57. scalebox/test/code_interpreter_test_sync.py +34 -34
  58. scalebox/test/run_stress_code_interpreter_sync.py +166 -0
  59. scalebox/test/simple_upload_example.py +123 -0
  60. scalebox/test/stabitiy_test.py +310 -0
  61. scalebox/test/test_browser_use.py +25 -0
  62. scalebox/test/test_browser_use_scalebox.py +61 -0
  63. scalebox/test/test_code_interpreter_sync_comprehensive.py +115 -65
  64. scalebox/test/test_connect_pause_async.py +277 -0
  65. scalebox/test/test_connect_pause_sync.py +267 -0
  66. scalebox/test/test_desktop_sandbox_sf.py +117 -0
  67. scalebox/test/test_download_url.py +49 -0
  68. scalebox/test/test_sandbox_async_comprehensive.py +1 -1
  69. scalebox/test/test_sandbox_object_storage_example.py +146 -0
  70. scalebox/test/test_sandbox_object_storage_example_async.py +156 -0
  71. scalebox/test/test_sf.py +137 -0
  72. scalebox/test/test_watch_dir_async.py +56 -0
  73. scalebox/test/testacreate.py +1 -1
  74. scalebox/test/testagetinfo.py +1 -1
  75. scalebox/test/testcomputeuse.py +243 -243
  76. scalebox/test/testsandbox_api.py +13 -0
  77. scalebox/test/testsandbox_sync.py +1 -1
  78. scalebox/test/upload_100mb_example.py +355 -0
  79. scalebox/utils/httpcoreclient.py +297 -297
  80. scalebox/utils/httpxclient.py +403 -403
  81. scalebox/version.py +2 -2
  82. {scalebox_sdk-0.1.24.dist-info → scalebox_sdk-1.0.1.dist-info}/METADATA +1 -1
  83. {scalebox_sdk-0.1.24.dist-info → scalebox_sdk-1.0.1.dist-info}/RECORD +87 -69
  84. {scalebox_sdk-0.1.24.dist-info → scalebox_sdk-1.0.1.dist-info}/WHEEL +1 -1
  85. {scalebox_sdk-0.1.24.dist-info → scalebox_sdk-1.0.1.dist-info}/entry_points.txt +0 -0
  86. {scalebox_sdk-0.1.24.dist-info → scalebox_sdk-1.0.1.dist-info}/licenses/LICENSE +0 -0
  87. {scalebox_sdk-0.1.24.dist-info → scalebox_sdk-1.0.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,277 @@
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("/tmp/test_dir/nested_file.txt", "Nested file content")
82
+ assert await self.sandbox.files.exists("/tmp/test_dir")
83
+ assert await self.sandbox.files.exists("/tmp/test_dir/nested_file.txt")
84
+ print("Created directory and nested file")
85
+
86
+ # 5. Pause沙箱
87
+ print("Step 5: Pausing sandbox...")
88
+ await self.sandbox.beta_pause()
89
+ print("Sandbox paused successfully")
90
+
91
+ # 等待一小段时间确保pause完成
92
+ await asyncio.sleep(2)
93
+
94
+ # 6. Connect到暂停的沙箱
95
+ print("Step 6: Connecting to paused sandbox...")
96
+ connected_sandbox = await AsyncSandbox.connect(self.sandbox_id)
97
+ assert connected_sandbox.sandbox_id == self.sandbox_id
98
+ print("Connected to sandbox successfully")
99
+
100
+ # 等待一小段时间确保connect完成
101
+ await asyncio.sleep(2)
102
+
103
+ # 7. 在connect之后校验文件操作结果
104
+ print("Step 7: Verifying file operations after connect...")
105
+
106
+ # 校验应该存在的文件
107
+ exists = await connected_sandbox.files.exists("/tmp/test_file1.txt")
108
+ assert exists, "test_file1.txt should exist"
109
+ file1_content = await connected_sandbox.files.read("/tmp/test_file1.txt", format="text")
110
+ assert "Content of test file 1" in file1_content
111
+ print("✅ Verified test_file1.txt exists and content is correct")
112
+
113
+ exists = await connected_sandbox.files.exists("/tmp/test_file3.bin")
114
+ assert exists, "test_file3.bin should exist"
115
+ file3_content = await connected_sandbox.files.read("/tmp/test_file3.bin", format="bytes")
116
+ assert bytes(file3_content) == b"Binary content for test file 3"
117
+ print("✅ Verified test_file3.bin exists and content is correct")
118
+
119
+ # 校验应该不存在的文件(被删除的)
120
+ exists = await connected_sandbox.files.exists("/tmp/test_file2.txt")
121
+ assert not exists, "test_file2.txt should not exist"
122
+ print("✅ Verified test_file2.txt was deleted correctly")
123
+
124
+ # 校验目录和嵌套文件
125
+ exists = await connected_sandbox.files.exists("/tmp/test_dir")
126
+ assert exists, "test_dir should exist"
127
+ exists = await connected_sandbox.files.exists("/tmp/test_dir/nested_file.txt")
128
+ assert exists, "nested_file.txt should exist"
129
+ nested_content = await connected_sandbox.files.read("/tmp/test_dir/nested_file.txt", format="text")
130
+ assert "Nested file content" in nested_content
131
+ print("✅ Verified directory and nested file exist and content is correct")
132
+
133
+ # 8. 在connect后的沙箱中执行一些操作验证功能正常
134
+ print("Step 8: Verifying sandbox functionality after connect...")
135
+ result = await connected_sandbox.commands.run("echo 'Test command after connect'")
136
+ assert result.exit_code == 0
137
+ assert "Test command after connect" in result.stdout
138
+ print("✅ Verified sandbox commands work after connect")
139
+
140
+ # 9. 创建新文件验证写入功能正常
141
+ print("Step 9: Testing file write after connect...")
142
+ new_file_path = "/tmp/new_file_after_connect.txt"
143
+ await connected_sandbox.files.write(new_file_path, "New file created after connect")
144
+ exists = await connected_sandbox.files.exists(new_file_path)
145
+ assert exists
146
+ new_content = await connected_sandbox.files.read(new_file_path, format="text")
147
+ assert "New file created after connect" in new_content
148
+ print("✅ Verified file write works after connect")
149
+
150
+ self.log_test_result("Connect and Pause with Files", True, "All verifications passed")
151
+ return True
152
+
153
+ except Exception as e:
154
+ self.log_test_result("Connect and Pause with Files", False, str(e))
155
+ print(f"Test failed with error: {e}")
156
+ import traceback
157
+ traceback.print_exc()
158
+ return False
159
+
160
+ async def test_connect_pause_multiple_operations(self):
161
+ """测试多次pause和connect操作"""
162
+ try:
163
+ # 1. 创建沙箱
164
+ print("Test 2: Creating sandbox for multiple operations test...")
165
+ sandbox = await AsyncSandbox.create(
166
+ template="base",
167
+ timeout=3600,
168
+ metadata={"test": "connect_pause_multiple_async"},
169
+ )
170
+ sandbox_id = sandbox.sandbox_id
171
+ print(f"Created sandbox with ID: {sandbox_id}")
172
+
173
+ # 2. 第一次操作:创建文件
174
+ print("First operation: Creating files...")
175
+ await sandbox.files.write("/tmp/multi_test1.txt", "First batch")
176
+ await sandbox.files.write("/tmp/multi_test2.txt", "First batch")
177
+
178
+ # 3. 第一次pause
179
+ print("First pause...")
180
+ await sandbox.beta_pause()
181
+ await asyncio.sleep(2)
182
+
183
+ # 4. 第一次connect
184
+ print("First connect...")
185
+ sandbox = await AsyncSandbox.connect(sandbox_id)
186
+ await asyncio.sleep(2)
187
+
188
+ # 5. 验证第一次的文件存在
189
+ assert await sandbox.files.exists("/tmp/multi_test1.txt")
190
+ assert await sandbox.files.exists("/tmp/multi_test2.txt")
191
+
192
+ # 6. 第二次操作:删除一个文件,创建新文件
193
+ print("Second operation: Deleting and creating files...")
194
+ await sandbox.files.remove("/tmp/multi_test1.txt")
195
+ await sandbox.files.write("/tmp/multi_test3.txt", "Second batch")
196
+
197
+ # 7. 第二次pause
198
+ print("Second pause...")
199
+ await sandbox.beta_pause()
200
+ await asyncio.sleep(2)
201
+
202
+ # 8. 第二次connect
203
+ print("Second connect...")
204
+ sandbox = await AsyncSandbox.connect(sandbox_id)
205
+ await asyncio.sleep(2)
206
+
207
+ # 9. 验证第二次操作的结果
208
+ exists = await sandbox.files.exists("/tmp/multi_test1.txt")
209
+ assert not exists, "multi_test1.txt should be deleted"
210
+ exists = await sandbox.files.exists("/tmp/multi_test2.txt")
211
+ assert exists, "multi_test2.txt should exist"
212
+ exists = await sandbox.files.exists("/tmp/multi_test3.txt")
213
+ assert exists, "multi_test3.txt should exist"
214
+ content = await sandbox.files.read("/tmp/multi_test3.txt", format="text")
215
+ assert "Second batch" in content
216
+
217
+ # 清理
218
+ await sandbox.kill()
219
+
220
+ self.log_test_result("Multiple Connect and Pause Operations", True, "All verifications passed")
221
+ return True
222
+
223
+ except Exception as e:
224
+ self.log_test_result("Multiple Connect and Pause Operations", False, str(e))
225
+ print(f"Test failed with error: {e}")
226
+ import traceback
227
+ traceback.print_exc()
228
+ return False
229
+
230
+ async def cleanup(self):
231
+ """清理资源"""
232
+ if self.sandbox:
233
+ try:
234
+ await self.sandbox.kill()
235
+ print("Sandbox cleaned up successfully")
236
+ except Exception as e:
237
+ print(f"Error cleaning up sandbox: {e}")
238
+
239
+ def print_summary(self):
240
+ """打印测试摘要"""
241
+ total_tests = len(self.test_results)
242
+ passed_tests = sum(1 for r in self.test_results if r["success"])
243
+ failed_tests = total_tests - passed_tests
244
+
245
+ print("\n" + "=" * 60)
246
+ print("Connect and Pause Test Report (Async)")
247
+ print("=" * 60)
248
+ print(f"总测试数: {total_tests}")
249
+ print(f"通过数: {passed_tests}")
250
+ print(f"失败数: {failed_tests}")
251
+ print(f"成功率: {(passed_tests/total_tests*100):.1f}%")
252
+
253
+ if failed_tests > 0:
254
+ print(f"\n失败的测试:")
255
+ for result in self.test_results:
256
+ if not result["success"]:
257
+ print(f" ❌ {result['test']}: {result['message']}")
258
+
259
+ print("=" * 60)
260
+
261
+
262
+ async def main():
263
+ """主函数"""
264
+ test_suite = ConnectPauseAsyncTest()
265
+
266
+ try:
267
+ # 运行测试
268
+ await test_suite.test_connect_pause_with_files()
269
+ await test_suite.test_connect_pause_multiple_operations()
270
+ finally:
271
+ await test_suite.cleanup()
272
+ test_suite.print_summary()
273
+
274
+
275
+ if __name__ == "__main__":
276
+ asyncio.run(main())
277
+
@@ -0,0 +1,267 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Test cases for connect and pause functionality (synchronous 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 time
14
+
15
+ from scalebox.sandbox.filesystem.filesystem import WriteInfo, FileType
16
+ from scalebox.sandbox_sync.main import Sandbox
17
+
18
+
19
+ class ConnectPauseSyncTest:
20
+ """Test suite for connect and pause functionality (sync)."""
21
+
22
+ def __init__(self):
23
+ self.sandbox = None
24
+ self.sandbox_id = None
25
+ self.test_results = []
26
+
27
+ def log_test_result(self, test_name: str, success: bool, message: str = ""):
28
+ """记录测试结果"""
29
+ status = "✅ PASS" if success else "❌ FAIL"
30
+ result = {
31
+ "test": test_name,
32
+ "success": success,
33
+ "message": message,
34
+ }
35
+ self.test_results.append(result)
36
+ print(f"{status} {test_name}: {message}")
37
+
38
+ def test_connect_pause_with_files(self):
39
+ """测试connect和pause功能,包含文件操作"""
40
+ try:
41
+ # 1. 创建沙箱
42
+ print("Step 1: Creating sandbox...")
43
+ self.sandbox = Sandbox.create(
44
+ template="code-interpreter",
45
+ timeout=3600,
46
+ metadata={"test": "connect_pause_sync"},
47
+ envs={"TEST_ENV": "sync_test"},
48
+ )
49
+ self.sandbox_id = self.sandbox.sandbox_id
50
+ assert self.sandbox is not None
51
+ assert self.sandbox_id is not None
52
+ print(f"Created sandbox with ID: {self.sandbox_id}")
53
+
54
+ # 2. 在pause之前增加文件
55
+ print("Step 2: Adding files before pause...")
56
+ test_files = {
57
+ "/tmp/test_file1.txt": "Content of test file 1",
58
+ "/tmp/test_file2.txt": "Content of test file 2",
59
+ "/tmp/test_file3.bin": b"Binary content for test file 3",
60
+ }
61
+
62
+ for file_path, content in test_files.items():
63
+ result = self.sandbox.files.write(file_path, content)
64
+ print(result)
65
+ assert isinstance(result, WriteInfo)
66
+ assert self.sandbox.files.exists(file_path)
67
+ print(f"Created file: {file_path}")
68
+
69
+ # 3. 在pause之前删除一个文件
70
+ print("Step 3: Deleting a file before pause...")
71
+ file_to_delete = "/tmp/test_file2.txt"
72
+ self.sandbox.files.remove(file_to_delete)
73
+ assert not self.sandbox.files.exists(file_to_delete)
74
+ print(f"Deleted file: {file_to_delete}")
75
+
76
+ # 4. 创建目录和文件
77
+ print("Step 4: Creating directory and nested files...")
78
+ self.sandbox.files.make_dir("/tmp/test_dir")
79
+ self.sandbox.files.write("/tmp/test_dir/nested_file.txt", "Nested file content")
80
+ assert self.sandbox.files.exists("/tmp/test_dir")
81
+ assert self.sandbox.files.exists("/tmp/test_dir/nested_file.txt")
82
+ print("Created directory and nested file")
83
+
84
+ # 5. Pause沙箱
85
+ print("Step 5: Pausing sandbox...")
86
+ self.sandbox.beta_pause()
87
+ print("Sandbox paused successfully")
88
+
89
+ # 等待一小段时间确保pause完成
90
+ time.sleep(1)
91
+
92
+ # 6. Connect到暂停的沙箱
93
+ print("Step 6: Connecting to paused sandbox...")
94
+ connected_sandbox = Sandbox.connect(self.sandbox_id)
95
+ print(connected_sandbox)
96
+ assert connected_sandbox.sandbox_id == self.sandbox_id
97
+ print("Connected to sandbox successfully")
98
+
99
+ # 等待一小段时间确保connect完成
100
+ time.sleep(2)
101
+
102
+ # 7. 在connect之后校验文件操作结果
103
+ print("Step 7: Verifying file operations after connect...")
104
+
105
+ # 校验应该存在的文件
106
+ assert connected_sandbox.files.exists("/tmp/test_file1.txt"), "test_file1.txt should exist"
107
+ file1_content = connected_sandbox.files.read("/tmp/test_file1.txt", format="text")
108
+ assert "Content of test file 1" in file1_content
109
+ print("✅ Verified test_file1.txt exists and content is correct")
110
+
111
+ assert connected_sandbox.files.exists("/tmp/test_file3.bin"), "test_file3.bin should exist"
112
+ file3_content = connected_sandbox.files.read("/tmp/test_file3.bin", format="bytes")
113
+ assert bytes(file3_content) == b"Binary content for test file 3"
114
+ print("✅ Verified test_file3.bin exists and content is correct")
115
+
116
+ # 校验应该不存在的文件(被删除的)
117
+ assert not connected_sandbox.files.exists("/tmp/test_file2.txt"), "test_file2.txt should not exist"
118
+ print("✅ Verified test_file2.txt was deleted correctly")
119
+
120
+ # 校验目录和嵌套文件
121
+ assert connected_sandbox.files.exists("/tmp/test_dir"), "test_dir should exist"
122
+ assert connected_sandbox.files.exists("/tmp/test_dir/nested_file.txt"), "nested_file.txt should exist"
123
+ nested_content = connected_sandbox.files.read("/tmp/test_dir/nested_file.txt", format="text")
124
+ assert "Nested file content" in nested_content
125
+ print("✅ Verified directory and nested file exist and content is correct")
126
+
127
+ # 8. 在connect后的沙箱中执行一些操作验证功能正常
128
+ print("Step 8: Verifying sandbox functionality after connect...")
129
+ result = connected_sandbox.commands.run("echo 'Test command after connect'")
130
+ assert result.exit_code == 0
131
+ assert "Test command after connect" in result.stdout
132
+ print("✅ Verified sandbox commands work after connect")
133
+
134
+ # 9. 创建新文件验证写入功能正常
135
+ print("Step 9: Testing file write after connect...")
136
+ new_file_path = "/tmp/new_file_after_connect.txt"
137
+ connected_sandbox.files.write(new_file_path, "New file created after connect")
138
+ assert connected_sandbox.files.exists(new_file_path)
139
+ new_content = connected_sandbox.files.read(new_file_path, format="text")
140
+ assert "New file created after connect" in new_content
141
+ print("✅ Verified file write works after connect")
142
+
143
+ self.log_test_result("Connect and Pause with Files", True, "All verifications passed")
144
+ return True
145
+
146
+ except Exception as e:
147
+ self.log_test_result("Connect and Pause with Files", False, str(e))
148
+ print(f"Test failed with error: {e}")
149
+ import traceback
150
+ traceback.print_exc()
151
+ return False
152
+
153
+ def test_connect_pause_multiple_operations(self):
154
+ """测试多次pause和connect操作"""
155
+ try:
156
+ # 1. 创建沙箱
157
+ print("Test 2: Creating sandbox for multiple operations test...")
158
+ sandbox = Sandbox.create(
159
+ template="base",
160
+ timeout=3600,
161
+ metadata={"test": "connect_pause_multiple_sync"},
162
+ )
163
+ sandbox_id = sandbox.sandbox_id
164
+ print(f"Created sandbox with ID: {sandbox_id}")
165
+
166
+ # 2. 第一次操作:创建文件
167
+ print("First operation: Creating files...")
168
+ sandbox.files.write("/tmp/multi_test1.txt", "First batch")
169
+ sandbox.files.write("/tmp/multi_test2.txt", "First batch")
170
+
171
+ # 3. 第一次pause
172
+ print("First pause...")
173
+ sandbox.beta_pause()
174
+ time.sleep(2)
175
+
176
+ # 4. 第一次connect
177
+ print("First connect...")
178
+ sandbox = Sandbox.connect(sandbox_id)
179
+ time.sleep(2)
180
+
181
+ # 5. 验证第一次的文件存在
182
+ assert sandbox.files.exists("/tmp/multi_test1.txt")
183
+ assert sandbox.files.exists("/tmp/multi_test2.txt")
184
+
185
+ # 6. 第二次操作:删除一个文件,创建新文件
186
+ print("Second operation: Deleting and creating files...")
187
+ sandbox.files.remove("/tmp/multi_test1.txt")
188
+ sandbox.files.write("/tmp/multi_test3.txt", "Second batch")
189
+
190
+ # 7. 第二次pause
191
+ print("Second pause...")
192
+ sandbox.beta_pause()
193
+ time.sleep(2)
194
+
195
+ # 8. 第二次connect
196
+ print("Second connect...")
197
+ sandbox = Sandbox.connect(sandbox_id)
198
+ time.sleep(2)
199
+
200
+ # 9. 验证第二次操作的结果
201
+ assert not sandbox.files.exists("/tmp/multi_test1.txt"), "multi_test1.txt should be deleted"
202
+ assert sandbox.files.exists("/tmp/multi_test2.txt"), "multi_test2.txt should exist"
203
+ assert sandbox.files.exists("/tmp/multi_test3.txt"), "multi_test3.txt should exist"
204
+ content = sandbox.files.read("/tmp/multi_test3.txt", format="text")
205
+ assert "Second batch" in content
206
+
207
+ # 清理
208
+ sandbox.kill()
209
+
210
+ self.log_test_result("Multiple Connect and Pause Operations", True, "All verifications passed")
211
+ return True
212
+
213
+ except Exception as e:
214
+ self.log_test_result("Multiple Connect and Pause Operations", False, str(e))
215
+ print(f"Test failed with error: {e}")
216
+ import traceback
217
+ traceback.print_exc()
218
+ return False
219
+
220
+ def cleanup(self):
221
+ """清理资源"""
222
+ if self.sandbox:
223
+ try:
224
+ self.sandbox.kill()
225
+ print("Sandbox cleaned up successfully")
226
+ except Exception as e:
227
+ print(f"Error cleaning up sandbox: {e}")
228
+
229
+ def print_summary(self):
230
+ """打印测试摘要"""
231
+ total_tests = len(self.test_results)
232
+ passed_tests = sum(1 for r in self.test_results if r["success"])
233
+ failed_tests = total_tests - passed_tests
234
+
235
+ print("\n" + "=" * 60)
236
+ print("Connect and Pause Test Report (Sync)")
237
+ print("=" * 60)
238
+ print(f"总测试数: {total_tests}")
239
+ print(f"通过数: {passed_tests}")
240
+ print(f"失败数: {failed_tests}")
241
+ print(f"成功率: {(passed_tests/total_tests*100):.1f}%")
242
+
243
+ if failed_tests > 0:
244
+ print(f"\n失败的测试:")
245
+ for result in self.test_results:
246
+ if not result["success"]:
247
+ print(f" ❌ {result['test']}: {result['message']}")
248
+
249
+ print("=" * 60)
250
+
251
+
252
+ def main():
253
+ """主函数"""
254
+ test_suite = ConnectPauseSyncTest()
255
+
256
+ try:
257
+ # 运行测试
258
+ test_suite.test_connect_pause_with_files()
259
+ # test_suite.test_connect_pause_multiple_operations()
260
+ finally:
261
+ test_suite.cleanup()
262
+ test_suite.print_summary()
263
+
264
+
265
+ if __name__ == "__main__":
266
+ main()
267
+
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 测试用例:基于desktop创建sandbox,上传并执行test_sf.py
4
+ """
5
+
6
+ import os
7
+ import re
8
+ import sys
9
+ import time
10
+
11
+ # 确保可以从项目根导入 scalebox
12
+ from scalebox.csx_desktop.main import Sandbox
13
+
14
+
15
+ def main():
16
+ """主测试函数"""
17
+ print("=" * 60)
18
+ print("开始测试:基于desktop创建sandbox并执行test_sf.py")
19
+ print("=" * 60)
20
+
21
+ # 1. 基于desktop创建sandbox
22
+ print("\n[步骤 1] 正在创建desktop sandbox...")
23
+ sandbox = Sandbox.create(
24
+ template="browser-use",
25
+ timeout=3600, # 1小时超时
26
+ )
27
+ print(f"✓ Sandbox创建成功,ID: {sandbox.sandbox_id}")
28
+
29
+ # 2. 获取network_proxy
30
+ print("\n[步骤 2] 获取network_proxy...")
31
+ network_proxy = sandbox.network_proxy()
32
+ print(f"✓ Network Proxy: {network_proxy}")
33
+
34
+ # 3. 获取VNC URL(不启动,直接获取)
35
+ print("\n[步骤 3] 获取VNC URL...")
36
+ vnc_url = sandbox.stream.get_url(auto_connect=True)
37
+ print(f"✓ VNC URL: {vnc_url}")
38
+
39
+ # 4. 读取test_sf.py并修改username和password
40
+ print("\n[步骤 4] 读取并修改test_sf.py...")
41
+ test_sf_path = os.path.join(os.path.dirname(__file__), "test_sf.py")
42
+
43
+ with open(test_sf_path, 'r', encoding='utf-8') as f:
44
+ test_sf_content = f.read()
45
+
46
+ # 从network_proxy中提取username和password
47
+ if network_proxy and isinstance(network_proxy, dict):
48
+ # network_proxy结构: {'proxy_configs': {'username': '...', 'password': '...', ...}, ...}
49
+ proxy_configs = network_proxy.get("proxy_configs", {})
50
+ if isinstance(proxy_configs, dict):
51
+ proxy_username = proxy_configs.get("username", "")
52
+ proxy_password = proxy_configs.get("password", "")
53
+ else:
54
+ # 兼容旧格式:直接在network_proxy中
55
+ proxy_username = network_proxy.get("username", "")
56
+ proxy_password = network_proxy.get("password", "")
57
+
58
+ if not proxy_username or not proxy_password:
59
+ print("⚠ 警告: network_proxy中缺少username或password字段")
60
+ print(f" network_proxy内容: {network_proxy}")
61
+ else:
62
+ # 替换test_sf.py中的username和password
63
+ # 匹配proxy字典中的username和password(处理可能的引号差异)
64
+ pattern_username = r'"username"\s*:\s*"[^"]*"'
65
+ pattern_password = r'"password"\s*:\s*"[^"]*"'
66
+
67
+ test_sf_content = re.sub(
68
+ pattern_username,
69
+ f'"username": "{proxy_username}"',
70
+ test_sf_content
71
+ )
72
+ test_sf_content = re.sub(
73
+ pattern_password,
74
+ f'"password": "{proxy_password}"',
75
+ test_sf_content
76
+ )
77
+
78
+ print(f"✓ 已更新username: {proxy_username}")
79
+ print(f"✓ 已更新password: {proxy_password[:20]}...")
80
+ else:
81
+ print("⚠ 警告: network_proxy为空或格式不正确,跳过修改")
82
+ print(f" network_proxy类型: {type(network_proxy)}, 值: {network_proxy}")
83
+
84
+ # 5. 上传test_sf.py到sandbox的/目录
85
+ print("\n[步骤 5] 上传test_sf.py到sandbox的/目录...")
86
+ result = sandbox.files.write("/test_sf.py", test_sf_content.encode('utf-8'))
87
+ print(f"✓ 文件上传成功: {result.path}")
88
+
89
+ # 6. 在sandbox中执行命令
90
+ print("\n[步骤 6] 在sandbox中执行: source /venv/bin/activate && python /test_sf.py")
91
+ try:
92
+ # 使用bash -c确保source命令和环境变量正确加载
93
+ result = sandbox.commands.run(
94
+ 'bash -c "source /venv/bin/activate && python /test_sf.py"',
95
+ timeout=600 # 10分钟超时
96
+ )
97
+ print(f"✓ 命令执行完成")
98
+ print(f"退出码: {result.exit_code}")
99
+ print(f"标准输出:\n{result.stdout}")
100
+ if result.stderr:
101
+ print(f"标准错误:\n{result.stderr}")
102
+ except Exception as e:
103
+ print(f"✗ 命令执行失败: {e}")
104
+ raise
105
+
106
+ print("\n" + "=" * 60)
107
+ print("测试完成!")
108
+ print("=" * 60)
109
+
110
+ # 清理(可选,注释掉以保留sandbox)
111
+ # sandbox.kill()
112
+ # print("Sandbox已清理")
113
+
114
+
115
+ if __name__ == "__main__":
116
+ main()
117
+