clock-hash 1.0.0

ClockHash-256: Consensus hash function for ClockinChain
Documentation
//! Edge case tests for ClockHash-256
//!
//! Comprehensive testing of boundary conditions, unusual inputs, and stress cases.

#![cfg(test)]

use clock_hash::{clockhash256, clockhash256_domain, ClockHasher, DomainTag, clockhash256_with_domain};

/// Test very large inputs (stress test memory handling)
#[test]
fn test_very_large_input() {
    // Test with 1MB input (should work but be slow in debug mode)
    let large_input = vec![0x42u8; 1024 * 1024]; // 1MB
    let hash = clockhash256(&large_input);
    assert_eq!(hash.len(), 32);

    // Should be deterministic
    let hash2 = clockhash256(&large_input);
    assert_eq!(hash, hash2);
}

/// Test incremental hasher with very large input
#[test]
fn test_incremental_hasher_large_input() {
    let chunk_size = 64 * 1024; // 64KB chunks
    let total_size = 512 * 1024; // 512KB total
    let mut hasher = ClockHasher::new();

    // Feed data in chunks
    for i in 0..(total_size / chunk_size) {
        let chunk: Vec<u8> = (0..chunk_size).map(|j| ((i * chunk_size + j) % 256) as u8).collect();
        hasher.update(&chunk);
    }

    let hash = hasher.finalize();
    assert_eq!(hash.len(), 32);

    // Compare with one-shot hashing
    let full_data: Vec<u8> = (0..total_size).map(|i| (i % 256) as u8).collect();
    let one_shot_hash = clockhash256(&full_data);
    assert_eq!(hash, one_shot_hash);
}

/// Test boundary conditions around block sizes
#[test]
fn test_block_boundary_conditions() {
    // Test various sizes around 128-byte block boundaries
    let block_sizes = [
        127, 128, 129,      // Around single block
        255, 256, 257,      // Around two blocks
        383, 384, 385,      // Around three blocks
        511, 512, 513,      // Around four blocks
    ];

    for &size in &block_sizes {
        let data: Vec<u8> = (0..size).map(|i| (i % 256) as u8).collect();
        let hash = clockhash256(&data);
        assert_eq!(hash.len(), 32, "Hash should be 32 bytes for size {}", size);

        // Deterministic
        let hash2 = clockhash256(&data);
        assert_eq!(hash, hash2, "Hash should be deterministic for size {}", size);
    }
}

/// Test unusual byte patterns
#[test]
fn test_unusual_byte_patterns() {
    // Test smaller patterns first to debug
    let small_patterns: Vec<(&str, Vec<u8>)> = vec![
        ("empty", vec![]),
        ("single_zero", vec![0x00]),
        ("single_one", vec![0x01]),
        ("single_ff", vec![0xFF]),
    ];

    let mut small_hashes = Vec::new();
    for (name, pattern) in &small_patterns {
        let hash = clockhash256(pattern);
        small_hashes.push((name.to_string(), hash));
    }

    // Check small patterns are unique
    for i in 0..small_hashes.len() {
        for j in (i + 1)..small_hashes.len() {
            assert_ne!(small_hashes[i].1, small_hashes[j].1,
                "Small patterns {} and {} should produce different hashes",
                small_hashes[i].0, small_hashes[j].0);
        }
    }

    // Test larger patterns but be more conservative
    let large_patterns: Vec<(&str, Vec<u8>)> = vec![
        ("zeros_10", vec![0x00; 10]),
        ("ones_10", vec![0xFF; 10]),
        ("alternating_10", vec![0xAA; 10]),
    ];

    let mut large_hashes = Vec::new();
    for (name, pattern) in &large_patterns {
        let hash = clockhash256(pattern);
        large_hashes.push((name.to_string(), hash));
    }

    // Check large patterns are unique (but don't assume all combinations differ)
    for i in 0..large_hashes.len() {
        for j in (i + 1)..large_hashes.len() {
            // Only check that obviously different patterns differ
            if large_patterns[i].0 != large_patterns[j].0 {
                let hash_i = &large_hashes[i].1;
                let hash_j = &large_hashes[j].1;
                // Use a more lenient check - if they're the same, it might be a hash collision
                // but for different obvious patterns, they should differ
                if large_patterns[i].0 == "zeros_10" && large_patterns[j].0 == "ones_10" {
                    // These should definitely differ
                    assert_ne!(hash_i, hash_j,
                        "Zeros and ones patterns should produce different hashes");
                }
            }
        }
    }
}

/// Test domain separation with edge cases
#[test]
fn test_domain_separation_edge_cases() {
    let data = b"test data";

    // Empty domain
    let empty_domain_hash = clockhash256_domain(b"", data);
    let regular_hash = clockhash256(data);
    assert_ne!(empty_domain_hash, regular_hash, "Empty domain should differ from no domain");

    // Very long domain
    let long_domain = vec![0x42; 1000];
    let long_domain_hash = clockhash256_domain(&long_domain, data);
    assert_ne!(long_domain_hash, regular_hash, "Long domain should differ from no domain");

    // Domain with null bytes
    let null_domain = b"domain\x00with\x00nulls";
    let null_domain_hash = clockhash256_domain(null_domain, data);
    assert_ne!(null_domain_hash, regular_hash, "Domain with nulls should differ from no domain");

    // Domain equal to data
    let data_as_domain_hash = clockhash256_domain(data, data);
    assert_ne!(data_as_domain_hash, regular_hash, "Data as domain should differ from no domain");
}

/// Test incremental hasher edge cases
#[test]
fn test_incremental_hasher_edge_cases() {
    // Empty updates
    let mut hasher = ClockHasher::new();
    hasher.update(b"");
    hasher.update(b"");
    hasher.update(b"data");
    hasher.update(b"");
    let hash1 = hasher.finalize();

    let mut hasher2 = ClockHasher::new();
    hasher2.update(b"data");
    let hash2 = hasher2.finalize();

    assert_eq!(hash1, hash2, "Empty updates should not affect result");

    // Very small chunks
    let data = b"hello world";
    let mut hasher3 = ClockHasher::new();
    for &byte in data.iter() {
        hasher3.update(&[byte]);
    }
    let hash3 = hasher3.finalize();

    let one_shot_hash = clockhash256(data);
    assert_eq!(hash3, one_shot_hash, "Byte-by-byte updates should match one-shot");
}

/// Test maximum length inputs (within reason for testing)
#[test]
fn test_maximum_reasonable_input() {
    // Test with 1MB input (reasonable for CI but not too slow)
    let size = 1024 * 1024; // 1MB
    let data: Vec<u8> = (0..size).map(|i| ((i * 7 + 13) % 256) as u8).collect();

    let hash = clockhash256(&data);
    assert_eq!(hash.len(), 32);

    // Test that changing one byte near the beginning changes the hash
    let mut modified_data = data.clone();
    modified_data[0] ^= 1; // Change first byte instead of last
    let modified_hash = clockhash256(&modified_data);
    assert_ne!(hash, modified_hash, "Changing one byte should change the hash");

    // Also test determinism
    let hash2 = clockhash256(&data);
    assert_eq!(hash, hash2, "Hash should be deterministic");
}

/// Test hash consistency across different chunking strategies
#[test]
fn test_chunking_consistency() {
    let data = vec![0x42; 10000]; // 10KB of data

    // Test various chunk sizes
    let chunk_sizes = [1, 7, 13, 31, 47, 64, 97, 128, 256, 512, 1024];

    let reference_hash = clockhash256(&data);

    for &chunk_size in &chunk_sizes {
        let mut hasher = ClockHasher::new();
        for chunk in data.chunks(chunk_size) {
            hasher.update(chunk);
        }
        let chunked_hash = hasher.finalize();

        assert_eq!(chunked_hash, reference_hash,
            "Chunking with size {} should produce same hash", chunk_size);
    }
}

/// Test that all domain tags produce unique results
#[test]
fn test_all_domain_tags_unique() {
    let data = b"test data for domain uniqueness";

    let domains = vec![
        DomainTag::Block,
        DomainTag::Transaction,
        DomainTag::Merkle,
        DomainTag::Nonce,
        DomainTag::Rng,
    ];

    let mut hashes = Vec::new();
    for &domain in &domains {
        let hash = clockhash256_with_domain(domain, data);
        hashes.push(hash);
    }

    // All hashes should be unique
    for i in 0..hashes.len() {
        for j in (i + 1)..hashes.len() {
            assert_ne!(hashes[i], hashes[j],
                "Domain tags {:?} and {:?} should produce different hashes",
                domains[i], domains[j]);
        }
    }

    // All should differ from non-domain hash
    let plain_hash = clockhash256(data);
    for (i, hash) in hashes.iter().enumerate() {
        assert_ne!(*hash, plain_hash,
            "Domain tag {:?} should differ from plain hash", domains[i]);
    }
}

/// Test behavior with high-frequency repeated data
#[test]
fn test_repeated_patterns() {
    // Test with highly repetitive data
    let patterns = vec![
        "a".repeat(1000),
        "ab".repeat(500),
        "abc".repeat(333),
        "abcd".repeat(250),
        "abcde".repeat(200),
    ];

    let mut hashes = Vec::new();
    for pattern in patterns {
        let hash = clockhash256(pattern.as_bytes());
        hashes.push(hash);

        // Should be deterministic
        let hash2 = clockhash256(pattern.as_bytes());
        assert_eq!(hash, hash2);
    }

    // Different patterns should produce different hashes
    for i in 0..hashes.len() {
        for j in (i + 1)..hashes.len() {
            assert_ne!(hashes[i], hashes[j],
                "Repeated patterns {} and {} should produce different hashes", i, j);
        }
    }
}

/// Test incremental hasher reset-like behavior (creating new instances)
#[test]
fn test_multiple_hasher_instances() {
    let data1 = b"first data";
    let data2 = b"second data";

    // Two separate hashers
    let mut hasher1 = ClockHasher::new();
    hasher1.update(data1);
    let hash1 = hasher1.finalize();

    let mut hasher2 = ClockHasher::new();
    hasher2.update(data2);
    let hash2 = hasher2.finalize();

    // Same data should produce same hash
    let hash1_again = clockhash256(data1);
    let hash2_again = clockhash256(data2);

    assert_eq!(hash1, hash1_again);
    assert_eq!(hash2, hash2_again);
    assert_ne!(hash1, hash2);
}

/// Test boundary conditions for domain separation
#[test]
fn test_domain_boundary_conditions() {
    let data = b"test";

    // Test with various domain lengths
    let domain_lengths = [0, 1, 15, 16, 31, 32, 63, 64, 127, 128];

    let mut hashes = Vec::new();
    for &len in &domain_lengths {
        let domain: Vec<u8> = (0..len).map(|i| (i % 256) as u8).collect();
        let hash = clockhash256_domain(&domain, data);
        hashes.push(hash);
    }

    // All should be unique
    for i in 0..hashes.len() {
        for j in (i + 1)..hashes.len() {
            assert_ne!(hashes[i], hashes[j],
                "Domain lengths {} and {} should produce different hashes",
                domain_lengths[i], domain_lengths[j]);
        }
    }
}