1use crate::error::{Error, Result};
18use bytes::{BufMut, Bytes, BytesMut};
19use chrono::{DateTime, NaiveDateTime};
20use std::convert::TryFrom;
21use std::time::Duration;
22
23pub const METADATA_LENGTH_SIZE: usize = 4;
25
26const NUMBER_SIZE: usize = 4;
28
29const OFFSET_SIZE: usize = 8;
31
32const LENGTH_SIZE: usize = 8;
34
35const DIGEST_LENGTH_SIZE: usize = 4;
37
38const PARENT_ID_LENGTH_SIZE: usize = 4;
40
41const TRAFFIC_TYPE_SIZE: usize = 1;
43
44const COST_SIZE: usize = 8;
46
47const CREATED_AT_SIZE: usize = 8;
49
50#[derive(Debug, Clone)]
74pub struct PersistentCachePieceContent {
75 metadata_length: u32,
76 metadata: PersistentCachePieceMetadata,
77}
78
79impl PersistentCachePieceContent {
81 #[allow(clippy::too_many_arguments)]
83 pub fn new(
84 number: u32,
85 offset: u64,
86 length: u64,
87 digest: String,
88 parent_id: String,
89 traffic_type: u8,
90 cost: Duration,
91 created_at: NaiveDateTime,
92 ) -> Self {
93 Self {
94 metadata_length: (NUMBER_SIZE
95 + OFFSET_SIZE
96 + LENGTH_SIZE
97 + DIGEST_LENGTH_SIZE
98 + digest.len()
99 + PARENT_ID_LENGTH_SIZE
100 + parent_id.len()
101 + TRAFFIC_TYPE_SIZE
102 + COST_SIZE
103 + CREATED_AT_SIZE) as u32,
104 metadata: PersistentCachePieceMetadata {
105 number,
106 offset,
107 length,
108 digest,
109 parent_id,
110 traffic_type,
111 cost,
112 created_at,
113 },
114 }
115 }
116
117 pub fn metadata(&self) -> PersistentCachePieceMetadata {
119 self.metadata.clone()
120 }
121
122 pub fn metadata_len(&self) -> u32 {
124 self.metadata_length
125 }
126
127 pub fn is_empty(&self) -> bool {
129 self.metadata.length == 0
130 }
131}
132
133impl TryFrom<Bytes> for PersistentCachePieceContent {
135 type Error = Error;
136
137 fn try_from(bytes: Bytes) -> Result<Self> {
139 let metadata_length = u32::from_be_bytes(
140 bytes
141 .get(..METADATA_LENGTH_SIZE)
142 .ok_or(Error::InvalidPacket(
143 "insufficient bytes for metadata length".to_string(),
144 ))?
145 .try_into()?,
146 );
147
148 if bytes.len() != METADATA_LENGTH_SIZE + metadata_length as usize {
149 return Err(Error::InvalidPacket(format!(
150 "expected {} bytes for PersistentCachePieceContent, got {}",
151 METADATA_LENGTH_SIZE + metadata_length as usize,
152 bytes.len()
153 )));
154 }
155
156 let metadata = (
157 bytes.slice(METADATA_LENGTH_SIZE..METADATA_LENGTH_SIZE + metadata_length as usize),
158 metadata_length,
159 )
160 .try_into()?;
161
162 Ok(PersistentCachePieceContent {
163 metadata_length,
164 metadata,
165 })
166 }
167}
168
169impl From<PersistentCachePieceContent> for Bytes {
171 fn from(content: PersistentCachePieceContent) -> Bytes {
173 let (metadata_bytes, metadata_length) = content.metadata.into();
174 let mut bytes = BytesMut::with_capacity(METADATA_LENGTH_SIZE + metadata_length as usize);
175 bytes.put_u32(metadata_length);
176 bytes.extend_from_slice(&metadata_bytes);
177 bytes.freeze()
178 }
179}
180
181#[derive(Debug, Clone)]
183pub struct PersistentCachePieceMetadata {
184 pub number: u32,
185 pub offset: u64,
186 pub length: u64,
187 pub digest: String,
188 pub parent_id: String,
189 pub traffic_type: u8,
190 pub cost: Duration,
191 pub created_at: NaiveDateTime,
192}
193
194impl PersistentCachePieceMetadata {
196 #[allow(clippy::too_many_arguments)]
198 pub fn new(
199 number: u32,
200 offset: u64,
201 length: u64,
202 digest: String,
203 parent_id: String,
204 traffic_type: u8,
205 cost: Duration,
206 created_at: NaiveDateTime,
207 ) -> Self {
208 Self {
209 number,
210 offset,
211 length,
212 digest,
213 parent_id,
214 traffic_type,
215 cost,
216 created_at,
217 }
218 }
219}
220
221impl TryFrom<(Bytes, u32)> for PersistentCachePieceMetadata {
223 type Error = Error;
224
225 fn try_from(input: (Bytes, u32)) -> Result<Self> {
227 let (bytes, length) = input;
228 if bytes.len() != length as usize {
229 return Err(Error::InvalidLength(format!(
230 "expected {} bytes for PersistentCachePieceMetadata, got {}",
231 length,
232 bytes.len()
233 )));
234 }
235
236 let mut bytes_offset = 0;
237 let number = u32::from_be_bytes(
238 bytes
239 .get(bytes_offset..bytes_offset + NUMBER_SIZE)
240 .ok_or(Error::InvalidPacket(
241 "insufficient bytes for piece number".to_string(),
242 ))?
243 .try_into()?,
244 );
245 bytes_offset += NUMBER_SIZE;
246
247 let offset = u64::from_be_bytes(
248 bytes
249 .get(bytes_offset..bytes_offset + OFFSET_SIZE)
250 .ok_or(Error::InvalidPacket(
251 "insufficient bytes for piece offset".to_string(),
252 ))?
253 .try_into()?,
254 );
255 bytes_offset += OFFSET_SIZE;
256
257 let length = u64::from_be_bytes(
258 bytes
259 .get(bytes_offset..bytes_offset + LENGTH_SIZE)
260 .ok_or(Error::InvalidPacket(
261 "insufficient bytes for piece length".to_string(),
262 ))?
263 .try_into()?,
264 );
265
266 bytes_offset += LENGTH_SIZE;
267
268 let digest_length = u32::from_be_bytes(
269 bytes
270 .get(bytes_offset..bytes_offset + DIGEST_LENGTH_SIZE)
271 .ok_or(Error::InvalidPacket(
272 "insufficient bytes for digest length".to_string(),
273 ))?
274 .try_into()?,
275 ) as usize;
276 bytes_offset += DIGEST_LENGTH_SIZE;
277
278 let digest = String::from_utf8(
279 bytes
280 .get(bytes_offset..bytes_offset + digest_length)
281 .ok_or(Error::InvalidPacket(
282 "insufficient bytes for digest length".to_string(),
283 ))?
284 .to_vec(),
285 )?;
286 bytes_offset += digest_length;
287
288 let parent_id_length = u32::from_be_bytes(
289 bytes
290 .get(bytes_offset..bytes_offset + PARENT_ID_LENGTH_SIZE)
291 .ok_or(Error::InvalidPacket(
292 "insufficient bytes for parent id length".to_string(),
293 ))?
294 .try_into()?,
295 ) as usize;
296 bytes_offset += PARENT_ID_LENGTH_SIZE;
297
298 let parent_id = String::from_utf8(
299 bytes
300 .get(bytes_offset..bytes_offset + parent_id_length)
301 .ok_or(Error::InvalidPacket(
302 "insufficient bytes for parent id".to_string(),
303 ))?
304 .to_vec(),
305 )?;
306 bytes_offset += parent_id_length;
307
308 let traffic_type = bytes
309 .get(bytes_offset)
310 .ok_or(Error::InvalidPacket(
311 "insufficient bytes for traffic type".to_string(),
312 ))?
313 .to_owned();
314 bytes_offset += TRAFFIC_TYPE_SIZE;
315
316 let cost = Duration::from_secs(u64::from_be_bytes(
317 bytes
318 .get(bytes_offset..bytes_offset + COST_SIZE)
319 .ok_or(Error::InvalidPacket(
320 "insufficient bytes for cost".to_string(),
321 ))?
322 .try_into()?,
323 ));
324 bytes_offset += COST_SIZE;
325
326 let created_at = DateTime::from_timestamp(
327 i64::from_be_bytes(
328 bytes
329 .get(bytes_offset..bytes_offset + CREATED_AT_SIZE)
330 .ok_or(Error::InvalidPacket(
331 "insufficient bytes for created_at".to_string(),
332 ))?
333 .try_into()?,
334 ),
335 0,
336 )
337 .ok_or_else(|| Error::InvalidPacket("invalid timestamp for created_at".to_string()))?
338 .naive_utc();
339 Ok(PersistentCachePieceMetadata {
340 number,
341 offset,
342 length,
343 digest,
344 parent_id,
345 traffic_type,
346 cost,
347 created_at,
348 })
349 }
350}
351
352impl From<PersistentCachePieceMetadata> for (Bytes, u32) {
354 fn from(metadata: PersistentCachePieceMetadata) -> (Bytes, u32) {
356 let PersistentCachePieceMetadata {
357 number,
358 offset,
359 length,
360 digest,
361 parent_id,
362 traffic_type,
363 cost,
364 created_at,
365 } = metadata;
366
367 let parent_id = parent_id.as_bytes();
368 let bytes_length = NUMBER_SIZE
369 + OFFSET_SIZE
370 + LENGTH_SIZE
371 + DIGEST_LENGTH_SIZE
372 + digest.len()
373 + PARENT_ID_LENGTH_SIZE
374 + parent_id.len()
375 + TRAFFIC_TYPE_SIZE
376 + COST_SIZE
377 + CREATED_AT_SIZE;
378
379 let mut bytes = BytesMut::with_capacity(bytes_length);
380 bytes.put_u32(number);
381 bytes.put_u64(offset);
382 bytes.put_u64(length);
383 bytes.put_u32(digest.len() as u32);
384 bytes.extend_from_slice(digest.as_bytes());
385 bytes.put_u32(parent_id.len() as u32);
386 bytes.extend_from_slice(parent_id);
387 bytes.put_u8(traffic_type);
388 bytes.put_u64(cost.as_secs());
389 bytes.put_i64(created_at.and_utc().timestamp());
390 (bytes.freeze(), bytes_length as u32)
391 }
392}
393
394#[cfg(test)]
395mod tests {
396 use super::*;
397 use bytes::Bytes;
398 use std::time::Duration;
399
400 fn create_test_persistent_cache_piece_content() -> PersistentCachePieceContent {
401 PersistentCachePieceContent::new(
402 42,
403 1024,
404 2048,
405 "a".repeat(32),
406 "test_parent_id".to_string(),
407 1,
408 Duration::from_secs(5),
409 DateTime::from_timestamp(1693152000, 0).unwrap().naive_utc(),
410 )
411 }
412
413 fn create_test_metadata() -> PersistentCachePieceMetadata {
414 PersistentCachePieceMetadata {
415 number: 42,
416 offset: 1024,
417 length: 2048,
418 digest: "a".repeat(32),
419 parent_id: "test_parent_id".to_string(),
420 traffic_type: 1,
421 cost: Duration::from_secs(5),
422 created_at: DateTime::from_timestamp(1693152000, 0).unwrap().naive_utc(),
423 }
424 }
425
426 #[test]
427 fn test_persistent_cache_piece_content_conversion_roundtrip() {
428 let original = create_test_persistent_cache_piece_content();
429 let bytes = Bytes::from(original.clone());
430 let result = PersistentCachePieceContent::try_from(bytes).unwrap();
431
432 assert_eq!(result.metadata().number, original.metadata().number);
433 assert_eq!(result.metadata().offset, original.metadata().offset);
434 assert_eq!(result.metadata().length, original.metadata().length);
435 assert_eq!(result.metadata().digest, original.metadata().digest);
436 assert_eq!(result.metadata().parent_id, original.metadata().parent_id);
437 assert_eq!(
438 result.metadata().traffic_type,
439 original.metadata().traffic_type
440 );
441 assert_eq!(result.metadata().cost, original.metadata().cost);
442 assert_eq!(result.metadata().created_at, original.metadata().created_at);
443 assert_eq!(result.metadata_len(), original.metadata_len());
444 }
445
446 #[test]
447 fn test_persistent_cache_piece_content_try_from_insufficient_bytes_for_metadata_length() {
448 let short_bytes = Bytes::from(vec![0u8; 4]); let result = PersistentCachePieceContent::try_from(short_bytes);
450
451 assert!(result.is_err());
452 assert!(matches!(result.unwrap_err(), Error::InvalidPacket(_)));
453 }
454
455 #[test]
456 fn test_persistent_cache_piece_content_try_from_insufficient_metadata_bytes() {
457 let mut bytes = BytesMut::new();
458 bytes.put_u32(100); bytes.put(&vec![0u8; 50][..]); let result = PersistentCachePieceContent::try_from(bytes.freeze());
462 assert!(result.is_err());
463 assert!(matches!(result.unwrap_err(), Error::InvalidPacket(_)));
464 }
465
466 #[test]
467 fn test_persistent_cache_piece_content_with_empty_parent_id() {
468 let persistent_cache_piece_content = PersistentCachePieceContent::new(
469 1,
470 0,
471 100,
472 "b".repeat(32),
473 String::new(), 2,
475 Duration::from_secs(1),
476 DateTime::from_timestamp(1693152000, 0).unwrap().naive_utc(),
477 );
478
479 let bytes = Bytes::from(persistent_cache_piece_content.clone());
480 let result = PersistentCachePieceContent::try_from(bytes).unwrap();
481
482 assert_eq!(result.metadata().parent_id, "");
483 assert_eq!(result.metadata().number, 1);
484 assert_eq!(result.metadata().traffic_type, 2);
485 assert_eq!(result.metadata().length, 100);
486 }
487
488 #[test]
489 fn test_persistent_cache_piece_content_with_long_parent_id() {
490 let long_parent_id = "x".repeat(1000);
491 let persistent_cache_piece_content = PersistentCachePieceContent::new(
492 999,
493 12345,
494 67890,
495 "c".repeat(32),
496 long_parent_id.clone(),
497 255,
498 Duration::from_secs(3600),
499 DateTime::from_timestamp(1693152000, 0).unwrap().naive_utc(),
500 );
501
502 let bytes = Bytes::from(persistent_cache_piece_content.clone());
503 let result = PersistentCachePieceContent::try_from(bytes).unwrap();
504
505 assert_eq!(result.metadata().parent_id, long_parent_id);
506 assert_eq!(result.metadata().number, 999);
507 assert_eq!(result.metadata().traffic_type, 255);
508 assert_eq!(result.metadata().length, 67890);
509 }
510
511 #[test]
512 fn test_persistent_cache_piece_content_with_zero_values() {
513 let persistent_cache_piece_content = PersistentCachePieceContent::new(
514 0,
515 0,
516 0,
517 "d".repeat(32),
518 "zero_test".to_string(),
519 0,
520 Duration::from_secs(0),
521 DateTime::from_timestamp(1693152000, 0).unwrap().naive_utc(),
522 );
523
524 let bytes = Bytes::from(persistent_cache_piece_content.clone());
525 let result = PersistentCachePieceContent::try_from(bytes).unwrap();
526
527 assert_eq!(result.metadata().number, 0);
528 assert_eq!(result.metadata().offset, 0);
529 assert_eq!(result.metadata().length, 0);
530 assert_eq!(result.metadata().traffic_type, 0);
531 assert_eq!(result.metadata().cost, Duration::from_secs(0));
532 assert!(result.is_empty());
533 }
534
535 #[test]
536 fn test_persistent_cache_piece_content_with_max_values() {
537 let persistent_cache_piece_content = PersistentCachePieceContent::new(
538 u32::MAX,
539 u64::MAX,
540 u64::MAX,
541 "e".repeat(32),
542 "max_values_test".to_string(),
543 u8::MAX,
544 Duration::from_secs(u64::MAX),
545 DateTime::from_timestamp(1693152000, 0).unwrap().naive_utc(),
546 );
547
548 let bytes = Bytes::from(persistent_cache_piece_content.clone());
549 let result = PersistentCachePieceContent::try_from(bytes).unwrap();
550
551 assert_eq!(result.metadata().number, u32::MAX);
552 assert_eq!(result.metadata().offset, u64::MAX);
553 assert_eq!(result.metadata().length, u64::MAX);
554 assert_eq!(result.metadata().traffic_type, u8::MAX);
555 assert_eq!(result.metadata().cost, Duration::from_secs(u64::MAX));
556 }
557
558 #[test]
559 fn test_persistent_cache_piece_content_metadata_length_calculation() {
560 let persistent_cache_piece_content = PersistentCachePieceContent::new(
561 123,
562 456,
563 789,
564 "f".repeat(32),
565 "length_test".to_string(),
566 42,
567 Duration::from_secs(100),
568 DateTime::from_timestamp(1693152000, 0).unwrap().naive_utc(),
569 );
570
571 let expected_length = (NUMBER_SIZE
572 + OFFSET_SIZE
573 + LENGTH_SIZE
574 + DIGEST_LENGTH_SIZE
575 + 32 + PARENT_ID_LENGTH_SIZE
577 + "length_test".len()
578 + TRAFFIC_TYPE_SIZE
579 + COST_SIZE
580 + CREATED_AT_SIZE) as u32;
581
582 assert_eq!(
583 persistent_cache_piece_content.metadata_len(),
584 expected_length
585 );
586
587 let bytes = Bytes::from(persistent_cache_piece_content.clone());
588 let result = PersistentCachePieceContent::try_from(bytes).unwrap();
589 assert_eq!(result.metadata_len(), expected_length);
590 }
591
592 #[test]
593 fn test_persistent_cache_piece_content_with_short_digest() {
594 let persistent_cache_piece_content = PersistentCachePieceContent::new(
595 1,
596 0,
597 100,
598 "short".to_string(), "test".to_string(),
600 1,
601 Duration::from_secs(1),
602 DateTime::from_timestamp(1693152000, 0).unwrap().naive_utc(),
603 );
604
605 let bytes = Bytes::from(persistent_cache_piece_content.clone());
606 let result = PersistentCachePieceContent::try_from(bytes).unwrap();
607
608 assert_eq!(result.metadata().digest, "short");
609 assert_eq!(result.metadata().number, 1);
610 }
611
612 #[test]
613 fn test_persistent_cache_piece_content_with_long_digest() {
614 let long_digest = "g".repeat(128); let persistent_cache_piece_content = PersistentCachePieceContent::new(
616 5,
617 1000,
618 2000,
619 long_digest.clone(),
620 "digest_test".to_string(),
621 10,
622 Duration::from_secs(50),
623 DateTime::from_timestamp(1693152000, 0).unwrap().naive_utc(),
624 );
625
626 let bytes = Bytes::from(persistent_cache_piece_content.clone());
627 let result = PersistentCachePieceContent::try_from(bytes).unwrap();
628
629 assert_eq!(result.metadata().digest, long_digest);
630 assert_eq!(result.metadata().number, 5);
631 assert_eq!(result.metadata().offset, 1000);
632 assert_eq!(result.metadata().length, 2000);
633 }
634
635 #[test]
636 fn test_persistent_cache_piece_content_bytes_structure() {
637 let persistent_cache_piece_content = create_test_persistent_cache_piece_content();
638 let bytes: Bytes = persistent_cache_piece_content.clone().into();
639
640 let metadata_length_bytes = &bytes[..METADATA_LENGTH_SIZE];
641 let metadata_length = u32::from_be_bytes(metadata_length_bytes.try_into().unwrap());
642 assert_eq!(
643 metadata_length,
644 persistent_cache_piece_content.metadata_len()
645 );
646 assert_eq!(bytes.len(), METADATA_LENGTH_SIZE + metadata_length as usize);
647 }
648
649 #[test]
650 fn test_persistent_cache_piece_content_new() {
651 let persistent_cache_piece_content = PersistentCachePieceContent::new(
652 42,
653 1024,
654 2048,
655 "a".repeat(32),
656 "test_parent_id".to_string(),
657 1,
658 Duration::from_secs(5),
659 DateTime::from_timestamp(1693152000, 0).unwrap().naive_utc(),
660 );
661
662 assert_eq!(persistent_cache_piece_content.metadata().number, 42);
663 assert_eq!(persistent_cache_piece_content.metadata().offset, 1024);
664 assert_eq!(persistent_cache_piece_content.metadata().length, 2048);
665 assert_eq!(
666 persistent_cache_piece_content.metadata().digest,
667 "a".repeat(32)
668 );
669 assert_eq!(
670 persistent_cache_piece_content.metadata().parent_id,
671 "test_parent_id"
672 );
673 assert_eq!(persistent_cache_piece_content.metadata().traffic_type, 1);
674 assert_eq!(
675 persistent_cache_piece_content.metadata().cost,
676 Duration::from_secs(5)
677 );
678 assert_eq!(
679 persistent_cache_piece_content.metadata_len(),
680 (NUMBER_SIZE
681 + OFFSET_SIZE
682 + LENGTH_SIZE
683 + DIGEST_LENGTH_SIZE
684 + persistent_cache_piece_content.metadata().digest.len()
685 + PARENT_ID_LENGTH_SIZE
686 + persistent_cache_piece_content.metadata().parent_id.len()
687 + TRAFFIC_TYPE_SIZE
688 + COST_SIZE
689 + CREATED_AT_SIZE) as u32,
690 );
691 }
692
693 #[test]
694 fn test_persistent_cache_piece_content_is_empty() {
695 let empty_persistent_cache_piece = PersistentCachePieceContent::new(
696 0,
697 0,
698 0,
699 "a".repeat(32),
700 "test".to_string(),
701 0,
702 Duration::from_secs(0),
703 DateTime::from_timestamp(1693152000, 0).unwrap().naive_utc(),
704 );
705
706 let non_empty_persistent_cache_piece = PersistentCachePieceContent::new(
707 1,
708 0,
709 100,
710 "a".repeat(32),
711 "test".to_string(),
712 0,
713 Duration::from_secs(0),
714 DateTime::from_timestamp(1693152000, 0).unwrap().naive_utc(),
715 );
716
717 assert!(empty_persistent_cache_piece.is_empty());
718 assert!(!non_empty_persistent_cache_piece.is_empty());
719 }
720
721 #[test]
722 fn test_persistent_cache_piece_metadata_conversion_roundtrip() {
723 let metadata = create_test_metadata();
724 let (bytes, length) = <(Bytes, u32)>::from(metadata.clone());
725 let result = PersistentCachePieceMetadata::try_from((bytes, length)).unwrap();
726
727 assert_eq!(result.number, metadata.number);
728 assert_eq!(result.offset, metadata.offset);
729 assert_eq!(result.length, metadata.length);
730 assert_eq!(result.digest, metadata.digest);
731 assert_eq!(result.parent_id, metadata.parent_id);
732 assert_eq!(result.traffic_type, metadata.traffic_type);
733 assert_eq!(result.cost, metadata.cost);
734 assert_eq!(result.created_at, metadata.created_at);
735 }
736
737 #[test]
738 fn test_persistent_cache_piece_metadata_try_from_invalid_length() {
739 let metadata = create_test_metadata();
740 let (bytes, correct_length) = <(Bytes, u32)>::from(metadata);
741 let wrong_length = correct_length + 10;
742 let result = PersistentCachePieceMetadata::try_from((bytes, wrong_length));
743
744 assert!(result.is_err());
745 assert!(matches!(result.unwrap_err(), Error::InvalidLength(_)));
746 }
747
748 #[test]
749 fn test_persistent_cache_piece_metadata_try_from_too_short_bytes() {
750 let short_bytes = Bytes::from(vec![0u8; 10]);
751 let result = PersistentCachePieceMetadata::try_from((short_bytes, 10));
752 assert!(result.is_err());
753 }
754
755 #[test]
756 fn test_persistent_cache_piece_metadata_with_empty_parent_id() {
757 let metadata = PersistentCachePieceMetadata {
758 number: 1,
759 offset: 0,
760 length: 100,
761 digest: "b".repeat(32),
762 parent_id: String::new(),
763 traffic_type: 2,
764 cost: Duration::from_secs(1),
765 created_at: DateTime::from_timestamp(1693152000, 0).unwrap().naive_utc(),
766 };
767
768 let (bytes, length) = <(Bytes, u32)>::from(metadata.clone());
769 let result = PersistentCachePieceMetadata::try_from((bytes, length)).unwrap();
770
771 assert_eq!(result.parent_id, "");
772 assert_eq!(result.number, 1);
773 assert_eq!(result.traffic_type, 2);
774 }
775
776 #[test]
777 fn test_persistent_cache_piece_metadata_with_long_parent_id() {
778 let long_parent_id = "x".repeat(1000); let metadata = PersistentCachePieceMetadata {
780 number: 999,
781 offset: 12345,
782 length: 67890,
783 digest: "c".repeat(32),
784 parent_id: long_parent_id.clone(),
785 traffic_type: 255,
786 cost: Duration::from_secs(3600),
787 created_at: DateTime::from_timestamp(1693152000, 0).unwrap().naive_utc(),
788 };
789
790 let (bytes, length) = <(Bytes, u32)>::from(metadata.clone());
791 let result = PersistentCachePieceMetadata::try_from((bytes, length)).unwrap();
792
793 assert_eq!(result.parent_id, long_parent_id);
794 assert_eq!(result.number, 999);
795 assert_eq!(result.traffic_type, 255);
796 }
797
798 #[test]
799 fn test_persistent_cache_piece_metadata_with_zero_cost() {
800 let metadata = PersistentCachePieceMetadata {
801 number: 0,
802 offset: 0,
803 length: 0,
804 digest: "d".repeat(32),
805 parent_id: "zero_cost_test".to_string(),
806 traffic_type: 0,
807 cost: Duration::from_secs(0),
808 created_at: DateTime::from_timestamp(1693152000, 0).unwrap().naive_utc(),
809 };
810
811 let (bytes, length) = <(Bytes, u32)>::from(metadata.clone());
812 let result = PersistentCachePieceMetadata::try_from((bytes, length)).unwrap();
813
814 assert_eq!(result.cost, Duration::from_secs(0));
815 assert_eq!(result.parent_id, "zero_cost_test");
816 }
817
818 #[test]
819 fn test_persistent_cache_piece_metadata_with_max_values() {
820 let metadata = PersistentCachePieceMetadata {
821 number: u32::MAX,
822 offset: u64::MAX,
823 length: u64::MAX,
824 digest: "e".repeat(32),
825 parent_id: "max_values_test".to_string(),
826 traffic_type: u8::MAX,
827 cost: Duration::from_secs(u64::MAX),
828 created_at: DateTime::from_timestamp(1693152000, 0).unwrap().naive_utc(),
829 };
830
831 let (bytes, length) = <(Bytes, u32)>::from(metadata.clone());
832 let result = PersistentCachePieceMetadata::try_from((bytes, length)).unwrap();
833
834 assert_eq!(result.number, u32::MAX);
835 assert_eq!(result.offset, u64::MAX);
836 assert_eq!(result.length, u64::MAX);
837 assert_eq!(result.traffic_type, u8::MAX);
838 assert_eq!(result.cost, Duration::from_secs(u64::MAX));
839 }
840
841 #[test]
842 fn test_persistent_cache_piece_metadata_invalid_utf8_in_digest() {
843 let metadata_with_short_digest = PersistentCachePieceMetadata {
844 number: 1,
845 offset: 0,
846 length: 100,
847 digest: "short".to_string(),
848 parent_id: "test".to_string(),
849 traffic_type: 1,
850 cost: Duration::from_secs(1),
851 created_at: DateTime::from_timestamp(1693152000, 0).unwrap().naive_utc(),
852 };
853
854 let (bytes, length) = <(Bytes, u32)>::from(metadata_with_short_digest);
855 let result = PersistentCachePieceMetadata::try_from((bytes, length));
856 assert!(result.is_ok());
857 }
858}