Files
core/test/rust/native/dependency/cache.rs
T
2026-11-03 03:32:07 +00:00

210 lines
8.9 KiB
Rust

use super::super::Directory;
use super::fixture::digest;
use std::io::{Read, Write};
use std::os::unix::fs::PermissionsExt;
use std::os::unix::net::UnixListener;
#[test]
fn cache_client_authenticates_receipts_and_rejects_missing_corrupt_or_truncated_bytes() {
for behavior in [
"valid",
"missing",
"receipt",
"digest",
"truncated",
"oversized",
] {
let directory = Directory::new();
let state = directory.0.join("state");
std::fs::create_dir(&state).unwrap();
std::fs::set_permissions(&state, std::fs::Permissions::from_mode(0o700)).unwrap();
let token = "a".repeat(64);
let token_path = state.join("deverd.token");
std::fs::write(&token_path, &token).unwrap();
std::fs::set_permissions(token_path, std::fs::Permissions::from_mode(0o600)).unwrap();
let endpoint = directory.0.join("cache.sock");
let server = UnixListener::bind(&endpoint).unwrap();
let bytes = b"archive fixture";
let sha256 = digest(bytes);
let expected = sha256.clone();
let worker = std::thread::spawn(move || {
let (mut stream, _) = server.accept().unwrap();
let mut length = [0; 4];
stream.read_exact(&mut length).unwrap();
let mut payload = vec![0; u32::from_be_bytes(length) as usize];
stream.read_exact(&mut payload).unwrap();
let request: serde_json::Value = serde_json::from_slice(&payload).unwrap();
assert_eq!(request["token"], token);
assert_eq!(request["operation"]["artifact_get"]["sha256"], expected);
assert_eq!(request["operation"]["artifact_get"]["bytes"], bytes.len());
if behavior == "oversized" {
stream
.write_all(&(1024_u32 * 1024 + 1).to_be_bytes())
.unwrap();
return;
}
let artifact = if behavior == "missing" {
serde_json::Value::Null
} else {
serde_json::json!({"sha256":if behavior == "receipt" { "0".repeat(64) } else { expected }, "bytes":bytes.len()})
};
let response = serde_json::to_vec(
&serde_json::json!({"ok":true, "status":null, "error":null, "artifact":artifact}),
)
.unwrap();
stream
.write_all(&(response.len() as u32).to_be_bytes())
.unwrap();
stream.write_all(&response).unwrap();
if !matches!(behavior, "missing" | "receipt") {
let mut content = bytes.to_vec();
if behavior == "digest" {
content[0] ^= 1;
}
if behavior == "truncated" {
content.pop();
}
stream.write_all(&content).unwrap();
}
});
let result = dever::process::cache::read(&state, &endpoint, &sha256, bytes.len() as u64);
worker.join().unwrap();
if behavior == "valid" {
assert_eq!(result.unwrap(), bytes);
} else {
assert!(result.is_err(), "accepted {behavior}");
}
}
}
#[test]
fn cache_client_rejects_untrusted_state_before_connecting() {
let directory = Directory::new();
std::fs::set_permissions(&directory.0, std::fs::Permissions::from_mode(0o777)).unwrap();
let error = dever::process::cache::read(
&directory.0,
&directory.0.join("absent.sock"),
&"a".repeat(64),
1,
)
.unwrap_err();
assert!(error.contains("not writable"), "{error}");
}
#[test]
fn cache_upload_requires_both_authenticated_admission_and_final_receipts() {
for rejected in ["none", "admission", "publication"] {
let directory = Directory::new();
let state = directory.0.join("state");
std::fs::create_dir(&state).unwrap();
std::fs::set_permissions(&state, std::fs::Permissions::from_mode(0o700)).unwrap();
let token = "b".repeat(64);
let path = state.join("deverd.token");
std::fs::write(&path, &token).unwrap();
std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o600)).unwrap();
let endpoint = directory.0.join("cache.sock");
let listener = UnixListener::bind(&endpoint).unwrap();
let bytes = b"content addressed upload";
let server = std::thread::spawn(move || {
let (mut stream, _) = listener.accept().unwrap();
stream
.set_read_timeout(Some(std::time::Duration::from_secs(5)))
.unwrap();
let mut prefix = [0; 4];
stream.read_exact(&mut prefix).unwrap();
let mut payload = vec![0; u32::from_be_bytes(prefix) as usize];
stream.read_exact(&mut payload).unwrap();
let request: serde_json::Value = serde_json::from_slice(&payload).unwrap();
assert_eq!(request["token"], token);
let expected = serde_json::json!({"sha256":digest(bytes), "bytes":bytes.len()});
assert_eq!(request["operation"]["artifact_put"], expected);
for phase in ["admission", "publication"] {
let artifact = if rejected == phase {
serde_json::json!({"sha256":"0".repeat(64),"bytes":bytes.len()})
} else {
expected.clone()
};
let response = serde_json::to_vec(
&serde_json::json!({"ok":true,"status":null,"error":null,"artifact":artifact}),
)
.unwrap();
stream
.write_all(&(response.len() as u32).to_be_bytes())
.unwrap();
stream.write_all(&response).unwrap();
if phase == "admission" {
if rejected == phase {
assert_eq!(stream.read(&mut prefix).unwrap(), 0);
break;
}
let mut received = vec![0; bytes.len()];
stream.read_exact(&mut received).unwrap();
assert_eq!(received, bytes);
}
}
});
let result = dever::process::cache::write(&state, &endpoint, bytes);
server.join().unwrap();
assert_eq!(result.is_ok(), rejected == "none");
}
}
#[test]
fn signed_resource_get_uses_the_historical_deverd_envelope_and_exact_receipt() {
// 原 service.rs::resource 请求版本/catalog/扩展身份;服务先回复 receipt,再写裸资产。
for corrupt in [false, true] {
let directory = Directory::new();
let state = directory.0.join("state");
std::fs::create_dir(&state).unwrap();
let token = "c".repeat(64);
let token_path = state.join("deverd.token");
std::fs::write(&token_path, &token).unwrap();
std::fs::set_permissions(token_path, std::fs::Permissions::from_mode(0o600)).unwrap();
let endpoint = directory.0.join("resources.sock");
let listener = UnixListener::bind(&endpoint).unwrap();
let bytes = b"signed runtime fixture";
let catalog = "d".repeat(64);
let extension = serde_json::json!({"kind":{"type":"runtime","ecosystem":"npm"},"target":"linux-x86_64"});
let expected = serde_json::json!({"token":token,"operation":{"resource_get":{"version":"0.0.1","catalog":catalog,"extension":extension,"artifact":{"sha256":digest(bytes),"bytes":bytes.len()}}},"installed":[]});
let worker = std::thread::spawn(move || {
let (mut stream, _) = listener.accept().unwrap();
stream
.set_read_timeout(Some(std::time::Duration::from_secs(5)))
.unwrap();
let mut prefix = [0; 4];
stream.read_exact(&mut prefix).unwrap();
let mut request = vec![0; u32::from_be_bytes(prefix) as usize];
stream.read_exact(&mut request).unwrap();
assert_eq!(
serde_json::from_slice::<serde_json::Value>(&request).unwrap(),
expected
);
let response = serde_json::to_vec(&serde_json::json!({"ok":true,"status":null,"error":null,"artifact":{"sha256":digest(bytes),"bytes":bytes.len()}})).unwrap();
stream
.write_all(&(response.len() as u32).to_be_bytes())
.unwrap();
stream.write_all(&response).unwrap();
let mut content = bytes.to_vec();
if corrupt {
content[0] ^= 1;
}
stream.write_all(&content).unwrap();
});
let result = dever::process::cache::resource(
&state,
&endpoint,
"0.0.1",
&catalog,
Some(&extension),
&digest(bytes),
bytes.len() as u64,
);
worker.join().unwrap();
if corrupt {
assert!(result.unwrap_err().contains("SHA-256"));
} else {
assert_eq!(result.unwrap(), bytes);
}
}
}