tgdata 0.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,253 @@
1
+ """
2
+ Smoke tests for advanced TgData features
3
+ """
4
+ # To run: python -m tgdata.smoke_tests.test_04_advanced_features
5
+
6
+ import asyncio
7
+ import sys
8
+ import os
9
+ import tempfile
10
+ from datetime import datetime, timedelta
11
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
12
+
13
+ from tgdata import TgData
14
+ import pandas as pd
15
+
16
+
17
+
18
+
19
+ async def test_connection_pooling():
20
+ """Test connection pooling features"""
21
+ print("\nTEST: Connection pooling...")
22
+ try:
23
+ # Test pool configuration
24
+ tg = TgData(connection_pool_size=5)
25
+ assert tg.connection_engine.pool_size == 5
26
+ print("✓ Connection pool size configured")
27
+
28
+ # Test health check structure
29
+ health = await tg.health_check()
30
+ assert isinstance(health, dict)
31
+ assert 'timestamp' in health
32
+ assert 'pool_connections' in health
33
+ print("✓ Health check includes pool information")
34
+
35
+ return True
36
+ except Exception as e:
37
+ print(f"✗ Connection pooling test failed: {e}")
38
+ return False
39
+
40
+
41
+ async def test_progress_tracking():
42
+ """Test progress tracking functionality"""
43
+ print("\nTEST: Progress tracking...")
44
+ try:
45
+ tg = TgData()
46
+
47
+ # Test that get_messages supports progress tracking
48
+ assert hasattr(tg, 'get_messages')
49
+
50
+ # Test progress callback structure
51
+ progress_data = []
52
+
53
+ def test_callback(current, total, rate):
54
+ progress_data.append({
55
+ 'current': current,
56
+ 'total': total,
57
+ 'rate': rate
58
+ })
59
+
60
+ # We can't actually fetch messages without auth, but we can test the parameter acceptance
61
+ try:
62
+ await tg.get_messages(
63
+ group_id=12345,
64
+ limit=10,
65
+ with_progress=True,
66
+ progress_callback=test_callback
67
+ )
68
+ except:
69
+ # Expected to fail without auth
70
+ pass
71
+
72
+ print("✓ Progress tracking parameters accepted")
73
+
74
+ return True
75
+ except Exception as e:
76
+ print(f"✗ Progress tracking test failed: {e}")
77
+ return False
78
+
79
+
80
+ async def test_date_filtering():
81
+ """Test date-based message filtering"""
82
+ print("\nTEST: Date filtering...")
83
+ try:
84
+ tg = TgData()
85
+
86
+ # Test filter_messages with date parameters
87
+ df = pd.DataFrame({
88
+ 'MessageId': [1, 2, 3, 4, 5],
89
+ 'Date': [
90
+ datetime.now() - timedelta(days=5),
91
+ datetime.now() - timedelta(days=3),
92
+ datetime.now() - timedelta(days=1),
93
+ datetime.now(),
94
+ datetime.now() + timedelta(days=1)
95
+ ],
96
+ 'Message': ['Old', 'Mid', 'Recent', 'Today', 'Future']
97
+ })
98
+
99
+ # Filter last 2 days
100
+ filtered = tg.filter_messages(
101
+ df,
102
+ start_date=datetime.now() - timedelta(days=2)
103
+ )
104
+ assert len(filtered) == 3 # Recent, Today, Future
105
+ print("✓ Start date filtering works")
106
+
107
+ # Filter with end date
108
+ filtered = tg.filter_messages(
109
+ df,
110
+ end_date=datetime.now()
111
+ )
112
+ assert len(filtered) == 4 # Excludes Future
113
+ print("✓ End date filtering works")
114
+
115
+ # Filter date range
116
+ filtered = tg.filter_messages(
117
+ df,
118
+ start_date=datetime.now() - timedelta(days=4),
119
+ end_date=datetime.now() - timedelta(days=2)
120
+ )
121
+ assert len(filtered) == 1 # Only Mid
122
+ print("✓ Date range filtering works")
123
+
124
+ return True
125
+ except Exception as e:
126
+ print(f"✗ Date filtering test failed: {e}")
127
+ return False
128
+
129
+
130
+ async def test_message_caching():
131
+ """Test message caching behavior"""
132
+ print("\nTEST: Message caching...")
133
+ try:
134
+ tg = TgData()
135
+
136
+ # Test cache clearing on group change
137
+ tg.set_group(12345)
138
+ # Add some fake cache data with proper key format
139
+ cache_key = '12345_test_key'
140
+ tg.message_engine._message_cache[cache_key] = pd.DataFrame([{'test': 'data'}])
141
+ assert cache_key in tg.message_engine._message_cache
142
+
143
+ # Change group should clear cache for previous group
144
+ tg.set_group(54321)
145
+ # Check that the cache for the old group is gone
146
+ assert cache_key not in tg.message_engine._message_cache
147
+ print("✓ Cache cleared on group change")
148
+
149
+ return True
150
+ except Exception as e:
151
+ print(f"✗ Message caching test failed: {e}")
152
+ import traceback
153
+ traceback.print_exc()
154
+ return False
155
+
156
+
157
+ async def test_metrics_and_logging():
158
+ """Test metrics collection and logging"""
159
+ print("\nTEST: Metrics and logging...")
160
+ try:
161
+ # Test with log file
162
+ with tempfile.NamedTemporaryFile(suffix='.log', delete=False) as tmp_log:
163
+ tg = TgData(log_file=tmp_log.name)
164
+
165
+ # Perform some operations to generate logs
166
+ tg.set_group(12345)
167
+
168
+ # Check log file exists
169
+ assert os.path.exists(tmp_log.name)
170
+ print("✓ Log file created")
171
+
172
+ # Test metrics
173
+ metrics = await tg.get_metrics()
174
+ assert 'current_group' in metrics
175
+ assert metrics['current_group'] == 12345
176
+ assert 'message_cache_size' in metrics
177
+ print("✓ Metrics include cache and group info")
178
+
179
+ os.unlink(tmp_log.name)
180
+
181
+ return True
182
+ except Exception as e:
183
+ print(f"✗ Metrics and logging test failed: {e}")
184
+ return False
185
+
186
+
187
+ async def test_validation_methods():
188
+ """Test connection validation methods"""
189
+ print("\nTEST: Validation methods...")
190
+ try:
191
+ tg = TgData()
192
+
193
+ # Test validate_connection
194
+ is_valid = await tg.validate_connection()
195
+ assert isinstance(is_valid, bool)
196
+ print(f"✓ Connection validation returns: {is_valid}")
197
+
198
+ # Test health check
199
+ health = await tg.health_check()
200
+ assert isinstance(health, dict)
201
+ assert 'primary_connection' in health
202
+ assert 'errors' in health
203
+ print("✓ Health check returns detailed status")
204
+
205
+ return True
206
+ except Exception as e:
207
+ print(f"✗ Validation methods test failed: {e}")
208
+ return False
209
+
210
+
211
+ async def main():
212
+ """Run all advanced feature tests"""
213
+ print("Advanced TgData Features Tests")
214
+ print("=" * 50)
215
+
216
+ tests = [
217
+ test_connection_pooling,
218
+ test_progress_tracking,
219
+ test_date_filtering,
220
+ test_message_caching,
221
+ test_metrics_and_logging,
222
+ test_validation_methods
223
+ ]
224
+
225
+ results = []
226
+ for test in tests:
227
+ try:
228
+ result = await test()
229
+ results.append(result)
230
+ except Exception as e:
231
+ print(f"✗ Test failed with error: {e}")
232
+ import traceback
233
+ traceback.print_exc()
234
+ results.append(False)
235
+
236
+ # Summary
237
+ print("\nSummary")
238
+ print("=" * 50)
239
+ passed = sum(results)
240
+ total = len(results)
241
+ print(f"Passed: {passed}/{total}")
242
+
243
+ if passed == total:
244
+ print("✓ All advanced feature tests passed!")
245
+ return 0
246
+ else:
247
+ print("✗ Some tests failed")
248
+ return 1
249
+
250
+
251
+ if __name__ == "__main__":
252
+ exit_code = asyncio.run(main())
253
+ sys.exit(exit_code)
@@ -0,0 +1,244 @@
1
+ """
2
+ Smoke tests for polling and real-time message features
3
+ """
4
+ # To run: python -m tgdata.smoke_tests.test_10_polling
5
+
6
+ import asyncio
7
+ import sys
8
+ import os
9
+ from datetime import datetime
10
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
11
+
12
+ from tgdata import TgData
13
+ import pandas as pd
14
+
15
+
16
+ async def test_polling_basic():
17
+ """Test basic polling functionality"""
18
+ print("TEST: Basic polling functionality...")
19
+
20
+ try:
21
+ # Initialize TgData
22
+ tg = TgData("config.ini")
23
+
24
+ # Get a test group
25
+ groups = await tg.list_groups()
26
+ if groups.empty:
27
+ print("✗ No groups available for testing")
28
+ return False
29
+
30
+ test_group = groups.iloc[0]
31
+ test_group_id = int(test_group['GroupID'])
32
+ print(f"Testing with group: {test_group['Title']} (ID: {test_group_id})")
33
+
34
+ # Define a callback to handle new messages
35
+ messages_received = []
36
+
37
+ async def message_callback(messages_df):
38
+ count = len(messages_df)
39
+ messages_received.append(count)
40
+ print(f" Received {count} new messages")
41
+ if not messages_df.empty:
42
+ # Show first message
43
+ first_msg = messages_df.iloc[0]
44
+ msg_text = first_msg.get('Message', 'No text')[:50]
45
+ print(f" First message: {msg_text}...")
46
+
47
+ # Get the latest message ID to start polling after
48
+ initial_messages = await tg.get_messages(group_id=test_group_id, limit=1)
49
+ start_after_id = 0
50
+ if not initial_messages.empty:
51
+ start_after_id = initial_messages['MessageId'].max()
52
+ print(f"Starting polling after message ID: {start_after_id}")
53
+
54
+ # Test polling with 3 iterations
55
+ print("\nPolling for 3 iterations (5 seconds each)...")
56
+ await tg.poll_for_messages(
57
+ group_id=test_group_id,
58
+ interval=5,
59
+ after_id=start_after_id,
60
+ callback=message_callback,
61
+ max_iterations=3
62
+ )
63
+
64
+ print(f"✓ Polling completed. Total polls that found messages: {len(messages_received)}")
65
+ await tg.close()
66
+ return True
67
+
68
+ except Exception as e:
69
+ print(f"✗ Polling test failed: {e}")
70
+ import traceback
71
+ traceback.print_exc()
72
+ return False
73
+
74
+
75
+ async def test_event_handler():
76
+ """Test real-time event handler"""
77
+ print("\nTEST: Real-time event handler...")
78
+
79
+ try:
80
+ # Initialize TgData
81
+ tg = TgData("config.ini")
82
+
83
+ # Get a test group
84
+ groups = await tg.list_groups()
85
+ if groups.empty:
86
+ print("✗ No groups available for testing")
87
+ return False
88
+
89
+ test_group = groups.iloc[0]
90
+ test_group_id = int(test_group['GroupID'])
91
+ print(f"Testing with group: {test_group['Title']} (ID: {test_group_id})")
92
+
93
+ # Track received messages
94
+ received_events = []
95
+
96
+ # Register event handler
97
+ @tg.on_new_message(group_id=test_group_id)
98
+ async def message_handler(event):
99
+ received_events.append(event)
100
+ print(f" Real-time message from {event.sender_id}: {event.message.text[:50]}...")
101
+
102
+ # Give time for handler to register
103
+ await asyncio.sleep(1)
104
+
105
+ print("✓ Event handler registered successfully")
106
+ print("Note: To fully test real-time events, you would need to:")
107
+ print(" 1. Call await tg.run_with_event_loop() to start listening")
108
+ print(" 2. Send messages to the group from another account")
109
+ print(" 3. See the messages appear in real-time")
110
+
111
+ await tg.close()
112
+ return True
113
+
114
+ except Exception as e:
115
+ print(f"✗ Event handler test failed: {e}")
116
+ import traceback
117
+ traceback.print_exc()
118
+ return False
119
+
120
+
121
+ async def test_polling_error_handling():
122
+ """Test polling error recovery"""
123
+ print("\nTEST: Polling error recovery...")
124
+
125
+ try:
126
+ tg = TgData("config.ini")
127
+
128
+ # Test with invalid group ID
129
+ error_count = []
130
+
131
+ async def error_callback(messages_df):
132
+ # This shouldn't be called for invalid group
133
+ error_count.append(1)
134
+
135
+ print("Testing polling with invalid group ID...")
136
+
137
+ # Create a task for polling (it will fail but should handle errors gracefully)
138
+ poll_task = asyncio.create_task(
139
+ tg.poll_for_messages(
140
+ group_id=999999999, # Invalid group ID
141
+ interval=2,
142
+ callback=error_callback,
143
+ max_iterations=2
144
+ )
145
+ )
146
+
147
+ # Wait for polling to complete
148
+ await poll_task
149
+
150
+ print("✓ Polling handled errors gracefully")
151
+ await tg.close()
152
+ return True
153
+
154
+ except Exception as e:
155
+ print(f"✗ Error handling test failed: {e}")
156
+ import traceback
157
+ traceback.print_exc()
158
+ return False
159
+
160
+
161
+ async def test_multiple_handlers():
162
+ """Test multiple event handlers"""
163
+ print("\nTEST: Multiple event handlers...")
164
+
165
+ try:
166
+ tg = TgData("config.ini")
167
+
168
+ # Get test groups
169
+ groups = await tg.list_groups()
170
+ if len(groups) < 2:
171
+ print("! Need at least 2 groups for this test, skipping")
172
+ await tg.close()
173
+ return True
174
+
175
+ group1_id = int(groups.iloc[0]['GroupID'])
176
+ group2_id = int(groups.iloc[1]['GroupID'])
177
+
178
+ # Register handler for specific group
179
+ @tg.on_new_message(group_id=group1_id)
180
+ async def group1_handler(event):
181
+ print(f" Group 1 message: {event.message.text[:30]}...")
182
+
183
+ # Register handler for all groups
184
+ @tg.on_new_message()
185
+ async def all_groups_handler(event):
186
+ print(f" Any group message from {event.chat_id}: {event.message.text[:30]}...")
187
+
188
+ await asyncio.sleep(1)
189
+
190
+ print(f"✓ Registered handler for group {group1_id}")
191
+ print("✓ Registered handler for all groups")
192
+
193
+ await tg.close()
194
+ return True
195
+
196
+ except Exception as e:
197
+ print(f"✗ Multiple handlers test failed: {e}")
198
+ import traceback
199
+ traceback.print_exc()
200
+ return False
201
+
202
+
203
+ async def main():
204
+ """Run all polling tests"""
205
+ print("Polling and Real-time Features Tests")
206
+ print("=" * 50)
207
+
208
+ tests = [
209
+ test_polling_basic,
210
+ test_event_handler,
211
+ test_polling_error_handling,
212
+ test_multiple_handlers
213
+ ]
214
+
215
+ results = []
216
+ for test in tests:
217
+ try:
218
+ result = await test()
219
+ results.append(result)
220
+ await asyncio.sleep(0.5)
221
+ except Exception as e:
222
+ print(f"✗ Test failed with error: {e}")
223
+ import traceback
224
+ traceback.print_exc()
225
+ results.append(False)
226
+
227
+ # Summary
228
+ print("\nSummary")
229
+ print("=" * 50)
230
+ passed = sum(results)
231
+ total = len(results)
232
+ print(f"Passed: {passed}/{total}")
233
+
234
+ if passed == total:
235
+ print("✓ All polling tests passed!")
236
+ return 0
237
+ else:
238
+ print("✗ Some tests failed")
239
+ return 1
240
+
241
+
242
+ if __name__ == "__main__":
243
+ exit_code = asyncio.run(main())
244
+ sys.exit(exit_code)