test: update service tests for fixture and API changes
Updates to work with refactored fixtures (no expunge): - Re-query entities when modifying state in tests - Remove assertions on expunged object properties Auth service tests: - Update to use email_or_username field instead of username Admin service tests: - Fix statistics test to use stats_service module - Remove vendor_name filter test (field removed) - Update import job assertions Inventory/Marketplace/Stats/Vendor service tests: - Refactor to work with attached session objects - Update assertions for changed response formats - Improve test isolation and cleanup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,19 +1,16 @@
|
||||
# tests/test_marketplace_service.py
|
||||
# tests/unit/services/test_marketplace_service.py
|
||||
"""Unit tests for MarketplaceImportJobService."""
|
||||
import uuid
|
||||
|
||||
import pytest
|
||||
from unittest.mock import patch
|
||||
|
||||
from app.exceptions.base import ValidationException
|
||||
from app.exceptions.marketplace_import_job import (
|
||||
ImportJobCannotBeCancelledException,
|
||||
ImportJobCannotBeDeletedException,
|
||||
ImportJobNotFoundException,
|
||||
ImportJobNotOwnedException,
|
||||
)
|
||||
from app.exceptions.vendor import (
|
||||
UnauthorizedVendorAccessException,
|
||||
VendorNotFoundException,
|
||||
)
|
||||
from app.exceptions.vendor import UnauthorizedVendorAccessException
|
||||
from app.services.marketplace_import_job_service import MarketplaceImportJobService
|
||||
from models.database.marketplace_import_job import MarketplaceImportJob
|
||||
from models.schema.marketplace_import_job import MarketplaceImportJobRequest
|
||||
@@ -21,122 +18,65 @@ from models.schema.marketplace_import_job import MarketplaceImportJobRequest
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.marketplace
|
||||
class TestMarketplaceService:
|
||||
class TestMarketplaceImportJobService:
|
||||
"""Test suite for MarketplaceImportJobService."""
|
||||
|
||||
def setup_method(self):
|
||||
self.service = MarketplaceImportJobService()
|
||||
|
||||
def test_validate_vendor_access_success(self, db, test_vendor, test_user):
|
||||
"""Test successful vendor access validation"""
|
||||
# Set the vendor owner to the test user
|
||||
test_vendor.owner_user_id = test_user.id
|
||||
db.commit()
|
||||
|
||||
result = self.service.validate_vendor_access(
|
||||
db, test_vendor.vendor_code, test_user
|
||||
)
|
||||
|
||||
assert result.vendor_code == test_vendor.vendor_code
|
||||
assert result.owner_user_id == test_user.id
|
||||
|
||||
def test_validate_vendor_access_admin_can_access_any_vendor(
|
||||
self, db, test_vendor, test_admin
|
||||
):
|
||||
"""Test that admin users can access any vendor"""
|
||||
result = self.service.validate_vendor_access(
|
||||
db, test_vendor.vendor_code, test_admin
|
||||
)
|
||||
|
||||
assert result.vendor_code == test_vendor.vendor_code
|
||||
|
||||
def test_validate_vendor_access_vendor_not_found(self, db, test_user):
|
||||
"""Test vendor access validation when vendor doesn't exist"""
|
||||
with pytest.raises(VendorNotFoundException) as exc_info:
|
||||
self.service.validate_vendor_access(db, "NONEXISTENT", test_user)
|
||||
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "VENDOR_NOT_FOUND"
|
||||
assert exception.status_code == 404
|
||||
assert "NONEXISTENT" in exception.message
|
||||
|
||||
def test_validate_vendor_access_permission_denied(
|
||||
self, db, test_vendor, test_user, other_user
|
||||
):
|
||||
"""Test vendor access validation when user doesn't own the vendor"""
|
||||
# Set the vendor owner to a different user
|
||||
test_vendor.owner_user_id = other_user.id
|
||||
db.commit()
|
||||
|
||||
with pytest.raises(UnauthorizedVendorAccessException) as exc_info:
|
||||
self.service.validate_vendor_access(db, test_vendor.vendor_code, test_user)
|
||||
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "UNAUTHORIZED_VENDOR_ACCESS"
|
||||
assert exception.status_code == 403
|
||||
assert test_vendor.vendor_code in exception.message
|
||||
# ==================== create_import_job Tests ====================
|
||||
|
||||
def test_create_import_job_success(self, db, test_vendor, test_user):
|
||||
"""Test successful creation of import job"""
|
||||
# Set the vendor owner to the test user
|
||||
test_vendor.owner_user_id = test_user.id
|
||||
db.commit()
|
||||
|
||||
"""Test successful creation of import job."""
|
||||
request = MarketplaceImportJobRequest(
|
||||
url="https://example.com/products.csv",
|
||||
source_url="https://example.com/products.csv",
|
||||
marketplace="Amazon",
|
||||
vendor_code=test_vendor.vendor_code,
|
||||
batch_size=1000,
|
||||
)
|
||||
|
||||
result = self.service.create_import_job(db, request, test_user)
|
||||
result = self.service.create_import_job(db, request, test_vendor, test_user)
|
||||
|
||||
assert result.marketplace == "Amazon"
|
||||
assert result.vendor_id == test_vendor.id
|
||||
assert result.user_id == test_user.id
|
||||
assert result.status == "pending"
|
||||
assert result.source_url == "https://example.com/products.csv"
|
||||
assert result.vendor_name == test_vendor.name
|
||||
|
||||
def test_create_import_job_invalid_vendor(self, db, test_user):
|
||||
"""Test import job creation with invalid vendor"""
|
||||
def test_create_import_job_default_marketplace(self, db, test_vendor, test_user):
|
||||
"""Test import job creation with default marketplace."""
|
||||
request = MarketplaceImportJobRequest(
|
||||
url="https://example.com/products.csv",
|
||||
marketplace="Amazon",
|
||||
vendor_code="INVALID_VENDOR",
|
||||
batch_size=1000,
|
||||
source_url="https://example.com/products.csv",
|
||||
)
|
||||
|
||||
with pytest.raises(VendorNotFoundException) as exc_info:
|
||||
self.service.create_import_job(db, request, test_user)
|
||||
result = self.service.create_import_job(db, request, test_vendor, test_user)
|
||||
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "VENDOR_NOT_FOUND"
|
||||
assert "INVALID_VENDOR" in exception.message
|
||||
|
||||
def test_create_import_job_unauthorized_access(
|
||||
self, db, test_vendor, test_user, other_user
|
||||
):
|
||||
"""Test import job creation with unauthorized vendor access"""
|
||||
# Set the vendor owner to a different user
|
||||
test_vendor.owner_user_id = other_user.id
|
||||
db.commit()
|
||||
assert result.marketplace == "Letzshop" # Default
|
||||
|
||||
def test_create_import_job_database_error(self, db, test_vendor, test_user, monkeypatch):
|
||||
"""Test import job creation handles database errors."""
|
||||
request = MarketplaceImportJobRequest(
|
||||
url="https://example.com/products.csv",
|
||||
source_url="https://example.com/products.csv",
|
||||
marketplace="Amazon",
|
||||
vendor_code=test_vendor.vendor_code,
|
||||
batch_size=1000,
|
||||
)
|
||||
|
||||
with pytest.raises(UnauthorizedVendorAccessException) as exc_info:
|
||||
self.service.create_import_job(db, request, test_user)
|
||||
def mock_commit():
|
||||
raise Exception("Database commit failed")
|
||||
|
||||
monkeypatch.setattr(db, "commit", mock_commit)
|
||||
|
||||
with pytest.raises(ValidationException) as exc_info:
|
||||
self.service.create_import_job(db, request, test_vendor, test_user)
|
||||
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "UNAUTHORIZED_VENDOR_ACCESS"
|
||||
assert exception.error_code == "VALIDATION_ERROR"
|
||||
assert "Failed to create import job" in exception.message
|
||||
|
||||
# ==================== get_import_job_by_id Tests ====================
|
||||
|
||||
def test_get_import_job_by_id_success(
|
||||
self, db, test_marketplace_import_job, test_user
|
||||
):
|
||||
"""Test getting import job by ID for job owner"""
|
||||
"""Test getting import job by ID for job owner."""
|
||||
result = self.service.get_import_job_by_id(
|
||||
db, test_marketplace_import_job.id, test_user
|
||||
)
|
||||
@@ -147,7 +87,7 @@ class TestMarketplaceService:
|
||||
def test_get_import_job_by_id_admin_access(
|
||||
self, db, test_marketplace_import_job, test_admin
|
||||
):
|
||||
"""Test that admin can access any import job"""
|
||||
"""Test that admin can access any import job."""
|
||||
result = self.service.get_import_job_by_id(
|
||||
db, test_marketplace_import_job.id, test_admin
|
||||
)
|
||||
@@ -155,7 +95,7 @@ class TestMarketplaceService:
|
||||
assert result.id == test_marketplace_import_job.id
|
||||
|
||||
def test_get_import_job_by_id_not_found(self, db, test_user):
|
||||
"""Test getting non-existent import job"""
|
||||
"""Test getting non-existent import job."""
|
||||
with pytest.raises(ImportJobNotFoundException) as exc_info:
|
||||
self.service.get_import_job_by_id(db, 99999, test_user)
|
||||
|
||||
@@ -167,7 +107,7 @@ class TestMarketplaceService:
|
||||
def test_get_import_job_by_id_access_denied(
|
||||
self, db, test_marketplace_import_job, other_user
|
||||
):
|
||||
"""Test access denied when user doesn't own the job"""
|
||||
"""Test access denied when user doesn't own the job."""
|
||||
with pytest.raises(ImportJobNotOwnedException) as exc_info:
|
||||
self.service.get_import_job_by_id(
|
||||
db, test_marketplace_import_job.id, other_user
|
||||
@@ -176,42 +116,101 @@ class TestMarketplaceService:
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "IMPORT_JOB_NOT_OWNED"
|
||||
assert exception.status_code == 403
|
||||
assert str(test_marketplace_import_job.id) in exception.message
|
||||
|
||||
def test_get_import_jobs_user_filter(
|
||||
self, db, test_marketplace_import_job, test_user
|
||||
def test_get_import_job_by_id_database_error(self, db, test_user, monkeypatch):
|
||||
"""Test get import job handles database errors."""
|
||||
def mock_query(*args):
|
||||
raise Exception("Database query failed")
|
||||
|
||||
monkeypatch.setattr(db, "query", mock_query)
|
||||
|
||||
with pytest.raises(ValidationException) as exc_info:
|
||||
self.service.get_import_job_by_id(db, 1, test_user)
|
||||
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "VALIDATION_ERROR"
|
||||
|
||||
# ==================== get_import_job_for_vendor Tests ====================
|
||||
|
||||
def test_get_import_job_for_vendor_success(
|
||||
self, db, test_marketplace_import_job, test_vendor
|
||||
):
|
||||
"""Test getting import jobs filtered by user"""
|
||||
jobs = self.service.get_import_jobs(db, test_user)
|
||||
"""Test getting import job for vendor."""
|
||||
result = self.service.get_import_job_for_vendor(
|
||||
db, test_marketplace_import_job.id, test_vendor.id
|
||||
)
|
||||
|
||||
assert result.id == test_marketplace_import_job.id
|
||||
assert result.vendor_id == test_vendor.id
|
||||
|
||||
def test_get_import_job_for_vendor_not_found(self, db, test_vendor):
|
||||
"""Test getting non-existent import job for vendor."""
|
||||
with pytest.raises(ImportJobNotFoundException) as exc_info:
|
||||
self.service.get_import_job_for_vendor(db, 99999, test_vendor.id)
|
||||
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "IMPORT_JOB_NOT_FOUND"
|
||||
|
||||
def test_get_import_job_for_vendor_wrong_vendor(
|
||||
self, db, test_marketplace_import_job, other_user, other_company
|
||||
):
|
||||
"""Test getting import job for wrong vendor."""
|
||||
from models.database.vendor import Vendor
|
||||
|
||||
# Create another vendor
|
||||
unique_id = str(uuid.uuid4())[:8]
|
||||
other_vendor = Vendor(
|
||||
company_id=other_company.id,
|
||||
vendor_code=f"OTHER_{unique_id.upper()}",
|
||||
subdomain=f"other{unique_id.lower()}",
|
||||
name=f"Other Vendor {unique_id}",
|
||||
is_active=True,
|
||||
)
|
||||
db.add(other_vendor)
|
||||
db.commit()
|
||||
db.refresh(other_vendor)
|
||||
|
||||
with pytest.raises(UnauthorizedVendorAccessException):
|
||||
self.service.get_import_job_for_vendor(
|
||||
db, test_marketplace_import_job.id, other_vendor.id
|
||||
)
|
||||
|
||||
# ==================== get_import_jobs Tests ====================
|
||||
|
||||
def test_get_import_jobs_success(
|
||||
self, db, test_marketplace_import_job, test_vendor, test_user
|
||||
):
|
||||
"""Test getting import jobs for vendor."""
|
||||
jobs = self.service.get_import_jobs(db, test_vendor, test_user)
|
||||
|
||||
assert len(jobs) >= 1
|
||||
assert any(job.id == test_marketplace_import_job.id for job in jobs)
|
||||
assert test_marketplace_import_job.user_id == test_user.id
|
||||
|
||||
def test_get_import_jobs_admin_sees_all(
|
||||
self, db, test_marketplace_import_job, test_admin
|
||||
def test_get_import_jobs_admin_sees_all_vendor_jobs(
|
||||
self, db, test_marketplace_import_job, test_vendor, test_admin
|
||||
):
|
||||
"""Test that admin sees all import jobs"""
|
||||
jobs = self.service.get_import_jobs(db, test_admin)
|
||||
"""Test that admin sees all vendor jobs."""
|
||||
jobs = self.service.get_import_jobs(db, test_vendor, test_admin)
|
||||
|
||||
assert len(jobs) >= 1
|
||||
assert any(job.id == test_marketplace_import_job.id for job in jobs)
|
||||
|
||||
def test_get_import_jobs_with_marketplace_filter(
|
||||
self, db, test_marketplace_import_job, test_user
|
||||
self, db, test_marketplace_import_job, test_vendor, test_user
|
||||
):
|
||||
"""Test getting import jobs with marketplace filter"""
|
||||
"""Test getting import jobs with marketplace filter."""
|
||||
jobs = self.service.get_import_jobs(
|
||||
db, test_user, marketplace=test_marketplace_import_job.marketplace
|
||||
db, test_vendor, test_user, marketplace=test_marketplace_import_job.marketplace
|
||||
)
|
||||
|
||||
assert len(jobs) >= 1
|
||||
assert any(
|
||||
job.marketplace == test_marketplace_import_job.marketplace for job in jobs
|
||||
assert all(
|
||||
test_marketplace_import_job.marketplace.lower() in job.marketplace.lower()
|
||||
for job in jobs
|
||||
)
|
||||
|
||||
def test_get_import_jobs_with_pagination(self, db, test_user, test_vendor):
|
||||
"""Test getting import jobs with pagination"""
|
||||
def test_get_import_jobs_with_pagination(self, db, test_vendor, test_user):
|
||||
"""Test getting import jobs with pagination."""
|
||||
unique_id = str(uuid.uuid4())[:8]
|
||||
|
||||
# Create multiple import jobs
|
||||
@@ -219,9 +218,8 @@ class TestMarketplaceService:
|
||||
job = MarketplaceImportJob(
|
||||
status="completed",
|
||||
marketplace=f"Marketplace_{unique_id}_{i}",
|
||||
vendor_name=f"Test_vendor_{unique_id}_{i}",
|
||||
user_id=test_user.id,
|
||||
vendor_id=test_vendor.id,
|
||||
user_id=test_user.id,
|
||||
source_url=f"https://test-{i}.example.com/import",
|
||||
imported_count=0,
|
||||
updated_count=0,
|
||||
@@ -231,263 +229,170 @@ class TestMarketplaceService:
|
||||
db.add(job)
|
||||
db.commit()
|
||||
|
||||
jobs = self.service.get_import_jobs(db, test_user, skip=2, limit=2)
|
||||
jobs = self.service.get_import_jobs(db, test_vendor, test_user, skip=2, limit=2)
|
||||
|
||||
assert len(jobs) <= 2 # Should be at most 2
|
||||
assert len(jobs) <= 2
|
||||
|
||||
def test_get_import_jobs_empty(self, db, test_user, other_user, other_company):
|
||||
"""Test getting import jobs when none exist."""
|
||||
from models.database.vendor import Vendor
|
||||
|
||||
# Create a vendor with no jobs
|
||||
unique_id = str(uuid.uuid4())[:8]
|
||||
empty_vendor = Vendor(
|
||||
company_id=other_company.id,
|
||||
vendor_code=f"EMPTY_{unique_id.upper()}",
|
||||
subdomain=f"empty{unique_id.lower()}",
|
||||
name=f"Empty Vendor {unique_id}",
|
||||
is_active=True,
|
||||
)
|
||||
db.add(empty_vendor)
|
||||
db.commit()
|
||||
db.refresh(empty_vendor)
|
||||
|
||||
jobs = self.service.get_import_jobs(db, empty_vendor, other_user)
|
||||
|
||||
assert len(jobs) == 0
|
||||
|
||||
def test_get_import_jobs_database_error(self, db, test_vendor, test_user, monkeypatch):
|
||||
"""Test get import jobs handles database errors."""
|
||||
def mock_query(*args):
|
||||
raise Exception("Database query failed")
|
||||
|
||||
monkeypatch.setattr(db, "query", mock_query)
|
||||
|
||||
def test_get_import_jobs_database_error(self, db_with_error, test_user):
|
||||
"""Test getting import jobs handles database errors"""
|
||||
with pytest.raises(ValidationException) as exc_info:
|
||||
self.service.get_import_jobs(db_with_error, test_user)
|
||||
self.service.get_import_jobs(db, test_vendor, test_user)
|
||||
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "VALIDATION_ERROR"
|
||||
assert "Failed to retrieve import jobs" in exception.message
|
||||
|
||||
def test_update_job_status_success(self, db, test_marketplace_import_job):
|
||||
"""Test updating job status"""
|
||||
result = self.service.update_job_status(
|
||||
db,
|
||||
test_marketplace_import_job.id,
|
||||
"completed",
|
||||
imported_count=100,
|
||||
total_processed=100,
|
||||
)
|
||||
# ==================== convert_to_response_model Tests ====================
|
||||
|
||||
assert result.status == "completed"
|
||||
assert result.imported_count == 100
|
||||
assert result.total_processed == 100
|
||||
def test_convert_to_response_model(self, db, test_marketplace_import_job, test_vendor):
|
||||
"""Test converting database model to response model."""
|
||||
from models.database.marketplace_import_job import MarketplaceImportJob as MIJ
|
||||
|
||||
def test_update_job_status_not_found(self, db):
|
||||
"""Test updating non-existent job status"""
|
||||
with pytest.raises(ImportJobNotFoundException) as exc_info:
|
||||
self.service.update_job_status(db, 99999, "completed")
|
||||
# Re-query to get fresh instance with relationships
|
||||
job = db.query(MIJ).filter(MIJ.id == test_marketplace_import_job.id).first()
|
||||
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "IMPORT_JOB_NOT_FOUND"
|
||||
assert "99999" in exception.message
|
||||
response = self.service.convert_to_response_model(job)
|
||||
|
||||
def test_update_job_status_database_error(self, db_with_error):
|
||||
"""Test updating job status handles database errors"""
|
||||
with pytest.raises(ValidationException) as exc_info:
|
||||
self.service.update_job_status(db_with_error, 1, "completed")
|
||||
assert response.job_id == job.id
|
||||
assert response.status == job.status
|
||||
assert response.marketplace == job.marketplace
|
||||
assert response.vendor_id == job.vendor_id
|
||||
assert response.imported == (job.imported_count or 0)
|
||||
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "VALIDATION_ERROR"
|
||||
assert "Failed to update job status" in exception.message
|
||||
|
||||
def test_get_job_stats_user(self, db, test_marketplace_import_job, test_user):
|
||||
"""Test getting job statistics for user"""
|
||||
stats = self.service.get_job_stats(db, test_user)
|
||||
|
||||
assert stats["total_jobs"] >= 1
|
||||
assert "pending_jobs" in stats
|
||||
assert "running_jobs" in stats
|
||||
assert "completed_jobs" in stats
|
||||
assert "failed_jobs" in stats
|
||||
assert isinstance(stats["total_jobs"], int)
|
||||
|
||||
def test_get_job_stats_admin(self, db, test_marketplace_import_job, test_admin):
|
||||
"""Test getting job statistics for admin"""
|
||||
stats = self.service.get_job_stats(db, test_admin)
|
||||
|
||||
assert stats["total_jobs"] >= 1
|
||||
assert isinstance(stats["total_jobs"], int)
|
||||
|
||||
def test_get_job_stats_database_error(self, db_with_error, test_user):
|
||||
"""Test getting job stats handles database errors"""
|
||||
with pytest.raises(ValidationException) as exc_info:
|
||||
self.service.get_job_stats(db_with_error, test_user)
|
||||
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "VALIDATION_ERROR"
|
||||
assert "Failed to retrieve job statistics" in exception.message
|
||||
|
||||
def test_convert_to_response_model(self, test_marketplace_import_job):
|
||||
"""Test converting database model to response model"""
|
||||
response = self.service.convert_to_response_model(test_marketplace_import_job)
|
||||
|
||||
assert response.job_id == test_marketplace_import_job.id
|
||||
assert response.status == test_marketplace_import_job.status
|
||||
assert response.marketplace == test_marketplace_import_job.marketplace
|
||||
assert response.imported == (test_marketplace_import_job.imported_count or 0)
|
||||
|
||||
def test_cancel_import_job_success(self, db, test_user, test_vendor):
|
||||
"""Test cancelling a pending import job"""
|
||||
unique_id = str(uuid.uuid4())[:8]
|
||||
|
||||
# Create a pending job
|
||||
job = MarketplaceImportJob(
|
||||
status="pending",
|
||||
marketplace="Amazon",
|
||||
vendor_name=f"TEST_VENDOR_{unique_id}",
|
||||
user_id=test_user.id,
|
||||
vendor_id=test_vendor.id,
|
||||
source_url="https://test.example.com/import",
|
||||
imported_count=0,
|
||||
updated_count=0,
|
||||
total_processed=0,
|
||||
error_count=0,
|
||||
)
|
||||
db.add(job)
|
||||
db.commit()
|
||||
db.refresh(job)
|
||||
|
||||
result = self.service.cancel_import_job(db, job.id, test_user)
|
||||
|
||||
assert result.status == "cancelled"
|
||||
assert result.completed_at is not None
|
||||
|
||||
def test_cancel_import_job_not_found(self, db, test_user):
|
||||
"""Test cancelling non-existent import job"""
|
||||
with pytest.raises(ImportJobNotFoundException) as exc_info:
|
||||
self.service.cancel_import_job(db, 99999, test_user)
|
||||
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "IMPORT_JOB_NOT_FOUND"
|
||||
|
||||
def test_cancel_import_job_access_denied(
|
||||
self, db, test_marketplace_import_job, other_user
|
||||
def test_convert_to_response_model_with_all_fields(
|
||||
self, db, test_vendor, test_user
|
||||
):
|
||||
"""Test cancelling import job without access"""
|
||||
with pytest.raises(ImportJobNotOwnedException) as exc_info:
|
||||
self.service.cancel_import_job(
|
||||
db, test_marketplace_import_job.id, other_user
|
||||
)
|
||||
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "IMPORT_JOB_NOT_OWNED"
|
||||
|
||||
def test_cancel_import_job_invalid_status(
|
||||
self, db, test_marketplace_import_job, test_user
|
||||
):
|
||||
"""Test cancelling a job that can't be cancelled"""
|
||||
# Set job status to completed
|
||||
test_marketplace_import_job.status = "completed"
|
||||
db.commit()
|
||||
|
||||
with pytest.raises(ImportJobCannotBeCancelledException) as exc_info:
|
||||
self.service.cancel_import_job(
|
||||
db, test_marketplace_import_job.id, test_user
|
||||
)
|
||||
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "IMPORT_JOB_CANNOT_BE_CANCELLED"
|
||||
assert exception.status_code == 400
|
||||
assert "completed" in exception.message
|
||||
|
||||
def test_delete_import_job_success(self, db, test_user, test_vendor):
|
||||
"""Test deleting a completed import job"""
|
||||
"""Test converting model with all fields populated."""
|
||||
unique_id = str(uuid.uuid4())[:8]
|
||||
from datetime import datetime
|
||||
|
||||
# Create a completed job
|
||||
job = MarketplaceImportJob(
|
||||
status="completed",
|
||||
marketplace="Amazon",
|
||||
vendor_name=f"TEST_VENDOR_{unique_id}",
|
||||
user_id=test_user.id,
|
||||
marketplace="TestMarket",
|
||||
vendor_id=test_vendor.id,
|
||||
source_url="https://test.example.com/import",
|
||||
imported_count=0,
|
||||
updated_count=0,
|
||||
total_processed=0,
|
||||
error_count=0,
|
||||
)
|
||||
db.add(job)
|
||||
db.commit()
|
||||
db.refresh(job)
|
||||
job_id = job.id
|
||||
|
||||
result = self.service.delete_import_job(db, job_id, test_user)
|
||||
|
||||
assert result is True
|
||||
|
||||
# Verify the job is actually deleted
|
||||
deleted_job = (
|
||||
db.query(MarketplaceImportJob)
|
||||
.filter(MarketplaceImportJob.id == job_id)
|
||||
.first()
|
||||
)
|
||||
assert deleted_job is None
|
||||
|
||||
def test_delete_import_job_not_found(self, db, test_user):
|
||||
"""Test deleting non-existent import job"""
|
||||
with pytest.raises(ImportJobNotFoundException) as exc_info:
|
||||
self.service.delete_import_job(db, 99999, test_user)
|
||||
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "IMPORT_JOB_NOT_FOUND"
|
||||
|
||||
def test_delete_import_job_access_denied(
|
||||
self, db, test_marketplace_import_job, other_user
|
||||
):
|
||||
"""Test deleting import job without access"""
|
||||
with pytest.raises(ImportJobNotOwnedException) as exc_info:
|
||||
self.service.delete_import_job(
|
||||
db, test_marketplace_import_job.id, other_user
|
||||
)
|
||||
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "IMPORT_JOB_NOT_OWNED"
|
||||
|
||||
def test_delete_import_job_invalid_status(self, db, test_user, test_vendor):
|
||||
"""Test deleting a job that can't be deleted"""
|
||||
unique_id = str(uuid.uuid4())[:8]
|
||||
|
||||
# Create a pending job
|
||||
job = MarketplaceImportJob(
|
||||
status="pending",
|
||||
marketplace="Amazon",
|
||||
vendor_name=f"TEST_VENDOR_{unique_id}",
|
||||
user_id=test_user.id,
|
||||
vendor_id=test_vendor.id,
|
||||
source_url="https://test.example.com/import",
|
||||
imported_count=0,
|
||||
updated_count=0,
|
||||
total_processed=0,
|
||||
error_count=0,
|
||||
imported_count=100,
|
||||
updated_count=50,
|
||||
total_processed=150,
|
||||
error_count=5,
|
||||
error_message="Some errors occurred",
|
||||
started_at=datetime.utcnow(),
|
||||
completed_at=datetime.utcnow(),
|
||||
)
|
||||
db.add(job)
|
||||
db.commit()
|
||||
db.refresh(job)
|
||||
|
||||
with pytest.raises(ImportJobCannotBeDeletedException) as exc_info:
|
||||
self.service.delete_import_job(db, job.id, test_user)
|
||||
response = self.service.convert_to_response_model(job)
|
||||
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "IMPORT_JOB_CANNOT_BE_DELETED"
|
||||
assert exception.status_code == 400
|
||||
assert "pending" in exception.message
|
||||
assert response.imported == 100
|
||||
assert response.updated == 50
|
||||
assert response.total_processed == 150
|
||||
assert response.error_count == 5
|
||||
assert response.error_message == "Some errors occurred"
|
||||
assert response.started_at is not None
|
||||
assert response.completed_at is not None
|
||||
|
||||
# Test edge cases and error scenarios
|
||||
def test_validate_vendor_access_case_insensitive(self, db, test_vendor, test_user):
|
||||
"""Test vendor access validation is case insensitive"""
|
||||
test_vendor.owner_user_id = test_user.id
|
||||
db.commit()
|
||||
|
||||
# Test with lowercase vendor code
|
||||
result = self.service.validate_vendor_access(
|
||||
db, test_vendor.vendor_code.lower(), test_user
|
||||
)
|
||||
assert result.vendor_code == test_vendor.vendor_code
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.marketplace
|
||||
class TestMarketplaceImportJobSchema:
|
||||
"""Test suite for MarketplaceImportJobRequest schema validation."""
|
||||
|
||||
# Test with uppercase vendor code
|
||||
result = self.service.validate_vendor_access(
|
||||
db, test_vendor.vendor_code.upper(), test_user
|
||||
)
|
||||
assert result.vendor_code == test_vendor.vendor_code
|
||||
|
||||
def test_create_import_job_database_error(self, db_with_error, test_user):
|
||||
"""Test import job creation handles database errors"""
|
||||
def test_valid_request(self):
|
||||
"""Test valid request schema."""
|
||||
request = MarketplaceImportJobRequest(
|
||||
url="https://example.com/products.csv",
|
||||
source_url="https://example.com/products.csv",
|
||||
marketplace="Amazon",
|
||||
vendor_code="TEST_VENDOR",
|
||||
batch_size=1000,
|
||||
)
|
||||
|
||||
with pytest.raises(ValidationException) as exc_info:
|
||||
self.service.create_import_job(db_with_error, request, test_user)
|
||||
assert request.source_url == "https://example.com/products.csv"
|
||||
assert request.marketplace == "Amazon"
|
||||
assert request.batch_size == 1000
|
||||
|
||||
exception = exc_info.value
|
||||
assert exception.error_code == "VALIDATION_ERROR"
|
||||
def test_default_values(self):
|
||||
"""Test default values for optional fields."""
|
||||
request = MarketplaceImportJobRequest(
|
||||
source_url="https://example.com/products.csv",
|
||||
)
|
||||
|
||||
assert request.marketplace == "Letzshop"
|
||||
assert request.batch_size == 1000
|
||||
|
||||
def test_url_validation_http(self):
|
||||
"""Test URL validation accepts http."""
|
||||
request = MarketplaceImportJobRequest(
|
||||
source_url="http://example.com/products.csv",
|
||||
)
|
||||
|
||||
assert request.source_url == "http://example.com/products.csv"
|
||||
|
||||
def test_url_validation_invalid(self):
|
||||
"""Test URL validation rejects invalid URLs."""
|
||||
with pytest.raises(ValueError) as exc_info:
|
||||
MarketplaceImportJobRequest(
|
||||
source_url="ftp://example.com/products.csv",
|
||||
)
|
||||
|
||||
assert "URL must start with http://" in str(exc_info.value)
|
||||
|
||||
def test_url_with_trailing_slash(self):
|
||||
"""Test URL with trailing slash is accepted."""
|
||||
request = MarketplaceImportJobRequest(
|
||||
source_url="https://example.com/products.csv/",
|
||||
)
|
||||
|
||||
assert request.source_url == "https://example.com/products.csv/"
|
||||
|
||||
def test_batch_size_validation_min(self):
|
||||
"""Test batch_size validation minimum."""
|
||||
with pytest.raises(ValueError):
|
||||
MarketplaceImportJobRequest(
|
||||
source_url="https://example.com/products.csv",
|
||||
batch_size=50, # Below minimum of 100
|
||||
)
|
||||
|
||||
def test_batch_size_validation_max(self):
|
||||
"""Test batch_size validation maximum."""
|
||||
with pytest.raises(ValueError):
|
||||
MarketplaceImportJobRequest(
|
||||
source_url="https://example.com/products.csv",
|
||||
batch_size=20000, # Above maximum of 10000
|
||||
)
|
||||
|
||||
def test_marketplace_strips_whitespace(self):
|
||||
"""Test marketplace strips whitespace."""
|
||||
request = MarketplaceImportJobRequest(
|
||||
source_url="https://example.com/products.csv",
|
||||
marketplace=" Amazon ",
|
||||
)
|
||||
|
||||
assert request.marketplace == "Amazon"
|
||||
|
||||
Reference in New Issue
Block a user