test(03-01): add 19 unit tests for crypto, compression, and format modules

- crypto: encrypt/decrypt roundtrip, empty data, size formula, HMAC compute/verify, SHA-256 known values
- compression: compress/decompress roundtrip, empty data, large data, should_compress heuristic
- format: header write/read roundtrip, TOC entry roundtrip (ASCII + Cyrillic + empty name), bad magic/version rejection, entry size calculation matching FORMAT.md worked example
- Update hex-literal to v1.1
This commit is contained in:
NikitolProject
2026-02-25 00:30:47 +03:00
parent ce9012c5c5
commit 3e96b1ed88
5 changed files with 302 additions and 3 deletions

View File

@@ -209,3 +209,191 @@ pub fn entry_size(entry: &TocEntry) -> u32 {
pub fn compute_toc_size(entries: &[TocEntry]) -> u32 {
entries.iter().map(entry_size).sum()
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Cursor;
#[test]
fn test_header_write_read_roundtrip() {
let header = Header {
version: 1,
flags: 0x01,
file_count: 3,
toc_offset: HEADER_SIZE,
toc_size: 330,
toc_iv: [0u8; 16],
reserved: [0u8; 8],
};
let mut buf = Vec::new();
write_header(&mut buf, &header).unwrap();
assert_eq!(buf.len(), HEADER_SIZE as usize);
let mut cursor = Cursor::new(&buf);
let read_back = read_header(&mut cursor).unwrap();
assert_eq!(read_back.version, header.version);
assert_eq!(read_back.flags, header.flags);
assert_eq!(read_back.file_count, header.file_count);
assert_eq!(read_back.toc_offset, header.toc_offset);
assert_eq!(read_back.toc_size, header.toc_size);
assert_eq!(read_back.toc_iv, header.toc_iv);
assert_eq!(read_back.reserved, header.reserved);
}
#[test]
fn test_toc_entry_roundtrip_ascii() {
let entry = TocEntry {
name: "hello.txt".to_string(),
original_size: 5,
compressed_size: 25,
encrypted_size: 32,
data_offset: 259,
iv: [0xAA; 16],
hmac: [0xBB; 32],
sha256: [0xCC; 32],
compression_flag: 1,
padding_after: 0,
};
let mut buf = Vec::new();
write_toc_entry(&mut buf, &entry).unwrap();
assert_eq!(buf.len(), 101 + 9); // 101 + "hello.txt".len()
let mut cursor = Cursor::new(&buf);
let read_back = read_toc_entry(&mut cursor).unwrap();
assert_eq!(read_back.name, entry.name);
assert_eq!(read_back.original_size, entry.original_size);
assert_eq!(read_back.compressed_size, entry.compressed_size);
assert_eq!(read_back.encrypted_size, entry.encrypted_size);
assert_eq!(read_back.data_offset, entry.data_offset);
assert_eq!(read_back.iv, entry.iv);
assert_eq!(read_back.hmac, entry.hmac);
assert_eq!(read_back.sha256, entry.sha256);
assert_eq!(read_back.compression_flag, entry.compression_flag);
assert_eq!(read_back.padding_after, entry.padding_after);
}
#[test]
fn test_toc_entry_roundtrip_cyrillic() {
let name = "\u{0442}\u{0435}\u{0441}\u{0442}\u{043e}\u{0432}\u{044b}\u{0439}_\u{0444}\u{0430}\u{0439}\u{043b}.txt";
let entry = TocEntry {
name: name.to_string(),
original_size: 100,
compressed_size: 80,
encrypted_size: 96,
data_offset: 500,
iv: [0x11; 16],
hmac: [0x22; 32],
sha256: [0x33; 32],
compression_flag: 1,
padding_after: 0,
};
let mut buf = Vec::new();
write_toc_entry(&mut buf, &entry).unwrap();
// "тестовый_файл.txt" UTF-8 length
let expected_name_len = name.len();
assert_eq!(buf.len(), 101 + expected_name_len);
let mut cursor = Cursor::new(&buf);
let read_back = read_toc_entry(&mut cursor).unwrap();
assert_eq!(read_back.name, name);
assert_eq!(read_back.original_size, entry.original_size);
assert_eq!(read_back.compressed_size, entry.compressed_size);
assert_eq!(read_back.encrypted_size, entry.encrypted_size);
assert_eq!(read_back.data_offset, entry.data_offset);
}
#[test]
fn test_toc_entry_roundtrip_empty_name() {
let entry = TocEntry {
name: "".to_string(),
original_size: 0,
compressed_size: 0,
encrypted_size: 16,
data_offset: 40,
iv: [0u8; 16],
hmac: [0u8; 32],
sha256: [0u8; 32],
compression_flag: 0,
padding_after: 0,
};
let mut buf = Vec::new();
write_toc_entry(&mut buf, &entry).unwrap();
let mut cursor = Cursor::new(&buf);
let read_back = read_toc_entry(&mut cursor).unwrap();
assert_eq!(read_back.name, "");
}
#[test]
fn test_header_rejects_bad_magic() {
let mut buf = vec![0u8; 40];
// Wrong magic bytes
buf[0] = 0xFF;
buf[1] = 0xFF;
buf[2] = 0xFF;
buf[3] = 0xFF;
buf[4] = 1; // version
let mut cursor = Cursor::new(&buf);
let result = read_header(&mut cursor);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("magic"));
}
#[test]
fn test_header_rejects_bad_version() {
let mut buf = vec![0u8; 40];
// Correct magic
buf[0..4].copy_from_slice(&MAGIC);
// Wrong version
buf[4] = 2;
let mut cursor = Cursor::new(&buf);
let result = read_header(&mut cursor);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("version"));
}
#[test]
fn test_entry_size_calculation() {
let entry_hello = TocEntry {
name: "hello.txt".to_string(), // 9 bytes
original_size: 5,
compressed_size: 25,
encrypted_size: 32,
data_offset: 259,
iv: [0u8; 16],
hmac: [0u8; 32],
sha256: [0u8; 32],
compression_flag: 1,
padding_after: 0,
};
assert_eq!(entry_size(&entry_hello), 110); // 101 + 9
let entry_data = TocEntry {
name: "data.bin".to_string(), // 8 bytes
original_size: 32,
compressed_size: 22,
encrypted_size: 32,
data_offset: 291,
iv: [0u8; 16],
hmac: [0u8; 32],
sha256: [0u8; 32],
compression_flag: 1,
padding_after: 0,
};
assert_eq!(entry_size(&entry_data), 109); // 101 + 8
// FORMAT.md worked example: 110 + 109 = 219
assert_eq!(compute_toc_size(&[entry_hello, entry_data]), 219);
}
}