fix(http/test-helper): branch on op_type in full_registry_with_ops (ADR-049 kind validation)

The to_mcp test helper full_registry_with_ops always registered ops
with HandlerKind::Once(make_echo_handler()) regardless of op_type.
When the search_returns_access_control_filtered_ops_excluding_subscriptions
test passed OperationType::Subscription for "events/stream", the
registry's kind validation (tightened in commit 9c81129, ADR-049)
rejected it with "handler kind mismatch: Subscription requires
HandlerKind::Stream (got HandlerKind::Once)" — panicking in
register().unwrap() before the test could run.

This was a pre-existing test-helper bug (predates Phase 6; verified by
stashing Phase 6 and reproducing on the develop baseline) but it
blocked Phase 9's 'Done when' criterion (cargo test -p alknet-http
passes).

Fix: added a handler_kind_for(op_type) helper that branches on op_type
(HandlerKind::Stream(make_echo_streaming_handler()) for Subscription,
HandlerKind::Once(make_echo_handler()) for Query/Mutation) and used
it in both register loops of full_registry_with_ops. The streaming
echo handler yields the input back as a single call.responded frame —
sufficient because the test only verifies that the MCP search tool
*excludes* Subscription ops from its listing; it never invokes the
handler.

Result: cargo test --workspace --all-features is fully green (1008
tests, 0 failures). Phase 9's 'Done when' criterion is met. The
findings doc's Phase 9 entry is updated to record the fix.

Closes Phase 9.
This commit is contained in:
glm-5.2 committed 2026-07-18 16:14:39 +00:00
1 parent 5902c8aca9
commit 859ad35896
2 files changed
+51 -20

No files matched your search

+34 -3
View File
@@ -432,7 +432,8 @@ mod tests {
services_list_handler, services_list_spec, services_schema_handler, services_schema_spec,
};
use alknet_call::registry::registration::{
make_handler, HandlerKind, HandlerRegistration, OperationProvenance, OperationRegistry,
make_handler, make_streaming_handler, HandlerKind, HandlerRegistration,
OperationProvenance, OperationRegistry,
};
use alknet_call::registry::spec::{AccessControl, OperationSpec, OperationType, Visibility};
use alknet_core::auth::{AuthToken, Identity, IdentityProvider};
@@ -498,6 +499,36 @@ mod tests {
)
}
/// A streaming echo handler: yields the input back as a single
/// `call.responded` frame, then the stream ends. Used for
/// `OperationType::Subscription` ops in `full_registry_with_ops` —
/// the registry's kind validation (ADR-049) requires
/// `HandlerKind::Stream` for `Subscription` ops; using
/// `HandlerKind::Once` is rejected with `"handler kind mismatch:
/// Subscription requires HandlerKind::Stream (got HandlerKind::Once)"`.
/// The test only verifies that the MCP `search` tool *excludes*
/// Subscription ops from its listing — it never invokes the handler —
/// so a single-frame echo is sufficient.
fn make_echo_streaming_handler() -> alknet_call::registry::registration::StreamingHandler {
make_streaming_handler(|input, context| {
futures::stream::iter(vec![ResponseEnvelope::ok(context.request_id, input)])
})
}
/// Build a `HandlerKind` matching the op's `OperationType`: `Once` for
/// Query/Mutation, `Stream` for Subscription. The registry's kind
/// validation (ADR-049) rejects a mismatch, so the helper must branch
/// — using `HandlerKind::Once` for a `Subscription` op panics in
/// `register().unwrap()`.
fn handler_kind_for(op_type: OperationType) -> HandlerKind {
match op_type {
OperationType::Subscription => HandlerKind::Stream(make_echo_streaming_handler()),
OperationType::Query | OperationType::Mutation => {
HandlerKind::Once(make_echo_handler())
}
}
}
fn full_registry_with_ops(
specs: Vec<(String, OperationType, AccessControl)>,
) -> Arc<OperationRegistry> {
@@ -506,7 +537,7 @@ mod tests {
inner
.register(HandlerRegistration::new(
external_spec(&name, op_type, acl),
HandlerKind::Once(make_echo_handler()),
handler_kind_for(op_type),
OperationProvenance::Local,
None,
None,
@@ -521,7 +552,7 @@ mod tests {
dispatch_registry
.register(HandlerRegistration::new(
external_spec(&op.name, op.op_type, op.access_control.clone()),
HandlerKind::Once(make_echo_handler()),
handler_kind_for(op.op_type),
OperationProvenance::Local,
None,
None,