- Implement JobsMixin for job-related operations including job overview, execution, and control. - Implement MediaMixin for media management, including creation, updating, and file uploads. - Implement ProductGroupsMixin for handling product groups, including bulk operations and hierarchy retrieval. - Implement ProductsMixin for product management, including bulk creation and updates. - Implement ProtocolsMixin for protocol-related operations, including retrieval by job ID and category. - Implement TextMixin for managing text entries, including bulk operations. - Implement TreeGroupsMixin for tree group management, including bulk operations and hierarchy retrieval.
175 lines
5.4 KiB
Python
175 lines
5.4 KiB
Python
"""Tests for the Elytra PIM Client with Pydantic validation"""
|
|
|
|
from unittest.mock import Mock, patch
|
|
|
|
import pytest
|
|
from pydantic import ValidationError
|
|
|
|
from elytra_client import ElytraClient, SingleNewProductRequestBody, SingleProductResponse
|
|
from elytra_client.exceptions import ElytraAuthenticationError, ElytraNotFoundError
|
|
|
|
|
|
@pytest.fixture
|
|
def client():
|
|
"""Create a test client"""
|
|
return ElytraClient(
|
|
base_url="https://test.example.com/api/v1",
|
|
api_key="test-api-key",
|
|
)
|
|
|
|
|
|
def test_client_initialization(client):
|
|
"""Test client initialization"""
|
|
assert client.base_url == "https://test.example.com/api/v1"
|
|
assert client.api_key == "test-api-key"
|
|
assert client.timeout == 30
|
|
|
|
|
|
def test_client_context_manager():
|
|
"""Test client context manager"""
|
|
with ElytraClient(
|
|
base_url="https://test.example.com/api/v1",
|
|
api_key="test-api-key",
|
|
) as client:
|
|
assert client is not None
|
|
|
|
|
|
@patch("elytra_client.client.requests.Session.request")
|
|
def test_get_products_with_pydantic_validation(mock_request, client):
|
|
"""Test get_products with Pydantic validation"""
|
|
mock_response = Mock()
|
|
mock_response.json.return_value = {
|
|
"items": [
|
|
{
|
|
"id": 1,
|
|
"clientId": 231,
|
|
"productName": "Product 1",
|
|
"treeId": 0,
|
|
"created": "2025-04-08T12:00:00Z",
|
|
"modified": "2025-04-08T12:00:00Z",
|
|
"creatorUserId": 235,
|
|
"modifierUserId": 235,
|
|
"objectStatus": "original",
|
|
"originalId": 0,
|
|
"attributes": [],
|
|
}
|
|
],
|
|
"total": 1,
|
|
"page": 1,
|
|
}
|
|
mock_request.return_value = mock_response
|
|
|
|
result = client.get_products(lang="en", page=1, limit=10)
|
|
|
|
assert result["total"] == 1
|
|
assert len(result["items"]) == 1
|
|
# Items should be Pydantic models
|
|
assert isinstance(result["items"][0], SingleProductResponse)
|
|
assert result["items"][0].productName == "Product 1"
|
|
mock_request.assert_called_once()
|
|
|
|
|
|
@patch("elytra_client.client.requests.Session.request")
|
|
def test_get_product_returns_pydantic_model(mock_request, client):
|
|
"""Test get_product returns Pydantic model"""
|
|
mock_response = Mock()
|
|
mock_response.json.return_value = {
|
|
"id": 123,
|
|
"clientId": 231,
|
|
"productName": "Test Product",
|
|
"treeId": 0,
|
|
"created": "2025-04-08T12:00:00Z",
|
|
"modified": "2025-04-08T12:00:00Z",
|
|
"creatorUserId": 235,
|
|
"modifierUserId": 235,
|
|
"objectStatus": "original",
|
|
"originalId": 0,
|
|
"attributes": [],
|
|
}
|
|
mock_request.return_value = mock_response
|
|
|
|
result = client.get_product(product_id=123, lang="en")
|
|
|
|
assert isinstance(result, SingleProductResponse)
|
|
assert result.id == 123
|
|
assert result.productName == "Test Product"
|
|
|
|
|
|
@patch("elytra_client.client.requests.Session.request")
|
|
def test_create_product_with_pydantic(mock_request, client):
|
|
"""Test product creation with Pydantic validation"""
|
|
mock_response = Mock()
|
|
mock_response.json.return_value = {
|
|
"id": 999,
|
|
"clientId": 231,
|
|
"productName": "NEW-PRODUCT-001",
|
|
"treeId": 0,
|
|
"created": "2025-04-08T12:00:00Z",
|
|
"modified": "2025-04-08T12:00:00Z",
|
|
"creatorUserId": 235,
|
|
"modifierUserId": 235,
|
|
"objectStatus": "original",
|
|
"originalId": 0,
|
|
"attributes": [],
|
|
}
|
|
mock_request.return_value = mock_response
|
|
|
|
# Create with Pydantic model
|
|
new_product = SingleNewProductRequestBody( # type: ignore[arg-type]
|
|
productName="NEW-PRODUCT-001",
|
|
parentId=1,
|
|
attributeGroupId=10,
|
|
)
|
|
|
|
result = client.create_product(new_product)
|
|
|
|
assert isinstance(result, SingleProductResponse)
|
|
assert result.id == 999
|
|
assert result.productName == "NEW-PRODUCT-001"
|
|
|
|
|
|
def test_pydantic_validation_on_creation():
|
|
"""Test Pydantic validation on model creation"""
|
|
# Valid model
|
|
valid_product = SingleNewProductRequestBody( # type: ignore[arg-type]
|
|
productName="VALID-PRODUCT",
|
|
parentId=1,
|
|
attributeGroupId=10,
|
|
)
|
|
assert valid_product.productName == "VALID-PRODUCT"
|
|
|
|
# Invalid model - missing required field
|
|
with pytest.raises(ValidationError):
|
|
SingleNewProductRequestBody( # type: ignore[arg-type]
|
|
productName="INVALID-PRODUCT",
|
|
# Missing parentId - required
|
|
attributeGroupId=10,
|
|
)
|
|
|
|
|
|
@patch("elytra_client.client.requests.Session.request")
|
|
def test_authentication_error(mock_request, client):
|
|
"""Test authentication error handling"""
|
|
mock_response = Mock()
|
|
mock_response.status_code = 401
|
|
mock_response.text = "Unauthorized"
|
|
mock_request.return_value.raise_for_status.side_effect = __import__(
|
|
"requests"
|
|
).exceptions.HTTPError(response=mock_response)
|
|
|
|
with pytest.raises(ElytraAuthenticationError):
|
|
client.get_products()
|
|
|
|
|
|
@patch("elytra_client.client.requests.Session.request")
|
|
def test_not_found_error(mock_request, client):
|
|
"""Test not found error handling"""
|
|
mock_response = Mock()
|
|
mock_response.status_code = 404
|
|
mock_response.text = "Not Found"
|
|
mock_request.return_value.raise_for_status.side_effect = __import__(
|
|
"requests"
|
|
).exceptions.HTTPError(response=mock_response)
|
|
|
|
with pytest.raises(ElytraNotFoundError):
|
|
client.get_product(product_id=999)
|