aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock63
-rw-r--r--Cargo.toml3
-rw-r--r--src/main.rs39
-rw-r--r--src/tests.rs104
4 files changed, 198 insertions, 11 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 562a296..f5fd4d1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -136,6 +136,12 @@ dependencies = [
]
[[package]]
+name = "fuchsia-cprng"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
+
+[[package]]
name = "futures-channel"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -223,6 +229,7 @@ dependencies = [
"pulldown-cmark",
"serde",
"sputnik",
+ "tempdir",
"tokio",
"toml",
"url",
@@ -633,6 +640,52 @@ dependencies = [
]
[[package]]
+name = "rand"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
+dependencies = [
+ "fuchsia-cprng",
+ "libc",
+ "rand_core 0.3.1",
+ "rdrand",
+ "winapi",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
+dependencies = [
+ "rand_core 0.4.2",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
+
+[[package]]
+name = "rdrand"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
+dependencies = [
+ "rand_core 0.3.1",
+]
+
+[[package]]
+name = "remove_dir_all"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
name = "ryu"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -728,6 +781,16 @@ dependencies = [
]
[[package]]
+name = "tempdir"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
+dependencies = [
+ "rand",
+ "remove_dir_all",
+]
+
+[[package]]
name = "termcolor"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 4d4df43..59492a0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -37,3 +37,6 @@ camino = "1"
[target.'cfg(unix)'.dependencies]
hyperlocal = { version = "0.8", features = ["server"] }
+
+[dev-dependencies]
+tempdir = "0.3.7"
diff --git a/src/main.rs b/src/main.rs
index 9251003..f1f9caa 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -49,6 +49,7 @@ mod get_routes;
mod origins;
mod post_routes;
mod shares;
+mod tests;
pub enum Response {
Raw(HyperResponse),
@@ -94,17 +95,22 @@ struct Args {
#[tokio::main]
async fn main() {
let args = Args::parse();
- let repo = Repository::open_bare(env::current_dir().unwrap())
+ let repo_path: &'static _ = Box::leak(Box::new(env::current_dir().unwrap()));
+ let repo = Repository::open_bare(repo_path)
.expect("expected current directory to be a bare Git repository");
if args.multiuser {
- serve(MultiUserController::new(&repo), args).await;
+ serve(repo_path, MultiUserController::new(&repo), args).await;
} else {
- serve(SoloController::new(&repo), args).await;
+ serve(repo_path, SoloController::new(&repo), args).await;
}
}
-async fn serve<C: Controller + Send + Sync + 'static>(controller: C, args: Args) {
+async fn serve<C: Controller + Send + Sync + 'static>(
+ repo_path: &'static Path,
+ controller: C,
+ args: Args,
+) {
let controller = Arc::new(controller);
#[cfg(unix)]
@@ -121,7 +127,7 @@ async fn serve<C: Controller + Send + Sync + 'static>(controller: C, args: Args)
async move {
Ok::<_, hyper::Error>(service_fn(move |req| {
- service(origin, controller.clone(), req)
+ service_wrapper(repo_path, origin, controller.clone(), req)
}))
}
});
@@ -173,7 +179,7 @@ async fn serve<C: Controller + Send + Sync + 'static>(controller: C, args: Args)
async move {
Ok::<_, hyper::Error>(service_fn(move |req| {
- service(origin, controller.clone(), req)
+ service_wrapper(repo_path, origin, controller.clone(), req)
}))
}
});
@@ -182,17 +188,27 @@ async fn serve<C: Controller + Send + Sync + 'static>(controller: C, args: Args)
server.await.expect("server error");
}
-async fn service<C: Controller>(
+async fn service_wrapper<C: Controller>(
+ repo_path: &Path,
origin: &HttpOrigin,
controller: Arc<C>,
request: Request,
) -> Result<HyperResponse, Infallible> {
+ Ok(service(repo_path, origin, &*controller, request).await)
+}
+
+async fn service<C: Controller>(
+ repo_path: &Path,
+ origin: &HttpOrigin,
+ controller: &C,
+ request: Request,
+) -> HyperResponse {
let (mut parts, body) = request.into_parts();
let mut script_csp = "'none'".into();
let mut frame_csp = "'none'";
- let mut resp = build_response(origin, &*controller, &mut parts, body)
+ let mut resp = build_response(repo_path, origin, controller, &mut parts, body)
.await
.map(|resp| match resp {
Response::Raw(resp) => resp,
@@ -205,7 +221,7 @@ async fn service<C: Controller>(
}
Builder::new()
.content_type(mime::TEXT_HTML)
- .body(render_page(&page, &*controller, &parts).into())
+ .body(render_page(&page, controller, &parts).into())
.unwrap()
}
})
@@ -224,7 +240,7 @@ async fn service<C: Controller>(
.parse()
.unwrap()
});
- Ok(resp)
+ resp
}
#[derive(Default)]
@@ -284,6 +300,7 @@ impl Branch {
}
async fn build_response<C: Controller>(
+ repo_path: &Path,
origin: &HttpOrigin,
controller: &C,
parts: &mut Parts,
@@ -309,7 +326,7 @@ async fn build_response<C: Controller>(
.map_err(|_| Error::BadRequest("failed to percent-decode path as UTF-8".into()))?
.into_owned();
- let repo = Repository::open_bare(env::current_dir().unwrap()).unwrap();
+ let repo = Repository::open_bare(repo_path).unwrap();
let (rev, unsanitized_path) = match controller.parse_url_path(&unsanitized_path, parts, &repo) {
Ok(parsed) => parsed,
diff --git a/src/tests.rs b/src/tests.rs
new file mode 100644
index 0000000..3eb71ec
--- /dev/null
+++ b/src/tests.rs
@@ -0,0 +1,104 @@
+#![cfg(test)]
+use std::{future::Future, path::PathBuf, pin::Pin};
+
+use crate::controller::Controller;
+
+use super::{
+ controller::{MultiUserController, SoloController},
+ origins::HttpOrigin,
+};
+use git2::Repository;
+use hyper::{body, http::request::Builder, Body, Request, Response};
+use tempdir::TempDir;
+
+fn temp_repo() -> (PathBuf, Repository) {
+ let path = TempDir::new("gitpad-test").unwrap().into_path();
+ let repo = Repository::init_bare(&path).unwrap();
+ (path, repo)
+}
+
+fn origin() -> HttpOrigin {
+ "http://pad.example.com".parse().unwrap()
+}
+
+struct Server<C> {
+ repo_path: PathBuf,
+ repo: Repository,
+ origin: HttpOrigin,
+ controller: C,
+}
+
+impl Server<SoloController> {
+ fn new() -> Self {
+ let (repo_path, repo) = temp_repo();
+ let origin = origin();
+ Self {
+ repo_path,
+ origin,
+ controller: SoloController::new(&repo),
+ repo,
+ }
+ }
+}
+
+impl Server<MultiUserController> {
+ fn new() -> Self {
+ let (repo_path, repo) = temp_repo();
+ let origin = origin();
+ Self {
+ repo_path,
+ origin,
+ controller: MultiUserController::new(&repo),
+ repo,
+ }
+ }
+}
+
+impl<C: Controller> Server<C> {
+ async fn serve(&self, req: Request<Body>) -> Response<Body> {
+ super::service(&self.repo_path, &self.origin, &self.controller, req).await
+ }
+}
+
+trait IntoText {
+ fn into_text(self) -> Pin<Box<dyn Future<Output = String>>>;
+}
+
+impl IntoText for Body {
+ fn into_text(self) -> Pin<Box<dyn Future<Output = String>>> {
+ Box::pin(async move {
+ std::str::from_utf8(&body::to_bytes(self).await.unwrap())
+ .unwrap()
+ .to_string()
+ })
+ }
+}
+
+#[tokio::test]
+async fn test_no_host_header() {
+ let server = Server::<SoloController>::new();
+ let res = server.serve(Builder::new().body("".into()).unwrap()).await;
+ assert_eq!(res.status(), 400);
+ assert!(res
+ .into_body()
+ .into_text()
+ .await
+ .contains("Host header required"));
+}
+
+#[tokio::test]
+async fn test_missing_branch() {
+ let server = Server::<SoloController>::new();
+
+ let res = server
+ .serve(
+ Builder::new()
+ .header("host", server.origin.host())
+ .body("".into())
+ .unwrap(),
+ )
+ .await;
+ assert_eq!(res.status(), 404); // branch not found
+
+ // TODO: create commit with file and test that it is retrievable
+}