Skip to main content
Glama
wolfiesch

iMessage MCP Server

by wolfiesch
test_contacts_sync.py10.3 kB
""" Unit tests for contacts sync functionality (Sprint 2). Tests: - Fuzzy name matching - Phone number normalization - macOS Contacts reading (requires macOS) """ import pytest from src.contacts_sync import ( FuzzyNameMatcher, normalize_phone_number, compare_phone_numbers, MacOSContact ) class TestFuzzyNameMatcher: """Test fuzzy name matching algorithm.""" def test_exact_match(self): """Test exact name match returns 1.0.""" matcher = FuzzyNameMatcher(threshold=0.85) score = matcher.calculate_similarity("John Doe", "John Doe") assert score == 1.0 def test_case_insensitive(self): """Test case-insensitive matching.""" matcher = FuzzyNameMatcher(threshold=0.85) score = matcher.calculate_similarity("John Doe", "john doe") assert score == 1.0 def test_typo_matching(self): """Test matching with typos.""" matcher = FuzzyNameMatcher(threshold=0.85) score = matcher.calculate_similarity("John Doe", "Jon Doe") assert score > 0.85, "Should match 'Jon Doe' to 'John Doe' (typo)" def test_partial_name_matching(self): """Test partial name matching.""" matcher = FuzzyNameMatcher(threshold=0.80) score = matcher.calculate_similarity("John", "John Doe") assert score >= 0.80, "Should match 'John' to 'John Doe'" def test_nickname_matching(self): """Test nickname matching.""" matcher = FuzzyNameMatcher(threshold=0.75) score = matcher.calculate_similarity("Mike Smith", "Michael Smith") # Note: This might not match perfectly without nickname dictionary # but should still score reasonably high assert score > 0.60, "Should partially match nickname" def test_word_order_matching(self): """Test matching with different word order.""" matcher = FuzzyNameMatcher(threshold=0.85) score = matcher.calculate_similarity("Doe John", "John Doe") assert score >= 0.85, "Should match despite word order" def test_find_best_match(self): """Test finding best match from candidates.""" matcher = FuzzyNameMatcher(threshold=0.85) candidates = ["John Doe", "Jane Smith", "John Smith", "Bob Johnson"] result = matcher.find_best_match("John Doe", candidates) assert result is not None best_match, score = result assert best_match == "John Doe" assert score == 1.0 def test_find_best_match_typo(self): """Test finding best match with typo.""" matcher = FuzzyNameMatcher(threshold=0.85) candidates = ["John Doe", "Jane Smith", "John Smith"] result = matcher.find_best_match("Jon Doe", candidates) assert result is not None best_match, score = result assert best_match == "John Doe" assert score > 0.85 def test_no_match_below_threshold(self): """Test that low matches return None.""" matcher = FuzzyNameMatcher(threshold=0.90) candidates = ["John Doe", "Jane Smith"] result = matcher.find_best_match("Bob Johnson", candidates) assert result is None, "Should not match unrelated names" def test_find_all_matches(self): """Test finding multiple matches.""" matcher = FuzzyNameMatcher(threshold=0.80) candidates = [ "John Doe", "John Smith", "Johnny Doe", "Jane Doe", "Bob Johnson" ] matches = matcher.find_all_matches("John Doe", candidates, limit=3) assert len(matches) > 0 assert matches[0][0] == "John Doe" # Best match first assert matches[0][1] == 1.0 # Should include other "John" names names = [m[0] for m in matches] assert "John Smith" in names or "Johnny Doe" in names class TestPhoneNumberNormalization: """Test phone number normalization.""" def test_basic_us_number(self): """Test basic US number normalization.""" assert normalize_phone_number("(415) 555-1234") == "14155551234" def test_us_number_with_country_code(self): """Test US number with +1 country code.""" assert normalize_phone_number("+1 (415) 555-1234") == "14155551234" def test_dots_format(self): """Test number with dots as separators.""" assert normalize_phone_number("415.555.1234") == "14155551234" def test_spaces_format(self): """Test number with spaces.""" assert normalize_phone_number("415 555 1234") == "14155551234" def test_no_formatting(self): """Test plain number.""" assert normalize_phone_number("4155551234") == "14155551234" def test_already_normalized(self): """Test already normalized number.""" assert normalize_phone_number("14155551234") == "14155551234" def test_international_number(self): """Test international number (UK).""" # +44 20 7946 0958 assert normalize_phone_number("+44 20 7946 0958") == "442079460958" def test_international_number_germany(self): """Test German number.""" # +49 30 123456 assert normalize_phone_number("+49 30 123456") == "4930123456" def test_empty_string(self): """Test empty string.""" assert normalize_phone_number("") == "" def test_invalid_input(self): """Test invalid input (no digits).""" assert normalize_phone_number("abc-def-ghij") == "" class TestPhoneNumberComparison: """Test phone number comparison.""" def test_exact_match(self): """Test exact match.""" assert compare_phone_numbers("14155551234", "14155551234") def test_format_differences(self): """Test different formats of same number.""" assert compare_phone_numbers( "+1 (415) 555-1234", "415.555.1234" ) def test_country_code_difference(self): """Test with/without country code.""" assert compare_phone_numbers("14155551234", "4155551234") assert compare_phone_numbers("4155551234", "14155551234") def test_different_numbers(self): """Test different numbers don't match.""" assert not compare_phone_numbers("14155551234", "14155555678") def test_empty_numbers(self): """Test empty numbers don't match.""" assert not compare_phone_numbers("", "14155551234") assert not compare_phone_numbers("14155551234", "") assert not compare_phone_numbers("", "") class TestMacOSContact: """Test MacOSContact class.""" def test_full_name_both_names(self): """Test full name with given and family name.""" contact = MacOSContact( identifier="123", given_name="John", family_name="Doe" ) assert contact.full_name == "John Doe" def test_full_name_given_only(self): """Test full name with only given name.""" contact = MacOSContact( identifier="123", given_name="John" ) assert contact.full_name == "John" def test_full_name_family_only(self): """Test full name with only family name.""" contact = MacOSContact( identifier="123", family_name="Doe" ) assert contact.full_name == "Doe" def test_full_name_organization(self): """Test full name falls back to organization.""" contact = MacOSContact( identifier="123", organization="Acme Corp" ) assert contact.full_name == "Acme Corp" def test_full_name_unknown(self): """Test full name defaults to Unknown.""" contact = MacOSContact(identifier="123") assert contact.full_name == "Unknown" def test_phone_numbers(self): """Test phone numbers storage.""" contact = MacOSContact( identifier="123", given_name="John", phone_numbers=[ {"label": "mobile", "value": "+14155551234"}, {"label": "work", "value": "+14155555678"} ] ) assert len(contact.phone_numbers) == 2 assert contact.phone_numbers[0]["label"] == "mobile" def test_email_addresses(self): """Test email addresses storage.""" contact = MacOSContact( identifier="123", given_name="John", email_addresses=[ {"label": "work", "value": "john@example.com"} ] ) assert len(contact.email_addresses) == 1 assert contact.email_addresses[0]["value"] == "john@example.com" # Integration test (requires macOS and permissions) # Skip by default - run with: pytest --run-integration class TestMacOSContactsReader: """Integration tests for macOS Contacts reader.""" @pytest.mark.skip(reason="Requires macOS Contacts access - run manually") def test_fetch_all_contacts(self): """Test fetching all contacts from macOS.""" from src.contacts_sync import MacOSContactsReader try: reader = MacOSContactsReader() contacts = reader.fetch_all_contacts() assert isinstance(contacts, list) assert len(contacts) > 0, "Should have at least one contact" # Check first contact structure contact = contacts[0] assert hasattr(contact, "identifier") assert hasattr(contact, "full_name") assert hasattr(contact, "phone_numbers") except RuntimeError as e: pytest.skip(f"Contacts access denied: {e}") @pytest.mark.skip(reason="Requires macOS Contacts access - run manually") def test_search_contacts(self): """Test searching contacts.""" from src.contacts_sync import MacOSContactsReader try: reader = MacOSContactsReader() # Search for a common name results = reader.search_contacts("John") assert isinstance(results, list) # May or may not have results depending on contacts except RuntimeError as e: pytest.skip(f"Contacts access denied: {e}") if __name__ == "__main__": # Run tests pytest.main([__file__, "-v"])

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/wolfiesch/imessage-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server