use either::Either;
use freenet_stdlib::prelude::*;
use std::sync::Arc;
use crate::contract::executor::mock_wasm_runtime::{MockWasmRuntime, UpdateOverride};
use crate::contract::executor::{ContractExecutor, Executor, UpsertResult};
use crate::wasm_runtime::MockStateStorage;
async fn create_executor() -> Executor<MockWasmRuntime, MockStateStorage> {
let storage = MockStateStorage::new();
Executor::new_mock_wasm("merge_rejected_test", storage, None, None)
.await
.expect("create MockWasmRuntime executor")
}
fn make_contract(code_bytes: &[u8]) -> ContractContainer {
let code = ContractCode::from(code_bytes.to_vec());
let params = Parameters::from(vec![]);
ContractContainer::Wasm(ContractWasmAPIVersion::V1(WrappedContract::new(
Arc::new(code),
params,
)))
}
#[tokio::test(flavor = "current_thread")]
async fn test_same_version_rejection_is_classified_as_invalid_update() {
let mut executor = create_executor().await;
let contract = make_contract(b"same_version_test");
let key = contract.key();
let initial_state = WrappedState::new(vec![1, 2, 3, 4]);
executor
.upsert_contract_state(
key,
Either::Left(initial_state.clone()),
RelatedContracts::default(),
Some(contract),
)
.await
.expect("initial PUT should succeed");
let version_rejection_reason =
"New state version 100 must be higher than current version 100".to_string();
executor.runtime.update_overrides.insert(
*key.id(),
UpdateOverride::RejectInvalidUpdate {
reason: version_rejection_reason.clone(),
},
);
let different_state = WrappedState::new(vec![5, 6, 7, 8]);
let err = executor
.upsert_contract_state(
key,
Either::Left(different_state),
RelatedContracts::default(),
None,
)
.await
.expect_err("same-version update should fail");
assert!(
err.is_invalid_update_rejection(),
"same-version WASM rejection must satisfy is_invalid_update_rejection(); \
got: {err:?}"
);
assert!(
err.is_contract_exec_rejection(),
"same-version rejection must also satisfy is_contract_exec_rejection()"
);
}
#[tokio::test(flavor = "current_thread")]
async fn test_identical_state_short_circuits_before_wasm_update() {
let mut executor = create_executor().await;
let contract = make_contract(b"short_circuit_test");
let key = contract.key();
let initial_state = WrappedState::new(vec![10, 20, 30, 40]);
executor
.upsert_contract_state(
key,
Either::Left(initial_state.clone()),
RelatedContracts::default(),
Some(contract),
)
.await
.expect("initial PUT should succeed");
executor.runtime.update_overrides.insert(
*key.id(),
UpdateOverride::RejectInvalidUpdate {
reason: "short-circuit spy: update_state must NOT be called for identical state"
.to_string(),
},
);
let result = executor
.upsert_contract_state(
key,
Either::Left(initial_state.clone()),
RelatedContracts::default(),
None,
)
.await
.expect("identical-state upsert must succeed (short-circuit, not WASM error)");
assert!(
matches!(result, UpsertResult::NoChange),
"byte-identical state update must return NoChange (short-circuit), got: {result:?}"
);
}
#[test]
fn test_is_invalid_update_rejection_matches_website_contract_format() {
use crate::contract::executor::ExecutorError;
use freenet_stdlib::client_api::ContractError as StdContractError;
let key = crate::contract::executor::test_fixtures::make_contract_key();
let cause_string = "invalid contract update, reason: \
New state version 100 must be higher than current version 100";
let err = ExecutorError::request(StdContractError::update_exec_error(key, cause_string));
assert!(
err.is_invalid_update_rejection(),
"predicate must match website-contract's same-version error format"
);
}