Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
346 changes: 346 additions & 0 deletions packages/rs-dpp/src/document/extended_document/v0/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1317,4 +1317,350 @@ mod tests {
"token_payment_info should be absent when None"
);
}

// ================================================================
// token_payment_info: Some branches for to_map_value / into_map_value /
// to_pretty_json.
//
// These exercise the `if let Some(token_payment_info) = ...` arms
// that prior tests never hit (since they all used token_payment_info =
// None).
// ================================================================

fn make_extended_document_with_token_payment_info(
platform_version: &PlatformVersion,
) -> (ExtendedDocumentV0, crate::prelude::DataContract) {
use crate::tokens::token_payment_info::v0::TokenPaymentInfoV0;
let contract = load_dashpay_contract(platform_version);
let document_type = contract
.document_type_for_name("profile")
.expect("expected profile document type");
let document = document_type
.random_document(Some(11), platform_version)
.expect("expected random document");
let tpi = TokenPaymentInfo::V0(TokenPaymentInfoV0::default());
let ext_doc = ExtendedDocumentV0 {
document_type_name: "profile".to_string(),
data_contract_id: contract.id(),
document,
data_contract: contract.clone(),
metadata: None,
entropy: Default::default(),
token_payment_info: Some(tpi),
};
(ext_doc, contract)
}

#[test]
fn to_map_value_contains_token_payment_info_when_some() {
let platform_version = PlatformVersion::latest();
let (ext_doc, _) = make_extended_document_with_token_payment_info(platform_version);
let map = ext_doc.to_map_value().expect("to_map_value ok");
assert!(
map.contains_key(property_names::TOKEN_PAYMENT_INFO),
"token_payment_info should be present in map when Some"
);
}

#[test]
fn into_map_value_contains_token_payment_info_when_some() {
let platform_version = PlatformVersion::latest();
let (ext_doc, _) = make_extended_document_with_token_payment_info(platform_version);
let map = ext_doc.into_map_value().expect("into_map_value ok");
assert!(
map.contains_key(property_names::TOKEN_PAYMENT_INFO),
"token_payment_info should be present in map when Some"
);
}

#[test]
fn to_pretty_json_includes_token_payment_info_when_some() {
let platform_version = PlatformVersion::latest();
let (ext_doc, _) = make_extended_document_with_token_payment_info(platform_version);
let json = ext_doc
.to_pretty_json(platform_version)
.expect("to_pretty_json ok");
let obj = json.as_object().expect("json object");
assert!(
obj.contains_key(property_names::TOKEN_PAYMENT_INFO),
"pretty JSON should include $tokenPaymentInfo when Some"
);
}

// ================================================================
// set_untrusted: binary-path arm (exercises ReplacementType::BinaryBytes).
//
// The contactInfo document type in dashpay has `encToUserId` as a
// byteArray property — which makes it a binary path. Hitting that
// branch exercises the second arm of set_untrusted's if-else chain
// (binary_paths.contains(path) → BinaryBytes replacement).
// ================================================================

#[test]
fn set_untrusted_binary_path_replaces_bytes() {
let platform_version = PlatformVersion::latest();
let contract = load_dashpay_contract(platform_version);
let document_type = contract
.document_type_for_name("contactInfo")
.expect("contactInfo type");
let document = document_type
.random_document(Some(3), platform_version)
.expect("random contactInfo");
let mut ext_doc = ExtendedDocumentV0::from_document_with_additional_info(
document,
contract,
"contactInfo".to_string(),
None,
);

// "encToUserId" IS a binary_path — the set_untrusted impl should
// invoke ReplacementType::BinaryBytes.replace_for_bytes.
let payload = vec![0xABu8; 32];
ext_doc
.set_untrusted("encToUserId", Value::Bytes(payload.clone()))
.expect("set_untrusted on a binary path should succeed");
// The stored value should be a Bytes variant — BinaryBytes replaces
// it into Bytes(bytes).
let val = ext_doc
.get_optional_value("encToUserId")
.expect("value should be stored");
// The replacement normalizes to Value::Bytes, not Value::Array(...).
match val {
Value::Bytes(b) => assert_eq!(b, &payload),
other => panic!("expected Value::Bytes after set_untrusted, got {:?}", other),
}
}

// ================================================================
// Hash differs when document_type_name is changed on an otherwise
// identical ExtendedDocument — the hash incorporates the contract
// serialization AND the type name length prefix.
// ================================================================

#[test]
fn hash_fails_when_document_type_name_unknown() {
// Exercises the error path of hash(): serialize_v0 calls
// document_type()? which fails when the name isn't in the contract.
let platform_version = PlatformVersion::latest();
let (mut ext_doc, _) = make_extended_document(platform_version);
ext_doc.document_type_name = "doesNotExist".to_string();
let result = ext_doc.hash(platform_version);
assert!(
result.is_err(),
"hash must fail when document_type_name is not on the contract"
);
}

// ================================================================
// from_trusted_platform_value honors a $dataContractId override in
// the input value (exercises the `remove_optional_hash256_bytes`
// branch that overrides the contract's default id).
// ================================================================

#[test]
fn from_trusted_platform_value_honors_data_contract_id_override() {
let platform_version = PlatformVersion::latest();
let (ext_doc, contract) = make_extended_document(platform_version);

let mut map = ext_doc.to_map_value().expect("to_map_value ok");
// Override the $dataContractId with a DIFFERENT id.
let override_id = [0xEEu8; 32];
map.insert(
property_names::DATA_CONTRACT_ID.to_string(),
Value::Bytes(override_id.to_vec()),
);
let val: Value = map.into();

let recovered =
ExtendedDocumentV0::from_trusted_platform_value(val, contract, platform_version)
.expect("from_trusted_platform_value should succeed");

assert_eq!(
recovered.data_contract_id,
Identifier::from(override_id),
"override $dataContractId should be used"
);
}

// ================================================================
// from_trusted_platform_value falls back to contract id when
// $dataContractId is absent from the value.
// ================================================================

#[test]
fn from_trusted_platform_value_falls_back_to_contract_id() {
let platform_version = PlatformVersion::latest();
let (ext_doc, contract) = make_extended_document(platform_version);
let expected_id = contract.id();

let mut map = ext_doc.to_map_value().expect("to_map_value ok");
// Remove the $dataContractId so the unwrap_or branch kicks in.
map.remove(property_names::DATA_CONTRACT_ID);
let val: Value = map.into();

let recovered =
ExtendedDocumentV0::from_trusted_platform_value(val, contract, platform_version)
.expect("from_trusted_platform_value ok");
assert_eq!(recovered.data_contract_id, expected_id);
}

// ================================================================
// from_trusted_platform_value fails when the Value is not a map
// (exercises the `into_btree_string_map` error branch).
// ================================================================

#[test]
fn from_trusted_platform_value_rejects_non_map_value() {
let platform_version = PlatformVersion::latest();
let contract = load_dashpay_contract(platform_version);
// Passing a scalar Value should fail the initial `into_btree_string_map`.
let result = ExtendedDocumentV0::from_trusted_platform_value(
Value::Text("not a map".to_string()),
contract,
platform_version,
);
assert!(result.is_err());
}

#[test]
fn from_untrusted_platform_value_rejects_non_map_value() {
let platform_version = PlatformVersion::latest();
let contract = load_dashpay_contract(platform_version);
let result = ExtendedDocumentV0::from_untrusted_platform_value(
Value::U64(42),
contract,
platform_version,
);
assert!(result.is_err());
}

// ================================================================
// from_document_with_additional_info preserves the token_payment_info
// argument — exercises the `Some(...)` code path for that constructor.
// ================================================================

#[test]
fn from_document_with_additional_info_stores_token_payment_info() {
use crate::tokens::token_payment_info::v0::TokenPaymentInfoV0;
let platform_version = PlatformVersion::latest();
let contract = load_dashpay_contract(platform_version);
let document_type = contract
.document_type_for_name("profile")
.expect("profile type");
let document = document_type
.random_document(Some(123), platform_version)
.expect("random");
let tpi = TokenPaymentInfo::V0(TokenPaymentInfoV0::default());

let ext_doc = ExtendedDocumentV0::from_document_with_additional_info(
document,
contract,
"profile".to_string(),
Some(tpi),
);
assert!(ext_doc.token_payment_info.is_some());
}

// ================================================================
// properties / properties_as_mut delegate to the underlying Document.
// Mutation via properties_as_mut must be visible through properties().
// ================================================================

#[test]
fn properties_as_mut_and_properties_share_state() {
let platform_version = PlatformVersion::latest();
let (mut ext_doc, _) = make_extended_document(platform_version);
ext_doc
.properties_as_mut()
.insert("bulkField".to_string(), Value::U64(777));
// properties() returns the same map.
assert_eq!(
ext_doc.properties().get("bulkField"),
Some(&Value::U64(777))
);
}

// ================================================================
// to_json_object_for_validation fails gracefully when the document
// type name is not in the contract.
// ================================================================

#[test]
fn to_pretty_json_fails_for_unknown_document_type_name() {
// to_pretty_json calls document.to_json() which succeeds, but later
// steps in from_trusted_platform_value / value_conversion would fail.
// For pretty-json itself the only invariant that matters is that
// document_type_name is a string the impl preserves. Verify that at
// least the call succeeds when document_type_name does NOT exist on
// the contract — since to_pretty_json doesn't look up the document
// type.
let platform_version = PlatformVersion::latest();
let (mut ext_doc, _) = make_extended_document(platform_version);
ext_doc.document_type_name = "doesNotExist".to_string();
// to_pretty_json does NOT look up the document type on the contract,
// so it should succeed and include the bogus name.
let pretty = ext_doc
.to_pretty_json(platform_version)
.expect("to_pretty_json should succeed for any string name");
let obj = pretty.as_object().expect("json object");
assert_eq!(
obj.get(property_names::DOCUMENT_TYPE_NAME)
.and_then(|v| v.as_str()),
Some("doesNotExist")
);
}

// ================================================================
// document_type() fails when document_type_name points to an unknown
// type (exercises the error branch in document_type()).
// This complements `document_type_fails_for_invalid_type_name` but
// goes through `can_be_modified()` and `requires_revision()` which
// also depend on document_type().
// ================================================================

#[test]
fn can_be_modified_fails_when_type_name_unknown() {
let platform_version = PlatformVersion::latest();
let (mut ext_doc, _) = make_extended_document(platform_version);
ext_doc.document_type_name = "doesNotExist".to_string();
assert!(ext_doc.can_be_modified().is_err());
assert!(ext_doc.requires_revision().is_err());
}

// ================================================================
// set_untrusted error path: when the target is an identifier path
// but the value cannot be converted to identifier bytes.
//
// We can approximate this by attempting set_untrusted on a binary
// path with a Value that is NOT byte-like. `to_identifier_bytes` will
// fail, which yields an Err from set_untrusted. This exercises the
// error branch.
// ================================================================

#[test]
fn set_untrusted_binary_path_returns_err_for_non_byte_value() {
let platform_version = PlatformVersion::latest();
let contract = load_dashpay_contract(platform_version);
let document_type = contract
.document_type_for_name("contactInfo")
.expect("contactInfo type");
let document = document_type
.random_document(Some(88), platform_version)
.expect("random contactInfo");
let mut ext_doc = ExtendedDocumentV0::from_document_with_additional_info(
document,
contract,
"contactInfo".to_string(),
None,
);

// Providing a Text value for a binary path should fail the
// to_identifier_bytes conversion.
let result = ext_doc.set_untrusted("encToUserId", Value::Text("not bytes".to_string()));
assert!(
result.is_err(),
"set_untrusted on binary path with non-byte value must fail, got {:?}",
result
);
}
}
Loading
Loading