1use crate::error::drive::DriveError;
2use crate::error::fee::FeeError;
3use crate::error::Error;
4use crate::util::object_size_info::DriveKeyInfo::{Key, KeySize};
5use crate::util::object_size_info::KeyValueInfo::{KeyRefRequest, KeyValueMaxSize};
6use crate::util::object_size_info::{DriveKeyInfo, KeyValueInfo};
7use crate::util::storage_flags::StorageFlags;
8use crate::util::type_constants::{
9 DEFAULT_HASH_SIZE_U16, DEFAULT_HASH_SIZE_U8, U32_SIZE_U16, U32_SIZE_U8, U64_SIZE_U16,
10 U64_SIZE_U8,
11};
12use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters;
13use dpp::data_contract::document_type::methods::DocumentTypeBasicMethods;
14use dpp::data_contract::document_type::{DocumentPropertyType, DocumentTypeRef, IndexLevel};
15use dpp::document::document_methods::DocumentMethodsV0;
16use dpp::document::{Document, DocumentV0Getters};
17use dpp::version::PlatformVersion;
18use grovedb::batch::key_info::KeyInfo;
19use std::borrow::Cow;
20
21#[derive(Clone, Debug)]
23pub enum DocumentInfo<'a> {
24 DocumentOwnedInfo((Document, Option<Cow<'a, StorageFlags>>)),
26 DocumentRefInfo((&'a Document, Option<Cow<'a, StorageFlags>>)),
28 DocumentRefAndSerialization((&'a Document, &'a [u8], Option<Cow<'a, StorageFlags>>)),
30 DocumentAndSerialization((Document, Vec<u8>, Option<Cow<'a, StorageFlags>>)),
32 DocumentEstimatedAverageSize(u32),
34}
35
36pub trait DocumentInfoV0Methods {
38 fn is_document_and_serialization(&self) -> bool;
40 fn is_document_size(&self) -> bool;
42 fn get_borrowed_document(&self) -> Option<&Document>;
44 fn id_key_value_info(&self) -> KeyValueInfo<'_>;
46 fn get_estimated_size_for_document_type(
48 &self,
49 key_path: &str,
50 document_type: DocumentTypeRef,
51 platform_version: &PlatformVersion,
52 ) -> Result<u16, Error>;
53 fn get_raw_for_document_type(
55 &self,
56 key_path: &str,
57 document_type: DocumentTypeRef,
58 owner_id: Option<[u8; 32]>,
59 size_info_with_base_event: Option<(&IndexLevel, [u8; 32])>,
60 platform_version: &PlatformVersion,
61 ) -> Result<Option<DriveKeyInfo<'_>>, Error>;
62 fn get_borrowed_document_and_storage_flags(&self)
64 -> Option<(&Document, Option<&StorageFlags>)>;
65 fn get_storage_flags_ref(&self) -> Option<&StorageFlags>;
67 fn get_document_id_as_slice(&self) -> Option<&[u8]>;
69}
70
71fn indexed_property_type<'a>(
75 document_type: &'a DocumentTypeRef<'a>,
76 key_path: &str,
77) -> Option<&'a DocumentPropertyType> {
78 match document_type.flattened_properties().get(key_path) {
79 Some(property) => Some(&property.property_type),
80 None => document_type.derived_index_property_type(key_path),
81 }
82}
83
84impl DocumentInfo<'_> {
85 pub fn without_storage_flags(self) -> Self {
89 match self {
90 DocumentInfo::DocumentOwnedInfo((document, _)) => {
91 DocumentInfo::DocumentOwnedInfo((document, None))
92 }
93 DocumentInfo::DocumentRefInfo((document, _)) => {
94 DocumentInfo::DocumentRefInfo((document, None))
95 }
96 DocumentInfo::DocumentRefAndSerialization((document, serialization, _)) => {
97 DocumentInfo::DocumentRefAndSerialization((document, serialization, None))
98 }
99 DocumentInfo::DocumentAndSerialization((document, serialization, _)) => {
100 DocumentInfo::DocumentAndSerialization((document, serialization, None))
101 }
102 DocumentInfo::DocumentEstimatedAverageSize(size) => {
103 DocumentInfo::DocumentEstimatedAverageSize(size)
104 }
105 }
106 }
107}
108
109impl DocumentInfoV0Methods for DocumentInfo<'_> {
110 fn is_document_and_serialization(&self) -> bool {
112 matches!(self, DocumentInfo::DocumentRefAndSerialization(..))
113 }
114
115 fn is_document_size(&self) -> bool {
117 matches!(self, DocumentInfo::DocumentEstimatedAverageSize(_))
118 }
119
120 fn get_borrowed_document(&self) -> Option<&Document> {
122 match self {
123 DocumentInfo::DocumentRefAndSerialization((document, _, _))
124 | DocumentInfo::DocumentRefInfo((document, _)) => Some(document),
125 DocumentInfo::DocumentOwnedInfo((document, _))
126 | DocumentInfo::DocumentAndSerialization((document, _, _)) => Some(document),
127 DocumentInfo::DocumentEstimatedAverageSize(_) => None,
128 }
129 }
130
131 fn id_key_value_info(&self) -> KeyValueInfo<'_> {
133 match self {
134 DocumentInfo::DocumentRefAndSerialization((document, _, _))
135 | DocumentInfo::DocumentRefInfo((document, _)) => {
136 KeyRefRequest(document.id_ref().as_slice())
137 }
138 DocumentInfo::DocumentOwnedInfo((document, _))
139 | DocumentInfo::DocumentAndSerialization((document, _, _)) => {
140 KeyRefRequest(document.id_ref().as_slice())
141 }
142 DocumentInfo::DocumentEstimatedAverageSize(document_max_size) => {
143 KeyValueMaxSize((32, *document_max_size))
144 }
145 }
146 }
147
148 fn get_estimated_size_for_document_type(
150 &self,
151 key_path: &str,
152 document_type: DocumentTypeRef,
153 platform_version: &PlatformVersion,
154 ) -> Result<u16, Error> {
155 match key_path {
156 "$ownerId" | "$id" | "$creatorId" | "$moderatedBy" => Ok(DEFAULT_HASH_SIZE_U16),
157 "$createdAt" | "$updatedAt" | "$transferredAt" | "$moderatedAt" => Ok(U64_SIZE_U16),
158 "$createdAtBlockHeight" | "$updatedAtBlockHeight" | "$transferredAtBlockHeight" => {
159 Ok(U64_SIZE_U16)
160 }
161 "$createdAtCoreBlockHeight"
162 | "$updatedAtCoreBlockHeight"
163 | "$transferredAtCoreBlockHeight" => Ok(U32_SIZE_U16),
164 key_path => {
165 let property_type = indexed_property_type(&document_type, key_path).ok_or({
166 Error::Fee(FeeError::DocumentTypeFieldNotFoundForEstimation(format!(
167 "incorrect key path [{}] for document type for estimated sizes",
168 key_path
169 )))
170 })?;
171 let estimated_size = property_type
172 .middle_byte_size_ceil(platform_version)?
173 .ok_or({
174 Error::Drive(DriveError::CorruptedCodeExecution(
175 "document type must have a max size",
176 ))
177 })?;
178 Ok(estimated_size)
179 }
180 }
181 }
182
183 fn get_raw_for_document_type(
185 &self,
186 key_path: &str,
187 document_type: DocumentTypeRef,
188 owner_id: Option<[u8; 32]>,
189 size_info_with_base_event: Option<(&IndexLevel, [u8; 32])>,
190 platform_version: &PlatformVersion,
191 ) -> Result<Option<DriveKeyInfo<'_>>, Error> {
192 match self {
193 DocumentInfo::DocumentRefAndSerialization((document, _, _))
194 | DocumentInfo::DocumentRefInfo((document, _)) => {
195 let raw_value = document.get_raw_for_document_type(
196 key_path,
197 document_type,
198 owner_id,
199 platform_version,
200 )?;
201 match raw_value {
202 None => Ok(None),
203 Some(value) => Ok(Some(Key(value))),
204 }
205 }
206 DocumentInfo::DocumentOwnedInfo((document, _))
207 | DocumentInfo::DocumentAndSerialization((document, _, _)) => {
208 let raw_value = document.get_raw_for_document_type(
209 key_path,
210 document_type,
211 owner_id,
212 platform_version,
213 )?;
214 match raw_value {
215 None => Ok(None),
216 Some(value) => Ok(Some(Key(value))),
217 }
218 }
219 DocumentInfo::DocumentEstimatedAverageSize(_) => {
220 let (index_level, base_event) = size_info_with_base_event.ok_or(Error::Drive(
221 DriveError::CorruptedCodeExecution("size_info_with_base_event None but needed"),
222 ))?;
223 match key_path {
224 "$ownerId" | "$id" | "$creatorId" | "$moderatedBy" => {
225 Ok(Some(KeySize(KeyInfo::MaxKeySize {
226 unique_id: document_type
227 .unique_id_for_document_field(index_level, base_event)
228 .to_vec(),
229 max_size: DEFAULT_HASH_SIZE_U8,
230 })))
231 }
232 "$createdAt" | "$updatedAt" | "$transferredAt" | "$moderatedAt" => {
233 Ok(Some(KeySize(KeyInfo::MaxKeySize {
234 unique_id: document_type
235 .unique_id_for_document_field(index_level, base_event)
236 .to_vec(),
237 max_size: U64_SIZE_U8,
238 })))
239 }
240 "$createdAtBlockHeight"
241 | "$updatedAtBlockHeight"
242 | "$transferredAtBlockHeight" => Ok(Some(KeySize(KeyInfo::MaxKeySize {
243 unique_id: document_type
244 .unique_id_for_document_field(index_level, base_event)
245 .to_vec(),
246 max_size: U64_SIZE_U8,
247 }))),
248 "$createdAtCoreBlockHeight"
249 | "$updatedAtCoreBlockHeight"
250 | "$transferredAtCoreBlockHeight" => Ok(Some(KeySize(KeyInfo::MaxKeySize {
251 unique_id: document_type
252 .unique_id_for_document_field(index_level, base_event)
253 .to_vec(),
254 max_size: U32_SIZE_U8,
255 }))),
256 key_path => {
257 let property_type = indexed_property_type(&document_type, key_path)
258 .ok_or({
259 Error::Fee(FeeError::DocumentTypeFieldNotFoundForEstimation(
260 format!("incorrect key path [{}] for document type for get_raw_for_document_type", key_path)
261 ))
262 })?;
263
264 let estimated_middle_size = property_type
265 .middle_byte_size_ceil(platform_version)?
266 .ok_or({
267 Error::Drive(DriveError::CorruptedCodeExecution(
268 "document type must have a max size",
269 ))
270 })?;
271 if estimated_middle_size > u8::MAX as u16 {
272 return Err(Error::Drive(DriveError::CorruptedCodeExecution(
274 "estimated middle size is too big for a key",
275 )));
276 }
277 Ok(Some(KeySize(KeyInfo::MaxKeySize {
278 unique_id: document_type
279 .unique_id_for_document_field(index_level, base_event)
280 .to_vec(),
281 max_size: estimated_middle_size as u8,
282 })))
283 }
284 }
285 }
286 }
287 }
288
289 fn get_borrowed_document_and_storage_flags(
291 &self,
292 ) -> Option<(&Document, Option<&StorageFlags>)> {
293 match self {
294 DocumentInfo::DocumentRefAndSerialization((document, _, storage_flags))
295 | DocumentInfo::DocumentRefInfo((document, storage_flags)) => {
296 Some((document, storage_flags.as_ref().map(|flags| flags.as_ref())))
297 }
298 DocumentInfo::DocumentOwnedInfo((document, storage_flags))
299 | DocumentInfo::DocumentAndSerialization((document, _, storage_flags)) => {
300 Some((document, storage_flags.as_ref().map(|flags| flags.as_ref())))
301 }
302 DocumentInfo::DocumentEstimatedAverageSize(_) => None,
303 }
304 }
305
306 fn get_storage_flags_ref(&self) -> Option<&StorageFlags> {
308 match self {
309 DocumentInfo::DocumentRefAndSerialization((_, _, storage_flags))
310 | DocumentInfo::DocumentRefInfo((_, storage_flags))
311 | DocumentInfo::DocumentOwnedInfo((_, storage_flags))
312 | DocumentInfo::DocumentAndSerialization((_, _, storage_flags)) => {
313 storage_flags.as_ref().map(|flags| flags.as_ref())
314 }
315 DocumentInfo::DocumentEstimatedAverageSize(_) => {
316 StorageFlags::optional_default_as_ref()
317 }
318 }
319 }
320
321 fn get_document_id_as_slice(&self) -> Option<&[u8]> {
323 match self {
324 DocumentInfo::DocumentRefAndSerialization((document, _, _))
325 | DocumentInfo::DocumentRefInfo((document, _)) => Some(document.id_ref().as_slice()),
326 DocumentInfo::DocumentOwnedInfo((document, _))
327 | DocumentInfo::DocumentAndSerialization((document, _, _)) => {
328 Some(document.id_ref().as_slice())
329 }
330 DocumentInfo::DocumentEstimatedAverageSize(_) => None,
331 }
332 }
333}
334
335#[cfg(test)]
336mod tests {
337 use super::*;
338 use dpp::document::DocumentV0;
339 use dpp::prelude::Identifier;
340 use std::collections::BTreeMap;
341
342 fn make_document(id_bytes: [u8; 32]) -> Document {
344 Document::V0(DocumentV0 {
345 contract_version: None,
346 id: Identifier::new(id_bytes),
347 owner_id: Identifier::new([0xAA; 32]),
348 properties: BTreeMap::new(),
349 revision: Some(1),
350 created_at: None,
351 updated_at: None,
352 transferred_at: None,
353 created_at_block_height: None,
354 updated_at_block_height: None,
355 transferred_at_block_height: None,
356 created_at_core_block_height: None,
357 updated_at_core_block_height: None,
358 transferred_at_core_block_height: None,
359 creator_id: None,
360 moderated_at: None,
361 moderated_by: None,
362 })
363 }
364
365 #[test]
370 fn test_is_document_and_serialization_true_for_ref_and_serialization() {
371 let doc = make_document([1; 32]);
372 let serialized = vec![1, 2, 3];
373 let info = DocumentInfo::DocumentRefAndSerialization((&doc, &serialized, None));
374 assert!(info.is_document_and_serialization());
375 }
376
377 #[test]
378 fn test_is_document_and_serialization_false_for_owned_info() {
379 let doc = make_document([2; 32]);
380 let info = DocumentInfo::DocumentOwnedInfo((doc, None));
381 assert!(!info.is_document_and_serialization());
382 }
383
384 #[test]
385 fn test_is_document_and_serialization_false_for_ref_info() {
386 let doc = make_document([3; 32]);
387 let info = DocumentInfo::DocumentRefInfo((&doc, None));
388 assert!(!info.is_document_and_serialization());
389 }
390
391 #[test]
392 fn test_is_document_and_serialization_false_for_estimated_size() {
393 let info = DocumentInfo::DocumentEstimatedAverageSize(100);
394 assert!(!info.is_document_and_serialization());
395 }
396
397 #[test]
398 fn test_is_document_and_serialization_false_for_document_and_serialization() {
399 let doc = make_document([4; 32]);
400 let info = DocumentInfo::DocumentAndSerialization((doc, vec![9, 8, 7], None));
401 assert!(!info.is_document_and_serialization());
402 }
403
404 #[test]
409 fn test_is_document_size_true_for_estimated() {
410 let info = DocumentInfo::DocumentEstimatedAverageSize(256);
411 assert!(info.is_document_size());
412 }
413
414 #[test]
415 fn test_is_document_size_false_for_owned_info() {
416 let doc = make_document([5; 32]);
417 let info = DocumentInfo::DocumentOwnedInfo((doc, None));
418 assert!(!info.is_document_size());
419 }
420
421 #[test]
422 fn test_is_document_size_false_for_ref_info() {
423 let doc = make_document([6; 32]);
424 let info = DocumentInfo::DocumentRefInfo((&doc, None));
425 assert!(!info.is_document_size());
426 }
427
428 #[test]
433 fn test_get_borrowed_document_from_ref_info() {
434 let doc = make_document([10; 32]);
435 let info = DocumentInfo::DocumentRefInfo((&doc, None));
436 let borrowed = info.get_borrowed_document();
437 assert!(borrowed.is_some());
438 assert_eq!(borrowed.unwrap().id_ref().as_slice(), &[10u8; 32]);
439 }
440
441 #[test]
442 fn test_get_borrowed_document_from_ref_and_serialization() {
443 let doc = make_document([11; 32]);
444 let ser = vec![0u8; 5];
445 let info = DocumentInfo::DocumentRefAndSerialization((&doc, &ser, None));
446 let borrowed = info.get_borrowed_document();
447 assert!(borrowed.is_some());
448 assert_eq!(borrowed.unwrap().id_ref().as_slice(), &[11u8; 32]);
449 }
450
451 #[test]
452 fn test_get_borrowed_document_from_owned_info() {
453 let doc = make_document([12; 32]);
454 let info = DocumentInfo::DocumentOwnedInfo((doc, None));
455 let borrowed = info.get_borrowed_document();
456 assert!(borrowed.is_some());
457 assert_eq!(borrowed.unwrap().id_ref().as_slice(), &[12u8; 32]);
458 }
459
460 #[test]
461 fn test_get_borrowed_document_from_document_and_serialization() {
462 let doc = make_document([13; 32]);
463 let info = DocumentInfo::DocumentAndSerialization((doc, vec![1, 2], None));
464 let borrowed = info.get_borrowed_document();
465 assert!(borrowed.is_some());
466 assert_eq!(borrowed.unwrap().id_ref().as_slice(), &[13u8; 32]);
467 }
468
469 #[test]
470 fn test_get_borrowed_document_none_for_estimated() {
471 let info = DocumentInfo::DocumentEstimatedAverageSize(500);
472 assert!(info.get_borrowed_document().is_none());
473 }
474
475 #[test]
480 fn test_id_key_value_info_ref_info_returns_key_ref_request() {
481 let doc = make_document([20; 32]);
482 let info = DocumentInfo::DocumentRefInfo((&doc, None));
483 match info.id_key_value_info() {
484 KeyRefRequest(key) => {
485 assert_eq!(key, &[20u8; 32]);
486 }
487 _ => panic!("expected KeyRefRequest"),
488 }
489 }
490
491 #[test]
492 fn test_id_key_value_info_owned_info_returns_key_ref_request() {
493 let doc = make_document([21; 32]);
494 let info = DocumentInfo::DocumentOwnedInfo((doc, None));
495 match info.id_key_value_info() {
496 KeyRefRequest(key) => {
497 assert_eq!(key, &[21u8; 32]);
498 }
499 _ => panic!("expected KeyRefRequest"),
500 }
501 }
502
503 #[test]
504 fn test_id_key_value_info_estimated_returns_key_value_max_size() {
505 let info = DocumentInfo::DocumentEstimatedAverageSize(999);
506 match info.id_key_value_info() {
507 KeyValueMaxSize((key_size, doc_size)) => {
508 assert_eq!(key_size, 32);
509 assert_eq!(doc_size, 999);
510 }
511 _ => panic!("expected KeyValueMaxSize"),
512 }
513 }
514
515 #[test]
516 fn test_id_key_value_info_ref_and_serialization_returns_key_ref_request() {
517 let doc = make_document([22; 32]);
518 let ser = vec![0u8; 3];
519 let info = DocumentInfo::DocumentRefAndSerialization((&doc, &ser, None));
520 match info.id_key_value_info() {
521 KeyRefRequest(key) => {
522 assert_eq!(key, &[22u8; 32]);
523 }
524 _ => panic!("expected KeyRefRequest"),
525 }
526 }
527
528 #[test]
529 fn test_id_key_value_info_document_and_serialization_returns_key_ref_request() {
530 let doc = make_document([23; 32]);
531 let info = DocumentInfo::DocumentAndSerialization((doc, vec![5, 6, 7], None));
532 match info.id_key_value_info() {
533 KeyRefRequest(key) => {
534 assert_eq!(key, &[23u8; 32]);
535 }
536 _ => panic!("expected KeyRefRequest"),
537 }
538 }
539
540 #[test]
545 fn test_estimated_size_for_owner_id() {
546 let info = DocumentInfo::DocumentEstimatedAverageSize(100);
547 assert_eq!(DEFAULT_HASH_SIZE_U16, 32);
563 assert_eq!(U64_SIZE_U16, 8);
564 assert_eq!(U32_SIZE_U16, 4);
565 drop(info);
568 }
569
570 #[test]
575 fn test_get_borrowed_document_and_storage_flags_from_ref_info_no_flags() {
576 let doc = make_document([30; 32]);
577 let info = DocumentInfo::DocumentRefInfo((&doc, None));
578 let result = info.get_borrowed_document_and_storage_flags();
579 assert!(result.is_some());
580 let (d, flags) = result.unwrap();
581 assert_eq!(d.id_ref().as_slice(), &[30u8; 32]);
582 assert!(flags.is_none());
583 }
584
585 #[test]
586 fn test_get_borrowed_document_and_storage_flags_from_owned_info_no_flags() {
587 let doc = make_document([31; 32]);
588 let info = DocumentInfo::DocumentOwnedInfo((doc, None));
589 let result = info.get_borrowed_document_and_storage_flags();
590 assert!(result.is_some());
591 let (d, flags) = result.unwrap();
592 assert_eq!(d.id_ref().as_slice(), &[31u8; 32]);
593 assert!(flags.is_none());
594 }
595
596 #[test]
597 fn test_get_borrowed_document_and_storage_flags_none_for_estimated() {
598 let info = DocumentInfo::DocumentEstimatedAverageSize(200);
599 assert!(info.get_borrowed_document_and_storage_flags().is_none());
600 }
601
602 #[test]
603 fn test_get_borrowed_document_and_storage_flags_ref_and_serialization() {
604 let doc = make_document([32; 32]);
605 let ser = vec![7u8; 4];
606 let info = DocumentInfo::DocumentRefAndSerialization((&doc, &ser, None));
607 let result = info.get_borrowed_document_and_storage_flags();
608 assert!(result.is_some());
609 let (d, flags) = result.unwrap();
610 assert_eq!(d.id_ref().as_slice(), &[32u8; 32]);
611 assert!(flags.is_none());
612 }
613
614 #[test]
615 fn test_get_borrowed_document_and_storage_flags_document_and_serialization() {
616 let doc = make_document([33; 32]);
617 let info = DocumentInfo::DocumentAndSerialization((doc, vec![10, 20], None));
618 let result = info.get_borrowed_document_and_storage_flags();
619 assert!(result.is_some());
620 let (d, flags) = result.unwrap();
621 assert_eq!(d.id_ref().as_slice(), &[33u8; 32]);
622 assert!(flags.is_none());
623 }
624
625 #[test]
630 fn test_get_storage_flags_ref_none_without_flags() {
631 let doc = make_document([40; 32]);
632 let info = DocumentInfo::DocumentRefInfo((&doc, None));
633 assert!(info.get_storage_flags_ref().is_none());
634 }
635
636 #[test]
637 fn test_get_storage_flags_ref_none_for_owned_without_flags() {
638 let doc = make_document([41; 32]);
639 let info = DocumentInfo::DocumentOwnedInfo((doc, None));
640 assert!(info.get_storage_flags_ref().is_none());
641 }
642
643 #[test]
648 fn test_get_document_id_as_slice_from_ref_info() {
649 let doc = make_document([50; 32]);
650 let info = DocumentInfo::DocumentRefInfo((&doc, None));
651 assert_eq!(info.get_document_id_as_slice(), Some([50u8; 32].as_slice()));
652 }
653
654 #[test]
655 fn test_get_document_id_as_slice_from_owned_info() {
656 let doc = make_document([51; 32]);
657 let info = DocumentInfo::DocumentOwnedInfo((doc, None));
658 assert_eq!(info.get_document_id_as_slice(), Some([51u8; 32].as_slice()));
659 }
660
661 #[test]
662 fn test_get_document_id_as_slice_from_ref_and_serialization() {
663 let doc = make_document([52; 32]);
664 let ser = vec![0u8; 2];
665 let info = DocumentInfo::DocumentRefAndSerialization((&doc, &ser, None));
666 assert_eq!(info.get_document_id_as_slice(), Some([52u8; 32].as_slice()));
667 }
668
669 #[test]
670 fn test_get_document_id_as_slice_from_document_and_serialization() {
671 let doc = make_document([53; 32]);
672 let info = DocumentInfo::DocumentAndSerialization((doc, vec![3, 4], None));
673 assert_eq!(info.get_document_id_as_slice(), Some([53u8; 32].as_slice()));
674 }
675
676 #[test]
677 fn test_get_document_id_as_slice_none_for_estimated() {
678 let info = DocumentInfo::DocumentEstimatedAverageSize(100);
679 assert!(info.get_document_id_as_slice().is_none());
680 }
681
682 #[test]
687 fn test_estimated_average_size_clone_preserves_value() {
688 let info = DocumentInfo::DocumentEstimatedAverageSize(42);
689 let cloned = info.clone();
690 match cloned {
691 DocumentInfo::DocumentEstimatedAverageSize(v) => assert_eq!(v, 42),
692 _ => panic!("clone should preserve variant"),
693 }
694 }
695}