Files
wehub-resource-sync b4fbd6fe9f
Deploy Site / deploy-vercel (push) Has been skipped
Deploy Site / deploy-docs (push) Has been skipped
Build Skills Index / build-index (push) Has been skipped
CI / Deny unrelated histories (push) Has been skipped
CI / Detect affected areas (push) Successful in 27m35s
CI / OSV scan (push) Failing after 4s
CI / Build&Test Docker image (push) Successful in 9s
CI / Supply-chain scan (push) Has been skipped
CI / Lint Docker scripts (push) Failing after 5m13s
CI / Check contributors (push) Failing after 12m8s
CI / Docs Site (push) Failing after 12m8s
CI / TypeScript (push) Failing after 12m8s
CI / Python lints (push) Failing after 12m9s
CI / Python tests (push) Failing after 12m9s
CI / Check uv.lock (push) Failing after 23m22s
CI / CI timing report (push) Has been cancelled
Build Skills Index / trigger-deploy (push) Has been cancelled
CI / All required checks pass (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 11:56:03 +08:00

267 lines
7.7 KiB
Python

import json
import sys
from hermes_cli.session_export import export_record_count, render_sessions_export
from hermes_cli.session_export_html import (
_generate_messages_html,
generate_multi_session_html_export,
)
def _sample_session():
return {
"id": "sess-123",
"source": "cli",
"model": "test/model",
"title": "Debug auth flow",
"started_at": 1700000000,
"message_count": 5,
"messages": [
{
"id": 1,
"role": "system",
"content": "hidden system context",
"timestamp": 1700000000,
},
{
"id": 2,
"role": "user",
"content": "Why is login broken?",
"timestamp": 1700000001,
"platform_message_id": "evt-2",
},
{
"id": 3,
"role": "assistant",
"content": "I will inspect the auth middleware.",
"timestamp": 1700000002,
},
{
"id": 4,
"role": "tool",
"tool_name": "read_file",
"content": "def redirect_after_login(): pass",
"timestamp": 1700000003,
},
{
"id": 5,
"role": "user",
"content": [{"type": "text", "text": "Only show me the prompts."}],
"timestamp": 1700000004,
},
],
}
def test_default_jsonl_preserves_full_session_shape():
session = _sample_session()
rendered = render_sessions_export([session])
assert [json.loads(line) for line in rendered.splitlines()] == [session]
def test_prompt_only_jsonl_emits_one_record_per_user_prompt():
rendered = render_sessions_export(
[_sample_session()],
only="user-prompts",
)
records = [json.loads(line) for line in rendered.splitlines()]
assert records == [
{
"session_id": "sess-123",
"index": 1,
"created_at": "2023-11-14T22:13:21Z",
"role": "user",
"text": "Why is login broken?",
"message_id": 2,
"event_id": "evt-2",
},
{
"session_id": "sess-123",
"index": 2,
"created_at": "2023-11-14T22:13:24Z",
"role": "user",
"text": "Only show me the prompts.",
"message_id": 5,
},
]
def test_prompt_only_markdown_excludes_assistant_tool_and_system_content():
rendered = render_sessions_export(
[_sample_session()],
fmt="markdown",
only="user-prompts",
)
assert "# User prompts for session sess-123" in rendered
assert "## 1. 2023-11-14T22:13:21Z" in rendered
assert "Why is login broken?" in rendered
assert "Only show me the prompts." in rendered
assert "I will inspect the auth middleware." not in rendered
assert "def redirect_after_login" not in rendered
assert "hidden system context" not in rendered
def test_full_markdown_renderer_collapses_tool_output_and_filters_system():
rendered = render_sessions_export([_sample_session()], fmt="markdown")
assert "# Session: Debug auth flow" in rendered
assert "## User - 2023-11-14T22:13:21Z" in rendered
assert "## Assistant - 2023-11-14T22:13:22Z" in rendered
assert "<details><summary>read_file</summary>" in rendered
assert "```text\ndef redirect_after_login(): pass\n```" in rendered
assert "hidden system context" not in rendered
def test_html_export_escapes_tool_call_names():
payload = '<img src=x onerror="alert(document.domain)">'
rendered = _generate_messages_html(
[
{
"role": "assistant",
"content": "",
"tool_calls": [
{
"id": "call_1",
"type": "function",
"function": {"name": payload, "arguments": "<b>x</b>"},
}
],
}
]
)
assert payload not in rendered
assert '&lt;img src=x onerror=&quot;alert(document.domain)&quot;&gt;' in rendered
assert "&lt;b&gt;x&lt;/b&gt;" in rendered
def test_html_export_uses_csp_without_inline_event_handlers():
first = _sample_session()
second = {**_sample_session(), "id": "sess-456", "title": "Second session"}
rendered = generate_multi_session_html_export([first, second])
assert "Content-Security-Policy" in rendered
assert "script-src 'nonce-" in rendered
assert "<script nonce=" in rendered
assert "onclick=" not in rendered
def test_export_record_count_switches_unit_for_prompt_only_exports():
assert export_record_count([_sample_session()]) == (1, "session")
assert export_record_count([_sample_session()], only="user-prompts") == (
2,
"prompt",
)
def test_sessions_export_cli_prompt_only_stdout(monkeypatch, capsys):
import hermes_cli.main as main_mod
import hermes_state
captured = {}
class FakeDB:
def resolve_session_id(self, session_id):
captured["resolved_from"] = session_id
return "sess-123"
def export_session(self, session_id):
captured["exported"] = session_id
return _sample_session()
def close(self):
captured["closed"] = True
monkeypatch.setattr(hermes_state, "SessionDB", lambda: FakeDB())
monkeypatch.setattr(
sys,
"argv",
["hermes", "sessions", "export", "-", "--session-id", "sess", "--only", "user-prompts"],
)
main_mod.main()
output = capsys.readouterr().out
records = [json.loads(line) for line in output.splitlines()]
assert [record["text"] for record in records] == [
"Why is login broken?",
"Only show me the prompts.",
]
assert captured == {
"resolved_from": "sess",
"exported": "sess-123",
"closed": True,
}
def test_sessions_export_cli_prompt_only_markdown_file(monkeypatch, capsys, tmp_path):
import hermes_cli.main as main_mod
import hermes_state
class FakeDB:
def resolve_session_id(self, _session_id):
return "sess-123"
def export_session(self, _session_id):
return _sample_session()
def close(self):
pass
output_path = tmp_path / "prompts.md"
monkeypatch.setattr(hermes_state, "SessionDB", lambda: FakeDB())
monkeypatch.setattr(
sys,
"argv",
[
"hermes",
"sessions",
"export",
str(output_path),
"--session-id",
"sess",
"--format",
"md",
"--only",
"user-prompts",
],
)
main_mod.main()
assert f"Exported 2 prompts to {output_path}" in capsys.readouterr().out
content = output_path.read_text(encoding="utf-8")
assert "# User prompts for session sess-123" in content
assert "Why is login broken?" in content
assert "I will inspect the auth middleware." not in content
def test_sessions_export_only_rejects_unsupported_format(monkeypatch, capsys):
import hermes_cli.main as main_mod
import hermes_state
class FakeDB:
def export_all(self, source=None):
raise AssertionError("should refuse before touching the DB")
def close(self):
pass
monkeypatch.setattr(hermes_state, "SessionDB", lambda: FakeDB())
monkeypatch.setattr(
sys,
"argv",
["hermes", "sessions", "export", "-", "--format", "html", "--only", "user-prompts"],
)
main_mod.main()
assert "--only user-prompts supports --format jsonl or md." in capsys.readouterr().out