query parsing and diagnostics

This commit is contained in:
2026-05-14 02:20:17 +02:00
parent a152968466
commit e5197e15e2
13 changed files with 777 additions and 90 deletions

View File

@@ -38,7 +38,7 @@
"capability_group": "discovery-query",
"tck_groups": ["QueryTestGroup"],
"expected": "partial-pass",
"known_gaps": ["full_cmis_sql_joins", "order_by"]
"known_gaps": ["full_cmis_sql_joins", "custom_order_by"]
},
{
"capability_group": "relationships",

View File

@@ -129,6 +129,7 @@ def test_cmis_repository_info_and_type_definitions(cmis_client) -> None:
assert repository["repositoryUrl"].endswith("/cmis/readonly-browser/browser")
assert repository["rootFolderUrl"].endswith("/cmis/readonly-browser/browser/root")
assert repository["capabilities"]["capabilityQuery"] == "metadataonly"
assert repository["capabilities"]["capabilityOrderBy"] == "common"
assert repository["capabilities"]["capabilityGetDescendants"] is False
assert browser_types["types"][0]["id"] == "cmis:document"
assert "propertyDefinitions" not in browser_types["types"][0]
@@ -166,10 +167,18 @@ def test_cmis_readonly_children_object_content_query_relationships_and_changes(c
"/cmis/readonly-browser/browser/query",
params={"q": "SELECT * FROM cmis:document"},
).json()
filtered_query = cmis_client.get(
"/cmis/readonly-browser/browser/query",
params={"q": "SELECT * FROM cmis:document WHERE kontextual:topics IN ('cmis') ORDER BY cmis:name ASC"},
).json()
relationships = cmis_client.get(
"/cmis/readonly-browser/browser/relationships",
params={"object_id": "cmis:asset:asset-source"},
).json()
target_relationships = cmis_client.get(
"/cmis/readonly-browser/browser/relationships",
params={"object_id": "cmis:asset:asset-public", "relationshipDirection": "target"},
).json()
changes = cmis_client.get("/cmis/readonly-browser/browser/changes").json()
root_ids = {item["object_id"] for item in root_children["objects"]}
@@ -182,8 +191,11 @@ def test_cmis_readonly_children_object_content_query_relationships_and_changes(c
assert "get_content_stream" in object_response["allowable_actions"]
assert content["mime_type"] == "text/markdown"
assert query["total_num_items"] == children["total_num_items"]
assert [item["object_id"] for item in filtered_query["results"]] == ["cmis:asset:asset-source"]
assert relationships["count"] == 1
assert relationships["items"][0]["properties"]["cmis:targetId"] == "cmis:asset:asset-public"
assert relationships["items"][0]["properties"]["kontextual:relationshipId"]
assert target_relationships["count"] == 1
assert changes["total_num_items"] >= 3
@@ -213,13 +225,24 @@ def test_cmis_query_reports_unsupported_subset_diagnostics(cmis_client) -> None:
params={"q": "SELECT * FROM cmis:document JOIN cmis:relationship"},
)
assert response.status_code == 400
assert response.json()["exception"] == "invalidArgument"
assert response.json()["details"]["supported"] == [
"SELECT * FROM cmis:document",
"SELECT * FROM kontextual:document",
assert response.status_code == 405
assert response.json()["exception"] == "notSupported"
assert "SELECT * FROM cmis:document" in response.json()["details"]["supported"]
assert response.json()["details"]["orderable_fields"] == [
"cmis:creationDate",
"cmis:lastModificationDate",
"cmis:name",
"cmis:objectId",
]
descendants = cmis_client.get(
"/cmis/readonly-browser/browser/root",
params={"cmisselector": "descendants"},
)
assert descendants.status_code == 405
assert descendants.json()["exception"] == "notSupported"
assert descendants.json()["details"]["unsupported_feature"] == "get_descendants"
def test_cmis_governed_authoring_routes_allow_selected_mutations(cmis_client) -> None:
created = cmis_client.post(

View File

@@ -65,7 +65,7 @@ def test_repository_info_uses_complete_conservative_cmis_11_capability_flags() -
assert capabilities["capability_renditions"] == "none"
assert capabilities["capability_get_descendants"] is False
assert capabilities["capability_get_folder_tree"] is False
assert capabilities["capability_order_by"] == "none"
assert capabilities["capability_order_by"] == "common"
assert capabilities["capability_multifiling"] is False
assert capabilities["capability_unfiling"] is False
assert capabilities["capability_version_specific_filing"] is False

View File

@@ -0,0 +1,72 @@
from __future__ import annotations
import os
from time import perf_counter
import pytest
from kontextual_engine import Classification, ServiceRuntime, Sensitivity
from kontextual_engine.adapters.memory import InMemoryAssetRegistryRepository
pytestmark = [pytest.mark.cmis, pytest.mark.capacity]
@pytest.mark.skipif(
os.getenv("KONTEXTUAL_RUN_CAPACITY") != "1",
reason="Set KONTEXTUAL_RUN_CAPACITY=1 to run CMIS read-side capacity probes.",
)
def test_cmis_read_side_query_and_relationship_capacity_probe() -> None:
runtime = ServiceRuntime(repository=InMemoryAssetRegistryRepository())
context = runtime.operation_context(actor_id="cmis-capacity", correlation_id="corr-cmis-capacity")
asset_count = 400
relationship_count = 250
for index in range(asset_count):
runtime.asset_service().create_asset(
f"Capacity Asset {index:04d}",
Classification(
asset_type="document",
sensitivity=Sensitivity.PUBLIC if index % 5 == 0 else Sensitivity.INTERNAL,
owner=f"capacity-owner-{index % 7}",
topics=("capacity", f"group-{index % 11}"),
),
context,
asset_id=f"asset-capacity-{index:04d}",
)
for index in range(relationship_count):
runtime.create_relationship(
{
"source_asset_id": f"asset-capacity-{index:04d}",
"target_id": f"asset-capacity-{asset_count - 1:04d}",
"predicate": "capacity-related-to",
"target_kind": "asset",
"confidence": 0.9,
},
context,
)
query_started = perf_counter()
query = runtime.cmis_query(
"readonly-browser",
"SELECT * FROM cmis:document WHERE kontextual:topics IN ('capacity') ORDER BY cmis:name DESC",
context,
max_items=25,
)
query_seconds = perf_counter() - query_started
relationships_started = perf_counter()
relationships = runtime.cmis_relationships(
"readonly-browser",
context,
object_id=f"cmis:asset:asset-capacity-{asset_count - 1:04d}",
relationship_direction="target",
)
relationships_seconds = perf_counter() - relationships_started
assert query["total_num_items"] == asset_count
assert query["num_items"] == 25
assert relationships["count"] == relationship_count
assert query_seconds < 5.0
assert relationships_seconds < 3.0

View File

@@ -110,17 +110,48 @@ def test_runtime_cmis_browser_content_query_relationships_and_changes(cmis_runti
content = runtime.cmis_content_stream("readonly-browser", "cmis:asset:asset-runtime-source", context)
query = runtime.cmis_query("readonly-browser", "SELECT * FROM cmis:document", context)
filtered_query = runtime.cmis_query(
"readonly-browser",
"SELECT * FROM cmis:document WHERE kontextual:sensitivity = 'internal' "
"AND kontextual:topics IN ('integration') ORDER BY cmis:name DESC",
context,
)
like_query = runtime.cmis_query(
"readonly-browser",
"SELECT * FROM cmis:document WHERE cmis:name LIKE 'Runtime %' ORDER BY cmis:name DESC",
context,
)
relationships = runtime.cmis_relationships(
"readonly-browser",
context,
object_id="cmis:asset:asset-runtime-source",
)
target_relationships = runtime.cmis_relationships(
"readonly-browser",
context,
object_id="cmis:asset:asset-runtime-public",
relationship_direction="target",
)
either_relationships = runtime.cmis_relationships(
"readonly-browser",
context,
object_id="cmis:asset:asset-runtime-public",
relationship_direction="either",
)
changes = runtime.cmis_change_log("readonly-browser", context)
assert content["mime_type"] in {"text/plain", "text/markdown"}
assert query["total_num_items"] == 2
assert [item["object_id"] for item in filtered_query["results"]] == [
"cmis:asset:asset-runtime-source"
]
assert [item["name"] for item in like_query["results"]] == ["Runtime Source", "Runtime Public"]
assert relationships["count"] == 1
assert relationships["items"][0]["properties"]["cmis:targetId"] == "cmis:asset:asset-runtime-public"
assert relationships["items"][0]["properties"]["cmis:changeToken"].startswith("relationship:")
assert relationships["items"][0]["properties"]["kontextual:direction"] == "outbound"
assert target_relationships["count"] == 1
assert either_relationships["count"] == 1
assert changes["total_num_items"] >= 3
assert all(change["object_id"] != "cmis:asset:asset-runtime-confidential" for change in changes["changes"])
@@ -137,6 +168,16 @@ def test_runtime_cmis_browser_rejects_unsupported_query_subset(cmis_runtime) ->
assert "Unsupported CMIS query subset" in str(exc_info.value)
with pytest.raises(Exception) as direction_exc:
runtime.cmis_relationships(
"readonly-browser",
context,
object_id="cmis:asset:asset-runtime-source",
relationship_direction="both",
)
assert "Unsupported CMIS relationship direction" in str(direction_exc.value)
def test_runtime_cmis_governed_authoring_allows_selected_mutations(cmis_runtime) -> None:
runtime, context = cmis_runtime
@@ -335,6 +376,10 @@ def test_runtime_cmis_acl_projection_and_redaction(cmis_runtime) -> None:
assert public_acl["is_exact"] is True
assert {entry["principal_id"] for entry in public_acl["aces"]} == {"cmis-runtime", "anyone"}
assert public_acl["policy_authority"] == "kontextual-policy-gateway"
assert public_acl["permission_mapping"]["cmis:read"] == "asset visible through profile policy"
assert {entry["principal_kind"] for entry in public_acl["aces"]} == {"human", "well_known"}
assert {entry["inherited"] for entry in public_acl["aces"]} == {False, True}
assert ["cmis:read", "cmis:write", "cmis:delete"] in [
entry["permissions"] for entry in internal_acl["aces"]
]