plexor-core 0.1.0-alpha.2

Core library for the rust implementation of the Plexo distributed system architecture, providing the fundamental Plexus, Neuron, Codec, and Axon abstractions.
Documentation
// Copyright 2025 Alecks Gates
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

//! # Type Erasure Safety & Regression Tests
//!
//! These tests verify the logic used to safely restore concrete types from type-erased
//! wrappers. This logic is an architectural necessity for **External Ganglia** where
//! incoming network bytes must be mapped to specific Rust types at runtime.
//!
//! The tests specifically exercise and justify the `unsafe` transmute paths by ensuring:
//! 1. `TypeId` verification correctly identifies identical types.
//! 2. Cross-type restoration is strictly prevented (safety check).
//! 3. Restoration works correctly in complex generic contexts (Universal Plexus).

use plexor_core::codec::{Codec, CodecError, CodecName};
use plexor_core::erasure::neuron::{NeuronErased, NeuronErasedWrapper, erase_neuron};
use plexor_core::erasure::synapse::{
    SynapseInternalErased, SynapseInternalErasedWrapper, erase_synapse_internal,
};
use plexor_core::namespace::NamespaceImpl;
use plexor_core::neuron::{Neuron, NeuronImpl};
use plexor_core::synapse::{SynapseInprocess, SynapseInternal};
use std::any::TypeId;
use std::sync::Arc;

// Define simple test types to be self-contained
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
struct TestStruct {
    foo: i32,
}

#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
struct OtherStruct {
    bar: String,
}

struct TestCodec;
impl CodecName for TestCodec {
    fn name() -> &'static str {
        "test"
    }
}
impl Codec<TestStruct> for TestCodec {
    fn encode(_data: &TestStruct) -> Result<Vec<u8>, CodecError> {
        Ok(vec![])
    }
    fn decode(_data: &[u8]) -> Result<TestStruct, CodecError> {
        Ok(TestStruct { foo: 42 })
    }
}
impl Codec<OtherStruct> for TestCodec {
    fn encode(_data: &OtherStruct) -> Result<Vec<u8>, CodecError> {
        Ok(vec![])
    }
    fn decode(_data: &[u8]) -> Result<OtherStruct, CodecError> {
        Ok(OtherStruct {
            bar: "other".to_string(),
        })
    }
}

fn test_namespace() -> Arc<NamespaceImpl> {
    Arc::new(NamespaceImpl {
        delimiter: ".",
        parts: vec!["test"],
    })
}

#[test]
fn test_synapse_erasure_and_restoration() {
    let ns = test_namespace();
    let neuron = Arc::new(NeuronImpl::<TestStruct, TestCodec>::new(ns));
    let synapse = SynapseInprocess::new(neuron.clone(), vec![], vec![]);

    // Erase it
    let erased = erase_synapse_internal(synapse);

    // Unerase it using the same types
    {
        let guard = erased.read();
        // This should hit the first 'if let Some' branch (safe Any downcast)
        let restored = guard
            .as_any()
            .downcast_ref::<SynapseInternalErasedWrapper<
                TestStruct,
                TestCodec,
                SynapseInprocess<TestStruct, TestCodec>,
            >>()
            .expect("Safe downcast should work");
        assert_eq!(restored.neuron_name(), neuron.name());
    }
}

#[test]
fn test_multiple_neuron_types_in_universal_plexus_context() {
    let ns1 = test_namespace();
    let neuron1 = Arc::new(NeuronImpl::<TestStruct, TestCodec>::new(ns1));

    let ns2 = test_namespace();
    let neuron2 = Arc::new(NeuronImpl::<OtherStruct, TestCodec>::new(ns2));

    // Both erased neurons can be stored in the same collection
    let neurons: Vec<Arc<dyn NeuronErased + Send + Sync>> =
        vec![erase_neuron(neuron1.clone()), erase_neuron(neuron2.clone())];

    assert_eq!(neurons.len(), 2);
    assert_ne!(neurons[0].payload_type_id(), neurons[1].payload_type_id());
    assert_eq!(neurons[0].codec_type_id(), neurons[1].codec_type_id());

    // Verify we can get back to the typed versions
    let restored1 = neurons[0]
        .as_any()
        .downcast_ref::<NeuronErasedWrapper<TestStruct, TestCodec>>()
        .expect("Should restore first neuron");
    assert_eq!(restored1.name(), neuron1.name());

    let restored2 = neurons[1]
        .as_any()
        .downcast_ref::<NeuronErasedWrapper<OtherStruct, TestCodec>>()
        .expect("Should restore second neuron");
    assert_eq!(restored2.name(), neuron2.name());
}

#[test]
fn test_unsafe_transmute_justification() {
    let ns = test_namespace();
    let neuron = Arc::new(NeuronImpl::<TestStruct, TestCodec>::new(ns));
    let synapse = SynapseInprocess::new(neuron.clone(), vec![], vec![]);
    let erased_wrapper = SynapseInternalErasedWrapper::new(synapse);

    // The 'to_typed_synapse' call should succeed
    // Even if we use different generic parameters that are bound to the same types
    let _ = erased_wrapper
        .to_typed_synapse::<TestStruct, TestCodec, SynapseInprocess<TestStruct, TestCodec>>()
        .expect("Should succeed");
}

#[test]
fn test_cross_type_interference_prevention() {
    let ns = test_namespace();
    let neuron1 = Arc::new(NeuronImpl::<TestStruct, TestCodec>::new(ns.clone()));
    let synapse1 = SynapseInprocess::new(neuron1.clone(), vec![], vec![]);
    let erased1 = SynapseInternalErasedWrapper::new(synapse1);

    // Try to restore synapse1 as if it were synapse2 (different message type)
    let result = erased1
        .to_typed_synapse::<OtherStruct, TestCodec, SynapseInprocess<OtherStruct, TestCodec>>();
    assert!(result.is_err(), "Should prevent cross-type restoration");
}

#[test]
fn test_complex_generic_restoration() {
    fn restore_generic<T, C, S>(
        erased: &SynapseInternalErasedWrapper<
            TestStruct,
            TestCodec,
            SynapseInprocess<TestStruct, TestCodec>,
        >,
    ) where
        T: Send + Sync + 'static,
        C: Codec<T> + CodecName + Send + Sync + 'static,
        S: SynapseInternal<T, C> + Send + Sync + 'static,
    {
        if TypeId::of::<T>() == TypeId::of::<TestStruct>()
            && TypeId::of::<C>() == TypeId::of::<TestCodec>()
            && TypeId::of::<S>() == TypeId::of::<SynapseInprocess<TestStruct, TestCodec>>()
        {
            let _ = erased
                .to_typed_synapse::<T, C, S>()
                .expect("Restoration should work when TypeIds match");
        }
    }

    let ns = test_namespace();
    let neuron = Arc::new(NeuronImpl::<TestStruct, TestCodec>::new(ns));
    let synapse = SynapseInprocess::new(neuron.clone(), vec![], vec![]);
    let erased = SynapseInternalErasedWrapper::new(synapse);

    restore_generic::<TestStruct, TestCodec, SynapseInprocess<TestStruct, TestCodec>>(&erased);
}