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
150 changes: 150 additions & 0 deletions packages/rs-dpp/src/document/accessors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,3 +214,153 @@ impl DocumentV0Setters for Document {
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::document::v0::DocumentV0;
use crate::prelude::Revision;

fn make_doc() -> Document {
Document::V0(DocumentV0 {
id: Identifier::new([1u8; 32]),
owner_id: Identifier::new([2u8; 32]),
properties: BTreeMap::new(),
revision: Some(1),
created_at: Some(10),
updated_at: Some(20),
transferred_at: Some(30),
created_at_block_height: Some(100),
updated_at_block_height: Some(200),
transferred_at_block_height: Some(300),
created_at_core_block_height: Some(1),
updated_at_core_block_height: Some(2),
transferred_at_core_block_height: Some(3),
creator_id: Some(Identifier::new([9u8; 32])),
})
}

// ================================================================
// Document-enum getters forward to the V0 variant.
// ================================================================

#[test]
fn getters_forward_all_fields_to_v0() {
let doc = make_doc();
assert_eq!(doc.id(), Identifier::new([1u8; 32]));
assert_eq!(doc.owner_id(), Identifier::new([2u8; 32]));
assert_eq!(doc.id_ref(), &Identifier::new([1u8; 32]));
assert_eq!(doc.owner_id_ref(), &Identifier::new([2u8; 32]));
assert_eq!(doc.revision(), Some(1));
assert_eq!(doc.created_at(), Some(10));
assert_eq!(doc.updated_at(), Some(20));
assert_eq!(doc.transferred_at(), Some(30));
assert_eq!(doc.created_at_block_height(), Some(100));
assert_eq!(doc.updated_at_block_height(), Some(200));
assert_eq!(doc.transferred_at_block_height(), Some(300));
assert_eq!(doc.created_at_core_block_height(), Some(1));
assert_eq!(doc.updated_at_core_block_height(), Some(2));
assert_eq!(doc.transferred_at_core_block_height(), Some(3));
assert_eq!(doc.creator_id(), Some(Identifier::new([9u8; 32])));
assert!(doc.properties().is_empty());
}

// ================================================================
// Document-enum properties_consumed returns the inner BTreeMap.
// ================================================================

#[test]
fn properties_consumed_forwards_to_v0() {
let Document::V0(mut v0) = make_doc();
v0.properties.insert("k".into(), Value::U64(77));
let doc = Document::V0(v0);
let map = doc.properties_consumed();
assert_eq!(map.get("k"), Some(&Value::U64(77)));
}

// ================================================================
// Document-enum properties_mut allows mutation.
// ================================================================

#[test]
fn properties_mut_allows_mutation_via_enum() {
let mut doc = make_doc();
doc.properties_mut().insert("x".into(), Value::U64(5));
assert_eq!(doc.properties().get("x"), Some(&Value::U64(5)));
}

// ================================================================
// Document-enum setters forward to the V0 variant.
// ================================================================

#[test]
fn setters_forward_all_fields_to_v0() {
let mut doc = make_doc();
doc.set_id(Identifier::new([42u8; 32]));
doc.set_owner_id(Identifier::new([43u8; 32]));
let mut p = BTreeMap::new();
p.insert("z".into(), Value::Bool(true));
doc.set_properties(p.clone());
doc.set_revision(Some(99));
doc.set_created_at(Some(1_000_000));
doc.set_updated_at(Some(2_000_000));
doc.set_transferred_at(Some(3_000_000));
doc.set_created_at_block_height(Some(111));
doc.set_updated_at_block_height(Some(222));
doc.set_transferred_at_block_height(Some(333));
doc.set_created_at_core_block_height(Some(4));
doc.set_updated_at_core_block_height(Some(5));
doc.set_transferred_at_core_block_height(Some(6));
doc.set_creator_id(Some(Identifier::new([7u8; 32])));

assert_eq!(doc.id(), Identifier::new([42u8; 32]));
assert_eq!(doc.owner_id(), Identifier::new([43u8; 32]));
assert_eq!(doc.properties(), &p);
assert_eq!(doc.revision(), Some(99));
assert_eq!(doc.created_at(), Some(1_000_000));
assert_eq!(doc.updated_at(), Some(2_000_000));
assert_eq!(doc.transferred_at(), Some(3_000_000));
assert_eq!(doc.created_at_block_height(), Some(111));
assert_eq!(doc.updated_at_block_height(), Some(222));
assert_eq!(doc.transferred_at_block_height(), Some(333));
assert_eq!(doc.created_at_core_block_height(), Some(4));
assert_eq!(doc.updated_at_core_block_height(), Some(5));
assert_eq!(doc.transferred_at_core_block_height(), Some(6));
assert_eq!(doc.creator_id(), Some(Identifier::new([7u8; 32])));
}

// ================================================================
// Document-enum bump_revision forwards to the V0 saturating_add.
// Unlike increment_revision, bump_revision never errors and
// saturates at Revision::MAX.
// ================================================================

#[test]
fn bump_revision_via_enum_saturates_at_max() {
let mut doc = make_doc();
doc.set_revision(Some(Revision::MAX));
doc.bump_revision();
assert_eq!(doc.revision(), Some(Revision::MAX));
}

#[test]
fn bump_revision_via_enum_is_noop_for_none() {
let mut doc = make_doc();
doc.set_revision(None);
doc.bump_revision();
assert_eq!(doc.revision(), None);
}

// ================================================================
// Document::get (default impl on the trait) returns a property
// at the given path or None.
// ================================================================

#[test]
fn get_returns_property_by_path() {
let mut doc = make_doc();
doc.properties_mut().insert("a".into(), Value::U64(1));
assert_eq!(doc.get("a"), Some(&Value::U64(1)));
assert_eq!(doc.get("missing"), None);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,139 @@ mod tests {
// User-defined property serialization
// ================================================================

// ================================================================
// None-valued system fields should return None (not panic).
// ================================================================

fn minimal_doc() -> DocumentV0 {
DocumentV0 {
id: Identifier::new([1u8; 32]),
owner_id: Identifier::new([2u8; 32]),
properties: BTreeMap::new(),
revision: None,
created_at: None,
updated_at: None,
transferred_at: None,
created_at_block_height: None,
updated_at_block_height: None,
transferred_at_block_height: None,
created_at_core_block_height: None,
updated_at_core_block_height: None,
transferred_at_core_block_height: None,
creator_id: None,
}
}

#[test]
fn get_raw_returns_none_for_unset_optional_system_fields() {
let platform_version = PlatformVersion::latest();
let contract = json_document_to_contract(
"../rs-drive/tests/supporting_files/contract/dashpay/dashpay-contract.json",
false,
platform_version,
)
.expect("expected contract");
let document_type = contract
.document_type_for_name("profile")
.expect("expected document type");

let doc = minimal_doc();
let keys = [
"$createdAt",
"$updatedAt",
"$transferredAt",
"$createdAtBlockHeight",
"$updatedAtBlockHeight",
"$transferredAtBlockHeight",
"$createdAtCoreBlockHeight",
"$updatedAtCoreBlockHeight",
"$transferredAtCoreBlockHeight",
"$creatorId",
];
for k in keys {
let raw = doc
.get_raw_for_document_type_v0(k, document_type, None, platform_version)
.expect("should succeed");
assert_eq!(raw, None, "{k} should yield None when unset");
}
}

// ================================================================
// get_raw_for_document_type_v0: $ownerId with owner_id override
// takes precedence even if the document's owner is different, but
// for $id the override is NOT applied (only $ownerId is gated).
// ================================================================

#[test]
fn get_raw_owner_id_override_only_affects_dollar_owner_id_path() {
let platform_version = PlatformVersion::latest();
let contract = json_document_to_contract(
"../rs-drive/tests/supporting_files/contract/dashpay/dashpay-contract.json",
false,
platform_version,
)
.expect("expected contract");
let document_type = contract
.document_type_for_name("profile")
.expect("expected document type");

let doc = make_document_with_known_ids();
let override_owner = [0xFF; 32];

// $ownerId path sees the override
let raw_owner = doc
.get_raw_for_document_type_v0(
"$ownerId",
document_type,
Some(override_owner),
platform_version,
)
.expect("owner should succeed");
assert_eq!(raw_owner, Some(Vec::from(override_owner)));

// $id path ignores the owner override
let raw_id = doc
.get_raw_for_document_type_v0(
"$id",
document_type,
Some(override_owner),
platform_version,
)
.expect("id should succeed");
assert_eq!(
raw_id,
Some(doc.id.to_vec()),
"$id path should not be affected by owner_id override"
);
}

// ================================================================
// get_raw_for_contract with unknown document_type_name errors.
// ================================================================

#[test]
fn get_raw_for_contract_with_unknown_document_type_errors() {
use crate::document::document_methods::DocumentGetRawForContractV0;
let platform_version = PlatformVersion::latest();
let contract = json_document_to_contract(
"../rs-drive/tests/supporting_files/contract/dashpay/dashpay-contract.json",
false,
platform_version,
)
.expect("expected contract");

let doc = make_document_with_known_ids();
let err = doc
.get_raw_for_contract_v0("$id", "nonExistentType", &contract, None, platform_version)
.expect_err("unknown document type should fail");
match err {
crate::ProtocolError::DataContractError(
crate::data_contract::errors::DataContractError::DocumentTypeNotFound(_),
) => {}
other => panic!("expected DocumentTypeNotFound, got {:?}", other),
}
}

#[test]
fn get_raw_serializes_user_defined_property() {
let platform_version = PlatformVersion::latest();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,4 +220,56 @@ mod tests {
"empty-property documents with same ids should be equal ignoring timestamps"
);
}

// ================================================================
// also_ignore_fields with a field that doesn't exist in either
// document: behaves the same as no ignore list.
// ================================================================

#[test]
fn also_ignore_fields_with_nonexistent_field_has_no_effect() {
let doc1 = make_base_document();
let doc2 = make_base_document();
assert!(doc1.is_equal_ignoring_time_based_fields_v0(
&doc2,
Some(vec!["this_field_does_not_exist"])
));
}

// ================================================================
// Empty also_ignore_fields vec: equivalent to None.
// ================================================================

#[test]
fn also_ignore_fields_empty_vec_is_equivalent_to_none() {
let doc1 = make_base_document();
let mut doc2 = make_base_document();
doc2.properties
.insert("name".to_string(), Value::Text("Bob".to_string()));
// Empty ignore list means all properties are compared.
assert!(!doc1.is_equal_ignoring_time_based_fields_v0(&doc2, Some(vec![])));
}

// ================================================================
// Revision None vs Some is not equal
// ================================================================

#[test]
fn documents_with_none_and_some_revision_are_not_equal() {
let mut doc1 = make_base_document();
let doc2 = make_base_document();
doc1.revision = None;
assert!(!doc1.is_equal_ignoring_time_based_fields_v0(&doc2, None));
}

// ================================================================
// Self-equal: same document equals itself (time-based differences
// shouldn't matter when both sides are identical).
// ================================================================

#[test]
fn a_document_equals_itself_ignoring_time_based_fields() {
let doc = make_base_document();
assert!(doc.is_equal_ignoring_time_based_fields_v0(&doc, None));
}
}
Loading
Loading