Spaces:
Sleeping
Sleeping
Update test_schema_tools.py
Browse filesAdd data recovery columns, batch error handling, human validation, and lead scrub/approve logic.
- test_schema_tools.py +58 -61
test_schema_tools.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
| 1 |
-
"""
|
| 2 |
-
|
| 3 |
-
Tests mapping, edge cases, error handling, and rollback/versioning with logs.
|
| 4 |
"""
|
| 5 |
import unittest
|
| 6 |
from schema_registry import SchemaRegistry
|
|
@@ -17,72 +16,70 @@ class TestSchemaTools(unittest.TestCase):
|
|
| 17 |
clean_files('schema_registry.json', 'schema_audit.log')
|
| 18 |
self.registry = SchemaRegistry('schema_registry.json', 'schema_audit.log')
|
| 19 |
self.agent = QuerySearchAgent(self.registry)
|
|
|
|
| 20 |
self.user_schema = {
|
| 21 |
'id': {'type': 'int', 'required': True},
|
| 22 |
'username': {'type': 'string', 'required': True},
|
| 23 |
'email': {'type': 'string', 'required': True},
|
| 24 |
-
'age': {'type': 'int', 'required': False}
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
'
|
| 29 |
-
'
|
| 30 |
}
|
| 31 |
-
self.registry.register_schema('
|
| 32 |
-
self.registry.register_mapping('user', 'short_user', {
|
| 33 |
-
'username': 'username',
|
| 34 |
-
'age': 'age'
|
| 35 |
-
})
|
| 36 |
-
|
| 37 |
-
def tearDown(self):
|
| 38 |
-
clean_files('schema_registry.json', 'schema_audit.log')
|
| 39 |
-
|
| 40 |
-
def test_schema_registration_and_mapping(self):
|
| 41 |
-
self.assertIn('user', self.registry.list_schemas())
|
| 42 |
-
self.assertIn('user->short_user', self.registry.list_mappings())
|
| 43 |
|
| 44 |
-
def
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
self.assertTrue(res)
|
| 48 |
-
self.assertEqual(errs, [])
|
| 49 |
-
missing_field = {'username': 'bob', 'email': 'bob@email.com'}
|
| 50 |
-
res, errs = self.registry.validate_data('user', missing_field)
|
| 51 |
-
self.assertFalse(res)
|
| 52 |
-
self.assertTrue(any('Required field' in err for err in errs))
|
| 53 |
-
|
| 54 |
-
def test_transform_data(self):
|
| 55 |
-
user = {'id': 1, 'username': 'alice', 'email': 'alice@email.com', 'age': 23}
|
| 56 |
-
transformed, errs = self.registry.transform_data(user, 'user', 'short_user')
|
| 57 |
-
self.assertIsNotNone(transformed)
|
| 58 |
-
self.assertEqual(set(transformed.keys()), set(['username', 'age']))
|
| 59 |
-
|
| 60 |
-
def test_search_and_query(self):
|
| 61 |
-
users = [
|
| 62 |
-
{'id': 1, 'username': 'alice', 'email': 'alice@email.com', 'age': 23},
|
| 63 |
-
{'id': 2, 'username': 'bob', 'email': 'bob@email.com', 'age': 28}
|
| 64 |
-
]
|
| 65 |
-
results, errors = self.agent.search('user', {'username': 'alice'}, users)
|
| 66 |
-
self.assertEqual(len(results), 1)
|
| 67 |
-
self.assertEqual(results[0]['username'], 'alice')
|
| 68 |
-
|
| 69 |
-
def test_advanced_query_and_errors(self):
|
| 70 |
-
users = [
|
| 71 |
{'id': 1, 'username': 'alice', 'email': 'alice@email.com', 'age': 23},
|
| 72 |
-
{'id': 2, 'username': 'bob', 'email': '
|
|
|
|
|
|
|
| 73 |
]
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
|
| 87 |
if __name__ == '__main__':
|
| 88 |
unittest.main()
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Test script for batch missing data recovery, validation, and lead status flows
|
|
|
|
| 3 |
"""
|
| 4 |
import unittest
|
| 5 |
from schema_registry import SchemaRegistry
|
|
|
|
| 16 |
clean_files('schema_registry.json', 'schema_audit.log')
|
| 17 |
self.registry = SchemaRegistry('schema_registry.json', 'schema_audit.log')
|
| 18 |
self.agent = QuerySearchAgent(self.registry)
|
| 19 |
+
# Add new columns for data recovery & validation
|
| 20 |
self.user_schema = {
|
| 21 |
'id': {'type': 'int', 'required': True},
|
| 22 |
'username': {'type': 'string', 'required': True},
|
| 23 |
'email': {'type': 'string', 'required': True},
|
| 24 |
+
'age': {'type': 'int', 'required': False},
|
| 25 |
+
'missing_data_flag': {'type': 'bool', 'required': False, 'default': False},
|
| 26 |
+
'data_recovery_tag': {'type': 'string', 'required': False, 'default': ''},
|
| 27 |
+
'recovery_attempted': {'type': 'bool', 'required': False, 'default': False},
|
| 28 |
+
'lead_status': {'type': 'string', 'required': False, 'default': 'active'},
|
| 29 |
+
'scrub_reason': {'type': 'string', 'required': False, 'default': ''},
|
| 30 |
}
|
| 31 |
+
self.registry.register_schema('user', self.user_schema, 'User schema with recovery fields')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
|
| 33 |
+
def test_batch_missing_data_and_recovery(self):
|
| 34 |
+
# Simulating a batch of leads, some missing required fields
|
| 35 |
+
leads = [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
{'id': 1, 'username': 'alice', 'email': 'alice@email.com', 'age': 23},
|
| 37 |
+
{'id': 2, 'username': 'bob', 'email': '', 'age': 31}, # Missing email
|
| 38 |
+
{'id': 3, 'username': '', 'email': 'sue@email.com', 'age': 22}, # Missing username
|
| 39 |
+
{'id': 4, 'username': 'ed', 'email': 'ed@email.com'}, # Partial
|
| 40 |
]
|
| 41 |
+
processed = []
|
| 42 |
+
recovery_queue = []
|
| 43 |
+
for lead in leads:
|
| 44 |
+
res, errs = self.registry.validate_data('user', lead)
|
| 45 |
+
if res:
|
| 46 |
+
lead.update({'missing_data_flag': False, 'data_recovery_tag': '', 'recovery_attempted': False, 'lead_status': 'active', 'scrub_reason': ''})
|
| 47 |
+
else:
|
| 48 |
+
lead['missing_data_flag'] = True
|
| 49 |
+
lead['data_recovery_tag'] = 'RECOVERY_TRIGGERED'
|
| 50 |
+
lead['recovery_attempted'] = False
|
| 51 |
+
lead['lead_status'] = 'pending_recovery'
|
| 52 |
+
lead['scrub_reason'] = ''
|
| 53 |
+
recovery_queue.append(lead)
|
| 54 |
+
processed.append(lead)
|
| 55 |
+
self.assertEqual(len(recovery_queue), 3)
|
| 56 |
+
# Batch: Mark recovery attempted & try recovery (simulate only 1 can be rescued)
|
| 57 |
+
for lead in recovery_queue:
|
| 58 |
+
lead['recovery_attempted'] = True
|
| 59 |
+
# Try recovery - for the test, rescue only id==2 (fill email)
|
| 60 |
+
if lead['id'] == 2:
|
| 61 |
+
lead['email'] = 'bob@email.com'
|
| 62 |
+
lead['missing_data_flag'] = False
|
| 63 |
+
lead['data_recovery_tag'] = ''
|
| 64 |
+
lead['lead_status'] = 'active'
|
| 65 |
+
else:
|
| 66 |
+
lead['missing_data_flag'] = True
|
| 67 |
+
lead['lead_status'] = 'needs_human_review'
|
| 68 |
+
recovery_for_human = [l for l in recovery_queue if l['lead_status'] == 'needs_human_review']
|
| 69 |
+
self.assertEqual(len(recovery_for_human), 2)
|
| 70 |
+
# Human reviews, decides to approve id==3 and scrub id==4
|
| 71 |
+
for l in recovery_for_human:
|
| 72 |
+
if l['id'] == 3:
|
| 73 |
+
l['lead_status'] = 'active' # Approved back
|
| 74 |
+
l['scrub_reason'] = ''
|
| 75 |
+
if l['id'] == 4:
|
| 76 |
+
l['lead_status'] = 'scrubbed'
|
| 77 |
+
l['scrub_reason'] = 'No username, unresolvable after recovery attempt.'
|
| 78 |
+
scrubbed_leads = [l for l in processed if l['lead_status'] == 'scrubbed']
|
| 79 |
+
self.assertTrue(any(l['id']==4 for l in scrubbed_leads))
|
| 80 |
+
# All others are either reactivated or pending normal
|
| 81 |
+
for l in processed:
|
| 82 |
+
self.assertIn(l['lead_status'], ['active','scrubbed','needs_human_review','pending_recovery'])
|
| 83 |
|
| 84 |
if __name__ == '__main__':
|
| 85 |
unittest.main()
|