1use std::sync::Mutex;
2
3use sofie_core::{Result, TestClock, Timestamp};
4use sofie_db::worker::Worker;
5use sofie_db::{Db, Migrations};
6use sofie_http::App;
7use sofie_postgres::Postgres;
8use sofie_values::Seed;
9
10use crate::wiring::{AppWiring, Flags, WorkerWiring};
11use crate::{TestApp, TestDatabase, TestServer};
12
13const SEED_VARIABLE: &str = "SOFIE_TEST_SEED";
15const DEFAULT_SEED: u64 = 0x5EED;
16#[cfg(feature = "flags")]
17const FLAGS_ENVIRONMENT: &str = "test";
18
19pub async fn start<Built>(
21 app: impl FnOnce(Db) -> Built,
22 worker: impl FnOnce(Db) -> Worker,
23) -> TestApp
24where
25 Built: Future<Output = Result<App>>,
26{
27 wired(&[], app, worker).await
28}
29
30#[cfg(feature = "flags")]
31pub async fn start_with_flags<Built>(
32 app: impl FnOnce(Db, Flags) -> Built,
33 worker: impl FnOnce(Db, &Flags) -> Worker,
34) -> TestApp
35where
36 Built: Future<Output = Result<App>>,
37{
38 wired(&[], app, worker).await
39}
40
41pub(crate) async fn wired<A, W, Application, Jobs>(
42 migrations: &[Migrations],
43 app: Application,
44 worker: Jobs,
45) -> TestApp
46where
47 Application: AppWiring<A>,
48 Jobs: WorkerWiring<W>,
49{
50 sofie::fakes_from_tests();
51 let database = TestDatabase::create().await;
52 let clock = TestClock::starting_at(Timestamp::now());
53 let db = Db::new(connect(database.url())).with_clock(clock.clock());
54 sofie_db::keep_raised(&db);
55 db.schema()
56 .migrate(migrations)
57 .await
58 .expect("migrating the test database");
59 sofie::install_for_tests(&db).expect("installing the framework's packages");
60 #[cfg(feature = "mail")]
62 let outbox = sofie_mail::testing::fake(&db);
63 let flags = start_flags(&db, Application::FLAGS).await;
64 let built: Result<App> = app.build(db.clone(), flags.as_ref()).await;
65 let server = TestServer::start(built.expect("building the app")).await;
66 let postgres = connect(database.url());
67 let db = db.with_signing_keys(server.signing_keys());
69 let worker = sofie::framework_jobs(worker.build(db.clone(), flags.as_ref()));
70 sofie_db::worker::publish_heard(&worker);
71 TestApp {
72 worker,
73 #[cfg(feature = "mail")]
74 outbox,
75 db,
76 clock,
77 postgres,
78 server,
79 seed: Mutex::new(Seed::new(seed())),
80 #[cfg(feature = "flags")]
81 flags,
82 _database: database,
83 }
84}
85
86fn seed() -> u64 {
87 std::env::var(SEED_VARIABLE).map_or(DEFAULT_SEED, |seed| {
88 seed.parse()
89 .unwrap_or_else(|_| panic!("{SEED_VARIABLE} must be a number, got `{seed}`"))
90 })
91}
92
93async fn start_flags(db: &Db, wanted: bool) -> Option<Flags> {
94 #[cfg(feature = "flags")]
95 if wanted {
96 return Some(
97 Flags::start(db, FLAGS_ENVIRONMENT)
98 .await
99 .expect("starting the feature flags"),
100 );
101 }
102 #[cfg(not(feature = "flags"))]
103 let _ = (db, wanted);
104 None
105}
106
107fn connect(url: &str) -> Postgres {
108 Postgres::connect(url).expect("connecting to the test database")
109}