mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-11-02 19:22:22 +01: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
|
import sys
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
for mod in ['src.agent_tools', 'src.tool_parsing', 'src.tool_schemas', 'src.tool_execution']:
|
# This module needs the real agent-tool stack; importing it pulls in heavy
|
||||||
sys.modules.pop(mod, None)
|
# DB/auth deps, so we stub those just long enough to import, then restore them.
|
||||||
for mod in [
|
# We deliberately do NOT pop src.tool_execution: popping and re-importing it
|
||||||
'sqlalchemy', 'sqlalchemy.orm', 'sqlalchemy.ext', 'sqlalchemy.ext.declarative',
|
# rebinds the `src` package's `tool_execution` attribute, so a later
|
||||||
'sqlalchemy.ext.hybrid', 'sqlalchemy.sql', 'sqlalchemy.sql.expression',
|
# `import src.tool_execution as te` resolves to a different module object than
|
||||||
'src.database', 'core.models', 'core.database', 'core.auth'
|
# 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.
|
||||||
if mod not in sys.modules:
|
_ABSENT = object()
|
||||||
sys.modules[mod] = MagicMock()
|
_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
|
import src.agent_tools # noqa: E402, F401
|
||||||
from src.tool_parsing import parse_tool_blocks, strip_tool_blocks # noqa: E402
|
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():
|
def test_inline_args_on_tag_line_parse():
|
||||||
# The original bug: ```list_email_accounts {} (args on the tag line)
|
# 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
|
import sys
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
for mod in ['src.agent_tools', 'src.tool_parsing', 'src.tool_schemas', 'src.tool_execution']:
|
# This module needs the real agent-tool stack; importing it pulls in heavy
|
||||||
sys.modules.pop(mod, None)
|
# DB/auth deps, so we stub those just long enough to import, then restore them.
|
||||||
for mod in [
|
# We deliberately do NOT pop src.tool_execution: popping and re-importing it
|
||||||
'sqlalchemy', 'sqlalchemy.orm', 'sqlalchemy.ext', 'sqlalchemy.ext.declarative',
|
# rebinds the `src` package's `tool_execution` attribute, so a later
|
||||||
'sqlalchemy.ext.hybrid', 'sqlalchemy.sql', 'sqlalchemy.sql.expression',
|
# `import src.tool_execution as te` resolves to a different module object than
|
||||||
'src.database', 'core.models', 'core.database', 'core.auth'
|
# 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.
|
||||||
if mod not in sys.modules:
|
_ABSENT = object()
|
||||||
sys.modules[mod] = MagicMock()
|
_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
|
import src.agent_tools # noqa: E402, F401
|
||||||
from src.tool_parsing import parse_tool_blocks # noqa: E402
|
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():
|
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```'
|
text = '```python\n<invoke name="foo-bar">\n<parameter name="x">1</parameter>\n</invoke>\n```'
|
||||||
|
|||||||
@@ -12,19 +12,38 @@ import json
|
|||||||
import sys
|
import sys
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
for mod in ['src.agent_tools', 'src.tool_parsing', 'src.tool_schemas', 'src.tool_execution']:
|
# This module needs the real agent-tool stack; importing it pulls in heavy
|
||||||
sys.modules.pop(mod, None)
|
# DB/auth deps, so we stub those just long enough to import, then restore them.
|
||||||
for mod in [
|
# We deliberately do NOT pop src.tool_execution: popping and re-importing it
|
||||||
'sqlalchemy', 'sqlalchemy.orm', 'sqlalchemy.ext', 'sqlalchemy.ext.declarative',
|
# rebinds the `src` package's `tool_execution` attribute, so a later
|
||||||
'sqlalchemy.ext.hybrid', 'sqlalchemy.sql', 'sqlalchemy.sql.expression',
|
# `import src.tool_execution as te` resolves to a different module object than
|
||||||
'src.database', 'core.models', 'core.database', 'core.auth'
|
# 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.
|
||||||
if mod not in sys.modules:
|
_ABSENT = object()
|
||||||
sys.modules[mod] = MagicMock()
|
_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
|
import src.agent_tools # noqa: E402, F401
|
||||||
from src.tool_parsing import parse_tool_blocks, strip_tool_blocks # noqa: E402
|
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():
|
def test_raw_json_after_web_search_phrase_runs_as_web_search():
|
||||||
text = (
|
text = (
|
||||||
|
|||||||
@@ -9,25 +9,40 @@ bare string (back-compat).
|
|||||||
import sys
|
import sys
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
# Clean up any mocks from previous tests to ensure we load real modules.
|
# This module needs the real agent-tool stack; importing it pulls in heavy
|
||||||
for mod in ['src.agent_tools', 'src.tool_parsing', 'src.tool_schemas', 'src.tool_execution']:
|
# DB/auth deps, so we stub those just long enough to import, then restore them.
|
||||||
sys.modules.pop(mod, None)
|
# 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
|
for _mod in _AGENT_MODULES:
|
||||||
# src.tool_schemas <-> src.agent_tools circular import pulling in the DB layer).
|
sys.modules.pop(_mod, None)
|
||||||
for mod in [
|
for _mod in _STUBBED:
|
||||||
'sqlalchemy', 'sqlalchemy.orm', 'sqlalchemy.ext', 'sqlalchemy.ext.declarative',
|
if _mod not in sys.modules:
|
||||||
'sqlalchemy.ext.hybrid', 'sqlalchemy.sql', 'sqlalchemy.sql.expression',
|
sys.modules[_mod] = MagicMock()
|
||||||
'src.database', 'core.models', 'core.database', 'core.auth'
|
|
||||||
]:
|
|
||||||
if mod not in sys.modules:
|
|
||||||
sys.modules[mod] = MagicMock()
|
|
||||||
|
|
||||||
import json # noqa: E402
|
import json # noqa: E402
|
||||||
|
|
||||||
import src.agent_tools # noqa: E402, F401
|
import src.agent_tools # noqa: E402, F401
|
||||||
from src.tool_schemas import function_call_to_tool_block # noqa: E402
|
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():
|
def test_time_filter_is_preserved_as_json():
|
||||||
block = function_call_to_tool_block(
|
block = function_call_to_tool_block(
|
||||||
|
|||||||
Reference in New Issue
Block a user