1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
//! T018 (US3) — `Message::fields_out_of_order` wire-sectioning detection (FR-006).
//!
//! `decode()` classifies each tag into header/body/trailer by static tag identity (not wire
//! position), so cross-section interleaving is only observable *during* decode; this flag is how
//! that observation survives past decode for `truefix-dict::validate()` to act on.
use truefix_core::decode;
/// Build a well-formed frame from `rest` (every field after BeginString/BodyLength, ending just
/// before CheckSum), computing BodyLength and CheckSum correctly regardless of `rest`'s order —
/// so the *only* thing under test is field ordering, never framing/checksum validity.
fn raw_ordered(rest: &[(u32, &str)]) -> Vec<u8> {
let body: String = rest.iter().map(|(t, v)| format!("{t}={v}\x01")).collect();
let prefix = format!("8=FIX.4.4\x019={}\x01", body.len());
let pre_checksum = format!("{prefix}{body}");
let checksum: u32 = pre_checksum.bytes().map(u32::from).sum::<u32>() & 0xFF;
format!("{pre_checksum}10={checksum:03}\x01").into_bytes()
}
#[test]
fn well_formed_message_is_not_out_of_order() {
let raw = raw_ordered(&[
(35, "D"),
(49, "CLIENT"),
(56, "SERVER"),
(34, "1"),
(52, "20240101-00:00:00"),
(55, "AAPL"),
]);
let msg = decode(&raw).expect("well-formed frame should decode");
assert!(!msg.fields_out_of_order());
}
#[test]
fn third_field_not_msg_type_is_out_of_order() {
// MsgType(35) must be the third field on the wire; here SenderCompID(49) usurps that slot.
let raw = raw_ordered(&[
(49, "CLIENT"),
(35, "D"),
(56, "SERVER"),
(34, "1"),
(52, "20240101-00:00:00"),
]);
let msg = decode(&raw).expect("still a well-formed frame (framing doesn't care about tag 3)");
assert!(msg.fields_out_of_order());
}
#[test]
fn body_field_before_header_is_complete_is_out_of_order() {
// A body field (Symbol, 55) interleaved before a header field (SenderCompID, 49).
let raw = raw_ordered(&[
(35, "D"),
(55, "AAPL"), // body — sets max_section_seen to "body"
(49, "CLIENT"), // header — arrives after body already started
(56, "SERVER"),
(34, "1"),
(52, "20240101-00:00:00"),
]);
let msg = decode(&raw).expect("well-formed frame");
assert!(msg.fields_out_of_order());
}
#[test]
fn header_and_body_fields_ordered_differently_is_out_of_order() {
// 15_HeaderAndBodyFieldsOrderedDifferently — header/body fields interleaved throughout,
// rather than cleanly sectioned.
let raw = raw_ordered(&[
(35, "D"),
(49, "CLIENT"),
(55, "AAPL"), // body field
(56, "SERVER"), // header field, after a body field already appeared
(34, "1"),
(52, "20240101-00:00:00"),
]);
let msg = decode(&raw).expect("well-formed frame");
assert!(msg.fields_out_of_order());
}
#[test]
fn manually_constructed_message_defaults_to_not_out_of_order() {
// A Message built in code (not decoded) has no wire to be out of order on.
assert!(!truefix_core::Message::new().fields_out_of_order());
}