mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 08:11:59 +00:00
aa18904af0
用户:「点击右上角运行后,会跳到流程;然后下方就又有 Copilot 对话框,把 结果和 Copilot 又放在一块儿了」。说得对,而且我上一版的理由是错的。 我当时把建图和跑图排进同一条时间轴,说它们是"一件事的两半"。它们在时间上 先后发生,但不是同一种东西——最直接的证据在我自己的代码里:运行那一轮的 question 只能填 undefined。一个没有提问的"对话轮次",本来就不是对话。 更实际的问题是那个输入框:它贴在运行结果下面,暗示你可以回复这次运行。 但你不能——在那儿打字改的是画布上的图。这是个假的可供性。两者的生命周期 也对不上:Copilot 的轮次会累积,运行永远只有最新一次。 现在右栏是一个常驻的家(Copilot 对话)加两层临时盖上来的东西(节点属性、 运行过程)。运行那一层没有输入框,有自己的头部和停止按钮;对话那一层底部 留一条运行进度,点一下过去。 --- 顺着这条线查下去,发现一个更严重的问题:**后端在丢事件**。 浏览器侧连跑 5 次,2 次丢了中间一条。抓 WebSocket 原始帧才看清:所有帧的 replay 都是 false——历史读回来是空的,全部走实时;丢的是 run.started, 它在客户端订阅之前就广播了,而那时还没落库,所以历史里也没有。 根因是 _emit 的顺序:先广播、后落库。订阅者的完整性靠"先订阅 → 读历史 → 接实时"拼起来,它需要这条保证——提交早于我这次 SELECT 的,在历史里;提交 晚于我这次 SELECT 的,广播也晚于我订阅,在实时流里。后半句只有在"提交先于 广播"时才成立。交换顺序后,6 次 WebSocket 探测 + 5 次浏览器运行全部无缺口。 (上一轮我修过这个文件的另一个窗口——先读历史后订阅。那个修复是对的,但 只堵住了两步之间的缝,堵不住订阅建立之前的那一段。) tests/test_emit_order.py 守这条不变式:广播的那一刻,这条事件必须已经能从 另一个 session 查到。去掉修复后它确实失败。 --- 还修了三处: - 停在人工介入时,"在不在等人"原先取自审批列表,而那是 4 秒轮询一次的; 中断后会有几秒钟界面说"这次运行已结束",而它正等着你点通过。改成从事件 推导(isAwaitingHuman),即时且和显示的内容一致。 - 新增 tests/conftest.py 把整个测试会话关进临时目录。test_emit_order 第一版 直接用 SessionLocal 往开发库里写,几行假运行排到了运行列表最前面。已清掉 那 12 行。这该是全局保障,不是只修一个文件。 - 开发期把 store 挂到 window.__studio,好让 playwright 直接读状态——"事件 收全了没有"在界面上看不出来,只能问 store。已确认不进生产包。
44 lines
1.5 KiB
Python
44 lines
1.5 KiB
Python
"""把整个测试会话关进一个临时目录。
|
||
|
||
不这么做的话,任何一个直接用 SessionLocal 的测试都会往开发库
|
||
(data/agentlab.db)里写行——写进去的 run 会出现在运行列表里,和真实运行
|
||
混在一起,而且带着测试的假数据。这不是假想:test_emit_order 第一版就这么
|
||
干了,几行 emit-order-* 的假运行直接排在了列表最前面。
|
||
|
||
必须在 app.* 被 import 之前设好环境变量:settings 是模块级单例,engine 在
|
||
app.db.base 导入时就按 db_url 建好了。conftest 早于测试模块加载,所以这里
|
||
是唯一还来得及的地方。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import os
|
||
import tempfile
|
||
from pathlib import Path
|
||
|
||
_TMP = Path(tempfile.mkdtemp(prefix="agentlab-tests-"))
|
||
os.environ["AGENTLAB_DATA_DIR"] = str(_TMP)
|
||
# 别把真实的 key 带进测试环境
|
||
os.environ.setdefault("AGENTLAB_SECRET_KEY", "test-only-not-a-real-key")
|
||
|
||
import pytest # noqa: E402
|
||
|
||
|
||
@pytest.fixture(scope="session", autouse=True)
|
||
def _prepare_schema():
|
||
"""建表。测试库是全新的临时文件,什么都没有。"""
|
||
import asyncio
|
||
|
||
from app.db.base import Base, engine
|
||
|
||
async def create() -> None:
|
||
async with engine.begin() as conn:
|
||
await conn.run_sync(Base.metadata.create_all)
|
||
|
||
asyncio.get_event_loop_policy().new_event_loop().run_until_complete(create())
|
||
yield
|
||
|
||
|
||
def pytest_report_header(config) -> str:
|
||
return f"测试数据目录:{_TMP}"
|