| """Unit tests for progress response models.""" |
|
|
| import pytest |
| from pydantic import ValidationError |
|
|
| from src.server.models.progress_models import ( |
| BaseProgressResponse, |
| CrawlProgressResponse, |
| ProgressDetails, |
| ProjectCreationProgressResponse, |
| UploadProgressResponse, |
| create_progress_response, |
| ) |
|
|
|
|
| class TestProgressDetails: |
| """Test cases for ProgressDetails model.""" |
|
|
| def test_create_with_snake_case_fields(self): |
| """Test creating ProgressDetails with snake_case field names.""" |
| details = ProgressDetails( |
| current_chunk=25, total_chunks=100, current_batch=3, total_batches=6, chunks_per_second=5.5 |
| ) |
|
|
| assert details.current_chunk == 25 |
| assert details.total_chunks == 100 |
| assert details.current_batch == 3 |
| assert details.total_batches == 6 |
| assert details.chunks_per_second == 5.5 |
|
|
| def test_create_with_camel_case_fields(self): |
| """Test creating ProgressDetails with camelCase field names.""" |
| details = ProgressDetails(currentChunk=25, totalChunks=100, currentBatch=3, totalBatches=6, chunksPerSecond=5.5) |
|
|
| assert details.current_chunk == 25 |
| assert details.total_chunks == 100 |
| assert details.current_batch == 3 |
| assert details.total_batches == 6 |
| assert details.chunks_per_second == 5.5 |
|
|
| def test_model_dump_uses_aliases(self): |
| """Test that model_dump uses camelCase aliases.""" |
| details = ProgressDetails(current_chunk=25, total_chunks=100, chunks_per_second=2.5) |
|
|
| data = details.model_dump(by_alias=True) |
|
|
| assert "currentChunk" in data |
| assert "totalChunks" in data |
| assert "chunksPerSecond" in data |
| assert "current_chunk" not in data |
| assert "total_chunks" not in data |
|
|
|
|
| class TestBaseProgressResponse: |
| """Test cases for BaseProgressResponse model.""" |
|
|
| def test_create_minimal_response(self): |
| """Test creating minimal progress response.""" |
| response = BaseProgressResponse( |
| progress_id="test-123", status="running", progress=50.0, message="Processing..." |
| ) |
|
|
| assert response.progress_id == "test-123" |
| assert response.status == "running" |
| assert response.progress == 50.0 |
| assert response.message == "Processing..." |
|
|
| def test_progress_validation(self): |
| """Test that progress is validated to be between 0-100.""" |
| |
| response = BaseProgressResponse(progress_id="test-123", status="running", progress=50.0) |
| assert response.progress == 50.0 |
|
|
| |
| with pytest.raises(ValidationError): |
| BaseProgressResponse(progress_id="test-123", status="running", progress=150.0) |
|
|
| |
| with pytest.raises(ValidationError): |
| BaseProgressResponse(progress_id="test-123", status="running", progress=-10.0) |
|
|
| def test_logs_validation_and_conversion(self): |
| """Test logs field validation and conversion.""" |
| |
| response = BaseProgressResponse( |
| progress_id="test-123", status="running", progress=50.0, logs=["Starting", "Processing", "Almost done"] |
| ) |
| assert response.logs == ["Starting", "Processing", "Almost done"] |
|
|
| |
| response = BaseProgressResponse( |
| progress_id="test-123", status="running", progress=50.0, logs="Single log message" |
| ) |
| assert response.logs == ["Single log message"] |
|
|
| |
| response = BaseProgressResponse( |
| progress_id="test-123", |
| status="running", |
| progress=50.0, |
| logs=[ |
| {"message": "Starting", "timestamp": "2024-01-01T10:00:00"}, |
| {"message": "Processing", "timestamp": "2024-01-01T10:01:00"}, |
| ], |
| ) |
| assert response.logs == ["Starting", "Processing"] |
|
|
| def test_camel_case_aliases(self): |
| """Test that camelCase aliases work correctly.""" |
| response = BaseProgressResponse( |
| progressId="test-123", |
| status="running", |
| progress=50.0, |
| currentStep="processing", |
| stepMessage="Working on it", |
| ) |
|
|
| assert response.progress_id == "test-123" |
| assert response.current_step == "processing" |
| assert response.step_message == "Working on it" |
|
|
|
|
| class TestCrawlProgressResponse: |
| """Test cases for CrawlProgressResponse model.""" |
|
|
| def test_create_crawl_response_with_batch_info(self): |
| """Test creating crawl response with batch processing information.""" |
| response = CrawlProgressResponse( |
| progress_id="crawl-123", |
| status="document_storage", |
| progress=45.0, |
| message="Processing batch 3/6", |
| total_pages=60, |
| processed_pages=60, |
| current_batch=3, |
| total_batches=6, |
| completed_batches=2, |
| chunks_in_batch=25, |
| active_workers=4, |
| ) |
|
|
| assert response.progress_id == "crawl-123" |
| assert response.status == "document_storage" |
| assert response.current_batch == 3 |
| assert response.total_batches == 6 |
| assert response.completed_batches == 2 |
| assert response.chunks_in_batch == 25 |
| assert response.active_workers == 4 |
|
|
| def test_create_with_code_extraction_fields(self): |
| """Test creating crawl response with code extraction fields.""" |
| response = CrawlProgressResponse( |
| progress_id="crawl-123", |
| status="code_extraction", |
| progress=75.0, |
| code_blocks_found=150, |
| code_examples_stored=120, |
| completed_documents=45, |
| total_documents=50, |
| completed_summaries=30, |
| total_summaries=40, |
| ) |
|
|
| assert response.code_blocks_found == 150 |
| assert response.code_examples_stored == 120 |
| assert response.completed_documents == 45 |
| assert response.total_documents == 50 |
| assert response.completed_summaries == 30 |
| assert response.total_summaries == 40 |
|
|
| def test_status_validation(self): |
| """Test that only valid crawl statuses are accepted.""" |
| valid_statuses = [ |
| "starting", |
| "analyzing", |
| "crawling", |
| "processing", |
| "source_creation", |
| "document_storage", |
| "code_extraction", |
| "code_storage", |
| "finalization", |
| "completed", |
| "failed", |
| "cancelled", |
| "stopping", |
| "error", |
| ] |
|
|
| for status in valid_statuses: |
| response = CrawlProgressResponse(progress_id="test-123", status=status, progress=50.0) |
| assert response.status == status |
|
|
| |
| with pytest.raises(ValidationError): |
| CrawlProgressResponse(progress_id="test-123", status="invalid_status", progress=50.0) |
|
|
| def test_camel_case_field_aliases(self): |
| """Test that crawl-specific fields use camelCase aliases.""" |
| response = CrawlProgressResponse( |
| progress_id="test-123", |
| status="code_extraction", |
| progress=50.0, |
| currentUrl="https://example.com/page1", |
| totalPages=100, |
| processedPages=50, |
| codeBlocksFound=75, |
| totalBatches=6, |
| currentBatch=3, |
| ) |
|
|
| assert response.current_url == "https://example.com/page1" |
| assert response.total_pages == 100 |
| assert response.processed_pages == 50 |
| assert response.code_blocks_found == 75 |
| assert response.total_batches == 6 |
| assert response.current_batch == 3 |
|
|
| def test_duration_conversion(self): |
| """Test that duration is converted to string.""" |
| |
| response = CrawlProgressResponse(progress_id="test-123", status="completed", progress=100.0, duration=123.45) |
| assert response.duration == "123.45" |
|
|
| |
| response = CrawlProgressResponse(progress_id="test-123", status="completed", progress=100.0, duration=120) |
| assert response.duration == "120" |
|
|
| |
| response = CrawlProgressResponse( |
| progress_id="test-123", |
| status="processing", |
| progress=50.0, |
| duration=None, |
| ) |
| assert response.duration is None |
|
|
|
|
| class TestUploadProgressResponse: |
| """Test cases for UploadProgressResponse model.""" |
|
|
| def test_create_upload_response(self): |
| """Test creating upload progress response.""" |
| response = UploadProgressResponse( |
| progress_id="upload-123", |
| status="storing", |
| progress=80.0, |
| upload_type="document", |
| file_name="document.pdf", |
| file_type="application/pdf", |
| chunks_stored=400, |
| word_count=5000, |
| ) |
|
|
| assert response.progress_id == "upload-123" |
| assert response.status == "storing" |
| assert response.upload_type == "document" |
| assert response.file_name == "document.pdf" |
| assert response.file_type == "application/pdf" |
| assert response.chunks_stored == 400 |
| assert response.word_count == 5000 |
|
|
| def test_upload_status_validation(self): |
| """Test upload status validation.""" |
| valid_statuses = [ |
| "starting", |
| "reading", |
| "chunking", |
| "source_creation", |
| "summarizing", |
| "storing", |
| "completed", |
| "failed", |
| "cancelled", |
| ] |
|
|
| for status in valid_statuses: |
| response = UploadProgressResponse(progress_id="test-123", status=status, progress=50.0) |
| assert response.status == status |
|
|
|
|
| class TestProjectCreationProgressResponse: |
| """Test cases for ProjectCreationProgressResponse model.""" |
|
|
| def test_project_creation_status_validation(self): |
| """Test project creation status validation.""" |
| valid_statuses = [ |
| "starting", |
| "analyzing", |
| "generating_prp", |
| "creating_tasks", |
| "organizing", |
| "completed", |
| "failed", |
| "error", |
| ] |
|
|
| for status in valid_statuses: |
| response = ProjectCreationProgressResponse(progress_id="test-123", status=status, progress=50.0) |
| assert response.status == status |
|
|
| |
| with pytest.raises(ValidationError): |
| ProjectCreationProgressResponse(progress_id="test-123", status="invalid_status", progress=50.0) |
|
|
|
|
| class TestProgressResponseFactory: |
| """Test cases for create_progress_response factory function.""" |
|
|
| def test_create_crawl_response(self): |
| """Test creating crawl progress response via factory.""" |
| progress_data = { |
| "progress_id": "crawl-123", |
| "status": "document_storage", |
| "progress": 50, |
| "log": "Processing batch 3/6", |
| "current_batch": 3, |
| "total_batches": 6, |
| "total_pages": 60, |
| "processed_pages": 60, |
| } |
|
|
| response = create_progress_response("crawl", progress_data) |
|
|
| assert isinstance(response, CrawlProgressResponse) |
| assert response.progress_id == "crawl-123" |
| assert response.status == "document_storage" |
| assert response.current_batch == 3 |
| assert response.total_batches == 6 |
|
|
| def test_create_upload_response(self): |
| """Test creating upload progress response via factory.""" |
| progress_data = { |
| "progress_id": "upload-123", |
| "status": "storing", |
| "progress": 75, |
| "log": "Storing document chunks", |
| "file_name": "document.pdf", |
| "chunks_stored": 300, |
| } |
|
|
| response = create_progress_response("upload", progress_data) |
|
|
| assert isinstance(response, UploadProgressResponse) |
| assert response.progress_id == "upload-123" |
| assert response.status == "storing" |
| assert response.file_name == "document.pdf" |
| assert response.chunks_stored == 300 |
|
|
| def test_create_response_with_details(self): |
| """Test that factory creates details object from progress data.""" |
| progress_data = { |
| "progress_id": "test-123", |
| "status": "processing", |
| "progress": 50, |
| "current_batch": 3, |
| "total_batches": 6, |
| "current_chunk": 150, |
| "total_chunks": 300, |
| "chunks_per_second": 5.5, |
| } |
|
|
| response = create_progress_response("crawl", progress_data) |
|
|
| assert response.details is not None |
| assert response.details.current_batch == 3 |
| assert response.details.total_batches == 6 |
| assert response.details.current_chunk == 150 |
| assert response.details.total_chunks == 300 |
| assert response.details.chunks_per_second == 5.5 |
|
|
| def test_factory_handles_missing_fields(self): |
| """Test that factory handles missing required fields gracefully.""" |
| |
| progress_data = {"progress_id": "test-123", "progress": 50} |
|
|
| response = create_progress_response("crawl", progress_data) |
| assert response.status == "starting" |
|
|
| |
| progress_data = {"progress_id": "test-123", "status": "processing"} |
|
|
| response = create_progress_response("crawl", progress_data) |
| assert response.progress == 0 |
|
|
| def test_factory_unknown_operation_type(self): |
| """Test factory with unknown operation type falls back to base response.""" |
| progress_data = {"progress_id": "test-123", "status": "processing", "progress": 50} |
|
|
| response = create_progress_response("unknown_type", progress_data) |
| assert isinstance(response, BaseProgressResponse) |
| assert not isinstance(response, CrawlProgressResponse) |
|
|
| def test_factory_validation_error_fallback(self): |
| """Test that factory falls back to base response on validation errors.""" |
| |
| progress_data = { |
| "progress_id": "test-123", |
| "status": "invalid_crawl_status", |
| "progress": 50, |
| } |
|
|
| response = create_progress_response("crawl", progress_data) |
|
|
| |
| assert isinstance(response, BaseProgressResponse) |
| assert response.progress_id == "test-123" |
|
|