1use std::sync::Mutex;
2use std::time::Duration;
3
4use sofie_core::{SqlValue, TestClock};
5use sofie_db::worker::Worker;
6use sofie_db::{Db, Id};
7use sofie_http::{Area, RoutePath};
8
9use crate::caller::{TestCaller, TestPrincipal};
10use crate::fixture::AggregateFixture;
11use crate::request::{TestRequest, TestSession};
12
13pub struct TestApp {
14 pub(crate) db: Db,
15 pub(crate) clock: TestClock,
16 pub(crate) worker: Worker,
17 pub(crate) postgres: sofie_postgres::Postgres,
18 pub(crate) server: crate::TestServer,
19 pub(crate) seed: Mutex<sofie_values::Seed>,
20 #[cfg(feature = "flags")]
21 pub(crate) flags: Option<sofie_flags::Flags>,
22 #[cfg(feature = "mail")]
23 pub(crate) outbox: sofie_mail::testing::Outbox,
24 pub(crate) _database: crate::TestDatabase,
26}
27
28impl TestApp {
29 #[must_use]
30 pub fn db(&self) -> &Db {
31 &self.db
32 }
33
34 #[must_use]
35 pub fn clock(&self) -> &TestClock {
36 &self.clock
37 }
38
39 #[cfg(feature = "flags")]
40 #[must_use]
41 pub fn flags(&self) -> &sofie_flags::Flags {
42 self.flags
43 .as_ref()
44 .expect("the app was started with flags: `start_with_flags` or a wiring taking them")
45 }
46
47 pub async fn drain(&self) -> usize {
49 crate::messages::drain(self).await
50 }
51
52 pub async fn advance(&self, by: Duration) -> usize {
54 crate::messages::advance(self, by).await
55 }
56
57 pub async fn query(&self, sql: &str, params: &[SqlValue]) -> Vec<Vec<SqlValue>> {
58 sofie_db::query_values(&self.postgres, sql, params)
59 .await
60 .unwrap_or_else(|error| panic!("running `{sql}`: {error}"))
61 }
62
63 #[must_use]
65 pub fn origin(&self) -> &str {
66 self.server.origin()
67 }
68
69 pub fn get(&self, path: &str) -> TestRequest {
70 self.server.get(path)
71 }
72
73 pub fn post(&self, path: &str) -> TestRequest {
74 self.server.post(path)
75 }
76
77 pub fn request(&self, route: &RoutePath) -> TestRequest {
78 self.server.request(route.method().clone(), route.path())
79 }
80
81 pub fn as_<A: Area>(&self, principal: &TestPrincipal<A>) -> TestCaller<'_> {
82 TestCaller::new(self, principal)
83 }
84
85 #[must_use]
87 pub fn principal_of<A: Area>(&self, session: &TestSession) -> Option<TestPrincipal<A>> {
88 let claims = self.server.open(session, crate::messages::now(self))?;
89 (claims.area() == A::NAME).then(|| TestPrincipal::new(Id::from_uuid(claims.principal())))
90 }
91
92 pub fn fixture<A: sofie_db::Create>(&self) -> AggregateFixture<'_, A> {
93 AggregateFixture::new(self)
94 }
95}