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 CachePieceContent {
75 metadata_length: u32,
76 metadata: CachePieceMetadata,
77}
78
79impl CachePieceContent {
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: CachePieceMetadata {
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) -> CachePieceMetadata {
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 CachePieceContent {
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 CachePieceContent, 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(CachePieceContent {
163 metadata_length,
164 metadata,
165 })
166 }
167}
168
169impl From<CachePieceContent> for Bytes {
171 fn from(content: CachePieceContent) -> 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 CachePieceMetadata {
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 CachePieceMetadata {
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 CachePieceMetadata {
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 CachePieceMetadata, 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(CachePieceMetadata {
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<CachePieceMetadata> for (Bytes, u32) {
354 fn from(metadata: CachePieceMetadata) -> (Bytes, u32) {
356 let CachePieceMetadata {
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_cache_piece_content() -> CachePieceContent {
401 CachePieceContent::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() -> CachePieceMetadata {
414 CachePieceMetadata {
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_cache_piece_content_conversion_roundtrip() {
428 let original = create_test_cache_piece_content();
429 let bytes = Bytes::from(original.clone());
430 let result = CachePieceContent::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_cache_piece_content_try_from_insufficient_bytes_for_metadata_length() {
448 let short_bytes = Bytes::from(vec![0u8; 4]); let result = CachePieceContent::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_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 = CachePieceContent::try_from(bytes.freeze());
462 assert!(result.is_err());
463 assert!(matches!(result.unwrap_err(), Error::InvalidPacket(_)));
464 }
465
466 #[test]
467 fn test_cache_piece_content_with_empty_parent_id() {
468 let cache_piece_content = CachePieceContent::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(cache_piece_content.clone());
480 let result = CachePieceContent::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_cache_piece_content_with_long_parent_id() {
490 let long_parent_id = "x".repeat(1000);
491 let cache_piece_content = CachePieceContent::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(cache_piece_content.clone());
503 let result = CachePieceContent::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_cache_piece_content_with_zero_values() {
513 let cache_piece_content = CachePieceContent::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(cache_piece_content.clone());
525 let result = CachePieceContent::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_cache_piece_content_with_max_values() {
537 let cache_piece_content = CachePieceContent::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(cache_piece_content.clone());
549 let result = CachePieceContent::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_cache_piece_content_metadata_length_calculation() {
560 let cache_piece_content = CachePieceContent::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!(cache_piece_content.metadata_len(), expected_length);
583
584 let bytes = Bytes::from(cache_piece_content.clone());
585 let result = CachePieceContent::try_from(bytes).unwrap();
586 assert_eq!(result.metadata_len(), expected_length);
587 }
588
589 #[test]
590 fn test_cache_piece_content_with_short_digest() {
591 let cache_piece_content = CachePieceContent::new(
592 1,
593 0,
594 100,
595 "short".to_string(), "test".to_string(),
597 1,
598 Duration::from_secs(1),
599 DateTime::from_timestamp(1693152000, 0).unwrap().naive_utc(),
600 );
601
602 let bytes = Bytes::from(cache_piece_content.clone());
603 let result = CachePieceContent::try_from(bytes).unwrap();
604
605 assert_eq!(result.metadata().digest, "short");
606 assert_eq!(result.metadata().number, 1);
607 }
608
609 #[test]
610 fn test_cache_piece_content_with_long_digest() {
611 let long_digest = "g".repeat(128); let cache_piece_content = CachePieceContent::new(
613 5,
614 1000,
615 2000,
616 long_digest.clone(),
617 "digest_test".to_string(),
618 10,
619 Duration::from_secs(50),
620 DateTime::from_timestamp(1693152000, 0).unwrap().naive_utc(),
621 );
622
623 let bytes = Bytes::from(cache_piece_content.clone());
624 let result = CachePieceContent::try_from(bytes).unwrap();
625
626 assert_eq!(result.metadata().digest, long_digest);
627 assert_eq!(result.metadata().number, 5);
628 assert_eq!(result.metadata().offset, 1000);
629 assert_eq!(result.metadata().length, 2000);
630 }
631
632 #[test]
633 fn test_cache_piece_content_bytes_structure() {
634 let cache_piece_content = create_test_cache_piece_content();
635 let bytes: Bytes = cache_piece_content.clone().into();
636
637 let metadata_length_bytes = &bytes[..METADATA_LENGTH_SIZE];
638 let metadata_length = u32::from_be_bytes(metadata_length_bytes.try_into().unwrap());
639 assert_eq!(metadata_length, cache_piece_content.metadata_len());
640 assert_eq!(bytes.len(), METADATA_LENGTH_SIZE + metadata_length as usize);
641 }
642
643 #[test]
644 fn test_cache_piece_content_new() {
645 let cache_piece_content = CachePieceContent::new(
646 42,
647 1024,
648 2048,
649 "a".repeat(32),
650 "test_parent_id".to_string(),
651 1,
652 Duration::from_secs(5),
653 DateTime::from_timestamp(1693152000, 0).unwrap().naive_utc(),
654 );
655
656 assert_eq!(cache_piece_content.metadata().number, 42);
657 assert_eq!(cache_piece_content.metadata().offset, 1024);
658 assert_eq!(cache_piece_content.metadata().length, 2048);
659 assert_eq!(cache_piece_content.metadata().digest, "a".repeat(32));
660 assert_eq!(cache_piece_content.metadata().parent_id, "test_parent_id");
661 assert_eq!(cache_piece_content.metadata().traffic_type, 1);
662 assert_eq!(cache_piece_content.metadata().cost, Duration::from_secs(5));
663 assert_eq!(
664 cache_piece_content.metadata_len(),
665 (NUMBER_SIZE
666 + OFFSET_SIZE
667 + LENGTH_SIZE
668 + DIGEST_LENGTH_SIZE
669 + cache_piece_content.metadata().digest.len()
670 + PARENT_ID_LENGTH_SIZE
671 + cache_piece_content.metadata().parent_id.len()
672 + TRAFFIC_TYPE_SIZE
673 + COST_SIZE
674 + CREATED_AT_SIZE) as u32,
675 );
676 }
677
678 #[test]
679 fn test_cache_piece_content_is_empty() {
680 let empty_cache_piece_content = CachePieceContent::new(
681 0,
682 0,
683 0,
684 "a".repeat(32),
685 "test".to_string(),
686 0,
687 Duration::from_secs(0),
688 DateTime::from_timestamp(1693152000, 0).unwrap().naive_utc(),
689 );
690
691 let non_empty_cache_piece_content = CachePieceContent::new(
692 1,
693 0,
694 100,
695 "a".repeat(32),
696 "test".to_string(),
697 0,
698 Duration::from_secs(0),
699 DateTime::from_timestamp(1693152000, 0).unwrap().naive_utc(),
700 );
701
702 assert!(empty_cache_piece_content.is_empty());
703 assert!(!non_empty_cache_piece_content.is_empty());
704 }
705
706 #[test]
707 fn test_cache_piece_metadata_conversion_roundtrip() {
708 let metadata = create_test_metadata();
709 let (bytes, length) = <(Bytes, u32)>::from(metadata.clone());
710 let result = CachePieceMetadata::try_from((bytes, length)).unwrap();
711
712 assert_eq!(result.number, metadata.number);
713 assert_eq!(result.offset, metadata.offset);
714 assert_eq!(result.length, metadata.length);
715 assert_eq!(result.digest, metadata.digest);
716 assert_eq!(result.parent_id, metadata.parent_id);
717 assert_eq!(result.traffic_type, metadata.traffic_type);
718 assert_eq!(result.cost, metadata.cost);
719 assert_eq!(result.created_at, metadata.created_at);
720 }
721
722 #[test]
723 fn test_cache_piece_metadata_try_from_invalid_length() {
724 let metadata = create_test_metadata();
725 let (bytes, correct_length) = <(Bytes, u32)>::from(metadata);
726 let wrong_length = correct_length + 10;
727 let result = CachePieceMetadata::try_from((bytes, wrong_length));
728
729 assert!(result.is_err());
730 assert!(matches!(result.unwrap_err(), Error::InvalidLength(_)));
731 }
732
733 #[test]
734 fn test_cache_piece_metadata_try_from_too_short_bytes() {
735 let short_bytes = Bytes::from(vec![0u8; 10]);
736 let result = CachePieceMetadata::try_from((short_bytes, 10));
737 assert!(result.is_err());
738 }
739
740 #[test]
741 fn test_cache_piece_metadata_with_empty_parent_id() {
742 let metadata = CachePieceMetadata {
743 number: 1,
744 offset: 0,
745 length: 100,
746 digest: "b".repeat(32),
747 parent_id: String::new(),
748 traffic_type: 2,
749 cost: Duration::from_secs(1),
750 created_at: DateTime::from_timestamp(1693152000, 0).unwrap().naive_utc(),
751 };
752
753 let (bytes, length) = <(Bytes, u32)>::from(metadata.clone());
754 let result = CachePieceMetadata::try_from((bytes, length)).unwrap();
755
756 assert_eq!(result.parent_id, "");
757 assert_eq!(result.number, 1);
758 assert_eq!(result.traffic_type, 2);
759 }
760
761 #[test]
762 fn test_cache_piece_metadata_with_long_parent_id() {
763 let long_parent_id = "x".repeat(1000); let metadata = CachePieceMetadata {
765 number: 999,
766 offset: 12345,
767 length: 67890,
768 digest: "c".repeat(32),
769 parent_id: long_parent_id.clone(),
770 traffic_type: 255,
771 cost: Duration::from_secs(3600),
772 created_at: DateTime::from_timestamp(1693152000, 0).unwrap().naive_utc(),
773 };
774
775 let (bytes, length) = <(Bytes, u32)>::from(metadata.clone());
776 let result = CachePieceMetadata::try_from((bytes, length)).unwrap();
777
778 assert_eq!(result.parent_id, long_parent_id);
779 assert_eq!(result.number, 999);
780 assert_eq!(result.traffic_type, 255);
781 }
782
783 #[test]
784 fn test_cache_piece_metadata_with_zero_cost() {
785 let metadata = CachePieceMetadata {
786 number: 0,
787 offset: 0,
788 length: 0,
789 digest: "d".repeat(32),
790 parent_id: "zero_cost_test".to_string(),
791 traffic_type: 0,
792 cost: Duration::from_secs(0),
793 created_at: DateTime::from_timestamp(1693152000, 0).unwrap().naive_utc(),
794 };
795
796 let (bytes, length) = <(Bytes, u32)>::from(metadata.clone());
797 let result = CachePieceMetadata::try_from((bytes, length)).unwrap();
798
799 assert_eq!(result.cost, Duration::from_secs(0));
800 assert_eq!(result.parent_id, "zero_cost_test");
801 }
802
803 #[test]
804 fn test_cache_piece_metadata_with_max_values() {
805 let metadata = CachePieceMetadata {
806 number: u32::MAX,
807 offset: u64::MAX,
808 length: u64::MAX,
809 digest: "e".repeat(32),
810 parent_id: "max_values_test".to_string(),
811 traffic_type: u8::MAX,
812 cost: Duration::from_secs(u64::MAX),
813 created_at: DateTime::from_timestamp(1693152000, 0).unwrap().naive_utc(),
814 };
815
816 let (bytes, length) = <(Bytes, u32)>::from(metadata.clone());
817 let result = CachePieceMetadata::try_from((bytes, length)).unwrap();
818
819 assert_eq!(result.number, u32::MAX);
820 assert_eq!(result.offset, u64::MAX);
821 assert_eq!(result.length, u64::MAX);
822 assert_eq!(result.traffic_type, u8::MAX);
823 assert_eq!(result.cost, Duration::from_secs(u64::MAX));
824 }
825
826 #[test]
827 fn test_cache_piece_metadata_invalid_utf8_in_digest() {
828 let metadata_with_short_digest = CachePieceMetadata {
829 number: 1,
830 offset: 0,
831 length: 100,
832 digest: "short".to_string(),
833 parent_id: "test".to_string(),
834 traffic_type: 1,
835 cost: Duration::from_secs(1),
836 created_at: DateTime::from_timestamp(1693152000, 0).unwrap().naive_utc(),
837 };
838
839 let (bytes, length) = <(Bytes, u32)>::from(metadata_with_short_digest);
840 let result = CachePieceMetadata::try_from((bytes, length));
841 assert!(result.is_ok());
842 }
843}