diff options
-rw-r--r-- | Cargo.toml | 3 | ||||
-rw-r--r-- | src/bin/tosin-admin.rs | 14 | ||||
-rw-r--r-- | tests/tutorial/mod.rs | 155 | ||||
-rw-r--r-- | tests/tutorial_steps.rs | 13 |
4 files changed, 185 insertions, 0 deletions
@@ -14,3 +14,6 @@ time = "0.2.27" tokio = { version = "1", features = ["full"] } tosin-macros = { version = "0.1.0", path = "./tosin-macros" } warp = "0.3" + +[dev-dependencies] +rand = "0.8.3" diff --git a/src/bin/tosin-admin.rs b/src/bin/tosin-admin.rs new file mode 100644 index 0000000..a1f03fb --- /dev/null +++ b/src/bin/tosin-admin.rs @@ -0,0 +1,14 @@ +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +enum Opt { + /// Start a new project/site (can contain multiple apps) + StartProject { + name: Option<String>, + } +} + +fn main() { + let opts = Opt::from_args(); + dbg!(opts); +} diff --git a/tests/tutorial/mod.rs b/tests/tutorial/mod.rs new file mode 100644 index 0000000..576d3af --- /dev/null +++ b/tests/tutorial/mod.rs @@ -0,0 +1,155 @@ +use std::env::set_current_dir; +use std::fs; +use std::io::Read; +use std::path::Path; +use std::process::Command; + +trait ExitStatusExt { + fn check(self); +} + +impl ExitStatusExt for std::process::ExitStatus { + fn check(self) { + assert!(self.success()); + } +} + +const CARGO: &str = env!("CARGO"); +const PROJECT_DIR: &str = env!("CARGO_MANIFEST_DIR"); +const TOSIN_ADMIN: &str = env!("CARGO_BIN_EXE_tosin-admin"); + +fn get(url: &'static str) -> (hyper::StatusCode, String) { + let get = async { + use hyper::{Client, Uri}; + + let client = Client::new(); + + let res = client.get(Uri::from_static(url)).await.unwrap(); + + let status = res.status(); + + let body = hyper::body::to_bytes(res).await.unwrap(); + let body = String::from_utf8_lossy(&body).into_owned(); + + (status, body) + }; + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap() + .block_on(get) +} + +pub fn step1(dest: &str) { + // tosin-admin start-project {dest} + set_current_dir(PROJECT_DIR).unwrap(); + set_current_dir("target").unwrap(); + if fs::metadata(dest).is_ok() { + fs::remove_dir_all(dest).unwrap(); + } + Command::new(TOSIN_ADMIN) + .args(&["start-project", dest]) + .status() + .unwrap() + .check(); + set_current_dir(dest).unwrap(); + assert!(fs::metadata("Cargo.toml").is_ok()); + assert!(fs::read_to_string("Cargo.toml").unwrap().contains("tosin = ")); + assert!(fs::metadata("src/main.rs").is_ok()); + assert!(fs::read_to_string("src/main.rs").unwrap().contains("tosin::main!")); + + // cargo run run-server + let mut server = Command::new(CARGO) + .args(&["run", "run-server", "8069"]) + .spawn() + .unwrap(); + let server_poke = get("http://127.0.0.1:8069"); + assert_eq!(server_poke.0, hyper::StatusCode::NOT_FOUND); + server.kill().unwrap(); + let mut server_stdout = String::new(); + server.stdout.unwrap().read_to_string(&mut server_stdout).unwrap(); + assert!(server_stdout.contains("http://127.0.0.1:8069")); + + // could `cargo run start-app polls` or `tosin-admin start-app polls` so + // flip a coin i guess + if rand::random() { + let mut cmd = Command::new(CARGO); + cmd.arg("run"); + cmd + } else { + Command::new(TOSIN_ADMIN) + } + .args(&["start-app", "polls"]) + .status().unwrap().check(); + assert!(fs::metadata("src/polls/mod.rs").is_ok()); + + // write views.rs + fs::write("src/polls/views.rs", r#" +use tosin::http::{Reply, Response}; + +pub fn index() -> Response { + "Hello, world. You're at the polls index.".into_response() +} + "#).unwrap(); + + // write urls.rs + fs::write("src/polls/urls.rs", r#" +use tosin::urls::{UrlMap, url_map}; + +use super::views; + +pub fn urls() -> UrlMap { + url_map! { + => views::index, // TODO name: "index" + } +} + "#).unwrap(); + + // update main.rs + fs::write("src/main.rs", r#" +use tosin::Settings; +use tosin::contrib::admin; +use tosin::urls::{UrlMap, url_map}; + +mod polls; + +fn urls() -> UrlMap { + url_map! { + "polls" / ..polls::urls(), + "admin" / ..admin::site::urls(), + } +} + +fn settings() -> Settings { + Settings { + ..Settings::default() + } +} + +tosin::main!(urls(), settings()); + "#).unwrap(); + + // poke that new route + let mut server = Command::new(CARGO) + .args(&["run", "run-server", "8069"]) + .spawn() + .unwrap(); + let server_poke = get("http://127.0.0.1:8069/polls/"); + assert_eq!(server_poke.0, hyper::StatusCode::OK); + assert_eq!(server_poke.1, "Hello, world. You're at the polls index."); + server.kill().unwrap(); + + // vibe check + let example_tutorial1 = Path::new(PROJECT_DIR).join("examples/tutorial01"); + for file in &["src/main.rs", "src/polls/mod.rs", "src/polls/urls.rs", "src/polls/views.rs"] { + let this_file = fs::read_to_string(file).unwrap(); + let example_tutorial1_path = example_tutorial1.join(file); + let example_tutorial1_file = fs::read_to_string(example_tutorial1_path).unwrap(); + assert_eq!(this_file.trim(), example_tutorial1_file.trim()); + } +} + +pub fn step2(dest: &str) { + step1(dest); + todo!(); +} diff --git a/tests/tutorial_steps.rs b/tests/tutorial_steps.rs new file mode 100644 index 0000000..5af6eb5 --- /dev/null +++ b/tests/tutorial_steps.rs @@ -0,0 +1,13 @@ +mod tutorial; + +use tutorial::*; + +#[test] +fn tutorial1() { + step1("tutorial1"); +} + +#[test] +fn tutorial2() { + step2("tutorial2"); +} |