#![allow(clippy::unwrap_used, clippy::expect_used, unsafe_code)]
use super::super::*;
use super::{
build_gtxpack, fixture_wasm, mock_store, pubkey_env_value, sample_describe,
sign_describe_like_store,
};
use ed25519_dalek::SigningKey;
#[tokio::test]
#[serial_test::serial]
async fn ensure_cached_verifies_and_caches_happy_path() {
let Some(src) = fixture_wasm() else {
return; };
let wasm = std::fs::read(&src).unwrap();
let signing = SigningKey::from_bytes(&[11u8; 32]);
let signed_describe = sign_describe_like_store(&sample_describe(), &signing);
let archive = build_gtxpack(&signed_describe, &wasm);
let digest = hex_sha256(&archive);
let server = mock_store(archive).await;
let cache = tempfile::tempdir().unwrap();
unsafe {
std::env::set_var("GREENTIC_MCP_LOCAL_CACHE_DIR", cache.path());
std::env::set_var(STORE_URL_ENV, server.uri());
std::env::set_var(TRUSTED_SIGNERS_ENV, pubkey_env_value(&signing));
}
let result = ensure_cached("router_echo", "1.0.0", &digest).await;
let dest = cache.path().join("router_echo.wasm");
let cached_ok = dest.exists();
let cached_bytes = if cached_ok {
std::fs::read(&dest).ok()
} else {
None
};
unsafe {
std::env::remove_var("GREENTIC_MCP_LOCAL_CACHE_DIR");
std::env::remove_var(STORE_URL_ENV);
std::env::remove_var(TRUSTED_SIGNERS_ENV);
}
result.expect("happy path must verify + cache");
assert!(cached_ok, "extension.wasm must be written to the cache");
assert_eq!(cached_bytes.as_deref(), Some(wasm.as_slice()));
}
#[tokio::test]
#[serial_test::serial]
async fn ensure_cached_wrong_digest_errors_and_caches_nothing() {
let Some(src) = fixture_wasm() else {
return;
};
let wasm = std::fs::read(&src).unwrap();
let signing = SigningKey::from_bytes(&[12u8; 32]);
let signed_describe = sign_describe_like_store(&sample_describe(), &signing);
let archive = build_gtxpack(&signed_describe, &wasm);
let server = mock_store(archive).await;
let cache = tempfile::tempdir().unwrap();
unsafe {
std::env::set_var("GREENTIC_MCP_LOCAL_CACHE_DIR", cache.path());
std::env::set_var(STORE_URL_ENV, server.uri());
std::env::set_var(TRUSTED_SIGNERS_ENV, pubkey_env_value(&signing));
}
let wrong_digest = "0".repeat(64);
let result = ensure_cached("router_echo", "1.0.0", &wrong_digest).await;
let dest = cache.path().join("router_echo.wasm");
let leaked = dest.exists();
unsafe {
std::env::remove_var("GREENTIC_MCP_LOCAL_CACHE_DIR");
std::env::remove_var(STORE_URL_ENV);
std::env::remove_var(TRUSTED_SIGNERS_ENV);
}
assert!(
matches!(result, Err(StorePullError::Integrity(_))),
"got: {result:?}"
);
assert!(!leaked, "nothing must be cached on a digest mismatch");
}
#[tokio::test]
#[serial_test::serial]
async fn ensure_cached_untrusted_signature_errors_and_caches_nothing() {
let Some(src) = fixture_wasm() else {
return;
};
let wasm = std::fs::read(&src).unwrap();
let signer = SigningKey::from_bytes(&[13u8; 32]);
let untrusted = SigningKey::from_bytes(&[14u8; 32]);
let signed_describe = sign_describe_like_store(&sample_describe(), &signer);
let archive = build_gtxpack(&signed_describe, &wasm);
let digest = hex_sha256(&archive);
let server = mock_store(archive).await;
let cache = tempfile::tempdir().unwrap();
unsafe {
std::env::set_var("GREENTIC_MCP_LOCAL_CACHE_DIR", cache.path());
std::env::set_var(STORE_URL_ENV, server.uri());
std::env::set_var(TRUSTED_SIGNERS_ENV, pubkey_env_value(&untrusted));
}
let result = ensure_cached("router_echo", "1.0.0", &digest).await;
let dest = cache.path().join("router_echo.wasm");
let leaked = dest.exists();
unsafe {
std::env::remove_var("GREENTIC_MCP_LOCAL_CACHE_DIR");
std::env::remove_var(STORE_URL_ENV);
std::env::remove_var(TRUSTED_SIGNERS_ENV);
}
assert!(
matches!(result, Err(StorePullError::Signature(_))),
"got: {result:?}"
);
assert!(!leaked, "an untrusted signature must cache nothing");
}
#[tokio::test]
#[serial_test::serial]
async fn wasm_without_sidecar_triggers_repull() {
let signing = SigningKey::from_bytes(&[42u8; 32]);
let fake_wasm = b"fake-wasm-bytes-for-sidecar-test";
let signed_describe = sign_describe_like_store(&sample_describe(), &signing);
let archive = build_gtxpack(&signed_describe, fake_wasm);
let digest = hex_sha256(&archive);
let cache = tempfile::tempdir().unwrap();
let dest = cache.path().join("router_echo.wasm");
std::fs::write(&dest, b"stale-unverified-bytes").unwrap();
assert!(dest.exists(), "pre-condition: wasm exists");
assert!(
!sidecar_path(&dest).exists(),
"pre-condition: sidecar absent"
);
let server = mock_store(archive).await;
unsafe {
std::env::set_var("GREENTIC_MCP_LOCAL_CACHE_DIR", cache.path());
std::env::set_var(STORE_URL_ENV, server.uri());
std::env::set_var(TRUSTED_SIGNERS_ENV, pubkey_env_value(&signing));
}
let result = ensure_cached("router_echo", "1.0.0", &digest).await;
let sidecar_exists = sidecar_path(&dest).exists();
unsafe {
std::env::remove_var("GREENTIC_MCP_LOCAL_CACHE_DIR");
std::env::remove_var(STORE_URL_ENV);
std::env::remove_var(TRUSTED_SIGNERS_ENV);
}
result.expect("re-pull of sidecar-less wasm must succeed");
assert!(
sidecar_exists,
"sidecar must be written after verified re-pull"
);
}
#[tokio::test]
#[serial_test::serial]
async fn cache_with_matching_marker_short_circuits() {
let cache = tempfile::tempdir().unwrap();
let dest = cache.path().join("router_echo.wasm");
std::fs::write(&dest, b"cached-wasm").unwrap();
std::fs::write(sidecar_path(&dest), b"somedigest").unwrap();
std::fs::write(gtxpack_marker_path(&dest), b"pinneddigest").unwrap();
unsafe {
std::env::set_var("GREENTIC_MCP_LOCAL_CACHE_DIR", cache.path());
std::env::set_var(STORE_URL_ENV, "http://127.0.0.1:1");
std::env::remove_var(TRUSTED_SIGNERS_ENV);
}
let result = ensure_cached("router_echo", "1.0.0", "pinneddigest").await;
unsafe {
std::env::remove_var("GREENTIC_MCP_LOCAL_CACHE_DIR");
std::env::remove_var(STORE_URL_ENV);
}
result.expect("wasm + sidecar + matching marker must short-circuit with Ok");
}
#[tokio::test]
#[serial_test::serial]
async fn stale_marker_triggers_repull_on_upgrade() {
let signing = SigningKey::from_bytes(&[7u8; 32]);
let new_wasm = b"new-verified-wasm-bytes";
let signed_describe = sign_describe_like_store(&sample_describe(), &signing);
let archive = build_gtxpack(&signed_describe, new_wasm);
let new_digest = hex_sha256(&archive);
let cache = tempfile::tempdir().unwrap();
let dest = cache.path().join("router_echo.wasm");
std::fs::write(&dest, b"old-cached-wasm").unwrap();
std::fs::write(sidecar_path(&dest), hex_sha256(b"old-cached-wasm")).unwrap();
std::fs::write(gtxpack_marker_path(&dest), b"0000oldolddigest").unwrap();
let server = mock_store(archive).await;
unsafe {
std::env::set_var("GREENTIC_MCP_LOCAL_CACHE_DIR", cache.path());
std::env::set_var(STORE_URL_ENV, server.uri());
std::env::set_var(TRUSTED_SIGNERS_ENV, pubkey_env_value(&signing));
}
let result = ensure_cached("router_echo", "1.0.0", &new_digest).await;
let cached_after = std::fs::read(&dest).unwrap_or_default();
let marker_after = std::fs::read_to_string(gtxpack_marker_path(&dest)).unwrap_or_default();
unsafe {
std::env::remove_var("GREENTIC_MCP_LOCAL_CACHE_DIR");
std::env::remove_var(STORE_URL_ENV);
std::env::remove_var(TRUSTED_SIGNERS_ENV);
}
result.expect("stale-marker cache must re-pull the upgraded component");
assert_eq!(
cached_after, new_wasm,
"the upgraded wasm must replace the stale one"
);
assert!(
marker_after.trim().eq_ignore_ascii_case(&new_digest),
"the marker must be rewritten to the new pinned digest"
);
}