import os
import shutil
import pytest
import uuid
from unittest.mock import patch, MagicMock
import numpy as np
from app.features.dxf_layered_curves.processing import process
from app.models import Job, JobStatus
ASSETS_DIR = os.path.join(os.path.dirname(__file__), "assets")
TEST_OUTPUT_DIR = os.path.join(os.path.dirname(__file__), "test_outputs")
VALID_FILE = os.path.join(ASSETS_DIR, "cube.obj")
EMPTY_FILE = os.path.join(ASSETS_DIR, "empty.obj")
@pytest.fixture
def job():
job_id = uuid.uuid4()
output_file = os.path.join(TEST_OUTPUT_DIR, f"{job_id}_test.dxf")
return Job(
id=job_id,
feature_id="dxf_layered_curves",
filename="cube.obj",
input_path=VALID_FILE,
output_path=output_file,
params={"num_layers": 5, "num_points_per_layer": 10},
)
@pytest.fixture(autouse=True)
def setup_and_teardown():
if os.path.exists(TEST_OUTPUT_DIR):
shutil.rmtree(TEST_OUTPUT_DIR)
os.makedirs(TEST_OUTPUT_DIR)
yield
async def run_generator_to_completion(generator):
final_result = None
async for result in generator:
final_result = result
return final_result
@pytest.mark.asyncio
async def test_happy_path_successful_processing(job):
final_status = await run_generator_to_completion(process(job))
assert final_status["status"] == "complete"
assert os.path.exists(job.output_path)
@pytest.mark.asyncio
@patch('trimesh.Trimesh.section', autospec=True)
async def test_partial_slicing_failure_completes_with_warnings(mock_section, job):
call_counter = {"count": 0}
fake_path = MagicMock()
fake_path.discrete = [np.array([[0,0], [1,0], [1,1], [0,1], [0,0]])]
fake_planar = MagicMock()
fake_planar.discrete = fake_path.discrete
fake_section = MagicMock()
fake_section.to_planar.return_value = (fake_planar, None)
def side_effect(self, *args, **kwargs):
call_counter["count"] += 1
return fake_section if call_counter["count"] <= 3 else None
mock_section.side_effect = side_effect
final_status = await run_generator_to_completion(process(job))
assert final_status["status"] == "complete"
assert "skipped" in final_status["message"] or "successfully" in final_status["message"]
assert os.path.exists(job.output_path)
@pytest.mark.asyncio
@patch('trimesh.Trimesh.section', return_value=None)
async def test_total_slicing_failure_fails_job(mock_section, job):
final_status = await run_generator_to_completion(process(job))
assert final_status["status"] == "failed"
assert not os.path.exists(job.output_path)