Fix integration test failures

- Fix test_activity_processing.py: Import activity module and use activity.* functions
- Fix test_app_activity_functions.py: Import activity module, use activity.* functions, initialize activity module with app's socketio and db
- Fix test_activity_integration.py: Update YAML format to match current specification
  - Change buckets from objects to simple string lists
  - Use next_section_and_step instead of separate next_section_id/next_step_id
  - Add required title fields and tokens_for_ai
  - Replace type field with content_blocks for info steps
This commit is contained in:
Russell Ballestrini 2025-11-10 16:06:59 -05:00
parent 9003240762
commit f9ddc4ec03
3 changed files with 66 additions and 48 deletions

View file

@ -116,37 +116,42 @@ class TestActivityIntegration(unittest.TestCase):
# Create minimal activity content
activity_content = """
default_max_attempts_per_step: 3
tokens_for_ai_rubric: "Test rubric"
sections:
- section_id: "section_1"
title: "Test Section"
steps:
- step_id: "step_1"
type: "question"
title: "Question 1"
question: "What is 2+2?"
tokens_for_ai: "Categorize as 'correct' if answer is 4 or four, otherwise 'incorrect'"
buckets:
- bucket_name: "correct"
bucket_criteria: "Answer is 4"
- bucket_name: "incorrect"
bucket_criteria: "Wrong answer"
- correct
- incorrect
transitions:
correct:
ai_feedback:
tokens_for_ai: "Provide encouragement"
next_section_id: "section_1"
next_step_id: "step_2"
content_blocks:
- "Great job!"
next_section_and_step: "section_1:step_2"
incorrect:
ai_feedback:
tokens_for_ai: "Try again"
next_section_id: "section_1"
next_step_id: "step_1"
content_blocks:
- "Try again!"
counts_as_attempt: true
next_section_and_step: "section_1:step_1"
- step_id: "step_2"
type: "question"
title: "Question 2"
question: "What is 3+3?"
tokens_for_ai: "Categorize as 'correct' if answer is 6 or six, otherwise 'incorrect'"
buckets:
- bucket_name: "correct"
bucket_criteria: "Answer is 6"
- correct
- incorrect
transitions:
correct:
content_blocks:
- "Excellent!"
incorrect:
content_blocks:
- "Not quite!"
"""
# Write to research directory
with tempfile.NamedTemporaryFile(
@ -364,24 +369,32 @@ script_result = metadata['counter']
from models import ActivityState
import activity
# Create activity with multiple info steps before question
# Create activity with multiple content-only steps before question
activity_content = """
default_max_attempts_per_step: 3
sections:
- section_id: "intro"
title: "Introduction"
steps:
- step_id: "info_1"
type: "info"
display_text: "Welcome!"
title: "Welcome"
content_blocks:
- "Welcome!"
- step_id: "info_2"
type: "info"
display_text: "Let's begin"
title: "Let's Begin"
content_blocks:
- "Let's begin"
- step_id: "question_1"
type: "question"
title: "Question"
question: "Ready?"
tokens_for_ai: "Categorize as 'yes' for any response"
buckets:
- bucket_name: "yes"
- yes
transitions:
yes:
content_blocks:
- "Great!"
"""
with tempfile.NamedTemporaryFile(
mode="w", suffix=".yaml", dir="research", delete=False

View file

@ -31,6 +31,7 @@ with patch.dict(
},
):
import app
import activity
class MockActivityState:
@ -178,7 +179,7 @@ script_result = {
# Execute the processing script
if transition.get("run_processing_script", False):
result = app.execute_processing_script(
result = activity.execute_processing_script(
activity_state.dict_metadata, script_step["processing_script"]
)
@ -236,7 +237,7 @@ script_result = {
temp_metadata["user_response"] = user_response
# Execute pre-script
pre_result = app.execute_processing_script(
pre_result = activity.execute_processing_script(
temp_metadata, pre_script_step["pre_script"]
)
@ -334,7 +335,7 @@ script_result = {
navigation_path = []
for _ in range(10): # Prevent infinite loop
next_section, next_step = app.get_next_step(
next_section, next_step = activity.get_next_step(
multi_section_activity, current_section, current_step
)
@ -371,9 +372,9 @@ script_result = {
)
with patch.object(
app, "provide_feedback", return_value=mock_feedback
activity, "provide_feedback", return_value=mock_feedback
) as mock_func:
result = app.provide_feedback(
result = activity.provide_feedback(
transition_with_feedback,
"correct",
"What is 2+2?",
@ -403,7 +404,7 @@ if True
# Should handle syntax errors gracefully
with self.assertRaises(SyntaxError):
app.execute_processing_script(metadata, invalid_script)
activity.execute_processing_script(metadata, invalid_script)
def test_processing_script_runtime_error(self):
"""Test handling of runtime errors in processing scripts"""
@ -416,15 +417,15 @@ script_result = {'status': 'error'}
# Should handle runtime errors gracefully
with self.assertRaises(ZeroDivisionError):
app.execute_processing_script(metadata, runtime_error_script)
activity.execute_processing_script(metadata, runtime_error_script)
def test_missing_activity_content(self):
"""Test handling of missing activity content"""
with patch.object(app, "get_activity_content") as mock_get_content:
with patch.object(activity, "get_activity_content") as mock_get_content:
mock_get_content.side_effect = FileNotFoundError("Activity file not found")
with self.assertRaises(FileNotFoundError):
app.get_activity_content("nonexistent_activity.yaml")
activity.get_activity_content("nonexistent_activity.yaml")
mock_get_content.assert_called_once_with("nonexistent_activity.yaml")
@ -448,7 +449,7 @@ script_result = {'status': 'error'}
f.write(malformed_yaml)
with self.assertRaises(yaml.YAMLError): # YAML parsing error
app.get_activity_content("research/malformed.yaml")
activity.get_activity_content("research/malformed.yaml")
finally:
os.unlink(temp_file)

View file

@ -23,6 +23,7 @@ from flask_socketio import SocketIO
# Import the main application
import app
import activity
from models import db, Room, ActivityState, Message
@ -60,6 +61,9 @@ class TestFlaskAppActivityFunctions(unittest.TestCase):
# Store original socketio for cleanup
self.original_socketio = app.socketio
# Initialize activity module with app's socketio and db
activity.init_activity_module(app.socketio, db)
def tearDown(self):
"""Clean up test environment"""
db.session.remove()
@ -116,7 +120,7 @@ sections:
try:
# Test the actual get_activity_content function
result = app.get_activity_content(f"research/{activity_file}")
result = activity.get_activity_content(f"research/{activity_file}")
# Verify structure
self.assertEqual(result["title"], "Test Activity")
@ -169,7 +173,7 @@ sections:
)()
# Test start_activity function
app.start_activity("test_room", f"research/{activity_file}", "testuser")
activity.start_activity("test_room", f"research/{activity_file}", "testuser")
# Verify activity state was created in database
activity_state = ActivityState.query.filter_by(
@ -244,7 +248,7 @@ sections:
db.session.commit()
# Test handling a correct response
app.handle_activity_response("test_room", "10", "testuser")
activity.handle_activity_response("test_room", "10", "testuser")
# Refresh activity state from database
db.session.refresh(activity_state)
@ -305,7 +309,7 @@ sections:
db.session.commit()
# Test display_activity_metadata function
app.display_activity_metadata("test_room", "testuser")
activity.display_activity_metadata("test_room", "testuser")
# Verify that a message was emitted
self.assertTrue(len(emitted_messages) > 0)
@ -367,7 +371,7 @@ sections:
)
# Test cancel_activity function
app.cancel_activity("test_room", "testuser")
activity.cancel_activity("test_room", "testuser")
# Verify activity was deleted from database
self.assertIsNone(
@ -421,7 +425,7 @@ script_result = {
}
# Test the actual execute_processing_script function
result = app.execute_processing_script(metadata, script)
result = activity.execute_processing_script(metadata, script)
# Verify script execution results
self.assertEqual(result["status"], "success")
@ -459,21 +463,21 @@ script_result = {
}
# Test navigation within section
next_section, next_step = app.get_next_step(
next_section, next_step = activity.get_next_step(
activity_content, "section_1", "step_1"
)
self.assertEqual(next_section["section_id"], "section_1")
self.assertEqual(next_step["step_id"], "step_2")
# Test navigation across sections
next_section, next_step = app.get_next_step(
next_section, next_step = activity.get_next_step(
activity_content, "section_1", "step_3"
)
self.assertEqual(next_section["section_id"], "section_2")
self.assertEqual(next_step["step_id"], "step_1")
# Test at end of activity
next_section, next_step = app.get_next_step(
next_section, next_step = activity.get_next_step(
activity_content, "section_2", "step_2"
)
self.assertIsNone(next_section)
@ -490,7 +494,7 @@ script_result = {
)
# Test the actual categorization function
result = app.categorize_response(question, response, buckets, tokens_for_ai)
result = activity.categorize_response(question, response, buckets, tokens_for_ai)
# Result should be either "correct", "incorrect", or an error message
self.assertIsInstance(result, str)
@ -502,19 +506,19 @@ script_result = {
"""Test text translation functionality"""
# Test English bypass
english_text = "Hello, world!"
result = app.translate_text(english_text, "English")
result = activity.translate_text(english_text, "English")
self.assertEqual(result, english_text)
# Test case insensitive
result = app.translate_text(english_text, "english")
result = activity.translate_text(english_text, "english")
self.assertEqual(result, english_text)
# Test with compound language
result = app.translate_text(english_text, "English please")
result = activity.translate_text(english_text, "English please")
self.assertEqual(result, english_text)
# Test other language (will use AI endpoint if available)
result = app.translate_text("Hello", "Spanish")
result = activity.translate_text("Hello", "Spanish")
self.assertIsInstance(result, str) # Should return some string result