70 lines
2.0 KiB
Python
70 lines
2.0 KiB
Python
"""Tests for `app.core.embeddings.HashEmbedder`."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import math
|
|
|
|
import pytest
|
|
|
|
from app.core.embeddings import HashEmbedder
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_hash_embedder_returns_correct_dimension():
|
|
emb = HashEmbedder(dimension=128)
|
|
vecs = await emb.embed(["hello world"])
|
|
assert len(vecs) == 1
|
|
assert len(vecs[0]) == 128
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_hash_embedder_empty_text_returns_zero_vector():
|
|
emb = HashEmbedder(dimension=64)
|
|
vecs = await emb.embed([""])
|
|
assert vecs[0] == [0.0] * 64
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_hash_embedder_deterministic():
|
|
emb = HashEmbedder(dimension=64)
|
|
v1 = (await emb.embed(["the quick brown fox"]))[0]
|
|
v2 = (await emb.embed(["the quick brown fox"]))[0]
|
|
assert v1 == v2
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_hash_embedder_normalized():
|
|
emb = HashEmbedder(dimension=64)
|
|
v = (await emb.embed(["some text with multiple words for hashing"]))[0]
|
|
norm = math.sqrt(sum(x * x for x in v))
|
|
assert abs(norm - 1.0) < 1e-6
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_hash_embedder_similar_texts_have_overlap():
|
|
"""Texts sharing tokens should have non-zero cosine similarity."""
|
|
emb = HashEmbedder(dimension=256)
|
|
v1 = (await emb.embed(["the dragon attacks the village"]))[0]
|
|
v2 = (await emb.embed(["the dragon breathes fire"]))[0]
|
|
v3 = (await emb.embed(["quantum mechanics equations"]))[0]
|
|
# Cosine similarity (vectors are already normalized)
|
|
sim_12 = sum(a * b for a, b in zip(v1, v2))
|
|
sim_13 = sum(a * b for a, b in zip(v1, v3))
|
|
# Shared-token texts should be more similar than disjoint ones
|
|
assert sim_12 > sim_13
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_hash_embedder_batch():
|
|
emb = HashEmbedder(dimension=32)
|
|
vecs = await emb.embed(["a", "b", "c"])
|
|
assert len(vecs) == 3
|
|
assert all(len(v) == 32 for v in vecs)
|
|
|
|
|
|
def test_hash_embedder_invalid_dimension():
|
|
with pytest.raises(ValueError):
|
|
HashEmbedder(dimension=0)
|
|
with pytest.raises(ValueError):
|
|
HashEmbedder(dimension=-1)
|