myrmidon / python /tests /progress_tracking /test_progress_models.py
tek Atrust
chore(deploy): build monolithic server for Hugging Face
d5ef46f
Raw
History Blame Contribute Delete
14.9 kB
"""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."""
# Valid progress
response = BaseProgressResponse(progress_id="test-123", status="running", progress=50.0)
assert response.progress == 50.0
# Invalid progress - too high
with pytest.raises(ValidationError):
BaseProgressResponse(progress_id="test-123", status="running", progress=150.0)
# Invalid progress - too low
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."""
# Test with list of strings
response = BaseProgressResponse(
progress_id="test-123", status="running", progress=50.0, logs=["Starting", "Processing", "Almost done"]
)
assert response.logs == ["Starting", "Processing", "Almost done"]
# Test with single string
response = BaseProgressResponse(
progress_id="test-123", status="running", progress=50.0, logs="Single log message"
)
assert response.logs == ["Single log message"]
# Test with list of dicts (log entries)
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", # camelCase
status="running",
progress=50.0,
currentStep="processing", # camelCase
stepMessage="Working on it", # camelCase
)
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
# Invalid status should raise validation error
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", # camelCase
totalPages=100, # camelCase
processedPages=50, # camelCase
codeBlocksFound=75, # camelCase
totalBatches=6, # camelCase
currentBatch=3, # camelCase
)
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."""
# Test with float
response = CrawlProgressResponse(progress_id="test-123", status="completed", progress=100.0, duration=123.45)
assert response.duration == "123.45"
# Test with int
response = CrawlProgressResponse(progress_id="test-123", status="completed", progress=100.0, duration=120)
assert response.duration == "120"
# Test with None
response = CrawlProgressResponse(
progress_id="test-123",
status="processing", # Use valid crawl status
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
# Invalid status should raise validation error
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."""
# Missing status
progress_data = {"progress_id": "test-123", "progress": 50}
response = create_progress_response("crawl", progress_data)
assert response.status == "starting" # Default
# Missing progress
progress_data = {"progress_id": "test-123", "status": "processing"}
response = create_progress_response("crawl", progress_data)
assert response.progress == 0 # Default
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."""
# Create invalid data that would fail CrawlProgressResponse validation
progress_data = {
"progress_id": "test-123",
"status": "invalid_crawl_status", # Invalid status
"progress": 50,
}
response = create_progress_response("crawl", progress_data)
# Should fall back to BaseProgressResponse
assert isinstance(response, BaseProgressResponse)
assert response.progress_id == "test-123"