1mod build;
16#[cfg(feature = "cli")]
17pub mod cli;
18mod components;
19mod distribution;
20mod env;
21pub mod error;
22mod events;
23mod paths;
24mod registry;
25mod settings;
26
27use std::path::{Path, PathBuf};
28
29use self::distribution::{
30 signature::{PrivateKey, PublicKey},
31 Platform,
32};
33use self::env::Environment;
34use self::events::{RzupEvent, TransferKind};
35use self::paths::Paths;
36use self::registry::Registry;
37use self::settings::Settings;
38
39#[cfg(feature = "publish")]
40use aws_credential_types::Credentials as AwsCredentials;
41
42#[cfg(not(feature = "publish"))]
43pub struct AwsCredentials;
44
45pub use self::components::Component;
46pub use self::error::{Result, RzupError};
47pub use semver::Version;
48
49#[derive(Clone, Debug)]
50pub struct BaseUrls {
51 pub risc0_github_base_url: String,
52 pub github_api_base_url: String,
53 pub risc0_base_url: String,
54 pub s3_base_url: String,
55}
56
57impl Default for BaseUrls {
58 fn default() -> Self {
59 Self {
60 risc0_github_base_url: "https://github.com/risc0".into(),
61 github_api_base_url: "https://api.github.com".into(),
62 risc0_base_url: "https://risczero.com".into(),
63 s3_base_url: "https://risc0-artifacts.s3.us-west-2.amazonaws.com".into(),
64 }
65 }
66}
67
68pub struct Rzup {
71 environment: Environment,
72 registry: Registry,
73}
74
75impl Rzup {
76 pub fn new() -> Result<Self> {
78 let environment = Environment::new(|s| std::env::var(s), |_| {})?;
79 let registry = Registry::new(&environment, Default::default())?;
80
81 Ok(Self {
82 environment,
83 registry,
84 })
85 }
86
87 pub fn new_with_event_handler(
89 event_handler: impl Fn(RzupEvent) + Send + Sync + 'static,
90 ) -> Result<Self> {
91 let environment = Environment::new(|s| std::env::var(s), event_handler)?;
92 let registry = Registry::new(&environment, Default::default())?;
93
94 Ok(Self {
95 environment,
96 registry,
97 })
98 }
99
100 #[allow(clippy::too_many_arguments)]
114 pub fn with_paths_urls_creds_platform_and_event_handler(
115 risc0_dir: impl Into<PathBuf>,
116 rustup_dir: impl AsRef<Path>,
117 cargo_dir: impl AsRef<Path>,
118 base_urls: BaseUrls,
119 github_token: Option<String>,
120 aws_creds_factory: impl Fn() -> Option<AwsCredentials> + Send + Sync + 'static,
121 private_key_getter: impl Fn() -> Result<PrivateKey> + Send + Sync + 'static,
122 public_key: PublicKey,
123 platform: Platform,
124 event_handler: impl Fn(RzupEvent) + Send + Sync + 'static,
125 ) -> Result<Self> {
126 let environment = Environment::with_paths_creds_platform_and_event_handler(
127 risc0_dir,
128 rustup_dir,
129 cargo_dir,
130 github_token,
131 aws_creds_factory,
132 private_key_getter,
133 public_key,
134 platform,
135 event_handler,
136 )?;
137 let registry = Registry::new(&environment, base_urls)?;
138
139 Ok(Self {
140 environment,
141 registry,
142 })
143 }
144
145 #[cfg(test)]
150 pub(crate) fn set_event_handler(
151 &mut self,
152 event_handler: impl Fn(RzupEvent) + Send + Sync + 'static,
153 ) {
154 self.environment.set_event_handler(event_handler);
155 }
156
157 #[cfg_attr(not(feature = "cli"), expect(dead_code))]
162 pub(crate) fn install_all(&mut self, force: bool) -> Result<()> {
163 self.registry
164 .install_all_components(&self.environment, force)?;
165 Ok(())
166 }
167
168 #[cfg_attr(not(feature = "cli"), expect(dead_code))]
175 pub(crate) fn install_component(
176 &mut self,
177 component: &Component,
178 version: Option<Version>,
179 force: bool,
180 ) -> Result<()> {
181 self.registry
182 .install_component(&self.environment, component, version, force)?;
183 Ok(())
184 }
185
186 #[cfg_attr(not(feature = "cli"), expect(dead_code))]
192 pub(crate) fn uninstall_component(
193 &mut self,
194 component: &Component,
195 version: Version,
196 ) -> Result<()> {
197 self.registry
198 .uninstall_component(&self.environment, component, version)?;
199 Ok(())
200 }
201
202 #[cfg_attr(not(feature = "cli"), expect(dead_code))]
210 pub(crate) fn list_versions(&self, component: &Component) -> Result<Vec<Version>> {
211 Ok(
212 Registry::list_component_versions(&self.environment, component)?
213 .into_iter()
214 .map(|(v, _)| v)
215 .collect(),
216 )
217 }
218
219 pub fn get_default_version(
224 &self,
225 component: &Component,
226 ) -> Result<Option<(Version, std::path::PathBuf)>> {
227 self.registry
228 .get_default_component_version(&self.environment, component)
229 }
230
231 fn emit(&self, event: RzupEvent) {
232 self.environment.emit(event)
233 }
234
235 #[cfg_attr(not(feature = "cli"), expect(dead_code))]
236 pub(crate) fn print(&self, message: String) {
237 self.emit(RzupEvent::Print { message });
238 }
239
240 #[cfg_attr(not(feature = "cli"), expect(dead_code))]
245 pub(crate) fn get_latest_version(&self, component: &Component) -> Result<Version> {
246 components::get_latest_version(component, &self.environment, self.registry.base_urls())
247 }
248
249 pub(crate) fn set_default_version(
255 &mut self,
256 component: &Component,
257 version: Version,
258 ) -> Result<()> {
259 self.registry
260 .set_default_component_version(&self.environment, component, version)
261 }
262
263 #[cfg_attr(not(feature = "cli"), expect(dead_code))]
269 pub(crate) fn version_exists(&self, component: &Component, version: &Version) -> Result<bool> {
270 let component_installed = component.parent_component().unwrap_or(*component);
271 Ok(Paths::find_version_dir(&self.environment, &component_installed, version)?.is_some())
272 }
273
274 #[cfg_attr(not(feature = "cli"), expect(dead_code))]
276 pub(crate) fn settings(&self) -> &Settings {
277 self.registry.settings()
278 }
279
280 pub fn get_version_dir(&self, component: &Component, version: &Version) -> Result<PathBuf> {
288 let component = component.parent_component().unwrap_or(*component);
289 Paths::find_version_dir(&self.environment, &component, version)?
290 .ok_or_else(|| RzupError::VersionNotFound(version.clone()))
291 }
292
293 #[cfg_attr(not(feature = "cli"), expect(dead_code))]
295 pub(crate) fn self_update(&self) -> Result<()> {
296 self.emit(RzupEvent::InstallationStarted {
297 id: "rzup".to_string(),
298 version: "latest".to_string(),
299 });
300
301 let script_contents = distribution::download_text(
302 format!(
303 "{base_url}/install",
304 base_url = self.registry.base_urls().risc0_base_url
305 ),
306 &None,
307 )?;
308
309 let output = std::process::Command::new("/usr/bin/env")
310 .args(["bash", "-c", &script_contents])
311 .output()
312 .map_err(|e| RzupError::Other(format!("Failed to execute update script: {e}")))?;
313
314 if !output.status.success() {
315 self.emit(RzupEvent::InstallationFailed {
316 id: "rzup".to_string(),
317 version: "latest".to_string(),
318 });
319 return Err(RzupError::Other(format!(
320 "Self-update failed: {}",
321 String::from_utf8_lossy(&output.stderr)
322 )));
323 }
324
325 self.emit(RzupEvent::InstallationCompleted {
326 id: "rzup".to_string(),
327 version: "latest".to_string(),
328 });
329
330 Ok(())
331 }
332
333 #[cfg_attr(not(feature = "cli"), expect(dead_code))]
334 pub(crate) fn build_rust_toolchain(
335 &mut self,
336 repo_url: &str,
337 tag_or_commit: &Option<String>,
338 path: &Option<String>,
339 ) -> Result<()> {
340 let version =
341 build::build_rust_toolchain(&self.environment, repo_url, tag_or_commit, path)?;
342 self.set_default_version(&Component::RustToolchain, version)?;
343 Ok(())
344 }
345
346 #[cfg(feature = "publish")]
351 #[cfg_attr(not(feature = "cli"), expect(dead_code))]
352 pub(crate) fn publish_upload(
353 &mut self,
354 component: &Component,
355 version: &Version,
356 platform: Option<Platform>,
357 payload: &Path,
358 force: bool,
359 ) -> Result<()> {
360 let s3 = distribution::s3::S3Bucket::new(self.registry.base_urls());
361 s3.publish_upload(
362 &self.environment,
363 component,
364 version,
365 platform,
366 payload,
367 force,
368 )
369 }
370
371 #[cfg(feature = "publish")]
375 #[cfg_attr(not(feature = "cli"), expect(dead_code))]
376 pub(crate) fn publish_set_latest(
377 &mut self,
378 component: &Component,
379 version: &Version,
380 ) -> Result<()> {
381 let s3 = distribution::s3::S3Bucket::new(self.registry.base_urls());
382 s3.publish_set_latest(&self.environment, component, version)
383 }
384
385 #[cfg(feature = "publish")]
387 #[cfg_attr(not(feature = "cli"), expect(dead_code))]
388 pub(crate) fn publish_create_artifact(
389 &mut self,
390 input: &Path,
391 output: &Path,
392 compression_level: u32,
393 ) -> Result<()> {
394 use liblzma::write::XzEncoder;
395
396 let (estimated_tar_size, paths) = self.recursively_find_paths(input)?;
397
398 let id = format!("artifact {}", output.display());
399 self.environment.emit(RzupEvent::TransferStarted {
400 kind: TransferKind::Compress,
401 id: id.clone(),
402 version: None,
403 url: None,
404 len: Some(estimated_tar_size),
405 });
406
407 let file = std::fs::File::create(output).map_err(|e| {
408 RzupError::Other(format!(
409 "error creating output path {}: {e}",
410 output.display()
411 ))
412 })?;
413 let compressor = XzEncoder::new_parallel(file, compression_level);
414 let writer =
415 crate::distribution::ProgressWriter::new(id.clone(), &self.environment, compressor);
416 let mut builder = tar::Builder::new(writer);
417
418 for (full_path, archive_path) in paths {
419 builder.append_path_with_name(full_path, archive_path)?;
420 }
421
422 builder.finish()?;
423 drop(builder);
424
425 self.environment.emit(RzupEvent::TransferCompleted {
426 kind: TransferKind::Compress,
427 id,
428 version: None,
429 });
430
431 Ok(())
432 }
433
434 #[cfg(feature = "publish")]
441 fn recursively_find_paths(&mut self, input: &Path) -> Result<(u64, Vec<(PathBuf, PathBuf)>)> {
442 let mut estimated_tar_size = 0;
443 let mut paths = vec![];
444 if input.is_dir() {
445 self.environment.emit(RzupEvent::Print {
446 message: "Walking input path".into(),
447 });
448 for entry in walkdir::WalkDir::new(input) {
449 let entry = entry.map_err(|e| {
450 RzupError::Other(format!(
451 "error reading entry from input path {}: {e}",
452 input.display()
453 ))
454 })?;
455 let full_path = entry.path();
456 let entry_metadata = entry.metadata().map_err(|e| {
457 RzupError::Other(format!("error reading entry {}: {e}", full_path.display()))
458 })?;
459 if entry_metadata.is_dir() {
460 continue;
461 }
462 let archive_path = full_path
463 .strip_prefix(input)
464 .expect("all walked paths are under the input path");
465
466 estimated_tar_size += entry_metadata.len();
467 estimated_tar_size += 512;
469
470 paths.push((full_path.to_owned(), archive_path.to_owned()));
471 }
472 } else {
473 estimated_tar_size += input
474 .metadata()
475 .map_err(|e| {
476 RzupError::Other(format!("error reading input file: {} {e}", input.display()))
477 })?
478 .len();
479 estimated_tar_size += 512;
481 paths.push((
482 input.to_owned(),
483 PathBuf::from(input.file_name().expect("non-directory has a name")),
484 ));
485 }
486 Ok((estimated_tar_size, paths))
487 }
488}
489
490#[cfg(test)]
491mod tests {
492 use super::*;
493 use crate::distribution::Os;
494 use serde_json::json;
495 use sha2::Digest as _;
496 use std::collections::HashMap;
497 use std::convert::Infallible;
498 use std::io::Write as _;
499 use std::net::SocketAddr;
500 use std::path::Path;
501 use std::sync::{Arc, Mutex};
502 use tempfile::TempDir;
503
504 pub struct MockDistributionServer {
505 pub base_urls: BaseUrls,
506 pub private_key: PrivateKey,
507 }
508
509 type HyperResponse = hyper::Response<http_body_util::Full<hyper::body::Bytes>>;
510
511 const HELLO_WORLD_DUMMY_TAR_XZ_SHA256: &str =
513 "fb05f4f1c334bd3d32ccb043626aad6a849467f9e7674856a1f81906d145f5ac";
514
515 const HELLO_WORLD2_DUMMY_TAR_XZ_SHA256: &str =
517 "68ec421acb728e69d0b4497fe6a622f7347b116649039b48fd00c5a062ceddb4";
518
519 const HELLO_WORLD3_DUMMY_TAR_XZ_SHA256: &str =
521 "ed9efde9a314a9063a9b91d21e9eb1508defce0817ee6a81142d8bf6fb1f045e";
522
523 fn dummy_tar_gz_response() -> HyperResponse {
524 let mut tar_bytes = vec![];
525 let mut tar_builder = tar::Builder::new(&mut tar_bytes);
526 let mut header = tar::Header::new_gnu();
527 header.set_size(4);
528 tar_builder
529 .append_data(&mut header, "tar_contents.bin", &[1, 2, 3, 4][..])
530 .unwrap();
531 tar_builder.finish().unwrap();
532 drop(tar_builder);
533
534 let mut tar_gz_bytes = vec![];
535 let mut encoder =
536 flate2::write::GzEncoder::new(&mut tar_gz_bytes, flate2::Compression::default());
537 encoder.write_all(&tar_bytes).unwrap();
538 drop(encoder);
539
540 hyper::Response::builder()
541 .status(200)
542 .header("content-type", "application/octet-stream")
543 .body(http_body_util::Full::new(hyper::body::Bytes::from(
544 tar_gz_bytes,
545 )))
546 .unwrap()
547 }
548
549 fn dummy_tar_xz_response(sub_dir: &str) -> HyperResponse {
550 let mut tar_bytes = vec![];
551 let mut tar_builder = tar::Builder::new(&mut tar_bytes);
552 let mut header = tar::Header::new_gnu();
553 header.set_size(4);
554 tar_builder
555 .append_data(
556 &mut header,
557 format!("{sub_dir}/tar_contents.bin"),
558 &[1, 2, 3, 4][..],
559 )
560 .unwrap();
561 tar_builder.finish().unwrap();
562 drop(tar_builder);
563
564 let mut tar_xz_bytes = vec![];
565 let mut encoder = liblzma::write::XzEncoder::new(&mut tar_xz_bytes, 1);
566 encoder.write_all(&tar_bytes).unwrap();
567 drop(encoder);
568
569 hyper::Response::builder()
570 .status(200)
571 .header("content-type", "application/octet-stream")
572 .body(http_body_util::Full::new(hyper::body::Bytes::from(
573 tar_xz_bytes,
574 )))
575 .unwrap()
576 }
577
578 fn build_mock_server_data(
579 install_script: String,
580 private_key: &PrivateKey,
581 ) -> HashMap<String, HyperResponse> {
582 fn json_response(json: impl Into<String>) -> HyperResponse {
583 hyper::Response::builder()
584 .status(200)
585 .header("content-type", "application/json")
586 .body(http_body_util::Full::new(hyper::body::Bytes::from(
587 json.into(),
588 )))
589 .unwrap()
590 }
591
592 fn not_found() -> HyperResponse {
593 hyper::Response::builder()
594 .status(404)
595 .body(http_body_util::Full::new(hyper::body::Bytes::from("")))
596 .unwrap()
597 }
598
599 fn text_response(text: String) -> HyperResponse {
600 hyper::Response::builder()
601 .status(200)
602 .header("content-type", "text/plain")
603 .body(http_body_util::Full::new(hyper::body::Bytes::from(text)))
604 .unwrap()
605 }
606
607 let mut risc0_groth16_manifest_json = json!({
608 "releases": {
609 "1.0.0": {
610 "target_agnostic": {
611 "artifact": {
612 "sha256_blobs": [
613 HELLO_WORLD_DUMMY_TAR_XZ_SHA256
614 ]
615 },
616 }
617 },
618 "2.0.0": {
619 "target_agnostic": {
620 "artifact": {
621 "sha256_blobs": [
622 HELLO_WORLD2_DUMMY_TAR_XZ_SHA256
623 ]
624 },
625 }
626 },
627 "3.0.0-badsha": {
628 "target_agnostic": {
629 "artifact": {
630 "sha256_blobs": [
631 HELLO_WORLD3_DUMMY_TAR_XZ_SHA256
632 ]
633 },
634 }
635 },
636 },
637 "latest_version": "2.0.0"
638 });
639
640 let signature =
641 private_key.sign(&serde_json::to_vec(&risc0_groth16_manifest_json).unwrap()[..]);
642 risc0_groth16_manifest_json
643 .as_object_mut()
644 .unwrap()
645 .insert("signature".into(), signature.to_string().into());
646
647 maplit::hashmap! {
648 "/github_api/repos/risc0/risc0/releases/latest".into() => {
649 json_response("{\"tag_name\":\"v1.1.0\"}")
650 },
651 "/github_api/repos/risc0/risc0/releases/tags/v1.0.0".into() => json_response("{}"),
652 "/github_api/repos/risc0/risc0/releases/tags/v1.0.0-rc.1".into() => json_response("{}"),
653 "/github_api/repos/risc0/risc0/releases/tags/v1.0.0-rc.2".into() => json_response("{}"),
654 "/github_api/repos/risc0/rust/releases/tags/r0.1.79.0".into() => json_response("{}"),
655 "/risc0_github/risc0/releases/download/v1.0.0/\
656 cargo-risczero-x86_64-unknown-linux-gnu.tgz".into() => dummy_tar_gz_response(),
657 "/risc0_github/risc0/releases/download/v1.0.0-rc.1/\
658 cargo-risczero-x86_64-unknown-linux-gnu.tgz".into() => dummy_tar_gz_response(),
659 "/risc0_github/risc0/releases/download/v1.0.0-rc.2/\
660 cargo-risczero-x86_64-unknown-linux-gnu.tgz".into() => dummy_tar_gz_response(),
661 "/risc0_github/risc0/releases/download/v1.0.0/\
662 cargo-risczero-aarch64-apple-darwin.tgz".into() => dummy_tar_gz_response(),
663 "/risc0_github/rust/releases/download/r0.1.79.0/\
664 rust-toolchain-x86_64-unknown-linux-gnu.tar.gz".into() => dummy_tar_gz_response(),
665 "/risc0_github/rust/releases/download/r0.1.79.0/\
666 rust-toolchain-aarch64-apple-darwin.tar.gz".into() => dummy_tar_gz_response(),
667 "/github_api/repos/risc0/toolchain/releases/tags/2024.01.05".into() =>
668 json_response("{}"),
669 "/risc0_github/toolchain/releases/download/2024.01.05/\
670 riscv32im-linux-x86_64.tar.xz".into() =>
671 dummy_tar_xz_response("riscv32im-linux-x86_64"),
672 "/risc0_github/toolchain/releases/download/2024.01.05/\
673 riscv32im-gdb-linux-x86_64.tar.xz".into() => dummy_tar_xz_response("."),
674 "/risc0_github/toolchain/releases/download/2024.01.05/\
675 riscv32im-osx-arm64.tar.xz".into() => dummy_tar_xz_response("riscv32im-osx-arm64"),
676 "/risc0_github/toolchain/releases/download/2024.01.05/\
677 riscv32im-gdb-osx-arm64.tar.xz".into() => dummy_tar_xz_response("."),
678 "/github_api/repos/risc0/toolchain/releases/tags/2024.01.06".into() =>
679 json_response("{}"),
680 "/risc0_github/toolchain/releases/download/2024.01.06/\
681 riscv32im-linux-x86_64.tar.xz".into() =>
682 dummy_tar_xz_response("riscv32im-linux-x86_64"),
683 "/risc0_github/toolchain/releases/download/2024.01.06/\
684 riscv32im-gdb-linux-x86_64.tar.xz".into() => dummy_tar_xz_response("."),
685 "/github_api/repos/risc0/rust/releases/tags/r0.1.81.0".into() => json_response("{}"),
686 "/risc0_github/rust/releases/download/r0.1.81.0/\
687 rust-toolchain-x86_64-unknown-linux-gnu.tar.gz".into() => dummy_tar_gz_response(),
688 "/risc0_github/rust/releases/download/r0.1.81.0/\
689 rust-toolchain-aarch64-apple-darwin.tar.gz".into() => dummy_tar_gz_response(),
690 "/github_api/repos/risc0/risc0/releases/tags/v5.0.0".into() => not_found(),
691 "/github_api/repos/risc0/risc0/releases/tags/v1.1.0".into() => json_response("{}"),
692 "/risc0_github/risc0/releases/download/v1.1.0/\
693 cargo-risczero-x86_64-unknown-linux-gnu.tgz".into() => dummy_tar_gz_response(),
694 "/risc0/install".into() => text_response(install_script.clone()),
695 "/s3/rzup/components/risc0-groth16/distribution_manifest.json".into() =>
696 json_response(serde_json::to_string(&risc0_groth16_manifest_json).unwrap()),
697 format!("/s3/rzup/components/risc0-groth16/sha256/{HELLO_WORLD_DUMMY_TAR_XZ_SHA256}") => dummy_tar_xz_response("hello-world"),
698 format!("/s3/rzup/components/risc0-groth16/sha256/{HELLO_WORLD2_DUMMY_TAR_XZ_SHA256}") => dummy_tar_xz_response("hello-world2"),
699 format!("/s3/rzup/components/risc0-groth16/sha256/{HELLO_WORLD3_DUMMY_TAR_XZ_SHA256}") => dummy_tar_xz_response("hello-world2"),
700 }
701 }
702
703 fn hyper_len(resp: HyperResponse) -> u64 {
704 use hyper::body::Body as _;
705
706 resp.body().size_hint().exact().unwrap()
707 }
708
709 async fn request_handler(
710 require_bearer_token: bool,
711 server_data: Arc<Mutex<HashMap<String, HyperResponse>>>,
712 req: hyper::Request<hyper::body::Incoming>,
713 ) -> std::result::Result<HyperResponse, Infallible> {
714 if require_bearer_token {
715 let value = req
716 .headers()
717 .get("Authorization")
718 .expect("Authorization provided");
719 assert_eq!(value, "Bearer suchsecrettesttoken");
720 }
721
722 let req_uri = req.uri().to_string();
723 if req.method() == http::Method::GET {
724 if let Some(response) = server_data.lock().unwrap().get(&req_uri) {
725 Ok(response.clone())
726 } else {
727 panic!("unexpected URI: {req_uri}");
728 }
729 } else if req.method() == http::Method::PUT {
730 use http_body_util::BodyExt as _;
731
732 for h in ["x-amz-date", "authorization", "x-amz-content-sha256"] {
734 req.headers()
735 .get(h)
736 .unwrap_or_else(|| panic!("expected header: {h}"));
737 }
738
739 let value = req
740 .headers()
741 .get("content-type")
742 .expect("expected header: content-type");
743 assert_eq!(value, "application/octet-stream");
744
745 let resp = hyper::Response::builder()
746 .status(200)
747 .header("content-type", "application/octet-stream")
748 .body(http_body_util::Full::new(
749 req.into_body().collect().await.unwrap().to_bytes(),
750 ))
751 .unwrap();
752 server_data.lock().unwrap().insert(req_uri, resp);
753
754 Ok(hyper::Response::builder()
755 .status(200)
756 .header("content-type", "application/xml")
757 .body(http_body_util::Full::new(hyper::body::Bytes::from(
758 "<xml>ok</xml>",
759 )))
760 .unwrap())
761 } else {
762 panic!("unexpected HTTP method {}", req.method());
763 }
764 }
765
766 #[tokio::main]
767 async fn server_main(
768 install_script: String,
769 require_bearer_token: bool,
770 private_key: PrivateKey,
771 sender: tokio::sync::oneshot::Sender<SocketAddr>,
772 ) {
773 let server_data = Arc::new(Mutex::new(build_mock_server_data(
774 install_script,
775 &private_key,
776 )));
777
778 let listener = tokio::net::TcpListener::bind("localhost:0").await.unwrap();
779 sender.send(listener.local_addr().unwrap()).unwrap();
780
781 while let Ok((stream, _)) = listener.accept().await {
782 let server_data = server_data.clone();
783 hyper::server::conn::http1::Builder::new()
784 .serve_connection(
785 hyper_util::rt::TokioIo::new(stream),
786 hyper::service::service_fn(move |req| {
787 request_handler(require_bearer_token, server_data.clone(), req)
788 }),
789 )
790 .await
791 .unwrap()
792 }
793 }
794
795 fn test_private_key() -> PrivateKey {
796 let mut rng = rand::thread_rng();
797 rsa::RsaPrivateKey::new(&mut rng, 2048).unwrap().into()
798 }
799
800 impl MockDistributionServer {
801 pub fn new_with_options(
802 install_script: String,
803 require_bearer_token: bool,
804 private_key: PrivateKey,
805 ) -> Self {
806 let (send, recv) = tokio::sync::oneshot::channel();
807 let private_key_other = private_key.clone();
808 std::thread::spawn(move || {
809 server_main(
810 install_script,
811 require_bearer_token,
812 private_key_other,
813 send,
814 )
815 });
816 let address = recv.blocking_recv().unwrap();
817 Self {
818 base_urls: BaseUrls {
819 risc0_github_base_url: format!("http://{address}/risc0_github"),
820 github_api_base_url: format!("http://{address}/github_api"),
821 risc0_base_url: format!("http://{address}/risc0"),
822 s3_base_url: format!("http://{address}/s3"),
823 },
824 private_key,
825 }
826 }
827
828 pub fn new() -> Self {
829 Self::new_with_options("".into(), false, test_private_key())
830 }
831
832 pub fn new_with_install_script(install_script: String) -> Self {
833 Self::new_with_options(install_script, false, test_private_key())
834 }
835
836 pub fn new_with_required_bearer_token() -> Self {
837 Self::new_with_options("".into(), true, test_private_key())
838 }
839 }
840
841 #[macro_export]
842 macro_rules! http_test_harness {
843 ($test_name:ident) => {
844 paste::paste! {
845 #[test]
846 #[ignore = "requires GitHub API access"]
847 fn [<$test_name _against_real_github>]() {
848 $test_name(Default::default(), PublicKey::official())
849 }
850
851 #[test]
852 fn [<$test_name _against_mock_server>]() {
853 let server = $crate::tests::MockDistributionServer::new();
854 $test_name(server.base_urls.clone(), server.private_key.public_key())
855 }
856 }
857 };
858 }
859
860 pub fn invalid_base_urls() -> BaseUrls {
861 BaseUrls {
862 risc0_github_base_url: "".into(),
863 github_api_base_url: "".into(),
864 risc0_base_url: "".into(),
865 s3_base_url: "".into(),
866 }
867 }
868
869 fn setup_test_env(
870 base_urls: BaseUrls,
871 github_token: Option<String>,
872 aws_creds: Option<AwsCredentials>,
873 private_key: PrivateKey,
874 platform: Platform,
875 ) -> (TempDir, Rzup) {
876 let tmp_dir = TempDir::new().unwrap();
877 let public_key = private_key.public_key();
878 let rzup = Rzup::with_paths_urls_creds_platform_and_event_handler(
879 tmp_dir.path().join(".risc0"),
880 tmp_dir.path().join(".rustup"),
881 tmp_dir.path().join(".cargo"),
882 base_urls,
883 github_token,
884 move || aws_creds.clone(),
885 move || Ok(private_key.clone()),
886 public_key,
887 platform,
888 |_| {},
889 )
890 .unwrap();
891 (tmp_dir, rzup)
892 }
893
894 #[test]
895 fn test_rzup_initialization() {
896 let (_tmp_dir, rzup) = setup_test_env(
897 invalid_base_urls(),
898 None,
899 None,
900 test_private_key(),
901 Platform::new("x86_64", Os::Linux),
902 );
903 assert!(rzup
904 .settings()
905 .get_default_version(&Component::RustToolchain)
906 .is_none());
907 assert!(rzup
908 .settings()
909 .get_default_version(&Component::CargoRiscZero)
910 .is_none());
911 }
912
913 fn test_install_and_uninstall_end_to_end(base_urls: BaseUrls, public_key: PublicKey) {
914 let tmp_dir = TempDir::new().unwrap();
915 let mut rzup = Rzup::with_paths_urls_creds_platform_and_event_handler(
916 tmp_dir.path().join(".risc0"),
917 tmp_dir.path().join(".rustup"),
918 tmp_dir.path().join(".cargo"),
919 base_urls,
920 None, || None,
922 || Err(RzupError::Other("no private key".into())),
923 public_key,
924 Platform::detect().unwrap(),
925 |_| {},
926 )
927 .unwrap();
928
929 let cargo_risczero_version = Version::new(1, 0, 0);
930
931 assert_eq!(
932 rzup.get_version_dir(&Component::CargoRiscZero, &cargo_risczero_version),
933 Err(RzupError::VersionNotFound(cargo_risczero_version.clone()))
934 );
935 assert_eq!(
936 rzup.get_version_dir(&Component::R0Vm, &cargo_risczero_version),
937 Err(RzupError::VersionNotFound(cargo_risczero_version.clone()))
938 );
939
940 rzup.install_component(
942 &Component::CargoRiscZero,
943 Some(cargo_risczero_version.clone()),
944 false,
945 )
946 .unwrap();
947 assert!(rzup
948 .version_exists(&Component::CargoRiscZero, &cargo_risczero_version)
949 .unwrap());
950 assert_eq!(
951 rzup.settings()
952 .get_default_version(&Component::CargoRiscZero)
953 .unwrap(),
954 cargo_risczero_version
955 );
956 assert_eq!(
957 rzup.list_versions(&Component::CargoRiscZero).unwrap(),
958 vec![Version::new(1, 0, 0)]
959 );
960 assert!(rzup
961 .get_version_dir(&Component::CargoRiscZero, &cargo_risczero_version)
962 .is_ok());
963
964 rzup.uninstall_component(&Component::CargoRiscZero, cargo_risczero_version.clone())
966 .unwrap();
967 assert!(!rzup
968 .version_exists(&Component::CargoRiscZero, &cargo_risczero_version)
969 .unwrap());
970 assert_eq!(
971 rzup.list_versions(&Component::CargoRiscZero).unwrap(),
972 vec![]
973 );
974 assert_eq!(
975 rzup.get_version_dir(&Component::CargoRiscZero, &cargo_risczero_version),
976 Err(RzupError::VersionNotFound(cargo_risczero_version.clone()))
977 );
978
979 rzup.install_component(
981 &Component::R0Vm,
982 Some(cargo_risczero_version.clone()),
983 false,
984 )
985 .unwrap();
986 assert!(rzup
987 .version_exists(&Component::R0Vm, &cargo_risczero_version)
988 .unwrap());
989 assert_eq!(
990 rzup.settings()
991 .get_default_version(&Component::R0Vm)
992 .unwrap(),
993 cargo_risczero_version
994 );
995 assert_eq!(
996 rzup.list_versions(&Component::R0Vm).unwrap(),
997 vec![Version::new(1, 0, 0)]
998 );
999 assert!(rzup
1000 .get_version_dir(&Component::R0Vm, &cargo_risczero_version)
1001 .is_ok());
1002
1003 rzup.uninstall_component(&Component::R0Vm, cargo_risczero_version.clone())
1005 .unwrap();
1006 assert!(!rzup
1007 .version_exists(&Component::R0Vm, &cargo_risczero_version)
1008 .unwrap());
1009 assert_eq!(rzup.list_versions(&Component::R0Vm).unwrap(), vec![]);
1010 assert_eq!(
1011 rzup.get_version_dir(&Component::R0Vm, &cargo_risczero_version),
1012 Err(RzupError::VersionNotFound(cargo_risczero_version.clone()))
1013 );
1014
1015 let rust_version = Version::new(1, 79, 0);
1017 assert_eq!(
1018 rzup.get_version_dir(&Component::RustToolchain, &rust_version),
1019 Err(RzupError::VersionNotFound(rust_version.clone()))
1020 );
1021 rzup.install_component(&Component::RustToolchain, Some(rust_version.clone()), false)
1022 .unwrap();
1023 assert!(rzup
1024 .version_exists(&Component::RustToolchain, &rust_version)
1025 .unwrap());
1026 assert_eq!(
1027 rzup.list_versions(&Component::RustToolchain).unwrap(),
1028 vec![Version::new(1, 79, 0)]
1029 );
1030 assert!(rzup
1031 .get_version_dir(&Component::RustToolchain, &rust_version)
1032 .is_ok());
1033
1034 rzup.uninstall_component(&Component::RustToolchain, rust_version.clone())
1036 .unwrap();
1037 assert!(!rzup
1038 .version_exists(&Component::RustToolchain, &rust_version)
1039 .unwrap());
1040 assert_eq!(
1041 rzup.list_versions(&Component::RustToolchain).unwrap(),
1042 vec![]
1043 );
1044 assert_eq!(
1045 rzup.get_version_dir(&Component::RustToolchain, &rust_version),
1046 Err(RzupError::VersionNotFound(rust_version.clone()))
1047 );
1048
1049 let cpp_version = Version::new(2024, 1, 5);
1051 assert_eq!(
1052 rzup.get_version_dir(&Component::CppToolchain, &cpp_version),
1053 Err(RzupError::VersionNotFound(cpp_version.clone()))
1054 );
1055 rzup.install_component(&Component::CppToolchain, Some(cpp_version.clone()), false)
1056 .unwrap();
1057 assert!(rzup
1058 .version_exists(&Component::CppToolchain, &cpp_version)
1059 .unwrap());
1060 assert_eq!(
1061 rzup.list_versions(&Component::CppToolchain).unwrap(),
1062 vec![Version::new(2024, 1, 5)]
1063 );
1064 assert!(rzup
1065 .get_version_dir(&Component::CppToolchain, &cpp_version)
1066 .is_ok());
1067
1068 rzup.uninstall_component(&Component::CppToolchain, cpp_version.clone())
1070 .unwrap();
1071 assert!(!rzup
1072 .version_exists(&Component::CppToolchain, &cpp_version)
1073 .unwrap());
1074 assert_eq!(
1075 rzup.list_versions(&Component::CppToolchain).unwrap(),
1076 vec![]
1077 );
1078 assert_eq!(
1079 rzup.get_version_dir(&Component::CppToolchain, &cpp_version),
1080 Err(RzupError::VersionNotFound(cpp_version.clone()))
1081 );
1082
1083 let groth16_version = Version::new(1, 0, 0);
1085 assert_eq!(
1086 rzup.get_version_dir(&Component::Risc0Groth16, &groth16_version),
1087 Err(RzupError::VersionNotFound(groth16_version.clone()))
1088 );
1089 rzup.install_component(
1090 &Component::Risc0Groth16,
1091 Some(groth16_version.clone()),
1092 false,
1093 )
1094 .unwrap();
1095 assert!(rzup
1096 .version_exists(&Component::Risc0Groth16, &groth16_version)
1097 .unwrap());
1098 assert_eq!(
1099 rzup.list_versions(&Component::Risc0Groth16).unwrap(),
1100 vec![Version::new(1, 0, 0)]
1101 );
1102 assert!(rzup
1103 .get_version_dir(&Component::Risc0Groth16, &groth16_version)
1104 .is_ok());
1105
1106 rzup.uninstall_component(&Component::Risc0Groth16, groth16_version.clone())
1108 .unwrap();
1109 assert!(!rzup
1110 .version_exists(&Component::Risc0Groth16, &groth16_version)
1111 .unwrap());
1112 assert_eq!(
1113 rzup.list_versions(&Component::Risc0Groth16).unwrap(),
1114 vec![]
1115 );
1116 assert_eq!(
1117 rzup.get_version_dir(&Component::Risc0Groth16, &groth16_version),
1118 Err(RzupError::VersionNotFound(groth16_version.clone()))
1119 );
1120 }
1121
1122 http_test_harness!(test_install_and_uninstall_end_to_end);
1123
1124 fn run_and_assert_events(
1125 rzup: &mut Rzup,
1126 body: impl FnOnce(&mut Rzup),
1127 expected_events: Vec<RzupEvent>,
1128 ) {
1129 let (event_send, event_recv) = std::sync::mpsc::channel();
1130 rzup.set_event_handler(move |event| {
1131 event_send.send(event).unwrap();
1132 });
1133
1134 body(rzup);
1135 rzup.set_event_handler(|_| {});
1136
1137 let mut events = vec![];
1138 while let Ok(event) = event_recv.recv() {
1139 if !matches!(event, RzupEvent::Debug { .. }) {
1140 events.push(event);
1141 }
1142 }
1143 assert_eq!(events, expected_events);
1144 }
1145
1146 fn assert_symlinks(path: &Path, mut expected_symlinks: Vec<(String, String)>) {
1147 let mut found_symlinks = vec![];
1148 for entry in walkdir::WalkDir::new(path) {
1149 let entry = entry.unwrap();
1150 if entry.path_is_symlink() {
1151 let entry_path = entry.path();
1152 let entry_relative_path = entry_path.strip_prefix(path).unwrap();
1153 let target_path = std::fs::read_link(entry_path).unwrap();
1154 let target_relative_path = target_path.strip_prefix(path).unwrap();
1155 found_symlinks.push((
1156 entry_relative_path.to_str().unwrap().to_owned(),
1157 target_relative_path.to_str().unwrap().to_owned(),
1158 ));
1159 }
1160 }
1161
1162 found_symlinks.sort();
1163 expected_symlinks.sort();
1164 assert_eq!(found_symlinks, expected_symlinks);
1165 }
1166
1167 fn assert_files(path: &Path, mut expected_files: Vec<String>) {
1168 expected_files.push(".risc0/settings.toml".into());
1170 expected_files.push(".risc0/.rzup".into());
1171
1172 let mut found_files = vec![];
1173 for entry in walkdir::WalkDir::new(path.join(".risc0")) {
1174 let entry = entry.unwrap();
1175 if entry.file_type().is_file() {
1176 let entry_path = entry.path();
1177 if entry_path.extension().is_none_or(|ext| ext != "lock") {
1178 let entry_relative_path = entry_path.strip_prefix(path).unwrap();
1179 found_files.push(entry_relative_path.to_str().unwrap().to_owned());
1180 }
1181 }
1182 }
1183
1184 found_files.sort();
1185 expected_files.sort();
1186 assert_eq!(found_files, expected_files);
1187 }
1188
1189 #[allow(clippy::too_many_arguments)]
1190 fn fresh_install_test(
1191 base_urls: BaseUrls,
1192 private_key: PrivateKey,
1193 component: Component,
1194 component_to_install: Component,
1195 version: Version,
1196 expected_url: String,
1197 download_length: u64,
1198 expected_files: Vec<String>,
1199 expected_symlinks: Vec<(String, String)>,
1200 expected_version_dir: &str,
1201 use_github_token: bool,
1202 platform: Platform,
1203 ) {
1204 let github_token = use_github_token.then_some("suchsecrettesttoken".into());
1205 let (tmp_dir, mut rzup) =
1206 setup_test_env(base_urls.clone(), github_token, None, private_key, platform);
1207
1208 run_and_assert_events(
1209 &mut rzup,
1210 |rzup| {
1211 rzup.install_component(&component, Some(version.clone()), false)
1212 .unwrap();
1213 assert!(rzup.version_exists(&component, &version).unwrap());
1214 },
1215 vec![
1216 RzupEvent::InstallationStarted {
1217 id: component.to_string(),
1218 version: version.to_string(),
1219 },
1220 RzupEvent::TransferStarted {
1221 kind: TransferKind::Download,
1222 id: component_to_install.to_string(),
1223 version: Some(version.to_string()),
1224 url: Some(expected_url),
1225 len: Some(download_length),
1226 },
1227 RzupEvent::TransferProgress {
1228 id: component_to_install.to_string(),
1229 incr: download_length,
1230 },
1231 RzupEvent::TransferCompleted {
1232 kind: TransferKind::Download,
1233 id: component_to_install.to_string(),
1234 version: Some(version.to_string()),
1235 },
1236 RzupEvent::InstallationCompleted {
1237 id: component.to_string(),
1238 version: version.to_string(),
1239 },
1240 ],
1241 );
1242
1243 let actual_version_dir = rzup.get_version_dir(&component, &version).unwrap();
1244 assert_eq!(
1245 actual_version_dir
1246 .strip_prefix(tmp_dir.path())
1247 .unwrap()
1248 .to_str()
1249 .unwrap(),
1250 expected_version_dir
1251 );
1252
1253 assert_symlinks(tmp_dir.path(), expected_symlinks);
1254 assert_files(tmp_dir.path(), expected_files);
1255 }
1256
1257 #[allow(clippy::too_many_arguments)]
1258 fn already_installed_test(
1259 base_urls: BaseUrls,
1260 private_key: PrivateKey,
1261 component: Component,
1262 version: Version,
1263 expected_files: Vec<String>,
1264 expected_symlinks: Vec<(String, String)>,
1265 expected_version_dir: &str,
1266 use_github_token: bool,
1267 platform: Platform,
1268 ) {
1269 let github_token = use_github_token.then_some("suchsecrettesttoken".into());
1270 let (tmp_dir, mut rzup) =
1271 setup_test_env(base_urls.clone(), github_token, None, private_key, platform);
1272
1273 rzup.install_component(&component, Some(version.clone()), false)
1274 .unwrap();
1275
1276 run_and_assert_events(
1277 &mut rzup,
1278 |rzup| {
1279 rzup.install_component(&component, Some(version.clone()), false)
1280 .unwrap();
1281 },
1282 vec![RzupEvent::ComponentAlreadyInstalled {
1283 id: component.to_string(),
1284 version: version.to_string(),
1285 }],
1286 );
1287
1288 let actual_version_dir = rzup.get_version_dir(&component, &version).unwrap();
1289 assert_eq!(
1290 actual_version_dir
1291 .strip_prefix(tmp_dir.path())
1292 .unwrap()
1293 .to_str()
1294 .unwrap(),
1295 expected_version_dir
1296 );
1297
1298 assert_symlinks(tmp_dir.path(), expected_symlinks);
1299 assert_files(tmp_dir.path(), expected_files);
1300 }
1301
1302 #[allow(clippy::too_many_arguments)]
1303 fn already_installed_force_test(
1304 base_urls: BaseUrls,
1305 private_key: PrivateKey,
1306 component: Component,
1307 component_to_install: Component,
1308 version: Version,
1309 expected_url: String,
1310 download_length: u64,
1311 expected_files: Vec<String>,
1312 expected_symlinks: Vec<(String, String)>,
1313 expected_version_dir: &str,
1314 use_github_token: bool,
1315 platform: Platform,
1316 ) {
1317 let github_token = use_github_token.then_some("suchsecrettesttoken".into());
1318 let (tmp_dir, mut rzup) =
1319 setup_test_env(base_urls.clone(), github_token, None, private_key, platform);
1320
1321 rzup.install_component(&component, Some(version.clone()), false)
1322 .unwrap();
1323
1324 run_and_assert_events(
1325 &mut rzup,
1326 |rzup| {
1327 rzup.install_component(&component, Some(version.clone()), true)
1328 .unwrap();
1329 },
1330 vec![
1331 RzupEvent::InstallationStarted {
1332 id: component.to_string(),
1333 version: version.to_string(),
1334 },
1335 RzupEvent::TransferStarted {
1336 kind: TransferKind::Download,
1337 id: component_to_install.to_string(),
1338 version: Some(version.to_string()),
1339 url: Some(expected_url),
1340 len: Some(download_length),
1341 },
1342 RzupEvent::TransferProgress {
1343 id: component_to_install.to_string(),
1344 incr: download_length,
1345 },
1346 RzupEvent::TransferCompleted {
1347 kind: TransferKind::Download,
1348 id: component_to_install.to_string(),
1349 version: Some(version.to_string()),
1350 },
1351 RzupEvent::InstallationCompleted {
1352 id: component.to_string(),
1353 version: version.to_string(),
1354 },
1355 ],
1356 );
1357
1358 let actual_version_dir = rzup.get_version_dir(&component, &version).unwrap();
1359 assert_eq!(
1360 actual_version_dir
1361 .strip_prefix(tmp_dir.path())
1362 .unwrap()
1363 .to_str()
1364 .unwrap(),
1365 expected_version_dir
1366 );
1367
1368 assert_symlinks(tmp_dir.path(), expected_symlinks);
1369 assert_files(tmp_dir.path(), expected_files);
1370 }
1371
1372 #[allow(clippy::too_many_arguments)]
1373 fn install_test(
1374 base_urls: BaseUrls,
1375 private_key: PrivateKey,
1376 component: Component,
1377 component_to_install: Component,
1378 version: Version,
1379 expected_url: String,
1380 download_length: u64,
1381 expected_files: Vec<String>,
1382 expected_symlinks: Vec<(String, String)>,
1383 expected_version_dir: &str,
1384 use_github_token: bool,
1385 platform: Platform,
1386 ) {
1387 fresh_install_test(
1388 base_urls.clone(),
1389 private_key.clone(),
1390 component,
1391 component_to_install,
1392 version.clone(),
1393 expected_url.clone(),
1394 download_length,
1395 expected_files.clone(),
1396 expected_symlinks.clone(),
1397 expected_version_dir,
1398 use_github_token,
1399 platform,
1400 );
1401
1402 already_installed_test(
1403 base_urls.clone(),
1404 private_key.clone(),
1405 component,
1406 version.clone(),
1407 expected_files.clone(),
1408 expected_symlinks.clone(),
1409 expected_version_dir,
1410 use_github_token,
1411 platform,
1412 );
1413
1414 already_installed_force_test(
1415 base_urls.clone(),
1416 private_key,
1417 component,
1418 component_to_install,
1419 version.clone(),
1420 expected_url.clone(),
1421 download_length,
1422 expected_files.clone(),
1423 expected_symlinks.clone(),
1424 expected_version_dir,
1425 use_github_token,
1426 platform,
1427 );
1428 }
1429
1430 fn test_install_cargo_risczero(platform: Platform, target_triple: &str) {
1431 let server = MockDistributionServer::new();
1432 install_test(
1433 server.base_urls.clone(),
1434 server.private_key.clone(),
1435 Component::CargoRiscZero,
1436 Component::CargoRiscZero,
1437 Version::new(1, 0, 0),
1438 format!(
1439 "{base_url}/risc0/releases/download/v1.0.0/\
1440 cargo-risczero-{target_triple}.tgz",
1441 base_url = server.base_urls.risc0_github_base_url
1442 ),
1443 hyper_len(dummy_tar_gz_response()),
1444 vec![format!(
1445 ".risc0/extensions/v1.0.0-cargo-risczero-{target_triple}/tar_contents.bin"
1446 )],
1447 vec![(
1448 ".cargo/bin/cargo-risczero".into(),
1449 format!(".risc0/extensions/v1.0.0-cargo-risczero-{target_triple}/cargo-risczero"),
1450 )],
1451 &format!(".risc0/extensions/v1.0.0-cargo-risczero-{target_triple}"),
1452 false, platform,
1454 )
1455 }
1456
1457 #[test]
1458 fn install_cargo_risczero_x86_64_linux() {
1459 test_install_cargo_risczero(
1460 Platform::new("x86_64", Os::Linux),
1461 "x86_64-unknown-linux-gnu",
1462 );
1463 }
1464
1465 #[test]
1466 fn install_cargo_risczero_aarch64_mac() {
1467 test_install_cargo_risczero(Platform::new("aarch64", Os::MacOs), "aarch64-apple-darwin");
1468 }
1469
1470 fn test_install_r0vm(platform: Platform, target_triple: &str) {
1471 let server = MockDistributionServer::new();
1472 install_test(
1473 server.base_urls.clone(),
1474 server.private_key.clone(),
1475 Component::R0Vm,
1476 Component::CargoRiscZero,
1477 Version::new(1, 0, 0),
1478 format!(
1479 "{base_url}/risc0/releases/download/v1.0.0/\
1480 cargo-risczero-{target_triple}.tgz",
1481 base_url = server.base_urls.risc0_github_base_url
1482 ),
1483 hyper_len(dummy_tar_gz_response()),
1484 vec![format!(
1485 ".risc0/extensions/v1.0.0-cargo-risczero-{target_triple}/tar_contents.bin"
1486 )],
1487 vec![
1488 (
1489 ".cargo/bin/cargo-risczero".into(),
1490 format!(
1491 ".risc0/extensions/v1.0.0-cargo-risczero-{target_triple}/cargo-risczero"
1492 ),
1493 ),
1494 (
1495 ".cargo/bin/r0vm".into(),
1496 format!(".risc0/extensions/v1.0.0-cargo-risczero-{target_triple}/r0vm"),
1497 ),
1498 ],
1499 &format!(".risc0/extensions/v1.0.0-cargo-risczero-{target_triple}"),
1500 false, platform,
1502 )
1503 }
1504
1505 #[test]
1506 fn install_r0vm_x86_64_linux() {
1507 test_install_r0vm(
1508 Platform::new("x86_64", Os::Linux),
1509 "x86_64-unknown-linux-gnu",
1510 );
1511 }
1512
1513 #[test]
1514 fn install_r0vm_aarch64_mac() {
1515 test_install_r0vm(Platform::new("aarch64", Os::MacOs), "aarch64-apple-darwin");
1516 }
1517
1518 fn test_install_rust(platform: Platform, target_triple: &str) {
1519 let server = MockDistributionServer::new();
1520 install_test(
1521 server.base_urls.clone(),
1522 server.private_key.clone(),
1523 Component::RustToolchain,
1524 Component::RustToolchain,
1525 Version::new(1, 81, 0),
1526 format!(
1527 "{base_url}/rust/releases/download/r0.1.81.0/\
1528 rust-toolchain-{target_triple}.tar.gz",
1529 base_url = server.base_urls.risc0_github_base_url
1530 ),
1531 hyper_len(dummy_tar_gz_response()),
1532 vec![format!(
1533 ".risc0/toolchains/v1.81.0-rust-{target_triple}/tar_contents.bin"
1534 )],
1535 vec![(
1536 ".rustup/toolchains/risc0".into(),
1537 format!(".risc0/toolchains/v1.81.0-rust-{target_triple}"),
1538 )],
1539 &format!(".risc0/toolchains/v1.81.0-rust-{target_triple}"),
1540 false, platform,
1542 )
1543 }
1544
1545 #[test]
1546 fn install_rust_x86_64_linux() {
1547 test_install_rust(
1548 Platform::new("x86_64", Os::Linux),
1549 "x86_64-unknown-linux-gnu",
1550 );
1551 }
1552
1553 #[test]
1554 fn install_rust_aarch64_mac() {
1555 test_install_rust(Platform::new("aarch64", Os::MacOs), "aarch64-apple-darwin");
1556 }
1557
1558 fn test_install_cpp(platform: Platform, target_double: &str, target_triple: &str) {
1559 let server = MockDistributionServer::new();
1560
1561 let download_size = hyper_len(dummy_tar_xz_response(&format!("riscv32im-{target_double}")));
1563
1564 install_test(
1565 server.base_urls.clone(),
1566 server.private_key.clone(),
1567 Component::CppToolchain,
1568 Component::CppToolchain,
1569 Version::new(2024, 1, 5),
1570 format!(
1571 "{base_url}/toolchain/releases/download/2024.01.05/\
1572 riscv32im-{target_double}.tar.xz",
1573 base_url = server.base_urls.risc0_github_base_url
1574 ),
1575 download_size,
1576 vec![format!(
1577 ".risc0/toolchains/v2024.1.5-cpp-{target_triple}/\
1578 riscv32im-{target_double}/tar_contents.bin"
1579 )],
1580 vec![(
1581 ".risc0/cpp".into(),
1582 format!(
1583 ".risc0/toolchains/v2024.1.5-cpp-{target_triple}/riscv32im-{target_double}"
1584 ),
1585 )],
1586 &format!(".risc0/toolchains/v2024.1.5-cpp-{target_triple}/riscv32im-{target_double}"),
1587 false, platform,
1589 )
1590 }
1591
1592 #[test]
1593 fn install_cpp_x86_64_linux() {
1594 test_install_cpp(
1595 Platform::new("x86_64", Os::Linux),
1596 "linux-x86_64",
1597 "x86_64-unknown-linux-gnu",
1598 );
1599 }
1600
1601 #[test]
1602 fn install_cpp_aarch64_mac() {
1603 test_install_cpp(
1604 Platform::new("aarch64", Os::MacOs),
1605 "osx-arm64",
1606 "aarch64-apple-darwin",
1607 );
1608 }
1609
1610 fn test_install_gdb(platform: Platform, target_double: &str, target_triple: &str) {
1611 let server = MockDistributionServer::new();
1612
1613 install_test(
1614 server.base_urls.clone(),
1615 server.private_key.clone(),
1616 Component::Gdb,
1617 Component::Gdb,
1618 Version::new(2024, 1, 5),
1619 format!(
1620 "{base_url}/toolchain/releases/download/2024.01.05/\
1621 riscv32im-gdb-{target_double}.tar.xz",
1622 base_url = server.base_urls.risc0_github_base_url
1623 ),
1624 hyper_len(dummy_tar_xz_response(".")), vec![format!(
1626 ".risc0/extensions/v2024.1.5-gdb-{target_triple}/tar_contents.bin"
1627 )],
1628 vec![(
1629 ".risc0/bin/riscv32im-gdb".into(),
1630 format!(".risc0/extensions/v2024.1.5-gdb-{target_triple}/riscv32im-gdb"),
1631 )],
1632 &format!(".risc0/extensions/v2024.1.5-gdb-{target_triple}"),
1633 false, platform,
1635 )
1636 }
1637
1638 #[test]
1639 fn install_gdb_x86_64_linux() {
1640 test_install_gdb(
1641 Platform::new("x86_64", Os::Linux),
1642 "linux-x86_64",
1643 "x86_64-unknown-linux-gnu",
1644 );
1645 }
1646
1647 #[test]
1648 fn install_gdb_aarch64_mac() {
1649 test_install_gdb(
1650 Platform::new("aarch64", Os::MacOs),
1651 "osx-arm64",
1652 "aarch64-apple-darwin",
1653 );
1654 }
1655
1656 fn test_install_risc0_groth16(platform: Platform) {
1657 let server = MockDistributionServer::new();
1658
1659 install_test(
1660 server.base_urls.clone(),
1661 server.private_key.clone(),
1662 Component::Risc0Groth16,
1663 Component::Risc0Groth16,
1664 Version::new(1, 0, 0),
1665 format!(
1666 "{base_url}/rzup/components/risc0-groth16/sha256/{HELLO_WORLD_DUMMY_TAR_XZ_SHA256}",
1667 base_url = server.base_urls.s3_base_url
1668 ),
1669 140, vec![format!(
1671 ".risc0/extensions/v1.0.0-risc0-groth16/hello-world/tar_contents.bin"
1672 )],
1673 vec![],
1674 ".risc0/extensions/v1.0.0-risc0-groth16",
1675 false, platform,
1677 )
1678 }
1679
1680 #[test]
1681 fn install_risc0_groth16_x86_64_linux() {
1682 test_install_risc0_groth16(Platform::new("x86_64", Os::Linux));
1683 }
1684
1685 #[test]
1686 fn install_risc0_groth16_aarch64_mac() {
1687 test_install_risc0_groth16(Platform::new("aarch64", Os::MacOs));
1688 }
1689
1690 #[test]
1691 fn install_with_github_token() {
1692 let server = MockDistributionServer::new_with_required_bearer_token();
1693 install_test(
1694 server.base_urls.clone(),
1695 server.private_key.clone(),
1696 Component::CargoRiscZero,
1697 Component::CargoRiscZero,
1698 Version::new(1, 0, 0),
1699 format!(
1700 "{base_url}/risc0/releases/download/v1.0.0/\
1701 cargo-risczero-x86_64-unknown-linux-gnu.tgz",
1702 base_url = server.base_urls.risc0_github_base_url
1703 ),
1704 hyper_len(dummy_tar_gz_response()),
1705 vec![
1706 ".risc0/extensions/v1.0.0-cargo-risczero-x86_64-unknown-linux-gnu/tar_contents.bin"
1707 .into(),
1708 ],
1709 vec![(
1710 ".cargo/bin/cargo-risczero".into(),
1711 ".risc0/extensions/v1.0.0-cargo-risczero-x86_64-unknown-linux-gnu/cargo-risczero"
1712 .into(),
1713 )],
1714 ".risc0/extensions/v1.0.0-cargo-risczero-x86_64-unknown-linux-gnu",
1715 true, Platform::new("x86_64", Os::Linux),
1717 )
1718 }
1719
1720 fn test_list_multiple_versions(component: Component, version1: Version, version2: Version) {
1721 let server = MockDistributionServer::new();
1722 let (_tmp_dir, mut rzup) = setup_test_env(
1723 server.base_urls.clone(),
1724 None,
1725 None,
1726 server.private_key.clone(),
1727 Platform::new("x86_64", Os::Linux),
1728 );
1729
1730 rzup.install_component(&component, Some(version1.clone()), false)
1731 .unwrap();
1732
1733 rzup.install_component(&component, Some(version2.clone()), false)
1734 .unwrap();
1735
1736 assert_eq!(
1737 rzup.list_versions(&component).unwrap(),
1738 vec![version2, version1]
1739 );
1740 }
1741
1742 #[test]
1743 fn list_multiple_versions_cargo_risczero() {
1744 test_list_multiple_versions(
1745 Component::CargoRiscZero,
1746 Version::new(1, 0, 0),
1747 Version::new(1, 1, 0),
1748 );
1749 }
1750
1751 #[test]
1752 fn list_multiple_versions_r0vm() {
1753 test_list_multiple_versions(
1754 Component::R0Vm,
1755 Version::new(1, 0, 0),
1756 Version::new(1, 1, 0),
1757 );
1758 }
1759
1760 #[test]
1761 fn list_multiple_versions_rust() {
1762 test_list_multiple_versions(
1763 Component::RustToolchain,
1764 Version::new(1, 79, 0),
1765 Version::new(1, 81, 0),
1766 );
1767 }
1768
1769 #[test]
1770 fn list_multiple_versions_cpp() {
1771 test_list_multiple_versions(
1772 Component::CppToolchain,
1773 Version::new(2024, 1, 5),
1774 Version::new(2024, 1, 6),
1775 );
1776 }
1777
1778 #[test]
1779 fn list_multiple_versions_gdb() {
1780 test_list_multiple_versions(
1781 Component::Gdb,
1782 Version::new(2024, 1, 5),
1783 Version::new(2024, 1, 6),
1784 );
1785 }
1786
1787 #[test]
1788 fn list_multiple_versions_risc0_groth16() {
1789 test_list_multiple_versions(
1790 Component::Risc0Groth16,
1791 Version::new(1, 0, 0),
1792 Version::new(2, 0, 0),
1793 );
1794 }
1795
1796 fn set_default_version_test(
1797 rzup: &mut Rzup,
1798 tmp_dir: &TempDir,
1799 component: Component,
1800 version1: Version,
1801 version2: Version,
1802 expected_symlinks1: Vec<(String, String)>,
1803 expected_symlinks2: Vec<(String, String)>,
1804 ) {
1805 rzup.install_component(&component, Some(version1.clone()), false)
1806 .unwrap();
1807
1808 assert_eq!(
1809 rzup.get_default_version(&component).unwrap().unwrap().0,
1810 version1
1811 );
1812
1813 rzup.install_component(&component, Some(version2.clone()), false)
1814 .unwrap();
1815 assert_symlinks(tmp_dir.path(), expected_symlinks2.clone());
1816
1817 assert_eq!(
1818 rzup.get_default_version(&component).unwrap().unwrap().0,
1819 version2
1820 );
1821
1822 rzup.set_default_version(&component, version1.clone())
1823 .unwrap();
1824 assert_symlinks(tmp_dir.path(), expected_symlinks1.clone());
1825
1826 assert_eq!(
1827 rzup.get_default_version(&component).unwrap().unwrap().0,
1828 version1
1829 );
1830 }
1831
1832 #[test]
1833 fn set_default_version_cargo_risczero() {
1834 let server = MockDistributionServer::new();
1835 let (tmp_dir, mut rzup) = setup_test_env(
1836 server.base_urls.clone(),
1837 None,
1838 None,
1839 server.private_key.clone(),
1840 Platform::new("x86_64", Os::Linux),
1841 );
1842
1843 set_default_version_test(
1844 &mut rzup,
1845 &tmp_dir,
1846 Component::CargoRiscZero,
1847 Version::new(1, 0, 0),
1848 Version::new(1, 1, 0),
1849 vec![(
1850 ".cargo/bin/cargo-risczero".into(),
1851 ".risc0/extensions/v1.0.0-cargo-risczero-x86_64-unknown-linux-gnu/cargo-risczero"
1852 .into(),
1853 )],
1854 vec![(
1855 ".cargo/bin/cargo-risczero".into(),
1856 ".risc0/extensions/v1.1.0-cargo-risczero-x86_64-unknown-linux-gnu/cargo-risczero"
1857 .into(),
1858 )],
1859 );
1860 }
1861
1862 #[test]
1863 fn set_default_version_r0vm() {
1864 let server = MockDistributionServer::new();
1865 let (tmp_dir, mut rzup) = setup_test_env(
1866 server.base_urls.clone(),
1867 None,
1868 None,
1869 server.private_key.clone(),
1870 Platform::new("x86_64", Os::Linux),
1871 );
1872
1873 set_default_version_test(
1874 &mut rzup,
1875 &tmp_dir,
1876 Component::R0Vm,
1877 Version::new(1, 0, 0),
1878 Version::new(1, 1, 0),
1879 vec![
1880 (
1881 ".cargo/bin/cargo-risczero".into(),
1882 ".risc0/extensions/v1.0.0-cargo-risczero-x86_64-unknown-linux-gnu/cargo-risczero"
1883 .into(),
1884 ),
1885 (
1886 ".cargo/bin/r0vm".into(),
1887 ".risc0/extensions/v1.0.0-cargo-risczero-x86_64-unknown-linux-gnu/r0vm".into(),
1888 )
1889 ],
1890 vec![
1891 (
1892 ".cargo/bin/cargo-risczero".into(),
1893 ".risc0/extensions/v1.0.0-cargo-risczero-x86_64-unknown-linux-gnu/cargo-risczero"
1894 .into(),
1895 ),
1896 (
1897 ".cargo/bin/r0vm".into(),
1898 ".risc0/extensions/v1.1.0-cargo-risczero-x86_64-unknown-linux-gnu/r0vm".into(),
1899 )
1900 ],
1901 );
1902 }
1903
1904 #[test]
1905 fn set_default_version_r0vm_rc_version() {
1906 let server = MockDistributionServer::new();
1907 let (tmp_dir, mut rzup) = setup_test_env(
1908 server.base_urls.clone(),
1909 None,
1910 None,
1911 server.private_key.clone(),
1912 Platform::new("x86_64", Os::Linux),
1913 );
1914
1915 set_default_version_test(
1916 &mut rzup,
1917 &tmp_dir,
1918 Component::R0Vm,
1919 Version::parse("1.0.0-rc.1").unwrap(),
1920 Version::parse("1.0.0-rc.2").unwrap(),
1921 vec![
1922 (
1923 ".cargo/bin/cargo-risczero".into(),
1924 ".risc0/extensions/v1.0.0-rc.1-cargo-risczero-x86_64-unknown-linux-gnu/cargo-risczero"
1925 .into(),
1926 ),
1927 (
1928 ".cargo/bin/r0vm".into(),
1929 ".risc0/extensions/v1.0.0-rc.1-cargo-risczero-x86_64-unknown-linux-gnu/r0vm"
1930 .into(),
1931 )
1932 ],
1933 vec![
1934 (
1935 ".cargo/bin/cargo-risczero".into(),
1936 ".risc0/extensions/v1.0.0-rc.1-cargo-risczero-x86_64-unknown-linux-gnu/cargo-risczero"
1937 .into(),
1938 ),
1939 (
1940 ".cargo/bin/r0vm".into(),
1941 ".risc0/extensions/v1.0.0-rc.2-cargo-risczero-x86_64-unknown-linux-gnu/r0vm"
1942 .into(),
1943 )
1944 ],
1945 );
1946 }
1947
1948 #[test]
1949 fn set_default_version_r0vm_after_cargo_risczero_installed() {
1950 let server = MockDistributionServer::new();
1951 let (tmp_dir, mut rzup) = setup_test_env(
1952 server.base_urls.clone(),
1953 None,
1954 None,
1955 server.private_key.clone(),
1956 Platform::new("x86_64", Os::Linux),
1957 );
1958
1959 rzup.install_component(
1960 &Component::CargoRiscZero,
1961 Some(Version::new(1, 0, 0)),
1962 false, )
1964 .unwrap();
1965 rzup.install_component(
1966 &Component::CargoRiscZero,
1967 Some(Version::new(1, 1, 0)),
1968 false, )
1970 .unwrap();
1971
1972 set_default_version_test(
1973 &mut rzup,
1974 &tmp_dir,
1975 Component::R0Vm,
1976 Version::new(1, 0, 0),
1977 Version::new(1, 1, 0),
1978 vec![
1979 (
1980 ".cargo/bin/cargo-risczero".into(),
1981 ".risc0/extensions/v1.1.0-cargo-risczero-x86_64-unknown-linux-gnu/cargo-risczero"
1982 .into(),
1983 ),
1984 (
1985 ".cargo/bin/r0vm".into(),
1986 ".risc0/extensions/v1.0.0-cargo-risczero-x86_64-unknown-linux-gnu/r0vm".into(),
1987 )
1988 ],
1989 vec![
1990 (
1991 ".cargo/bin/cargo-risczero".into(),
1992 ".risc0/extensions/v1.1.0-cargo-risczero-x86_64-unknown-linux-gnu/cargo-risczero"
1993 .into(),
1994 ),
1995 (
1996 ".cargo/bin/r0vm".into(),
1997 ".risc0/extensions/v1.1.0-cargo-risczero-x86_64-unknown-linux-gnu/r0vm".into(),
1998 )
1999 ],
2000 );
2001 }
2002
2003 #[test]
2004 fn set_default_version_rust() {
2005 let server = MockDistributionServer::new();
2006 let (tmp_dir, mut rzup) = setup_test_env(
2007 server.base_urls.clone(),
2008 None,
2009 None,
2010 server.private_key.clone(),
2011 Platform::new("x86_64", Os::Linux),
2012 );
2013
2014 set_default_version_test(
2015 &mut rzup,
2016 &tmp_dir,
2017 Component::RustToolchain,
2018 Version::new(1, 79, 0),
2019 Version::new(1, 81, 0),
2020 vec![(
2021 ".rustup/toolchains/risc0".into(),
2022 ".risc0/toolchains/v1.79.0-rust-x86_64-unknown-linux-gnu".into(),
2023 )],
2024 vec![(
2025 ".rustup/toolchains/risc0".into(),
2026 ".risc0/toolchains/v1.81.0-rust-x86_64-unknown-linux-gnu".into(),
2027 )],
2028 );
2029 }
2030
2031 #[test]
2032 fn set_default_version_cpp() {
2033 let server = MockDistributionServer::new();
2034 let (tmp_dir, mut rzup) = setup_test_env(
2035 server.base_urls.clone(),
2036 None,
2037 None,
2038 server.private_key.clone(),
2039 Platform::new("x86_64", Os::Linux),
2040 );
2041
2042 set_default_version_test(
2043 &mut rzup,
2044 &tmp_dir,
2045 Component::CppToolchain,
2046 Version::new(2024, 1, 5),
2047 Version::new(2024, 1, 6),
2048 vec![(
2049 ".risc0/cpp".into(),
2050 ".risc0/toolchains/v2024.1.5-cpp-x86_64-unknown-linux-gnu/riscv32im-linux-x86_64"
2051 .into(),
2052 )],
2053 vec![(
2054 ".risc0/cpp".into(),
2055 ".risc0/toolchains/v2024.1.6-cpp-x86_64-unknown-linux-gnu/riscv32im-linux-x86_64"
2056 .into(),
2057 )],
2058 );
2059 }
2060
2061 #[test]
2062 fn set_default_version_gdb() {
2063 let server = MockDistributionServer::new();
2064 let (tmp_dir, mut rzup) = setup_test_env(
2065 server.base_urls.clone(),
2066 None,
2067 None,
2068 server.private_key.clone(),
2069 Platform::new("x86_64", Os::Linux),
2070 );
2071
2072 set_default_version_test(
2073 &mut rzup,
2074 &tmp_dir,
2075 Component::Gdb,
2076 Version::new(2024, 1, 5),
2077 Version::new(2024, 1, 6),
2078 vec![(
2079 ".risc0/bin/riscv32im-gdb".into(),
2080 ".risc0/extensions/v2024.1.5-gdb-x86_64-unknown-linux-gnu/riscv32im-gdb".into(),
2081 )],
2082 vec![(
2083 ".risc0/bin/riscv32im-gdb".into(),
2084 ".risc0/extensions/v2024.1.6-gdb-x86_64-unknown-linux-gnu/riscv32im-gdb".into(),
2085 )],
2086 );
2087 }
2088
2089 #[test]
2090 fn set_default_version_risc0_groth16() {
2091 let server = MockDistributionServer::new();
2092 let (tmp_dir, mut rzup) = setup_test_env(
2093 server.base_urls.clone(),
2094 None,
2095 None,
2096 server.private_key.clone(),
2097 Platform::new("x86_64", Os::Linux),
2098 );
2099
2100 set_default_version_test(
2101 &mut rzup,
2102 &tmp_dir,
2103 Component::Risc0Groth16,
2104 Version::new(1, 0, 0),
2105 Version::new(2, 0, 0),
2106 vec![],
2107 vec![],
2108 );
2109 }
2110
2111 fn default_version_after_uninstall(
2112 tmp_dir: &TempDir,
2113 rzup: &mut Rzup,
2114 component: Component,
2115 version1: Version,
2116 version2: Version,
2117 uninstall_with_rm: bool,
2118 expected_path: &Path,
2119 ) {
2120 rzup.install_component(&component, Some(version2.clone()), false)
2121 .unwrap();
2122
2123 rzup.install_component(&component, Some(version1.clone()), false)
2124 .unwrap();
2125
2126 if uninstall_with_rm {
2127 let mut version_dir = rzup.get_version_dir(&component, &version1).unwrap();
2128 if component == Component::CppToolchain {
2130 version_dir.pop();
2131 }
2132 std::fs::remove_dir_all(version_dir).unwrap()
2133 } else {
2134 rzup.uninstall_component(&component, version1.clone())
2135 .unwrap();
2136
2137 let settings: settings::Settings = toml::from_str(
2139 &std::fs::read_to_string(tmp_dir.path().join(".risc0/settings.toml")).unwrap(),
2140 )
2141 .unwrap();
2142 let mut expected = settings::Settings::default();
2143 expected.set_default_version(&component, &version2);
2144 if let Some(parent) = component.parent_component() {
2145 expected.set_default_version(&parent, &version2);
2146 }
2147 assert_eq!(settings, expected);
2148 }
2149
2150 let (default_version, path) = rzup.get_default_version(&component).unwrap().unwrap();
2151 assert_eq!(default_version, version2);
2152 assert_eq!(path, expected_path);
2153 }
2154
2155 #[test]
2156 fn default_version_after_uninstall_cargo_risczero() {
2157 let server = MockDistributionServer::new();
2158 let (tmp_dir, mut rzup) = setup_test_env(
2159 server.base_urls.clone(),
2160 None,
2161 None,
2162 server.private_key.clone(),
2163 Platform::new("x86_64", Os::Linux),
2164 );
2165
2166 for uninstall_with_rm in [true, false] {
2167 default_version_after_uninstall(
2168 &tmp_dir,
2169 &mut rzup,
2170 Component::CargoRiscZero,
2171 Version::new(1, 0, 0),
2172 Version::new(1, 1, 0),
2173 uninstall_with_rm,
2174 &tmp_dir
2175 .path()
2176 .join(".risc0/extensions/v1.1.0-cargo-risczero-x86_64-unknown-linux-gnu"),
2177 );
2178 }
2179 }
2180
2181 #[test]
2182 fn default_version_after_uninstall_r0vm() {
2183 let server = MockDistributionServer::new();
2184 let (tmp_dir, mut rzup) = setup_test_env(
2185 server.base_urls.clone(),
2186 None,
2187 None,
2188 server.private_key.clone(),
2189 Platform::new("x86_64", Os::Linux),
2190 );
2191
2192 for uninstall_with_rm in [true, false] {
2193 default_version_after_uninstall(
2194 &tmp_dir,
2195 &mut rzup,
2196 Component::R0Vm,
2197 Version::new(1, 0, 0),
2198 Version::new(1, 1, 0),
2199 uninstall_with_rm,
2200 &tmp_dir
2201 .path()
2202 .join(".risc0/extensions/v1.1.0-cargo-risczero-x86_64-unknown-linux-gnu"),
2203 );
2204 }
2205 }
2206
2207 #[test]
2208 fn default_version_after_uninstall_rust() {
2209 let server = MockDistributionServer::new();
2210 let (tmp_dir, mut rzup) = setup_test_env(
2211 server.base_urls.clone(),
2212 None,
2213 None,
2214 server.private_key.clone(),
2215 Platform::new("x86_64", Os::Linux),
2216 );
2217
2218 for uninstall_with_rm in [true, false] {
2219 default_version_after_uninstall(
2220 &tmp_dir,
2221 &mut rzup,
2222 Component::RustToolchain,
2223 Version::new(1, 79, 0),
2224 Version::new(1, 81, 0),
2225 uninstall_with_rm,
2226 &tmp_dir
2227 .path()
2228 .join(".risc0/toolchains/v1.81.0-rust-x86_64-unknown-linux-gnu"),
2229 );
2230 }
2231 }
2232
2233 #[test]
2234 fn default_version_after_uninstall_cpp() {
2235 let server = MockDistributionServer::new();
2236 let (tmp_dir, mut rzup) = setup_test_env(
2237 server.base_urls.clone(),
2238 None,
2239 None,
2240 server.private_key.clone(),
2241 Platform::new("x86_64", Os::Linux),
2242 );
2243
2244 for uninstall_with_rm in [true, false] {
2245 default_version_after_uninstall(
2246 &tmp_dir,
2247 &mut rzup,
2248 Component::CppToolchain,
2249 Version::new(2024, 1, 5),
2250 Version::new(2024, 1, 6),
2251 uninstall_with_rm,
2252 &tmp_dir.path().join(
2253 ".risc0/toolchains/v2024.1.6-cpp-x86_64-unknown-linux-gnu/riscv32im-linux-x86_64",
2254 ),
2255 );
2256 }
2257 }
2258
2259 #[test]
2260 fn default_version_after_uninstall_gdb() {
2261 let server = MockDistributionServer::new();
2262 let (tmp_dir, mut rzup) = setup_test_env(
2263 server.base_urls.clone(),
2264 None,
2265 None,
2266 server.private_key.clone(),
2267 Platform::new("x86_64", Os::Linux),
2268 );
2269
2270 for uninstall_with_rm in [true, false] {
2271 default_version_after_uninstall(
2272 &tmp_dir,
2273 &mut rzup,
2274 Component::Gdb,
2275 Version::new(2024, 1, 5),
2276 Version::new(2024, 1, 6),
2277 uninstall_with_rm,
2278 &tmp_dir
2279 .path()
2280 .join(".risc0/extensions/v2024.1.6-gdb-x86_64-unknown-linux-gnu"),
2281 );
2282 }
2283 }
2284
2285 #[test]
2286 fn default_version_after_uninstall_risc0_groth16() {
2287 let server = MockDistributionServer::new();
2288 let (tmp_dir, mut rzup) = setup_test_env(
2289 server.base_urls.clone(),
2290 None,
2291 None,
2292 server.private_key.clone(),
2293 Platform::new("x86_64", Os::Linux),
2294 );
2295
2296 for uninstall_with_rm in [true, false] {
2297 default_version_after_uninstall(
2298 &tmp_dir,
2299 &mut rzup,
2300 Component::Risc0Groth16,
2301 Version::new(1, 0, 0),
2302 Version::new(2, 0, 0),
2303 uninstall_with_rm,
2304 &tmp_dir
2305 .path()
2306 .join(".risc0/extensions/v2.0.0-risc0-groth16"),
2307 );
2308 }
2309 }
2310
2311 #[test]
2312 fn install_non_existent() {
2313 let server = MockDistributionServer::new();
2314 let (_tmp_dir, mut rzup) = setup_test_env(
2315 server.base_urls.clone(),
2316 None,
2317 None,
2318 server.private_key.clone(),
2319 Platform::new("x86_64", Os::Linux),
2320 );
2321 let cargo_risczero_version = Version::new(5, 0, 0);
2322
2323 run_and_assert_events(
2324 &mut rzup,
2325 |rzup| {
2326 let error = rzup
2327 .install_component(
2328 &Component::CargoRiscZero,
2329 Some(cargo_risczero_version.clone()),
2330 false,
2331 )
2332 .unwrap_err();
2333 assert_eq!(
2334 error,
2335 RzupError::InvalidVersion("5.0.0 is not available for cargo-risczero".into())
2336 );
2337 },
2338 vec![
2339 RzupEvent::InstallationStarted {
2340 id: "cargo-risczero".into(),
2341 version: "5.0.0".into(),
2342 },
2343 RzupEvent::InstallationFailed {
2344 id: "cargo-risczero".into(),
2345 version: "5.0.0".into(),
2346 },
2347 ],
2348 );
2349
2350 assert_eq!(
2351 rzup.list_versions(&Component::CargoRiscZero).unwrap(),
2352 vec![]
2353 );
2354 }
2355
2356 #[test]
2357 fn install_bad_shasum() {
2358 let server = MockDistributionServer::new();
2359 let (_tmp_dir, mut rzup) = setup_test_env(
2360 server.base_urls.clone(),
2361 None,
2362 None,
2363 server.private_key.clone(),
2364 Platform::new("x86_64", Os::Linux),
2365 );
2366
2367 let base_url = &server.base_urls.s3_base_url;
2368 run_and_assert_events(
2369 &mut rzup,
2370 |rzup| {
2371 let error = rzup
2372 .install_component(
2373 &Component::Risc0Groth16,
2374 Some("3.0.0-badsha".parse().unwrap()),
2375 false,
2376 )
2377 .unwrap_err();
2378 assert_eq!(
2379 error,
2380 RzupError::Sha256Mismatch {
2381 expected: HELLO_WORLD3_DUMMY_TAR_XZ_SHA256.into(),
2382 actual: HELLO_WORLD2_DUMMY_TAR_XZ_SHA256.into()
2383 }
2384 );
2385 },
2386 vec![
2387 RzupEvent::InstallationStarted {
2388 id: "risc0-groth16".into(),
2389 version: "3.0.0-badsha".into(),
2390 },
2391 RzupEvent::TransferStarted {
2392 kind: TransferKind::Download,
2393 id: "risc0-groth16".into(),
2394 version: Some("3.0.0-badsha".into()),
2395 url: Some(format!(
2396 "{base_url}/rzup/components/risc0-groth16/sha256/{HELLO_WORLD3_DUMMY_TAR_XZ_SHA256}"
2397 )),
2398 len: Some(hyper_len(dummy_tar_xz_response("hello-world"))),
2399 },
2400 RzupEvent::TransferProgress {
2401 id: "risc0-groth16".into(),
2402 incr: hyper_len(dummy_tar_xz_response("hello-world")),
2403 },
2404 RzupEvent::InstallationFailed {
2405 id: "risc0-groth16".into(),
2406 version: "3.0.0-badsha".into(),
2407 },
2408 ],
2409 );
2410
2411 assert_eq!(
2412 rzup.list_versions(&Component::Risc0Groth16).unwrap(),
2413 vec![]
2414 );
2415 }
2416
2417 #[test]
2418 fn install_bad_signature() {
2419 let server = MockDistributionServer::new();
2420 let (_tmp_dir, mut rzup) = setup_test_env(
2421 server.base_urls.clone(),
2422 None,
2423 None,
2424 test_private_key(), Platform::new("x86_64", Os::Linux),
2426 );
2427
2428 run_and_assert_events(
2429 &mut rzup,
2430 |rzup| {
2431 let error = rzup
2432 .install_component(
2433 &Component::Risc0Groth16,
2434 Some("2.0.0".parse().unwrap()),
2435 false,
2436 )
2437 .unwrap_err();
2438 assert_eq!(
2439 error,
2440 RzupError::InvalidSignature("signature error: verification error".into())
2441 );
2442 },
2443 vec![RzupEvent::InstallationStarted {
2444 id: "risc0-groth16".into(),
2445 version: "2.0.0".into(),
2446 }],
2447 );
2448
2449 assert_eq!(
2450 rzup.list_versions(&Component::Risc0Groth16).unwrap(),
2451 vec![]
2452 );
2453 }
2454
2455 fn modify_published_distribution_manifest_json(
2456 s3_base_url: &str,
2457 modify: impl FnOnce(&mut serde_json::Value),
2458 ) {
2459 let manifest_url =
2460 format!("{s3_base_url}/rzup/components/risc0-groth16/distribution_manifest.json",);
2461 let client = reqwest::blocking::Client::new();
2462 let mut manifest: serde_json::Value =
2463 client.get(&manifest_url).send().unwrap().json().unwrap();
2464 modify(&mut manifest);
2465 client
2466 .put(manifest_url)
2467 .header("x-amz-date", "foo")
2468 .header("authorization", "foo")
2469 .header("x-amz-content-sha256", "foo")
2470 .header("content-type", "application/octet-stream")
2471 .body(serde_json::to_vec(&manifest).unwrap())
2472 .send()
2473 .unwrap();
2474 }
2475
2476 #[test]
2477 fn install_missing_signature() {
2478 let server = MockDistributionServer::new();
2479 let (_tmp_dir, mut rzup) = setup_test_env(
2480 server.base_urls.clone(),
2481 None,
2482 None,
2483 server.private_key.clone(),
2484 Platform::new("x86_64", Os::Linux),
2485 );
2486
2487 modify_published_distribution_manifest_json(&server.base_urls.s3_base_url, |manifest| {
2489 manifest.as_object_mut().unwrap().remove("signature");
2490 });
2491
2492 run_and_assert_events(
2493 &mut rzup,
2494 |rzup| {
2495 let error = rzup
2496 .install_component(
2497 &Component::Risc0Groth16,
2498 Some("2.0.0".parse().unwrap()),
2499 false,
2500 )
2501 .unwrap_err();
2502 assert_eq!(
2503 error,
2504 RzupError::Other("distribution_manifest.json missing signature".into())
2505 );
2506 },
2507 vec![RzupEvent::InstallationStarted {
2508 id: "risc0-groth16".into(),
2509 version: "2.0.0".into(),
2510 }],
2511 );
2512
2513 assert_eq!(
2514 rzup.list_versions(&Component::Risc0Groth16).unwrap(),
2515 vec![]
2516 );
2517 }
2518
2519 fn uninstall_test(component: Component, version: Version) {
2520 let server = MockDistributionServer::new();
2521 let (tmp_dir, mut rzup) = setup_test_env(
2522 server.base_urls.clone(),
2523 None,
2524 None,
2525 server.private_key.clone(),
2526 Platform::new("x86_64", Os::Linux),
2527 );
2528
2529 rzup.install_component(&component, Some(version.clone()), false)
2530 .unwrap();
2531
2532 run_and_assert_events(
2533 &mut rzup,
2534 |rzup| {
2535 rzup.uninstall_component(&component, version.clone())
2536 .unwrap();
2537 },
2538 vec![RzupEvent::Uninstalled {
2539 id: component.to_string(),
2540 version: version.to_string(),
2541 }],
2542 );
2543
2544 assert_files(tmp_dir.path(), vec![]);
2545 }
2546
2547 #[test]
2548 fn uninstall_cargo_risczero() {
2549 uninstall_test(Component::CargoRiscZero, Version::new(1, 0, 0));
2550 }
2551
2552 #[test]
2553 fn uninstall_r0vm() {
2554 uninstall_test(Component::R0Vm, Version::new(1, 0, 0));
2555 }
2556
2557 #[test]
2558 fn uninstall_rust() {
2559 uninstall_test(Component::RustToolchain, Version::new(1, 81, 0));
2560 }
2561
2562 #[test]
2563 fn uninstall_cpp() {
2564 uninstall_test(Component::CppToolchain, Version::new(2024, 1, 5));
2565 }
2566
2567 #[test]
2568 fn uninstall_gdb() {
2569 uninstall_test(Component::Gdb, Version::new(2024, 1, 5));
2570 }
2571
2572 #[test]
2573 fn uninstall_risc0_groth16() {
2574 uninstall_test(Component::Risc0Groth16, Version::new(1, 0, 0));
2575 }
2576
2577 #[test]
2578 fn get_latest_version_cargo_risczero() {
2579 let server = MockDistributionServer::new();
2580 let (_tmp_dir, rzup) = setup_test_env(
2581 server.base_urls.clone(),
2582 None,
2583 None,
2584 server.private_key.clone(),
2585 Platform::new("x86_64", Os::Linux),
2586 );
2587
2588 assert_eq!(
2589 rzup.get_latest_version(&Component::CargoRiscZero).unwrap(),
2590 Version::new(1, 1, 0)
2591 );
2592 }
2593
2594 #[test]
2595 fn get_latest_version_risc0_groth16() {
2596 let server = MockDistributionServer::new();
2597 let (_tmp_dir, rzup) = setup_test_env(
2598 server.base_urls.clone(),
2599 None,
2600 None,
2601 server.private_key.clone(),
2602 Platform::new("x86_64", Os::Linux),
2603 );
2604
2605 assert_eq!(
2606 rzup.get_latest_version(&Component::Risc0Groth16).unwrap(),
2607 Version::new(2, 0, 0)
2608 );
2609 }
2610
2611 struct LegacyVersionsFixture {
2612 rzup: Rzup,
2613 tmp_dir: TempDir,
2614 legacy_rust_dir: PathBuf,
2615 legacy_cargo_risczero_dir: PathBuf,
2616 legacy_cpp_dir: PathBuf,
2617 }
2618
2619 impl LegacyVersionsFixture {
2620 fn new(rust_dir_name: &str, cargo_risczero_dir_name: &str, cpp_dir_name: &str) -> Self {
2621 let (tmp_dir, rzup) = setup_test_env(
2622 invalid_base_urls(),
2623 None,
2624 None,
2625 test_private_key(),
2626 Platform::new("x86_64", Os::Linux),
2627 );
2628
2629 let legacy_rust_dir = tmp_dir.path().join(".risc0/toolchains").join(rust_dir_name);
2630 std::fs::create_dir_all(&legacy_rust_dir).unwrap();
2631
2632 let legacy_cargo_risczero_dir = tmp_dir
2633 .path()
2634 .join(".risc0/extensions")
2635 .join(cargo_risczero_dir_name);
2636 std::fs::create_dir_all(&legacy_cargo_risczero_dir).unwrap();
2637
2638 let legacy_cpp_dir = tmp_dir.path().join(".risc0/toolchains").join(cpp_dir_name);
2639 std::fs::create_dir_all(&legacy_cpp_dir).unwrap();
2640
2641 Self {
2642 rzup,
2643 tmp_dir,
2644 legacy_rust_dir,
2645 legacy_cargo_risczero_dir,
2646 legacy_cpp_dir,
2647 }
2648 }
2649 }
2650
2651 fn get_legacy_versions(rust_dir_name: &str, cargo_risczero_dir_name: &str, cpp_dir_name: &str) {
2652 let fix = LegacyVersionsFixture::new(rust_dir_name, cargo_risczero_dir_name, cpp_dir_name);
2653
2654 assert_eq!(
2655 fix.rzup
2656 .get_version_dir(&Component::RustToolchain, &Version::new(1, 81, 0))
2657 .unwrap(),
2658 fix.legacy_rust_dir
2659 );
2660 assert_eq!(
2661 fix.rzup
2662 .get_version_dir(
2663 &Component::CargoRiscZero,
2664 &Version::parse("1.2.1-rc.0").unwrap()
2665 )
2666 .unwrap(),
2667 fix.legacy_cargo_risczero_dir
2668 );
2669 assert_eq!(
2670 fix.rzup
2671 .get_version_dir(&Component::CppToolchain, &Version::new(2024, 1, 5))
2672 .unwrap(),
2673 fix.legacy_cpp_dir
2674 );
2675 }
2676
2677 #[test]
2678 fn get_legacy_versions_old_rzup_apple_aarch64() {
2679 get_legacy_versions(
2680 "r0.1.81.0-risc0-rust-aarch64-apple-darwin",
2681 "v1.2.1-rc.0-cargo-risczero",
2682 "2024.01.05-risc0-cpp-aarch64-apple-darwin",
2683 );
2684 }
2685
2686 #[test]
2687 fn get_legacy_versions_old_rzup_linux_x86() {
2688 get_legacy_versions(
2689 "r0.1.81.0-risc0-rust-x86_64-unknown-linux-gnu",
2690 "v1.2.1-rc.0-cargo-risczero",
2691 "2024.01.05-risc0-cpp-x86_64-unknown-linux-gnu",
2692 );
2693 }
2694
2695 #[test]
2696 fn get_legacy_versions_cargo_risczero_install_apple_aarch64() {
2697 get_legacy_versions(
2698 "rust_aarch64-apple-darwin_r0.1.81.0",
2699 "v1.2.1-rc.0-cargo-risczero",
2700 "c_aarch64-apple-darwin_2024.01.05",
2701 );
2702 }
2703
2704 #[test]
2705 fn get_legacy_versions_cargo_risczero_install_linux_x86() {
2706 get_legacy_versions(
2707 "rust_x86_64-unknown-linux-gnu_r0.1.81.0",
2708 "v1.2.1-rc.0-cargo-risczero",
2709 "c_x86_64-unknown-linux-gnu_2024.01.05",
2710 );
2711 }
2712
2713 fn get_default_legacy_versions(
2714 rust_dir_name: &str,
2715 cargo_risczero_dir_name: &str,
2716 cpp_dir_name: &str,
2717 ) {
2718 let fix = LegacyVersionsFixture::new(rust_dir_name, cargo_risczero_dir_name, cpp_dir_name);
2719
2720 assert_eq!(
2721 fix.rzup
2722 .get_default_version(&Component::RustToolchain)
2723 .unwrap()
2724 .unwrap()
2725 .0,
2726 Version::new(1, 81, 0)
2727 );
2728
2729 assert_eq!(
2730 fix.rzup
2731 .get_default_version(&Component::CargoRiscZero)
2732 .unwrap()
2733 .unwrap()
2734 .0,
2735 Version::parse("1.2.1-rc.0").unwrap()
2736 );
2737
2738 assert_eq!(
2739 fix.rzup
2740 .get_default_version(&Component::CppToolchain)
2741 .unwrap()
2742 .unwrap()
2743 .0,
2744 Version::new(2024, 1, 5)
2745 );
2746 }
2747
2748 #[test]
2749 fn get_default_legacy_versions_old_rzup_apple_aarch64() {
2750 get_default_legacy_versions(
2751 "r0.1.81.0-risc0-rust-aarch64-apple-darwin",
2752 "v1.2.1-rc.0-cargo-risczero",
2753 "2024.01.05-risc0-cpp-aarch64-apple-darwin",
2754 );
2755 }
2756
2757 #[test]
2758 fn get_default_legacy_versions_old_rzup_linux_x86() {
2759 get_default_legacy_versions(
2760 "r0.1.81.0-risc0-rust-x86_64-unknown-linux-gnu",
2761 "v1.2.1-rc.0-cargo-risczero",
2762 "2024.01.05-risc0-cpp-x86_64-unknown-linux-gnu",
2763 );
2764 }
2765
2766 #[test]
2767 fn get_default_legacy_versions_cargo_risczero_install_apple_aarch64() {
2768 get_default_legacy_versions(
2769 "rust_aarch64-apple-darwin_r0.1.81.0",
2770 "v1.2.1-rc.0-cargo-risczero",
2771 "c_aarch64-apple-darwin_2024.01.05",
2772 );
2773 }
2774
2775 #[test]
2776 fn get_default_legacy_versions_cargo_risczero_install_linux_x86() {
2777 get_default_legacy_versions(
2778 "rust_x86_64-unknown-linux-gnu_r0.1.81.0",
2779 "v1.2.1-rc.0-cargo-risczero",
2780 "c_x86_64-unknown-linux-gnu_2024.01.05",
2781 );
2782 }
2783
2784 fn list_legacy_versions(dir1: &str, dir2: &str, expected_versions: Vec<Version>) {
2785 let (tmp_dir, rzup) = setup_test_env(
2786 invalid_base_urls(),
2787 None,
2788 None,
2789 test_private_key(),
2790 Platform::new("x86_64", Os::Linux),
2791 );
2792
2793 let legacy_rust_dir1 = tmp_dir.path().join(".risc0/toolchains").join(dir1);
2794 std::fs::create_dir_all(&legacy_rust_dir1).unwrap();
2795
2796 let legacy_rust_dir2 = tmp_dir.path().join(".risc0/toolchains").join(dir2);
2797 std::fs::create_dir_all(&legacy_rust_dir2).unwrap();
2798
2799 assert_eq!(
2800 rzup.list_versions(&Component::RustToolchain).unwrap(),
2801 expected_versions
2802 );
2803 }
2804
2805 #[test]
2806 fn list_legacy_versions_old_rzup_apple_aarch64() {
2807 list_legacy_versions(
2808 "r0.1.79.0-risc0-rust-aarch64-apple-darwin",
2809 "r0.1.81.0-risc0-rust-aarch64-apple-darwin",
2810 vec![Version::new(1, 81, 0), Version::new(1, 79, 0)],
2811 );
2812 }
2813
2814 #[test]
2815 fn list_legacy_versions_old_rzup_linux_x86() {
2816 list_legacy_versions(
2817 "r0.1.79.0-risc0-rust-x86_64-unknown-linux-gnu",
2818 "r0.1.81.0-risc0-rust-x86_64-unknown-linux-gnu",
2819 vec![Version::new(1, 81, 0), Version::new(1, 79, 0)],
2820 );
2821 }
2822
2823 #[test]
2824 fn list_legacy_versions_cargo_risczero_install_aaple_aarch64() {
2825 list_legacy_versions(
2826 "rust_aarch64-apple-darwin_r0.1.79.0",
2827 "rust_aarch64-apple-darwin_r0.1.81.0",
2828 vec![Version::new(1, 81, 0), Version::new(1, 79, 0)],
2829 );
2830 }
2831
2832 #[test]
2833 fn list_legacy_versions_cargo_risczero_install_linux_x86() {
2834 list_legacy_versions(
2835 "rust_x86_64-unknown-linux-gnu_r0.1.79.0",
2836 "rust_x86_64-unknown-linux-gnu_r0.1.81.0",
2837 vec![Version::new(1, 81, 0), Version::new(1, 79, 0)],
2838 );
2839 }
2840
2841 fn set_default_version_legacy_versions(
2842 rust_dir_name: &str,
2843 cargo_risczero_dir_name: &str,
2844 cpp_dir_name: &str,
2845 ) {
2846 let mut fix =
2847 LegacyVersionsFixture::new(rust_dir_name, cargo_risczero_dir_name, cpp_dir_name);
2848 fix.rzup
2849 .set_default_version(&Component::RustToolchain, Version::new(1, 81, 0))
2850 .unwrap();
2851
2852 fix.rzup
2853 .set_default_version(
2854 &Component::CargoRiscZero,
2855 Version::parse("1.2.1-rc.0").unwrap(),
2856 )
2857 .unwrap();
2858
2859 fix.rzup
2860 .set_default_version(&Component::CppToolchain, Version::new(2024, 1, 5))
2861 .unwrap();
2862
2863 assert_symlinks(
2864 fix.tmp_dir.path(),
2865 vec![
2866 (
2867 ".cargo/bin/cargo-risczero".into(),
2868 fix.legacy_cargo_risczero_dir
2869 .strip_prefix(fix.tmp_dir.path())
2870 .unwrap()
2871 .join("cargo-risczero")
2872 .to_str()
2873 .unwrap()
2874 .into(),
2875 ),
2876 (
2877 ".rustup/toolchains/risc0".into(),
2878 fix.legacy_rust_dir
2879 .strip_prefix(fix.tmp_dir.path())
2880 .unwrap()
2881 .to_str()
2882 .unwrap()
2883 .into(),
2884 ),
2885 (
2886 ".risc0/cpp".into(),
2887 fix.legacy_cpp_dir
2888 .strip_prefix(fix.tmp_dir.path())
2889 .unwrap()
2890 .to_str()
2891 .unwrap()
2892 .into(),
2893 ),
2894 ],
2895 );
2896 }
2897
2898 #[test]
2899 fn set_default_version_legacy_versions_old_rzup_apple_aarch64() {
2900 set_default_version_legacy_versions(
2901 "r0.1.81.0-risc0-rust-aarch64-apple-darwin",
2902 "v1.2.1-rc.0-cargo-risczero",
2903 "2024.01.05-risc0-cpp-aarch64-apple-darwin",
2904 );
2905 }
2906
2907 #[test]
2908 fn set_default_version_legacy_versions_old_rzup_linux_x86() {
2909 set_default_version_legacy_versions(
2910 "r0.1.81.0-risc0-rust-x86_64-unknown-linux-gnu",
2911 "v1.2.1-rc.0-cargo-risczero",
2912 "2024.01.05-risc0-cpp-x86_64-unknown-linux-gnu",
2913 );
2914 }
2915
2916 #[test]
2917 fn set_default_version_legacy_versions_cargo_risczero_install_apple_aarch64() {
2918 set_default_version_legacy_versions(
2919 "rust_aarch64-apple-darwin_r0.1.81.0",
2920 "v1.2.1-rc.0-cargo-risczero",
2921 "c_aarch64-apple-darwin_2024.01.05",
2922 );
2923 }
2924
2925 #[test]
2926 fn set_default_version_legacy_versions_cargo_risczero_install_linux_x86() {
2927 set_default_version_legacy_versions(
2928 "rust_x86_64-unknown-linux-gnu_r0.1.81.0",
2929 "v1.2.1-rc.0-cargo-risczero",
2930 "c_x86_64-unknown-linux-gnu_2024.01.05",
2931 );
2932 }
2933
2934 #[test]
2935 fn set_default_version_legacy_version_has_dir_instead_of_symlink() {
2936 let (tmp_dir, mut rzup) = setup_test_env(
2937 invalid_base_urls(),
2938 None,
2939 None,
2940 test_private_key(),
2941 Platform::new("x86_64", Os::Linux),
2942 );
2943
2944 let legacy_cpp_dir =
2945 PathBuf::from(".risc0/toolchains/2024.01.05-risc0-cpp-x86_64-unknown-linux-gnu");
2946 std::fs::create_dir_all(tmp_dir.path().join(&legacy_cpp_dir)).unwrap();
2947
2948 std::fs::create_dir(tmp_dir.path().join(".risc0/cpp")).unwrap();
2951
2952 rzup.set_default_version(&Component::CppToolchain, Version::new(2024, 1, 5))
2953 .unwrap();
2954
2955 assert_symlinks(
2956 tmp_dir.path(),
2957 vec![(".risc0/cpp".into(), legacy_cpp_dir.to_str().unwrap().into())],
2958 );
2959 }
2960
2961 fn uninstall_legacy_versions(
2962 rust_dir_name: &str,
2963 cargo_risczero_dir_name: &str,
2964 cpp_dir_name: &str,
2965 ) {
2966 let mut fix =
2967 LegacyVersionsFixture::new(rust_dir_name, cargo_risczero_dir_name, cpp_dir_name);
2968
2969 fix.rzup
2970 .set_default_version(&Component::RustToolchain, Version::new(1, 81, 0))
2971 .unwrap();
2972
2973 fix.rzup
2974 .uninstall_component(&Component::RustToolchain, Version::new(1, 81, 0))
2975 .unwrap();
2976
2977 fix.rzup
2978 .set_default_version(
2979 &Component::CargoRiscZero,
2980 Version::parse("1.2.1-rc.0").unwrap(),
2981 )
2982 .unwrap();
2983
2984 fix.rzup
2985 .uninstall_component(
2986 &Component::CargoRiscZero,
2987 Version::parse("1.2.1-rc.0").unwrap(),
2988 )
2989 .unwrap();
2990
2991 fix.rzup
2992 .set_default_version(&Component::CppToolchain, Version::new(2024, 1, 5))
2993 .unwrap();
2994
2995 fix.rzup
2996 .uninstall_component(&Component::CppToolchain, Version::new(2024, 1, 5))
2997 .unwrap();
2998
2999 assert_files(fix.tmp_dir.path(), vec![]);
3000 }
3001
3002 #[test]
3003 fn uninstall_legacy_versions_old_rzup_apple_aarch64() {
3004 uninstall_legacy_versions(
3005 "r0.1.81.0-risc0-rust-aarch64-apple-darwin",
3006 "v1.2.1-rc.0-cargo-risczero",
3007 "2024.01.05-risc0-cpp-aarch64-apple-darwin",
3008 )
3009 }
3010
3011 #[test]
3012 fn uninstall_legacy_versions_old_rzup_linux_x86() {
3013 uninstall_legacy_versions(
3014 "r0.1.81.0-risc0-rust-x86_64-unknown-linux-gnu",
3015 "v1.2.1-rc.0-cargo-risczero",
3016 "2024.01.05-risc0-cpp-x86_64-unknown-linux-gnu",
3017 )
3018 }
3019
3020 #[test]
3021 fn uninstall_legacy_versions_cargo_risczero_install_apple_aarch64() {
3022 uninstall_legacy_versions(
3023 "rust_aarch64-apple-darwin_r0.1.81.0",
3024 "v1.2.1-rc.0-cargo-risczero",
3025 "c_aarch64-apple-darwin_2024.01.05",
3026 )
3027 }
3028
3029 #[test]
3030 fn uninstall_legacy_versions_cargo_risczero_install_linux_x86() {
3031 uninstall_legacy_versions(
3032 "rust_x86_64-unknown-linux-gnu_r0.1.81.0",
3033 "v1.2.1-rc.0-cargo-risczero",
3034 "c_x86_64-unknown-linux-gnu_2024.01.05",
3035 )
3036 }
3037
3038 #[test]
3039 fn self_update() {
3040 let temp_dir = TempDir::new().unwrap();
3041 let server = MockDistributionServer::new_with_install_script(format!(
3042 "#!/bin/bash
3043 set -eo pipefail
3044 touch {}/self_update_ran
3045 ",
3046 temp_dir.path().display()
3047 ));
3048 let (_, mut rzup) = setup_test_env(
3049 server.base_urls.clone(),
3050 None,
3051 None,
3052 server.private_key.clone(),
3053 Platform::new("x86_64", Os::Linux),
3054 );
3055
3056 run_and_assert_events(
3057 &mut rzup,
3058 |rzup| {
3059 rzup.self_update().unwrap();
3060 },
3061 vec![
3062 RzupEvent::InstallationStarted {
3063 id: "rzup".into(),
3064 version: "latest".into(),
3065 },
3066 RzupEvent::InstallationCompleted {
3067 id: "rzup".into(),
3068 version: "latest".into(),
3069 },
3070 ],
3071 );
3072 assert!(temp_dir.path().join("self_update_ran").exists());
3073 }
3074
3075 #[test]
3076 fn self_update_failure() {
3077 let temp_dir = TempDir::new().unwrap();
3078 let server = MockDistributionServer::new_with_install_script(
3079 "#!/bin/bash
3080 set -eo pipefail
3081 echo test_failure 1>&2
3082 exit 1
3083 "
3084 .into(),
3085 );
3086 let (_, mut rzup) = setup_test_env(
3087 server.base_urls.clone(),
3088 None,
3089 None,
3090 server.private_key.clone(),
3091 Platform::new("x86_64", Os::Linux),
3092 );
3093
3094 run_and_assert_events(
3095 &mut rzup,
3096 |rzup| {
3097 let error = rzup.self_update().unwrap_err();
3098 assert_eq!(
3099 error,
3100 RzupError::Other("Self-update failed: test_failure\n".into())
3101 );
3102 },
3103 vec![
3104 RzupEvent::InstallationStarted {
3105 id: "rzup".into(),
3106 version: "latest".into(),
3107 },
3108 RzupEvent::InstallationFailed {
3109 id: "rzup".into(),
3110 version: "latest".into(),
3111 },
3112 ],
3113 );
3114 assert!(!temp_dir.path().join("self_update_ran").exists());
3115 }
3116
3117 fn write_script(path: &Path, contents: &str) {
3118 use std::os::unix::fs::PermissionsExt as _;
3119
3120 std::fs::write(path, contents).unwrap();
3121 let mut perms = std::fs::metadata(path).unwrap().permissions();
3122 perms.set_mode(0o775);
3123 std::fs::set_permissions(path, perms).unwrap();
3124 }
3125
3126 fn create_fake_rust_repo(tmp_dir: &TempDir, rust_version: Version) -> (PathBuf, Version) {
3127 let test_repo = tmp_dir.path().join("test-repo");
3128 std::fs::create_dir(&test_repo).unwrap();
3129 build::run_command(
3130 "git",
3131 &["-c", "init.defaultBranch=master", "init"],
3132 Some(&test_repo),
3133 &[
3134 ("GIT_CONFIG_SYSTEM", "/dev/null"),
3135 ("GIT_CONFIG_GLOBAL", "/dev/null"),
3136 ],
3137 )
3138 .unwrap();
3139
3140 std::fs::create_dir(test_repo.join("src")).unwrap();
3141 std::fs::write(test_repo.join("src/version"), rust_version.to_string()).unwrap();
3142 write_script(
3143 &test_repo.join("x"),
3144 "\
3145 #!/bin/bash
3146 mkdir -p build/foo/stage2/bin
3147 mkdir -p build/foo/stage2-tools-bin
3148 mkdir -p build/foo/stage3/lib/rustlib/riscv32im-risc0-zkvm-elf
3149 touch build/foo/stage2/bin/rustc
3150 touch build/foo/stage2-tools-bin/cargo-fmt
3151 echo 'build output line 1'
3152 echo 'build output line 2'
3153 ",
3154 );
3155
3156 build::run_command("git", &["add", "."], Some(&test_repo), &[]).unwrap();
3157 build::run_command(
3158 "git",
3159 &[
3160 "-c",
3161 "user.name=Testy",
3162 "-c",
3163 "user.email=testy@example.com",
3164 "commit",
3165 "--message",
3166 "initial commit",
3167 ],
3168 Some(&test_repo),
3169 &[
3170 ("GIT_CONFIG_SYSTEM", "/dev/null"),
3171 ("GIT_CONFIG_GLOBAL", "/dev/null"),
3172 ],
3173 )
3174 .unwrap();
3175 build::run_command("git", &["tag", "foo"], Some(&test_repo), &[]).unwrap();
3176
3177 write_script(
3178 &test_repo.join("x"),
3179 "\
3180 #!/bin/bash
3181 mkdir -p build/foo/stage2/bin
3182 mkdir -p build/foo/stage2-tools-bin
3183 mkdir -p build/foo/stage3/lib/rustlib/riscv32im-risc0-zkvm-elf
3184 touch build/foo/stage2/bin/rustc
3185 touch build/foo/stage2-tools-bin/cargo-fmt
3186 touch build/foo/stage2-tools-bin/bar-fmt
3187 echo 'build output line 1'
3188 echo 'build output line 2'
3189 env >> x_env
3190 echo '=====' >> x_env
3191 ",
3192 );
3193
3194 build::run_command("git", &["add", "."], Some(&test_repo), &[]).unwrap();
3195 build::run_command(
3196 "git",
3197 &[
3198 "-c",
3199 "user.name=Testy",
3200 "-c",
3201 "user.email=testy@example.com",
3202 "commit",
3203 "--message",
3204 "bar",
3205 ],
3206 Some(&test_repo),
3207 &[
3208 ("GIT_CONFIG_SYSTEM", "/dev/null"),
3209 ("GIT_CONFIG_GLOBAL", "/dev/null"),
3210 ],
3211 )
3212 .unwrap();
3213
3214 let commit = build::git_short_rev_parse(&test_repo, "HEAD").unwrap();
3215 let mut version = Version::new(1, 34, 0);
3216 version.build = semver::BuildMetadata::new(&commit).unwrap();
3217
3218 (test_repo, version)
3219 }
3220
3221 #[test]
3222 fn build_rust_toolchain() {
3223 let server = MockDistributionServer::new();
3224 let (tmp_dir, mut rzup) = setup_test_env(
3225 server.base_urls.clone(),
3226 None,
3227 None,
3228 server.private_key.clone(),
3229 Platform::new("x86_64", Os::Linux),
3230 );
3231
3232 let (test_repo, version) = create_fake_rust_repo(&tmp_dir, Version::new(1, 34, 0));
3233
3234 let repo_url = format!("file://{}", test_repo.display());
3235
3236 run_and_assert_events(
3237 &mut rzup,
3238 |rzup| {
3239 rzup.build_rust_toolchain(&repo_url, &Some("master".to_string()), &None)
3240 .unwrap();
3241 },
3242 vec![
3243 RzupEvent::BuildingRustToolchain,
3244 RzupEvent::BuildingRustToolchainUpdate {
3245 message: "cloning git repository".into(),
3246 },
3247 RzupEvent::BuildingRustToolchainUpdate {
3248 message: "./x build".into(),
3249 },
3250 RzupEvent::BuildingRustToolchainUpdate {
3251 message: "build output line 1".into(),
3252 },
3253 RzupEvent::BuildingRustToolchainUpdate {
3254 message: "build output line 2".into(),
3255 },
3256 RzupEvent::BuildingRustToolchainUpdate {
3257 message: "./x build --stage 2".into(),
3258 },
3259 RzupEvent::BuildingRustToolchainUpdate {
3260 message: "build output line 1".into(),
3261 },
3262 RzupEvent::BuildingRustToolchainUpdate {
3263 message: "build output line 2".into(),
3264 },
3265 RzupEvent::BuildingRustToolchainUpdate {
3266 message: "./x build --stage 3".into(),
3267 },
3268 RzupEvent::BuildingRustToolchainUpdate {
3269 message: "build output line 1".into(),
3270 },
3271 RzupEvent::BuildingRustToolchainUpdate {
3272 message: "build output line 2".into(),
3273 },
3274 RzupEvent::BuildingRustToolchainUpdate {
3275 message: "installing".into(),
3276 },
3277 RzupEvent::DoneBuildingRustToolchain {
3278 version: version.to_string(),
3279 },
3280 ],
3281 );
3282
3283 assert_eq!(
3284 rzup.get_default_version(&Component::RustToolchain)
3285 .unwrap()
3286 .unwrap()
3287 .0,
3288 version
3289 );
3290
3291 std::fs::remove_dir_all(tmp_dir.path().join(".risc0/tmp")).unwrap();
3292 assert_files(
3293 tmp_dir.path(),
3294 vec![
3295 format!(".risc0/toolchains/v{version}-rust-x86_64-unknown-linux-gnu/bin/bar-fmt"),
3296 format!(".risc0/toolchains/v{version}-rust-x86_64-unknown-linux-gnu/bin/cargo-fmt"),
3297 format!(".risc0/toolchains/v{version}-rust-x86_64-unknown-linux-gnu/bin/rustc"),
3298 ],
3299 );
3300 }
3301
3302 #[test]
3303 fn build_rust_toolchain_twice_different_versions() {
3304 let server = MockDistributionServer::new();
3305 let (tmp_dir, mut rzup) = setup_test_env(
3306 server.base_urls.clone(),
3307 None,
3308 None,
3309 server.private_key.clone(),
3310 Platform::new("x86_64", Os::Linux),
3311 );
3312
3313 let (test_repo, master) = create_fake_rust_repo(&tmp_dir, Version::new(1, 34, 0));
3314
3315 let repo_url = format!("file://{}", test_repo.display());
3316 rzup.build_rust_toolchain(&repo_url, &Some("foo".to_string()), &None)
3317 .unwrap();
3318
3319 let foo_commit = build::git_short_rev_parse(&test_repo, "foo").unwrap();
3320 let mut foo = master.clone();
3321 foo.build = semver::BuildMetadata::new(&foo_commit).unwrap();
3322
3323 rzup.build_rust_toolchain(&repo_url, &Some("master".to_string()), &None)
3324 .unwrap();
3325
3326 std::fs::remove_dir_all(tmp_dir.path().join(".risc0/tmp")).unwrap();
3327 assert_files(
3328 tmp_dir.path(),
3329 vec![
3330 format!(".risc0/toolchains/v{master}-rust-x86_64-unknown-linux-gnu/bin/bar-fmt"),
3331 format!(".risc0/toolchains/v{master}-rust-x86_64-unknown-linux-gnu/bin/cargo-fmt"),
3332 format!(".risc0/toolchains/v{master}-rust-x86_64-unknown-linux-gnu/bin/rustc"),
3333 format!(".risc0/toolchains/v{foo}-rust-x86_64-unknown-linux-gnu/bin/cargo-fmt"),
3334 format!(".risc0/toolchains/v{foo}-rust-x86_64-unknown-linux-gnu/bin/rustc"),
3335 ],
3336 );
3337 }
3338
3339 fn parse_env_output(input: &str) -> Vec<HashMap<&str, &str>> {
3340 let Some(last_divider) = input.rfind("====") else {
3341 return vec![];
3342 };
3343
3344 input[..last_divider]
3345 .split("====")
3346 .map(|invocation| {
3347 invocation
3348 .split("\n")
3349 .filter_map(|line| line.split_once("="))
3350 .collect()
3351 })
3352 .collect()
3353 }
3354
3355 #[test]
3356 fn build_rust_toolchain_loweratomic_flag() {
3357 let expectations = [
3358 (Version::new(1, 80, 0), "-Cpasses=loweratomic"),
3359 (Version::new(1, 81, 0), "-Cpasses=loweratomic"),
3360 (Version::new(1, 81, 1), "-Cpasses=loweratomic"),
3361 (Version::new(1, 82, 0), "-Cpasses=lower-atomic"),
3362 (Version::new(1, 82, 1), "-Cpasses=lower-atomic"),
3363 (Version::new(1, 83, 0), "-Cpasses=lower-atomic"),
3364 ];
3365
3366 for (version, expected_lower_atomic) in expectations {
3367 let server = MockDistributionServer::new();
3368 let (tmp_dir, mut rzup) = setup_test_env(
3369 server.base_urls.clone(),
3370 None,
3371 None,
3372 server.private_key.clone(),
3373 Platform::new("x86_64", Os::Linux),
3374 );
3375
3376 let (test_repo, _) = create_fake_rust_repo(&tmp_dir, version.clone());
3377 let repo_url = format!("file://{}", test_repo.display());
3378
3379 rzup.build_rust_toolchain(&repo_url, &Some("master".to_string()), &None)
3380 .unwrap();
3381
3382 let env_raw = std::fs::read_to_string(
3384 tmp_dir.path().join(".risc0/tmp/build-rust-toolchain/x_env"),
3385 )
3386 .unwrap();
3387 let env = parse_env_output(&env_raw);
3388
3389 assert_eq!(env.len(), 3);
3391
3392 for e in env {
3393 assert_eq!(
3394 e["CARGO_TARGET_RISCV32IM_RISC0_ZKVM_ELF_RUSTFLAGS"], expected_lower_atomic,
3395 "lower atomic unexpected for {version}"
3396 );
3397 }
3398 }
3399 }
3400
3401 fn create_test_tar_for_upload(tmp_dir: &TempDir, id: u64) -> (u64, PathBuf, String) {
3402 let mut tar_bytes = vec![];
3403 let mut tar_builder = tar::Builder::new(&mut tar_bytes);
3404 let mut header = tar::Header::new_gnu();
3405 header.set_size(4);
3406 tar_builder
3407 .append_data(
3408 &mut header,
3409 format!("tar_contents{id}.bin"),
3410 &[1, 2, 3, 4][..],
3411 )
3412 .unwrap();
3413 tar_builder.finish().unwrap();
3414 drop(tar_builder);
3415
3416 let mut tar_xz_bytes = vec![];
3417 let mut encoder = liblzma::write::XzEncoder::new(&mut tar_xz_bytes, 1);
3418 encoder.write_all(&tar_bytes).unwrap();
3419 drop(encoder);
3420
3421 let mut hasher = sha2::Sha256::new();
3422 hasher.update(&tar_xz_bytes);
3423 let sha256 = format!("{:x}", hasher.finalize());
3424
3425 let download_size = tar_xz_bytes.len() as u64;
3426 let payload = tmp_dir.path().join("upload.tar.xz");
3427 std::fs::write(&payload, tar_xz_bytes).unwrap();
3428
3429 (download_size, payload, sha256)
3430 }
3431
3432 fn upload_test(
3433 base_url: &str,
3434 download_size: u64,
3435 platform: Option<Platform>,
3436 payload: &Path,
3437 sha256: &str,
3438 force: bool,
3439 rzup: &mut Rzup,
3440 ) {
3441 run_and_assert_events(
3442 rzup,
3443 |rzup| {
3444 rzup.publish_upload(
3445 &Component::Risc0Groth16,
3446 &Version::new(4, 0, 0),
3447 platform,
3448 payload,
3449 force,
3450 )
3451 .unwrap();
3452 },
3453 vec![
3454 RzupEvent::Print {
3455 message: "Getting private key from AWS".into(),
3456 },
3457 RzupEvent::Print {
3458 message: "Reading distribution_manifest.json".into(),
3459 },
3460 RzupEvent::Print {
3461 message: "Validating artifact".into(),
3462 },
3463 RzupEvent::Print {
3464 message: "Calculating sha256 for risc0-groth16 4.0.0".into(),
3465 },
3466 RzupEvent::TransferStarted {
3467 kind: TransferKind::Upload,
3468 id: format!("risc0-groth16/{sha256}"),
3469 version: Some("4.0.0".into()),
3470 url: Some(format!(
3471 "{base_url}/rzup/components/risc0-groth16/sha256/{sha256}"
3472 )),
3473 len: Some(download_size),
3474 },
3475 RzupEvent::TransferProgress {
3476 id: format!("risc0-groth16/{sha256}"),
3477 incr: download_size,
3478 },
3479 RzupEvent::TransferCompleted {
3480 kind: TransferKind::Upload,
3481 id: format!("risc0-groth16/{sha256}"),
3482 version: Some("4.0.0".into()),
3483 },
3484 RzupEvent::Print {
3485 message: "Updating distribution_manifest.json for risc0-groth16 4.0.0".into(),
3486 },
3487 ],
3488 );
3489 }
3490
3491 fn publish_fixture() -> (MockDistributionServer, Platform, TempDir, Rzup) {
3492 let server = MockDistributionServer::new();
3493 let aws_creds = AwsCredentials::new(
3494 "AKIDEXAMPLE",
3495 "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY",
3496 None,
3497 None,
3498 "hardcoded-credentials",
3499 );
3500 let platform = Platform::new("x86_64", Os::Linux);
3501 let (tmp_dir, rzup) = setup_test_env(
3502 server.base_urls.clone(),
3503 None,
3504 Some(aws_creds),
3505 server.private_key.clone(),
3506 platform,
3507 );
3508 (server, platform, tmp_dir, rzup)
3509 }
3510
3511 fn publish_upload_test(target_specific: bool) {
3512 let (server, platform, tmp_dir, mut rzup) = publish_fixture();
3513
3514 let base_url = &server.base_urls.s3_base_url;
3515 let (download_size, payload, sha256) = create_test_tar_for_upload(&tmp_dir, 1);
3516 upload_test(
3517 base_url,
3518 download_size,
3519 target_specific.then_some(platform),
3520 &payload,
3521 &sha256,
3522 false, &mut rzup,
3524 );
3525
3526 install_test(
3527 server.base_urls.clone(),
3528 server.private_key.clone(),
3529 Component::Risc0Groth16,
3530 Component::Risc0Groth16,
3531 Version::new(4, 0, 0),
3532 format!(
3533 "{base_url}/rzup/components/risc0-groth16/sha256/{sha256}",
3534 base_url = server.base_urls.s3_base_url
3535 ),
3536 download_size,
3537 vec![format!(
3538 ".risc0/extensions/v4.0.0-risc0-groth16/tar_contents1.bin"
3539 )],
3540 vec![],
3541 ".risc0/extensions/v4.0.0-risc0-groth16",
3542 false, platform,
3544 );
3545
3546 assert_eq!(
3547 rzup.get_latest_version(&Component::Risc0Groth16).unwrap(),
3548 Version::new(2, 0, 0)
3549 );
3550 }
3551
3552 #[test]
3553 fn publish_upload_target_agnostic() {
3554 publish_upload_test(true )
3555 }
3556
3557 #[test]
3558 fn publish_upload_target_specific() {
3559 publish_upload_test(true )
3560 }
3561
3562 #[test]
3563 fn publish_upload_invalid_tar_xz() {
3564 let (_server, _platform, tmp_dir, mut rzup) = publish_fixture();
3565
3566 let data = b"abcdef";
3567 let payload = tmp_dir.path().join("upload_bin");
3568 std::fs::write(&payload, data).unwrap();
3569
3570 let err = rzup
3571 .publish_upload(
3572 &Component::Risc0Groth16,
3573 &Version::new(4, 0, 0),
3574 None, &payload,
3576 false, )
3578 .unwrap_err();
3579
3580 assert_eq!(
3581 err,
3582 RzupError::Other("invalid tar.xz file: premature eof".into())
3583 );
3584 }
3585
3586 #[test]
3587 fn publish_upload_empty_tar_xz() {
3588 let (_server, _platform, tmp_dir, mut rzup) = publish_fixture();
3589
3590 let mut tar_bytes = vec![];
3591 let mut tar_builder = tar::Builder::new(&mut tar_bytes);
3592 tar_builder.finish().unwrap();
3593 drop(tar_builder);
3594
3595 let mut tar_xz_bytes = vec![];
3596 let mut encoder = liblzma::write::XzEncoder::new(&mut tar_xz_bytes, 1);
3597 encoder.write_all(&tar_bytes).unwrap();
3598 drop(encoder);
3599
3600 let payload = tmp_dir.path().join("upload.tar.xz");
3601 std::fs::write(&payload, tar_xz_bytes).unwrap();
3602
3603 let err = rzup
3604 .publish_upload(
3605 &Component::Risc0Groth16,
3606 &Version::new(4, 0, 0),
3607 None, &payload,
3609 false, )
3611 .unwrap_err();
3612
3613 assert_eq!(err, RzupError::Other("invalid tar.xz file: empty".into()));
3614 }
3615
3616 fn publish_upload_duplicate_force_false_test(
3617 a_platform: Option<Platform>,
3618 b_platform: Option<Platform>,
3619 expected_msg: &str,
3620 ) {
3621 let (server, _platform, tmp_dir, mut rzup) = publish_fixture();
3622
3623 let base_url = &server.base_urls.s3_base_url;
3624 let (download_size, payload, sha256) = create_test_tar_for_upload(&tmp_dir, 1);
3625 upload_test(
3626 base_url,
3627 download_size,
3628 a_platform,
3629 &payload,
3630 &sha256,
3631 false, &mut rzup,
3633 );
3634
3635 let err = rzup
3636 .publish_upload(
3637 &Component::Risc0Groth16,
3638 &Version::new(4, 0, 0),
3639 b_platform,
3640 &payload,
3641 false, )
3643 .unwrap_err();
3644 assert_eq!(err, RzupError::Other(expected_msg.into()));
3645 }
3646
3647 #[test]
3648 fn publish_upload_duplicate_force_false() {
3649 publish_upload_duplicate_force_false_test(
3650 None,
3651 None,
3652 "artifact already exists for this release, add --force flag to overwrite",
3653 );
3654 publish_upload_duplicate_force_false_test(
3655 Some(Platform::new("x86_64", Os::Linux)),
3656 None,
3657 "target-specific artifact already exists for this release version, \
3658 add --force flag to overwrite",
3659 );
3660 publish_upload_duplicate_force_false_test(
3661 None,
3662 Some(Platform::new("x86_64", Os::Linux)),
3663 "target-agnostic artifact already exists for this release version, \
3664 add --force flag to overwrite",
3665 );
3666 publish_upload_duplicate_force_false_test(
3667 Some(Platform::new("x86_64", Os::Linux)),
3668 Some(Platform::new("x86_64", Os::Linux)),
3669 "artifact already exists for this release and target, add --force flag to overwrite",
3670 );
3671 }
3672
3673 fn publish_upload_duplicate_force_true_test(
3674 a_platform: Option<Platform>,
3675 b_platform: Option<Platform>,
3676 ) {
3677 let (server, platform, tmp_dir, mut rzup) = publish_fixture();
3678
3679 let base_url = &server.base_urls.s3_base_url;
3680 let (download_size, payload, sha256) = create_test_tar_for_upload(&tmp_dir, 1);
3681 upload_test(
3682 base_url,
3683 download_size,
3684 a_platform,
3685 &payload,
3686 &sha256,
3687 false, &mut rzup,
3689 );
3690
3691 let (download_size, payload, sha256) = create_test_tar_for_upload(&tmp_dir, 2);
3692 upload_test(
3693 base_url,
3694 download_size,
3695 b_platform,
3696 &payload,
3697 &sha256,
3698 true, &mut rzup,
3700 );
3701
3702 install_test(
3703 server.base_urls.clone(),
3704 server.private_key.clone(),
3705 Component::Risc0Groth16,
3706 Component::Risc0Groth16,
3707 Version::new(4, 0, 0),
3708 format!(
3709 "{base_url}/rzup/components/risc0-groth16/sha256/{sha256}",
3710 base_url = server.base_urls.s3_base_url
3711 ),
3712 download_size,
3713 vec![format!(
3714 ".risc0/extensions/v4.0.0-risc0-groth16/tar_contents2.bin"
3715 )],
3716 vec![],
3717 ".risc0/extensions/v4.0.0-risc0-groth16",
3718 false, platform,
3720 );
3721 }
3722
3723 #[test]
3724 fn publish_upload_duplicate_force_true() {
3725 publish_upload_duplicate_force_true_test(None, None);
3726 publish_upload_duplicate_force_true_test(Some(Platform::new("x86_64", Os::Linux)), None);
3727 publish_upload_duplicate_force_true_test(None, Some(Platform::new("x86_64", Os::Linux)));
3728 publish_upload_duplicate_force_true_test(
3729 Some(Platform::new("x86_64", Os::Linux)),
3730 Some(Platform::new("x86_64", Os::Linux)),
3731 );
3732 }
3733
3734 #[test]
3735 fn publish_set_latest() {
3736 let (_server, _platform, _tmp_dir, mut rzup) = publish_fixture();
3737
3738 assert_eq!(
3739 rzup.get_latest_version(&Component::Risc0Groth16).unwrap(),
3740 Version::new(2, 0, 0)
3741 );
3742
3743 run_and_assert_events(
3744 &mut rzup,
3745 |rzup| {
3746 rzup.publish_set_latest(&Component::Risc0Groth16, &Version::new(1, 0, 0))
3747 .unwrap();
3748 },
3749 vec![RzupEvent::Print {
3750 message: "Updating distribution_manifest.json for risc0-groth16, \
3751 setting latest-version to 1.0.0"
3752 .into(),
3753 }],
3754 );
3755
3756 assert_eq!(
3757 rzup.get_latest_version(&Component::Risc0Groth16).unwrap(),
3758 Version::new(1, 0, 0)
3759 );
3760 }
3761
3762 #[test]
3763 fn publish_set_latest_not_found() {
3764 let (_server, _platform, _tmp_dir, mut rzup) = publish_fixture();
3765
3766 assert_eq!(
3767 rzup.get_latest_version(&Component::Risc0Groth16).unwrap(),
3768 Version::new(2, 0, 0)
3769 );
3770
3771 let err = rzup
3772 .publish_set_latest(&Component::Risc0Groth16, &Version::new(7, 0, 0))
3773 .unwrap_err();
3774 assert_eq!(
3775 err,
3776 RzupError::Other("release for risc0-groth16 at version 7.0.0 not found".into())
3777 );
3778 }
3779
3780 #[test]
3781 fn publish_create_artifact_directory() {
3782 let (_server, _platform, tmp_dir, mut rzup) = publish_fixture();
3783
3784 let input_path = tmp_dir.path().join("input-path");
3786 std::fs::create_dir_all(&input_path).unwrap();
3787 for p in [Path::new("a.txt"), Path::new("b/c.txt")] {
3788 let p = input_path.join(p);
3789 if let Some(parent) = p.parent() {
3790 std::fs::create_dir_all(parent).unwrap();
3791 }
3792 std::fs::write(p, "hello world").unwrap();
3793 }
3794
3795 let output_path = tmp_dir.path().join("output.tar.xz");
3796 rzup.publish_create_artifact(&input_path, &output_path, 6 )
3797 .unwrap();
3798
3799 let file = std::fs::File::open(output_path).unwrap();
3800 let mut tar_reader = tar::Archive::new(liblzma::bufread::XzDecoder::new(
3801 std::io::BufReader::new(file),
3802 ));
3803 let mut paths: Vec<_> = tar_reader
3804 .entries()
3805 .unwrap()
3806 .map(|e| PathBuf::from(e.unwrap().path().unwrap()))
3807 .collect();
3808 paths.sort();
3809 assert_eq!(paths, vec![Path::new("a.txt"), Path::new("b/c.txt")]);
3810 }
3811
3812 #[test]
3813 fn publish_create_artifact_file() {
3814 let (_server, _platform, tmp_dir, mut rzup) = publish_fixture();
3815
3816 let input_path = tmp_dir.path().join("input-path.txt");
3818 std::fs::write(&input_path, "hello world").unwrap();
3819
3820 let output_path = tmp_dir.path().join("output.tar.xz");
3821 rzup.publish_create_artifact(&input_path, &output_path, 6 )
3822 .unwrap();
3823
3824 let file = std::fs::File::open(output_path).unwrap();
3825 let mut tar_reader = tar::Archive::new(liblzma::bufread::XzDecoder::new(
3826 std::io::BufReader::new(file),
3827 ));
3828 let mut paths: Vec<_> = tar_reader
3829 .entries()
3830 .unwrap()
3831 .map(|e| PathBuf::from(e.unwrap().path().unwrap()))
3832 .collect();
3833 paths.sort();
3834 assert_eq!(paths, vec![Path::new("input-path.txt")]);
3835 }
3836}