| """
|
| Unit tests for PostgreSQL database module.
|
|
|
| Tests database connection, schema initialization, and CRUD operations.
|
|
|
| Task 6.2 Acceptance Criteria:
|
| - AC-2.3.3: PostgreSQL stores complete logs
|
| """
|
|
|
| import pytest
|
| import os
|
| from unittest.mock import patch, MagicMock
|
| from sqlalchemy.exc import SQLAlchemyError
|
|
|
| from app.database.postgres import (
|
| get_db_connection,
|
| init_database,
|
| verify_schema,
|
| get_db_session,
|
| init_engine,
|
| save_conversation,
|
| get_conversation,
|
| update_conversation,
|
| delete_conversation,
|
| save_messages,
|
| save_intelligence,
|
| get_conversations_by_date,
|
| get_scammer_profiles,
|
| get_conversation_stats,
|
| )
|
|
|
|
|
| class TestPostgresConnection:
|
| """Test PostgreSQL connection functionality."""
|
|
|
| def test_get_db_connection_no_config(self):
|
| """Test connection fails gracefully when POSTGRES_URL not set."""
|
| with patch('app.database.postgres.settings') as mock_settings:
|
| mock_settings.POSTGRES_URL = None
|
| with patch('app.database.postgres.engine', None):
|
| with pytest.raises(ConnectionError, match="not initialized"):
|
| get_db_connection()
|
|
|
| def test_init_engine_success(self):
|
| """Test engine initialization with valid URL."""
|
| test_url = "postgresql://user:pass@localhost:5432/testdb"
|
| with patch('app.database.postgres.settings') as mock_settings:
|
| mock_settings.POSTGRES_URL = test_url
|
| with patch('app.database.postgres.create_engine') as mock_create:
|
| mock_engine = MagicMock()
|
| mock_create.return_value = mock_engine
|
|
|
| init_engine()
|
|
|
| mock_create.assert_called_once()
|
| assert mock_create.call_args[0][0] == test_url
|
|
|
| def test_init_engine_no_url(self):
|
| """Test engine initialization fails gracefully without URL."""
|
|
|
| import app.database.postgres as postgres_module
|
| postgres_module.engine = None
|
| postgres_module.SessionLocal = None
|
|
|
| with patch('app.database.postgres.settings') as mock_settings:
|
| mock_settings.POSTGRES_URL = None
|
| with patch('app.database.postgres.logger') as mock_logger:
|
| init_engine()
|
| mock_logger.warning.assert_called()
|
|
|
| def test_get_db_session_context_manager(self):
|
| """Test database session context manager."""
|
| mock_session = MagicMock()
|
| mock_session_factory = MagicMock(return_value=mock_session)
|
|
|
| with patch('app.database.postgres.SessionLocal', mock_session_factory):
|
| with get_db_session() as session:
|
| assert session == mock_session
|
|
|
| mock_session.commit.assert_called_once()
|
| mock_session.close.assert_called_once()
|
|
|
| def test_get_db_session_rollback_on_error(self):
|
| """Test session rolls back on error."""
|
| mock_session = MagicMock()
|
| mock_session_factory = MagicMock(return_value=mock_session)
|
|
|
| with patch('app.database.postgres.SessionLocal', mock_session_factory):
|
| with pytest.raises(ValueError):
|
| with get_db_session():
|
| raise ValueError("Test error")
|
|
|
| mock_session.rollback.assert_called_once()
|
| mock_session.close.assert_called_once()
|
|
|
|
|
| class TestSchemaInitialization:
|
| """Test database schema initialization."""
|
|
|
| def test_init_database_creates_tables(self):
|
| """Test that init_database creates all required tables."""
|
| mock_engine = MagicMock()
|
| mock_conn = MagicMock()
|
| mock_engine.connect.return_value.__enter__ = MagicMock(return_value=mock_conn)
|
| mock_engine.connect.return_value.__exit__ = MagicMock(return_value=False)
|
|
|
| with patch('app.database.postgres.engine', mock_engine):
|
| with patch('app.database.postgres.text') as mock_text:
|
| init_database()
|
|
|
|
|
| assert mock_engine.connect.called
|
|
|
| assert mock_conn.commit.called
|
|
|
| def test_init_database_no_engine(self):
|
| """Test init_database fails gracefully without engine."""
|
| with patch('app.database.postgres.engine', None):
|
| with patch('app.database.postgres.init_engine') as mock_init:
|
| mock_init.side_effect = ConnectionError("No URL")
|
| with pytest.raises(ConnectionError):
|
| init_database()
|
|
|
| def test_verify_schema_checks_tables(self):
|
| """Test schema verification checks for required tables."""
|
| mock_inspector = MagicMock()
|
| mock_inspector.get_table_names.return_value = [
|
| 'conversations',
|
| 'messages',
|
| 'extracted_intelligence'
|
| ]
|
| mock_inspector.get_indexes.return_value = [
|
| {'name': 'idx_session_id'},
|
| {'name': 'idx_created_at'},
|
| {'name': 'idx_scam_detected'}
|
| ]
|
|
|
| mock_engine = MagicMock()
|
| with patch('app.database.postgres.engine', mock_engine):
|
| with patch('app.database.postgres.inspect', return_value=mock_inspector):
|
| result = verify_schema()
|
| assert result is True
|
|
|
| def test_verify_schema_missing_tables(self):
|
| """Test schema verification detects missing tables."""
|
| mock_inspector = MagicMock()
|
| mock_inspector.get_table_names.return_value = ['conversations']
|
| mock_inspector.get_indexes.return_value = []
|
|
|
| mock_engine = MagicMock()
|
| with patch('app.database.postgres.engine', mock_engine):
|
| with patch('app.database.postgres.inspect', return_value=mock_inspector):
|
| with patch('app.database.postgres.logger') as mock_logger:
|
| result = verify_schema()
|
| assert result is False
|
| mock_logger.warning.assert_called()
|
|
|
| def test_verify_schema_no_engine(self):
|
| """Test schema verification fails gracefully without engine."""
|
| with patch('app.database.postgres.engine', None):
|
| result = verify_schema()
|
| assert result is False
|
|
|
|
|
| class TestPostgresErrorHandling:
|
| """Test error handling in PostgreSQL operations."""
|
|
|
| def test_connection_error_handling(self):
|
| """Test connection errors are handled gracefully."""
|
| mock_engine = MagicMock()
|
| mock_engine.connect.side_effect = SQLAlchemyError("Connection failed")
|
|
|
| with patch('app.database.postgres.engine', mock_engine):
|
| with patch('app.database.postgres.logger') as mock_logger:
|
| with pytest.raises(ConnectionError):
|
| get_db_connection()
|
| mock_logger.error.assert_called()
|
|
|
| def test_schema_error_handling(self):
|
| """Test schema creation errors are handled gracefully."""
|
| mock_engine = MagicMock()
|
| mock_conn = MagicMock()
|
|
|
| mock_conn.execute.side_effect = SQLAlchemyError("Schema error - table locked")
|
| mock_conn.commit = MagicMock()
|
| mock_engine.connect.return_value.__enter__ = MagicMock(return_value=mock_conn)
|
| mock_engine.connect.return_value.__exit__ = MagicMock(return_value=False)
|
|
|
| with patch('app.database.postgres.engine', mock_engine):
|
| with patch('app.database.postgres.text') as mock_text:
|
| mock_text.return_value = MagicMock()
|
| with patch('app.database.postgres.logger') as mock_logger:
|
|
|
| init_database()
|
|
|
| mock_logger.warning.assert_called()
|
|
|
| assert mock_conn.execute.called
|
|
|
|
|
|
|
|
|
|
|
|
|
| class TestSaveConversation:
|
| """Tests for save_conversation function."""
|
|
|
| def test_save_conversation_no_engine(self):
|
| """Test save_conversation returns 0 when engine not initialized."""
|
| with patch('app.database.postgres.engine', None):
|
| with patch('app.database.postgres.init_engine') as mock_init:
|
|
|
| mock_init.return_value = None
|
|
|
| result = save_conversation("session-123", {"language": "en"})
|
|
|
| assert result == 0
|
|
|
| def test_save_conversation_new_session(self):
|
| """Test saving a new conversation."""
|
| mock_engine = MagicMock()
|
| mock_conn = MagicMock()
|
|
|
|
|
| mock_check_result = MagicMock()
|
| mock_check_result.fetchone.return_value = None
|
|
|
|
|
| mock_insert_result = MagicMock()
|
| mock_insert_result.fetchone.return_value = (42,)
|
|
|
| mock_conn.execute.side_effect = [mock_check_result, mock_insert_result]
|
| mock_conn.commit = MagicMock()
|
|
|
| mock_engine.connect.return_value.__enter__ = MagicMock(return_value=mock_conn)
|
| mock_engine.connect.return_value.__exit__ = MagicMock(return_value=False)
|
|
|
| with patch('app.database.postgres.engine', mock_engine):
|
| with patch('app.database.postgres.text') as mock_text:
|
| mock_text.return_value = MagicMock()
|
|
|
| result = save_conversation("session-123", {
|
| "language": "en",
|
| "persona": "elderly",
|
| "scam_confidence": 0.85,
|
| "turn_count": 5,
|
| })
|
|
|
| assert result == 42
|
|
|
| def test_save_conversation_update_existing(self):
|
| """Test updating an existing conversation."""
|
| mock_engine = MagicMock()
|
| mock_conn = MagicMock()
|
|
|
|
|
| mock_check_result = MagicMock()
|
| mock_check_result.fetchone.return_value = (10,)
|
|
|
|
|
| mock_update_result = MagicMock()
|
| mock_update_result.fetchone.return_value = (10,)
|
|
|
| mock_conn.execute.side_effect = [mock_check_result, mock_update_result]
|
| mock_conn.commit = MagicMock()
|
|
|
| mock_engine.connect.return_value.__enter__ = MagicMock(return_value=mock_conn)
|
| mock_engine.connect.return_value.__exit__ = MagicMock(return_value=False)
|
|
|
| with patch('app.database.postgres.engine', mock_engine):
|
| with patch('app.database.postgres.text') as mock_text:
|
| mock_text.return_value = MagicMock()
|
|
|
| result = save_conversation("session-123", {
|
| "language": "hi",
|
| "turn_count": 10,
|
| })
|
|
|
| assert result == 10
|
|
|
| def test_save_conversation_with_messages(self):
|
| """Test saving conversation with messages."""
|
| mock_engine = MagicMock()
|
| mock_conn = MagicMock()
|
|
|
| mock_check_result = MagicMock()
|
| mock_check_result.fetchone.return_value = None
|
|
|
| mock_insert_result = MagicMock()
|
| mock_insert_result.fetchone.return_value = (42,)
|
|
|
| mock_conn.execute.side_effect = [mock_check_result, mock_insert_result]
|
| mock_conn.commit = MagicMock()
|
|
|
| mock_engine.connect.return_value.__enter__ = MagicMock(return_value=mock_conn)
|
| mock_engine.connect.return_value.__exit__ = MagicMock(return_value=False)
|
|
|
| with patch('app.database.postgres.engine', mock_engine):
|
| with patch('app.database.postgres.text') as mock_text:
|
| mock_text.return_value = MagicMock()
|
| with patch('app.database.postgres.save_messages') as mock_save_msgs:
|
| mock_save_msgs.return_value = 2
|
|
|
| result = save_conversation("session-123", {
|
| "language": "en",
|
| "messages": [
|
| {"turn": 1, "sender": "scammer", "message": "Hello"},
|
| {"turn": 2, "sender": "agent", "message": "Hi"},
|
| ],
|
| })
|
|
|
| assert result == 42
|
| mock_save_msgs.assert_called_once()
|
|
|
| def test_save_conversation_sqlalchemy_error(self):
|
| """Test save_conversation handles SQLAlchemy errors."""
|
| mock_engine = MagicMock()
|
| mock_conn = MagicMock()
|
| mock_conn.execute.side_effect = SQLAlchemyError("DB error")
|
|
|
| mock_engine.connect.return_value.__enter__ = MagicMock(return_value=mock_conn)
|
| mock_engine.connect.return_value.__exit__ = MagicMock(return_value=False)
|
|
|
| with patch('app.database.postgres.engine', mock_engine):
|
| with patch('app.database.postgres.text'):
|
| with patch('app.database.postgres.logger') as mock_logger:
|
| result = save_conversation("session-123", {})
|
|
|
| assert result == 0
|
| mock_logger.error.assert_called()
|
|
|
|
|
| class TestGetConversation:
|
| """Tests for get_conversation function."""
|
|
|
| def test_get_conversation_no_engine(self):
|
| """Test get_conversation returns None when engine not initialized."""
|
| with patch('app.database.postgres.engine', None):
|
| with patch('app.database.postgres.init_engine'):
|
| result = get_conversation("session-123")
|
| assert result is None
|
|
|
| def test_get_conversation_not_found(self):
|
| """Test get_conversation returns None for non-existent session."""
|
| mock_engine = MagicMock()
|
| mock_conn = MagicMock()
|
|
|
| mock_result = MagicMock()
|
| mock_result.fetchone.return_value = None
|
| mock_conn.execute.return_value = mock_result
|
|
|
| mock_engine.connect.return_value.__enter__ = MagicMock(return_value=mock_conn)
|
| mock_engine.connect.return_value.__exit__ = MagicMock(return_value=False)
|
|
|
| with patch('app.database.postgres.engine', mock_engine):
|
| with patch('app.database.postgres.text'):
|
| result = get_conversation("non-existent")
|
| assert result is None
|
|
|
| def test_get_conversation_success(self):
|
| """Test get_conversation returns full data."""
|
| from datetime import datetime
|
| mock_engine = MagicMock()
|
| mock_conn = MagicMock()
|
|
|
| now = datetime.utcnow()
|
|
|
|
|
| conv_result = MagicMock()
|
| conv_result.fetchone.return_value = (
|
| 1, "session-123", "en", "elderly", True, 0.85, 5, now, now
|
| )
|
|
|
|
|
| msg_result = MagicMock()
|
| msg_result.fetchall.return_value = [
|
| (1, "scammer", "Hello", now),
|
| (2, "agent", "Hi", now),
|
| ]
|
|
|
|
|
| intel_result = MagicMock()
|
| intel_result.fetchone.return_value = (
|
| ["test@upi"], ["1234567890"], ["IFSC123"],
|
| ["9876543210"], ["http://scam.com"], 0.9
|
| )
|
|
|
| mock_conn.execute.side_effect = [conv_result, msg_result, intel_result]
|
|
|
| mock_engine.connect.return_value.__enter__ = MagicMock(return_value=mock_conn)
|
| mock_engine.connect.return_value.__exit__ = MagicMock(return_value=False)
|
|
|
| with patch('app.database.postgres.engine', mock_engine):
|
| with patch('app.database.postgres.text'):
|
| result = get_conversation("session-123")
|
|
|
| assert result is not None
|
| assert result["session_id"] == "session-123"
|
| assert result["language"] == "en"
|
| assert result["persona"] == "elderly"
|
| assert result["scam_detected"] is True
|
| assert len(result["messages"]) == 2
|
| assert result["extracted_intel"]["upi_ids"] == ["test@upi"]
|
|
|
|
|
| class TestUpdateConversation:
|
| """Tests for update_conversation function."""
|
|
|
| def test_update_conversation_no_engine(self):
|
| """Test update_conversation returns False when engine not initialized."""
|
| with patch('app.database.postgres.engine', None):
|
| with patch('app.database.postgres.init_engine'):
|
| result = update_conversation("session-123", {"turn_count": 10})
|
| assert result is False
|
|
|
| def test_update_conversation_empty_updates(self):
|
| """Test update_conversation with empty updates returns True."""
|
| with patch('app.database.postgres.engine', MagicMock()):
|
| result = update_conversation("session-123", {})
|
| assert result is True
|
|
|
| def test_update_conversation_invalid_fields(self):
|
| """Test update_conversation ignores invalid fields."""
|
| with patch('app.database.postgres.engine', MagicMock()):
|
| with patch('app.database.postgres.logger') as mock_logger:
|
| result = update_conversation("session-123", {"invalid_field": "value"})
|
| assert result is False
|
| mock_logger.warning.assert_called()
|
|
|
| def test_update_conversation_success(self):
|
| """Test successful conversation update."""
|
| mock_engine = MagicMock()
|
| mock_conn = MagicMock()
|
|
|
| mock_result = MagicMock()
|
| mock_result.rowcount = 1
|
| mock_conn.execute.return_value = mock_result
|
| mock_conn.commit = MagicMock()
|
|
|
| mock_engine.connect.return_value.__enter__ = MagicMock(return_value=mock_conn)
|
| mock_engine.connect.return_value.__exit__ = MagicMock(return_value=False)
|
|
|
| with patch('app.database.postgres.engine', mock_engine):
|
| with patch('app.database.postgres.text'):
|
| result = update_conversation("session-123", {"turn_count": 15})
|
| assert result is True
|
|
|
|
|
| class TestSaveMessages:
|
| """Tests for save_messages function."""
|
|
|
| def test_save_messages_no_engine(self):
|
| """Test save_messages returns 0 when engine not initialized."""
|
| with patch('app.database.postgres.engine', None):
|
| with patch('app.database.postgres.init_engine'):
|
| result = save_messages(1, [{"turn": 1, "sender": "agent", "message": "Hi"}])
|
| assert result == 0
|
|
|
| def test_save_messages_empty_list(self):
|
| """Test save_messages with empty list returns 0."""
|
| with patch('app.database.postgres.engine', MagicMock()):
|
| result = save_messages(1, [])
|
| assert result == 0
|
|
|
| def test_save_messages_skips_duplicates(self):
|
| """Test save_messages skips existing turns."""
|
| mock_engine = MagicMock()
|
| mock_conn = MagicMock()
|
|
|
|
|
| existing_result = MagicMock()
|
| existing_result.fetchall.return_value = [(1,), (2,)]
|
|
|
| mock_conn.execute.return_value = existing_result
|
| mock_conn.commit = MagicMock()
|
|
|
| mock_engine.connect.return_value.__enter__ = MagicMock(return_value=mock_conn)
|
| mock_engine.connect.return_value.__exit__ = MagicMock(return_value=False)
|
|
|
| with patch('app.database.postgres.engine', mock_engine):
|
| with patch('app.database.postgres.text'):
|
| result = save_messages(1, [
|
| {"turn": 1, "sender": "scammer", "message": "Hi"},
|
| {"turn": 3, "sender": "agent", "message": "Hello"},
|
| ])
|
|
|
|
|
| assert result == 1
|
|
|
|
|
| class TestSaveIntelligence:
|
| """Tests for save_intelligence function."""
|
|
|
| def test_save_intelligence_no_engine(self):
|
| """Test save_intelligence returns 0 when engine not initialized."""
|
| with patch('app.database.postgres.engine', None):
|
| with patch('app.database.postgres.init_engine'):
|
| result = save_intelligence(1, {"upi_ids": ["test@upi"]}, 0.9)
|
| assert result == 0
|
|
|
| def test_save_intelligence_success(self):
|
| """Test successful intelligence saving."""
|
| mock_engine = MagicMock()
|
| mock_conn = MagicMock()
|
|
|
| mock_result = MagicMock()
|
| mock_result.fetchone.return_value = (99,)
|
| mock_conn.execute.return_value = mock_result
|
| mock_conn.commit = MagicMock()
|
|
|
| mock_engine.connect.return_value.__enter__ = MagicMock(return_value=mock_conn)
|
| mock_engine.connect.return_value.__exit__ = MagicMock(return_value=False)
|
|
|
| with patch('app.database.postgres.engine', mock_engine):
|
| with patch('app.database.postgres.text'):
|
| result = save_intelligence(1, {
|
| "upi_ids": ["test@upi"],
|
| "phone_numbers": ["9876543210"],
|
| }, 0.85)
|
|
|
| assert result == 99
|
|
|
|
|
| class TestDeleteConversation:
|
| """Tests for delete_conversation function."""
|
|
|
| def test_delete_conversation_no_engine(self):
|
| """Test delete_conversation returns False when engine not initialized."""
|
| with patch('app.database.postgres.engine', None):
|
| with patch('app.database.postgres.init_engine'):
|
| result = delete_conversation("session-123")
|
| assert result is False
|
|
|
| def test_delete_conversation_success(self):
|
| """Test successful conversation deletion."""
|
| mock_engine = MagicMock()
|
| mock_conn = MagicMock()
|
|
|
| mock_result = MagicMock()
|
| mock_result.rowcount = 1
|
| mock_conn.execute.return_value = mock_result
|
| mock_conn.commit = MagicMock()
|
|
|
| mock_engine.connect.return_value.__enter__ = MagicMock(return_value=mock_conn)
|
| mock_engine.connect.return_value.__exit__ = MagicMock(return_value=False)
|
|
|
| with patch('app.database.postgres.engine', mock_engine):
|
| with patch('app.database.postgres.text'):
|
| result = delete_conversation("session-123")
|
| assert result is True
|
|
|
| def test_delete_conversation_not_found(self):
|
| """Test delete_conversation returns False for non-existent session."""
|
| mock_engine = MagicMock()
|
| mock_conn = MagicMock()
|
|
|
| mock_result = MagicMock()
|
| mock_result.rowcount = 0
|
| mock_conn.execute.return_value = mock_result
|
| mock_conn.commit = MagicMock()
|
|
|
| mock_engine.connect.return_value.__enter__ = MagicMock(return_value=mock_conn)
|
| mock_engine.connect.return_value.__exit__ = MagicMock(return_value=False)
|
|
|
| with patch('app.database.postgres.engine', mock_engine):
|
| with patch('app.database.postgres.text'):
|
| result = delete_conversation("non-existent")
|
| assert result is False
|
|
|
|
|
| class TestGetConversationsByDate:
|
| """Tests for get_conversations_by_date function."""
|
|
|
| def test_get_conversations_by_date_no_engine(self):
|
| """Test returns empty list when engine not initialized."""
|
| with patch('app.database.postgres.engine', None):
|
| with patch('app.database.postgres.init_engine'):
|
| result = get_conversations_by_date("2024-01-01", "2024-01-31")
|
| assert result == []
|
|
|
| def test_get_conversations_by_date_success(self):
|
| """Test successful date-based query."""
|
| from datetime import datetime
|
| mock_engine = MagicMock()
|
| mock_conn = MagicMock()
|
|
|
| now = datetime.utcnow()
|
| mock_result = MagicMock()
|
| mock_result.fetchall.return_value = [
|
| (1, "session-1", "en", "elderly", True, 0.9, 10, now, now),
|
| (2, "session-2", "hi", "eager", False, 0.3, 5, now, now),
|
| ]
|
| mock_conn.execute.return_value = mock_result
|
|
|
| mock_engine.connect.return_value.__enter__ = MagicMock(return_value=mock_conn)
|
| mock_engine.connect.return_value.__exit__ = MagicMock(return_value=False)
|
|
|
| with patch('app.database.postgres.engine', mock_engine):
|
| with patch('app.database.postgres.text'):
|
| result = get_conversations_by_date("2024-01-01", "2024-01-31")
|
|
|
| assert len(result) == 2
|
| assert result[0]["session_id"] == "session-1"
|
| assert result[1]["language"] == "hi"
|
|
|
|
|
| class TestGetConversationStats:
|
| """Tests for get_conversation_stats function."""
|
|
|
| def test_get_conversation_stats_no_engine(self):
|
| """Test returns error when engine not initialized."""
|
| with patch('app.database.postgres.engine', None):
|
| with patch('app.database.postgres.init_engine'):
|
| result = get_conversation_stats()
|
| assert "error" in result
|
|
|
| def test_get_conversation_stats_success(self):
|
| """Test successful stats retrieval."""
|
| mock_engine = MagicMock()
|
| mock_conn = MagicMock()
|
|
|
| mock_result = MagicMock()
|
| mock_result.fetchone.return_value = (100, 75, 0.85, 8.5, 3)
|
| mock_conn.execute.return_value = mock_result
|
|
|
| mock_engine.connect.return_value.__enter__ = MagicMock(return_value=mock_conn)
|
| mock_engine.connect.return_value.__exit__ = MagicMock(return_value=False)
|
|
|
| with patch('app.database.postgres.engine', mock_engine):
|
| with patch('app.database.postgres.text'):
|
| result = get_conversation_stats()
|
|
|
| assert result["total_conversations"] == 100
|
| assert result["scam_count"] == 75
|
| assert result["avg_confidence"] == 0.85
|
|
|
|
|
| class TestAcceptanceCriteria:
|
| """Tests for Task 6.2 Acceptance Criteria."""
|
|
|
| def test_ac_2_3_3_complete_logs_stored(self):
|
| """AC-2.3.3: PostgreSQL stores complete logs."""
|
|
|
| conversation_data = {
|
| "language": "en",
|
| "persona": "elderly",
|
| "scam_confidence": 0.85,
|
| "turn_count": 10,
|
| "messages": [
|
| {"turn": 1, "sender": "scammer", "message": "Hello"},
|
| {"turn": 2, "sender": "agent", "message": "Hi"},
|
| ],
|
| "extracted_intel": {
|
| "upi_ids": ["test@upi"],
|
| "bank_accounts": ["1234567890"],
|
| "ifsc_codes": ["IFSC123"],
|
| "phone_numbers": ["9876543210"],
|
| "phishing_links": ["http://scam.com"],
|
| },
|
| "extraction_confidence": 0.9,
|
| }
|
|
|
|
|
| assert "language" in conversation_data
|
| assert "persona" in conversation_data
|
| assert "scam_confidence" in conversation_data
|
| assert "turn_count" in conversation_data
|
| assert "messages" in conversation_data
|
| assert "extracted_intel" in conversation_data
|
|
|
|
|
| intel = conversation_data["extracted_intel"]
|
| assert "upi_ids" in intel
|
| assert "bank_accounts" in intel
|
| assert "ifsc_codes" in intel
|
| assert "phone_numbers" in intel
|
| assert "phishing_links" in intel
|
|
|