Co-authored-by: Clément Sirieix <clement.sirieix@mistral.ai>
Co-authored-by: Kim-Adeline Miguel <51720070+kimadeline@users.noreply.github.com>
Co-authored-by: Mathias Gesbert <mathias.gesbert@mistral.ai>
Co-authored-by: Maxime Dolores <maxime.dolores@ext.mistral.ai>
Co-authored-by: Michel Thomazo <51709227+michelTho@users.noreply.github.com>
Co-authored-by: Paul Cacheux <paul.cacheux@mistral.ai>
Co-authored-by: Quentin <quentin.torroba@mistral.ai>
Co-authored-by: Simon <80467011+sorgfresser@users.noreply.github.com>
Co-authored-by: angelapopopo <angele.lenglemetz@mistral.ai>
Co-authored-by: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
Pierre Rossinès 2026-04-16 11:10:26 +02:00 committed by GitHub
parent e1a25caa52
commit 95336528f6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
59 changed files with 3992 additions and 403 deletions

163
tests/core/test_merge.py Normal file
View file

@ -0,0 +1,163 @@
from __future__ import annotations
from unittest.mock import MagicMock
import pytest
from vibe.core.utils.merge import MergeConflictError, MergeStrategy
class TestMergeStrategyEnum:
def test_values_are_lowercase_strings(self) -> None:
assert MergeStrategy.REPLACE == "replace"
assert MergeStrategy.CONCAT == "concat"
assert MergeStrategy.UNION == "union"
assert MergeStrategy.MERGE == "merge"
assert MergeStrategy.CONFLICT == "conflict"
def test_all_strategies_exist(self) -> None:
assert len(MergeStrategy) == 5
def test_unknown_strategy_raises_not_implemented(self) -> None:
fake = MagicMock(spec=MergeStrategy)
with pytest.raises(NotImplementedError, match="not implemented"):
MergeStrategy.apply(fake, "a", "b")
class TestReplace:
def test_override_wins(self) -> None:
assert MergeStrategy.REPLACE.apply(1, 2) == 2
def test_base_returned_when_override_none(self) -> None:
assert MergeStrategy.REPLACE.apply(1, None) == 1
def test_both_none_returns_none(self) -> None:
assert MergeStrategy.REPLACE.apply(None, None) is None
def test_override_none_preserves_base_type(self) -> None:
assert MergeStrategy.REPLACE.apply([1, 2], None) == [1, 2]
assert MergeStrategy.REPLACE.apply({"a": 1}, None) == {"a": 1}
def test_override_can_be_falsy(self) -> None:
assert MergeStrategy.REPLACE.apply(1, 0) == 0
assert MergeStrategy.REPLACE.apply(1, "") == ""
assert MergeStrategy.REPLACE.apply(1, False) is False
class TestConcat:
def test_lists_concatenated(self) -> None:
assert MergeStrategy.CONCAT.apply([1, 2], [3, 4]) == [1, 2, 3, 4]
def test_base_none_returns_override(self) -> None:
assert MergeStrategy.CONCAT.apply(None, [1]) == [1]
def test_override_none_returns_base(self) -> None:
assert MergeStrategy.CONCAT.apply([1], None) == [1]
def test_both_none_returns_none(self) -> None:
assert MergeStrategy.CONCAT.apply(None, None) is None
def test_empty_lists(self) -> None:
assert MergeStrategy.CONCAT.apply([], []) == []
def test_raises_type_error_for_non_list(self) -> None:
with pytest.raises(TypeError, match="CONCAT requires list operands"):
MergeStrategy.CONCAT.apply("a", [1])
with pytest.raises(TypeError, match="CONCAT requires list operands"):
MergeStrategy.CONCAT.apply([1], "b")
class TestUnion:
def test_merge_by_key_override_wins(self) -> None:
base = [{"name": "a", "v": 1}, {"name": "b", "v": 2}]
override = [{"name": "b", "v": 99}]
result = MergeStrategy.UNION.apply(base, override, key_fn=lambda x: x["name"])
assert result == [{"name": "a", "v": 1}, {"name": "b", "v": 99}]
def test_preserves_order_base_first(self) -> None:
base = [{"name": "a"}, {"name": "b"}]
override = [{"name": "c"}]
result = MergeStrategy.UNION.apply(base, override, key_fn=lambda x: x["name"])
assert [x["name"] for x in result] == ["a", "b", "c"]
def test_new_keys_from_override_appended(self) -> None:
base = [{"name": "a"}]
override = [{"name": "b"}, {"name": "c"}]
result = MergeStrategy.UNION.apply(base, override, key_fn=lambda x: x["name"])
assert len(result) == 3
def test_base_none_returns_override(self) -> None:
assert MergeStrategy.UNION.apply(None, [1], key_fn=str) == [1]
def test_override_none_returns_base(self) -> None:
assert MergeStrategy.UNION.apply([1], None, key_fn=str) == [1]
def test_both_none_returns_none(self) -> None:
assert MergeStrategy.UNION.apply(None, None) is None
def test_raises_without_key_fn(self) -> None:
with pytest.raises(ValueError, match="UNION strategy requires key_fn"):
MergeStrategy.UNION.apply([1], [2])
def test_raises_type_error_for_non_list(self) -> None:
with pytest.raises(TypeError, match="UNION requires list operands"):
MergeStrategy.UNION.apply("a", [1], key_fn=str)
class TestMerge:
def test_dicts_merged_one_level(self) -> None:
result = MergeStrategy.MERGE.apply({"a": 1, "b": 2}, {"b": 3, "c": 4})
assert result == {"a": 1, "b": 3, "c": 4}
def test_nested_dicts_not_recursed(self) -> None:
base = {"a": {"x": 1, "y": 2}}
override = {"a": {"y": 3, "z": 4}}
result = MergeStrategy.MERGE.apply(base, override)
assert result == {"a": {"y": 3, "z": 4}}
def test_base_none_returns_override(self) -> None:
assert MergeStrategy.MERGE.apply(None, {"a": 1}) == {"a": 1}
def test_override_none_returns_base(self) -> None:
assert MergeStrategy.MERGE.apply({"a": 1}, None) == {"a": 1}
def test_both_none_returns_none(self) -> None:
assert MergeStrategy.MERGE.apply(None, None) is None
def test_raises_type_error_for_non_dict(self) -> None:
with pytest.raises(TypeError, match="MERGE requires dict operands"):
MergeStrategy.MERGE.apply([1], {"a": 1})
def test_does_not_mutate_inputs(self) -> None:
base = {"a": 1}
override = {"b": 2}
MergeStrategy.MERGE.apply(base, override)
assert base == {"a": 1}
assert override == {"b": 2}
class TestConflict:
def test_raises_when_both_provided(self) -> None:
with pytest.raises(MergeConflictError):
MergeStrategy.CONFLICT.apply(1, 2)
def test_base_only_returns_base(self) -> None:
assert MergeStrategy.CONFLICT.apply(1, None) == 1
def test_override_only_returns_override(self) -> None:
assert MergeStrategy.CONFLICT.apply(None, 2) == 2
def test_both_none_returns_none(self) -> None:
assert MergeStrategy.CONFLICT.apply(None, None) is None
class TestMergeConflictError:
def test_default_message(self) -> None:
err = MergeConflictError()
assert str(err) == "Merge conflict"
assert err.field_name == ""
def test_message_with_field_name(self) -> None:
err = MergeConflictError("active_model")
assert str(err) == "Merge conflict on field 'active_model'"
assert err.field_name == "active_model"

View file

@ -489,7 +489,7 @@ class TestTelemetryClient:
assert (
client._get_telemetry_url("not-a-valid-url")
== "https://codestral.mistral.ai/v1/datalake/events"
== "https://api.mistral.ai/v1/datalake/events"
)
def test_is_active_false_when_mistral_provider_exists_but_no_api_key(