megacommerce_shared/models/
files.rs

1use base64::{engine::general_purpose::STANDARD, Engine as _};
2use regex::Regex;
3use sha2::{Digest, Sha256};
4
5use super::errors::{ErrorType, SimpleError};
6
7pub enum UnitSizeType {
8  Bytes,
9  KB,
10  MB,
11  GB,
12}
13
14impl UnitSizeType {
15  pub const fn as_str(&self) -> &'static str {
16    match self {
17      Self::Bytes => "Bytes",
18      Self::KB => "Kb",
19      Self::MB => "Mb",
20      Self::GB => "Gb",
21    }
22  }
23}
24
25pub fn validate_base64_checksum(
26  base64_string: &str,
27  expected_checksum: &str,
28  pre_decoded_data: Option<Vec<u8>>,
29) -> Result<bool, SimpleError> {
30  let data = match pre_decoded_data {
31    Some(dec) => dec,
32    None => {
33      // Remove data URL prefix if present
34      let re = Regex::new(r"^data:[^;]+;base64,").map_err(|err| SimpleError {
35        err: Box::new(err),
36        message: "failed to create a new regex".to_string(),
37        _type: ErrorType::RegexInvalid,
38      })?;
39      let base64_data = re.replace(base64_string, "");
40      STANDARD.decode(base64_data.as_bytes()).map_err(|err| SimpleError {
41        err: Box::new(err),
42        message: "invalid base64 data string".to_string(),
43        _type: ErrorType::Base64Invalid,
44      })?
45    }
46  };
47
48  let mut hasher = Sha256::new();
49  hasher.update(&data);
50  let hash_result = hasher.finalize();
51
52  let computed_checksum = hash_result.iter().map(|b| format!("{:02x}", b)).collect::<String>();
53
54  Ok(computed_checksum == expected_checksum)
55}
56
57#[cfg(test)]
58mod tests {
59  use super::*;
60  use base64::{engine::general_purpose::STANDARD, Engine};
61
62  // Test helper: create a known test image base64 and its correct SHA-256 hash
63  fn get_test_data() -> (String, String, Vec<u8>) {
64    // A small 1x1 pixel PNG image in base64
65    let base64_data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==";
66
67    // Calculate the actual SHA-256 hash of this base64 data
68    let decoded_data = STANDARD.decode(base64_data).unwrap();
69    let mut hasher = Sha256::new();
70    hasher.update(&decoded_data);
71    let hash_result = hasher.finalize();
72    let actual_hash = hash_result.iter().map(|b| format!("{:02x}", b)).collect::<String>();
73
74    (base64_data.to_string(), actual_hash, decoded_data)
75  }
76
77  #[test]
78  fn test_valid_checksum_without_pre_decoded() {
79    let (base64, expected_hash, _) = get_test_data();
80
81    let result = validate_base64_checksum(&base64, &expected_hash, None).unwrap();
82
83    assert!(result);
84  }
85
86  #[test]
87  fn test_valid_checksum_with_pre_decoded() {
88    let (base64, expected_hash, decoded_data) = get_test_data();
89
90    let result = validate_base64_checksum(&base64, &expected_hash, Some(decoded_data)).unwrap();
91
92    assert!(result);
93  }
94
95  #[test]
96  fn test_invalid_checksum() {
97    let (base64, expected_hash, _) = get_test_data();
98    let wrong_hash = "0000000000000000000000000000000000000000000000000000000000000000";
99
100    let result = validate_base64_checksum(&base64, wrong_hash, None).unwrap();
101
102    assert!(!result);
103  }
104
105  #[test]
106  fn test_data_url_without_pre_decoded() {
107    let (base64, expected_hash, _) = get_test_data();
108    let data_url = format!("data:image/png;base64,{}", base64);
109
110    let result = validate_base64_checksum(&data_url, &expected_hash, None).unwrap();
111
112    assert!(result);
113  }
114
115  #[test]
116  fn test_data_url_with_pre_decoded() {
117    let (base64, expected_hash, decoded_data) = get_test_data();
118    let data_url = format!("data:image/png;base64,{}", base64);
119
120    let result = validate_base64_checksum(&data_url, &expected_hash, Some(decoded_data)).unwrap();
121
122    assert!(result);
123  }
124
125  #[test]
126  fn test_different_data_url_mime_types() {
127    let (base64, expected_hash, _) = get_test_data();
128    let mime_types = vec![
129      "data:image/jpeg;base64,",
130      "data:application/octet-stream;base64,",
131      "data:text/plain;base64,",
132    ];
133
134    for mime in mime_types {
135      let data_url = format!("{}{}", mime, base64);
136      let result = validate_base64_checksum(&data_url, &expected_hash, None).unwrap();
137      assert!(result, "Failed with MIME type: {}", mime);
138    }
139  }
140
141  #[test]
142  fn test_invalid_base64() {
143    let invalid_base64 = "invalid!!!base64@@@data";
144
145    let result = validate_base64_checksum(invalid_base64, "some_hash", None);
146
147    assert!(result.is_err());
148    if let Err(err) = result {
149      assert_eq!(err._type, ErrorType::Base64Invalid);
150    }
151  }
152
153  #[test]
154  fn test_empty_base64_string() {
155    let empty_hash = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; // SHA-256 of empty data
156
157    let result = validate_base64_checksum("", empty_hash, None).unwrap();
158    assert!(result);
159  }
160
161  #[test]
162  fn test_pre_decoded_takes_precedence() {
163    let (base64, expected_hash, _) = get_test_data();
164
165    // Provide different pre-decoded data than what the base64 string contains
166    let different_data = b"completely different data".to_vec();
167
168    // This should use the pre_decoded_data and fail validation
169    let result = validate_base64_checksum(&base64, &expected_hash, Some(different_data)).unwrap();
170
171    assert!(!result); // Should fail because pre_decoded_data doesn't match the expected hash
172  }
173
174  #[test]
175  fn test_consistency_between_with_and_without_pre_decoded() {
176    let (base64, expected_hash, decoded_data) = get_test_data();
177
178    let result1 = validate_base64_checksum(&base64, &expected_hash, None).unwrap();
179    let result2 = validate_base64_checksum(&base64, &expected_hash, Some(decoded_data)).unwrap();
180
181    assert_eq!(result1, result2);
182    assert!(result1);
183    assert!(result2);
184  }
185
186  // Additional test: verify the actual hash value for the test data
187  #[test]
188  fn test_known_hash_value() {
189    let base64_data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==";
190    let expected_hash = "1c30f1a2686fbf86c76c9115b471df8c0b6b9c5b6c5e2c7b8c5c6c5c6c5c6c5c6c5"; // This will be the actual hash
191
192    // First, let's see what the actual hash is
193    let decoded = STANDARD.decode(base64_data).unwrap();
194    let mut hasher = Sha256::new();
195    hasher.update(&decoded);
196    let actual_hash = hasher.finalize();
197    let actual_hash_hex = actual_hash.iter().map(|b| format!("{:02x}", b)).collect::<String>();
198
199    println!("Actual hash for test data: {}", actual_hash_hex);
200
201    // Now test with the correct hash
202    let result = validate_base64_checksum(base64_data, &actual_hash_hex, None).unwrap();
203    assert!(result);
204  }
205}