From fa68a0d91f5c5973b9519b42d09c91d5b71c234a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jesper=20H=C3=A5kansson?= <jesper@jesperh.se>
Date: Mon, 5 Nov 2018 21:54:42 +0100
Subject: [PATCH] test: Create and return tarball in tests, add more test cases

---
 binary-install/src/lib.rs    |   5 +-
 binary-install/tests/path.rs |  13 ++--
 binary-install/tests/test.rs | 111 ++++++++++++++++++++++++++++++++---
 3 files changed, 112 insertions(+), 17 deletions(-)

diff --git a/binary-install/src/lib.rs b/binary-install/src/lib.rs
index 728ee12..09f6982 100644
--- a/binary-install/src/lib.rs
+++ b/binary-install/src/lib.rs
@@ -47,8 +47,9 @@ where
     let bin = local_bin_dir(crate_path);
 
     for entry in archive.entries()? {
-        let mut entry =
-            entry.map_err(|_err| Error::archive(&format!("Invalid tarball at {}", url)))?;
+        let mut entry = entry.map_err(|err| {
+            Error::archive(&format!("Invalid tarball at {}. Inner error: {}", url, err))
+        })?;
 
         let dest = match entry.path()?.file_stem() {
             Some(f) if binaries.contains(f) => {
diff --git a/binary-install/tests/path.rs b/binary-install/tests/path.rs
index a9c3b95..08139ec 100644
--- a/binary-install/tests/path.rs
+++ b/binary-install/tests/path.rs
@@ -9,7 +9,7 @@ use slog::Drain;
 use std::env;
 use std::fs;
 use std::io;
-use std::path::{Path, PathBuf};
+use std::path::PathBuf;
 
 fn logger() -> slog::Logger {
     let decorator = slog_term::TermDecorator::new().build();
@@ -26,11 +26,10 @@ fn get_tests_bin_path() -> PathBuf {
 #[test]
 #[cfg(not(target_os = "windows"))]
 fn get_local_bin_path_should_return_a_path() {
-    let crate_path = Path::new("");
-
-    let expected_path = Path::new("bin/wasm-bindgen");
+    let crate_path = get_tests_bin_path();
+    let expected_path = crate_path.join("bin/wasm-bindgen");
 
-    let result = local_bin_path(crate_path, "wasm-bindgen");
+    let result = local_bin_path(&crate_path, "wasm-bindgen");
 
     assert_eq!(expected_path, result);
 }
@@ -38,9 +37,9 @@ fn get_local_bin_path_should_return_a_path() {
 #[test]
 #[cfg(target_os = "windows")]
 fn get_local_bin_path_should_return_with_exe_for_windows() {
-    let crate_path = Path::new("");
+    let crate_path = get_tests_bin_path();
 
-    let expected_path = Path::new("bin/wasm-bindgen.exe");
+    let expected_path = crate_path.join("bin/wasm-bindgen.exe");
 
     let result = local_bin_path(crate_path, "wasm-bindgen");
 
diff --git a/binary-install/tests/test.rs b/binary-install/tests/test.rs
index 73f4298..923688b 100644
--- a/binary-install/tests/test.rs
+++ b/binary-install/tests/test.rs
@@ -1,18 +1,25 @@
 extern crate binary_install;
 extern crate curl;
 extern crate failure;
+extern crate flate2;
+extern crate tar;
 
 use binary_install::{error::Error, install_binaries_from_targz_at_url};
-use std::io::{Read, Write};
+use flate2::write::GzEncoder;
+use flate2::Compression;
+use std::env;
+use std::fs::{File, OpenOptions};
+use std::io::{self, Read, Write};
 use std::net::TcpListener;
 use std::path::Path;
 use std::thread;
 
-const SERVER_URL: &'static str = "localhost:7878";
+const SERVER_HOST: &'static str = "localhost";
+
+fn start_server(port: u32, tarball: Option<Vec<u8>>) -> thread::JoinHandle<TcpListener> {
+    thread::spawn(move || {
+        let listener = TcpListener::bind(format!("{}:{}", SERVER_HOST, port)).unwrap();
 
-fn start_server() -> thread::JoinHandle<TcpListener> {
-    thread::spawn(|| {
-        let listener = TcpListener::bind(SERVER_URL).unwrap();
         for stream in listener.incoming() {
             let mut stream = stream.unwrap();
 
@@ -23,12 +30,49 @@ fn start_server() -> thread::JoinHandle<TcpListener> {
             let response = "HTTP/1.1 200 OK\r\n\r\n";
 
             stream.write(response.as_bytes()).unwrap();
+
+            match tarball.to_owned() {
+                Some(tar) => {
+                    stream.write(tar.as_ref()).unwrap();
+                }
+                None => {}
+            }
+
             stream.flush().unwrap();
         }
         listener
     })
 }
 
+fn create_tarball(binary_name: &str) -> Result<Vec<u8>, io::Error> {
+    let temp_dir = env::temp_dir();
+    let tar = OpenOptions::new()
+        .create(true)
+        .read(true)
+        .write(true)
+        .open(temp_dir.join("foo.tar.gz"))?;
+
+    let mut file = OpenOptions::new()
+        .create(true)
+        .read(true)
+        .write(true)
+        .open(temp_dir.join(binary_name))?;
+
+    let mut encoder = GzEncoder::new(tar, Compression::default());
+    {
+        let mut archive = tar::Builder::new(&mut encoder);
+        archive.append_file(binary_name, &mut file)?;
+    }
+
+    let mut contents = vec![];
+
+    encoder.finish()?;
+
+    File::open(temp_dir.join("foo.tar.gz"))?.read_to_end(&mut contents)?;
+
+    Ok(contents)
+}
+
 #[test]
 fn install_binaries_from_targz_at_url_should_return_http_error_for_bad_url() {
     let crate_path = Path::new("");
@@ -51,12 +95,43 @@ fn install_binaries_from_targz_at_url_should_return_http_error_for_bad_url() {
 
 #[test]
 fn install_binaries_from_targz_at_url_should_return_archive_error_when_tarball_is_missing() {
+    let server_port = 7878;
+    let url = format!("http://{}:{}", SERVER_HOST, server_port);
     let crate_path = Path::new("");
-    let url = format!("http://{}", SERVER_URL);
     let binaries = vec![""];
 
     // Spin up a local TcpListener.
-    start_server();
+    start_server(server_port, None);
+
+    let result = install_binaries_from_targz_at_url(crate_path, &url, binaries);
+    assert!(result.is_err());
+
+    let err = result.err().unwrap();
+    let err = err.downcast_ref::<Error>().unwrap();
+
+    let expected_message = format!(
+        "Invalid tarball at {}. Inner error: failed to fill whole buffer",
+        url
+    );
+
+    match err {
+        Error::Archive { message } => assert_eq!(&expected_message, message),
+        _ => panic!("Wrong error returned"),
+    }
+}
+
+#[test]
+fn install_binaries_from_targz_at_url_should_return_archive_error_when_tarball_does_not_include_all_files(
+) {
+    let server_port = 7879;
+    let url = format!("http://{}:{}", SERVER_HOST, server_port);
+    let crate_path = Path::new("");
+    let binaries = vec!["wasm-pack"];
+
+    // Create a temporary tarball.
+    let tarball = create_tarball("foo.txt").ok();
+    // Spin up a local TcpListener.
+    start_server(server_port, tarball);
 
     let result = install_binaries_from_targz_at_url(crate_path, &url, binaries);
     assert!(result.is_err());
@@ -64,10 +139,30 @@ fn install_binaries_from_targz_at_url_should_return_archive_error_when_tarball_i
     let err = result.err().unwrap();
     let err = err.downcast_ref::<Error>().unwrap();
 
-    let expected_message = format!("Invalid tarball at {}", url);
+    let expected_message = format!(
+        "the tarball at {} was missing expected executables: {}",
+        url, "wasm-pack"
+    );
 
     match err {
         Error::Archive { message } => assert_eq!(&expected_message, message),
         _ => panic!("Wrong error returned"),
     }
 }
+
+#[test]
+fn install_binaries_from_targz_at_url_should_return_ok_if_binary_is_found() {
+    let server_port = 7880;
+    let url = format!("http://{}:{}", SERVER_HOST, server_port);
+    let binary_name = "wasm-pack";
+    let crate_path = Path::new("");
+    let binaries = vec![binary_name];
+
+    // Create a temporary tarball.
+    let tarball = create_tarball(binary_name).ok();
+    // Spin up a local TcpListener.
+    start_server(server_port, tarball);
+
+    let result = install_binaries_from_targz_at_url(crate_path, &url, binaries);
+    assert!(result.is_ok());
+}