diff --git a/.coverage b/.coverage new file mode 100644 index 0000000..b26616e Binary files /dev/null and b/.coverage differ diff --git a/.gitignore b/.gitignore index 5556c9f..7004e84 100644 --- a/.gitignore +++ b/.gitignore @@ -6,5 +6,6 @@ venv/ *.pyc __pycache__ .pytest_cache/ +.coverage *.lock examples/rag/README.md diff --git a/tests/test_bit.py b/tests/test_bit.py index 5a71642..bc8fdf1 100644 --- a/tests/test_bit.py +++ b/tests/test_bit.py @@ -61,3 +61,80 @@ def test_repr(self): def test_equality(self): assert Bit([True, False, True]) == Bit([True, False, True]) assert Bit([True, False, True]) != Bit([True, False, False]) + + def test_equality_with_different_type(self): + assert Bit([True, False, True]) != [True, False, True] + assert Bit([True, False, True]) != "not a bit" + assert Bit([True, False, True]) != None + + def test_length(self): + assert len(Bit([True, False, True]).to_list()) == 3 + assert len(Bit('10101010').to_list()) == 8 + + def test_to_text(self): + assert Bit([True, False, True]).to_text() == '101' + assert Bit('10101010').to_text() == '10101010' + + def test_from_text(self): + bit = Bit.from_text('101') + assert bit.to_list() == [True, False, True] + + def test_to_binary(self): + bit = Bit([True, False, True]) + binary = bit.to_binary() + assert isinstance(binary, bytes) + assert len(binary) > 0 + + def test_from_binary(self): + bit = Bit([True, False, True, False, True, False, True, False]) + binary = bit.to_binary() + restored = Bit.from_binary(binary) + assert restored == bit + + def test_from_binary_error(self): + with pytest.raises(ValueError, match='expected bytes'): + Bit.from_binary('not bytes') + + def test_to_db(self): + bit = Bit([True, False, True]) + assert Bit._to_db(bit) == '101' + + def test_to_db_error(self): + with pytest.raises(ValueError, match='expected bit'): + Bit._to_db([True, False, True]) + + def test_to_db_binary(self): + bit = Bit([True, False, True]) + result = Bit._to_db_binary(bit) + assert isinstance(result, bytes) + + def test_to_db_binary_error(self): + with pytest.raises(ValueError, match='expected bit'): + Bit._to_db_binary([True, False, True]) + + def test_empty_bit(self): + bit = Bit([]) + assert bit.to_list() == [] + assert bit.to_text() == '' + + def test_single_bit(self): + bit = Bit([True]) + assert bit.to_list() == [True] + assert bit.to_text() == '1' + + def test_bytes_constructor(self): + bit = Bit(b'\x01') + assert len(bit.to_list()) == 8 + assert bit.to_text() == '00000001' + + def test_roundtrip_text(self): + original = Bit('10110011') + text = original.to_text() + restored = Bit.from_text(text) + assert restored == original + + def test_roundtrip_binary(self): + original = Bit([True, False, True, True, False, False, True, True]) + binary = original.to_binary() + restored = Bit.from_binary(binary) + assert restored == original diff --git a/tests/test_half_vector.py b/tests/test_half_vector.py index 78b4977..d36774a 100644 --- a/tests/test_half_vector.py +++ b/tests/test_half_vector.py @@ -43,6 +43,11 @@ def test_equality(self): assert HalfVector([1, 2, 3]) == HalfVector([1, 2, 3]) assert HalfVector([1, 2, 3]) != HalfVector([1, 2, 4]) + def test_equality_with_different_type(self): + assert HalfVector([1, 2, 3]) != [1, 2, 3] + assert HalfVector([1, 2, 3]) != "not a vector" + assert HalfVector([1, 2, 3]) != None + def test_dimensions(self): assert HalfVector([1, 2, 3]).dimensions() == 3 @@ -57,3 +62,75 @@ def test_from_binary(self): assert vec.to_list() == [1.5, 2, 3] assert np.array_equal(vec.to_numpy(), [1.5, 2, 3]) assert vec.to_binary() == data + + def test_to_text(self): + vec = HalfVector([1, 2, 3]) + assert vec.to_text() == '[1.0,2.0,3.0]' + + def test_to_db_none(self): + assert HalfVector._to_db(None) is None + + def test_to_db_vector(self): + vec = HalfVector([1, 2, 3]) + assert HalfVector._to_db(vec) == '[1.0,2.0,3.0]' + + def test_to_db_list(self): + assert HalfVector._to_db([1, 2, 3]) == '[1.0,2.0,3.0]' + + def test_to_db_with_dim(self): + assert HalfVector._to_db([1, 2, 3], 3) == '[1.0,2.0,3.0]' + + def test_to_db_wrong_dim(self): + with pytest.raises(ValueError, match='expected 3 dimensions, not 2'): + HalfVector._to_db([1, 2], 3) + + def test_to_db_binary_none(self): + assert HalfVector._to_db_binary(None) is None + + def test_to_db_binary_vector(self): + vec = HalfVector([1, 2, 3]) + result = HalfVector._to_db_binary(vec) + assert result == pack('>HH3e', 3, 0, 1, 2, 3) + + def test_to_db_binary_list(self): + result = HalfVector._to_db_binary([1, 2, 3]) + assert result == pack('>HH3e', 3, 0, 1, 2, 3) + + def test_from_db_none(self): + assert HalfVector._from_db(None) is None + + def test_from_db_halfvector(self): + vec = HalfVector([1, 2, 3]) + assert HalfVector._from_db(vec) is vec + + def test_from_db_text(self): + result = HalfVector._from_db('[1.5,2,3]') + assert isinstance(result, HalfVector) + assert result.to_list() == [1.5, 2, 3] + + def test_from_db_binary_none(self): + assert HalfVector._from_db_binary(None) is None + + def test_from_db_binary_halfvector(self): + vec = HalfVector([1, 2, 3]) + assert HalfVector._from_db_binary(vec) is vec + + def test_from_db_binary_bytes(self): + data = pack('>HH3e', 3, 0, 1.5, 2, 3) + result = HalfVector._from_db_binary(data) + assert isinstance(result, HalfVector) + assert result.to_list() == [1.5, 2, 3] + + def test_empty_vector(self): + vec = HalfVector([]) + assert vec.dimensions() == 0 + assert vec.to_list() == [] + + def test_single_element(self): + vec = HalfVector([42]) + assert vec.dimensions() == 1 + assert vec.to_list() == [42] + + def test_negative_values(self): + vec = HalfVector([-1, -2, -3]) + assert vec.to_list() == [-1, -2, -3] diff --git a/tests/test_sparse_vector.py b/tests/test_sparse_vector.py index d580f32..3e1a27a 100644 --- a/tests/test_sparse_vector.py +++ b/tests/test_sparse_vector.py @@ -67,6 +67,12 @@ def test_csr_matrix(self): assert vec.to_list() == [1, 0, 2, 0, 3, 0] assert vec.indices() == [0, 2, 4] + def test_sparse_array_wrong_ndim(self): + # 2D array with wrong ndim (not 1D as required) + arr = coo_array(np.array([[1, 0, 2], [0, 3, 0]])) + with pytest.raises(ValueError, match='expected ndim to be 1'): + SparseVector(arr) + def test_repr(self): assert repr(SparseVector([1, 0, 2, 0, 3, 0])) == 'SparseVector({0: 1.0, 2: 2.0, 4: 3.0}, 6)' assert str(SparseVector([1, 0, 2, 0, 3, 0])) == 'SparseVector({0: 1.0, 2: 2.0, 4: 3.0}, 6)' @@ -77,6 +83,11 @@ def test_equality(self): assert SparseVector([1, 0, 2, 0, 3, 0]) == SparseVector({2: 2, 4: 3, 0: 1, 3: 0}, 6) assert SparseVector({}, 1) != SparseVector({}, 2) + def test_equality_with_different_type(self): + assert SparseVector([1, 0, 2, 0, 3, 0]) != [1, 0, 2, 0, 3, 0] + assert SparseVector([1, 0, 2, 0, 3, 0]) != "not a sparse vector" + assert SparseVector([1, 0, 2, 0, 3, 0]) != None + def test_dimensions(self): assert SparseVector([1, 0, 2, 0, 3, 0]).dimensions() == 6 @@ -110,3 +121,93 @@ def test_from_binary(self): assert vec.to_list() == [1.5, 0, 2, 0, 3, 0] assert np.array_equal(vec.to_numpy(), [1.5, 0, 2, 0, 3, 0]) assert vec.to_binary() == data + + def test_to_text(self): + vec = SparseVector([1, 0, 2, 0, 3, 0]) + assert vec.to_text() == '{1:1.0,3:2.0,5:3.0}/6' + + def test_to_db_none(self): + assert SparseVector._to_db(None) is None + + def test_to_db_vector(self): + vec = SparseVector([1, 0, 2, 0, 3, 0]) + assert SparseVector._to_db(vec) == '{1:1.0,3:2.0,5:3.0}/6' + + def test_to_db_list(self): + result = SparseVector._to_db([1, 0, 2, 0, 3, 0]) + assert result == '{1:1.0,3:2.0,5:3.0}/6' + + def test_to_db_with_dim(self): + result = SparseVector._to_db([1, 0, 2, 0, 3, 0], 6) + assert result == '{1:1.0,3:2.0,5:3.0}/6' + + def test_to_db_wrong_dim(self): + with pytest.raises(ValueError, match='expected 6 dimensions, not 5'): + SparseVector._to_db([1, 0, 2, 0, 3], 6) + + def test_to_db_binary_none(self): + assert SparseVector._to_db_binary(None) is None + + def test_to_db_binary_vector(self): + vec = SparseVector([1, 0, 2, 0, 3, 0]) + result = SparseVector._to_db_binary(vec) + assert isinstance(result, bytes) + + def test_to_db_binary_list(self): + result = SparseVector._to_db_binary([1, 0, 2, 0, 3, 0]) + assert isinstance(result, bytes) + + def test_from_db_none(self): + assert SparseVector._from_db(None) is None + + def test_from_db_sparsevector(self): + vec = SparseVector([1, 0, 2, 0, 3, 0]) + assert SparseVector._from_db(vec) is vec + + def test_from_db_text(self): + result = SparseVector._from_db('{1:1.5,3:2,5:3}/6') + assert isinstance(result, SparseVector) + assert result.to_list() == [1.5, 0, 2, 0, 3, 0] + + def test_from_db_binary_none(self): + assert SparseVector._from_db_binary(None) is None + + def test_from_db_binary_sparsevector(self): + vec = SparseVector([1, 0, 2, 0, 3, 0]) + assert SparseVector._from_db_binary(vec) is vec + + def test_from_db_binary_bytes(self): + data = pack('>iii3i3f', 6, 3, 0, 0, 2, 4, 1.5, 2, 3) + result = SparseVector._from_db_binary(data) + assert isinstance(result, SparseVector) + assert result.to_list() == [1.5, 0, 2, 0, 3, 0] + + def test_empty_sparse_vector(self): + vec = SparseVector({}, 5) + assert vec.dimensions() == 5 + assert vec.indices() == [] + assert vec.values() == [] + assert vec.to_list() == [0, 0, 0, 0, 0] + + def test_single_nonzero(self): + vec = SparseVector({3: 42}, 10) + assert vec.dimensions() == 10 + assert vec.indices() == [3] + assert vec.values() == [42] + + def test_negative_values_sparse(self): + vec = SparseVector([-1, 0, -2, 0, -3]) + assert vec.values() == [-1, -2, -3] + assert vec.to_list() == [-1, 0, -2, 0, -3] + + def test_roundtrip_text_sparse(self): + original = SparseVector([1.5, 0, 2.5, 0, 3.5, 0]) + text = original.to_text() + restored = SparseVector.from_text(text) + assert restored == original + + def test_roundtrip_binary_sparse(self): + original = SparseVector([1.5, 0, 2.5, 0, 3.5, 0]) + binary = original.to_binary() + restored = SparseVector.from_binary(binary) + assert restored == original diff --git a/tests/test_vector.py b/tests/test_vector.py index e5a16fe..74014c7 100644 --- a/tests/test_vector.py +++ b/tests/test_vector.py @@ -43,6 +43,11 @@ def test_equality(self): assert Vector([1, 2, 3]) == Vector([1, 2, 3]) assert Vector([1, 2, 3]) != Vector([1, 2, 4]) + def test_equality_with_different_type(self): + assert Vector([1, 2, 3]) != [1, 2, 3] + assert Vector([1, 2, 3]) != "not a vector" + assert Vector([1, 2, 3]) != None + def test_dimensions(self): assert Vector([1, 2, 3]).dimensions() == 3 @@ -57,3 +62,83 @@ def test_from_binary(self): assert vec.to_list() == [1.5, 2, 3] assert np.array_equal(vec.to_numpy(), [1.5, 2, 3]) assert vec.to_binary() == data + + def test_to_text(self): + vec = Vector([1, 2, 3]) + assert vec.to_text() == '[1.0,2.0,3.0]' + + def test_to_db_none(self): + assert Vector._to_db(None) is None + + def test_to_db_vector(self): + vec = Vector([1, 2, 3]) + assert Vector._to_db(vec) == '[1.0,2.0,3.0]' + + def test_to_db_list(self): + assert Vector._to_db([1, 2, 3]) == '[1.0,2.0,3.0]' + + def test_to_db_with_dim(self): + assert Vector._to_db([1, 2, 3], 3) == '[1.0,2.0,3.0]' + + def test_to_db_wrong_dim(self): + with pytest.raises(ValueError, match='expected 3 dimensions, not 2'): + Vector._to_db([1, 2], 3) + + def test_to_db_binary_none(self): + assert Vector._to_db_binary(None) is None + + def test_to_db_binary_vector(self): + vec = Vector([1, 2, 3]) + result = Vector._to_db_binary(vec) + assert result == pack('>HH3f', 3, 0, 1, 2, 3) + + def test_to_db_binary_list(self): + result = Vector._to_db_binary([1, 2, 3]) + assert result == pack('>HH3f', 3, 0, 1, 2, 3) + + def test_from_db_none(self): + assert Vector._from_db(None) is None + + def test_from_db_ndarray(self): + arr = np.array([1, 2, 3], dtype=np.float32) + assert Vector._from_db(arr) is arr + + def test_from_db_text(self): + result = Vector._from_db('[1.5,2,3]') + expected = np.array([1.5, 2, 3], dtype=np.float32) + assert np.array_equal(result, expected) + + def test_from_db_binary_none(self): + assert Vector._from_db_binary(None) is None + + def test_from_db_binary_ndarray(self): + arr = np.array([1, 2, 3], dtype=np.float32) + assert Vector._from_db_binary(arr) is arr + + def test_from_db_binary_bytes(self): + data = pack('>HH3f', 3, 0, 1.5, 2, 3) + result = Vector._from_db_binary(data) + expected = np.array([1.5, 2, 3], dtype=np.float32) + assert np.array_equal(result, expected) + + def test_empty_vector(self): + vec = Vector([]) + assert vec.dimensions() == 0 + assert vec.to_list() == [] + + def test_single_element(self): + vec = Vector([42]) + assert vec.dimensions() == 1 + assert vec.to_list() == [42] + + def test_negative_values(self): + vec = Vector([-1, -2, -3]) + assert vec.to_list() == [-1, -2, -3] + + def test_float_precision(self): + vec = Vector([1.123456789, 2.987654321]) + # Float32 precision test + result = vec.to_list() + assert len(result) == 2 + assert abs(result[0] - 1.123456789) < 1e-6 + assert abs(result[1] - 2.987654321) < 1e-6