Skip to main content

aura_composition/
registry.rs

1//! Effect registry system for dynamic effect dispatch
2//!
3//! This module provides a registry-based system for dispatching effects
4//! to appropriate handlers. The registry enables dynamic composition
5//! and runtime reconfiguration of effect handlers.
6
7use crate::adapters::{
8    ConsoleHandlerAdapter, CryptoHandlerAdapter, LoggingSystemHandlerAdapter, RandomHandlerAdapter,
9    StorageHandlerAdapter, TimeHandlerAdapter, TraceHandlerAdapter,
10};
11use async_trait::async_trait;
12use aura_core::{
13    hash, AuthorityId, ContextId, ContextSnapshot, EffectType, ExecutionMode, OperationSessionId,
14    SessionId,
15};
16use aura_effects::{
17    console::RealConsoleHandler, crypto::RealCryptoHandler, random::RealRandomHandler,
18    storage::FilesystemStorageHandler, system::logging::LoggingSystemHandler,
19    time::PhysicalTimeHandler, trace::TraceHandler,
20};
21use aura_mpst::LocalSessionType;
22use cfg_if::cfg_if;
23use serde::{Deserialize, Serialize};
24use std::collections::HashMap;
25use thiserror::Error;
26use uuid::Uuid;
27
28cfg_if! {
29    if #[cfg(not(target_arch = "wasm32"))] {
30        use crate::adapters::TransportHandlerAdapter;
31        use aura_effects::TcpTransportHandler as RealTransportHandler;
32    }
33}
34
35/// Typed effect identifier for registry metadata keys.
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
37pub struct EffectId(pub EffectType);
38
39impl From<EffectType> for EffectId {
40    fn from(value: EffectType) -> Self {
41        Self(value)
42    }
43}
44
45impl From<EffectId> for EffectType {
46    fn from(value: EffectId) -> Self {
47        value.0
48    }
49}
50
51/// Typed capability identifier for registry metadata keys.
52#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
53pub struct CapabilityId(String);
54
55impl CapabilityId {
56    pub fn new(value: impl Into<String>) -> Self {
57        Self(value.into())
58    }
59
60    pub fn as_str(&self) -> &str {
61        &self.0
62    }
63}
64
65impl From<&str> for CapabilityId {
66    fn from(value: &str) -> Self {
67        Self(value.to_string())
68    }
69}
70
71impl From<String> for CapabilityId {
72    fn from(value: String) -> Self {
73        Self(value)
74    }
75}
76
77/// Typed metadata keys to prevent registry key collisions.
78#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
79pub enum MetadataKey {
80    Effect(EffectId),
81    Capability(CapabilityId),
82}
83
84/// Simplified context for handler execution
85#[derive(Debug, Clone)]
86pub struct HandlerContext {
87    pub authority_id: AuthorityId,
88    pub context_id: ContextId,
89    pub execution_mode: ExecutionMode,
90    pub session_id: OperationSessionId,
91    pub operation_id: Uuid,
92    pub metadata: HashMap<MetadataKey, String>,
93}
94
95impl HandlerContext {
96    fn deterministic_operation_id(
97        authority_id: AuthorityId,
98        context_id: ContextId,
99        execution_mode: ExecutionMode,
100    ) -> Uuid {
101        let mut seed = Vec::with_capacity(33);
102        seed.extend_from_slice(&authority_id.to_bytes());
103        seed.extend_from_slice(&context_id.to_bytes());
104        match execution_mode {
105            ExecutionMode::Testing => seed.push(0),
106            ExecutionMode::Production => seed.push(1),
107            ExecutionMode::Simulation { seed: sim_seed } => {
108                seed.push(2);
109                seed.extend_from_slice(&sim_seed.to_le_bytes());
110            }
111        }
112        let digest = hash::hash(&seed);
113        let mut op_bytes = [0u8; 16];
114        op_bytes.copy_from_slice(&digest[..16]);
115        Uuid::from_bytes(op_bytes)
116    }
117
118    fn deterministic_session_id(
119        authority_id: AuthorityId,
120        context_id: ContextId,
121        execution_mode: ExecutionMode,
122    ) -> OperationSessionId {
123        let mut seed = Vec::with_capacity(41);
124        seed.extend_from_slice(b"aura-session");
125        seed.extend_from_slice(&authority_id.to_bytes());
126        seed.extend_from_slice(&context_id.to_bytes());
127        match execution_mode {
128            ExecutionMode::Testing => seed.push(0),
129            ExecutionMode::Production => seed.push(1),
130            ExecutionMode::Simulation { seed: sim_seed } => {
131                seed.push(2);
132                seed.extend_from_slice(&sim_seed.to_le_bytes());
133            }
134        }
135        OperationSessionId::new(SessionId::new_from_entropy(hash::hash(&seed)))
136    }
137
138    // Registry helper
139    /// Create a new handler context
140    pub fn new(
141        authority_id: AuthorityId,
142        context_id: ContextId,
143        execution_mode: ExecutionMode,
144    ) -> Self {
145        let operation_id =
146            Self::deterministic_operation_id(authority_id, context_id, execution_mode);
147        Self {
148            authority_id,
149            context_id,
150            execution_mode,
151            session_id: Self::deterministic_session_id(authority_id, context_id, execution_mode),
152            operation_id,
153            metadata: HashMap::new(),
154        }
155    }
156
157    /// Create a new handler context from a lightweight snapshot.
158    pub fn from_snapshot(snapshot: ContextSnapshot) -> Self {
159        let authority_id = snapshot.authority_id();
160        let context_id = snapshot.context_id();
161        let execution_mode = snapshot.execution_mode();
162        let operation_id =
163            Self::deterministic_operation_id(authority_id, context_id, execution_mode);
164        Self {
165            authority_id,
166            context_id,
167            execution_mode,
168            session_id: snapshot.session_id(),
169            operation_id,
170            metadata: HashMap::new(),
171        }
172    }
173
174    /// Set session ID
175    pub fn with_session_id(mut self, session_id: OperationSessionId) -> Self {
176        self.session_id = session_id;
177        self
178    }
179
180    /// Add metadata
181    pub fn with_metadata(mut self, key: MetadataKey, value: String) -> Self {
182        self.metadata.insert(key, value);
183        self
184    }
185
186    /// Add effect-scoped metadata
187    pub fn with_effect_metadata(mut self, effect: EffectType, value: String) -> Self {
188        self.metadata
189            .insert(MetadataKey::Effect(effect.into()), value);
190        self
191    }
192
193    /// Add capability-scoped metadata
194    pub fn with_capability_metadata(
195        mut self,
196        capability: impl Into<CapabilityId>,
197        value: String,
198    ) -> Self {
199        self.metadata
200            .insert(MetadataKey::Capability(capability.into()), value);
201        self
202    }
203}
204
205/// Options for bulk registering default handlers.
206#[derive(Debug, Clone, Copy, Default)]
207pub struct RegisterAllOptions {
208    /// Allow registering impure handlers that touch OS resources.
209    ///
210    /// Impure handlers include: Console, Random, Crypto, Storage, Time,
211    /// Network (TCP), Trace, and System logging handlers.
212    pub allow_impure: bool,
213}
214
215impl RegisterAllOptions {
216    /// Explicit opt-in for impure default handlers.
217    pub fn allow_impure() -> Self {
218        Self { allow_impure: true }
219    }
220}
221
222/// Error type for handler operations
223#[derive(Debug, Error)]
224pub enum HandlerError {
225    /// Effect type not supported
226    #[error("Effect {effect_type:?} not supported")]
227    UnsupportedEffect { effect_type: EffectType },
228
229    /// Operation not found within effect type
230    #[error("Operation '{operation}' not found in effect {effect_type:?}")]
231    UnknownOperation {
232        effect_type: EffectType,
233        operation: String,
234    },
235
236    /// Effect parameter serialization failed
237    #[error("Failed to serialize parameters for {effect_type:?}.{operation}")]
238    EffectSerialization {
239        effect_type: EffectType,
240        operation: String,
241        #[source]
242        source: Box<dyn std::error::Error + Send + Sync>,
243    },
244
245    /// Effect parameter deserialization failed
246    #[error("Failed to deserialize parameters for {effect_type:?}.{operation}")]
247    EffectDeserialization {
248        effect_type: EffectType,
249        operation: String,
250        #[source]
251        source: Box<dyn std::error::Error + Send + Sync>,
252    },
253
254    /// Session execution failed
255    #[error("Session type execution failed")]
256    SessionExecution {
257        #[source]
258        source: Box<dyn std::error::Error + Send + Sync>,
259    },
260
261    /// Context operation failed
262    #[error("Context operation failed: {message}")]
263    ContextError { message: String },
264
265    /// Registry operation failed
266    #[error("Registry operation failed")]
267    RegistryError {
268        #[source]
269        source: Box<dyn std::error::Error + Send + Sync>,
270    },
271
272    /// Execution failed
273    #[error("Effect execution failed")]
274    ExecutionFailed {
275        #[source]
276        source: Box<dyn std::error::Error + Send + Sync>,
277    },
278}
279
280/// Primary interface for all Aura handlers
281///
282/// This trait defines the unified interface for effect execution and session
283/// interpretation. All handlers in the Aura system implement this trait.
284/// Uses serialized bytes for parameters and results to enable trait object compatibility.
285#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
286#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
287pub trait Handler: Send + Sync {
288    /// Execute an effect with serialized parameters and return serialized result
289    async fn execute_effect(
290        &self,
291        effect_type: EffectType,
292        operation: &str,
293        parameters: &[u8],
294        ctx: &HandlerContext,
295    ) -> Result<Vec<u8>, HandlerError>;
296
297    /// Execute a session type
298    async fn execute_session(
299        &self,
300        session: LocalSessionType,
301        ctx: &HandlerContext,
302    ) -> Result<(), HandlerError>;
303
304    /// Check if this handler supports a specific effect type
305    fn supports_effect(&self, effect_type: EffectType) -> bool;
306
307    /// Get the execution mode of this handler
308    fn execution_mode(&self) -> ExecutionMode;
309
310    /// Get supported effect types
311    fn supported_effects(&self) -> Vec<EffectType> {
312        EffectType::all()
313            .into_iter()
314            .filter(|&effect_type| self.supports_effect(effect_type))
315            .collect()
316    }
317}
318
319/// Error type for registry operations
320#[derive(Debug, Error)]
321pub enum RegistryError {
322    /// Effect type not registered
323    #[error("Effect type {effect_type:?} not registered")]
324    EffectTypeNotRegistered { effect_type: EffectType },
325
326    /// Operation not supported by registered handler
327    #[error("Operation '{operation}' not supported by handler for {effect_type:?}")]
328    OperationNotSupported {
329        effect_type: EffectType,
330        operation: String,
331    },
332
333    /// Handler registration failed
334    #[error("Failed to register handler for {effect_type:?}")]
335    RegistrationFailed {
336        effect_type: EffectType,
337        #[source]
338        source: Box<dyn std::error::Error + Send + Sync>,
339    },
340
341    /// Handler execution failed
342    #[error("Handler execution failed for {effect_type:?}")]
343    HandlerExecutionFailed {
344        effect_type: EffectType,
345        #[source]
346        source: Box<dyn std::error::Error + Send + Sync>,
347    },
348
349    /// Parameter deserialization failed
350    #[error("Failed to deserialize result from {effect_type:?} operation '{operation}'")]
351    ParameterDeserialization {
352        effect_type: EffectType,
353        operation: String,
354        #[source]
355        source: Box<dyn std::error::Error + Send + Sync>,
356    },
357}
358
359impl RegistryError {
360    /// Create a registration failed error
361    pub fn registration_failed(
362        effect_type: EffectType,
363        source: impl std::error::Error + Send + Sync + 'static,
364    ) -> Self {
365        Self::RegistrationFailed {
366            effect_type,
367            source: Box::new(source),
368        }
369    }
370
371    /// Create a handler execution failed error
372    pub fn handler_execution_failed(
373        effect_type: EffectType,
374        source: impl std::error::Error + Send + Sync + 'static,
375    ) -> Self {
376        Self::HandlerExecutionFailed {
377            effect_type,
378            source: Box::new(source),
379        }
380    }
381}
382
383/// Trait for handlers that can be registered in the effect registry
384///
385/// This trait provides a type-erased interface for effect execution
386/// that can be used in trait objects.
387#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
388#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
389pub trait RegistrableHandler: Send + Sync {
390    /// Execute a specific operation within an effect type
391    ///
392    /// This method is called by the registry to execute specific operations
393    /// after the effect has been dispatched to the correct handler.
394    /// Returns serialized result bytes.
395    async fn execute_operation_bytes(
396        &self,
397        effect_type: EffectType,
398        operation: &str,
399        parameters: &[u8],
400        ctx: &HandlerContext,
401    ) -> Result<Vec<u8>, HandlerError>;
402
403    /// Get the list of operations supported by this handler for a given effect type
404    ///
405    /// Returns the operation names that this handler can execute for the
406    /// specified effect type. Used for capability discovery and validation.
407    fn supported_operations(&self, effect_type: EffectType) -> Vec<String>;
408
409    /// Check if a specific operation is supported
410    fn supports_operation(&self, effect_type: EffectType, operation: &str) -> bool {
411        self.supported_operations(effect_type)
412            .contains(&operation.to_string())
413    }
414
415    /// Check if this handler supports the given effect type
416    fn supports_effect(&self, effect_type: EffectType) -> bool;
417
418    /// Get the execution mode of this handler
419    fn execution_mode(&self) -> ExecutionMode;
420}
421
422/// Registry for effect handlers
423///
424/// The registry maintains a mapping from effect types to handlers and
425/// provides dynamic dispatch capabilities for effect execution.
426pub struct EffectRegistry {
427    /// Registered handlers by effect type
428    handlers: HashMap<EffectType, Box<dyn RegistrableHandler>>,
429    /// Default execution mode for the registry
430    default_execution_mode: ExecutionMode,
431}
432
433impl std::fmt::Debug for EffectRegistry {
434    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
435        f.debug_struct("EffectRegistry")
436            .field(
437                "handlers",
438                &format!("HashMap with {} entries", self.handlers.len()),
439            )
440            .field("default_execution_mode", &self.default_execution_mode)
441            .finish()
442    }
443}
444
445impl EffectRegistry {
446    /// Create a new effect registry
447    pub fn new(default_execution_mode: ExecutionMode) -> Self {
448        Self {
449            handlers: HashMap::new(),
450            default_execution_mode,
451        }
452    }
453
454    /// Register a handler for a specific effect type
455    ///
456    /// # Errors
457    ///
458    /// Returns an error if the handler doesn't support the effect type
459    /// or if there's a conflict with an existing registration.
460    pub fn register_handler(
461        &mut self,
462        effect_type: EffectType,
463        handler: Box<dyn RegistrableHandler>,
464    ) -> Result<(), RegistryError> {
465        // Validate that the handler supports this effect type
466        if !handler.supports_effect(effect_type) {
467            return Err(RegistryError::registration_failed(
468                effect_type,
469                std::io::Error::new(
470                    std::io::ErrorKind::InvalidInput,
471                    "Handler does not support the specified effect type",
472                ),
473            ));
474        }
475
476        // Register the handler
477        self.handlers.insert(effect_type, handler);
478        Ok(())
479    }
480
481    /// Register a standard set of default handlers.
482    ///
483    /// This method requires explicit opt-in for impure handlers because it
484    /// registers OS-integrated implementations (console, randomness, crypto,
485    /// storage, time, TCP transport, tracing, and system logging).
486    ///
487    /// Intended for runtime assembly in `aura-agent` only.
488    pub fn register_all(&mut self, options: RegisterAllOptions) -> Result<(), RegistryError> {
489        if !options.allow_impure {
490            return Err(RegistryError::registration_failed(
491                EffectType::System,
492                std::io::Error::new(
493                    std::io::ErrorKind::PermissionDenied,
494                    "register_all requires allow_impure=true for OS-backed handlers",
495                ),
496            ));
497        }
498
499        self.register_handler(
500            EffectType::Console,
501            Box::new(ConsoleHandlerAdapter::new(RealConsoleHandler::new())),
502        )?;
503        self.register_handler(
504            EffectType::Random,
505            Box::new(RandomHandlerAdapter::new(RealRandomHandler::new())),
506        )?;
507        self.register_handler(
508            EffectType::Crypto,
509            Box::new(CryptoHandlerAdapter::new(RealCryptoHandler::new())),
510        )?;
511        self.register_handler(
512            EffectType::Storage,
513            Box::new(StorageHandlerAdapter::new(
514                FilesystemStorageHandler::with_default_path(),
515            )),
516        )?;
517        self.register_handler(
518            EffectType::Time,
519            Box::new(TimeHandlerAdapter::new(PhysicalTimeHandler::new())),
520        )?;
521        cfg_if! {
522            if #[cfg(not(target_arch = "wasm32"))] {
523                self.register_handler(
524                    EffectType::Network,
525                    Box::new(TransportHandlerAdapter::new(RealTransportHandler::default())),
526                )?;
527            }
528        }
529        self.register_handler(
530            EffectType::Trace,
531            Box::new(TraceHandlerAdapter::new(TraceHandler::new())),
532        )?;
533        self.register_handler(
534            EffectType::System,
535            Box::new(LoggingSystemHandlerAdapter::new(
536                LoggingSystemHandler::default(),
537            )),
538        )?;
539
540        Ok(())
541    }
542
543    /// Unregister a handler for a specific effect type
544    pub fn unregister_handler(
545        &mut self,
546        effect_type: EffectType,
547    ) -> Option<Box<dyn RegistrableHandler>> {
548        self.handlers.remove(&effect_type)
549    }
550
551    /// Check if a handler is registered for an effect type
552    pub fn is_registered(&self, effect_type: EffectType) -> bool {
553        self.handlers.contains_key(&effect_type)
554    }
555
556    /// Get all registered effect types
557    pub fn registered_effect_types(&self) -> Vec<EffectType> {
558        self.handlers.keys().copied().collect()
559    }
560
561    /// Number of registered handlers (test helper)
562    pub fn handlers_len(&self) -> usize {
563        self.handlers.len()
564    }
565
566    /// Get supported operations for an effect type
567    pub fn supported_operations(
568        &self,
569        effect_type: EffectType,
570    ) -> Result<Vec<String>, RegistryError> {
571        match self.handlers.get(&effect_type) {
572            Some(handler) => Ok(handler.supported_operations(effect_type)),
573            None => Err(RegistryError::EffectTypeNotRegistered { effect_type }),
574        }
575    }
576
577    /// Check if an operation is supported for an effect type
578    pub fn supports_operation(&self, effect_type: EffectType, operation: &str) -> bool {
579        self.handlers
580            .get(&effect_type)
581            .map(|h| h.supports_operation(effect_type, operation))
582            .unwrap_or(false)
583    }
584
585    /// Execute a session type through the registry
586    ///
587    /// Routes session execution to an appropriate handler. For session types,
588    /// this typically uses the choreographic effect handler.
589    pub async fn execute_session(
590        &mut self,
591        _session: LocalSessionType,
592        _ctx: &mut HandlerContext,
593    ) -> Result<(), RegistryError> {
594        Err(RegistryError::OperationNotSupported {
595            effect_type: EffectType::Choreographic,
596            operation: "execute_session".to_string(),
597        })
598    }
599
600    /// Get the execution mode of the registry
601    pub fn execution_mode(&self) -> ExecutionMode {
602        self.default_execution_mode
603    }
604
605    /// Get a summary of registry capabilities
606    pub fn capability_summary(&self) -> RegistryCapabilities {
607        let mut capabilities = RegistryCapabilities {
608            registered_effects: Vec::new(),
609            total_operations: 0,
610            execution_modes: Vec::new(),
611        };
612
613        for (effect_type, handler) in &self.handlers {
614            let operations = handler.supported_operations(*effect_type);
615            let operation_count = operations.len();
616            capabilities.registered_effects.push(EffectCapability {
617                effect_type: *effect_type,
618                operation_count,
619                operations,
620            });
621            capabilities.total_operations += operation_count;
622
623            let mode = handler.execution_mode();
624            if !capabilities.execution_modes.contains(&mode) {
625                capabilities.execution_modes.push(mode);
626            }
627        }
628
629        capabilities
630    }
631}
632
633#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
634#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
635impl Handler for EffectRegistry {
636    async fn execute_effect(
637        &self,
638        effect_type: EffectType,
639        operation: &str,
640        parameters: &[u8],
641        ctx: &HandlerContext,
642    ) -> Result<Vec<u8>, HandlerError> {
643        // Route to the appropriate registered handler
644        if let Some(handler) = self.handlers.get(&effect_type) {
645            handler
646                .execute_operation_bytes(effect_type, operation, parameters, ctx)
647                .await
648        } else {
649            Err(HandlerError::UnsupportedEffect { effect_type })
650        }
651    }
652
653    async fn execute_session(
654        &self,
655        _session: LocalSessionType,
656        _ctx: &HandlerContext,
657    ) -> Result<(), HandlerError> {
658        let err = std::io::Error::other("session execution not wired in registry");
659        Err(HandlerError::SessionExecution {
660            source: Box::new(err),
661        })
662    }
663
664    fn supports_effect(&self, effect_type: EffectType) -> bool {
665        self.is_registered(effect_type)
666    }
667
668    fn execution_mode(&self) -> ExecutionMode {
669        self.default_execution_mode
670    }
671}
672
673/// Summary of registry capabilities
674#[derive(Debug, Clone)]
675pub struct RegistryCapabilities {
676    /// All registered effect types with their capabilities
677    pub registered_effects: Vec<EffectCapability>,
678    /// Total number of operations across all effects
679    pub total_operations: usize, // usize ok: internal summary struct, not serialized
680    /// Execution modes of registered handlers
681    pub execution_modes: Vec<ExecutionMode>,
682}
683
684/// Capability information for a single effect type
685#[derive(Debug, Clone)]
686pub struct EffectCapability {
687    /// The effect type
688    pub effect_type: EffectType,
689    /// Number of supported operations
690    pub operation_count: usize, // usize ok: internal capability struct, not serialized
691    /// List of supported operation names
692    pub operations: Vec<String>,
693}
694
695impl RegistryCapabilities {
696    /// Check if a specific effect type is registered
697    pub fn has_effect_type(&self, effect_type: EffectType) -> bool {
698        self.registered_effects
699            .iter()
700            .any(|cap| cap.effect_type == effect_type)
701    }
702
703    /// Get capability information for a specific effect type
704    pub fn get_effect_capability(&self, effect_type: EffectType) -> Option<&EffectCapability> {
705        self.registered_effects
706            .iter()
707            .find(|cap| cap.effect_type == effect_type)
708    }
709
710    /// Check if a specific operation is supported
711    pub fn supports_operation(&self, effect_type: EffectType, operation: &str) -> bool {
712        self.get_effect_capability(effect_type)
713            .map(|cap| cap.operations.contains(&operation.to_string()))
714            .unwrap_or(false)
715    }
716
717    /// Get the number of registered effect types
718    pub fn effect_type_count(&self) -> usize {
719        self.registered_effects.len()
720    }
721
722    /// Check if the registry supports a specific execution mode
723    pub fn supports_execution_mode(&self, mode: ExecutionMode) -> bool {
724        self.execution_modes.contains(&mode)
725    }
726}
727
728#[cfg(test)]
729mod tests {
730    use super::*;
731
732    // Mock registrable handler for testing
733    struct MockRegistrableHandler {
734        effect_type: EffectType,
735        operations: Vec<String>,
736        execution_mode: ExecutionMode,
737    }
738
739    impl MockRegistrableHandler {
740        // Adapter test shim
741        fn new(
742            effect_type: EffectType,
743            operations: Vec<&str>,
744            execution_mode: ExecutionMode,
745        ) -> Self {
746            Self {
747                effect_type,
748                operations: operations.into_iter().map(|s| s.to_string()).collect(),
749                execution_mode,
750            }
751        }
752    }
753
754    #[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
755    #[cfg_attr(not(target_arch = "wasm32"), async_trait)]
756    impl Handler for MockRegistrableHandler {
757        // Adapter test shim
758        async fn execute_effect(
759            &self,
760            effect_type: EffectType,
761            operation: &str,
762            _parameters: &[u8],
763            _ctx: &HandlerContext,
764        ) -> Result<Vec<u8>, HandlerError> {
765            if self.effect_type == effect_type && self.operations.contains(&operation.to_string()) {
766                // Mock successful result
767                aura_core::util::serialization::to_vec(&serde_json::Value::String(
768                    "mock_result".to_string(),
769                ))
770                .map_err(|e| HandlerError::EffectSerialization {
771                    effect_type,
772                    operation: operation.to_string(),
773                    source: e.into(),
774                })
775            } else {
776                Err(HandlerError::UnsupportedEffect { effect_type })
777            }
778        }
779
780        async fn execute_session(
781            &self,
782            _session: LocalSessionType,
783            _ctx: &HandlerContext,
784        ) -> Result<(), HandlerError> {
785            Ok(())
786        }
787
788        fn supports_effect(&self, effect_type: EffectType) -> bool {
789            self.effect_type == effect_type
790        }
791
792        fn execution_mode(&self) -> ExecutionMode {
793            self.execution_mode
794        }
795    }
796
797    #[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
798    #[cfg_attr(not(target_arch = "wasm32"), async_trait)]
799    impl RegistrableHandler for MockRegistrableHandler {
800        // Adapter test shim
801        async fn execute_operation_bytes(
802            &self,
803            _effect_type: EffectType,
804            operation: &str,
805            _parameters: &[u8],
806            _ctx: &HandlerContext,
807        ) -> Result<Vec<u8>, HandlerError> {
808            if self.operations.contains(&operation.to_string()) {
809                // Mock successful operation - return serialized mock result
810                let mock_result = serde_json::Value::String("mock_result".to_string());
811                aura_core::util::serialization::to_vec(&mock_result).map_err(|e| {
812                    HandlerError::EffectSerialization {
813                        effect_type: self.effect_type,
814                        operation: operation.to_string(),
815                        source: e.into(),
816                    }
817                })
818            } else {
819                Err(HandlerError::UnknownOperation {
820                    effect_type: self.effect_type,
821                    operation: operation.to_string(),
822                })
823            }
824        }
825
826        fn supported_operations(&self, effect_type: EffectType) -> Vec<String> {
827            if self.effect_type == effect_type {
828                self.operations.clone()
829            } else {
830                Vec::new()
831            }
832        }
833
834        fn supports_effect(&self, effect_type: EffectType) -> bool {
835            self.effect_type == effect_type
836        }
837
838        fn execution_mode(&self) -> ExecutionMode {
839            self.execution_mode
840        }
841    }
842
843    /// Fresh registry has the requested mode and no registered handlers.
844    #[test]
845    fn test_registry_creation() {
846        let registry = EffectRegistry::new(ExecutionMode::Testing);
847        assert_eq!(registry.execution_mode(), ExecutionMode::Testing);
848        assert!(registry.registered_effect_types().is_empty());
849    }
850
851    /// Registering a handler makes its effect type discoverable in the registry.
852    #[test]
853    fn test_handler_registration() {
854        let mut registry = EffectRegistry::new(ExecutionMode::Testing);
855
856        let handler = Box::new(MockRegistrableHandler::new(
857            EffectType::Crypto,
858            vec!["hash", "sign", "verify"],
859            ExecutionMode::Testing,
860        ));
861
862        // Register handler
863        registry
864            .register_handler(EffectType::Crypto, handler)
865            .unwrap();
866
867        assert!(registry.is_registered(EffectType::Crypto));
868        assert!(!registry.is_registered(EffectType::Network));
869
870        let effect_types = registry.registered_effect_types();
871        assert_eq!(effect_types.len(), 1);
872        assert!(effect_types.contains(&EffectType::Crypto));
873    }
874
875    /// `supported_operations` and `supports_operation` agree with what the
876    /// handler declared.
877    #[test]
878    fn test_operation_support() {
879        let mut registry = EffectRegistry::new(ExecutionMode::Testing);
880
881        let handler = Box::new(MockRegistrableHandler::new(
882            EffectType::Crypto,
883            vec!["hash", "sign", "verify"],
884            ExecutionMode::Testing,
885        ));
886
887        registry
888            .register_handler(EffectType::Crypto, handler)
889            .unwrap();
890
891        // Test supported operations
892        let operations = registry.supported_operations(EffectType::Crypto).unwrap();
893        assert_eq!(operations.len(), 3);
894        assert!(operations.contains(&"hash".to_string()));
895        assert!(operations.contains(&"sign".to_string()));
896        assert!(operations.contains(&"verify".to_string()));
897
898        // Test operation support checks
899        assert!(registry.supports_operation(EffectType::Crypto, "hash"));
900        assert!(registry.supports_operation(EffectType::Crypto, "sign"));
901        assert!(!registry.supports_operation(EffectType::Crypto, "encrypt"));
902        assert!(!registry.supports_operation(EffectType::Network, "send"));
903    }
904
905    /// Capability summary aggregates operation counts and execution modes
906    /// across multiple registered handlers.
907    #[test]
908    fn test_capability_summary() {
909        let mut registry = EffectRegistry::new(ExecutionMode::Testing);
910
911        // Register multiple handlers
912        let crypto_handler = Box::new(MockRegistrableHandler::new(
913            EffectType::Crypto,
914            vec!["hash", "sign"],
915            ExecutionMode::Testing,
916        ));
917        let network_handler = Box::new(MockRegistrableHandler::new(
918            EffectType::Network,
919            vec!["send", "receive", "broadcast"],
920            ExecutionMode::Production,
921        ));
922
923        registry
924            .register_handler(EffectType::Crypto, crypto_handler)
925            .unwrap();
926        registry
927            .register_handler(EffectType::Network, network_handler)
928            .unwrap();
929
930        let capabilities = registry.capability_summary();
931
932        assert_eq!(capabilities.effect_type_count(), 2);
933        assert_eq!(capabilities.total_operations, 5); // 2 + 3
934        assert!(capabilities.has_effect_type(EffectType::Crypto));
935        assert!(capabilities.has_effect_type(EffectType::Network));
936        assert!(!capabilities.has_effect_type(EffectType::Storage));
937
938        // Test operation support
939        assert!(capabilities.supports_operation(EffectType::Crypto, "hash"));
940        assert!(capabilities.supports_operation(EffectType::Network, "broadcast"));
941        assert!(!capabilities.supports_operation(EffectType::Crypto, "encrypt"));
942
943        // Test execution modes
944        assert!(capabilities.supports_execution_mode(ExecutionMode::Testing));
945        assert!(capabilities.supports_execution_mode(ExecutionMode::Production));
946        assert!(!capabilities.supports_execution_mode(ExecutionMode::Simulation { seed: 42 }));
947    }
948
949    /// Same (authority, context, mode) triple produces the same operation_id;
950    /// changing any input produces a different one.
951    #[test]
952    fn test_handler_context_operation_id_deterministic() {
953        let authority_id = AuthorityId::new_from_entropy([1u8; 32]);
954        let context_id = ContextId::new_from_entropy([2u8; 32]);
955        let ctx1 = HandlerContext::new(authority_id, context_id, ExecutionMode::Testing);
956        let ctx2 = HandlerContext::new(authority_id, context_id, ExecutionMode::Testing);
957
958        // Same inputs produce the same operation_id (deterministic)
959        assert_eq!(ctx1.operation_id, ctx2.operation_id);
960
961        // Different inputs produce different operation_ids
962        let different_authority = AuthorityId::new_from_entropy([3u8; 32]);
963        let ctx3 = HandlerContext::new(different_authority, context_id, ExecutionMode::Testing);
964        assert_ne!(ctx1.operation_id, ctx3.operation_id);
965
966        // Different execution mode produces different operation_id
967        let ctx4 = HandlerContext::new(authority_id, context_id, ExecutionMode::Production);
968        assert_ne!(ctx1.operation_id, ctx4.operation_id);
969    }
970
971    /// Registering a second handler for the same effect type silently
972    /// replaces the first. Callers relying on the original handler would
973    /// route to the wrong implementation. This test documents the current
974    /// behavior so any change is caught.
975    #[test]
976    fn test_duplicate_registration_replaces_handler() {
977        let mut registry = EffectRegistry::new(ExecutionMode::Testing);
978
979        let handler1 = Box::new(MockRegistrableHandler::new(
980            EffectType::Crypto,
981            vec!["hash", "sign"],
982            ExecutionMode::Testing,
983        ));
984        let handler2 = Box::new(MockRegistrableHandler::new(
985            EffectType::Crypto,
986            vec!["hash", "sign", "verify", "encrypt"],
987            ExecutionMode::Testing,
988        ));
989
990        registry
991            .register_handler(EffectType::Crypto, handler1)
992            .unwrap();
993        registry
994            .register_handler(EffectType::Crypto, handler2)
995            .unwrap();
996
997        // Second registration wins — operation list reflects handler2
998        let ops = registry.supported_operations(EffectType::Crypto).unwrap();
999        assert_eq!(ops.len(), 4);
1000        assert!(ops.contains(&"encrypt".to_string()));
1001    }
1002}