feat: expand module workflows, dynamic config, notifications and console API
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
|
||||
from gyxx_flow.adapters import feishu_messages
|
||||
|
||||
|
||||
def _completed(payload: dict, *, returncode: int = 0) -> SimpleNamespace:
|
||||
return SimpleNamespace(
|
||||
returncode=returncode,
|
||||
stdout=json.dumps(payload),
|
||||
stderr="",
|
||||
)
|
||||
|
||||
|
||||
def test_bot_text_send_has_one_fixed_profile_identity_and_stable_key(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
commands: list[list[str]] = []
|
||||
monkeypatch.setattr(feishu_messages, "_lark_command", lambda: ["node", "run.js"])
|
||||
monkeypatch.setattr(
|
||||
feishu_messages,
|
||||
"resolve_notification_recipients",
|
||||
lambda recipients, *, app_profile: tuple(recipients),
|
||||
)
|
||||
|
||||
def fake_run(command: list[str], **_kwargs: object) -> SimpleNamespace:
|
||||
commands.append(command)
|
||||
return _completed({"message_id": "om_receipt123", "chat_id": "oc_chat123"})
|
||||
|
||||
monkeypatch.setattr(feishu_messages.subprocess, "run", fake_run)
|
||||
|
||||
first = feishu_messages.send_lark_bot_message(
|
||||
user_id="ou_recipient123",
|
||||
text="告警正文",
|
||||
idempotency_key="product.alert:2026-08-10",
|
||||
)
|
||||
second = feishu_messages.send_lark_bot_message(
|
||||
user_id="ou_recipient123",
|
||||
text="告警正文",
|
||||
idempotency_key="product.alert:2026-08-10",
|
||||
)
|
||||
|
||||
assert first["message_id"] == second["message_id"] == "om_receipt123"
|
||||
assert len(commands) == 2
|
||||
assert commands[0][:4] == ["node", "run.js", "--profile", "hermes-analyzer"]
|
||||
assert commands[0][commands[0].index("--as") + 1] == "bot"
|
||||
assert commands[0][commands[0].index("--user-id") + 1] == "ou_recipient123"
|
||||
assert commands[0][commands[0].index("--text") + 1] == "告警正文"
|
||||
key_index = commands[0].index("--idempotency-key") + 1
|
||||
assert commands[0][key_index] == commands[1][key_index]
|
||||
assert commands[0][key_index].startswith("gyxx-")
|
||||
assert len(commands[0][key_index]) == 45
|
||||
|
||||
|
||||
def test_interactive_bot_send_preserves_card_json_and_nested_receipt(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
observed: dict[str, object] = {}
|
||||
monkeypatch.setattr(feishu_messages, "_lark_command", lambda: ["node", "run.js"])
|
||||
monkeypatch.setattr(
|
||||
feishu_messages,
|
||||
"resolve_notification_recipients",
|
||||
lambda recipients, *, app_profile: tuple(recipients),
|
||||
)
|
||||
|
||||
def fake_run(command: list[str], **kwargs: object) -> SimpleNamespace:
|
||||
observed["command"] = command
|
||||
observed["kwargs"] = kwargs
|
||||
return _completed({"data": {"message_id": "om_nested-123"}})
|
||||
|
||||
monkeypatch.setattr(feishu_messages.subprocess, "run", fake_run)
|
||||
card = {"header": {"title": {"tag": "plain_text", "content": "日报"}}}
|
||||
|
||||
result = feishu_messages.send_lark_bot_message(
|
||||
user_id="ou_recipient123",
|
||||
content=card,
|
||||
msg_type="interactive",
|
||||
)
|
||||
|
||||
command = observed["command"]
|
||||
assert isinstance(command, list)
|
||||
assert json.loads(command[command.index("--content") + 1]) == card
|
||||
assert command[command.index("--msg-type") + 1] == "interactive"
|
||||
assert command[command.index("--as") + 1] == "bot"
|
||||
assert result["message_id"] == "om_nested-123"
|
||||
|
||||
|
||||
def test_chat_and_image_messages_use_bot_identity(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
commands: list[list[str]] = []
|
||||
image = tmp_path / "qr.png"
|
||||
image.write_bytes(b"png")
|
||||
monkeypatch.setattr(feishu_messages, "_lark_command", lambda: ["node", "run.js"])
|
||||
|
||||
def fake_run(command: list[str], **_kwargs: object) -> SimpleNamespace:
|
||||
commands.append(command)
|
||||
return _completed({"message_id": f"om_image{len(commands)}"})
|
||||
|
||||
monkeypatch.setattr(feishu_messages.subprocess, "run", fake_run)
|
||||
|
||||
feishu_messages.send_lark_bot_message(chat_id="oc_group123", text="群通知")
|
||||
feishu_messages.send_lark_bot_message(user_id="ou_owner123", image=image)
|
||||
|
||||
assert commands[0][commands[0].index("--chat-id") + 1] == "oc_group123"
|
||||
assert commands[1][commands[1].index("--image") + 1] == "./qr.png"
|
||||
assert all(command[command.index("--as") + 1] == "bot" for command in commands)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("completed", "message"),
|
||||
(
|
||||
(_completed({}, returncode=1), "exit=1"),
|
||||
(_completed({"ok": False, "error": "denied"}), "denied"),
|
||||
(_completed({"id": "chatcmpl-not-a-receipt"}), "no valid message_id"),
|
||||
),
|
||||
)
|
||||
def test_bot_send_fails_without_a_verified_lark_receipt(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
completed: SimpleNamespace,
|
||||
message: str,
|
||||
) -> None:
|
||||
monkeypatch.setattr(feishu_messages, "_lark_command", lambda: ["node", "run.js"])
|
||||
monkeypatch.setattr(
|
||||
feishu_messages,
|
||||
"resolve_notification_recipients",
|
||||
lambda recipients, *, app_profile: tuple(recipients),
|
||||
)
|
||||
monkeypatch.setattr(feishu_messages.subprocess, "run", lambda *_args, **_kwargs: completed)
|
||||
|
||||
with pytest.raises(RuntimeError, match=message):
|
||||
feishu_messages.send_lark_bot_message(
|
||||
user_id="ou_recipient123",
|
||||
text="告警正文",
|
||||
)
|
||||
|
||||
|
||||
def test_sender_accepts_collector_bot_and_rejects_other_profiles(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
monkeypatch.setattr(feishu_messages, "_lark_command", lambda: ["node", "run.js"])
|
||||
monkeypatch.setattr(
|
||||
feishu_messages.subprocess,
|
||||
"run",
|
||||
lambda *_args, **_kwargs: _completed({"message_id": "om_collector123"}),
|
||||
)
|
||||
result = feishu_messages.send_lark_bot_message(
|
||||
user_id="ou_recipient123",
|
||||
text="采集心跳",
|
||||
profile=feishu_messages.COLLECTOR_NOTIFICATION_APP_PROFILE,
|
||||
)
|
||||
assert result["message_id"] == "om_collector123"
|
||||
|
||||
with pytest.raises(ValueError, match="Hermes application profile"):
|
||||
feishu_messages.send_lark_bot_message(
|
||||
user_id="ou_recipient123",
|
||||
text="告警正文",
|
||||
profile="collector",
|
||||
)
|
||||
Reference in New Issue
Block a user