saveinmed/saveinmed-bff/tests/test_pedidos.py
Tiago Yamamoto b39caf0fd0 first commit
2025-12-17 13:58:26 -03:00

393 lines
14 KiB
Python

from __future__ import annotations
import json
import os
import sys
from datetime import datetime, timezone
from pathlib import Path
from typing import Dict
from appwrite.exception import AppwriteException
import pytest
from fastapi import HTTPException, status
from fastapi.testclient import TestClient
PROJECT_ROOT = Path(__file__).resolve().parents[1]
if str(PROJECT_ROOT) not in sys.path:
sys.path.insert(0, str(PROJECT_ROOT))
os.environ.setdefault("APPWRITE_ENDPOINT", "http://localhost/v1")
os.environ.setdefault("APPWRITE_PROJECT_ID", "test-project")
os.environ.setdefault("APPWRITE_API_KEY", "test-key")
os.environ.setdefault("APPWRITE_DATABASE_ID", "test-db")
os.environ.setdefault("APPWRITE_COLLECTION_PEDIDOS_ID", "test-orders")
os.environ.setdefault("APPWRITE_COLLECTION_CARRINHOS_ID", "test-carts")
os.environ.setdefault("APPWRITE_COLLECTION_CATEGORIAS_ID", "test-categorias")
os.environ.setdefault("APPWRITE_COLLECTION_PRODUTOS_CATALOGO_ID", "test-produtos")
os.environ.setdefault("APPWRITE_COLLECTION_LABORATORIOS_ID", "test-labs")
os.environ.setdefault("APPWRITE_COLLECTION_CATEGORIAS_ID", "test-cat-rel")
os.environ.setdefault("APPWRITE_COLLECTION_SUBCATEGORIAS_ID", "test-subcat")
os.environ.setdefault("APPWRITE_COLLECTION_PAGAMENTOS_ID", "test-payments")
os.environ.setdefault("APPWRITE_COLLECTION_PRODUTOS_ESTOQUE_ID", "test-produtos-estoque")
os.environ.setdefault("APPWRITE_COLLECTION_PRODUTOS_VENDA_ID", "test-produtos-venda")
os.environ.setdefault("APPWRITE_COLLECTION_USUARIOS_DATA_ID", "test-usuarios-data")
os.environ.setdefault("APPWRITE_COLLECTION_USUARIOS_PERFIL_ID", "test-usuarios-perfil")
os.environ.setdefault("APPWRITE_COLLECTION_USUARIO_EMPRESA_PERFIS_ID", "test-usuario-empresa-perfis")
os.environ.setdefault("APPWRITE_COLLECTION_FATURAS_ID", "test-faturas")
os.environ.setdefault("APPWRITE_COLLECTION_USUARIOS_ID", "test-users")
os.environ.setdefault("APPWRITE_COLLECTION_EMPRESAS_ID", "test-companies")
os.environ.setdefault("APPWRITE_COLLECTION_EMPRESAS_DADOS_ID", "test-companies-data")
os.environ.setdefault("APPWRITE_COLLECTION_EMPRESAS_SOCIOS_ID", "test-empresas-socios")
os.environ.setdefault("APPWRITE_COLLECTION_ENDERECOS_ID", "test-addresses")
os.environ.setdefault("APPWRITE_COLLECTION_ENTREGAS_ID", "test-entregas")
os.environ.setdefault("MERCADO_PAGO_ACCESS_TOKEN", "test-mercado-pago-token")
os.environ.setdefault("MERCADO_PAGO_PUBLIC_KEY", "test-mercado-pago-key")
os.environ.setdefault("JWT_SECRET", "test-secret")
os.environ.setdefault("JWT_ALGORITHM", "HS256")
from src.app.main import app
from src.core.deps import get_pedidos_service, require_api_key, require_user
from src.modules.pedidos.schemas import (
PedidoCreateRequest,
PedidoOut,
PedidoStatusUpdate,
PedidoUpdate,
)
from src.modules.pedidos.service import PedidosService
class InMemoryPedidosService:
def __init__(self) -> None:
self._items: Dict[str, Dict[str, object]] = {}
self._counter = 0
async def list(
self,
page: int,
limit: int | None,
q: str | None,
usuario_id: str | None,
sort: str | None,
order: str | None,
) -> Dict[str, object]:
limit_value = limit
items = list(self._items.values())
items.sort(key=lambda item: item.get("createdAt", ""), reverse=True)
if usuario_id:
items = [item for item in items if item.get("usuarios") == usuario_id]
total = len(items)
return {
"page": page,
"limit": limit_value,
"total": total,
"items": [PedidoOut(**item).model_dump() for item in items],
}
async def get(self, pedido_id: str) -> PedidoOut:
if pedido_id not in self._items:
raise AssertionError("Pedido not found in test store")
return PedidoOut(**self._items[pedido_id])
async def create(self, payload: PedidoCreateRequest) -> PedidoOut:
data = payload.data.model_dump(by_alias=True, exclude_unset=True, exclude_none=True)
self._counter += 1
identifier = f"order_{self._counter}"
timestamp = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
record = {
"id": identifier,
"status": data.get("status", "pendente"),
"valor_total": data.get("valor-total"),
"itens": data.get("itens", []),
"quantidade": data.get("quantidade", []),
"pagamentos": data.get("pagamentos"),
"carrinhos": data.get("carrinhos"),
"usuarios": data.get("usuarios"),
"faturas": data.get("faturas"),
"createdAt": timestamp,
"updatedAt": timestamp,
}
self._items[identifier] = record
return PedidoOut(**record)
async def update(self, pedido_id: str, payload: PedidoUpdate) -> PedidoOut:
if pedido_id not in self._items:
raise AssertionError("Pedido not found in test store")
stored = self._items[pedido_id].copy()
data = payload.model_dump(by_alias=True, exclude_unset=True, exclude_none=True)
stored.update({k: v for k, v in data.items() if v is not None})
stored["updatedAt"] = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
self._items[pedido_id] = stored
return PedidoOut(**stored)
async def update_status(self, pedido_id: str, payload: PedidoStatusUpdate) -> PedidoOut:
if pedido_id not in self._items:
raise AssertionError("Pedido not found in test store")
stored = self._items[pedido_id].copy()
stored["status"] = payload.status
stored["updatedAt"] = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
self._items[pedido_id] = stored
return PedidoOut(**stored)
async def delete(self, pedido_id: str) -> None:
self._items.pop(pedido_id, None)
@pytest.fixture()
def test_client() -> TestClient:
service = InMemoryPedidosService()
def override_service() -> InMemoryPedidosService:
return service
app.dependency_overrides[get_pedidos_service] = override_service
app.dependency_overrides[require_api_key] = lambda: None
app.dependency_overrides[require_user] = lambda: {"id": "user_1"}
client = TestClient(app)
yield client
app.dependency_overrides.clear()
client.close()
def test_ping_route_is_available(test_client: TestClient) -> None:
response = test_client.get("/api/v1/ping")
assert response.status_code == 200
def test_create_pedido_returns_201(test_client: TestClient) -> None:
payload = {
"documentId": "unique()",
"data": {
"status": "pendente",
"valor-total": 99.5,
"itens": ["item_1"],
"quantidade": [1],
"usuarios": "user_1",
"carrinhos": "cart_1",
"pagamentos": "pay_1",
"faturas": "fat_1",
},
}
response = test_client.post("/api/v1/pedidos", json=payload)
assert response.status_code == 201
body = response.json()
assert body["status"] == "pendente"
assert body["itens"] == ["item_1"]
assert body["usuarios"] == "user_1"
def test_list_pedidos_returns_created_items(test_client: TestClient) -> None:
payload = {
"documentId": "unique()",
"data": {
"status": "pendente",
"valor-total": 50.0,
"itens": ["item_1"],
"quantidade": [1],
"usuarios": "user_1",
"carrinhos": "cart_1",
"pagamentos": "pay_1",
"faturas": "fat_1",
},
}
test_client.post("/api/v1/pedidos", json=payload)
response = test_client.get("/api/v1/pedidos")
assert response.status_code == 200
data = response.json()
assert data["total"] == 1
assert len(data["items"]) == 1
def test_list_pedidos_returns_latest_first(test_client: TestClient) -> None:
payload = {
"documentId": "unique()",
"data": {
"status": "pendente",
"valor-total": 10.0,
"itens": ["item_1"],
"quantidade": [1],
"usuarios": "user_1",
"carrinhos": "cart_1",
"pagamentos": "pay_1",
"faturas": "fat_1",
},
}
response_first = test_client.post("/api/v1/pedidos", json=payload)
assert response_first.status_code == 201
first_id = response_first.json()["id"]
response_second = test_client.post("/api/v1/pedidos", json=payload)
assert response_second.status_code == 201
second_id = response_second.json()["id"]
list_response = test_client.get("/api/v1/pedidos")
assert list_response.status_code == 200
items = list_response.json()["items"]
assert [item["id"] for item in items[:2]] == [second_id, first_id]
def test_list_pedidos_filters_by_usuario_id(test_client: TestClient) -> None:
payload_user1 = {
"documentId": "unique()",
"data": {
"status": "pendente",
"valor-total": 50.0,
"itens": ["item_1"],
"quantidade": [1],
"usuarios": "user_1",
"carrinhos": "cart_1",
"pagamentos": "pay_1",
"faturas": "fat_1",
},
}
payload_user2 = {
"documentId": "unique()",
"data": {
"status": "pendente",
"valor-total": 60.0,
"itens": ["item_2"],
"quantidade": [1],
"usuarios": "user_2",
"carrinhos": "cart_2",
"pagamentos": "pay_2",
"faturas": "fat_2",
},
}
test_client.post("/api/v1/pedidos", json=payload_user1)
test_client.post("/api/v1/pedidos", json=payload_user2)
response = test_client.get("/api/v1/pedidos", params={"usuario_id": "user_2"})
assert response.status_code == 200
data = response.json()
assert data["total"] == 1
assert len(data["items"]) == 1
assert data["items"][0]["usuarios"] == "user_2"
def test_update_status_endpoint(test_client: TestClient) -> None:
payload = {
"documentId": "unique()",
"data": {
"status": "pendente",
"itens": ["item_1"],
"quantidade": [1],
"usuarios": "user_1",
"carrinhos": "cart_1",
"pagamentos": "pay_1",
"faturas": "fat_1",
},
}
creation = test_client.post("/api/v1/pedidos", json=payload)
identifier = creation.json()["id"]
response = test_client.patch(
f"/api/v1/pedidos/{identifier}/status",
json={"status": "aprovado"},
)
assert response.status_code == 200
assert response.json()["status"] == "aprovado"
@pytest.mark.asyncio()
async def test_create_pedido_translates_appwrite_401_error() -> None:
class FakeDatabases:
def create_document(self, *args, **kwargs):
raise AppwriteException(
"The current user is not authorized to perform the requested action.",
401,
)
service = PedidosService(
databases=FakeDatabases(),
database_id="db",
collection_id="col",
)
payload = PedidoCreateRequest.model_validate(
{
"documentId": "unique()",
"data": {
"status": "pendente",
"valor-total": 10.0,
"itens": [],
"quantidade": [],
},
}
)
with pytest.raises(HTTPException) as exc_info:
await service.create(payload)
assert exc_info.value.status_code == status.HTTP_401_UNAUTHORIZED
assert "Falha na autenticação com Appwrite" in exc_info.value.detail
@pytest.mark.asyncio()
async def test_list_with_usuario_id_preserves_pagination() -> None:
documents = [
{
"$id": f"order_{index}",
"status": "pendente",
"valor-total": 9.99,
"itens": ["item_1"],
"quantidade": [1],
"usuarios": "user_1",
"pagamentos": None,
"carrinhos": None,
"faturas": None,
"$createdAt": f"2025-01-0{index}T12:00:00.000+00:00",
"$updatedAt": f"2025-01-0{index}T12:00:00.000+00:00",
}
for index in range(1, 6)
]
class FakeDatabases:
def list_documents(self, _database_id, _collection_id, queries=None):
limit = 20
offset = 0
filtered = list(documents)
for query in queries or []:
query_obj = json.loads(query) if isinstance(query, str) else query
method = query_obj.get("method")
if method == "limit":
limit = int(query_obj["values"][0])
elif method == "offset":
offset = int(query_obj["values"][0])
elif method == "equal" and query_obj.get("attribute") == "usuarios":
value = query_obj["values"][0]
filtered = [doc for doc in filtered if doc.get("usuarios") == value]
elif method == "orderDesc":
attribute = query_obj.get("attribute")
filtered.sort(key=lambda doc: doc.get(attribute), reverse=True)
elif method == "orderAsc":
attribute = query_obj.get("attribute")
filtered.sort(key=lambda doc: doc.get(attribute))
total = len(filtered)
sliced = filtered[offset : offset + limit]
return {"documents": sliced, "total": total}
service = PedidosService(
databases=FakeDatabases(),
database_id="db",
collection_id="col",
)
result = await service.list(
page=2,
limit=2,
q=None,
usuario_id="user_1",
sort=None,
order=None,
)
assert result["page"] == 2
assert result["limit"] == 2
assert result["total"] == 5
assert [item.id for item in result["items"]] == ["order_3", "order_2"]