ADK-gateway/backend/tests/test_agent_repo.py
2026-08-05 16:33:27 +08:00

161 lines
6.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Agent 池存储层单元测试。"""
import time
import pytest
from app.constants import AgentStatus
from app.models.schemas import AgentInfo
@pytest.mark.asyncio
async def test_upsert_and_get(agent_repo):
a = AgentInfo(agent_id="a1", endpoint="agent-1:5000", agent_tags=["compile", "test"], max_concurrent=2)
created = await agent_repo.upsert(a)
assert created is True
got = await agent_repo.get("a1")
assert got is not None
assert got.agent_tags == ["compile", "test"]
assert got.status == AgentStatus.READY
assert got.priority == 3 # 默认优先级
@pytest.mark.asyncio
async def test_priority_roundtrip_and_update(agent_repo):
a = AgentInfo(agent_id="a1", endpoint="e1", agent_tags=["compile"], priority=1)
await agent_repo.upsert(a)
assert (await agent_repo.get("a1")).priority == 1
assert await agent_repo.update_priority("a1", 5) is True
assert (await agent_repo.get("a1")).priority == 5
assert await agent_repo.update_priority("missing", 2) is False
@pytest.mark.asyncio
async def test_beat_and_stale(agent_repo):
a = AgentInfo(agent_id="a1", endpoint="agent-1:5000", agent_tags=["compile"])
await agent_repo.upsert(a)
assert await agent_repo.beat("a1", 1) is True
# 心跳为最新,不应 stale
stale = await agent_repo.stale_agents(1)
assert "a1" not in stale
@pytest.mark.asyncio
async def test_by_tags(agent_repo):
a1 = AgentInfo(agent_id="a1", endpoint="e1", agent_tags=["compile"], max_concurrent=2)
a2 = AgentInfo(agent_id="a2", endpoint="e2", agent_tags=["compile", "test"], max_concurrent=2)
a3 = AgentInfo(agent_id="a3", endpoint="e3", agent_tags=["test"], max_concurrent=2)
await agent_repo.upsert(a1)
await agent_repo.upsert(a2)
await agent_repo.upsert(a3)
ids = {a.agent_id for a in await agent_repo.by_tags(["compile"])}
assert ids == {"a1", "a2"}
ids = {a.agent_id for a in await agent_repo.by_tags(["compile", "test"])}
assert ids == {"a2"}
@pytest.mark.asyncio
async def test_by_tags_excludes_offline_unavailable_stopping(agent_repo):
"""调度候选排除 offline/unavailable/stopping允许 ready/processingprocessing 仍有并发容量)。"""
a1 = AgentInfo(agent_id="a1", endpoint="e1", agent_tags=["compile"], max_concurrent=2)
a2 = AgentInfo(agent_id="a2", endpoint="e2", agent_tags=["compile"], max_concurrent=2)
a3 = AgentInfo(agent_id="a3", endpoint="e3", agent_tags=["compile"], max_concurrent=2)
a4 = AgentInfo(agent_id="a4", endpoint="e4", agent_tags=["compile"], max_concurrent=2)
a5 = AgentInfo(agent_id="a5", endpoint="e5", agent_tags=["compile"], max_concurrent=2)
await agent_repo.upsert(a1)
await agent_repo.upsert(a2)
await agent_repo.upsert(a3)
await agent_repo.upsert(a4)
await agent_repo.upsert(a5)
await agent_repo.update_status("a2", AgentStatus.OFFLINE) # 离线(心跳失联)不可调度
await agent_repo.set_unavailable("a3") # 手动不可用不可调度
await agent_repo.update_status("a4", AgentStatus.STOPPING) # 停止中不可调度
await agent_repo.update_status("a5", AgentStatus.PROCESSING) # 处理中但还有容量,可调度
ids = {a.agent_id for a in await agent_repo.by_tags(["compile"])}
assert ids == {"a1", "a5"}
@pytest.mark.asyncio
async def test_unavailable_heartbeat_not_revive(agent_repo):
"""手动置不可用unavailable即使发心跳也不复活。"""
a1 = AgentInfo(agent_id="a1", endpoint="e1", agent_tags=["compile"], max_concurrent=2)
await agent_repo.upsert(a1)
await agent_repo.set_unavailable("a1")
assert (await agent_repo.get("a1")).status == AgentStatus.UNAVAILABLE
# 心跳不应复活
await agent_repo.beat("a1", 0)
assert (await agent_repo.get("a1")).status == AgentStatus.UNAVAILABLE
# 恢复可用
await agent_repo.set_available("a1")
assert (await agent_repo.get("a1")).status == AgentStatus.READY
@pytest.mark.asyncio
async def test_unavailable_distinct_from_offline(agent_repo):
"""离线(心跳失联)与不可用(手动禁用)是两个独立状态。"""
a1 = AgentInfo(agent_id="a1", endpoint="e1", agent_tags=["compile"], max_concurrent=2)
a2 = AgentInfo(agent_id="a2", endpoint="e2", agent_tags=["compile"], max_concurrent=2)
await agent_repo.upsert(a1)
await agent_repo.upsert(a2)
# a1 心跳失联→离线a2 手动禁用→不可用
await agent_repo.update_status("a1", AgentStatus.OFFLINE)
await agent_repo.set_unavailable("a2")
assert (await agent_repo.get("a1")).status == AgentStatus.OFFLINE
assert (await agent_repo.get("a2")).status == AgentStatus.UNAVAILABLE
# 发心跳:离线的 a1 恢复 ready不可用的 a2 保持 unavailable
await agent_repo.beat("a1", 0)
await agent_repo.beat("a2", 0)
assert (await agent_repo.get("a1")).status == AgentStatus.READY
assert (await agent_repo.get("a2")).status == AgentStatus.UNAVAILABLE
@pytest.mark.asyncio
async def test_mark_idle_keeps_unavailable(agent_repo):
"""任务结束后 mark_idleunavailable 的 agent 保持不可用,其余置回 ready。"""
a1 = AgentInfo(agent_id="a1", endpoint="e1", agent_tags=["compile"], max_concurrent=2)
a2 = AgentInfo(agent_id="a2", endpoint="e2", agent_tags=["compile"], max_concurrent=2)
await agent_repo.upsert(a1)
await agent_repo.upsert(a2)
await agent_repo.set_unavailable("a1")
await agent_repo.update_status("a2", AgentStatus.PROCESSING)
await agent_repo.mark_idle("a1")
await agent_repo.mark_idle("a2")
assert (await agent_repo.get("a1")).status == AgentStatus.UNAVAILABLE
assert (await agent_repo.get("a2")).status == AgentStatus.READY
@pytest.mark.asyncio
async def test_heartbeat_recovers_stale_offline(agent_repo):
"""超时离线(非手动)的 agent 在发心跳后恢复为 ready。"""
a1 = AgentInfo(agent_id="a1", endpoint="e1", agent_tags=["compile"], max_concurrent=2)
await agent_repo.upsert(a1)
await agent_repo.update_status("a1", AgentStatus.OFFLINE)
assert (await agent_repo.get("a1")).status == AgentStatus.OFFLINE
await agent_repo.beat("a1", 0)
assert (await agent_repo.get("a1")).status == AgentStatus.READY
@pytest.mark.asyncio
async def test_heartbeat_keeps_busy_states(agent_repo):
"""processing/stopping 状态不因心跳改变。"""
for st in (AgentStatus.PROCESSING, AgentStatus.STOPPING):
a = AgentInfo(agent_id=f"a-{st.value}", endpoint="e1", agent_tags=["compile"], max_concurrent=2)
await agent_repo.upsert(a)
await agent_repo.update_status(a.agent_id, st)
await agent_repo.beat(a.agent_id, 1)
assert (await agent_repo.get(a.agent_id)).status == st
@pytest.mark.asyncio
async def test_remove(agent_repo):
a1 = AgentInfo(agent_id="a1", endpoint="e1", agent_tags=["compile"])
await agent_repo.upsert(a1)
await agent_repo.remove("a1")
assert await agent_repo.get("a1") is None
assert await agent_repo.by_tags(["compile"]) == []