Newer
Older
CNCTools / ReferenceSurfaceGenerator / backend / app / tests / test_processing.py
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)