diff --git a/tests/testsuite/config.rs b/tests/testsuite/config.rs
index f5b14875..253cc0f7 100644
--- a/tests/testsuite/config.rs
+++ b/tests/testsuite/config.rs
@@ -204,6 +204,139 @@ ERROR Invalid configuration file
unknown field `title`, expected `edition`
+"#]]);
+ });
+}
+
+// An invalid top-level key in the environment.
+#[test]
+fn env_invalid_config_key() {
+ BookTest::from_dir("config/empty").run("build", |cmd| {
+ cmd.env("MDBOOK_FOO", "testing")
+ .expect_failure()
+ .expect_stdout(str![[""]])
+ .expect_stderr(str![[r#"
+
+thread 'main' ([..]) panicked at [..]
+unreachable: invalid key `foo`
+note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+
+"#]]);
+ });
+}
+
+// An invalid value in the environment.
+#[test]
+fn env_invalid_value() {
+ BookTest::from_dir("config/empty")
+ .run("build", |cmd| {
+ cmd.env("MDBOOK_BOOK", r#"{"titlez": "typo"}"#)
+ .expect_stdout(str![[""]])
+ .expect_stderr(str![[r#"
+ INFO Book building has started
+ INFO Running the html backend
+ INFO HTML book written to `[ROOT]/book`
+
+"#]]);
+ })
+ .run("build", |cmd| {
+ cmd.env("MDBOOK_BOOK__TITLE", r#"{"looks like obj": "abc"}"#)
+ .expect_stdout(str![[""]])
+ .expect_stderr(str![[r#"
+ INFO Book building has started
+ INFO Running the html backend
+ INFO HTML book written to `[ROOT]/book`
+
+"#]]);
+ })
+ .check_file_contains("book/index.html", "
Chapter 1")
+ .run("build", |cmd| {
+ cmd.env("MDBOOK_BOOK__TITLE", r#"{braces}"#)
+ .expect_stdout(str![[""]])
+ .expect_stderr(str![[r#"
+ INFO Book building has started
+ INFO Running the html backend
+ INFO HTML book written to `[ROOT]/book`
+
+"#]]);
+ })
+ .check_file_contains("book/index.html", "Chapter 1 - {braces}");
+}
+
+// Replacing the entire book table from the environment.
+#[test]
+fn env_entire_book_table() {
+ BookTest::init(|_| {})
+ .change_file(
+ "book.toml",
+ "[book]\n\
+ title = \"config title\"\n\
+ ",
+ )
+ .run("build", |cmd| {
+ cmd.env("MDBOOK_BOOK", r#"{"description": "custom description"}"#);
+ })
+ .check_file_contains("book/index.html", "Chapter 1 - config title")
+ .check_file_contains(
+ "book/index.html",
+ r#""#,
+ );
+}
+
+// Replacing the entire output or preprocessor table from the environment.
+#[test]
+fn env_entire_output_preprocessor_table() {
+ BookTest::from_dir("config/empty")
+ .rust_program(
+ "mdbook-my-preprocessor",
+ r#"
+ fn main() {
+ let mut args = std::env::args().skip(1);
+ if args.next().as_deref() == Some("supports") {
+ return;
+ }
+ use std::io::Read;
+ let mut s = String::new();
+ std::io::stdin().read_to_string(&mut s).unwrap();
+ assert!(s.contains("custom preprocessor config"));
+ println!("{{\"items\": []}}");
+ }
+ "#,
+ )
+ .rust_program(
+ "mdbook-my-output",
+ r#"
+ fn main() {
+ use std::io::Read;
+ let mut s = String::new();
+ std::io::stdin().read_to_string(&mut s).unwrap();
+ assert!(s.contains("custom output config"));
+ }
+ "#,
+ )
+ .run("build", |cmd| {
+ let mut paths: Vec<_> =
+ std::env::split_paths(&std::env::var_os("PATH").unwrap_or_default()).collect();
+ paths.push(cmd.dir.clone());
+ let path = std::env::join_paths(paths).unwrap().into_string().unwrap();
+
+ cmd.env(
+ "MDBOOK_OUTPUT",
+ r#"{"my-output": {"foo": "custom output config"}}"#,
+ )
+ .env(
+ "MDBOOK_PREPROCESSOR",
+ r#"{"my-preprocessor": {"foo": "custom preprocessor config"}}"#,
+ )
+ .env("PATH", path)
+ .expect_failure()
+ .expect_stdout(str![[""]])
+ .expect_stderr(str![[r#"
+
+thread 'main' ([..]) panicked at [..]
+unreachable: invalid key `output`
+note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+
"#]]);
});
}