siderite_testkit/builder.rs
1//! [`TestClientBuilder`]: configure an [`App`] for testing, then build.
2
3use crate::TestClient;
4use siderite_core::di::{Dependency, RequestHead};
5use siderite_core::{ApiError, App, ServerError};
6use siderite_orm::Db;
7use std::future::Future;
8
9/// Applies test-only configuration to an [`App`] before it becomes a
10/// [`TestClient`].
11///
12/// A built client owns an immutable service, so overrides and databases must
13/// be set here rather than on the client.
14#[derive(Debug)]
15pub struct TestClientBuilder {
16 app: App,
17}
18
19impl TestClientBuilder {
20 pub(crate) fn new(app: App) -> Self {
21 Self { app }
22 }
23
24 /// Replace `T::resolve` with `f` for every request (see
25 /// [`App::override_dependency`]).
26 #[must_use]
27 pub fn override_dependency<T, F, Fut>(self, f: F) -> Self
28 where
29 T: Dependency,
30 F: Fn(RequestHead) -> Fut + Send + Sync + 'static,
31 Fut: Future<Output = Result<T, ApiError>> + Send + 'static,
32 {
33 Self {
34 app: self.app.override_dependency(f),
35 }
36 }
37
38 /// Replace `T::resolve` with a clone of `value`.
39 #[must_use]
40 pub fn override_value<T: Dependency + Clone>(self, value: T) -> Self {
41 Self {
42 app: self.app.override_value(value),
43 }
44 }
45
46 /// Register `db` under `alias`, replacing any database the app already
47 /// registered with that alias (see [`App::database`]).
48 #[must_use]
49 pub fn with_database(self, alias: impl Into<String>, db: Db) -> Self {
50 Self {
51 app: self.app.database(alias, db),
52 }
53 }
54
55 /// Build the client without running startup hooks.
56 ///
57 /// # Errors
58 /// The app's configuration error.
59 pub fn try_build(self) -> Result<TestClient, ServerError> {
60 TestClient::try_new(self.app)
61 }
62
63 /// Build the client, for use in tests.
64 ///
65 /// # Panics
66 /// Panics if the app is misconfigured; use [`try_build`](Self::try_build)
67 /// to assert on configuration errors.
68 pub fn build(self) -> TestClient {
69 TestClient::new(self.app)
70 }
71
72 /// Build the client and run the app's startup hooks (see
73 /// [`TestClient::start`]).
74 ///
75 /// # Errors
76 /// Configuration or startup-hook failure.
77 pub async fn start(self) -> Result<TestClient, ServerError> {
78 TestClient::start(self.app).await
79 }
80}