11"""Tests for Celery tasks."""
22
3+ import pytest
4+
35from src .celery_app import celery_app
4- from src .tasks import hello_world
6+ from src .tasks import (
7+ build_classify_and_update_chain ,
8+ classify_turn_message ,
9+ update_turn_message_label ,
10+ )
11+ from src .turn_client import TurnAPIClientError , TurnAPIServerError
512
613# Enable eager mode for all tests in this module (run tasks synchronously)
714celery_app .conf .task_always_eager = True
815celery_app .conf .task_eager_propagates = True
916
1017
11- class TestHelloWorldTask :
12- """Tests for the hello_world task."""
13-
14- def test_hello_world_default (self ):
15- """Test hello world task with default parameter."""
16- result = hello_world ()
17- assert result == {"status" : "success" , "message" : "Hello, World!" }
18-
19- def test_hello_world_custom_name (self ):
20- """Test hello world task with custom name."""
21- result = hello_world ("Celery" )
22- assert result == {"status" : "success" , "message" : "Hello, Celery!" }
23-
24- def test_hello_world_synchronous (self ):
25- """Test hello world task called synchronously."""
26- result = hello_world ("Testing" )
27- assert result == {"status" : "success" , "message" : "Hello, Testing!" }
28-
29-
3018class TestCeleryConfiguration :
3119 """Tests for Celery app configuration."""
3220
@@ -45,3 +33,133 @@ def test_task_serializer_is_json(self):
4533 """Test that task serializer is set to JSON."""
4634 assert celery_app .conf .task_serializer == "json"
4735 assert "json" in celery_app .conf .accept_content
36+
37+
38+ class TestClassifyTurnMessageTask :
39+ """Tests for the classify_turn_message task."""
40+
41+ def test_classify_success (self , mocker ):
42+ """Test successful classification."""
43+ # Mock classifier
44+ mock_classifier = mocker .Mock ()
45+ mock_classifier .classify .return_value = ("compliment" , 0.95 )
46+ mocker .patch ("src.tasks.classifier" , mock_classifier )
47+
48+ # Execute task
49+ result = classify_turn_message ("msg-123" , "Thank you so much!" )
50+
51+ # Verify classifier was called
52+ mock_classifier .classify .assert_called_once_with ("Thank you so much!" )
53+
54+ # Verify result
55+ assert result ["message_id" ] == "msg-123"
56+ assert result ["intent" ] == "compliment"
57+ assert result ["confidence" ] == 0.95
58+
59+ def test_classify_classifier_not_loaded (self , mocker ):
60+ """Test error when classifier is not loaded."""
61+ # Mock classifier as None (not loaded)
62+ mocker .patch ("src.tasks.classifier" , None )
63+
64+ # Execute task - should raise ValueError
65+ with pytest .raises (ValueError , match = "Intent classifier not loaded" ):
66+ classify_turn_message ("msg-789" , "Hello world" )
67+
68+ def test_classify_unclassified_intent (self , mocker ):
69+ """Test handling of unclassified intent."""
70+ # Mock classifier to return Unclassified
71+ mock_classifier = mocker .Mock ()
72+ mock_classifier .classify .return_value = ("Unclassified" , 0.42 )
73+ mocker .patch ("src.tasks.classifier" , mock_classifier )
74+
75+ # Execute task
76+ result = classify_turn_message ("msg-unclass" , "asdfghjkl" )
77+
78+ # Verify result
79+ assert result ["intent" ] == "Unclassified"
80+ assert result ["confidence" ] == 0.42
81+
82+
83+ class TestUpdateTurnMessageLabelTask :
84+ """Tests for the update_turn_message_label task."""
85+
86+ def test_update_turn_label_success (self , mocker , monkeypatch ):
87+ """Test successful Turn API update."""
88+ # Set up Turn API env vars
89+ monkeypatch .setenv ("TURN_API_BASE_URL" , "https://api.turn.io" )
90+ monkeypatch .setenv ("TURN_API_TOKEN" , "test-token" )
91+
92+ # Mock Turn API client
93+ mock_turn_client = mocker .Mock ()
94+ mock_turn_client .update_message_label .return_value = {
95+ "status" : "success" ,
96+ "message_id" : "msg-123" ,
97+ }
98+ mocker .patch ("src.tasks.TurnAPIClient" , return_value = mock_turn_client )
99+
100+ # Execute task
101+ result = update_turn_message_label (
102+ {"message_id" : "msg-123" , "intent" : "compliment" }
103+ )
104+
105+ # Verify Turn API was called
106+ mock_turn_client .update_message_label .assert_called_once_with (
107+ "msg-123" , "compliment"
108+ )
109+
110+ # Verify result
111+ assert result ["message_id" ] == "msg-123"
112+ assert result ["intent" ] == "compliment"
113+ assert result ["turn_response" ]["status" ] == "success"
114+
115+ def test_update_turn_label_client_error (self , mocker , monkeypatch ):
116+ """Test that 4xx client errors do NOT trigger retry."""
117+ # Set up Turn API env vars
118+ monkeypatch .setenv ("TURN_API_BASE_URL" , "https://api.turn.io" )
119+ monkeypatch .setenv ("TURN_API_TOKEN" , "test-token" )
120+
121+ # Mock Turn API client to raise client error (4xx)
122+ mock_turn_client = mocker .Mock ()
123+ mock_turn_client .update_message_label .side_effect = TurnAPIClientError (
124+ "Turn API client error (HTTP 400): Invalid label"
125+ )
126+ mocker .patch ("src.tasks.TurnAPIClient" , return_value = mock_turn_client )
127+
128+ # Execute task - should raise TurnAPIClientError without retry
129+ with pytest .raises (TurnAPIClientError , match = "Turn API client error" ):
130+ update_turn_message_label ({"message_id" : "msg-400" , "intent" : "greeting" })
131+
132+ # Verify Turn API was called only once (no retry)
133+ mock_turn_client .update_message_label .assert_called_once ()
134+
135+ def test_update_turn_label_server_error_retry (self , mocker , monkeypatch ):
136+ """Test that 5xx server errors trigger automatic retry."""
137+ # Set up Turn API env vars
138+ monkeypatch .setenv ("TURN_API_BASE_URL" , "https://api.turn.io" )
139+ monkeypatch .setenv ("TURN_API_TOKEN" , "test-token" )
140+
141+ # Mock Turn API client to raise server error (5xx)
142+ mock_turn_client = mocker .Mock ()
143+ mock_turn_client .update_message_label .side_effect = TurnAPIServerError (
144+ "Turn API server error (HTTP 500): Internal server error"
145+ )
146+ mocker .patch ("src.tasks.TurnAPIClient" , return_value = mock_turn_client )
147+
148+ # Execute task - should raise TurnAPIServerError (retry will be handled by Celery)
149+ with pytest .raises (TurnAPIServerError , match = "Turn API server error" ):
150+ update_turn_message_label ({"message_id" : "msg-500" , "intent" : "question" })
151+
152+ # Verify Turn API was called
153+ mock_turn_client .update_message_label .assert_called_once ()
154+
155+
156+ class TestClassifyAndUpdateChain :
157+ """Tests for the classify and update chain helper."""
158+
159+ def test_build_chain_signatures (self ):
160+ """Ensure chain is composed of classify then update tasks."""
161+ chain_sig = build_classify_and_update_chain ("msg-123" , "Hello there" )
162+
163+ assert len (chain_sig .tasks ) == 2
164+ assert chain_sig .tasks [0 ].name == classify_turn_message .name
165+ assert chain_sig .tasks [1 ].name == update_turn_message_label .name
0 commit comments