|
1 | | -"""Unit tests for service.update() model conversion flow. |
| 1 | +"""Unit tests for service model conversion flow. |
2 | 2 |
|
3 | 3 | These tests verify that update input types are routed through to_model(data, "update") |
4 | | -and the update lifecycle hook before persistence. |
| 4 | +and the update lifecycle hook before persistence. They also cover service-level |
| 5 | +input normalization before repository delegation. |
5 | 6 | """ |
6 | 7 |
|
7 | 8 | from __future__ import annotations |
|
17 | 18 | from advanced_alchemy.base import UUIDAuditBase |
18 | 19 | from advanced_alchemy.repository import SQLAlchemyAsyncRepository, SQLAlchemySyncRepository |
19 | 20 | from advanced_alchemy.repository._util import get_primary_key_info |
| 21 | +from advanced_alchemy.repository.typing import PrimaryKeyType |
20 | 22 | from advanced_alchemy.service import SchemaDumpConfig, SQLAlchemyAsyncRepositoryService, SQLAlchemySyncRepositoryService |
21 | 23 | from advanced_alchemy.utils.serialization import ATTRS_INSTALLED, MSGSPEC_INSTALLED, PYDANTIC_INSTALLED, ModelDictT |
22 | 24 |
|
@@ -141,6 +143,24 @@ def to_model_on_update(self, data: ModelDictT[MockModel]) -> ModelDictT[MockMode |
141 | 143 | # Tests for async service |
142 | 144 |
|
143 | 145 |
|
| 146 | +@pytest.mark.asyncio |
| 147 | +async def test_delete_many_passes_id_only_input_through_without_copying() -> None: |
| 148 | + """Id-only delete_many() input should keep the existing bulk-delete fast path.""" |
| 149 | + service = TrackingService() |
| 150 | + captured: dict[str, Any] = {} |
| 151 | + |
| 152 | + async def delete_many(item_ids: list[Any], **_: Any) -> list[MockModel]: |
| 153 | + captured["item_ids"] = item_ids |
| 154 | + return [] |
| 155 | + |
| 156 | + service.repository.delete_many = AsyncMock(side_effect=delete_many) # type: ignore[method-assign] |
| 157 | + |
| 158 | + item_ids: list[PrimaryKeyType] = ["first-id", "second-id"] |
| 159 | + await service.delete_many(item_ids) |
| 160 | + |
| 161 | + assert captured["item_ids"] is item_ids |
| 162 | + |
| 163 | + |
144 | 164 | @pytest.mark.asyncio |
145 | 165 | async def test_update_dict_calls_to_model_with_operation() -> None: |
146 | 166 | """Test that update() with dict data calls to_model(data, 'update').""" |
@@ -358,6 +378,43 @@ async def test_update_propagates_with_for_update_flag() -> None: |
358 | 378 | # Tests for sync service |
359 | 379 |
|
360 | 380 |
|
| 381 | +def test_sync_delete_many_passes_id_only_input_through_without_copying() -> None: |
| 382 | + """Sync id-only delete_many() input should keep the existing bulk-delete fast path.""" |
| 383 | + service = TrackingSyncService() |
| 384 | + captured: dict[str, Any] = {} |
| 385 | + |
| 386 | + def delete_many(item_ids: list[Any], **_: Any) -> list[MockModel]: |
| 387 | + captured["item_ids"] = item_ids |
| 388 | + return [] |
| 389 | + |
| 390 | + service.repository.delete_many = MagicMock(side_effect=delete_many) # type: ignore[method-assign] |
| 391 | + |
| 392 | + item_ids: list[PrimaryKeyType] = ["first-id", "second-id"] |
| 393 | + service.delete_many(item_ids) |
| 394 | + |
| 395 | + assert captured["item_ids"] is item_ids |
| 396 | + |
| 397 | + |
| 398 | +def test_sync_delete_many_resolves_model_instances_before_delegating() -> None: |
| 399 | + """Sync delete_many() should resolve model instances to primary key values.""" |
| 400 | + service = TrackingSyncService() |
| 401 | + captured: dict[str, Any] = {} |
| 402 | + |
| 403 | + def delete_many(item_ids: list[Any], **_: Any) -> list[MockModel]: |
| 404 | + captured["item_ids"] = item_ids |
| 405 | + return [] |
| 406 | + |
| 407 | + service.repository.delete_many = MagicMock(side_effect=delete_many) # type: ignore[method-assign] |
| 408 | + |
| 409 | + model = MockModel() |
| 410 | + model.id = "model-id" # type: ignore[assignment] |
| 411 | + item_ids = cast("list[PrimaryKeyType]", [model, "raw-id"]) |
| 412 | + service.delete_many(item_ids) |
| 413 | + |
| 414 | + assert captured["item_ids"] == ["model-id", "raw-id"] |
| 415 | + assert captured["item_ids"] is not item_ids |
| 416 | + |
| 417 | + |
361 | 418 | def test_sync_update_dict_calls_to_model_with_operation() -> None: |
362 | 419 | """Test that sync update() with dict data calls to_model(data, 'update').""" |
363 | 420 | service = TrackingSyncService() |
|
0 commit comments