mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-11 03:31:55 +00:00
feat(teams): add pipeline outbound delivery via existing adapter
This commit is contained in:
parent
a99547740d
commit
397f750bb4
4 changed files with 378 additions and 1 deletions
|
|
@ -1,15 +1,19 @@
|
|||
"""Tests for the Microsoft Teams platform adapter plugin."""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import types
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
from gateway.config import Platform, PlatformConfig, HomeChannel
|
||||
from plugins.teams_pipeline.models import TeamsMeetingRef, TeamsMeetingSummaryPayload
|
||||
from tests.gateway._plugin_adapter_loader import load_plugin_adapter
|
||||
|
||||
|
||||
|
|
@ -177,6 +181,7 @@ if _mt and _teams_mod.TypingActivityInput is None:
|
|||
_teams_mod.TypingActivityInput = _mt.TypingActivityInput
|
||||
|
||||
TeamsAdapter = _teams_mod.TeamsAdapter
|
||||
TeamsSummaryWriter = _teams_mod.TeamsSummaryWriter
|
||||
check_requirements = _teams_mod.check_requirements
|
||||
check_teams_requirements = _teams_mod.check_teams_requirements
|
||||
validate_config = _teams_mod.validate_config
|
||||
|
|
@ -449,6 +454,108 @@ class TestTeamsSend:
|
|||
assert call_args[0][0] == "conv-id"
|
||||
|
||||
|
||||
def _make_summary_payload():
|
||||
return TeamsMeetingSummaryPayload(
|
||||
meeting_ref=TeamsMeetingRef(meeting_id="meeting-123"),
|
||||
title="Weekly Sync",
|
||||
summary="Discussed launch readiness.",
|
||||
key_decisions=["Proceed with staged rollout."],
|
||||
action_items=["Send launch checklist."],
|
||||
risks=["QA sign-off still pending."],
|
||||
)
|
||||
|
||||
|
||||
class TestTeamsSummaryWriter:
|
||||
@pytest.mark.anyio
|
||||
async def test_incoming_webhook_posts_summary_text(self):
|
||||
seen = {}
|
||||
|
||||
def _handler(request: httpx.Request) -> httpx.Response:
|
||||
seen["url"] = str(request.url)
|
||||
seen["body"] = json.loads(request.content.decode("utf-8"))
|
||||
return httpx.Response(200, json={"ok": True})
|
||||
|
||||
writer = TeamsSummaryWriter(transport=httpx.MockTransport(_handler))
|
||||
payload = _make_summary_payload()
|
||||
|
||||
result = await writer.write_summary(
|
||||
payload,
|
||||
{
|
||||
"delivery_mode": "incoming_webhook",
|
||||
"incoming_webhook_url": "https://example.test/teams-webhook",
|
||||
},
|
||||
)
|
||||
|
||||
assert result["delivery_mode"] == "incoming_webhook"
|
||||
assert seen["url"] == "https://example.test/teams-webhook"
|
||||
assert "Weekly Sync" in seen["body"]["text"]
|
||||
assert "Proceed with staged rollout." in seen["body"]["text"]
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_graph_delivery_posts_to_channel(self):
|
||||
graph_client = SimpleNamespace(
|
||||
post_json=AsyncMock(return_value={"id": "msg-123", "webUrl": "https://teams.example/messages/123"})
|
||||
)
|
||||
writer = TeamsSummaryWriter(graph_client=graph_client)
|
||||
payload = _make_summary_payload()
|
||||
|
||||
result = await writer.write_summary(
|
||||
payload,
|
||||
{
|
||||
"delivery_mode": "graph",
|
||||
"team_id": "team-1",
|
||||
"channel_id": "channel-1",
|
||||
},
|
||||
)
|
||||
|
||||
assert result["target_type"] == "channel"
|
||||
assert result["message_id"] == "msg-123"
|
||||
graph_client.post_json.assert_awaited_once()
|
||||
path = graph_client.post_json.await_args.args[0]
|
||||
body = graph_client.post_json.await_args.kwargs["json_body"]
|
||||
assert path == "/teams/team-1/channels/channel-1/messages"
|
||||
assert body["body"]["contentType"] == "html"
|
||||
assert "Weekly Sync" in body["body"]["content"]
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_graph_delivery_falls_back_to_platform_home_channel(self):
|
||||
graph_client = SimpleNamespace(post_json=AsyncMock(return_value={"id": "msg-home"}))
|
||||
platform_config = PlatformConfig(
|
||||
enabled=True,
|
||||
extra={"team_id": "team-home", "delivery_mode": "graph"},
|
||||
home_channel=HomeChannel(
|
||||
platform=Platform("teams"),
|
||||
chat_id="channel-home",
|
||||
name="Teams Home",
|
||||
),
|
||||
)
|
||||
writer = TeamsSummaryWriter(platform_config=platform_config, graph_client=graph_client)
|
||||
|
||||
await writer.write_summary(_make_summary_payload(), {})
|
||||
|
||||
graph_client.post_json.assert_awaited_once()
|
||||
assert graph_client.post_json.await_args.args[0] == "/teams/team-home/channels/channel-home/messages"
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_existing_record_is_reused_without_force_resend(self):
|
||||
graph_client = SimpleNamespace(post_json=AsyncMock())
|
||||
writer = TeamsSummaryWriter(graph_client=graph_client)
|
||||
existing = {"delivery_mode": "graph", "message_id": "msg-existing"}
|
||||
|
||||
result = await writer.write_summary(
|
||||
_make_summary_payload(),
|
||||
{
|
||||
"delivery_mode": "graph",
|
||||
"team_id": "team-1",
|
||||
"channel_id": "channel-1",
|
||||
},
|
||||
existing_record=existing,
|
||||
)
|
||||
|
||||
assert result == existing
|
||||
graph_client.post_json.assert_not_awaited()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tests: Message Handling
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -82,6 +82,34 @@ def test_runtime_config_uses_existing_teams_platform_settings():
|
|||
}
|
||||
|
||||
|
||||
def test_build_pipeline_runtime_reuses_existing_teams_adapter_surface(monkeypatch, tmp_path):
|
||||
from plugins.teams_pipeline import runtime as runtime_module
|
||||
|
||||
class FakeWriter:
|
||||
def __init__(self, platform_config=None, **kwargs) -> None:
|
||||
self.platform_config = platform_config
|
||||
|
||||
monkeypatch.setattr(runtime_module, "build_graph_client", lambda: object())
|
||||
monkeypatch.setattr(runtime_module, "resolve_teams_pipeline_store_path", lambda: tmp_path / "teams-store.json")
|
||||
monkeypatch.setattr("plugins.platforms.teams.adapter.TeamsSummaryWriter", FakeWriter)
|
||||
|
||||
gateway = SimpleNamespace(
|
||||
config=GatewayConfig(
|
||||
platforms={
|
||||
Platform("teams"): PlatformConfig(
|
||||
enabled=True,
|
||||
extra={"delivery_mode": "incoming_webhook"},
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
runtime = runtime_module.build_pipeline_runtime(gateway)
|
||||
|
||||
assert isinstance(runtime.teams_sender, FakeWriter)
|
||||
assert runtime.teams_sender.platform_config is gateway.config.platforms[Platform("teams")]
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_bind_gateway_runtime_attaches_scheduler(monkeypatch, tmp_path):
|
||||
from plugins.teams_pipeline import runtime as runtime_module
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue