Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/agent_mailer/routes/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ def _parse_agent(row) -> dict:
async def admin_list_agents(request: Request, user: dict = Depends(get_current_user)):
db = request.app.state.db
cursor = await db.execute(
"SELECT * FROM agents WHERE user_id = ? ORDER BY created_at", (user["id"],)
"SELECT * FROM agents WHERE user_id = ? AND COALESCE(status, 'active') != 'deleted' ORDER BY created_at",
(user["id"],),
)
rows = await cursor.fetchall()
return [AgentResponse(**_parse_agent(row)) for row in rows]
Expand Down Expand Up @@ -224,7 +225,7 @@ async def agents_stats(request: Request, user: dict = Depends(get_current_user))
SUM(CASE WHEN action = 'forward' THEN 1 ELSE 0 END) AS forward_count
FROM messages GROUP BY from_agent
) sent ON sent.from_agent = a.address
WHERE a.user_id = ?
WHERE a.user_id = ? AND COALESCE(a.status, 'active') != 'deleted'
ORDER BY a.created_at
""", (user["id"],))
rows = await cursor.fetchall()
Expand Down
7 changes: 5 additions & 2 deletions src/agent_mailer/routes/teams.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ async def list_teams(request: Request, user: dict = Depends(get_current_user)):
cursor = await db.execute(
"""SELECT t.*, COUNT(a.id) AS agent_count
FROM teams t
LEFT JOIN agents a ON a.team_id = t.id
LEFT JOIN agents a
ON a.team_id = t.id
AND COALESCE(a.status, 'active') != 'deleted'
WHERE t.user_id = ?
GROUP BY t.id
ORDER BY t.created_at""",
Expand All @@ -80,7 +82,8 @@ async def get_team(team_id: str, request: Request, user: dict = Depends(get_curr
raise HTTPException(status_code=404, detail="Team not found")

cursor = await db.execute(
"SELECT * FROM agents WHERE team_id = ? ORDER BY created_at", (team_id,)
"SELECT * FROM agents WHERE team_id = ? AND COALESCE(status, 'active') != 'deleted' ORDER BY created_at",
(team_id,),
)
agent_rows = await cursor.fetchall()
agents = [AgentResponse(**_parse_agent(r)) for r in agent_rows]
Expand Down
15 changes: 15 additions & 0 deletions tests/test_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,21 @@ async def test_delete_agent_preserves_messages(client, agents):
assert len(thread_resp.json()) == 1


async def test_soft_deleted_agent_hidden_from_admin_agents_and_stats(client, agents):
agent_id = agents["coder"]["id"]

delete_resp = await client.delete(f"/users/me/agents/{agent_id}")
assert delete_resp.status_code == 200

agents_resp = await client.get("/admin/agents")
assert agents_resp.status_code == 200
assert agent_id not in {a["id"] for a in agents_resp.json()}

stats_resp = await client.get("/admin/agents/stats")
assert stats_resp.status_code == 200
assert agent_id not in {a["agent_id"] for a in stats_resp.json()}


# --- Agent Tags ---

async def test_update_agent_tags(client, agents):
Expand Down
23 changes: 23 additions & 0 deletions tests/test_teams.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,29 @@ async def test_get_team_detail(client):
assert data["agents"] == []


async def test_deleted_agent_hidden_from_team_count_and_detail(client):
create_resp = await client.post("/admin/teams", json={"name": "Soft Deleted"})
team_id = create_resp.json()["id"]
agent = await _register_agent(client, "softdel")

add_resp = await client.post(f"/admin/teams/{team_id}/agents", json={"agent_id": agent["id"]})
assert add_resp.status_code == 200

delete_resp = await client.delete(f"/users/me/agents/{agent['id']}")
assert delete_resp.status_code == 200

teams_resp = await client.get("/admin/teams")
assert teams_resp.status_code == 200
team = next(t for t in teams_resp.json() if t["id"] == team_id)
assert team["agent_count"] == 0

detail_resp = await client.get(f"/admin/teams/{team_id}")
assert detail_resp.status_code == 200
detail = detail_resp.json()
assert detail["agent_count"] == 0
assert detail["agents"] == []


async def test_get_team_not_found(client):
resp = await client.get("/admin/teams/nonexistent-id")
assert resp.status_code == 404
Expand Down