Skip to main content

copybook_corruption/
lib.rs

1#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used))]
2// SPDX-License-Identifier: AGPL-3.0-or-later
3//! Transfer-corruption detection façade.
4//!
5//! This crate keeps a narrow public surface: all corruption heuristics are
6//! delegated to dedicated microcrates and re-exported here for stable call sites.
7
8pub use copybook_corruption_detectors::{detect_ebcdic_corruption, detect_packed_corruption};
9pub use copybook_corruption_rdw::detect_rdw_ascii_corruption;
10
11#[cfg(test)]
12#[allow(clippy::expect_used, clippy::unwrap_used)]
13mod tests {
14    use super::*;
15    use copybook_core::ErrorCode;
16
17    #[test]
18    fn facade_detects_rdw_ascii_corruption() {
19        let rdw_with_ascii = [b'1', b'2', 0x00, 0x00];
20        let result = detect_rdw_ascii_corruption(&rdw_with_ascii)
21            .expect("expected ASCII corruption to be detected");
22        assert_eq!(
23            result.code,
24            copybook_core::ErrorCode::CBKF104_RDW_SUSPECT_ASCII
25        );
26    }
27
28    #[test]
29    fn facade_ebcdic_clean_data_returns_empty() {
30        let clean = [0xC1, 0xC2, 0xC3, 0xC4];
31        assert!(detect_ebcdic_corruption(&clean, "FIELD-A").is_empty());
32    }
33
34    #[test]
35    fn facade_ebcdic_detects_null_byte() {
36        let data = [0xC1, 0x00, 0xC3];
37        let errors = detect_ebcdic_corruption(&data, "REC.FLD");
38        assert_eq!(errors.len(), 1);
39        assert_eq!(errors[0].code, ErrorCode::CBKC301_INVALID_EBCDIC_BYTE);
40    }
41
42    #[test]
43    fn facade_ebcdic_empty_data() {
44        assert!(detect_ebcdic_corruption(&[], "EMPTY").is_empty());
45    }
46
47    #[test]
48    fn facade_packed_valid_positive() {
49        let valid = [0x12, 0x34, 0x5C]; // +12345
50        assert!(detect_packed_corruption(&valid, "AMT").is_empty());
51    }
52
53    #[test]
54    fn facade_packed_valid_negative() {
55        let valid = [0x98, 0x76, 0x5D]; // -98765
56        assert!(detect_packed_corruption(&valid, "AMT").is_empty());
57    }
58
59    #[test]
60    fn facade_packed_valid_unsigned() {
61        let valid = [0x01, 0x23, 0x4F]; // unsigned 01234
62        assert!(detect_packed_corruption(&valid, "AMT").is_empty());
63    }
64
65    #[test]
66    fn facade_packed_detects_bad_sign() {
67        let bad_sign = [0x12, 0x37]; // sign nibble 7 is invalid
68        let errors = detect_packed_corruption(&bad_sign, "BAD");
69        assert!(!errors.is_empty());
70    }
71
72    #[test]
73    fn facade_packed_empty_data() {
74        assert!(detect_packed_corruption(&[], "EMPTY").is_empty());
75    }
76
77    #[test]
78    fn facade_rdw_clean_header_returns_none() {
79        let clean = [0x00, 0x50, 0x00, 0x00];
80        assert!(detect_rdw_ascii_corruption(&clean).is_none());
81    }
82
83    #[test]
84    fn facade_rdw_short_input_returns_none() {
85        assert!(detect_rdw_ascii_corruption(&[0x31]).is_none());
86    }
87
88    #[test]
89    fn facade_ebcdic_all_corrupted_range() {
90        // Every byte in 0x00..=0x1F is flagged
91        let data: Vec<u8> = (0x00..=0x1F).collect();
92        let errors = detect_ebcdic_corruption(&data, "CTRL");
93        assert_eq!(errors.len(), data.len());
94    }
95}