2018-10-07 00:24:11 +00:00
|
|
|
#[cfg(all(feature = "diesel_sqlite_pool", feature = "diesel_postgres_pool"))]
|
2018-07-21 22:11:08 +00:00
|
|
|
mod databases_tests {
|
2024-03-19 21:08:14 +00:00
|
|
|
#![allow(dead_code)]
|
|
|
|
|
|
|
|
use rocket_sync_db_pools::database;
|
2018-07-21 22:11:08 +00:00
|
|
|
|
2022-05-24 23:47:09 +00:00
|
|
|
#[database("example")]
|
|
|
|
struct ExampleDb(diesel::SqliteConnection);
|
2018-07-21 22:11:08 +00:00
|
|
|
|
|
|
|
#[database("bar")]
|
|
|
|
struct PrimaryDb(diesel::PgConnection);
|
|
|
|
}
|
2018-12-12 20:23:06 +00:00
|
|
|
|
2024-08-14 22:44:01 +00:00
|
|
|
#[cfg(feature = "memcache_pool")]
|
|
|
|
mod memcache_pool_tests {
|
|
|
|
#![allow(dead_code)]
|
|
|
|
|
|
|
|
use rocket_sync_db_pools::database;
|
|
|
|
|
|
|
|
#[database("test_db")]
|
|
|
|
struct MemcacheDb(memcache::Client);
|
|
|
|
}
|
|
|
|
|
2018-12-12 20:23:06 +00:00
|
|
|
#[cfg(test)]
|
2024-05-20 18:39:14 +00:00
|
|
|
#[cfg(all(feature = "sqlite_pool"))]
|
2018-12-12 20:23:06 +00:00
|
|
|
mod rusqlite_integration_test {
|
2021-05-25 01:58:05 +00:00
|
|
|
use rocket_sync_db_pools::{rusqlite, database};
|
2018-12-12 20:23:06 +00:00
|
|
|
|
2019-06-13 02:17:59 +00:00
|
|
|
use rusqlite::types::ToSql;
|
2019-01-27 15:52:03 +00:00
|
|
|
|
2018-12-12 20:23:06 +00:00
|
|
|
#[database("test_db")]
|
|
|
|
struct SqliteDb(pub rusqlite::Connection);
|
|
|
|
|
2020-07-11 18:17:43 +00:00
|
|
|
// Test to ensure that multiple databases of the same type can be used
|
|
|
|
#[database("test_db_2")]
|
|
|
|
struct SqliteDb2(pub rusqlite::Connection);
|
2018-12-12 20:23:06 +00:00
|
|
|
|
2020-06-14 15:57:53 +00:00
|
|
|
#[rocket::async_test]
|
2020-07-11 18:17:43 +00:00
|
|
|
async fn test_db() {
|
2020-09-03 05:41:31 +00:00
|
|
|
use rocket::figment::{Figment, util::map};
|
|
|
|
|
|
|
|
let options = map!["url" => ":memory:"];
|
2021-04-08 06:06:44 +00:00
|
|
|
let config = Figment::from(rocket::Config::debug_default())
|
2020-09-03 05:41:31 +00:00
|
|
|
.merge(("databases", map!["test_db" => &options]))
|
|
|
|
.merge(("databases", map!["test_db_2" => &options]));
|
|
|
|
|
2020-10-22 10:27:04 +00:00
|
|
|
let rocket = rocket::custom(config)
|
2020-09-03 05:41:31 +00:00
|
|
|
.attach(SqliteDb::fairing())
|
Remove 'attach' fairings. Add 'liftoff' fairings.
Launch fairings are now fallible and take the place of attach fairings,
but they are only run, as the name implies, at launch time.
This is is a fundamental shift from eager execution of set-up routines,
including the now defunct attach fairings, to lazy execution,
precipitated by the transition to `async`. The previous functionality,
while simple, caused grave issues:
1. A instance of 'Rocket' with async attach fairings requires an async
runtime to be constructed.
2. The instance is accessible in non-async contexts.
3. The async attach fairings have no runtime in which to be run.
Here's an example:
```rust
let rocket = rocket::ignite()
.attach(AttachFairing::from(|rocket| async {
Ok(rocket.manage(load_from_network::<T>().await))
}));
let state = rocket.state::<T>();
```
This had no real meaning previously yet was accepted by running the
attach fairing future in an isolated runtime. In isolation, this causes
no issue, but when attach fairing futures share reactor state with other
futures in Rocket, panics ensue.
The new Rocket application lifecycle is this:
* Build - A Rocket instance is constructed. No fairings are run.
* Ignition - All launch fairings are run.
* Liftoff - If all launch fairings succeeded, the server is started.
New 'liftoff' fairings are run in this third phase.
2021-04-01 19:32:52 +00:00
|
|
|
.attach(SqliteDb2::fairing())
|
Introduce statically-enforced 'Rocket' phasing.
The core 'Rocket' type is parameterized: 'Rocket<P: Phase>', where
'Phase' is a newly introduced, sealed marker trait. The trait is
implemented by three new marker types representing the three launch
phases: 'Build', 'Ignite', and 'Orbit'. Progression through these three
phases, in order, is enforced, as are the invariants guaranteed by each
phase. In particular, an instance of 'Rocket' is guaranteed to be in its
final configuration after the 'Build' phase and represent a running
local or public server in the 'Orbit' phase. The 'Ignite' phase serves
as an intermediate, enabling inspection of a finalized but stationary
instance. Transition between phases validates the invariants required
by the transition.
All APIs have been adjusted appropriately, requiring either an instance
of 'Rocket' in a particular phase ('Rocket<Build>', 'Rocket<Ignite>', or
'Rocket<Orbit>') or operating generically on a 'Rocket<P>'.
Documentation is also updated and substantially improved to mention
required and guaranteed invariants.
Additionally, this commit makes the following relevant changes:
* 'Rocket::ignite()' is now a public interface.
* 'Rocket::{build,custom}' methods can no longer panic.
* 'Launch' fairings are now 'ignite' fairings.
* 'Liftoff' fairings are always run, even in local mode.
* All 'ignite' fairings run concurrently at ignition.
* Launch logging occurs on launch, not any point prior.
* Launch log messages have improved formatting.
* A new launch error kind, 'Config', was added.
* A 'fairing::Result' type alias was introduced.
* 'Shutdown::shutdown()' is now 'Shutdown::notify()'.
Some internal changes were also introduced:
* Fairing 'Info' name for 'Templates' is now 'Templating'.
* Shutdown is implemented using 'tokio::sync::Notify'.
* 'Client::debug()' is used nearly universally in tests.
Resolves #1154.
Resolves #1136.
2021-04-14 02:26:45 +00:00
|
|
|
.ignite()
|
Remove 'attach' fairings. Add 'liftoff' fairings.
Launch fairings are now fallible and take the place of attach fairings,
but they are only run, as the name implies, at launch time.
This is is a fundamental shift from eager execution of set-up routines,
including the now defunct attach fairings, to lazy execution,
precipitated by the transition to `async`. The previous functionality,
while simple, caused grave issues:
1. A instance of 'Rocket' with async attach fairings requires an async
runtime to be constructed.
2. The instance is accessible in non-async contexts.
3. The async attach fairings have no runtime in which to be run.
Here's an example:
```rust
let rocket = rocket::ignite()
.attach(AttachFairing::from(|rocket| async {
Ok(rocket.manage(load_from_network::<T>().await))
}));
let state = rocket.state::<T>();
```
This had no real meaning previously yet was accepted by running the
attach fairing future in an isolated runtime. In isolation, this causes
no issue, but when attach fairing futures share reactor state with other
futures in Rocket, panics ensue.
The new Rocket application lifecycle is this:
* Build - A Rocket instance is constructed. No fairings are run.
* Ignition - All launch fairings are run.
* Liftoff - If all launch fairings succeeded, the server is started.
New 'liftoff' fairings are run in this third phase.
2021-04-01 19:32:52 +00:00
|
|
|
.await
|
|
|
|
.unwrap();
|
2020-09-03 05:41:31 +00:00
|
|
|
|
2020-10-22 10:27:04 +00:00
|
|
|
let conn = SqliteDb::get_one(&rocket).await
|
2020-09-03 05:41:31 +00:00
|
|
|
.expect("unable to get connection");
|
2020-07-11 18:17:43 +00:00
|
|
|
|
|
|
|
// Rusqlite's `transaction()` method takes `&mut self`; this tests that
|
|
|
|
// the &mut method can be called inside the closure passed to `run()`.
|
|
|
|
conn.run(|conn| {
|
|
|
|
let tx = conn.transaction().unwrap();
|
2020-10-22 10:27:04 +00:00
|
|
|
let _: i32 = tx.query_row(
|
|
|
|
"SELECT 1", &[] as &[&dyn ToSql], |row| row.get(0)
|
|
|
|
).expect("get row");
|
|
|
|
|
2020-07-11 18:17:43 +00:00
|
|
|
tx.commit().expect("committed transaction");
|
|
|
|
}).await;
|
2018-12-12 20:23:06 +00:00
|
|
|
}
|
|
|
|
}
|
2020-11-05 04:05:31 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
Introduce sentinels: auto-discovered launch abort.
Sentinels resolve a long-standing usability and functional correctness
issue in Rocket: starting an application with guards and/or responders
that depend on state that isn't available. The canonical example is the
'State' guard. Prior to this commit, an application with routes that
queried unmanaged state via 'State' would fail at runtime. With this
commit, the application refuses to launch with a detailed error message.
The 'Sentinel' docs explains it as:
A sentinel, automatically run on ignition, can trigger a launch
abort should an instance fail to meet arbitrary conditions. Every
type that appears in a mounted route's type signature is eligible to
be a sentinel. Of these, those that implement 'Sentinel' have their
'abort()' method invoked automatically, immediately after ignition,
once for each unique type. Sentinels inspect the finalized instance
of 'Rocket' and can trigger a launch abort by returning 'true'.
The following types are now sentinels:
* 'contrib::databases::Connection' (any '#[database]' type)
* 'contrib::templates::Metadata'
* 'contrib::templates::Template'
* 'core::State'
The following are "specialized" sentinels, which allow sentinel
discovery even through type aliases:
* 'Option<T>', 'Debug<T>' if 'T: Sentinel'
* 'Result<T, E>', 'Either<T, E>' if 'T: Sentinel', 'E: Sentinel'
Closes #464.
2021-04-16 08:23:15 +00:00
|
|
|
mod sentinel_and_runtime_test {
|
Introduce statically-enforced 'Rocket' phasing.
The core 'Rocket' type is parameterized: 'Rocket<P: Phase>', where
'Phase' is a newly introduced, sealed marker trait. The trait is
implemented by three new marker types representing the three launch
phases: 'Build', 'Ignite', and 'Orbit'. Progression through these three
phases, in order, is enforced, as are the invariants guaranteed by each
phase. In particular, an instance of 'Rocket' is guaranteed to be in its
final configuration after the 'Build' phase and represent a running
local or public server in the 'Orbit' phase. The 'Ignite' phase serves
as an intermediate, enabling inspection of a finalized but stationary
instance. Transition between phases validates the invariants required
by the transition.
All APIs have been adjusted appropriately, requiring either an instance
of 'Rocket' in a particular phase ('Rocket<Build>', 'Rocket<Ignite>', or
'Rocket<Orbit>') or operating generically on a 'Rocket<P>'.
Documentation is also updated and substantially improved to mention
required and guaranteed invariants.
Additionally, this commit makes the following relevant changes:
* 'Rocket::ignite()' is now a public interface.
* 'Rocket::{build,custom}' methods can no longer panic.
* 'Launch' fairings are now 'ignite' fairings.
* 'Liftoff' fairings are always run, even in local mode.
* All 'ignite' fairings run concurrently at ignition.
* Launch logging occurs on launch, not any point prior.
* Launch log messages have improved formatting.
* A new launch error kind, 'Config', was added.
* A 'fairing::Result' type alias was introduced.
* 'Shutdown::shutdown()' is now 'Shutdown::notify()'.
Some internal changes were also introduced:
* Fairing 'Info' name for 'Templates' is now 'Templating'.
* Shutdown is implemented using 'tokio::sync::Notify'.
* 'Client::debug()' is used nearly universally in tests.
Resolves #1154.
Resolves #1136.
2021-04-14 02:26:45 +00:00
|
|
|
use rocket::{Rocket, Build};
|
2020-11-05 04:05:31 +00:00
|
|
|
use r2d2::{ManageConnection, Pool};
|
2021-05-25 01:58:05 +00:00
|
|
|
use rocket_sync_db_pools::{database, Poolable, PoolResult};
|
2020-11-05 04:05:31 +00:00
|
|
|
use tokio::runtime::Runtime;
|
|
|
|
|
2024-05-20 18:39:14 +00:00
|
|
|
#[allow(dead_code)]
|
2020-11-05 04:05:31 +00:00
|
|
|
struct ContainsRuntime(Runtime);
|
|
|
|
struct TestConnection;
|
|
|
|
|
|
|
|
impl ManageConnection for ContainsRuntime {
|
|
|
|
type Connection = TestConnection;
|
|
|
|
type Error = std::convert::Infallible;
|
|
|
|
|
|
|
|
fn connect(&self) -> Result<Self::Connection, Self::Error> {
|
|
|
|
Ok(TestConnection)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_valid(&self, _conn: &mut Self::Connection) -> Result<(), Self::Error> {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn has_broken(&self, _conn: &mut Self::Connection) -> bool {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Poolable for TestConnection {
|
|
|
|
type Manager = ContainsRuntime;
|
|
|
|
type Error = ();
|
|
|
|
|
Introduce statically-enforced 'Rocket' phasing.
The core 'Rocket' type is parameterized: 'Rocket<P: Phase>', where
'Phase' is a newly introduced, sealed marker trait. The trait is
implemented by three new marker types representing the three launch
phases: 'Build', 'Ignite', and 'Orbit'. Progression through these three
phases, in order, is enforced, as are the invariants guaranteed by each
phase. In particular, an instance of 'Rocket' is guaranteed to be in its
final configuration after the 'Build' phase and represent a running
local or public server in the 'Orbit' phase. The 'Ignite' phase serves
as an intermediate, enabling inspection of a finalized but stationary
instance. Transition between phases validates the invariants required
by the transition.
All APIs have been adjusted appropriately, requiring either an instance
of 'Rocket' in a particular phase ('Rocket<Build>', 'Rocket<Ignite>', or
'Rocket<Orbit>') or operating generically on a 'Rocket<P>'.
Documentation is also updated and substantially improved to mention
required and guaranteed invariants.
Additionally, this commit makes the following relevant changes:
* 'Rocket::ignite()' is now a public interface.
* 'Rocket::{build,custom}' methods can no longer panic.
* 'Launch' fairings are now 'ignite' fairings.
* 'Liftoff' fairings are always run, even in local mode.
* All 'ignite' fairings run concurrently at ignition.
* Launch logging occurs on launch, not any point prior.
* Launch log messages have improved formatting.
* A new launch error kind, 'Config', was added.
* A 'fairing::Result' type alias was introduced.
* 'Shutdown::shutdown()' is now 'Shutdown::notify()'.
Some internal changes were also introduced:
* Fairing 'Info' name for 'Templates' is now 'Templating'.
* Shutdown is implemented using 'tokio::sync::Notify'.
* 'Client::debug()' is used nearly universally in tests.
Resolves #1154.
Resolves #1136.
2021-04-14 02:26:45 +00:00
|
|
|
fn pool(_db_name: &str, _rocket: &Rocket<Build>) -> PoolResult<Self> {
|
2020-11-05 04:05:31 +00:00
|
|
|
let manager = ContainsRuntime(tokio::runtime::Runtime::new().unwrap());
|
|
|
|
Ok(Pool::builder().build(manager)?)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[database("test_db")]
|
|
|
|
struct TestDb(TestConnection);
|
|
|
|
|
|
|
|
#[rocket::async_test]
|
|
|
|
async fn test_drop_runtime() {
|
|
|
|
use rocket::figment::{Figment, util::map};
|
|
|
|
|
2021-04-08 06:06:44 +00:00
|
|
|
let config = Figment::from(rocket::Config::debug_default())
|
2020-11-05 04:05:31 +00:00
|
|
|
.merge(("databases", map!["test_db" => map!["url" => ""]]));
|
|
|
|
|
|
|
|
let rocket = rocket::custom(config).attach(TestDb::fairing());
|
|
|
|
drop(rocket);
|
|
|
|
}
|
Introduce sentinels: auto-discovered launch abort.
Sentinels resolve a long-standing usability and functional correctness
issue in Rocket: starting an application with guards and/or responders
that depend on state that isn't available. The canonical example is the
'State' guard. Prior to this commit, an application with routes that
queried unmanaged state via 'State' would fail at runtime. With this
commit, the application refuses to launch with a detailed error message.
The 'Sentinel' docs explains it as:
A sentinel, automatically run on ignition, can trigger a launch
abort should an instance fail to meet arbitrary conditions. Every
type that appears in a mounted route's type signature is eligible to
be a sentinel. Of these, those that implement 'Sentinel' have their
'abort()' method invoked automatically, immediately after ignition,
once for each unique type. Sentinels inspect the finalized instance
of 'Rocket' and can trigger a launch abort by returning 'true'.
The following types are now sentinels:
* 'contrib::databases::Connection' (any '#[database]' type)
* 'contrib::templates::Metadata'
* 'contrib::templates::Template'
* 'core::State'
The following are "specialized" sentinels, which allow sentinel
discovery even through type aliases:
* 'Option<T>', 'Debug<T>' if 'T: Sentinel'
* 'Result<T, E>', 'Either<T, E>' if 'T: Sentinel', 'E: Sentinel'
Closes #464.
2021-04-16 08:23:15 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_sentinel() {
|
|
|
|
use rocket::{*, local::blocking::Client, error::ErrorKind::SentinelAborts};
|
|
|
|
|
|
|
|
#[get("/")]
|
|
|
|
fn use_db(_db: TestDb) {}
|
|
|
|
|
|
|
|
let err = Client::debug_with(routes![use_db]).unwrap_err();
|
|
|
|
assert!(matches!(err.kind(), SentinelAborts(vec) if vec.len() == 1));
|
|
|
|
}
|
2020-11-05 04:05:31 +00:00
|
|
|
}
|