from __future__ import annotations from typing import Any import pytest from yuxi.channels.pipeline.base import BaseInboundPipeline from yuxi.channels.pipeline.context import PipelineContext class _TestPipeline(BaseInboundPipeline): def __init__(self, stages=None): super().__init__(adapter=None) self._custom_stages = stages or [] async def _build_stages(self): return self._custom_stages class TestPipelineContext: def test_default_values(self): ctx = PipelineContext() assert ctx.event_type == "" assert ctx.msg_id == "" assert ctx.stopped is False def test_stop_with_default_reason(self): ctx = PipelineContext() ctx.stop() assert ctx.stopped is True assert ctx._stop_reason == "pipeline_stop" def test_stop_with_custom_reason(self): ctx = PipelineContext() ctx.stop("access_denied") assert ctx.stopped is True assert ctx._stop_reason == "access_denied" def test_debug_summary(self): ctx = PipelineContext(event_type="C2C_MESSAGE_CREATE", sender_id="user-1", msg_id="msg-1") summary = ctx.debug_summary() assert "C2C_MESSAGE_CREATE" in summary assert "user-1" in summary assert "msg-1" in summary assert "stop=None" in summary def test_debug_summary_stopped(self): ctx = PipelineContext(event_type="C2C_MESSAGE_CREATE") ctx.stop("access_denied") summary = ctx.debug_summary() assert "stop=access_denied" in summary def test_metadata_persists(self): ctx = PipelineContext(metadata={"key": "value"}) assert ctx.metadata["key"] == "value" class TestBaseInboundPipeline: @pytest.mark.asyncio async def test_empty_pipeline_returns_context(self): pipeline = _TestPipeline(stages=[]) await pipeline.initialize() result = await pipeline.process("test_event", {"data": "value"}) assert result is not None assert result.event_type == "test_event" @pytest.mark.asyncio async def test_single_stage_success(self): async def stage_pass(pipe, ctx): ctx.sender_id = "user-1" return ctx pipeline = _TestPipeline(stages=[stage_pass]) await pipeline.initialize() result = await pipeline.process("test_event", {}) assert result is not None assert result.sender_id == "user-1" @pytest.mark.asyncio async def test_stage_returns_none_short_circuits(self): async def stage_reject(pipe, ctx): return None pipeline = _TestPipeline(stages=[stage_reject]) await pipeline.initialize() result = await pipeline.process("test_event", {}) assert result is None @pytest.mark.asyncio async def test_stage_exception_short_circuits(self): async def stage_error(pipe, ctx): raise RuntimeError("stage failure") pipeline = _TestPipeline(stages=[stage_error]) await pipeline.initialize() result = await pipeline.process("test_event", {}) assert result is None @pytest.mark.asyncio async def test_ctx_stop_short_circuits(self): async def stage_with_stop(pipe, ctx): ctx.stop("test_stop") return ctx pipeline = _TestPipeline(stages=[stage_with_stop]) await pipeline.initialize() result = await pipeline.process("test_event", {}) assert result is None @pytest.mark.asyncio async def test_multiple_stages_execute_in_order(self): order: list[str] = [] async def stage_1(pipe, ctx): order.append("stage_1") return ctx async def stage_2(pipe, ctx): order.append("stage_2") return ctx async def stage_3(pipe, ctx): order.append("stage_3") return ctx pipeline = _TestPipeline(stages=[stage_1, stage_2, stage_3]) await pipeline.initialize() await pipeline.process("test_event", {}) assert order == ["stage_1", "stage_2", "stage_3"] @pytest.mark.asyncio async def test_stage_short_circuit_stops_remaining(self): order: list[str] = [] async def stage_1(pipe, ctx): order.append("stage_1") return ctx async def stage_2(pipe, ctx): order.append("stage_2") return None async def stage_3(pipe, ctx): order.append("stage_3") return ctx pipeline = _TestPipeline(stages=[stage_1, stage_2, stage_3]) await pipeline.initialize() result = await pipeline.process("test_event", {}) assert result is None assert order == ["stage_1", "stage_2"] @pytest.mark.asyncio async def test_stages_share_same_context(self): async def stage_set(pipe, ctx): ctx.sender_id = "user-1" ctx.metadata["first"] = True return ctx async def stage_read(pipe, ctx): assert ctx.sender_id == "user-1" assert ctx.metadata["first"] is True ctx.chat_id = "chat-1" return ctx pipeline = _TestPipeline(stages=[stage_set, stage_read]) await pipeline.initialize() result = await pipeline.process("test_event", {}) assert result is not None assert result.sender_id == "user-1" assert result.chat_id == "chat-1" @pytest.mark.asyncio async def test_adapter_property(self): adapter = object() pipeline = BaseInboundPipeline(adapter) assert pipeline.adapter is adapter class TestPipelineImport: def test_import_from_package(self): from yuxi.channels.pipeline import BaseInboundPipeline, PipelineContext assert BaseInboundPipeline is not None assert PipelineContext is not None