Skip to content

Commit 3e4500b

Browse files
committed
add tests
1 parent 2beb595 commit 3e4500b

File tree

4 files changed

+217
-29
lines changed

4 files changed

+217
-29
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# ---
2+
import os
3+
import logging
4+
import sys
5+
import time
6+
7+
from UnleashClient import UnleashClient
8+
9+
root = logging.getLogger()
10+
root.setLevel(logging.DEBUG)
11+
12+
handler = logging.StreamHandler(sys.stdout)
13+
handler.setLevel(logging.DEBUG)
14+
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
15+
handler.setFormatter(formatter)
16+
root.addHandler(handler)
17+
# ---
18+
19+
api_url = os.getenv('UNLEASH_API_URL', 'https://app.unleash-hosted.com/demo/api')
20+
api_token = os.getenv('UNLEASH_API_TOKEN', 'demo-app:dev.9fc74dd72d2b88bea5253c04240b21a54841f08d9918046ed55a06b5')
21+
flag = "example-flag"
22+
use_streaming = os.getenv("USE_STREAMING", "true").lower() == "true"
23+
24+
client = UnleashClient(
25+
url=api_url,
26+
app_name="integration-python",
27+
custom_headers={'Authorization': api_token},
28+
experimental_mode={"type": "streaming"} if use_streaming else None,
29+
metrics_interval=1)
30+
31+
client.initialize_client()
32+
33+
while True:
34+
print(f"'{flag}' is enabled: {client.is_enabled(flag)}")
35+
time.sleep(3)

tests/integration_tests/integration_unleashheroku.py

Lines changed: 0 additions & 29 deletions
This file was deleted.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
from __future__ import annotations
2+
3+
import json
4+
5+
import pytest
6+
7+
from UnleashClient.streaming.event_processor import StreamingEventProcessor
8+
9+
10+
class FakeEngine:
11+
def __init__(self):
12+
self.states = []
13+
14+
def take_state(self, state):
15+
self.states.append(state)
16+
17+
18+
class FakeEvent:
19+
def __init__(self, event: str, data):
20+
self.event = event
21+
self.data = data
22+
23+
24+
def test_processor_hydrates_on_connected():
25+
engine = FakeEngine()
26+
processor = StreamingEventProcessor(engine)
27+
28+
assert processor.hydrated is False
29+
30+
payload = {"version": 1, "features": [], "segments": []}
31+
processor.process(FakeEvent("unleash-connected", payload))
32+
33+
assert processor.hydrated is True
34+
assert engine.states == [payload]
35+
36+
37+
def test_processor_applies_updates():
38+
engine = FakeEngine()
39+
processor = StreamingEventProcessor(engine)
40+
41+
connected_payload = {"version": 1, "features": ["f1"], "segments": []}
42+
update_payload = {"version": 2, "features": ["f1", "f2"], "segments": []}
43+
44+
processor.process(FakeEvent("unleash-connected", connected_payload))
45+
processor.process(FakeEvent("unleash-updated", update_payload))
46+
47+
assert processor.hydrated is True
48+
assert engine.states == [connected_payload, update_payload]
49+
50+
51+
def test_processor_ignores_unknown_event_types():
52+
engine = FakeEngine()
53+
processor = StreamingEventProcessor(engine)
54+
55+
processor.process(FakeEvent("heartbeat", {}))
56+
processor.process(FakeEvent("message", {}))
57+
58+
# No states should be applied
59+
assert engine.states == []
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
from __future__ import annotations
2+
3+
import json
4+
import threading
5+
import time
6+
from typing import Iterable
7+
from unittest.mock import MagicMock, patch
8+
9+
import pytest
10+
11+
from UnleashClient.streaming import StreamingConnector
12+
from UnleashClient.streaming.event_processor import StreamingEventProcessor
13+
14+
15+
class FakeEngine:
16+
def __init__(self):
17+
self.states = []
18+
19+
def take_state(self, state):
20+
self.states.append(state)
21+
22+
23+
class FakeEvent:
24+
def __init__(self, event: str, data):
25+
self.event = event
26+
self.data = data
27+
28+
29+
class FiniteSSEClient:
30+
"""SSE client that yields given events then stops."""
31+
32+
def __init__(self, events: Iterable[FakeEvent]):
33+
self._events = list(events)
34+
self.closed = False
35+
36+
@property
37+
def events(self):
38+
for e in self._events:
39+
if self.closed:
40+
break
41+
yield e
42+
43+
def close(self):
44+
self.closed = True
45+
46+
def interrupt(self):
47+
self.close()
48+
49+
50+
class FailingSSEClient:
51+
"""SSE client that fails immediately when iterating events."""
52+
53+
def __init__(self):
54+
self.closed = False
55+
56+
@property
57+
def events(self):
58+
raise ConnectionError("Simulated connection failure")
59+
60+
def close(self):
61+
self.closed = True
62+
63+
def interrupt(self):
64+
self.close()
65+
66+
def test_successful_connection_calls_ready():
67+
engine = FakeEngine()
68+
processor = StreamingEventProcessor(engine)
69+
70+
ready_calls = {"n": 0}
71+
72+
def on_ready():
73+
ready_calls["n"] += 1
74+
75+
events = [
76+
FakeEvent("unleash-connected", {"version": 1, "features": [], "segments": []}),
77+
FakeEvent("unleash-updated", {"version": 2, "features": [], "segments": []}),
78+
]
79+
80+
controller = StreamingConnector(
81+
url="http://unleash.example",
82+
headers={},
83+
request_timeout=5,
84+
event_processor=processor,
85+
on_ready=on_ready,
86+
sse_client_factory=lambda url, headers, timeout: FiniteSSEClient(events),
87+
)
88+
89+
th = threading.Thread(target=controller._run, daemon=True)
90+
th.start()
91+
time.sleep(0.05)
92+
controller.stop()
93+
th.join(timeout=1)
94+
95+
assert engine.states # at least one state applied
96+
assert ready_calls["n"] == 1
97+
98+
99+
def test_connection_failures_trigger_retries():
100+
engine = FakeEngine()
101+
processor = StreamingEventProcessor(engine)
102+
103+
attempts = []
104+
105+
def factory(url, headers, timeout):
106+
attempts.append(time.time())
107+
return FailingSSEClient()
108+
109+
controller = StreamingConnector(
110+
url="http://unleash.example",
111+
headers={},
112+
request_timeout=5,
113+
event_processor=processor,
114+
sse_client_factory=factory,
115+
)
116+
117+
th = threading.Thread(target=controller._run, daemon=True)
118+
th.start()
119+
time.sleep(1.5)
120+
controller.stop()
121+
th.join(timeout=1)
122+
123+
assert len(attempts) >= 1

0 commit comments

Comments
 (0)