import threading
import time
import unittest
class TestFixLoopback(unittest.TestCase):
def test_connect_and_accept_loopback(self):
from wingfoil import fix_accept, fix_connect
port = 19877
results = {"acc_status": None, "init_status": None}
def run_acceptor():
acc_data, acc_status = fix_accept(port, "ACCEPTOR", "INITIATOR")
status_stream = acc_status.collect()
from wingfoil import Graph
Graph([acc_data, status_stream]).run(realtime=True, duration=2.0)
results["acc_status"] = status_stream.peek_value()
acc_thread = threading.Thread(target=run_acceptor, daemon=True)
acc_thread.start()
time.sleep(0.3)
init_data, init_status = fix_connect("127.0.0.1", port, "INITIATOR", "ACCEPTOR")
status_stream = init_status.collect()
from wingfoil import Graph
Graph([init_data, status_stream]).run(realtime=True, duration=1.5)
results["init_status"] = status_stream.peek_value()
acc_thread.join(timeout=5)
init_statuses = results["init_status"]
self.assertIsNotNone(init_statuses)
all_statuses = [s for tick in init_statuses for s in tick]
self.assertTrue(
any(s == "logged_in" for s in all_statuses),
f"Expected 'logged_in' in initiator statuses, got: {all_statuses}",
)
def test_fix_connect_returns_tuple(self):
from wingfoil import fix_connect
result = fix_connect("127.0.0.1", 1, "S", "T")
self.assertIsInstance(result, tuple)
self.assertEqual(len(result), 2)
def test_fix_accept_returns_tuple(self):
from wingfoil import fix_accept
result = fix_accept(19878, "S", "T")
self.assertIsInstance(result, tuple)
self.assertEqual(len(result), 2)
def test_fix_connect_tls_returns_triple(self):
from wingfoil import fix_connect_tls
result = fix_connect_tls("127.0.0.1", 443, "S", "T")
self.assertIsInstance(result, tuple)
self.assertEqual(len(result), 3)
class TestFixSender(unittest.TestCase):
def _make_sender(self):
from wingfoil import fix_connect_tls
_, _, sender = fix_connect_tls(
"127.0.0.1", 443, "SENDER", "TARGET", password="secret"
)
return sender
def test_send_accepts_valid_message(self):
sender = self._make_sender()
try:
sender.send({"msg_type": "D", "fields": [[55, "AAPL"], [38, "100"]]})
except RuntimeError:
pass
def test_send_missing_msg_type_raises(self):
sender = self._make_sender()
with self.assertRaises(KeyError):
sender.send({"fields": []})
def test_send_missing_fields_raises(self):
sender = self._make_sender()
with self.assertRaises(KeyError):
sender.send({"msg_type": "D"})
def test_send_field_wrong_length_raises(self):
sender = self._make_sender()
with self.assertRaises(ValueError):
sender.send({"msg_type": "D", "fields": [[55]]})
with self.assertRaises(ValueError):
sender.send({"msg_type": "D", "fields": [[55, "x", "y"]]})
class TestFixConnectTlsNoPassword(unittest.TestCase):
def test_connect_tls_without_password(self):
from wingfoil import fix_connect_tls
data, status, sender = fix_connect_tls("127.0.0.1", 443, "S", "T")
self.assertIsNotNone(data)
self.assertIsNotNone(status)
self.assertIsNotNone(sender)
class TestFixLoopbackData(unittest.TestCase):
def test_loopback_acceptor_sees_disconnect(self):
import threading
import time
from wingfoil import Graph, fix_accept, fix_connect
port = 19879
acc_statuses = []
def run_acceptor():
acc_data, acc_status = fix_accept(port, "ACCEPTOR", "INITIATOR")
status_stream = acc_status.collect()
Graph([acc_data, status_stream]).run(realtime=True, duration=2.0)
vals = status_stream.peek_value() or []
acc_statuses.extend(s for tick in vals for s in tick)
t = threading.Thread(target=run_acceptor, daemon=True)
t.start()
time.sleep(0.3)
init_data, init_status = fix_connect("127.0.0.1", port, "INITIATOR", "ACCEPTOR")
status_stream = init_status.collect()
Graph([init_data, status_stream]).run(realtime=True, duration=0.8)
t.join(timeout=5)
self.assertIn("logged_in", acc_statuses)
if __name__ == "__main__":
unittest.main()