meshagent-api 0.45.7__tar.gz → 0.46.0__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.
- {meshagent_api-0.45.7/meshagent_api.egg-info → meshagent_api-0.46.0}/PKG-INFO +1 -1
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/client.py +0 -1
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/client_test.py +5 -1
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/crdt.py +1 -1
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/room_server_client.py +28 -18
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/room_server_client_test.py +237 -4
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/runtime.py +1 -1
- meshagent_api-0.46.0/meshagent/api/runtime_test.py +1447 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/schema_document.py +2 -1
- meshagent_api-0.46.0/meshagent/api/schema_document_test.py +494 -0
- meshagent_api-0.46.0/meshagent/api/version.py +1 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0/meshagent_api.egg-info}/PKG-INFO +1 -1
- meshagent_api-0.45.7/meshagent/api/runtime_test.py +0 -325
- meshagent_api-0.45.7/meshagent/api/schema_document_test.py +0 -107
- meshagent_api-0.45.7/meshagent/api/version.py +0 -1
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/LICENSE +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/MANIFEST.in +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/README.md +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/__init__.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/agent_content.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/aiohttp_redaction.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/chan.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/dataset_schema.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/entrypoint.js +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/error_codes.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/helpers.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/http.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/image_runtime.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/keys.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/managed_agents.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/managed_agents_test.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/messaging.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/messaging_test.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/oauth.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/oauth_scopes.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/oauth_scopes_test.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/participant.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/participant_token.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/participant_token_test.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/port_forward.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/port_forward_test.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/protocol.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/protocol_test.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/py.typed +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/reasoning_schema.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/registry_auth.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/room_ports.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/schema.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/schema_registry.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/schema_test.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/schema_util.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/service_spec_test.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/service_template_test.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/services.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/specs/service.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/sql.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/sql_test.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/token_test.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/urls.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/webhooks.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/webhooks_test.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/websocket_protocol.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent/api/websocket_protocol_test.py +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent_api.egg-info/SOURCES.txt +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent_api.egg-info/dependency_links.txt +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent_api.egg-info/requires.txt +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/meshagent_api.egg-info/top_level.txt +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/pyproject.toml +0 -0
- {meshagent_api-0.45.7 → meshagent_api-0.46.0}/setup.cfg +0 -0
|
@@ -916,7 +916,7 @@ def _secret_version_payload(**overrides):
|
|
|
916
916
|
"id": "version-1",
|
|
917
917
|
"secret_id": "secret-1",
|
|
918
918
|
"version": 1,
|
|
919
|
-
"encryption_key_id": "
|
|
919
|
+
"encryption_key_id": "supabase-vault",
|
|
920
920
|
"value_sha256": None,
|
|
921
921
|
"created_by_user_id": "user-1",
|
|
922
922
|
"created_by_service_account_id": None,
|
|
@@ -1059,7 +1059,9 @@ async def test_user_secret_methods_use_new_user_scoped_routes():
|
|
|
1059
1059
|
assert fetched.value_base64 == base64.b64encode(b"value").decode()
|
|
1060
1060
|
assert updated.name == "renamed"
|
|
1061
1061
|
assert versions[0].version == 1
|
|
1062
|
+
assert not hasattr(versions[0], "encryption_key_id")
|
|
1062
1063
|
assert created_version.value_sha256 == base64.b64encode(b"hash").decode()
|
|
1064
|
+
assert not hasattr(created_version, "encryption_key_id")
|
|
1063
1065
|
assert version_value == b"version value"
|
|
1064
1066
|
assert proxy_access.continuation_token == "next-grants-page"
|
|
1065
1067
|
assert proxy_access.access_grants[0].subject.type == "service_account"
|
|
@@ -1367,7 +1369,9 @@ async def test_service_account_secret_methods_use_service_account_scoped_routes(
|
|
|
1367
1369
|
assert fetched.value_base64 == base64.b64encode(b"service value").decode()
|
|
1368
1370
|
assert updated.http_only is True
|
|
1369
1371
|
assert versions[0].created_by_service_account_id == "sa-1"
|
|
1372
|
+
assert not hasattr(versions[0], "encryption_key_id")
|
|
1370
1373
|
assert created_version.id == "version-2"
|
|
1374
|
+
assert not hasattr(created_version, "encryption_key_id")
|
|
1371
1375
|
assert version_value == b"service version value"
|
|
1372
1376
|
assert pull_secrets[0].owner_service_account_id == "sa-1"
|
|
1373
1377
|
assert session.calls == [
|
|
@@ -73,7 +73,7 @@ class ServerXmlDocument:
|
|
|
73
73
|
doc["xml"] = XmlFragment()
|
|
74
74
|
self._root: XmlFragment = doc["xml"] # fragment not element
|
|
75
75
|
self.doc = doc
|
|
76
|
-
self._undo_mgr = UndoManager(self._root) if undo else None
|
|
76
|
+
self._undo_mgr = UndoManager(scopes=[self._root]) if undo else None
|
|
77
77
|
self._notify = notify_changes
|
|
78
78
|
|
|
79
79
|
# Deep observation (all element / text descendants):contentReference[oaicite:0]{index=0}
|
|
@@ -25,6 +25,7 @@ from pydantic import (
|
|
|
25
25
|
Field,
|
|
26
26
|
JsonValue,
|
|
27
27
|
ConfigDict,
|
|
28
|
+
PositiveInt,
|
|
28
29
|
ValidationError,
|
|
29
30
|
field_validator,
|
|
30
31
|
)
|
|
@@ -3152,6 +3153,18 @@ class ServiceRuntimeEvent(BaseModel):
|
|
|
3152
3153
|
last_timestamp: float
|
|
3153
3154
|
|
|
3154
3155
|
|
|
3156
|
+
class ServicePortRuntimeState(BaseModel):
|
|
3157
|
+
num: Literal["*"] | PositiveInt
|
|
3158
|
+
liveness: Optional[str] = None
|
|
3159
|
+
liveness_status: Literal["not_configured", "not_ready", "ready"] = "not_configured"
|
|
3160
|
+
last_checked_at: Optional[float] = None
|
|
3161
|
+
last_error: Optional[str] = None
|
|
3162
|
+
|
|
3163
|
+
|
|
3164
|
+
class ServiceRuntimeStatus(BaseModel):
|
|
3165
|
+
ports: list[ServicePortRuntimeState] = Field(default_factory=list)
|
|
3166
|
+
|
|
3167
|
+
|
|
3155
3168
|
class ServiceRuntimeState(BaseModel):
|
|
3156
3169
|
service_id: str
|
|
3157
3170
|
state: str
|
|
@@ -3163,6 +3176,7 @@ class ServiceRuntimeState(BaseModel):
|
|
|
3163
3176
|
last_exit_at: Optional[float] = None
|
|
3164
3177
|
last_start_error: Optional[str] = None
|
|
3165
3178
|
last_start_error_at: Optional[float] = None
|
|
3179
|
+
status: ServiceRuntimeStatus = Field(default_factory=ServiceRuntimeStatus)
|
|
3166
3180
|
events: list[ServiceRuntimeEvent] = Field(default_factory=list)
|
|
3167
3181
|
|
|
3168
3182
|
|
|
@@ -3198,15 +3212,6 @@ class ServicesClient:
|
|
|
3198
3212
|
|
|
3199
3213
|
async def list(
|
|
3200
3214
|
self,
|
|
3201
|
-
) -> List[ServiceSpec]:
|
|
3202
|
-
"""
|
|
3203
|
-
Fetch a list of services.
|
|
3204
|
-
"""
|
|
3205
|
-
|
|
3206
|
-
return (await self.list_with_state()).services
|
|
3207
|
-
|
|
3208
|
-
async def list_with_state(
|
|
3209
|
-
self,
|
|
3210
3215
|
) -> ListServicesResult:
|
|
3211
3216
|
"""
|
|
3212
3217
|
Fetch a list of services plus runtime state details from the service controller.
|
|
@@ -5596,54 +5601,56 @@ class SqliteDatabaseDetails(BaseModel):
|
|
|
5596
5601
|
tables: int | None = None
|
|
5597
5602
|
size_bytes: int | None = None
|
|
5598
5603
|
|
|
5604
|
+
model_config = ConfigDict(extra="forbid", strict=True)
|
|
5605
|
+
|
|
5599
5606
|
|
|
5600
5607
|
class _SqliteListDatabasesResponse(BaseModel):
|
|
5601
5608
|
databases: list[str]
|
|
5602
5609
|
|
|
5603
|
-
model_config = ConfigDict(extra="forbid")
|
|
5610
|
+
model_config = ConfigDict(extra="forbid", strict=True)
|
|
5604
5611
|
|
|
5605
5612
|
|
|
5606
5613
|
class _SqliteListTablesResponse(BaseModel):
|
|
5607
5614
|
tables: list[str]
|
|
5608
5615
|
|
|
5609
|
-
model_config = ConfigDict(extra="forbid")
|
|
5616
|
+
model_config = ConfigDict(extra="forbid", strict=True)
|
|
5610
5617
|
|
|
5611
5618
|
|
|
5612
5619
|
class _SqliteRowsAffectedResponse(BaseModel):
|
|
5613
5620
|
rows_affected: int
|
|
5614
5621
|
|
|
5615
|
-
model_config = ConfigDict(extra="forbid")
|
|
5622
|
+
model_config = ConfigDict(extra="forbid", strict=True)
|
|
5616
5623
|
|
|
5617
5624
|
|
|
5618
5625
|
class _SqliteCountResponse(BaseModel):
|
|
5619
5626
|
count: int
|
|
5620
5627
|
|
|
5621
|
-
model_config = ConfigDict(extra="forbid")
|
|
5628
|
+
model_config = ConfigDict(extra="forbid", strict=True)
|
|
5622
5629
|
|
|
5623
5630
|
|
|
5624
5631
|
class _SqliteStatementResponse(_SqliteRowsAffectedResponse):
|
|
5625
5632
|
kind: Literal["statement"]
|
|
5626
5633
|
|
|
5627
|
-
model_config = ConfigDict(extra="forbid")
|
|
5634
|
+
model_config = ConfigDict(extra="forbid", strict=True)
|
|
5628
5635
|
|
|
5629
5636
|
|
|
5630
5637
|
class _SqliteCancelResponse(BaseModel):
|
|
5631
5638
|
status: Literal["cancelled", "cancelling", "not_cancellable"]
|
|
5632
5639
|
|
|
5633
|
-
model_config = ConfigDict(extra="forbid")
|
|
5640
|
+
model_config = ConfigDict(extra="forbid", strict=True)
|
|
5634
5641
|
|
|
5635
5642
|
|
|
5636
5643
|
class _SqliteQueryHeaders(BaseModel):
|
|
5637
5644
|
kind: Literal["query"]
|
|
5638
5645
|
query_id: str
|
|
5639
5646
|
|
|
5640
|
-
model_config = ConfigDict(extra="forbid")
|
|
5647
|
+
model_config = ConfigDict(extra="forbid", strict=True)
|
|
5641
5648
|
|
|
5642
5649
|
|
|
5643
5650
|
class _SqliteKindHeaders(BaseModel):
|
|
5644
5651
|
kind: str
|
|
5645
5652
|
|
|
5646
|
-
model_config = ConfigDict(extra="forbid")
|
|
5653
|
+
model_config = ConfigDict(extra="forbid", strict=True)
|
|
5647
5654
|
|
|
5648
5655
|
|
|
5649
5656
|
@dataclass(frozen=True, slots=True)
|
|
@@ -7757,7 +7764,10 @@ class SqliteClient:
|
|
|
7757
7764
|
)
|
|
7758
7765
|
if not isinstance(response, JsonContent):
|
|
7759
7766
|
raise self._unexpected_response_error(operation="inspect_database")
|
|
7760
|
-
|
|
7767
|
+
try:
|
|
7768
|
+
return SqliteDatabaseDetails.model_validate(response.json)
|
|
7769
|
+
except ValidationError as exc:
|
|
7770
|
+
raise self._unexpected_response_error(operation="inspect_database") from exc
|
|
7761
7771
|
|
|
7762
7772
|
async def list_tables(
|
|
7763
7773
|
self, *, database: str, namespace: Optional[list[str]] = None
|
|
@@ -2577,6 +2577,64 @@ async def test_memory_client_list_validates_typed_response() -> None:
|
|
|
2577
2577
|
assert ex.value.code == ErrorCode.UNEXPECTED_RESPONSE_TYPE
|
|
2578
2578
|
|
|
2579
2579
|
|
|
2580
|
+
def test_memory_ingest_request_temperature_uses_pydantic_float_coercion() -> None:
|
|
2581
|
+
request_model = room_server_client._MemoryIngestTextRequest
|
|
2582
|
+
|
|
2583
|
+
assert (
|
|
2584
|
+
request_model.model_validate(
|
|
2585
|
+
{"name": "graph", "text": "Alice", "llm_temperature": "0.4"}
|
|
2586
|
+
).llm_temperature
|
|
2587
|
+
== 0.4
|
|
2588
|
+
)
|
|
2589
|
+
assert (
|
|
2590
|
+
request_model.model_validate(
|
|
2591
|
+
{"name": "graph", "text": "Alice", "llm_temperature": 1}
|
|
2592
|
+
).llm_temperature
|
|
2593
|
+
== 1.0
|
|
2594
|
+
)
|
|
2595
|
+
assert (
|
|
2596
|
+
request_model.model_validate(
|
|
2597
|
+
{"name": "graph", "text": "Alice", "llm_temperature": True}
|
|
2598
|
+
).llm_temperature
|
|
2599
|
+
== 1.0
|
|
2600
|
+
)
|
|
2601
|
+
assert (
|
|
2602
|
+
request_model.model_validate(
|
|
2603
|
+
{"name": "graph", "text": "Alice", "llm_temperature": None}
|
|
2604
|
+
).llm_temperature
|
|
2605
|
+
is None
|
|
2606
|
+
)
|
|
2607
|
+
|
|
2608
|
+
with pytest.raises(ValidationError):
|
|
2609
|
+
request_model.model_validate(
|
|
2610
|
+
{"name": "graph", "text": "Alice", "llm_temperature": ""}
|
|
2611
|
+
)
|
|
2612
|
+
|
|
2613
|
+
|
|
2614
|
+
def test_memory_integer_limits_use_pydantic_int_coercion() -> None:
|
|
2615
|
+
ingest_request_model = room_server_client._MemoryIngestFromTableRequest
|
|
2616
|
+
recall_request_model = room_server_client._MemoryRecallRequest
|
|
2617
|
+
|
|
2618
|
+
for value in ["5", "5.0", 5.0, True, False, -1, "-1.0", None]:
|
|
2619
|
+
assert ingest_request_model.model_validate(
|
|
2620
|
+
{"name": "graph", "table": "Entity", "limit": value}
|
|
2621
|
+
).limit == (value if value is None else int(float(value)))
|
|
2622
|
+
|
|
2623
|
+
for value in ["5", "5.0", 5.0, True, False, -1, "-1.0"]:
|
|
2624
|
+
assert recall_request_model.model_validate(
|
|
2625
|
+
{"name": "graph", "query": "Alice", "limit": value}
|
|
2626
|
+
).limit == int(float(value))
|
|
2627
|
+
|
|
2628
|
+
with pytest.raises(ValidationError):
|
|
2629
|
+
ingest_request_model.model_validate(
|
|
2630
|
+
{"name": "graph", "table": "Entity", "limit": "5.5"}
|
|
2631
|
+
)
|
|
2632
|
+
with pytest.raises(ValidationError):
|
|
2633
|
+
recall_request_model.model_validate(
|
|
2634
|
+
{"name": "graph", "query": "Alice", "limit": None}
|
|
2635
|
+
)
|
|
2636
|
+
|
|
2637
|
+
|
|
2580
2638
|
@pytest.mark.asyncio
|
|
2581
2639
|
async def test_memory_client_uses_room_invoke_for_commands() -> None:
|
|
2582
2640
|
class _FakeMemoryRoom(_FakeRoom):
|
|
@@ -3644,6 +3702,20 @@ async def test_sqlite_client_unexpected_response_uses_error_code() -> None:
|
|
|
3644
3702
|
|
|
3645
3703
|
@pytest.mark.asyncio
|
|
3646
3704
|
async def test_sqlite_client_validates_typed_responses() -> None:
|
|
3705
|
+
async def assert_unexpected_response(call: Callable[[], object]) -> None:
|
|
3706
|
+
with pytest.raises(
|
|
3707
|
+
RoomException,
|
|
3708
|
+
match=r"unexpected return type from sqlite\.",
|
|
3709
|
+
) as ex:
|
|
3710
|
+
result = call()
|
|
3711
|
+
if hasattr(result, "__await__"):
|
|
3712
|
+
await result
|
|
3713
|
+
else:
|
|
3714
|
+
async for _ in result: # type: ignore[union-attr]
|
|
3715
|
+
pass
|
|
3716
|
+
|
|
3717
|
+
assert ex.value.code == ErrorCode.UNEXPECTED_RESPONSE_TYPE
|
|
3718
|
+
|
|
3647
3719
|
malformed_databases_client = SqliteClient(
|
|
3648
3720
|
room=_StaticInvokeRoom(JsonContent(json={"databases": ["app", 2]}))
|
|
3649
3721
|
) # type: ignore[arg-type]
|
|
@@ -3666,6 +3738,26 @@ async def test_sqlite_client_validates_typed_responses() -> None:
|
|
|
3666
3738
|
|
|
3667
3739
|
assert ex.value.code == ErrorCode.UNEXPECTED_RESPONSE_TYPE
|
|
3668
3740
|
|
|
3741
|
+
malformed_inspect_database_client = SqliteClient(
|
|
3742
|
+
room=_StaticInvokeRoom(
|
|
3743
|
+
JsonContent(
|
|
3744
|
+
json={
|
|
3745
|
+
"name": "app",
|
|
3746
|
+
"namespace": ["team"],
|
|
3747
|
+
"tables": "1",
|
|
3748
|
+
"size_bytes": 1024,
|
|
3749
|
+
}
|
|
3750
|
+
)
|
|
3751
|
+
)
|
|
3752
|
+
) # type: ignore[arg-type]
|
|
3753
|
+
|
|
3754
|
+
with pytest.raises(
|
|
3755
|
+
RoomException, match="unexpected return type from sqlite.inspect_database"
|
|
3756
|
+
) as ex:
|
|
3757
|
+
await malformed_inspect_database_client.inspect_database(name="app")
|
|
3758
|
+
|
|
3759
|
+
assert ex.value.code == ErrorCode.UNEXPECTED_RESPONSE_TYPE
|
|
3760
|
+
|
|
3669
3761
|
malformed_query_client = SqliteClient(
|
|
3670
3762
|
room=_StaticInvokeRoom(
|
|
3671
3763
|
BinaryContent(
|
|
@@ -3687,6 +3779,147 @@ async def test_sqlite_client_validates_typed_responses() -> None:
|
|
|
3687
3779
|
|
|
3688
3780
|
assert ex.value.code == ErrorCode.UNEXPECTED_RESPONSE_TYPE
|
|
3689
3781
|
|
|
3782
|
+
await assert_unexpected_response(
|
|
3783
|
+
lambda: (
|
|
3784
|
+
SqliteClient( # type: ignore[arg-type]
|
|
3785
|
+
room=_StaticInvokeRoom(JsonContent(json={"count": "1"}))
|
|
3786
|
+
)
|
|
3787
|
+
.database("app")
|
|
3788
|
+
.count(table="records")
|
|
3789
|
+
)
|
|
3790
|
+
)
|
|
3791
|
+
await assert_unexpected_response(
|
|
3792
|
+
lambda: (
|
|
3793
|
+
SqliteClient( # type: ignore[arg-type]
|
|
3794
|
+
room=_StaticInvokeRoom(JsonContent(json={"rows_affected": "2"}))
|
|
3795
|
+
)
|
|
3796
|
+
.database("app")
|
|
3797
|
+
.update(table="records", where="id = ?", values={"name": "x"})
|
|
3798
|
+
)
|
|
3799
|
+
)
|
|
3800
|
+
await assert_unexpected_response(
|
|
3801
|
+
lambda: (
|
|
3802
|
+
SqliteClient( # type: ignore[arg-type]
|
|
3803
|
+
room=_StaticInvokeRoom(JsonContent(json={"rows_affected": "2"}))
|
|
3804
|
+
)
|
|
3805
|
+
.database("app")
|
|
3806
|
+
.delete(table="records", where="id = ?")
|
|
3807
|
+
)
|
|
3808
|
+
)
|
|
3809
|
+
await assert_unexpected_response(
|
|
3810
|
+
lambda: (
|
|
3811
|
+
SqliteClient( # type: ignore[arg-type]
|
|
3812
|
+
room=_StaticInvokeRoom(
|
|
3813
|
+
JsonContent(json={"kind": "statement", "rows_affected": "3"})
|
|
3814
|
+
)
|
|
3815
|
+
)
|
|
3816
|
+
.database("app")
|
|
3817
|
+
.execute_sql(query="DELETE FROM records")
|
|
3818
|
+
)
|
|
3819
|
+
)
|
|
3820
|
+
await assert_unexpected_response(
|
|
3821
|
+
lambda: (
|
|
3822
|
+
SqliteClient( # type: ignore[arg-type]
|
|
3823
|
+
room=_StaticInvokeRoom(JsonContent(json={"rows_affected": "3"}))
|
|
3824
|
+
)
|
|
3825
|
+
.database("app")
|
|
3826
|
+
.execute_sql_statement(query="DELETE FROM records")
|
|
3827
|
+
)
|
|
3828
|
+
)
|
|
3829
|
+
await assert_unexpected_response(
|
|
3830
|
+
lambda: SqliteClient( # type: ignore[arg-type]
|
|
3831
|
+
room=_StaticInvokeRoom(JsonContent(json={"status": "bad"}))
|
|
3832
|
+
).cancel_sql_query(query_id="sqlite-query-1")
|
|
3833
|
+
)
|
|
3834
|
+
await assert_unexpected_response(
|
|
3835
|
+
lambda: (
|
|
3836
|
+
SqliteClient( # type: ignore[arg-type]
|
|
3837
|
+
room=_StaticInvokeRoom(
|
|
3838
|
+
BinaryContent(
|
|
3839
|
+
data=room_server_client._schema_to_arrow_ipc(
|
|
3840
|
+
pa.schema([pa.field("id", pa.int64())])
|
|
3841
|
+
),
|
|
3842
|
+
headers={"kind": "query", "query_id": 123},
|
|
3843
|
+
)
|
|
3844
|
+
)
|
|
3845
|
+
)
|
|
3846
|
+
.database("app")
|
|
3847
|
+
.execute_sql(query="SELECT id FROM records")
|
|
3848
|
+
)
|
|
3849
|
+
)
|
|
3850
|
+
|
|
3851
|
+
|
|
3852
|
+
@pytest.mark.asyncio
|
|
3853
|
+
async def test_sqlite_client_propagates_stream_errors_and_rejects_malformed_chunks() -> (
|
|
3854
|
+
None
|
|
3855
|
+
):
|
|
3856
|
+
class _StaticStreamInvokeRoom:
|
|
3857
|
+
def __init__(self, chunks: list[Content]):
|
|
3858
|
+
self.chunks = chunks
|
|
3859
|
+
|
|
3860
|
+
async def invoke(self, **kwargs) -> AsyncIterable[Content]:
|
|
3861
|
+
del kwargs
|
|
3862
|
+
|
|
3863
|
+
async def stream() -> AsyncIterator[Content]:
|
|
3864
|
+
for chunk in self.chunks:
|
|
3865
|
+
yield chunk
|
|
3866
|
+
|
|
3867
|
+
return stream()
|
|
3868
|
+
|
|
3869
|
+
malformed_write_client = SqliteClient(
|
|
3870
|
+
room=_StaticStreamInvokeRoom(
|
|
3871
|
+
[BinaryContent(data=b"", headers={"kind": "data"})]
|
|
3872
|
+
)
|
|
3873
|
+
) # type: ignore[arg-type]
|
|
3874
|
+
|
|
3875
|
+
with pytest.raises(
|
|
3876
|
+
RoomException, match="unexpected return type from sqlite.create_table"
|
|
3877
|
+
) as ex:
|
|
3878
|
+
await malformed_write_client.database("app").create_table_from_data(
|
|
3879
|
+
name="records",
|
|
3880
|
+
data=pa.table({"id": [1]}),
|
|
3881
|
+
)
|
|
3882
|
+
|
|
3883
|
+
assert ex.value.code == ErrorCode.UNEXPECTED_RESPONSE_TYPE
|
|
3884
|
+
|
|
3885
|
+
write_error_client = SqliteClient(
|
|
3886
|
+
room=_StaticStreamInvokeRoom(
|
|
3887
|
+
[ErrorContent(text="write failed", code=ErrorCode.INVALID_REQUEST)]
|
|
3888
|
+
)
|
|
3889
|
+
) # type: ignore[arg-type]
|
|
3890
|
+
|
|
3891
|
+
with pytest.raises(RoomException, match="write failed") as ex:
|
|
3892
|
+
await write_error_client.database("app").insert(
|
|
3893
|
+
table="records",
|
|
3894
|
+
records=pa.table({"id": [1]}),
|
|
3895
|
+
)
|
|
3896
|
+
|
|
3897
|
+
assert ex.value.code == ErrorCode.INVALID_REQUEST
|
|
3898
|
+
|
|
3899
|
+
malformed_read_client = SqliteClient(
|
|
3900
|
+
room=_StaticStreamInvokeRoom(
|
|
3901
|
+
[BinaryContent(data=b"", headers={"kind": "pull"})]
|
|
3902
|
+
)
|
|
3903
|
+
) # type: ignore[arg-type]
|
|
3904
|
+
|
|
3905
|
+
with pytest.raises(
|
|
3906
|
+
RoomException, match="unexpected return type from sqlite.search"
|
|
3907
|
+
) as ex:
|
|
3908
|
+
await malformed_read_client.database("app").search(table="records")
|
|
3909
|
+
|
|
3910
|
+
assert ex.value.code == ErrorCode.UNEXPECTED_RESPONSE_TYPE
|
|
3911
|
+
|
|
3912
|
+
read_error_client = SqliteClient(
|
|
3913
|
+
room=_StaticStreamInvokeRoom(
|
|
3914
|
+
[ErrorContent(text="read failed", code=ErrorCode.INVALID_REQUEST)]
|
|
3915
|
+
)
|
|
3916
|
+
) # type: ignore[arg-type]
|
|
3917
|
+
|
|
3918
|
+
with pytest.raises(RoomException, match="read failed") as ex:
|
|
3919
|
+
await read_error_client.database("app").search(table="records")
|
|
3920
|
+
|
|
3921
|
+
assert ex.value.code == ErrorCode.INVALID_REQUEST
|
|
3922
|
+
|
|
3690
3923
|
|
|
3691
3924
|
@pytest.mark.asyncio
|
|
3692
3925
|
@pytest.mark.parametrize(
|
|
@@ -4112,7 +4345,7 @@ async def test_services_client_unexpected_response_uses_error_code() -> None:
|
|
|
4112
4345
|
with pytest.raises(
|
|
4113
4346
|
RoomException, match="unexpected return type from services.list"
|
|
4114
4347
|
) as ex:
|
|
4115
|
-
await client.
|
|
4348
|
+
await client.list()
|
|
4116
4349
|
|
|
4117
4350
|
assert ex.value.code == ErrorCode.UNEXPECTED_RESPONSE_TYPE
|
|
4118
4351
|
|
|
@@ -4174,7 +4407,7 @@ async def test_services_client_uses_room_invoke_and_translates_service_states()
|
|
|
4174
4407
|
room = _InvokeRoom()
|
|
4175
4408
|
client = ServicesClient(room=room) # type: ignore[arg-type]
|
|
4176
4409
|
|
|
4177
|
-
result = await client.
|
|
4410
|
+
result = await client.list()
|
|
4178
4411
|
|
|
4179
4412
|
assert room.calls == [
|
|
4180
4413
|
{
|
|
@@ -4212,7 +4445,7 @@ async def test_services_client_defaults_missing_service_events() -> None:
|
|
|
4212
4445
|
)
|
|
4213
4446
|
) # type: ignore[arg-type]
|
|
4214
4447
|
|
|
4215
|
-
result = await client.
|
|
4448
|
+
result = await client.list()
|
|
4216
4449
|
|
|
4217
4450
|
assert result.service_states["svc-1"].restart_count == 0
|
|
4218
4451
|
assert result.service_states["svc-1"].last_start_error is None
|
|
@@ -4288,7 +4521,7 @@ async def test_services_client_rejects_nested_service_spec_invalid_payloads() ->
|
|
|
4288
4521
|
with pytest.raises(
|
|
4289
4522
|
RoomException, match="unexpected return type from services.list"
|
|
4290
4523
|
) as ex:
|
|
4291
|
-
await client.
|
|
4524
|
+
await client.list()
|
|
4292
4525
|
|
|
4293
4526
|
assert ex.value.code == ErrorCode.UNEXPECTED_RESPONSE_TYPE
|
|
4294
4527
|
|