Files
wehub-resource-sync 6ede33ccdb
Build and Push Docker Images / create_manifest (web, surfsense-web, , cpu) (push) Has been cancelled
Build and Push Docker Images / finalize_release (push) Has been cancelled
Obsidian Plugin Lint / lint (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_web, cpu, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-24.04-arm, linux/arm64, arm64, , runner, false, cpu) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_web, cpu, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-latest, linux/amd64, amd64, , runner, false, cpu) (push) Has been cancelled
Build and Push Docker Images / compute_version (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cpu, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64, , production, false, cpu) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cpu, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64, , production, false, cpu) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu126, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64, -cuda126, production, true, cuda126) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu126, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64, -cuda126, production, true, cuda126) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu128, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64, -cuda, production, true, cuda) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu128, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64, -cuda, production, true, cuda) (push) Has been cancelled
Build and Push Docker Images / verify_digests (push) Has been cancelled
Build and Push Docker Images / create_manifest (backend, surfsense-backend, , cpu) (push) Has been cancelled
Build and Push Docker Images / create_manifest (backend, surfsense-backend, -cuda, cuda) (push) Has been cancelled
Build and Push Docker Images / create_manifest (backend, surfsense-backend, -cuda126, cuda126) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:33:44 +08:00

90 lines
3.0 KiB
Python

"""Pin interrupt-payload normalisation and the optional correlation fields on the wire."""
from __future__ import annotations
import json
import pytest
from app.services.streaming.events.interrupt import (
format_interrupt_request,
normalize_interrupt_payload,
)
pytestmark = pytest.mark.unit
def _decode(frame: str) -> dict:
body = frame.removeprefix("data: ").removesuffix("\n\n")
return json.loads(body)
def test_hitlrequest_shape_is_passed_through_unchanged() -> None:
raw = {
"action_requests": [{"name": "send_email", "args": {"to": "a@b"}}],
"review_configs": [
{"action_name": "send_email", "allowed_decisions": ["approve"]}
],
}
assert normalize_interrupt_payload(raw) == raw
def test_custom_interrupt_primitive_is_converted_to_canonical_shape() -> None:
raw = {
"type": "permission",
"message": "Allow send?",
"action": {"tool": "send_email", "params": {"to": "a@b"}},
"context": {"reason": "destructive"},
}
out = normalize_interrupt_payload(raw)
assert out["action_requests"] == [{"name": "send_email", "args": {"to": "a@b"}}]
assert out["review_configs"] == [
{
"action_name": "send_email",
"allowed_decisions": ["approve", "edit", "reject"],
}
]
assert out["interrupt_type"] == "permission"
assert out["message"] == "Allow send?"
assert out["context"] == {"reason": "destructive"}
def test_custom_interrupt_without_message_omits_message_key() -> None:
"""Optional fields stay optional on the wire; FE does not see ``"message": None``."""
raw = {"action": {"tool": "send_email"}}
out = normalize_interrupt_payload(raw)
assert "message" not in out
def test_custom_interrupt_without_tool_falls_back_to_unknown_tool() -> None:
"""Defensive: a malformed ``action`` block must not crash the relay."""
out = normalize_interrupt_payload({"type": "x", "action": {}})
assert out["action_requests"][0]["name"] == "unknown_tool"
assert out["review_configs"][0]["action_name"] == "unknown_tool"
def test_format_interrupt_request_carries_correlation_fields_on_the_wire() -> None:
frame = format_interrupt_request(
{"action_requests": [], "review_configs": []},
interrupt_id="int_42",
pending_interrupt_count=3,
chat_turn_id="turn_99",
)
payload = _decode(frame)
assert payload["type"] == "data-interrupt-request"
inner = payload["data"]
assert inner["interrupt_id"] == "int_42"
assert inner["pending_interrupt_count"] == 3
assert inner["chat_turn_id"] == "turn_99"
def test_format_interrupt_request_omits_correlation_fields_when_unset() -> None:
"""Backward compat: legacy single-interrupt callers don't have to supply ids."""
frame = format_interrupt_request(
{"action_requests": [], "review_configs": []},
)
inner = _decode(frame)["data"]
assert "interrupt_id" not in inner
assert "pending_interrupt_count" not in inner
assert "chat_turn_id" not in inner