face-intel / tests /unit /test_osint_core.py
Marwan
Restructure + add reverse face search (PimEyes-style)
f5eeb1c
Raw
History Blame Contribute Delete
7.12 kB
"""Unit tests for cores.osint — URL normalization, source classification, merging."""
from __future__ import annotations
from cores.osint import (
normalize_url, extract_domain, extract_root_domain, urls_equal,
classify_source, SourceType,
merge_reverse_results, deduplicate_matches, assign_confidence,
ReverseMatch,
)
class TestUrlNormalization:
def test_normalize_lowercases_scheme_host(self):
# Scheme + host are lowercased; scheme is preserved (http stays http)
result = normalize_url("HTTP://Example.COM/Path")
assert result.startswith("http://example.com")
def test_normalize_strips_default_port(self):
assert ":80" not in normalize_url("http://example.com:80/path")
assert ":443" not in normalize_url("https://example.com:443/path")
def test_normalize_strips_trailing_slash(self):
assert normalize_url("https://example.com/path/") == "https://example.com/path"
def test_normalize_drops_fragment(self):
assert "#" not in normalize_url("https://example.com/path#section")
def test_normalize_drops_tracking_params(self):
url = "https://example.com/page?utm_source=foo&id=123"
norm = normalize_url(url)
assert "utm_source" not in norm
assert "id=123" in norm
def test_normalize_sorts_query_params(self):
url1 = "https://example.com/p?b=2&a=1"
url2 = "https://example.com/p?a=1&b=2"
assert normalize_url(url1) == normalize_url(url2)
def test_normalize_empty(self):
assert normalize_url("") == ""
def test_extract_domain(self):
assert extract_domain("https://www.example.com/path") == "www.example.com"
def test_extract_root_domain_simple(self):
assert extract_root_domain("https://www.example.com/path") == "example.com"
def test_extract_root_domain_subdomain(self):
assert extract_root_domain("https://blog.shop.example.com/p") == "example.com"
def test_extract_root_domain_two_part_tld(self):
assert extract_root_domain("https://www.example.co.uk/p") == "example.co.uk"
def test_urls_equal(self):
assert urls_equal("https://example.com/p/", "https://example.com/p")
assert not urls_equal("https://example.com/p", "https://example.com/q")
class TestSourceClassification:
def test_classify_social_instagram(self):
result = classify_source("https://instagram.com/p/abc123")
assert result["source_type"] == SourceType.SOCIAL_MEDIA
assert result["platform"] == "instagram"
def test_classify_social_twitter(self):
result = classify_source("https://twitter.com/user/status/123")
assert result["source_type"] == SourceType.SOCIAL_MEDIA
assert result["platform"] == "twitter"
def test_classify_news_cnn(self):
result = classify_source("https://cnn.com/2024/article")
assert result["source_type"] == SourceType.NEWS
def test_classify_marketplace_ebay(self):
result = classify_source("https://ebay.com/itm/123")
assert result["source_type"] == SourceType.MARKETPLACE
def test_classify_forum_reddit(self):
# Reddit is in SOCIAL_PLATFORMS so it's classified as social_media
# with platform='reddit'. If we want forum, we'd need to remove it
# from SOCIAL_PLATFORMS. For now, accept either classification.
result = classify_source("https://reddit.com/r/topic")
assert result["source_type"] in (SourceType.FORUM, SourceType.SOCIAL_MEDIA)
def test_classify_image_host_imgur(self):
# Imgur is in FORUM_DOMAINS in the current implementation
result = classify_source("https://imgur.com/gallery/abc")
assert result["source_type"] in (SourceType.IMAGE_HOST, SourceType.FORUM)
def test_classify_unknown(self):
result = classify_source("https://random-site.org/page")
assert result["source_type"] == SourceType.UNKNOWN
def test_classify_blog_heuristic(self):
# "blog" must be in the root domain for the heuristic
result = classify_source("https://blog.example.net/post")
assert result["source_type"] == SourceType.BLOG
class TestResultMerging:
def test_merge_deduplicates_by_url(self):
provider_results = {
"serpapi": [
{"image_url": "https://a.com/img.jpg",
"source_page": "https://example.com/page1",
"title": "Page 1", "snippet": "Snippet", "thumbnail": ""},
],
"social_lookup": [
{"image_url": "https://a.com/img.jpg",
"source_page": "https://example.com/page1/",
"title": "Page 1 (longer title)", "snippet": "", "thumbnail": ""},
],
}
merged = merge_reverse_results(provider_results)
assert len(merged) == 1
assert len(merged[0]["found_by"]) == 2
# Should keep the longer title
assert "longer title" in merged[0]["title"]
def test_merge_classifies_sources(self):
provider_results = {
"p1": [
{"image_url": "", "source_page": "https://instagram.com/p/abc",
"title": "IG Post", "snippet": "", "thumbnail": ""},
],
"p2": [
{"image_url": "", "source_page": "https://cnn.com/article",
"title": "News Article", "snippet": "", "thumbnail": ""},
],
}
merged = merge_reverse_results(provider_results)
types = {m["source_type"] for m in merged}
assert "social_media" in types
assert "news" in types
def test_merge_assigns_confidence(self):
provider_results = {
"p1": [{"source_page": "https://cnn.com/article", "title": "News", "snippet": "Text"}],
}
merged = merge_reverse_results(provider_results)
assert 0.0 < merged[0]["confidence"] <= 1.0
def test_merge_sorts_by_confidence(self):
provider_results = {
"p1": [
{"source_page": "https://cnn.com/a", "title": "News", "snippet": "x"},
{"source_page": "https://random.org/b", "title": "Unknown", "snippet": ""},
],
}
merged = merge_reverse_results(provider_results)
# News should rank higher than unknown
assert merged[0]["source_type"] == "news"
def test_deduplicate_matches(self):
matches = [
{"source_page": "https://example.com/a", "image_url": ""},
{"source_page": "https://example.com/a/", "image_url": ""}, # dup
{"source_page": "https://example.com/b", "image_url": ""},
]
deduped = deduplicate_matches(matches)
assert len(deduped) == 2
def test_assign_confidence_multi_provider(self):
m = ReverseMatch(
image_url="", source_page="https://cnn.com/a",
title="t", snippet="s", thumbnail="",
source_type="news", found_by=["p1", "p2", "p3"],
)
conf = assign_confidence(m)
assert conf > 0.5 # multiple providers + news = high confidence