mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-06 06:52:20 +02:00
test(runtime): isolate tool execution module state
This commit is contained in:
@@ -8,19 +8,38 @@ language hint, not a "python" tool call with content "3\n...".
|
||||
import sys
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
for mod in ['src.agent_tools', 'src.tool_parsing', 'src.tool_schemas', 'src.tool_execution']:
|
||||
sys.modules.pop(mod, None)
|
||||
for mod in [
|
||||
'sqlalchemy', 'sqlalchemy.orm', 'sqlalchemy.ext', 'sqlalchemy.ext.declarative',
|
||||
'sqlalchemy.ext.hybrid', 'sqlalchemy.sql', 'sqlalchemy.sql.expression',
|
||||
'src.database', 'core.models', 'core.database', 'core.auth'
|
||||
]:
|
||||
if mod not in sys.modules:
|
||||
sys.modules[mod] = MagicMock()
|
||||
# This module needs the real agent-tool stack; importing it pulls in heavy
|
||||
# DB/auth deps, so we stub those just long enough to import, then restore them.
|
||||
# We deliberately do NOT pop src.tool_execution: popping and re-importing it
|
||||
# rebinds the `src` package's `tool_execution` attribute, so a later
|
||||
# `import src.tool_execution as te` resolves to a different module object than
|
||||
# the one its functions live in - which silently breaks tests that monkeypatch
|
||||
# it (e.g. test_edit_file's admin gate) and breaks request-scoped ContextVars.
|
||||
_ABSENT = object()
|
||||
_AGENT_MODULES = ["src.agent_tools", "src.tool_parsing", "src.tool_schemas"]
|
||||
_STUBBED = [
|
||||
"sqlalchemy", "sqlalchemy.orm", "sqlalchemy.ext", "sqlalchemy.ext.declarative",
|
||||
"sqlalchemy.ext.hybrid", "sqlalchemy.sql", "sqlalchemy.sql.expression",
|
||||
"src.database", "core.models", "core.database", "core.auth",
|
||||
]
|
||||
_saved_stubs = {name: sys.modules.get(name, _ABSENT) for name in _STUBBED}
|
||||
|
||||
for _mod in _AGENT_MODULES:
|
||||
sys.modules.pop(_mod, None)
|
||||
for _mod in _STUBBED:
|
||||
if _mod not in sys.modules:
|
||||
sys.modules[_mod] = MagicMock()
|
||||
|
||||
import src.agent_tools # noqa: E402, F401
|
||||
from src.tool_parsing import parse_tool_blocks, strip_tool_blocks # noqa: E402
|
||||
|
||||
# Drop the stubs we installed so they do not leak into later tests.
|
||||
for _name, _original in _saved_stubs.items():
|
||||
if _original is _ABSENT:
|
||||
sys.modules.pop(_name, None)
|
||||
else:
|
||||
sys.modules[_name] = _original
|
||||
|
||||
|
||||
def test_inline_args_on_tag_line_parse():
|
||||
# The original bug: ```list_email_accounts {} (args on the tag line)
|
||||
|
||||
@@ -6,19 +6,38 @@ to the code executor as if it were python/bash.
|
||||
import sys
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
for mod in ['src.agent_tools', 'src.tool_parsing', 'src.tool_schemas', 'src.tool_execution']:
|
||||
sys.modules.pop(mod, None)
|
||||
for mod in [
|
||||
'sqlalchemy', 'sqlalchemy.orm', 'sqlalchemy.ext', 'sqlalchemy.ext.declarative',
|
||||
'sqlalchemy.ext.hybrid', 'sqlalchemy.sql', 'sqlalchemy.sql.expression',
|
||||
'src.database', 'core.models', 'core.database', 'core.auth'
|
||||
]:
|
||||
if mod not in sys.modules:
|
||||
sys.modules[mod] = MagicMock()
|
||||
# This module needs the real agent-tool stack; importing it pulls in heavy
|
||||
# DB/auth deps, so we stub those just long enough to import, then restore them.
|
||||
# We deliberately do NOT pop src.tool_execution: popping and re-importing it
|
||||
# rebinds the `src` package's `tool_execution` attribute, so a later
|
||||
# `import src.tool_execution as te` resolves to a different module object than
|
||||
# the one its functions live in - which silently breaks tests that monkeypatch
|
||||
# it (e.g. test_edit_file's admin gate) and breaks request-scoped ContextVars.
|
||||
_ABSENT = object()
|
||||
_AGENT_MODULES = ["src.agent_tools", "src.tool_parsing", "src.tool_schemas"]
|
||||
_STUBBED = [
|
||||
"sqlalchemy", "sqlalchemy.orm", "sqlalchemy.ext", "sqlalchemy.ext.declarative",
|
||||
"sqlalchemy.ext.hybrid", "sqlalchemy.sql", "sqlalchemy.sql.expression",
|
||||
"src.database", "core.models", "core.database", "core.auth",
|
||||
]
|
||||
_saved_stubs = {name: sys.modules.get(name, _ABSENT) for name in _STUBBED}
|
||||
|
||||
for _mod in _AGENT_MODULES:
|
||||
sys.modules.pop(_mod, None)
|
||||
for _mod in _STUBBED:
|
||||
if _mod not in sys.modules:
|
||||
sys.modules[_mod] = MagicMock()
|
||||
|
||||
import src.agent_tools # noqa: E402, F401
|
||||
from src.tool_parsing import parse_tool_blocks # noqa: E402
|
||||
|
||||
# Drop the stubs we installed so they do not leak into later tests.
|
||||
for _name, _original in _saved_stubs.items():
|
||||
if _original is _ABSENT:
|
||||
sys.modules.pop(_name, None)
|
||||
else:
|
||||
sys.modules[_name] = _original
|
||||
|
||||
|
||||
def test_unconvertible_invoke_in_fence_is_not_executed_as_code():
|
||||
text = '```python\n<invoke name="foo-bar">\n<parameter name="x">1</parameter>\n</invoke>\n```'
|
||||
|
||||
@@ -12,19 +12,38 @@ import json
|
||||
import sys
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
for mod in ['src.agent_tools', 'src.tool_parsing', 'src.tool_schemas', 'src.tool_execution']:
|
||||
sys.modules.pop(mod, None)
|
||||
for mod in [
|
||||
'sqlalchemy', 'sqlalchemy.orm', 'sqlalchemy.ext', 'sqlalchemy.ext.declarative',
|
||||
'sqlalchemy.ext.hybrid', 'sqlalchemy.sql', 'sqlalchemy.sql.expression',
|
||||
'src.database', 'core.models', 'core.database', 'core.auth'
|
||||
]:
|
||||
if mod not in sys.modules:
|
||||
sys.modules[mod] = MagicMock()
|
||||
# This module needs the real agent-tool stack; importing it pulls in heavy
|
||||
# DB/auth deps, so we stub those just long enough to import, then restore them.
|
||||
# We deliberately do NOT pop src.tool_execution: popping and re-importing it
|
||||
# rebinds the `src` package's `tool_execution` attribute, so a later
|
||||
# `import src.tool_execution as te` resolves to a different module object than
|
||||
# the one its functions live in - which silently breaks tests that monkeypatch
|
||||
# it (e.g. test_edit_file's admin gate) and breaks request-scoped ContextVars.
|
||||
_ABSENT = object()
|
||||
_AGENT_MODULES = ["src.agent_tools", "src.tool_parsing", "src.tool_schemas"]
|
||||
_STUBBED = [
|
||||
"sqlalchemy", "sqlalchemy.orm", "sqlalchemy.ext", "sqlalchemy.ext.declarative",
|
||||
"sqlalchemy.ext.hybrid", "sqlalchemy.sql", "sqlalchemy.sql.expression",
|
||||
"src.database", "core.models", "core.database", "core.auth",
|
||||
]
|
||||
_saved_stubs = {name: sys.modules.get(name, _ABSENT) for name in _STUBBED}
|
||||
|
||||
for _mod in _AGENT_MODULES:
|
||||
sys.modules.pop(_mod, None)
|
||||
for _mod in _STUBBED:
|
||||
if _mod not in sys.modules:
|
||||
sys.modules[_mod] = MagicMock()
|
||||
|
||||
import src.agent_tools # noqa: E402, F401
|
||||
from src.tool_parsing import parse_tool_blocks, strip_tool_blocks # noqa: E402
|
||||
|
||||
# Drop the stubs we installed so they do not leak into later tests.
|
||||
for _name, _original in _saved_stubs.items():
|
||||
if _original is _ABSENT:
|
||||
sys.modules.pop(_name, None)
|
||||
else:
|
||||
sys.modules[_name] = _original
|
||||
|
||||
|
||||
def test_raw_json_after_web_search_phrase_runs_as_web_search():
|
||||
text = (
|
||||
|
||||
@@ -9,25 +9,40 @@ bare string (back-compat).
|
||||
import sys
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
# Clean up any mocks from previous tests to ensure we load real modules.
|
||||
for mod in ['src.agent_tools', 'src.tool_parsing', 'src.tool_schemas', 'src.tool_execution']:
|
||||
sys.modules.pop(mod, None)
|
||||
# This module needs the real agent-tool stack; importing it pulls in heavy
|
||||
# DB/auth deps, so we stub those just long enough to import, then restore them.
|
||||
# We deliberately do NOT pop src.tool_execution: popping and re-importing it
|
||||
# rebinds the `src` package's `tool_execution` attribute, so a later
|
||||
# `import src.tool_execution as te` resolves to a different module object than
|
||||
# the one its functions live in - which silently breaks tests that monkeypatch
|
||||
# it (e.g. test_edit_file's admin gate) and breaks request-scoped ContextVars.
|
||||
_ABSENT = object()
|
||||
_AGENT_MODULES = ["src.agent_tools", "src.tool_parsing", "src.tool_schemas"]
|
||||
_STUBBED = [
|
||||
"sqlalchemy", "sqlalchemy.orm", "sqlalchemy.ext", "sqlalchemy.ext.declarative",
|
||||
"sqlalchemy.ext.hybrid", "sqlalchemy.sql", "sqlalchemy.sql.expression",
|
||||
"src.database", "core.models", "core.database", "core.auth",
|
||||
]
|
||||
_saved_stubs = {name: sys.modules.get(name, _ABSENT) for name in _STUBBED}
|
||||
|
||||
# Mock heavy database/model dependencies before importing (avoids the
|
||||
# src.tool_schemas <-> src.agent_tools circular import pulling in the DB layer).
|
||||
for mod in [
|
||||
'sqlalchemy', 'sqlalchemy.orm', 'sqlalchemy.ext', 'sqlalchemy.ext.declarative',
|
||||
'sqlalchemy.ext.hybrid', 'sqlalchemy.sql', 'sqlalchemy.sql.expression',
|
||||
'src.database', 'core.models', 'core.database', 'core.auth'
|
||||
]:
|
||||
if mod not in sys.modules:
|
||||
sys.modules[mod] = MagicMock()
|
||||
for _mod in _AGENT_MODULES:
|
||||
sys.modules.pop(_mod, None)
|
||||
for _mod in _STUBBED:
|
||||
if _mod not in sys.modules:
|
||||
sys.modules[_mod] = MagicMock()
|
||||
|
||||
import json # noqa: E402
|
||||
|
||||
import src.agent_tools # noqa: E402, F401
|
||||
from src.tool_schemas import function_call_to_tool_block # noqa: E402
|
||||
|
||||
# Drop the stubs we installed so they do not leak into later tests.
|
||||
for _name, _original in _saved_stubs.items():
|
||||
if _original is _ABSENT:
|
||||
sys.modules.pop(_name, None)
|
||||
else:
|
||||
sys.modules[_name] = _original
|
||||
|
||||
|
||||
def test_time_filter_is_preserved_as_json():
|
||||
block = function_call_to_tool_block(
|
||||
|
||||
Reference in New Issue
Block a user