huskies: merge 1202 bug Flaky test: matrix pull pull_rejects_tampered_artifact_sha256_mismatch fails nondeterministically in merge gates

This commit is contained in:
Huskies Agent
2026-07-17 21:45:53 +00:00
parent 38bdfa2ab4
commit fd83ff2f53
+73 -2
View File
@@ -306,10 +306,23 @@ mod tests {
/// Spawn a minimal HTTP server serving fixed responses for /// Spawn a minimal HTTP server serving fixed responses for
/// `/manifest.json` and `/<artifact>` so `run_pull` can be exercised /// `/manifest.json` and `/<artifact>` so `run_pull` can be exercised
/// end-to-end without a real network. /// end-to-end without a real network.
///
/// `run_pull` issues its manifest fetch and artifact download
/// sequentially over one shared `reqwest::Client`, whose HTTP/1.1 pool
/// assumes keep-alive by default. Every response here closes its
/// connection after a single request, so we send `Connection: close` to
/// stop the client from ever trying to reuse a connection this server
/// has already torn down (a real source of intermittent "cannot reach"
/// fetch errors that raced ahead of the sha256 check). We also block on
/// a readiness signal fired from inside the accept loop so the returned
/// port is guaranteed to have an active acceptor before any caller
/// connects to it.
async fn spawn_mock_channel(manifest_body: Vec<u8>, artifact_body: Vec<u8>) -> u16 { async fn spawn_mock_channel(manifest_body: Vec<u8>, artifact_body: Vec<u8>) -> u16 {
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let port = listener.local_addr().unwrap().port(); let port = listener.local_addr().unwrap().port();
let (ready_tx, ready_rx) = tokio::sync::oneshot::channel();
tokio::spawn(async move { tokio::spawn(async move {
let _ = ready_tx.send(());
loop { loop {
let Ok((mut stream, _)) = listener.accept().await else { let Ok((mut stream, _)) = listener.accept().await else {
break; break;
@@ -327,13 +340,17 @@ mod tests {
} else { } else {
artifact_body artifact_body
}; };
let response = let response = format!(
format!("HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n", body.len()); "HTTP/1.1 200 OK\r\nContent-Length: {}\r\nConnection: close\r\n\r\n",
body.len()
);
let _ = stream.write_all(response.as_bytes()).await; let _ = stream.write_all(response.as_bytes()).await;
let _ = stream.write_all(&body).await; let _ = stream.write_all(&body).await;
let _ = stream.shutdown().await;
}); });
} }
}); });
let _ = ready_rx.await;
port port
} }
@@ -468,10 +485,64 @@ mod tests {
msg.contains("sha256 mismatch"), msg.contains("sha256 mismatch"),
"expected sha256 failure: {msg}" "expected sha256 failure: {msg}"
); );
assert!(
!msg.contains("could not fetch manifest")
&& !msg.contains("could not download artifact"),
"manifest fetch and artifact download must succeed so the sha256 check is reached, \
not preempted by a fetch/download error: {msg}"
);
assert!(msg.contains("Nothing was installed")); assert!(msg.contains("Nothing was installed"));
assert!(!dir.path().join("huskies-linux-arm64").exists()); assert!(!dir.path().join("huskies-linux-arm64").exists());
} }
/// Stress variant of [`pull_rejects_tampered_artifact_sha256_mismatch`]:
/// repeats the same scenario against many independent mock channels to
/// catch any reintroduction of the fetch-ordering race under load.
#[tokio::test]
async fn pull_rejects_tampered_artifact_sha256_mismatch_stress() {
for _ in 0..50 {
let kp = make_keypair();
let pubkey = crate::node_identity::public_key_hex(&kp);
use sha2::{Digest, Sha256};
let mut hasher = Sha256::new();
hasher.update(b"the real bytes");
let real_sha256: String = hasher
.finalize()
.iter()
.map(|b| format!("{b:02x}"))
.collect();
let manifest = ReleaseManifest {
artifact: "huskies-linux-arm64".to_string(),
sha256: real_sha256,
version: "abc1234".to_string(),
channel: "stable".to_string(),
timestamp: 1,
};
let signed_manifest = signed(&kp, manifest);
let manifest_body = serde_json::to_vec(&signed_manifest).unwrap();
let port = spawn_mock_channel(manifest_body, b"tampered bytes".to_vec()).await;
let dir = tempfile::tempdir().unwrap();
let msg = run_pull(
"stable",
&format!("http://127.0.0.1:{port}"),
&pubkey,
None,
dir.path(),
"huskies-linux-arm64",
)
.await;
assert!(
msg.contains("sha256 mismatch"),
"expected sha256 failure on every iteration: {msg}"
);
assert!(!dir.path().join("huskies-linux-arm64").exists());
}
}
#[tokio::test] #[tokio::test]
async fn pull_missing_manifest_endpoint_is_loud_error() { async fn pull_missing_manifest_endpoint_is_loud_error() {
let dir = tempfile::tempdir().unwrap(); let dir = tempfile::tempdir().unwrap();