1use std::num::NonZeroUsize;
17
18use jiff::Timestamp;
19use nautilus_core::{Params, UUID4, UnixNanos};
20use nautilus_model::{
21 data::{BarType, DataType},
22 identifiers::{ClientId, InstrumentId, OptionSeriesId, Venue},
23};
24use serde::{Deserialize, Serialize};
25
26use super::{
27 IntoBarTypes, IntoInstrumentIds, check_client_id_or_venue,
28 identifiers::{
29 deserialize_bar_types, deserialize_instrument_ids, single_bar_type, single_instrument_id,
30 },
31};
32
33#[derive(Clone, Debug, Serialize, Deserialize)]
34pub struct RequestCustomData {
35 pub client_id: ClientId,
36 pub data_type: DataType,
37 pub start: Option<Timestamp>,
38 pub end: Option<Timestamp>,
39 pub limit: Option<NonZeroUsize>,
40 pub request_id: UUID4,
41 pub ts_init: UnixNanos,
42 pub params: Option<Params>,
43}
44
45impl RequestCustomData {
46 #[expect(clippy::too_many_arguments)]
48 pub fn new(
49 client_id: ClientId,
50 data_type: DataType,
51 start: Option<Timestamp>,
52 end: Option<Timestamp>,
53 limit: Option<NonZeroUsize>,
54 request_id: UUID4,
55 ts_init: UnixNanos,
56 params: Option<Params>,
57 ) -> Self {
58 Self {
59 client_id,
60 data_type,
61 start,
62 end,
63 limit,
64 request_id,
65 ts_init,
66 params,
67 }
68 }
69}
70
71#[derive(Clone, Debug, Serialize, Deserialize)]
72pub struct RequestInstrument {
73 #[serde(deserialize_with = "deserialize_instrument_ids")]
74 pub instrument_ids: Vec<InstrumentId>,
75 pub start: Option<Timestamp>,
76 pub end: Option<Timestamp>,
77 pub client_id: Option<ClientId>,
78 pub request_id: UUID4,
79 pub ts_init: UnixNanos,
80 pub params: Option<Params>,
81}
82
83impl RequestInstrument {
84 pub fn new(
92 instrument_ids: impl IntoInstrumentIds,
93 start: Option<Timestamp>,
94 end: Option<Timestamp>,
95 client_id: Option<ClientId>,
96 request_id: UUID4,
97 ts_init: UnixNanos,
98 params: Option<Params>,
99 ) -> Self {
100 let instrument_ids = instrument_ids
101 .into_instrument_ids()
102 .expect("invalid instrument ID list");
103
104 Self {
105 instrument_ids,
106 start,
107 end,
108 client_id,
109 request_id,
110 ts_init,
111 params,
112 }
113 }
114
115 pub fn single_instrument_id(&self) -> anyhow::Result<InstrumentId> {
121 single_instrument_id(&self.instrument_ids, "request")
122 }
123}
124
125#[derive(Clone, Debug, Serialize, Deserialize)]
126pub struct RequestInstruments {
127 pub start: Option<Timestamp>,
128 pub end: Option<Timestamp>,
129 pub client_id: Option<ClientId>,
130 pub venue: Option<Venue>,
131 pub request_id: UUID4,
132 pub ts_init: UnixNanos,
133 pub params: Option<Params>,
134}
135
136impl RequestInstruments {
137 pub fn new(
139 start: Option<Timestamp>,
140 end: Option<Timestamp>,
141 client_id: Option<ClientId>,
142 venue: Option<Venue>,
143 request_id: UUID4,
144 ts_init: UnixNanos,
145 params: Option<Params>,
146 ) -> Self {
147 check_client_id_or_venue(&client_id, &venue);
148 Self {
149 start,
150 end,
151 client_id,
152 venue,
153 request_id,
154 ts_init,
155 params,
156 }
157 }
158}
159
160#[derive(Clone, Debug, Serialize, Deserialize)]
161pub struct RequestBookSnapshot {
162 #[serde(deserialize_with = "deserialize_instrument_ids")]
163 pub instrument_ids: Vec<InstrumentId>,
164 pub depth: Option<NonZeroUsize>,
165 pub client_id: Option<ClientId>,
166 pub request_id: UUID4,
167 pub ts_init: UnixNanos,
168 pub params: Option<Params>,
169}
170
171impl RequestBookSnapshot {
172 pub fn new(
180 instrument_ids: impl IntoInstrumentIds,
181 depth: Option<NonZeroUsize>,
182 client_id: Option<ClientId>,
183 request_id: UUID4,
184 ts_init: UnixNanos,
185 params: Option<Params>,
186 ) -> Self {
187 let instrument_ids = instrument_ids
188 .into_instrument_ids()
189 .expect("invalid instrument ID list");
190
191 Self {
192 instrument_ids,
193 depth,
194 client_id,
195 request_id,
196 ts_init,
197 params,
198 }
199 }
200
201 pub fn single_instrument_id(&self) -> anyhow::Result<InstrumentId> {
207 single_instrument_id(&self.instrument_ids, "request")
208 }
209}
210
211#[derive(Clone, Debug, Serialize, Deserialize)]
212pub struct RequestQuotes {
213 #[serde(deserialize_with = "deserialize_instrument_ids")]
214 pub instrument_ids: Vec<InstrumentId>,
215 pub start: Option<Timestamp>,
216 pub end: Option<Timestamp>,
217 pub limit: Option<NonZeroUsize>,
218 pub client_id: Option<ClientId>,
219 pub request_id: UUID4,
220 pub ts_init: UnixNanos,
221 pub params: Option<Params>,
222}
223
224impl RequestQuotes {
225 #[expect(clippy::too_many_arguments)]
233 pub fn new(
234 instrument_ids: impl IntoInstrumentIds,
235 start: Option<Timestamp>,
236 end: Option<Timestamp>,
237 limit: Option<NonZeroUsize>,
238 client_id: Option<ClientId>,
239 request_id: UUID4,
240 ts_init: UnixNanos,
241 params: Option<Params>,
242 ) -> Self {
243 let instrument_ids = instrument_ids
244 .into_instrument_ids()
245 .expect("invalid instrument ID list");
246
247 Self {
248 instrument_ids,
249 start,
250 end,
251 limit,
252 client_id,
253 request_id,
254 ts_init,
255 params,
256 }
257 }
258
259 pub fn single_instrument_id(&self) -> anyhow::Result<InstrumentId> {
265 single_instrument_id(&self.instrument_ids, "request")
266 }
267}
268
269#[derive(Clone, Debug, Serialize, Deserialize)]
270pub struct RequestTrades {
271 #[serde(deserialize_with = "deserialize_instrument_ids")]
272 pub instrument_ids: Vec<InstrumentId>,
273 pub start: Option<Timestamp>,
274 pub end: Option<Timestamp>,
275 pub limit: Option<NonZeroUsize>,
276 pub client_id: Option<ClientId>,
277 pub request_id: UUID4,
278 pub ts_init: UnixNanos,
279 pub params: Option<Params>,
280}
281
282impl RequestTrades {
283 #[expect(clippy::too_many_arguments)]
291 pub fn new(
292 instrument_ids: impl IntoInstrumentIds,
293 start: Option<Timestamp>,
294 end: Option<Timestamp>,
295 limit: Option<NonZeroUsize>,
296 client_id: Option<ClientId>,
297 request_id: UUID4,
298 ts_init: UnixNanos,
299 params: Option<Params>,
300 ) -> Self {
301 let instrument_ids = instrument_ids
302 .into_instrument_ids()
303 .expect("invalid instrument ID list");
304
305 Self {
306 instrument_ids,
307 start,
308 end,
309 limit,
310 client_id,
311 request_id,
312 ts_init,
313 params,
314 }
315 }
316
317 pub fn single_instrument_id(&self) -> anyhow::Result<InstrumentId> {
323 single_instrument_id(&self.instrument_ids, "request")
324 }
325}
326
327#[derive(Clone, Debug, Serialize, Deserialize)]
328pub struct RequestFundingRates {
329 #[serde(deserialize_with = "deserialize_instrument_ids")]
330 pub instrument_ids: Vec<InstrumentId>,
331 pub start: Option<Timestamp>,
332 pub end: Option<Timestamp>,
333 pub limit: Option<NonZeroUsize>,
334 pub client_id: Option<ClientId>,
335 pub request_id: UUID4,
336 pub ts_init: UnixNanos,
337 pub params: Option<Params>,
338}
339
340impl RequestFundingRates {
341 #[expect(clippy::too_many_arguments)]
349 pub fn new(
350 instrument_ids: impl IntoInstrumentIds,
351 start: Option<Timestamp>,
352 end: Option<Timestamp>,
353 limit: Option<NonZeroUsize>,
354 client_id: Option<ClientId>,
355 request_id: UUID4,
356 ts_init: UnixNanos,
357 params: Option<Params>,
358 ) -> Self {
359 let instrument_ids = instrument_ids
360 .into_instrument_ids()
361 .expect("invalid instrument ID list");
362
363 Self {
364 instrument_ids,
365 start,
366 end,
367 limit,
368 client_id,
369 request_id,
370 ts_init,
371 params,
372 }
373 }
374
375 pub fn single_instrument_id(&self) -> anyhow::Result<InstrumentId> {
381 single_instrument_id(&self.instrument_ids, "request")
382 }
383}
384
385#[derive(Clone, Debug, Serialize, Deserialize)]
386pub struct RequestOptionChainReferencePrice {
387 pub series_id: OptionSeriesId,
388 pub instrument_id: InstrumentId,
389 pub client_id: Option<ClientId>,
390 pub request_id: UUID4,
391 pub ts_init: UnixNanos,
392 pub params: Option<Params>,
393}
394
395impl RequestOptionChainReferencePrice {
396 pub fn new(
398 series_id: OptionSeriesId,
399 instrument_id: InstrumentId,
400 client_id: Option<ClientId>,
401 request_id: UUID4,
402 ts_init: UnixNanos,
403 params: Option<Params>,
404 ) -> Self {
405 Self {
406 series_id,
407 instrument_id,
408 client_id,
409 request_id,
410 ts_init,
411 params,
412 }
413 }
414}
415
416#[derive(Clone, Debug, Serialize, Deserialize)]
417pub struct RequestBookDepth {
418 #[serde(deserialize_with = "deserialize_instrument_ids")]
419 pub instrument_ids: Vec<InstrumentId>,
420 pub start: Option<Timestamp>,
421 pub end: Option<Timestamp>,
422 pub limit: Option<NonZeroUsize>,
423 pub depth: Option<NonZeroUsize>,
424 pub client_id: Option<ClientId>,
425 pub request_id: UUID4,
426 pub ts_init: UnixNanos,
427 pub params: Option<Params>,
428}
429
430impl RequestBookDepth {
431 #[expect(clippy::too_many_arguments)]
439 pub fn new(
440 instrument_ids: impl IntoInstrumentIds,
441 start: Option<Timestamp>,
442 end: Option<Timestamp>,
443 limit: Option<NonZeroUsize>,
444 depth: Option<NonZeroUsize>,
445 client_id: Option<ClientId>,
446 request_id: UUID4,
447 ts_init: UnixNanos,
448 params: Option<Params>,
449 ) -> Self {
450 let instrument_ids = instrument_ids
451 .into_instrument_ids()
452 .expect("invalid instrument ID list");
453
454 Self {
455 instrument_ids,
456 start,
457 end,
458 limit,
459 depth,
460 client_id,
461 request_id,
462 ts_init,
463 params,
464 }
465 }
466
467 pub fn single_instrument_id(&self) -> anyhow::Result<InstrumentId> {
473 single_instrument_id(&self.instrument_ids, "request")
474 }
475}
476
477#[derive(Clone, Debug, Serialize, Deserialize)]
478pub struct RequestBookDeltas {
479 #[serde(deserialize_with = "deserialize_instrument_ids")]
480 pub instrument_ids: Vec<InstrumentId>,
481 pub start: Option<Timestamp>,
482 pub end: Option<Timestamp>,
483 pub limit: Option<NonZeroUsize>,
484 pub client_id: Option<ClientId>,
485 pub request_id: UUID4,
486 pub ts_init: UnixNanos,
487 pub params: Option<Params>,
488}
489
490impl RequestBookDeltas {
491 #[expect(clippy::too_many_arguments)]
499 pub fn new(
500 instrument_ids: impl IntoInstrumentIds,
501 start: Option<Timestamp>,
502 end: Option<Timestamp>,
503 limit: Option<NonZeroUsize>,
504 client_id: Option<ClientId>,
505 request_id: UUID4,
506 ts_init: UnixNanos,
507 params: Option<Params>,
508 ) -> Self {
509 let instrument_ids = instrument_ids
510 .into_instrument_ids()
511 .expect("invalid instrument ID list");
512
513 Self {
514 instrument_ids,
515 start,
516 end,
517 limit,
518 client_id,
519 request_id,
520 ts_init,
521 params,
522 }
523 }
524
525 pub fn single_instrument_id(&self) -> anyhow::Result<InstrumentId> {
531 single_instrument_id(&self.instrument_ids, "request")
532 }
533}
534
535#[derive(Clone, Debug, Serialize, Deserialize)]
536pub struct RequestBars {
537 #[serde(deserialize_with = "deserialize_bar_types")]
538 pub bar_types: Vec<BarType>,
539 pub start: Option<Timestamp>,
540 pub end: Option<Timestamp>,
541 pub limit: Option<NonZeroUsize>,
542 pub client_id: Option<ClientId>,
543 pub request_id: UUID4,
544 pub ts_init: UnixNanos,
545 pub params: Option<Params>,
546}
547
548impl RequestBars {
549 #[expect(clippy::too_many_arguments)]
557 pub fn new(
558 bar_types: impl IntoBarTypes,
559 start: Option<Timestamp>,
560 end: Option<Timestamp>,
561 limit: Option<NonZeroUsize>,
562 client_id: Option<ClientId>,
563 request_id: UUID4,
564 ts_init: UnixNanos,
565 params: Option<Params>,
566 ) -> Self {
567 let bar_types = bar_types.into_bar_types().expect("invalid bar type list");
568
569 Self {
570 bar_types,
571 start,
572 end,
573 limit,
574 client_id,
575 request_id,
576 ts_init,
577 params,
578 }
579 }
580
581 pub fn single_bar_type(&self) -> anyhow::Result<BarType> {
587 single_bar_type(&self.bar_types, "request")
588 }
589}
590
591#[derive(Clone, Debug, Serialize, Deserialize)]
597pub struct RequestJoin {
598 pub request_ids: Vec<UUID4>,
599 pub start: Option<Timestamp>,
600 pub end: Option<Timestamp>,
601 pub request_id: UUID4,
602 pub ts_init: UnixNanos,
603 pub params: Option<Params>,
604 pub correlation_id: Option<UUID4>,
605}
606
607impl RequestJoin {
608 pub fn new(
614 request_ids: Vec<UUID4>,
615 start: Option<Timestamp>,
616 end: Option<Timestamp>,
617 request_id: UUID4,
618 ts_init: UnixNanos,
619 params: Option<Params>,
620 correlation_id: Option<UUID4>,
621 ) -> Self {
622 assert!(!request_ids.is_empty(), "request_ids must not be empty");
623 Self {
624 request_ids,
625 start,
626 end,
627 request_id,
628 ts_init,
629 params,
630 correlation_id,
631 }
632 }
633
634 #[must_use]
640 pub fn with_dates(
641 &self,
642 start: Option<Timestamp>,
643 end: Option<Timestamp>,
644 ts_init: UnixNanos,
645 ) -> Self {
646 Self {
647 request_ids: self.request_ids.clone(),
648 start,
649 end,
650 request_id: UUID4::new(),
651 ts_init,
652 params: self.params.clone(),
653 correlation_id: Some(self.request_id),
654 }
655 }
656}
657
658#[cfg(test)]
659mod tests {
660 use rstest::rstest;
661
662 use super::*;
663 use crate::messages::data::IdentifierCountError;
664
665 macro_rules! instrument_request_tests {
666 ($test:ident, $request:ident, $($field:ident = $value:expr),* $(,)?) => {
667 #[rstest]
668 fn $test() {
669 let first = InstrumentId::from("ESM4.GLBX");
670 let second = InstrumentId::from("NQM4.GLBX");
671 let client_id = Some(ClientId::from("HISTORY"));
672 let request_id = UUID4::new();
673 let ts_init = UnixNanos::from(29);
674 let params = Some(Params::new([("source".into(), "history".into())].into()));
675 let request = $request::new(
676 [second, first, second],
677 $($value,)*
678 client_id,
679 request_id,
680 ts_init,
681 params.clone(),
682 );
683 let mut serialized = serde_json::to_value(&request).unwrap();
684 serialized["instrument_ids"] = serde_json::to_value([first, second, first]).unwrap();
685 let decoded: $request = serde_json::from_value(serialized.clone()).unwrap();
686 let mut expected = serialized.clone();
687 expected["instrument_ids"] = serde_json::to_value([first, second]).unwrap();
688 let single = $request::new(
689 first,
690 $($value,)*
691 client_id,
692 request_id,
693 ts_init,
694 params.clone(),
695 );
696 serialized["instrument_ids"] = serde_json::json!([]);
697 let empty = serde_json::from_value::<$request>(serialized);
698 let rejection = request.single_instrument_id().unwrap_err();
699
700 assert_eq!(request.instrument_ids, vec![second, first]);
701 $(assert_eq!(request.$field, $value);)*
702 assert_eq!(request.client_id, client_id);
703 assert_eq!(request.request_id, request_id);
704 assert_eq!(request.ts_init, ts_init);
705 assert_eq!(request.params, params);
706 assert!(rejection.is::<IdentifierCountError>());
707 assert_eq!(
708 rejection.to_string(),
709 "request requires one instrument ID on this adapter path, received 2",
710 );
711 assert_eq!(serde_json::to_value(decoded).unwrap(), expected);
712 assert_eq!(single.single_instrument_id().unwrap(), first);
713 assert!(empty.unwrap_err().to_string().contains("instrument IDs cannot be empty"));
714 }
715 };
716 }
717
718 instrument_request_tests!(
719 test_instrument_request_identifier_lists,
720 RequestInstrument,
721 start = Some(Timestamp::from_second(11).unwrap()),
722 end = Some(Timestamp::from_second(19).unwrap()),
723 );
724 instrument_request_tests!(
725 test_book_snapshot_request_identifier_lists,
726 RequestBookSnapshot,
727 depth = NonZeroUsize::new(7),
728 );
729 instrument_request_tests!(
730 test_quotes_request_identifier_lists,
731 RequestQuotes,
732 start = Some(Timestamp::from_second(11).unwrap()),
733 end = Some(Timestamp::from_second(19).unwrap()),
734 limit = NonZeroUsize::new(3),
735 );
736 instrument_request_tests!(
737 test_trades_request_identifier_lists,
738 RequestTrades,
739 start = Some(Timestamp::from_second(11).unwrap()),
740 end = Some(Timestamp::from_second(19).unwrap()),
741 limit = NonZeroUsize::new(3),
742 );
743 instrument_request_tests!(
744 test_funding_rates_request_identifier_lists,
745 RequestFundingRates,
746 start = Some(Timestamp::from_second(11).unwrap()),
747 end = Some(Timestamp::from_second(19).unwrap()),
748 limit = NonZeroUsize::new(3),
749 );
750 instrument_request_tests!(
751 test_book_depth_request_identifier_lists,
752 RequestBookDepth,
753 start = Some(Timestamp::from_second(11).unwrap()),
754 end = Some(Timestamp::from_second(19).unwrap()),
755 limit = NonZeroUsize::new(3),
756 depth = NonZeroUsize::new(7),
757 );
758 instrument_request_tests!(
759 test_book_deltas_request_identifier_lists,
760 RequestBookDeltas,
761 start = Some(Timestamp::from_second(11).unwrap()),
762 end = Some(Timestamp::from_second(19).unwrap()),
763 limit = NonZeroUsize::new(3),
764 );
765
766 #[rstest]
767 fn test_bars_request_identifier_lists() {
768 let first = BarType::from("ESM4.GLBX-1-MINUTE-LAST-EXTERNAL");
769 let second = BarType::from("ESM4.GLBX-5-MINUTE-LAST-EXTERNAL");
770 let start = Some(Timestamp::from_second(11).unwrap());
771 let end = Some(Timestamp::from_second(19).unwrap());
772 let limit = NonZeroUsize::new(3);
773 let client_id = Some(ClientId::from("HISTORY"));
774 let request_id = UUID4::new();
775 let ts_init = UnixNanos::from(29);
776 let params = Some(Params::new([("source".into(), "history".into())].into()));
777
778 let request = RequestBars::new(
779 [second, first, second],
780 start,
781 end,
782 limit,
783 client_id,
784 request_id,
785 ts_init,
786 params.clone(),
787 );
788 let mut serialized = serde_json::to_value(&request).unwrap();
789 serialized["bar_types"] = serde_json::to_value([first, second, first]).unwrap();
790 let decoded: RequestBars = serde_json::from_value(serialized.clone()).unwrap();
791 let mut expected = serialized.clone();
792 expected["bar_types"] = serde_json::to_value([first, second]).unwrap();
793 serialized["bar_types"] = serde_json::json!([]);
794 let empty = serde_json::from_value::<RequestBars>(serialized);
795 let rejection = request.single_bar_type().unwrap_err();
796
797 assert_eq!(request.bar_types, vec![second, first]);
798 assert_eq!(request.start, start);
799 assert_eq!(request.end, end);
800 assert_eq!(request.limit, limit);
801 assert_eq!(request.client_id, client_id);
802 assert_eq!(request.request_id, request_id);
803 assert_eq!(request.ts_init, ts_init);
804 assert_eq!(request.params, params);
805 assert!(rejection.is::<IdentifierCountError>());
806 assert_eq!(
807 rejection.to_string(),
808 "request requires one bar type on this adapter path, received 2",
809 );
810 assert_eq!(serde_json::to_value(decoded).unwrap(), expected);
811 assert!(
812 empty
813 .unwrap_err()
814 .to_string()
815 .contains("bar types cannot be empty")
816 );
817 }
818
819 fn leg_request_ids() -> Vec<UUID4> {
820 vec![
821 UUID4::from("00000000-0000-4000-8000-000000000001"),
822 UUID4::from("00000000-0000-4000-8000-000000000002"),
823 ]
824 }
825
826 fn join_request() -> RequestJoin {
827 let params = Params::new([("venue".into(), "SIM".into())].into());
828
829 RequestJoin::new(
830 leg_request_ids(),
831 Some(Timestamp::from_second(1_700_000_000).unwrap()),
832 Some(Timestamp::from_second(1_700_003_600).unwrap()),
833 UUID4::from("00000000-0000-4000-8000-000000000003"),
834 UnixNanos::from(7),
835 Some(params),
836 None,
837 )
838 }
839
840 #[rstest]
841 fn test_new_assigns_every_field() {
842 let request = join_request();
843
844 assert_eq!(request.request_ids, leg_request_ids());
845 assert_eq!(
846 request.request_id,
847 UUID4::from("00000000-0000-4000-8000-000000000003")
848 );
849 assert_eq!(
850 request.start,
851 Some(Timestamp::from_second(1_700_000_000).unwrap())
852 );
853 assert_eq!(
854 request.end,
855 Some(Timestamp::from_second(1_700_003_600).unwrap())
856 );
857 assert_eq!(request.ts_init, UnixNanos::from(7));
858 assert_eq!(
859 request.params.as_ref().unwrap().get("venue"),
860 Some(&"SIM".into())
861 );
862 assert_eq!(request.correlation_id, None);
863 }
864
865 #[rstest]
866 #[should_panic(expected = "request_ids must not be empty")]
867 fn test_new_rejects_empty_request_ids() {
868 let _ = RequestJoin::new(
869 Vec::new(),
870 None,
871 None,
872 UUID4::new(),
873 UnixNanos::from(1),
874 None,
875 None,
876 );
877 }
878
879 #[rstest]
880 fn test_with_dates_correlates_the_leg_back_to_the_parent() {
881 let parent = join_request();
882 let start = Timestamp::from_second(1_700_010_000).unwrap();
883 let end = Timestamp::from_second(1_700_013_600).unwrap();
884
885 let leg = parent.with_dates(Some(start), Some(end), UnixNanos::from(11));
886
887 assert_eq!(leg.correlation_id, Some(parent.request_id));
888 assert_ne!(leg.request_id, parent.request_id);
889 assert_eq!(leg.request_ids, parent.request_ids);
890 assert_eq!(leg.params, parent.params);
891 assert_eq!(leg.start, Some(start));
892 assert_eq!(leg.end, Some(end));
893 assert_eq!(leg.ts_init, UnixNanos::from(11));
894 }
895
896 #[rstest]
897 fn test_with_dates_replaces_rather_than_merges_the_parent_window() {
898 let parent = join_request();
899
900 let leg = parent.with_dates(None, None, UnixNanos::from(13));
901
902 assert_eq!(leg.start, None);
903 assert_eq!(leg.end, None);
904 assert_eq!(leg.correlation_id, Some(parent.request_id));
905 }
906
907 #[rstest]
908 fn test_with_dates_always_mints_a_fresh_request_id() {
909 let parent = join_request();
910
911 let first = parent.with_dates(None, None, UnixNanos::from(1));
912 let second = parent.with_dates(None, None, UnixNanos::from(1));
913
914 assert_ne!(first.request_id, second.request_id);
915 assert_eq!(first.correlation_id, second.correlation_id);
916 }
917}