iii-sdk 0.21.0.dev1__tar.gz → 0.21.2.dev1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/PKG-INFO +2 -2
  2. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/pyproject.toml +2 -2
  3. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_baggage_span_processor.py +21 -69
  4. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_register_function_args.py +41 -19
  5. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/.gitignore +0 -0
  6. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/README.md +0 -0
  7. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/src/iii/__init__.py +0 -0
  8. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/src/iii/channel.py +0 -0
  9. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/src/iii/channels.py +0 -0
  10. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/src/iii/engine.py +0 -0
  11. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/src/iii/errors.py +0 -0
  12. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/src/iii/format_utils.py +0 -0
  13. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/src/iii/helpers.py +0 -0
  14. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/src/iii/iii.py +0 -0
  15. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/src/iii/iii_constants.py +0 -0
  16. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/src/iii/iii_types.py +0 -0
  17. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/src/iii/internal.py +0 -0
  18. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/src/iii/otel_worker_gauges.py +0 -0
  19. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/src/iii/protocol.py +0 -0
  20. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/src/iii/runtime.py +0 -0
  21. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/src/iii/state.py +0 -0
  22. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/src/iii/stream.py +0 -0
  23. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/src/iii/trigger.py +0 -0
  24. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/src/iii/triggers.py +0 -0
  25. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/src/iii/types.py +0 -0
  26. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/src/iii/utils.py +0 -0
  27. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/src/iii/worker_metrics.py +0 -0
  28. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/conftest.py +0 -0
  29. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_api_triggers.py +0 -0
  30. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_async_api.py +0 -0
  31. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_bridge.py +0 -0
  32. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_channel_close_delay.py +0 -0
  33. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_channel_submodule.py +0 -0
  34. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_context_propagation.py +0 -0
  35. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_data_channels.py +0 -0
  36. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_engine_constants.py +0 -0
  37. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_errors.py +0 -0
  38. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_format_utils.py +0 -0
  39. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_healthcheck.py +0 -0
  40. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_helpers.py +0 -0
  41. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_hold_process.py +0 -0
  42. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_http_external_functions_integration.py +0 -0
  43. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_iii_registration_dedup.py +0 -0
  44. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_init_api.py +0 -0
  45. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_invocation_exception.py +0 -0
  46. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_invocation_metadata.py +0 -0
  47. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_logger_function_ids.py +0 -0
  48. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_logger_otel.py +0 -0
  49. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_middleware.py +0 -0
  50. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_payload.py +0 -0
  51. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_pubsub.py +0 -0
  52. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_queue_integration.py +0 -0
  53. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_rbac_workers.py +0 -0
  54. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_runtime_submodule.py +0 -0
  55. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_span_ops.py +0 -0
  56. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_state.py +0 -0
  57. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_stream_models.py +0 -0
  58. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_stream_types.py +0 -0
  59. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_streams.py +0 -0
  60. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_streams_runtime_annotations.py +0 -0
  61. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_sync_api.py +0 -0
  62. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_telemetry.py +0 -0
  63. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_telemetry_exporters.py +0 -0
  64. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_telemetry_types.py +0 -0
  65. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_trace_helpers.py +0 -0
  66. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_trigger_metadata.py +0 -0
  67. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_trigger_registration_error.py +0 -0
  68. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_trigger_submodule.py +0 -0
  69. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_trigger_type_lifecycle.py +0 -0
  70. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_utils.py +0 -0
  71. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_worker_metadata.py +0 -0
  72. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/tests/test_worker_metrics.py +0 -0
  73. {iii_sdk-0.21.0.dev1 → iii_sdk-0.21.2.dev1}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iii-sdk
3
- Version: 0.21.0.dev1
3
+ Version: 0.21.2.dev1
4
4
  Summary: III SDK for Python
5
5
  Project-URL: Homepage, https://github.com/iii-hq/iii
6
6
  Project-URL: Repository, https://github.com/iii-hq/iii
@@ -14,7 +14,7 @@ Classifier: Programming Language :: Python :: 3.10
14
14
  Classifier: Programming Language :: Python :: 3.11
15
15
  Classifier: Programming Language :: Python :: 3.12
16
16
  Requires-Python: >=3.10
17
- Requires-Dist: iii-helpers==0.21.0.dev1
17
+ Requires-Dist: iii-helpers==0.21.2.dev1
18
18
  Requires-Dist: opentelemetry-api>=1.25
19
19
  Requires-Dist: pydantic>=2.0
20
20
  Requires-Dist: websockets>=12.0
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "iii-sdk"
7
- version = "0.21.0.dev1"
7
+ version = "0.21.2.dev1"
8
8
  description = "III SDK for Python"
9
9
  authors = [{ name = "III" }]
10
10
  license = { text = "Apache-2.0" }
@@ -23,7 +23,7 @@ dependencies = [
23
23
  "websockets>=12.0",
24
24
  "pydantic>=2.0",
25
25
  "opentelemetry-api>=1.25",
26
- "iii-helpers==0.21.0.dev1",
26
+ "iii-helpers==0.21.2.dev1",
27
27
  ]
28
28
 
29
29
  [project.urls]
@@ -10,10 +10,7 @@ from opentelemetry.sdk.trace.export.in_memory_span_exporter import (
10
10
  )
11
11
  from opentelemetry.sdk.trace.sampling import ALWAYS_OFF
12
12
 
13
- from iii_helpers.observability import (
14
- DEFAULT_ALLOWLIST,
15
- BaggageSpanProcessor,
16
- )
13
+ from iii_helpers.observability import BaggageSpanProcessor
17
14
 
18
15
 
19
16
  def _build_test_provider(
@@ -40,26 +37,35 @@ def _first_span_attr(exporter: InMemorySpanExporter, key: str) -> object | None:
40
37
  return spans[0].attributes.get(key) if spans[0].attributes else None
41
38
 
42
39
 
43
- def test_copies_default_allowlist_from_baggage_to_attributes() -> None:
40
+ def test_copies_every_baggage_entry_to_attributes() -> None:
41
+ """No key policy: baggage copies to span attributes unconditionally.
42
+
43
+ Which attributes *mean* something (`iii.tag.*`, `iii.session.*`) is a
44
+ query-side convention owned by the engine's traces API — a filtering
45
+ policy baked into worker binaries would silently drop newer keys from
46
+ any worker running an older SDK.
47
+ """
44
48
  provider, exporter = _build_test_provider(BaggageSpanProcessor())
45
49
  tracer = provider.get_tracer("test")
46
50
 
47
- token = _attach_baggage(
48
- {
49
- "iii.session.id": "S-1",
50
- "iii.message.id": "M-1",
51
- "iii.function.id": "auth::set_token",
52
- }
53
- )
51
+ entries = {
52
+ "iii.session.id": "S-1",
53
+ "iii.session.name": "refactor auth",
54
+ "iii.message.id": "M-1",
55
+ "iii.function.id": "auth::set_token",
56
+ "iii.tag.message": "fix the login bug",
57
+ # Non-iii baggage is a first-class tag source too.
58
+ "tenant.id": "t-42",
59
+ }
60
+ token = _attach_baggage(entries)
54
61
  try:
55
62
  with tracer.start_as_current_span("inner"):
56
63
  pass
57
64
  finally:
58
65
  context.detach(token)
59
66
 
60
- assert _first_span_attr(exporter, "iii.session.id") == "S-1"
61
- assert _first_span_attr(exporter, "iii.message.id") == "M-1"
62
- assert _first_span_attr(exporter, "iii.function.id") == "auth::set_token"
67
+ for key, value in entries.items():
68
+ assert _first_span_attr(exporter, key) == value, key
63
69
 
64
70
 
65
71
  def test_missing_baggage_entry_means_attribute_not_set() -> None:
@@ -78,51 +84,6 @@ def test_missing_baggage_entry_means_attribute_not_set() -> None:
78
84
  assert _first_span_attr(exporter, "iii.function.id") is None
79
85
 
80
86
 
81
- def test_baggage_entries_not_in_allowlist_are_dropped() -> None:
82
- provider, exporter = _build_test_provider(BaggageSpanProcessor())
83
- tracer = provider.get_tracer("test")
84
-
85
- token = _attach_baggage(
86
- {
87
- "iii.message.id": "M",
88
- "tenant.id": "t-42",
89
- "debug.feature_flag": "on",
90
- }
91
- )
92
- try:
93
- with tracer.start_as_current_span("inner"):
94
- pass
95
- finally:
96
- context.detach(token)
97
-
98
- assert _first_span_attr(exporter, "iii.message.id") == "M"
99
- assert _first_span_attr(exporter, "tenant.id") is None
100
- assert _first_span_attr(exporter, "debug.feature_flag") is None
101
-
102
-
103
- def test_custom_allowlist_is_honored() -> None:
104
- processor = BaggageSpanProcessor(allowlist=["tenant.id", "iii.message.id"])
105
- provider, exporter = _build_test_provider(processor)
106
- tracer = provider.get_tracer("test")
107
-
108
- token = _attach_baggage(
109
- {
110
- "tenant.id": "t-1",
111
- "iii.message.id": "M",
112
- "iii.session.id": "S-not-copied",
113
- }
114
- )
115
- try:
116
- with tracer.start_as_current_span("inner"):
117
- pass
118
- finally:
119
- context.detach(token)
120
-
121
- assert _first_span_attr(exporter, "tenant.id") == "t-1"
122
- assert _first_span_attr(exporter, "iii.message.id") == "M"
123
- assert _first_span_attr(exporter, "iii.session.id") is None
124
-
125
-
126
87
  def test_empty_parent_context_produces_no_attributes() -> None:
127
88
  provider, exporter = _build_test_provider(BaggageSpanProcessor())
128
89
  tracer = provider.get_tracer("test")
@@ -151,12 +112,3 @@ def test_noop_guard_skips_processing_when_sampled_out() -> None:
151
112
  context.detach(token)
152
113
 
153
114
  assert exporter.get_finished_spans() == ()
154
-
155
-
156
- def test_default_allowlist_matches_other_sdks() -> None:
157
- """DEFAULT_ALLOWLIST drift across languages would break worker chains."""
158
- assert tuple(DEFAULT_ALLOWLIST) == (
159
- "iii.session.id",
160
- "iii.message.id",
161
- "iii.function.id",
162
- )
@@ -2,6 +2,7 @@
2
2
 
3
3
  import json
4
4
  import time
5
+ from collections.abc import Callable
5
6
  from types import SimpleNamespace
6
7
  from typing import Any
7
8
 
@@ -17,6 +18,8 @@ from iii.iii_types import RegisterFunctionFormat
17
18
  # FakeWs helpers
18
19
  # ---------------------------------------------------------------------------
19
20
 
21
+ _CREATED_CLIENTS: list[III] = []
22
+
20
23
 
21
24
  class FakeWebSocket:
22
25
  def __init__(self) -> None:
@@ -51,10 +54,38 @@ def _patch_ws(monkeypatch: pytest.MonkeyPatch) -> FakeWebSocket:
51
54
 
52
55
  def _make_client() -> III:
53
56
  client = III("ws://fake", InitOptions())
54
- time.sleep(0.05)
57
+ _CREATED_CLIENTS.append(client)
58
+ _wait_for(lambda: client.get_connection_state() == "connected", "fake WebSocket client to connect")
55
59
  return client
56
60
 
57
61
 
62
+ @pytest.fixture(autouse=True)
63
+ def _shutdown_created_clients() -> None:
64
+ yield
65
+ while _CREATED_CLIENTS:
66
+ client = _CREATED_CLIENTS.pop()
67
+ if client._thread.is_alive():
68
+ client.shutdown()
69
+
70
+
71
+ def _wait_for(predicate: Callable[[], bool], what: str, timeout: float = 2.0) -> None:
72
+ deadline = time.monotonic() + timeout
73
+ while time.monotonic() < deadline:
74
+ if predicate():
75
+ return
76
+ time.sleep(0.005)
77
+ pytest.fail(f"timed out waiting for {what}")
78
+
79
+
80
+ def _registration_messages(ws: FakeWebSocket, function_id: str) -> list[dict[str, Any]]:
81
+ return [m for m in ws.sent if m.get("type") == "registerfunction" and m.get("id") == function_id]
82
+
83
+
84
+ def _wait_for_registration(ws: FakeWebSocket, function_id: str) -> list[dict[str, Any]]:
85
+ _wait_for(lambda: bool(_registration_messages(ws, function_id)), f"registration for {function_id}")
86
+ return _registration_messages(ws, function_id)
87
+
88
+
58
89
  # ---------------------------------------------------------------------------
59
90
  # Two-arg register_function tests
60
91
  # ---------------------------------------------------------------------------
@@ -78,9 +109,8 @@ def test_register_function_str_with_request_format(monkeypatch: pytest.MonkeyPat
78
109
  return data
79
110
 
80
111
  client.register_function("demo.with_args", handler, request_format=req_fmt)
81
- time.sleep(0.02)
82
112
 
83
- reg_msgs = [m for m in ws.sent if m.get("type") == "registerfunction" and m.get("id") == "demo.with_args"]
113
+ reg_msgs = _wait_for_registration(ws, "demo.with_args")
84
114
  assert len(reg_msgs) == 1
85
115
 
86
116
  sent_req_fmt = reg_msgs[0].get("request_format")
@@ -127,9 +157,8 @@ def test_register_function_str_with_both_formats(monkeypatch: pytest.MonkeyPatch
127
157
  response_format=res_fmt,
128
158
  metadata={"version": "1"},
129
159
  )
130
- time.sleep(0.02)
131
160
 
132
- reg_msgs = [m for m in ws.sent if m.get("type") == "registerfunction" and m.get("id") == "demo.both_formats"]
161
+ reg_msgs = _wait_for_registration(ws, "demo.both_formats")
133
162
  assert len(reg_msgs) == 1
134
163
  assert reg_msgs[0].get("description") == "A search function"
135
164
  assert reg_msgs[0].get("request_format") is not None
@@ -149,9 +178,8 @@ def test_register_function_str_minimal(monkeypatch: pytest.MonkeyPatch) -> None:
149
178
  return data
150
179
 
151
180
  client.register_function("demo.minimal", handler)
152
- time.sleep(0.02)
153
181
 
154
- reg_msgs = [m for m in ws.sent if m.get("type") == "registerfunction" and m.get("id") == "demo.minimal"]
182
+ reg_msgs = _wait_for_registration(ws, "demo.minimal")
155
183
  assert len(reg_msgs) == 1
156
184
  assert "request_format" not in reg_msgs[0]
157
185
  assert "response_format" not in reg_msgs[0]
@@ -179,9 +207,8 @@ def test_register_function_str_with_http_invocation_and_format(monkeypatch: pyte
179
207
  HttpInvocationConfig(url="https://example.com/fn", method="POST"),
180
208
  request_format=req_fmt,
181
209
  )
182
- time.sleep(0.02)
183
210
 
184
- reg_msgs = [m for m in ws.sent if m.get("type") == "registerfunction" and m.get("id") == "external::with_format"]
211
+ reg_msgs = _wait_for_registration(ws, "external::with_format")
185
212
  assert len(reg_msgs) == 1
186
213
  assert reg_msgs[0].get("invocation", {}).get("url") == "https://example.com/fn"
187
214
  assert reg_msgs[0].get("request_format") is not None
@@ -230,9 +257,8 @@ def test_register_function_str_id_auto_extracts_formats(monkeypatch: pytest.Monk
230
257
  return UserOutput(message=f"Hello {data.name}")
231
258
 
232
259
  client.register_function("demo.auto", handler, description="Auto-extract demo")
233
- time.sleep(0.02)
234
260
 
235
- reg_msgs = [m for m in ws.sent if m.get("type") == "registerfunction" and m.get("id") == "demo.auto"]
261
+ reg_msgs = _wait_for_registration(ws, "demo.auto")
236
262
  assert len(reg_msgs) == 1
237
263
  msg = reg_msgs[0]
238
264
 
@@ -282,9 +308,8 @@ def test_register_function_str_id_explicit_formats_override(monkeypatch: pytest.
282
308
  request_format=explicit_req,
283
309
  response_format=explicit_res,
284
310
  )
285
- time.sleep(0.02)
286
311
 
287
- reg_msgs = [m for m in ws.sent if m.get("type") == "registerfunction" and m.get("id") == "demo.explicit"]
312
+ reg_msgs = _wait_for_registration(ws, "demo.explicit")
288
313
  assert len(reg_msgs) == 1
289
314
  msg = reg_msgs[0]
290
315
 
@@ -306,9 +331,8 @@ def test_register_function_str_id_no_annotations(monkeypatch: pytest.MonkeyPatch
306
331
  return data
307
332
 
308
333
  client.register_function("demo.no_hints", handler)
309
- time.sleep(0.02)
310
334
 
311
- reg_msgs = [m for m in ws.sent if m.get("type") == "registerfunction" and m.get("id") == "demo.no_hints"]
335
+ reg_msgs = _wait_for_registration(ws, "demo.no_hints")
312
336
  assert len(reg_msgs) == 1
313
337
  assert "request_format" not in reg_msgs[0]
314
338
  assert "response_format" not in reg_msgs[0]
@@ -325,9 +349,8 @@ def test_register_function_str_id_with_metadata(monkeypatch: pytest.MonkeyPatch)
325
349
  return data
326
350
 
327
351
  client.register_function("demo.meta", handler, metadata={"version": "2.0"})
328
- time.sleep(0.02)
329
352
 
330
- reg_msgs = [m for m in ws.sent if m.get("type") == "registerfunction" and m.get("id") == "demo.meta"]
353
+ reg_msgs = _wait_for_registration(ws, "demo.meta")
331
354
  assert len(reg_msgs) == 1
332
355
  assert reg_msgs[0]["metadata"] == {"version": "2.0"}
333
356
 
@@ -352,9 +375,8 @@ def test_register_function_str_id_with_http_invocation(monkeypatch: pytest.Monke
352
375
  HttpInvocationConfig(url="https://example.com/fn", method="POST"),
353
376
  description="HTTP function",
354
377
  )
355
- time.sleep(0.02)
356
378
 
357
- reg_msgs = [m for m in ws.sent if m.get("type") == "registerfunction" and m.get("id") == "demo.http"]
379
+ reg_msgs = _wait_for_registration(ws, "demo.http")
358
380
  assert len(reg_msgs) == 1
359
381
  assert reg_msgs[0]["invocation"]["url"] == "https://example.com/fn"
360
382
  assert reg_msgs[0]["description"] == "HTTP function"
File without changes
File without changes
File without changes