use either::Either;
use freenet_stdlib::prelude::*;
use crate::contract::executor::mock_runtime::MockRuntime;
use crate::contract::executor::mock_wasm_runtime::MockWasmRuntime;
use crate::contract::executor::{ContractExecutor, Executor};
use crate::wasm_runtime::MockStateStorage;
async fn create_mock_runtime_executor() -> Executor<MockRuntime, MockStateStorage> {
let storage = MockStateStorage::new();
Executor::new_mock_in_memory("conformance_mock", storage, None, None)
.await
.expect("create MockRuntime executor")
}
async fn create_mock_wasm_executor() -> Executor<MockWasmRuntime, MockStateStorage> {
let storage = MockStateStorage::new();
Executor::new_mock_wasm("conformance_wasm", storage, None, None, None)
.await
.expect("create MockWasmRuntime executor")
}
use super::super::mock_runtime::test::create_test_contract as test_contract;
#[tokio::test(flavor = "current_thread")]
async fn conformance_put_new_contract_both_return_updated() {
let mut mock = create_mock_runtime_executor().await;
let mut wasm = create_mock_wasm_executor().await;
let contract = test_contract(b"conformance_put_test");
let key = contract.key();
let state = WrappedState::new(vec![10, 20, 30]);
let mock_result = mock
.upsert_contract_state(
key,
Either::Left(state.clone()),
RelatedContracts::default(),
Some(contract.clone()),
)
.await
.expect("MockRuntime PUT should succeed");
let wasm_result = wasm
.upsert_contract_state(
key,
Either::Left(state.clone()),
RelatedContracts::default(),
Some(contract.clone()),
)
.await
.expect("MockWasmRuntime PUT should succeed");
assert!(
matches!(mock_result, crate::contract::UpsertResult::Updated(_)),
"MockRuntime: new contract PUT must return Updated"
);
assert!(
matches!(wasm_result, crate::contract::UpsertResult::Updated(_)),
"MockWasmRuntime: new contract PUT must return Updated"
);
}
#[tokio::test(flavor = "current_thread")]
async fn conformance_fetch_returns_same_state() {
let mut mock = create_mock_runtime_executor().await;
let mut wasm = create_mock_wasm_executor().await;
let contract = test_contract(b"conformance_fetch_test");
let key = contract.key();
let state = WrappedState::new(vec![42, 43, 44, 45]);
mock.upsert_contract_state(
key,
Either::Left(state.clone()),
RelatedContracts::default(),
Some(contract.clone()),
)
.await
.unwrap();
wasm.upsert_contract_state(
key,
Either::Left(state.clone()),
RelatedContracts::default(),
Some(contract.clone()),
)
.await
.unwrap();
let (mock_state, mock_code) = mock.fetch_contract(key, true).await.unwrap();
let (wasm_state, wasm_code) = wasm.fetch_contract(key, true).await.unwrap();
assert_eq!(
mock_state.as_ref().map(|s| s.as_ref()),
wasm_state.as_ref().map(|s| s.as_ref()),
"Both runtimes must return the same state after PUT"
);
assert_eq!(
mock_state.as_ref().map(|s| s.as_ref()),
Some(state.as_ref()),
"Fetched state must equal the originally stored state"
);
assert!(
mock_code.is_some(),
"MockRuntime must return contract code when requested"
);
assert!(
wasm_code.is_some(),
"MockWasmRuntime must return contract code when requested"
);
}
#[tokio::test(flavor = "current_thread")]
async fn conformance_lookup_key_after_put() {
let mut mock = create_mock_runtime_executor().await;
let mut wasm = create_mock_wasm_executor().await;
let contract = test_contract(b"conformance_lookup_test");
let key = contract.key();
let instance_id = *key.id();
let state = WrappedState::new(vec![1]);
mock.upsert_contract_state(
key,
Either::Left(state.clone()),
RelatedContracts::default(),
Some(contract.clone()),
)
.await
.unwrap();
wasm.upsert_contract_state(
key,
Either::Left(state.clone()),
RelatedContracts::default(),
Some(contract.clone()),
)
.await
.unwrap();
let mock_key = mock.lookup_key(&instance_id);
let wasm_key = wasm.lookup_key(&instance_id);
assert!(mock_key.is_some(), "MockRuntime must find key after PUT");
assert!(
wasm_key.is_some(),
"MockWasmRuntime must find key after PUT"
);
assert_eq!(
mock_key.unwrap(),
wasm_key.unwrap(),
"Both runtimes must return the same ContractKey"
);
}
#[tokio::test(flavor = "current_thread")]
async fn conformance_summarize_succeeds_on_both() {
let mut mock = create_mock_runtime_executor().await;
let mut wasm = create_mock_wasm_executor().await;
let contract = test_contract(b"conformance_summarize_test");
let key = contract.key();
let state = WrappedState::new(vec![100, 200]);
mock.upsert_contract_state(
key,
Either::Left(state.clone()),
RelatedContracts::default(),
Some(contract.clone()),
)
.await
.unwrap();
wasm.upsert_contract_state(
key,
Either::Left(state.clone()),
RelatedContracts::default(),
Some(contract.clone()),
)
.await
.unwrap();
let mock_summary = mock
.summarize_contract_state(key)
.await
.expect("MockRuntime summarize must succeed");
let wasm_summary = wasm
.summarize_contract_state(key)
.await
.expect("MockWasmRuntime summarize must succeed");
assert_eq!(
mock_summary.as_ref(),
wasm_summary.as_ref(),
"Summaries must match: both runtimes use blake3 hashing"
);
assert_eq!(
mock_summary.as_ref().len(),
32,
"Summary must be a 32-byte blake3 hash"
);
let expected = blake3::hash(state.as_ref());
assert_eq!(mock_summary.as_ref(), expected.as_bytes());
}
#[tokio::test(flavor = "current_thread")]
async fn conformance_update_full_state_both_succeed() {
let mut mock = create_mock_runtime_executor().await;
let mut wasm = create_mock_wasm_executor().await;
let contract = test_contract(b"conformance_update_test");
let key = contract.key();
let initial_state = WrappedState::new(vec![1, 2, 3]);
let updated_state = WrappedState::new(vec![4, 5, 6]);
assert!(
blake3::hash(updated_state.as_ref()).as_bytes()
> blake3::hash(initial_state.as_ref()).as_bytes(),
"Test data assumption violated: updated_state must have a larger blake3 hash \
than initial_state for MockRuntime to return Updated. Change the test values."
);
mock.upsert_contract_state(
key,
Either::Left(initial_state.clone()),
RelatedContracts::default(),
Some(contract.clone()),
)
.await
.unwrap();
wasm.upsert_contract_state(
key,
Either::Left(initial_state.clone()),
RelatedContracts::default(),
Some(contract.clone()),
)
.await
.unwrap();
let mock_result = mock
.upsert_contract_state(
key,
Either::Left(updated_state.clone()),
RelatedContracts::default(),
None,
)
.await
.expect("MockRuntime UPDATE should not error");
let wasm_result = wasm
.upsert_contract_state(
key,
Either::Left(updated_state.clone()),
RelatedContracts::default(),
None,
)
.await
.expect("MockWasmRuntime UPDATE should not error");
let mock_state = match mock_result {
crate::contract::UpsertResult::Updated(s) => s,
other @ crate::contract::UpsertResult::NoChange
| other @ crate::contract::UpsertResult::CurrentWon(_) => {
panic!("MockRuntime: expected Updated, got {other:?}")
}
};
let wasm_state = match wasm_result {
crate::contract::UpsertResult::Updated(s) => s,
other @ crate::contract::UpsertResult::NoChange
| other @ crate::contract::UpsertResult::CurrentWon(_) => {
panic!("MockWasmRuntime: expected Updated, got {other:?}")
}
};
assert_eq!(
mock_state.as_ref(),
updated_state.as_ref(),
"MockRuntime must store the updated state"
);
assert_eq!(
wasm_state.as_ref(),
updated_state.as_ref(),
"MockWasmRuntime must store the updated state"
);
}
#[tokio::test(flavor = "current_thread")]
async fn drift_detector_register_notifier_divergence() {
let mut mock = create_mock_runtime_executor().await;
let mut wasm = create_mock_wasm_executor().await;
let contract = test_contract(b"conformance_notifier_test");
let key = contract.key();
let instance_id = *key.id();
let state = WrappedState::new(vec![1]);
mock.upsert_contract_state(
key,
Either::Left(state.clone()),
RelatedContracts::default(),
Some(contract.clone()),
)
.await
.unwrap();
wasm.upsert_contract_state(
key,
Either::Left(state.clone()),
RelatedContracts::default(),
Some(contract.clone()),
)
.await
.unwrap();
let (tx1, _rx1) =
tokio::sync::mpsc::channel(crate::contract::SUBSCRIBER_NOTIFICATION_CHANNEL_SIZE);
let (tx2, _rx2) =
tokio::sync::mpsc::channel(crate::contract::SUBSCRIBER_NOTIFICATION_CHANNEL_SIZE);
let client_id = crate::client_events::ClientId::next();
mock.register_contract_notifier(instance_id, client_id, tx1, None)
.expect("MockRuntime register should succeed (no-op)");
wasm.register_contract_notifier(instance_id, client_id, tx2, None)
.expect("MockWasmRuntime register should succeed");
let mock_subs = mock.get_subscription_info();
let wasm_subs = wasm.get_subscription_info();
assert!(
mock_subs.is_empty(),
"DRIFT DETECTOR: MockRuntime.get_subscription_info() is a no-op. \
If this fails, MockRuntime now tracks subscribers — update this test!"
);
assert!(
!wasm_subs.is_empty(),
"MockWasmRuntime must track subscribers via bridged path. \
If this fails, the production subscriber tracking path is broken!"
);
}
#[tokio::test(flavor = "current_thread")]
async fn conformance_missing_contract_errors_on_both() {
let mut mock = create_mock_runtime_executor().await;
let mut wasm = create_mock_wasm_executor().await;
let contract = test_contract(b"conformance_missing_test");
let key = contract.key();
let mock_result = mock
.upsert_contract_state(
key,
Either::Left(WrappedState::new(vec![1])),
RelatedContracts::default(),
None,
)
.await;
let wasm_result = wasm
.upsert_contract_state(
key,
Either::Left(WrappedState::new(vec![1])),
RelatedContracts::default(),
None,
)
.await;
assert!(
mock_result.is_err(),
"MockRuntime must error on UPDATE to non-existent contract"
);
assert!(
wasm_result.is_err(),
"MockWasmRuntime must error on UPDATE to non-existent contract"
);
}
#[tokio::test(flavor = "current_thread")]
async fn conformance_lookup_unknown_returns_none() {
let mock = create_mock_runtime_executor().await;
let wasm = create_mock_wasm_executor().await;
let contract = test_contract(b"conformance_unknown_test");
let instance_id = *contract.key().id();
assert!(
mock.lookup_key(&instance_id).is_none(),
"MockRuntime must return None for unknown contract"
);
assert!(
wasm.lookup_key(&instance_id).is_none(),
"MockWasmRuntime must return None for unknown contract"
);
}