marketplace refactoring
This commit is contained in:
@@ -137,7 +137,7 @@ class TestAdminAPI:
|
||||
assert data["error_code"] == "SHOP_NOT_FOUND"
|
||||
|
||||
def test_get_marketplace_import_jobs_admin(
|
||||
self, client, admin_headers, test_marketplace_job
|
||||
self, client, admin_headers, test_marketplace_import_job
|
||||
):
|
||||
"""Test admin getting marketplace import jobs"""
|
||||
response = client.get(
|
||||
@@ -148,17 +148,17 @@ class TestAdminAPI:
|
||||
data = response.json()
|
||||
assert len(data) >= 1
|
||||
|
||||
# Check that test_marketplace_job is in the response
|
||||
# Check that test_marketplace_import_job is in the response
|
||||
job_ids = [job["job_id"] for job in data if "job_id" in job]
|
||||
assert test_marketplace_job.id in job_ids
|
||||
assert test_marketplace_import_job.id in job_ids
|
||||
|
||||
def test_get_marketplace_import_jobs_with_filters(
|
||||
self, client, admin_headers, test_marketplace_job
|
||||
self, client, admin_headers, test_marketplace_import_job
|
||||
):
|
||||
"""Test admin getting marketplace import jobs with filters"""
|
||||
response = client.get(
|
||||
"/api/v1/admin/marketplace-import-jobs",
|
||||
params={"marketplace": test_marketplace_job.marketplace},
|
||||
params={"marketplace": test_marketplace_import_job.marketplace},
|
||||
headers=admin_headers,
|
||||
)
|
||||
|
||||
@@ -166,7 +166,7 @@ class TestAdminAPI:
|
||||
data = response.json()
|
||||
assert len(data) >= 1
|
||||
assert all(
|
||||
job["marketplace"] == test_marketplace_job.marketplace for job in data
|
||||
job["marketplace"] == test_marketplace_import_job.marketplace for job in data
|
||||
)
|
||||
|
||||
def test_get_marketplace_import_jobs_non_admin(self, client, auth_headers):
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
# tests/integration/api/v1/test_filtering.py
|
||||
import pytest
|
||||
|
||||
from models.database.product import Product
|
||||
from models.database.marketplace_product import MarketplaceProduct
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.mark.api
|
||||
@pytest.mark.products
|
||||
@pytest.mark.marketplace
|
||||
class TestFiltering:
|
||||
|
||||
def test_product_brand_filter_success(self, client, auth_headers, db):
|
||||
@@ -16,27 +17,27 @@ class TestFiltering:
|
||||
unique_suffix = str(uuid.uuid4())[:8]
|
||||
|
||||
products = [
|
||||
Product(product_id=f"BRAND1_{unique_suffix}", title="Product 1", brand="BrandA"),
|
||||
Product(product_id=f"BRAND2_{unique_suffix}", title="Product 2", brand="BrandB"),
|
||||
Product(product_id=f"BRAND3_{unique_suffix}", title="Product 3", brand="BrandA"),
|
||||
MarketplaceProduct(marketplace_product_id=f"BRAND1_{unique_suffix}", title="MarketplaceProduct 1", brand="BrandA"),
|
||||
MarketplaceProduct(marketplace_product_id=f"BRAND2_{unique_suffix}", title="MarketplaceProduct 2", brand="BrandB"),
|
||||
MarketplaceProduct(marketplace_product_id=f"BRAND3_{unique_suffix}", title="MarketplaceProduct 3", brand="BrandA"),
|
||||
]
|
||||
|
||||
db.add_all(products)
|
||||
db.commit()
|
||||
|
||||
# Filter by BrandA
|
||||
response = client.get("/api/v1/product?brand=BrandA", headers=auth_headers)
|
||||
response = client.get("/api/v1/marketplace/product?brand=BrandA", headers=auth_headers)
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["total"] >= 2 # At least our test products
|
||||
|
||||
# Verify all returned products have BrandA
|
||||
for product in data["products"]:
|
||||
if product["product_id"].endswith(unique_suffix):
|
||||
if product["marketplace_product_id"].endswith(unique_suffix):
|
||||
assert product["brand"] == "BrandA"
|
||||
|
||||
# Filter by BrandB
|
||||
response = client.get("/api/v1/product?brand=BrandB", headers=auth_headers)
|
||||
response = client.get("/api/v1/marketplace/product?brand=BrandB", headers=auth_headers)
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["total"] >= 1 # At least our test product
|
||||
@@ -47,21 +48,21 @@ class TestFiltering:
|
||||
unique_suffix = str(uuid.uuid4())[:8]
|
||||
|
||||
products = [
|
||||
Product(product_id=f"MKT1_{unique_suffix}", title="Product 1", marketplace="Amazon"),
|
||||
Product(product_id=f"MKT2_{unique_suffix}", title="Product 2", marketplace="eBay"),
|
||||
Product(product_id=f"MKT3_{unique_suffix}", title="Product 3", marketplace="Amazon"),
|
||||
MarketplaceProduct(marketplace_product_id=f"MKT1_{unique_suffix}", title="MarketplaceProduct 1", marketplace="Amazon"),
|
||||
MarketplaceProduct(marketplace_product_id=f"MKT2_{unique_suffix}", title="MarketplaceProduct 2", marketplace="eBay"),
|
||||
MarketplaceProduct(marketplace_product_id=f"MKT3_{unique_suffix}", title="MarketplaceProduct 3", marketplace="Amazon"),
|
||||
]
|
||||
|
||||
db.add_all(products)
|
||||
db.commit()
|
||||
|
||||
response = client.get("/api/v1/product?marketplace=Amazon", headers=auth_headers)
|
||||
response = client.get("/api/v1/marketplace/product?marketplace=Amazon", headers=auth_headers)
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["total"] >= 2 # At least our test products
|
||||
|
||||
# Verify all returned products have Amazon marketplace
|
||||
amazon_products = [p for p in data["products"] if p["product_id"].endswith(unique_suffix)]
|
||||
amazon_products = [p for p in data["products"] if p["marketplace_product_id"].endswith(unique_suffix)]
|
||||
for product in amazon_products:
|
||||
assert product["marketplace"] == "Amazon"
|
||||
|
||||
@@ -71,18 +72,18 @@ class TestFiltering:
|
||||
unique_suffix = str(uuid.uuid4())[:8]
|
||||
|
||||
products = [
|
||||
Product(
|
||||
product_id=f"SEARCH1_{unique_suffix}",
|
||||
MarketplaceProduct(
|
||||
marketplace_product_id=f"SEARCH1_{unique_suffix}",
|
||||
title=f"Apple iPhone {unique_suffix}",
|
||||
description="Smartphone"
|
||||
),
|
||||
Product(
|
||||
product_id=f"SEARCH2_{unique_suffix}",
|
||||
MarketplaceProduct(
|
||||
marketplace_product_id=f"SEARCH2_{unique_suffix}",
|
||||
title=f"Samsung Galaxy {unique_suffix}",
|
||||
description="Android phone",
|
||||
),
|
||||
Product(
|
||||
product_id=f"SEARCH3_{unique_suffix}",
|
||||
MarketplaceProduct(
|
||||
marketplace_product_id=f"SEARCH3_{unique_suffix}",
|
||||
title=f"iPad Tablet {unique_suffix}",
|
||||
description="Apple tablet"
|
||||
),
|
||||
@@ -92,13 +93,13 @@ class TestFiltering:
|
||||
db.commit()
|
||||
|
||||
# Search for "Apple"
|
||||
response = client.get(f"/api/v1/product?search=Apple", headers=auth_headers)
|
||||
response = client.get(f"/api/v1/marketplace/product?search=Apple", headers=auth_headers)
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["total"] >= 2 # iPhone and iPad
|
||||
|
||||
# Search for "phone"
|
||||
response = client.get(f"/api/v1/product?search=phone", headers=auth_headers)
|
||||
response = client.get(f"/api/v1/marketplace/product?search=phone", headers=auth_headers)
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["total"] >= 2 # iPhone and Galaxy
|
||||
@@ -109,20 +110,20 @@ class TestFiltering:
|
||||
unique_suffix = str(uuid.uuid4())[:8]
|
||||
|
||||
products = [
|
||||
Product(
|
||||
product_id=f"COMBO1_{unique_suffix}",
|
||||
MarketplaceProduct(
|
||||
marketplace_product_id=f"COMBO1_{unique_suffix}",
|
||||
title=f"Apple iPhone {unique_suffix}",
|
||||
brand="Apple",
|
||||
marketplace="Amazon",
|
||||
),
|
||||
Product(
|
||||
product_id=f"COMBO2_{unique_suffix}",
|
||||
MarketplaceProduct(
|
||||
marketplace_product_id=f"COMBO2_{unique_suffix}",
|
||||
title=f"Apple iPad {unique_suffix}",
|
||||
brand="Apple",
|
||||
marketplace="eBay",
|
||||
),
|
||||
Product(
|
||||
product_id=f"COMBO3_{unique_suffix}",
|
||||
MarketplaceProduct(
|
||||
marketplace_product_id=f"COMBO3_{unique_suffix}",
|
||||
title=f"Samsung Phone {unique_suffix}",
|
||||
brand="Samsung",
|
||||
marketplace="Amazon",
|
||||
@@ -134,14 +135,14 @@ class TestFiltering:
|
||||
|
||||
# Filter by brand AND marketplace
|
||||
response = client.get(
|
||||
"/api/v1/product?brand=Apple&marketplace=Amazon", headers=auth_headers
|
||||
"/api/v1/marketplace/product?brand=Apple&marketplace=Amazon", headers=auth_headers
|
||||
)
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["total"] >= 1 # At least iPhone matches both
|
||||
|
||||
# Find our specific test product
|
||||
matching_products = [p for p in data["products"] if p["product_id"].endswith(unique_suffix)]
|
||||
matching_products = [p for p in data["products"] if p["marketplace_product_id"].endswith(unique_suffix)]
|
||||
for product in matching_products:
|
||||
assert product["brand"] == "Apple"
|
||||
assert product["marketplace"] == "Amazon"
|
||||
@@ -149,7 +150,7 @@ class TestFiltering:
|
||||
def test_filter_with_no_results(self, client, auth_headers):
|
||||
"""Test filtering with criteria that returns no results"""
|
||||
response = client.get(
|
||||
"/api/v1/product?brand=NonexistentBrand123456", headers=auth_headers
|
||||
"/api/v1/marketplace/product?brand=NonexistentBrand123456", headers=auth_headers
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
@@ -162,9 +163,9 @@ class TestFiltering:
|
||||
import uuid
|
||||
unique_suffix = str(uuid.uuid4())[:8]
|
||||
|
||||
product = Product(
|
||||
product_id=f"CASE_{unique_suffix}",
|
||||
title="Test Product",
|
||||
product = MarketplaceProduct(
|
||||
marketplace_product_id=f"CASE_{unique_suffix}",
|
||||
title="Test MarketplaceProduct",
|
||||
brand="TestBrand",
|
||||
marketplace="TestMarket",
|
||||
)
|
||||
@@ -173,7 +174,7 @@ class TestFiltering:
|
||||
|
||||
# Test different case variations
|
||||
for brand_filter in ["TestBrand", "testbrand", "TESTBRAND"]:
|
||||
response = client.get(f"/api/v1/product?brand={brand_filter}", headers=auth_headers)
|
||||
response = client.get(f"/api/v1/marketplace/product?brand={brand_filter}", headers=auth_headers)
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["total"] >= 1
|
||||
@@ -182,9 +183,9 @@ class TestFiltering:
|
||||
"""Test behavior with invalid filter parameters"""
|
||||
# Test with very long filter values
|
||||
long_brand = "A" * 1000
|
||||
response = client.get(f"/api/v1/product?brand={long_brand}", headers=auth_headers)
|
||||
response = client.get(f"/api/v1/marketplace/product?brand={long_brand}", headers=auth_headers)
|
||||
assert response.status_code == 200 # Should handle gracefully
|
||||
|
||||
# Test with special characters
|
||||
response = client.get("/api/v1/product?brand=<script>alert('test')</script>", headers=auth_headers)
|
||||
response = client.get("/api/v1/marketplace/product?brand=<script>alert('test')</script>", headers=auth_headers)
|
||||
assert response.status_code == 200 # Should handle gracefully
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# tests/integration/api/v1/test_marketplace_endpoints.py
|
||||
# tests/integration/api/v1/test_marketplace_import_job_endpoints.py
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
@@ -7,7 +7,7 @@ import pytest
|
||||
@pytest.mark.integration
|
||||
@pytest.mark.api
|
||||
@pytest.mark.marketplace
|
||||
class TestMarketplaceAPI:
|
||||
class TestMarketplaceImportJobAPI:
|
||||
def test_import_from_marketplace(self, client, auth_headers, test_shop, test_user):
|
||||
"""Test marketplace import endpoint - just test job creation"""
|
||||
# Ensure user owns the shop
|
||||
@@ -102,18 +102,18 @@ class TestMarketplaceAPI:
|
||||
assert data["marketplace"] == "AdminMarket"
|
||||
assert data["shop_code"] == test_shop.shop_code
|
||||
|
||||
def test_get_marketplace_import_status(self, client, auth_headers, test_marketplace_job):
|
||||
def test_get_marketplace_import_status(self, client, auth_headers, test_marketplace_import_job):
|
||||
"""Test getting marketplace import status"""
|
||||
response = client.get(
|
||||
f"/api/v1/marketplace/import-status/{test_marketplace_job.id}",
|
||||
f"/api/v1/marketplace/import-status/{test_marketplace_import_job.id}",
|
||||
headers=auth_headers
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["job_id"] == test_marketplace_job.id
|
||||
assert data["status"] == test_marketplace_job.status
|
||||
assert data["marketplace"] == test_marketplace_job.marketplace
|
||||
assert data["job_id"] == test_marketplace_import_job.id
|
||||
assert data["status"] == test_marketplace_import_job.status
|
||||
assert data["marketplace"] == test_marketplace_import_job.marketplace
|
||||
|
||||
def test_get_marketplace_import_status_not_found(self, client, auth_headers):
|
||||
"""Test getting status of non-existent import job"""
|
||||
@@ -127,13 +127,13 @@ class TestMarketplaceAPI:
|
||||
assert data["error_code"] == "IMPORT_JOB_NOT_FOUND"
|
||||
assert "99999" in data["message"]
|
||||
|
||||
def test_get_marketplace_import_status_unauthorized(self, client, auth_headers, test_marketplace_job, other_user):
|
||||
def test_get_marketplace_import_status_unauthorized(self, client, auth_headers, test_marketplace_import_job, other_user):
|
||||
"""Test getting status of unauthorized import job"""
|
||||
# Change job owner to other user
|
||||
test_marketplace_job.user_id = other_user.id
|
||||
test_marketplace_import_job.user_id = other_user.id
|
||||
|
||||
response = client.get(
|
||||
f"/api/v1/marketplace/import-status/{test_marketplace_job.id}",
|
||||
f"/api/v1/marketplace/import-status/{test_marketplace_import_job.id}",
|
||||
headers=auth_headers
|
||||
)
|
||||
|
||||
@@ -141,7 +141,7 @@ class TestMarketplaceAPI:
|
||||
data = response.json()
|
||||
assert data["error_code"] == "IMPORT_JOB_NOT_OWNED"
|
||||
|
||||
def test_get_marketplace_import_jobs(self, client, auth_headers, test_marketplace_job):
|
||||
def test_get_marketplace_import_jobs(self, client, auth_headers, test_marketplace_import_job):
|
||||
"""Test getting marketplace import jobs"""
|
||||
response = client.get("/api/v1/marketplace/import-jobs", headers=auth_headers)
|
||||
|
||||
@@ -152,12 +152,12 @@ class TestMarketplaceAPI:
|
||||
|
||||
# Find our test job in the results
|
||||
job_ids = [job["job_id"] for job in data]
|
||||
assert test_marketplace_job.id in job_ids
|
||||
assert test_marketplace_import_job.id in job_ids
|
||||
|
||||
def test_get_marketplace_import_jobs_with_filters(self, client, auth_headers, test_marketplace_job):
|
||||
def test_get_marketplace_import_jobs_with_filters(self, client, auth_headers, test_marketplace_import_job):
|
||||
"""Test getting import jobs with filters"""
|
||||
response = client.get(
|
||||
f"/api/v1/marketplace/import-jobs?marketplace={test_marketplace_job.marketplace}",
|
||||
f"/api/v1/marketplace/import-jobs?marketplace={test_marketplace_import_job.marketplace}",
|
||||
headers=auth_headers
|
||||
)
|
||||
|
||||
@@ -167,7 +167,7 @@ class TestMarketplaceAPI:
|
||||
assert len(data) >= 1
|
||||
|
||||
for job in data:
|
||||
assert test_marketplace_job.marketplace.lower() in job["marketplace"].lower()
|
||||
assert test_marketplace_import_job.marketplace.lower() in job["marketplace"].lower()
|
||||
|
||||
def test_get_marketplace_import_jobs_pagination(self, client, auth_headers):
|
||||
"""Test import jobs pagination"""
|
||||
@@ -181,7 +181,7 @@ class TestMarketplaceAPI:
|
||||
assert isinstance(data, list)
|
||||
assert len(data) <= 5
|
||||
|
||||
def test_get_marketplace_import_stats(self, client, auth_headers, test_marketplace_job):
|
||||
def test_get_marketplace_import_stats(self, client, auth_headers, test_marketplace_import_job):
|
||||
"""Test getting marketplace import statistics"""
|
||||
response = client.get("/api/v1/marketplace/marketplace-import-stats", headers=auth_headers)
|
||||
|
||||
@@ -198,7 +198,7 @@ class TestMarketplaceAPI:
|
||||
def test_cancel_marketplace_import_job(self, client, auth_headers, test_user, test_shop, db):
|
||||
"""Test cancelling a marketplace import job"""
|
||||
# Create a pending job that can be cancelled
|
||||
from models.database.marketplace import MarketplaceImportJob
|
||||
from models.database.marketplace_import_job import MarketplaceImportJob
|
||||
import uuid
|
||||
|
||||
unique_id = str(uuid.uuid4())[:8]
|
||||
@@ -240,14 +240,14 @@ class TestMarketplaceAPI:
|
||||
data = response.json()
|
||||
assert data["error_code"] == "IMPORT_JOB_NOT_FOUND"
|
||||
|
||||
def test_cancel_marketplace_import_job_cannot_cancel(self, client, auth_headers, test_marketplace_job, db):
|
||||
def test_cancel_marketplace_import_job_cannot_cancel(self, client, auth_headers, test_marketplace_import_job, db):
|
||||
"""Test cancelling a job that cannot be cancelled"""
|
||||
# Set job to completed status
|
||||
test_marketplace_job.status = "completed"
|
||||
test_marketplace_import_job.status = "completed"
|
||||
db.commit()
|
||||
|
||||
response = client.put(
|
||||
f"/api/v1/marketplace/import-jobs/{test_marketplace_job.id}/cancel",
|
||||
f"/api/v1/marketplace/import-jobs/{test_marketplace_import_job.id}/cancel",
|
||||
headers=auth_headers
|
||||
)
|
||||
|
||||
@@ -259,7 +259,7 @@ class TestMarketplaceAPI:
|
||||
def test_delete_marketplace_import_job(self, client, auth_headers, test_user, test_shop, db):
|
||||
"""Test deleting a marketplace import job"""
|
||||
# Create a completed job that can be deleted
|
||||
from models.database.marketplace import MarketplaceImportJob
|
||||
from models.database.marketplace_import_job import MarketplaceImportJob
|
||||
import uuid
|
||||
|
||||
unique_id = str(uuid.uuid4())[:8]
|
||||
@@ -302,7 +302,7 @@ class TestMarketplaceAPI:
|
||||
def test_delete_marketplace_import_job_cannot_delete(self, client, auth_headers, test_user, test_shop, db):
|
||||
"""Test deleting a job that cannot be deleted"""
|
||||
# Create a pending job that cannot be deleted
|
||||
from models.database.marketplace import MarketplaceImportJob
|
||||
from models.database.marketplace_import_job import MarketplaceImportJob
|
||||
import uuid
|
||||
|
||||
unique_id = str(uuid.uuid4())[:8]
|
||||
@@ -352,7 +352,7 @@ class TestMarketplaceAPI:
|
||||
data = response.json()
|
||||
assert data["error_code"] == "INVALID_TOKEN"
|
||||
|
||||
def test_admin_can_access_all_jobs(self, client, admin_headers, test_marketplace_job):
|
||||
def test_admin_can_access_all_jobs(self, client, admin_headers, test_marketplace_import_job):
|
||||
"""Test that admin can access all import jobs"""
|
||||
response = client.get("/api/v1/marketplace/import-jobs", headers=admin_headers)
|
||||
|
||||
@@ -361,23 +361,23 @@ class TestMarketplaceAPI:
|
||||
assert isinstance(data, list)
|
||||
# Admin should see all jobs, including the test job
|
||||
job_ids = [job["job_id"] for job in data]
|
||||
assert test_marketplace_job.id in job_ids
|
||||
assert test_marketplace_import_job.id in job_ids
|
||||
|
||||
def test_admin_can_view_any_job_status(self, client, admin_headers, test_marketplace_job):
|
||||
def test_admin_can_view_any_job_status(self, client, admin_headers, test_marketplace_import_job):
|
||||
"""Test that admin can view any job status"""
|
||||
response = client.get(
|
||||
f"/api/v1/marketplace/import-status/{test_marketplace_job.id}",
|
||||
f"/api/v1/marketplace/import-status/{test_marketplace_import_job.id}",
|
||||
headers=admin_headers
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["job_id"] == test_marketplace_job.id
|
||||
assert data["job_id"] == test_marketplace_import_job.id
|
||||
|
||||
def test_admin_can_cancel_any_job(self, client, admin_headers, test_user, test_shop, db):
|
||||
"""Test that admin can cancel any job"""
|
||||
# Create a pending job owned by different user
|
||||
from models.database.marketplace import MarketplaceImportJob
|
||||
from models.database.marketplace_import_job import MarketplaceImportJob
|
||||
import uuid
|
||||
|
||||
unique_id = str(uuid.uuid4())[:8]
|
||||
@@ -5,7 +5,7 @@ import uuid
|
||||
|
||||
import pytest
|
||||
|
||||
from models.database.product import Product
|
||||
from models.database.marketplace_product import MarketplaceProduct
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@@ -13,9 +13,9 @@ from models.database.product import Product
|
||||
@pytest.mark.performance # for the performance test
|
||||
class TestExportFunctionality:
|
||||
|
||||
def test_csv_export_basic_success(self, client, auth_headers, test_product):
|
||||
def test_csv_export_basic_success(self, client, auth_headers, test_marketplace_product):
|
||||
"""Test basic CSV export functionality successfully"""
|
||||
response = client.get("/api/v1/product/export-csv", headers=auth_headers)
|
||||
response = client.get("/api/v1/marketplace/product/export-csv", headers=auth_headers)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.headers["content-type"] == "text/csv; charset=utf-8"
|
||||
@@ -26,13 +26,13 @@ class TestExportFunctionality:
|
||||
|
||||
# Check header row
|
||||
header = next(csv_reader)
|
||||
expected_fields = ["product_id", "title", "description", "price", "marketplace"]
|
||||
expected_fields = ["marketplace_product_id", "title", "description", "price", "marketplace"]
|
||||
for field in expected_fields:
|
||||
assert field in header
|
||||
|
||||
# Verify test product appears in export
|
||||
csv_lines = csv_content.split('\n')
|
||||
test_product_found = any(test_product.product_id in line for line in csv_lines)
|
||||
test_product_found = any(test_marketplace_product.marketplace_product_id in line for line in csv_lines)
|
||||
assert test_product_found, "Test product should appear in CSV export"
|
||||
|
||||
def test_csv_export_with_marketplace_filter_success(self, client, auth_headers, db):
|
||||
@@ -40,14 +40,14 @@ class TestExportFunctionality:
|
||||
# Create products in different marketplaces with unique IDs
|
||||
unique_suffix = str(uuid.uuid4())[:8]
|
||||
products = [
|
||||
Product(
|
||||
product_id=f"EXP1_{unique_suffix}",
|
||||
title=f"Amazon Product {unique_suffix}",
|
||||
MarketplaceProduct(
|
||||
marketplace_product_id=f"EXP1_{unique_suffix}",
|
||||
title=f"Amazon MarketplaceProduct {unique_suffix}",
|
||||
marketplace="Amazon"
|
||||
),
|
||||
Product(
|
||||
product_id=f"EXP2_{unique_suffix}",
|
||||
title=f"eBay Product {unique_suffix}",
|
||||
MarketplaceProduct(
|
||||
marketplace_product_id=f"EXP2_{unique_suffix}",
|
||||
title=f"eBay MarketplaceProduct {unique_suffix}",
|
||||
marketplace="eBay"
|
||||
),
|
||||
]
|
||||
@@ -56,7 +56,7 @@ class TestExportFunctionality:
|
||||
db.commit()
|
||||
|
||||
response = client.get(
|
||||
"/api/v1/product/export-csv?marketplace=Amazon", headers=auth_headers
|
||||
"/api/v1/marketplace/product/export-csv?marketplace=Amazon", headers=auth_headers
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert response.headers["content-type"] == "text/csv; charset=utf-8"
|
||||
@@ -69,14 +69,14 @@ class TestExportFunctionality:
|
||||
"""Test CSV export with shop name filtering successfully"""
|
||||
unique_suffix = str(uuid.uuid4())[:8]
|
||||
products = [
|
||||
Product(
|
||||
product_id=f"SHOP1_{unique_suffix}",
|
||||
title=f"Shop1 Product {unique_suffix}",
|
||||
MarketplaceProduct(
|
||||
marketplace_product_id=f"SHOP1_{unique_suffix}",
|
||||
title=f"Shop1 MarketplaceProduct {unique_suffix}",
|
||||
shop_name="TestShop1"
|
||||
),
|
||||
Product(
|
||||
product_id=f"SHOP2_{unique_suffix}",
|
||||
title=f"Shop2 Product {unique_suffix}",
|
||||
MarketplaceProduct(
|
||||
marketplace_product_id=f"SHOP2_{unique_suffix}",
|
||||
title=f"Shop2 MarketplaceProduct {unique_suffix}",
|
||||
shop_name="TestShop2"
|
||||
),
|
||||
]
|
||||
@@ -85,7 +85,7 @@ class TestExportFunctionality:
|
||||
db.commit()
|
||||
|
||||
response = client.get(
|
||||
"/api/v1/product/export-csv?shop_name=TestShop1", headers=auth_headers
|
||||
"/api/v1/marketplace/product?shop_name=TestShop1", headers=auth_headers
|
||||
)
|
||||
assert response.status_code == 200
|
||||
|
||||
@@ -97,21 +97,21 @@ class TestExportFunctionality:
|
||||
"""Test CSV export with combined marketplace and shop filters successfully"""
|
||||
unique_suffix = str(uuid.uuid4())[:8]
|
||||
products = [
|
||||
Product(
|
||||
product_id=f"COMBO1_{unique_suffix}",
|
||||
title=f"Combo Product 1 {unique_suffix}",
|
||||
MarketplaceProduct(
|
||||
marketplace_product_id=f"COMBO1_{unique_suffix}",
|
||||
title=f"Combo MarketplaceProduct 1 {unique_suffix}",
|
||||
marketplace="Amazon",
|
||||
shop_name="TestShop"
|
||||
),
|
||||
Product(
|
||||
product_id=f"COMBO2_{unique_suffix}",
|
||||
title=f"Combo Product 2 {unique_suffix}",
|
||||
MarketplaceProduct(
|
||||
marketplace_product_id=f"COMBO2_{unique_suffix}",
|
||||
title=f"Combo MarketplaceProduct 2 {unique_suffix}",
|
||||
marketplace="eBay",
|
||||
shop_name="TestShop"
|
||||
),
|
||||
Product(
|
||||
product_id=f"COMBO3_{unique_suffix}",
|
||||
title=f"Combo Product 3 {unique_suffix}",
|
||||
MarketplaceProduct(
|
||||
marketplace_product_id=f"COMBO3_{unique_suffix}",
|
||||
title=f"Combo MarketplaceProduct 3 {unique_suffix}",
|
||||
marketplace="Amazon",
|
||||
shop_name="OtherShop"
|
||||
),
|
||||
@@ -121,7 +121,7 @@ class TestExportFunctionality:
|
||||
db.commit()
|
||||
|
||||
response = client.get(
|
||||
"/api/v1/product/export-csv?marketplace=Amazon&shop_name=TestShop",
|
||||
"/api/v1/marketplace/product?marketplace=Amazon&shop_name=TestShop",
|
||||
headers=auth_headers
|
||||
)
|
||||
assert response.status_code == 200
|
||||
@@ -134,7 +134,7 @@ class TestExportFunctionality:
|
||||
def test_csv_export_no_results(self, client, auth_headers):
|
||||
"""Test CSV export with filters that return no results"""
|
||||
response = client.get(
|
||||
"/api/v1/product/export-csv?marketplace=NonexistentMarketplace12345",
|
||||
"/api/v1/marketplace/product/export-csv?marketplace=NonexistentMarketplace12345",
|
||||
headers=auth_headers
|
||||
)
|
||||
|
||||
@@ -146,7 +146,7 @@ class TestExportFunctionality:
|
||||
# Should have header row even with no data
|
||||
assert len(csv_lines) >= 1
|
||||
# First line should be headers
|
||||
assert "product_id" in csv_lines[0]
|
||||
assert "marketplace_product_id" in csv_lines[0]
|
||||
|
||||
def test_csv_export_performance_large_dataset(self, client, auth_headers, db):
|
||||
"""Test CSV export performance with many products"""
|
||||
@@ -156,9 +156,9 @@ class TestExportFunctionality:
|
||||
products = []
|
||||
batch_size = 100 # Reduced from 1000 for faster test execution
|
||||
for i in range(batch_size):
|
||||
product = Product(
|
||||
product_id=f"PERF{i:04d}_{unique_suffix}",
|
||||
title=f"Performance Product {i}",
|
||||
product = MarketplaceProduct(
|
||||
marketplace_product_id=f"PERF{i:04d}_{unique_suffix}",
|
||||
title=f"Performance MarketplaceProduct {i}",
|
||||
marketplace="Performance",
|
||||
description=f"Performance test product {i}",
|
||||
price="10.99"
|
||||
@@ -174,7 +174,7 @@ class TestExportFunctionality:
|
||||
import time
|
||||
start_time = time.time()
|
||||
|
||||
response = client.get("/api/v1/product/export-csv", headers=auth_headers)
|
||||
response = client.get("/api/v1/marketplace/product/export-csv", headers=auth_headers)
|
||||
|
||||
end_time = time.time()
|
||||
execution_time = end_time - start_time
|
||||
@@ -186,20 +186,20 @@ class TestExportFunctionality:
|
||||
# Verify content contains our test data
|
||||
csv_content = response.content.decode("utf-8")
|
||||
assert f"PERF0000_{unique_suffix}" in csv_content
|
||||
assert "Performance Product" in csv_content
|
||||
assert "Performance MarketplaceProduct" in csv_content
|
||||
|
||||
def test_csv_export_without_auth_returns_invalid_token(self, client):
|
||||
"""Test that CSV export requires authentication returns InvalidTokenException"""
|
||||
response = client.get("/api/v1/product/export-csv")
|
||||
response = client.get("/api/v1/marketplace/product")
|
||||
|
||||
assert response.status_code == 401
|
||||
data = response.json()
|
||||
assert data["error_code"] == "INVALID_TOKEN"
|
||||
assert data["status_code"] == 401
|
||||
|
||||
def test_csv_export_streaming_response_format(self, client, auth_headers, test_product):
|
||||
def test_csv_export_streaming_response_format(self, client, auth_headers, test_marketplace_product):
|
||||
"""Test that CSV export returns proper streaming response with correct headers"""
|
||||
response = client.get("/api/v1/product/export-csv", headers=auth_headers)
|
||||
response = client.get("/api/v1/marketplace/product/export-csv", headers=auth_headers)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.headers["content-type"] == "text/csv; charset=utf-8"
|
||||
@@ -215,9 +215,9 @@ class TestExportFunctionality:
|
||||
unique_suffix = str(uuid.uuid4())[:8]
|
||||
|
||||
# Create product with special characters that might break CSV
|
||||
product = Product(
|
||||
product_id=f"SPECIAL_{unique_suffix}",
|
||||
title=f'Product with quotes and commas {unique_suffix}', # Simplified to avoid CSV escaping issues
|
||||
product = MarketplaceProduct(
|
||||
marketplace_product_id=f"SPECIAL_{unique_suffix}",
|
||||
title=f'MarketplaceProduct with quotes and commas {unique_suffix}', # Simplified to avoid CSV escaping issues
|
||||
description=f"Description with special chars {unique_suffix}",
|
||||
marketplace="Test Market",
|
||||
price="19.99"
|
||||
@@ -226,14 +226,14 @@ class TestExportFunctionality:
|
||||
db.add(product)
|
||||
db.commit()
|
||||
|
||||
response = client.get("/api/v1/product/export-csv", headers=auth_headers)
|
||||
response = client.get("/api/v1/marketplace/product/export-csv", headers=auth_headers)
|
||||
|
||||
assert response.status_code == 200
|
||||
csv_content = response.content.decode("utf-8")
|
||||
|
||||
# Verify our test product appears in the CSV content
|
||||
assert f"SPECIAL_{unique_suffix}" in csv_content
|
||||
assert f"Product with quotes and commas {unique_suffix}" in csv_content
|
||||
assert f"MarketplaceProduct with quotes and commas {unique_suffix}" in csv_content
|
||||
assert "Test Market" in csv_content
|
||||
assert "19.99" in csv_content
|
||||
|
||||
@@ -243,7 +243,7 @@ class TestExportFunctionality:
|
||||
header = next(csv_reader)
|
||||
|
||||
# Verify header contains expected fields
|
||||
expected_fields = ["product_id", "title", "marketplace", "price"]
|
||||
expected_fields = ["marketplace_product_id", "title", "marketplace", "price"]
|
||||
for field in expected_fields:
|
||||
assert field in header
|
||||
|
||||
@@ -274,7 +274,7 @@ class TestExportFunctionality:
|
||||
|
||||
# This would require access to your service instance to mock properly
|
||||
# For now, we test that the endpoint structure supports error handling
|
||||
response = client.get("/api/v1/product/export-csv", headers=auth_headers)
|
||||
response = client.get("/api/v1/marketplace/product", headers=auth_headers)
|
||||
|
||||
# Should either succeed or return proper error response
|
||||
assert response.status_code in [200, 400, 500]
|
||||
@@ -293,7 +293,7 @@ class TestExportFunctionality:
|
||||
def test_csv_export_filename_generation(self, client, auth_headers):
|
||||
"""Test CSV export generates appropriate filenames based on filters"""
|
||||
# Test basic export filename
|
||||
response = client.get("/api/v1/product/export-csv", headers=auth_headers)
|
||||
response = client.get("/api/v1/marketplace/product/export-csv", headers=auth_headers)
|
||||
assert response.status_code == 200
|
||||
|
||||
content_disposition = response.headers.get("content-disposition", "")
|
||||
@@ -301,7 +301,7 @@ class TestExportFunctionality:
|
||||
|
||||
# Test with marketplace filter
|
||||
response = client.get(
|
||||
"/api/v1/product/export-csv?marketplace=Amazon",
|
||||
"/api/v1/marketplace/product/export-csv?marketplace=Amazon",
|
||||
headers=auth_headers
|
||||
)
|
||||
assert response.status_code == 200
|
||||
@@ -1,49 +1,49 @@
|
||||
# tests/integration/api/v1/test_product_endpoints.py
|
||||
# tests/integration/api/v1/test_marketplace_products_endpoints.py
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.mark.api
|
||||
@pytest.mark.products
|
||||
class TestProductsAPI:
|
||||
class TestMarketplaceProductsAPI:
|
||||
|
||||
def test_get_products_empty(self, client, auth_headers):
|
||||
"""Test getting products when none exist"""
|
||||
response = client.get("/api/v1/product", headers=auth_headers)
|
||||
response = client.get("/api/v1/marketplace/product", headers=auth_headers)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["products"] == []
|
||||
assert data["total"] == 0
|
||||
|
||||
def test_get_products_with_data(self, client, auth_headers, test_product):
|
||||
def test_get_products_with_data(self, client, auth_headers, test_marketplace_product):
|
||||
"""Test getting products with data"""
|
||||
response = client.get("/api/v1/product", headers=auth_headers)
|
||||
response = client.get("/api/v1/marketplace/product", headers=auth_headers)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert len(data["products"]) >= 1
|
||||
assert data["total"] >= 1
|
||||
# Find our test product
|
||||
test_product_found = any(p["product_id"] == test_product.product_id for p in data["products"])
|
||||
test_product_found = any(p["marketplace_product_id"] == test_marketplace_product.marketplace_product_id for p in data["products"])
|
||||
assert test_product_found
|
||||
|
||||
def test_get_products_with_filters(self, client, auth_headers, test_product):
|
||||
def test_get_products_with_filters(self, client, auth_headers, test_marketplace_product):
|
||||
"""Test filtering products"""
|
||||
# Test brand filter
|
||||
response = client.get(f"/api/v1/product?brand={test_product.brand}", headers=auth_headers)
|
||||
response = client.get(f"/api/v1/marketplace/product?brand={test_marketplace_product.brand}", headers=auth_headers)
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["total"] >= 1
|
||||
|
||||
# Test marketplace filter
|
||||
response = client.get(f"/api/v1/product?marketplace={test_product.marketplace}", headers=auth_headers)
|
||||
response = client.get(f"/api/v1/marketplace/product?marketplace={test_marketplace_product.marketplace}", headers=auth_headers)
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["total"] >= 1
|
||||
|
||||
# Test search
|
||||
response = client.get("/api/v1/product?search=Test", headers=auth_headers)
|
||||
response = client.get("/api/v1/marketplace/product?search=Test", headers=auth_headers)
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["total"] >= 1
|
||||
@@ -51,8 +51,8 @@ class TestProductsAPI:
|
||||
def test_create_product_success(self, client, auth_headers):
|
||||
"""Test creating a new product successfully"""
|
||||
product_data = {
|
||||
"product_id": "NEW001",
|
||||
"title": "New Product",
|
||||
"marketplace_product_id": "NEW001",
|
||||
"title": "New MarketplaceProduct",
|
||||
"description": "A new product",
|
||||
"price": "15.99",
|
||||
"brand": "NewBrand",
|
||||
@@ -62,19 +62,19 @@ class TestProductsAPI:
|
||||
}
|
||||
|
||||
response = client.post(
|
||||
"/api/v1/product", headers=auth_headers, json=product_data
|
||||
"/api/v1/marketplace/product", headers=auth_headers, json=product_data
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["product_id"] == "NEW001"
|
||||
assert data["title"] == "New Product"
|
||||
assert data["marketplace_product_id"] == "NEW001"
|
||||
assert data["title"] == "New MarketplaceProduct"
|
||||
assert data["marketplace"] == "Amazon"
|
||||
|
||||
def test_create_product_duplicate_id_returns_conflict(self, client, auth_headers, test_product):
|
||||
"""Test creating product with duplicate ID returns ProductAlreadyExistsException"""
|
||||
def test_create_product_duplicate_id_returns_conflict(self, client, auth_headers, test_marketplace_product):
|
||||
"""Test creating product with duplicate ID returns MarketplaceProductAlreadyExistsException"""
|
||||
product_data = {
|
||||
"product_id": test_product.product_id,
|
||||
"marketplace_product_id": test_marketplace_product.marketplace_product_id,
|
||||
"title": "Different Title",
|
||||
"description": "A new product",
|
||||
"price": "15.99",
|
||||
@@ -85,65 +85,65 @@ class TestProductsAPI:
|
||||
}
|
||||
|
||||
response = client.post(
|
||||
"/api/v1/product", headers=auth_headers, json=product_data
|
||||
"/api/v1/marketplace/product", headers=auth_headers, json=product_data
|
||||
)
|
||||
|
||||
assert response.status_code == 409
|
||||
data = response.json()
|
||||
assert data["error_code"] == "PRODUCT_ALREADY_EXISTS"
|
||||
assert data["status_code"] == 409
|
||||
assert test_product.product_id in data["message"]
|
||||
assert data["details"]["product_id"] == test_product.product_id
|
||||
assert test_marketplace_product.marketplace_product_id in data["message"]
|
||||
assert data["details"]["marketplace_product_id"] == test_marketplace_product.marketplace_product_id
|
||||
|
||||
def test_create_product_missing_title_validation_error(self, client, auth_headers):
|
||||
"""Test creating product without title returns ValidationException"""
|
||||
product_data = {
|
||||
"product_id": "VALID001",
|
||||
"marketplace_product_id": "VALID001",
|
||||
"title": "", # Empty title
|
||||
"price": "15.99",
|
||||
}
|
||||
|
||||
response = client.post(
|
||||
"/api/v1/product", headers=auth_headers, json=product_data
|
||||
"/api/v1/marketplace/product", headers=auth_headers, json=product_data
|
||||
)
|
||||
|
||||
assert response.status_code == 422 # Pydantic validation error
|
||||
data = response.json()
|
||||
assert data["error_code"] == "PRODUCT_VALIDATION_FAILED"
|
||||
assert data["status_code"] == 422
|
||||
assert "Product title is required" in data["message"]
|
||||
assert "MarketplaceProduct title is required" in data["message"]
|
||||
assert data["details"]["field"] == "title"
|
||||
|
||||
def test_create_product_missing_product_id_validation_error(self, client, auth_headers):
|
||||
"""Test creating product without product_id returns ValidationException"""
|
||||
"""Test creating product without marketplace_product_id returns ValidationException"""
|
||||
product_data = {
|
||||
"product_id": "", # Empty product ID
|
||||
"marketplace_product_id": "", # Empty product ID
|
||||
"title": "Valid Title",
|
||||
"price": "15.99",
|
||||
}
|
||||
|
||||
response = client.post(
|
||||
"/api/v1/product", headers=auth_headers, json=product_data
|
||||
"/api/v1/marketplace/product", headers=auth_headers, json=product_data
|
||||
)
|
||||
|
||||
assert response.status_code == 422
|
||||
data = response.json()
|
||||
assert data["error_code"] == "PRODUCT_VALIDATION_FAILED"
|
||||
assert data["status_code"] == 422
|
||||
assert "Product ID is required" in data["message"]
|
||||
assert data["details"]["field"] == "product_id"
|
||||
assert "MarketplaceProduct ID is required" in data["message"]
|
||||
assert data["details"]["field"] == "marketplace_product_id"
|
||||
|
||||
def test_create_product_invalid_gtin_data_error(self, client, auth_headers):
|
||||
"""Test creating product with invalid GTIN returns InvalidProductDataException"""
|
||||
"""Test creating product with invalid GTIN returns InvalidMarketplaceProductDataException"""
|
||||
product_data = {
|
||||
"product_id": "GTIN001",
|
||||
"title": "GTIN Test Product",
|
||||
"marketplace_product_id": "GTIN001",
|
||||
"title": "GTIN Test MarketplaceProduct",
|
||||
"price": "15.99",
|
||||
"gtin": "invalid_gtin",
|
||||
}
|
||||
|
||||
response = client.post(
|
||||
"/api/v1/product", headers=auth_headers, json=product_data
|
||||
"/api/v1/marketplace/product", headers=auth_headers, json=product_data
|
||||
)
|
||||
|
||||
assert response.status_code == 422
|
||||
@@ -154,15 +154,15 @@ class TestProductsAPI:
|
||||
assert data["details"]["field"] == "gtin"
|
||||
|
||||
def test_create_product_invalid_price_data_error(self, client, auth_headers):
|
||||
"""Test creating product with invalid price returns InvalidProductDataException"""
|
||||
"""Test creating product with invalid price returns InvalidMarketplaceProductDataException"""
|
||||
product_data = {
|
||||
"product_id": "PRICE001",
|
||||
"title": "Price Test Product",
|
||||
"marketplace_product_id": "PRICE001",
|
||||
"title": "Price Test MarketplaceProduct",
|
||||
"price": "invalid_price",
|
||||
}
|
||||
|
||||
response = client.post(
|
||||
"/api/v1/product", headers=auth_headers, json=product_data
|
||||
"/api/v1/marketplace/product", headers=auth_headers, json=product_data
|
||||
)
|
||||
|
||||
assert response.status_code == 422
|
||||
@@ -181,7 +181,7 @@ class TestProductsAPI:
|
||||
}
|
||||
|
||||
response = client.post(
|
||||
"/api/v1/product", headers=auth_headers, json=product_data
|
||||
"/api/v1/marketplace/product", headers=auth_headers, json=product_data
|
||||
)
|
||||
|
||||
assert response.status_code == 422
|
||||
@@ -191,50 +191,50 @@ class TestProductsAPI:
|
||||
assert "Request validation failed" in data["message"]
|
||||
assert "validation_errors" in data["details"]
|
||||
|
||||
def test_get_product_by_id_success(self, client, auth_headers, test_product):
|
||||
def test_get_product_by_id_success(self, client, auth_headers, test_marketplace_product):
|
||||
"""Test getting specific product successfully"""
|
||||
response = client.get(
|
||||
f"/api/v1/product/{test_product.product_id}", headers=auth_headers
|
||||
f"/api/v1/marketplace/product/{test_marketplace_product.marketplace_product_id}", headers=auth_headers
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["product"]["product_id"] == test_product.product_id
|
||||
assert data["product"]["title"] == test_product.title
|
||||
assert data["product"]["marketplace_product_id"] == test_marketplace_product.marketplace_product_id
|
||||
assert data["product"]["title"] == test_marketplace_product.title
|
||||
|
||||
def test_get_nonexistent_product_returns_not_found(self, client, auth_headers):
|
||||
"""Test getting nonexistent product returns ProductNotFoundException"""
|
||||
response = client.get("/api/v1/product/NONEXISTENT", headers=auth_headers)
|
||||
"""Test getting nonexistent product returns MarketplaceProductNotFoundException"""
|
||||
response = client.get("/api/v1/marketplace/product/NONEXISTENT", headers=auth_headers)
|
||||
|
||||
assert response.status_code == 404
|
||||
data = response.json()
|
||||
assert data["error_code"] == "PRODUCT_NOT_FOUND"
|
||||
assert data["status_code"] == 404
|
||||
assert "NONEXISTENT" in data["message"]
|
||||
assert data["details"]["resource_type"] == "Product"
|
||||
assert data["details"]["resource_type"] == "MarketplaceProduct"
|
||||
assert data["details"]["identifier"] == "NONEXISTENT"
|
||||
|
||||
def test_update_product_success(self, client, auth_headers, test_product):
|
||||
def test_update_product_success(self, client, auth_headers, test_marketplace_product):
|
||||
"""Test updating product successfully"""
|
||||
update_data = {"title": "Updated Product Title", "price": "25.99"}
|
||||
update_data = {"title": "Updated MarketplaceProduct Title", "price": "25.99"}
|
||||
|
||||
response = client.put(
|
||||
f"/api/v1/product/{test_product.product_id}",
|
||||
f"/api/v1/marketplace/product/{test_marketplace_product.marketplace_product_id}",
|
||||
headers=auth_headers,
|
||||
json=update_data,
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["title"] == "Updated Product Title"
|
||||
assert data["title"] == "Updated MarketplaceProduct Title"
|
||||
assert data["price"] == "25.99"
|
||||
|
||||
def test_update_nonexistent_product_returns_not_found(self, client, auth_headers):
|
||||
"""Test updating nonexistent product returns ProductNotFoundException"""
|
||||
update_data = {"title": "Updated Product Title"}
|
||||
"""Test updating nonexistent product returns MarketplaceProductNotFoundException"""
|
||||
update_data = {"title": "Updated MarketplaceProduct Title"}
|
||||
|
||||
response = client.put(
|
||||
"/api/v1/product/NONEXISTENT",
|
||||
"/api/v1/marketplace/product/NONEXISTENT",
|
||||
headers=auth_headers,
|
||||
json=update_data,
|
||||
)
|
||||
@@ -244,15 +244,15 @@ class TestProductsAPI:
|
||||
assert data["error_code"] == "PRODUCT_NOT_FOUND"
|
||||
assert data["status_code"] == 404
|
||||
assert "NONEXISTENT" in data["message"]
|
||||
assert data["details"]["resource_type"] == "Product"
|
||||
assert data["details"]["resource_type"] == "MarketplaceProduct"
|
||||
assert data["details"]["identifier"] == "NONEXISTENT"
|
||||
|
||||
def test_update_product_empty_title_validation_error(self, client, auth_headers, test_product):
|
||||
"""Test updating product with empty title returns ProductValidationException"""
|
||||
def test_update_product_empty_title_validation_error(self, client, auth_headers, test_marketplace_product):
|
||||
"""Test updating product with empty title returns MarketplaceProductValidationException"""
|
||||
update_data = {"title": ""}
|
||||
|
||||
response = client.put(
|
||||
f"/api/v1/product/{test_product.product_id}",
|
||||
f"/api/v1/marketplace/product/{test_marketplace_product.marketplace_product_id}",
|
||||
headers=auth_headers,
|
||||
json=update_data,
|
||||
)
|
||||
@@ -261,15 +261,15 @@ class TestProductsAPI:
|
||||
data = response.json()
|
||||
assert data["error_code"] == "PRODUCT_VALIDATION_FAILED"
|
||||
assert data["status_code"] == 422
|
||||
assert "Product title cannot be empty" in data["message"]
|
||||
assert "MarketplaceProduct title cannot be empty" in data["message"]
|
||||
assert data["details"]["field"] == "title"
|
||||
|
||||
def test_update_product_invalid_gtin_data_error(self, client, auth_headers, test_product):
|
||||
"""Test updating product with invalid GTIN returns InvalidProductDataException"""
|
||||
def test_update_product_invalid_gtin_data_error(self, client, auth_headers, test_marketplace_product):
|
||||
"""Test updating product with invalid GTIN returns InvalidMarketplaceProductDataException"""
|
||||
update_data = {"gtin": "invalid_gtin"}
|
||||
|
||||
response = client.put(
|
||||
f"/api/v1/product/{test_product.product_id}",
|
||||
f"/api/v1/marketplace/product/{test_marketplace_product.marketplace_product_id}",
|
||||
headers=auth_headers,
|
||||
json=update_data,
|
||||
)
|
||||
@@ -281,12 +281,12 @@ class TestProductsAPI:
|
||||
assert "Invalid GTIN format" in data["message"]
|
||||
assert data["details"]["field"] == "gtin"
|
||||
|
||||
def test_update_product_invalid_price_data_error(self, client, auth_headers, test_product):
|
||||
"""Test updating product with invalid price returns InvalidProductDataException"""
|
||||
def test_update_product_invalid_price_data_error(self, client, auth_headers, test_marketplace_product):
|
||||
"""Test updating product with invalid price returns InvalidMarketplaceProductDataException"""
|
||||
update_data = {"price": "invalid_price"}
|
||||
|
||||
response = client.put(
|
||||
f"/api/v1/product/{test_product.product_id}",
|
||||
f"/api/v1/marketplace/product/{test_marketplace_product.marketplace_product_id}",
|
||||
headers=auth_headers,
|
||||
json=update_data,
|
||||
)
|
||||
@@ -298,30 +298,30 @@ class TestProductsAPI:
|
||||
assert "Invalid price format" in data["message"]
|
||||
assert data["details"]["field"] == "price"
|
||||
|
||||
def test_delete_product_success(self, client, auth_headers, test_product):
|
||||
def test_delete_product_success(self, client, auth_headers, test_marketplace_product):
|
||||
"""Test deleting product successfully"""
|
||||
response = client.delete(
|
||||
f"/api/v1/product/{test_product.product_id}", headers=auth_headers
|
||||
f"/api/v1/marketplace/product/{test_marketplace_product.marketplace_product_id}", headers=auth_headers
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "deleted successfully" in response.json()["message"]
|
||||
|
||||
def test_delete_nonexistent_product_returns_not_found(self, client, auth_headers):
|
||||
"""Test deleting nonexistent product returns ProductNotFoundException"""
|
||||
response = client.delete("/api/v1/product/NONEXISTENT", headers=auth_headers)
|
||||
"""Test deleting nonexistent product returns MarketplaceProductNotFoundException"""
|
||||
response = client.delete("/api/v1/marketplace/product/NONEXISTENT", headers=auth_headers)
|
||||
|
||||
assert response.status_code == 404
|
||||
data = response.json()
|
||||
assert data["error_code"] == "PRODUCT_NOT_FOUND"
|
||||
assert data["status_code"] == 404
|
||||
assert "NONEXISTENT" in data["message"]
|
||||
assert data["details"]["resource_type"] == "Product"
|
||||
assert data["details"]["resource_type"] == "MarketplaceProduct"
|
||||
assert data["details"]["identifier"] == "NONEXISTENT"
|
||||
|
||||
def test_get_product_without_auth_returns_invalid_token(self, client):
|
||||
"""Test that product endpoints require authentication returns InvalidTokenException"""
|
||||
response = client.get("/api/v1/product")
|
||||
response = client.get("/api/v1/marketplace/product")
|
||||
|
||||
assert response.status_code == 401
|
||||
data = response.json()
|
||||
@@ -331,7 +331,7 @@ class TestProductsAPI:
|
||||
def test_exception_structure_consistency(self, client, auth_headers):
|
||||
"""Test that all exceptions follow the consistent LetzShopException structure"""
|
||||
# Test with a known error case
|
||||
response = client.get("/api/v1/product/NONEXISTENT", headers=auth_headers)
|
||||
response = client.get("/api/v1/marketplace/product/NONEXISTENT", headers=auth_headers)
|
||||
|
||||
assert response.status_code == 404
|
||||
data = response.json()
|
||||
@@ -1,7 +1,7 @@
|
||||
# tests/integration/api/v1/test_pagination.py
|
||||
import pytest
|
||||
|
||||
from models.database.product import Product
|
||||
from models.database.marketplace_product import MarketplaceProduct
|
||||
from models.database.shop import Shop
|
||||
|
||||
@pytest.mark.integration
|
||||
@@ -18,9 +18,9 @@ class TestPagination:
|
||||
# Create multiple products
|
||||
products = []
|
||||
for i in range(25):
|
||||
product = Product(
|
||||
product_id=f"PAGE{i:03d}_{unique_suffix}",
|
||||
title=f"Pagination Test Product {i}",
|
||||
product = MarketplaceProduct(
|
||||
marketplace_product_id=f"PAGE{i:03d}_{unique_suffix}",
|
||||
title=f"Pagination Test MarketplaceProduct {i}",
|
||||
marketplace="PaginationTest",
|
||||
)
|
||||
products.append(product)
|
||||
@@ -29,7 +29,7 @@ class TestPagination:
|
||||
db.commit()
|
||||
|
||||
# Test first page
|
||||
response = client.get("/api/v1/product?limit=10&skip=0", headers=auth_headers)
|
||||
response = client.get("/api/v1/marketplace/product?limit=10&skip=0", headers=auth_headers)
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert len(data["products"]) == 10
|
||||
@@ -38,21 +38,21 @@ class TestPagination:
|
||||
assert data["limit"] == 10
|
||||
|
||||
# Test second page
|
||||
response = client.get("/api/v1/product?limit=10&skip=10", headers=auth_headers)
|
||||
response = client.get("/api/v1/marketplace/product?limit=10&skip=10", headers=auth_headers)
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert len(data["products"]) == 10
|
||||
assert data["skip"] == 10
|
||||
|
||||
# Test last page (should have remaining products)
|
||||
response = client.get("/api/v1/product?limit=10&skip=20", headers=auth_headers)
|
||||
response = client.get("/api/v1/marketplace/product?limit=10&skip=20", headers=auth_headers)
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert len(data["products"]) >= 5 # At least 5 remaining from our test set
|
||||
|
||||
def test_pagination_boundary_negative_skip_validation_error(self, client, auth_headers):
|
||||
"""Test negative skip parameter returns ValidationException"""
|
||||
response = client.get("/api/v1/product?skip=-1", headers=auth_headers)
|
||||
response = client.get("/api/v1/marketplace/product?skip=-1", headers=auth_headers)
|
||||
|
||||
assert response.status_code == 422
|
||||
data = response.json()
|
||||
@@ -63,7 +63,7 @@ class TestPagination:
|
||||
|
||||
def test_pagination_boundary_zero_limit_validation_error(self, client, auth_headers):
|
||||
"""Test zero limit parameter returns ValidationException"""
|
||||
response = client.get("/api/v1/product?limit=0", headers=auth_headers)
|
||||
response = client.get("/api/v1/marketplace/product?limit=0", headers=auth_headers)
|
||||
|
||||
assert response.status_code == 422
|
||||
data = response.json()
|
||||
@@ -73,7 +73,7 @@ class TestPagination:
|
||||
|
||||
def test_pagination_boundary_excessive_limit_validation_error(self, client, auth_headers):
|
||||
"""Test excessive limit parameter returns ValidationException"""
|
||||
response = client.get("/api/v1/product?limit=10000", headers=auth_headers)
|
||||
response = client.get("/api/v1/marketplace/product?limit=10000", headers=auth_headers)
|
||||
|
||||
assert response.status_code == 422
|
||||
data = response.json()
|
||||
@@ -84,7 +84,7 @@ class TestPagination:
|
||||
def test_pagination_beyond_available_records(self, client, auth_headers, db):
|
||||
"""Test pagination beyond available records returns empty results"""
|
||||
# Test skip beyond available records
|
||||
response = client.get("/api/v1/product?skip=10000&limit=10", headers=auth_headers)
|
||||
response = client.get("/api/v1/marketplace/product?skip=10000&limit=10", headers=auth_headers)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
@@ -101,9 +101,9 @@ class TestPagination:
|
||||
# Create products with same brand for filtering
|
||||
products = []
|
||||
for i in range(15):
|
||||
product = Product(
|
||||
product_id=f"FILTPAGE{i:03d}_{unique_suffix}",
|
||||
title=f"Filter Page Product {i}",
|
||||
product = MarketplaceProduct(
|
||||
marketplace_product_id=f"FILTPAGE{i:03d}_{unique_suffix}",
|
||||
title=f"Filter Page MarketplaceProduct {i}",
|
||||
brand="FilterBrand",
|
||||
marketplace="FilterMarket",
|
||||
)
|
||||
@@ -114,7 +114,7 @@ class TestPagination:
|
||||
|
||||
# Test first page with filter
|
||||
response = client.get(
|
||||
"/api/v1/product?brand=FilterBrand&limit=5&skip=0",
|
||||
"/api/v1/marketplace/product?brand=FilterBrand&limit=5&skip=0",
|
||||
headers=auth_headers
|
||||
)
|
||||
|
||||
@@ -124,13 +124,13 @@ class TestPagination:
|
||||
assert data["total"] >= 15 # At least our test products
|
||||
|
||||
# Verify all products have the filtered brand
|
||||
test_products = [p for p in data["products"] if p["product_id"].endswith(unique_suffix)]
|
||||
test_products = [p for p in data["products"] if p["marketplace_product_id"].endswith(unique_suffix)]
|
||||
for product in test_products:
|
||||
assert product["brand"] == "FilterBrand"
|
||||
|
||||
# Test second page with same filter
|
||||
response = client.get(
|
||||
"/api/v1/product?brand=FilterBrand&limit=5&skip=5",
|
||||
"/api/v1/marketplace/product?brand=FilterBrand&limit=5&skip=5",
|
||||
headers=auth_headers
|
||||
)
|
||||
|
||||
@@ -141,7 +141,7 @@ class TestPagination:
|
||||
|
||||
def test_pagination_default_values(self, client, auth_headers):
|
||||
"""Test pagination with default values"""
|
||||
response = client.get("/api/v1/product", headers=auth_headers)
|
||||
response = client.get("/api/v1/marketplace/product", headers=auth_headers)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
@@ -157,9 +157,9 @@ class TestPagination:
|
||||
# Create products with predictable ordering
|
||||
products = []
|
||||
for i in range(10):
|
||||
product = Product(
|
||||
product_id=f"CONSIST{i:03d}_{unique_suffix}",
|
||||
title=f"Consistent Product {i:03d}",
|
||||
product = MarketplaceProduct(
|
||||
marketplace_product_id=f"CONSIST{i:03d}_{unique_suffix}",
|
||||
title=f"Consistent MarketplaceProduct {i:03d}",
|
||||
marketplace="ConsistentMarket",
|
||||
)
|
||||
products.append(product)
|
||||
@@ -168,14 +168,14 @@ class TestPagination:
|
||||
db.commit()
|
||||
|
||||
# Get first page
|
||||
response1 = client.get("/api/v1/product?limit=5&skip=0", headers=auth_headers)
|
||||
response1 = client.get("/api/v1/marketplace/product?limit=5&skip=0", headers=auth_headers)
|
||||
assert response1.status_code == 200
|
||||
first_page_ids = [p["product_id"] for p in response1.json()["products"]]
|
||||
first_page_ids = [p["marketplace_product_id"] for p in response1.json()["products"]]
|
||||
|
||||
# Get second page
|
||||
response2 = client.get("/api/v1/product?limit=5&skip=5", headers=auth_headers)
|
||||
response2 = client.get("/api/v1/marketplace/product?limit=5&skip=5", headers=auth_headers)
|
||||
assert response2.status_code == 200
|
||||
second_page_ids = [p["product_id"] for p in response2.json()["products"]]
|
||||
second_page_ids = [p["marketplace_product_id"] for p in response2.json()["products"]]
|
||||
|
||||
# Verify no overlap between pages
|
||||
overlap = set(first_page_ids) & set(second_page_ids)
|
||||
@@ -249,7 +249,7 @@ class TestPagination:
|
||||
import time
|
||||
|
||||
start_time = time.time()
|
||||
response = client.get("/api/v1/product?skip=1000&limit=10", headers=auth_headers)
|
||||
response = client.get("/api/v1/marketplace/product?skip=1000&limit=10", headers=auth_headers)
|
||||
end_time = time.time()
|
||||
|
||||
assert response.status_code == 200
|
||||
@@ -262,26 +262,26 @@ class TestPagination:
|
||||
def test_pagination_with_invalid_parameters_types(self, client, auth_headers):
|
||||
"""Test pagination with invalid parameter types returns ValidationException"""
|
||||
# Test non-numeric skip
|
||||
response = client.get("/api/v1/product?skip=invalid", headers=auth_headers)
|
||||
response = client.get("/api/v1/marketplace/product?skip=invalid", headers=auth_headers)
|
||||
assert response.status_code == 422
|
||||
data = response.json()
|
||||
assert data["error_code"] == "VALIDATION_ERROR"
|
||||
|
||||
# Test non-numeric limit
|
||||
response = client.get("/api/v1/product?limit=invalid", headers=auth_headers)
|
||||
response = client.get("/api/v1/marketplace/product?limit=invalid", headers=auth_headers)
|
||||
assert response.status_code == 422
|
||||
data = response.json()
|
||||
assert data["error_code"] == "VALIDATION_ERROR"
|
||||
|
||||
# Test float values (should be converted or rejected)
|
||||
response = client.get("/api/v1/product?skip=10.5&limit=5.5", headers=auth_headers)
|
||||
response = client.get("/api/v1/marketplace/product?skip=10.5&limit=5.5", headers=auth_headers)
|
||||
assert response.status_code in [200, 422] # Depends on implementation
|
||||
|
||||
def test_empty_dataset_pagination(self, client, auth_headers):
|
||||
"""Test pagination behavior with empty dataset"""
|
||||
# Use a filter that should return no results
|
||||
response = client.get(
|
||||
"/api/v1/product?brand=NonexistentBrand999&limit=10&skip=0",
|
||||
"/api/v1/marketplace/product?brand=NonexistentBrand999&limit=10&skip=0",
|
||||
headers=auth_headers
|
||||
)
|
||||
|
||||
@@ -294,7 +294,7 @@ class TestPagination:
|
||||
|
||||
def test_exception_structure_in_pagination_errors(self, client, auth_headers):
|
||||
"""Test that pagination validation errors follow consistent exception structure"""
|
||||
response = client.get("/api/v1/product?skip=-1", headers=auth_headers)
|
||||
response = client.get("/api/v1/marketplace/product?skip=-1", headers=auth_headers)
|
||||
|
||||
assert response.status_code == 422
|
||||
data = response.json()
|
||||
|
||||
@@ -184,7 +184,7 @@ 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_id": unique_product.product_id, # Use string product_id, not database id
|
||||
"marketplace_product_id": unique_product.marketplace_product_id, # Use string marketplace_product_id, not database id
|
||||
"shop_price": 29.99,
|
||||
"is_active": True,
|
||||
"is_featured": False,
|
||||
@@ -205,18 +205,18 @@ class TestShopsAPI:
|
||||
assert data["is_active"] is True
|
||||
assert data["is_featured"] is False
|
||||
|
||||
# Product details are nested in the 'product' field
|
||||
# MarketplaceProduct details are nested in the 'product' field
|
||||
assert "product" in data
|
||||
assert data["product"]["product_id"] == unique_product.product_id
|
||||
assert data["product"]["marketplace_product_id"] == unique_product.marketplace_product_id
|
||||
assert data["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 product_id string
|
||||
# shop_product fixture already creates a relationship, get the marketplace_product_id string
|
||||
existing_product = shop_product.product
|
||||
|
||||
shop_product_data = {
|
||||
"product_id": existing_product.product_id, # Use string product_id
|
||||
"marketplace_product_id": existing_product.marketplace_product_id, # Use string marketplace_product_id
|
||||
"shop_price": 29.99,
|
||||
}
|
||||
|
||||
@@ -231,12 +231,12 @@ class TestShopsAPI:
|
||||
assert data["error_code"] == "SHOP_PRODUCT_ALREADY_EXISTS"
|
||||
assert data["status_code"] == 409
|
||||
assert test_shop.shop_code in data["message"]
|
||||
assert existing_product.product_id 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 ProductNotFoundException"""
|
||||
"""Test adding nonexistent product to shop returns MarketplaceProductNotFoundException"""
|
||||
shop_product_data = {
|
||||
"product_id": "NONEXISTENT_PRODUCT", # Use string product_id that doesn't exist
|
||||
"marketplace_product_id": "NONEXISTENT_PRODUCT", # Use string marketplace_product_id that doesn't exist
|
||||
"shop_price": 29.99,
|
||||
}
|
||||
|
||||
@@ -321,7 +321,7 @@ class TestShopsAPI:
|
||||
|
||||
# Test adding products (might require verification)
|
||||
product_data = {
|
||||
"product_id": 1,
|
||||
"marketplace_product_id": 1,
|
||||
"shop_price": 29.99,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
# tests/integration/api/v1/test_stats_endpoints.py
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.mark.api
|
||||
@pytest.mark.stats
|
||||
class TestStatsAPI:
|
||||
def test_get_basic_stats(self, client, auth_headers, test_product):
|
||||
def test_get_basic_stats(self, client, auth_headers, test_marketplace_product):
|
||||
"""Test getting basic statistics"""
|
||||
response = client.get("/api/v1/stats", headers=auth_headers)
|
||||
|
||||
@@ -16,7 +18,7 @@ class TestStatsAPI:
|
||||
assert "unique_shops" in data
|
||||
assert data["total_products"] >= 1
|
||||
|
||||
def test_get_marketplace_stats(self, client, auth_headers, test_product):
|
||||
def test_get_marketplace_stats(self, client, auth_headers, test_marketplace_product):
|
||||
"""Test getting marketplace statistics"""
|
||||
response = client.get("/api/v1/stats/marketplace", headers=auth_headers)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user