pgits Claude commited on
Commit
1e041ce
Β·
1 Parent(s): 3722be6

TESTS: Organize tests into unit_tests directory with runner

Browse files

Created organized unit test suite accessible via HuggingFace SSH:

1. New Directory Structure:
- unit_tests/__init__.py - Package initialization
- unit_tests/README.md - Complete testing documentation
- unit_tests/run_all_tests.py - Run all tests with summary

2. Moved Existing Tests:
- test_booking_interception.py
- test_chat_widget.py
- test_custom_parser.py
- test_integration.py

3. Added New Test:
- test_google_meet.py - Verifies Google Meet conference creation
* Creates test event with Meet enabled
* Extracts and validates Meet link
* Auto-cleanup (deletes test event)
* Shows conferenceData debugging

4. HuggingFace SSH Access:
ssh -i ~/.ssh/id_ed25519 pgits-voicecal-ai@ssh.hf.space
cd /app/unit_tests
python test_google_meet.py # Individual test
python run_all_tests.py # All tests

5. Test Runner Features:
- Runs all test_*.py files automatically
- Shows pass/fail summary
- Detailed error output for failures
- Returns proper exit codes

Documentation includes:
- How to run tests on HF and locally
- What each test does
- Expected output
- Debugging tips
- How to add new tests

πŸ€– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
  [tool.poetry]
2
  name = "voicecal-ai"
3
- version = "1.3.4"
4
  description = "A friendly interactive AI speechbot for booking Google Calendar appointments"
5
  authors = ["Peter <pgits.job@gmail.com>"]
6
 
 
1
  [tool.poetry]
2
  name = "voicecal-ai"
3
+ version = "1.3.5"
4
  description = "A friendly interactive AI speechbot for booking Google Calendar appointments"
5
  authors = ["Peter <pgits.job@gmail.com>"]
6
 
unit_tests/README.md ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # VoiceCal.ai Unit Tests
2
+
3
+ This directory contains unit tests for VoiceCal.ai components.
4
+
5
+ ## Running Tests
6
+
7
+ ### On HuggingFace (via SSH)
8
+
9
+ 1. **SSH into your HuggingFace Space:**
10
+ ```bash
11
+ ssh -i ~/.ssh/id_ed25519 pgits-voicecal-ai@ssh.hf.space
12
+ ```
13
+
14
+ 2. **Navigate to tests directory:**
15
+ ```bash
16
+ cd /app/unit_tests
17
+ ```
18
+
19
+ 3. **Run individual tests:**
20
+ ```bash
21
+ python test_google_meet.py # Test Google Meet integration
22
+ python test_booking_interception.py # Test booking interception
23
+ python test_chat_widget.py # Test chat widget
24
+ python test_custom_parser.py # Test datetime parser
25
+ python test_integration.py # Integration tests
26
+ ```
27
+
28
+ 4. **Run all tests:**
29
+ ```bash
30
+ python run_all_tests.py
31
+ ```
32
+
33
+ ### Locally
34
+
35
+ 1. **Ensure environment is set up:**
36
+ ```bash
37
+ poetry install
38
+ # or
39
+ pip install -r requirements.txt
40
+ ```
41
+
42
+ 2. **Run from project root:**
43
+ ```bash
44
+ cd /Users/petergits/dev/voiceCal-ai-v3
45
+ python3 unit_tests/test_google_meet.py
46
+ ```
47
+
48
+ ## Available Tests
49
+
50
+ ### test_google_meet.py
51
+ Tests Google Meet conference creation in calendar events.
52
+
53
+ **What it does:**
54
+ - Creates a test event with Google Meet enabled
55
+ - Verifies Meet link is generated
56
+ - Checks conferenceData extraction
57
+ - Cleans up by deleting test event
58
+
59
+ **Expected output:**
60
+ ```
61
+ βœ… SUCCESS! Google Meet link created:
62
+ https://meet.google.com/abc-defg-hij
63
+ ```
64
+
65
+ ### test_booking_interception.py
66
+ Tests meeting booking interception and validation.
67
+
68
+ ### test_chat_widget.py
69
+ Tests chat widget functionality and UI interactions.
70
+
71
+ ### test_custom_parser.py
72
+ Tests natural language datetime parsing.
73
+
74
+ ### test_integration.py
75
+ End-to-end integration tests for the full booking flow.
76
+
77
+ ## Debugging
78
+
79
+ If tests fail, check:
80
+
81
+ 1. **Authentication:**
82
+ ```bash
83
+ cd /app
84
+ python refresh_credentials.py
85
+ ```
86
+
87
+ 2. **Environment Variables:**
88
+ ```bash
89
+ env | grep GOOGLE
90
+ ```
91
+
92
+ 3. **Logs:**
93
+ ```bash
94
+ tail -f /tmp/app.log
95
+ ```
96
+
97
+ ## Adding New Tests
98
+
99
+ 1. Create `test_<feature_name>.py` in this directory
100
+ 2. Follow the pattern from existing tests
101
+ 3. Include:
102
+ - Clear test description
103
+ - Setup and teardown
104
+ - Expected vs actual output
105
+ - Cleanup (delete test data)
106
+
107
+ ## Notes
108
+
109
+ - Tests that create calendar events will clean up automatically
110
+ - All tests are safe to run in production (they clean up after themselves)
111
+ - Failed tests will show diagnostics to help troubleshoot
unit_tests/__init__.py ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ """
2
+ VoiceCal.ai Unit Tests
3
+
4
+ This package contains unit tests for VoiceCal.ai components.
5
+ """
unit_tests/run_all_tests.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Run all VoiceCal.ai unit tests.
4
+
5
+ Usage:
6
+ python3 run_all_tests.py
7
+
8
+ # On HuggingFace SSH:
9
+ ssh -i ~/.ssh/id_ed25519 pgits-voicecal-ai@ssh.hf.space
10
+ cd /app/unit_tests && python run_all_tests.py
11
+ """
12
+
13
+ import sys
14
+ import os
15
+ import importlib.util
16
+ from pathlib import Path
17
+
18
+ # Add parent directory to path
19
+ sys.path.insert(0, str(Path(__file__).parent.parent))
20
+
21
+
22
+ def run_test_module(test_file):
23
+ """Run a single test module."""
24
+ module_name = test_file.stem
25
+
26
+ print(f"\n{'=' * 70}")
27
+ print(f"Running: {module_name}")
28
+ print('=' * 70)
29
+
30
+ try:
31
+ # Load the module
32
+ spec = importlib.util.spec_from_file_location(module_name, test_file)
33
+ module = importlib.util.module_from_spec(spec)
34
+ spec.loader.exec_module(module)
35
+
36
+ # Look for main function or just run the module
37
+ if hasattr(module, 'main'):
38
+ result = module.main()
39
+ return (module_name, result)
40
+ else:
41
+ print(f"βœ… {module_name} executed successfully")
42
+ return (module_name, True)
43
+
44
+ except Exception as e:
45
+ print(f"❌ {module_name} failed: {e}")
46
+ import traceback
47
+ traceback.print_exc()
48
+ return (module_name, False)
49
+
50
+
51
+ def main():
52
+ """Run all tests in the unit_tests directory."""
53
+ print("=" * 70)
54
+ print("VOICECAL.AI - UNIT TEST RUNNER")
55
+ print("=" * 70)
56
+
57
+ # Find all test files
58
+ tests_dir = Path(__file__).parent
59
+ test_files = sorted(tests_dir.glob("test_*.py"))
60
+
61
+ if not test_files:
62
+ print("❌ No test files found!")
63
+ return False
64
+
65
+ print(f"\nFound {len(test_files)} test(s):")
66
+ for test_file in test_files:
67
+ print(f" - {test_file.name}")
68
+
69
+ # Run each test
70
+ results = []
71
+ for test_file in test_files:
72
+ result = run_test_module(test_file)
73
+ results.append(result)
74
+
75
+ # Summary
76
+ print("\n" + "=" * 70)
77
+ print("TEST SUMMARY")
78
+ print("=" * 70)
79
+
80
+ passed = sum(1 for _, result in results if result)
81
+ failed = len(results) - passed
82
+
83
+ for test_name, result in results:
84
+ status = "βœ… PASS" if result else "❌ FAIL"
85
+ print(f"{status}: {test_name}")
86
+
87
+ print("\n" + "=" * 70)
88
+ print(f"Results: {passed} passed, {failed} failed out of {len(results)} total")
89
+ print("=" * 70)
90
+
91
+ return failed == 0
92
+
93
+
94
+ if __name__ == "__main__":
95
+ success = main()
96
+ sys.exit(0 if success else 1)
test_booking_interception.py β†’ unit_tests/test_booking_interception.py RENAMED
File without changes
test_chat_widget.py β†’ unit_tests/test_chat_widget.py RENAMED
File without changes
test_custom_parser.py β†’ unit_tests/test_custom_parser.py RENAMED
File without changes
unit_tests/test_google_meet.py ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Test script to verify Google Meet conference creation is working.
4
+
5
+ Usage:
6
+ python3 test_google_meet.py
7
+ """
8
+
9
+ import sys
10
+ import os
11
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
12
+
13
+ from datetime import datetime, timedelta
14
+ from app.calendar.service import CalendarService
15
+ from app.calendar.auth import CalendarAuth
16
+ import json
17
+
18
+
19
+ def test_google_meet_creation():
20
+ """Test creating an event with Google Meet."""
21
+ print("=" * 70)
22
+ print("GOOGLE MEET CREATION TEST")
23
+ print("=" * 70)
24
+
25
+ try:
26
+ # Initialize auth and service
27
+ print("\n1. Initializing CalendarAuth...")
28
+ auth = CalendarAuth()
29
+
30
+ print("2. Getting credentials...")
31
+ credentials = auth.load_credentials()
32
+
33
+ if not credentials:
34
+ print("❌ No credentials found!")
35
+ print(" Run: python3 refresh_credentials.py")
36
+ return False
37
+
38
+ print(f"βœ… Credentials loaded")
39
+ print(f" Expired: {credentials.expired}")
40
+ print(f" Expiry: {credentials.expiry}")
41
+
42
+ # Initialize calendar service
43
+ print("\n3. Initializing CalendarService...")
44
+ service = CalendarService()
45
+
46
+ # Create test event with Google Meet
47
+ print("\n4. Creating test event with Google Meet...")
48
+
49
+ # Schedule for 5 minutes from now
50
+ start_time = datetime.now() + timedelta(minutes=5)
51
+ end_time = start_time + timedelta(minutes=30)
52
+
53
+ print(f" Title: TEST - Google Meet Verification")
54
+ print(f" Start: {start_time}")
55
+ print(f" End: {end_time}")
56
+ print(f" Google Meet: ENABLED")
57
+
58
+ event = service.create_event(
59
+ summary="TEST - Google Meet Verification",
60
+ start_time=start_time,
61
+ end_time=end_time,
62
+ description="Automated test to verify Google Meet creation is working",
63
+ create_meet_conference=True
64
+ )
65
+
66
+ print("\n5. Event created successfully!")
67
+ print(f" Event ID: {event.get('id')}")
68
+
69
+ # Check for Google Meet link
70
+ print("\n6. Checking for Google Meet conference data...")
71
+
72
+ meet_link = None
73
+
74
+ # Method 1: Check conferenceData
75
+ if 'conferenceData' in event:
76
+ print(" βœ… conferenceData found in event")
77
+ conference_data = event['conferenceData']
78
+ print(f" Conference data keys: {list(conference_data.keys())}")
79
+
80
+ if 'entryPoints' in conference_data:
81
+ print(f" βœ… entryPoints found")
82
+ entry_points = conference_data['entryPoints']
83
+ print(f" Number of entry points: {len(entry_points)}")
84
+
85
+ for idx, entry in enumerate(entry_points):
86
+ print(f" Entry {idx+1}: {entry.get('entryPointType')} - {entry.get('uri', 'No URI')}")
87
+ if entry.get('entryPointType') == 'video':
88
+ meet_link = entry.get('uri')
89
+ print(f" βœ… VIDEO ENTRY FOUND: {meet_link}")
90
+ else:
91
+ print(" ⚠️ No entryPoints in conferenceData")
92
+ print(f" Available keys: {list(conference_data.keys())}")
93
+ else:
94
+ print(" ⚠️ No conferenceData in event")
95
+
96
+ # Method 2: Check hangoutLink (legacy)
97
+ if not meet_link and 'hangoutLink' in event:
98
+ meet_link = event['hangoutLink']
99
+ print(f" βœ… hangoutLink found (legacy): {meet_link}")
100
+
101
+ # Method 3: Check htmlLink
102
+ if 'htmlLink' in event:
103
+ print(f" πŸ“… Calendar event link: {event['htmlLink']}")
104
+
105
+ print("\n" + "=" * 70)
106
+ print("TEST RESULTS")
107
+ print("=" * 70)
108
+
109
+ if meet_link:
110
+ print(f"βœ… SUCCESS! Google Meet link created:")
111
+ print(f" {meet_link}")
112
+ print(f"\nβœ… Event ID: {event.get('id')}")
113
+ print(f"βœ… Event link: {event.get('htmlLink')}")
114
+
115
+ # Now delete the test event
116
+ print("\n7. Cleaning up - deleting test event...")
117
+ try:
118
+ service._get_service().events().delete(
119
+ calendarId='primary',
120
+ eventId=event['id']
121
+ ).execute()
122
+ print(" βœ… Test event deleted")
123
+ except Exception as e:
124
+ print(f" ⚠️ Could not delete test event: {e}")
125
+ print(f" Please manually delete event ID: {event['id']}")
126
+
127
+ return True
128
+ else:
129
+ print("❌ FAILED! No Google Meet link was created")
130
+ print("\nπŸ” DIAGNOSIS:")
131
+ print(" The event was created but without a Google Meet conference.")
132
+ print("\nπŸ’‘ POSSIBLE CAUSES:")
133
+ print(" 1. Google Calendar API might not have Meet enabled")
134
+ print(" 2. OAuth scope might be missing conferenceData permissions")
135
+ print(" 3. Account might not have Google Meet enabled")
136
+ print("\nπŸ› οΈ SOLUTION:")
137
+ print(" Check Google Cloud Console -> APIs & Services -> Credentials")
138
+ print(" Ensure these scopes are included:")
139
+ print(" - https://www.googleapis.com/auth/calendar")
140
+ print(" - https://www.googleapis.com/auth/calendar.events")
141
+
142
+ # Clean up
143
+ print("\n7. Cleaning up - deleting test event...")
144
+ try:
145
+ service._get_service().events().delete(
146
+ calendarId='primary',
147
+ eventId=event['id']
148
+ ).execute()
149
+ print(" βœ… Test event deleted")
150
+ except Exception as e:
151
+ print(f" ⚠️ Could not delete: {e}")
152
+
153
+ return False
154
+
155
+ except Exception as e:
156
+ print(f"\n❌ TEST FAILED WITH ERROR:")
157
+ print(f" {e}")
158
+ import traceback
159
+ traceback.print_exc()
160
+ return False
161
+
162
+
163
+ if __name__ == "__main__":
164
+ print("\nVoiceCal.ai - Google Meet Integration Test")
165
+ print("This will create and delete a test event")
166
+ print()
167
+
168
+ success = test_google_meet_creation()
169
+
170
+ print("\n" + "=" * 70)
171
+ if success:
172
+ print("πŸŽ‰ Google Meet integration is working correctly!")
173
+ else:
174
+ print("⚠️ Google Meet integration needs attention")
175
+ print("=" * 70)
test_integration.py β†’ unit_tests/test_integration.py RENAMED
File without changes
version.txt CHANGED
@@ -1 +1 @@
1
- 1.3.4
 
1
+ 1.3.5