#![allow(dead_code)] #![allow(unused_variables)] use std::future::Future; mod sync { use entrait::*; #[entrait(Foo)] fn foo(deps: &impl Bar) -> String { deps.bar() } #[entrait(Bar)] fn bar(_: &()) -> String { "string".to_string() } } trait Lol { fn hei(&self) -> impl Future + Send; } mod auth { use entrait::*; use unimock::*; type Error = (); #[derive(Clone)] pub struct User { username: String, hash: String, } #[entrait(GetUsername)] async fn get_username( rt: &impl Authenticate, id: u32, password: &str, ) -> Result { let user = rt.authenticate(id, password).await?; Ok(user.username) } #[entrait(Authenticate, mock_api=AuthenticateMock)] async fn authenticate( deps: &(impl FetchUser + VerifyPassword), id: u32, password: &str, ) -> Result { let user = deps.fetch_user(id).ok_or(())?; if deps.verify_password(password, &user.hash) { Ok(user) } else { Err(()) } } #[entrait(FetchUser, mock_api=FetchUserMock)] fn fetch_user(_: &T, _id: u32) -> Option { Some(User { username: "name".into(), hash: "h4sh".into(), }) } #[entrait(VerifyPassword, mock_api=VerifyPasswordMock)] fn verify_password(_: &T, _password: &str, _hash: &str) -> bool { true } #[tokio::test] async fn test_get_username() { let username = get_username( &Unimock::new(AuthenticateMock.stub(|each| { each.call(matching!(_, _)).returns(Ok(User { username: "foobar".into(), hash: "h4sh".into(), })); })), 42, "pw", ) .await .unwrap(); assert_eq!("foobar", username); } #[tokio::test] async fn test_authenticate() { let mocks = Unimock::new(( FetchUserMock.each_call(matching!(42)).returns(Some(User { username: "foobar".into(), hash: "h4sh".into(), })), VerifyPasswordMock .each_call(matching!("pw", "h4sh")) .returns(true) .once(), )); let user = authenticate(&mocks, 42, "pw").await.unwrap(); assert_eq!("foobar", user.username); } #[tokio::test] async fn test_partial_no_overrides() { let user = authenticate(&Unimock::new_partial(()), 42, "pw") .await .unwrap(); assert_eq!("name", user.username); } #[tokio::test] async fn test_impl() { assert_eq!( "name", Impl::new(()).get_username(42, "password").await.unwrap() ); } } mod multi_mock { use entrait::*; use unimock::*; #[entrait(Bar, mock_api = BarMock)] async fn bar(_: &A) -> i32 { unimplemented!() } #[entrait(Baz, mock_api = BazMock)] async fn baz(_: &A) -> i32 { unimplemented!() } mod inline_bounds { use super::*; use entrait::entrait; #[entrait(Sum)] async fn sum(a: &A) -> i32 { a.bar().await + a.baz().await } #[tokio::test] async fn test_mock() { let mock = Unimock::new(( BarMock.each_call(matching!()).returns(40), BazMock.each_call(matching!()).returns(2), )); let result = sum(&mock).await; assert_eq!(42, result); } } mod where_bounds { use super::*; #[entrait(Sum)] async fn sum(a: &A) -> i32 where A: Bar + Baz, { a.bar().await + a.baz().await } #[tokio::test] async fn test_mock() { assert_eq!( 42, sum(&Unimock::new(( BarMock.each_call(matching!()).returns(40), BazMock.each_call(matching!()).returns(2), ))) .await ); } } mod impl_trait_bounds { use super::*; #[entrait(Sum)] async fn sum(a: &(impl Bar + Baz)) -> i32 { a.bar().await + a.baz().await } #[tokio::test] async fn test_mock() { assert_eq!( 42, sum(&Unimock::new(( BarMock.each_call(matching!()).returns(40), BazMock.each_call(matching!()).returns(2), ))) .await ); } } } mod future_send_bound_for_tokio_spawn { use std::future::Future; use entrait::*; use unimock::*; #[entrait(Spawning)] async fn spawning(deps: &(impl Bar + Clone + Send + Sync + 'static)) -> i32 { let handles = [deps.clone(), deps.clone()] .into_iter() .map(|deps| tokio::spawn(async move { deps.bar().await })); let mut result = 0; for handle in handles { result += handle.await.unwrap(); } result } // important: takes T _by value_ #[entrait(Bar, mock_api = BarMock)] async fn bar(_: T) -> i32 { 1 } #[tokio::test] async fn test_spawning_impl() { let result = spawning(&implementation::Impl::new(())).await; assert_eq!(2, result); } #[tokio::test] async fn test_spawning_partial_unmocked() { let result = spawning(&Unimock::new_partial(())).await; assert_eq!(2, result); } #[tokio::test] async fn test_spawning_override_bar() { let result = spawning(&Unimock::new_partial(( BarMock.next_call(matching!()).returns(1).once(), BarMock.next_call(matching!()).returns(2).once(), ))) .await; assert_eq!(3, result); } } mod more_async { use entrait::*; use unimock::*; struct State(u32); #[entrait(Foo)] async fn foo(a: &A) -> u32 { a.bar().await } #[entrait(Bar, mock_api = BarMock)] async fn bar(state: &State) -> u32 { state.0 } #[tokio::test] async fn test() { let state = Impl::new(State(42)); let result = state.foo().await; assert_eq!(42, result); } #[tokio::test] async fn test_mock() { let result = foo(&Unimock::new( BarMock.each_call(matching!()).returns(84_u32), )) .await; assert_eq!(84, result); } #[tokio::test] async fn test_impl() { let state = Impl::new(State(42)); assert_eq!(42, state.foo().await); } } mod async_no_deps_etc { use entrait::*; use unimock::*; struct App; #[entrait(Foo, mock_api = FooMock)] async fn foo(deps: &impl Bar) -> i32 { deps.bar().await } #[entrait(Bar, mock_api = BarMock)] async fn bar(deps: &impl Baz) -> i32 { deps.baz().await } #[entrait(Baz, mock_api = BazMock)] async fn baz(_: &App) -> i32 { 42 } #[entrait(NoDeps, no_deps)] async fn no_deps(arg: i32) -> i32 { arg } #[entrait(Borrow1)] async fn borrow1(_: &impl Bar) -> &i32 { panic!() } #[entrait(Borrow2)] async fn borrow2<'a, 'b>(_: &'a impl Bar, _arg: &'b i32) -> &'a i32 { panic!() } #[entrait(Borrow3)] async fn borrow3<'a>(_: &impl Bar, arg: &'a i32) -> &'a i32 { arg } #[allow(unused)] pub struct Borrowing<'a>(&'a i32); #[entrait(Borrow4)] async fn borrow4<'a>(_: &'a impl Bar, _arg: &i32) -> Borrowing<'a> { panic!() } #[tokio::test] async fn test_it() { let app = ::entrait::Impl::new(App); let _ = app.foo().await; } #[tokio::test] async fn mock_it() { let unimock = Unimock::new_partial(BazMock.each_call(matching!()).returns(42)); let answer = unimock.foo().await; assert_eq!(42, answer); } } mod generics { use entrait::*; use std::any::Any; use unimock::*; #[entrait(GenericDepsGenericReturn, mock_api = Mock1)] fn generic_deps_generic_return(_: &impl Any) -> T { Default::default() } #[entrait(ConcreteDepsGenericReturn, mock_api = Mock2)] fn concrete_deps_generic_return(_: &()) -> T { Default::default() } #[entrait(GenericDepsGenericParam, mock_api = Mock3)] fn generic_deps_generic_param(_: &impl Any, _arg: T) -> i32 { 42 } #[entrait(ConcreteDepsGenericParam, mock_api = Mock4)] fn concrete_deps_generic_param(_: &(), _arg: T) -> i32 { 42 } #[test] fn generic_mock_fns() { let s: String = Unimock::new( Mock1 .with_types::() .some_call(matching!()) .returns("hey".to_string()), ) .generic_deps_generic_return(); assert_eq!("hey".to_string(), s); assert_eq!( 42, Unimock::new( Mock3 .with_types::() .some_call(matching!("hey")) .returns(42), ) .generic_deps_generic_param("hey".to_string()) ); assert_eq!( 42, Unimock::new( Mock3 .with_types::() .some_call(matching!(1337)) .returns(42), ) .generic_deps_generic_param(1337), ); } #[entrait(ConcreteDepsGenericReturnWhere)] fn concrete_deps_generic_return_where(_: &()) -> T where T: Default, { Default::default() } #[entrait(GenericDepsGenericReturnWhere)] fn generic_deps_generic_return_where(_: &impl Any) -> T where T: Default, { Default::default() } } mod destructuring_params { use entrait::entrait; pub struct NewType(T); #[entrait(Destructuring1)] fn destructuring1(_: &impl std::any::Any, NewType(num): NewType) {} #[entrait(Destructuring2)] fn destructuring2( _: &impl std::any::Any, NewType(num1): NewType, NewType(num2): NewType, ) { } #[entrait(DestructuringNoDeps, no_deps)] fn destructuring_no_deps(NewType(num1): NewType, NewType(num2): NewType) {} // Should become (arg1, _arg1) #[entrait(DestructuringNoDepsMixedConflict, no_deps)] fn destructuring_no_deps_mixed_confict(arg1: NewType, NewType(num2): NewType) {} // Should become (arg1, _arg1) #[entrait(WildcardParams, no_deps)] fn wildcard_params(_: i32, _: i32) {} } mod entrait_for_trait_unimock { use entrait::*; use unimock::*; #[entrait(mock_api=TraitMock)] trait Trait { fn method1(&self) -> i32; } #[test] fn entraited_trait_should_have_unimock_impl() { assert_eq!( 42, Unimock::new(TraitMock::method1.each_call(matching!()).returns(42)).method1() ); } #[test] #[should_panic( expected = "Trait::method1 cannot be unmocked as there is no function available to call." )] fn entraited_trait_should_not_be_unmockable() { Unimock::new_partial(()).method1(); } } mod naming_conflict_between_fn_and_param { use entrait::*; #[entrait(Foo)] fn foo(_: &T, foo: i32) {} } mod module { use entrait::*; use std::any::Any; use unimock::*; #[entrait(pub Foo, mock_api=FooMock)] fn foo(_: &impl Any) -> i32 { 7 } #[entrait(pub BarBaz, mock_api=BarBazMock)] mod bar_baz { pub fn bar(deps: &impl super::Foo) -> i32 { deps.foo() } } fn takes_barbaz(deps: &impl BarBaz) -> i32 { deps.bar() } #[test] fn test_it() { let deps = Unimock::new(bar_baz::BarBazMock::bar.each_call(matching!()).returns(42)); assert_eq!(42, takes_barbaz(&deps)); } } mod module_async { use entrait::*; #[entrait(pub Mixed)] mod mixed { use std::any::Any; pub fn bar(_: &impl Any) {} pub async fn bar_async(_: &impl Any) {} } async fn takes_mixed(deps: &impl Mixed) { deps.bar(); let _ = deps.bar_async().await; } #[entrait(pub MultiAsync)] mod multi_async { use std::any::Any; pub async fn foo(_: &impl Any) {} pub async fn bar(_: &impl Any) {} } async fn takes_multi_async(deps: &impl MultiAsync) { deps.foo().await; deps.bar().await; } } mod module_generic { use entrait::*; #[entrait(M1)] mod m1 { use std::any::Any; pub fn a(_deps: &impl Any, arg: T) {} pub fn b(_deps: &impl Any, arg: U) {} } } #[test] fn level_without_mock_support() { use entrait::*; use unimock::*; #[entrait(A)] fn a(deps: &(impl B + C)) { deps.b(); deps.c(); } #[entrait(B, mock_api=BMock)] fn b(deps: &impl std::any::Any) {} #[entrait(CImpl, delegate_by = DelegateC)] pub trait C { fn c(&self); } #[entrait(pub D)] mod d {} fn takes_a(a: &impl A) {} fn takes_b(b: &impl B) {} fn takes_c(b: &impl C) {} takes_a(&Unimock::new(())); takes_b(&Unimock::new(())); takes_c(&Unimock::new(())); } // unimock issue https://github.com/audunhalland/unimock/issues/40 mod arg_mutation_and_result_alias { use std::process::ExitStatus; use entrait::*; use unimock::*; type MyResult = Result; #[entrait(pub Exec, mock_api=ExecMock)] fn exec( _deps: &impl std::any::Any, command: &mut std::process::Command, ) -> MyResult<(ExitStatus, String)> { Err(()) } #[test] #[should_panic(expected = "Dead mocks should be removed")] fn test() { use std::os::unix::process::ExitStatusExt; Unimock::new( ExecMock .each_call(matching!((command) if command.get_program() == "editor")) .answers(&|_, _| Ok((ExitStatusExt::from_raw(1), String::new()))), ); } }