Files
wehub-resource-sync c56bef871b
Sync docs with Docusaurus / sync (push) Waiting to run
Tests / Check if changed (push) Waiting to run
Tests / format (push) Blocked by required conditions
Tests / check-imports (push) Blocked by required conditions
Tests / Unit / macos-latest (push) Blocked by required conditions
Tests / Unit / ubuntu-latest (push) Blocked by required conditions
Tests / Unit / windows-latest (push) Blocked by required conditions
Tests / mypy (push) Blocked by required conditions
Tests / Integration / ubuntu-latest (push) Blocked by required conditions
Tests / Integration / macos-latest (push) Blocked by required conditions
Tests / Integration / windows-latest (push) Blocked by required conditions
Tests / notify-slack-on-failure (push) Blocked by required conditions
Tests / Mark tests as completed (push) Blocked by required conditions
Docker image release / Build base image (push) Waiting to run
CodeQL / Analyze (python) (push) Has been cancelled
Update Platform Components Table / update (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:22:28 +08:00

201 lines
8.1 KiB
Python

# SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>
#
# SPDX-License-Identifier: Apache-2.0
import json
from unittest.mock import patch
import pytest
from haystack import Document
from haystack.utils.misc import (
_deduplicate_documents,
_normalize_metadata_field_name,
_parse_dict_from_json,
_reciprocal_rank_fusion,
expand_page_range,
)
class TestNormalizeMetadataFieldName:
def test_removes_meta_prefix(self):
assert _normalize_metadata_field_name("meta.year") == "year"
assert _normalize_metadata_field_name("meta.category") == "category"
def test_returns_unchanged_when_no_prefix(self):
assert _normalize_metadata_field_name("year") == "year"
assert _normalize_metadata_field_name("category") == "category"
def test_meta_prefix_only_returns_empty_string(self):
assert _normalize_metadata_field_name("meta.") == ""
def test_does_not_strip_meta_substring(self):
assert _normalize_metadata_field_name("my_meta_field") == "my_meta_field"
class TestDeduplicateDocuments:
def test_deduplicate_documents_keeps_highest_score(self):
documents = [
Document(id="duplicate", content="keep me", score=0.9),
Document(id="duplicate", content="drop me", score=0.1),
Document(id="unique", content="unique"),
]
result = _deduplicate_documents(documents)
assert len(result) == 2
assert result[0].content == "keep me"
assert result[1].content == "unique"
def test_deduplicate_documents_keeps_first_when_scores_missing(self):
documents = [Document(id="duplicate", content="first"), Document(id="duplicate", content="second")]
result = _deduplicate_documents(documents)
assert len(result) == 1
assert result[0].content == "first"
class TestReciprocalRankFusion:
def test_empty_input_returns_empty(self):
assert _reciprocal_rank_fusion([]) == []
def test_single_list_assigns_scores(self):
docs = [Document(id="a"), Document(id="b"), Document(id="c")]
result = _reciprocal_rank_fusion([docs])
assert len(result) == 3
assert all(doc.score is not None for doc in result)
def test_scores_decrease_with_rank(self):
docs = [Document(id="a"), Document(id="b"), Document(id="c")]
result = _reciprocal_rank_fusion([docs])
by_id = {doc.id: doc.score for doc in result}
assert by_id["a"] is not None and by_id["b"] is not None and by_id["c"] is not None
assert by_id["a"] > by_id["b"] > by_id["c"]
def test_deduplicates_across_lists(self):
docs_a = [Document(id="a"), Document(id="b")]
docs_b = [Document(id="b"), Document(id="c")]
result = _reciprocal_rank_fusion([docs_a, docs_b])
assert len(result) == 3
assert {doc.id for doc in result} == {"a", "b", "c"}
def test_higher_ranked_doc_gets_higher_score(self):
docs_a = [Document(id="a"), Document(id="b"), Document(id="c")]
docs_b = [Document(id="c"), Document(id="a"), Document(id="d")]
result = _reciprocal_rank_fusion([docs_a, docs_b])
by_id = {doc.id: doc.score for doc in result}
# "a" is ranked 1st and 2nd; "c" is ranked 3rd and 1st — "a" should win
assert by_id["a"] is not None and by_id["c"] is not None
assert by_id["a"] > by_id["c"]
def test_equal_weights_by_default(self):
docs_a = [Document(id="x")]
docs_b = [Document(id="x")]
result_default = _reciprocal_rank_fusion([docs_a, docs_b])
result_explicit = _reciprocal_rank_fusion([docs_a, docs_b], weights=[0.5, 0.5])
assert result_default[0].score == pytest.approx(result_explicit[0].score)
def test_weights_influence_scores(self):
docs_a = [Document(id="a"), Document(id="b")]
docs_b = [Document(id="b"), Document(id="a")]
result_equal = _reciprocal_rank_fusion([docs_a, docs_b])
result_weighted = _reciprocal_rank_fusion([docs_a, docs_b], weights=[0.9, 0.1])
equal_by_id = {doc.id: doc.score for doc in result_equal}
weighted_by_id = {doc.id: doc.score for doc in result_weighted}
# with equal weights both docs score the same; with heavy weight on list_a, "a" should outscore "b"
assert equal_by_id["a"] == pytest.approx(equal_by_id["b"])
assert weighted_by_id["a"] is not None and weighted_by_id["b"] is not None
assert weighted_by_id["a"] > weighted_by_id["b"]
class TestJsonParsing:
@pytest.fixture
def mock_logger(self):
with patch("haystack.utils.misc.logger") as mock:
yield mock
def test_parse_dict_from_json_valid(self):
text = '{"key": "value"}'
assert _parse_dict_from_json(text) == {"key": "value"}
def test_parse_dict_from_json_invalid(self):
text = "invalid json"
with pytest.raises(json.JSONDecodeError):
_parse_dict_from_json(text)
def test_parse_dict_from_json_empty(self):
text = ""
with pytest.raises(json.JSONDecodeError):
_parse_dict_from_json(text)
def test_parse_dict_from_json_expected_keys(self):
text = '{"key1": "value1", "key2": "value2"}'
result = _parse_dict_from_json(text, expected_keys=["key1"])
assert result == {"key1": "value1", "key2": "value2"}
def test_parse_dict_from_json_missing_keys(self):
text = '{"key1": "value1"}'
with pytest.raises(ValueError, match="Missing expected keys"):
_parse_dict_from_json(text, expected_keys=["key1", "key2"])
def test_parse_dict_from_json_not_dict(self):
text = '["item1", "item2"]'
with pytest.raises(ValueError, match="Expected a JSON object"):
_parse_dict_from_json(text, expected_keys=["key1"])
def test_parse_dict_from_json_not_dict_raise_false(self, mock_logger):
text = '["item1", "item2"]'
result = _parse_dict_from_json(text, expected_keys=["key1"], raise_on_failure=False)
assert result is None
mock_logger.warning.assert_called_once()
args, kwargs = mock_logger.warning.call_args
assert "Expected a JSON object containing a dictionary but got {type}" in args[0]
assert kwargs["type"] == "list"
def test_parse_dict_from_json_invalid_raise_false(self, mock_logger):
text = "invalid json"
result = _parse_dict_from_json(text, raise_on_failure=False)
assert result is None
mock_logger.warning.assert_called_once()
args, kwargs = mock_logger.warning.call_args
assert "Failed to parse JSON from text: {text}" in args[0]
assert kwargs["text"] == text
assert isinstance(kwargs["error"], json.JSONDecodeError)
def test_parse_dict_from_json_missing_keys_raise_false(self, mock_logger):
text = '{"key1": "value1"}'
result = _parse_dict_from_json(text, expected_keys=["key1", "key2"], raise_on_failure=False)
assert result is None
mock_logger.warning.assert_called_once()
args, kwargs = mock_logger.warning.call_args
assert "Missing expected keys in JSON: {missing_keys}" in args[0]
assert kwargs["missing_keys"] == ["key2"]
assert kwargs["keys"] == ["key1"]
class TestExpandPageRange:
def test_single_page_integers(self):
assert expand_page_range([1, 3, 5]) == [1, 3, 5]
def test_single_page_strings(self):
assert expand_page_range(["1", "3", "5"]) == [1, 3, 5]
def test_range_strings_expanded(self):
assert expand_page_range(["1-3", "5", "8", "10-12"]) == [1, 2, 3, 5, 8, 10, 11, 12]
def test_mixed_integers_and_range_strings(self):
assert expand_page_range([1, "3-5", 7]) == [1, 3, 4, 5, 7]
def test_empty_input_raises_value_error(self):
with pytest.raises(ValueError, match="No valid page numbers"):
expand_page_range([])
def test_invalid_string_raises_value_error(self):
with pytest.raises(ValueError, match="Invalid page range"):
expand_page_range(["abc"])
def test_malformed_range_with_multiple_hyphens_raises_value_error(self):
with pytest.raises(ValueError, match="Invalid page range"):
expand_page_range(["1-3", "5-10-15"])