use anyhow::{anyhow, bail};
use rustwide::cmd::{Command, CommandError, SandboxBuilder};
use rustwide::{Crate, PrepareError, Toolchain, Workspace};
#[test]
fn test_fetch() -> anyhow::Result<()> {
let workspace = crate::utils::init_workspace()?;
let toolchain = Toolchain::dist("stable");
toolchain.install(&workspace)?;
let mut repo = Repo::new(&workspace)?;
let krate = Crate::git(&repo.serve()?);
krate.fetch(&workspace)?;
let cloned_commit = || -> anyhow::Result<String> {
let mut dir = workspace.build_dir("integration-crates_git-test_fetch");
dir.purge()?;
dir.build(&toolchain, &krate, SandboxBuilder::new())
.run(|build| {
Ok(Command::new(&workspace, "git")
.args(&["rev-parse", "HEAD"])
.cd(build.host_source_dir())
.run_capture()?
.stdout_lines()[0]
.to_string())
})
};
let initial_commit = repo.last_commit_sha.clone().unwrap();
assert_eq!(initial_commit, krate.git_commit(&workspace).unwrap());
assert_eq!(initial_commit, cloned_commit()?);
repo.commit(&workspace)?;
let new_commit = repo.last_commit_sha.unwrap();
assert_ne!(initial_commit, new_commit);
assert_eq!(initial_commit, krate.git_commit(&workspace).unwrap());
assert_eq!(initial_commit, cloned_commit()?);
krate.fetch(&workspace)?;
assert_eq!(new_commit, krate.git_commit(&workspace).unwrap());
assert_eq!(new_commit, cloned_commit()?);
Ok(())
}
#[test]
fn test_fetch_with_authentication() -> anyhow::Result<()> {
let workspace = crate::utils::init_workspace()?;
let repo = Repo::new(&workspace)?.authenticated();
let krate = Crate::git(&repo.serve()?);
let err = krate.fetch(&workspace).unwrap_err();
if let Some(&CommandError::Timeout(_)) = err.downcast_ref() {
panic!("an authentication prompt was shown during the fetch");
} else if let Some(&PrepareError::PrivateGitRepository) = err.downcast_ref() {
} else {
panic!("unexpected error: {}", err);
}
Ok(())
}
struct Repo {
source: tempfile::TempDir,
last_commit_sha: Option<String>,
require_auth: bool,
}
impl Repo {
fn new(workspace: &Workspace) -> anyhow::Result<Self> {
let source = tempfile::tempdir()?;
Command::new(workspace, "cargo")
.args(&["init", "--name", "foo", "--bin"])
.args(&[source.path()])
.run()?;
let mut repo = Repo {
source,
last_commit_sha: None,
require_auth: false,
};
repo.commit(workspace)?;
Ok(repo)
}
fn authenticated(mut self) -> Self {
self.require_auth = true;
self
}
fn commit(&mut self, workspace: &Workspace) -> anyhow::Result<()> {
Command::new(workspace, "git")
.args(&["add", "."])
.cd(self.source.path())
.run()?;
Command::new(workspace, "git")
.args(&["-c", "commit.gpgsign=false"])
.args(&["-c", "user.name=test"])
.args(&["-c", "user.email=test@example.com"])
.args(&["commit", "-m", "auto commit"])
.args(&["--allow-empty"])
.cd(self.source.path())
.run()?;
Command::new(workspace, "git")
.args(&["update-server-info"])
.cd(self.source.path())
.run()?;
self.last_commit_sha = Some(
Command::new(workspace, "git")
.args(&["rev-parse", "HEAD"])
.cd(self.source.path())
.run_capture()?
.stdout_lines()[0]
.to_string(),
);
Ok(())
}
fn serve(&self) -> anyhow::Result<String> {
let server = tiny_http::Server::http("localhost:0").map_err(|e| anyhow!(e.to_string()))?;
#[allow(irrefutable_let_patterns)]
let port = if let tiny_http::ListenAddr::IP(socket_addr) = server.server_addr() {
socket_addr.port()
} else {
bail!("found a non-IP server address");
};
let base = self.source.path().join(".git");
let require_auth = self.require_auth;
std::thread::spawn(move || {
while let Ok(req) = server.recv() {
let url = req.url().split('?').next().unwrap()[1..].to_string();
let file = std::fs::File::open(base.join(url));
if require_auth {
let resp = tiny_http::Response::new_empty(tiny_http::StatusCode(401));
let _ = req.respond(resp.with_header(tiny_http::Header {
field: "WWW-Authenticate".parse().unwrap(),
value: "Basic realm=\"Dummy\"".parse().unwrap(),
}));
} else if file.is_ok() {
let resp = tiny_http::Response::from_file(file.unwrap());
let _ = req.respond(resp);
} else {
let resp = tiny_http::Response::new_empty(tiny_http::StatusCode(404));
let _ = req.respond(resp);
}
}
});
Ok(format!("http://localhost:{}", port))
}
}