From 23245e20efbcf10e62f7b306e16b3c0427c6615a Mon Sep 17 00:00:00 2001 From: Popochounet Date: Fri, 3 Jul 2026 17:37:26 +0200 Subject: [PATCH 1/2] feat(tests): EventGraph unit test --- tests/__init__.py | 0 tests/core/__init__.py | 0 tests/core/test_events.py | 230 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 230 insertions(+) create mode 100644 tests/__init__.py create mode 100644 tests/core/__init__.py create mode 100644 tests/core/test_events.py diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/core/__init__.py b/tests/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/core/test_events.py b/tests/core/test_events.py new file mode 100644 index 0000000..c4485b1 --- /dev/null +++ b/tests/core/test_events.py @@ -0,0 +1,230 @@ +import asyncio +import logging +from dataclasses import dataclass + +import numpy as np +import pytest + +from src.core.events import EventData, EventGraph, _summarize + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + + +class DummyModule: + input_type = None + output_type = None + + async def process(self, data): + raise NotImplementedError + + +@dataclass +class DummyEvent(EventData): + value: int + + +@dataclass +class ArrayEvent(EventData): + data: np.ndarray + + +# --------------------------------------------------------------------------- +# register() +# --------------------------------------------------------------------------- + + +def test_register(): + graph = EventGraph() + + module = DummyModule() + module.input_type = "input" + + graph.register(module) + + assert module in graph.subscribers["input"] + + +# --------------------------------------------------------------------------- +# publish() +# --------------------------------------------------------------------------- + + +@pytest.mark.asyncio +async def test_publish_calls_subscriber(monkeypatch): + graph = EventGraph() + + called = asyncio.Event() + + class Module(DummyModule): + input_type = "input" + + async def process(self, data): + called.set() + return None + + module = Module() + graph.register(module) + + await graph.publish("input", DummyEvent(1)) + + await asyncio.wait_for(called.wait(), timeout=1) + + +# --------------------------------------------------------------------------- +# coroutine output +# --------------------------------------------------------------------------- + + +@pytest.mark.asyncio +async def test_coroutine_output_published(): + graph = EventGraph() + + received = asyncio.Event() + + class Producer(DummyModule): + input_type = "start" + output_type = "next" + + async def process(self, data): + return DummyEvent(42) + + class Consumer(DummyModule): + input_type = "next" + + async def process(self, data): + assert data.value == 42 + received.set() + + graph.register(Producer()) + graph.register(Consumer()) + + await graph.publish("start", DummyEvent(0)) + + await asyncio.wait_for(received.wait(), timeout=1) + + +# --------------------------------------------------------------------------- +# async generator output +# --------------------------------------------------------------------------- + + +@pytest.mark.asyncio +async def test_async_generator_output(): + graph = EventGraph() + + values = [] + + class Producer(DummyModule): + input_type = "start" + output_type = "stream" + + async def process(self, data): + yield DummyEvent(1) + yield DummyEvent(2) + yield DummyEvent(3) + + class Consumer(DummyModule): + input_type = "stream" + + async def process(self, data): + values.append(data.value) + + graph.register(Producer()) + graph.register(Consumer()) + + await graph.publish("start", DummyEvent(0)) + + await asyncio.sleep(0.1) + + assert values == [1, 2, 3] + + +# --------------------------------------------------------------------------- +# None outputs ignored +# --------------------------------------------------------------------------- + + +@pytest.mark.asyncio +async def test_none_output_not_published(): + graph = EventGraph() + + called = False + + class Producer(DummyModule): + input_type = "start" + output_type = "next" + + async def process(self, data): + return None + + class Consumer(DummyModule): + input_type = "next" + + async def process(self, data): + nonlocal called + called = True + + graph.register(Producer()) + graph.register(Consumer()) + + await graph.publish("start", DummyEvent(0)) + await asyncio.sleep(0.05) + + assert not called + + +# --------------------------------------------------------------------------- +# recursive routing +# --------------------------------------------------------------------------- + + +@pytest.mark.asyncio +async def test_recursive_routing(): + graph = EventGraph() + + done = asyncio.Event() + + class First(DummyModule): + input_type = "a" + output_type = "b" + + async def process(self, data): + return DummyEvent(data.value + 1) + + class Second(DummyModule): + input_type = "b" + + async def process(self, data): + assert data.value == 2 + done.set() + + graph.register(First()) + graph.register(Second()) + + await graph.publish("a", DummyEvent(1)) + + await asyncio.wait_for(done.wait(), timeout=1) + + +# --------------------------------------------------------------------------- +# summarize() +# --------------------------------------------------------------------------- + + +def test_summarize_numpy(): + event = ArrayEvent(np.zeros((3, 4), dtype=np.float32)) + + text = _summarize(event) + + assert "shape=(3, 4)" in text + assert "float32" in text + + +def test_summarize_regular(): + event = DummyEvent(123) + + text = _summarize(event) + + assert "DummyEvent" in text From 09563b0517a615927fb8e958a5f19357f2d29171 Mon Sep 17 00:00:00 2001 From: Popochounet Date: Fri, 3 Jul 2026 17:37:48 +0200 Subject: [PATCH 2/2] feat(requirements): added versionning --- requirements.txt | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/requirements.txt b/requirements.txt index 9f337e0..eadd691 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,24 +1,25 @@ -black -isort -mypy -flake8 -flake8-toml-config -pytest +black==26.3.1 +flake8==7.3.0 +flake8-toml-config==1.0.0 +isort==8.0.1 +mypy==1.20.2 +pytest==9.0.3 +pytest-asyncio==1.4.0 # server -numpy -ray[serve] -webrtcvad -faster-whisper -qdrant-client -sentence-transformers -pypdf -semantic_chunker +numpy==1.26.4 +ray[serve]==2.55.1 +webrtcvad==2.0.10 +faster-whisper==1.2.1 +qdrant-client==1.18.0 +sentence-transformers==3.2.1 +pypdf==5.1.0 +semantic_chunker==0.2.0 # client -sounddevice -websockets -omegaconf -prompt-toolkit +sounddevice==0.5.5 +websockets==16.0 +omegaconf==2.3.0 +prompt-toolkit==3.0.52