Skip to content

Commit 6eeb2e0

Browse files
schwaaampclaude
andcommitted
Fix Libre 403 errors and add structured logging improvements
Critical Fixes: - Fix Libre 403 errors by fetching correct patient ID from connections endpoint - Add 403 error handling in sync-libre to auto-flag needs_reauth - Add structured logging for WHOOP token refresh success (was console.log) Testing Infrastructure: - Add comprehensive Edge Function test suite (Deno tests) - Add unit tests for WHOOP/Fitbit transformers and clients - Add schema contract tests for database operations - Add orchestrator tests for sync-all-devices - Add pre-deploy test script and CI/CD runner Documentation: - Add Edge Function testing guide (TESTING.md) - Add phase completion summaries (transformers, tests, contracts) - Add Jest performance optimization guide - Update CI/CD documentation with test instructions Mobile: - Add ConnectedDeviceCard needs_reauth UI tests - Optimize Jest setup with mockdate for performance Web: - Re-enable SSR in react-router.config.ts after cache clear Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent fbf3146 commit 6eeb2e0

31 files changed

+7854
-31
lines changed

.claude/settings.local.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,19 @@
121121
"WebFetch(domain:developer.dexcom.com)",
122122
"WebFetch(domain:github.com)",
123123
"Bash(supabase db execute:*)",
124-
"Bash(git push:*)"
124+
"Bash(git push:*)",
125+
"Bash(/tmp/check-sync-errors.sql:*)",
126+
"Bash(/tmp/check-ingestion-errors.sql <<'EOF'\n-- Check ingestion_log for recent failures\nSELECT \n occurred_at,\n provider,\n status,\n message,\n user_id\nFROM ingestion_log\nWHERE occurred_at > NOW\\(\\) - INTERVAL '2 hours'\n AND status IN \\('failure', 'warning'\\)\nORDER BY occurred_at DESC\nLIMIT 20;\nEOF)",
127+
"Bash(/tmp/check-cron-and-tokens.sql:*)",
128+
"Bash(/tmp/diagnose_sync_failure.sql <<'EOF'\n-- ============================================================================\n-- DIAGNOSTIC QUERIES FOR SYNC-ALL-DEVICES 401 ERRORS\n-- ============================================================================\n\n-- 1. Check what cron jobs are currently scheduled\nSELECT \n jobname, \n schedule, \n command, \n active,\n jobid\nFROM cron.job\nWHERE jobname LIKE '%sync%' OR jobname LIKE '%fitness%'\nORDER BY jobname;\n\n-- 2. Check recent cron job runs \\(last 3 hours\\)\nSELECT \n j.jobname,\n r.status,\n r.return_message,\n r.start_time,\n r.end_time,\n EXTRACT\\(EPOCH FROM \\(r.end_time - r.start_time\\)\\) as duration_seconds\nFROM cron.job_run_details r\nJOIN cron.job j ON r.jobid = j.jobid\nWHERE r.start_time > NOW\\(\\) - INTERVAL '3 hours'\n AND \\(j.jobname LIKE '%sync%' OR j.jobname LIKE '%fitness%'\\)\nORDER BY r.start_time DESC;\n\n-- 3. Check if vault secrets are configured\nSELECT \n name,\n CASE \n WHEN decrypted_secret IS NOT NULL THEN 'EXISTS \\(length: ' || LENGTH\\(decrypted_secret\\) || '\\)'\n ELSE 'MISSING'\n END as status\nFROM vault.decrypted_secrets\nWHERE name IN \\('service_role_key', 'supabase_url'\\)\nORDER BY name;\n\n-- 4. Check recent sync-all-devices errors from app_logs\nSELECT \n created_at,\n level,\n category,\n message,\n metadata->>'error' as error_detail\nFROM app_logs\nWHERE function_name = 'sync-all-devices'\n AND created_at > NOW\\(\\) - INTERVAL '3 hours'\nORDER BY created_at DESC\nLIMIT 10;\n\n-- 5. Check token expiration status for all connected devices\nSELECT \n cd.provider,\n cd.device_id,\n cd.user_id,\n cd.needs_reauth,\n CASE \n WHEN cd.provider = 'whoop' THEN wt.expires_at\n WHEN cd.provider = 'fitbit' THEN ft.expires_at\n WHEN cd.provider = 'oura' THEN ot.expires_at\n WHEN cd.provider = 'libre' THEN lt.expires_at\n END as token_expires_at,\n CASE \n WHEN cd.provider = 'whoop' THEN wt.expires_at < NOW\\(\\)\n WHEN cd.provider = 'fitbit' THEN ft.expires_at < NOW\\(\\)\n WHEN cd.provider = 'oura' THEN ot.expires_at < NOW\\(\\)\n WHEN cd.provider = 'libre' THEN lt.expires_at < NOW\\(\\)\n END as is_expired\nFROM connected_devices cd\nLEFT JOIN whoop_tokens wt ON cd.provider = 'whoop' AND cd.user_id = wt.user_id\nLEFT JOIN fitbit_tokens ft ON cd.provider = 'fitbit' AND cd.user_id = ft.user_id\nLEFT JOIN oura_tokens ot ON cd.provider = 'oura' AND cd.user_id = ot.user_id\nLEFT JOIN libre_tokens lt ON cd.provider = 'libre' AND cd.user_id = lt.user_id\nORDER BY cd.provider;\n\n-- 6. Check recent ingestion_log entries\nSELECT \n occurred_at,\n provider,\n status,\n message\nFROM ingestion_log\nWHERE occurred_at > NOW\\(\\) - INTERVAL '3 hours'\nORDER BY occurred_at DESC\nLIMIT 20;\nEOF)",
129+
"Bash(/tmp/fix_cron_job.sql <<'EOF'\n-- ============================================================================\n-- FIX CRON JOB TO USE CORRECT FUNCTION\n-- ============================================================================\n\n-- Unschedule old jobs\nSELECT cron.unschedule\\('hourly-fitness-tracker-sync'\\)\nWHERE EXISTS \\(\n SELECT 1 FROM cron.job WHERE jobname = 'hourly-fitness-tracker-sync'\n\\);\n\nSELECT cron.unschedule\\('daily-fitness-tracker-sync'\\)\nWHERE EXISTS \\(\n SELECT 1 FROM cron.job WHERE jobname = 'daily-fitness-tracker-sync'\n\\);\n\n-- Schedule new job with correct function name\nSELECT cron.schedule\\(\n 'hourly-device-sync', -- New job name\n '0 * * * *', -- Every hour at :00\n $SELECT trigger_device_sync\\(\\)$\n\\);\n\n-- Verify the job was created\nSELECT jobname, schedule, command, active\nFROM cron.job\nWHERE jobname = 'hourly-device-sync';\nEOF)",
130+
"Bash(deno test:*)",
131+
"Bash(deno --version:*)",
132+
"Bash(psql:*)",
133+
"Bash(~/.bash_profile)",
134+
"Bash(/Users/schwaaamp/.deno/bin/deno test:*)",
135+
"Bash(/Users/schwaaamp/DashMobileApp/supabase/functions/run-tests.sh:*)",
136+
"Bash(./run-tests.sh:*)"
125137
],
126138
"additionalDirectories": [
127139
"/Users/schwaaamp/DashMobileApp/scripts/canonical-pipeline"

ci-cd/EDGE-FUNCTION-TESTS.md

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
## Quick Reference
44

5-
**Status**: ✅ 67 tests implemented and passing, ready for CI/CD
6-
**Runtime**: ~145ms (fast enough for pre-commit hooks)
7-
**Coverage**: Unit tests (44) + Integration tests (11) + Regression tests (12)
5+
**Status**: ✅ 86 tests implemented and passing, ready for CI/CD
6+
**Runtime**: ~184ms (fast enough for pre-commit hooks)
7+
**Coverage**: Unit tests (44) + Integration tests (11) + Regression tests (12) + Contract tests (19)
88

99
---
1010

@@ -55,13 +55,15 @@ jobs:
5555
echo " • Fitbit client integration tests: 6 tests"
5656
echo " • Fitbit transformer unit tests: 28 tests"
5757
echo " • WHOOP transformer unit tests: 16 tests"
58+
echo " • Schema contract tests: 19 tests"
5859
echo ""
59-
echo "Total: 67 tests"
60+
echo "Total: 86 tests"
6061
echo ""
6162
echo "Coverage:"
6263
echo " ✅ All data transformation functions (100%)"
6364
echo " ✅ Critical API client behavior"
6465
echo " ✅ Production bug regressions"
66+
echo " ✅ Database schema contracts (4 tables)"
6567
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
6668
```
6769
@@ -181,6 +183,26 @@ Now PRs cannot merge without passing tests.
181183
- Filtering devices that need reauth
182184
- Batching by provider for rate limiting
183185

186+
#### 4. Schema Contracts (19 tests)
187+
188+
**Purpose**: Prevent schema drift and ensure transformers produce valid database records
189+
190+
**All Fitness Tables**:
191+
- ✅ Use `device_id` (not `tracker_id`) - REGRESSION: Column rename
192+
- ✅ Have all required fields (`device_id`, `vendor_metadata`, etc.)
193+
- ✅ Allow null for optional fields (steps, distance, calories)
194+
- ✅ Preserve 0 values (0 ≠ null)
195+
196+
**Data Type Contracts**:
197+
- ✅ Timestamps are ISO 8601 strings (not Date objects)
198+
- ✅ Dates are YYYY-MM-DD format (not full timestamps)
199+
- ✅ Numeric fields accept 0 as valid value
200+
201+
**Cross-Table Consistency**:
202+
- ✅ All tables use `device_id` consistently
203+
- ✅ All tables have `vendor_metadata` for extensibility
204+
- ✅ Naming conventions enforced (FK ends with `_id`, timestamps with `_time`)
205+
184206
### Critical Bugs Caught
185207

186208
#### Zero Value Handling Bug
@@ -249,6 +271,7 @@ supabase/functions/
249271
├── _shared/
250272
│ ├── transformers.ts # Fitbit transformers
251273
│ ├── transformers.test.ts # 28 tests ✅
274+
│ ├── schema-contracts.test.ts # 19 tests ✅ (NEW)
252275
│ ├── whoop-client-simple.test.ts # 5 tests ✅
253276
│ └── fitbit-client-simple.test.ts # 6 tests ✅
254277
└── sync-all-devices/
@@ -359,11 +382,12 @@ Check test results: `https://github.com/YOUR_ORG/DashMobileApp/actions`
359382

360383
| Metric | Target | Actual | Status |
361384
|--------|--------|--------|--------|
362-
| Test Pass Rate | 100% | 100% (67/67) ||
363-
| Runtime | < 500ms | ~145ms ||
385+
| Test Pass Rate | 100% | 100% (86/86) ||
386+
| Runtime | < 500ms | ~184ms ||
364387
| Flaky Tests | 0 | 0 ||
365388
| Transformer Coverage | 100% | 100% (10/10 functions) ||
366-
| Critical Bugs Found | N/A | 1 (zero value handling) | ✅ Fixed |
389+
| Schema Contract Coverage | All tables | 4/4 fitness tables ||
390+
| Critical Bugs Found | N/A | 5 (zero values, tracker_id, timestamps, required fields, 0→null) | ✅ Fixed |
367391

368392
---
369393

@@ -390,6 +414,6 @@ Check test results: `https://github.com/YOUR_ORG/DashMobileApp/actions`
390414
---
391415

392416
**Last Updated**: February 3, 2026
393-
**Test Suite Version**: Phase 1 + Phase 2 Complete
394-
**Total Tests**: 67 (all passing)
417+
**Test Suite Version**: Phases 1-4 Complete
418+
**Total Tests**: 86 (all passing)
395419
**Ready for Production**: ✅ Yes

ci-cd/README.md

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -140,12 +140,13 @@ All CI/CD configuration is version controlled:
140140

141141
### Overview
142142

143-
A comprehensive test suite for Supabase Edge Functions has been implemented with **67 tests** covering:
143+
A comprehensive test suite for Supabase Edge Functions has been implemented with **86 tests** covering:
144144
- **Unit Tests (44 tests)**: Pure transformer functions with 100% coverage
145145
- **Integration Tests (11 tests)**: API client behavior (WHOOP, Fitbit)
146146
- **Regression Tests (12 tests)**: Critical bugs that occurred in production
147+
- **Contract Tests (19 tests)**: Database schema validation and drift prevention
147148

148-
**Status**: ✅ All 67 tests passing, ~145ms runtime, ready for CI/CD integration
149+
**Status**: ✅ All 86 tests passing, ~184ms runtime, ready for CI/CD integration
149150

150151
### Running Tests Locally
151152

@@ -305,9 +306,10 @@ fi
305306

306307
### Documentation
307308

308-
- **Testing Plan**: `docs/edge-functions-testing-plan.md` - Full 4-phase plan
309-
- **Phase 1 Complete**: `docs/phase-1-transformers-complete.md` - Transformer tests
310-
- **Phase 2 Complete**: `docs/phase-2-tests-fixed.md` - Integration tests
309+
- **Testing Plan**: `docs/edge-functions-testing-plan.md` - Full 5-phase plan
310+
- **Phase 1 Complete**: `docs/phase-1-transformers-complete.md` - Transformer tests (44 tests)
311+
- **Phase 2 Complete**: `docs/phase-2-tests-fixed.md` - Integration tests (23 tests)
312+
- **Phase 4 Complete**: `docs/phase-4-contract-tests-complete.md` - Schema contract tests (19 tests)
311313
- **Quick Reference**: `supabase/functions/TESTING.md` - Commands and examples
312314
- **Test Files**: All tests include inline documentation and comments
313315

@@ -337,11 +339,11 @@ fi
337339
| **Transformers** | 100% | 44 | ✅ Complete |
338340
| **API Clients** | Critical paths | 11 | ✅ Complete |
339341
| **Orchestrator** | All regressions | 12 | ✅ Complete |
340-
| **Overall** | Phase 1+2 complete | 67 | ✅ 100% passing |
342+
| **Overall** | Phases 1-4 complete | 86 | ✅ 100% passing |
341343

342344
**Remaining Phases**:
343-
- Phase 4: Contract tests (database schema) - TODO
344-
- Phase 5: E2E tests with test database - TODO
345+
- Phase 4: Contract tests (database schema) - ✅ COMPLETE (19 tests)
346+
- Phase 5: E2E tests with test database - TODO (optional)
345347

346348
## 📈 Recommended Activation Order
347349

0 commit comments

Comments
 (0)