#![allow(clippy::needless_pass_by_value, clippy::type_complexity)]
use std::cell::Cell;
use std::rc::Rc;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use trait_kit::impl_module_meta;
use trait_kit::prelude::*;
mod core_scenarios {
use super::*;
use serial_test::serial;
use std::any::TypeId;
struct AlphaModule;
impl_module_meta!(AlphaModule, "alpha");
impl AutoBuilder for AlphaModule {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(_kit: &Kit) -> Result<Self::Capability, Self::Error> {
Ok(Arc::new(1))
}
}
struct BetaModule;
impl_module_meta!(BetaModule, "beta");
impl AutoBuilder for BetaModule {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(_kit: &Kit) -> Result<Self::Capability, Self::Error> {
Ok(Arc::new(2))
}
}
#[allow(dead_code)]
struct GammaModule;
impl_module_meta!(GammaModule, "gamma", deps = [BetaModule]);
impl AutoBuilder for GammaModule {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(kit: &Kit) -> Result<Self::Capability, Self::Error> {
let beta = kit.require::<BetaModule>()?;
Ok(Arc::new(*beta + 100))
}
}
struct ChainA;
impl_module_meta!(ChainA, "chain-a");
impl AutoBuilder for ChainA {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(_kit: &Kit) -> Result<Self::Capability, Self::Error> {
Ok(Arc::new(10))
}
}
struct ChainB;
impl_module_meta!(ChainB, "chain-b", deps = [ChainA]);
impl AutoBuilder for ChainB {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(kit: &Kit) -> Result<Self::Capability, Self::Error> {
let a = kit.require::<ChainA>()?;
Ok(Arc::new(*a + 1))
}
}
struct ChainC;
impl_module_meta!(ChainC, "chain-c", deps = [ChainB]);
impl AutoBuilder for ChainC {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(kit: &Kit) -> Result<Self::Capability, Self::Error> {
let b = kit.require::<ChainB>()?;
Ok(Arc::new(*b + 1))
}
}
fn make_counted_module() -> (
Rc<Cell<u32>>,
impl AutoBuilder<Capability = Arc<u32>, Error = TraitKitError>,
) {
let counter = Rc::new(Cell::new(0u32));
(counter, AlphaModule)
}
#[allow(dead_code)]
fn _silence_make_counted_module_warning() {
let _ = make_counted_module();
}
static COUNTED_BUILDS: AtomicUsize = AtomicUsize::new(0);
struct CountedModule;
impl_module_meta!(CountedModule, "counted");
impl AutoBuilder for CountedModule {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(_kit: &Kit) -> Result<Self::Capability, Self::Error> {
let n = COUNTED_BUILDS.fetch_add(1, Ordering::SeqCst);
Ok(Arc::new(n as u32))
}
}
struct ConfigReaderModule;
impl_module_meta!(ConfigReaderModule, "config-reader");
impl AutoBuilder for ConfigReaderModule {
type Capability = Arc<i32>;
type Error = TraitKitError;
fn build(kit: &Kit) -> Result<Self::Capability, Self::Error> {
let v: i32 = kit.config()?;
Ok(Arc::new(v))
}
}
#[derive(Debug, thiserror::Error)]
#[error("intentional build failure for {0}")]
struct BuildErr(String);
struct FailingModule;
impl_module_meta!(FailingModule, "failing");
impl AutoBuilder for FailingModule {
type Capability = Arc<u32>;
type Error = BuildErr;
fn build(_kit: &Kit) -> Result<Self::Capability, Self::Error> {
Err(BuildErr("failing-module".to_string()))
}
}
struct FailingLazyModule;
impl_module_meta!(FailingLazyModule, "failing-lazy");
impl AutoBuilder for FailingLazyModule {
type Capability = Arc<u32>;
type Error = BuildErr;
fn build(_kit: &Kit) -> Result<Self::Capability, Self::Error> {
Err(BuildErr("failing-lazy-module".to_string()))
}
}
#[test]
fn b01_empty_kit_build_succeeds() {
let kit = Kit::new();
let built = kit.build().expect("empty Kit build should succeed");
assert!(!built.contains::<AlphaModule>());
}
#[test]
fn b03_multiple_independent_modules_build() {
let mut kit = Kit::new();
kit.register::<AlphaModule>().expect("register Alpha");
kit.register::<BetaModule>().expect("register Beta");
let built = kit.build().expect("build should succeed");
let a = built.require::<AlphaModule>().expect("require Alpha");
let b = built.require::<BetaModule>().expect("require Beta");
assert_eq!(*a, 1);
assert_eq!(*b, 2);
}
#[test]
fn b04_chain_dependency_topological_build() {
let mut kit = Kit::new();
kit.register::<ChainA>().expect("register ChainA");
kit.register::<ChainB>().expect("register ChainB");
kit.register::<ChainC>().expect("register ChainC");
let built = kit.build().expect("build should succeed");
let c = built.require::<ChainC>().expect("require ChainC");
assert_eq!(*c, 12);
}
#[test]
fn b06_config_override_last_write_wins() {
let kit = Kit::new();
kit.set_config(1i32);
kit.set_config(2i32);
let built = kit.build().expect("build should succeed");
let v: i32 = built.config().expect("config should be retrievable");
assert_eq!(v, 2, "second set_config should override first");
}
#[test]
fn b07_build_callback_reads_config() {
let mut kit = Kit::new();
kit.set_config(99i32);
kit.register::<ConfigReaderModule>().expect("register");
let built = kit.build().expect("build should succeed");
let cap = built.require::<ConfigReaderModule>().expect("require");
assert_eq!(*cap, 99);
}
#[test]
fn b09_optional_returns_some_for_built_module() {
let mut kit = Kit::new();
kit.register::<AlphaModule>().expect("register");
let built = kit.build().expect("build");
let opt = built.optional::<AlphaModule>();
assert!(
opt.is_some(),
"optional should return Some for built module"
);
assert_eq!(*opt.unwrap(), 1);
}
#[test]
fn b11_contains_reflects_built_state() {
let mut kit = Kit::new();
kit.register::<AlphaModule>().expect("register");
let built = kit.build().expect("build");
assert!(built.contains::<AlphaModule>(), "Alpha should be present");
assert!(
!built.contains::<BetaModule>(),
"Beta was not registered, should be absent"
);
}
#[test]
fn b12_contains_config_reflects_set_state() {
let kit = Kit::new();
kit.set_config(7u64);
let built = kit.build().expect("build");
assert!(
built.contains_config::<u64>(),
"u64 config should be present"
);
assert!(
!built.contains_config::<i32>(),
"i32 config was never set, should be absent"
);
}
#[test]
fn b13_require_ref_returns_zero_copy_reference() {
let mut kit = Kit::new();
kit.register::<AlphaModule>().expect("register");
let built = kit.build().expect("build");
let r = built.require_ref::<AlphaModule>().expect("require_ref");
assert_eq!(**r, 1, "deref should yield the built value");
drop(r);
}
#[test]
#[serial]
fn a01_register_lazy_first_require_triggers_build() {
COUNTED_BUILDS.store(0, Ordering::SeqCst);
let mut kit = Kit::new();
kit.register_lazy::<CountedModule>().expect("register_lazy");
let built = kit.build().expect("build should succeed");
assert!(!built.contains::<CountedModule>());
assert_eq!(COUNTED_BUILDS.load(Ordering::SeqCst), 0);
let cap = built.require::<CountedModule>().expect("require");
assert_eq!(COUNTED_BUILDS.load(Ordering::SeqCst), 1);
assert_eq!(*cap, 0);
}
#[test]
#[serial]
fn a02_lazy_module_not_rebuilt_on_second_require() {
COUNTED_BUILDS.store(0, Ordering::SeqCst);
let mut kit = Kit::new();
kit.register_lazy::<CountedModule>().expect("register_lazy");
let built = kit.build().expect("build");
let cap1 = built.require::<CountedModule>().expect("first require");
let cap2 = built.require::<CountedModule>().expect("second require");
assert_eq!(*cap1, 0, "first require returns 0");
assert_eq!(*cap2, 0, "second require returns same value (cached)");
assert_eq!(
COUNTED_BUILDS.load(Ordering::SeqCst),
1,
"build_fn invoked exactly once"
);
}
struct EagerDep;
impl_module_meta!(EagerDep, "eager-dep");
impl AutoBuilder for EagerDep {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(_kit: &Kit) -> Result<Self::Capability, Self::Error> {
Ok(Arc::new(42))
}
}
struct LazyDependent;
impl_module_meta!(LazyDependent, "lazy-dependent", deps = [EagerDep]);
impl AutoBuilder for LazyDependent {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(kit: &Kit) -> Result<Self::Capability, Self::Error> {
let dep = kit.require::<EagerDep>()?;
Ok(Arc::new(*dep + 100))
}
}
#[test]
fn a03_lazy_module_depends_on_eager_module() {
let mut kit = Kit::new();
kit.register::<EagerDep>().expect("register eager dep");
kit.register_lazy::<LazyDependent>().expect("register lazy");
let built = kit.build().expect("build");
assert!(built.contains::<EagerDep>());
assert!(!built.contains::<LazyDependent>());
let cap = built.require::<LazyDependent>().expect("require lazy");
assert_eq!(*cap, 142, "42 + 100");
}
struct MultiA;
impl_module_meta!(MultiA, "multi-a");
impl AutoBuilder for MultiA {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(_kit: &Kit) -> Result<Self::Capability, Self::Error> {
Ok(Arc::new(10))
}
}
struct MultiB;
impl_module_meta!(MultiB, "multi-b");
impl AutoBuilder for MultiB {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(_kit: &Kit) -> Result<Self::Capability, Self::Error> {
Ok(Arc::new(20))
}
}
struct MultiC;
impl_module_meta!(MultiC, "multi-c");
impl AutoBuilder for MultiC {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(_kit: &Kit) -> Result<Self::Capability, Self::Error> {
Ok(Arc::new(30))
}
}
#[test]
fn a04_register_multi_require_all_preserves_order() {
let mut kit = Kit::new();
kit.register_multi::<MultiA>().expect("register multi A");
kit.register_multi::<MultiB>().expect("register multi B");
kit.register_multi::<MultiC>().expect("register multi C");
let built = kit.build().expect("build");
let caps = built.require_all::<MultiA>().expect("require_all");
assert_eq!(caps.len(), 3);
assert_eq!(*caps[0], 10, "first cap = MultiA (10)");
assert_eq!(*caps[1], 20, "second cap = MultiB (20)");
assert_eq!(*caps[2], 30, "third cap = MultiC (30)");
}
struct SingleBinding;
impl_module_meta!(SingleBinding, "single-binding");
impl AutoBuilder for SingleBinding {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(_kit: &Kit) -> Result<Self::Capability, Self::Error> {
Ok(Arc::new(999))
}
}
#[test]
fn a05_multi_binding_coexists_with_single_binding() {
let mut kit = Kit::new();
kit.register::<SingleBinding>().expect("register single");
kit.register_multi::<MultiA>().expect("register multi A");
kit.register_multi::<MultiB>().expect("register multi B");
let built = kit.build().expect("build");
let single = built.require::<SingleBinding>().expect("require single");
assert_eq!(*single, 999);
let multi = built.require_all::<MultiA>().expect("require_all");
assert_eq!(multi.len(), 2);
assert_eq!(*multi[0], 10);
assert_eq!(*multi[1], 20);
}
#[test]
#[serial]
fn a06_override_module_skips_build_fn() {
COUNTED_BUILDS.store(0, Ordering::SeqCst);
let mut kit = Kit::new();
kit.register::<CountedModule>().expect("register");
kit.override_module::<CountedModule>(Arc::new(777u32));
let built = kit.build().expect("build");
let cap = built.require::<CountedModule>().expect("require");
assert_eq!(*cap, 777, "override value should be returned");
assert_eq!(
COUNTED_BUILDS.load(Ordering::SeqCst),
0,
"build_fn should not have been invoked"
);
}
#[test]
fn a07_override_module_on_unregistered_module() {
let kit = Kit::new();
kit.override_module::<AlphaModule>(Arc::new(555u32));
let built = kit.build().expect("build");
let cap = built.require::<AlphaModule>().expect("require overridden");
assert_eq!(*cap, 555);
}
struct StrictTarget;
impl_module_meta!(StrictTarget, "strict-target", deps = [AlphaModule]);
impl AutoBuilder for StrictTarget {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(_kit: &Kit) -> Result<Self::Capability, Self::Error> {
Ok(Arc::new(0))
}
}
#[test]
fn a08_override_module_strict_succeeds_when_deps_registered() {
let mut kit = Kit::new();
kit.register::<AlphaModule>().expect("register dep");
kit.override_module_strict::<StrictTarget>(Arc::new(888u32))
.expect("strict override should succeed");
let built = kit.build().expect("build");
let cap = built.require::<StrictTarget>().expect("require");
assert_eq!(*cap, 888);
}
struct MetaNoDeps;
impl_module_meta!(MetaNoDeps, "meta-no-deps");
struct MetaDep1;
impl_module_meta!(MetaDep1, "meta-dep1");
struct MetaDep2;
impl_module_meta!(MetaDep2, "meta-dep2");
struct MetaWithDeps;
impl_module_meta!(MetaWithDeps, "meta-with-deps", deps = [MetaDep1, MetaDep2]);
struct MetaEmptyDeps;
impl_module_meta!(MetaEmptyDeps, "meta-empty-deps", deps = []);
#[test]
fn a25_impl_module_meta_macro_three_forms() {
use trait_kit::core::ModuleMeta;
assert_eq!(MetaNoDeps::NAME, "meta-no-deps");
assert!(MetaNoDeps::dependencies().is_empty());
assert_eq!(MetaWithDeps::NAME, "meta-with-deps");
let deps = MetaWithDeps::dependencies();
assert_eq!(deps.len(), 2);
assert_eq!(deps[0].0, "meta-dep1");
assert_eq!(deps[1].0, "meta-dep2");
assert_eq!(deps[0].1, TypeId::of::<MetaDep1>());
assert_eq!(deps[1].1, TypeId::of::<MetaDep2>());
assert_eq!(MetaEmptyDeps::NAME, "meta-empty-deps");
assert!(MetaEmptyDeps::dependencies().is_empty());
}
struct CycleA;
impl_module_meta!(CycleA, "cycle-a", deps = [CycleC]);
impl AutoBuilder for CycleA {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(_kit: &Kit) -> Result<Self::Capability, Self::Error> {
Ok(Arc::new(0))
}
}
struct CycleB;
impl_module_meta!(CycleB, "cycle-b", deps = [CycleA]);
impl AutoBuilder for CycleB {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(_kit: &Kit) -> Result<Self::Capability, Self::Error> {
Ok(Arc::new(0))
}
}
struct CycleC;
impl_module_meta!(CycleC, "cycle-c", deps = [CycleB]);
impl AutoBuilder for CycleC {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(_kit: &Kit) -> Result<Self::Capability, Self::Error> {
Ok(Arc::new(0))
}
}
#[test]
fn e03_three_node_cycle_detected() {
let mut kit = Kit::new();
kit.register::<CycleA>().expect("register A");
kit.register::<CycleB>().expect("register B");
kit.register::<CycleC>().expect("register C");
match kit.build() {
Err(TraitKitError::CycleDetected { cycle }) => {
assert!(
cycle.contains(&"cycle-a"),
"cycle should mention cycle-a: {cycle:?}"
);
assert!(
cycle.contains(&"cycle-b"),
"cycle should mention cycle-b: {cycle:?}"
);
assert!(
cycle.contains(&"cycle-c"),
"cycle should mention cycle-c: {cycle:?}"
);
}
other => panic!("expected CycleDetected, got: {other:?}"),
}
}
#[test]
fn e05_cross_method_duplicate_registration() {
let mut kit = Kit::new();
kit.register::<AlphaModule>().expect("register first");
let result = kit.register_lazy::<AlphaModule>();
match result {
Err(TraitKitError::AlreadyRegistered { module }) => {
assert_eq!(module, "alpha");
}
other => panic!("expected AlreadyRegistered, got: {other:?}"),
}
}
#[test]
fn e06_register_multi_duplicate_returns_already_registered() {
let mut kit = Kit::new();
kit.register_multi::<MultiA>().expect("first multi");
let result = kit.register_multi::<MultiA>();
match result {
Err(TraitKitError::AlreadyRegistered { module }) => {
assert_eq!(module, "multi-a");
}
other => panic!("expected AlreadyRegistered, got: {other:?}"),
}
}
#[test]
fn e08_build_fn_returns_err_propagates_build_failed() {
let mut kit = Kit::new();
kit.register::<FailingModule>().expect("register");
match kit.build() {
Err(TraitKitError::BuildFailed { context, source }) => {
assert_eq!(&*context, "failing");
assert!(
source.to_string().contains("intentional build failure"),
"source should mention intentional failure: {source}"
);
}
other => panic!("expected BuildFailed, got: {other:?}"),
}
}
#[test]
fn e11_lazy_build_fails_on_first_require() {
let mut kit = Kit::new();
kit.register_lazy::<FailingLazyModule>()
.expect("register_lazy");
let built = kit
.build()
.expect("build should succeed (lazy not yet built)");
match built.require::<FailingLazyModule>() {
Err(TraitKitError::BuildFailed { context, source }) => {
assert_eq!(&*context, "failing-lazy");
assert!(
source.to_string().contains("intentional build failure"),
"source should mention failure: {source}"
);
}
other => panic!("expected BuildFailed, got: {other:?}"),
}
}
#[test]
fn e19_override_module_strict_missing_dep_returns_dependency_missing() {
let mut kit = Kit::new();
let result = kit.override_module_strict::<StrictTarget>(Arc::new(0u32));
match result {
Err(TraitKitError::DependencyMissing { module, missing }) => {
assert_eq!(module, "strict-target");
assert_eq!(missing, "alpha");
}
other => panic!("expected DependencyMissing, got: {other:?}"),
}
}
#[test]
fn e26_require_all_unregistered_returns_missing_capability() {
let kit = Kit::new();
let built = kit.build().expect("build");
match built.require_all::<MultiA>() {
Err(TraitKitError::MissingCapability { key }) => {
assert_eq!(&*key, "multi-a");
}
other => panic!("expected MissingCapability, got: {other:?}"),
}
}
#[test]
fn c01_empty_dependency_graph_build_succeeds() {
let kit = Kit::new();
let built = kit.build().expect("empty build succeeds");
let debug = format!("{built:?}");
assert!(debug.contains("Kit<Ready>"));
assert!(debug.contains("modules: 0"));
}
struct SelfCycle;
impl_module_meta!(SelfCycle, "self-cycle", deps = [SelfCycle]);
impl AutoBuilder for SelfCycle {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(_kit: &Kit) -> Result<Self::Capability, Self::Error> {
Ok(Arc::new(0))
}
}
#[test]
fn c02_self_dependency_detected_as_cycle() {
let mut kit = Kit::new();
kit.register::<SelfCycle>().expect("register");
match kit.build() {
Err(TraitKitError::CycleDetected { cycle }) => {
assert!(
cycle.contains(&"self-cycle"),
"cycle should mention self-cycle: {cycle:?}"
);
}
other => panic!("expected CycleDetected, got: {other:?}"),
}
}
macro_rules! define_deep_chain {
($($name:ident),+ $(,)?) => {
$(
struct $name;
)+
};
}
define_deep_chain!(
Deep0, Deep1, Deep2, Deep3, Deep4, Deep5, Deep6, Deep7, Deep8, Deep9, Deep10
);
impl_module_meta!(Deep0, "deep-0");
impl AutoBuilder for Deep0 {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(_kit: &Kit) -> Result<Self::Capability, Self::Error> {
Ok(Arc::new(0))
}
}
impl_module_meta!(Deep1, "deep-1", deps = [Deep0]);
impl AutoBuilder for Deep1 {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(kit: &Kit) -> Result<Self::Capability, Self::Error> {
let p = kit.require::<Deep0>()?;
Ok(Arc::new(*p + 1))
}
}
impl_module_meta!(Deep2, "deep-2", deps = [Deep1]);
impl AutoBuilder for Deep2 {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(kit: &Kit) -> Result<Self::Capability, Self::Error> {
let p = kit.require::<Deep1>()?;
Ok(Arc::new(*p + 1))
}
}
impl_module_meta!(Deep3, "deep-3", deps = [Deep2]);
impl AutoBuilder for Deep3 {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(kit: &Kit) -> Result<Self::Capability, Self::Error> {
let p = kit.require::<Deep2>()?;
Ok(Arc::new(*p + 1))
}
}
impl_module_meta!(Deep4, "deep-4", deps = [Deep3]);
impl AutoBuilder for Deep4 {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(kit: &Kit) -> Result<Self::Capability, Self::Error> {
let p = kit.require::<Deep3>()?;
Ok(Arc::new(*p + 1))
}
}
impl_module_meta!(Deep5, "deep-5", deps = [Deep4]);
impl AutoBuilder for Deep5 {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(kit: &Kit) -> Result<Self::Capability, Self::Error> {
let p = kit.require::<Deep4>()?;
Ok(Arc::new(*p + 1))
}
}
impl_module_meta!(Deep6, "deep-6", deps = [Deep5]);
impl AutoBuilder for Deep6 {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(kit: &Kit) -> Result<Self::Capability, Self::Error> {
let p = kit.require::<Deep5>()?;
Ok(Arc::new(*p + 1))
}
}
impl_module_meta!(Deep7, "deep-7", deps = [Deep6]);
impl AutoBuilder for Deep7 {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(kit: &Kit) -> Result<Self::Capability, Self::Error> {
let p = kit.require::<Deep6>()?;
Ok(Arc::new(*p + 1))
}
}
impl_module_meta!(Deep8, "deep-8", deps = [Deep7]);
impl AutoBuilder for Deep8 {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(kit: &Kit) -> Result<Self::Capability, Self::Error> {
let p = kit.require::<Deep7>()?;
Ok(Arc::new(*p + 1))
}
}
impl_module_meta!(Deep9, "deep-9", deps = [Deep8]);
impl AutoBuilder for Deep9 {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(kit: &Kit) -> Result<Self::Capability, Self::Error> {
let p = kit.require::<Deep8>()?;
Ok(Arc::new(*p + 1))
}
}
impl_module_meta!(Deep10, "deep-10", deps = [Deep9]);
impl AutoBuilder for Deep10 {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(kit: &Kit) -> Result<Self::Capability, Self::Error> {
let p = kit.require::<Deep9>()?;
Ok(Arc::new(*p + 1))
}
}
#[test]
fn c03_deep_dependency_chain_10_layers_builds_in_topo_order() {
let mut kit = Kit::new();
kit.register::<Deep10>().expect("register Deep10");
kit.register::<Deep9>().expect("register Deep9");
kit.register::<Deep8>().expect("register Deep8");
kit.register::<Deep7>().expect("register Deep7");
kit.register::<Deep6>().expect("register Deep6");
kit.register::<Deep5>().expect("register Deep5");
kit.register::<Deep4>().expect("register Deep4");
kit.register::<Deep3>().expect("register Deep3");
kit.register::<Deep2>().expect("register Deep2");
kit.register::<Deep1>().expect("register Deep1");
kit.register::<Deep0>().expect("register Deep0");
let built = kit.build().expect("deep chain should build");
let cap = built.require::<Deep10>().expect("require Deep10");
assert_eq!(*cap, 10);
}
struct DiamondTop;
impl_module_meta!(DiamondTop, "diamond-top");
impl AutoBuilder for DiamondTop {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(_kit: &Kit) -> Result<Self::Capability, Self::Error> {
Ok(Arc::new(1))
}
}
struct DiamondLeft;
impl_module_meta!(DiamondLeft, "diamond-left", deps = [DiamondTop]);
impl AutoBuilder for DiamondLeft {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(kit: &Kit) -> Result<Self::Capability, Self::Error> {
let t = kit.require::<DiamondTop>()?;
Ok(Arc::new(*t + 10))
}
}
struct DiamondRight;
impl_module_meta!(DiamondRight, "diamond-right", deps = [DiamondTop]);
impl AutoBuilder for DiamondRight {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(kit: &Kit) -> Result<Self::Capability, Self::Error> {
let t = kit.require::<DiamondTop>()?;
Ok(Arc::new(*t + 20))
}
}
struct DiamondBottom;
impl_module_meta!(
DiamondBottom,
"diamond-bottom",
deps = [DiamondLeft, DiamondRight]
);
impl AutoBuilder for DiamondBottom {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(kit: &Kit) -> Result<Self::Capability, Self::Error> {
let l = kit.require::<DiamondLeft>()?;
let r = kit.require::<DiamondRight>()?;
Ok(Arc::new(*l + *r))
}
}
#[test]
fn c04_diamond_dependency_builds_successfully() {
let mut kit = Kit::new();
kit.register::<DiamondTop>().expect("register top");
kit.register::<DiamondLeft>().expect("register left");
kit.register::<DiamondRight>().expect("register right");
kit.register::<DiamondBottom>().expect("register bottom");
let built = kit.build().expect("diamond should build");
let cap = built.require::<DiamondBottom>().expect("require bottom");
assert_eq!(*cap, 32);
}
macro_rules! define_large_modules {
($($name:ident),+ $(,)?) => {
$(
struct $name;
impl ModuleMeta for $name {
const NAME: &'static str = stringify!($name);
fn dependencies() -> &'static [(&'static str, std::any::TypeId)] {
&[]
}
}
impl AutoBuilder for $name {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(_kit: &Kit) -> Result<Self::Capability, Self::Error> {
Ok(Arc::new(0))
}
}
)+
};
}
define_large_modules!(
Large00, Large01, Large02, Large03, Large04, Large05, Large06, Large07, Large08, Large09,
Large10, Large11, Large12, Large13, Large14, Large15, Large16, Large17, Large18, Large19,
Large20, Large21, Large22, Large23, Large24, Large25, Large26, Large27, Large28, Large29,
Large30, Large31, Large32, Large33, Large34, Large35, Large36, Large37, Large38, Large39,
Large40, Large41, Large42, Large43, Large44, Large45, Large46, Large47, Large48, Large49,
Large50, Large51, Large52, Large53, Large54, Large55, Large56, Large57, Large58, Large59,
Large60, Large61, Large62, Large63, Large64, Large65, Large66, Large67, Large68, Large69,
Large70, Large71, Large72, Large73, Large74, Large75, Large76, Large77, Large78, Large79,
Large80, Large81, Large82, Large83, Large84, Large85, Large86, Large87, Large88, Large89,
Large90, Large91, Large92, Large93, Large94, Large95, Large96, Large97, Large98, Large99,
);
#[test]
fn c06_large_number_of_modules_100_registers_and_builds() {
let mut kit = Kit::new();
macro_rules! register_all {
($($name:ident),+ $(,)?) => {
$(
kit.register::<$name>().expect(concat!("register ", stringify!($name)));
)+
};
}
register_all!(
Large00, Large01, Large02, Large03, Large04, Large05, Large06, Large07, Large08,
Large09, Large10, Large11, Large12, Large13, Large14, Large15, Large16, Large17,
Large18, Large19, Large20, Large21, Large22, Large23, Large24, Large25, Large26,
Large27, Large28, Large29, Large30, Large31, Large32, Large33, Large34, Large35,
Large36, Large37, Large38, Large39, Large40, Large41, Large42, Large43, Large44,
Large45, Large46, Large47, Large48, Large49, Large50, Large51, Large52, Large53,
Large54, Large55, Large56, Large57, Large58, Large59, Large60, Large61, Large62,
Large63, Large64, Large65, Large66, Large67, Large68, Large69, Large70, Large71,
Large72, Large73, Large74, Large75, Large76, Large77, Large78, Large79, Large80,
Large81, Large82, Large83, Large84, Large85, Large86, Large87, Large88, Large89,
Large90, Large91, Large92, Large93, Large94, Large95, Large96, Large97, Large98,
Large99,
);
let built = kit.build().expect("100-module build should succeed");
let first = built.require::<Large00>().expect("require Large00");
let last = built.require::<Large99>().expect("require Large99");
assert_eq!(*first, 0);
assert_eq!(*last, 0);
}
macro_rules! define_config_types {
($($name:ident),+ $(,)?) => {
$(
#[derive(Clone, Debug, PartialEq, Eq)]
struct $name(u32);
)+
};
}
define_config_types!(
Cfg00, Cfg01, Cfg02, Cfg03, Cfg04, Cfg05, Cfg06, Cfg07, Cfg08, Cfg09, Cfg10, Cfg11, Cfg12,
Cfg13, Cfg14, Cfg15, Cfg16, Cfg17, Cfg18, Cfg19,
);
#[test]
fn c07_many_config_types_20_set_and_read() {
let kit = Kit::new();
macro_rules! set_all {
($($name:ident),+ $(,)?) => {
$(
kit.set_config($name(0));
)+
};
}
set_all!(
Cfg00, Cfg01, Cfg02, Cfg03, Cfg04, Cfg05, Cfg06, Cfg07, Cfg08, Cfg09, Cfg10, Cfg11,
Cfg12, Cfg13, Cfg14, Cfg15, Cfg16, Cfg17, Cfg18, Cfg19,
);
kit.set_config(Cfg00(100));
kit.set_config(Cfg19(119));
let built = kit.build().expect("build should succeed");
assert_eq!(built.config::<Cfg00>().unwrap(), Cfg00(100));
assert_eq!(built.config::<Cfg19>().unwrap(), Cfg19(119));
assert!(built.contains_config::<Cfg00>());
assert!(built.contains_config::<Cfg09>());
assert!(built.contains_config::<Cfg19>());
}
#[test]
fn c08_config_type_conflict_last_write_wins() {
let kit = Kit::new();
kit.set_config(1i32);
kit.set_config(2i32);
kit.set_config(3i32);
let built = kit.build().expect("build");
let v: i32 = built.config().expect("config");
assert_eq!(v, 3, "last set_config should win");
}
struct SameNameA;
impl ModuleMeta for SameNameA {
const NAME: &'static str = "same-name";
fn dependencies() -> &'static [(&'static str, std::any::TypeId)] {
&[]
}
}
impl AutoBuilder for SameNameA {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(_kit: &Kit) -> Result<Self::Capability, Self::Error> {
Ok(Arc::new(1))
}
}
struct SameNameB;
impl ModuleMeta for SameNameB {
const NAME: &'static str = "same-name"; fn dependencies() -> &'static [(&'static str, std::any::TypeId)] {
&[]
}
}
impl AutoBuilder for SameNameB {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(_kit: &Kit) -> Result<Self::Capability, Self::Error> {
Ok(Arc::new(2))
}
}
#[test]
fn c10_same_name_different_typeid_both_register() {
let mut kit = Kit::new();
kit.register::<SameNameA>().expect("register SameNameA");
kit.register::<SameNameB>()
.expect("register SameNameB (same NAME, different TypeId)");
let built = kit
.build()
.expect("build should succeed (TypeId distinguishes)");
let a = built.require::<SameNameA>().expect("require SameNameA");
let b = built.require::<SameNameB>().expect("require SameNameB");
assert_eq!(*a, 1);
assert_eq!(*b, 2);
}
static C11_COUNT: AtomicUsize = AtomicUsize::new(0);
struct C11LazyModule;
impl_module_meta!(C11LazyModule, "c11-lazy");
impl AutoBuilder for C11LazyModule {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(_kit: &Kit) -> Result<Self::Capability, Self::Error> {
let n = C11_COUNT.fetch_add(1, Ordering::SeqCst);
Ok(Arc::new(n as u32))
}
}
#[test]
fn c11_build_then_require_lazy_twice_returns_cached() {
C11_COUNT.store(0, Ordering::SeqCst);
let mut kit = Kit::new();
kit.register_lazy::<C11LazyModule>().expect("register_lazy");
let built = kit.build().expect("build");
let cap1 = built.require::<C11LazyModule>().expect("first require");
let cap2 = built.require::<C11LazyModule>().expect("second require");
assert_eq!(*cap1, 0);
assert_eq!(*cap2, 0, "second require should return cached value");
assert_eq!(C11_COUNT.load(Ordering::SeqCst), 1, "build_fn invoked once");
}
struct OrderC;
impl_module_meta!(OrderC, "order-c");
impl AutoBuilder for OrderC {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(_kit: &Kit) -> Result<Self::Capability, Self::Error> {
Ok(Arc::new(3))
}
}
struct OrderA;
impl_module_meta!(OrderA, "order-a");
impl AutoBuilder for OrderA {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(_kit: &Kit) -> Result<Self::Capability, Self::Error> {
Ok(Arc::new(1))
}
}
struct OrderB;
impl_module_meta!(OrderB, "order-b");
impl AutoBuilder for OrderB {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(_kit: &Kit) -> Result<Self::Capability, Self::Error> {
Ok(Arc::new(2))
}
}
#[test]
fn c13_multi_binding_registration_order_preserved() {
let mut kit = Kit::new();
kit.register_multi::<OrderC>().expect("register OrderC");
kit.register_multi::<OrderA>().expect("register OrderA");
kit.register_multi::<OrderB>().expect("register OrderB");
let built = kit.build().expect("build");
let caps = built.require_all::<OrderC>().expect("require_all");
assert_eq!(caps.len(), 3);
assert_eq!(*caps[0], 3, "first cap = OrderC (3)");
assert_eq!(*caps[1], 1, "second cap = OrderA (1)");
assert_eq!(*caps[2], 2, "third cap = OrderB (2)");
}
struct SelfSufficient;
impl_module_meta!(SelfSufficient, "self-sufficient");
impl AutoBuilder for SelfSufficient {
type Capability = Arc<u32>;
type Error = TraitKitError;
fn build(_kit: &Kit) -> Result<Self::Capability, Self::Error> {
Ok(Arc::new(42))
}
}
#[test]
fn c22_single_self_sufficient_module_builds() {
let mut kit = Kit::new();
kit.register::<SelfSufficient>().expect("register");
let built = kit.build().expect("build");
let cap = built.require::<SelfSufficient>().expect("require");
assert_eq!(*cap, 42);
}
#[test]
fn c23_all_core_registration_methods_mixed() {
let mut kit = Kit::new();
kit.register::<AlphaModule>().expect("register Alpha");
kit.register_lazy::<BetaModule>()
.expect("register_lazy Beta");
kit.register_multi::<MultiA>().expect("register_multi A");
kit.register_multi::<MultiB>().expect("register_multi B");
kit.override_module::<SelfSufficient>(Arc::new(123u32));
kit.register::<EagerDep>().expect("register EagerDep");
kit.override_module_strict::<LazyDependent>(Arc::new(456u32))
.expect("strict override");
let built = kit.build().expect("build should succeed");
assert_eq!(*built.require::<AlphaModule>().unwrap(), 1);
assert_eq!(*built.require::<BetaModule>().unwrap(), 2);
let multi = built.require_all::<MultiA>().unwrap();
assert_eq!(multi.len(), 2);
assert_eq!(*multi[0], 10);
assert_eq!(*multi[1], 20);
assert_eq!(*built.require::<SelfSufficient>().unwrap(), 123);
assert_eq!(*built.require::<LazyDependent>().unwrap(), 456);
}
}
#[cfg(feature = "async")]
mod async_scenarios {
use super::*;
use std::future::Future;
use std::pin::Pin;
use std::task::{self, Poll};
use trait_kit::core::{AsyncAutoBuilder, ModuleMeta};
use trait_kit::impl_async_auto_builder;
fn block_on<F: Future>(future: F) -> F::Output {
let waker = task::Waker::noop();
#[allow(clippy::needless_borrow)]
let mut cx = task::Context::from_waker(&waker);
let mut future = std::pin::pin!(future);
loop {
match future.as_mut().poll(&mut cx) {
Poll::Ready(v) => return v,
Poll::Pending => std::hint::spin_loop(),
}
}
}
#[derive(Debug, thiserror::Error)]
#[error("async test error: {0}")]
struct AsyncErr(String);
#[derive(Clone, Debug, PartialEq)]
struct AsyncCap {
value: u32,
}
struct AsyncBase;
impl ModuleMeta for AsyncBase {
const NAME: &'static str = "async-base";
fn dependencies() -> &'static [(&'static str, std::any::TypeId)] {
&[]
}
}
impl AsyncAutoBuilder for AsyncBase {
type Capability = Arc<AsyncCap>;
type Error = AsyncErr;
fn build<'a>(
kit: &'a AsyncKit,
) -> Pin<Box<dyn Future<Output = Result<Self::Capability, Self::Error>> + Send + 'a>>
{
let _ = kit;
Box::pin(async move { Ok(Arc::new(AsyncCap { value: 42 })) })
}
}
struct AsyncDep;
impl ModuleMeta for AsyncDep {
const NAME: &'static str = "async-dep";
fn dependencies() -> &'static [(&'static str, std::any::TypeId)] {
static DEPS: &[(&str, std::any::TypeId)] =
&[("async-base", std::any::TypeId::of::<AsyncBase>())];
DEPS
}
}
impl AsyncAutoBuilder for AsyncDep {
type Capability = Arc<AsyncCap>;
type Error = AsyncErr;
fn build<'a>(
kit: &'a AsyncKit,
) -> Pin<Box<dyn Future<Output = Result<Self::Capability, Self::Error>> + Send + 'a>>
{
Box::pin(async move {
let base = kit
.require::<AsyncBase>()
.map_err(|e| AsyncErr(e.to_string()))?;
Ok(Arc::new(AsyncCap {
value: base.value + 100,
}))
})
}
}
struct AsyncGrandchild;
impl ModuleMeta for AsyncGrandchild {
const NAME: &'static str = "async-grandchild";
fn dependencies() -> &'static [(&'static str, std::any::TypeId)] {
static DEPS: &[(&str, std::any::TypeId)] =
&[("async-dep", std::any::TypeId::of::<AsyncDep>())];
DEPS
}
}
impl AsyncAutoBuilder for AsyncGrandchild {
type Capability = Arc<AsyncCap>;
type Error = AsyncErr;
fn build<'a>(
kit: &'a AsyncKit,
) -> Pin<Box<dyn Future<Output = Result<Self::Capability, Self::Error>> + Send + 'a>>
{
Box::pin(async move {
let dep = kit
.require::<AsyncDep>()
.map_err(|e| AsyncErr(e.to_string()))?;
Ok(Arc::new(AsyncCap {
value: dep.value + 1000,
}))
})
}
}
struct AsyncConfigReader;
impl ModuleMeta for AsyncConfigReader {
const NAME: &'static str = "async-config-reader";
fn dependencies() -> &'static [(&'static str, std::any::TypeId)] {
&[]
}
}
impl AsyncAutoBuilder for AsyncConfigReader {
type Capability = Arc<u32>;
type Error = AsyncErr;
fn build<'a>(
kit: &'a AsyncKit,
) -> Pin<Box<dyn Future<Output = Result<Self::Capability, Self::Error>> + Send + 'a>>
{
Box::pin(async move {
let v = kit.config::<u32>().map_err(|e| AsyncErr(e.to_string()))?;
Ok(Arc::new(v))
})
}
}
struct AsyncFailing;
impl ModuleMeta for AsyncFailing {
const NAME: &'static str = "async-failing";
fn dependencies() -> &'static [(&'static str, std::any::TypeId)] {
&[]
}
}
impl AsyncAutoBuilder for AsyncFailing {
type Capability = Arc<AsyncCap>;
type Error = AsyncErr;
fn build<'a>(
kit: &'a AsyncKit,
) -> Pin<Box<dyn Future<Output = Result<Self::Capability, Self::Error>> + Send + 'a>>
{
let _ = kit;
Box::pin(async move { Err(AsyncErr("intentional async failure".to_string())) })
}
}
struct AsyncUnregistered;
impl ModuleMeta for AsyncUnregistered {
const NAME: &'static str = "async-unregistered";
fn dependencies() -> &'static [(&'static str, std::any::TypeId)] {
&[]
}
}
impl AsyncAutoBuilder for AsyncUnregistered {
type Capability = Arc<()>;
type Error = AsyncErr;
fn build<'a>(
kit: &'a AsyncKit,
) -> Pin<Box<dyn Future<Output = Result<Self::Capability, Self::Error>> + Send + 'a>>
{
let _ = kit;
Box::pin(async move { Ok(Arc::new(())) })
}
}
struct AsyncMissingDep;
impl ModuleMeta for AsyncMissingDep {
const NAME: &'static str = "async-missing-dep";
fn dependencies() -> &'static [(&'static str, std::any::TypeId)] {
static DEPS: &[(&str, std::any::TypeId)] = &[(
"async-unregistered",
std::any::TypeId::of::<AsyncUnregistered>(),
)];
DEPS
}
}
impl AsyncAutoBuilder for AsyncMissingDep {
type Capability = Arc<()>;
type Error = AsyncErr;
fn build<'a>(
kit: &'a AsyncKit,
) -> Pin<Box<dyn Future<Output = Result<Self::Capability, Self::Error>> + Send + 'a>>
{
let _ = kit;
Box::pin(async move { Ok(Arc::new(())) })
}
}
struct AsyncCycleA;
impl ModuleMeta for AsyncCycleA {
const NAME: &'static str = "async-cycle-a";
fn dependencies() -> &'static [(&'static str, std::any::TypeId)] {
static DEPS: &[(&str, std::any::TypeId)] =
&[("async-cycle-b", std::any::TypeId::of::<AsyncCycleB>())];
DEPS
}
}
impl AsyncAutoBuilder for AsyncCycleA {
type Capability = Arc<()>;
type Error = AsyncErr;
fn build<'a>(
kit: &'a AsyncKit,
) -> Pin<Box<dyn Future<Output = Result<Self::Capability, Self::Error>> + Send + 'a>>
{
let _ = kit;
Box::pin(async move { Ok(Arc::new(())) })
}
}
struct AsyncCycleB;
impl ModuleMeta for AsyncCycleB {
const NAME: &'static str = "async-cycle-b";
fn dependencies() -> &'static [(&'static str, std::any::TypeId)] {
static DEPS: &[(&str, std::any::TypeId)] =
&[("async-cycle-a", std::any::TypeId::of::<AsyncCycleA>())];
DEPS
}
}
impl AsyncAutoBuilder for AsyncCycleB {
type Capability = Arc<()>;
type Error = AsyncErr;
fn build<'a>(
kit: &'a AsyncKit,
) -> Pin<Box<dyn Future<Output = Result<Self::Capability, Self::Error>> + Send + 'a>>
{
let _ = kit;
Box::pin(async move { Ok(Arc::new(())) })
}
}
struct AsyncRequireUnbuilt;
impl ModuleMeta for AsyncRequireUnbuilt {
const NAME: &'static str = "async-require-unbuilt";
fn dependencies() -> &'static [(&'static str, std::any::TypeId)] {
&[]
}
}
impl AsyncAutoBuilder for AsyncRequireUnbuilt {
type Capability = Arc<()>;
type Error = AsyncErr;
fn build<'a>(
kit: &'a AsyncKit,
) -> Pin<Box<dyn Future<Output = Result<Self::Capability, Self::Error>> + Send + 'a>>
{
Box::pin(async move {
let _ = kit
.require::<AsyncUnregistered>()
.map_err(|e| AsyncErr(e.to_string()))?;
Ok(Arc::new(()))
})
}
}
#[test]
fn a09_async_kit_basic_flow() {
let mut kit = AsyncKit::new();
kit.register::<AsyncBase>().expect("register");
let built = block_on(kit.build()).expect("build should succeed");
let cap = built.require::<AsyncBase>().expect("require");
assert_eq!(cap.value, 42);
}
#[test]
fn a10_async_cross_module_di() {
let mut kit = AsyncKit::new();
kit.register::<AsyncBase>().expect("register base");
kit.register::<AsyncDep>().expect("register dep");
let built = block_on(kit.build()).expect("build");
let cap = built.require::<AsyncDep>().expect("require dep");
assert_eq!(cap.value, 142);
}
#[test]
fn a11_async_transitive_chain_di() {
let mut kit = AsyncKit::new();
kit.register::<AsyncBase>().expect("register base");
kit.register::<AsyncDep>().expect("register dep");
kit.register::<AsyncGrandchild>()
.expect("register grandchild");
let built = block_on(kit.build()).expect("build");
let cap = built
.require::<AsyncGrandchild>()
.expect("require grandchild");
assert_eq!(cap.value, 1142);
}
#[test]
fn a12_async_config_read_in_build() {
let mut kit = AsyncKit::new();
kit.set_config(777u32);
kit.register::<AsyncConfigReader>().expect("register");
let built = block_on(kit.build()).expect("build");
let cap = built.require::<AsyncConfigReader>().expect("require");
assert_eq!(*cap, 777);
}
struct MacroAsyncModule;
impl_module_meta!(MacroAsyncModule, "macro-async");
impl_async_auto_builder!(MacroAsyncModule, Arc<AsyncCap>, AsyncErr, |kit| Box::pin(
async move {
let _ = kit;
Ok(Arc::new(AsyncCap { value: 26 }))
}
));
#[test]
fn a26_impl_async_auto_builder_macro() {
let mut kit = AsyncKit::new();
kit.register::<MacroAsyncModule>().expect("register");
let built = block_on(kit.build()).expect("build");
let cap = built.require::<MacroAsyncModule>().expect("require");
assert_eq!(cap.value, 26);
assert_eq!(MacroAsyncModule::NAME, "macro-async");
assert!(MacroAsyncModule::dependencies().is_empty());
}
#[test]
fn e23_async_build_fails_returns_build_failed() {
let mut kit = AsyncKit::new();
kit.register::<AsyncFailing>().expect("register");
match block_on(kit.build()) {
Err(TraitKitError::BuildFailed { context, source }) => {
assert_eq!(&*context, "async-failing");
assert!(
source.to_string().contains("intentional async failure"),
"source should mention failure: {source}"
);
}
other => panic!("expected BuildFailed, got: {other:?}"),
}
}
#[test]
fn e24_async_dependency_missing_returns_dependency_missing() {
let mut kit = AsyncKit::new();
kit.register::<AsyncMissingDep>().expect("register");
match block_on(kit.build()) {
Err(TraitKitError::DependencyMissing { module, missing }) => {
assert_eq!(module, "async-missing-dep");
assert_eq!(missing, "async-unregistered");
}
other => panic!("expected DependencyMissing, got: {other:?}"),
}
}
#[test]
fn e25_async_cycle_detected() {
let mut kit = AsyncKit::new();
kit.register::<AsyncCycleA>().expect("register A");
kit.register::<AsyncCycleB>().expect("register B");
match block_on(kit.build()) {
Err(TraitKitError::CycleDetected { cycle }) => {
assert!(cycle.contains(&"async-cycle-a"));
assert!(cycle.contains(&"async-cycle-b"));
}
other => panic!("expected CycleDetected, got: {other:?}"),
}
}
#[test]
fn c19_async_build_require_unregistered_returns_build_failed() {
let mut kit = AsyncKit::new();
kit.register::<AsyncRequireUnbuilt>().expect("register");
match block_on(kit.build()) {
Err(TraitKitError::BuildFailed { context, source }) => {
assert_eq!(&*context, "async-require-unbuilt");
assert!(
source.to_string().contains("async-unregistered"),
"source should mention the unregistered module: {source}"
);
}
other => panic!("expected BuildFailed, got: {other:?}"),
}
}
}
#[cfg(feature = "interface")]
mod interface_scenarios {
use super::*;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use trait_kit::core::{InterfaceBuilder, ModuleMeta};
trait Logger: 'static {
fn log(&self, msg: &str) -> String;
}
struct ConsoleLogger;
impl Logger for ConsoleLogger {
fn log(&self, msg: &str) -> String {
format!("[console] {msg}")
}
}
struct FileLogger;
impl Logger for FileLogger {
fn log(&self, msg: &str) -> String {
format!("[file] {msg}")
}
}
#[derive(Debug)]
struct InterfaceErr;
impl std::fmt::Display for InterfaceErr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "interface test error")
}
}
impl std::error::Error for InterfaceErr {}
struct ConsoleLoggerModule;
impl ModuleMeta for ConsoleLoggerModule {
const NAME: &'static str = "iface-console-logger";
fn dependencies() -> &'static [(&'static str, std::any::TypeId)] {
&[]
}
}
impl InterfaceBuilder for ConsoleLoggerModule {
type Interface = dyn Logger;
type Capability = Arc<ConsoleLogger>;
type Error = InterfaceErr;
fn build(_kit: &Kit) -> Result<Arc<ConsoleLogger>, InterfaceErr> {
Ok(Arc::new(ConsoleLogger))
}
fn into_interface(cap: Arc<ConsoleLogger>) -> Arc<dyn Logger> {
cap
}
}
struct FileLoggerModule;
impl ModuleMeta for FileLoggerModule {
const NAME: &'static str = "iface-file-logger";
fn dependencies() -> &'static [(&'static str, std::any::TypeId)] {
&[]
}
}
impl InterfaceBuilder for FileLoggerModule {
type Interface = dyn Logger;
type Capability = Arc<FileLogger>;
type Error = InterfaceErr;
fn build(_kit: &Kit) -> Result<Arc<FileLogger>, InterfaceErr> {
Ok(Arc::new(FileLogger))
}
fn into_interface(cap: Arc<FileLogger>) -> Arc<dyn Logger> {
cap
}
}
struct RegularModule;
impl ModuleMeta for RegularModule {
const NAME: &'static str = "iface-regular";
fn dependencies() -> &'static [(&'static str, std::any::TypeId)] {
&[]
}
}
impl AutoBuilder for RegularModule {
type Capability = Arc<AtomicUsize>;
type Error = TraitKitError;
fn build(_kit: &Kit) -> Result<Arc<AtomicUsize>, TraitKitError> {
Ok(Arc::new(AtomicUsize::new(42)))
}
}
#[test]
fn a19_register_as_then_resolve() {
let mut kit = Kit::new();
kit.register_as::<ConsoleLoggerModule>()
.expect("register_as");
let built = kit.build().expect("build");
let logger: Arc<dyn Logger> = built.resolve::<dyn Logger>().expect("resolve");
assert_eq!(logger.log("hello"), "[console] hello");
}
#[test]
fn a20_interface_coexists_with_regular_register() {
let mut kit = Kit::new();
kit.register::<RegularModule>().expect("register regular");
kit.register_as::<ConsoleLoggerModule>()
.expect("register_as");
let built = kit.build().expect("build");
let cap = built.require::<RegularModule>().expect("require regular");
assert_eq!(cap.load(Ordering::SeqCst), 42);
let logger: Arc<dyn Logger> = built.resolve::<dyn Logger>().expect("resolve");
assert_eq!(logger.log("coexist"), "[console] coexist");
}
#[test]
fn e07_duplicate_interface_registration_returns_already_registered() {
let mut kit = Kit::new();
kit.register_as::<ConsoleLoggerModule>()
.expect("first register_as");
match kit.register_as::<FileLoggerModule>() {
Err(TraitKitError::AlreadyRegistered { module }) => {
assert_eq!(module, "iface-file-logger");
}
Ok(_) => panic!("expected AlreadyRegistered, got Ok"),
Err(e) => panic!("expected AlreadyRegistered, got: {e:?}"),
}
}
#[test]
fn e27_resolve_unregistered_interface_returns_missing_capability() {
let kit = Kit::new();
let built = kit.build().expect("build");
match built.resolve::<dyn Logger>() {
Err(TraitKitError::MissingCapability { key }) => {
assert_eq!(&*key, "interface");
}
Ok(_) => panic!("expected MissingCapability, got Ok"),
Err(e) => panic!("expected MissingCapability, got: {e:?}"),
}
}
}
#[cfg(feature = "i18n")]
mod i18n_scenarios {
use icu::plurals::PluralCategory;
use std::cmp::Ordering;
use trait_kit::i18n::{I18nError, I18nFormatter};
#[test]
fn i01_valid_locale_creation() {
for tag in &["en-US", "zh-CN", "ja-JP"] {
let fmt = I18nFormatter::new(tag);
assert!(fmt.is_ok(), "locale {tag} should parse successfully");
}
}
#[test]
fn i02_invalid_locale_creation_returns_error() {
let result = I18nFormatter::new("not-a-valid-locale!!!");
match result {
Err(I18nError::InvalidLocale { input, .. }) => {
assert_eq!(input, "not-a-valid-locale!!!");
}
Ok(_) => panic!("expected InvalidLocale, got Ok"),
Err(e) => panic!("expected InvalidLocale, got: {e:?}"),
}
}
#[test]
fn i03_integer_formatting_en_us() {
let fmt = I18nFormatter::new("en-US").expect("en-US");
let result = fmt.format_number(1_234_567_f64).expect("format");
assert!(
result.contains(','),
"en-US integer should contain thousands separator: got '{result}'"
);
assert!(
!result.contains('.'),
"integer should not contain decimal point: got '{result}'"
);
assert!(
result.contains("1") && result.contains("234") && result.contains("567"),
"should contain digit groups: got '{result}'"
);
}
#[test]
fn i04_float_formatting_en_us() {
let fmt = I18nFormatter::new("en-US").expect("en-US");
let result = fmt.format_number(1_234_567.89_f64).expect("format");
assert!(
result.contains(','),
"en-US float should contain thousands separator: got '{result}'"
);
assert!(
result.contains('.'),
"en-US float should contain decimal point: got '{result}'"
);
assert!(
result.contains("89"),
"should contain fractional part: got '{result}'"
);
}
#[test]
fn i05_nan_formatting_returns_invalid_number() {
let fmt = I18nFormatter::new("en-US").expect("en-US");
match fmt.format_number(f64::NAN) {
Err(I18nError::InvalidNumber { .. }) => {}
other => panic!("expected InvalidNumber, got: {other:?}"),
}
}
#[test]
fn i06_infinity_formatting_returns_invalid_number() {
let fmt = I18nFormatter::new("en-US").expect("en-US");
match fmt.format_number(f64::INFINITY) {
Err(I18nError::InvalidNumber { .. }) => {}
other => panic!("expected InvalidNumber, got: {other:?}"),
}
match fmt.format_number(f64::NEG_INFINITY) {
Err(I18nError::InvalidNumber { .. }) => {}
other => panic!("expected InvalidNumber for -Infinity, got: {other:?}"),
}
}
#[test]
fn i07_negative_number_formatting_en_us() {
let fmt = I18nFormatter::new("en-US").expect("en-US");
let result = fmt.format_number(-1234.5_f64).expect("format");
assert!(
result.contains('-'),
"negative number should contain minus sign: got '{result}'"
);
assert!(
result.contains("1") && result.contains("234"),
"should contain digit groups: got '{result}'"
);
}
#[test]
fn i08_zero_formatting_en_us() {
let fmt = I18nFormatter::new("en-US").expect("en-US");
let result = fmt.format_number(0.0_f64).expect("format");
assert!(
result.contains('0'),
"zero should contain '0': got '{result}'"
);
assert!(
!result.contains(','),
"zero should not contain thousands separator: got '{result}'"
);
}
#[test]
fn i09_valid_date_formatting() {
let fmt = I18nFormatter::new("en-US").expect("en-US");
let result = fmt.format_date(2026, 7, 11).expect("format date");
assert!(
result.contains("2026"),
"date should contain year: got '{result}'"
);
assert!(!result.is_empty());
}
#[test]
fn i10_invalid_month_returns_date_error() {
let fmt = I18nFormatter::new("en-US").expect("en-US");
match fmt.format_date(2026, 13, 1) {
Err(I18nError::DateError(_)) => {}
other => panic!("expected DateError for month=13, got: {other:?}"),
}
match fmt.format_date(2026, 0, 1) {
Err(I18nError::DateError(_)) => {}
other => panic!("expected DateError for month=0, got: {other:?}"),
}
}
#[test]
fn i11_invalid_day_returns_date_error() {
let fmt = I18nFormatter::new("en-US").expect("en-US");
match fmt.format_date(2026, 2, 30) {
Err(I18nError::DateError(_)) => {}
other => panic!("expected DateError for Feb 30, got: {other:?}"),
}
match fmt.format_date(2026, 1, 0) {
Err(I18nError::DateError(_)) => {}
other => panic!("expected DateError for day=0, got: {other:?}"),
}
match fmt.format_date(2026, 1, 32) {
Err(I18nError::DateError(_)) => {}
other => panic!("expected DateError for Jan 32, got: {other:?}"),
}
}
#[test]
fn i12_leap_year_date_succeeds() {
let fmt = I18nFormatter::new("en-US").expect("en-US");
let result = fmt
.format_date(2024, 2, 29)
.expect("leap year date should be valid");
assert!(
result.contains("2024"),
"leap year date should contain year: got '{result}'"
);
match fmt.format_date(2023, 2, 29) {
Err(I18nError::DateError(_)) => {}
other => panic!("expected DateError for 2023-02-29 (non-leap), got: {other:?}"),
}
}
#[test]
fn i13_plural_category_one() {
let fmt = I18nFormatter::new("en").expect("en");
assert_eq!(
fmt.plural_category(1).expect("plural 1"),
PluralCategory::One,
"en: count=1 should be One"
);
}
#[test]
fn i14_plural_category_other() {
let fmt = I18nFormatter::new("en").expect("en");
for count in &[0u64, 2, 5, 100] {
assert_eq!(
fmt.plural_category(*count).expect("plural"),
PluralCategory::Other,
"en: count={count} should be Other"
);
}
}
#[test]
fn i15_compare_less() {
let fmt = I18nFormatter::new("en").expect("en");
assert_eq!(
fmt.compare("apple", "banana").expect("compare"),
Ordering::Less,
"apple < banana"
);
}
#[test]
fn i16_compare_greater() {
let fmt = I18nFormatter::new("en").expect("en");
assert_eq!(
fmt.compare("banana", "apple").expect("compare"),
Ordering::Greater,
"banana > apple"
);
}
#[test]
fn i17_compare_equal() {
let fmt = I18nFormatter::new("en").expect("en");
assert_eq!(
fmt.compare("apple", "apple").expect("compare"),
Ordering::Equal,
"apple == apple"
);
}
#[test]
fn i18_empty_string_compare_equal() {
let fmt = I18nFormatter::new("en").expect("en");
assert_eq!(
fmt.compare("", "").expect("compare"),
Ordering::Equal,
"'' == ''"
);
assert_eq!(
fmt.compare("", "a").expect("compare"),
Ordering::Less,
"'' < 'a'"
);
assert_eq!(
fmt.compare("a", "").expect("compare"),
Ordering::Greater,
"'a' > ''"
);
}
#[test]
fn i19_unicode_collation() {
let fmt = I18nFormatter::new("en").expect("en");
let ord = fmt.compare("café", "cafe").expect("compare");
let _ = ord;
assert_eq!(
fmt.compare("café", "café").expect("compare"),
Ordering::Equal,
"identical Unicode strings should be Equal"
);
}
#[test]
fn e20_invalid_locale_error_message() {
let result = I18nFormatter::new("!!!");
match result {
Err(I18nError::InvalidLocale { input, reason }) => {
assert_eq!(input, "!!!");
assert!(!reason.is_empty(), "reason should be non-empty");
}
Ok(_) => panic!("expected InvalidLocale, got Ok"),
Err(e) => panic!("expected InvalidLocale, got: {e:?}"),
}
}
#[test]
fn e21_non_finite_number_returns_invalid_number() {
let fmt = I18nFormatter::new("en-US").expect("en-US");
for v in &[f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
match fmt.format_number(*v) {
Err(I18nError::InvalidNumber { input, reason }) => {
assert!(
reason.contains("not finite"),
"reason should mention non-finite: got '{reason}'"
);
let _ = input;
}
other => panic!("expected InvalidNumber for {v}, got: {other:?}"),
}
}
}
#[test]
fn e22_invalid_date_returns_date_error() {
let fmt = I18nFormatter::new("en-US").expect("en-US");
match fmt.format_date(2026, 13, 1) {
Err(I18nError::DateError(msg)) => {
assert!(!msg.is_empty(), "date error message should be non-empty");
}
other => panic!("expected DateError, got: {other:?}"),
}
match fmt.format_date(2026, 1, 32) {
Err(I18nError::DateError(_)) => {}
other => panic!("expected DateError for Jan 32, got: {other:?}"),
}
}
#[test]
fn c20_empty_string_sorting() {
let fmt = I18nFormatter::new("en").expect("en");
assert_eq!(fmt.compare("", "").expect("compare"), Ordering::Equal);
assert_eq!(
fmt.compare("", "anything").expect("compare"),
Ordering::Less
);
let mut words = vec!["", "banana", "apple", ""];
words.sort_by(|a, b| fmt.compare(a, b).expect("compare"));
assert_eq!(words, vec!["", "", "apple", "banana"]);
}
#[test]
fn c21_unicode_sorting() {
let fmt = I18nFormatter::new("en").expect("en");
let mut words = vec!["café", "cafe", "apple", "Zebra", "zebra"];
words.sort_by(|a, b| fmt.compare(a, b).expect("compare"));
assert_eq!(words.len(), 5);
let apple_idx = words.iter().position(|s| *s == "apple").unwrap();
let cafe_idx = words.iter().position(|s| *s == "cafe").unwrap();
let cafè_idx = words.iter().position(|s| *s == "café").unwrap();
assert!(
apple_idx < cafe_idx,
"apple should sort before cafe: {words:?}"
);
assert!(
apple_idx < cafè_idx,
"apple should sort before café: {words:?}"
);
}
}
#[cfg(feature = "encryption")]
mod encryption_boundary {
use super::*;
use trait_kit::kit::ModuleConfig;
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
struct BoundaryConfig {
payload: String,
}
impl ModuleConfig for BoundaryConfig {
const PATH: &'static str = "config/boundary.toml";
fn default_value() -> Self {
Self {
payload: String::new(),
}
}
}
const MASTER_KEY: [u8; 32] = *b"0123456789abcdef0123456789abcdef";
#[test]
fn c14_empty_master_key_encryption_behavior() {
let kit = Kit::new();
let original = BoundaryConfig {
payload: "test-payload".to_string(),
};
let enc_result = kit.set_encrypted(&original, &[]);
match enc_result {
Ok(()) => {
assert!(kit.contains_encrypted::<BoundaryConfig>());
let built = kit.build().expect("build should succeed");
let decrypted: BoundaryConfig = built
.get_encrypted(&[])
.expect("decrypt with same empty key should succeed");
assert_eq!(decrypted, original);
}
Err(TraitKitError::BuildFailed { context, .. }) => {
assert_eq!(&*context, "set_encrypted");
}
other => panic!("unexpected result for empty master_key: {other:?}"),
}
}
#[test]
fn c15_large_config_value_encryption_1mb() {
let kit = Kit::new();
let large_payload = "x".repeat(1_048_576);
let original = BoundaryConfig {
payload: large_payload,
};
kit.set_encrypted(&original, &MASTER_KEY)
.expect("encrypt 1MB should succeed");
assert!(kit.contains_encrypted::<BoundaryConfig>());
let built = kit.build().expect("build should succeed");
let decrypted: BoundaryConfig = built
.get_encrypted(&MASTER_KEY)
.expect("decrypt 1MB should succeed");
assert_eq!(decrypted.payload.len(), 1_048_576);
assert_eq!(decrypted, original);
}
#[test]
fn e15_hkdf_failure_path_documented() {
let kit = Kit::new();
let cfg = BoundaryConfig {
payload: "hkdf-edge".to_string(),
};
let r0 = kit.set_encrypted(&cfg, &[]);
let r1 = kit.set_encrypted(&cfg, &[42u8]);
let r100 = kit.set_encrypted(&cfg, &[0u8; 100]);
let _ = r0;
let _ = r1;
let _ = r100;
}
#[test]
fn e17_tampered_ciphertext_analog_via_wrong_key() {
let kit = Kit::new();
let original = BoundaryConfig {
payload: "tamper-test".to_string(),
};
kit.set_encrypted(&original, &MASTER_KEY)
.expect("encrypt should succeed");
let built = kit.build().expect("build");
let wrong_key: [u8; 32] = *b"fedcba9876543210fedcba9876543210";
match built.get_encrypted::<BoundaryConfig>(&wrong_key) {
Err(TraitKitError::BuildFailed { context, .. }) => {
assert_eq!(&*context, "get_encrypted");
}
other => panic!(
"expected BuildFailed for wrong-key (analog of tampered ciphertext), got: {other:?}"
),
}
}
}
#[cfg(feature = "shutdown")]
mod shutdown_scenarios {
use super::*;
use std::time::Duration;
use trait_kit::kit::shutdown::{
ShutdownCoordinator, ShutdownPhase, ShutdownPhaseResult, ShutdownResult,
};
#[test]
fn s01_basic_three_phase_shutdown() {
let coord = ShutdownCoordinator::new();
let counter = Arc::new(AtomicUsize::new(0));
let c = counter.clone();
coord.register_hook(ShutdownPhase::StopRequests, move || {
c.fetch_add(1, Ordering::SeqCst);
});
let c = counter.clone();
coord.register_hook(ShutdownPhase::DrainQueue, move || {
c.fetch_add(1, Ordering::SeqCst);
});
let c = counter.clone();
coord.register_hook(ShutdownPhase::CloseConnections, move || {
c.fetch_add(1, Ordering::SeqCst);
});
let results = coord.shutdown();
assert_eq!(results.len(), 3);
assert!(results.iter().all(|r| r.is_ok()));
assert_eq!(counter.load(Ordering::SeqCst), 3);
}
#[test]
fn s02_phase_execution_order() {
let order = Arc::new(std::sync::Mutex::new(Vec::new()));
let coord = ShutdownCoordinator::new();
let o = order.clone();
coord.register_hook(ShutdownPhase::CloseConnections, move || {
o.lock().unwrap().push("close");
});
let o = order.clone();
coord.register_hook(ShutdownPhase::StopRequests, move || {
o.lock().unwrap().push("stop");
});
let o = order.clone();
coord.register_hook(ShutdownPhase::DrainQueue, move || {
o.lock().unwrap().push("drain");
});
let results = coord.shutdown();
assert!(results.iter().all(|r| r.is_ok()));
let order = order.lock().unwrap();
assert_eq!(*order, vec!["stop", "drain", "close"]);
}
#[test]
fn s03_phase_timeout_skips_remaining_hooks() {
let counter = Arc::new(AtomicUsize::new(0));
let coord = ShutdownCoordinator::new();
coord.set_phase_timeout(ShutdownPhase::DrainQueue, Duration::from_millis(10));
let c = counter.clone();
coord.register_hook(ShutdownPhase::StopRequests, move || {
c.fetch_add(1, Ordering::SeqCst);
});
let c = counter.clone();
coord.register_hook(ShutdownPhase::DrainQueue, move || {
std::thread::sleep(Duration::from_millis(50));
c.fetch_add(1, Ordering::SeqCst);
});
let c = counter.clone();
coord.register_hook(ShutdownPhase::DrainQueue, move || {
c.fetch_add(1, Ordering::SeqCst);
});
let c = counter.clone();
coord.register_hook(ShutdownPhase::CloseConnections, move || {
c.fetch_add(1, Ordering::SeqCst);
});
let results = coord.shutdown();
assert!(results[1].timed_out, "DrainQueue should have timed out");
assert_eq!(counter.load(Ordering::SeqCst), 3);
}
#[test]
fn s04_global_timeout_skips_later_phases() {
let counter = Arc::new(AtomicUsize::new(0));
let coord = ShutdownCoordinator::new();
coord.set_global_timeout(Duration::from_millis(10));
let c = counter.clone();
coord.register_hook(ShutdownPhase::StopRequests, move || {
std::thread::sleep(Duration::from_millis(50));
c.fetch_add(1, Ordering::SeqCst);
});
let c = counter.clone();
coord.register_hook(ShutdownPhase::DrainQueue, move || {
c.fetch_add(1, Ordering::SeqCst);
});
let results = coord.shutdown();
assert!(results[0].is_ok()); assert!(results[1].timed_out); assert!(results[2].timed_out); }
#[test]
fn s05_shutdown_result_into_result() {
let ok_result = ShutdownResult {
phases: vec![
ShutdownPhaseResult {
phase: ShutdownPhase::StopRequests,
timed_out: false,
elapsed: Duration::from_millis(1),
},
ShutdownPhaseResult {
phase: ShutdownPhase::DrainQueue,
timed_out: false,
elapsed: Duration::from_millis(1),
},
ShutdownPhaseResult {
phase: ShutdownPhase::CloseConnections,
timed_out: false,
elapsed: Duration::from_millis(1),
},
],
};
assert!(ok_result.into_result().is_ok());
let timeout_result = ShutdownResult {
phases: vec![
ShutdownPhaseResult {
phase: ShutdownPhase::StopRequests,
timed_out: false,
elapsed: Duration::from_millis(1),
},
ShutdownPhaseResult {
phase: ShutdownPhase::DrainQueue,
timed_out: true,
elapsed: Duration::from_secs(30),
},
ShutdownPhaseResult {
phase: ShutdownPhase::CloseConnections,
timed_out: false,
elapsed: Duration::from_millis(1),
},
],
};
let err = timeout_result.into_result().unwrap_err();
let msg = format!("{err}");
assert!(
msg.contains("drain_queue"),
"error should mention timed-out phase: {msg}"
);
}
#[test]
fn s06_hooks_not_reentrant() {
let counter = Arc::new(AtomicUsize::new(0));
let coord = ShutdownCoordinator::new();
let c = counter.clone();
coord.register_hook(ShutdownPhase::StopRequests, move || {
c.fetch_add(1, Ordering::SeqCst);
});
let _ = coord.shutdown();
assert_eq!(counter.load(Ordering::SeqCst), 1);
let _ = coord.shutdown();
assert_eq!(counter.load(Ordering::SeqCst), 1);
}
}