inkly-core 0.1.0

Core PII detection engine for Inkly SecureText
Documentation
#[cfg(test)]
mod tests {
    use crate::{Rule, Span};
    use wasm_bindgen_test::*;

    // WASM tests requiring a browser environment
    #[cfg(target_arch = "wasm32")]
    wasm_bindgen_test_configure!(run_in_browser);

    // Regular Rust tests that don't require WASM
    #[test]
    fn test_rule_get_type() {
        assert_eq!(Rule::Email.get_type(), "pii.email");
        assert_eq!(Rule::Ssn.get_type(), "pii.ssn");
        assert_eq!(Rule::CreditCard.get_type(), "pii.credit_card");
        assert_eq!(Rule::Phone.get_type(), "pii.phone");
        assert_eq!(Rule::SomaliNid.get_type(), "pii.id.somali_nid");
        
        let custom_rule = Rule::Custom {
            type_: "pii.custom.test".to_string(),
            regex: "abc".to_string(),
        };
        assert_eq!(custom_rule.get_type(), "pii.custom.test");
    }

    #[test]
    fn test_rule_get_regex() {
        // Just test that the regexes compile without errors
        let _email_regex = Rule::Email.get_regex();
        let _ssn_regex = Rule::Ssn.get_regex();
        let _cc_regex = Rule::CreditCard.get_regex();
        let _phone_regex = Rule::Phone.get_regex();
        let _nid_regex = Rule::SomaliNid.get_regex();
        
        let custom_rule = Rule::Custom {
            type_: "pii.custom.test".to_string(),
            regex: "abc".to_string(),
        };
        let _custom_regex = custom_rule.get_regex();
    }

    #[test]
    fn test_find_pii_internal_email() {
        let text = "Contact me at test@example.com for more information.";
        let rules = vec![Rule::Email];
        
        let spans = find_pii_internal(text, rules);
        
        assert_eq!(spans.len(), 1);
        assert_eq!(spans[0].type_, "pii.email");
        assert!(text[spans[0].start..spans[0].end].contains("@example.com"));
    }

    #[test]
    fn test_find_pii_internal_ssn() {
        let text = "My SSN is 123-45-6789 and should be kept private.";
        let rules = vec![Rule::Ssn];
        
        let spans = find_pii_internal(text, rules);
        
        assert_eq!(spans.len(), 1);
        assert_eq!(spans[0].type_, "pii.ssn");
        assert_eq!(&text[spans[0].start..spans[0].end], "123-45-6789");
    }

    #[test]
    fn test_find_pii_internal_multiple() {
        let text = "Contact: test@example.com, SSN: 123-45-6789";
        let rules = vec![Rule::Email, Rule::Ssn];
        
        let spans = find_pii_internal(text, rules);
        
        assert_eq!(spans.len(), 2);
        assert_eq!(spans[0].type_, "pii.email");
        assert_eq!(spans[1].type_, "pii.ssn");
    }

    // Keep the WASM tests below
    #[wasm_bindgen_test]
    fn test_email_detection() {
        let text = "Contact me at test@example.com for more information.";
        let rules = vec![Rule::Email];
        
        let spans = find_pii_internal(text, rules);
        
        assert_eq!(spans.len(), 1);
        assert_eq!(spans[0].type_, "pii.email");
        assert!(text[spans[0].start..spans[0].end].contains("@example.com"));
    }

    #[wasm_bindgen_test]
    fn test_ssn_detection() {
        let text = "My SSN is 123-45-6789 and should be kept private.";
        let rules = vec![Rule::Ssn];
        
        let spans = find_pii_internal(text, rules);
        
        assert_eq!(spans.len(), 1);
        assert_eq!(spans[0].type_, "pii.ssn");
        assert_eq!(&text[spans[0].start..spans[0].end], "123-45-6789");
    }

    #[wasm_bindgen_test]
    fn test_credit_card_detection() {
        let text = "Payment with 4111 1111 1111 1111 was successful.";
        let rules = vec![Rule::CreditCard];
        
        let spans = find_pii_internal(text, rules);
        
        assert_eq!(spans.len(), 1);
        assert_eq!(spans[0].type_, "pii.credit_card");
        assert_eq!(&text[spans[0].start..spans[0].end], "4111 1111 1111 1111");
    }

    #[wasm_bindgen_test]
    fn test_phone_detection() {
        let text = "Call me at +1 (555) 123-4567 anytime.";
        let rules = vec![Rule::Phone];
        
        let spans = find_pii_internal(text, rules);
        
        assert_eq!(spans.len(), 1);
        assert_eq!(spans[0].type_, "pii.phone");
        assert_eq!(&text[spans[0].start..spans[0].end], "+1 (555) 123-4567");
    }

    #[wasm_bindgen_test]
    fn test_somali_nid_detection() {
        let text = "My Somali National ID is AB1234567.";
        let rules = vec![Rule::SomaliNid];
        
        let spans = find_pii_internal(text, rules);
        
        assert_eq!(spans.len(), 1);
        assert_eq!(spans[0].type_, "pii.id.somali_nid");
        assert_eq!(&text[spans[0].start..spans[0].end], "AB1234567");
    }

    #[wasm_bindgen_test]
    fn test_custom_rule() {
        let text = "Employee ID: EMP-123456";
        let custom_rule = Rule::Custom {
            type_: "pii.custom.employee_id".to_string(),
            regex: r"EMP-\d{6}".to_string(),
        };
        
        let rules = vec![custom_rule];
        let spans = find_pii_internal(text, rules);
        
        assert_eq!(spans.len(), 1);
        assert_eq!(spans[0].type_, "pii.custom.employee_id");
        assert_eq!(&text[spans[0].start..spans[0].end], "EMP-123456");
    }

    #[wasm_bindgen_test]
    fn test_multiple_rules() {
        let text = "Contact: test@example.com, SSN: 123-45-6789";
        let rules = vec![Rule::Email, Rule::Ssn];
        
        let spans = find_pii_internal(text, rules);
        
        assert_eq!(spans.len(), 2);
        assert_eq!(spans[0].type_, "pii.email");
        assert_eq!(spans[1].type_, "pii.ssn");
    }

    #[wasm_bindgen_test]
    fn test_no_matches() {
        let text = "This text does not contain any PII.";
        let rules = vec![Rule::Email, Rule::Ssn, Rule::CreditCard, Rule::Phone, Rule::SomaliNid];
        
        let spans = find_pii_internal(text, rules);
        
        assert_eq!(spans.len(), 0);
    }

    #[wasm_bindgen_test]
    fn test_empty_text() {
        let text = "";
        let rules = vec![Rule::Email, Rule::Ssn];
        
        let spans = find_pii_internal(text, rules);
        
        assert_eq!(spans.len(), 0);
    }

    #[wasm_bindgen_test]
    fn test_invalid_custom_regex() {
        let text = "Some text";
        let custom_rule = Rule::Custom {
            type_: "pii.invalid".to_string(),
            regex: r"[invalid regex".to_string(), // Invalid regex
        };
        
        let rules = vec![custom_rule];
        let spans = find_pii_internal(text, rules);
        
        // Should not panic, should return empty results
        assert_eq!(spans.len(), 0);
    }

    // Helper function to call find_pii without dealing with JsValue conversion
    fn find_pii_internal(text: &str, rules: Vec<Rule>) -> Vec<Span> {
        let mut spans = Vec::new();

        for rule in rules {
            let rule_type = rule.get_type();
            let regex = rule.get_regex();
            
            for mat in regex.find_iter(text) {
                spans.push(Span {
                    start: mat.start(),
                    end: mat.end(),
                    type_: rule_type.clone(),
                });
            }
        }

        // Sort spans by start position
        spans.sort_by_key(|span| span.start);
        
        spans
    }
}