tigrbl_tests 0.4.3.dev4__py3-none-any.whl → 0.4.4.dev7__py3-none-any.whl
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.
- tests/architecture/test_runtime_structure.py +6 -2
- tests/i9n/test_batch_scheduler_i9n.py +1 -1
- tests/i9n/test_resident_batch_scheduler_runtime.py +1 -1
- tests/i9n/test_schema_ctx_attributes_integration.py +3 -1
- tests/i9n/test_webtransport_tigrcorn_bridge.py +4 -2
- tests/i9n/test_webtransport_tigrcorn_session_multiplexing.py +18 -15
- tests/test_secdeps_execute_in_pre_tx.py +1 -1
- tests/unit/decorators/test_declarative_surface.py +3 -2
- tests/unit/decorators/test_schema_ctx_bindings.py +2 -0
- tests/unit/runtime/test_binding_exchange_normalization_contract.py +7 -7
- tests/unit/runtime/test_binding_token_lowering_contract.py +83 -8
- tests/unit/runtime/test_bindingspec_event_subevent_schema_contract.py +1 -1
- tests/unit/runtime/test_bindingspec_kernelplan_protocol_compilation_contract.py +58 -2
- tests/unit/runtime/test_canonical_bindingspec_framing_policy.py +50 -37
- tests/unit/runtime/test_contract_classification_consumption_policy.py +0 -2
- tests/unit/runtime/test_cross_transport_equivalence_contract.py +1 -1
- tests/unit/runtime/test_eventful_protocol_decorator_surface_contract.py +3 -1
- tests/unit/runtime/test_eventful_subevent_surface_contracts.py +8 -2
- tests/unit/runtime/test_framing_matrix_ssot_conformance.py +3 -3
- tests/unit/runtime/test_http_stream_client_stream_runtime_contract.py +2 -2
- tests/unit/runtime/test_op_verb_to_default_binding_matrix_contract.py +44 -1
- tests/unit/runtime/test_protocol_anchor_ordering_parity_contract.py +3 -5
- tests/unit/runtime/test_python_only_runtime_no_rust_public_exports.py +14 -0
- tests/unit/runtime/test_python_only_runtime_rust_executor_rejection.py +17 -31
- tests/unit/runtime/test_python_only_runtime_rust_kernel_module_retirement.py +9 -30
- tests/unit/runtime/test_python_only_runtime_ssot_docs_rust_parity_ban.py +2 -8
- tests/unit/runtime/test_runtime_frame_codec_contract.py +65 -27
- tests/unit/runtime/test_session_leakage_prevention_contract.py +1 -1
- tests/unit/runtime/test_stream_table_current_behavior_contract.py +145 -0
- tests/unit/runtime/test_table_profile_op_selection_matrix_contract.py +9 -3
- tests/unit/runtime/test_table_transport_binding_profiles_contract.py +24 -1
- tests/unit/runtime/test_transport_delivery_guarantees_contract.py +7 -3
- tests/unit/runtime/test_unsupported_framing_fail_closed_contract.py +18 -20
- tests/unit/runtime/test_websocket_framing_runtime_contract.py +18 -20
- tests/unit/runtime/test_webtransport_lane_framing_policy.py +28 -22
- tests/unit/test_handler_entrypoints.py +60 -0
- tests/unit/test_operator_surface_closure.py +2 -1
- tests/unit/test_operator_websocket_route_contracts.py +2 -1
- tests/unit/test_package_badges_and_notices.py +1 -1
- tests/unit/test_schema_ctx_attributes.py +29 -0
- tests/unit/test_schema_ctx_plain_class.py +2 -0
- tests/unit/test_spec_binding_app_engine_contracts.py +4 -2
- tests/unit/test_spec_contracts_session_bindings.py +5 -4
- {tigrbl_tests-0.4.3.dev4.dist-info → tigrbl_tests-0.4.4.dev7.dist-info}/METADATA +1 -1
- {tigrbl_tests-0.4.3.dev4.dist-info → tigrbl_tests-0.4.4.dev7.dist-info}/RECORD +48 -51
- tests/rust/atoms/test_rust_atoms_public_surface.py +0 -21
- tests/rust/ffi/test_rust_binding_trace.py +0 -21
- tests/rust/kernel/test_rust_kernel_public_surface.py +0 -17
- tests/rust/runtime/test_rust_runtime_engine_policy.py +0 -12
- tests/rust/runtime/test_rust_runtime_public_surface.py +0 -36
- {tigrbl_tests-0.4.3.dev4.dist-info → tigrbl_tests-0.4.4.dev7.dist-info}/WHEEL +0 -0
- {tigrbl_tests-0.4.3.dev4.dist-info → tigrbl_tests-0.4.4.dev7.dist-info}/licenses/LICENSE +0 -0
- {tigrbl_tests-0.4.3.dev4.dist-info → tigrbl_tests-0.4.4.dev7.dist-info}/licenses/NOTICE +0 -0
|
@@ -9,10 +9,14 @@ RUNTIME_PKG = _STANDARDS / "tigrbl_runtime" / "tigrbl_runtime"
|
|
|
9
9
|
|
|
10
10
|
def test_dependency_invoke_is_runtime_event_anchor():
|
|
11
11
|
system = (RUNTIME_PKG / "runtime" / "system.py").read_text()
|
|
12
|
-
|
|
12
|
+
phase_runner = (RUNTIME_PKG / "executors" / "phase_runner.py").read_text()
|
|
13
|
+
executors_init = (RUNTIME_PKG / "executors" / "__init__.py").read_text()
|
|
14
|
+
assert not (RUNTIME_PKG / "executors" / "invoke.py").exists()
|
|
13
15
|
assert "DEP_EXTRA" in system
|
|
14
16
|
assert '"PRE_TX_BEGIN"' in system
|
|
15
|
-
assert '"PRE_TX_BEGIN"' in
|
|
17
|
+
assert '"PRE_TX_BEGIN"' in phase_runner
|
|
18
|
+
assert "invoke_op" not in executors_init
|
|
19
|
+
assert "tigrbl_ops_oltp" not in phase_runner
|
|
16
20
|
|
|
17
21
|
|
|
18
22
|
def test_runtime_gateway_owns_runtime_entrypoint_and_send():
|
|
@@ -53,7 +53,7 @@ async def test_batch_scheduler_i9n_executemany_to_grouped_fanout() -> None:
|
|
|
53
53
|
db=Db(),
|
|
54
54
|
op="create",
|
|
55
55
|
model=object,
|
|
56
|
-
batch_policy={"enabled": True, "max_size": 2},
|
|
56
|
+
batch_policy={"enabled": True, "max_size": 2, "max_delay_ms": 60_000},
|
|
57
57
|
temp={},
|
|
58
58
|
)
|
|
59
59
|
|
|
@@ -55,9 +55,11 @@ async def test_schema_ctx_bindings(schema_ctx_client):
|
|
|
55
55
|
@pytest.mark.i9n
|
|
56
56
|
@pytest.mark.asyncio
|
|
57
57
|
async def test_schema_ctx_request_response_schema(schema_ctx_client):
|
|
58
|
-
_, router,
|
|
58
|
+
_, router, Widget, _ = schema_ctx_client
|
|
59
59
|
create_schema = router.schemas.Widget.create.in_
|
|
60
60
|
read_schema = router.schemas.Widget.read.out
|
|
61
|
+
assert create_schema is Widget.schemas.create.in_
|
|
62
|
+
assert read_schema is Widget.schemas.read.out
|
|
61
63
|
assert create_schema.model_fields["name"].is_required()
|
|
62
64
|
assert "age" in read_schema.model_fields
|
|
63
65
|
|
|
@@ -7,6 +7,7 @@ from typing import Any
|
|
|
7
7
|
import pytest
|
|
8
8
|
|
|
9
9
|
from tigrbl import WebTransportBindingSpec
|
|
10
|
+
from tigrbl_core._spec import TextFramingSpec
|
|
10
11
|
from tigrbl_core._spec.hook_spec import HookSpec
|
|
11
12
|
from tigrbl_core._spec.hook_types import HookPhase
|
|
12
13
|
from tigrbl_concrete._concrete._app import App as TigrblApp
|
|
@@ -77,7 +78,7 @@ def _app(path: str) -> TigrblApp:
|
|
|
77
78
|
proto="webtransport",
|
|
78
79
|
path=path,
|
|
79
80
|
profile="bidi_stream",
|
|
80
|
-
inner_framing=
|
|
81
|
+
inner_framing=TextFramingSpec(),
|
|
81
82
|
),
|
|
82
83
|
tigrbl_exchange="bidirectional_stream",
|
|
83
84
|
)
|
|
@@ -132,6 +133,7 @@ async def test_tigrbl_webtransport_bidi_stream_runs_over_tigrcorn_contract_event
|
|
|
132
133
|
"session_id": "session-1",
|
|
133
134
|
"stream_id": "stream-1",
|
|
134
135
|
"stream_direction": "bidi",
|
|
136
|
+
"stream_initiator": "client",
|
|
135
137
|
"framing": "text",
|
|
136
138
|
"data": b"echo:hello",
|
|
137
139
|
"more": False,
|
|
@@ -220,7 +222,7 @@ async def test_webtransport_bidi_and_unidi_lane_metadata_reaches_hooks() -> None
|
|
|
220
222
|
proto="webtransport",
|
|
221
223
|
path="/transport/lanes",
|
|
222
224
|
profile="bidi_stream",
|
|
223
|
-
inner_framing=
|
|
225
|
+
inner_framing=TextFramingSpec(),
|
|
224
226
|
),
|
|
225
227
|
tigrbl_exchange="bidirectional_stream",
|
|
226
228
|
)
|
|
@@ -8,6 +8,7 @@ from typing import Any
|
|
|
8
8
|
import pytest
|
|
9
9
|
|
|
10
10
|
from tigrbl import WebTransportBindingSpec
|
|
11
|
+
from tigrbl_core._spec import TextFramingSpec
|
|
11
12
|
from tigrbl_core._spec.hook_spec import HookSpec
|
|
12
13
|
from tigrbl_core._spec.hook_types import HookPhase
|
|
13
14
|
from tigrbl_concrete._concrete._app import App as TigrblApp
|
|
@@ -87,7 +88,7 @@ def _app(path: str) -> TigrblApp:
|
|
|
87
88
|
proto="webtransport",
|
|
88
89
|
path=path,
|
|
89
90
|
profile="bidi_stream",
|
|
90
|
-
inner_framing=
|
|
91
|
+
inner_framing=TextFramingSpec(),
|
|
91
92
|
),
|
|
92
93
|
tigrbl_exchange="bidirectional_stream",
|
|
93
94
|
)
|
|
@@ -239,7 +240,7 @@ async def test_webtransport_tigrcorn_session_stays_open_for_delayed_lanes() -> N
|
|
|
239
240
|
proto="webtransport",
|
|
240
241
|
path="/transport/echo-live",
|
|
241
242
|
profile="bidi_stream",
|
|
242
|
-
inner_framing=
|
|
243
|
+
inner_framing=TextFramingSpec(),
|
|
243
244
|
),
|
|
244
245
|
tigrbl_exchange="bidirectional_stream",
|
|
245
246
|
)
|
|
@@ -266,20 +267,22 @@ async def test_webtransport_tigrcorn_session_stays_open_for_delayed_lanes() -> N
|
|
|
266
267
|
assert stream_sends == [
|
|
267
268
|
{
|
|
268
269
|
"type": "webtransport.stream.send",
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
270
|
+
"session_id": "session-1",
|
|
271
|
+
"stream_id": "bidi-1",
|
|
272
|
+
"stream_direction": "bidi",
|
|
273
|
+
"stream_initiator": "client",
|
|
274
|
+
"framing": "text",
|
|
275
|
+
"data": b"echo:alpha",
|
|
274
276
|
"more": False,
|
|
275
277
|
},
|
|
276
278
|
{
|
|
277
279
|
"type": "webtransport.stream.send",
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
280
|
+
"session_id": "session-1",
|
|
281
|
+
"stream_id": "bidi-2",
|
|
282
|
+
"stream_direction": "bidi",
|
|
283
|
+
"stream_initiator": "client",
|
|
284
|
+
"framing": "text",
|
|
285
|
+
"data": b"echo:beta",
|
|
283
286
|
"more": False,
|
|
284
287
|
},
|
|
285
288
|
]
|
|
@@ -297,11 +300,11 @@ async def test_webtransport_tigrcorn_session_stays_open_for_delayed_lanes() -> N
|
|
|
297
300
|
assert [
|
|
298
301
|
(item["direction"], item["type"], item.get("stream_id"), item.get("datagram_id"))
|
|
299
302
|
for item in trace
|
|
300
|
-
if item["
|
|
303
|
+
if item["phase"] == "ctx.channel_message"
|
|
301
304
|
] == [
|
|
302
305
|
("receive", "webtransport.connect", None, None),
|
|
303
|
-
("
|
|
304
|
-
("
|
|
306
|
+
("bidirectional", "webtransport.stream.receive", "bidi-1", None),
|
|
307
|
+
("bidirectional", "webtransport.stream.receive", "bidi-2", None),
|
|
305
308
|
("receive", "webtransport.datagram.receive", None, "dg-client-1"),
|
|
306
309
|
("receive", "webtransport.disconnect", None, None),
|
|
307
310
|
]
|
|
@@ -8,7 +8,7 @@ from tigrbl._spec import OpSpec
|
|
|
8
8
|
from tigrbl.runtime import events as _ev
|
|
9
9
|
from tigrbl.runtime import system as _sys
|
|
10
10
|
from tigrbl_kernel import Kernel
|
|
11
|
-
from
|
|
11
|
+
from tigrbl_concrete._mapping.invoke import invoke_op
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class _FakeDB:
|
|
@@ -20,6 +20,7 @@ from tigrbl import (
|
|
|
20
20
|
webtransport_ctx,
|
|
21
21
|
)
|
|
22
22
|
from tigrbl_core.config.constants import HOOK_DECLS_ATTR
|
|
23
|
+
from tigrbl_core._spec import JsonRpcFramingSpec, StreamFramingSpec
|
|
23
24
|
from tests.conftest import mro_collect_decorated_ops
|
|
24
25
|
|
|
25
26
|
|
|
@@ -80,10 +81,10 @@ def test_alias_surface_decorators_attach_expected_bindings() -> None:
|
|
|
80
81
|
|
|
81
82
|
specs = {spec.alias: spec for spec in mro_collect_decorated_ops(Widget)}
|
|
82
83
|
assert isinstance(specs["socket"].bindings[0], WsBindingSpec)
|
|
83
|
-
assert specs["socket"].bindings[0].framing ==
|
|
84
|
+
assert specs["socket"].bindings[0].framing == JsonRpcFramingSpec()
|
|
84
85
|
assert isinstance(specs["events"].bindings[0], SseBindingSpec)
|
|
85
86
|
assert specs["events"].exchange == "server_stream"
|
|
86
|
-
assert specs["stream"].bindings[0].framing ==
|
|
87
|
+
assert specs["stream"].bindings[0].framing == StreamFramingSpec()
|
|
87
88
|
assert specs["transport"].bindings[0].proto == "webtransport"
|
|
88
89
|
|
|
89
90
|
|
|
@@ -15,6 +15,7 @@ def test_schema_ctx_internal_binding_attaches_decl_to_schema():
|
|
|
15
15
|
assert decl.alias == "Search"
|
|
16
16
|
assert decl.kind == "in"
|
|
17
17
|
assert not hasattr(Widget, TIGRBL_SCHEMA_DECLS_ATTR)
|
|
18
|
+
assert not hasattr(Widget, "schemas")
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
def test_schema_ctx_external_binding_uses_for_argument():
|
|
@@ -27,6 +28,7 @@ def test_schema_ctx_external_binding_uses_for_argument():
|
|
|
27
28
|
|
|
28
29
|
mapping = getattr(Gadget, TIGRBL_SCHEMA_DECLS_ATTR)
|
|
29
30
|
assert mapping["Result"]["out"] is ResultSchema
|
|
31
|
+
assert Gadget.schemas.Result.out is ResultSchema
|
|
30
32
|
decl = ResultSchema.__tigrbl_schema_decl__
|
|
31
33
|
assert decl.alias == "Result"
|
|
32
34
|
assert decl.kind == "out"
|
|
@@ -66,12 +66,12 @@ def test_transport_bindings_project_canonical_exchange_family_and_subevents() ->
|
|
|
66
66
|
),
|
|
67
67
|
(
|
|
68
68
|
binding_spec.HttpStreamBindingSpec(proto="http.stream", path="/items/stream"),
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
69
|
+
{
|
|
70
|
+
"proto": "http.stream",
|
|
71
|
+
"exchange": "server_stream",
|
|
72
|
+
"framing": "stream",
|
|
73
|
+
"family": "stream",
|
|
74
|
+
},
|
|
75
75
|
),
|
|
76
76
|
(
|
|
77
77
|
binding_spec.SseBindingSpec(path="/items/events"),
|
|
@@ -96,7 +96,7 @@ def test_transport_bindings_project_canonical_exchange_family_and_subevents() ->
|
|
|
96
96
|
{
|
|
97
97
|
"proto": "webtransport",
|
|
98
98
|
"exchange": "bidirectional_stream",
|
|
99
|
-
"framing": "
|
|
99
|
+
"framing": "",
|
|
100
100
|
"family": "session",
|
|
101
101
|
},
|
|
102
102
|
),
|
|
@@ -20,6 +20,10 @@ from tigrbl import (
|
|
|
20
20
|
)
|
|
21
21
|
from tigrbl_core._spec import (
|
|
22
22
|
HttpStreamBindingSpec,
|
|
23
|
+
BytesFramingSpec,
|
|
24
|
+
JsonFramingSpec,
|
|
25
|
+
JsonRpcFramingSpec,
|
|
26
|
+
NdjsonFramingSpec,
|
|
23
27
|
OpSpec,
|
|
24
28
|
TableProfileError,
|
|
25
29
|
TableProfileSpec,
|
|
@@ -44,7 +48,7 @@ def test_binding_tokens_have_canonical_shape() -> None:
|
|
|
44
48
|
assert token.op_alias == "create"
|
|
45
49
|
assert token.op_target == "create"
|
|
46
50
|
assert token.binding_kind == "http.rest"
|
|
47
|
-
assert token.path == "/
|
|
51
|
+
assert token.path == "/resttable"
|
|
48
52
|
assert token.methods == ("POST",)
|
|
49
53
|
assert token.framing == "json"
|
|
50
54
|
assert token.exchange == "request_response"
|
|
@@ -136,9 +140,9 @@ def test_oltp_and_olap_token_details_preserve_protocol_specific_selectors() -> N
|
|
|
136
140
|
jsonrpc_olap = {token.op_alias: token for token in _tokens(JsonRpcOlapTable)}
|
|
137
141
|
|
|
138
142
|
assert rest_oltp["merge"].methods == ("PATCH",)
|
|
139
|
-
assert rest_oltp["merge"].path == "/
|
|
140
|
-
assert rest_olap["aggregate"].methods == ("
|
|
141
|
-
assert rest_olap["aggregate"].path == "/
|
|
143
|
+
assert rest_oltp["merge"].path == "/restoltptable/{item_id}"
|
|
144
|
+
assert rest_olap["aggregate"].methods == ("POST",)
|
|
145
|
+
assert rest_olap["aggregate"].path == "/restolaptable"
|
|
142
146
|
assert jsonrpc_oltp["merge"].rpc_method == "JsonRpcOltpTable.merge"
|
|
143
147
|
assert jsonrpc_oltp["merge"].framing == "jsonrpc"
|
|
144
148
|
assert jsonrpc_olap["aggregate"].rpc_method == "JsonRpcOlapTable.aggregate"
|
|
@@ -152,7 +156,7 @@ def test_dual_tokens_keep_independent_rest_path_and_jsonrpc_method() -> None:
|
|
|
152
156
|
|
|
153
157
|
merge = pairs["merge"]
|
|
154
158
|
|
|
155
|
-
assert merge["http.rest"].path == "/
|
|
159
|
+
assert merge["http.rest"].path == "/restjsonrpcoltptable/{item_id}"
|
|
156
160
|
assert merge["http.rest"].methods == ("PATCH",)
|
|
157
161
|
assert merge["http.rest"].rpc_method is None
|
|
158
162
|
assert merge["http.jsonrpc"].path == ""
|
|
@@ -189,7 +193,7 @@ def test_rest_binding_tokens_include_method_path_and_media_type() -> None:
|
|
|
189
193
|
assert tokens["update"].methods == ("PATCH",)
|
|
190
194
|
assert tokens["replace"].methods == ("PUT",)
|
|
191
195
|
assert tokens["delete"].methods == ("DELETE",)
|
|
192
|
-
assert tokens["list"].path == "/
|
|
196
|
+
assert tokens["list"].path == "/resttable"
|
|
193
197
|
assert tokens["list"].framing == "json"
|
|
194
198
|
|
|
195
199
|
|
|
@@ -205,7 +209,11 @@ def test_websocket_binding_tokens_include_framing_and_session_lanes() -> None:
|
|
|
205
209
|
tokens = _tokens(WebSocketJsonRpcTable)
|
|
206
210
|
|
|
207
211
|
assert {token.binding_kind for token in tokens} == {"ws"}
|
|
212
|
+
assert {token.protocol_kind for token in tokens} == {"ws"}
|
|
208
213
|
assert {token.framing for token in tokens} == {"jsonrpc"}
|
|
214
|
+
assert {token.framing_kind for token in tokens} == {"jsonrpc"}
|
|
215
|
+
assert {token.framing_spec for token in tokens} == {"JsonRpcFramingSpec"}
|
|
216
|
+
assert {token.required_subprotocol for token in tokens} == {"jsonrpc"}
|
|
209
217
|
assert {tuple(binding.subprotocols) for op in TableSpec.collect(WebSocketJsonRpcTable).ops for binding in op.bindings} == {
|
|
210
218
|
("jsonrpc",)
|
|
211
219
|
}
|
|
@@ -221,6 +229,73 @@ def test_webtransport_binding_tokens_include_stream_and_datagram_lanes() -> None
|
|
|
221
229
|
assert {token.op_target for token in datagram_tokens} == {"send_datagram"}
|
|
222
230
|
|
|
223
231
|
|
|
232
|
+
def test_webtransport_op_lane_binding_contract() -> None:
|
|
233
|
+
profile = TableProfileSpec(
|
|
234
|
+
kind="webtransport_ops",
|
|
235
|
+
ops=(
|
|
236
|
+
OpSpec(alias="create", target="create"),
|
|
237
|
+
OpSpec(alias="tail", target="tail"),
|
|
238
|
+
OpSpec(alias="append_chunk", target="append_chunk"),
|
|
239
|
+
OpSpec(alias="send_datagram", target="send_datagram"),
|
|
240
|
+
OpSpec(alias="open_unidi_stream", target="open_unidi_stream"),
|
|
241
|
+
),
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
lowered = lower_table_profile_bindings(WebTransportBidiTable, profile, tuple(profile.ops))
|
|
245
|
+
by_target = {op.target: op.bindings[0] for op in lowered}
|
|
246
|
+
|
|
247
|
+
assert (by_target["create"].lane, by_target["create"].inner_framing) == (
|
|
248
|
+
"bidi_stream",
|
|
249
|
+
JsonRpcFramingSpec(),
|
|
250
|
+
)
|
|
251
|
+
assert (by_target["tail"].lane, by_target["tail"].inner_framing) == (
|
|
252
|
+
"unidi_server_stream",
|
|
253
|
+
NdjsonFramingSpec(),
|
|
254
|
+
)
|
|
255
|
+
assert (
|
|
256
|
+
by_target["append_chunk"].lane,
|
|
257
|
+
by_target["append_chunk"].inner_framing,
|
|
258
|
+
) == ("unidi_client_stream", BytesFramingSpec())
|
|
259
|
+
assert (
|
|
260
|
+
by_target["send_datagram"].lane,
|
|
261
|
+
by_target["send_datagram"].inner_framing,
|
|
262
|
+
) == ("datagram", JsonFramingSpec())
|
|
263
|
+
assert (
|
|
264
|
+
by_target["open_unidi_stream"].lane,
|
|
265
|
+
by_target["open_unidi_stream"].inner_framing,
|
|
266
|
+
) == ("bidi_stream", JsonRpcFramingSpec())
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def test_canonical_binding_token_typed_framing_fields() -> None:
|
|
270
|
+
profile = TableProfileSpec(
|
|
271
|
+
kind="tests.explicit-ws-jsonrpc",
|
|
272
|
+
ops=(
|
|
273
|
+
OpSpec(
|
|
274
|
+
alias="create",
|
|
275
|
+
target="create",
|
|
276
|
+
bindings=(
|
|
277
|
+
WebTransportBindingSpec(
|
|
278
|
+
profile="bidi_stream",
|
|
279
|
+
inner_framing=JsonRpcFramingSpec(),
|
|
280
|
+
),
|
|
281
|
+
),
|
|
282
|
+
),
|
|
283
|
+
),
|
|
284
|
+
custom=True,
|
|
285
|
+
namespace="tests",
|
|
286
|
+
)
|
|
287
|
+
op = tuple(profile.ops)[0]
|
|
288
|
+
|
|
289
|
+
token = lower_binding_tokens_for_ops(WebTransportBidiTable, profile, (op,))[0]
|
|
290
|
+
|
|
291
|
+
assert token.protocol_kind == "webtransport"
|
|
292
|
+
assert token.framing == ""
|
|
293
|
+
assert token.framing_kind == ""
|
|
294
|
+
assert token.framing_spec == ""
|
|
295
|
+
assert token.lane == "bidi_stream"
|
|
296
|
+
assert token.inner_framing == "jsonrpc"
|
|
297
|
+
|
|
298
|
+
|
|
224
299
|
def test_binding_token_lowering_rejects_unsupported_transport_profile_pairs() -> None:
|
|
225
300
|
profile = make_table_profile("stream", ())
|
|
226
301
|
op = OpSpec(alias="create", target="create")
|
|
@@ -230,8 +305,8 @@ def test_binding_token_lowering_rejects_unsupported_transport_profile_pairs() ->
|
|
|
230
305
|
|
|
231
306
|
|
|
232
307
|
def test_binding_token_lowering_rejects_unsupported_framing_fallback() -> None:
|
|
233
|
-
with pytest.raises(
|
|
234
|
-
WebTransportBindingSpec(profile="datagram", inner_framing="
|
|
308
|
+
with pytest.raises(TypeError, match="FramingSpec"):
|
|
309
|
+
WebTransportBindingSpec(profile="datagram", inner_framing="ndjson")
|
|
235
310
|
|
|
236
311
|
|
|
237
312
|
def test_binding_token_lowering_reports_source_precedence() -> None:
|
|
@@ -69,7 +69,7 @@ def _require(module_name: str, attr_name: str):
|
|
|
69
69
|
{
|
|
70
70
|
"exchange": "bidirectional_stream",
|
|
71
71
|
"family": "session",
|
|
72
|
-
"framing": "
|
|
72
|
+
"framing": "",
|
|
73
73
|
"subevents": ("session.open", "stream.received", "datagram.received", "session.close"),
|
|
74
74
|
},
|
|
75
75
|
),
|
|
@@ -4,6 +4,8 @@ import importlib
|
|
|
4
4
|
|
|
5
5
|
import pytest
|
|
6
6
|
|
|
7
|
+
from tigrbl_core._spec.binding_spec import JsonRpcFramingSpec
|
|
8
|
+
|
|
7
9
|
|
|
8
10
|
def _require(module_name: str, attr_name: str):
|
|
9
11
|
try:
|
|
@@ -29,7 +31,7 @@ def _require(module_name: str, attr_name: str):
|
|
|
29
31
|
),
|
|
30
32
|
(
|
|
31
33
|
{"kind": "http.stream", "path": "/stream"},
|
|
32
|
-
{"family": "stream", "framing": "
|
|
34
|
+
{"family": "stream", "framing": "bytes", "anchors": ("transport.emit", "transport.emit_complete")},
|
|
33
35
|
),
|
|
34
36
|
(
|
|
35
37
|
{"kind": "http.sse", "path": "/events"},
|
|
@@ -41,7 +43,7 @@ def _require(module_name: str, attr_name: str):
|
|
|
41
43
|
),
|
|
42
44
|
(
|
|
43
45
|
{"kind": "webtransport", "path": "/transport"},
|
|
44
|
-
{"family": "session", "framing": "
|
|
46
|
+
{"family": "session", "framing": "", "anchors": ("transport.accept", "dispatch.subevent.derive", "transport.close")},
|
|
45
47
|
),
|
|
46
48
|
),
|
|
47
49
|
)
|
|
@@ -71,6 +73,59 @@ def test_protocol_compilation_is_deterministic_for_equivalent_bindings() -> None
|
|
|
71
73
|
assert first == second
|
|
72
74
|
|
|
73
75
|
|
|
76
|
+
def test_kernel_protocol_plan_accepts_typed_websocket_framing_spec() -> None:
|
|
77
|
+
compile_binding = _require("tigrbl_kernel.protocol_bindings", "compile_binding_protocol_plan")
|
|
78
|
+
|
|
79
|
+
plan = compile_binding(
|
|
80
|
+
op_id="Socket.rpc",
|
|
81
|
+
binding={"kind": "ws", "path": "/socket", "framing": JsonRpcFramingSpec()},
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
assert plan["framing"] == "jsonrpc"
|
|
85
|
+
assert plan["framing_kind"] == "jsonrpc"
|
|
86
|
+
assert plan["framing_spec"] == "JsonRpcFramingSpec"
|
|
87
|
+
assert plan["websocket_subprotocol"] == "jsonrpc"
|
|
88
|
+
assert "required_subprotocol" not in plan
|
|
89
|
+
assert "subprotocols" not in plan
|
|
90
|
+
assert plan["event_key_inputs"]["websocket_subprotocol"] == "jsonrpc"
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_kernel_protocol_plan_rejects_conflicting_websocket_subprotocol() -> None:
|
|
94
|
+
compile_binding = _require("tigrbl_kernel.protocol_bindings", "compile_binding_protocol_plan")
|
|
95
|
+
|
|
96
|
+
with pytest.raises(ValueError, match="conflicts with subprotocols"):
|
|
97
|
+
compile_binding(
|
|
98
|
+
op_id="Socket.rpc",
|
|
99
|
+
binding={
|
|
100
|
+
"kind": "ws",
|
|
101
|
+
"path": "/socket",
|
|
102
|
+
"framing": JsonRpcFramingSpec(),
|
|
103
|
+
"subprotocols": ("graphql-ws",),
|
|
104
|
+
},
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def test_kernel_protocol_plan_normalizes_webtransport_inner_framing_spec() -> None:
|
|
109
|
+
compile_binding = _require("tigrbl_kernel.protocol_bindings", "compile_binding_protocol_plan")
|
|
110
|
+
|
|
111
|
+
plan = compile_binding(
|
|
112
|
+
op_id="Transport.create",
|
|
113
|
+
binding={
|
|
114
|
+
"kind": "webtransport",
|
|
115
|
+
"path": "/wt",
|
|
116
|
+
"profile": "bidi_stream",
|
|
117
|
+
"inner_framing": JsonRpcFramingSpec(),
|
|
118
|
+
},
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
assert plan["framing"] == "jsonrpc"
|
|
122
|
+
assert plan["framing_kind"] == "jsonrpc"
|
|
123
|
+
assert plan["framing_spec"] == "JsonRpcFramingSpec"
|
|
124
|
+
assert plan["inner_framing"] == "jsonrpc"
|
|
125
|
+
assert plan["inner_framing_spec"] == "JsonRpcFramingSpec"
|
|
126
|
+
assert plan["event_key_inputs"]["inner_framing"] == "jsonrpc"
|
|
127
|
+
|
|
128
|
+
|
|
74
129
|
def test_protocol_compilation_emits_lifecycle_matrix_rows_for_each_subevent() -> None:
|
|
75
130
|
compile_binding = _require("tigrbl_kernel.protocol_bindings", "compile_binding_protocol_plan")
|
|
76
131
|
|
|
@@ -98,6 +153,7 @@ def test_protocol_compilation_uses_bindingspec_as_source_of_truth_not_transport_
|
|
|
98
153
|
{"kind": "http.rest", "path": "/items", "framing": "sse"},
|
|
99
154
|
{"kind": "http.jsonrpc", "path": "/rpc"},
|
|
100
155
|
{"kind": "ws", "path": "/socket", "methods": ("GET",)},
|
|
156
|
+
{"kind": "webtransport", "path": "/transport", "framing": "webtransport"},
|
|
101
157
|
{"kind": "webtransport", "path": "/transport", "exchange": "request_response"},
|
|
102
158
|
),
|
|
103
159
|
)
|