import threading
import time
import unittest
import pytest
ENDPOINT = "redis://localhost:6379"
UNREACHABLE_ENDPOINT = "redis://127.0.0.1:1"
CHANNEL_PREFIX = "wingfoil_pytest_"
class TestRedisConstruction(unittest.TestCase):
def test_sub_constructs_stream(self):
from wingfoil import redis_sub
stream = redis_sub(UNREACHABLE_ENDPOINT, "ch")
self.assertIsNotNone(stream)
def test_pub_method_constructs_node(self):
from wingfoil import constant
node = constant({"channel": "ch", "payload": b"v"}).redis_pub(
UNREACHABLE_ENDPOINT, "ch"
)
self.assertIsNotNone(node)
class TestRedisUnreachable(unittest.TestCase):
def test_pub_single_dict_marshals_then_errors(self):
from wingfoil import constant
node = constant({"channel": "ch", "payload": b"v"}).redis_pub(
UNREACHABLE_ENDPOINT, "ch"
)
with self.assertRaises(Exception):
node.run(realtime=True, cycles=1)
def test_pub_list_of_dicts_marshals_then_errors(self):
from wingfoil import constant
records = [{"channel": "ch", "payload": b"v"}, {"payload": b"v2"}] node = constant(records).redis_pub(UNREACHABLE_ENDPOINT, "ch")
with self.assertRaises(Exception):
node.run(realtime=True, cycles=1)
def test_pub_bad_value_type_marshals_empty_burst(self):
from wingfoil import constant
node = constant("not a dict").redis_pub(UNREACHABLE_ENDPOINT, "ch")
with self.assertRaises(Exception):
node.run(realtime=True, cycles=1)
def test_sub_unreachable_errors(self):
from wingfoil import redis_sub
stream = redis_sub(UNREACHABLE_ENDPOINT, "ch").collect()
with self.assertRaises(Exception):
stream.run(realtime=True, cycles=1)
def test_stream_read_constructs_stream(self):
from wingfoil import redis_stream_read
stream = redis_stream_read(UNREACHABLE_ENDPOINT, "events")
self.assertIsNotNone(stream)
def test_stream_write_marshals_then_errors(self):
from wingfoil import constant
node = constant({"key": "events", "fields": {"kind": b"login"}}).redis_stream_write(
UNREACHABLE_ENDPOINT, "events"
)
with self.assertRaises(Exception):
node.run(realtime=True, cycles=1)
def test_stream_write_list_marshals_then_errors(self):
from wingfoil import constant
records = [
{"key": "events", "fields": {"kind": b"login"}},
{"fields": {"kind": b"logout"}}, ]
node = constant(records).redis_stream_write(UNREACHABLE_ENDPOINT, "events")
with self.assertRaises(Exception):
node.run(realtime=True, cycles=1)
@pytest.mark.requires_redis
class TestRedisSub(unittest.TestCase):
def test_sub_returns_expected_shape(self):
from wingfoil import redis_sub
channel = f"{CHANNEL_PREFIX}sub_shape"
stream = redis_sub(ENDPOINT, channel).collect()
stream.run(realtime=True, duration=2.0)
result = stream.peek_value()
self.assertTrue(result is None or isinstance(result, list))
@pytest.mark.requires_redis
class TestRedisPub(unittest.TestCase):
def test_pub_to_no_subscriber_succeeds(self):
from wingfoil import constant
channel = f"{CHANNEL_PREFIX}pub_noop"
constant({"channel": channel, "payload": b"v"}).redis_pub(
ENDPOINT, channel
).run(realtime=True, cycles=1)
def test_pub_sub_round_trip(self):
from wingfoil import constant, redis_sub
channel = f"{CHANNEL_PREFIX}round_trip"
received = []
def subscriber():
stream = redis_sub(ENDPOINT, channel).collect()
stream.run(realtime=True, duration=3.0)
for tick in stream.peek_value() or []:
for event in tick:
received.append(event["payload"])
t = threading.Thread(target=subscriber)
t.start()
time.sleep(0.8)
constant({"channel": channel, "payload": b"hello_from_python"}).redis_pub(
ENDPOINT, channel
).run(realtime=True, cycles=1)
t.join()
self.assertIn(b"hello_from_python", received)
@pytest.mark.requires_redis
class TestRedisStreams(unittest.TestCase):
def test_stream_write_and_read_snapshot(self):
from wingfoil import constant, redis_stream_read
key = f"{CHANNEL_PREFIX}stream_snapshot"
constant(
[
{"key": key, "fields": {"kind": b"login"}},
{"key": key, "fields": {"kind": b"logout"}},
]
).redis_stream_write(ENDPOINT, key).run(realtime=True, cycles=1)
stream = redis_stream_read(ENDPOINT, key).collect()
stream.run(realtime=True, cycles=1)
events = [e for tick in (stream.peek_value() or []) for e in tick]
kinds = [e["fields"]["kind"] for e in events]
self.assertIn(b"login", kinds)
self.assertIn(b"logout", kinds)
for e in events:
self.assertIn("key", e)
self.assertIn("id", e)
self.assertIsInstance(e["fields"], dict)
if __name__ == "__main__":
unittest.main()