Skip to main content

nautilus_model/events/order/spec/
released.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16use nautilus_core::{UUID4, UnixNanos};
17
18use crate::{
19    events::OrderReleased,
20    identifiers::{ClientOrderId, InstrumentId, StrategyId, TraderId},
21    stubs::{TestDefault, test_uuid},
22    types::Price,
23};
24
25/// Test-only fluent spec for [`OrderReleased`].
26///
27/// All fields carry sensible defaults so callers only set what differs.
28/// `build()` constructs the event through [`OrderReleased::new`] so any future invariants
29/// added to the production constructor are exercised by tests built on this spec.
30#[derive(Debug, Clone, bon::Builder)]
31#[builder(finish_fn = into_spec)]
32pub struct OrderReleasedSpec {
33    #[builder(default = TraderId::test_default())]
34    pub trader_id: TraderId,
35    #[builder(default = StrategyId::test_default())]
36    pub strategy_id: StrategyId,
37    #[builder(default = InstrumentId::test_default())]
38    pub instrument_id: InstrumentId,
39    #[builder(default = ClientOrderId::test_default())]
40    pub client_order_id: ClientOrderId,
41    #[builder(default = Price::from("1.00000"))]
42    pub released_price: Price,
43    #[builder(default = test_uuid())]
44    pub event_id: UUID4,
45    #[builder(default = UnixNanos::default())]
46    pub ts_event: UnixNanos,
47    #[builder(default = UnixNanos::default())]
48    pub ts_init: UnixNanos,
49}
50
51impl<S: order_released_spec_builder::IsComplete> OrderReleasedSpecBuilder<S> {
52    /// Builds the spec and constructs an [`OrderReleased`] through its production constructor.
53    #[must_use]
54    pub fn build(self) -> OrderReleased {
55        let spec = self.into_spec();
56        OrderReleased::new(
57            spec.trader_id,
58            spec.strategy_id,
59            spec.instrument_id,
60            spec.client_order_id,
61            spec.released_price,
62            spec.event_id,
63            spec.ts_event,
64            spec.ts_init,
65        )
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use rstest::rstest;
72
73    use super::*;
74    use crate::stubs::reset_test_uuid_rng;
75
76    #[rstest]
77    fn defaults_are_sensible() {
78        // Pin the spec's no-arg defaults so accidental drift in any individual default surfaces here,
79        // rather than as silent behavior change in downstream tests.
80        let event = OrderReleasedSpec::builder().build();
81        assert_eq!(event.trader_id, TraderId::test_default());
82        assert_eq!(event.strategy_id, StrategyId::test_default());
83        assert_eq!(event.instrument_id, InstrumentId::test_default());
84        assert_eq!(event.client_order_id, ClientOrderId::test_default());
85        assert_eq!(event.released_price, Price::from("1.00000"));
86        assert_eq!(event.ts_event, UnixNanos::default());
87        assert_eq!(event.ts_init, UnixNanos::default());
88    }
89
90    #[rstest]
91    fn overrides_apply_through_constructor() {
92        let event = OrderReleasedSpec::builder()
93            .released_price(Price::from("22000"))
94            .build();
95
96        assert_eq!(event.released_price, Price::from("22000"));
97        assert_eq!(event.trader_id, TraderId::test_default());
98    }
99
100    #[rstest]
101    fn event_ids_are_unique_within_a_run() {
102        reset_test_uuid_rng();
103        let a = OrderReleasedSpec::builder().build();
104        let b = OrderReleasedSpec::builder().build();
105        let c = OrderReleasedSpec::builder().build();
106        assert_ne!(a.event_id, b.event_id);
107        assert_ne!(b.event_id, c.event_id);
108        assert_ne!(a.event_id, c.event_id);
109    }
110
111    #[rstest]
112    fn event_id_sequence_is_reproducible() {
113        // Reset before each draw so the comparison is run-order independent.
114        reset_test_uuid_rng();
115        let first_run: Vec<_> = (0..3)
116            .map(|_| OrderReleasedSpec::builder().build().event_id)
117            .collect();
118
119        reset_test_uuid_rng();
120        let second_run: Vec<_> = (0..3)
121            .map(|_| OrderReleasedSpec::builder().build().event_id)
122            .collect();
123
124        assert_eq!(first_run, second_run);
125    }
126}