1use axum::{Extension, Router};
2use http::Method;
3use nidus_config::Config;
4use nidus_core::{
5 Container, LifecycleHook, LifecycleRunner, Module, ModuleDefinition, Nidus, RequestScope,
6 Result,
7};
8use nidus_http::middleware::request_scope_layer;
9use std::sync::Arc;
10
11use crate::request::TestRequest;
12
13#[derive(Clone)]
41pub struct TestApp {
42 router: Router,
43 container: Arc<Container>,
44 config: Config,
45 lifecycle: Arc<LifecycleRunner>,
46}
47
48impl TestApp {
49 pub fn bootstrap<M>() -> Result<TestAppBuilder>
51 where
52 M: Module,
53 {
54 Self::bootstrap_with_router::<M>(Router::new())
55 }
56
57 pub fn bootstrap_with_router<M>(router: Router) -> Result<TestAppBuilder>
59 where
60 M: Module,
61 {
62 Nidus::bootstrap::<M>()?;
63 Ok(Self::builder(router))
64 }
65
66 pub fn bootstrap_with_modules<M, I>(modules: I) -> Result<TestAppBuilder>
68 where
69 M: Module,
70 I: IntoIterator<Item = ModuleDefinition>,
71 {
72 Self::bootstrap_with_modules_and_router::<M, I>(modules, Router::new())
73 }
74
75 pub fn bootstrap_with_modules_and_router<M, I>(
77 modules: I,
78 router: Router,
79 ) -> Result<TestAppBuilder>
80 where
81 M: Module,
82 I: IntoIterator<Item = ModuleDefinition>,
83 {
84 Nidus::bootstrap_with_modules::<M, I>(modules)?;
85 Ok(Self::builder(router))
86 }
87
88 pub fn from_router(router: Router) -> Self {
94 let container = Arc::new(Container::new());
95 Self {
96 router: router.layer(Extension(Arc::clone(&container))),
97 container,
98 config: Config::new(),
99 lifecycle: Arc::new(LifecycleRunner::new()),
100 }
101 }
102
103 pub fn builder(router: Router) -> TestAppBuilder {
105 TestAppBuilder {
106 router,
107 container: Container::new(),
108 config: Config::new(),
109 lifecycle: LifecycleRunner::new(),
110 request_scope: false,
111 }
112 }
113
114 pub fn get(&self, path: impl Into<String>) -> TestRequest {
116 self.request(Method::GET, path)
117 }
118
119 pub fn post(&self, path: impl Into<String>) -> TestRequest {
121 self.request(Method::POST, path)
122 }
123
124 pub fn put(&self, path: impl Into<String>) -> TestRequest {
126 self.request(Method::PUT, path)
127 }
128
129 pub fn patch(&self, path: impl Into<String>) -> TestRequest {
131 self.request(Method::PATCH, path)
132 }
133
134 pub fn delete(&self, path: impl Into<String>) -> TestRequest {
136 self.request(Method::DELETE, path)
137 }
138
139 pub fn request(&self, method: Method, path: impl Into<String>) -> TestRequest {
145 TestRequest::new(self.router.clone(), method, path.into())
146 }
147
148 pub fn resolve<T>(&self) -> Result<Arc<T>>
150 where
151 T: Send + Sync + 'static,
152 {
153 self.container.resolve::<T>()
154 }
155
156 pub fn request_scope(&self) -> RequestScope<'_> {
158 self.container.request_scope()
159 }
160
161 pub fn config(&self) -> &Config {
163 &self.config
164 }
165
166 pub async fn shutdown(&self) -> Result<()> {
168 self.lifecycle.shutdown().await
169 }
170}
171
172pub struct TestAppBuilder {
178 router: Router,
179 container: Container,
180 config: Config,
181 lifecycle: LifecycleRunner,
182 request_scope: bool,
183}
184
185impl TestAppBuilder {
186 pub fn provider<T>(mut self, value: T) -> Result<Self>
188 where
189 T: Send + Sync + 'static,
190 {
191 self.container.register_singleton(value)?;
192 Ok(self)
193 }
194
195 pub fn transient_provider<T, F>(mut self, factory: F) -> Result<Self>
197 where
198 T: Send + Sync + 'static,
199 F: Fn(&Container) -> Result<T> + Send + Sync + 'static,
200 {
201 self.container.register_transient::<T, F>(factory)?;
202 Ok(self)
203 }
204
205 pub fn request_provider<T, F>(mut self, factory: F) -> Result<Self>
207 where
208 T: Send + Sync + 'static,
209 F: Fn(&Container) -> Result<T> + Send + Sync + 'static,
210 {
211 self.container.register_request::<T, F>(factory)?;
212 Ok(self)
213 }
214
215 pub fn request_scoped_provider<T, F>(mut self, factory: F) -> Result<Self>
223 where
224 T: Send + Sync + 'static,
225 F: for<'scope> Fn(&RequestScope<'scope>) -> Result<T> + Send + Sync + 'static,
226 {
227 self.container.register_request_scoped::<T, F>(factory)?;
228 Ok(self)
229 }
230
231 pub fn with_request_scope(mut self) -> Self {
238 self.request_scope = true;
239 self
240 }
241
242 pub fn override_provider<T>(mut self, value: T) -> Result<Self>
244 where
245 T: Send + Sync + 'static,
246 {
247 self.container.override_singleton(value)?;
248 Ok(self)
249 }
250
251 pub fn config(mut self, config: Config) -> Self {
253 self.config = config;
254 self
255 }
256
257 pub fn lifecycle_hook<H>(mut self, hook: H) -> Self
259 where
260 H: LifecycleHook,
261 {
262 self.lifecycle = self.lifecycle.hook(hook);
263 self
264 }
265
266 pub fn build(self) -> TestApp {
268 let container = Arc::new(self.container);
269 let mut router = self.router.layer(Extension(Arc::clone(&container)));
270 if self.request_scope {
271 router = router.layer(request_scope_layer(Arc::clone(&container)));
272 }
273 TestApp {
274 router,
275 container,
276 config: self.config,
277 lifecycle: Arc::new(self.lifecycle),
278 }
279 }
280
281 pub async fn build_started(self) -> Result<TestApp> {
283 self.lifecycle.startup().await?;
284 Ok(self.build())
285 }
286}