mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-29 18:46:59 +00:00
test(photon): replace source-grep URL-routing tests with behavior tests
Follow-up to the URL markdown fix: extract the /send builder decision into sidecar/send-format.mjs and rewrite test_url_send_path.py to execute the real module under node (format+text in -> chosen builder out) instead of regex- grepping index.mjs source, which is a banned test pattern in this repo.
This commit is contained in:
parent
709dd3282f
commit
87fe75fde4
3 changed files with 105 additions and 40 deletions
|
|
@ -66,6 +66,7 @@ import http from "node:http";
|
|||
import crypto from "node:crypto";
|
||||
import { once } from "node:events";
|
||||
import { patchSpectrumTs } from "./patch-spectrum-mixed-attachments.mjs";
|
||||
import { chooseSendFormat } from "./send-format.mjs";
|
||||
|
||||
const projectId = process.env.PHOTON_PROJECT_ID;
|
||||
const projectSecret = process.env.PHOTON_PROJECT_SECRET;
|
||||
|
|
@ -894,10 +895,10 @@ const server = http.createServer(async (req, res) => {
|
|||
// iMessage API, which can 500 on messages containing raw URLs.
|
||||
// Plain-text URLs are auto-linked by iMessage, so route markdown
|
||||
// messages that contain URLs through spectrumText while preserving
|
||||
// spectrumMarkdown for URL-free markdown.
|
||||
const hasUrl = /https?:\/\/[^\s)'"<>]+/i.test(text);
|
||||
// spectrumMarkdown for URL-free markdown. The decision lives in
|
||||
// send-format.mjs so tests can exercise it directly.
|
||||
const builder =
|
||||
format === "markdown" && !hasUrl
|
||||
chooseSendFormat(format, text) === "markdown"
|
||||
? spectrumMarkdown(text)
|
||||
: spectrumText(text);
|
||||
const result = await space.send(builder);
|
||||
|
|
|
|||
27
plugins/platforms/photon/sidecar/send-format.mjs
Normal file
27
plugins/platforms/photon/sidecar/send-format.mjs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
// Outbound /send builder selection for the Photon sidecar.
|
||||
//
|
||||
// spectrumMarkdown() enables data detection (enableDataDetection) in the
|
||||
// underlying iMessage API, which can 500 on messages containing raw URLs.
|
||||
// Plain-text URLs are auto-linked by iMessage anyway, so markdown messages
|
||||
// that contain a URL are routed through the text builder, while URL-free
|
||||
// markdown keeps native markdown rendering.
|
||||
//
|
||||
// This lives in its own module (rather than inline in index.mjs) so tests can
|
||||
// execute the real decision logic under node instead of grepping source —
|
||||
// see tests/plugins/platforms/photon/test_url_send_path.py.
|
||||
|
||||
const URL_RE = /https?:\/\/[^\s)'"<>]+/i;
|
||||
|
||||
/**
|
||||
* Decide which spectrum-ts builder the /send handler should use.
|
||||
*
|
||||
* @param {string} format "markdown" | "text" (already validated by /send)
|
||||
* @param {string} text the outbound message body
|
||||
* @returns {"markdown"|"text"}
|
||||
*/
|
||||
export function chooseSendFormat(format, text) {
|
||||
if (format === "markdown" && !URL_RE.test(String(text))) {
|
||||
return "markdown";
|
||||
}
|
||||
return "text";
|
||||
}
|
||||
|
|
@ -1,50 +1,87 @@
|
|||
"""Regression tests for Photon raw-URL message delivery.
|
||||
"""Behavior tests for Photon raw-URL outbound routing (issue: markdown 500s).
|
||||
|
||||
The iMessage markdown builder enables data detection inside spectrum-ts. On
|
||||
some IMAgentKit sends, that path returns a 500 when the message contains a raw
|
||||
URL. The sidecar should keep markdown rendering for URL-free messages, but use
|
||||
URL. The sidecar keeps markdown rendering for URL-free messages, but must use
|
||||
plain text for messages containing URLs so iMessage can auto-link them without
|
||||
hitting the data-detection failure path.
|
||||
"""
|
||||
|
||||
The routing decision lives in
|
||||
``plugins/platforms/photon/sidecar/send-format.mjs`` (imported by index.mjs's
|
||||
``/send`` handler). These tests *execute* that real module under node and
|
||||
assert the chosen builder for representative payloads — they do not read the
|
||||
sidecar source.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import json
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from typing import Dict, Tuple
|
||||
|
||||
import pytest
|
||||
|
||||
_MODULE = Path("plugins/platforms/photon/sidecar/send-format.mjs").resolve()
|
||||
|
||||
_CASES: Dict[str, Tuple[str, str, str]] = {
|
||||
# name: (format, text, expected builder)
|
||||
"markdown_without_url_keeps_markdown": (
|
||||
"markdown", "**bold** and `code`", "markdown",
|
||||
),
|
||||
"markdown_with_https_url_falls_back_to_text": (
|
||||
"markdown", "see **this**: https://example.com/a?b=1", "text",
|
||||
),
|
||||
"markdown_with_http_url_falls_back_to_text": (
|
||||
"markdown", "http://example.com", "text",
|
||||
),
|
||||
"markdown_link_syntax_also_falls_back_to_text": (
|
||||
"markdown", "[docs](https://example.com/docs)", "text",
|
||||
),
|
||||
"markdown_with_uppercase_scheme_falls_back_to_text": (
|
||||
"markdown", "HTTPS://EXAMPLE.COM is loud", "text",
|
||||
),
|
||||
"markdown_bare_domain_without_scheme_keeps_markdown": (
|
||||
# Only scheme'd URLs trip iMessage's data-detection 500; a bare domain
|
||||
# is ordinary text and must keep markdown rendering.
|
||||
"markdown", "ask example.com about *this*", "markdown",
|
||||
),
|
||||
"text_format_stays_text": ("text", "plain message", "text"),
|
||||
"text_format_with_url_stays_text": (
|
||||
"text", "https://example.com", "text",
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
SIDECAR = Path("plugins/platforms/photon/sidecar/index.mjs")
|
||||
|
||||
|
||||
def _source() -> str:
|
||||
return SIDECAR.read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def test_photon_sidecar_detects_raw_urls_before_builder_selection() -> None:
|
||||
src = _source()
|
||||
|
||||
assert "hasUrl" in src
|
||||
assert "https?:\\/\\/" in src
|
||||
assert re.search(r"const\s+hasUrl\s*=\s*/https\?:", src)
|
||||
|
||||
|
||||
def test_markdown_builder_is_used_only_for_url_free_markdown() -> None:
|
||||
src = _source()
|
||||
|
||||
assert re.search(
|
||||
r'format\s*===\s*["\']markdown["\']\s*&&\s*!hasUrl',
|
||||
src,
|
||||
@pytest.fixture(scope="module")
|
||||
def verdicts() -> Dict[str, str]:
|
||||
"""Run every case through the real send-format module in one node call."""
|
||||
harness = (
|
||||
f"import {{ chooseSendFormat }} from {json.dumps(_MODULE.as_uri())};\n"
|
||||
"const chunks = [];\n"
|
||||
"process.stdin.on('data', (c) => chunks.push(c));\n"
|
||||
"process.stdin.on('end', () => {\n"
|
||||
" const cases = JSON.parse(Buffer.concat(chunks).toString('utf-8'));\n"
|
||||
" const out = {};\n"
|
||||
" for (const [name, [format, text]] of Object.entries(cases)) {\n"
|
||||
" out[name] = chooseSendFormat(format, text);\n"
|
||||
" }\n"
|
||||
" process.stdout.write(JSON.stringify(out));\n"
|
||||
"});\n"
|
||||
)
|
||||
assert "spectrumMarkdown(text)" in src
|
||||
|
||||
|
||||
def test_text_builder_is_fallback_for_markdown_messages_with_urls() -> None:
|
||||
src = _source()
|
||||
|
||||
# Builder selection must still have spectrumText as the fallback branch so
|
||||
# markdown+URL messages avoid spectrumMarkdown's data-detection path.
|
||||
assert "spectrumText(text)" in src
|
||||
assert re.search(
|
||||
r'format\s*===\s*["\']markdown["\']\s*&&\s*!hasUrl[\s\S]*\?[\s\S]*spectrumMarkdown\(text\)[\s\S]*:[\s\S]*spectrumText\(text\)',
|
||||
src,
|
||||
payload = {name: [fmt, text] for name, (fmt, text, _) in _CASES.items()}
|
||||
run = subprocess.run(
|
||||
["node", "--input-type=module", "-e", harness],
|
||||
input=json.dumps(payload),
|
||||
cwd=Path.cwd(),
|
||||
text=True,
|
||||
capture_output=True,
|
||||
check=False,
|
||||
)
|
||||
assert run.returncode == 0, run.stderr
|
||||
return json.loads(run.stdout)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name", sorted(_CASES))
|
||||
def test_send_builder_selection(name: str, verdicts: Dict[str, str]) -> None:
|
||||
_, _, expected = _CASES[name]
|
||||
assert verdicts[name] == expected
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue