1+ use std:: io:: Cursor ;
2+
3+ use bgpkit_parser:: models:: { CommonHeader , EntryType } ;
4+ use bgpkit_parser:: BgpkitParser ;
5+
6+ fn build_valid_bgp4mp_zero_len_record ( ) -> Vec < u8 > {
7+ let hdr = CommonHeader {
8+ timestamp : 1 ,
9+ microsecond_timestamp : None ,
10+ entry_type : EntryType :: BGP4MP ,
11+ entry_subtype : 0 ,
12+ length : 0 ,
13+ } ;
14+ hdr. encode ( ) . to_vec ( )
15+ }
16+
17+ fn build_bgp4mp_with_len ( len : u32 ) -> Vec < u8 > {
18+ let hdr = CommonHeader {
19+ timestamp : 2 ,
20+ microsecond_timestamp : None ,
21+ entry_type : EntryType :: BGP4MP ,
22+ entry_subtype : 0 ,
23+ length : len,
24+ } ;
25+ let mut bytes = hdr. encode ( ) . to_vec ( ) ;
26+ bytes. extend ( std:: iter:: repeat ( 0u8 ) . take ( len as usize ) ) ;
27+ bytes
28+ }
29+
30+ fn build_invalid_entry_type_header ( ) -> Vec < u8 > {
31+ // Manually craft a header with an undefined EntryType (e.g., 15)
32+ let mut bytes = Vec :: new ( ) ;
33+ bytes. extend ( 0u32 . to_be_bytes ( ) ) ; // timestamp
34+ bytes. extend ( 15u16 . to_be_bytes ( ) ) ; // invalid entry type (not defined)
35+ bytes. extend ( 0u16 . to_be_bytes ( ) ) ; // subtype
36+ bytes. extend ( 0u32 . to_be_bytes ( ) ) ; // length
37+ bytes
38+ }
39+
40+ #[ test]
41+ fn test_raw_record_iterator_yields_valid_record ( ) {
42+ let mut data = Vec :: new ( ) ;
43+ data. extend ( build_valid_bgp4mp_zero_len_record ( ) ) ;
44+
45+ let cursor = Cursor :: new ( data) ;
46+ let parser = BgpkitParser :: from_reader ( cursor) ;
47+ let mut iter = parser. into_raw_record_iter ( ) ;
48+
49+ let rec = iter. next ( ) . expect ( "should yield one record" ) ;
50+ assert_eq ! ( rec. common_header. entry_type, EntryType :: BGP4MP ) ;
51+ assert_eq ! ( rec. common_header. length, 0 ) ;
52+ assert_eq ! ( rec. raw_bytes. len( ) , 0 ) ;
53+
54+ // then end of stream
55+ assert ! ( iter. next( ) . is_none( ) ) ;
56+ }
57+
58+ #[ test]
59+ fn test_raw_record_iterator_empty_stream_is_eof ( ) {
60+ let cursor = Cursor :: new ( Vec :: < u8 > :: new ( ) ) ;
61+ let parser = BgpkitParser :: from_reader ( cursor) ;
62+ let mut iter = parser. into_raw_record_iter ( ) ;
63+
64+ assert ! ( iter. next( ) . is_none( ) , "empty stream should be EOF" ) ;
65+ }
66+
67+ #[ test]
68+ fn test_raw_record_iterator_parse_error_skips_when_no_core_dump ( ) {
69+ // Stream: [invalid header][valid record]
70+ let mut data = Vec :: new ( ) ;
71+ data. extend ( build_invalid_entry_type_header ( ) ) ;
72+ data. extend ( build_bgp4mp_with_len ( 0 ) ) ;
73+
74+ let cursor = Cursor :: new ( data) ;
75+ let parser = BgpkitParser :: from_reader ( cursor) ; // core_dump=false by default
76+ let mut iter = parser. into_raw_record_iter ( ) ;
77+
78+ // Iterator should skip the invalid header and yield the valid record
79+ let rec = iter. next ( ) . expect ( "should yield valid record after skipping parse error" ) ;
80+ assert_eq ! ( rec. common_header. entry_type, EntryType :: BGP4MP ) ;
81+ assert_eq ! ( rec. raw_bytes. len( ) , 0 ) ;
82+ assert ! ( iter. next( ) . is_none( ) ) ;
83+ }
84+
85+ #[ test]
86+ fn test_raw_record_iterator_parse_error_stops_when_core_dump_enabled ( ) {
87+ // Stream: [invalid header][valid record]
88+ let mut data = Vec :: new ( ) ;
89+ data. extend ( build_invalid_entry_type_header ( ) ) ;
90+ data. extend ( build_bgp4mp_with_len ( 0 ) ) ;
91+
92+ let cursor = Cursor :: new ( data) ;
93+ let parser = BgpkitParser :: from_reader ( cursor) . enable_core_dump ( ) ;
94+ let mut iter = parser. into_raw_record_iter ( ) ;
95+
96+ // Iterator should stop on parse error when core_dump is enabled
97+ assert ! ( iter. next( ) . is_none( ) ) ;
98+ }
99+
100+ #[ test]
101+ fn test_raw_record_iterator_io_error_stops_iteration ( ) {
102+ // Craft a header that declares 5 bytes but only provide 3 -> triggers IoError in chunk_mrt_record
103+ let hdr = CommonHeader {
104+ timestamp : 3 ,
105+ microsecond_timestamp : None ,
106+ entry_type : EntryType :: BGP4MP ,
107+ entry_subtype : 0 ,
108+ length : 5 ,
109+ } ;
110+ let mut data = hdr. encode ( ) . to_vec ( ) ;
111+ data. extend ( [ 0u8 ; 3 ] ) ; // insufficient bytes
112+
113+ let cursor = Cursor :: new ( data) ;
114+ let parser = BgpkitParser :: from_reader ( cursor) ;
115+ let mut iter = parser. into_raw_record_iter ( ) ;
116+
117+ // On IoError branch, iterator stops and returns None immediately
118+ assert ! ( iter. next( ) . is_none( ) ) ;
119+ }
0 commit comments