Skip to main content

drive/util/object_size_info/
document_info.rs

1use crate::drive::document::index_level_tree_types::distinct_estimated_keys;
2use crate::error::drive::DriveError;
3use crate::error::fee::FeeError;
4use crate::error::Error;
5use crate::util::object_size_info::DriveKeyInfo::{Key, KeySize};
6use crate::util::object_size_info::KeyValueInfo::{KeyRefRequest, KeyValueMaxSize};
7use crate::util::object_size_info::{DriveKeyInfo, KeyValueInfo};
8use crate::util::storage_flags::StorageFlags;
9use crate::util::type_constants::{
10    DEFAULT_HASH_SIZE_U16, DEFAULT_HASH_SIZE_U8, U32_SIZE_U16, U32_SIZE_U8, U64_SIZE_U16,
11    U64_SIZE_U8,
12};
13use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters;
14use dpp::data_contract::document_type::methods::DocumentTypeBasicMethods;
15use dpp::data_contract::document_type::{DocumentPropertyType, DocumentTypeRef, IndexLevel};
16use dpp::document::document_methods::DocumentMethodsV0;
17use dpp::document::{Document, DocumentV0Getters};
18use dpp::version::PlatformVersion;
19use grovedb::batch::key_info::KeyInfo;
20use std::borrow::Cow;
21
22/// Document info
23#[derive(Clone, Debug)]
24pub enum DocumentInfo<'a> {
25    /// The document without it's serialized form
26    DocumentOwnedInfo((Document, Option<Cow<'a, StorageFlags>>)),
27    /// The borrowed document without it's serialized form
28    DocumentRefInfo((&'a Document, Option<Cow<'a, StorageFlags>>)),
29    /// The borrowed document and it's serialized form
30    DocumentRefAndSerialization((&'a Document, &'a [u8], Option<Cow<'a, StorageFlags>>)),
31    /// The document and it's serialized form
32    DocumentAndSerialization((Document, Vec<u8>, Option<Cow<'a, StorageFlags>>)),
33    /// An element size
34    DocumentEstimatedAverageSize(u32),
35}
36
37/// DocumentInfo V0 Methods
38pub trait DocumentInfoV0Methods {
39    /// Returns true if self is a document with serialization.
40    fn is_document_and_serialization(&self) -> bool;
41    /// Returns true if self is a document size.
42    fn is_document_size(&self) -> bool;
43    /// Gets the borrowed document
44    fn get_borrowed_document(&self) -> Option<&Document>;
45    /// Makes the document ID the key.
46    fn id_key_value_info(&self) -> KeyValueInfo<'_>;
47    /// Gets the raw path for the given document type
48    fn get_estimated_size_for_document_type(
49        &self,
50        key_path: &str,
51        document_type: DocumentTypeRef,
52        platform_version: &PlatformVersion,
53    ) -> Result<u16, Error>;
54    /// Gets the raw path for the given document type
55    fn get_raw_for_document_type(
56        &self,
57        key_path: &str,
58        document_type: DocumentTypeRef,
59        owner_id: Option<[u8; 32]>,
60        size_info_with_base_event: Option<(&IndexLevel, [u8; 32])>,
61        platform_version: &PlatformVersion,
62    ) -> Result<Option<DriveKeyInfo<'_>>, Error>;
63    /// The keys an index naming the typed array at `key_path` enters the document under at
64    /// `size_info_with_base_event`'s level: one per distinct element, sorted, none for an
65    /// absent or empty list. A worst-case size holds `maxItems` distinct elements of the
66    /// element type's typical size, each a key of its own.
67    fn get_raw_array_elements_for_document_type(
68        &self,
69        key_path: &str,
70        document_type: DocumentTypeRef,
71        size_info_with_base_event: Option<(&IndexLevel, [u8; 32])>,
72        platform_version: &PlatformVersion,
73    ) -> Result<Vec<DriveKeyInfo<'_>>, Error>;
74    /// Gets the borrowed document
75    fn get_borrowed_document_and_storage_flags(&self)
76        -> Option<(&Document, Option<&StorageFlags>)>;
77    /// Gets storage flags
78    fn get_storage_flags_ref(&self) -> Option<&StorageFlags>;
79    /// Gets storage flags
80    fn get_document_id_as_slice(&self) -> Option<&[u8]>;
81}
82
83/// The type of the value an index keys `key_path` by: the document type's property at that
84/// path, the element type of a typed array, which an index keys by each element (protocol
85/// version 14), or the field a derived index property reads on the referenced document
86/// (protocol version 14), which no property of the type carries.
87fn indexed_property_type<'a>(
88    document_type: &'a DocumentTypeRef<'a>,
89    key_path: &str,
90) -> Option<&'a DocumentPropertyType> {
91    match document_type.flattened_properties().get(key_path) {
92        Some(property) => match &property.property_type {
93            DocumentPropertyType::TypedArray(typed_array) => Some(typed_array.item_type.as_ref()),
94            property_type => Some(property_type),
95        },
96        None => document_type.derived_index_property_type(key_path),
97    }
98}
99
100impl DocumentInfo<'_> {
101    /// The same document without storage flags: how the document of a type declaring a
102    /// `ttl` is written, since such a document refunds nothing. A worst-case size passes
103    /// through unchanged.
104    pub fn without_storage_flags(self) -> Self {
105        match self {
106            DocumentInfo::DocumentOwnedInfo((document, _)) => {
107                DocumentInfo::DocumentOwnedInfo((document, None))
108            }
109            DocumentInfo::DocumentRefInfo((document, _)) => {
110                DocumentInfo::DocumentRefInfo((document, None))
111            }
112            DocumentInfo::DocumentRefAndSerialization((document, serialization, _)) => {
113                DocumentInfo::DocumentRefAndSerialization((document, serialization, None))
114            }
115            DocumentInfo::DocumentAndSerialization((document, serialization, _)) => {
116                DocumentInfo::DocumentAndSerialization((document, serialization, None))
117            }
118            DocumentInfo::DocumentEstimatedAverageSize(size) => {
119                DocumentInfo::DocumentEstimatedAverageSize(size)
120            }
121        }
122    }
123}
124
125impl DocumentInfoV0Methods for DocumentInfo<'_> {
126    /// Returns true if self is a document with serialization.
127    fn is_document_and_serialization(&self) -> bool {
128        matches!(self, DocumentInfo::DocumentRefAndSerialization(..))
129    }
130
131    /// Returns true if self is a document size.
132    fn is_document_size(&self) -> bool {
133        matches!(self, DocumentInfo::DocumentEstimatedAverageSize(_))
134    }
135
136    /// Gets the borrowed document
137    fn get_borrowed_document(&self) -> Option<&Document> {
138        match self {
139            DocumentInfo::DocumentRefAndSerialization((document, _, _))
140            | DocumentInfo::DocumentRefInfo((document, _)) => Some(document),
141            DocumentInfo::DocumentOwnedInfo((document, _))
142            | DocumentInfo::DocumentAndSerialization((document, _, _)) => Some(document),
143            DocumentInfo::DocumentEstimatedAverageSize(_) => None,
144        }
145    }
146
147    /// Makes the document ID the key.
148    fn id_key_value_info(&self) -> KeyValueInfo<'_> {
149        match self {
150            DocumentInfo::DocumentRefAndSerialization((document, _, _))
151            | DocumentInfo::DocumentRefInfo((document, _)) => {
152                KeyRefRequest(document.id_ref().as_slice())
153            }
154            DocumentInfo::DocumentOwnedInfo((document, _))
155            | DocumentInfo::DocumentAndSerialization((document, _, _)) => {
156                KeyRefRequest(document.id_ref().as_slice())
157            }
158            DocumentInfo::DocumentEstimatedAverageSize(document_max_size) => {
159                KeyValueMaxSize((32, *document_max_size))
160            }
161        }
162    }
163
164    /// Gets the raw path for the given document type
165    fn get_estimated_size_for_document_type(
166        &self,
167        key_path: &str,
168        document_type: DocumentTypeRef,
169        platform_version: &PlatformVersion,
170    ) -> Result<u16, Error> {
171        match key_path {
172            "$ownerId" | "$id" | "$creatorId" | "$moderatedBy" => Ok(DEFAULT_HASH_SIZE_U16),
173            "$createdAt" | "$updatedAt" | "$transferredAt" | "$moderatedAt" => Ok(U64_SIZE_U16),
174            "$createdAtBlockHeight" | "$updatedAtBlockHeight" | "$transferredAtBlockHeight" => {
175                Ok(U64_SIZE_U16)
176            }
177            "$createdAtCoreBlockHeight"
178            | "$updatedAtCoreBlockHeight"
179            | "$transferredAtCoreBlockHeight" => Ok(U32_SIZE_U16),
180            key_path => {
181                let property_type = indexed_property_type(&document_type, key_path).ok_or({
182                    Error::Fee(FeeError::DocumentTypeFieldNotFoundForEstimation(format!(
183                        "incorrect key path [{}] for document type for estimated sizes",
184                        key_path
185                    )))
186                })?;
187                let estimated_size = property_type
188                    .middle_byte_size_ceil(platform_version)?
189                    .ok_or({
190                        Error::Drive(DriveError::CorruptedCodeExecution(
191                            "document type must have a max size",
192                        ))
193                    })?;
194                Ok(estimated_size)
195            }
196        }
197    }
198
199    /// Gets the raw path for the given document type
200    fn get_raw_for_document_type(
201        &self,
202        key_path: &str,
203        document_type: DocumentTypeRef,
204        owner_id: Option<[u8; 32]>,
205        size_info_with_base_event: Option<(&IndexLevel, [u8; 32])>,
206        platform_version: &PlatformVersion,
207    ) -> Result<Option<DriveKeyInfo<'_>>, Error> {
208        match self {
209            DocumentInfo::DocumentRefAndSerialization((document, _, _))
210            | DocumentInfo::DocumentRefInfo((document, _)) => {
211                let raw_value = document.get_raw_for_document_type(
212                    key_path,
213                    document_type,
214                    owner_id,
215                    platform_version,
216                )?;
217                match raw_value {
218                    None => Ok(None),
219                    Some(value) => Ok(Some(Key(value))),
220                }
221            }
222            DocumentInfo::DocumentOwnedInfo((document, _))
223            | DocumentInfo::DocumentAndSerialization((document, _, _)) => {
224                let raw_value = document.get_raw_for_document_type(
225                    key_path,
226                    document_type,
227                    owner_id,
228                    platform_version,
229                )?;
230                match raw_value {
231                    None => Ok(None),
232                    Some(value) => Ok(Some(Key(value))),
233                }
234            }
235            DocumentInfo::DocumentEstimatedAverageSize(_) => {
236                let (index_level, base_event) = size_info_with_base_event.ok_or(Error::Drive(
237                    DriveError::CorruptedCodeExecution("size_info_with_base_event None but needed"),
238                ))?;
239                match key_path {
240                    "$ownerId" | "$id" | "$creatorId" | "$moderatedBy" => {
241                        Ok(Some(KeySize(KeyInfo::MaxKeySize {
242                            unique_id: document_type
243                                .unique_id_for_document_field(index_level, base_event)
244                                .to_vec(),
245                            max_size: DEFAULT_HASH_SIZE_U8,
246                        })))
247                    }
248                    "$createdAt" | "$updatedAt" | "$transferredAt" | "$moderatedAt" => {
249                        Ok(Some(KeySize(KeyInfo::MaxKeySize {
250                            unique_id: document_type
251                                .unique_id_for_document_field(index_level, base_event)
252                                .to_vec(),
253                            max_size: U64_SIZE_U8,
254                        })))
255                    }
256                    "$createdAtBlockHeight"
257                    | "$updatedAtBlockHeight"
258                    | "$transferredAtBlockHeight" => Ok(Some(KeySize(KeyInfo::MaxKeySize {
259                        unique_id: document_type
260                            .unique_id_for_document_field(index_level, base_event)
261                            .to_vec(),
262                        max_size: U64_SIZE_U8,
263                    }))),
264                    "$createdAtCoreBlockHeight"
265                    | "$updatedAtCoreBlockHeight"
266                    | "$transferredAtCoreBlockHeight" => Ok(Some(KeySize(KeyInfo::MaxKeySize {
267                        unique_id: document_type
268                            .unique_id_for_document_field(index_level, base_event)
269                            .to_vec(),
270                        max_size: U32_SIZE_U8,
271                    }))),
272                    key_path => {
273                        let property_type = indexed_property_type(&document_type, key_path)
274                            .ok_or({
275                                Error::Fee(FeeError::DocumentTypeFieldNotFoundForEstimation(
276                                    format!("incorrect key path [{}] for document type for get_raw_for_document_type", key_path)
277                                ))
278                            })?;
279
280                        let estimated_middle_size = property_type
281                            .middle_byte_size_ceil(platform_version)?
282                            .ok_or({
283                                Error::Drive(DriveError::CorruptedCodeExecution(
284                                    "document type must have a max size",
285                                ))
286                            })?;
287                        if estimated_middle_size > u8::MAX as u16 {
288                            // this is too big for a key
289                            return Err(Error::Drive(DriveError::CorruptedCodeExecution(
290                                "estimated middle size is too big for a key",
291                            )));
292                        }
293                        Ok(Some(KeySize(KeyInfo::MaxKeySize {
294                            unique_id: document_type
295                                .unique_id_for_document_field(index_level, base_event)
296                                .to_vec(),
297                            max_size: estimated_middle_size as u8,
298                        })))
299                    }
300                }
301            }
302        }
303    }
304
305    fn get_raw_array_elements_for_document_type(
306        &self,
307        key_path: &str,
308        document_type: DocumentTypeRef,
309        size_info_with_base_event: Option<(&IndexLevel, [u8; 32])>,
310        platform_version: &PlatformVersion,
311    ) -> Result<Vec<DriveKeyInfo<'_>>, Error> {
312        if let Some(document) = self.get_borrowed_document() {
313            return Ok(document
314                .get_raw_array_elements_for_document_type(
315                    key_path,
316                    document_type,
317                    platform_version,
318                )?
319                .into_iter()
320                .map(Key)
321                .collect());
322        }
323        // The worst case holds `maxItems` distinct elements, each estimated as
324        // a key of the element's type is (`get_raw_for_document_type` reads a
325        // typed array as its element type), and each a key of its own
326        let (index_level, _) = size_info_with_base_event.ok_or(Error::Drive(
327            DriveError::CorruptedCodeExecution("size_info_with_base_event None but needed"),
328        ))?;
329        let max_items = index_level.indexed_array_max_items().ok_or(Error::Fee(
330            FeeError::DocumentTypeFieldNotFoundForEstimation(format!(
331                "key path [{}] does not reach a level of typed array elements",
332                key_path
333            )),
334        ))?;
335        match self.get_raw_for_document_type(
336            key_path,
337            document_type,
338            None,
339            size_info_with_base_event,
340            platform_version,
341        )? {
342            Some(KeySize(key_info)) => {
343                Ok(distinct_estimated_keys(&key_info, usize::from(max_items)))
344            }
345            _ => Err(Error::Drive(DriveError::CorruptedCodeExecution(
346                "an estimated document estimates its keys",
347            ))),
348        }
349    }
350
351    /// Gets the borrowed document
352    fn get_borrowed_document_and_storage_flags(
353        &self,
354    ) -> Option<(&Document, Option<&StorageFlags>)> {
355        match self {
356            DocumentInfo::DocumentRefAndSerialization((document, _, storage_flags))
357            | DocumentInfo::DocumentRefInfo((document, storage_flags)) => {
358                Some((document, storage_flags.as_ref().map(|flags| flags.as_ref())))
359            }
360            DocumentInfo::DocumentOwnedInfo((document, storage_flags))
361            | DocumentInfo::DocumentAndSerialization((document, _, storage_flags)) => {
362                Some((document, storage_flags.as_ref().map(|flags| flags.as_ref())))
363            }
364            DocumentInfo::DocumentEstimatedAverageSize(_) => None,
365        }
366    }
367
368    /// Gets storage flags
369    fn get_storage_flags_ref(&self) -> Option<&StorageFlags> {
370        match self {
371            DocumentInfo::DocumentRefAndSerialization((_, _, storage_flags))
372            | DocumentInfo::DocumentRefInfo((_, storage_flags))
373            | DocumentInfo::DocumentOwnedInfo((_, storage_flags))
374            | DocumentInfo::DocumentAndSerialization((_, _, storage_flags)) => {
375                storage_flags.as_ref().map(|flags| flags.as_ref())
376            }
377            DocumentInfo::DocumentEstimatedAverageSize(_) => {
378                StorageFlags::optional_default_as_ref()
379            }
380        }
381    }
382
383    /// Gets storage flags
384    fn get_document_id_as_slice(&self) -> Option<&[u8]> {
385        match self {
386            DocumentInfo::DocumentRefAndSerialization((document, _, _))
387            | DocumentInfo::DocumentRefInfo((document, _)) => Some(document.id_ref().as_slice()),
388            DocumentInfo::DocumentOwnedInfo((document, _))
389            | DocumentInfo::DocumentAndSerialization((document, _, _)) => {
390                Some(document.id_ref().as_slice())
391            }
392            DocumentInfo::DocumentEstimatedAverageSize(_) => None,
393        }
394    }
395}
396
397#[cfg(test)]
398mod tests {
399    use super::*;
400    use dpp::document::DocumentV0;
401    use dpp::prelude::Identifier;
402    use std::collections::BTreeMap;
403
404    /// Helper: build a minimal Document (V0) with a given 32-byte id.
405    fn make_document(id_bytes: [u8; 32]) -> Document {
406        Document::V0(DocumentV0 {
407            contract_version: None,
408            id: Identifier::new(id_bytes),
409            owner_id: Identifier::new([0xAA; 32]),
410            properties: BTreeMap::new(),
411            revision: Some(1),
412            created_at: None,
413            updated_at: None,
414            transferred_at: None,
415            created_at_block_height: None,
416            updated_at_block_height: None,
417            transferred_at_block_height: None,
418            created_at_core_block_height: None,
419            updated_at_core_block_height: None,
420            transferred_at_core_block_height: None,
421            creator_id: None,
422            moderated_at: None,
423            moderated_by: None,
424        })
425    }
426
427    // ---------------------------------------------------------------
428    // is_document_and_serialization
429    // ---------------------------------------------------------------
430
431    #[test]
432    fn test_is_document_and_serialization_true_for_ref_and_serialization() {
433        let doc = make_document([1; 32]);
434        let serialized = vec![1, 2, 3];
435        let info = DocumentInfo::DocumentRefAndSerialization((&doc, &serialized, None));
436        assert!(info.is_document_and_serialization());
437    }
438
439    #[test]
440    fn test_is_document_and_serialization_false_for_owned_info() {
441        let doc = make_document([2; 32]);
442        let info = DocumentInfo::DocumentOwnedInfo((doc, None));
443        assert!(!info.is_document_and_serialization());
444    }
445
446    #[test]
447    fn test_is_document_and_serialization_false_for_ref_info() {
448        let doc = make_document([3; 32]);
449        let info = DocumentInfo::DocumentRefInfo((&doc, None));
450        assert!(!info.is_document_and_serialization());
451    }
452
453    #[test]
454    fn test_is_document_and_serialization_false_for_estimated_size() {
455        let info = DocumentInfo::DocumentEstimatedAverageSize(100);
456        assert!(!info.is_document_and_serialization());
457    }
458
459    #[test]
460    fn test_is_document_and_serialization_false_for_document_and_serialization() {
461        let doc = make_document([4; 32]);
462        let info = DocumentInfo::DocumentAndSerialization((doc, vec![9, 8, 7], None));
463        assert!(!info.is_document_and_serialization());
464    }
465
466    // ---------------------------------------------------------------
467    // is_document_size
468    // ---------------------------------------------------------------
469
470    #[test]
471    fn test_is_document_size_true_for_estimated() {
472        let info = DocumentInfo::DocumentEstimatedAverageSize(256);
473        assert!(info.is_document_size());
474    }
475
476    #[test]
477    fn test_is_document_size_false_for_owned_info() {
478        let doc = make_document([5; 32]);
479        let info = DocumentInfo::DocumentOwnedInfo((doc, None));
480        assert!(!info.is_document_size());
481    }
482
483    #[test]
484    fn test_is_document_size_false_for_ref_info() {
485        let doc = make_document([6; 32]);
486        let info = DocumentInfo::DocumentRefInfo((&doc, None));
487        assert!(!info.is_document_size());
488    }
489
490    // ---------------------------------------------------------------
491    // get_borrowed_document
492    // ---------------------------------------------------------------
493
494    #[test]
495    fn test_get_borrowed_document_from_ref_info() {
496        let doc = make_document([10; 32]);
497        let info = DocumentInfo::DocumentRefInfo((&doc, None));
498        let borrowed = info.get_borrowed_document();
499        assert!(borrowed.is_some());
500        assert_eq!(borrowed.unwrap().id_ref().as_slice(), &[10u8; 32]);
501    }
502
503    #[test]
504    fn test_get_borrowed_document_from_ref_and_serialization() {
505        let doc = make_document([11; 32]);
506        let ser = vec![0u8; 5];
507        let info = DocumentInfo::DocumentRefAndSerialization((&doc, &ser, None));
508        let borrowed = info.get_borrowed_document();
509        assert!(borrowed.is_some());
510        assert_eq!(borrowed.unwrap().id_ref().as_slice(), &[11u8; 32]);
511    }
512
513    #[test]
514    fn test_get_borrowed_document_from_owned_info() {
515        let doc = make_document([12; 32]);
516        let info = DocumentInfo::DocumentOwnedInfo((doc, None));
517        let borrowed = info.get_borrowed_document();
518        assert!(borrowed.is_some());
519        assert_eq!(borrowed.unwrap().id_ref().as_slice(), &[12u8; 32]);
520    }
521
522    #[test]
523    fn test_get_borrowed_document_from_document_and_serialization() {
524        let doc = make_document([13; 32]);
525        let info = DocumentInfo::DocumentAndSerialization((doc, vec![1, 2], None));
526        let borrowed = info.get_borrowed_document();
527        assert!(borrowed.is_some());
528        assert_eq!(borrowed.unwrap().id_ref().as_slice(), &[13u8; 32]);
529    }
530
531    #[test]
532    fn test_get_borrowed_document_none_for_estimated() {
533        let info = DocumentInfo::DocumentEstimatedAverageSize(500);
534        assert!(info.get_borrowed_document().is_none());
535    }
536
537    // ---------------------------------------------------------------
538    // id_key_value_info
539    // ---------------------------------------------------------------
540
541    #[test]
542    fn test_id_key_value_info_ref_info_returns_key_ref_request() {
543        let doc = make_document([20; 32]);
544        let info = DocumentInfo::DocumentRefInfo((&doc, None));
545        match info.id_key_value_info() {
546            KeyRefRequest(key) => {
547                assert_eq!(key, &[20u8; 32]);
548            }
549            _ => panic!("expected KeyRefRequest"),
550        }
551    }
552
553    #[test]
554    fn test_id_key_value_info_owned_info_returns_key_ref_request() {
555        let doc = make_document([21; 32]);
556        let info = DocumentInfo::DocumentOwnedInfo((doc, None));
557        match info.id_key_value_info() {
558            KeyRefRequest(key) => {
559                assert_eq!(key, &[21u8; 32]);
560            }
561            _ => panic!("expected KeyRefRequest"),
562        }
563    }
564
565    #[test]
566    fn test_id_key_value_info_estimated_returns_key_value_max_size() {
567        let info = DocumentInfo::DocumentEstimatedAverageSize(999);
568        match info.id_key_value_info() {
569            KeyValueMaxSize((key_size, doc_size)) => {
570                assert_eq!(key_size, 32);
571                assert_eq!(doc_size, 999);
572            }
573            _ => panic!("expected KeyValueMaxSize"),
574        }
575    }
576
577    #[test]
578    fn test_id_key_value_info_ref_and_serialization_returns_key_ref_request() {
579        let doc = make_document([22; 32]);
580        let ser = vec![0u8; 3];
581        let info = DocumentInfo::DocumentRefAndSerialization((&doc, &ser, None));
582        match info.id_key_value_info() {
583            KeyRefRequest(key) => {
584                assert_eq!(key, &[22u8; 32]);
585            }
586            _ => panic!("expected KeyRefRequest"),
587        }
588    }
589
590    #[test]
591    fn test_id_key_value_info_document_and_serialization_returns_key_ref_request() {
592        let doc = make_document([23; 32]);
593        let info = DocumentInfo::DocumentAndSerialization((doc, vec![5, 6, 7], None));
594        match info.id_key_value_info() {
595            KeyRefRequest(key) => {
596                assert_eq!(key, &[23u8; 32]);
597            }
598            _ => panic!("expected KeyRefRequest"),
599        }
600    }
601
602    // ---------------------------------------------------------------
603    // get_estimated_size_for_document_type (system fields)
604    // ---------------------------------------------------------------
605
606    #[test]
607    fn test_estimated_size_for_owner_id() {
608        let info = DocumentInfo::DocumentEstimatedAverageSize(100);
609        // We cannot build a real DocumentTypeRef without a full contract,
610        // but for system fields the document type is not consulted.
611        // The implementation matches on the string key_path first.
612        // We use a "dummy" DocumentTypeRef -- however, DocumentTypeRef requires real data.
613        // Instead, let's verify the system field sizes returned by the function
614        // by checking the match arms directly. Since we can't create a
615        // DocumentTypeRef trivially, we verify the returned sizes are correct
616        // by calling get_estimated_size_for_document_type with a system field.
617        // Unfortunately, DocumentTypeRef is a reference to a real document type,
618        // so we can only test the specific match arms for system fields in a
619        // limited way without creating an entire DataContract. We will
620        // exercise those constant-return paths indirectly through other tests
621        // or verify the constants themselves.
622        //
623        // For now, verify the constants these arms return:
624        assert_eq!(DEFAULT_HASH_SIZE_U16, 32);
625        assert_eq!(U64_SIZE_U16, 8);
626        assert_eq!(U32_SIZE_U16, 4);
627        // These are the values returned for $ownerId/$id, $createdAt/$updatedAt,
628        // and $createdAtCoreBlockHeight etc. respectively.
629        drop(info);
630    }
631
632    // ---------------------------------------------------------------
633    // get_borrowed_document_and_storage_flags
634    // ---------------------------------------------------------------
635
636    #[test]
637    fn test_get_borrowed_document_and_storage_flags_from_ref_info_no_flags() {
638        let doc = make_document([30; 32]);
639        let info = DocumentInfo::DocumentRefInfo((&doc, None));
640        let result = info.get_borrowed_document_and_storage_flags();
641        assert!(result.is_some());
642        let (d, flags) = result.unwrap();
643        assert_eq!(d.id_ref().as_slice(), &[30u8; 32]);
644        assert!(flags.is_none());
645    }
646
647    #[test]
648    fn test_get_borrowed_document_and_storage_flags_from_owned_info_no_flags() {
649        let doc = make_document([31; 32]);
650        let info = DocumentInfo::DocumentOwnedInfo((doc, None));
651        let result = info.get_borrowed_document_and_storage_flags();
652        assert!(result.is_some());
653        let (d, flags) = result.unwrap();
654        assert_eq!(d.id_ref().as_slice(), &[31u8; 32]);
655        assert!(flags.is_none());
656    }
657
658    #[test]
659    fn test_get_borrowed_document_and_storage_flags_none_for_estimated() {
660        let info = DocumentInfo::DocumentEstimatedAverageSize(200);
661        assert!(info.get_borrowed_document_and_storage_flags().is_none());
662    }
663
664    #[test]
665    fn test_get_borrowed_document_and_storage_flags_ref_and_serialization() {
666        let doc = make_document([32; 32]);
667        let ser = vec![7u8; 4];
668        let info = DocumentInfo::DocumentRefAndSerialization((&doc, &ser, None));
669        let result = info.get_borrowed_document_and_storage_flags();
670        assert!(result.is_some());
671        let (d, flags) = result.unwrap();
672        assert_eq!(d.id_ref().as_slice(), &[32u8; 32]);
673        assert!(flags.is_none());
674    }
675
676    #[test]
677    fn test_get_borrowed_document_and_storage_flags_document_and_serialization() {
678        let doc = make_document([33; 32]);
679        let info = DocumentInfo::DocumentAndSerialization((doc, vec![10, 20], None));
680        let result = info.get_borrowed_document_and_storage_flags();
681        assert!(result.is_some());
682        let (d, flags) = result.unwrap();
683        assert_eq!(d.id_ref().as_slice(), &[33u8; 32]);
684        assert!(flags.is_none());
685    }
686
687    // ---------------------------------------------------------------
688    // get_storage_flags_ref
689    // ---------------------------------------------------------------
690
691    #[test]
692    fn test_get_storage_flags_ref_none_without_flags() {
693        let doc = make_document([40; 32]);
694        let info = DocumentInfo::DocumentRefInfo((&doc, None));
695        assert!(info.get_storage_flags_ref().is_none());
696    }
697
698    #[test]
699    fn test_get_storage_flags_ref_none_for_owned_without_flags() {
700        let doc = make_document([41; 32]);
701        let info = DocumentInfo::DocumentOwnedInfo((doc, None));
702        assert!(info.get_storage_flags_ref().is_none());
703    }
704
705    // ---------------------------------------------------------------
706    // get_document_id_as_slice
707    // ---------------------------------------------------------------
708
709    #[test]
710    fn test_get_document_id_as_slice_from_ref_info() {
711        let doc = make_document([50; 32]);
712        let info = DocumentInfo::DocumentRefInfo((&doc, None));
713        assert_eq!(info.get_document_id_as_slice(), Some([50u8; 32].as_slice()));
714    }
715
716    #[test]
717    fn test_get_document_id_as_slice_from_owned_info() {
718        let doc = make_document([51; 32]);
719        let info = DocumentInfo::DocumentOwnedInfo((doc, None));
720        assert_eq!(info.get_document_id_as_slice(), Some([51u8; 32].as_slice()));
721    }
722
723    #[test]
724    fn test_get_document_id_as_slice_from_ref_and_serialization() {
725        let doc = make_document([52; 32]);
726        let ser = vec![0u8; 2];
727        let info = DocumentInfo::DocumentRefAndSerialization((&doc, &ser, None));
728        assert_eq!(info.get_document_id_as_slice(), Some([52u8; 32].as_slice()));
729    }
730
731    #[test]
732    fn test_get_document_id_as_slice_from_document_and_serialization() {
733        let doc = make_document([53; 32]);
734        let info = DocumentInfo::DocumentAndSerialization((doc, vec![3, 4], None));
735        assert_eq!(info.get_document_id_as_slice(), Some([53u8; 32].as_slice()));
736    }
737
738    #[test]
739    fn test_get_document_id_as_slice_none_for_estimated() {
740        let info = DocumentInfo::DocumentEstimatedAverageSize(100);
741        assert!(info.get_document_id_as_slice().is_none());
742    }
743
744    // ---------------------------------------------------------------
745    // Clone behavior
746    // ---------------------------------------------------------------
747
748    #[test]
749    fn test_estimated_average_size_clone_preserves_value() {
750        let info = DocumentInfo::DocumentEstimatedAverageSize(42);
751        let cloned = info.clone();
752        match cloned {
753            DocumentInfo::DocumentEstimatedAverageSize(v) => assert_eq!(v, 42),
754            _ => panic!("clone should preserve variant"),
755        }
756    }
757}