aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'tests/smoke/src/main.rs')
-rw-r--r--tests/smoke/src/main.rs403+0 −403
1 files changed, 0 insertions, 403 deletions
diff --git a/tests/smoke/src/main.rs b/tests/smoke/src/main.rs
deleted file mode 100644
--- a/tests/smoke/src/main.rs
+++ /dev/null
@@ -1,403 +0,0 @@
-// SPDX-FileCopyrightText: 2026 Nikolay Govorov
-// SPDX-License-Identifier: AGPL-3.0-or-later
-
-// Black-box smoke tests for Tesor.
-//
-// Downloads archives from Tesor and upstream, compares sha256 hashes,
-// verifies that minisig signatures (Zig) and sha256 checksums (Go) match upstream.
-//
-// Usage:
-// TESOR_URL=https://pkg.earth cargo run -p smoke
-
-use sha2::{Digest, Sha256};
-
-const GO_FILES: &[&str] = &[
- "go1.23.0.linux-amd64.tar.gz",
- "go1.23.0.darwin-arm64.tar.gz",
- "go1.21.0.windows-amd64.zip",
-];
-
-struct ZigTestFile {
- file: &'static str,
- version: &'static str,
-}
-
-const ZIG_FILES: &[ZigTestFile] = &[
- // 0.15.2 (new tarball name format, some new targets)
- ZigTestFile {
- file: "zig-0.15.2.tar.xz",
- version: "0.15.2",
- },
- ZigTestFile {
- file: "zig-x86_64-windows-0.15.2.zip",
- version: "0.15.2",
- },
- ZigTestFile {
- file: "zig-aarch64-macos-0.15.2.tar.xz",
- version: "0.15.2",
- },
- ZigTestFile {
- file: "zig-aarch64-netbsd-0.15.2.tar.xz",
- version: "0.15.2",
- },
- ZigTestFile {
- file: "zig-powerpc64le-freebsd-0.15.2.tar.xz",
- version: "0.15.2",
- },
- // Every 'zig-xxx-linux-0.14.1.tar.xz' (same order as on the website)
- ZigTestFile {
- file: "zig-x86_64-linux-0.14.1.tar.xz",
- version: "0.14.1",
- },
- ZigTestFile {
- file: "zig-aarch64-linux-0.14.1.tar.xz",
- version: "0.14.1",
- },
- ZigTestFile {
- file: "zig-armv7a-linux-0.14.1.tar.xz",
- version: "0.14.1",
- },
- ZigTestFile {
- file: "zig-riscv64-linux-0.14.1.tar.xz",
- version: "0.14.1",
- },
- ZigTestFile {
- file: "zig-powerpc64le-linux-0.14.1.tar.xz",
- version: "0.14.1",
- },
- ZigTestFile {
- file: "zig-x86-linux-0.14.1.tar.xz",
- version: "0.14.1",
- },
- ZigTestFile {
- file: "zig-loongarch64-linux-0.14.1.tar.xz",
- version: "0.14.1",
- },
- ZigTestFile {
- file: "zig-s390x-linux-0.14.1.tar.xz",
- version: "0.14.1",
- },
- // 0.10.1 (last stage1 release)
- ZigTestFile {
- file: "zig-0.10.1.tar.xz",
- version: "0.10.1",
- },
- ZigTestFile {
- file: "zig-bootstrap-0.10.1.tar.xz",
- version: "0.10.1",
- },
- ZigTestFile {
- file: "zig-linux-i386-0.10.1.tar.xz",
- version: "0.10.1",
- },
- ZigTestFile {
- file: "zig-macos-aarch64-0.10.1.tar.xz",
- version: "0.10.1",
- },
- ZigTestFile {
- file: "zig-windows-x86_64-0.10.1.zip",
- version: "0.10.1",
- },
- // 0.7.1 (oldest supported patch release)
- ZigTestFile {
- file: "zig-0.7.1.tar.xz",
- version: "0.7.1",
- },
- ZigTestFile {
- file: "zig-linux-x86_64-0.7.1.tar.xz",
- version: "0.7.1",
- },
- // 0.6.0 (oldest supported version)
- ZigTestFile {
- file: "zig-0.6.0.tar.xz",
- version: "0.6.0",
- },
- ZigTestFile {
- file: "zig-linux-x86_64-0.6.0.tar.xz",
- version: "0.6.0",
- },
- // 0.1.1 (first release)
- ZigTestFile {
- file: "zig-win64-0.1.1.zip",
- version: "0.1.1",
- },
-];
-
-struct Runner {
- passed: u32,
- failed: u32,
- client: reqwest::blocking::Client,
-}
-
-impl Runner {
- fn new() -> Self {
- Self {
- passed: 0,
- failed: 0,
- client: reqwest::blocking::Client::builder()
- .timeout(std::time::Duration::from_secs(600))
- .build()
- .expect("failed to create HTTP client"),
- }
- }
-
- fn ok(&mut self, name: &str) {
- self.passed += 1;
- println!(" \x1b[32mok\x1b[0m {name}");
- }
-
- fn fail(&mut self, name: &str, reason: &str) {
- self.failed += 1;
- println!(" \x1b[31mFAIL\x1b[0m {name}");
- println!(" {reason}");
- }
-
- fn fetch_bytes(&self, url: &str) -> Result<Vec<u8>, String> {
- let resp = self.client.get(url).send().map_err(|e| format!("{e}"))?;
- if !resp.status().is_success() {
- return Err(format!("{} for {url}", resp.status()));
- }
- resp.bytes().map(|b| b.to_vec()).map_err(|e| format!("{e}"))
- }
-
- fn fetch_status(&self, url: &str) -> Result<u16, String> {
- let resp = self.client.get(url).send().map_err(|e| format!("{e}"))?;
- Ok(resp.status().as_u16())
- }
-
- fn fetch_text(&self, url: &str) -> Result<(u16, String, String), String> {
- let resp = self.client.get(url).send().map_err(|e| format!("{e}"))?;
- let status = resp.status().as_u16();
- let content_type = resp
- .headers()
- .get("content-type")
- .and_then(|v| v.to_str().ok())
- .unwrap_or("")
- .to_string();
- let body = resp.text().map_err(|e| format!("{e}"))?;
- Ok((status, content_type, body))
- }
-}
-
-fn sha256hex(data: &[u8]) -> String {
- hex::encode(Sha256::digest(data))
-}
-
-fn zig_upstream_url(file: &str, version: &str) -> String {
- format!("https://ziglang.org/download/{version}/{file}")
-}
-
-fn test_web(r: &mut Runner, base_url: &str) {
- println!("\n--- Web ---");
-
- {
- let name = "GET / returns html";
- match r.fetch_text(&format!("{base_url}/")) {
- Ok((status, ct, body)) => {
- if status != 200 {
- r.fail(name, &format!("status {status}, expected 200"));
- } else if !ct.contains("text/html") {
- r.fail(name, &format!("content-type: {ct}"));
- } else if !body.contains("Tesor") {
- r.fail(name, "body missing \"Tesor\"");
- } else {
- r.ok(name);
- }
- }
- Err(e) => r.fail(name, &e),
- }
- }
-
- {
- let name = "GET /base.css returns css";
- match r.fetch_text(&format!("{base_url}/base.css")) {
- Ok((status, ct, body)) => {
- if status != 200 {
- r.fail(name, &format!("status {status}"));
- } else if !ct.contains("text/css") {
- r.fail(name, &format!("content-type: {ct}"));
- } else if body.is_empty() {
- r.fail(name, "empty body");
- } else {
- r.ok(name);
- }
- }
- Err(e) => r.fail(name, &e),
- }
- }
-
- {
- let name = "GET /favicon.ico returns icon";
- match r.fetch_status(&format!("{base_url}/favicon.ico")) {
- Ok(200) => r.ok(name),
- Ok(s) => r.fail(name, &format!("status {s}")),
- Err(e) => r.fail(name, &e),
- }
- }
-
- {
- let name = "GET /about/licenses returns html";
- match r.fetch_text(&format!("{base_url}/about/licenses")) {
- Ok((status, ct, body)) => {
- if status != 200 {
- r.fail(name, &format!("status {status}"));
- } else if !ct.contains("text/html") {
- r.fail(name, &format!("content-type: {ct}"));
- } else if !body.contains("AGPL") {
- r.fail(name, "body missing \"AGPL\"");
- } else {
- r.ok(name);
- }
- }
- Err(e) => r.fail(name, &e),
- }
- }
-
- {
- let name = "GET /does-not-exist returns 404";
- match r.fetch_status(&format!("{base_url}/does-not-exist.xyz")) {
- Ok(404) => r.ok(name),
- Ok(s) => r.fail(name, &format!("status {s}, expected 404")),
- Err(e) => r.fail(name, &e),
- }
- }
-}
-
-fn test_go(r: &mut Runner, base_url: &str) {
- println!("\n--- Go ---");
-
- for file in GO_FILES {
- // Archive: compare sha256 with upstream
- {
- let name = format!("go: {file} matches upstream");
- match (
- r.fetch_bytes(&format!("{base_url}/go/{file}")),
- r.fetch_bytes(&format!("https://go.dev/dl/{file}")),
- ) {
- (Ok(tesor), Ok(upstream)) => {
- let zh = sha256hex(&tesor);
- let uh = sha256hex(&upstream);
- if zh != uh {
- r.fail(&name, &format!("tesor={zh} upstream={uh}"));
- } else {
- r.ok(&name);
- }
- }
- (Err(e), _) | (_, Err(e)) => r.fail(&name, &e),
- }
- }
-
- // Checksum: compare .sha256 endpoint with computed hash
- {
- let name = format!("go: {file}.sha256 matches content");
- match (
- r.fetch_bytes(&format!("{base_url}/go/{file}")),
- r.fetch_text(&format!("{base_url}/go/{file}.sha256")),
- ) {
- (Ok(archive), Ok((200, _, checksum_body))) => {
- let computed = sha256hex(&archive);
- let expected = checksum_body.trim();
- if computed != expected {
- r.fail(&name, &format!("computed={computed} endpoint={expected}"));
- } else {
- r.ok(&name);
- }
- }
- (Ok(_), Ok((s, _, _))) => {
- r.fail(&name, &format!("sha256 endpoint returned {s}"));
- }
- (Err(e), _) | (_, Err(e)) => r.fail(&name, &e),
- }
- }
- }
-
- {
- let name = "go: nonexistent version returns 404";
- match r.fetch_status(&format!("{base_url}/go/go99.99.99.linux-amd64.tar.gz")) {
- Ok(404) => r.ok(name),
- Ok(s) => r.fail(name, &format!("status {s}, expected 404")),
- Err(e) => r.fail(name, &e),
- }
- }
-}
-
-fn test_zig(r: &mut Runner, base_url: &str) {
- println!("\n--- Zig ---");
-
- for entry in ZIG_FILES {
- // Archive: compare sha256 with upstream
- {
- let name = format!("zig: {} matches upstream", entry.file);
- match (
- r.fetch_bytes(&format!("{base_url}/zig/{}", entry.file)),
- r.fetch_bytes(&zig_upstream_url(entry.file, entry.version)),
- ) {
- (Ok(tesor), Ok(upstream)) => {
- let zh = sha256hex(&tesor);
- let uh = sha256hex(&upstream);
- if zh != uh {
- r.fail(&name, &format!("tesor={zh} upstream={uh}"));
- } else {
- r.ok(&name);
- }
- }
- (Err(e), _) | (_, Err(e)) => r.fail(&name, &e),
- }
- }
-
- // Signature: compare .minisig with upstream
- {
- let name = format!("zig: {}.minisig matches upstream", entry.file);
- let tesor_url = format!("{base_url}/zig/{}.minisig", entry.file);
- let upstream_url = format!("{}.minisig", zig_upstream_url(entry.file, entry.version));
- match (r.fetch_bytes(&tesor_url), r.fetch_bytes(&upstream_url)) {
- (Ok(tesor), Ok(upstream)) => {
- if tesor != upstream {
- r.fail(&name, "minisig content differs from upstream");
- } else {
- r.ok(&name);
- }
- }
- (Err(e), _) | (_, Err(e)) => r.fail(&name, &e),
- }
- }
- }
-
- {
- let name = "zig: nonexistent version returns 404";
- match r.fetch_status(&format!("{base_url}/zig/zig-x86_64-linux-99.99.99.tar.xz")) {
- Ok(404) => r.ok(name),
- Ok(s) => r.fail(name, &format!("status {s}, expected 404")),
- Err(e) => r.fail(name, &e),
- }
- }
-}
-
-fn main() {
- let base_url = match std::env::var("TESOR_URL") {
- Ok(url) => url,
- Err(_) => {
- eprintln!("Error: TESOR_URL environment variable is not set.");
- eprintln!("Usage: TESOR_URL=https://pkg.earth cargo run -p smoke");
- std::process::exit(1);
- }
- };
-
- let base_url = base_url.trim_end_matches('/');
- println!("Smoke tests against {base_url}");
-
- let mut runner = Runner::new();
-
- test_web(&mut runner, base_url);
- test_go(&mut runner, base_url);
- test_zig(&mut runner, base_url);
-
- println!(
- "\n--- Results: {} passed, {} failed ---",
- runner.passed, runner.failed
- );
-
- if runner.failed > 0 {
- std::process::exit(1);
- }
-}