mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-09 00:40:45 +08:00
完善 agent/workspace 运行链路与工具结果治理测试覆盖,确保路由、标题生成与上下文截断行为稳定可回归。
同时将 data/wiki 用户数据纳入忽略规则,避免运行态数据被误纳入版本管理。 Made-with: Cursor
This commit is contained in:
parent
45e412b4ab
commit
20362527e2
17 changed files with 974 additions and 41 deletions
|
|
@ -13,12 +13,15 @@ from oclaw.interfaces.http.fastapi_app import create_app
|
|||
from oclaw.platform.config.paths import db_path
|
||||
from oclaw.platform.persistence.sqlite_store import SqliteStore
|
||||
from oclaw.runtime.tools.experts.workspace.fs_tools import list_files_tool, write_file_tool
|
||||
from oclaw.runtime.tools.experts.workspace.shell_tools import run_command_tool
|
||||
from oclaw.runtime.tools.experts.workspace.workspace_base import (
|
||||
access_from_env,
|
||||
build_workspace_path_access,
|
||||
clear_workspace_path_access_for_tests,
|
||||
current_workspace_write_namespace,
|
||||
resolve_workspace_path,
|
||||
workspace_path_access_scope,
|
||||
workspace_write_namespace_scope,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -111,10 +114,223 @@ class WorkspacePathGuardTests(unittest.TestCase):
|
|||
with workspace_path_access_scope(None, None):
|
||||
r = spec.handler({"path": "generated.py", "content": "print('ok')\n", "mode": "overwrite"})
|
||||
self.assertTrue(r.get("ok"), r)
|
||||
expected = (self.root / "data" / self.root.name / "generated.py").resolve()
|
||||
expected = (self.root / "data" / "workspace" / "generated.py").resolve()
|
||||
self.assertEqual(str(expected), str(r.get("path")))
|
||||
self.assertTrue(expected.exists())
|
||||
|
||||
def test_write_file_relative_path_uses_workspace_namespace_scope(self) -> None:
|
||||
with mock.patch.dict(
|
||||
os.environ,
|
||||
{"OPS_WORKSPACE_ROOT": str(self.root), "OPS_WORKSPACE_EXTRA_ROOTS": "", "OPS_WORKSPACE_ALLOW_ANY_PATH": ""},
|
||||
clear=False,
|
||||
):
|
||||
clear_workspace_path_access_for_tests()
|
||||
spec = write_file_tool()
|
||||
with workspace_path_access_scope(None, None), workspace_write_namespace_scope("ops"):
|
||||
self.assertEqual(current_workspace_write_namespace(), "ops")
|
||||
r = spec.handler({"path": "generated.py", "content": "print('ok')\n", "mode": "overwrite"})
|
||||
self.assertTrue(r.get("ok"), r)
|
||||
expected = (self.root / "data" / "workspace" / "generated.py").resolve()
|
||||
self.assertEqual(str(expected), str(r.get("path")))
|
||||
self.assertTrue(expected.exists())
|
||||
|
||||
def test_write_file_absolute_path_is_forced_into_workspace_sandbox(self) -> None:
|
||||
with mock.patch.dict(
|
||||
os.environ,
|
||||
{"OPS_WORKSPACE_ROOT": str(self.root), "OPS_WORKSPACE_EXTRA_ROOTS": "", "OPS_WORKSPACE_ALLOW_ANY_PATH": ""},
|
||||
clear=False,
|
||||
):
|
||||
clear_workspace_path_access_for_tests()
|
||||
spec = write_file_tool()
|
||||
abs_target = str((self.root / "count_items.py").resolve())
|
||||
with workspace_path_access_scope(None, None), workspace_write_namespace_scope("ops"):
|
||||
r = spec.handler({"path": abs_target, "content": "print('ok')\n", "mode": "overwrite"})
|
||||
self.assertTrue(r.get("ok"), r)
|
||||
expected = (self.root / "data" / "workspace" / "count_items.py").resolve()
|
||||
self.assertEqual(str(expected), str(r.get("path")))
|
||||
self.assertTrue(expected.exists())
|
||||
|
||||
def test_run_command_default_cwd_uses_workspace_namespace_sandbox(self) -> None:
|
||||
with mock.patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
"OPS_WORKSPACE_ROOT": str(self.root),
|
||||
"OPS_WORKSPACE_EXTRA_ROOTS": "",
|
||||
"OPS_WORKSPACE_ALLOW_ANY_PATH": "",
|
||||
"AIA_ENABLE_RUN_COMMAND": "1",
|
||||
},
|
||||
clear=False,
|
||||
):
|
||||
clear_workspace_path_access_for_tests()
|
||||
spec = run_command_tool()
|
||||
with workspace_path_access_scope(None, None), workspace_write_namespace_scope("ops"):
|
||||
r = spec.handler({"command": "python -c \"print('ok')\""})
|
||||
self.assertTrue(r.get("ok"), r)
|
||||
expected_cwd = (self.root / "data" / "workspace").resolve()
|
||||
self.assertEqual(str(expected_cwd), str(r.get("cwd")))
|
||||
|
||||
def test_run_command_strips_leading_cd_chain_in_default_sandbox(self) -> None:
|
||||
with mock.patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
"OPS_WORKSPACE_ROOT": str(self.root),
|
||||
"OPS_WORKSPACE_EXTRA_ROOTS": "",
|
||||
"OPS_WORKSPACE_ALLOW_ANY_PATH": "",
|
||||
"AIA_ENABLE_RUN_COMMAND": "1",
|
||||
},
|
||||
clear=False,
|
||||
):
|
||||
clear_workspace_path_access_for_tests()
|
||||
spec = run_command_tool()
|
||||
cmd = f'cd /d "{self.root}" && python -c "print(123)"'
|
||||
with workspace_path_access_scope(None, None), workspace_write_namespace_scope("ops"):
|
||||
r = spec.handler({"command": cmd})
|
||||
self.assertTrue(r.get("ok"), r)
|
||||
self.assertTrue(bool(r.get("normalized_cd_removed")))
|
||||
expected_cwd = (self.root / "data" / "workspace").resolve()
|
||||
self.assertEqual(str(expected_cwd), str(r.get("cwd")))
|
||||
self.assertIn("123", str(r.get("output") or ""))
|
||||
|
||||
def test_run_command_strips_windows_drive_prefix_cd_chain(self) -> None:
|
||||
with mock.patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
"OPS_WORKSPACE_ROOT": str(self.root),
|
||||
"OPS_WORKSPACE_EXTRA_ROOTS": "",
|
||||
"OPS_WORKSPACE_ALLOW_ANY_PATH": "",
|
||||
"AIA_ENABLE_RUN_COMMAND": "1",
|
||||
},
|
||||
clear=False,
|
||||
):
|
||||
clear_workspace_path_access_for_tests()
|
||||
ws_root = self.root / "data" / "workspace"
|
||||
ws_root.mkdir(parents=True, exist_ok=True)
|
||||
(ws_root / "count_directory.py").write_text("print('drive-cd-ok')\n", encoding="utf-8")
|
||||
spec = run_command_tool()
|
||||
cmd = f'D: && cd /d "{self.root}" && python count_directory.py'
|
||||
with workspace_path_access_scope(None, None):
|
||||
r = spec.handler({"command": cmd})
|
||||
self.assertTrue(r.get("ok"), r)
|
||||
self.assertTrue(bool(r.get("normalized_cd_removed")), r)
|
||||
self.assertTrue(bool(r.get("script_path_rewritten")), r)
|
||||
self.assertIn("drive-cd-ok", str(r.get("output") or ""))
|
||||
|
||||
def test_run_command_output_flags_distinguish_empty_from_truncation(self) -> None:
|
||||
with mock.patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
"OPS_WORKSPACE_ROOT": str(self.root),
|
||||
"OPS_WORKSPACE_EXTRA_ROOTS": "",
|
||||
"OPS_WORKSPACE_ALLOW_ANY_PATH": "",
|
||||
"AIA_ENABLE_RUN_COMMAND": "1",
|
||||
},
|
||||
clear=False,
|
||||
):
|
||||
clear_workspace_path_access_for_tests()
|
||||
spec = run_command_tool()
|
||||
with workspace_path_access_scope(None, None), workspace_write_namespace_scope("ops"):
|
||||
r = spec.handler({"command": 'python -c "pass"'})
|
||||
self.assertTrue(r.get("ok"), r)
|
||||
self.assertTrue(bool(r.get("output_empty")))
|
||||
self.assertFalse(bool(r.get("output_truncated")))
|
||||
self.assertTrue(bool(r.get("output_not_truncated")))
|
||||
self.assertEqual(str(r.get("error_code") or ""), "")
|
||||
|
||||
def test_run_command_nonzero_exit_marks_failure_not_truncation(self) -> None:
|
||||
with mock.patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
"OPS_WORKSPACE_ROOT": str(self.root),
|
||||
"OPS_WORKSPACE_EXTRA_ROOTS": "",
|
||||
"OPS_WORKSPACE_ALLOW_ANY_PATH": "",
|
||||
"AIA_ENABLE_RUN_COMMAND": "1",
|
||||
},
|
||||
clear=False,
|
||||
):
|
||||
clear_workspace_path_access_for_tests()
|
||||
spec = run_command_tool()
|
||||
with workspace_path_access_scope(None, None), workspace_write_namespace_scope("ops"):
|
||||
r = spec.handler({"command": 'python -c "import sys; sys.exit(3)"'})
|
||||
self.assertFalse(bool(r.get("ok")))
|
||||
self.assertEqual(int(r.get("exit_code") or 0), 3)
|
||||
self.assertEqual(str(r.get("error_code") or ""), "command_exit_nonzero")
|
||||
self.assertFalse(bool(r.get("output_truncated")))
|
||||
|
||||
def test_run_command_rewrites_workspace_absolute_script_path_to_sandbox(self) -> None:
|
||||
with mock.patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
"OPS_WORKSPACE_ROOT": str(self.root),
|
||||
"OPS_WORKSPACE_EXTRA_ROOTS": "",
|
||||
"OPS_WORKSPACE_ALLOW_ANY_PATH": "",
|
||||
"AIA_ENABLE_RUN_COMMAND": "1",
|
||||
},
|
||||
clear=False,
|
||||
):
|
||||
clear_workspace_path_access_for_tests()
|
||||
# Prepare script inside sandbox, but command will reference repo-root absolute path.
|
||||
ws_script = self.root / "data" / "workspace" / "count_files.py"
|
||||
ws_script.parent.mkdir(parents=True, exist_ok=True)
|
||||
ws_script.write_text("print('sandbox-ok')\n", encoding="utf-8")
|
||||
spec = run_command_tool()
|
||||
absolute_repo_script = str((self.root / "count_files.py").resolve())
|
||||
with workspace_path_access_scope(None, None), workspace_write_namespace_scope("ops"):
|
||||
r = spec.handler({"command": f'python "{absolute_repo_script}"'})
|
||||
self.assertTrue(r.get("ok"), r)
|
||||
self.assertTrue(bool(r.get("command_rewritten")), r)
|
||||
self.assertIn("sandbox-ok", str(r.get("output") or ""))
|
||||
|
||||
def test_run_command_explicit_repo_root_cwd_is_redirected_to_sandbox(self) -> None:
|
||||
with mock.patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
"OPS_WORKSPACE_ROOT": str(self.root),
|
||||
"OPS_WORKSPACE_EXTRA_ROOTS": "",
|
||||
"OPS_WORKSPACE_ALLOW_ANY_PATH": "",
|
||||
"AIA_ENABLE_RUN_COMMAND": "1",
|
||||
},
|
||||
clear=False,
|
||||
):
|
||||
clear_workspace_path_access_for_tests()
|
||||
ws_script = self.root / "data" / "workspace" / "count_files.py"
|
||||
ws_script.parent.mkdir(parents=True, exist_ok=True)
|
||||
ws_script.write_text("print('redirect-ok')\n", encoding="utf-8")
|
||||
spec = run_command_tool()
|
||||
abs_repo_script = str((self.root / "count_files.py").resolve())
|
||||
with workspace_path_access_scope(None, None):
|
||||
r = spec.handler(
|
||||
{
|
||||
"command": f'python "{abs_repo_script}"',
|
||||
"cwd": str(self.root),
|
||||
}
|
||||
)
|
||||
self.assertTrue(r.get("ok"), r)
|
||||
self.assertTrue(bool(r.get("cwd_redirected_to_sandbox")), r)
|
||||
self.assertTrue(bool(r.get("command_rewritten")), r)
|
||||
self.assertIn("redirect-ok", str(r.get("output") or ""))
|
||||
|
||||
def test_run_command_rewrites_relative_python_script_to_sandbox_root(self) -> None:
|
||||
with mock.patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
"OPS_WORKSPACE_ROOT": str(self.root),
|
||||
"OPS_WORKSPACE_EXTRA_ROOTS": "",
|
||||
"OPS_WORKSPACE_ALLOW_ANY_PATH": "",
|
||||
"AIA_ENABLE_RUN_COMMAND": "1",
|
||||
},
|
||||
clear=False,
|
||||
):
|
||||
clear_workspace_path_access_for_tests()
|
||||
ws_root = self.root / "data" / "workspace"
|
||||
ws_root.mkdir(parents=True, exist_ok=True)
|
||||
(ws_root / "count_directory.py").write_text("print('found-in-sandbox-root')\n", encoding="utf-8")
|
||||
spec = run_command_tool()
|
||||
with workspace_path_access_scope(None, None):
|
||||
r = spec.handler({"command": "python count_directory.py"})
|
||||
self.assertTrue(r.get("ok"), r)
|
||||
self.assertTrue(bool(r.get("script_path_rewritten")), r)
|
||||
self.assertIn("found-in-sandbox-root", str(r.get("output") or ""))
|
||||
|
||||
def test_per_user_extra_roots_from_db(self) -> None:
|
||||
f = self.extra / "u.txt"
|
||||
f.write_text("u", encoding="utf-8")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue