aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMelody Horn / boringcactus <melody@boringcactus.com>2021-06-13 19:35:58 -0600
committerMelody Horn / boringcactus <melody@boringcactus.com>2021-06-13 19:35:58 -0600
commit75e5e5f857b50a0eedbde84bcb10035479fc4571 (patch)
treef85ad816f176e244a4fdfc75b621af6170e4d7a8
parent311b49a2fdd97b8b870dbaccccb55058ee0207c8 (diff)
downloadtosin-75e5e5f857b50a0eedbde84bcb10035479fc4571.tar.gz
tosin-75e5e5f857b50a0eedbde84bcb10035479fc4571.zip
start testing tutorial workflow
-rw-r--r--Cargo.toml3
-rw-r--r--src/bin/tosin-admin.rs14
-rw-r--r--tests/tutorial/mod.rs155
-rw-r--r--tests/tutorial_steps.rs13
4 files changed, 185 insertions, 0 deletions
diff --git a/Cargo.toml b/Cargo.toml
index aa99c74..9cf36a9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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");
+}