feat(cybersecurity): auto-commit con 48 cambios

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-04 23:44:39 +02:00
parent efc9911925
commit 729921e16e
48 changed files with 3765 additions and 8 deletions
@@ -0,0 +1,283 @@
"""Tests para las funciones puras de tee_anthropic_sse.
Cubre split_sse_events y event_to_ndjson sin necesitar mitmproxy.
"""
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
from tee_anthropic_sse import split_sse_events, event_to_ndjson
# ---------------------------------------------------------------------------
# SSE fixture — captura real de la API de Anthropic
# ---------------------------------------------------------------------------
_RAW_SSE = (
b"event: message_start\n"
b'data: {"type":"message_start","message":{"model":"claude-opus-4-8","id":"msg_x",'
b'"type":"message","role":"assistant","content":[],"stop_reason":null}}\n'
b"\n"
b"event: content_block_start\n"
b'data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}\n'
b"\n"
b"event: ping\n"
b'data: {"type": "ping"}\n'
b"\n"
b"event: content_block_delta\n"
b'data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"P"}}\n'
b"\n"
b"event: content_block_delta\n"
b'data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"ONG"}}\n'
b"\n"
b"event: content_block_stop\n"
b'data: {"type":"content_block_stop","index":0}\n'
b"\n"
b"event: message_delta\n"
b'data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},'
b'"usage":{"output_tokens":5}}\n'
b"\n"
b"event: message_stop\n"
b'data: {"type":"message_stop"}\n'
b"\n"
)
# ---------------------------------------------------------------------------
# split_sse_events
# ---------------------------------------------------------------------------
def test_split_buffer_completo_devuelve_8_bloques():
"""Con el buffer completo devuelve los 8 bloques y leftover vacio."""
events, leftover = split_sse_events(_RAW_SSE)
assert len(events) == 8
assert leftover == b""
def test_split_bloques_contienen_event_y_data():
"""Cada bloque contiene las lineas event: y data: esperadas."""
events, _ = split_sse_events(_RAW_SSE)
assert "event: message_start" in events[0]
assert "event: ping" in events[2]
assert "event: message_stop" in events[7]
def test_split_buffer_cortado_preserva_incompleto():
"""Con un buffer cortado a la mitad de un evento, devuelve solo los completos."""
# Encontrar la SEGUNDA aparicion de content_block_delta (quinto evento en total)
first_occ = _RAW_SSE.find(b"event: content_block_delta\ndata:")
second_occ = _RAW_SSE.find(b"event: content_block_delta\ndata:", first_occ + 1)
# Cortar en medio del data: del segundo content_block_delta
cut_buf = _RAW_SSE[:second_occ + 20]
events, leftover = split_sse_events(cut_buf)
# Debe haber exactamente 4 eventos completos:
# message_start, content_block_start, ping, primer content_block_delta
assert len(events) == 4
# El leftover no debe estar vacio (el segundo delta queda a medias)
assert len(leftover) > 0
def test_split_resto_mas_continuacion_reconstruye_evento():
"""Concatenar leftover + continuacion reconstituye el evento cortado."""
# Cortar justo antes del \n\n que cierra el primer delta
cut_point = _RAW_SSE.find(b"\n\nevent: content_block_delta\n", 100)
first_half = _RAW_SSE[:cut_point + 1] # termina dentro del separador
second_half = _RAW_SSE[cut_point + 1:]
events1, leftover1 = split_sse_events(first_half)
combined = leftover1 + second_half
events2, leftover2 = split_sse_events(combined)
# La union debe cubrir todos los bloques del segundo tramo
all_events = events1 + events2
assert len(all_events) == 8
assert leftover2 == b""
def test_split_buffer_vacio():
"""Buffer vacio devuelve lista vacia y leftover vacio."""
events, leftover = split_sse_events(b"")
assert events == []
assert leftover == b""
def test_split_evento_unico_sin_separador_final():
"""Un evento sin separador final queda como leftover."""
chunk = b"event: ping\ndata: {\"type\":\"ping\"}"
events, leftover = split_sse_events(chunk)
assert events == []
assert b"ping" in leftover
# ---------------------------------------------------------------------------
# event_to_ndjson
# ---------------------------------------------------------------------------
def test_text_delta_p():
"""content_block_delta text_delta 'P' -> [{type:text_delta, stream_id:1, text:'P'}]."""
block = (
"event: content_block_delta\n"
'data: {"type":"content_block_delta","index":0,'
'"delta":{"type":"text_delta","text":"P"}}'
)
result = event_to_ndjson(block, 1, {})
assert result == [{"type": "text_delta", "stream_id": 1, "text": "P"}]
def test_text_delta_ong():
"""content_block_delta text_delta 'ONG' -> text 'ONG'."""
block = (
"event: content_block_delta\n"
'data: {"type":"content_block_delta","index":0,'
'"delta":{"type":"text_delta","text":"ONG"}}'
)
result = event_to_ndjson(block, 1, {})
assert result == [{"type": "text_delta", "stream_id": 1, "text": "ONG"}]
def test_message_stop_con_stop_holder_previo():
"""message_stop con stop_holder ya cargado -> stop_reason end_turn."""
stop_holder: dict = {}
# Primero simular message_delta para poblar el holder
delta_block = (
"event: message_delta\n"
'data: {"type":"message_delta","delta":{"stop_reason":"end_turn",'
'"stop_sequence":null},"usage":{"output_tokens":5}}'
)
event_to_ndjson(delta_block, 1, stop_holder)
assert stop_holder.get("stop_reason") == "end_turn"
# Ahora message_stop
stop_block = (
"event: message_stop\n"
'data: {"type":"message_stop"}'
)
result = event_to_ndjson(stop_block, 1, stop_holder)
assert result == [{"type": "message_stop", "stream_id": 1, "stop_reason": "end_turn"}]
def test_ping_devuelve_lista_vacia():
"""ping -> []."""
block = "event: ping\ndata: {\"type\": \"ping\"}"
result = event_to_ndjson(block, 1, {})
assert result == []
def test_content_block_start_text_devuelve_vacio():
"""content_block_start para un bloque de texto -> []."""
block = (
"event: content_block_start\n"
'data: {"type":"content_block_start","index":0,'
'"content_block":{"type":"text","text":""}}'
)
result = event_to_ndjson(block, 1, {})
assert result == []
def test_content_block_start_tool_use():
"""content_block_start tool_use -> tool_use_start con name e id."""
block = (
"event: content_block_start\n"
'data: {"type":"content_block_start","index":1,'
'"content_block":{"type":"tool_use","id":"toolu_01abc","name":"Bash"}}'
)
result = event_to_ndjson(block, 2, {})
assert result == [
{
"type": "tool_use_start",
"stream_id": 2,
"tool_name": "Bash",
"tool_id": "toolu_01abc",
}
]
def test_tool_json_delta():
"""content_block_delta input_json_delta -> tool_json_delta."""
# Construir el bloque SSE con json.dumps para que el partial_json quede
# correctamente escapado dentro del JSON del campo data:
import json as _json
data_payload = {
"type": "content_block_delta",
"index": 1,
"delta": {
"type": "input_json_delta",
"partial_json": '{"command":"ls',
},
}
block = "event: content_block_delta\ndata: " + _json.dumps(data_payload)
result = event_to_ndjson(block, 3, {})
assert result == [
{
"type": "tool_json_delta",
"stream_id": 3,
"partial_json": '{"command":"ls',
}
]
def test_json_invalido_en_data_devuelve_vacio():
"""Linea data: con JSON invalido -> [] (sin excepcion)."""
block = "event: content_block_delta\ndata: {esto no es json"
result = event_to_ndjson(block, 1, {})
assert result == []
def test_bloque_sin_data_devuelve_vacio():
"""Bloque sin linea data: -> []."""
block = "event: content_block_stop\n"
result = event_to_ndjson(block, 1, {})
assert result == []
# ---------------------------------------------------------------------------
# Integración del parseo: secuencia completa produce PONG + message_stop
# ---------------------------------------------------------------------------
def test_integracion_secuencia_completa_produce_pong_y_stop():
"""Los 8 bloques en orden producen text_delta 'P'+'ONG' y un message_stop end_turn."""
events, leftover = split_sse_events(_RAW_SSE)
assert leftover == b""
stop_holder: dict = {}
all_ndjson: list[dict] = []
for block in events:
all_ndjson.extend(event_to_ndjson(block, 1, stop_holder))
text_deltas = [o for o in all_ndjson if o["type"] == "text_delta"]
message_stops = [o for o in all_ndjson if o["type"] == "message_stop"]
concatenated = "".join(d["text"] for d in text_deltas)
assert concatenated == "PONG"
assert len(message_stops) == 1
assert message_stops[0]["stop_reason"] == "end_turn"
assert message_stops[0]["stream_id"] == 1
def test_integracion_stream_id_se_propaga():
"""stream_id se propaga correctamente a todos los eventos emitidos."""
events, _ = split_sse_events(_RAW_SSE)
stop_holder: dict = {}
for block in events:
for obj in event_to_ndjson(block, 42, stop_holder):
assert obj["stream_id"] == 42
def test_integracion_determinismo():
"""Parsear el mismo buffer dos veces produce exactamente el mismo resultado."""
def parse_all(stream_id: int) -> list[dict]:
evs, _ = split_sse_events(_RAW_SSE)
holder: dict = {}
result: list[dict] = []
for b in evs:
result.extend(event_to_ndjson(b, stream_id, holder))
return result
assert parse_all(1) == parse_all(1)