shop product refactoring
This commit is contained in:
@@ -10,7 +10,8 @@ from main import app
|
||||
# Import all models to ensure they're registered with Base metadata
|
||||
from models.database.marketplace_import_job import MarketplaceImportJob
|
||||
from models.database.marketplace_product import MarketplaceProduct
|
||||
from models.database.shop import Shop, ShopProduct
|
||||
from models.database.shop import Shop
|
||||
from models.database.product import Product
|
||||
from models.database.stock import Stock
|
||||
from models.database.user import User
|
||||
|
||||
|
||||
25
tests/fixtures/shop_fixtures.py
vendored
25
tests/fixtures/shop_fixtures.py
vendored
@@ -3,7 +3,8 @@ import uuid
|
||||
|
||||
import pytest
|
||||
|
||||
from models.database.shop import Shop, ShopProduct
|
||||
from models.database.shop import Shop
|
||||
from models.database.product import Product
|
||||
from models.database.stock import Stock
|
||||
|
||||
|
||||
@@ -77,23 +78,23 @@ def verified_shop(db, other_user):
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def shop_product(db, test_shop, unique_product):
|
||||
def test_product(db, test_shop, unique_product):
|
||||
"""Create a shop product relationship"""
|
||||
shop_product = ShopProduct(
|
||||
product = Product(
|
||||
shop_id=test_shop.id, marketplace_product_id=unique_product.id, is_active=True
|
||||
)
|
||||
# Add optional fields if they exist in your model
|
||||
if hasattr(ShopProduct, "shop_price"):
|
||||
shop_product.shop_price = 24.99
|
||||
if hasattr(ShopProduct, "is_featured"):
|
||||
shop_product.is_featured = False
|
||||
if hasattr(ShopProduct, "min_quantity"):
|
||||
shop_product.min_quantity = 1
|
||||
if hasattr(Product, "shop_price"):
|
||||
product.price = 24.99
|
||||
if hasattr(Product, "is_featured"):
|
||||
product.is_featured = False
|
||||
if hasattr(Product, "min_quantity"):
|
||||
product.min_quantity = 1
|
||||
|
||||
db.add(shop_product)
|
||||
db.add(product)
|
||||
db.commit()
|
||||
db.refresh(shop_product)
|
||||
return shop_product
|
||||
db.refresh(product)
|
||||
return product
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
||||
4
tests/fixtures/testing_fixtures.py
vendored
4
tests/fixtures/testing_fixtures.py
vendored
@@ -22,9 +22,9 @@ def empty_db(db):
|
||||
# In order to respect foreign key constraints
|
||||
tables_to_clear = [
|
||||
"marketplace_import_jobs", # Has foreign keys to shops and users
|
||||
"shop_products", # Has foreign keys to shops and products
|
||||
"products", # Has foreign keys to shops and products
|
||||
"stock", # Fixed: singular not plural
|
||||
"products", # Referenced by shop_products
|
||||
"products", # Referenced by products
|
||||
"shops", # Has foreign key to users
|
||||
"users" # Base table
|
||||
]
|
||||
|
||||
@@ -183,9 +183,9 @@ class TestShopsAPI:
|
||||
|
||||
def test_add_product_to_shop_success(self, client, auth_headers, test_shop, unique_product):
|
||||
"""Test adding product to shop successfully"""
|
||||
shop_product_data = {
|
||||
product_data = {
|
||||
"marketplace_product_id": unique_product.marketplace_product_id, # Use string marketplace_product_id, not database id
|
||||
"shop_price": 29.99,
|
||||
"price": 29.99,
|
||||
"is_active": True,
|
||||
"is_featured": False,
|
||||
}
|
||||
@@ -193,7 +193,7 @@ class TestShopsAPI:
|
||||
response = client.post(
|
||||
f"/api/v1/shop/{test_shop.shop_code}/products",
|
||||
headers=auth_headers,
|
||||
json=shop_product_data
|
||||
json=product_data
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
@@ -201,21 +201,21 @@ class TestShopsAPI:
|
||||
|
||||
# The response structure contains nested product data
|
||||
assert data["shop_id"] == test_shop.id
|
||||
assert data["shop_price"] == 29.99
|
||||
assert data["price"] == 29.99
|
||||
assert data["is_active"] is True
|
||||
assert data["is_featured"] is False
|
||||
|
||||
# MarketplaceProduct details are nested in the 'product' field
|
||||
assert "product" in data
|
||||
assert data["product"]["marketplace_product_id"] == unique_product.marketplace_product_id
|
||||
assert data["product"]["id"] == unique_product.id
|
||||
# MarketplaceProduct details are nested in the 'marketplace_product' field
|
||||
assert "marketplace_product" in data
|
||||
assert data["marketplace_product"]["marketplace_product_id"] == unique_product.marketplace_product_id
|
||||
assert data["marketplace_product"]["id"] == unique_product.id
|
||||
|
||||
def test_add_product_to_shop_already_exists_conflict(self, client, auth_headers, test_shop, shop_product):
|
||||
"""Test adding product that already exists in shop returns ShopProductAlreadyExistsException"""
|
||||
# shop_product fixture already creates a relationship, get the marketplace_product_id string
|
||||
existing_product = shop_product.product
|
||||
def test_add_product_to_shop_already_exists_conflict(self, client, auth_headers, test_shop, test_product):
|
||||
"""Test adding product that already exists in shop returns ProductAlreadyExistsException"""
|
||||
# test_product fixture already creates a relationship, get the marketplace_product_id string
|
||||
existing_product = test_product.marketplace_product
|
||||
|
||||
shop_product_data = {
|
||||
product_data = {
|
||||
"marketplace_product_id": existing_product.marketplace_product_id, # Use string marketplace_product_id
|
||||
"shop_price": 29.99,
|
||||
}
|
||||
@@ -223,19 +223,19 @@ class TestShopsAPI:
|
||||
response = client.post(
|
||||
f"/api/v1/shop/{test_shop.shop_code}/products",
|
||||
headers=auth_headers,
|
||||
json=shop_product_data
|
||||
json=product_data
|
||||
)
|
||||
|
||||
assert response.status_code == 409
|
||||
data = response.json()
|
||||
assert data["error_code"] == "SHOP_PRODUCT_ALREADY_EXISTS"
|
||||
assert data["error_code"] == "PRODUCT_ALREADY_EXISTS"
|
||||
assert data["status_code"] == 409
|
||||
assert test_shop.shop_code in data["message"]
|
||||
assert existing_product.marketplace_product_id in data["message"]
|
||||
|
||||
def test_add_nonexistent_product_to_shop_not_found(self, client, auth_headers, test_shop):
|
||||
"""Test adding nonexistent product to shop returns MarketplaceProductNotFoundException"""
|
||||
shop_product_data = {
|
||||
product_data = {
|
||||
"marketplace_product_id": "NONEXISTENT_PRODUCT", # Use string marketplace_product_id that doesn't exist
|
||||
"shop_price": 29.99,
|
||||
}
|
||||
@@ -243,7 +243,7 @@ class TestShopsAPI:
|
||||
response = client.post(
|
||||
f"/api/v1/shop/{test_shop.shop_code}/products",
|
||||
headers=auth_headers,
|
||||
json=shop_product_data
|
||||
json=product_data
|
||||
)
|
||||
|
||||
assert response.status_code == 404
|
||||
@@ -252,7 +252,7 @@ class TestShopsAPI:
|
||||
assert data["status_code"] == 404
|
||||
assert "NONEXISTENT_PRODUCT" in data["message"]
|
||||
|
||||
def test_get_shop_products_success(self, client, auth_headers, test_shop, shop_product):
|
||||
def test_get_products_success(self, client, auth_headers, test_shop, test_product):
|
||||
"""Test getting shop products successfully"""
|
||||
response = client.get(
|
||||
f"/api/v1/shop/{test_shop.shop_code}/products",
|
||||
@@ -266,7 +266,7 @@ class TestShopsAPI:
|
||||
assert "shop" in data
|
||||
assert data["shop"]["shop_code"] == test_shop.shop_code
|
||||
|
||||
def test_get_shop_products_with_filters(self, client, auth_headers, test_shop):
|
||||
def test_get_products_with_filters(self, client, auth_headers, test_shop):
|
||||
"""Test getting shop products with filtering"""
|
||||
# Test active_only filter
|
||||
response = client.get(
|
||||
|
||||
@@ -60,7 +60,7 @@ class TestIntegrationFlows:
|
||||
assert response.status_code == 200
|
||||
assert response.json()["total"] == 1
|
||||
|
||||
def test_shop_product_workflow(self, client, auth_headers):
|
||||
def test_product_workflow(self, client, auth_headers):
|
||||
"""Test shop creation and product management workflow"""
|
||||
# 1. Create a shop
|
||||
shop_data = {
|
||||
|
||||
@@ -8,11 +8,12 @@ from app.exceptions import (
|
||||
UnauthorizedShopAccessException,
|
||||
InvalidShopDataException,
|
||||
MarketplaceProductNotFoundException,
|
||||
ShopProductAlreadyExistsException,
|
||||
ProductAlreadyExistsException,
|
||||
MaxShopsReachedException,
|
||||
ValidationException,
|
||||
)
|
||||
from models.schemas.shop import ShopCreate, ShopProductCreate
|
||||
from models.schemas.shop import ShopCreate
|
||||
from models.schemas.product import ProductCreate
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -178,27 +179,26 @@ class TestShopService:
|
||||
|
||||
def test_add_product_to_shop_success(self, db, test_shop, unique_product):
|
||||
"""Test successfully adding product to shop"""
|
||||
shop_product_data = ShopProductCreate(
|
||||
product_data = ProductCreate(
|
||||
marketplace_product_id=unique_product.marketplace_product_id,
|
||||
price="15.99",
|
||||
is_featured=True,
|
||||
stock_quantity=5,
|
||||
)
|
||||
|
||||
shop_product = self.service.add_product_to_shop(
|
||||
db, test_shop, shop_product_data
|
||||
product = self.service.add_product_to_shop(
|
||||
db, test_shop, product_data
|
||||
)
|
||||
|
||||
assert shop_product is not None
|
||||
assert shop_product.shop_id == test_shop.id
|
||||
assert shop_product.marketplace_product_id == unique_product.id
|
||||
assert product is not None
|
||||
assert product.shop_id == test_shop.id
|
||||
assert product.marketplace_product_id == unique_product.id
|
||||
|
||||
def test_add_product_to_shop_product_not_found(self, db, test_shop):
|
||||
"""Test adding non-existent product to shop fails"""
|
||||
shop_product_data = ShopProductCreate(marketplace_product_id="NONEXISTENT", price="15.99")
|
||||
product_data = ProductCreate(marketplace_product_id="NONEXISTENT", price="15.99")
|
||||
|
||||
with pytest.raises(MarketplaceProductNotFoundException) as exc_info:
|
||||
self.service.add_product_to_shop(db, test_shop, shop_product_data)
|
||||
self.service.add_product_to_shop(db, test_shop, product_data)
|
||||
|
||||
exception = exc_info.value
|
||||
assert exception.status_code == 404
|
||||
@@ -206,36 +206,36 @@ class TestShopService:
|
||||
assert exception.details["resource_type"] == "MarketplaceProduct"
|
||||
assert exception.details["identifier"] == "NONEXISTENT"
|
||||
|
||||
def test_add_product_to_shop_already_exists(self, db, test_shop, shop_product):
|
||||
def test_add_product_to_shop_already_exists(self, db, test_shop, test_product):
|
||||
"""Test adding product that's already in shop fails"""
|
||||
shop_product_data = ShopProductCreate(
|
||||
marketplace_product_id=shop_product.product.marketplace_product_id, price="15.99"
|
||||
product_data = ProductCreate(
|
||||
marketplace_product_id=test_product.marketplace_product.marketplace_product_id, price="15.99"
|
||||
)
|
||||
|
||||
with pytest.raises(ShopProductAlreadyExistsException) as exc_info:
|
||||
self.service.add_product_to_shop(db, test_shop, shop_product_data)
|
||||
with pytest.raises(ProductAlreadyExistsException) as exc_info:
|
||||
self.service.add_product_to_shop(db, test_shop, product_data)
|
||||
|
||||
exception = exc_info.value
|
||||
assert exception.status_code == 409
|
||||
assert exception.error_code == "SHOP_PRODUCT_ALREADY_EXISTS"
|
||||
assert exception.error_code == "PRODUCT_ALREADY_EXISTS"
|
||||
assert exception.details["shop_code"] == test_shop.shop_code
|
||||
assert exception.details["marketplace_product_id"] == shop_product.product.marketplace_product_id
|
||||
assert exception.details["marketplace_product_id"] == test_product.marketplace_product.marketplace_product_id
|
||||
|
||||
def test_get_shop_products_owner_access(
|
||||
self, db, test_user, test_shop, shop_product
|
||||
def test_get_products_owner_access(
|
||||
self, db, test_user, test_shop, test_product
|
||||
):
|
||||
"""Test shop owner can get shop products"""
|
||||
products, total = self.service.get_shop_products(db, test_shop, test_user)
|
||||
products, total = self.service.get_products(db, test_shop, test_user)
|
||||
|
||||
assert total >= 1
|
||||
assert len(products) >= 1
|
||||
product_ids = [p.marketplace_product_id for p in products]
|
||||
assert shop_product.marketplace_product_id in product_ids
|
||||
assert test_product.marketplace_product_id in product_ids
|
||||
|
||||
def test_get_shop_products_access_denied(self, db, test_user, inactive_shop):
|
||||
def test_get_products_access_denied(self, db, test_user, inactive_shop):
|
||||
"""Test non-owner cannot access unverified shop products"""
|
||||
with pytest.raises(UnauthorizedShopAccessException) as exc_info:
|
||||
self.service.get_shop_products(db, inactive_shop, test_user)
|
||||
self.service.get_products(db, inactive_shop, test_user)
|
||||
|
||||
exception = exc_info.value
|
||||
assert exception.status_code == 403
|
||||
@@ -243,16 +243,16 @@ class TestShopService:
|
||||
assert exception.details["shop_code"] == inactive_shop.shop_code
|
||||
assert exception.details["user_id"] == test_user.id
|
||||
|
||||
def test_get_shop_products_with_filters(self, db, test_user, test_shop, shop_product):
|
||||
def test_get_products_with_filters(self, db, test_user, test_shop, test_product):
|
||||
"""Test getting shop products with various filters"""
|
||||
# Test active only filter
|
||||
products, total = self.service.get_shop_products(
|
||||
products, total = self.service.get_products(
|
||||
db, test_shop, test_user, active_only=True
|
||||
)
|
||||
assert all(p.is_active for p in products)
|
||||
|
||||
# Test featured only filter
|
||||
products, total = self.service.get_shop_products(
|
||||
products, total = self.service.get_products(
|
||||
db, test_shop, test_user, featured_only=True
|
||||
)
|
||||
assert all(p.is_featured for p in products)
|
||||
@@ -299,12 +299,12 @@ class TestShopService:
|
||||
|
||||
monkeypatch.setattr(db, "commit", mock_commit)
|
||||
|
||||
shop_product_data = ShopProductCreate(
|
||||
product_data = ProductCreate(
|
||||
marketplace_product_id=unique_product.marketplace_product_id, price="15.99"
|
||||
)
|
||||
|
||||
with pytest.raises(ValidationException) as exc_info:
|
||||
self.service.add_product_to_shop(db, test_shop, shop_product_data)
|
||||
self.service.add_product_to_shop(db, test_shop, product_data)
|
||||
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "VALIDATION_ERROR"
|
||||
|
||||
@@ -315,7 +315,7 @@ class TestStatsService:
|
||||
def test_get_unique_shops_count(self, db, test_marketplace_product):
|
||||
"""Test getting unique shops count"""
|
||||
# Add products with different shop names
|
||||
shop_products = [
|
||||
products = [
|
||||
MarketplaceProduct(
|
||||
marketplace_product_id="SHOP001",
|
||||
title="Shop MarketplaceProduct 1",
|
||||
@@ -333,7 +333,7 @@ class TestStatsService:
|
||||
currency="EUR",
|
||||
),
|
||||
]
|
||||
db.add_all(shop_products)
|
||||
db.add_all(products)
|
||||
db.commit()
|
||||
|
||||
count = self.service._get_unique_shops_count(db)
|
||||
|
||||
Reference in New Issue
Block a user