2018-10-07 00:24:11 +00:00
|
|
|
#[cfg(feature = "templates")]
|
2018-06-28 15:55:15 +00:00
|
|
|
#[macro_use] extern crate rocket;
|
2018-10-07 00:24:11 +00:00
|
|
|
|
2018-06-03 18:36:10 +00:00
|
|
|
#[cfg(feature = "templates")]
|
|
|
|
mod templates_tests {
|
2018-08-13 07:46:53 +00:00
|
|
|
use std::path::{Path, PathBuf};
|
2017-01-12 10:52:23 +00:00
|
|
|
|
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-09-03 05:41:31 +00:00
|
|
|
use rocket::config::Config;
|
2018-10-07 00:24:11 +00:00
|
|
|
use rocket_contrib::templates::{Template, Metadata};
|
2018-07-11 00:07:53 +00:00
|
|
|
|
2018-07-09 12:36:40 +00:00
|
|
|
#[get("/<engine>/<name>")]
|
UTF-8 routes. Forms revamp. Temp files. Capped.
So. Many. Changes.
This is an insane commit: simultaneously one of the best (because of all
the wonderful improvements!) and one of the worst (because it is just
massive) in the project's history.
Routing:
* All UTF-8 characters are accepted everywhere in route paths. (#998)
* `path` is now `uri` in `route` attribute: `#[route(GET, path = "..")]`
becomes `#[route(GET, uri = "..")]`.
Forms Revamp
* All form related types now reside in a new `form` module.
* Multipart forms are supported. (resolves #106)
* Collections are supported in forms and queries. (resolves #205)
* Nested structures in forms and queries are supported. (resolves #313)
* Form fields can be ad-hoc validated with `#[field(validate = expr)]`.
* `FromFormValue` is now `FromFormField`, blanket implements `FromForm`.
* Form field values are always percent-decoded apriori.
Temporary Files
* A new `TempFile` data and form guard allows streaming data directly to a
file which can then be persisted.
* A new `temp_dir` config parameter specifies where to store `TempFile`.
* The limits `file` and `file/$ext`, where `$ext` is the file extension,
determines the data limit for a `TempFile`.
Capped
* A new `Capped` type is used to indicate when data has been truncated due to
incoming data limits. It allows checking whether data is complete or
truncated.
* `DataStream` methods return `Capped` types.
* `DataStream` API has been revamped to account for `Capped` types.
* Several `Capped<T>` types implement `FromData`, `FromForm`.
* HTTP 413 (Payload Too Large) errors are now returned when data limits are
exceeded. (resolves #972)
Hierarchical Limits
* Data limits are now hierarchical, delimited with `/`. A limit of `a/b/c`
falls back to `a/b` then `a`.
Core
* `&RawStr` no longer implements `FromParam`.
* `&str` implements `FromParam`, `FromData`, `FromForm`.
* `FromTransformedData` was removed.
* `FromData` gained a lifetime for use with request-local data.
* The default error HTML is more compact.
* `&Config` is a request guard.
* The `DataStream` interface was entirely revamped.
* `State` is only exported via `rocket::State`.
* A `request::local_cache!()` macro was added for storing values in
request-local cache without consideration for type uniqueness by using a
locally generated anonymous type.
* `Request::get_param()` is now `Request::param()`.
* `Request::get_segments()` is now `Request::segments()`, takes a range.
* `Request::get_query_value()` is now `Request::query_value()`, can parse any
`FromForm` including sequences.
* `std::io::Error` implements `Responder` like `Debug<std::io::Error>`.
* `(Status, R)` where `R: Responder` implements `Responder` by overriding the
`Status` of `R`.
* The name of a route is printed first during route matching.
* `FlashMessage` now only has one lifetime generic.
HTTP
* `RawStr` implements `serde::{Serialize, Deserialize}`.
* `RawStr` implements _many_ more methods, in particular, those related to the
`Pattern` API.
* `RawStr::from_str()` is now `RawStr::new()`.
* `RawStr::url_decode()` and `RawStr::url_decode_lossy()` only allocate as
necessary, return `Cow`.
* `Status` implements `Default` with `Status::Ok`.
* `Status` implements `PartialEq`, `Eq`, `Hash`, `PartialOrd`, `Ord`.
* Authority and origin part of `Absolute` can be modified with new
`Absolute::{with,set}_authority()`, `Absolute::{with,set}_origin()` methods.
* `Origin::segments()` was removed in favor of methods split into query and
path parts and into raw and decoded versions.
* The `Segments` iterator is smarter, returns decoded `&str` items.
* `Segments::into_path_buf()` is now `Segments::to_path_buf()`.
* A new `QuerySegments` is the analogous query segment iterator.
* Once set, `expires` on private cookies is not overwritten. (resolves #1506)
* `Origin::path()` and `Origin::query()` return `&RawStr`, not `&str`.
Codegen
* Preserve more spans in `uri!` macro.
* Preserve spans `FromForm` field types.
* All dynamic parameters in a query string must typecheck as `FromForm`.
* `FromFormValue` derive removed; `FromFormField` added.
* The `form` `FromForm` and `FromFormField` field attribute is now named
`field`. `#[form(field = ..)]` is now `#[field(name = ..)]`.
Contrib
* `Json` implements `FromForm`.
* `MsgPack` implements `FromForm`.
* The `json!` macro is exported as `rocket_contrib::json::json!`.
* Added clarifying docs to `StaticFiles`.
Examples
* `form_validation` and `form_kitchen_sink` removed in favor of `forms`.
* The `hello_world` example uses unicode in paths.
* The `json` example only allocates as necessary.
Internal
* Codegen uses new `exports` module with the following conventions:
- Locals starts with `__` and are lowercased.
- Rocket modules start with `_` and are lowercased.
- `std` types start with `_` and are titlecased.
- Rocket types are titlecased.
* A `header` module was added to `http`, contains header types.
* `SAFETY` is used as doc-string keyword for `unsafe` related comments.
* The `Uri` parser no longer recognizes Rocket route URIs.
2020-10-30 03:50:06 +00:00
|
|
|
fn template_check(md: Metadata<'_>, engine: &str, name: &str) -> Option<()> {
|
2018-07-11 00:07:53 +00:00
|
|
|
match md.contains_template(&format!("{}/{}", engine, name)) {
|
|
|
|
true => Some(()),
|
|
|
|
false => None
|
|
|
|
}
|
2018-07-09 12:36:40 +00:00
|
|
|
}
|
2017-12-29 03:53:15 +00:00
|
|
|
|
2018-10-23 20:22:26 +00:00
|
|
|
#[get("/is_reloading")]
|
2019-06-13 02:17:59 +00:00
|
|
|
fn is_reloading(md: Metadata<'_>) -> Option<()> {
|
2018-10-23 20:22:26 +00:00
|
|
|
if md.reloading() { Some(()) } else { None }
|
|
|
|
}
|
|
|
|
|
2018-06-03 18:36:10 +00:00
|
|
|
fn template_root() -> PathBuf {
|
2018-08-13 07:46:53 +00:00
|
|
|
Path::new(env!("CARGO_MANIFEST_DIR")).join("tests").join("templates")
|
2018-06-03 18:36:10 +00:00
|
|
|
}
|
2017-01-12 10:52:23 +00:00
|
|
|
|
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 rocket() -> Rocket<Build> {
|
2020-09-03 05:41:31 +00:00
|
|
|
rocket::custom(Config::figment().merge(("template_dir", template_root())))
|
|
|
|
.attach(Template::fairing())
|
2018-10-23 20:22:26 +00:00
|
|
|
.mount("/", routes![template_check, is_reloading])
|
2018-06-03 18:36:10 +00:00
|
|
|
}
|
2017-12-29 03:53:15 +00:00
|
|
|
|
2020-11-04 10:39:40 +00:00
|
|
|
#[test]
|
|
|
|
fn test_callback_error() {
|
|
|
|
use rocket::{local::blocking::Client, error::ErrorKind::FailedFairings};
|
|
|
|
|
2021-04-08 08:07:52 +00:00
|
|
|
let rocket = rocket::build().attach(Template::try_custom(|_| {
|
2020-11-04 10:39:40 +00:00
|
|
|
Err("error reloading templates!".into())
|
|
|
|
}));
|
|
|
|
|
Test 'secret_key' validation, now on pre-launch.
Prior to this commit, it was not possible to test Rocket crates in
production mode without setting a global secret key or bypassing secret
key checking - the testing script did the latter. The consequence is
that it became impossible to test secret key related failures because
the tests passed regardless.
This commit undoes this. As a consequence, all tests are now aware of
the difference between debug and release configurations, the latter of
which validates 'secret_key' by default. New 'Client::debug()' and
'Client::debug_with()' simplify creating an instance of 'Client' with
configuration in debug mode to avoid undesired test failures.
The summary of changes in this commit are:
* Config 'secret_key' success and failure are now tested.
* 'secret_key' validation was moved to pre-launch from 'Config:from()'.
* 'Config::from()' only extracts the config.
* Added 'Config::try_from()' for non-panicking extraction.
* 'Config' now knows the profile it was extracted from.
* The 'Config' provider sets a profile of 'Config.profile'.
* 'Rocket', 'Client', 'Fairings', implement 'Debug'.
* 'fairing::Info' implements 'Copy', 'Clone'.
* 'Fairings' keeps track of, logs attach fairings.
* 'Rocket::reconfigure()' was added to allow modifying a config.
Internally, the testing script was refactored to properly test the
codebase with the new changes. In particular, it no longer sets a rustc
'cfg' to avoid secret-key checking.
Resolves #1543.
Fixes #1564.
2021-03-09 08:07:43 +00:00
|
|
|
let error = Client::debug(rocket).expect_err("client failure");
|
|
|
|
match error.kind() {
|
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
|
|
|
FailedFairings(failures) => assert_eq!(failures[0].name, "Templating"),
|
Test 'secret_key' validation, now on pre-launch.
Prior to this commit, it was not possible to test Rocket crates in
production mode without setting a global secret key or bypassing secret
key checking - the testing script did the latter. The consequence is
that it became impossible to test secret key related failures because
the tests passed regardless.
This commit undoes this. As a consequence, all tests are now aware of
the difference between debug and release configurations, the latter of
which validates 'secret_key' by default. New 'Client::debug()' and
'Client::debug_with()' simplify creating an instance of 'Client' with
configuration in debug mode to avoid undesired test failures.
The summary of changes in this commit are:
* Config 'secret_key' success and failure are now tested.
* 'secret_key' validation was moved to pre-launch from 'Config:from()'.
* 'Config::from()' only extracts the config.
* Added 'Config::try_from()' for non-panicking extraction.
* 'Config' now knows the profile it was extracted from.
* The 'Config' provider sets a profile of 'Config.profile'.
* 'Rocket', 'Client', 'Fairings', implement 'Debug'.
* 'fairing::Info' implements 'Copy', 'Clone'.
* 'Fairings' keeps track of, logs attach fairings.
* 'Rocket::reconfigure()' was added to allow modifying a config.
Internally, the testing script was refactored to properly test the
codebase with the new changes. In particular, it no longer sets a rustc
'cfg' to avoid secret-key checking.
Resolves #1543.
Fixes #1564.
2021-03-09 08:07:43 +00:00
|
|
|
_ => panic!("Wrong kind of launch error"),
|
2020-11-04 10:39:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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};
|
|
|
|
|
|
|
|
let err = Client::debug_with(routes![is_reloading]).unwrap_err();
|
|
|
|
assert!(matches!(err.kind(), SentinelAborts(vec) if vec.len() == 1));
|
|
|
|
|
|
|
|
let err = Client::debug_with(routes![is_reloading, template_check]).unwrap_err();
|
|
|
|
assert!(matches!(err.kind(), SentinelAborts(vec) if vec.len() == 2));
|
|
|
|
|
|
|
|
#[get("/")]
|
|
|
|
fn return_template() -> Template {
|
|
|
|
Template::render("foo", ())
|
|
|
|
}
|
|
|
|
|
|
|
|
let err = Client::debug_with(routes![return_template]).unwrap_err();
|
|
|
|
assert!(matches!(err.kind(), SentinelAborts(vec) if vec.len() == 1));
|
|
|
|
|
|
|
|
#[get("/")]
|
|
|
|
fn return_opt_template() -> Option<Template> {
|
|
|
|
Some(Template::render("foo", ()))
|
|
|
|
}
|
|
|
|
|
|
|
|
let err = Client::debug_with(routes![return_opt_template]).unwrap_err();
|
|
|
|
assert!(matches!(err.kind(), SentinelAborts(vec) if vec.len() == 1));
|
|
|
|
|
|
|
|
#[derive(rocket::Responder)]
|
|
|
|
struct MyThing<T>(T);
|
|
|
|
|
|
|
|
#[get("/")]
|
|
|
|
fn return_custom_template() -> MyThing<Template> {
|
|
|
|
MyThing(Template::render("foo", ()))
|
|
|
|
}
|
|
|
|
|
|
|
|
let err = Client::debug_with(routes![return_custom_template]).unwrap_err();
|
|
|
|
assert!(matches!(err.kind(), SentinelAborts(vec) if vec.len() == 1));
|
|
|
|
|
|
|
|
#[derive(rocket::Responder)]
|
|
|
|
struct MyOkayThing<T>(Option<T>);
|
|
|
|
|
|
|
|
impl<T> rocket::Sentinel for MyOkayThing<T> {
|
|
|
|
fn abort(_: &Rocket<rocket::Ignite>) -> bool {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/")]
|
|
|
|
fn always_ok_sentinel() -> MyOkayThing<Template> {
|
|
|
|
MyOkayThing(None)
|
|
|
|
}
|
|
|
|
|
|
|
|
Client::debug_with(routes![always_ok_sentinel]).expect("no sentinel abort");
|
|
|
|
}
|
|
|
|
|
2018-06-03 18:36:10 +00:00
|
|
|
#[cfg(feature = "tera_templates")]
|
|
|
|
mod tera_tests {
|
|
|
|
use super::*;
|
|
|
|
use std::collections::HashMap;
|
2018-07-09 12:36:40 +00:00
|
|
|
use rocket::http::Status;
|
2020-07-05 18:35:36 +00:00
|
|
|
use rocket::local::blocking::Client;
|
2017-01-12 10:52:23 +00:00
|
|
|
|
2018-06-03 18:36:10 +00:00
|
|
|
const UNESCAPED_EXPECTED: &'static str
|
|
|
|
= "\nh_start\ntitle: _test_\nh_end\n\n\n<script />\n\nfoot\n";
|
|
|
|
const ESCAPED_EXPECTED: &'static str
|
|
|
|
= "\nh_start\ntitle: _test_\nh_end\n\n\n<script />\n\nfoot\n";
|
2017-01-12 10:52:23 +00:00
|
|
|
|
2020-10-22 10:27:04 +00:00
|
|
|
#[test]
|
|
|
|
fn test_tera_templates() {
|
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
|
|
|
let client = Client::debug(rocket()).unwrap();
|
2018-06-03 18:36:10 +00:00
|
|
|
let mut map = HashMap::new();
|
|
|
|
map.insert("title", "_test_");
|
|
|
|
map.insert("content", "<script />");
|
2017-01-12 10:52:23 +00:00
|
|
|
|
2018-06-03 18:36:10 +00:00
|
|
|
// Test with a txt file, which shouldn't escape.
|
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
|
|
|
let template = Template::show(client.rocket(), "tera/txt_test", &map);
|
2018-06-03 18:36:10 +00:00
|
|
|
assert_eq!(template, Some(UNESCAPED_EXPECTED.into()));
|
2017-01-12 10:52:23 +00:00
|
|
|
|
2018-06-03 18:36:10 +00:00
|
|
|
// Now with an HTML file, which should.
|
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
|
|
|
let template = Template::show(client.rocket(), "tera/html_test", &map);
|
2018-06-03 18:36:10 +00:00
|
|
|
assert_eq!(template, Some(ESCAPED_EXPECTED.into()));
|
|
|
|
}
|
2018-07-09 12:36:40 +00:00
|
|
|
|
2020-07-05 18:35:36 +00:00
|
|
|
#[test]
|
|
|
|
fn test_template_metadata_with_tera() {
|
Test 'secret_key' validation, now on pre-launch.
Prior to this commit, it was not possible to test Rocket crates in
production mode without setting a global secret key or bypassing secret
key checking - the testing script did the latter. The consequence is
that it became impossible to test secret key related failures because
the tests passed regardless.
This commit undoes this. As a consequence, all tests are now aware of
the difference between debug and release configurations, the latter of
which validates 'secret_key' by default. New 'Client::debug()' and
'Client::debug_with()' simplify creating an instance of 'Client' with
configuration in debug mode to avoid undesired test failures.
The summary of changes in this commit are:
* Config 'secret_key' success and failure are now tested.
* 'secret_key' validation was moved to pre-launch from 'Config:from()'.
* 'Config::from()' only extracts the config.
* Added 'Config::try_from()' for non-panicking extraction.
* 'Config' now knows the profile it was extracted from.
* The 'Config' provider sets a profile of 'Config.profile'.
* 'Rocket', 'Client', 'Fairings', implement 'Debug'.
* 'fairing::Info' implements 'Copy', 'Clone'.
* 'Fairings' keeps track of, logs attach fairings.
* 'Rocket::reconfigure()' was added to allow modifying a config.
Internally, the testing script was refactored to properly test the
codebase with the new changes. In particular, it no longer sets a rustc
'cfg' to avoid secret-key checking.
Resolves #1543.
Fixes #1564.
2021-03-09 08:07:43 +00:00
|
|
|
let client = Client::debug(rocket()).unwrap();
|
2018-07-09 12:36:40 +00:00
|
|
|
|
2020-07-05 18:35:36 +00:00
|
|
|
let response = client.get("/tera/txt_test").dispatch();
|
2018-07-09 12:36:40 +00:00
|
|
|
assert_eq!(response.status(), Status::Ok);
|
|
|
|
|
2020-07-05 18:35:36 +00:00
|
|
|
let response = client.get("/tera/html_test").dispatch();
|
2018-07-09 12:36:40 +00:00
|
|
|
assert_eq!(response.status(), Status::Ok);
|
|
|
|
|
2020-07-05 18:35:36 +00:00
|
|
|
let response = client.get("/tera/not_existing").dispatch();
|
2018-07-11 00:07:53 +00:00
|
|
|
assert_eq!(response.status(), Status::NotFound);
|
|
|
|
|
2020-07-05 18:35:36 +00:00
|
|
|
let response = client.get("/hbs/txt_test").dispatch();
|
2018-07-11 00:07:53 +00:00
|
|
|
assert_eq!(response.status(), Status::NotFound);
|
2018-07-09 12:36:40 +00:00
|
|
|
}
|
2017-01-12 10:52:23 +00:00
|
|
|
}
|
|
|
|
|
2018-06-03 18:36:10 +00:00
|
|
|
#[cfg(feature = "handlebars_templates")]
|
|
|
|
mod handlebars_tests {
|
|
|
|
use super::*;
|
|
|
|
use std::collections::HashMap;
|
2018-07-09 12:36:40 +00:00
|
|
|
use rocket::http::Status;
|
2020-07-05 18:35:36 +00:00
|
|
|
use rocket::local::blocking::Client;
|
2017-01-12 10:52:23 +00:00
|
|
|
|
2018-06-03 18:36:10 +00:00
|
|
|
const EXPECTED: &'static str
|
|
|
|
= "Hello _test_!\n\n<main> <script /> hi </main>\nDone.\n\n";
|
2017-01-12 10:52:23 +00:00
|
|
|
|
2020-10-22 10:27:04 +00:00
|
|
|
#[test]
|
|
|
|
fn test_handlebars_templates() {
|
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
|
|
|
let client = Client::debug(rocket()).unwrap();
|
2018-06-03 18:36:10 +00:00
|
|
|
let mut map = HashMap::new();
|
|
|
|
map.insert("title", "_test_");
|
|
|
|
map.insert("content", "<script /> hi");
|
2017-01-12 10:52:23 +00:00
|
|
|
|
2018-06-03 18:36:10 +00:00
|
|
|
// Test with a txt file, which shouldn't escape.
|
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
|
|
|
let template = Template::show(client.rocket(), "hbs/test", &map);
|
2018-06-03 18:36:10 +00:00
|
|
|
assert_eq!(template, Some(EXPECTED.into()));
|
|
|
|
}
|
2018-07-09 12:36:40 +00:00
|
|
|
|
2020-07-05 18:35:36 +00:00
|
|
|
#[test]
|
|
|
|
fn test_template_metadata_with_handlebars() {
|
Test 'secret_key' validation, now on pre-launch.
Prior to this commit, it was not possible to test Rocket crates in
production mode without setting a global secret key or bypassing secret
key checking - the testing script did the latter. The consequence is
that it became impossible to test secret key related failures because
the tests passed regardless.
This commit undoes this. As a consequence, all tests are now aware of
the difference between debug and release configurations, the latter of
which validates 'secret_key' by default. New 'Client::debug()' and
'Client::debug_with()' simplify creating an instance of 'Client' with
configuration in debug mode to avoid undesired test failures.
The summary of changes in this commit are:
* Config 'secret_key' success and failure are now tested.
* 'secret_key' validation was moved to pre-launch from 'Config:from()'.
* 'Config::from()' only extracts the config.
* Added 'Config::try_from()' for non-panicking extraction.
* 'Config' now knows the profile it was extracted from.
* The 'Config' provider sets a profile of 'Config.profile'.
* 'Rocket', 'Client', 'Fairings', implement 'Debug'.
* 'fairing::Info' implements 'Copy', 'Clone'.
* 'Fairings' keeps track of, logs attach fairings.
* 'Rocket::reconfigure()' was added to allow modifying a config.
Internally, the testing script was refactored to properly test the
codebase with the new changes. In particular, it no longer sets a rustc
'cfg' to avoid secret-key checking.
Resolves #1543.
Fixes #1564.
2021-03-09 08:07:43 +00:00
|
|
|
let client = Client::debug(rocket()).unwrap();
|
2018-07-09 12:36:40 +00:00
|
|
|
|
2020-07-05 18:35:36 +00:00
|
|
|
let response = client.get("/hbs/test").dispatch();
|
2018-07-09 12:36:40 +00:00
|
|
|
assert_eq!(response.status(), Status::Ok);
|
|
|
|
|
2020-07-05 18:35:36 +00:00
|
|
|
let response = client.get("/hbs/not_existing").dispatch();
|
2018-07-11 00:07:53 +00:00
|
|
|
assert_eq!(response.status(), Status::NotFound);
|
|
|
|
|
2020-07-05 18:35:36 +00:00
|
|
|
let response = client.get("/tera/test").dispatch();
|
2018-07-11 00:07:53 +00:00
|
|
|
assert_eq!(response.status(), Status::NotFound);
|
2018-07-09 12:36:40 +00:00
|
|
|
}
|
2018-01-10 19:27:51 +00:00
|
|
|
|
2020-07-05 18:35:36 +00:00
|
|
|
#[test]
|
2018-01-10 19:27:51 +00:00
|
|
|
#[cfg(debug_assertions)]
|
2020-07-05 18:35:36 +00:00
|
|
|
fn test_template_reload() {
|
2018-01-10 19:27:51 +00:00
|
|
|
use std::fs::File;
|
|
|
|
use std::io::Write;
|
|
|
|
use std::time::Duration;
|
|
|
|
|
2020-07-05 18:35:36 +00:00
|
|
|
use rocket::local::blocking::Client;
|
2018-01-10 19:27:51 +00:00
|
|
|
|
|
|
|
const RELOAD_TEMPLATE: &str = "hbs/reload";
|
|
|
|
const INITIAL_TEXT: &str = "initial";
|
|
|
|
const NEW_TEXT: &str = "reload";
|
|
|
|
|
|
|
|
fn write_file(path: &Path, text: &str) {
|
|
|
|
let mut file = File::create(path).expect("open file");
|
|
|
|
file.write_all(text.as_bytes()).expect("write file");
|
|
|
|
file.sync_all().expect("sync file");
|
|
|
|
}
|
|
|
|
|
|
|
|
// set up the template before initializing the Rocket instance so
|
|
|
|
// that it will be picked up in the initial loading of templates.
|
2018-08-13 07:46:53 +00:00
|
|
|
let reload_path = template_root().join("hbs").join("reload.txt.hbs");
|
2018-01-10 19:27:51 +00:00
|
|
|
write_file(&reload_path, INITIAL_TEXT);
|
|
|
|
|
2018-10-23 20:22:26 +00:00
|
|
|
// set up the client. if we can't reload templates, then just quit
|
Test 'secret_key' validation, now on pre-launch.
Prior to this commit, it was not possible to test Rocket crates in
production mode without setting a global secret key or bypassing secret
key checking - the testing script did the latter. The consequence is
that it became impossible to test secret key related failures because
the tests passed regardless.
This commit undoes this. As a consequence, all tests are now aware of
the difference between debug and release configurations, the latter of
which validates 'secret_key' by default. New 'Client::debug()' and
'Client::debug_with()' simplify creating an instance of 'Client' with
configuration in debug mode to avoid undesired test failures.
The summary of changes in this commit are:
* Config 'secret_key' success and failure are now tested.
* 'secret_key' validation was moved to pre-launch from 'Config:from()'.
* 'Config::from()' only extracts the config.
* Added 'Config::try_from()' for non-panicking extraction.
* 'Config' now knows the profile it was extracted from.
* The 'Config' provider sets a profile of 'Config.profile'.
* 'Rocket', 'Client', 'Fairings', implement 'Debug'.
* 'fairing::Info' implements 'Copy', 'Clone'.
* 'Fairings' keeps track of, logs attach fairings.
* 'Rocket::reconfigure()' was added to allow modifying a config.
Internally, the testing script was refactored to properly test the
codebase with the new changes. In particular, it no longer sets a rustc
'cfg' to avoid secret-key checking.
Resolves #1543.
Fixes #1564.
2021-03-09 08:07:43 +00:00
|
|
|
let client = Client::debug(rocket()).unwrap();
|
2020-07-05 18:35:36 +00:00
|
|
|
let res = client.get("/is_reloading").dispatch();
|
2018-10-23 20:22:26 +00:00
|
|
|
if res.status() != Status::Ok {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// verify that the initial content is correct
|
2020-10-22 10:27:04 +00:00
|
|
|
let initial_rendered = Template::show(client.rocket(), RELOAD_TEMPLATE, ());
|
2018-01-10 19:27:51 +00:00
|
|
|
assert_eq!(initial_rendered, Some(INITIAL_TEXT.into()));
|
|
|
|
|
|
|
|
// write a change to the file
|
|
|
|
write_file(&reload_path, NEW_TEXT);
|
|
|
|
|
|
|
|
for _ in 0..6 {
|
|
|
|
// dispatch any request to trigger a template reload
|
2020-07-05 18:35:36 +00:00
|
|
|
client.get("/").dispatch();
|
2018-01-10 19:27:51 +00:00
|
|
|
|
|
|
|
// if the new content is correct, we are done
|
2020-10-22 10:27:04 +00:00
|
|
|
let new_rendered = Template::show(client.rocket(), RELOAD_TEMPLATE, ());
|
2018-01-10 19:27:51 +00:00
|
|
|
if new_rendered == Some(NEW_TEXT.into()) {
|
2018-10-24 06:57:40 +00:00
|
|
|
write_file(&reload_path, INITIAL_TEXT);
|
2018-01-10 19:27:51 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// otherwise, retry a few times, waiting 250ms in between
|
2020-07-05 18:35:36 +00:00
|
|
|
std::thread::sleep(Duration::from_millis(250));
|
2018-01-10 19:27:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
panic!("failed to reload modified template in 1.5s");
|
|
|
|
}
|
2017-01-12 10:52:23 +00:00
|
|
|
}
|
|
|
|
}
|