Files
forge/apps/api/tests/test_e2e_regressions.py
nihalashetty ae67bff5a3 feat: deep-agent canvas, live observability, and multi-environment tooling
Self-hosted platform for building, testing, and shipping LangChain/LangGraph agents. Deep-agent sub-agents on the canvas, a live tracing/observability timeline, auto-provisioned built-in tools with import/export, per-environment tool variables, streamed evaluations, and per-user auth token forwarding.
2026-07-28 01:49:19 +05:30

69 lines
3.1 KiB
Python

"""Regressions found by the full end-to-end application test (don't let them come back)."""
from __future__ import annotations
from forge.db.base import SessionLocal
from forge.models import Trigger
from forge.services.tools import ToolService
from forge.services.validation import validate_workflow
from forge.services.workflows import WorkflowService
def test_trigger_and_flow_nodes_validate():
"""node_schema_ref must resolve trigger/flow schemas whose file name != node type
(e.g. webhook_in -> forge/nodes/trigger_webhook). Previously: Unresolvable schema."""
wf = {
"id": "w", "version": 1,
"state": {"messages": {"type": "list[message]", "reducer": "add_messages"}},
"entry_node": "hook",
"nodes": [
{"id": "hook", "type": "webhook_in", "config": {"message_path": "text"}},
{"id": "agent", "type": "agent", "config": {"flavor": "agent", "model": "fake:hi"}},
{"id": "end", "type": "end", "config": {}},
],
"edges": [{"source": "hook", "target": "agent"}, {"source": "agent", "target": "end"}],
}
res = validate_workflow(wf)
assert res.valid, res.errors
async def test_tool_test_supports_code_and_sql(tmp_path, monkeypatch):
import sqlite3
from forge.config import settings
# Code tools are OFF by default (unsandboxed RestrictedPython is not an isolation
# boundary - audit S5); this test exercises the feature, so opt in explicitly.
monkeypatch.setattr(settings, "enable_code_tools", True)
code = {"name": "u", "kind": "code", "description": "upper", "language": "python",
"source": "def main(s):\n return s.upper()", "args_schema": {"properties": {"s": {"type": "string"}}, "required": ["s"]}}
r = await ToolService.test("t", "p", code, {"s": "hi"})
assert r["ok"] and r["projected"] == "HI"
db = tmp_path / "t.db"
con = sqlite3.connect(db)
con.executescript("CREATE TABLE t(id int, name text); INSERT INTO t VALUES (1,'x');")
con.commit()
con.close()
sql = {"name": "q", "kind": "sql", "description": "q", "connection_url": f"sqlite+aiosqlite:///{db.as_posix()}",
"query": "SELECT name FROM t WHERE id = :id", "args_schema": {"properties": {"id": {"type": "integer"}}}}
r = await ToolService.test("t", "p", sql, {"id": 1})
assert r["ok"] and r["projected"] == [{"name": "x"}]
async def test_workflow_create_syncs_triggers():
"""Creating a workflow whose executable has a webhook_in must register a Trigger row."""
ex = {
"id": "w2", "version": 1,
"state": {"messages": {"type": "list[message]", "reducer": "add_messages"}},
"entry_node": "hook",
"nodes": [
{"id": "hook", "type": "webhook_in", "config": {}},
{"id": "end", "type": "end", "config": {}},
],
"edges": [{"source": "hook", "target": "end"}],
}
async with SessionLocal() as s:
wf = await WorkflowService.create(s, "t_sync", "p_sync", name="Hooked", executable=ex)
rows = (await s.execute(Trigger.__table__.select().where(Trigger.workflow_id == wf.id))).fetchall()
assert len(rows) == 1 and rows[0].kind == "webhook_in" and rows[0].key