@@ -134,6 +134,23 @@ def test_template_list(self):
134134 data = json .loads (result .output )
135135 assert len (data ) == 2
136136
137+ def test_template_list_with_type_filter (self ):
138+ """--type should be propagated into PageableInput."""
139+ mock_mod = _mock_sandbox_modules ()
140+ mock_mod .Sandbox .list_templates .return_value = [_make_template_obj (template_type = "Browser" )]
141+ with _patch_sdk (mock_mod ):
142+ runner = CliRunner ()
143+ result = runner .invoke (cli , [
144+ "sandbox" , "template" , "list" ,
145+ "--type" , "Browser" ,
146+ "--page-size" , "5" ,
147+ ])
148+ assert result .exit_code == 0 , result .output
149+
150+ call_kwargs = mock_mod .PageableInput .call_args .kwargs
151+ assert call_kwargs ["template_type" ] == "Browser"
152+ assert call_kwargs ["page_size" ] == 5
153+
137154 def test_template_update (self ):
138155 mock_mod = _mock_sandbox_modules ()
139156 existing = _make_template_obj ()
@@ -266,6 +283,44 @@ def test_exec_with_code(self):
266283 data = json .loads (result .output )
267284 assert data ["output" ] == "hello\n "
268285
286+ call_kwargs = sb .context .execute .call_args .kwargs
287+ assert call_kwargs ["language" ] == "python"
288+ assert call_kwargs ["context_id" ] is None
289+
290+ def test_exec_with_context_id_drops_language (self ):
291+ """--context-id alone should send language=None (server forbids both)."""
292+ mock_mod = _mock_sandbox_modules ()
293+ sb = _make_sandbox_obj ()
294+ sb .context .execute .return_value = {"output" : "ok\n " }
295+ mock_mod .Sandbox .connect .return_value = sb
296+ with _patch_sdk (mock_mod ):
297+ runner = CliRunner ()
298+ result = runner .invoke (cli , [
299+ "sandbox" , "exec" , "sb-xxx" ,
300+ "--code" , "print('x')" ,
301+ "--context-id" , "ctx-1" ,
302+ ])
303+ assert result .exit_code == 0 , result .output
304+
305+ call_kwargs = sb .context .execute .call_args .kwargs
306+ assert call_kwargs ["language" ] is None
307+ assert call_kwargs ["context_id" ] == "ctx-1"
308+
309+ def test_exec_context_id_and_language_mutually_exclusive (self ):
310+ mock_mod = _mock_sandbox_modules ()
311+ sb = _make_sandbox_obj ()
312+ mock_mod .Sandbox .connect .return_value = sb
313+ with _patch_sdk (mock_mod ):
314+ runner = CliRunner ()
315+ result = runner .invoke (cli , [
316+ "sandbox" , "exec" , "sb-xxx" ,
317+ "--code" , "print('x')" ,
318+ "--context-id" , "ctx-1" ,
319+ "--language" , "python" ,
320+ ])
321+ assert result .exit_code != 0
322+ assert "mutually exclusive" in result .output .lower ()
323+
269324 def test_cmd (self ):
270325 mock_mod = _mock_sandbox_modules ()
271326 sb = _make_sandbox_obj ()
@@ -286,16 +341,24 @@ def test_cmd(self):
286341class TestContextCommands :
287342
288343 def test_context_create (self ):
344+ """SDK returns a ContextOperations-like object; CLI must flatten it."""
289345 mock_mod = _mock_sandbox_modules ()
290346 sb = _make_sandbox_obj ()
291- sb .context .create .return_value = {"id" : "ctx-xxx" , "language" : "python" }
347+ ops = SimpleNamespace (
348+ context_id = "ctx-xxx" ,
349+ _language = "python" ,
350+ _cwd = "/workspace" ,
351+ )
352+ sb .context .create .return_value = ops
292353 mock_mod .Sandbox .connect .return_value = sb
293354 with _patch_sdk (mock_mod ):
294355 runner = CliRunner ()
295- result = runner .invoke (cli , ["sandbox" , "context" , "create" , "sb-xxx" ])
356+ result = runner .invoke (cli , ["sandbox" , "context" , "create" , "sb-xxx" , "--cwd" , "/workspace" ])
296357 assert result .exit_code == 0 , result .output
297358 data = json .loads (result .output )
298359 assert data ["id" ] == "ctx-xxx"
360+ assert data ["language" ] == "python"
361+ assert data ["cwd" ] == "/workspace"
299362
300363 def test_context_list (self ):
301364 mock_mod = _mock_sandbox_modules ()
@@ -308,14 +371,23 @@ def test_context_list(self):
308371 assert result .exit_code == 0 , result .output
309372
310373 def test_context_get (self ):
374+ """SDK returns a ContextOperations-like object; CLI must flatten it."""
311375 mock_mod = _mock_sandbox_modules ()
312376 sb = _make_sandbox_obj ()
313- sb .context .get .return_value = {"id" : "ctx-xxx" , "language" : "python" }
377+ ops = SimpleNamespace (
378+ context_id = "ctx-xxx" ,
379+ _language = "python" ,
380+ _cwd = "/home/user" ,
381+ )
382+ sb .context .get .return_value = ops
314383 mock_mod .Sandbox .connect .return_value = sb
315384 with _patch_sdk (mock_mod ):
316385 runner = CliRunner ()
317386 result = runner .invoke (cli , ["sandbox" , "ctx" , "get" , "sb-xxx" , "ctx-xxx" ])
318387 assert result .exit_code == 0 , result .output
388+ data = json .loads (result .output )
389+ assert data ["id" ] == "ctx-xxx"
390+ assert data ["language" ] == "python"
319391
320392 def test_context_delete (self ):
321393 mock_mod = _mock_sandbox_modules ()
@@ -355,6 +427,7 @@ def test_file_write(self):
355427 assert result .exit_code == 0 , result .output
356428
357429 def test_file_upload (self , tmp_path ):
430+ """CLI must call SDK upload with local_file_path / target_file_path kwargs."""
358431 mock_mod , sb = self ._setup ()
359432 sb .file_system .upload .return_value = {"success" : True }
360433 local_file = tmp_path / "data.csv"
@@ -364,6 +437,12 @@ def test_file_upload(self, tmp_path):
364437 result = runner .invoke (cli , ["sandbox" , "f" , "upload" , "sb-xxx" , str (local_file ), "/data.csv" ])
365438 assert result .exit_code == 0 , result .output
366439
440+ call_kwargs = sb .file_system .upload .call_args .kwargs
441+ assert call_kwargs ["local_file_path" ] == str (local_file )
442+ assert call_kwargs ["target_file_path" ] == "/data.csv"
443+ assert "local_path" not in call_kwargs
444+ assert "remote_path" not in call_kwargs
445+
367446 def test_file_download (self ):
368447 mock_mod , sb = self ._setup ()
369448 sb .file_system .download .return_value = {"saved_path" : "./out.txt" , "size" : 10 }
@@ -447,6 +526,26 @@ def test_process_kill(self):
447526 data = json .loads (result .output )
448527 assert data ["killed" ] is True
449528
529+ def test_process_kill_force_shell (self ):
530+ """--force-shell routes through process.cmd('kill -9 <pid>')."""
531+ mock_mod = _mock_sandbox_modules ()
532+ sb = _make_sandbox_obj ()
533+ sb .process .cmd .return_value = {"exit_code" : 0 , "stdout" : "" , "stderr" : "" }
534+ mock_mod .Sandbox .connect .return_value = sb
535+ with _patch_sdk (mock_mod ):
536+ runner = CliRunner ()
537+ result = runner .invoke (cli , ["sandbox" , "ps" , "kill" , "sb-xxx" , "128" , "--force-shell" ])
538+ assert result .exit_code == 0 , result .output
539+ data = json .loads (result .output )
540+ assert data ["pid" ] == "128"
541+ assert data ["killed_via" ] == "shell"
542+
543+ call_kwargs = sb .process .cmd .call_args .kwargs
544+ assert call_kwargs ["command" ] == "kill -9 128"
545+ assert call_kwargs ["cwd" ] == "/"
546+ # Regular kill should not have been called.
547+ sb .process .kill .assert_not_called ()
548+
450549
451550class TestBrowserCommands :
452551
0 commit comments