# # This file is part of gunicorn released under the MIT license. # See the NOTICE for more information. """ Unit Tests for Task Workers These tests verify the task worker logic without running Gunicorn. They test the DirtyApp classes directly. """ import pytest from examples.celery_alternative.tasks import ( EmailWorker, ImageWorker, DataWorker, ScheduledWorker, ) class TestEmailWorker: """Tests for EmailWorker task class.""" def setup_method(self): """Set up test fixtures.""" self.worker = EmailWorker() self.worker.init() def test_send_email(self): """Test sending a single email.""" result = self.worker("send_email", to="test@example.com", subject="Test", body="Hello") assert result["status"] == "sent" assert result["to"] == "test@example.com" assert result["subject"] == "Test" assert "message_id" in result assert "timestamp" in result def test_send_email_increments_counter(self): """Test that email counter increments.""" initial_count = self.worker.emails_sent self.worker("send_email", to="a@x.com", subject="S", body="B") self.worker("send_email", to="b@x.com", subject="S", body="B") assert self.worker.emails_sent == initial_count + 2 def test_send_bulk_emails_streaming(self): """Test bulk email sending with progress streaming.""" recipients = ["a@x.com", "b@x.com", "c@x.com"] results = list(self.worker("send_bulk_emails", recipients=recipients, subject="Bulk", body="Hello all")) # Should have progress updates + final complete assert len(results) == len(recipients) + 1 # Check progress updates for i, r in enumerate(results[:-1]): assert r["type"] == "progress" assert r["current"] == i + 1 assert r["total"] == len(recipients) # Check final result final = results[-1] assert final["type"] == "complete" assert final["total"] == len(recipients) assert final["sent"] == len(recipients) def test_stats(self): """Test worker statistics.""" self.worker("send_email", to="x@x.com", subject="S", body="B") stats = self.worker("stats") assert stats["emails_sent"] >= 1 assert stats["smtp_connected"] is True assert "worker_pid" in stats def test_unknown_action_raises(self): """Test that unknown actions raise ValueError.""" with pytest.raises(ValueError, match="Unknown action"): self.worker("nonexistent_action") def test_private_method_raises(self): """Test that private methods cannot be called.""" with pytest.raises(ValueError, match="Unknown action"): self.worker("_connect_smtp") class TestImageWorker: """Tests for ImageWorker task class.""" def setup_method(self): """Set up test fixtures.""" self.worker = ImageWorker() self.worker.init() def test_resize_image(self): """Test image resizing.""" result = self.worker("resize", image_data="fake_image_data", width=800, height=600) assert result["status"] == "resized" assert result["target_dimensions"] == "800x600" assert "result_id" in result def test_generate_thumbnail(self): """Test thumbnail generation.""" result = self.worker("generate_thumbnail", image_data="fake_image_data", size=150) assert result["status"] == "resized" assert result["target_dimensions"] == "150x150" def test_process_batch_streaming(self): """Test batch processing with progress streaming.""" images = [ {"id": "img1", "data": b"data1"}, {"id": "img2", "data": b"data2"}, {"id": "img3", "data": b"data3"}, ] results = list(self.worker("process_batch", images=images, operation="resize", width=800, height=600)) # Progress for each image + complete assert len(results) == len(images) + 1 # Check progress updates for i, r in enumerate(results[:-1]): assert r["type"] == "progress" assert r["image_id"] == f"img{i+1}" assert "result" in r # Check final result final = results[-1] assert final["type"] == "complete" def test_stats(self): """Test worker statistics.""" self.worker("resize", image_data=b"x", width=100, height=100) stats = self.worker("stats") assert stats["images_processed"] >= 1 assert "pil_available" in stats assert "worker_pid" in stats class TestDataWorker: """Tests for DataWorker task class.""" def setup_method(self): """Set up test fixtures.""" self.worker = DataWorker() self.worker.init() def test_aggregate_sum(self): """Test data aggregation with sum.""" data = [ {"category": "A", "value": 10}, {"category": "B", "value": 20}, {"category": "A", "value": 30}, ] result = self.worker("aggregate", data=data, group_by="category", agg_field="value", agg_func="sum") assert result["status"] == "completed" assert result["result"]["A"] == 40 assert result["result"]["B"] == 20 def test_aggregate_count(self): """Test data aggregation with count.""" data = [ {"category": "A", "value": 10}, {"category": "B", "value": 20}, {"category": "A", "value": 30}, ] result = self.worker("aggregate", data=data, group_by="category", agg_field="value", agg_func="count") assert result["result"]["A"] == 2 assert result["result"]["B"] == 1 def test_etl_pipeline_streaming(self): """Test ETL pipeline with progress streaming.""" source_data = [ {"name": "alice", "status": "active"}, {"name": "bob", "status": "inactive"}, {"name": "charlie", "status": "active"}, ] transformations = [ {"name": "filter_active", "type": "filter", "field": "status", "value": "active"}, {"name": "uppercase", "type": "map", "field": "name", "func": "upper"}, ] results = list(self.worker("etl_pipeline", source_data=source_data, transformations=transformations)) # extract + transforms + load + complete expected_steps = 1 + len(transformations) + 1 + 1 assert len(results) == expected_steps # Check phases assert results[0]["phase"] == "extract" assert results[1]["phase"] == "transform" assert results[2]["phase"] == "transform" assert results[3]["phase"] == "load" assert results[4]["type"] == "complete" # Final should have 2 records (filtered) assert results[4]["records_output"] == 2 def test_cached_query_miss_then_hit(self): """Test query caching - miss then hit.""" # First call - cache miss result1 = self.worker("cached_query", query_key="test_query", ttl=300) assert result1["status"] == "cache_miss" # Second call - cache hit result2 = self.worker("cached_query", query_key="test_query", ttl=300) assert result2["status"] == "cache_hit" def test_stats(self): """Test worker statistics.""" self.worker("aggregate", data=[{"a": 1}], group_by="a", agg_field="a") stats = self.worker("stats") assert stats["tasks_completed"] >= 1 assert "cache_size" in stats assert stats["db_connected"] is True class TestScheduledWorker: """Tests for ScheduledWorker task class.""" def setup_method(self): """Set up test fixtures.""" self.worker = ScheduledWorker() def test_cleanup_old_files(self): """Test file cleanup task.""" result = self.worker("cleanup_old_files", directory="/tmp/test", max_age_days=7) assert result["status"] == "completed" assert result["directory"] == "/tmp/test" assert "files_deleted" in result assert "space_freed_mb" in result def test_generate_daily_report(self): """Test daily report generation.""" result = self.worker("generate_daily_report") assert result["status"] == "completed" assert "report_date" in result assert "metrics" in result assert "active_users" in result["metrics"] assert "new_signups" in result["metrics"] assert "revenue" in result["metrics"] def test_sync_external_data(self): """Test external data sync.""" result = self.worker("sync_external_data", source="test_api") assert result["status"] == "completed" assert result["source"] == "test_api" assert "records_synced" in result def test_stats_tracks_runs(self): """Test that stats tracks task runs.""" self.worker("cleanup_old_files", directory="/tmp", max_age_days=1) self.worker("cleanup_old_files", directory="/tmp", max_age_days=1) self.worker("generate_daily_report") stats = self.worker("stats") assert stats["run_counts"]["cleanup_old_files"] == 2 assert stats["run_counts"]["generate_daily_report"] == 1 assert "cleanup_old_files" in stats["last_runs"]