From b6da360856f989a9a194ef1e9d00bd1b789cf8ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Tue, 3 Nov 2026 20:32:35 +0100 Subject: [PATCH 1/7] refactor(db): drop the runtime schema mirror ensureDbCompatibility ran ALTER TABLE / CREATE TABLE IF NOT EXISTS on the login, register, token and session paths, mirroring columns every migration under prisma/migrations already creates. The entrypoint applies those migrations before the server listens and stops the boot when one fails, so the mirror never had work to do, and DDL on a request path needs the table's owner: a connection that may only read and write rows fails every login on it. Drop the module, its call sites and the mocks that stubbed it out. --- src/__tests__/api/analytics/since-cap.test.ts | 4 - .../api/auth/login/mfa-branch.test.ts | 3 - src/__tests__/api/auth/mfa/verify.test.ts | 3 - .../auth/registration-status/route.test.ts | 4 - .../baselines/version-stamp-comments.json | 1 - src/__tests__/sql-now-utc-guard.test.ts | 4 - src/__tests__/tz-day-paths-harness.test.ts | 3 - .../__tests__/route.test.ts | 3 - .../measurements/__tests__/status.test.ts | 3 - .../pulse-day-mean/__tests__/route.test.ts | 3 - .../assistant-flags/__tests__/route.test.ts | 3 - .../__tests__/route.test.ts | 3 - src/app/api/analytics/__tests__/route.test.ts | 4 - .../login/__tests__/constant-work.test.ts | 4 - .../auth/login/__tests__/native-token.test.ts | 6 - .../api/auth/login/__tests__/route.test.ts | 6 - .../login/__tests__/trusted-device.test.ts | 4 - src/app/api/auth/login/route.ts | 3 - src/app/api/auth/mfa/verify/route.ts | 3 - .../webauthn/verify/__tests__/route.test.ts | 4 - .../auth/mfa/webauthn/verify/options/route.ts | 3 - src/app/api/auth/mfa/webauthn/verify/route.ts | 3 - .../auth/native/token/__tests__/route.test.ts | 4 - src/app/api/auth/native/token/route.ts | 3 - .../oidc/native/token/__tests__/route.test.ts | 4 - src/app/api/auth/oidc/native/token/route.ts | 3 - .../__tests__/native-token.test.ts | 4 - .../login-verify/__tests__/oidc-only.test.ts | 4 - .../login-verify/__tests__/refusals.test.ts | 4 - .../api/auth/passkey/login-verify/route.ts | 3 - .../api/auth/password/__tests__/route.test.ts | 3 - .../api/auth/register/__tests__/route.test.ts | 3 - src/app/api/auth/register/route.ts | 3 - .../[id]/__tests__/delete-record-auth.test.ts | 3 - .../biomarkers/[id]/__tests__/route.test.ts | 4 - .../__tests__/error-contract.test.ts | 3 - .../c/[token]/unlock/__tests__/route.test.ts | 4 - .../api/coach/plans/__tests__/route.test.ts | 4 - .../coach/reminders/__tests__/route.test.ts | 3 - .../suggested-actions/__tests__/route.test.ts | 3 - .../api/consent/ai/__tests__/route.test.ts | 4 - .../consent/ai/latest/__tests__/route.test.ts | 4 - .../consent/ai/web/__tests__/route.test.ts | 4 - .../[id]/__tests__/route.test.ts | 4 - .../entries/__tests__/note-encryption.test.ts | 4 - .../[id]/entries/__tests__/route.test.ts | 4 - .../__tests__/soft-delete-restore.test.ts | 4 - .../custom-metrics/__tests__/route.test.ts | 4 - .../cycle/__tests__/external-id-floor.test.ts | 3 - .../calendar/__tests__/day-entries.test.ts | 3 - .../__tests__/verdict-round-trip.test.ts | 3 - .../day-logs/bulk/__tests__/route.test.ts | 3 - .../insights/__tests__/route-locale.test.ts | 4 - .../custom/__tests__/custom-crud.test.ts | 3 - .../api/daily/digest/__tests__/route.test.ts | 3 - .../digest/dismiss/__tests__/route.test.ts | 3 - .../__tests__/route.test.ts | 3 - .../snapshot/__tests__/route.test.ts | 3 - .../summary/__tests__/module-gate.test.ts | 3 - .../dashboard/summary/__tests__/route.test.ts | 4 - .../dashboard/widgets/__tests__/route.test.ts | 4 - .../api/devices/[id]/__tests__/route.test.ts | 4 - src/app/api/devices/__tests__/route.test.ts | 4 - .../inbound/[id]/__tests__/route.test.ts | 3 - .../inbound/[id]/chat/__tests__/route.test.ts | 3 - .../[id]/extract/__tests__/route.test.ts | 3 - .../[id]/index/__tests__/route.test.ts | 3 - .../[id]/original/__tests__/route.test.ts | 3 - .../[id]/suggest/__tests__/route.test.ts | 3 - .../[id]/summary/__tests__/route.test.ts | 3 - .../[id]/thumbnail/__tests__/route.test.ts | 3 - .../documents/inbound/__tests__/route.test.ts | 3 - .../inbound/layout/__tests__/route.test.ts | 4 - .../inbound/reindex/__tests__/route.test.ts | 3 - .../api/feature-flags/__tests__/route.test.ts | 4 - .../fhir/__tests__/observation-series.test.ts | 3 - src/app/api/fhir/__tests__/route.test.ts | 3 - .../__tests__/hidden-redaction.test.ts | 4 - .../achievements/__tests__/ios-format.test.ts | 4 - .../__tests__/module-gate.test.ts | 3 - .../__tests__/writes-nothing.test.ts | 3 - .../episodes/[id]/__tests__/body-site.test.ts | 3 - .../[id]/day-logs/__tests__/route.test.ts | 3 - .../[id]/restore/__tests__/route.test.ts | 3 - .../[jobId]/status/__tests__/route.test.ts | 3 - .../__tests__/route.test.ts | 3 - .../__tests__/route.test.ts | 3 - .../ingest/medication/__tests__/route.test.ts | 3 - .../__tests__/route.test.ts | 4 - .../__tests__/route.test.ts | 4 - .../insights/cards/__tests__/route.test.ts | 4 - .../[id]/attachments/__tests__/route.test.ts | 3 - .../chat/fenced/__tests__/route.test.ts | 3 - .../coach-read/__tests__/route.test.ts | 4 - .../facts/__tests__/route-memory.test.ts | 4 - .../coach/facts/__tests__/route.test.ts | 4 - .../comprehensive/__tests__/route.test.ts | 4 - .../correlations/__tests__/route.test.ts | 4 - .../insights/derived/__tests__/route.test.ts | 3 - .../derived/batch/__tests__/route.test.ts | 3 - .../insights/ecg/[id]/__tests__/route.test.ts | 3 - .../api/insights/ecg/__tests__/route.test.ts | 3 - .../insights/layout/__tests__/route.test.ts | 4 - .../metric-status/__tests__/route.test.ts | 4 - .../mood-status/__tests__/route.test.ts | 4 - .../patterns/[id]/__tests__/route.test.ts | 3 - .../insights/patterns/__tests__/route.test.ts | 3 - .../pregenerate/__tests__/route.test.ts | 4 - .../rhythm-events/__tests__/route.test.ts | 3 - .../insights/targets/__tests__/route.test.ts | 4 - .../healthkit/__tests__/route.test.ts | 4 - .../api/labs/__tests__/error-contract.test.ts | 3 - .../labs/ocr/commit/__tests__/route.test.ts | 3 - .../api/labs/restore/__tests__/route.test.ts | 3 - .../__tests__/route.test.ts | 4 - .../measurements/[id]/__tests__/route.test.ts | 3 - .../__tests__/group-by-day.test.ts | 3 - .../__tests__/put-duplicate-timestamp.test.ts | 4 - .../__tests__/range-aggregation-route.test.ts | 3 - .../__tests__/zod-multi-issue.test.ts | 3 - .../__tests__/aggregated-hr-wire.test.ts | 3 - .../__tests__/duplicate-prefilter.test.ts | 3 - .../__tests__/hr-bucket-observability.test.ts | 3 - .../insert-returning-arrivals.test.ts | 3 - .../batch/__tests__/issue-paths.test.ts | 3 - .../batch/__tests__/pr-detection-hook.test.ts | 4 - .../batch/__tests__/refusal-detail.test.ts | 3 - .../batch/__tests__/sync-trigger.test.ts | 3 - .../__tests__/timestamp-plausibility.test.ts | 4 - .../__tests__/unstable-external-id.test.ts | 4 - .../bulk-delete/__tests__/route.test.ts | 3 - .../by-external-ids/__tests__/route.test.ts | 3 - .../restore/__tests__/route.test.ts | 3 - .../series-batch/__tests__/route.test.ts | 3 - .../series/__tests__/route.test.ts | 4 - .../unknown-query-param-open.test.ts | 4 - .../medications/[id]/__tests__/route.test.ts | 3 - .../[id]/compliance/__tests__/route.test.ts | 4 - .../[id]/dose-history/__tests__/route.test.ts | 3 - .../[id]/glp1/__tests__/route.test.ts | 4 - .../intake/[eventId]/__tests__/route.test.ts | 3 - .../[id]/intake/__tests__/route.test.ts | 4 - .../__tests__/source-provenance.test.ts | 4 - .../intake/import/__tests__/route.test.ts | 3 - .../__tests__/route.test.ts | 3 - .../api/medications/__tests__/route.test.ts | 3 - .../compliance/__tests__/route.test.ts | 4 - .../intake/__tests__/route.test.ts | 4 - .../bulk/__tests__/dose-arrival-scope.test.ts | 3 - .../intake/bulk/__tests__/route.test.ts | 3 - .../__tests__/route.test.ts | 3 - .../layout/__tests__/route.test.ts | 4 - .../assessments/[id]/__tests__/route.test.ts | 3 - .../assessments/__tests__/route.test.ts | 3 - .../meta/capabilities/__tests__/route.test.ts | 4 - .../mood-entries/[id]/__tests__/route.test.ts | 3 - .../api/mood-entries/__tests__/route.test.ts | 3 - .../bulk-delete/__tests__/route.test.ts | 3 - .../mood-entries/bulk/__tests__/route.test.ts | 3 - .../restore/__tests__/route.test.ts | 3 - .../mood/analytics/__tests__/route.test.ts | 4 - .../api/mood/insights/__tests__/route.test.ts | 3 - .../action/[token]/__tests__/route.test.ts | 4 - .../status/__tests__/route.test.ts | 4 - src/app/api/nutrients/__tests__/route.test.ts | 3 - .../nutrients/batch/__tests__/route.test.ts | 3 - .../nutrients/daily/__tests__/route.test.ts | 3 - .../nutrients/water/__tests__/route.test.ts | 3 - .../__tests__/error-contract.test.ts | 3 - .../complete/__tests__/route.test.ts | 4 - .../personal-records/__tests__/route.test.ts | 4 - .../api/share-links/__tests__/route.test.ts | 3 - .../api/sleep/night/__tests__/route.test.ts | 4 - .../api/sleep/rhythm/__tests__/route.test.ts | 4 - .../changes/__tests__/module-gate.test.ts | 3 - .../user/avatar/[id]/__tests__/route.test.ts | 4 - .../api/user/avatar/__tests__/route.test.ts | 4 - .../api/user/profile/__tests__/route.test.ts | 4 - .../user/thresholds/__tests__/route.test.ts | 3 - .../credentials/__tests__/route.test.ts | 3 - .../workouts/__tests__/batch-create.test.ts | 4 - .../__tests__/unstable-external-id.test.ts | 3 - .../c/[token]/d/[id]/__tests__/route.test.ts | 3 - .../glitchtip-path-secret-redaction.test.ts | 4 - .../__tests__/sleep-stage-composition.test.ts | 4 - .../session-locale-reconcile.test.ts | 4 - src/lib/auth/__tests__/session.test.ts | 4 - src/lib/auth/session.ts | 13 +- src/lib/db-compat.ts | 277 ------------------ .../account-deletion-grace.test.ts | 4 - .../account-grant-lifecycle.test.ts | 4 - .../acting-account-resolver.test.ts | 4 - .../activity-calendar-read.test.ts | 4 - tests/integration/admin-backups-audit.test.ts | 4 - .../admin-backups-canonical-roundtrip.test.ts | 4 - .../admin-backups-download.test.ts | 4 - .../integration/admin-backups-restore.test.ts | 4 - .../admin-backups-upload-too-large.test.ts | 3 - .../integration/admin-backups-upload.test.ts | 4 - tests/integration/admin-data-wipe.test.ts | 4 - .../admin-drain-cumulative-endpoint.test.ts | 4 - tests/integration/admin-host-metrics.test.ts | 4 - .../integration/admin-reset-password.test.ts | 4 - .../admin-rollups-recompute.test.ts | 4 - .../integration/admin-support-report.test.ts | 4 - tests/integration/admin-user-deletion.test.ts | 4 - tests/integration/ai-consent-explicit.test.ts | 4 - .../ai-extraction-capability.test.ts | 4 - tests/integration/ai-optional-actions.test.ts | 4 - .../ai-optional-data-routes.test.ts | 4 - tests/integration/ai-optional-jobs.test.ts | 4 - .../ai-optional-mixed-routes.test.ts | 4 - .../ai-provider-key-removal.test.ts | 4 - tests/integration/ai-usage-levels.test.ts | 4 - .../analytics-bp-aggregate-paged.test.ts | 4 - .../analytics-health-score.test.ts | 4 - .../analytics-sleep-stages.test.ts | 4 - .../analytics-summaries-slice.test.ts | 4 - .../integration/analytics-weekday-tz.test.ts | 4 - tests/integration/apns-dispatch.test.ts | 4 - .../apple-health-import-ecg.test.ts | 4 - .../apple-health-import-reconcile.test.ts | 4 - .../apple-health-import-rollup-window.test.ts | 3 - ...pple-health-import-worker-failures.test.ts | 4 - tests/integration/auth-flow.test.ts | 11 +- tests/integration/auth-ledger-cleanup.test.ts | 4 - .../auth-me-ai-capabilities.test.ts | 4 - .../auth-me-coach-reasoning.test.ts | 4 - .../integration/auth-password-change.test.ts | 4 - .../backup-chunked-storage.test.ts | 4 - ...ackup-custom-cycle-symptom-restore.test.ts | 4 - .../backup-cycle-cross-key-restore.test.ts | 4 - ...kup-cycle-symptom-severity-restore.test.ts | 4 - .../backup-document-import-ledger.test.ts | 4 - .../backup-document-streaming.test.ts | 4 - .../backup-dr-stamps-and-ciphertext.test.ts | 4 - .../backup-erased-account-restore.test.ts | 4 - tests/integration/backup-key-needs.test.ts | 4 - .../backup-legacy-rmssd-restore.test.ts | 4 - .../backup-missing-section-restore.test.ts | 4 - ...ackup-mood-label-cross-key-restore.test.ts | 4 - tests/integration/backup-parity.test.ts | 4 - .../backup-portable-file-restore.test.ts | 4 - tests/integration/backup-preview.test.ts | 4 - .../backup-qualitative-lab-restore.test.ts | 4 - tests/integration/backup-restore-job.test.ts | 4 - .../integration/backup-restore-memory.test.ts | 4 - .../backup-restore-statement-timeout.test.ts | 4 - tests/integration/backup-round-trip.test.ts | 4 - .../backup-token-link-restore.test.ts | 4 - ...ckup-unknown-catalogue-key-restore.test.ts | 4 - .../backup-upload-document-memory.test.ts | 4 - .../backup-visits-cross-key-restore.test.ts | 4 - .../bearer-scope-enforcement.test.ts | 4 - .../biomarker-backup-completeness.test.ts | 4 - tests/integration/biomarker-detail.test.ts | 4 - tests/integration/biomarker-merge.test.ts | 4 - .../blood-pressure-reading-delete.test.ts | 4 - tests/integration/body-sites.test.ts | 4 - tests/integration/bp-in-target.test.ts | 4 - .../integration/briefing-cache-locale.test.ts | 4 - .../bulk-delete-source-filter.test.ts | 4 - .../bulk-writer-cache-invalidation.test.ts | 3 - .../cache-invalidation-coverage.test.ts | 21 -- .../integration/changepoint-extended.test.ts | 4 - .../chart-all-range-meaning.test.ts | 3 - tests/integration/chart-overlay-prefs.test.ts | 4 - tests/integration/chart-range-dense.test.ts | 4 - tests/integration/checkup-stays-due.test.ts | 4 - .../coach-availability-probe.test.ts | 4 - tests/integration/coach-chat.test.ts | 4 - .../coach-conversation-delete.test.ts | 4 - tests/integration/coach-conversations.test.ts | 4 - tests/integration/coach-feedback.test.ts | 4 - tests/integration/coach-history-reach.test.ts | 3 - tests/integration/coach-labs-history.test.ts | 3 - tests/integration/coach-metric-table.test.ts | 3 - tests/integration/coach-prefs.test.ts | 4 - .../coach-series-long-window.test.ts | 3 - .../coach-stale-rollup-tail.test.ts | 4 - tests/integration/coach-trail.test.ts | 4 - .../integration/codex-credential-park.test.ts | 4 - .../integration/compaction-tombstones.test.ts | 4 - .../correlation-extended-channels.test.ts | 4 - ...indings-before-seasonal-adjustment.test.ts | 4 - .../correlations-rollup-swap.test.ts | 4 - .../credential-enrollment-proof.test.ts | 4 - .../csv-import-contextless-glucose.test.ts | 4 - .../csv-reimport-idempotent.test.ts | 4 - .../cumulative-pr-rederive-atomic.test.ts | 4 - .../cycle-absorbed-restored.test.ts | 4 - .../cycle-crosstab-day-aggregates.test.ts | 4 - .../cycle-flow-opens-cycle.test.ts | 4 - .../cycle-insights-dense-reads.test.ts | 4 - .../cycle-insights-scoped-delegate.test.ts | 4 - tests/integration/cycle-routes.test.ts | 4 - .../cycle-start-round-trip.test.ts | 4 - .../daily-digest-today-overview.test.ts | 4 - .../integration/daily-series-user-tz.test.ts | 4 - ...ashboard-chart-overlay-concurrency.test.ts | 4 - .../dashboard-glucose-untagged.test.ts | 4 - .../dashboard-own-target-bands.test.ts | 4 - .../dashboard-pulse-latest-day.test.ts | 4 - .../dashboard-widgets-save.test.ts | 4 - tests/integration/dawarich-context.test.ts | 4 - tests/integration/day-view.test.ts | 4 - .../delegated-intake-notification.test.ts | 4 - .../demo-mode-profile-write.test.ts | 4 - tests/integration/demo-seed-ai-texts.test.ts | 4 - tests/integration/demo-seed-shape.test.ts | 4 - tests/integration/demo-seed-sources.test.ts | 4 - .../dense-glucose-raw-window-series.test.ts | 4 - .../dense-pulse-raw-window-reads.test.ts | 4 - tests/integration/dense-read-bounded.test.ts | 4 - .../integration/dense-series-hrv-spo2.test.ts | 4 - tests/integration/dense-targets-reads.test.ts | 4 - tests/integration/device-register.test.ts | 4 - .../doctor-report-fixed-window.test.ts | 4 - .../doctor-report-sections.test.ts | 4 - tests/integration/document-ai-runs.test.ts | 4 - tests/integration/document-sources.test.ts | 4 - .../document-undated-lab-value.test.ts | 4 - .../documents-content-index.test.ts | 4 - tests/integration/documents-import.test.ts | 4 - .../documents-recently-deleted.test.ts | 4 - tests/integration/documents-vault.test.ts | 4 - .../dose-history-pre-creation-slots.test.ts | 4 - .../integration/ecg-ingest-roundtrip.test.ts | 4 - .../emergency-profile-route.test.ts | 4 - .../encounter-appointment-reminder.test.ts | 4 - .../encounter-phase2-surfaces.test.ts | 4 - .../integration/encounter-preparation.test.ts | 4 - .../integration/encounter-procedures.test.ts | 4 - .../encounter-scoped-delegate.test.ts | 4 - .../integration/encryption-key-check.test.ts | 4 - .../environment-air-quality-history.test.ts | 4 - .../environment-coarse-location.test.ts | 4 - .../environment-location-encryption.test.ts | 4 - tests/integration/environment-setup.ts | 4 - tests/integration/export-per-type.test.ts | 4 - tests/integration/fitbit-retirement.test.ts | 3 - .../integration/fold-complete-windows.test.ts | 4 - tests/integration/fold-horizon.test.ts | 4 - .../fold-leftover-provenance.test.ts | 4 - .../integration/full-backup-nutrients.test.ts | 4 - tests/integration/golden-backups.test.ts | 4 - .../google-health-account-id.test.ts | 4 - .../google-health-backfill-multi-year.test.ts | 4 - ...gle-health-backfill-progress-bound.test.ts | 4 - .../google-health-backfill-rate-limit.test.ts | 4 - ...oogle-health-dense-backfill-memory.test.ts | 4 - .../google-health-sleep-overlap.test.ts | 4 - .../integration/health-connect-import.test.ts | 4 - .../health-score-configured-flag.test.ts | 4 - .../health-score-labs-pillars.test.ts | 4 - tests/integration/health-score-record.test.ts | 4 - .../health-status-implausible-values.test.ts | 4 - tests/integration/healthkit-type-sync.test.ts | 4 - .../hour-buckets-and-backfill.test.ts | 4 - .../hrv-rmssd-fitbit-google-migration.test.ts | 3 - tests/integration/hrv-rmssd-ingest.test.ts | 4 - .../idempotency-body-fingerprint.test.ts | 4 - .../idempotency-record-scoped-cell.test.ts | 4 - tests/integration/idempotency-replay.test.ts | 4 - .../insights-comprehensive-cache.test.ts | 4 - tests/integration/insights-feedback.test.ts | 4 - .../insights-provider-chain-put.test.ts | 4 - .../insights-provider-chain.test.ts | 4 - .../insights-status-real-text.test.ts | 4 - .../intake-backfill-missed-slot.test.ts | 4 - .../intake-late-take-future-slot.test.ts | 4 - .../intake-scheduled-for-targeting.test.ts | 4 - tests/integration/intake-slot-dedup.test.ts | 4 - tests/integration/integration-status.test.ts | 4 - .../integration-sync-escalation.test.ts | 4 - .../integration/integration-watchdog.test.ts | 4 - .../lab-source-reference-range.test.ts | 4 - tests/integration/lab-unit-mismatch.test.ts | 4 - tests/integration/labs-analyte-key.test.ts | 4 - .../labs-overview-long-history.test.ts | 4 - .../integration/labs-unit-conversion.test.ts | 4 - .../large-account-google-health.test.ts | 4 - .../large-backup-round-trip.test.ts | 4 - tests/integration/life-circumstances.test.ts | 4 - .../list-pagination-tiebreaker.test.ts | 4 - tests/integration/local-day-keys.test.ts | 4 - .../login-insecure-transport.test.ts | 4 - .../integration/managed-profile-edit.test.ts | 4 - .../managed-profile-handover-sso.test.ts | 4 - ...anaged-profile-handover-stepup-sso.test.ts | 4 - .../managed-profile-handover.test.ts | 4 - .../managed-profile-lifecycle.test.ts | 4 - .../managed-profile-oauth-connect.test.ts | 4 - .../managed-profile-passkey-step-up.test.ts | 4 - .../managed-profile-surface.test.ts | 4 - .../managed-record-capabilities.test.ts | 4 - .../managed-record-modules.test.ts | 4 - tests/integration/manual-workout-edit.test.ts | 4 - .../mcp-metric-series-source-snapshot.test.ts | 3 - tests/integration/measurement-rollups.test.ts | 4 - .../measurement-soft-delete.test.ts | 4 - .../measurement-source-write-smoke.test.ts | 4 - .../measurements-aggregate-daily.test.ts | 4 - .../measurements-batch-delete.test.ts | 4 - .../measurements-batch-perf.test.ts | 4 - ...asurements-batch-set-insert-golden.test.ts | 4 - tests/integration/measurements-batch.test.ts | 4 - .../measurements-day-grouped-paging.test.ts | 4 - .../measurements-duplicate-conflict.test.ts | 4 - .../measurements-named-sleep-stage.test.ts | 4 - .../measurements-sleep-list.test.ts | 4 - .../medication-auto-mark-taken.test.ts | 3 - .../medication-cadence-worker.test.ts | 4 - .../integration/medication-course-end.test.ts | 3 - tests/integration/medication-courses.test.ts | 4 - .../medication-custom-categories.test.ts | 4 - ...medication-extra-dose-apple-health.test.ts | 4 - .../medication-intake-tracking.test.ts | 3 - .../medication-put-course-race.test.ts | 4 - ...dication-rolling-overdue-consumers.test.ts | 4 - .../medication-write-idempotency.test.ts | 4 - .../medications-intake-bulk.test.ts | 4 - .../integration/mental-health-wording.test.ts | 4 - tests/integration/mfa-login-flow.test.ts | 4 - .../mood-backup-round-trip.test.ts | 4 - .../integration/mood-context-linkage.test.ts | 4 - tests/integration/mood-context.test.ts | 4 - tests/integration/mood-entries-bulk.test.ts | 4 - .../mood-entries-external-id.test.ts | 4 - .../mood-entries-note-column.test.ts | 4 - .../mood-insights-crosstab.test.ts | 4 - .../integration/mood-prognosis-ladder.test.ts | 4 - tests/integration/mood-tag-groups.test.ts | 4 - tests/integration/mood-writers-agree.test.ts | 4 - .../notification-action-mood.test.ts | 4 - .../notification-event-defaults.test.ts | 4 - ...notification-global-channel-switch.test.ts | 4 - tests/integration/nutrients-batch.test.ts | 4 - .../oauth-callback-single-use.test.ts | 4 - .../offhost-purge-on-deletion.test.ts | 4 - tests/integration/oidc-pending-link.test.ts | 4 - tests/integration/oidc-reproof.test.ts | 4 - tests/integration/onboarding-needs.test.ts | 4 - .../integration/openapi-get-contract.test.ts | 4 - .../openapi-response-shape.test.ts | 4 - tests/integration/oura-backfill.test.ts | 4 - .../partial-unique-live-rows.test.ts | 4 - tests/integration/passkey-register.test.ts | 4 - .../pr-detection-cumulative-paged.test.ts | 4 - .../pr-detection-end-to-end.test.ts | 4 - .../practitioner-contact-encryption.test.ts | 4 - .../practitioner-specialty-search.test.ts | 4 - .../integration/profile-display-name.test.ts | 4 - tests/integration/provider-dense-fold.test.ts | 4 - tests/integration/provider-pause.test.ts | 4 - .../integration/pulse-day-mean-zones.test.ts | 4 - .../integration/pulse-hourly-mean-day.test.ts | 4 - .../pulse-window-mean-readers.test.ts | 4 - .../query-budget-hot-routes.test.ts | 4 - .../rate-limit-429-headers.test.ts | 4 - tests/integration/recent-proof.test.ts | 4 - tests/integration/record-csv-import.test.ts | 3 - tests/integration/record-search.test.ts | 4 - .../record-session-fence-idempotency.test.ts | 4 - .../integration/record-session-fence.test.ts | 4 - ...ecord-settings-integrations-health.test.ts | 4 - tests/integration/recovery-kit.test.ts | 4 - .../recovery-readiness-dump-retention.test.ts | 4 - .../recovery-readiness-notify.test.ts | 4 - .../relation-links-portable-restore.test.ts | 4 - .../request-clock-windows-session-tz.test.ts | 4 - tests/integration/rest-mode-chronic.test.ts | 4 - .../restore-consolidation-deadlock.test.ts | 4 - .../rollup-concurrent-recompute.test.ts | 4 - .../integration/rollup-coverage-probe.test.ts | 3 - .../rollup-keyed-refold-session-tz.test.ts | 4 - .../rollup-per-day-canonical.test.ts | 3 - ...llup-regression-parity.integration.test.ts | 4 - tests/integration/score-history.test.ts | 4 - .../second-factor-ends-other-sessions.test.ts | 4 - .../integration/series-source-ladder.test.ts | 4 - tests/integration/server-cache-routes.test.ts | 4 - .../settings-account-delete.test.ts | 4 - tests/integration/settings-data-wipe.test.ts | 4 - .../settings-erasure-bearer-step-up.test.ts | 4 - .../integration/settings-prefs-routes.test.ts | 4 - .../integration/share-link-documents.test.ts | 4 - .../sharing-account-access.test.ts | 4 - tests/integration/sharing-audit-actor.test.ts | 4 - .../sharing-delegable-routes.test.ts | 4 - .../sharing-delegable-writes.test.ts | 4 - tests/integration/sharing-front-door.test.ts | 4 - .../sharing-idempotency-revocation.test.ts | 4 - tests/integration/sharing-lifecycle.test.ts | 4 - .../sharing-manage-handler-matrix.test.ts | 4 - .../sharing-provider-origin.test.ts | 4 - tests/integration/sharing-scope-fence.test.ts | 4 - .../sleep-entry-hours-round-trip.test.ts | 4 - .../sleep-night-sql-parity.test.ts | 4 - .../soft-delete-copy-parity.test.ts | 4 - tests/integration/soft-delete-purge.test.ts | 4 - .../source-aware-rollups.integration.test.ts | 4 - .../source-ladder-new-sources.test.ts | 4 - .../source-priority-two-axis.test.ts | 4 - tests/integration/source-provenance.test.ts | 4 - tests/integration/step-up-elevation.test.ts | 4 - .../integration/supplement-nutrients.test.ts | 3 - ...itched-session-destructive-refusal.test.ts | 4 - tests/integration/symptoms.test.ts | 4 - tests/integration/sync-changes.test.ts | 4 - .../sync-reset-after-restore.test.ts | 4 - tests/integration/sync-state.test.ts | 4 - .../tiered-context-local-day.test.ts | 4 - tests/integration/timeline-lab-series.test.ts | 4 - .../timeline-rollup-boundary-zone.test.ts | 4 - .../integration/timeline-sleep-nights.test.ts | 4 - tests/integration/timezone-per-user.test.ts | 4 - .../tombstone-cleanup-restore-lock.test.ts | 4 - tests/integration/user-avatar.test.ts | 4 - .../vaccination-booster-mint.test.ts | 4 - .../vaccination-booster-satisfy.test.ts | 4 - ...accination-booster-scoped-delegate.test.ts | 4 - tests/integration/vaccination-custom.test.ts | 4 - .../vaccination-managed-profile.test.ts | 4 - tests/integration/vaccination-renewal.test.ts | 4 - .../visit-link-picker-long-history.test.ts | 4 - .../visit-link-picker-period-search.test.ts | 4 - .../integration/whoop-connect-ticket.test.ts | 4 - tests/integration/withings-oauth-flow.test.ts | 4 - tests/integration/withings-oauth.test.ts | 4 - .../integration/withings-sync-volume.test.ts | 4 - .../integration/workout-batch-create.test.ts | 4 - .../integration/workout-batch-enrich.test.ts | 4 - tests/integration/workout-batch-race.test.ts | 4 - .../workout-detail-hr-series.test.ts | 4 - .../integration/workout-manual-entry.test.ts | 4 - tests/integration/write-meter.test.ts | 4 - 537 files changed, 3 insertions(+), 2326 deletions(-) delete mode 100644 src/lib/db-compat.ts diff --git a/src/__tests__/api/analytics/since-cap.test.ts b/src/__tests__/api/analytics/since-cap.test.ts index 1c9c0f996..0bfd4adb1 100644 --- a/src/__tests__/api/analytics/since-cap.test.ts +++ b/src/__tests__/api/analytics/since-cap.test.ts @@ -46,10 +46,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); // The slim slice now strips disabled-module types before answering, from diff --git a/src/__tests__/api/auth/login/mfa-branch.test.ts b/src/__tests__/api/auth/login/mfa-branch.test.ts index 1b9a644e1..3fdb91dd0 100644 --- a/src/__tests__/api/auth/login/mfa-branch.test.ts +++ b/src/__tests__/api/auth/login/mfa-branch.test.ts @@ -23,9 +23,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/auth/hmac", () => ({ hashToken: vi.fn(() => "h") })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); vi.mock("@/lib/rate-limit", () => ({ checkAuthSurfaceRateLimit: vi.fn().mockResolvedValue({ diff --git a/src/__tests__/api/auth/mfa/verify.test.ts b/src/__tests__/api/auth/mfa/verify.test.ts index 094614336..d23956b80 100644 --- a/src/__tests__/api/auth/mfa/verify.test.ts +++ b/src/__tests__/api/auth/mfa/verify.test.ts @@ -3,9 +3,6 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; vi.mock("@/lib/db", () => ({ prisma: { user: { findUnique: vi.fn() } }, })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), diff --git a/src/__tests__/api/auth/registration-status/route.test.ts b/src/__tests__/api/auth/registration-status/route.test.ts index 38f04e448..4eac4cf0c 100644 --- a/src/__tests__/api/auth/registration-status/route.test.ts +++ b/src/__tests__/api/auth/registration-status/route.test.ts @@ -37,10 +37,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); vi.mock("next/headers", () => ({ diff --git a/src/__tests__/baselines/version-stamp-comments.json b/src/__tests__/baselines/version-stamp-comments.json index 69c0ee030..b95f224f2 100644 --- a/src/__tests__/baselines/version-stamp-comments.json +++ b/src/__tests__/baselines/version-stamp-comments.json @@ -1959,7 +1959,6 @@ "lib/day/values.ts": 1, "lib/day/visit-preparation.ts": 1, "lib/day/wire-schemas.ts": 1, - "lib/db-compat.ts": 1, "lib/devices/__tests__/revoke.test.ts": 1, "lib/devices/revoke.ts": 1, "lib/doctor-report-helpers.ts": 1, diff --git a/src/__tests__/sql-now-utc-guard.test.ts b/src/__tests__/sql-now-utc-guard.test.ts index 4dca58fec..8875c0007 100644 --- a/src/__tests__/sql-now-utc-guard.test.ts +++ b/src/__tests__/sql-now-utc-guard.test.ts @@ -47,10 +47,6 @@ const ALLOWED: Record = { hits: 1, why: "pg-boss tables are timestamptz", }, - "src/lib/db-compat.ts": { - hits: 1, - why: "DDL default on a timestamptz column", - }, }; const SQL_CLOCK = diff --git a/src/__tests__/tz-day-paths-harness.test.ts b/src/__tests__/tz-day-paths-harness.test.ts index edd870974..6be62ece6 100644 --- a/src/__tests__/tz-day-paths-harness.test.ts +++ b/src/__tests__/tz-day-paths-harness.test.ts @@ -65,9 +65,6 @@ vi.mock("@/lib/rollups/measurement-read", () => ({ vi.mock("@/lib/analytics/mood-series", () => ({ buildMoodDailySeries: vi.fn(), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/admin/import-apple-health-export/__tests__/route.test.ts b/src/app/api/admin/import-apple-health-export/__tests__/route.test.ts index fcb453ed6..dcbada60d 100644 --- a/src/app/api/admin/import-apple-health-export/__tests__/route.test.ts +++ b/src/app/api/admin/import-apple-health-export/__tests__/route.test.ts @@ -12,9 +12,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/admin/maintenance/measurements/__tests__/status.test.ts b/src/app/api/admin/maintenance/measurements/__tests__/status.test.ts index b4cb3012e..26de3e9e7 100644 --- a/src/app/api/admin/maintenance/measurements/__tests__/status.test.ts +++ b/src/app/api/admin/maintenance/measurements/__tests__/status.test.ts @@ -12,9 +12,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/admin/maintenance/pulse-day-mean/__tests__/route.test.ts b/src/app/api/admin/maintenance/pulse-day-mean/__tests__/route.test.ts index 26c89266e..e3c764f42 100644 --- a/src/app/api/admin/maintenance/pulse-day-mean/__tests__/route.test.ts +++ b/src/app/api/admin/maintenance/pulse-day-mean/__tests__/route.test.ts @@ -18,9 +18,6 @@ vi.mock("@/lib/auth/audit", () => ({ })); vi.mock("@/lib/jobs/boss-instance", () => ({ getGlobalBoss: vi.fn() })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/admin/settings/assistant-flags/__tests__/route.test.ts b/src/app/api/admin/settings/assistant-flags/__tests__/route.test.ts index 350c54de8..c23eb48c5 100644 --- a/src/app/api/admin/settings/assistant-flags/__tests__/route.test.ts +++ b/src/app/api/admin/settings/assistant-flags/__tests__/route.test.ts @@ -15,9 +15,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/admin/settings/module-availability/__tests__/route.test.ts b/src/app/api/admin/settings/module-availability/__tests__/route.test.ts index 846061632..fa2f7278e 100644 --- a/src/app/api/admin/settings/module-availability/__tests__/route.test.ts +++ b/src/app/api/admin/settings/module-availability/__tests__/route.test.ts @@ -15,9 +15,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/analytics/__tests__/route.test.ts b/src/app/api/analytics/__tests__/route.test.ts index 068af6252..c04a111c9 100644 --- a/src/app/api/analytics/__tests__/route.test.ts +++ b/src/app/api/analytics/__tests__/route.test.ts @@ -75,10 +75,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/insights/correlation-patterns", () => ({ PATTERN_FAMILIES: { fixed: "FIXED" }, syncAcceptedPatterns: vi.fn().mockResolvedValue(new Map()), diff --git a/src/app/api/auth/login/__tests__/constant-work.test.ts b/src/app/api/auth/login/__tests__/constant-work.test.ts index a41edac7b..39446e467 100644 --- a/src/app/api/auth/login/__tests__/constant-work.test.ts +++ b/src/app/api/auth/login/__tests__/constant-work.test.ts @@ -58,10 +58,6 @@ vi.mock("@/lib/rate-limit", () => ({ refundRateLimit: vi.fn(async () => undefined), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/auth/hmac", () => ({ hashToken: vi.fn(() => "aa".repeat(32)), })); diff --git a/src/app/api/auth/login/__tests__/native-token.test.ts b/src/app/api/auth/login/__tests__/native-token.test.ts index 4478b89eb..c0ca5e861 100644 --- a/src/app/api/auth/login/__tests__/native-token.test.ts +++ b/src/app/api/auth/login/__tests__/native-token.test.ts @@ -61,10 +61,6 @@ vi.mock("@/lib/rate-limit", () => ({ refundRateLimit: vi.fn(async () => undefined), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/auth/hmac", () => ({ hashToken: vi.fn((raw: string) => `hashed:${raw}`), })); @@ -90,7 +86,6 @@ import { checkAuthSurfaceRateLimit, rateLimitHeaders, } from "@/lib/rate-limit"; -import { ensureDbCompatibility } from "@/lib/db-compat"; import { auditLog } from "@/lib/auth/audit"; import { createSession } from "@/lib/auth/session"; @@ -136,7 +131,6 @@ beforeEach(() => { ip: "203.0.113.1", } as never); vi.mocked(rateLimitHeaders).mockReturnValue({} as never); - vi.mocked(ensureDbCompatibility).mockResolvedValue(undefined); vi.mocked(auditLog).mockResolvedValue(undefined as never); vi.mocked(createSession).mockResolvedValue("session-id"); }); diff --git a/src/app/api/auth/login/__tests__/route.test.ts b/src/app/api/auth/login/__tests__/route.test.ts index e9c8e3298..5de021855 100644 --- a/src/app/api/auth/login/__tests__/route.test.ts +++ b/src/app/api/auth/login/__tests__/route.test.ts @@ -52,10 +52,6 @@ vi.mock("@/lib/rate-limit", () => ({ rateLimitHeaders: vi.fn(() => ({})), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/auth/hmac", () => ({ // Return a deterministic hex string that does NOT echo the raw // value — mirrors the real HMAC-SHA256 shape. The audit row's @@ -88,7 +84,6 @@ import { checkAuthSurfaceRateLimit, rateLimitHeaders, } from "@/lib/rate-limit"; -import { ensureDbCompatibility } from "@/lib/db-compat"; const TYPED_EMAIL = "leaked-identifier@example.com"; @@ -117,7 +112,6 @@ beforeEach(() => { ip: "1.2.3.4", } as never); vi.mocked(rateLimitHeaders).mockReturnValue({} as never); - vi.mocked(ensureDbCompatibility).mockResolvedValue(undefined); }); describe("POST /api/auth/login — auth.login.failed audit row (H-1)", () => { diff --git a/src/app/api/auth/login/__tests__/trusted-device.test.ts b/src/app/api/auth/login/__tests__/trusted-device.test.ts index 95b7a3492..49d89ff87 100644 --- a/src/app/api/auth/login/__tests__/trusted-device.test.ts +++ b/src/app/api/auth/login/__tests__/trusted-device.test.ts @@ -52,10 +52,6 @@ vi.mock("@/lib/rate-limit", () => ({ refundRateLimit: vi.fn(async () => undefined), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/auth/hmac", () => ({ hashToken: vi.fn(() => "h") })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); vi.mock("next/headers", () => ({ diff --git a/src/app/api/auth/login/route.ts b/src/app/api/auth/login/route.ts index 4c8927f91..0f11f9daf 100644 --- a/src/app/api/auth/login/route.ts +++ b/src/app/api/auth/login/route.ts @@ -11,7 +11,6 @@ import { } from "@/lib/api-response"; import { checkAuthSurfaceRateLimit, rateLimitHeaders } from "@/lib/rate-limit"; import { beginAccountLoginAttempt } from "@/lib/auth/login-throttle"; -import { ensureDbCompatibility } from "@/lib/db-compat"; import { NextRequest, NextResponse } from "next/server"; import { apiHandler } from "@/lib/api-handler"; import { annotate } from "@/lib/logging/context"; @@ -59,8 +58,6 @@ export const POST = apiHandler(async (request: NextRequest) => { ); } - await ensureDbCompatibility(); - const { data: body, error: jsonError } = await safeJson(request, { maxBytes: 64 * 1024, }); diff --git a/src/app/api/auth/mfa/verify/route.ts b/src/app/api/auth/mfa/verify/route.ts index 9ee262970..25cb920de 100644 --- a/src/app/api/auth/mfa/verify/route.ts +++ b/src/app/api/auth/mfa/verify/route.ts @@ -27,7 +27,6 @@ import { apiHandler } from "@/lib/api-handler"; import { annotate } from "@/lib/logging/context"; import { auditLog } from "@/lib/auth/audit"; import { prisma } from "@/lib/db"; -import { ensureDbCompatibility } from "@/lib/db-compat"; import { checkAuthSurfaceRateLimit, rateLimitHeaders } from "@/lib/rate-limit"; import { finishLogin } from "@/lib/auth/login-response"; import { @@ -59,8 +58,6 @@ export const POST = apiHandler(async (request: NextRequest) => { }); } - await ensureDbCompatibility(); - const { data: body, error: jsonError } = await safeJson(request, { maxBytes: 16 * 1024, }); diff --git a/src/app/api/auth/mfa/webauthn/verify/__tests__/route.test.ts b/src/app/api/auth/mfa/webauthn/verify/__tests__/route.test.ts index fa86f7987..cf7f1ddf8 100644 --- a/src/app/api/auth/mfa/webauthn/verify/__tests__/route.test.ts +++ b/src/app/api/auth/mfa/webauthn/verify/__tests__/route.test.ts @@ -14,10 +14,6 @@ vi.mock("@/lib/db", () => ({ prisma: { user: { findUnique: vi.fn() } }, })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/rate-limit", () => ({ checkAuthSurfaceRateLimit: vi.fn(), rateLimitHeaders: vi.fn(() => ({})), diff --git a/src/app/api/auth/mfa/webauthn/verify/options/route.ts b/src/app/api/auth/mfa/webauthn/verify/options/route.ts index 2cd02aa6d..c6c8edd76 100644 --- a/src/app/api/auth/mfa/webauthn/verify/options/route.ts +++ b/src/app/api/auth/mfa/webauthn/verify/options/route.ts @@ -16,7 +16,6 @@ import { apiHandler } from "@/lib/api-handler"; import { apiError, apiSuccess } from "@/lib/api-response"; import { parseJsonBody } from "@/lib/api/body"; import { annotate } from "@/lib/logging/context"; -import { ensureDbCompatibility } from "@/lib/db-compat"; import { checkAuthSurfaceRateLimit, rateLimitHeaders } from "@/lib/rate-limit"; import { loadActiveChallenge } from "@/lib/auth/mfa/challenge"; import { createMfaAuthenticationOptions } from "@/lib/auth/mfa/webauthn"; @@ -37,8 +36,6 @@ export const POST = apiHandler(async (request: NextRequest) => { }); } - await ensureDbCompatibility(); - const parsed = await parseJsonBody(request, mfaWebauthnLoginOptionsSchema, { maxBytes: 16 * 1024, message: "Invalid request", diff --git a/src/app/api/auth/mfa/webauthn/verify/route.ts b/src/app/api/auth/mfa/webauthn/verify/route.ts index d548c9c08..a19585bfe 100644 --- a/src/app/api/auth/mfa/webauthn/verify/route.ts +++ b/src/app/api/auth/mfa/webauthn/verify/route.ts @@ -25,7 +25,6 @@ import { import { annotate } from "@/lib/logging/context"; import { auditLog } from "@/lib/auth/audit"; import { prisma } from "@/lib/db"; -import { ensureDbCompatibility } from "@/lib/db-compat"; import { checkAuthSurfaceRateLimit, rateLimitHeaders } from "@/lib/rate-limit"; import { finishLogin } from "@/lib/auth/login-response"; import { @@ -57,8 +56,6 @@ export const POST = apiHandler(async (request: NextRequest) => { }); } - await ensureDbCompatibility(); - const { data: body, error: jsonError } = await safeJson(request, { maxBytes: 64 * 1024, }); diff --git a/src/app/api/auth/native/token/__tests__/route.test.ts b/src/app/api/auth/native/token/__tests__/route.test.ts index ea0f1361f..b1f733168 100644 --- a/src/app/api/auth/native/token/__tests__/route.test.ts +++ b/src/app/api/auth/native/token/__tests__/route.test.ts @@ -12,10 +12,6 @@ vi.mock("@/lib/db", () => ({ prisma: { user: { findUnique: vi.fn() } }, })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/rate-limit", () => ({ checkAuthSurfaceRateLimit: vi.fn(), rateLimitHeaders: vi.fn(() => ({})), diff --git a/src/app/api/auth/native/token/route.ts b/src/app/api/auth/native/token/route.ts index 606cfba38..f0199e82a 100644 --- a/src/app/api/auth/native/token/route.ts +++ b/src/app/api/auth/native/token/route.ts @@ -26,7 +26,6 @@ import { apiHandler } from "@/lib/api-handler"; import { annotate } from "@/lib/logging/context"; import { auditLog } from "@/lib/auth/audit"; import { prisma } from "@/lib/db"; -import { ensureDbCompatibility } from "@/lib/db-compat"; import { checkAuthSurfaceRateLimit, rateLimitHeaders } from "@/lib/rate-limit"; import { isCookielessNativeCaller } from "@/lib/auth/native-client"; import { isNativeClientRequest } from "@/lib/auth/issue-token"; @@ -74,8 +73,6 @@ export const POST = apiHandler(async (request: NextRequest) => { return invalidCode(); } - await ensureDbCompatibility(); - // (3) Body — bounded, then the exact wire contract. const parsed = await parseJsonBody(request, nativeHandoffTokenSchema, { maxBytes: 16 * 1024, diff --git a/src/app/api/auth/oidc/native/token/__tests__/route.test.ts b/src/app/api/auth/oidc/native/token/__tests__/route.test.ts index 6fc48da0a..7b5daa67a 100644 --- a/src/app/api/auth/oidc/native/token/__tests__/route.test.ts +++ b/src/app/api/auth/oidc/native/token/__tests__/route.test.ts @@ -9,10 +9,6 @@ vi.mock("@/lib/db", () => ({ prisma: { user: { findUnique: vi.fn() } }, })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/rate-limit", () => ({ checkAuthSurfaceRateLimit: vi.fn(), rateLimitHeaders: vi.fn(() => ({})), diff --git a/src/app/api/auth/oidc/native/token/route.ts b/src/app/api/auth/oidc/native/token/route.ts index b3d480082..63b8ef1e9 100644 --- a/src/app/api/auth/oidc/native/token/route.ts +++ b/src/app/api/auth/oidc/native/token/route.ts @@ -29,7 +29,6 @@ import { apiHandler } from "@/lib/api-handler"; import { annotate } from "@/lib/logging/context"; import { auditLog } from "@/lib/auth/audit"; import { prisma } from "@/lib/db"; -import { ensureDbCompatibility } from "@/lib/db-compat"; import { checkAuthSurfaceRateLimit, rateLimitHeaders } from "@/lib/rate-limit"; import { isCookielessNativeCaller } from "@/lib/auth/native-client"; import { isNativeClientRequest } from "@/lib/auth/issue-token"; @@ -80,8 +79,6 @@ export const POST = apiHandler(async (request: NextRequest) => { return invalidCode(); } - await ensureDbCompatibility(); - // (3) Body — bounded, then the exact wire contract. const parsed = await parseJsonBody(request, oidcNativeTokenSchema, { maxBytes: 16 * 1024, diff --git a/src/app/api/auth/passkey/login-verify/__tests__/native-token.test.ts b/src/app/api/auth/passkey/login-verify/__tests__/native-token.test.ts index 8256d35f2..b4216edfe 100644 --- a/src/app/api/auth/passkey/login-verify/__tests__/native-token.test.ts +++ b/src/app/api/auth/passkey/login-verify/__tests__/native-token.test.ts @@ -37,10 +37,6 @@ vi.mock("@/lib/rate-limit", () => ({ checkAuthSurfaceRateLimit: vi.fn(), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/auth/hmac", () => ({ hashToken: vi.fn((raw: string) => `hashed:${raw}`), })); diff --git a/src/app/api/auth/passkey/login-verify/__tests__/oidc-only.test.ts b/src/app/api/auth/passkey/login-verify/__tests__/oidc-only.test.ts index 5d451530f..72263e6ba 100644 --- a/src/app/api/auth/passkey/login-verify/__tests__/oidc-only.test.ts +++ b/src/app/api/auth/passkey/login-verify/__tests__/oidc-only.test.ts @@ -35,10 +35,6 @@ vi.mock("@/lib/rate-limit", () => ({ checkAuthSurfaceRateLimit: vi.fn(), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/auth/hmac", () => ({ hashToken: vi.fn((raw: string) => `hashed:${raw}`), })); diff --git a/src/app/api/auth/passkey/login-verify/__tests__/refusals.test.ts b/src/app/api/auth/passkey/login-verify/__tests__/refusals.test.ts index a41950f37..b7ff7fee6 100644 --- a/src/app/api/auth/passkey/login-verify/__tests__/refusals.test.ts +++ b/src/app/api/auth/passkey/login-verify/__tests__/refusals.test.ts @@ -31,10 +31,6 @@ vi.mock("@/lib/rate-limit", () => ({ checkAuthSurfaceRateLimit: vi.fn(), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); vi.mock("next/headers", () => ({ diff --git a/src/app/api/auth/passkey/login-verify/route.ts b/src/app/api/auth/passkey/login-verify/route.ts index 76c6487c4..b65593baf 100644 --- a/src/app/api/auth/passkey/login-verify/route.ts +++ b/src/app/api/auth/passkey/login-verify/route.ts @@ -9,7 +9,6 @@ import { recordSignInDevice } from "@/lib/auth/login-alert"; import { auditLog } from "@/lib/auth/audit"; import { apiSuccess, apiError, safeJson } from "@/lib/api-response"; import { prisma } from "@/lib/db"; -import { ensureDbCompatibility } from "@/lib/db-compat"; import { checkAuthSurfaceRateLimit } from "@/lib/rate-limit"; import { NextRequest } from "next/server"; import { apiHandler } from "@/lib/api-handler"; @@ -71,8 +70,6 @@ export const POST = apiHandler(async (request: NextRequest) => { }); } - await ensureDbCompatibility(); - // v1.4.43 — tighter shared bucket on trust-chain misconfig. const rl = await checkAuthSurfaceRateLimit( request, diff --git a/src/app/api/auth/password/__tests__/route.test.ts b/src/app/api/auth/password/__tests__/route.test.ts index 5cee4d7cd..ab15ac964 100644 --- a/src/app/api/auth/password/__tests__/route.test.ts +++ b/src/app/api/auth/password/__tests__/route.test.ts @@ -63,9 +63,6 @@ vi.mock("@/lib/auth/hibp", () => ({ })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/auth/register/__tests__/route.test.ts b/src/app/api/auth/register/__tests__/route.test.ts index 00611fffc..3b435bf4b 100644 --- a/src/app/api/auth/register/__tests__/route.test.ts +++ b/src/app/api/auth/register/__tests__/route.test.ts @@ -72,9 +72,6 @@ vi.mock("@/lib/tz/resolver", () => ({ resolveServerDefaultTimezone: vi.fn().mockResolvedValue("Europe/Berlin"), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), diff --git a/src/app/api/auth/register/route.ts b/src/app/api/auth/register/route.ts index 6e374033b..a50be77c2 100644 --- a/src/app/api/auth/register/route.ts +++ b/src/app/api/auth/register/route.ts @@ -12,7 +12,6 @@ import { auditLog } from "@/lib/auth/audit"; import { apiSuccess, apiError } from "@/lib/api-response"; import { parseJsonBody } from "@/lib/api/body"; import { checkAuthSurfaceRateLimit, rateLimitHeaders } from "@/lib/rate-limit"; -import { ensureDbCompatibility } from "@/lib/db-compat"; import { NextRequest, NextResponse } from "next/server"; import { apiHandler } from "@/lib/api-handler"; import { annotate } from "@/lib/logging/context"; @@ -79,8 +78,6 @@ export const POST = apiHandler(async (request: NextRequest) => { ); } - await ensureDbCompatibility(); - const userCount = await prisma.user.count(); // Check if registration is enabled. diff --git a/src/app/api/biomarkers/[id]/__tests__/delete-record-auth.test.ts b/src/app/api/biomarkers/[id]/__tests__/delete-record-auth.test.ts index 3b9cb8f7a..468bb14ea 100644 --- a/src/app/api/biomarkers/[id]/__tests__/delete-record-auth.test.ts +++ b/src/app/api/biomarkers/[id]/__tests__/delete-record-auth.test.ts @@ -33,9 +33,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/cache/invalidate", () => ({ invalidateUserHealthScore: vi.fn(), })); diff --git a/src/app/api/biomarkers/[id]/__tests__/route.test.ts b/src/app/api/biomarkers/[id]/__tests__/route.test.ts index 46a840f1f..bf781ebbc 100644 --- a/src/app/api/biomarkers/[id]/__tests__/route.test.ts +++ b/src/app/api/biomarkers/[id]/__tests__/route.test.ts @@ -32,10 +32,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/labs/biomarker-store", () => ({ encryptContextToBytes: vi.fn(() => new Uint8Array([1])), decryptContextFromBytes: vi.fn(() => "ctx"), diff --git a/src/app/api/biomarkers/__tests__/error-contract.test.ts b/src/app/api/biomarkers/__tests__/error-contract.test.ts index dcbfe3362..4f4e1506b 100644 --- a/src/app/api/biomarkers/__tests__/error-contract.test.ts +++ b/src/app/api/biomarkers/__tests__/error-contract.test.ts @@ -31,9 +31,6 @@ vi.mock("@/lib/labs/biomarker-store", () => ({ encryptContextToBytes: vi.fn(), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/c/[token]/unlock/__tests__/route.test.ts b/src/app/api/c/[token]/unlock/__tests__/route.test.ts index e7cf2ef56..317199c29 100644 --- a/src/app/api/c/[token]/unlock/__tests__/route.test.ts +++ b/src/app/api/c/[token]/unlock/__tests__/route.test.ts @@ -37,10 +37,6 @@ vi.mock("@/lib/rate-limit", () => ({ checkAuthSurfaceRateLimit: vi.fn(), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - import { POST } from "../route"; import { resolveShareGateState } from "@/lib/clinician-share/resolve-share-token"; import { verifyPassphrase } from "@/lib/clinician-share/passphrase"; diff --git a/src/app/api/coach/plans/__tests__/route.test.ts b/src/app/api/coach/plans/__tests__/route.test.ts index 6738a2a44..6d3885d21 100644 --- a/src/app/api/coach/plans/__tests__/route.test.ts +++ b/src/app/api/coach/plans/__tests__/route.test.ts @@ -54,10 +54,6 @@ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn(), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/logging/context", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, annotate: vi.fn() }; diff --git a/src/app/api/coach/reminders/__tests__/route.test.ts b/src/app/api/coach/reminders/__tests__/route.test.ts index 2bdea457f..9e3391106 100644 --- a/src/app/api/coach/reminders/__tests__/route.test.ts +++ b/src/app/api/coach/reminders/__tests__/route.test.ts @@ -48,9 +48,6 @@ vi.mock("@/lib/ai/coach/bytes-codec", () => ({ encryptToBytes: vi.fn((s: string) => new Uint8Array(Buffer.from(`enc:${s}`))), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/logging/context", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, annotate: vi.fn() }; diff --git a/src/app/api/coach/suggested-actions/__tests__/route.test.ts b/src/app/api/coach/suggested-actions/__tests__/route.test.ts index d1f49c9a0..e629d2c44 100644 --- a/src/app/api/coach/suggested-actions/__tests__/route.test.ts +++ b/src/app/api/coach/suggested-actions/__tests__/route.test.ts @@ -48,9 +48,6 @@ vi.mock("@/lib/measurement-reminders/dto", () => ({ toMeasurementReminderDto: vi.fn((r: { id: string }) => ({ id: r.id })), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/logging/context", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, annotate: vi.fn() }; diff --git a/src/app/api/consent/ai/__tests__/route.test.ts b/src/app/api/consent/ai/__tests__/route.test.ts index c3b9b9ba1..bcfb7341f 100644 --- a/src/app/api/consent/ai/__tests__/route.test.ts +++ b/src/app/api/consent/ai/__tests__/route.test.ts @@ -36,10 +36,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/consent/ai/latest/__tests__/route.test.ts b/src/app/api/consent/ai/latest/__tests__/route.test.ts index fcbf29309..c34d9cc2c 100644 --- a/src/app/api/consent/ai/latest/__tests__/route.test.ts +++ b/src/app/api/consent/ai/latest/__tests__/route.test.ts @@ -40,10 +40,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/consent/ai/web/__tests__/route.test.ts b/src/app/api/consent/ai/web/__tests__/route.test.ts index 4b101efd5..d6ab7b671 100644 --- a/src/app/api/consent/ai/web/__tests__/route.test.ts +++ b/src/app/api/consent/ai/web/__tests__/route.test.ts @@ -8,10 +8,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/custom-metrics/[id]/__tests__/route.test.ts b/src/app/api/custom-metrics/[id]/__tests__/route.test.ts index bc70c9d62..76d08eb28 100644 --- a/src/app/api/custom-metrics/[id]/__tests__/route.test.ts +++ b/src/app/api/custom-metrics/[id]/__tests__/route.test.ts @@ -16,10 +16,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/custom-metrics/[id]/entries/__tests__/note-encryption.test.ts b/src/app/api/custom-metrics/[id]/entries/__tests__/note-encryption.test.ts index 89609be1b..ec03e0a59 100644 --- a/src/app/api/custom-metrics/[id]/entries/__tests__/note-encryption.test.ts +++ b/src/app/api/custom-metrics/[id]/entries/__tests__/note-encryption.test.ts @@ -37,10 +37,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/idempotency", () => ({ withIdempotency: (fn: unknown) => fn, })); diff --git a/src/app/api/custom-metrics/[id]/entries/__tests__/route.test.ts b/src/app/api/custom-metrics/[id]/entries/__tests__/route.test.ts index 2f02eb2b3..a416eb63e 100644 --- a/src/app/api/custom-metrics/[id]/entries/__tests__/route.test.ts +++ b/src/app/api/custom-metrics/[id]/entries/__tests__/route.test.ts @@ -32,10 +32,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/idempotency", () => ({ withIdempotency: (fn: unknown) => fn, })); diff --git a/src/app/api/custom-metrics/[id]/entries/__tests__/soft-delete-restore.test.ts b/src/app/api/custom-metrics/[id]/entries/__tests__/soft-delete-restore.test.ts index 9794e159b..1cb0edf37 100644 --- a/src/app/api/custom-metrics/[id]/entries/__tests__/soft-delete-restore.test.ts +++ b/src/app/api/custom-metrics/[id]/entries/__tests__/soft-delete-restore.test.ts @@ -30,10 +30,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/cache/invalidate", () => ({ invalidateUserCorrelationPatterns: vi.fn(), })); diff --git a/src/app/api/custom-metrics/__tests__/route.test.ts b/src/app/api/custom-metrics/__tests__/route.test.ts index 50ee33ccf..1a9a0492c 100644 --- a/src/app/api/custom-metrics/__tests__/route.test.ts +++ b/src/app/api/custom-metrics/__tests__/route.test.ts @@ -22,10 +22,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/idempotency", () => ({ withIdempotency: (fn: unknown) => fn, })); diff --git a/src/app/api/cycle/__tests__/external-id-floor.test.ts b/src/app/api/cycle/__tests__/external-id-floor.test.ts index 5e1428830..9fe3b848c 100644 --- a/src/app/api/cycle/__tests__/external-id-floor.test.ts +++ b/src/app/api/cycle/__tests__/external-id-floor.test.ts @@ -64,9 +64,6 @@ vi.mock("@/lib/cycle/dto", async (importOriginal) => ({ toMenstrualCycleDTO: vi.fn(() => ({ id: "cycle-1" })), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/cycle/calendar/__tests__/day-entries.test.ts b/src/app/api/cycle/calendar/__tests__/day-entries.test.ts index 48e25b400..bd64f018a 100644 --- a/src/app/api/cycle/calendar/__tests__/day-entries.test.ts +++ b/src/app/api/cycle/calendar/__tests__/day-entries.test.ts @@ -41,9 +41,6 @@ vi.mock("@/lib/rate-limit", () => ({ })); vi.mock("@/lib/cycle/gate", () => ({ requireCycleEnabled: vi.fn() })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/cycle/calendar/__tests__/verdict-round-trip.test.ts b/src/app/api/cycle/calendar/__tests__/verdict-round-trip.test.ts index 08f125662..ed1dced44 100644 --- a/src/app/api/cycle/calendar/__tests__/verdict-round-trip.test.ts +++ b/src/app/api/cycle/calendar/__tests__/verdict-round-trip.test.ts @@ -41,9 +41,6 @@ vi.mock("@/lib/rate-limit", () => ({ })); vi.mock("@/lib/cycle/gate", () => ({ requireCycleEnabled: vi.fn() })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/cycle/day-logs/bulk/__tests__/route.test.ts b/src/app/api/cycle/day-logs/bulk/__tests__/route.test.ts index 8b59f8fec..aaada8c8c 100644 --- a/src/app/api/cycle/day-logs/bulk/__tests__/route.test.ts +++ b/src/app/api/cycle/day-logs/bulk/__tests__/route.test.ts @@ -36,9 +36,6 @@ vi.mock("@/lib/cycle/day-log-write", () => ({ upsertCycleDayLog: vi.fn(), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/cycle/insights/__tests__/route-locale.test.ts b/src/app/api/cycle/insights/__tests__/route-locale.test.ts index 0a516735c..4c44ed11e 100644 --- a/src/app/api/cycle/insights/__tests__/route-locale.test.ts +++ b/src/app/api/cycle/insights/__tests__/route-locale.test.ts @@ -67,10 +67,6 @@ vi.mock("@/lib/rate-limit", () => ({ checkRateLimit: vi.fn(async () => ({ allowed: true })), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const localeCookie = vi.fn<() => string | undefined>(() => undefined); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), diff --git a/src/app/api/cycle/symptoms/custom/__tests__/custom-crud.test.ts b/src/app/api/cycle/symptoms/custom/__tests__/custom-crud.test.ts index eec847a26..cf0c9bfc7 100644 --- a/src/app/api/cycle/symptoms/custom/__tests__/custom-crud.test.ts +++ b/src/app/api/cycle/symptoms/custom/__tests__/custom-crud.test.ts @@ -27,9 +27,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/cycle/gate", () => ({ requireCycleEnabled: vi.fn(), })); diff --git a/src/app/api/daily/digest/__tests__/route.test.ts b/src/app/api/daily/digest/__tests__/route.test.ts index 10af3f903..1362d9554 100644 --- a/src/app/api/daily/digest/__tests__/route.test.ts +++ b/src/app/api/daily/digest/__tests__/route.test.ts @@ -28,9 +28,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/daily/digest/dismiss/__tests__/route.test.ts b/src/app/api/daily/digest/dismiss/__tests__/route.test.ts index 71b989788..6797ca8ac 100644 --- a/src/app/api/daily/digest/dismiss/__tests__/route.test.ts +++ b/src/app/api/daily/digest/dismiss/__tests__/route.test.ts @@ -40,9 +40,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/logging/context", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, annotate: vi.fn() }; diff --git a/src/app/api/dashboard/chart-overlay-prefs/__tests__/route.test.ts b/src/app/api/dashboard/chart-overlay-prefs/__tests__/route.test.ts index 29b6c0ccf..7d77f2585 100644 --- a/src/app/api/dashboard/chart-overlay-prefs/__tests__/route.test.ts +++ b/src/app/api/dashboard/chart-overlay-prefs/__tests__/route.test.ts @@ -22,9 +22,6 @@ vi.mock("@/lib/cache/invalidate", () => ({ invalidateUserDashboardWidgets: vi.fn(), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/dashboard/snapshot/__tests__/route.test.ts b/src/app/api/dashboard/snapshot/__tests__/route.test.ts index e161146af..db2349498 100644 --- a/src/app/api/dashboard/snapshot/__tests__/route.test.ts +++ b/src/app/api/dashboard/snapshot/__tests__/route.test.ts @@ -26,9 +26,6 @@ vi.mock("@/lib/auth/session", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn(), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/dashboard/summary/__tests__/module-gate.test.ts b/src/app/api/dashboard/summary/__tests__/module-gate.test.ts index 90bbfee18..ba4647551 100644 --- a/src/app/api/dashboard/summary/__tests__/module-gate.test.ts +++ b/src/app/api/dashboard/summary/__tests__/module-gate.test.ts @@ -18,9 +18,6 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; vi.mock("@/lib/auth/session", () => ({ getSession: vi.fn() })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); // The gate resolver runs for real; only its data sources are stubbed, so the // module state is driven the way production drives it. vi.mock("@/lib/db", () => ({ diff --git a/src/app/api/dashboard/summary/__tests__/route.test.ts b/src/app/api/dashboard/summary/__tests__/route.test.ts index c6fd2ca31..df9e71ef4 100644 --- a/src/app/api/dashboard/summary/__tests__/route.test.ts +++ b/src/app/api/dashboard/summary/__tests__/route.test.ts @@ -92,10 +92,6 @@ vi.mock("@/lib/analytics/mood-series", () => ({ })), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/dashboard/widgets/__tests__/route.test.ts b/src/app/api/dashboard/widgets/__tests__/route.test.ts index 45777dd56..29ad25c97 100644 --- a/src/app/api/dashboard/widgets/__tests__/route.test.ts +++ b/src/app/api/dashboard/widgets/__tests__/route.test.ts @@ -58,10 +58,6 @@ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn(), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/logging/context", async (importOriginal) => { const actual = await importOriginal(); return { diff --git a/src/app/api/devices/[id]/__tests__/route.test.ts b/src/app/api/devices/[id]/__tests__/route.test.ts index 6f274a931..b6df569cb 100644 --- a/src/app/api/devices/[id]/__tests__/route.test.ts +++ b/src/app/api/devices/[id]/__tests__/route.test.ts @@ -29,10 +29,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/devices/__tests__/route.test.ts b/src/app/api/devices/__tests__/route.test.ts index ef65576c7..a06d413e0 100644 --- a/src/app/api/devices/__tests__/route.test.ts +++ b/src/app/api/devices/__tests__/route.test.ts @@ -37,10 +37,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/rate-limit", () => ({ checkRateLimit: vi.fn().mockResolvedValue({ allowed: true, diff --git a/src/app/api/documents/inbound/[id]/__tests__/route.test.ts b/src/app/api/documents/inbound/[id]/__tests__/route.test.ts index e9a75d030..1ccc9afd0 100644 --- a/src/app/api/documents/inbound/[id]/__tests__/route.test.ts +++ b/src/app/api/documents/inbound/[id]/__tests__/route.test.ts @@ -98,9 +98,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/documents/inbound/[id]/chat/__tests__/route.test.ts b/src/app/api/documents/inbound/[id]/chat/__tests__/route.test.ts index 823dd0fd6..c8f303f28 100644 --- a/src/app/api/documents/inbound/[id]/chat/__tests__/route.test.ts +++ b/src/app/api/documents/inbound/[id]/chat/__tests__/route.test.ts @@ -72,9 +72,6 @@ vi.mock("@/lib/auth/audit", () => ({ })); vi.mock("@/lib/auth/session", () => ({ getSession: vi.fn() })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/documents/inbound/[id]/extract/__tests__/route.test.ts b/src/app/api/documents/inbound/[id]/extract/__tests__/route.test.ts index 0506f136d..6789ff28f 100644 --- a/src/app/api/documents/inbound/[id]/extract/__tests__/route.test.ts +++ b/src/app/api/documents/inbound/[id]/extract/__tests__/route.test.ts @@ -96,9 +96,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/documents/inbound/[id]/index/__tests__/route.test.ts b/src/app/api/documents/inbound/[id]/index/__tests__/route.test.ts index 8307653b2..bbd04cc0c 100644 --- a/src/app/api/documents/inbound/[id]/index/__tests__/route.test.ts +++ b/src/app/api/documents/inbound/[id]/index/__tests__/route.test.ts @@ -76,9 +76,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/documents/inbound/[id]/original/__tests__/route.test.ts b/src/app/api/documents/inbound/[id]/original/__tests__/route.test.ts index fffc37bcc..617ad602c 100644 --- a/src/app/api/documents/inbound/[id]/original/__tests__/route.test.ts +++ b/src/app/api/documents/inbound/[id]/original/__tests__/route.test.ts @@ -46,9 +46,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/documents/inbound/[id]/suggest/__tests__/route.test.ts b/src/app/api/documents/inbound/[id]/suggest/__tests__/route.test.ts index bde68062a..7dbed6993 100644 --- a/src/app/api/documents/inbound/[id]/suggest/__tests__/route.test.ts +++ b/src/app/api/documents/inbound/[id]/suggest/__tests__/route.test.ts @@ -55,9 +55,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/documents/inbound/[id]/summary/__tests__/route.test.ts b/src/app/api/documents/inbound/[id]/summary/__tests__/route.test.ts index 9a0c0b309..62c7de63b 100644 --- a/src/app/api/documents/inbound/[id]/summary/__tests__/route.test.ts +++ b/src/app/api/documents/inbound/[id]/summary/__tests__/route.test.ts @@ -68,9 +68,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/documents/inbound/[id]/thumbnail/__tests__/route.test.ts b/src/app/api/documents/inbound/[id]/thumbnail/__tests__/route.test.ts index 5d31b1102..08f499b4b 100644 --- a/src/app/api/documents/inbound/[id]/thumbnail/__tests__/route.test.ts +++ b/src/app/api/documents/inbound/[id]/thumbnail/__tests__/route.test.ts @@ -48,9 +48,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/documents/inbound/__tests__/route.test.ts b/src/app/api/documents/inbound/__tests__/route.test.ts index 81319039f..a68945788 100644 --- a/src/app/api/documents/inbound/__tests__/route.test.ts +++ b/src/app/api/documents/inbound/__tests__/route.test.ts @@ -133,9 +133,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/documents/inbound/layout/__tests__/route.test.ts b/src/app/api/documents/inbound/layout/__tests__/route.test.ts index 41ca79b2b..1926696d4 100644 --- a/src/app/api/documents/inbound/layout/__tests__/route.test.ts +++ b/src/app/api/documents/inbound/layout/__tests__/route.test.ts @@ -27,10 +27,6 @@ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn(), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/modules/gate", () => ({ requireModuleEnabled: vi.fn(), })); diff --git a/src/app/api/documents/inbound/reindex/__tests__/route.test.ts b/src/app/api/documents/inbound/reindex/__tests__/route.test.ts index dcdaad6ec..e908f1d1b 100644 --- a/src/app/api/documents/inbound/reindex/__tests__/route.test.ts +++ b/src/app/api/documents/inbound/reindex/__tests__/route.test.ts @@ -37,9 +37,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/feature-flags/__tests__/route.test.ts b/src/app/api/feature-flags/__tests__/route.test.ts index cec44a7be..8445347ba 100644 --- a/src/app/api/feature-flags/__tests__/route.test.ts +++ b/src/app/api/feature-flags/__tests__/route.test.ts @@ -9,10 +9,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/fhir/__tests__/observation-series.test.ts b/src/app/api/fhir/__tests__/observation-series.test.ts index 25fb76e6f..790ea1409 100644 --- a/src/app/api/fhir/__tests__/observation-series.test.ts +++ b/src/app/api/fhir/__tests__/observation-series.test.ts @@ -11,9 +11,6 @@ import { NextRequest } from "next/server"; vi.mock("@/lib/auth/session", () => ({ getSession: vi.fn() })); vi.mock("@/lib/rate-limit", () => ({ checkRateLimit: vi.fn() })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/modules/gate", () => ({ requireModuleEnabled: vi.fn(), isModuleEnabled: vi.fn(), diff --git a/src/app/api/fhir/__tests__/route.test.ts b/src/app/api/fhir/__tests__/route.test.ts index 5d2fd4499..6279318b1 100644 --- a/src/app/api/fhir/__tests__/route.test.ts +++ b/src/app/api/fhir/__tests__/route.test.ts @@ -13,9 +13,6 @@ import { NextRequest } from "next/server"; vi.mock("@/lib/auth/session", () => ({ getSession: vi.fn() })); vi.mock("@/lib/rate-limit", () => ({ checkRateLimit: vi.fn() })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/modules/gate", () => ({ requireModuleEnabled: vi.fn(), isModuleEnabled: vi.fn(), diff --git a/src/app/api/gamification/achievements/__tests__/hidden-redaction.test.ts b/src/app/api/gamification/achievements/__tests__/hidden-redaction.test.ts index 0b3a403a6..1ff461b37 100644 --- a/src/app/api/gamification/achievements/__tests__/hidden-redaction.test.ts +++ b/src/app/api/gamification/achievements/__tests__/hidden-redaction.test.ts @@ -38,10 +38,6 @@ vi.mock("@/lib/modules/gate", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/gamification/achievements/__tests__/ios-format.test.ts b/src/app/api/gamification/achievements/__tests__/ios-format.test.ts index 6ed08a845..c3d338474 100644 --- a/src/app/api/gamification/achievements/__tests__/ios-format.test.ts +++ b/src/app/api/gamification/achievements/__tests__/ios-format.test.ts @@ -38,10 +38,6 @@ vi.mock("@/lib/modules/gate", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/gamification/achievements/__tests__/module-gate.test.ts b/src/app/api/gamification/achievements/__tests__/module-gate.test.ts index 57575b470..ad48c68f6 100644 --- a/src/app/api/gamification/achievements/__tests__/module-gate.test.ts +++ b/src/app/api/gamification/achievements/__tests__/module-gate.test.ts @@ -36,9 +36,6 @@ vi.mock("@/lib/db", () => ({ vi.mock("@/lib/auth/session", () => ({ getSession: vi.fn() })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/gamification/achievements/__tests__/writes-nothing.test.ts b/src/app/api/gamification/achievements/__tests__/writes-nothing.test.ts index fc04778b3..4775ccf13 100644 --- a/src/app/api/gamification/achievements/__tests__/writes-nothing.test.ts +++ b/src/app/api/gamification/achievements/__tests__/writes-nothing.test.ts @@ -20,9 +20,6 @@ vi.mock("@/lib/db", () => ({ vi.mock("@/lib/auth/session", () => ({ getSession: vi.fn() })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/illness/episodes/[id]/__tests__/body-site.test.ts b/src/app/api/illness/episodes/[id]/__tests__/body-site.test.ts index 10698f21c..e61916a04 100644 --- a/src/app/api/illness/episodes/[id]/__tests__/body-site.test.ts +++ b/src/app/api/illness/episodes/[id]/__tests__/body-site.test.ts @@ -47,9 +47,6 @@ vi.mock("@/lib/idempotency", () => ({ fn(...args), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/illness/episodes/[id]/day-logs/__tests__/route.test.ts b/src/app/api/illness/episodes/[id]/day-logs/__tests__/route.test.ts index 6c2ae4d52..017352d2b 100644 --- a/src/app/api/illness/episodes/[id]/day-logs/__tests__/route.test.ts +++ b/src/app/api/illness/episodes/[id]/day-logs/__tests__/route.test.ts @@ -25,9 +25,6 @@ vi.mock("@/lib/illness/gate", () => ({ requireIllnessEnabled: vi.fn(), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/illness/episodes/[id]/restore/__tests__/route.test.ts b/src/app/api/illness/episodes/[id]/restore/__tests__/route.test.ts index b7ad8a4e7..dc180da9a 100644 --- a/src/app/api/illness/episodes/[id]/restore/__tests__/route.test.ts +++ b/src/app/api/illness/episodes/[id]/restore/__tests__/route.test.ts @@ -31,9 +31,6 @@ vi.mock("@/lib/idempotency", () => ({ fn(...args), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/import/apple-health-export/[jobId]/status/__tests__/route.test.ts b/src/app/api/import/apple-health-export/[jobId]/status/__tests__/route.test.ts index b0435ae42..68b53197d 100644 --- a/src/app/api/import/apple-health-export/[jobId]/status/__tests__/route.test.ts +++ b/src/app/api/import/apple-health-export/[jobId]/status/__tests__/route.test.ts @@ -10,9 +10,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/import/apple-health-export/__tests__/route.test.ts b/src/app/api/import/apple-health-export/__tests__/route.test.ts index fb762f7fa..cc26e6570 100644 --- a/src/app/api/import/apple-health-export/__tests__/route.test.ts +++ b/src/app/api/import/apple-health-export/__tests__/route.test.ts @@ -18,9 +18,6 @@ vi.mock("@/lib/logging/signal", async (importOriginal) => ({ ...(await importOriginal()), emitSignal: vi.fn(), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/import/health-connect-export/__tests__/route.test.ts b/src/app/api/import/health-connect-export/__tests__/route.test.ts index bba3c79b6..c55a0ba0a 100644 --- a/src/app/api/import/health-connect-export/__tests__/route.test.ts +++ b/src/app/api/import/health-connect-export/__tests__/route.test.ts @@ -11,9 +11,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/ingest/medication/__tests__/route.test.ts b/src/app/api/ingest/medication/__tests__/route.test.ts index 4f1f66c5b..254d84f47 100644 --- a/src/app/api/ingest/medication/__tests__/route.test.ts +++ b/src/app/api/ingest/medication/__tests__/route.test.ts @@ -52,9 +52,6 @@ vi.mock("@/lib/cache/invalidate", () => ({ invalidateUserMedications: vi.fn(), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/insights/biomarker-assessment/__tests__/route.test.ts b/src/app/api/insights/biomarker-assessment/__tests__/route.test.ts index d46454899..a50e0b238 100644 --- a/src/app/api/insights/biomarker-assessment/__tests__/route.test.ts +++ b/src/app/api/insights/biomarker-assessment/__tests__/route.test.ts @@ -30,10 +30,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/insights/blood-pressure-status/__tests__/route.test.ts b/src/app/api/insights/blood-pressure-status/__tests__/route.test.ts index 6bec84c6f..d8fe900da 100644 --- a/src/app/api/insights/blood-pressure-status/__tests__/route.test.ts +++ b/src/app/api/insights/blood-pressure-status/__tests__/route.test.ts @@ -26,10 +26,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/insights/cards/__tests__/route.test.ts b/src/app/api/insights/cards/__tests__/route.test.ts index e54879a31..ad4c51001 100644 --- a/src/app/api/insights/cards/__tests__/route.test.ts +++ b/src/app/api/insights/cards/__tests__/route.test.ts @@ -43,10 +43,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/insights/chat/[id]/attachments/__tests__/route.test.ts b/src/app/api/insights/chat/[id]/attachments/__tests__/route.test.ts index a715ba6c3..00c7e8c2d 100644 --- a/src/app/api/insights/chat/[id]/attachments/__tests__/route.test.ts +++ b/src/app/api/insights/chat/[id]/attachments/__tests__/route.test.ts @@ -34,9 +34,6 @@ vi.mock("@/lib/ai/capabilities/gate", async () => await import("@/__tests__/helpers/provider-order-mock") ).openCapabilityGateMock(), ); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/insights/chat/fenced/__tests__/route.test.ts b/src/app/api/insights/chat/fenced/__tests__/route.test.ts index 52b4a1720..1e7656a50 100644 --- a/src/app/api/insights/chat/fenced/__tests__/route.test.ts +++ b/src/app/api/insights/chat/fenced/__tests__/route.test.ts @@ -45,9 +45,6 @@ vi.mock("@/lib/auth/audit", () => ({ })); vi.mock("@/lib/auth/session", () => ({ getSession: vi.fn() })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/insights/coach-read/__tests__/route.test.ts b/src/app/api/insights/coach-read/__tests__/route.test.ts index 4425cd2eb..8d20f555f 100644 --- a/src/app/api/insights/coach-read/__tests__/route.test.ts +++ b/src/app/api/insights/coach-read/__tests__/route.test.ts @@ -45,10 +45,6 @@ vi.mock("@/lib/rate-limit", () => ({ checkAnalyticsReadRateLimit: vi.fn(), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const localeCookie = vi.fn<() => string | undefined>(() => undefined); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), diff --git a/src/app/api/insights/coach/facts/__tests__/route-memory.test.ts b/src/app/api/insights/coach/facts/__tests__/route-memory.test.ts index 314c23803..9fb3ffac3 100644 --- a/src/app/api/insights/coach/facts/__tests__/route-memory.test.ts +++ b/src/app/api/insights/coach/facts/__tests__/route-memory.test.ts @@ -63,10 +63,6 @@ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn(), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/logging/context", async (importOriginal) => { const actual = await importOriginal(); return { diff --git a/src/app/api/insights/coach/facts/__tests__/route.test.ts b/src/app/api/insights/coach/facts/__tests__/route.test.ts index 4ce52362c..97591c5c0 100644 --- a/src/app/api/insights/coach/facts/__tests__/route.test.ts +++ b/src/app/api/insights/coach/facts/__tests__/route.test.ts @@ -59,10 +59,6 @@ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn(), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/logging/context", async (importOriginal) => { const actual = await importOriginal(); return { diff --git a/src/app/api/insights/comprehensive/__tests__/route.test.ts b/src/app/api/insights/comprehensive/__tests__/route.test.ts index 3454f7fc2..723cc103a 100644 --- a/src/app/api/insights/comprehensive/__tests__/route.test.ts +++ b/src/app/api/insights/comprehensive/__tests__/route.test.ts @@ -66,10 +66,6 @@ vi.mock("@/lib/rate-limit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/insights/correlations/__tests__/route.test.ts b/src/app/api/insights/correlations/__tests__/route.test.ts index 37759be1d..38fd9d900 100644 --- a/src/app/api/insights/correlations/__tests__/route.test.ts +++ b/src/app/api/insights/correlations/__tests__/route.test.ts @@ -64,10 +64,6 @@ vi.mock("@/lib/rate-limit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/insights/correlation-patterns", () => ({ PATTERN_FAMILIES: { discoveryRetrospective: "DISCOVERY_RETROSPECTIVE", diff --git a/src/app/api/insights/derived/__tests__/route.test.ts b/src/app/api/insights/derived/__tests__/route.test.ts index efdb4407d..b06acdf1e 100644 --- a/src/app/api/insights/derived/__tests__/route.test.ts +++ b/src/app/api/insights/derived/__tests__/route.test.ts @@ -39,9 +39,6 @@ vi.mock("@/lib/rate-limit", () => ({ }), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/insights/derived/batch/__tests__/route.test.ts b/src/app/api/insights/derived/batch/__tests__/route.test.ts index c3469b575..34a490f84 100644 --- a/src/app/api/insights/derived/batch/__tests__/route.test.ts +++ b/src/app/api/insights/derived/batch/__tests__/route.test.ts @@ -23,9 +23,6 @@ vi.mock("@/lib/rate-limit", () => ({ rateLimitHeaders: () => ({}), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/insights/ecg/[id]/__tests__/route.test.ts b/src/app/api/insights/ecg/[id]/__tests__/route.test.ts index 125e93b9c..19037f656 100644 --- a/src/app/api/insights/ecg/[id]/__tests__/route.test.ts +++ b/src/app/api/insights/ecg/[id]/__tests__/route.test.ts @@ -32,9 +32,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/insights/ecg/__tests__/route.test.ts b/src/app/api/insights/ecg/__tests__/route.test.ts index 413465864..2b01992ae 100644 --- a/src/app/api/insights/ecg/__tests__/route.test.ts +++ b/src/app/api/insights/ecg/__tests__/route.test.ts @@ -30,9 +30,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/insights/layout/__tests__/route.test.ts b/src/app/api/insights/layout/__tests__/route.test.ts index 341812186..5cfd660ad 100644 --- a/src/app/api/insights/layout/__tests__/route.test.ts +++ b/src/app/api/insights/layout/__tests__/route.test.ts @@ -32,10 +32,6 @@ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn(), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/logging/context", async (importOriginal) => { const actual = await importOriginal(); return { diff --git a/src/app/api/insights/metric-status/__tests__/route.test.ts b/src/app/api/insights/metric-status/__tests__/route.test.ts index 0ed114640..fc703ea6e 100644 --- a/src/app/api/insights/metric-status/__tests__/route.test.ts +++ b/src/app/api/insights/metric-status/__tests__/route.test.ts @@ -32,10 +32,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/insights/mood-status/__tests__/route.test.ts b/src/app/api/insights/mood-status/__tests__/route.test.ts index 10c532985..5133db316 100644 --- a/src/app/api/insights/mood-status/__tests__/route.test.ts +++ b/src/app/api/insights/mood-status/__tests__/route.test.ts @@ -23,10 +23,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/insights/patterns/[id]/__tests__/route.test.ts b/src/app/api/insights/patterns/[id]/__tests__/route.test.ts index dca313586..4c1d6edb1 100644 --- a/src/app/api/insights/patterns/[id]/__tests__/route.test.ts +++ b/src/app/api/insights/patterns/[id]/__tests__/route.test.ts @@ -13,9 +13,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/modules/gate", async (importOriginal) => ({ ...(await importOriginal()), requireModuleEnabled: vi.fn(), diff --git a/src/app/api/insights/patterns/__tests__/route.test.ts b/src/app/api/insights/patterns/__tests__/route.test.ts index a1ca0dee5..d9483b169 100644 --- a/src/app/api/insights/patterns/__tests__/route.test.ts +++ b/src/app/api/insights/patterns/__tests__/route.test.ts @@ -8,9 +8,6 @@ vi.mock("@/lib/db", () => ({ })); vi.mock("@/lib/auth/session", () => ({ getSession: vi.fn() })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/modules/gate", async (importOriginal) => ({ ...(await importOriginal()), requireModuleEnabled: vi.fn(), diff --git a/src/app/api/insights/pregenerate/__tests__/route.test.ts b/src/app/api/insights/pregenerate/__tests__/route.test.ts index e85491006..570240ffe 100644 --- a/src/app/api/insights/pregenerate/__tests__/route.test.ts +++ b/src/app/api/insights/pregenerate/__tests__/route.test.ts @@ -19,10 +19,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/insights/rhythm-events/__tests__/route.test.ts b/src/app/api/insights/rhythm-events/__tests__/route.test.ts index 65f5c094e..f5b5ba143 100644 --- a/src/app/api/insights/rhythm-events/__tests__/route.test.ts +++ b/src/app/api/insights/rhythm-events/__tests__/route.test.ts @@ -33,9 +33,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/insights/targets/__tests__/route.test.ts b/src/app/api/insights/targets/__tests__/route.test.ts index b948897de..bfc24c417 100644 --- a/src/app/api/insights/targets/__tests__/route.test.ts +++ b/src/app/api/insights/targets/__tests__/route.test.ts @@ -65,10 +65,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); vi.mock("next/headers", () => ({ diff --git a/src/app/api/integrations/healthkit/__tests__/route.test.ts b/src/app/api/integrations/healthkit/__tests__/route.test.ts index c7d7c6176..57e944359 100644 --- a/src/app/api/integrations/healthkit/__tests__/route.test.ts +++ b/src/app/api/integrations/healthkit/__tests__/route.test.ts @@ -30,10 +30,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/labs/__tests__/error-contract.test.ts b/src/app/api/labs/__tests__/error-contract.test.ts index fb2d8c4a6..ec812c84f 100644 --- a/src/app/api/labs/__tests__/error-contract.test.ts +++ b/src/app/api/labs/__tests__/error-contract.test.ts @@ -46,9 +46,6 @@ vi.mock("@/lib/labs/store", () => ({ decryptNoteFromBytes: vi.fn(), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/labs/ocr/commit/__tests__/route.test.ts b/src/app/api/labs/ocr/commit/__tests__/route.test.ts index 435dd77eb..8f51cc8ae 100644 --- a/src/app/api/labs/ocr/commit/__tests__/route.test.ts +++ b/src/app/api/labs/ocr/commit/__tests__/route.test.ts @@ -63,9 +63,6 @@ vi.mock("@/lib/labs/serialise", () => ({ serialiseLabResult: (created: { id: string }) => ({ id: created.id }), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/labs/restore/__tests__/route.test.ts b/src/app/api/labs/restore/__tests__/route.test.ts index 5a4e3bcaf..bba0026cf 100644 --- a/src/app/api/labs/restore/__tests__/route.test.ts +++ b/src/app/api/labs/restore/__tests__/route.test.ts @@ -40,9 +40,6 @@ vi.mock("@/lib/cache/invalidate", () => ({ invalidateUserHealthScore: vi.fn(), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/measurement-categories/__tests__/route.test.ts b/src/app/api/measurement-categories/__tests__/route.test.ts index 6db396ff7..1fd11c4e2 100644 --- a/src/app/api/measurement-categories/__tests__/route.test.ts +++ b/src/app/api/measurement-categories/__tests__/route.test.ts @@ -9,10 +9,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/measurements/[id]/__tests__/route.test.ts b/src/app/api/measurements/[id]/__tests__/route.test.ts index 01b3cb23b..6ab8c132b 100644 --- a/src/app/api/measurements/[id]/__tests__/route.test.ts +++ b/src/app/api/measurements/[id]/__tests__/route.test.ts @@ -19,9 +19,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/cache/invalidate", () => ({ invalidateUserMeasurements: vi.fn(), })); diff --git a/src/app/api/measurements/__tests__/group-by-day.test.ts b/src/app/api/measurements/__tests__/group-by-day.test.ts index db8f542f9..4a305dd28 100644 --- a/src/app/api/measurements/__tests__/group-by-day.test.ts +++ b/src/app/api/measurements/__tests__/group-by-day.test.ts @@ -48,9 +48,6 @@ vi.mock("@/lib/db", () => ({ vi.mock("@/lib/auth/session", () => ({ getSession: vi.fn() })); vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn() })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/measurements/__tests__/put-duplicate-timestamp.test.ts b/src/app/api/measurements/__tests__/put-duplicate-timestamp.test.ts index 2424881af..6c6abdf48 100644 --- a/src/app/api/measurements/__tests__/put-duplicate-timestamp.test.ts +++ b/src/app/api/measurements/__tests__/put-duplicate-timestamp.test.ts @@ -16,10 +16,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn() })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/measurements/__tests__/range-aggregation-route.test.ts b/src/app/api/measurements/__tests__/range-aggregation-route.test.ts index d5f0db0d6..82360e130 100644 --- a/src/app/api/measurements/__tests__/range-aggregation-route.test.ts +++ b/src/app/api/measurements/__tests__/range-aggregation-route.test.ts @@ -48,9 +48,6 @@ vi.mock("@/lib/rollups/measurement-rollups", async () => { vi.mock("@/lib/auth/session", () => ({ getSession: vi.fn() })); vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn() })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/measurements/__tests__/zod-multi-issue.test.ts b/src/app/api/measurements/__tests__/zod-multi-issue.test.ts index 894a71648..695d56780 100644 --- a/src/app/api/measurements/__tests__/zod-multi-issue.test.ts +++ b/src/app/api/measurements/__tests__/zod-multi-issue.test.ts @@ -43,9 +43,6 @@ vi.mock("@/lib/db", () => ({ vi.mock("@/lib/auth/session", () => ({ getSession: vi.fn() })); vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn() })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/idempotency", () => ({ withIdempotency: (fn: (...args: Args) => Promise) => diff --git a/src/app/api/measurements/batch/__tests__/aggregated-hr-wire.test.ts b/src/app/api/measurements/batch/__tests__/aggregated-hr-wire.test.ts index 085fe93d4..b8e4c6d01 100644 --- a/src/app/api/measurements/batch/__tests__/aggregated-hr-wire.test.ts +++ b/src/app/api/measurements/batch/__tests__/aggregated-hr-wire.test.ts @@ -99,9 +99,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/rate-limit", () => ({ checkRateLimit: vi.fn() })); vi.mock("@/lib/jobs/pr-detection", () => ({ enqueuePrDetection: vi.fn().mockResolvedValue(undefined), diff --git a/src/app/api/measurements/batch/__tests__/duplicate-prefilter.test.ts b/src/app/api/measurements/batch/__tests__/duplicate-prefilter.test.ts index af4a4a395..8f21a1f31 100644 --- a/src/app/api/measurements/batch/__tests__/duplicate-prefilter.test.ts +++ b/src/app/api/measurements/batch/__tests__/duplicate-prefilter.test.ts @@ -226,9 +226,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/rate-limit", () => ({ checkRateLimit: vi.fn() })); vi.mock("@/lib/jobs/pr-detection", () => ({ enqueuePrDetection: vi.fn().mockResolvedValue(undefined), diff --git a/src/app/api/measurements/batch/__tests__/hr-bucket-observability.test.ts b/src/app/api/measurements/batch/__tests__/hr-bucket-observability.test.ts index 188687af5..8d5eb4a0e 100644 --- a/src/app/api/measurements/batch/__tests__/hr-bucket-observability.test.ts +++ b/src/app/api/measurements/batch/__tests__/hr-bucket-observability.test.ts @@ -106,9 +106,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/rate-limit", () => ({ checkRateLimit: vi.fn() })); vi.mock("@/lib/jobs/pr-detection", () => ({ enqueuePrDetection: vi.fn().mockResolvedValue(undefined), diff --git a/src/app/api/measurements/batch/__tests__/insert-returning-arrivals.test.ts b/src/app/api/measurements/batch/__tests__/insert-returning-arrivals.test.ts index e7eb79f74..539305fb0 100644 --- a/src/app/api/measurements/batch/__tests__/insert-returning-arrivals.test.ts +++ b/src/app/api/measurements/batch/__tests__/insert-returning-arrivals.test.ts @@ -77,9 +77,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/rate-limit", () => ({ checkRateLimit: vi.fn() })); vi.mock("@/lib/jobs/pr-detection", () => ({ enqueuePrDetection: vi.fn().mockResolvedValue(undefined), diff --git a/src/app/api/measurements/batch/__tests__/issue-paths.test.ts b/src/app/api/measurements/batch/__tests__/issue-paths.test.ts index 8d7edf6c0..4b864ee89 100644 --- a/src/app/api/measurements/batch/__tests__/issue-paths.test.ts +++ b/src/app/api/measurements/batch/__tests__/issue-paths.test.ts @@ -38,9 +38,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/rate-limit", () => ({ checkRateLimit: vi.fn() })); vi.mock("@/lib/jobs/pr-detection", () => ({ enqueuePrDetection: vi.fn().mockResolvedValue(undefined), diff --git a/src/app/api/measurements/batch/__tests__/pr-detection-hook.test.ts b/src/app/api/measurements/batch/__tests__/pr-detection-hook.test.ts index e7c8f58f2..f29879bc8 100644 --- a/src/app/api/measurements/batch/__tests__/pr-detection-hook.test.ts +++ b/src/app/api/measurements/batch/__tests__/pr-detection-hook.test.ts @@ -58,10 +58,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/rate-limit", () => ({ checkRateLimit: vi.fn(), })); diff --git a/src/app/api/measurements/batch/__tests__/refusal-detail.test.ts b/src/app/api/measurements/batch/__tests__/refusal-detail.test.ts index 921cda502..34a571f02 100644 --- a/src/app/api/measurements/batch/__tests__/refusal-detail.test.ts +++ b/src/app/api/measurements/batch/__tests__/refusal-detail.test.ts @@ -45,9 +45,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/rate-limit", () => ({ checkRateLimit: vi.fn() })); vi.mock("@/lib/jobs/pr-detection", () => ({ enqueuePrDetection: vi.fn().mockResolvedValue(undefined), diff --git a/src/app/api/measurements/batch/__tests__/sync-trigger.test.ts b/src/app/api/measurements/batch/__tests__/sync-trigger.test.ts index 38f2318fb..b73a963fa 100644 --- a/src/app/api/measurements/batch/__tests__/sync-trigger.test.ts +++ b/src/app/api/measurements/batch/__tests__/sync-trigger.test.ts @@ -108,9 +108,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/rate-limit", () => ({ checkRateLimit: vi.fn() })); vi.mock("@/lib/jobs/pr-detection", () => ({ enqueuePrDetection: vi.fn().mockResolvedValue(undefined), diff --git a/src/app/api/measurements/batch/__tests__/timestamp-plausibility.test.ts b/src/app/api/measurements/batch/__tests__/timestamp-plausibility.test.ts index 8fdeb46da..b9a2ec68b 100644 --- a/src/app/api/measurements/batch/__tests__/timestamp-plausibility.test.ts +++ b/src/app/api/measurements/batch/__tests__/timestamp-plausibility.test.ts @@ -48,10 +48,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/rate-limit", () => ({ checkRateLimit: vi.fn(), })); diff --git a/src/app/api/measurements/batch/__tests__/unstable-external-id.test.ts b/src/app/api/measurements/batch/__tests__/unstable-external-id.test.ts index a68fdd012..c03197a76 100644 --- a/src/app/api/measurements/batch/__tests__/unstable-external-id.test.ts +++ b/src/app/api/measurements/batch/__tests__/unstable-external-id.test.ts @@ -46,10 +46,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/rate-limit", () => ({ checkRateLimit: vi.fn(), })); diff --git a/src/app/api/measurements/bulk-delete/__tests__/route.test.ts b/src/app/api/measurements/bulk-delete/__tests__/route.test.ts index f546f195d..55f4cb17c 100644 --- a/src/app/api/measurements/bulk-delete/__tests__/route.test.ts +++ b/src/app/api/measurements/bulk-delete/__tests__/route.test.ts @@ -68,9 +68,6 @@ vi.mock("@/lib/rollups/measurement-rollups", async () => { }; }); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/measurements/by-external-ids/__tests__/route.test.ts b/src/app/api/measurements/by-external-ids/__tests__/route.test.ts index 5cfee6105..58729c791 100644 --- a/src/app/api/measurements/by-external-ids/__tests__/route.test.ts +++ b/src/app/api/measurements/by-external-ids/__tests__/route.test.ts @@ -45,9 +45,6 @@ vi.mock("@/lib/rollups/measurement-rollups", async () => { }; }); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/measurements/restore/__tests__/route.test.ts b/src/app/api/measurements/restore/__tests__/route.test.ts index 73ce2540e..6678f068c 100644 --- a/src/app/api/measurements/restore/__tests__/route.test.ts +++ b/src/app/api/measurements/restore/__tests__/route.test.ts @@ -68,9 +68,6 @@ vi.mock("@/lib/rollups/measurement-rollups", async () => { }; }); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/measurements/series-batch/__tests__/route.test.ts b/src/app/api/measurements/series-batch/__tests__/route.test.ts index 9e34cea5d..d0184557e 100644 --- a/src/app/api/measurements/series-batch/__tests__/route.test.ts +++ b/src/app/api/measurements/series-batch/__tests__/route.test.ts @@ -15,9 +15,6 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; import { NextRequest } from "next/server"; vi.mock("@/lib/auth/session", () => ({ getSession: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); vi.mock("@/lib/logging/context", async (importOriginal) => { const actual = await importOriginal(); diff --git a/src/app/api/measurements/series/__tests__/route.test.ts b/src/app/api/measurements/series/__tests__/route.test.ts index ea63202db..2b529b188 100644 --- a/src/app/api/measurements/series/__tests__/route.test.ts +++ b/src/app/api/measurements/series/__tests__/route.test.ts @@ -84,10 +84,6 @@ vi.mock("@/lib/rollups/measurement-read", () => ({ loadUserSourcePriority: vi.fn(async () => null), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/logging/context", async (importOriginal) => { const actual = await importOriginal(); return { diff --git a/src/app/api/measurements/series/__tests__/unknown-query-param-open.test.ts b/src/app/api/measurements/series/__tests__/unknown-query-param-open.test.ts index b852744ff..701429dfe 100644 --- a/src/app/api/measurements/series/__tests__/unknown-query-param-open.test.ts +++ b/src/app/api/measurements/series/__tests__/unknown-query-param-open.test.ts @@ -39,10 +39,6 @@ vi.mock("@/lib/rollups/measurement-read", () => ({ loadUserSourcePriority: vi.fn(async () => null), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/logging/context", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, annotate: vi.fn() }; diff --git a/src/app/api/medications/[id]/__tests__/route.test.ts b/src/app/api/medications/[id]/__tests__/route.test.ts index 39b43f603..f78763616 100644 --- a/src/app/api/medications/[id]/__tests__/route.test.ts +++ b/src/app/api/medications/[id]/__tests__/route.test.ts @@ -104,9 +104,6 @@ vi.mock("@/lib/medication-category", () => ({ setMedicationCategory: vi.fn().mockResolvedValue("OTHER"), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/medications/[id]/compliance/__tests__/route.test.ts b/src/app/api/medications/[id]/compliance/__tests__/route.test.ts index 7e39647d6..b5c3258a3 100644 --- a/src/app/api/medications/[id]/compliance/__tests__/route.test.ts +++ b/src/app/api/medications/[id]/compliance/__tests__/route.test.ts @@ -54,10 +54,6 @@ vi.mock("@/lib/rate-limit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/medications/[id]/dose-history/__tests__/route.test.ts b/src/app/api/medications/[id]/dose-history/__tests__/route.test.ts index fe998e2a6..5c55e140a 100644 --- a/src/app/api/medications/[id]/dose-history/__tests__/route.test.ts +++ b/src/app/api/medications/[id]/dose-history/__tests__/route.test.ts @@ -42,9 +42,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/medications/[id]/glp1/__tests__/route.test.ts b/src/app/api/medications/[id]/glp1/__tests__/route.test.ts index 2b28fec55..156492bfc 100644 --- a/src/app/api/medications/[id]/glp1/__tests__/route.test.ts +++ b/src/app/api/medications/[id]/glp1/__tests__/route.test.ts @@ -47,10 +47,6 @@ vi.mock("@/lib/rate-limit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/medications/[id]/intake/[eventId]/__tests__/route.test.ts b/src/app/api/medications/[id]/intake/[eventId]/__tests__/route.test.ts index cedb34685..2f4eb9b7a 100644 --- a/src/app/api/medications/[id]/intake/[eventId]/__tests__/route.test.ts +++ b/src/app/api/medications/[id]/intake/[eventId]/__tests__/route.test.ts @@ -41,9 +41,6 @@ vi.mock("@/lib/medications/dose-day-refresh", () => ({ refreshDoseSlot: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/medications/[id]/intake/__tests__/route.test.ts b/src/app/api/medications/[id]/intake/__tests__/route.test.ts index 42b3eaf50..e5bfe0558 100644 --- a/src/app/api/medications/[id]/intake/__tests__/route.test.ts +++ b/src/app/api/medications/[id]/intake/__tests__/route.test.ts @@ -88,10 +88,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn() })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/medications/[id]/intake/__tests__/source-provenance.test.ts b/src/app/api/medications/[id]/intake/__tests__/source-provenance.test.ts index 759d6ff64..2a24675ad 100644 --- a/src/app/api/medications/[id]/intake/__tests__/source-provenance.test.ts +++ b/src/app/api/medications/[id]/intake/__tests__/source-provenance.test.ts @@ -109,10 +109,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // Default to no Authorization header; the Bearer tests override this. const headerStore = { authorization: null as string | null }; vi.mock("next/headers", () => ({ diff --git a/src/app/api/medications/[id]/intake/import/__tests__/route.test.ts b/src/app/api/medications/[id]/intake/import/__tests__/route.test.ts index e04be16be..933854051 100644 --- a/src/app/api/medications/[id]/intake/import/__tests__/route.test.ts +++ b/src/app/api/medications/[id]/intake/import/__tests__/route.test.ts @@ -45,9 +45,6 @@ vi.mock("@/lib/jobs/boss-instance", () => ({ getGlobalBoss: vi.fn(), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/medications/[id]/schedule-revisions/__tests__/route.test.ts b/src/app/api/medications/[id]/schedule-revisions/__tests__/route.test.ts index a54c4b903..9cffd05d0 100644 --- a/src/app/api/medications/[id]/schedule-revisions/__tests__/route.test.ts +++ b/src/app/api/medications/[id]/schedule-revisions/__tests__/route.test.ts @@ -43,9 +43,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/medications/__tests__/route.test.ts b/src/app/api/medications/__tests__/route.test.ts index 1159f63a2..b8a7c5527 100644 --- a/src/app/api/medications/__tests__/route.test.ts +++ b/src/app/api/medications/__tests__/route.test.ts @@ -107,9 +107,6 @@ vi.mock("@/lib/tz/local-day", async (importOriginal) => ({ .mockReturnValue({ start: new Date(), end: new Date() }), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/medications/compliance/__tests__/route.test.ts b/src/app/api/medications/compliance/__tests__/route.test.ts index 1f7059142..15e0a3e90 100644 --- a/src/app/api/medications/compliance/__tests__/route.test.ts +++ b/src/app/api/medications/compliance/__tests__/route.test.ts @@ -38,10 +38,6 @@ vi.mock("@/lib/rate-limit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/medications/intake/__tests__/route.test.ts b/src/app/api/medications/intake/__tests__/route.test.ts index 87d35347f..3b0cf8ce1 100644 --- a/src/app/api/medications/intake/__tests__/route.test.ts +++ b/src/app/api/medications/intake/__tests__/route.test.ts @@ -65,10 +65,6 @@ vi.mock("@/lib/notifications/medication-intake-sync", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/medications/intake/bulk/__tests__/dose-arrival-scope.test.ts b/src/app/api/medications/intake/bulk/__tests__/dose-arrival-scope.test.ts index 18232033f..d0c59a771 100644 --- a/src/app/api/medications/intake/bulk/__tests__/dose-arrival-scope.test.ts +++ b/src/app/api/medications/intake/bulk/__tests__/dose-arrival-scope.test.ts @@ -54,9 +54,6 @@ vi.mock("@/lib/notifications/medication-intake-sync", () => ({ queueMedicationIntakeSync: vi.fn(), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/medications/intake/bulk/__tests__/route.test.ts b/src/app/api/medications/intake/bulk/__tests__/route.test.ts index 006d9c7ee..ff800ad0d 100644 --- a/src/app/api/medications/intake/bulk/__tests__/route.test.ts +++ b/src/app/api/medications/intake/bulk/__tests__/route.test.ts @@ -56,9 +56,6 @@ vi.mock("@/lib/medications/dose-day-refresh", () => ({ dayKeyForScheduledFor: vi.fn().mockReturnValue("2026-01-01"), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/medications/intake/dose-history-import/__tests__/route.test.ts b/src/app/api/medications/intake/dose-history-import/__tests__/route.test.ts index 491f52dd5..39b3c6b1b 100644 --- a/src/app/api/medications/intake/dose-history-import/__tests__/route.test.ts +++ b/src/app/api/medications/intake/dose-history-import/__tests__/route.test.ts @@ -40,9 +40,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/rate-limit", () => ({ checkRateLimit: vi.fn() })); vi.mock("@/lib/jobs/boss-instance", () => ({ getGlobalBoss: vi.fn() })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/medications/layout/__tests__/route.test.ts b/src/app/api/medications/layout/__tests__/route.test.ts index cc8f500b6..3d7e94637 100644 --- a/src/app/api/medications/layout/__tests__/route.test.ts +++ b/src/app/api/medications/layout/__tests__/route.test.ts @@ -32,10 +32,6 @@ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn(), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/mental-health/assessments/[id]/__tests__/route.test.ts b/src/app/api/mental-health/assessments/[id]/__tests__/route.test.ts index be19cee4b..c1fa7bdde 100644 --- a/src/app/api/mental-health/assessments/[id]/__tests__/route.test.ts +++ b/src/app/api/mental-health/assessments/[id]/__tests__/route.test.ts @@ -34,9 +34,6 @@ vi.mock("@/lib/ai/coach/bytes-codec", () => ({ vi.mock("@/lib/auth/session", () => ({ getSession: vi.fn() })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/mental-health/assessments/__tests__/route.test.ts b/src/app/api/mental-health/assessments/__tests__/route.test.ts index 3dfa8ded0..bc4e01570 100644 --- a/src/app/api/mental-health/assessments/__tests__/route.test.ts +++ b/src/app/api/mental-health/assessments/__tests__/route.test.ts @@ -52,9 +52,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/meta/capabilities/__tests__/route.test.ts b/src/app/api/meta/capabilities/__tests__/route.test.ts index 1a068b6d0..c82648f28 100644 --- a/src/app/api/meta/capabilities/__tests__/route.test.ts +++ b/src/app/api/meta/capabilities/__tests__/route.test.ts @@ -29,10 +29,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/mood-entries/[id]/__tests__/route.test.ts b/src/app/api/mood-entries/[id]/__tests__/route.test.ts index 19727e41f..0d0055689 100644 --- a/src/app/api/mood-entries/[id]/__tests__/route.test.ts +++ b/src/app/api/mood-entries/[id]/__tests__/route.test.ts @@ -56,9 +56,6 @@ vi.mock("@/lib/rollups/mood-rollups", () => ({ recomputeMoodBucketsForEntry: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/mood-entries/__tests__/route.test.ts b/src/app/api/mood-entries/__tests__/route.test.ts index db99a0445..d13157028 100644 --- a/src/app/api/mood-entries/__tests__/route.test.ts +++ b/src/app/api/mood-entries/__tests__/route.test.ts @@ -88,9 +88,6 @@ vi.mock("@/lib/rollups/mood-rollups", () => ({ recomputeMoodBucketsForEntry: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/mood-entries/bulk-delete/__tests__/route.test.ts b/src/app/api/mood-entries/bulk-delete/__tests__/route.test.ts index f90b842bc..e78b9e4f3 100644 --- a/src/app/api/mood-entries/bulk-delete/__tests__/route.test.ts +++ b/src/app/api/mood-entries/bulk-delete/__tests__/route.test.ts @@ -40,9 +40,6 @@ vi.mock("@/lib/rollups/mood-rollups", () => ({ recomputeMoodBucketsForEntry: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/mood-entries/bulk/__tests__/route.test.ts b/src/app/api/mood-entries/bulk/__tests__/route.test.ts index e77da8c5b..a4f9f0628 100644 --- a/src/app/api/mood-entries/bulk/__tests__/route.test.ts +++ b/src/app/api/mood-entries/bulk/__tests__/route.test.ts @@ -83,9 +83,6 @@ vi.mock("@/lib/rollups/mood-rollups", () => ({ recomputeMoodBucketsForEntry: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/mood-entries/restore/__tests__/route.test.ts b/src/app/api/mood-entries/restore/__tests__/route.test.ts index 7ff413f63..d015a817d 100644 --- a/src/app/api/mood-entries/restore/__tests__/route.test.ts +++ b/src/app/api/mood-entries/restore/__tests__/route.test.ts @@ -41,9 +41,6 @@ vi.mock("@/lib/rollups/mood-rollups", () => ({ recomputeMoodBucketsForEntry: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/mood/analytics/__tests__/route.test.ts b/src/app/api/mood/analytics/__tests__/route.test.ts index 9e538462f..dfcefc9e7 100644 --- a/src/app/api/mood/analytics/__tests__/route.test.ts +++ b/src/app/api/mood/analytics/__tests__/route.test.ts @@ -43,10 +43,6 @@ vi.mock("@/lib/modules/gate", () => ({ requireModuleEnabled: vi.fn(async () => ({ enabled: true })), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/mood/insights/__tests__/route.test.ts b/src/app/api/mood/insights/__tests__/route.test.ts index a00ef9dbf..deea12a50 100644 --- a/src/app/api/mood/insights/__tests__/route.test.ts +++ b/src/app/api/mood/insights/__tests__/route.test.ts @@ -15,9 +15,6 @@ vi.mock("@/lib/cache/server-cache", () => ({ caches: { moodInsights: {} }, })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/notifications/action/[token]/__tests__/route.test.ts b/src/app/api/notifications/action/[token]/__tests__/route.test.ts index 54f10d451..3de1caaba 100644 --- a/src/app/api/notifications/action/[token]/__tests__/route.test.ts +++ b/src/app/api/notifications/action/[token]/__tests__/route.test.ts @@ -27,10 +27,6 @@ vi.mock("@/lib/mood/create-from-telegram", () => ({ logTelegramMood: vi.fn(), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - import { POST } from "../route"; import { prisma } from "@/lib/db"; import { checkAuthSurfaceRateLimit, checkRateLimit } from "@/lib/rate-limit"; diff --git a/src/app/api/notifications/status/__tests__/route.test.ts b/src/app/api/notifications/status/__tests__/route.test.ts index dac4be431..28f01b7bb 100644 --- a/src/app/api/notifications/status/__tests__/route.test.ts +++ b/src/app/api/notifications/status/__tests__/route.test.ts @@ -38,10 +38,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/nutrients/__tests__/route.test.ts b/src/app/api/nutrients/__tests__/route.test.ts index 8f2d5100c..0315b747a 100644 --- a/src/app/api/nutrients/__tests__/route.test.ts +++ b/src/app/api/nutrients/__tests__/route.test.ts @@ -23,9 +23,6 @@ vi.mock("@/lib/modules/gate", () => ({ vi.mock("@/lib/auth/session", () => ({ getSession: vi.fn() })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/nutrients/batch/__tests__/route.test.ts b/src/app/api/nutrients/batch/__tests__/route.test.ts index cc6ea246a..63079a20b 100644 --- a/src/app/api/nutrients/batch/__tests__/route.test.ts +++ b/src/app/api/nutrients/batch/__tests__/route.test.ts @@ -49,9 +49,6 @@ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); vi.mock("@/lib/cache/invalidate", () => ({ invalidateUserDashboardSnapshot: vi.fn(), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/nutrients/daily/__tests__/route.test.ts b/src/app/api/nutrients/daily/__tests__/route.test.ts index af942a685..2c326657f 100644 --- a/src/app/api/nutrients/daily/__tests__/route.test.ts +++ b/src/app/api/nutrients/daily/__tests__/route.test.ts @@ -23,9 +23,6 @@ vi.mock("@/lib/modules/gate", () => ({ vi.mock("@/lib/auth/session", () => ({ getSession: vi.fn() })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/nutrients/water/__tests__/route.test.ts b/src/app/api/nutrients/water/__tests__/route.test.ts index 354ce7cc2..b1ad8c701 100644 --- a/src/app/api/nutrients/water/__tests__/route.test.ts +++ b/src/app/api/nutrients/water/__tests__/route.test.ts @@ -44,9 +44,6 @@ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/onboarding/__tests__/error-contract.test.ts b/src/app/api/onboarding/__tests__/error-contract.test.ts index 4bbffc9b2..a219fe909 100644 --- a/src/app/api/onboarding/__tests__/error-contract.test.ts +++ b/src/app/api/onboarding/__tests__/error-contract.test.ts @@ -9,9 +9,6 @@ vi.mock("@/lib/auth/session", () => ({ setOnboardingPendingCookie: vi.fn(), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/onboarding/complete/__tests__/route.test.ts b/src/app/api/onboarding/complete/__tests__/route.test.ts index 4fbc8c9a8..c33b8a690 100644 --- a/src/app/api/onboarding/complete/__tests__/route.test.ts +++ b/src/app/api/onboarding/complete/__tests__/route.test.ts @@ -28,10 +28,6 @@ vi.mock("@/lib/auth/session", async (importOriginal) => { vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/logging/context", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, annotate: vi.fn() }; diff --git a/src/app/api/personal-records/__tests__/route.test.ts b/src/app/api/personal-records/__tests__/route.test.ts index 2593e518d..e02a60ecb 100644 --- a/src/app/api/personal-records/__tests__/route.test.ts +++ b/src/app/api/personal-records/__tests__/route.test.ts @@ -19,10 +19,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/share-links/__tests__/route.test.ts b/src/app/api/share-links/__tests__/route.test.ts index bc5e3ad24..2373645a9 100644 --- a/src/app/api/share-links/__tests__/route.test.ts +++ b/src/app/api/share-links/__tests__/route.test.ts @@ -38,9 +38,6 @@ vi.mock("@/lib/auth/hmac", () => ({ hashToken: (s: string) => `hash(${s})` })); vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn() })); vi.mock("@/lib/rate-limit", () => ({ checkRateLimit: vi.fn() })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/sleep/night/__tests__/route.test.ts b/src/app/api/sleep/night/__tests__/route.test.ts index 483f888ee..fb957b139 100644 --- a/src/app/api/sleep/night/__tests__/route.test.ts +++ b/src/app/api/sleep/night/__tests__/route.test.ts @@ -39,10 +39,6 @@ vi.mock("@/lib/measurements/list/sleep-nights", async (importOriginal) => { return { ...actual, latestMainSleepNight: vi.fn(async () => null) }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/logging/context", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, annotate: vi.fn() }; diff --git a/src/app/api/sleep/rhythm/__tests__/route.test.ts b/src/app/api/sleep/rhythm/__tests__/route.test.ts index f8e3ee7b5..c712c5d6e 100644 --- a/src/app/api/sleep/rhythm/__tests__/route.test.ts +++ b/src/app/api/sleep/rhythm/__tests__/route.test.ts @@ -28,10 +28,6 @@ vi.mock("@/lib/modules/gate", () => ({ requireModuleEnabled: vi.fn(async () => ({ enabled: true })), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/logging/context", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, annotate: vi.fn() }; diff --git a/src/app/api/sync/changes/__tests__/module-gate.test.ts b/src/app/api/sync/changes/__tests__/module-gate.test.ts index 9640f30c1..a6cc395ac 100644 --- a/src/app/api/sync/changes/__tests__/module-gate.test.ts +++ b/src/app/api/sync/changes/__tests__/module-gate.test.ts @@ -15,9 +15,6 @@ import { NextRequest } from "next/server"; vi.mock("@/lib/auth/session", () => ({ getSession: vi.fn() })); vi.mock("@/lib/rate-limit", () => ({ checkRateLimit: vi.fn() })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/modules/gate", () => ({ isModuleEnabled: vi.fn() })); vi.mock("@/lib/db", () => ({ prisma: { diff --git a/src/app/api/user/avatar/[id]/__tests__/route.test.ts b/src/app/api/user/avatar/[id]/__tests__/route.test.ts index 0cb2596a0..7b71d3ed2 100644 --- a/src/app/api/user/avatar/[id]/__tests__/route.test.ts +++ b/src/app/api/user/avatar/[id]/__tests__/route.test.ts @@ -16,10 +16,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/user/avatar/__tests__/route.test.ts b/src/app/api/user/avatar/__tests__/route.test.ts index 72e005dce..042086f44 100644 --- a/src/app/api/user/avatar/__tests__/route.test.ts +++ b/src/app/api/user/avatar/__tests__/route.test.ts @@ -17,10 +17,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/rate-limit", () => ({ checkRateLimit: vi.fn().mockResolvedValue({ allowed: true, diff --git a/src/app/api/user/profile/__tests__/route.test.ts b/src/app/api/user/profile/__tests__/route.test.ts index 03567bd91..511b6f3e5 100644 --- a/src/app/api/user/profile/__tests__/route.test.ts +++ b/src/app/api/user/profile/__tests__/route.test.ts @@ -16,10 +16,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // v1.18.0 — the GET alias resolves the module map. Stub it so these // tests assert the projection wiring without standing up the gate's // own DB round-trips (operator availability + assistant flags + cycle). diff --git a/src/app/api/user/thresholds/__tests__/route.test.ts b/src/app/api/user/thresholds/__tests__/route.test.ts index 573d737d2..c7865e32f 100644 --- a/src/app/api/user/thresholds/__tests__/route.test.ts +++ b/src/app/api/user/thresholds/__tests__/route.test.ts @@ -35,9 +35,6 @@ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); vi.mock("@/lib/cache/invalidate", () => ({ invalidateUserProfile: vi.fn(), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/withings/credentials/__tests__/route.test.ts b/src/app/api/withings/credentials/__tests__/route.test.ts index 737ce1a9f..09f30b1e5 100644 --- a/src/app/api/withings/credentials/__tests__/route.test.ts +++ b/src/app/api/withings/credentials/__tests__/route.test.ts @@ -28,9 +28,6 @@ vi.mock("@/lib/integrations/status", () => ({ markDisconnected: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/app/api/workouts/__tests__/batch-create.test.ts b/src/app/api/workouts/__tests__/batch-create.test.ts index 877dee0cc..34af4f9cd 100644 --- a/src/app/api/workouts/__tests__/batch-create.test.ts +++ b/src/app/api/workouts/__tests__/batch-create.test.ts @@ -48,10 +48,6 @@ vi.mock("@/lib/auth/audit", () => ({ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/rate-limit", () => ({ checkRateLimit: vi.fn(), })); diff --git a/src/app/api/workouts/batch/__tests__/unstable-external-id.test.ts b/src/app/api/workouts/batch/__tests__/unstable-external-id.test.ts index 1e2cd9cd1..64dc293c3 100644 --- a/src/app/api/workouts/batch/__tests__/unstable-external-id.test.ts +++ b/src/app/api/workouts/batch/__tests__/unstable-external-id.test.ts @@ -55,9 +55,6 @@ vi.mock("@/lib/arrivals/emit-shared", () => ({ emitDataArrival: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/logging/context", async (importOriginal) => { const actual = await importOriginal>(); return { ...actual, annotate: vi.fn() }; diff --git a/src/app/c/[token]/d/[id]/__tests__/route.test.ts b/src/app/c/[token]/d/[id]/__tests__/route.test.ts index b22022580..0018989ba 100644 --- a/src/app/c/[token]/d/[id]/__tests__/route.test.ts +++ b/src/app/c/[token]/d/[id]/__tests__/route.test.ts @@ -52,9 +52,6 @@ vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); vi.mock("@/lib/modules/gate", () => ({ isModuleEnabled: vi.fn(async () => true), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("next/headers", () => ({ headers: vi.fn(async () => ({ get: () => null })), cookies: vi.fn(async () => ({ diff --git a/src/lib/__tests__/glitchtip-path-secret-redaction.test.ts b/src/lib/__tests__/glitchtip-path-secret-redaction.test.ts index b24d2dba1..a2a1f295a 100644 --- a/src/lib/__tests__/glitchtip-path-secret-redaction.test.ts +++ b/src/lib/__tests__/glitchtip-path-secret-redaction.test.ts @@ -27,10 +27,6 @@ vi.mock("@/lib/auth/audit", () => ({ auditLog: vi.fn().mockResolvedValue(undefined), })); vi.mock("@/lib/logging/transports", () => ({ emitIfSampled: vi.fn() })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/monitoring-settings", () => ({ getGlitchtipSettings: vi.fn(async () => ({ glitchtipEnabled: true, diff --git a/src/lib/analytics/__tests__/sleep-stage-composition.test.ts b/src/lib/analytics/__tests__/sleep-stage-composition.test.ts index bb6db6fb2..520aa239b 100644 --- a/src/lib/analytics/__tests__/sleep-stage-composition.test.ts +++ b/src/lib/analytics/__tests__/sleep-stage-composition.test.ts @@ -28,10 +28,6 @@ vi.mock("@/lib/modules/gate", () => ({ requireModuleEnabled: vi.fn(async () => ({ enabled: true })), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/logging/context", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, annotate: vi.fn() }; diff --git a/src/lib/auth/__tests__/session-locale-reconcile.test.ts b/src/lib/auth/__tests__/session-locale-reconcile.test.ts index 01b356880..7836216cd 100644 --- a/src/lib/auth/__tests__/session-locale-reconcile.test.ts +++ b/src/lib/auth/__tests__/session-locale-reconcile.test.ts @@ -37,10 +37,6 @@ vi.mock("@/lib/db", () => ({ }, })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/logging/context", () => ({ getEvent: () => undefined })); vi.mock("@/lib/auth/hmac", () => ({ diff --git a/src/lib/auth/__tests__/session.test.ts b/src/lib/auth/__tests__/session.test.ts index ecf516f9a..1384dcb54 100644 --- a/src/lib/auth/__tests__/session.test.ts +++ b/src/lib/auth/__tests__/session.test.ts @@ -57,10 +57,6 @@ vi.mock("@/lib/db", () => ({ }, })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/logging/context", () => ({ getEvent: () => undefined, })); diff --git a/src/lib/auth/session.ts b/src/lib/auth/session.ts index 3350c5ea8..4c8188a44 100644 --- a/src/lib/auth/session.ts +++ b/src/lib/auth/session.ts @@ -3,7 +3,6 @@ import { prisma } from "@/lib/db"; import { cookies } from "next/headers"; import { hashToken } from "@/lib/auth/hmac"; import { SESSION_USER_SELECT, type SessionUser } from "@/lib/auth/session-user"; -import { ensureDbCompatibility } from "@/lib/db-compat"; import { getEvent } from "@/lib/logging/context"; import { shouldEmitSecureCookie } from "@/lib/auth/secure-cookie"; import { isInputRefusal, isP2025 } from "@/lib/prisma-errors"; @@ -311,16 +310,6 @@ export async function getSession(): Promise<{ }; user: SessionUser; } | null> { - try { - await ensureDbCompatibility(); - } catch (error) { - getEvent()?.setError( - error instanceof Error - ? error - : new Error("DB compatibility check failed"), - ); - } - const cookieStore = await cookies(); const carried = readSessionCookie(cookieStore); if (!carried) return null; @@ -490,7 +479,7 @@ export async function getSession(): Promise<{ /** * Lightweight read of the signed-in user's persisted locale for the root * layout's first-paint language resolution. Deliberately NOT - * `getSession()`: no db-compat check, no sliding-expiry write, no cookie + * `getSession()`: no sliding-expiry write, no cookie * mutation — the root layout renders on every request in a context where * the cookie store is read-only, so the full session touch is both dead * weight and a throw hazard there. Returns null for missing/expired diff --git a/src/lib/db-compat.ts b/src/lib/db-compat.ts deleted file mode 100644 index b6e1151e0..000000000 --- a/src/lib/db-compat.ts +++ /dev/null @@ -1,277 +0,0 @@ -/** - * Schema-bootstrap path for fresh self-host containers. - * - * Production deploys run `prisma migrate deploy` from the entrypoint, - * so the canonical migration history lives in `prisma/migrations/`. - * That history is the source of truth — any new column or table must - * land there first. - * - * This file is the belt-and-suspenders companion: it issues - * idempotent `ALTER TABLE … ADD COLUMN IF NOT EXISTS` statements on - * boot so a container that came up against a database which somehow - * missed a migration (interrupted deploy, restored from an older - * dump, etc.) self-heals before the app starts taking traffic. - * - * Treat every statement here as a mirror of an existing migration — - * never invent schema in this file. If a column appears here without - * a corresponding entry under `prisma/migrations/`, that is a bug. - */ -import { prisma } from "@/lib/db"; - -let dbCompatibilityPromise: Promise | null = null; - -async function ensureUserSchema() { - await prisma.$executeRawUnsafe(` - ALTER TABLE "users" - ADD COLUMN IF NOT EXISTS "role" TEXT NOT NULL DEFAULT 'USER'; - `); - - await prisma.$executeRawUnsafe(` - ALTER TABLE "users" - ADD COLUMN IF NOT EXISTS "date_of_birth" TIMESTAMP(3); - `); - - await prisma.$executeRawUnsafe(` - ALTER TABLE "users" - ADD COLUMN IF NOT EXISTS "gender" TEXT; - `); - - await prisma.$executeRawUnsafe(` - ALTER TABLE "users" - ADD COLUMN IF NOT EXISTS "telegram_bot_token" TEXT; - `); - - await prisma.$executeRawUnsafe(` - ALTER TABLE "users" - ADD COLUMN IF NOT EXISTS "telegram_chat_id" TEXT; - `); - - await prisma.$executeRawUnsafe(` - ALTER TABLE "users" - ADD COLUMN IF NOT EXISTS "telegram_enabled" BOOLEAN NOT NULL DEFAULT false; - `); - - await prisma.$executeRawUnsafe(` - ALTER TABLE "users" - ADD COLUMN IF NOT EXISTS "withings_client_id_encrypted" TEXT; - `); - - await prisma.$executeRawUnsafe(` - ALTER TABLE "users" - ADD COLUMN IF NOT EXISTS "withings_client_secret_encrypted" TEXT; - `); - - await prisma.$executeRawUnsafe(` - ALTER TABLE "users" - ADD COLUMN IF NOT EXISTS "locale" TEXT; - `); - - await prisma.$executeRawUnsafe(` - ALTER TABLE "users" - ADD COLUMN IF NOT EXISTS "onboarding_completed_at" TIMESTAMP(3); - `); -} - -async function ensureAppSettingsSchema() { - await prisma.$executeRawUnsafe(` - CREATE TABLE IF NOT EXISTS "app_settings" ( - "id" TEXT NOT NULL DEFAULT 'singleton', - "registration_enabled" BOOLEAN NOT NULL DEFAULT true, - CONSTRAINT "app_settings_pkey" PRIMARY KEY ("id") - ); - `); - - await prisma.$executeRawUnsafe(` - INSERT INTO "app_settings" ("id", "registration_enabled") - VALUES ('singleton', true) - ON CONFLICT ("id") DO NOTHING; - `); - - await prisma.$executeRawUnsafe(` - ALTER TABLE "app_settings" - ADD COLUMN IF NOT EXISTS "default_locale" TEXT NOT NULL DEFAULT 'de'; - `); - - await prisma.$executeRawUnsafe(` - ALTER TABLE "app_settings" - ADD COLUMN IF NOT EXISTS "telegram_global" BOOLEAN NOT NULL DEFAULT true; - `); - - await prisma.$executeRawUnsafe(` - ALTER TABLE "app_settings" - ADD COLUMN IF NOT EXISTS "ntfy_global" BOOLEAN NOT NULL DEFAULT true; - `); - - await prisma.$executeRawUnsafe(` - ALTER TABLE "app_settings" - ADD COLUMN IF NOT EXISTS "web_push_global" BOOLEAN NOT NULL DEFAULT true; - `); - - await prisma.$executeRawUnsafe(` - ALTER TABLE "app_settings" - ADD COLUMN IF NOT EXISTS "api_global" BOOLEAN NOT NULL DEFAULT true; - `); - - await prisma.$executeRawUnsafe(` - ALTER TABLE "app_settings" - ADD COLUMN IF NOT EXISTS "web_push_vapid_public_key" TEXT; - `); - - await prisma.$executeRawUnsafe(` - ALTER TABLE "app_settings" - ADD COLUMN IF NOT EXISTS "web_push_vapid_private_key_encrypted" TEXT; - `); - - await prisma.$executeRawUnsafe(` - ALTER TABLE "app_settings" - ADD COLUMN IF NOT EXISTS "web_push_vapid_subject" TEXT; - `); - - await prisma.$executeRawUnsafe(` - ALTER TABLE "app_settings" - ADD COLUMN IF NOT EXISTS "umami_enabled" BOOLEAN NOT NULL DEFAULT false; - `); - - await prisma.$executeRawUnsafe(` - ALTER TABLE "app_settings" - ADD COLUMN IF NOT EXISTS "umami_script_url" TEXT; - `); - - await prisma.$executeRawUnsafe(` - ALTER TABLE "app_settings" - ADD COLUMN IF NOT EXISTS "umami_website_id" TEXT; - `); - - await prisma.$executeRawUnsafe(` - ALTER TABLE "app_settings" - ADD COLUMN IF NOT EXISTS "glitchtip_enabled" BOOLEAN NOT NULL DEFAULT false; - `); - - await prisma.$executeRawUnsafe(` - ALTER TABLE "app_settings" - ADD COLUMN IF NOT EXISTS "glitchtip_dsn" TEXT; - `); - - await prisma.$executeRawUnsafe(` - ALTER TABLE "app_settings" - ADD COLUMN IF NOT EXISTS "glitchtip_environment" TEXT; - `); -} - -async function ensureMedicationSchema() { - await prisma.$executeRawUnsafe(` - ALTER TABLE "medications" - ADD COLUMN IF NOT EXISTS "notifications_enabled" BOOLEAN NOT NULL DEFAULT true; - `); - - await prisma.$executeRawUnsafe(` - ALTER TABLE "medications" - ADD COLUMN IF NOT EXISTS "paused_at" TIMESTAMP(3); - `); -} - -async function ensureMedicationScheduleSchema() { - await prisma.$executeRawUnsafe(` - ALTER TABLE "medication_schedules" - ADD COLUMN IF NOT EXISTS "dose" TEXT; - `); - - await prisma.$executeRawUnsafe(` - ALTER TABLE "medication_schedules" - ADD COLUMN IF NOT EXISTS "days_of_week" TEXT; - `); -} - -async function ensureEnumCompatibility() { - await prisma.$executeRawUnsafe(` - ALTER TYPE "intake_source" ADD VALUE IF NOT EXISTS 'IMPORT'; - `); -} - -/** - * v1.5.0 — fresh container bootstrap for the persistent measurement- - * rollup table. Mirrors `prisma/migrations/0067_v1434_measurement_rollups` - * so a container that came up against a database which skipped the - * migration still self-heals before the app takes traffic. - * - * Idempotent: `CREATE TYPE` is guarded with a `pg_type` lookup - * (Postgres rejects `CREATE TYPE IF NOT EXISTS`), `CREATE TABLE` / - * `CREATE INDEX` carry `IF NOT EXISTS`. The FK is added via the - * `pg_constraint` lookup so re-running the bootstrap on a populated - * DB is a no-op. - */ -async function ensureMeasurementRollupsSchema() { - await prisma.$executeRawUnsafe(` - DO $$ - BEGIN - IF NOT EXISTS ( - SELECT 1 FROM pg_type - WHERE typname = 'measurement_rollup_granularity' - ) THEN - CREATE TYPE "measurement_rollup_granularity" - AS ENUM ('DAY', 'WEEK', 'MONTH', 'YEAR'); - END IF; - END - $$; - `); - - await prisma.$executeRawUnsafe(` - CREATE TABLE IF NOT EXISTS "measurement_rollups" ( - "user_id" TEXT NOT NULL, - "type" "measurement_type" NOT NULL, - "granularity" "measurement_rollup_granularity" NOT NULL, - "bucket_start" TIMESTAMPTZ(3) NOT NULL, - "count" INTEGER NOT NULL, - "mean" DOUBLE PRECISION NOT NULL, - "min_value" DOUBLE PRECISION NOT NULL, - "max_value" DOUBLE PRECISION NOT NULL, - "sd" DOUBLE PRECISION, - "slope" DOUBLE PRECISION, - "r2" DOUBLE PRECISION, - "computed_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - CONSTRAINT "measurement_rollups_pkey" - PRIMARY KEY ("user_id", "type", "granularity", "bucket_start") - ); - `); - - await prisma.$executeRawUnsafe(` - CREATE INDEX IF NOT EXISTS "measurement_rollups_user_type_granularity_bucket_desc_idx" - ON "measurement_rollups" ("user_id", "type", "granularity", "bucket_start" DESC); - `); - - await prisma.$executeRawUnsafe(` - DO $$ - BEGIN - IF NOT EXISTS ( - SELECT 1 FROM pg_constraint - WHERE conname = 'measurement_rollups_user_id_fkey' - ) THEN - ALTER TABLE "measurement_rollups" - ADD CONSTRAINT "measurement_rollups_user_id_fkey" - FOREIGN KEY ("user_id") REFERENCES "users"("id") - ON DELETE CASCADE ON UPDATE CASCADE; - END IF; - END - $$; - `); -} - -export async function ensureDbCompatibility() { - if (!dbCompatibilityPromise) { - dbCompatibilityPromise = Promise.all([ - ensureUserSchema(), - ensureAppSettingsSchema(), - ensureMedicationSchema(), - ensureMedicationScheduleSchema(), - ensureEnumCompatibility(), - ensureMeasurementRollupsSchema(), - ]) - .then(() => undefined) - .catch((error) => { - dbCompatibilityPromise = null; - throw error; - }); - } - - await dbCompatibilityPromise; -} diff --git a/tests/integration/account-deletion-grace.test.ts b/tests/integration/account-deletion-grace.test.ts index 410474e40..337331945 100644 --- a/tests/integration/account-deletion-grace.test.ts +++ b/tests/integration/account-deletion-grace.test.ts @@ -42,10 +42,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const adminNotices = vi.hoisted(() => [] as Array>); vi.mock("@/lib/notifications/dispatch-localised", () => ({ dispatchLocalisedNotification: vi.fn( diff --git a/tests/integration/account-grant-lifecycle.test.ts b/tests/integration/account-grant-lifecycle.test.ts index eb573e09d..1224db4ed 100644 --- a/tests/integration/account-grant-lifecycle.test.ts +++ b/tests/integration/account-grant-lifecycle.test.ts @@ -65,10 +65,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - let counter = 0; async function makeUser(label: string, role: "USER" | "ADMIN" = "USER") { diff --git a/tests/integration/acting-account-resolver.test.ts b/tests/integration/acting-account-resolver.test.ts index 923088189..5a7d6f4b1 100644 --- a/tests/integration/acting-account-resolver.test.ts +++ b/tests/integration/acting-account-resolver.test.ts @@ -63,10 +63,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - import { ACCOUNT_SELECTOR_HEADER, apiHandler, diff --git a/tests/integration/activity-calendar-read.test.ts b/tests/integration/activity-calendar-read.test.ts index 2c6c86c42..4169cda1e 100644 --- a/tests/integration/activity-calendar-read.test.ts +++ b/tests/integration/activity-calendar-read.test.ts @@ -15,10 +15,6 @@ import { readActivityDays } from "@/lib/activity/calendar-read"; import { invalidateUserTimezone } from "@/lib/tz/resolver"; import { userDayKey } from "@/lib/tz/format"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const prisma = getPrismaClient(); const TZ = "Europe/Berlin"; const USER = "activity-reader"; diff --git a/tests/integration/admin-backups-audit.test.ts b/tests/integration/admin-backups-audit.test.ts index 0a68fd32a..10b7b00c9 100644 --- a/tests/integration/admin-backups-audit.test.ts +++ b/tests/integration/admin-backups-audit.test.ts @@ -48,10 +48,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // Stub `getGlobalBoss` so the run endpoint can audit-log its happy // path without booting an actual pg-boss worker against the test DB. // The fake boss returns a deterministic job id so the audit row can be diff --git a/tests/integration/admin-backups-canonical-roundtrip.test.ts b/tests/integration/admin-backups-canonical-roundtrip.test.ts index 334f0507e..2235729ea 100644 --- a/tests/integration/admin-backups-canonical-roundtrip.test.ts +++ b/tests/integration/admin-backups-canonical-roundtrip.test.ts @@ -48,10 +48,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/cache/invalidate", () => ({ invalidateUserData: vi.fn(), })); diff --git a/tests/integration/admin-backups-download.test.ts b/tests/integration/admin-backups-download.test.ts index 7ceb2f598..faa2ea064 100644 --- a/tests/integration/admin-backups-download.test.ts +++ b/tests/integration/admin-backups-download.test.ts @@ -45,10 +45,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/admin-backups-restore.test.ts b/tests/integration/admin-backups-restore.test.ts index a27239b1b..e9bcfb8d8 100644 --- a/tests/integration/admin-backups-restore.test.ts +++ b/tests/integration/admin-backups-restore.test.ts @@ -49,10 +49,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/admin-backups-upload-too-large.test.ts b/tests/integration/admin-backups-upload-too-large.test.ts index f288cfa02..4ed38dddf 100644 --- a/tests/integration/admin-backups-upload-too-large.test.ts +++ b/tests/integration/admin-backups-upload-too-large.test.ts @@ -37,9 +37,6 @@ vi.mock("next/headers", async () => { })), }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/export/store-backup-blob", async (importOriginal) => { const actual = await importOriginal(); diff --git a/tests/integration/admin-backups-upload.test.ts b/tests/integration/admin-backups-upload.test.ts index 26edf32f6..bbbbf7707 100644 --- a/tests/integration/admin-backups-upload.test.ts +++ b/tests/integration/admin-backups-upload.test.ts @@ -41,10 +41,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/admin-data-wipe.test.ts b/tests/integration/admin-data-wipe.test.ts index b8dff9919..81605562c 100644 --- a/tests/integration/admin-data-wipe.test.ts +++ b/tests/integration/admin-data-wipe.test.ts @@ -66,10 +66,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/admin-drain-cumulative-endpoint.test.ts b/tests/integration/admin-drain-cumulative-endpoint.test.ts index 014deb2c7..4c9969aaa 100644 --- a/tests/integration/admin-drain-cumulative-endpoint.test.ts +++ b/tests/integration/admin-drain-cumulative-endpoint.test.ts @@ -35,10 +35,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/admin-host-metrics.test.ts b/tests/integration/admin-host-metrics.test.ts index 523b9025a..589d89c21 100644 --- a/tests/integration/admin-host-metrics.test.ts +++ b/tests/integration/admin-host-metrics.test.ts @@ -37,10 +37,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/admin-reset-password.test.ts b/tests/integration/admin-reset-password.test.ts index ca33f1497..db189f7d4 100644 --- a/tests/integration/admin-reset-password.test.ts +++ b/tests/integration/admin-reset-password.test.ts @@ -42,10 +42,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const STRONG_NEW_PW = "Adm1nReset!P@ssw0rd789"; async function seedCaller(role: "ADMIN" | "USER"): Promise<{ diff --git a/tests/integration/admin-rollups-recompute.test.ts b/tests/integration/admin-rollups-recompute.test.ts index 0ae49e279..780312dcb 100644 --- a/tests/integration/admin-rollups-recompute.test.ts +++ b/tests/integration/admin-rollups-recompute.test.ts @@ -42,10 +42,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/rollups/full-backfill", async (importOriginal) => { const actual = await importOriginal(); diff --git a/tests/integration/admin-support-report.test.ts b/tests/integration/admin-support-report.test.ts index 94b0e431e..2d26b17a5 100644 --- a/tests/integration/admin-support-report.test.ts +++ b/tests/integration/admin-support-report.test.ts @@ -37,10 +37,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - import { GET } from "@/app/api/admin/support-report/route"; beforeEach(async () => { diff --git a/tests/integration/admin-user-deletion.test.ts b/tests/integration/admin-user-deletion.test.ts index 4bfdcd2a7..7c1280d59 100644 --- a/tests/integration/admin-user-deletion.test.ts +++ b/tests/integration/admin-user-deletion.test.ts @@ -33,10 +33,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const DAY_MS = 24 * 60 * 60 * 1000; const HOUR_MS = 60 * 60 * 1000; diff --git a/tests/integration/ai-consent-explicit.test.ts b/tests/integration/ai-consent-explicit.test.ts index c10652bfc..ef3a706f0 100644 --- a/tests/integration/ai-consent-explicit.test.ts +++ b/tests/integration/ai-consent-explicit.test.ts @@ -45,10 +45,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const providerSpy = vi.fn(async () => ({ content: JSON.stringify({ summary: "A calm and steady stretch." }), model: "spy-model", diff --git a/tests/integration/ai-extraction-capability.test.ts b/tests/integration/ai-extraction-capability.test.ts index 263eb2c48..331b2e757 100644 --- a/tests/integration/ai-extraction-capability.test.ts +++ b/tests/integration/ai-extraction-capability.test.ts @@ -59,10 +59,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // v1.40 — a lab scan is queued for the background worker; a queue that takes // the send lets the route answer 202, and the test then runs the worker. vi.mock("@/lib/jobs/boss-instance", () => ({ diff --git a/tests/integration/ai-optional-actions.test.ts b/tests/integration/ai-optional-actions.test.ts index 255b4467a..403a21768 100644 --- a/tests/integration/ai-optional-actions.test.ts +++ b/tests/integration/ai-optional-actions.test.ts @@ -47,10 +47,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // Provider spies: a refusal must never reach either. const runner = vi.hoisted(() => ({ raw: vi.fn(), diff --git a/tests/integration/ai-optional-data-routes.test.ts b/tests/integration/ai-optional-data-routes.test.ts index 465e775d0..303682912 100644 --- a/tests/integration/ai-optional-data-routes.test.ts +++ b/tests/integration/ai-optional-data-routes.test.ts @@ -46,10 +46,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const DAY = 24 * 60 * 60 * 1000; const NOW = Date.now(); diff --git a/tests/integration/ai-optional-jobs.test.ts b/tests/integration/ai-optional-jobs.test.ts index f3b4c61d0..6af05fd89 100644 --- a/tests/integration/ai-optional-jobs.test.ts +++ b/tests/integration/ai-optional-jobs.test.ts @@ -23,10 +23,6 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const providerSpy = vi.fn(async () => ({ content: JSON.stringify({ summary: "A calm and steady stretch." }), model: "spy-model", diff --git a/tests/integration/ai-optional-mixed-routes.test.ts b/tests/integration/ai-optional-mixed-routes.test.ts index 2475a1ab5..3e0683969 100644 --- a/tests/integration/ai-optional-mixed-routes.test.ts +++ b/tests/integration/ai-optional-mixed-routes.test.ts @@ -46,10 +46,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // The two enqueue seams a mixed read may reach. Spied, never run: the test // asserts they are not called while a capability is unavailable. const enqueueNarrativeWarm = vi.fn(async () => undefined); diff --git a/tests/integration/ai-provider-key-removal.test.ts b/tests/integration/ai-provider-key-removal.test.ts index c1944acf7..c307d9da9 100644 --- a/tests/integration/ai-provider-key-removal.test.ts +++ b/tests/integration/ai-provider-key-removal.test.ts @@ -37,10 +37,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - async function seedAccountWithKeys(): Promise { const prisma = getPrismaClient(); const { encrypt } = await import("@/lib/crypto"); diff --git a/tests/integration/ai-usage-levels.test.ts b/tests/integration/ai-usage-levels.test.ts index f19361326..a0c629e33 100644 --- a/tests/integration/ai-usage-levels.test.ts +++ b/tests/integration/ai-usage-levels.test.ts @@ -43,10 +43,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // The queue is not under test: record what the invalidator enqueues. const enqueued = vi.hoisted(() => [] as Array<{ metric: string }>); vi.mock("@/lib/jobs/insight-status-generate-shared", async (orig) => ({ diff --git a/tests/integration/analytics-bp-aggregate-paged.test.ts b/tests/integration/analytics-bp-aggregate-paged.test.ts index ca33c5d19..ac8b48648 100644 --- a/tests/integration/analytics-bp-aggregate-paged.test.ts +++ b/tests/integration/analytics-bp-aggregate-paged.test.ts @@ -50,10 +50,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // Read wide-events back out of the in-memory ring buffer the // `emitEvent` transport already populates — avoids coupling to the // Loki/stdout plumbing and works regardless of `LOG_SAMPLE_RATE`. diff --git a/tests/integration/analytics-health-score.test.ts b/tests/integration/analytics-health-score.test.ts index b2a0b0363..ac765f95c 100644 --- a/tests/integration/analytics-health-score.test.ts +++ b/tests/integration/analytics-health-score.test.ts @@ -38,10 +38,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/analytics-sleep-stages.test.ts b/tests/integration/analytics-sleep-stages.test.ts index a944b591c..da5cb8513 100644 --- a/tests/integration/analytics-sleep-stages.test.ts +++ b/tests/integration/analytics-sleep-stages.test.ts @@ -39,10 +39,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/analytics-summaries-slice.test.ts b/tests/integration/analytics-summaries-slice.test.ts index 7c88560a8..52a71b694 100644 --- a/tests/integration/analytics-summaries-slice.test.ts +++ b/tests/integration/analytics-summaries-slice.test.ts @@ -51,10 +51,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/analytics-weekday-tz.test.ts b/tests/integration/analytics-weekday-tz.test.ts index 2e2101d2f..1f23bc43a 100644 --- a/tests/integration/analytics-weekday-tz.test.ts +++ b/tests/integration/analytics-weekday-tz.test.ts @@ -43,10 +43,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/apns-dispatch.test.ts b/tests/integration/apns-dispatch.test.ts index 03b4fe0bb..9995a4f3c 100644 --- a/tests/integration/apns-dispatch.test.ts +++ b/tests/integration/apns-dispatch.test.ts @@ -55,10 +55,6 @@ vi.mock("@/lib/notifications/senders/telegram", () => ({ sendViaTelegram: (...args: unknown[]) => telegramSendMock(...args), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - import { getPrismaClient, truncateAllTables } from "./setup"; const TEST_USER_ID = "user-apns-dispatch-test"; diff --git a/tests/integration/apple-health-import-ecg.test.ts b/tests/integration/apple-health-import-ecg.test.ts index 2bd581f7d..751846661 100644 --- a/tests/integration/apple-health-import-ecg.test.ts +++ b/tests/integration/apple-health-import-ecg.test.ts @@ -23,10 +23,6 @@ import { } from "@/lib/jobs/apple-health-import-worker"; import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/jobs/boss-instance", () => ({ getGlobalBoss: vi.fn(() => null), })); diff --git a/tests/integration/apple-health-import-reconcile.test.ts b/tests/integration/apple-health-import-reconcile.test.ts index 7dbd7c9a4..191508724 100644 --- a/tests/integration/apple-health-import-reconcile.test.ts +++ b/tests/integration/apple-health-import-reconcile.test.ts @@ -26,10 +26,6 @@ import { _setWorkerPrismaForTests, } from "@/lib/jobs/apple-health-import-worker"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // Controllable pg-boss handle: the reconcile consults `getJobById` for a // row's real queue state. const bossMock = vi.hoisted(() => ({ diff --git a/tests/integration/apple-health-import-rollup-window.test.ts b/tests/integration/apple-health-import-rollup-window.test.ts index be558605e..a42b8be96 100644 --- a/tests/integration/apple-health-import-rollup-window.test.ts +++ b/tests/integration/apple-health-import-rollup-window.test.ts @@ -24,9 +24,6 @@ import { } from "@/lib/jobs/apple-health-import-worker"; import { ROLLUP_FOLD_WINDOW_MS } from "@/lib/rollups/measurement-rollups"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/jobs/boss-instance", () => ({ getGlobalBoss: vi.fn(() => null), })); diff --git a/tests/integration/apple-health-import-worker-failures.test.ts b/tests/integration/apple-health-import-worker-failures.test.ts index 2793396ad..51bae2417 100644 --- a/tests/integration/apple-health-import-worker-failures.test.ts +++ b/tests/integration/apple-health-import-worker-failures.test.ts @@ -34,10 +34,6 @@ import { type AppleHealthImportPayload, } from "@/lib/jobs/apple-health-import-worker"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // No pg-boss attached — the end-of-import rollup fold's enqueue helpers // are silent no-ops without a boss. vi.mock("@/lib/jobs/boss-instance", () => ({ diff --git a/tests/integration/auth-flow.test.ts b/tests/integration/auth-flow.test.ts index da873f510..d57c94b55 100644 --- a/tests/integration/auth-flow.test.ts +++ b/tests/integration/auth-flow.test.ts @@ -1,9 +1,8 @@ /** * Integration regression guard for `src/lib/auth/session.ts`. * - * The session helpers couple Postgres (`Session` row), the `cookies()` - * adapter from `next/headers`, and the `db-compat` schema-migration - * fallback. A unit test with a mocked Prisma client cannot verify the + * The session helpers couple Postgres (`Session` row) and the `cookies()` + * adapter from `next/headers`. A unit test with a mocked Prisma client cannot verify the * end-to-end flow — particularly the expired-session purge behaviour, * which silently deletes the row before returning null. */ @@ -38,12 +37,6 @@ vi.mock("next/headers", async () => { }; }); -// `db-compat` runs ALTER TABLE IF NOT EXISTS statements that are no-ops -// against a freshly-migrated schema but slow tests down — short-circuit. -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/auth-ledger-cleanup.test.ts b/tests/integration/auth-ledger-cleanup.test.ts index 8de6fe9bd..be4ddcee4 100644 --- a/tests/integration/auth-ledger-cleanup.test.ts +++ b/tests/integration/auth-ledger-cleanup.test.ts @@ -13,10 +13,6 @@ import { cleanupAuthLedgers } from "@/lib/jobs/auth-ledger-cleanup"; import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const USER = "auth-ledger-user"; const NOW = new Date("2026-10-20T03:35:00.000Z"); const DAY = 86_400_000; diff --git a/tests/integration/auth-me-ai-capabilities.test.ts b/tests/integration/auth-me-ai-capabilities.test.ts index 090dbcb14..5fa15f6a8 100644 --- a/tests/integration/auth-me-ai-capabilities.test.ts +++ b/tests/integration/auth-me-ai-capabilities.test.ts @@ -41,10 +41,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const ALL = [ "coach", "briefing", diff --git a/tests/integration/auth-me-coach-reasoning.test.ts b/tests/integration/auth-me-coach-reasoning.test.ts index 186352bd5..f93095591 100644 --- a/tests/integration/auth-me-coach-reasoning.test.ts +++ b/tests/integration/auth-me-coach-reasoning.test.ts @@ -40,10 +40,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - interface CoachReasoning { level: string; preference: string; diff --git a/tests/integration/auth-password-change.test.ts b/tests/integration/auth-password-change.test.ts index 90fa48f52..18cb9e89a 100644 --- a/tests/integration/auth-password-change.test.ts +++ b/tests/integration/auth-password-change.test.ts @@ -44,10 +44,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const STRONG_CURRENT = "OldStrongP@ssw0rd!123abc"; const STRONG_NEW = "NewStrongP@ssw0rd!456xyz"; diff --git a/tests/integration/backup-chunked-storage.test.ts b/tests/integration/backup-chunked-storage.test.ts index b53e9ae51..1d2418c27 100644 --- a/tests/integration/backup-chunked-storage.test.ts +++ b/tests/integration/backup-chunked-storage.test.ts @@ -52,10 +52,6 @@ vi.mock("next/headers", async () => { })), }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const COUNT = 12_000; /** Small pieces, so a test-sized record spans many of them. */ const SMALL = { chunkBytes: 4 * 1024 }; diff --git a/tests/integration/backup-custom-cycle-symptom-restore.test.ts b/tests/integration/backup-custom-cycle-symptom-restore.test.ts index 98ff701ba..8f621afa6 100644 --- a/tests/integration/backup-custom-cycle-symptom-restore.test.ts +++ b/tests/integration/backup-custom-cycle-symptom-restore.test.ts @@ -63,10 +63,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/cache/invalidate", () => ({ invalidateUserData: vi.fn(), })); diff --git a/tests/integration/backup-cycle-cross-key-restore.test.ts b/tests/integration/backup-cycle-cross-key-restore.test.ts index 4d3f50aee..0ad7ca5eb 100644 --- a/tests/integration/backup-cycle-cross-key-restore.test.ts +++ b/tests/integration/backup-cycle-cross-key-restore.test.ts @@ -46,10 +46,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/cache/invalidate", () => ({ invalidateUserData: vi.fn(), })); diff --git a/tests/integration/backup-cycle-symptom-severity-restore.test.ts b/tests/integration/backup-cycle-symptom-severity-restore.test.ts index cf392785b..762193a5b 100644 --- a/tests/integration/backup-cycle-symptom-severity-restore.test.ts +++ b/tests/integration/backup-cycle-symptom-severity-restore.test.ts @@ -51,10 +51,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/cache/invalidate", () => ({ invalidateUserData: vi.fn(), })); diff --git a/tests/integration/backup-document-import-ledger.test.ts b/tests/integration/backup-document-import-ledger.test.ts index fa207d3c8..228c97d89 100644 --- a/tests/integration/backup-document-import-ledger.test.ts +++ b/tests/integration/backup-document-import-ledger.test.ts @@ -45,10 +45,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/cache/invalidate", async (importOriginal) => { const actual = await importOriginal(); diff --git a/tests/integration/backup-document-streaming.test.ts b/tests/integration/backup-document-streaming.test.ts index 63c087d9d..2768a7644 100644 --- a/tests/integration/backup-document-streaming.test.ts +++ b/tests/integration/backup-document-streaming.test.ts @@ -42,10 +42,6 @@ import { STORED_BACKUP_SELECT } from "@/lib/export/stored-backup"; import { parseBackupPayload } from "@/lib/validations/backup"; import { summarizeBackup } from "@/lib/validations/backup-summary"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const prisma = getPrismaClient(); const OWNER_ID = "backup-document-owner"; diff --git a/tests/integration/backup-dr-stamps-and-ciphertext.test.ts b/tests/integration/backup-dr-stamps-and-ciphertext.test.ts index 512f70636..4a90e80a6 100644 --- a/tests/integration/backup-dr-stamps-and-ciphertext.test.ts +++ b/tests/integration/backup-dr-stamps-and-ciphertext.test.ts @@ -53,10 +53,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const prisma = getPrismaClient(); const CREATED = new Date("2026-03-03T03:03:03.000Z"); const UPDATED = new Date("2026-04-04T04:04:04.000Z"); diff --git a/tests/integration/backup-erased-account-restore.test.ts b/tests/integration/backup-erased-account-restore.test.ts index 3b2ea8c7a..6eb2ae2e3 100644 --- a/tests/integration/backup-erased-account-restore.test.ts +++ b/tests/integration/backup-erased-account-restore.test.ts @@ -78,10 +78,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/notifications/dispatch-localised", () => ({ dispatchLocalisedNotification: vi.fn(async () => undefined), })); diff --git a/tests/integration/backup-key-needs.test.ts b/tests/integration/backup-key-needs.test.ts index 3aeb4179c..9edbb180f 100644 --- a/tests/integration/backup-key-needs.test.ts +++ b/tests/integration/backup-key-needs.test.ts @@ -41,10 +41,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const saved = { key: process.env.ENCRYPTION_KEY, keys: process.env.ENCRYPTION_KEYS, diff --git a/tests/integration/backup-legacy-rmssd-restore.test.ts b/tests/integration/backup-legacy-rmssd-restore.test.ts index 25a8ad4c2..45c376a23 100644 --- a/tests/integration/backup-legacy-rmssd-restore.test.ts +++ b/tests/integration/backup-legacy-rmssd-restore.test.ts @@ -39,10 +39,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/cache/invalidate", async (importOriginal) => { const actual = await importOriginal(); diff --git a/tests/integration/backup-missing-section-restore.test.ts b/tests/integration/backup-missing-section-restore.test.ts index 1c3afad66..7eaaba04a 100644 --- a/tests/integration/backup-missing-section-restore.test.ts +++ b/tests/integration/backup-missing-section-restore.test.ts @@ -57,10 +57,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/cache/invalidate", () => ({ invalidateUserData: vi.fn(), })); diff --git a/tests/integration/backup-mood-label-cross-key-restore.test.ts b/tests/integration/backup-mood-label-cross-key-restore.test.ts index f1a0b6719..7a52cc152 100644 --- a/tests/integration/backup-mood-label-cross-key-restore.test.ts +++ b/tests/integration/backup-mood-label-cross-key-restore.test.ts @@ -40,10 +40,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/cache/invalidate", () => ({ invalidateUserData: vi.fn(), })); diff --git a/tests/integration/backup-parity.test.ts b/tests/integration/backup-parity.test.ts index 3e83e614c..c8e2d98f1 100644 --- a/tests/integration/backup-parity.test.ts +++ b/tests/integration/backup-parity.test.ts @@ -123,10 +123,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - /** What the restore asked of the world outside its transaction. */ const effects = vi.hoisted(() => ({ invalidated: [] as string[], diff --git a/tests/integration/backup-portable-file-restore.test.ts b/tests/integration/backup-portable-file-restore.test.ts index a840163ee..380f891cd 100644 --- a/tests/integration/backup-portable-file-restore.test.ts +++ b/tests/integration/backup-portable-file-restore.test.ts @@ -76,10 +76,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/cache/invalidate", () => ({ invalidateUserData: vi.fn(), })); diff --git a/tests/integration/backup-preview.test.ts b/tests/integration/backup-preview.test.ts index 81757c6c7..03702b3f3 100644 --- a/tests/integration/backup-preview.test.ts +++ b/tests/integration/backup-preview.test.ts @@ -45,10 +45,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const opened = vi.hoisted(() => ({ count: 0 })); vi.mock("@/lib/export/stored-backup", async (importOriginal) => { const actual = diff --git a/tests/integration/backup-qualitative-lab-restore.test.ts b/tests/integration/backup-qualitative-lab-restore.test.ts index c0f0dc071..232ffe4ee 100644 --- a/tests/integration/backup-qualitative-lab-restore.test.ts +++ b/tests/integration/backup-qualitative-lab-restore.test.ts @@ -44,10 +44,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/cache/invalidate", async (importOriginal) => { const actual = await importOriginal(); diff --git a/tests/integration/backup-restore-job.test.ts b/tests/integration/backup-restore-job.test.ts index 29c0be93d..7d3d934ba 100644 --- a/tests/integration/backup-restore-job.test.ts +++ b/tests/integration/backup-restore-job.test.ts @@ -75,10 +75,6 @@ vi.mock("next/headers", async () => { })), }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - /** A queue that records what it is sent and what it is told is finished. */ function recordingBoss() { const sent: Array<{ queue: string; data: unknown }> = []; diff --git a/tests/integration/backup-restore-memory.test.ts b/tests/integration/backup-restore-memory.test.ts index a3d00e570..56648a68b 100644 --- a/tests/integration/backup-restore-memory.test.ts +++ b/tests/integration/backup-restore-memory.test.ts @@ -35,10 +35,6 @@ import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const OWNER = "restore-memory-owner"; const ROWS = 150_000; diff --git a/tests/integration/backup-restore-statement-timeout.test.ts b/tests/integration/backup-restore-statement-timeout.test.ts index 0fd6d182a..cd8ef27ce 100644 --- a/tests/integration/backup-restore-statement-timeout.test.ts +++ b/tests/integration/backup-restore-statement-timeout.test.ts @@ -71,10 +71,6 @@ vi.mock("next/headers", async () => { })), }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const COUNT = 600; /** diff --git a/tests/integration/backup-round-trip.test.ts b/tests/integration/backup-round-trip.test.ts index 0698b1bb1..cc617b2ec 100644 --- a/tests/integration/backup-round-trip.test.ts +++ b/tests/integration/backup-round-trip.test.ts @@ -147,10 +147,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/cache/invalidate", () => ({ invalidateUserData: vi.fn(), })); diff --git a/tests/integration/backup-token-link-restore.test.ts b/tests/integration/backup-token-link-restore.test.ts index f5881593b..802439814 100644 --- a/tests/integration/backup-token-link-restore.test.ts +++ b/tests/integration/backup-token-link-restore.test.ts @@ -39,10 +39,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/cache/invalidate", async (importOriginal) => { const actual = await importOriginal(); diff --git a/tests/integration/backup-unknown-catalogue-key-restore.test.ts b/tests/integration/backup-unknown-catalogue-key-restore.test.ts index 6347ba32f..7248b910c 100644 --- a/tests/integration/backup-unknown-catalogue-key-restore.test.ts +++ b/tests/integration/backup-unknown-catalogue-key-restore.test.ts @@ -61,10 +61,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/cache/invalidate", () => ({ invalidateUserData: vi.fn(), })); diff --git a/tests/integration/backup-upload-document-memory.test.ts b/tests/integration/backup-upload-document-memory.test.ts index c3221d22f..1cf0c8949 100644 --- a/tests/integration/backup-upload-document-memory.test.ts +++ b/tests/integration/backup-upload-document-memory.test.ts @@ -47,10 +47,6 @@ import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; import { cookieJar, headerJar } from "./mock-next-headers"; import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const prisma = getPrismaClient(); /** A hundred documents of 2.75 MiB of base64 each: about 275 MB of vault. */ diff --git a/tests/integration/backup-visits-cross-key-restore.test.ts b/tests/integration/backup-visits-cross-key-restore.test.ts index 234f6c136..c3b11f06e 100644 --- a/tests/integration/backup-visits-cross-key-restore.test.ts +++ b/tests/integration/backup-visits-cross-key-restore.test.ts @@ -43,10 +43,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/cache/invalidate", () => ({ invalidateUserData: vi.fn(), })); diff --git a/tests/integration/bearer-scope-enforcement.test.ts b/tests/integration/bearer-scope-enforcement.test.ts index 40a5b3b0d..551fbdd45 100644 --- a/tests/integration/bearer-scope-enforcement.test.ts +++ b/tests/integration/bearer-scope-enforcement.test.ts @@ -55,10 +55,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/biomarker-backup-completeness.test.ts b/tests/integration/biomarker-backup-completeness.test.ts index 7fe4faa53..f787d0510 100644 --- a/tests/integration/biomarker-backup-completeness.test.ts +++ b/tests/integration/biomarker-backup-completeness.test.ts @@ -44,10 +44,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/cache/invalidate", async (importOriginal) => ({ ...(await importOriginal()), invalidateUserData: vi.fn(), diff --git a/tests/integration/biomarker-detail.test.ts b/tests/integration/biomarker-detail.test.ts index 2de6f2362..b9c408396 100644 --- a/tests/integration/biomarker-detail.test.ts +++ b/tests/integration/biomarker-detail.test.ts @@ -39,10 +39,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/biomarker-merge.test.ts b/tests/integration/biomarker-merge.test.ts index 87ac6b667..b3fdad383 100644 --- a/tests/integration/biomarker-merge.test.ts +++ b/tests/integration/biomarker-merge.test.ts @@ -35,10 +35,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const USER_ID = "user-biomarker-merge"; const OTHER_ID = "user-biomarker-merge-other"; const DAY_MS = 86_400_000; diff --git a/tests/integration/blood-pressure-reading-delete.test.ts b/tests/integration/blood-pressure-reading-delete.test.ts index 2e930187d..445511cc8 100644 --- a/tests/integration/blood-pressure-reading-delete.test.ts +++ b/tests/integration/blood-pressure-reading-delete.test.ts @@ -44,10 +44,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const USER = "user-bp-reading-delete"; const AT = new Date("2026-10-20T07:30:00.000Z"); const MINUTE_LATER = new Date(AT.getTime() + 60_000); diff --git a/tests/integration/body-sites.test.ts b/tests/integration/body-sites.test.ts index 77fb04830..5ec9a2f8c 100644 --- a/tests/integration/body-sites.test.ts +++ b/tests/integration/body-sites.test.ts @@ -51,10 +51,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const OWNER_ID = "body-site-owner"; const DELEGATE_ID = "body-site-delegate"; diff --git a/tests/integration/bp-in-target.test.ts b/tests/integration/bp-in-target.test.ts index 5dac3ef43..45927342a 100644 --- a/tests/integration/bp-in-target.test.ts +++ b/tests/integration/bp-in-target.test.ts @@ -55,10 +55,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/briefing-cache-locale.test.ts b/tests/integration/briefing-cache-locale.test.ts index 8589571c3..6dffd1396 100644 --- a/tests/integration/briefing-cache-locale.test.ts +++ b/tests/integration/briefing-cache-locale.test.ts @@ -36,10 +36,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const BRIEFING = { greeting: "Good morning", paragraph: "Your readings this week stayed inside their usual range.", diff --git a/tests/integration/bulk-delete-source-filter.test.ts b/tests/integration/bulk-delete-source-filter.test.ts index 5ea9af473..a3056fbe1 100644 --- a/tests/integration/bulk-delete-source-filter.test.ts +++ b/tests/integration/bulk-delete-source-filter.test.ts @@ -41,10 +41,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - async function seedUser(id: string, username: string) { const prisma = getPrismaClient(); await prisma.user.create({ diff --git a/tests/integration/bulk-writer-cache-invalidation.test.ts b/tests/integration/bulk-writer-cache-invalidation.test.ts index 81ce584cc..6225069bd 100644 --- a/tests/integration/bulk-writer-cache-invalidation.test.ts +++ b/tests/integration/bulk-writer-cache-invalidation.test.ts @@ -35,9 +35,6 @@ import { sweepStaleSleepSegments } from "@/lib/sleep/sweep-stale-segments"; import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/jobs/boss-instance", () => ({ getGlobalBoss: vi.fn(() => null), })); diff --git a/tests/integration/cache-invalidation-coverage.test.ts b/tests/integration/cache-invalidation-coverage.test.ts index 578463b94..821729db7 100644 --- a/tests/integration/cache-invalidation-coverage.test.ts +++ b/tests/integration/cache-invalidation-coverage.test.ts @@ -42,21 +42,6 @@ vi.mock("next/headers", async () => { }; }); -// `ensureDbCompatibility` is intentionally LEFT REAL for this file — -// the admin app-settings PUT touches the `app_settings` table whose -// columns land via that helper rather than the migration set in -// `prisma/migrations/`. Mocking it out (as the other integration tests -// do for speed) would make the upsert reference a `default_locale` -// column the test container doesn't carry. Letting the real helper -// run inside the route handler keeps the schema consistent for the -// admin-settings test case in this file. -// -// `isolate: false` in `vitest.integration.config.mts` shares module -// state across files, so a sibling test file that mocks db-compat -// would leak its mock here. The beforeEach below explicitly imports -// the actual implementation through `vi.importActual` and runs it on -// each test setup to defeat that bleed-through. - const PRIMARY_USER_ID = "user-cache-invalidation-primary"; const OTHER_USER_ID = "user-cache-invalidation-other"; @@ -98,12 +83,6 @@ beforeEach(async () => { headerJar.clear(); const { __resetAllCachesForTests } = await import("@/lib/cache/server-cache"); __resetAllCachesForTests(); - // Run the real db-compat helper directly so a sibling test file's - // `vi.mock("@/lib/db-compat", ...)` cannot leave the `app_settings` - // table missing columns the admin-settings PUT writes through. - const realDbCompat = - await vi.importActual("@/lib/db-compat"); - await realDbCompat.ensureDbCompatibility(); await seedUsers(); }); diff --git a/tests/integration/changepoint-extended.test.ts b/tests/integration/changepoint-extended.test.ts index d369dc6b6..536c9df0c 100644 --- a/tests/integration/changepoint-extended.test.ts +++ b/tests/integration/changepoint-extended.test.ts @@ -9,10 +9,6 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; import { buildExtendedChangepointSignals } from "@/lib/insights/derived/changepoint"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const DAY_MS = 24 * 60 * 60 * 1000; function at(daysAgo: number): Date { diff --git a/tests/integration/chart-all-range-meaning.test.ts b/tests/integration/chart-all-range-meaning.test.ts index bfc406909..8f2400eaf 100644 --- a/tests/integration/chart-all-range-meaning.test.ts +++ b/tests/integration/chart-all-range-meaning.test.ts @@ -18,9 +18,6 @@ import { recomputeUserRollups } from "@/lib/rollups/measurement-rollups"; import { bucketTimeSeries } from "@/lib/charts/bucket-time-series"; import type { MeasurementType } from "@/generated/prisma/client"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/jobs/boss-instance", () => ({ getGlobalBoss: vi.fn(() => null), })); diff --git a/tests/integration/chart-overlay-prefs.test.ts b/tests/integration/chart-overlay-prefs.test.ts index d86fe392e..93ff64412 100644 --- a/tests/integration/chart-overlay-prefs.test.ts +++ b/tests/integration/chart-overlay-prefs.test.ts @@ -40,10 +40,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/chart-range-dense.test.ts b/tests/integration/chart-range-dense.test.ts index 5e36f45cd..e7a39d9df 100644 --- a/tests/integration/chart-range-dense.test.ts +++ b/tests/integration/chart-range-dense.test.ts @@ -37,10 +37,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const USER = "chart-range-dense-user"; const TZ = "Europe/Berlin"; const TO = new Date("2026-09-20T18:00:00.000Z"); diff --git a/tests/integration/checkup-stays-due.test.ts b/tests/integration/checkup-stays-due.test.ts index efb3a1fde..8c2dd23e9 100644 --- a/tests/integration/checkup-stays-due.test.ts +++ b/tests/integration/checkup-stays-due.test.ts @@ -29,10 +29,6 @@ import type { DispatchOutcome } from "@/lib/notifications/dispatcher"; import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const MIGRATION = readFileSync( path.join( process.cwd(), diff --git a/tests/integration/coach-availability-probe.test.ts b/tests/integration/coach-availability-probe.test.ts index 4ea36eed0..6713eec3f 100644 --- a/tests/integration/coach-availability-probe.test.ts +++ b/tests/integration/coach-availability-probe.test.ts @@ -16,10 +16,6 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; import { probeCoachAvailability } from "@/lib/ai/coach/tools/availability"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const NOW = new Date("2026-07-26T09:00:00Z"); beforeEach(async () => { diff --git a/tests/integration/coach-chat.test.ts b/tests/integration/coach-chat.test.ts index 00c6cb523..0eb20b8af 100644 --- a/tests/integration/coach-chat.test.ts +++ b/tests/integration/coach-chat.test.ts @@ -47,10 +47,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // Stub feature extraction so we don't need a full measurement seed. vi.mock("@/lib/insights/features", () => ({ extractFeatures: vi.fn(async () => ({ diff --git a/tests/integration/coach-conversation-delete.test.ts b/tests/integration/coach-conversation-delete.test.ts index 4216ab8d8..c46de55ef 100644 --- a/tests/integration/coach-conversation-delete.test.ts +++ b/tests/integration/coach-conversation-delete.test.ts @@ -34,10 +34,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/coach-conversations.test.ts b/tests/integration/coach-conversations.test.ts index 7ca67f8c5..41261eec0 100644 --- a/tests/integration/coach-conversations.test.ts +++ b/tests/integration/coach-conversations.test.ts @@ -37,10 +37,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/coach-feedback.test.ts b/tests/integration/coach-feedback.test.ts index af7f7daad..292f6103a 100644 --- a/tests/integration/coach-feedback.test.ts +++ b/tests/integration/coach-feedback.test.ts @@ -36,10 +36,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/coach-history-reach.test.ts b/tests/integration/coach-history-reach.test.ts index d32a45b0c..ad726dcdb 100644 --- a/tests/integration/coach-history-reach.test.ts +++ b/tests/integration/coach-history-reach.test.ts @@ -26,9 +26,6 @@ import { readCoachReach } from "@/lib/ai/coach/history-reach-read"; import { UNBOUNDED_REACH } from "@/lib/ai/coach/history-reach"; import { assembleTurnContext } from "@/lib/ai/coach/turn/context"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/jobs/boss-instance", () => ({ getGlobalBoss: vi.fn(() => null), })); diff --git a/tests/integration/coach-labs-history.test.ts b/tests/integration/coach-labs-history.test.ts index 7273636d4..cf8ed571a 100644 --- a/tests/integration/coach-labs-history.test.ts +++ b/tests/integration/coach-labs-history.test.ts @@ -24,9 +24,6 @@ import { } from "@/lib/ai/coach/history-reach"; import type { CoachLabsBlock } from "@/lib/ai/coach/labs-snapshot"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/jobs/boss-instance", () => ({ getGlobalBoss: vi.fn(() => null), })); diff --git a/tests/integration/coach-metric-table.test.ts b/tests/integration/coach-metric-table.test.ts index 479725ea0..f1537ade2 100644 --- a/tests/integration/coach-metric-table.test.ts +++ b/tests/integration/coach-metric-table.test.ts @@ -26,9 +26,6 @@ import { executeCoachTool } from "@/lib/ai/coach/tools/executor"; import { appendMessage, createConversation } from "@/lib/ai/coach/persistence"; import type { CoachResultTable } from "@/lib/ai/coach/types"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/jobs/boss-instance", () => ({ getGlobalBoss: vi.fn(() => null), })); diff --git a/tests/integration/coach-prefs.test.ts b/tests/integration/coach-prefs.test.ts index 38168275a..b5dc6a91e 100644 --- a/tests/integration/coach-prefs.test.ts +++ b/tests/integration/coach-prefs.test.ts @@ -39,10 +39,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/coach-series-long-window.test.ts b/tests/integration/coach-series-long-window.test.ts index f14546702..856444d19 100644 --- a/tests/integration/coach-series-long-window.test.ts +++ b/tests/integration/coach-series-long-window.test.ts @@ -33,9 +33,6 @@ import { getServerTranslator } from "@/lib/i18n/server-translator"; import { pluralKey } from "@/lib/i18n/plural"; import type { MeasurementType } from "@/generated/prisma/client"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/jobs/boss-instance", () => ({ getGlobalBoss: vi.fn(() => null), })); diff --git a/tests/integration/coach-stale-rollup-tail.test.ts b/tests/integration/coach-stale-rollup-tail.test.ts index 60a579b4b..a9093dcfc 100644 --- a/tests/integration/coach-stale-rollup-tail.test.ts +++ b/tests/integration/coach-stale-rollup-tail.test.ts @@ -37,10 +37,6 @@ process.env.ENCRYPTION_KEY ??= import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - import { buildCoachSnapshot } from "@/lib/ai/coach/snapshot"; import { recomputeUserRollups } from "@/lib/rollups/measurement-rollups"; diff --git a/tests/integration/coach-trail.test.ts b/tests/integration/coach-trail.test.ts index 3878e6fdd..60f568d4d 100644 --- a/tests/integration/coach-trail.test.ts +++ b/tests/integration/coach-trail.test.ts @@ -36,10 +36,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // Whether the record's Coach text may be shown is its own suite's question // (`ai-optional-*`); here it is a switch. vi.mock("@/lib/ai/capabilities/gate", async (importOriginal) => ({ diff --git a/tests/integration/codex-credential-park.test.ts b/tests/integration/codex-credential-park.test.ts index 1eb6c4c8c..1857c5619 100644 --- a/tests/integration/codex-credential-park.test.ts +++ b/tests/integration/codex-credential-park.test.ts @@ -40,10 +40,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/logging/signal", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, emitSignal: vi.fn(actual.emitSignal) }; diff --git a/tests/integration/compaction-tombstones.test.ts b/tests/integration/compaction-tombstones.test.ts index a4afa1c42..d955dcbec 100644 --- a/tests/integration/compaction-tombstones.test.ts +++ b/tests/integration/compaction-tombstones.test.ts @@ -41,10 +41,6 @@ vi.mock("next/headers", async () => { })), }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const HOUR = 3_600_000; const DAY = 24 * HOUR; const NOW = Date.now(); diff --git a/tests/integration/correlation-extended-channels.test.ts b/tests/integration/correlation-extended-channels.test.ts index 30c5c8bf0..9082e4440 100644 --- a/tests/integration/correlation-extended-channels.test.ts +++ b/tests/integration/correlation-extended-channels.test.ts @@ -17,10 +17,6 @@ import { fetchTrainingSeries, } from "@/lib/insights/correlation-channel-series"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const DAY_MS = 24 * 60 * 60 * 1000; const TZ = "Europe/Berlin"; diff --git a/tests/integration/correlation-findings-before-seasonal-adjustment.test.ts b/tests/integration/correlation-findings-before-seasonal-adjustment.test.ts index 91af5b9ee..b43a8b830 100644 --- a/tests/integration/correlation-findings-before-seasonal-adjustment.test.ts +++ b/tests/integration/correlation-findings-before-seasonal-adjustment.test.ts @@ -22,10 +22,6 @@ import { restoreProfileData, } from "@/lib/export/profile-backup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const DAY_MS = 24 * 60 * 60 * 1000; const MARKER = new Date("2026-10-08T00:00:00.000Z"); const BEFORE = new Date(MARKER.getTime() - 30 * DAY_MS); diff --git a/tests/integration/correlations-rollup-swap.test.ts b/tests/integration/correlations-rollup-swap.test.ts index c160aa598..e27eba182 100644 --- a/tests/integration/correlations-rollup-swap.test.ts +++ b/tests/integration/correlations-rollup-swap.test.ts @@ -26,10 +26,6 @@ import { getPrismaClient, truncateAllTables } from "./setup"; import { recomputeUserRollups } from "@/lib/rollups/measurement-rollups"; import { fetchMeasurementDailySeriesTiered } from "@/lib/insights/correlation-channel-series"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // The DAY-only recompute never touches pg-boss; leave the boss detached for // parity with the sibling rollup suites (`isolate: false` shares the mock). vi.mock("@/lib/jobs/boss-instance", () => ({ diff --git a/tests/integration/credential-enrollment-proof.test.ts b/tests/integration/credential-enrollment-proof.test.ts index 383b40a0c..78b94d0b3 100644 --- a/tests/integration/credential-enrollment-proof.test.ts +++ b/tests/integration/credential-enrollment-proof.test.ts @@ -41,10 +41,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const USER_ID = "user-enroll-proof"; const PASSWORD = "Correct horse battery staple!42"; const HOUR = 60 * 60 * 1000; diff --git a/tests/integration/csv-import-contextless-glucose.test.ts b/tests/integration/csv-import-contextless-glucose.test.ts index 9918bd2e0..8396162fb 100644 --- a/tests/integration/csv-import-contextless-glucose.test.ts +++ b/tests/integration/csv-import-contextless-glucose.test.ts @@ -43,10 +43,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const HEADER = "type,value,unit,measuredAt,glucoseContext,notes,externalId"; async function signIn() { diff --git a/tests/integration/csv-reimport-idempotent.test.ts b/tests/integration/csv-reimport-idempotent.test.ts index f0e8d1621..16efd8cd9 100644 --- a/tests/integration/csv-reimport-idempotent.test.ts +++ b/tests/integration/csv-reimport-idempotent.test.ts @@ -36,10 +36,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const HEADER = "type,value,unit,measuredAt,glucoseContext,notes,externalId"; const ROWS = 9000; diff --git a/tests/integration/cumulative-pr-rederive-atomic.test.ts b/tests/integration/cumulative-pr-rederive-atomic.test.ts index 77c8509a2..a6ed49f24 100644 --- a/tests/integration/cumulative-pr-rederive-atomic.test.ts +++ b/tests/integration/cumulative-pr-rederive-atomic.test.ts @@ -29,10 +29,6 @@ vi.mock("@/lib/personal-records/pr-detection-worker", async (importActual) => { }, }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const { runCumulativePrRederivationForUser, CUMULATIVE_PR_FIX_CUTOFF } = await import("@/lib/personal-records/cumulative-pr-rederivation"); diff --git a/tests/integration/cycle-absorbed-restored.test.ts b/tests/integration/cycle-absorbed-restored.test.ts index 0d807a61c..171f9a9e7 100644 --- a/tests/integration/cycle-absorbed-restored.test.ts +++ b/tests/integration/cycle-absorbed-restored.test.ts @@ -40,10 +40,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/cycle-crosstab-day-aggregates.test.ts b/tests/integration/cycle-crosstab-day-aggregates.test.ts index 43a04ef05..3c89c9b56 100644 --- a/tests/integration/cycle-crosstab-day-aggregates.test.ts +++ b/tests/integration/cycle-crosstab-day-aggregates.test.ts @@ -14,10 +14,6 @@ import { metricDayMap } from "@/lib/insights/mood-crosstab"; import { PHASE_CROSSTAB_METRIC_TYPES } from "@/lib/cycle/phase-crosstab"; import type { CrossMetricMeasurement } from "@/lib/insights/mood-aggregates"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const prisma = getPrismaClient(); const USER = "cycle-crosstab-agg"; const SINCE = new Date("2026-06-01T00:00:00Z"); diff --git a/tests/integration/cycle-flow-opens-cycle.test.ts b/tests/integration/cycle-flow-opens-cycle.test.ts index a77156ee0..e7eb32bc6 100644 --- a/tests/integration/cycle-flow-opens-cycle.test.ts +++ b/tests/integration/cycle-flow-opens-cycle.test.ts @@ -47,10 +47,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - async function loginAs(userId: string): Promise { cookieJar.clear(); headerJar.clear(); diff --git a/tests/integration/cycle-insights-dense-reads.test.ts b/tests/integration/cycle-insights-dense-reads.test.ts index 545e1d24b..5facb4ed0 100644 --- a/tests/integration/cycle-insights-dense-reads.test.ts +++ b/tests/integration/cycle-insights-dense-reads.test.ts @@ -39,10 +39,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - /** When set, the fold is replaced by every raw reading as a one-row group. */ const mode = vi.hoisted(() => ({ raw: false })); diff --git a/tests/integration/cycle-insights-scoped-delegate.test.ts b/tests/integration/cycle-insights-scoped-delegate.test.ts index 482afc6f5..ec908a677 100644 --- a/tests/integration/cycle-insights-scoped-delegate.test.ts +++ b/tests/integration/cycle-insights-scoped-delegate.test.ts @@ -47,10 +47,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const OWNER_ID = "cycle-insights-owner"; const DELEGATE_ID = "cycle-insights-delegate"; diff --git a/tests/integration/cycle-routes.test.ts b/tests/integration/cycle-routes.test.ts index d75c8822f..0dd4b29af 100644 --- a/tests/integration/cycle-routes.test.ts +++ b/tests/integration/cycle-routes.test.ts @@ -45,10 +45,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - async function loginAs(userId: string): Promise { cookieJar.clear(); headerJar.clear(); diff --git a/tests/integration/cycle-start-round-trip.test.ts b/tests/integration/cycle-start-round-trip.test.ts index bbb458a1e..fcd7ccbaa 100644 --- a/tests/integration/cycle-start-round-trip.test.ts +++ b/tests/integration/cycle-start-round-trip.test.ts @@ -46,10 +46,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - async function loginAs(userId: string): Promise { cookieJar.clear(); headerJar.clear(); diff --git a/tests/integration/daily-digest-today-overview.test.ts b/tests/integration/daily-digest-today-overview.test.ts index 844e73392..5b64beffa 100644 --- a/tests/integration/daily-digest-today-overview.test.ts +++ b/tests/integration/daily-digest-today-overview.test.ts @@ -19,10 +19,6 @@ import { PRIORITY_ITEM_KINDS } from "@/lib/daily/priority-item"; import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const TZ = "Europe/Berlin"; const DAY = 24 * 60 * 60 * 1000; // 08:00 in Berlin on a Sunday morning. diff --git a/tests/integration/daily-series-user-tz.test.ts b/tests/integration/daily-series-user-tz.test.ts index 4ae147839..7d55f2e75 100644 --- a/tests/integration/daily-series-user-tz.test.ts +++ b/tests/integration/daily-series-user-tz.test.ts @@ -28,10 +28,6 @@ import { invalidateUserTimezone } from "@/lib/tz/resolver"; import { userDayKey } from "@/lib/tz/format"; import { localHmAsUtc } from "@/lib/tz/local-day"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); }); diff --git a/tests/integration/dashboard-chart-overlay-concurrency.test.ts b/tests/integration/dashboard-chart-overlay-concurrency.test.ts index 7ccf4b471..c42466c27 100644 --- a/tests/integration/dashboard-chart-overlay-concurrency.test.ts +++ b/tests/integration/dashboard-chart-overlay-concurrency.test.ts @@ -40,10 +40,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/dashboard-glucose-untagged.test.ts b/tests/integration/dashboard-glucose-untagged.test.ts index 810696cac..b8a501eed 100644 --- a/tests/integration/dashboard-glucose-untagged.test.ts +++ b/tests/integration/dashboard-glucose-untagged.test.ts @@ -40,10 +40,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - interface AnalyticsEnvelope { data: { glucoseByContext: Record | null; diff --git a/tests/integration/dashboard-own-target-bands.test.ts b/tests/integration/dashboard-own-target-bands.test.ts index bc0c7994b..faae99863 100644 --- a/tests/integration/dashboard-own-target-bands.test.ts +++ b/tests/integration/dashboard-own-target-bands.test.ts @@ -40,10 +40,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/dashboard-pulse-latest-day.test.ts b/tests/integration/dashboard-pulse-latest-day.test.ts index 066e55f41..2ab6716ff 100644 --- a/tests/integration/dashboard-pulse-latest-day.test.ts +++ b/tests/integration/dashboard-pulse-latest-day.test.ts @@ -36,10 +36,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const prisma = getPrismaClient(); const USER = "pulse-latest-day"; const now = new Date(); diff --git a/tests/integration/dashboard-widgets-save.test.ts b/tests/integration/dashboard-widgets-save.test.ts index f5e16f850..f0136a4f9 100644 --- a/tests/integration/dashboard-widgets-save.test.ts +++ b/tests/integration/dashboard-widgets-save.test.ts @@ -47,10 +47,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/dawarich-context.test.ts b/tests/integration/dawarich-context.test.ts index a0f8ddb1a..6cb4b905a 100644 --- a/tests/integration/dawarich-context.test.ts +++ b/tests/integration/dawarich-context.test.ts @@ -55,10 +55,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const USER_ID = "user-dawarich"; const API_KEY = "dawarich-test-key"; const HOME = 7; diff --git a/tests/integration/day-view.test.ts b/tests/integration/day-view.test.ts index 173db8466..94c2ef33f 100644 --- a/tests/integration/day-view.test.ts +++ b/tests/integration/day-view.test.ts @@ -39,10 +39,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - type Handler = ( request: NextRequest, // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/tests/integration/delegated-intake-notification.test.ts b/tests/integration/delegated-intake-notification.test.ts index c5eb0c47e..311edfad0 100644 --- a/tests/integration/delegated-intake-notification.test.ts +++ b/tests/integration/delegated-intake-notification.test.ts @@ -50,10 +50,6 @@ vi.mock("@parse/node-apn", () => { return { default: { Provider, Notification }, Provider, Notification }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - import { getPrismaClient, truncateAllTables } from "./setup"; const OWNER_ID = "user-delegated-intake-owner"; diff --git a/tests/integration/demo-mode-profile-write.test.ts b/tests/integration/demo-mode-profile-write.test.ts index 5da3aa440..914e58d4f 100644 --- a/tests/integration/demo-mode-profile-write.test.ts +++ b/tests/integration/demo-mode-profile-write.test.ts @@ -42,10 +42,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - /** What the shared demo account carries before any visitor touches it. */ const SEEDED = { username: "demo", diff --git a/tests/integration/demo-seed-ai-texts.test.ts b/tests/integration/demo-seed-ai-texts.test.ts index e4481f1c6..44c24ed66 100644 --- a/tests/integration/demo-seed-ai-texts.test.ts +++ b/tests/integration/demo-seed-ai-texts.test.ts @@ -38,10 +38,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // A warm on the demo can only fail against the seeded placeholder key, so a // fresh seed must not ask for one. const enqueueForceWarm = vi.fn(); diff --git a/tests/integration/demo-seed-shape.test.ts b/tests/integration/demo-seed-shape.test.ts index d7557b8bd..92099600f 100644 --- a/tests/integration/demo-seed-shape.test.ts +++ b/tests/integration/demo-seed-shape.test.ts @@ -22,10 +22,6 @@ import { beforeAll, describe, expect, it, vi } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - let systolic: number[]; let diastolic: number[]; let means: number[]; diff --git a/tests/integration/demo-seed-sources.test.ts b/tests/integration/demo-seed-sources.test.ts index 3841c61d9..81254beec 100644 --- a/tests/integration/demo-seed-sources.test.ts +++ b/tests/integration/demo-seed-sources.test.ts @@ -16,10 +16,6 @@ import { getPrismaClient, truncateAllTables } from "./setup"; import { APPLE_HEALTH_TYPE_MAP } from "@/lib/measurements/apple-health-mapping"; import { MEASURE_TYPE_MAP } from "@/lib/withings/client"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const WITHINGS_TYPES = new Set( Object.values(MEASURE_TYPE_MAP).map((m) => m.type), ); diff --git a/tests/integration/dense-glucose-raw-window-series.test.ts b/tests/integration/dense-glucose-raw-window-series.test.ts index ceb6991a6..21e704c13 100644 --- a/tests/integration/dense-glucose-raw-window-series.test.ts +++ b/tests/integration/dense-glucose-raw-window-series.test.ts @@ -48,10 +48,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const prisma = getPrismaClient(); const TZ = "America/New_York"; const DENSE = "dense-glucose-owner"; diff --git a/tests/integration/dense-pulse-raw-window-reads.test.ts b/tests/integration/dense-pulse-raw-window-reads.test.ts index 886507ace..962443a52 100644 --- a/tests/integration/dense-pulse-raw-window-reads.test.ts +++ b/tests/integration/dense-pulse-raw-window-reads.test.ts @@ -48,10 +48,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const prisma = getPrismaClient(); const TZ = "America/New_York"; const DENSE = "dense-raw-window-owner"; diff --git a/tests/integration/dense-read-bounded.test.ts b/tests/integration/dense-read-bounded.test.ts index a77ec520c..95821e82b 100644 --- a/tests/integration/dense-read-bounded.test.ts +++ b/tests/integration/dense-read-bounded.test.ts @@ -32,10 +32,6 @@ import { localHmAsUtc } from "@/lib/tz/local-day"; import { dayValue } from "@/lib/measurements/day-mean"; import { userDayKey } from "@/lib/tz/format"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const prisma = getPrismaClient(); const DENSE_USER = "dense-pulse-owner"; const PARITY_USER = "dense-parity-owner"; diff --git a/tests/integration/dense-series-hrv-spo2.test.ts b/tests/integration/dense-series-hrv-spo2.test.ts index 0145a4ae2..dca6f79fb 100644 --- a/tests/integration/dense-series-hrv-spo2.test.ts +++ b/tests/integration/dense-series-hrv-spo2.test.ts @@ -41,10 +41,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const prisma = getPrismaClient(); const TZ = "Europe/Berlin"; const DENSE = "dense-hrv-spo2-owner"; diff --git a/tests/integration/dense-targets-reads.test.ts b/tests/integration/dense-targets-reads.test.ts index f2bf0c2ce..9fb0818c1 100644 --- a/tests/integration/dense-targets-reads.test.ts +++ b/tests/integration/dense-targets-reads.test.ts @@ -27,10 +27,6 @@ import { buildVitalTargets } from "@/lib/targets/vitals-builder"; import { userDayKey } from "@/lib/tz/format"; import { invalidateUserTimezone } from "@/lib/tz/resolver"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const prisma = getPrismaClient(); const TZ = "America/New_York"; const DENSE = "dense-targets-owner"; diff --git a/tests/integration/device-register.test.ts b/tests/integration/device-register.test.ts index eeaf368fe..2db21ac8e 100644 --- a/tests/integration/device-register.test.ts +++ b/tests/integration/device-register.test.ts @@ -44,10 +44,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/doctor-report-fixed-window.test.ts b/tests/integration/doctor-report-fixed-window.test.ts index e42ceb95c..321d892f9 100644 --- a/tests/integration/doctor-report-fixed-window.test.ts +++ b/tests/integration/doctor-report-fixed-window.test.ts @@ -17,10 +17,6 @@ import { selectionFromLeaves } from "@/lib/report-selection/selection"; import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const RANGE = { start: new Date("2026-01-01T00:00:00.000Z"), end: new Date("2026-03-31T23:59:59.999Z"), diff --git a/tests/integration/doctor-report-sections.test.ts b/tests/integration/doctor-report-sections.test.ts index 197beaa68..8b857cf4d 100644 --- a/tests/integration/doctor-report-sections.test.ts +++ b/tests/integration/doctor-report-sections.test.ts @@ -48,10 +48,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/document-ai-runs.test.ts b/tests/integration/document-ai-runs.test.ts index 177493de2..9f2c94f8e 100644 --- a/tests/integration/document-ai-runs.test.ts +++ b/tests/integration/document-ai-runs.test.ts @@ -57,10 +57,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const queue = vi.hoisted(() => ({ up: true, send: vi.fn(async () => "job-1"), diff --git a/tests/integration/document-sources.test.ts b/tests/integration/document-sources.test.ts index 7793fab8d..4c0209f66 100644 --- a/tests/integration/document-sources.test.ts +++ b/tests/integration/document-sources.test.ts @@ -49,10 +49,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // ── The fake Paperless-ngx behind `safeFetch` ────────────────────────────── const ORIGIN = "http://paperless.lan:8000"; diff --git a/tests/integration/document-undated-lab-value.test.ts b/tests/integration/document-undated-lab-value.test.ts index 154bb4619..6bdbcf2a0 100644 --- a/tests/integration/document-undated-lab-value.test.ts +++ b/tests/integration/document-undated-lab-value.test.ts @@ -42,10 +42,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/documents-content-index.test.ts b/tests/integration/documents-content-index.test.ts index 0436158f3..629b9ecda 100644 --- a/tests/integration/documents-content-index.test.ts +++ b/tests/integration/documents-content-index.test.ts @@ -37,10 +37,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const PNG_1X1 = Buffer.from( "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR4nGNgYGBgAAAABQABXvMqOgAAAABJRU5ErkJggg==", "base64", diff --git a/tests/integration/documents-import.test.ts b/tests/integration/documents-import.test.ts index d6b82bcc7..67ef7398d 100644 --- a/tests/integration/documents-import.test.ts +++ b/tests/integration/documents-import.test.ts @@ -45,10 +45,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const PNG_1X1 = Buffer.from( "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR4nGNgYGBgAAAABQABXvMqOgAAAABJRU5ErkJggg==", "base64", diff --git a/tests/integration/documents-recently-deleted.test.ts b/tests/integration/documents-recently-deleted.test.ts index 10e474589..6dc0028c9 100644 --- a/tests/integration/documents-recently-deleted.test.ts +++ b/tests/integration/documents-recently-deleted.test.ts @@ -40,10 +40,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const pdf = (tag: string) => Buffer.from(`%PDF-1.7\n% ${tag}\n1 0 obj\n<<>>\nendobj\n%%EOF\n`); diff --git a/tests/integration/documents-vault.test.ts b/tests/integration/documents-vault.test.ts index 54abfc2aa..367c3a2db 100644 --- a/tests/integration/documents-vault.test.ts +++ b/tests/integration/documents-vault.test.ts @@ -54,10 +54,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const PNG_1X1 = Buffer.from( "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR4nGNgYGBgAAAABQABXvMqOgAAAABJRU5ErkJggg==", "base64", diff --git a/tests/integration/dose-history-pre-creation-slots.test.ts b/tests/integration/dose-history-pre-creation-slots.test.ts index 3cf129004..49b9ffeb7 100644 --- a/tests/integration/dose-history-pre-creation-slots.test.ts +++ b/tests/integration/dose-history-pre-creation-slots.test.ts @@ -49,10 +49,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const DAY_MS = 24 * 60 * 60 * 1000; type Handler = ( diff --git a/tests/integration/ecg-ingest-roundtrip.test.ts b/tests/integration/ecg-ingest-roundtrip.test.ts index 24ac2f631..c2f568e4c 100644 --- a/tests/integration/ecg-ingest-roundtrip.test.ts +++ b/tests/integration/ecg-ingest-roundtrip.test.ts @@ -41,10 +41,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - import { GET as getEcgList, POST as postEcg, diff --git a/tests/integration/emergency-profile-route.test.ts b/tests/integration/emergency-profile-route.test.ts index 1a275fdab..f5cbdc20a 100644 --- a/tests/integration/emergency-profile-route.test.ts +++ b/tests/integration/emergency-profile-route.test.ts @@ -38,10 +38,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - async function loginAs(userId: string): Promise { cookieJar.clear(); headerJar.clear(); diff --git a/tests/integration/encounter-appointment-reminder.test.ts b/tests/integration/encounter-appointment-reminder.test.ts index 2ae3e692f..388f971cb 100644 --- a/tests/integration/encounter-appointment-reminder.test.ts +++ b/tests/integration/encounter-appointment-reminder.test.ts @@ -42,10 +42,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const OWNER_ID = "appointment-owner"; /** Far enough out that the appointment is still in the future mid-run. */ diff --git a/tests/integration/encounter-phase2-surfaces.test.ts b/tests/integration/encounter-phase2-surfaces.test.ts index 7e3e5687f..c58aa34e7 100644 --- a/tests/integration/encounter-phase2-surfaces.test.ts +++ b/tests/integration/encounter-phase2-surfaces.test.ts @@ -46,10 +46,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const OWNER_ID = "phase2-owner"; function daysFromNow(days: number): Date { diff --git a/tests/integration/encounter-preparation.test.ts b/tests/integration/encounter-preparation.test.ts index 02c6a628e..a269c1f72 100644 --- a/tests/integration/encounter-preparation.test.ts +++ b/tests/integration/encounter-preparation.test.ts @@ -37,10 +37,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const OWNER_ID = "prep-owner"; const DAY_MS = 24 * 60 * 60 * 1000; diff --git a/tests/integration/encounter-procedures.test.ts b/tests/integration/encounter-procedures.test.ts index 036aa2416..465b97856 100644 --- a/tests/integration/encounter-procedures.test.ts +++ b/tests/integration/encounter-procedures.test.ts @@ -59,10 +59,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const OWNER_ID = "procedures-owner"; const STRANGER_ID = "procedures-stranger"; diff --git a/tests/integration/encounter-scoped-delegate.test.ts b/tests/integration/encounter-scoped-delegate.test.ts index c00a22c58..3fb8d713c 100644 --- a/tests/integration/encounter-scoped-delegate.test.ts +++ b/tests/integration/encounter-scoped-delegate.test.ts @@ -49,10 +49,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const OWNER_ID = "scoped-visit-owner"; const DELEGATE_ID = "scoped-visit-delegate"; diff --git a/tests/integration/encryption-key-check.test.ts b/tests/integration/encryption-key-check.test.ts index 7e4700609..f9cb6c3fd 100644 --- a/tests/integration/encryption-key-check.test.ts +++ b/tests/integration/encryption-key-check.test.ts @@ -39,10 +39,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - /** * The start-up probe is bounded in time on purpose: ten seconds in all and * two per query, so a host with a huge table still starts. Those bounds are diff --git a/tests/integration/environment-air-quality-history.test.ts b/tests/integration/environment-air-quality-history.test.ts index 26ef059c2..87e7c32dd 100644 --- a/tests/integration/environment-air-quality-history.test.ts +++ b/tests/integration/environment-air-quality-history.test.ts @@ -29,10 +29,6 @@ import { openLocation, sealLocation } from "@/lib/environment/location-cipher"; import { handleEnvironmentAqHistory } from "@/lib/jobs/environment-air-quality-history"; import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const OWNER = "aq-history-owner"; const NOW = new Date("2026-10-09T03:00:00Z"); const BOCHUM = { lat: 51.5, lon: 7.2, label: "Bochum" }; diff --git a/tests/integration/environment-coarse-location.test.ts b/tests/integration/environment-coarse-location.test.ts index 07e753ae9..53a31d8cb 100644 --- a/tests/integration/environment-coarse-location.test.ts +++ b/tests/integration/environment-coarse-location.test.ts @@ -43,10 +43,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const OWNER_ID = "environment-coarse-owner"; function jsonRequest(url: string, method: string, body: unknown) { diff --git a/tests/integration/environment-location-encryption.test.ts b/tests/integration/environment-location-encryption.test.ts index 79251745e..76b18b1b9 100644 --- a/tests/integration/environment-location-encryption.test.ts +++ b/tests/integration/environment-location-encryption.test.ts @@ -53,10 +53,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const OWNER = "env-sealed-owner"; function jsonRequest(url: string, method: string, body: unknown) { diff --git a/tests/integration/environment-setup.ts b/tests/integration/environment-setup.ts index ac64aff72..078fd12c7 100644 --- a/tests/integration/environment-setup.ts +++ b/tests/integration/environment-setup.ts @@ -78,10 +78,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - /** * The same pin the unit setup applies (see `vitest.setup.ts`): the Fitbit * suites run before the Web API turndown, whatever the real date, and a test diff --git a/tests/integration/export-per-type.test.ts b/tests/integration/export-per-type.test.ts index 6b7b2ed08..15261893d 100644 --- a/tests/integration/export-per-type.test.ts +++ b/tests/integration/export-per-type.test.ts @@ -44,10 +44,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/fitbit-retirement.test.ts b/tests/integration/fitbit-retirement.test.ts index b75f0d2b3..087161cd1 100644 --- a/tests/integration/fitbit-retirement.test.ts +++ b/tests/integration/fitbit-retirement.test.ts @@ -9,9 +9,6 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/notifications/dispatcher", () => ({ dispatchNotification: vi.fn().mockResolvedValue({ dispatched: true, diff --git a/tests/integration/fold-complete-windows.test.ts b/tests/integration/fold-complete-windows.test.ts index 24ce9a024..9064bb4c9 100644 --- a/tests/integration/fold-complete-windows.test.ts +++ b/tests/integration/fold-complete-windows.test.ts @@ -49,10 +49,6 @@ vi.mock("next/headers", async () => { })), }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const WS_HK = "HKQuantityTypeIdentifierWalkingSpeed"; const HRV_HK = "HKQuantityTypeIdentifierHeartRateVariabilitySDNN"; const HR_HK = "HKQuantityTypeIdentifierHeartRate"; diff --git a/tests/integration/fold-horizon.test.ts b/tests/integration/fold-horizon.test.ts index f0f6d72b2..5169e538b 100644 --- a/tests/integration/fold-horizon.test.ts +++ b/tests/integration/fold-horizon.test.ts @@ -18,10 +18,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const WS_HK = "HKQuantityTypeIdentifierWalkingSpeed"; const HRV_HK = "HKQuantityTypeIdentifierHeartRateVariabilitySDNN"; diff --git a/tests/integration/fold-leftover-provenance.test.ts b/tests/integration/fold-leftover-provenance.test.ts index 34052920f..2833e05c6 100644 --- a/tests/integration/fold-leftover-provenance.test.ts +++ b/tests/integration/fold-leftover-provenance.test.ts @@ -17,10 +17,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const WS_HK = "HKQuantityTypeIdentifierWalkingSpeed"; const HRV_HK = "HKQuantityTypeIdentifierHeartRateVariabilitySDNN"; diff --git a/tests/integration/full-backup-nutrients.test.ts b/tests/integration/full-backup-nutrients.test.ts index 84e0b713d..27730abdb 100644 --- a/tests/integration/full-backup-nutrients.test.ts +++ b/tests/integration/full-backup-nutrients.test.ts @@ -36,10 +36,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/golden-backups.test.ts b/tests/integration/golden-backups.test.ts index a1cee3be6..e9d7d4a7f 100644 --- a/tests/integration/golden-backups.test.ts +++ b/tests/integration/golden-backups.test.ts @@ -60,10 +60,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const FIXTURE_DIR = resolve(__dirname, "../fixtures/golden-backups"); const WRITE = process.env.GOLDEN_BACKUP_WRITE === "1"; const SLOW = 240_000; diff --git a/tests/integration/google-health-account-id.test.ts b/tests/integration/google-health-account-id.test.ts index 07bfd9332..854e5a597 100644 --- a/tests/integration/google-health-account-id.test.ts +++ b/tests/integration/google-health-account-id.test.ts @@ -44,10 +44,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/jobs/boss-ready", () => ({ enqueueWhenBossReady: vi.fn().mockResolvedValue("skipped"), })); diff --git a/tests/integration/google-health-backfill-multi-year.test.ts b/tests/integration/google-health-backfill-multi-year.test.ts index b45003c83..659042467 100644 --- a/tests/integration/google-health-backfill-multi-year.test.ts +++ b/tests/integration/google-health-backfill-multi-year.test.ts @@ -259,10 +259,6 @@ vi.mock("@/lib/logging/transports", async (importOriginal) => ({ }, })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const forceGc = ((): (() => void) => { v8.setFlagsFromString("--expose-gc"); const gc = vm.runInNewContext("gc") as () => void; diff --git a/tests/integration/google-health-backfill-progress-bound.test.ts b/tests/integration/google-health-backfill-progress-bound.test.ts index ac40e43d1..c4d983d5f 100644 --- a/tests/integration/google-health-backfill-progress-bound.test.ts +++ b/tests/integration/google-health-backfill-progress-bound.test.ts @@ -137,10 +137,6 @@ vi.mock("@/lib/jobs/boss-instance", async (importOriginal) => ({ }), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const OTHER_COLLECTIONS = [ "fetchSteps", "fetchDistance", diff --git a/tests/integration/google-health-backfill-rate-limit.test.ts b/tests/integration/google-health-backfill-rate-limit.test.ts index 457a00d0c..1451a91b7 100644 --- a/tests/integration/google-health-backfill-rate-limit.test.ts +++ b/tests/integration/google-health-backfill-rate-limit.test.ts @@ -195,10 +195,6 @@ vi.mock("@/lib/jobs/boss-instance", async (importOriginal) => ({ }), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - async function seed(): Promise { const prisma = getPrismaClient(); await truncateAllTables(prisma); diff --git a/tests/integration/google-health-dense-backfill-memory.test.ts b/tests/integration/google-health-dense-backfill-memory.test.ts index 51af6c4b4..8297ec954 100644 --- a/tests/integration/google-health-dense-backfill-memory.test.ts +++ b/tests/integration/google-health-dense-backfill-memory.test.ts @@ -105,10 +105,6 @@ vi.mock("@/lib/safe-fetch", async (importOriginal) => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const forceGc = ((): (() => void) => { v8.setFlagsFromString("--expose-gc"); const gc = vm.runInNewContext("gc") as () => void; diff --git a/tests/integration/google-health-sleep-overlap.test.ts b/tests/integration/google-health-sleep-overlap.test.ts index ebc1e1e6a..1cfa1ddfd 100644 --- a/tests/integration/google-health-sleep-overlap.test.ts +++ b/tests/integration/google-health-sleep-overlap.test.ts @@ -49,10 +49,6 @@ vi.mock("@/lib/safe-fetch", async (importOriginal) => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - /** The night under test: 22:00 UTC two days ago to 06:00 UTC yesterday. */ const NIGHT_START = (() => { const d = new Date(); diff --git a/tests/integration/health-connect-import.test.ts b/tests/integration/health-connect-import.test.ts index 3c965acac..d2d10c3f3 100644 --- a/tests/integration/health-connect-import.test.ts +++ b/tests/integration/health-connect-import.test.ts @@ -42,10 +42,6 @@ import { reconcileOrphanImportJobs, } from "@/lib/jobs/apple-health-import-worker"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const bossMock = vi.hoisted(() => ({ handle: null as null | { getJobById: (queue: string, id: string) => Promise; diff --git a/tests/integration/health-score-configured-flag.test.ts b/tests/integration/health-score-configured-flag.test.ts index 91a556b29..97b3d2ee5 100644 --- a/tests/integration/health-score-configured-flag.test.ts +++ b/tests/integration/health-score-configured-flag.test.ts @@ -59,10 +59,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // The provider read touches an `app_settings` column that trails the // production schema in this environment, and no surface under test needs // a provider: the digest lifts a cached briefing, it never generates one. diff --git a/tests/integration/health-score-labs-pillars.test.ts b/tests/integration/health-score-labs-pillars.test.ts index 00c1be13a..af2f34614 100644 --- a/tests/integration/health-score-labs-pillars.test.ts +++ b/tests/integration/health-score-labs-pillars.test.ts @@ -50,10 +50,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/health-score-record.test.ts b/tests/integration/health-score-record.test.ts index 53a49120b..d0a55a25b 100644 --- a/tests/integration/health-score-record.test.ts +++ b/tests/integration/health-score-record.test.ts @@ -60,10 +60,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/cache/invalidate", () => ({ invalidateUserData: vi.fn(), invalidateUserHealthScore: vi.fn(), diff --git a/tests/integration/health-status-implausible-values.test.ts b/tests/integration/health-status-implausible-values.test.ts index 282ed03c2..e4c093910 100644 --- a/tests/integration/health-status-implausible-values.test.ts +++ b/tests/integration/health-status-implausible-values.test.ts @@ -38,10 +38,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const DAY_MS = 24 * 60 * 60 * 1000; /** The two numbers off the report, kept verbatim so the case stays legible. */ const IMPOSSIBLE_PULSE = 111287531.01; diff --git a/tests/integration/healthkit-type-sync.test.ts b/tests/integration/healthkit-type-sync.test.ts index fd8dd5455..9e403b194 100644 --- a/tests/integration/healthkit-type-sync.test.ts +++ b/tests/integration/healthkit-type-sync.test.ts @@ -44,10 +44,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/hour-buckets-and-backfill.test.ts b/tests/integration/hour-buckets-and-backfill.test.ts index 299b76dfa..30c396c12 100644 --- a/tests/integration/hour-buckets-and-backfill.test.ts +++ b/tests/integration/hour-buckets-and-backfill.test.ts @@ -38,10 +38,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/hrv-rmssd-fitbit-google-migration.test.ts b/tests/integration/hrv-rmssd-fitbit-google-migration.test.ts index cbbadf508..8e2742407 100644 --- a/tests/integration/hrv-rmssd-fitbit-google-migration.test.ts +++ b/tests/integration/hrv-rmssd-fitbit-google-migration.test.ts @@ -15,9 +15,6 @@ import type { Prisma } from "@/generated/prisma/client"; import { enqueueBootTimeRollupBackfill } from "@/lib/rollups/full-backfill"; import { recomputeUserRollups } from "@/lib/rollups/measurement-rollups"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/jobs/boss-instance", () => ({ getGlobalBoss: vi.fn(() => null), })); diff --git a/tests/integration/hrv-rmssd-ingest.test.ts b/tests/integration/hrv-rmssd-ingest.test.ts index 47a932972..ce7e482a7 100644 --- a/tests/integration/hrv-rmssd-ingest.test.ts +++ b/tests/integration/hrv-rmssd-ingest.test.ts @@ -38,10 +38,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/idempotency-body-fingerprint.test.ts b/tests/integration/idempotency-body-fingerprint.test.ts index c0d04d93c..ebc26eedb 100644 --- a/tests/integration/idempotency-body-fingerprint.test.ts +++ b/tests/integration/idempotency-body-fingerprint.test.ts @@ -46,10 +46,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const USER_A = "idem-fingerprint-a"; const USER_B = "idem-fingerprint-b"; const KEY = "bp-entry-5c1e2a7f"; diff --git a/tests/integration/idempotency-record-scoped-cell.test.ts b/tests/integration/idempotency-record-scoped-cell.test.ts index 9fc440c9b..d1b5a41da 100644 --- a/tests/integration/idempotency-record-scoped-cell.test.ts +++ b/tests/integration/idempotency-record-scoped-cell.test.ts @@ -41,10 +41,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - import { ACCOUNT_SELECTOR_HEADER, apiHandler, diff --git a/tests/integration/idempotency-replay.test.ts b/tests/integration/idempotency-replay.test.ts index 567760834..a8b243060 100644 --- a/tests/integration/idempotency-replay.test.ts +++ b/tests/integration/idempotency-replay.test.ts @@ -41,10 +41,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/insights-comprehensive-cache.test.ts b/tests/integration/insights-comprehensive-cache.test.ts index fec430c3d..84f7f5f11 100644 --- a/tests/integration/insights-comprehensive-cache.test.ts +++ b/tests/integration/insights-comprehensive-cache.test.ts @@ -41,10 +41,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // `resolveProvider` reads `app_settings.default_locale` which trails the // production schema in this integration env. Mock the leaf: the route // only uses `provider.type !== "none"` to set the `hasProvider` flag. diff --git a/tests/integration/insights-feedback.test.ts b/tests/integration/insights-feedback.test.ts index d6c233a25..1b5173466 100644 --- a/tests/integration/insights-feedback.test.ts +++ b/tests/integration/insights-feedback.test.ts @@ -39,10 +39,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/insights-provider-chain-put.test.ts b/tests/integration/insights-provider-chain-put.test.ts index d3ac5aba3..c4ad6a292 100644 --- a/tests/integration/insights-provider-chain-put.test.ts +++ b/tests/integration/insights-provider-chain-put.test.ts @@ -43,10 +43,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/insights-provider-chain.test.ts b/tests/integration/insights-provider-chain.test.ts index 017d27094..1fccdc3cc 100644 --- a/tests/integration/insights-provider-chain.test.ts +++ b/tests/integration/insights-provider-chain.test.ts @@ -42,10 +42,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // safeFetch's requirePublicHost path runs through undici's own `fetch` // (version-locked with its dispatcher) rather than the global. Delegate it to // the global `fetch` spy these tests install so the provider HTTP is mocked. diff --git a/tests/integration/insights-status-real-text.test.ts b/tests/integration/insights-status-real-text.test.ts index c0f9c3517..a4ac51be7 100644 --- a/tests/integration/insights-status-real-text.test.ts +++ b/tests/integration/insights-status-real-text.test.ts @@ -18,10 +18,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // Stub the provider resolution so the chain runner has a deterministic // provider to walk. `runRawCompletionWithFallback` runs for real on top // of this — exercising the same plumbing the live status path uses. diff --git a/tests/integration/intake-backfill-missed-slot.test.ts b/tests/integration/intake-backfill-missed-slot.test.ts index c4b613ffa..3e8aa837f 100644 --- a/tests/integration/intake-backfill-missed-slot.test.ts +++ b/tests/integration/intake-backfill-missed-slot.test.ts @@ -45,10 +45,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const TZ = "Europe/Berlin"; const DAY_MS = 24 * 60 * 60 * 1000; diff --git a/tests/integration/intake-late-take-future-slot.test.ts b/tests/integration/intake-late-take-future-slot.test.ts index 797d809d3..673405c4e 100644 --- a/tests/integration/intake-late-take-future-slot.test.ts +++ b/tests/integration/intake-late-take-future-slot.test.ts @@ -38,10 +38,6 @@ import { localHmAsUtc } from "@/lib/tz/local-day"; const TEST_USER_ID = "user-late-take-future-slot"; const TZ = "Europe/Berlin"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // Fixed non-DST-boundary weekday so the slot instants are deterministic. const DAY = new Date("2026-03-10T12:00:00.000Z"); const slot0900 = () => localHmAsUtc(DAY, TZ, 9, 0); diff --git a/tests/integration/intake-scheduled-for-targeting.test.ts b/tests/integration/intake-scheduled-for-targeting.test.ts index 9fe820313..e6dcba33a 100644 --- a/tests/integration/intake-scheduled-for-targeting.test.ts +++ b/tests/integration/intake-scheduled-for-targeting.test.ts @@ -28,10 +28,6 @@ import { localHmAsUtc } from "@/lib/tz/local-day"; const TEST_USER_ID = "user-intake-scheduledfor-targeting"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { const prisma = getPrismaClient(); await truncateAllTables(prisma); diff --git a/tests/integration/intake-slot-dedup.test.ts b/tests/integration/intake-slot-dedup.test.ts index 57da5c2a0..d35b9114e 100644 --- a/tests/integration/intake-slot-dedup.test.ts +++ b/tests/integration/intake-slot-dedup.test.ts @@ -34,10 +34,6 @@ import { localHmAsUtc } from "@/lib/tz/local-day"; const TEST_USER_ID = "user-intake-slot-dedup"; const OTHER_USER_ID = "user-intake-slot-dedup-clean"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { const prisma = getPrismaClient(); await truncateAllTables(prisma); diff --git a/tests/integration/integration-status.test.ts b/tests/integration/integration-status.test.ts index 39198b593..6b08f217e 100644 --- a/tests/integration/integration-status.test.ts +++ b/tests/integration/integration-status.test.ts @@ -22,10 +22,6 @@ process.env.ENCRYPTION_KEY ??= import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // The dispatcher would otherwise try to read encrypted channel // configs and call sendViaTelegram. We're not testing that path — // B3 owns it. Stub returns void so the helper's await chain diff --git a/tests/integration/integration-sync-escalation.test.ts b/tests/integration/integration-sync-escalation.test.ts index f39a66614..8423e5ec7 100644 --- a/tests/integration/integration-sync-escalation.test.ts +++ b/tests/integration/integration-sync-escalation.test.ts @@ -26,10 +26,6 @@ process.env.ENCRYPTION_KEY ??= import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/logging/signal", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, emitSignal: vi.fn(actual.emitSignal) }; diff --git a/tests/integration/integration-watchdog.test.ts b/tests/integration/integration-watchdog.test.ts index 777e3ce80..d31115bd0 100644 --- a/tests/integration/integration-watchdog.test.ts +++ b/tests/integration/integration-watchdog.test.ts @@ -14,10 +14,6 @@ process.env.ENCRYPTION_KEY ??= import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/notifications/dispatcher", () => ({ dispatchNotification: vi.fn().mockResolvedValue({ dispatched: true, diff --git a/tests/integration/lab-source-reference-range.test.ts b/tests/integration/lab-source-reference-range.test.ts index 7cb112060..2591a63ea 100644 --- a/tests/integration/lab-source-reference-range.test.ts +++ b/tests/integration/lab-source-reference-range.test.ts @@ -47,10 +47,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/lab-unit-mismatch.test.ts b/tests/integration/lab-unit-mismatch.test.ts index 06a926202..abe0c670e 100644 --- a/tests/integration/lab-unit-mismatch.test.ts +++ b/tests/integration/lab-unit-mismatch.test.ts @@ -46,10 +46,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/labs-analyte-key.test.ts b/tests/integration/labs-analyte-key.test.ts index 0e17970ff..ca6c4b9fb 100644 --- a/tests/integration/labs-analyte-key.test.ts +++ b/tests/integration/labs-analyte-key.test.ts @@ -42,10 +42,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const prisma = getPrismaClient(); const USER = "analyte-key-user"; diff --git a/tests/integration/labs-overview-long-history.test.ts b/tests/integration/labs-overview-long-history.test.ts index bf0bbaa6c..35c6704f8 100644 --- a/tests/integration/labs-overview-long-history.test.ts +++ b/tests/integration/labs-overview-long-history.test.ts @@ -38,10 +38,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - /** Days before now, so the fixture never ages out of its own windows. */ const daysAgo = (days: number) => new Date(Date.now() - days * DAY_MS); diff --git a/tests/integration/labs-unit-conversion.test.ts b/tests/integration/labs-unit-conversion.test.ts index 89af81ce8..6ff07e615 100644 --- a/tests/integration/labs-unit-conversion.test.ts +++ b/tests/integration/labs-unit-conversion.test.ts @@ -41,10 +41,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const prisma = getPrismaClient(); const USER = "lab-conversion-user"; const MGDL_PER_MMOL = 18.0182; diff --git a/tests/integration/large-account-google-health.test.ts b/tests/integration/large-account-google-health.test.ts index 630a2898b..5faa85235 100644 --- a/tests/integration/large-account-google-health.test.ts +++ b/tests/integration/large-account-google-health.test.ts @@ -74,10 +74,6 @@ vi.mock("@/lib/safe-fetch", async (importOriginal) => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/jobs/boss-instance", () => ({ getGlobalBoss: vi.fn(() => null), })); diff --git a/tests/integration/large-backup-round-trip.test.ts b/tests/integration/large-backup-round-trip.test.ts index 13276e15c..33a58165c 100644 --- a/tests/integration/large-backup-round-trip.test.ts +++ b/tests/integration/large-backup-round-trip.test.ts @@ -49,10 +49,6 @@ vi.mock("next/headers", async () => { })), }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const COUNT = 7_300; // more than one read page and several write batches beforeEach(async () => { diff --git a/tests/integration/life-circumstances.test.ts b/tests/integration/life-circumstances.test.ts index 508c8a53c..2a738add8 100644 --- a/tests/integration/life-circumstances.test.ts +++ b/tests/integration/life-circumstances.test.ts @@ -35,10 +35,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - type Handler = (request: Request, ctx?: unknown) => Promise; let counter = 0; diff --git a/tests/integration/list-pagination-tiebreaker.test.ts b/tests/integration/list-pagination-tiebreaker.test.ts index 171aa9aad..3f4336989 100644 --- a/tests/integration/list-pagination-tiebreaker.test.ts +++ b/tests/integration/list-pagination-tiebreaker.test.ts @@ -48,10 +48,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - /** * Insertion order deliberately unrelated to id order: 04, 09, 01, 06, 00, … * A scan that ignores the secondary key returns this sequence; one that diff --git a/tests/integration/local-day-keys.test.ts b/tests/integration/local-day-keys.test.ts index 5f9d2e5ef..4b9d4e50b 100644 --- a/tests/integration/local-day-keys.test.ts +++ b/tests/integration/local-day-keys.test.ts @@ -14,10 +14,6 @@ import { getPrismaClient, truncateAllTables } from "./setup"; import { readActivityDayKeys } from "@/lib/dashboard/summary-reads"; import { userDayKey } from "@/lib/tz/format"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const prisma = getPrismaClient(); const USER = "local-day-keys"; const now = new Date(); diff --git a/tests/integration/login-insecure-transport.test.ts b/tests/integration/login-insecure-transport.test.ts index c23df31ba..f675eceb7 100644 --- a/tests/integration/login-insecure-transport.test.ts +++ b/tests/integration/login-insecure-transport.test.ts @@ -42,10 +42,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const { hashPassword } = await import("@/lib/auth/password"); const { encrypt } = await import("@/lib/crypto"); const { generateTotpSecret } = await import("@/lib/auth/mfa/totp"); diff --git a/tests/integration/managed-profile-edit.test.ts b/tests/integration/managed-profile-edit.test.ts index afb9fdcf0..0f905bc28 100644 --- a/tests/integration/managed-profile-edit.test.ts +++ b/tests/integration/managed-profile-edit.test.ts @@ -48,10 +48,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - let sequence = 0; interface Person { diff --git a/tests/integration/managed-profile-handover-sso.test.ts b/tests/integration/managed-profile-handover-sso.test.ts index 842e9e8c7..2beab6343 100644 --- a/tests/integration/managed-profile-handover-sso.test.ts +++ b/tests/integration/managed-profile-handover-sso.test.ts @@ -54,10 +54,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/password-breach-check", () => ({ checkPasswordBreachIfEnabled: vi.fn().mockResolvedValue(null), })); diff --git a/tests/integration/managed-profile-handover-stepup-sso.test.ts b/tests/integration/managed-profile-handover-stepup-sso.test.ts index bcb8f6d63..f4da13f7b 100644 --- a/tests/integration/managed-profile-handover-stepup-sso.test.ts +++ b/tests/integration/managed-profile-handover-stepup-sso.test.ts @@ -67,10 +67,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/notifications/dispatch-localised", () => ({ dispatchLocalisedNotification: vi.fn(async () => {}), })); diff --git a/tests/integration/managed-profile-handover.test.ts b/tests/integration/managed-profile-handover.test.ts index 6a6c5a5c7..de881e84c 100644 --- a/tests/integration/managed-profile-handover.test.ts +++ b/tests/integration/managed-profile-handover.test.ts @@ -78,10 +78,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // The breach corpus is a network call; the claim's own logic is the subject. vi.mock("@/lib/password-breach-check", () => ({ checkPasswordBreachIfEnabled: vi.fn().mockResolvedValue(null), diff --git a/tests/integration/managed-profile-lifecycle.test.ts b/tests/integration/managed-profile-lifecycle.test.ts index b1367db2d..d97a7419e 100644 --- a/tests/integration/managed-profile-lifecycle.test.ts +++ b/tests/integration/managed-profile-lifecycle.test.ts @@ -40,10 +40,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - let sequence = 0; async function createFreshMfaUser(label = "guardian") { diff --git a/tests/integration/managed-profile-oauth-connect.test.ts b/tests/integration/managed-profile-oauth-connect.test.ts index be879252e..7c6828cfa 100644 --- a/tests/integration/managed-profile-oauth-connect.test.ts +++ b/tests/integration/managed-profile-oauth-connect.test.ts @@ -38,10 +38,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const OURA_TOKEN_URL = "https://moi.ouraring.com/oauth/v2/ext/oauth-token"; const OURA_ISS = "https://moi.ouraring.com/oauth/v2/ext/oauth-anonymous"; const GUARDIAN = "guardian-oauth"; diff --git a/tests/integration/managed-profile-passkey-step-up.test.ts b/tests/integration/managed-profile-passkey-step-up.test.ts index 06651bbb8..36e1bca54 100644 --- a/tests/integration/managed-profile-passkey-step-up.test.ts +++ b/tests/integration/managed-profile-passkey-step-up.test.ts @@ -44,10 +44,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/notifications/dispatch-localised", () => ({ dispatchLocalisedNotification: vi.fn(async () => {}), })); diff --git a/tests/integration/managed-profile-surface.test.ts b/tests/integration/managed-profile-surface.test.ts index b135ce059..57ed68f4b 100644 --- a/tests/integration/managed-profile-surface.test.ts +++ b/tests/integration/managed-profile-surface.test.ts @@ -58,10 +58,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - let sequence = 0; interface Person { diff --git a/tests/integration/managed-record-capabilities.test.ts b/tests/integration/managed-record-capabilities.test.ts index 66458e9ff..a2ef88e52 100644 --- a/tests/integration/managed-record-capabilities.test.ts +++ b/tests/integration/managed-record-capabilities.test.ts @@ -49,10 +49,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - import { delegatedDomains } from "@/lib/sharing/domain-write-support"; import type { ShareDomain } from "@/lib/sharing/scope"; diff --git a/tests/integration/managed-record-modules.test.ts b/tests/integration/managed-record-modules.test.ts index 181607419..c9d6db1bc 100644 --- a/tests/integration/managed-record-modules.test.ts +++ b/tests/integration/managed-record-modules.test.ts @@ -50,10 +50,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - let counter = 0; async function makeUser(label: string) { diff --git a/tests/integration/manual-workout-edit.test.ts b/tests/integration/manual-workout-edit.test.ts index 1abf71e2a..5cbfb8d20 100644 --- a/tests/integration/manual-workout-edit.test.ts +++ b/tests/integration/manual-workout-edit.test.ts @@ -43,10 +43,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/mcp-metric-series-source-snapshot.test.ts b/tests/integration/mcp-metric-series-source-snapshot.test.ts index ab95bc21a..37e6a3148 100644 --- a/tests/integration/mcp-metric-series-source-snapshot.test.ts +++ b/tests/integration/mcp-metric-series-source-snapshot.test.ts @@ -26,9 +26,6 @@ import { ensureUserMoodRollupsFresh } from "@/lib/rollups/mood-rollups"; import type { CoachScopeSource, CoachScopeWindow } from "@/lib/ai/coach/types"; import type { MeasurementType, SleepStage } from "@/generated/prisma/client"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/jobs/boss-instance", () => ({ getGlobalBoss: vi.fn(() => null), })); diff --git a/tests/integration/measurement-rollups.test.ts b/tests/integration/measurement-rollups.test.ts index 795b30c2a..10441641a 100644 --- a/tests/integration/measurement-rollups.test.ts +++ b/tests/integration/measurement-rollups.test.ts @@ -41,10 +41,6 @@ import { aggregateBuckets } from "@/lib/rollups/measurement-read"; import { buildComprehensiveAggregate } from "@/lib/insights/comprehensive-aggregator"; import { windowWeighting } from "@/lib/measurements/day-statistic"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // Stub boss-instance with a vi.fn so we can swap the return per test. // The integration suite runs with `isolate: false`, so the mock state // is shared with sibling files (e.g. admin-backups-audit.test.ts also diff --git a/tests/integration/measurement-soft-delete.test.ts b/tests/integration/measurement-soft-delete.test.ts index 8703b9e46..7071e7612 100644 --- a/tests/integration/measurement-soft-delete.test.ts +++ b/tests/integration/measurement-soft-delete.test.ts @@ -60,10 +60,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/measurement-source-write-smoke.test.ts b/tests/integration/measurement-source-write-smoke.test.ts index d117bd576..d2aa00ea3 100644 --- a/tests/integration/measurement-source-write-smoke.test.ts +++ b/tests/integration/measurement-source-write-smoke.test.ts @@ -52,10 +52,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const USER_ID = "measurement-source-write-smoke"; const MEASURED_AT = new Date("2026-07-20T08:00:00.000Z"); diff --git a/tests/integration/measurements-aggregate-daily.test.ts b/tests/integration/measurements-aggregate-daily.test.ts index 041ce9716..2783efcc6 100644 --- a/tests/integration/measurements-aggregate-daily.test.ts +++ b/tests/integration/measurements-aggregate-daily.test.ts @@ -42,10 +42,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/measurements-batch-delete.test.ts b/tests/integration/measurements-batch-delete.test.ts index a3a3f9d55..1c62f9a88 100644 --- a/tests/integration/measurements-batch-delete.test.ts +++ b/tests/integration/measurements-batch-delete.test.ts @@ -44,10 +44,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/measurements-batch-perf.test.ts b/tests/integration/measurements-batch-perf.test.ts index 900a4e4d1..2553f7739 100644 --- a/tests/integration/measurements-batch-perf.test.ts +++ b/tests/integration/measurements-batch-perf.test.ts @@ -43,10 +43,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const rollupCalls = vi.hoisted(() => ({ count: 0 })); vi.mock("@/lib/rollups/after-measurement-mutation", async () => { const actual = await vi.importActual< diff --git a/tests/integration/measurements-batch-set-insert-golden.test.ts b/tests/integration/measurements-batch-set-insert-golden.test.ts index 1d1b8eaf6..260a2447f 100644 --- a/tests/integration/measurements-batch-set-insert-golden.test.ts +++ b/tests/integration/measurements-batch-set-insert-golden.test.ts @@ -38,10 +38,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const SET_USER = "golden-set-based"; const RECONCILER_USER = "golden-reconciler"; diff --git a/tests/integration/measurements-batch.test.ts b/tests/integration/measurements-batch.test.ts index 91c1b0295..4e1cab34e 100644 --- a/tests/integration/measurements-batch.test.ts +++ b/tests/integration/measurements-batch.test.ts @@ -41,10 +41,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/measurements-day-grouped-paging.test.ts b/tests/integration/measurements-day-grouped-paging.test.ts index 17adde8f9..8ab4fd79a 100644 --- a/tests/integration/measurements-day-grouped-paging.test.ts +++ b/tests/integration/measurements-day-grouped-paging.test.ts @@ -32,10 +32,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/measurements-duplicate-conflict.test.ts b/tests/integration/measurements-duplicate-conflict.test.ts index 98425868e..ce12ea7f8 100644 --- a/tests/integration/measurements-duplicate-conflict.test.ts +++ b/tests/integration/measurements-duplicate-conflict.test.ts @@ -51,10 +51,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - let counter = 0; const MEASURED_AT = "2026-03-04T07:30:00.000Z"; diff --git a/tests/integration/measurements-named-sleep-stage.test.ts b/tests/integration/measurements-named-sleep-stage.test.ts index 809206262..c604e749a 100644 --- a/tests/integration/measurements-named-sleep-stage.test.ts +++ b/tests/integration/measurements-named-sleep-stage.test.ts @@ -47,10 +47,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const { POST } = await import("@/app/api/measurements/route"); const { POST: POST_BATCH } = await import("@/app/api/measurements/batch/route"); diff --git a/tests/integration/measurements-sleep-list.test.ts b/tests/integration/measurements-sleep-list.test.ts index 673347319..c71a34789 100644 --- a/tests/integration/measurements-sleep-list.test.ts +++ b/tests/integration/measurements-sleep-list.test.ts @@ -29,10 +29,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/medication-auto-mark-taken.test.ts b/tests/integration/medication-auto-mark-taken.test.ts index 6719dd9c2..8279113f7 100644 --- a/tests/integration/medication-auto-mark-taken.test.ts +++ b/tests/integration/medication-auto-mark-taken.test.ts @@ -39,9 +39,6 @@ vi.mock("next/headers", async () => { })), }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/notifications/dispatcher", () => ({ dispatchNotification: vi.fn().mockResolvedValue({ dispatched: true, diff --git a/tests/integration/medication-cadence-worker.test.ts b/tests/integration/medication-cadence-worker.test.ts index f0c6b6600..0f296ef09 100644 --- a/tests/integration/medication-cadence-worker.test.ts +++ b/tests/integration/medication-cadence-worker.test.ts @@ -66,10 +66,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // Side-effects on the create-intake hot path that the one-shot test // exercises but doesn't care about — keep them inert so the test // stays focused on the schedule-resolution + lifecycle contract. diff --git a/tests/integration/medication-course-end.test.ts b/tests/integration/medication-course-end.test.ts index 714e9e4f8..1e6c9c806 100644 --- a/tests/integration/medication-course-end.test.ts +++ b/tests/integration/medication-course-end.test.ts @@ -42,9 +42,6 @@ vi.mock("next/headers", async () => { })), }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/notifications/dispatcher", () => ({ dispatchNotification: vi.fn().mockResolvedValue({ dispatched: true, diff --git a/tests/integration/medication-courses.test.ts b/tests/integration/medication-courses.test.ts index dc9682456..21cdd5c02 100644 --- a/tests/integration/medication-courses.test.ts +++ b/tests/integration/medication-courses.test.ts @@ -45,10 +45,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { const prisma = getPrismaClient(); await truncateAllTables(prisma); diff --git a/tests/integration/medication-custom-categories.test.ts b/tests/integration/medication-custom-categories.test.ts index 262ea7e75..a711fbb56 100644 --- a/tests/integration/medication-custom-categories.test.ts +++ b/tests/integration/medication-custom-categories.test.ts @@ -39,10 +39,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - async function signIn(userId: string) { const session = await getPrismaClient().session.create({ data: { userId, expiresAt: new Date(Date.now() + 60 * 60 * 1000) }, diff --git a/tests/integration/medication-extra-dose-apple-health.test.ts b/tests/integration/medication-extra-dose-apple-health.test.ts index 4e58ba78d..30ed71fc9 100644 --- a/tests/integration/medication-extra-dose-apple-health.test.ts +++ b/tests/integration/medication-extra-dose-apple-health.test.ts @@ -46,10 +46,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const TZ = "Europe/Berlin"; const DAY_MS = 24 * 60 * 60 * 1000; const MIN = 60_000; diff --git a/tests/integration/medication-intake-tracking.test.ts b/tests/integration/medication-intake-tracking.test.ts index 68ab45991..7ec5617d3 100644 --- a/tests/integration/medication-intake-tracking.test.ts +++ b/tests/integration/medication-intake-tracking.test.ts @@ -35,9 +35,6 @@ vi.mock("next/headers", async () => { })), }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/notifications/dispatcher", () => ({ dispatchNotification: vi.fn().mockResolvedValue({ dispatched: true, diff --git a/tests/integration/medication-put-course-race.test.ts b/tests/integration/medication-put-course-race.test.ts index 5b6410394..7864cff89 100644 --- a/tests/integration/medication-put-course-race.test.ts +++ b/tests/integration/medication-put-course-race.test.ts @@ -40,10 +40,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // The check the concurrent request outran: it saw no conflict. vi.mock("@/lib/medications/courses", async (importOriginal) => ({ ...(await importOriginal()), diff --git a/tests/integration/medication-rolling-overdue-consumers.test.ts b/tests/integration/medication-rolling-overdue-consumers.test.ts index 2565b58bf..064df492c 100644 --- a/tests/integration/medication-rolling-overdue-consumers.test.ts +++ b/tests/integration/medication-rolling-overdue-consumers.test.ts @@ -28,10 +28,6 @@ const LAST_TAKEN = new Date("2026-07-21T06:00:00.000Z"); const OVERDUE_OCCURRENCE = new Date("2026-07-28T06:00:00.000Z"); const NEXT_OCCURRENCE = new Date("2026-08-04T06:00:00.000Z"); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { vi.useFakeTimers({ toFake: ["Date"] }); vi.setSystemTime(NOW); diff --git a/tests/integration/medication-write-idempotency.test.ts b/tests/integration/medication-write-idempotency.test.ts index b83a9d138..6c42a9723 100644 --- a/tests/integration/medication-write-idempotency.test.ts +++ b/tests/integration/medication-write-idempotency.test.ts @@ -55,10 +55,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/medications-intake-bulk.test.ts b/tests/integration/medications-intake-bulk.test.ts index 886e2dc90..df63bb077 100644 --- a/tests/integration/medications-intake-bulk.test.ts +++ b/tests/integration/medications-intake-bulk.test.ts @@ -38,10 +38,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - let ownedMedId = ""; let foreignMedId = ""; diff --git a/tests/integration/mental-health-wording.test.ts b/tests/integration/mental-health-wording.test.ts index 5414872b9..03fc2a6cc 100644 --- a/tests/integration/mental-health-wording.test.ts +++ b/tests/integration/mental-health-wording.test.ts @@ -28,10 +28,6 @@ import { callRoute } from "../kit/call-route"; import { makeSession, makeUser, type SessionFixture } from "../kit/factories"; import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // The reminder satisfy and the rollup tail hand work to pg-boss; the queue is // not what this file is about. vi.mock("@/lib/jobs/boss-instance", () => ({ diff --git a/tests/integration/mfa-login-flow.test.ts b/tests/integration/mfa-login-flow.test.ts index 43e53ccfc..926c09dc2 100644 --- a/tests/integration/mfa-login-flow.test.ts +++ b/tests/integration/mfa-login-flow.test.ts @@ -15,10 +15,6 @@ import { getPrismaClient, truncateAllTables } from "./setup"; process.env.API_TOKEN_HMAC_KEY ??= "test-hmac-key-test-hmac-key-test-hmac-key-0123456789"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - function codeAt(secretBase32: string, atMs: number): string { const totp = new OTPAuth.TOTP({ issuer: "HealthLog", diff --git a/tests/integration/mood-backup-round-trip.test.ts b/tests/integration/mood-backup-round-trip.test.ts index 7317d44c0..ae20ff812 100644 --- a/tests/integration/mood-backup-round-trip.test.ts +++ b/tests/integration/mood-backup-round-trip.test.ts @@ -53,10 +53,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/cache/invalidate", () => ({ invalidateUserData: vi.fn(), })); diff --git a/tests/integration/mood-context-linkage.test.ts b/tests/integration/mood-context-linkage.test.ts index e3230ec3f..eb9645656 100644 --- a/tests/integration/mood-context-linkage.test.ts +++ b/tests/integration/mood-context-linkage.test.ts @@ -41,10 +41,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/mood-context.test.ts b/tests/integration/mood-context.test.ts index c5222f1db..42a5e9ae3 100644 --- a/tests/integration/mood-context.test.ts +++ b/tests/integration/mood-context.test.ts @@ -45,10 +45,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/mood-entries-bulk.test.ts b/tests/integration/mood-entries-bulk.test.ts index 50818240e..b0048e416 100644 --- a/tests/integration/mood-entries-bulk.test.ts +++ b/tests/integration/mood-entries-bulk.test.ts @@ -35,10 +35,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/mood-entries-external-id.test.ts b/tests/integration/mood-entries-external-id.test.ts index 6945419ab..223648ee7 100644 --- a/tests/integration/mood-entries-external-id.test.ts +++ b/tests/integration/mood-entries-external-id.test.ts @@ -45,10 +45,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - async function createUserSession(userId: string, username: string) { await getPrismaClient().user.create({ data: { diff --git a/tests/integration/mood-entries-note-column.test.ts b/tests/integration/mood-entries-note-column.test.ts index 3fb786d07..543513f7b 100644 --- a/tests/integration/mood-entries-note-column.test.ts +++ b/tests/integration/mood-entries-note-column.test.ts @@ -40,10 +40,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/mood-insights-crosstab.test.ts b/tests/integration/mood-insights-crosstab.test.ts index 54428f853..fb2576ab9 100644 --- a/tests/integration/mood-insights-crosstab.test.ts +++ b/tests/integration/mood-insights-crosstab.test.ts @@ -13,10 +13,6 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; import { fetchMoodAggregates } from "@/lib/insights/mood-aggregates"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const TEST_USER_ID = "user-crosstab"; const dayMs = 24 * 60 * 60 * 1000; const NOW = new Date("2026-06-01T12:00:00.000Z"); diff --git a/tests/integration/mood-prognosis-ladder.test.ts b/tests/integration/mood-prognosis-ladder.test.ts index 294941871..7123f3bb4 100644 --- a/tests/integration/mood-prognosis-ladder.test.ts +++ b/tests/integration/mood-prognosis-ladder.test.ts @@ -41,10 +41,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - /** Fixed clock: a ladder test that slides with the calendar is a date bomb. */ const FIXED_NOW = new Date("2026-08-08T09:00:00.000Z"); /** diff --git a/tests/integration/mood-tag-groups.test.ts b/tests/integration/mood-tag-groups.test.ts index 58b1081dc..418476ee3 100644 --- a/tests/integration/mood-tag-groups.test.ts +++ b/tests/integration/mood-tag-groups.test.ts @@ -46,10 +46,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - async function createUserSession(userId: string, username: string) { await getPrismaClient().user.create({ data: { diff --git a/tests/integration/mood-writers-agree.test.ts b/tests/integration/mood-writers-agree.test.ts index 5978d7311..603e0a43b 100644 --- a/tests/integration/mood-writers-agree.test.ts +++ b/tests/integration/mood-writers-agree.test.ts @@ -47,10 +47,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - async function createUserSession() { await getPrismaClient().user.create({ data: { diff --git a/tests/integration/notification-action-mood.test.ts b/tests/integration/notification-action-mood.test.ts index 59fd707f7..cb7584409 100644 --- a/tests/integration/notification-action-mood.test.ts +++ b/tests/integration/notification-action-mood.test.ts @@ -14,10 +14,6 @@ import { getPrismaClient, truncateAllTables } from "./setup"; const USER = "user-notification-action"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - function call(token: string) { return import("@/app/api/notifications/action/[token]/route").then( ({ POST }) => diff --git a/tests/integration/notification-event-defaults.test.ts b/tests/integration/notification-event-defaults.test.ts index fee0d689b..588a4700b 100644 --- a/tests/integration/notification-event-defaults.test.ts +++ b/tests/integration/notification-event-defaults.test.ts @@ -44,10 +44,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const USERNAME = "event-defaults"; async function seedAccount(moodReminderEnabled: boolean): Promise { diff --git a/tests/integration/notification-global-channel-switch.test.ts b/tests/integration/notification-global-channel-switch.test.ts index ef66389b1..7216b486b 100644 --- a/tests/integration/notification-global-channel-switch.test.ts +++ b/tests/integration/notification-global-channel-switch.test.ts @@ -18,10 +18,6 @@ vi.mock("@/lib/notifications/senders/ntfy", () => ({ sendViaNtfy: (...args: unknown[]) => ntfySendMock(...args), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - import { getPrismaClient, truncateAllTables } from "./setup"; const TEST_USER_ID = "user-global-channel-switch"; diff --git a/tests/integration/nutrients-batch.test.ts b/tests/integration/nutrients-batch.test.ts index 75400f8c0..8587ad74a 100644 --- a/tests/integration/nutrients-batch.test.ts +++ b/tests/integration/nutrients-batch.test.ts @@ -40,10 +40,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - async function signIn(userId: string): Promise { const session = await getPrismaClient().session.create({ data: { diff --git a/tests/integration/oauth-callback-single-use.test.ts b/tests/integration/oauth-callback-single-use.test.ts index 6f71de311..150121b3a 100644 --- a/tests/integration/oauth-callback-single-use.test.ts +++ b/tests/integration/oauth-callback-single-use.test.ts @@ -44,10 +44,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const USER = "user-oauth-single-use"; const OURA_TOKEN_URL = "https://moi.ouraring.com/oauth/v2/ext/oauth-token"; const OURA_ISS = "https://moi.ouraring.com/oauth/v2/ext/oauth-anonymous"; diff --git a/tests/integration/offhost-purge-on-deletion.test.ts b/tests/integration/offhost-purge-on-deletion.test.ts index 4e141292b..8ca5af4ca 100644 --- a/tests/integration/offhost-purge-on-deletion.test.ts +++ b/tests/integration/offhost-purge-on-deletion.test.ts @@ -64,10 +64,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/oidc-pending-link.test.ts b/tests/integration/oidc-pending-link.test.ts index 4bc34c225..d98afa310 100644 --- a/tests/integration/oidc-pending-link.test.ts +++ b/tests/integration/oidc-pending-link.test.ts @@ -34,10 +34,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const { hashPassword } = await import("@/lib/auth/password"); const { encrypt } = await import("@/lib/crypto"); diff --git a/tests/integration/oidc-reproof.test.ts b/tests/integration/oidc-reproof.test.ts index cc5f728dc..d59622006 100644 --- a/tests/integration/oidc-reproof.test.ts +++ b/tests/integration/oidc-reproof.test.ts @@ -36,10 +36,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const ISSUER = "https://idp.example.test"; const METADATA = { issuer: ISSUER, diff --git a/tests/integration/onboarding-needs.test.ts b/tests/integration/onboarding-needs.test.ts index b45732e56..49b54f12d 100644 --- a/tests/integration/onboarding-needs.test.ts +++ b/tests/integration/onboarding-needs.test.ts @@ -47,10 +47,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - import { acceptGrant, inviteGrant } from "@/lib/sharing/grants"; import { DEFAULT_DASHBOARD_LAYOUT, diff --git a/tests/integration/openapi-get-contract.test.ts b/tests/integration/openapi-get-contract.test.ts index f21ed4cbc..477749079 100644 --- a/tests/integration/openapi-get-contract.test.ts +++ b/tests/integration/openapi-get-contract.test.ts @@ -77,10 +77,6 @@ vi.mock("@/lib/jobs/worker-status", async (importOriginal) => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // No request leaves the process. An operation that needs the network to // answer belongs in NOT_CALLED with that reason; one that merely tries and // copes with the failure is checked on the path it takes when it cannot. diff --git a/tests/integration/openapi-response-shape.test.ts b/tests/integration/openapi-response-shape.test.ts index 59c640c99..40a39a6c3 100644 --- a/tests/integration/openapi-response-shape.test.ts +++ b/tests/integration/openapi-response-shape.test.ts @@ -59,10 +59,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { const prisma = getPrismaClient(); await truncateAllTables(prisma); diff --git a/tests/integration/oura-backfill.test.ts b/tests/integration/oura-backfill.test.ts index 8dd616149..1a6337d61 100644 --- a/tests/integration/oura-backfill.test.ts +++ b/tests/integration/oura-backfill.test.ts @@ -51,10 +51,6 @@ const fake = vi.hoisted(() => ({ sends: [] as Array<{ queue: string; payload: unknown; options: unknown }>, })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/jobs/boss-instance", () => ({ getGlobalBoss: () => ({ send: async (queue: string, payload: unknown, options: unknown) => { diff --git a/tests/integration/partial-unique-live-rows.test.ts b/tests/integration/partial-unique-live-rows.test.ts index 109a0c674..571744249 100644 --- a/tests/integration/partial-unique-live-rows.test.ts +++ b/tests/integration/partial-unique-live-rows.test.ts @@ -29,10 +29,6 @@ import { applyCanonicalSlotWrite } from "@/lib/medications/scheduling/slot-upser import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); }); diff --git a/tests/integration/passkey-register.test.ts b/tests/integration/passkey-register.test.ts index eeb054816..2566b9323 100644 --- a/tests/integration/passkey-register.test.ts +++ b/tests/integration/passkey-register.test.ts @@ -42,10 +42,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/auth/passkey", async () => { const actual = await vi.importActual( diff --git a/tests/integration/pr-detection-cumulative-paged.test.ts b/tests/integration/pr-detection-cumulative-paged.test.ts index 9bfc38159..16abfa6f8 100644 --- a/tests/integration/pr-detection-cumulative-paged.test.ts +++ b/tests/integration/pr-detection-cumulative-paged.test.ts @@ -16,10 +16,6 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const { detectPersonalRecordsForUser } = await import("@/lib/personal-records/pr-detection-worker"); diff --git a/tests/integration/pr-detection-end-to-end.test.ts b/tests/integration/pr-detection-end-to-end.test.ts index 2efb51c63..91e8d85e7 100644 --- a/tests/integration/pr-detection-end-to-end.test.ts +++ b/tests/integration/pr-detection-end-to-end.test.ts @@ -22,10 +22,6 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const TEST_USER_ID = "user-pr-detection-e2e"; const { detectPersonalRecordsForUser, PR_DETECTION_WARMUP_THRESHOLD } = diff --git a/tests/integration/practitioner-contact-encryption.test.ts b/tests/integration/practitioner-contact-encryption.test.ts index 82df5cb39..7465e871c 100644 --- a/tests/integration/practitioner-contact-encryption.test.ts +++ b/tests/integration/practitioner-contact-encryption.test.ts @@ -44,10 +44,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const OWNER_ID = "practitioner-contact-owner"; const PHONE = "+49 30 9876543"; const ADDRESS = "Lindenweg 12, 10115 Berlin"; diff --git a/tests/integration/practitioner-specialty-search.test.ts b/tests/integration/practitioner-specialty-search.test.ts index 5448c0103..1b0e2d996 100644 --- a/tests/integration/practitioner-specialty-search.test.ts +++ b/tests/integration/practitioner-specialty-search.test.ts @@ -34,10 +34,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const OWNER_ID = "practitioner-search-owner"; function daysFromNow(days: number): Date { diff --git a/tests/integration/profile-display-name.test.ts b/tests/integration/profile-display-name.test.ts index 42c2092ec..f2479812b 100644 --- a/tests/integration/profile-display-name.test.ts +++ b/tests/integration/profile-display-name.test.ts @@ -38,10 +38,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - async function seed(displayName: string | null): Promise { const prisma = getPrismaClient(); const user = await prisma.user.create({ diff --git a/tests/integration/provider-dense-fold.test.ts b/tests/integration/provider-dense-fold.test.ts index d25d0a9f6..095cdd3f1 100644 --- a/tests/integration/provider-dense-fold.test.ts +++ b/tests/integration/provider-dense-fold.test.ts @@ -30,10 +30,6 @@ import { makeUser } from "../kit/factories"; import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const prisma = getPrismaClient(); const DAY_MS = 86_400_000; const MIN_MS = 60_000; diff --git a/tests/integration/provider-pause.test.ts b/tests/integration/provider-pause.test.ts index 7926d84ef..5f82db3b1 100644 --- a/tests/integration/provider-pause.test.ts +++ b/tests/integration/provider-pause.test.ts @@ -11,10 +11,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - import { HARD_FAILURE_COOLDOWN_MS, PROVIDER_PAUSE_BASE_MS, diff --git a/tests/integration/pulse-day-mean-zones.test.ts b/tests/integration/pulse-day-mean-zones.test.ts index da0bdc286..2157070ac 100644 --- a/tests/integration/pulse-day-mean-zones.test.ts +++ b/tests/integration/pulse-day-mean-zones.test.ts @@ -48,10 +48,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const prisma = getPrismaClient(); const DAY = 86_400_000; const now = new Date(); diff --git a/tests/integration/pulse-hourly-mean-day.test.ts b/tests/integration/pulse-hourly-mean-day.test.ts index 5d87bbc8a..0cae7b067 100644 --- a/tests/integration/pulse-hourly-mean-day.test.ts +++ b/tests/integration/pulse-hourly-mean-day.test.ts @@ -45,10 +45,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const prisma = getPrismaClient(); const USER = "pulse-hourly-mean"; const DAY = 86_400_000; diff --git a/tests/integration/pulse-window-mean-readers.test.ts b/tests/integration/pulse-window-mean-readers.test.ts index 086adfef8..85819cb9c 100644 --- a/tests/integration/pulse-window-mean-readers.test.ts +++ b/tests/integration/pulse-window-mean-readers.test.ts @@ -46,10 +46,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // The reads kick off a background rollup refresh; keep it off so the live // user stays on the live path for the whole file. vi.mock("@/lib/rollups/measurement-rollups", async (importOriginal) => { diff --git a/tests/integration/query-budget-hot-routes.test.ts b/tests/integration/query-budget-hot-routes.test.ts index 0a50202d6..6d8a6af4c 100644 --- a/tests/integration/query-budget-hot-routes.test.ts +++ b/tests/integration/query-budget-hot-routes.test.ts @@ -54,10 +54,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const USER_ID = "user-query-budget"; const DAY_MS = 86_400_000; diff --git a/tests/integration/rate-limit-429-headers.test.ts b/tests/integration/rate-limit-429-headers.test.ts index db4608027..23bb31d3a 100644 --- a/tests/integration/rate-limit-429-headers.test.ts +++ b/tests/integration/rate-limit-429-headers.test.ts @@ -53,10 +53,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/recent-proof.test.ts b/tests/integration/recent-proof.test.ts index 1a1325823..0349fc5c3 100644 --- a/tests/integration/recent-proof.test.ts +++ b/tests/integration/recent-proof.test.ts @@ -35,10 +35,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const USER_ID = "user-recent-proof"; const PASSWORD = "Correct horse battery staple!42"; const HOUR = 60 * 60 * 1000; diff --git a/tests/integration/record-csv-import.test.ts b/tests/integration/record-csv-import.test.ts index de25d0251..e04587f97 100644 --- a/tests/integration/record-csv-import.test.ts +++ b/tests/integration/record-csv-import.test.ts @@ -22,9 +22,6 @@ import { medicationsCsvQuerySchema, } from "@/lib/validations/record-import"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/jobs/boss-instance", () => ({ getGlobalBoss: vi.fn(() => null), })); diff --git a/tests/integration/record-search.test.ts b/tests/integration/record-search.test.ts index f3f915254..a1e5e3f03 100644 --- a/tests/integration/record-search.test.ts +++ b/tests/integration/record-search.test.ts @@ -49,10 +49,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - let counter = 0; async function makeUser(label: string, modules: Record = {}) { diff --git a/tests/integration/record-session-fence-idempotency.test.ts b/tests/integration/record-session-fence-idempotency.test.ts index c0691cb9a..1071018b7 100644 --- a/tests/integration/record-session-fence-idempotency.test.ts +++ b/tests/integration/record-session-fence-idempotency.test.ts @@ -51,10 +51,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - import { apiHandler, requireRecordAuth } from "@/lib/api-handler"; import { prisma } from "@/lib/db"; import { withIdempotency } from "@/lib/idempotency"; diff --git a/tests/integration/record-session-fence.test.ts b/tests/integration/record-session-fence.test.ts index 881877328..7a5561a10 100644 --- a/tests/integration/record-session-fence.test.ts +++ b/tests/integration/record-session-fence.test.ts @@ -61,10 +61,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - import { apiHandler, requireRecordAuth } from "@/lib/api-handler"; import { hashToken } from "@/lib/auth/hmac"; import { prisma } from "@/lib/db"; diff --git a/tests/integration/record-settings-integrations-health.test.ts b/tests/integration/record-settings-integrations-health.test.ts index bbd8a61c6..2ceace31b 100644 --- a/tests/integration/record-settings-integrations-health.test.ts +++ b/tests/integration/record-settings-integrations-health.test.ts @@ -30,10 +30,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - interface ManagedIntegration { integration: string; state: string; diff --git a/tests/integration/recovery-kit.test.ts b/tests/integration/recovery-kit.test.ts index d100f1d4e..8e939fb8b 100644 --- a/tests/integration/recovery-kit.test.ts +++ b/tests/integration/recovery-kit.test.ts @@ -44,10 +44,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - import { _resetCryptoCacheForTests, getKeyFingerprint } from "@/lib/crypto"; import { offhostKeyId } from "@/lib/jobs/offhost-key"; import type { RecoveryKit } from "@/lib/recovery/kit-rules"; diff --git a/tests/integration/recovery-readiness-dump-retention.test.ts b/tests/integration/recovery-readiness-dump-retention.test.ts index b2f0eeb3b..9fedd424a 100644 --- a/tests/integration/recovery-readiness-dump-retention.test.ts +++ b/tests/integration/recovery-readiness-dump-retention.test.ts @@ -19,10 +19,6 @@ vi.mock("@/lib/jobs/offhost-backup", async (importOriginal) => ({ probeOffhostLifecycle: () => probe(), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - import { getPrismaClient, truncateAllTables } from "./setup"; const NOW = new Date(); diff --git a/tests/integration/recovery-readiness-notify.test.ts b/tests/integration/recovery-readiness-notify.test.ts index 863f2220b..317adf29b 100644 --- a/tests/integration/recovery-readiness-notify.test.ts +++ b/tests/integration/recovery-readiness-notify.test.ts @@ -55,10 +55,6 @@ vi.mock("@/lib/recovery/database-dump-health", () => ({ readDatabaseBackupHealth: async () => null, })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - import { getPrismaClient, truncateAllTables } from "./setup"; const ADMIN_ID = "user-readiness-admin"; diff --git a/tests/integration/relation-links-portable-restore.test.ts b/tests/integration/relation-links-portable-restore.test.ts index 308369684..0c3e95438 100644 --- a/tests/integration/relation-links-portable-restore.test.ts +++ b/tests/integration/relation-links-portable-restore.test.ts @@ -51,10 +51,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/cache/invalidate", () => ({ invalidateUserData: vi.fn(), })); diff --git a/tests/integration/request-clock-windows-session-tz.test.ts b/tests/integration/request-clock-windows-session-tz.test.ts index 3b0d9aea7..7e2a1f2a1 100644 --- a/tests/integration/request-clock-windows-session-tz.test.ts +++ b/tests/integration/request-clock-windows-session-tz.test.ts @@ -12,10 +12,6 @@ vi.mock("@/lib/db-session", async (importOriginal) => { return { ...actual, TIME_ZONE_OPTION: "-c TimeZone=Pacific/Auckland" }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // The read path's fire-and-forget refresh would build the rollup tier behind // the live case; the cases build it themselves when they mean to. vi.mock("@/lib/rollups/measurement-rollups", async (importOriginal) => ({ diff --git a/tests/integration/rest-mode-chronic.test.ts b/tests/integration/rest-mode-chronic.test.ts index 201d458fc..e335f3c50 100644 --- a/tests/integration/rest-mode-chronic.test.ts +++ b/tests/integration/rest-mode-chronic.test.ts @@ -21,10 +21,6 @@ import type { ModuleKey } from "@/lib/modules/registry"; import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const USER = "rest-mode-chronic"; const DAY = 24 * 60 * 60 * 1000; diff --git a/tests/integration/restore-consolidation-deadlock.test.ts b/tests/integration/restore-consolidation-deadlock.test.ts index 82b6b991b..0e9deeb46 100644 --- a/tests/integration/restore-consolidation-deadlock.test.ts +++ b/tests/integration/restore-consolidation-deadlock.test.ts @@ -41,10 +41,6 @@ import { import { getPrismaClient, truncateAllTables } from "./setup"; import { acceptingBoss } from "./restore-job-driver"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const OWNER = "restore-deadlock-owner"; const DAY = "2026-01-10"; const TOTAL_ID = "deadlock-total"; diff --git a/tests/integration/rollup-concurrent-recompute.test.ts b/tests/integration/rollup-concurrent-recompute.test.ts index d9ea3d6b3..c98f3b890 100644 --- a/tests/integration/rollup-concurrent-recompute.test.ts +++ b/tests/integration/rollup-concurrent-recompute.test.ts @@ -40,10 +40,6 @@ import { recomputeUserRollups, } from "@/lib/rollups/measurement-rollups"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // No pg-boss in the integration harness — the WEEK/MONTH/YEAR enqueue // inside the write hook is a silent no-op when no boss is attached. vi.mock("@/lib/jobs/boss-instance", () => ({ diff --git a/tests/integration/rollup-coverage-probe.test.ts b/tests/integration/rollup-coverage-probe.test.ts index 05f99cf44..936d57b63 100644 --- a/tests/integration/rollup-coverage-probe.test.ts +++ b/tests/integration/rollup-coverage-probe.test.ts @@ -14,9 +14,6 @@ import { getPrismaClient, truncateAllTables } from "./setup"; import { probeRollupCoverage } from "@/lib/rollups/measurement-coverage"; import type { MeasurementType } from "@/generated/prisma/client"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/jobs/boss-instance", () => ({ getGlobalBoss: vi.fn(() => null), })); diff --git a/tests/integration/rollup-keyed-refold-session-tz.test.ts b/tests/integration/rollup-keyed-refold-session-tz.test.ts index 30e88bb3c..45cfc04b8 100644 --- a/tests/integration/rollup-keyed-refold-session-tz.test.ts +++ b/tests/integration/rollup-keyed-refold-session-tz.test.ts @@ -15,10 +15,6 @@ vi.mock("@/lib/db-session", async (importOriginal) => { return { ...actual, TIME_ZONE_OPTION: "-c TimeZone=Pacific/Auckland" }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - import { getPrismaClient, truncateAllTables } from "./setup"; import { prisma } from "@/lib/db"; import { diff --git a/tests/integration/rollup-per-day-canonical.test.ts b/tests/integration/rollup-per-day-canonical.test.ts index c24606ade..b0278bd65 100644 --- a/tests/integration/rollup-per-day-canonical.test.ts +++ b/tests/integration/rollup-per-day-canonical.test.ts @@ -25,9 +25,6 @@ import { readAllTimeExtremes } from "@/lib/insights/feature-blocks"; import { computeSummariesSlice } from "@/lib/analytics/summaries-slice"; import type { MeasurementType } from "@/generated/prisma/client"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/jobs/boss-instance", () => ({ getGlobalBoss: vi.fn(() => null), })); diff --git a/tests/integration/rollup-regression-parity.integration.test.ts b/tests/integration/rollup-regression-parity.integration.test.ts index 27a105734..71bf9ae43 100644 --- a/tests/integration/rollup-regression-parity.integration.test.ts +++ b/tests/integration/rollup-regression-parity.integration.test.ts @@ -43,10 +43,6 @@ import { type RegressionAccumulators, } from "@/lib/rollups/measurement-read"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/jobs/boss-instance", () => ({ getGlobalBoss: vi.fn(() => null), })); diff --git a/tests/integration/score-history.test.ts b/tests/integration/score-history.test.ts index 7049a3594..433fb2697 100644 --- a/tests/integration/score-history.test.ts +++ b/tests/integration/score-history.test.ts @@ -37,10 +37,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - type Handler = ( request: NextRequest, // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/tests/integration/second-factor-ends-other-sessions.test.ts b/tests/integration/second-factor-ends-other-sessions.test.ts index d0afaeab0..4007ffcc0 100644 --- a/tests/integration/second-factor-ends-other-sessions.test.ts +++ b/tests/integration/second-factor-ends-other-sessions.test.ts @@ -31,10 +31,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const USER_ID = "user-second-factor"; const HOUR = 60 * 60 * 1000; diff --git a/tests/integration/series-source-ladder.test.ts b/tests/integration/series-source-ladder.test.ts index 440d9fc71..566b3b109 100644 --- a/tests/integration/series-source-ladder.test.ts +++ b/tests/integration/series-source-ladder.test.ts @@ -46,10 +46,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const prisma = getPrismaClient(); const TZ = "Europe/Berlin"; const DUAL = "ladder-dual"; diff --git a/tests/integration/server-cache-routes.test.ts b/tests/integration/server-cache-routes.test.ts index 64ad40cf2..53349ebb7 100644 --- a/tests/integration/server-cache-routes.test.ts +++ b/tests/integration/server-cache-routes.test.ts @@ -50,10 +50,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/settings-account-delete.test.ts b/tests/integration/settings-account-delete.test.ts index 2ba87b9cd..e33bf1d7f 100644 --- a/tests/integration/settings-account-delete.test.ts +++ b/tests/integration/settings-account-delete.test.ts @@ -41,10 +41,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/settings-data-wipe.test.ts b/tests/integration/settings-data-wipe.test.ts index eda115f08..59a391330 100644 --- a/tests/integration/settings-data-wipe.test.ts +++ b/tests/integration/settings-data-wipe.test.ts @@ -65,10 +65,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - async function managedProfileLockIsHeld( prisma: PrismaClient, profileId: string, diff --git a/tests/integration/settings-erasure-bearer-step-up.test.ts b/tests/integration/settings-erasure-bearer-step-up.test.ts index 9ecabde82..3a04a9f2e 100644 --- a/tests/integration/settings-erasure-bearer-step-up.test.ts +++ b/tests/integration/settings-erasure-bearer-step-up.test.ts @@ -47,10 +47,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/settings-prefs-routes.test.ts b/tests/integration/settings-prefs-routes.test.ts index 2a18cdf04..b0b91739d 100644 --- a/tests/integration/settings-prefs-routes.test.ts +++ b/tests/integration/settings-prefs-routes.test.ts @@ -35,10 +35,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/share-link-documents.test.ts b/tests/integration/share-link-documents.test.ts index c2dfc0097..0d7c3861b 100644 --- a/tests/integration/share-link-documents.test.ts +++ b/tests/integration/share-link-documents.test.ts @@ -42,10 +42,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const PDF_SMALL = Buffer.from("%PDF-1.7\n1 0 obj\n<<>>\nendobj\n%%EOF\n"); async function seedVaultUser(username: string) { diff --git a/tests/integration/sharing-account-access.test.ts b/tests/integration/sharing-account-access.test.ts index 204e2e4de..d02ec5aa3 100644 --- a/tests/integration/sharing-account-access.test.ts +++ b/tests/integration/sharing-account-access.test.ts @@ -36,10 +36,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - import { ACCOUNT_SELECTOR_HEADER } from "@/lib/api-handler"; import { hashToken } from "@/lib/auth/hmac"; diff --git a/tests/integration/sharing-audit-actor.test.ts b/tests/integration/sharing-audit-actor.test.ts index 57874c92f..1d789e94a 100644 --- a/tests/integration/sharing-audit-actor.test.ts +++ b/tests/integration/sharing-audit-actor.test.ts @@ -43,10 +43,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - import { apiHandler, requireRecordAuth } from "@/lib/api-handler"; import { auditLog, DELEGATED_ACCESS_ACTION } from "@/lib/auth/audit"; import { acceptGrant, inviteGrant } from "@/lib/sharing/grants"; diff --git a/tests/integration/sharing-delegable-routes.test.ts b/tests/integration/sharing-delegable-routes.test.ts index de4647e82..9a277462d 100644 --- a/tests/integration/sharing-delegable-routes.test.ts +++ b/tests/integration/sharing-delegable-routes.test.ts @@ -53,10 +53,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - let counter = 0; async function makeUser(label: string) { diff --git a/tests/integration/sharing-delegable-writes.test.ts b/tests/integration/sharing-delegable-writes.test.ts index 5d698aade..76ddb68b3 100644 --- a/tests/integration/sharing-delegable-writes.test.ts +++ b/tests/integration/sharing-delegable-writes.test.ts @@ -62,10 +62,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - /** * The one collaborator this file stubs, and only at the seam it is testing. * diff --git a/tests/integration/sharing-front-door.test.ts b/tests/integration/sharing-front-door.test.ts index 16f1a0d28..de425efd1 100644 --- a/tests/integration/sharing-front-door.test.ts +++ b/tests/integration/sharing-front-door.test.ts @@ -56,10 +56,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - let counter = 0; async function makeUser(label: string) { diff --git a/tests/integration/sharing-idempotency-revocation.test.ts b/tests/integration/sharing-idempotency-revocation.test.ts index b944ae5bc..f8f120676 100644 --- a/tests/integration/sharing-idempotency-revocation.test.ts +++ b/tests/integration/sharing-idempotency-revocation.test.ts @@ -33,10 +33,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - import { ACCOUNT_SELECTOR_HEADER, apiHandler, diff --git a/tests/integration/sharing-lifecycle.test.ts b/tests/integration/sharing-lifecycle.test.ts index c9a1cd724..895acee73 100644 --- a/tests/integration/sharing-lifecycle.test.ts +++ b/tests/integration/sharing-lifecycle.test.ts @@ -44,10 +44,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - import { apiHandler, requireRecordAuth } from "@/lib/api-handler"; import { hashToken } from "@/lib/auth/hmac"; import { prisma } from "@/lib/db"; diff --git a/tests/integration/sharing-manage-handler-matrix.test.ts b/tests/integration/sharing-manage-handler-matrix.test.ts index abc6f0f37..247513c75 100644 --- a/tests/integration/sharing-manage-handler-matrix.test.ts +++ b/tests/integration/sharing-manage-handler-matrix.test.ts @@ -66,10 +66,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // Import admissions have a durable database effect plus an asynchronous queue // hand-off. Keep the hand-off deterministic here so each route test can prove // the persisted job and actor audit without requiring a running worker. diff --git a/tests/integration/sharing-provider-origin.test.ts b/tests/integration/sharing-provider-origin.test.ts index d433804e9..b8c2503e7 100644 --- a/tests/integration/sharing-provider-origin.test.ts +++ b/tests/integration/sharing-provider-origin.test.ts @@ -33,10 +33,6 @@ vi.mock("@/lib/jobs/boss-instance", () => ({ getGlobalBoss: vi.fn(() => ({ send })), })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - import { ACCOUNT_SELECTOR_HEADER, apiHandler, diff --git a/tests/integration/sharing-scope-fence.test.ts b/tests/integration/sharing-scope-fence.test.ts index f8d88f3e8..6ec2dd6a3 100644 --- a/tests/integration/sharing-scope-fence.test.ts +++ b/tests/integration/sharing-scope-fence.test.ts @@ -57,10 +57,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - let counter = 0; async function makeUser(label: string) { diff --git a/tests/integration/sleep-entry-hours-round-trip.test.ts b/tests/integration/sleep-entry-hours-round-trip.test.ts index 9389eb742..6ea00020e 100644 --- a/tests/integration/sleep-entry-hours-round-trip.test.ts +++ b/tests/integration/sleep-entry-hours-round-trip.test.ts @@ -46,10 +46,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - let counter = 0; async function makeUser() { diff --git a/tests/integration/sleep-night-sql-parity.test.ts b/tests/integration/sleep-night-sql-parity.test.ts index b6a11b0d7..c35ea37e4 100644 --- a/tests/integration/sleep-night-sql-parity.test.ts +++ b/tests/integration/sleep-night-sql-parity.test.ts @@ -29,10 +29,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/soft-delete-copy-parity.test.ts b/tests/integration/soft-delete-copy-parity.test.ts index 052cc1c63..cff137e12 100644 --- a/tests/integration/soft-delete-copy-parity.test.ts +++ b/tests/integration/soft-delete-copy-parity.test.ts @@ -46,10 +46,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - async function signIn(username: string): Promise { const prisma = getPrismaClient(); const user = await prisma.user.create({ diff --git a/tests/integration/soft-delete-purge.test.ts b/tests/integration/soft-delete-purge.test.ts index d84cc7b01..ce16cfbf6 100644 --- a/tests/integration/soft-delete-purge.test.ts +++ b/tests/integration/soft-delete-purge.test.ts @@ -20,10 +20,6 @@ import { import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const USER = "soft-delete-purge-user"; const NOW = new Date("2026-10-20T03:42:00.000Z"); const DAY = 86_400_000; diff --git a/tests/integration/source-aware-rollups.integration.test.ts b/tests/integration/source-aware-rollups.integration.test.ts index f180dab39..5bceaeab8 100644 --- a/tests/integration/source-aware-rollups.integration.test.ts +++ b/tests/integration/source-aware-rollups.integration.test.ts @@ -37,10 +37,6 @@ import { readRollupBuckets, } from "@/lib/rollups/measurement-rollups"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - // `recomputeBucketsForMeasurement` enqueues WEEK/MONTH/YEAR jobs via pg-boss; // the DAY-only `recomputeUserRollups` calls here never touch the queue, but the // sibling rollups test mocks the boss instance and the integration suite runs diff --git a/tests/integration/source-ladder-new-sources.test.ts b/tests/integration/source-ladder-new-sources.test.ts index 963ca9d00..f6a6379b4 100644 --- a/tests/integration/source-ladder-new-sources.test.ts +++ b/tests/integration/source-ladder-new-sources.test.ts @@ -22,10 +22,6 @@ import { } from "@/lib/analytics/source-rank-sql"; import { boundInstantSql } from "@/lib/analytics/request-clock-sql"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const prisma = getPrismaClient(); const USER = "ladder-new-sources"; const DAY = 86_400_000; diff --git a/tests/integration/source-priority-two-axis.test.ts b/tests/integration/source-priority-two-axis.test.ts index aa10d6c63..baa194bc9 100644 --- a/tests/integration/source-priority-two-axis.test.ts +++ b/tests/integration/source-priority-two-axis.test.ts @@ -64,10 +64,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/source-provenance.test.ts b/tests/integration/source-provenance.test.ts index 14b04ae3c..42b82c950 100644 --- a/tests/integration/source-provenance.test.ts +++ b/tests/integration/source-provenance.test.ts @@ -22,10 +22,6 @@ import { } from "@/lib/provenance/source-provenance"; import type { Prisma } from "@/generated/prisma/client"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const prisma = getPrismaClient(); const USER = "provenance-user"; const DAY = 86_400_000; diff --git a/tests/integration/step-up-elevation.test.ts b/tests/integration/step-up-elevation.test.ts index 7e6294853..aba5a7901 100644 --- a/tests/integration/step-up-elevation.test.ts +++ b/tests/integration/step-up-elevation.test.ts @@ -51,10 +51,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/supplement-nutrients.test.ts b/tests/integration/supplement-nutrients.test.ts index 75f698b0e..d746fe44b 100644 --- a/tests/integration/supplement-nutrients.test.ts +++ b/tests/integration/supplement-nutrients.test.ts @@ -49,9 +49,6 @@ vi.mock("next/headers", async () => { })), }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); vi.mock("@/lib/notifications/medication-intake-sync", () => ({ queueMedicationIntakeSync: vi.fn(), })); diff --git a/tests/integration/switched-session-destructive-refusal.test.ts b/tests/integration/switched-session-destructive-refusal.test.ts index dfd48f675..8664b39c0 100644 --- a/tests/integration/switched-session-destructive-refusal.test.ts +++ b/tests/integration/switched-session-destructive-refusal.test.ts @@ -43,10 +43,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const OWNER_ID = "switch-destructive-owner"; const DELEGATE_ID = "switch-destructive-delegate"; diff --git a/tests/integration/symptoms.test.ts b/tests/integration/symptoms.test.ts index 3271ec9ea..53c4b0a27 100644 --- a/tests/integration/symptoms.test.ts +++ b/tests/integration/symptoms.test.ts @@ -66,10 +66,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const OWNER_ID = "symptoms-owner"; const OTHER_ID = "symptoms-other"; diff --git a/tests/integration/sync-changes.test.ts b/tests/integration/sync-changes.test.ts index 671161f3f..b7092d344 100644 --- a/tests/integration/sync-changes.test.ts +++ b/tests/integration/sync-changes.test.ts @@ -41,10 +41,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/sync-reset-after-restore.test.ts b/tests/integration/sync-reset-after-restore.test.ts index d0d8f90d0..65f1a12f7 100644 --- a/tests/integration/sync-reset-after-restore.test.ts +++ b/tests/integration/sync-reset-after-restore.test.ts @@ -44,10 +44,6 @@ vi.mock("next/headers", async () => { })), }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/sync-state.test.ts b/tests/integration/sync-state.test.ts index b3318519f..ecaa6578a 100644 --- a/tests/integration/sync-state.test.ts +++ b/tests/integration/sync-state.test.ts @@ -36,10 +36,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/tiered-context-local-day.test.ts b/tests/integration/tiered-context-local-day.test.ts index a7dd40527..2405d5094 100644 --- a/tests/integration/tiered-context-local-day.test.ts +++ b/tests/integration/tiered-context-local-day.test.ts @@ -18,10 +18,6 @@ import { beforeAll, describe, expect, it, vi } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - import { buildTieredSeries } from "@/lib/rollups/tiered-context"; const prisma = getPrismaClient(); diff --git a/tests/integration/timeline-lab-series.test.ts b/tests/integration/timeline-lab-series.test.ts index 48a71e6ca..8f31406d2 100644 --- a/tests/integration/timeline-lab-series.test.ts +++ b/tests/integration/timeline-lab-series.test.ts @@ -42,10 +42,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - type Handler = ( request: NextRequest, // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/tests/integration/timeline-rollup-boundary-zone.test.ts b/tests/integration/timeline-rollup-boundary-zone.test.ts index 1fb74bd51..dc4f838b5 100644 --- a/tests/integration/timeline-rollup-boundary-zone.test.ts +++ b/tests/integration/timeline-rollup-boundary-zone.test.ts @@ -12,10 +12,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - /** * Five years before this the fold boundary falls on 2021-10-10, so the first * rolled-up month is November 2021 and the first rolled-up quarter Q1 2022. diff --git a/tests/integration/timeline-sleep-nights.test.ts b/tests/integration/timeline-sleep-nights.test.ts index d6ee07a80..a4c810e0e 100644 --- a/tests/integration/timeline-sleep-nights.test.ts +++ b/tests/integration/timeline-sleep-nights.test.ts @@ -13,10 +13,6 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const TZ = "Europe/Berlin"; const NOW = new Date("2026-10-09T12:00:00.000Z"); diff --git a/tests/integration/timezone-per-user.test.ts b/tests/integration/timezone-per-user.test.ts index 30a1861d3..938c40f01 100644 --- a/tests/integration/timezone-per-user.test.ts +++ b/tests/integration/timezone-per-user.test.ts @@ -53,10 +53,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/tombstone-cleanup-restore-lock.test.ts b/tests/integration/tombstone-cleanup-restore-lock.test.ts index a639df410..16f5e882f 100644 --- a/tests/integration/tombstone-cleanup-restore-lock.test.ts +++ b/tests/integration/tombstone-cleanup-restore-lock.test.ts @@ -21,10 +21,6 @@ import { afterAll, beforeEach, describe, expect, it, vi } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const RESTORING = "tombstone-restoring"; const OTHER = "tombstone-other"; const HOLD_MS = 3_000; diff --git a/tests/integration/user-avatar.test.ts b/tests/integration/user-avatar.test.ts index dbecb31a0..1f3c0df19 100644 --- a/tests/integration/user-avatar.test.ts +++ b/tests/integration/user-avatar.test.ts @@ -43,10 +43,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const PNG_1X1 = Buffer.from( "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR4nGNgYGBgAAAABQABXvMqOgAAAABJRU5ErkJggg==", "base64", diff --git a/tests/integration/vaccination-booster-mint.test.ts b/tests/integration/vaccination-booster-mint.test.ts index 1e89514a5..3386bd2aa 100644 --- a/tests/integration/vaccination-booster-mint.test.ts +++ b/tests/integration/vaccination-booster-mint.test.ts @@ -41,10 +41,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const OWNER_ID = "mint-owner"; const DAY_MS = 24 * 60 * 60 * 1000; diff --git a/tests/integration/vaccination-booster-satisfy.test.ts b/tests/integration/vaccination-booster-satisfy.test.ts index 8de0be3ef..f6d1db2a3 100644 --- a/tests/integration/vaccination-booster-satisfy.test.ts +++ b/tests/integration/vaccination-booster-satisfy.test.ts @@ -45,10 +45,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const OWNER_ID = "booster-owner"; const DAY_MS = 24 * 60 * 60 * 1000; diff --git a/tests/integration/vaccination-booster-scoped-delegate.test.ts b/tests/integration/vaccination-booster-scoped-delegate.test.ts index 3427956f0..6d6dadb5b 100644 --- a/tests/integration/vaccination-booster-scoped-delegate.test.ts +++ b/tests/integration/vaccination-booster-scoped-delegate.test.ts @@ -44,10 +44,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const OWNER_ID = "booster-scope-owner"; const DELEGATE_ID = "booster-scope-delegate"; diff --git a/tests/integration/vaccination-custom.test.ts b/tests/integration/vaccination-custom.test.ts index c7e7b1e06..70e69f226 100644 --- a/tests/integration/vaccination-custom.test.ts +++ b/tests/integration/vaccination-custom.test.ts @@ -50,10 +50,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const DAY_MS = 24 * 60 * 60 * 1000; const daysAgo = (days: number) => new Date(Date.now() - days * DAY_MS); const daysFromNow = (days: number) => new Date(Date.now() + days * DAY_MS); diff --git a/tests/integration/vaccination-managed-profile.test.ts b/tests/integration/vaccination-managed-profile.test.ts index a49ddc77a..6bcf8c8c0 100644 --- a/tests/integration/vaccination-managed-profile.test.ts +++ b/tests/integration/vaccination-managed-profile.test.ts @@ -50,10 +50,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const DAY_MS = 24 * 60 * 60 * 1000; function daysAgo(days: number): Date { diff --git a/tests/integration/vaccination-renewal.test.ts b/tests/integration/vaccination-renewal.test.ts index a51e0025b..a95cf9d37 100644 --- a/tests/integration/vaccination-renewal.test.ts +++ b/tests/integration/vaccination-renewal.test.ts @@ -32,10 +32,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const OWNER_ID = "renewal-owner"; const DELEGATE_ID = "renewal-delegate"; diff --git a/tests/integration/visit-link-picker-long-history.test.ts b/tests/integration/visit-link-picker-long-history.test.ts index e67f629dd..cbb7ff090 100644 --- a/tests/integration/visit-link-picker-long-history.test.ts +++ b/tests/integration/visit-link-picker-long-history.test.ts @@ -56,10 +56,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const OWNER_ID = "long-history-owner"; const DAY_MS = 24 * 60 * 60 * 1000; /** Historical on purpose: the visit and its panel sit years back. */ diff --git a/tests/integration/visit-link-picker-period-search.test.ts b/tests/integration/visit-link-picker-period-search.test.ts index 8c11213fa..82fcac854 100644 --- a/tests/integration/visit-link-picker-period-search.test.ts +++ b/tests/integration/visit-link-picker-period-search.test.ts @@ -58,10 +58,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const OWNER_ID = "period-search-owner"; const DAY_MS = 24 * 60 * 60 * 1000; const TZ = "Europe/Berlin"; diff --git a/tests/integration/whoop-connect-ticket.test.ts b/tests/integration/whoop-connect-ticket.test.ts index 915e88348..ce7104f65 100644 --- a/tests/integration/whoop-connect-ticket.test.ts +++ b/tests/integration/whoop-connect-ticket.test.ts @@ -50,10 +50,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const TEST_USER_ID = "user-whoop-connect-ticket"; const RAW_BEARER = "hlk_whoopconnectticketintegrationrawtokenvalue00000001"; const WHOOP_AUTH_URL = "https://api.prod.whoop.com/oauth/oauth2/auth"; diff --git a/tests/integration/withings-oauth-flow.test.ts b/tests/integration/withings-oauth-flow.test.ts index 1ba5e6197..a95a17e71 100644 --- a/tests/integration/withings-oauth-flow.test.ts +++ b/tests/integration/withings-oauth-flow.test.ts @@ -52,10 +52,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const TEST_USER_ID = "user-withings-oauth-flow"; const WITHINGS_OAUTH_URL = diff --git a/tests/integration/withings-oauth.test.ts b/tests/integration/withings-oauth.test.ts index d8275fce0..7d3d3c9fd 100644 --- a/tests/integration/withings-oauth.test.ts +++ b/tests/integration/withings-oauth.test.ts @@ -51,10 +51,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const TEST_USER_ID = "user-withings-oauth"; const WITHINGS_TOKEN_URL = "https://wbsapi.withings.net/v2/oauth2"; diff --git a/tests/integration/withings-sync-volume.test.ts b/tests/integration/withings-sync-volume.test.ts index c2b97d9aa..ea4069bf1 100644 --- a/tests/integration/withings-sync-volume.test.ts +++ b/tests/integration/withings-sync-volume.test.ts @@ -20,10 +20,6 @@ import { encrypt } from "@/lib/crypto"; import { getPrismaClient, truncateAllTables } from "./setup"; -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - vi.mock("@/lib/withings/client", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, fetchMeasurements: vi.fn() }; diff --git a/tests/integration/workout-batch-create.test.ts b/tests/integration/workout-batch-create.test.ts index 00e431dfd..da4fda1a3 100644 --- a/tests/integration/workout-batch-create.test.ts +++ b/tests/integration/workout-batch-create.test.ts @@ -56,10 +56,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/workout-batch-enrich.test.ts b/tests/integration/workout-batch-enrich.test.ts index 3b80f526e..d0f1838dc 100644 --- a/tests/integration/workout-batch-enrich.test.ts +++ b/tests/integration/workout-batch-enrich.test.ts @@ -53,10 +53,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/workout-batch-race.test.ts b/tests/integration/workout-batch-race.test.ts index d55aed62b..5d4cd0ac6 100644 --- a/tests/integration/workout-batch-race.test.ts +++ b/tests/integration/workout-batch-race.test.ts @@ -45,10 +45,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - beforeEach(async () => { await truncateAllTables(getPrismaClient()); cookieJar.clear(); diff --git a/tests/integration/workout-detail-hr-series.test.ts b/tests/integration/workout-detail-hr-series.test.ts index 7f3d904be..ac616d76e 100644 --- a/tests/integration/workout-detail-hr-series.test.ts +++ b/tests/integration/workout-detail-hr-series.test.ts @@ -56,10 +56,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const { GET } = await import("@/app/api/workouts/[id]/route"); /** diff --git a/tests/integration/workout-manual-entry.test.ts b/tests/integration/workout-manual-entry.test.ts index 434d4839c..ff28f18ac 100644 --- a/tests/integration/workout-manual-entry.test.ts +++ b/tests/integration/workout-manual-entry.test.ts @@ -53,10 +53,6 @@ vi.mock("next/headers", async () => { const emitDataArrival = vi.hoisted(() => vi.fn(async () => undefined)); vi.mock("@/lib/arrivals/emit-shared", () => ({ emitDataArrival })); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const { POST: postBatch } = await import("@/app/api/workouts/batch/route"); const { GET: listWorkouts } = await import("@/app/api/workouts/route"); const { DELETE: deleteWorkout } = await import("@/app/api/workouts/[id]/route"); diff --git a/tests/integration/write-meter.test.ts b/tests/integration/write-meter.test.ts index cd945a9c1..56b6abe57 100644 --- a/tests/integration/write-meter.test.ts +++ b/tests/integration/write-meter.test.ts @@ -41,10 +41,6 @@ vi.mock("next/headers", async () => { }; }); -vi.mock("@/lib/db-compat", () => ({ - ensureDbCompatibility: vi.fn().mockResolvedValue(undefined), -})); - const USER_ID = "user-write-meter"; const BUCKET = `${RECORD_WRITE_BUCKET_PREFIX}:${USER_ID}`; From f05a112c614f3e44adbe3c27200b3d8620cf87b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Tue, 3 Nov 2026 20:32:53 +0100 Subject: [PATCH 2/7] feat(jobs): leave index drops to the owner when maintenance runs without it DROP INDEX belongs to the table's owner; the MAINTAIN privilege covers VACUUM and REINDEX but not the drops of retired indexes and invalid rebuild copies. A run that does not own measurements now skips those drops and names what is left, for the boot to drop as the owner. The retired-index list moves to a plain module the boot script can read. --- src/lib/jobs/measurement-maintenance.ts | 77 ++++++++++++++++---- src/lib/jobs/retired-measurement-indexes.mjs | 21 ++++++ 2 files changed, 83 insertions(+), 15 deletions(-) create mode 100644 src/lib/jobs/retired-measurement-indexes.mjs diff --git a/src/lib/jobs/measurement-maintenance.ts b/src/lib/jobs/measurement-maintenance.ts index a917f77a3..84afa4e9f 100644 --- a/src/lib/jobs/measurement-maintenance.ts +++ b/src/lib/jobs/measurement-maintenance.ts @@ -61,6 +61,10 @@ import type { Job } from "pg-boss"; import { sessionOptionsDisabled } from "@/lib/db"; import { withJobLock } from "@/lib/jobs/job-lock"; +import { + CONCURRENT_COPY, + RETIRED_MEASUREMENT_INDEXES as RETIRED_INDEXES_SHARED, +} from "@/lib/jobs/retired-measurement-indexes.mjs"; import { jobDone, jobFailed, @@ -170,14 +174,11 @@ export function maintenanceSessionStatements(): string[] { /** * Indexes on `measurements` the schema no longer declares, which a run drops - * when the migration that retired them could not. Each one's migration names - * it; `IF EXISTS` makes the drop a no-op once it is gone. + * when the migration that retired them could not. The list lives in a plain + * module because the boot step `scripts/db-app-role.mjs` reads it too. */ -export const RETIRED_MEASUREMENT_INDEXES: readonly string[] = [ - // 0416: a prefix of the unique key `(user_id, type, measured_at, source, - // sleep_stage)`, which plans every query it served at the same cost. - "measurements_user_id_type_measured_at_idx", -]; +export const RETIRED_MEASUREMENT_INDEXES: readonly string[] = + RETIRED_INDEXES_SHARED; /** How long a run waits for the locks that drop a retired index. */ export const RETIRED_INDEX_LOCK_TIMEOUT = "60s"; @@ -187,9 +188,6 @@ const LOCK_TIMEOUT_VALUE = /^\d{1,6}(ms|s)$/; /** The only shape an index name may have before it is spliced into SQL. */ const INDEX_NAME = /^[a-z0-9_]{1,63}$/; -/** Postgres's suffixes for the copies a concurrent rebuild leaves behind. */ -const CONCURRENT_COPY = /_ccnew\d*$|_ccold\d*$/; - const PURGE_QUEUE = "compaction-tombstone-purge"; export interface MaintenanceStep { @@ -214,6 +212,12 @@ export interface MeasurementMaintenanceSummary { tableBytesAfter: number; indexBytesBefore: number; indexBytesAfter: number; + /** + * Retired indexes and invalid rebuild copies the run found but may not + * drop, because it does not connect as the table's owner. The next start + * drops them as the owner (`scripts/db-app-role.mjs`). + */ + dropsLeftForBoot: string[]; } interface IndexRow { @@ -333,6 +337,34 @@ function errorText(err: unknown): string { return typeof code === "string" ? `${code}: ${message}` : message; } +/** + * Whether this connection may drop indexes on `measurements`, which only the + * table's owner (or a member of the owning role) may. The app's restricted + * role may not: it holds MAINTAIN on the table, which covers `VACUUM` and + * `REINDEX` but not `DROP INDEX`. Under it the drops wait for the next start, + * which runs them as the owner (`scripts/db-app-role.mjs`). + */ +export async function mayDropMeasurementIndexes( + db: MaintenanceConnection, +): Promise { + const { rows } = await db.query<{ owns: boolean }>( + `SELECT pg_has_role(current_user, c.relowner, 'USAGE') AS owns + FROM pg_class c WHERE c.oid = 'measurements'::regclass`, + ); + return rows[0]?.owns === true; +} + +/** The droppable indexes present on the table: retired ones and invalid copies. */ +async function indexesToDrop(db: MaintenanceConnection): Promise { + return (await measurementIndexes(db)) + .filter( + (index) => + RETIRED_MEASUREMENT_INDEXES.includes(index.name) || + (!index.valid && CONCURRENT_COPY.test(index.name)), + ) + .map((index) => index.name); +} + /** Drop the invalid copies an interrupted concurrent rebuild left behind. */ async function dropInvalidCopies( db: MaintenanceConnection, @@ -434,21 +466,25 @@ export async function runMeasurementMaintenance( ): Promise { const before = await relationSizes(db); const steps: MaintenanceStep[] = []; - const summary = ( + const mayDrop = await mayDropMeasurementIndexes(db); + const summary = async ( outcome: MeasurementMaintenanceSummary["outcome"], after = before, - ): MeasurementMaintenanceSummary => ({ + ): Promise => ({ outcome, steps, tableBytesBefore: before.table, tableBytesAfter: after.table, indexBytesBefore: before.indexes, indexBytesAfter: after.indexes, + dropsLeftForBoot: mayDrop ? [] : await indexesToDrop(db), }); // Every run, the nightly tick included: a retired index costs every write // until it is gone, and the drop blocks no reader or writer. - await dropRetiredIndexes(db, steps, options.retiredLockTimeout); + if (mayDrop) { + await dropRetiredIndexes(db, steps, options.retiredLockTimeout); + } if (!payload.vacuum && !payload.reindex) return summary("nothing_due"); @@ -495,7 +531,7 @@ export async function runMeasurementMaintenance( } if (payload.reindex) { - await dropInvalidCopies(db, steps); + if (mayDrop) await dropInvalidCopies(db, steps); const targets = (await measurementIndexes(db)).filter( (index) => index.valid && @@ -529,7 +565,9 @@ export async function runMeasurementMaintenance( }); // A failed concurrent rebuild leaves its half-built copy behind, // holding disk. Drop it and stop: the likeliest cause is the disk. - await dropInvalidCopies(db, steps); + // Without the owner's right to drop it, the copy stays until the next + // start and the summary names it. + if (mayDrop) await dropInvalidCopies(db, steps); return summary("failed", await relationSizes(db)); } } @@ -603,6 +641,15 @@ export async function handleMeasurementMaintenance( ); } } + if (result.dropsLeftForBoot.length > 0) { + evt.addMeta( + "maintenance_drops_left_for_boot", + result.dropsLeftForBoot.length, + ); + evt.addWarning( + `index ${result.dropsLeftForBoot.join(", ")} on measurements is due to be dropped, which only the table's owner may do; the next start drops it as the owner`, + ); + } const retiredLeft = result.steps.filter( (s) => s.step === "drop_retired" && !s.ok, ); diff --git a/src/lib/jobs/retired-measurement-indexes.mjs b/src/lib/jobs/retired-measurement-indexes.mjs new file mode 100644 index 000000000..339d35660 --- /dev/null +++ b/src/lib/jobs/retired-measurement-indexes.mjs @@ -0,0 +1,21 @@ +/** + * Indexes on `measurements` the schema no longer declares, which are dropped + * when the migration that retired them could not. Each one's migration names + * it; `IF EXISTS` makes the drop a no-op once it is gone. + * + * Plain ESM with no imports, because two processes read it: the measurement + * maintenance job (`measurement-maintenance.ts`), and the boot step in + * `scripts/db-app-role.mjs`, which drops them as the database owner when the + * app itself connects as a role that may not (`DROP INDEX` belongs to the + * table's owner, not to the MAINTAIN privilege). + * + * @type {readonly string[]} + */ +export const RETIRED_MEASUREMENT_INDEXES = Object.freeze([ + // 0416: a prefix of the unique key `(user_id, type, measured_at, source, + // sleep_stage)`, which plans every query it served at the same cost. + "measurements_user_id_type_measured_at_idx", +]); + +/** Postgres's suffixes for the copies a concurrent rebuild leaves behind. */ +export const CONCURRENT_COPY = /_ccnew\d*$|_ccold\d*$/; From 95525a02595a3f0cd2a27b23ace8bca66c86d0b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Tue, 3 Nov 2026 20:32:53 +0100 Subject: [PATCH 3/7] fix(jobs): let pg-boss create its schema only when it is missing Postgres checks the database's CREATE privilege before it checks whether the schema exists, so CREATE SCHEMA IF NOT EXISTS fails for a role that may not create schemas even when the schema is there. The worker now asks first and passes createSchema: false when it exists. --- src/lib/jobs/boss-instance.ts | 18 ++++++++++++++++-- src/lib/jobs/reminder-worker.ts | 9 ++++++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/lib/jobs/boss-instance.ts b/src/lib/jobs/boss-instance.ts index ef3fa2121..169021e24 100644 --- a/src/lib/jobs/boss-instance.ts +++ b/src/lib/jobs/boss-instance.ts @@ -88,8 +88,22 @@ function databaseOptions( } as const; } -export function createWorkerBoss(connectionString: string): PgBoss { - return new PgBoss(databaseOptions(connectionString, "worker")); +/** + * The worker's pg-boss. `createSchema` says whether pg-boss's first install + * may run `CREATE SCHEMA IF NOT EXISTS pgboss`. Postgres checks the database's + * CREATE privilege before it checks whether the schema exists, so under the + * app's restricted role (`scripts/db-app-role.mjs`), which may not create + * schemas, that statement fails even though the boot already created the + * schema for it. The caller passes false when the schema is there. + */ +export function createWorkerBoss( + connectionString: string, + options: { createSchema?: boolean } = {}, +): PgBoss { + return new PgBoss({ + ...databaseOptions(connectionString, "worker"), + ...(options.createSchema === false ? { createSchema: false } : {}), + }); } function producerOptions(connectionString: string) { diff --git a/src/lib/jobs/reminder-worker.ts b/src/lib/jobs/reminder-worker.ts index 715ce80b2..72991a313 100644 --- a/src/lib/jobs/reminder-worker.ts +++ b/src/lib/jobs/reminder-worker.ts @@ -11,6 +11,7 @@ * Usage: Run as a standalone process or call startReminderWorker() from a * custom server setup. In dev, use: npx tsx src/lib/jobs/reminder-worker.ts */ +import { prisma } from "@/lib/db"; import { markWorkerStarted, recordError } from "@/lib/jobs/worker-status"; import { createWorkerBoss, setGlobalBoss } from "@/lib/jobs/boss-instance"; import { reportJobsCutOffAtBoot } from "@/lib/jobs/job-observer"; @@ -47,7 +48,13 @@ export async function startReminderWorker() { return; } - const boss = createWorkerBoss(DATABASE_URL); + // The queue schema exists once a worker has started, and on every boot + // the restricted database role runs under (the entrypoint creates it for + // that role); only a fresh database without either needs pg-boss to + // create it. + const [{ present }] = await prisma.$queryRaw<{ present: boolean }[]>` + SELECT to_regnamespace('pgboss') IS NOT NULL AS present`; + const boss = createWorkerBoss(DATABASE_URL, { createSchema: !present }); boss.on("error", (error: unknown) => { workerLog("error", "boss emitted error", error); From 485161a657e4657bf284bac91cc9a7cef013b98f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Tue, 3 Nov 2026 20:36:37 +0100 Subject: [PATCH 4/7] feat(db): run the app as a role that cannot change the schema After the migrations the entrypoint ensures healthlog_app, a login role with row access to the app's schema, MAINTAIN on measurements and ownership of the pgboss schema, and starts the app with a DATABASE_URL for it in place of the owner's. The role cannot alter or drop tables, create roles, schemas or functions in the app's schema, read server files or run programs, or write the migration history. The password is an HMAC of a fixed label under API_TOKEN_HMAC_KEY, so a separate worker container derives the same one, and it is sent as a SCRAM verifier. Every boot re-applies the grants in one transaction under an advisory lock, and drops as the owner the indexes the maintenance job may no longer drop. When the owner cannot create roles, the server is older than Postgres 17, or the role cannot log in, the boot says why and the app stays on the owner as before. DB_APP_ROLE=off skips it. --- Dockerfile | 2 + docker-entrypoint.sh | 16 + scripts/db-app-role.mjs | 648 ++++++++++++++++++ .../container-scripts-ship-guard.test.ts | 9 +- tests/integration/db-app-role.test.ts | 387 +++++++++++ 5 files changed, 1059 insertions(+), 3 deletions(-) create mode 100644 scripts/db-app-role.mjs create mode 100644 tests/integration/db-app-role.test.ts diff --git a/Dockerfile b/Dockerfile index 37143e516..7c03b74e2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -257,6 +257,8 @@ COPY --from=builder /app/scripts/pending-migrations.mjs ./scripts/pending-migrat COPY --from=builder /app/scripts/migrate-deploy.mjs ./scripts/migrate-deploy.mjs COPY --from=builder /app/scripts/db-dump.mjs ./scripts/db-dump.mjs COPY --from=builder /app/scripts/db-restore.mjs ./scripts/db-restore.mjs +COPY --from=builder /app/scripts/db-app-role.mjs ./scripts/db-app-role.mjs +COPY --from=builder /app/src/lib/jobs/retired-measurement-indexes.mjs ./src/lib/jobs/retired-measurement-indexes.mjs COPY --from=builder /app/src/lib/database-backup/dump-core.mjs ./src/lib/database-backup/dump-core.mjs COPY --from=builder /app/src/lib/auth/argon2-params.mjs ./src/lib/auth/argon2-params.mjs COPY --from=builder /app/scripts/repair-intake-anomalies.ts ./scripts/repair-intake-anomalies.ts diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index b31e9871e..e74be1f56 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -107,6 +107,22 @@ NODE_PATH=/opt/prisma-cli/node_modules \ node scripts/migrate-deploy.mjs echo "HealthLog: Migrations complete." +# ── The app's database role ────────────────────────────────── +# Everything above ran as the owner DATABASE_URL names. The app itself +# connects as `healthlog_app`, which reads and writes rows and runs the queue +# but cannot change the schema (scripts/db-app-role.mjs says what it may and +# may not do, and docs/self-hosting/database-role.md how to opt out). The +# script hands the role's URL over on descriptor 3 only, never on stdout or +# stderr; nothing handed over means it said why and the app stays on the +# owner, as before. The owner's URL is replaced, not kept beside it, so it +# does not reach the app's environment. +app_database_url="$(node scripts/db-app-role.mjs 3>&1 1>&2)" || app_database_url="" +if [ -n "$app_database_url" ]; then + DATABASE_URL="$app_database_url" + export DATABASE_URL +fi +unset app_database_url + # ── Start application ──────────────────────────────────────── echo "HealthLog: Starting application..." exec "$@" diff --git a/scripts/db-app-role.mjs b/scripts/db-app-role.mjs new file mode 100644 index 000000000..73b9ca2f5 --- /dev/null +++ b/scripts/db-app-role.mjs @@ -0,0 +1,648 @@ +#!/usr/bin/env node +/** + * The database role the running app connects as. + * + * The migrations, the dump before them and the restore connect as the owner + * `DATABASE_URL` names. The long-running web and worker processes do not need + * the owner: they read and write rows, run the queue, and maintain one table. + * After the migrations, `docker-entrypoint.sh` runs this script as the owner. + * It ensures a login role, `healthlog_app`, that can do exactly that, and + * hands the entrypoint a `DATABASE_URL` for it; the entrypoint then starts the + * app with that URL in place of the owner's, so the owner's credentials never + * reach the app's environment. + * + * What the role can do: + * - SELECT, INSERT, UPDATE, DELETE on every table of the app's schema, and + * USAGE, SELECT, UPDATE on its sequences. `_prisma_migrations` is SELECT + * only: the app reads the migration history, it never writes it. + * - MAINTAIN on `measurements` (Postgres 17+): the maintenance job's + * `VACUUM (ANALYZE)` and `REINDEX INDEX CONCURRENTLY`. + * - Own the `pgboss` schema and everything in it. pg-boss creates and + * migrates its own tables, functions and per-queue partitions at runtime, + * so it needs DDL there; it is the queue's private schema and nothing of + * the app's data lives in it. + * + * What it cannot do: create, alter or drop anything in the app's schema; + * create schemas, databases or roles; act as a superuser, so no + * `COPY ... FROM PROGRAM`, no `pg_read_file`, no untrusted languages; write + * the migration history. `CREATE` on the app's schema is revoked from PUBLIC + * as well (the default since Postgres 15; a database first created on an + * older major still grants it), where the owner may do so. + * + * Every boot re-applies it, inside one transaction under an advisory lock, so + * a table a new migration created is covered before the app starts, a web and + * a worker container booting together do not race, and a restored database + * (whose objects `pg_restore --no-owner` hands to the owner) is put right. + * Grants only touch what lacks them, so a boot after the first writes + * almost nothing to the catalog. + * + * The password is derived, not stored: HMAC-SHA256 of a fixed label under + * API_TOKEN_HMAC_KEY. Every container of one instance carries that key, so a + * separate worker container derives the same password without a new + * variable. It is set as a SCRAM verifier computed here, so the plaintext + * never travels to the server, where a statement log could keep it. The role + * is marked with a comment; a role of the same name without the mark is not + * HealthLog's, and is left alone. + * + * Fail open, loudly. When the owner cannot do this (a managed or external + * database whose user may not create roles, Postgres before 17, a pg_hba.conf + * that does not let the new role in, a lock that did not come in time), the + * script says why in one notice and the app runs as the owner, as it did + * before. `DB_APP_ROLE=off` skips it for an operator who manages roles + * themselves, including one whose `DATABASE_URL` already names a restricted role. + * + * Why Postgres 17: the maintenance job vacuums and rebuilds the indexes of + * `measurements`, and before the MAINTAIN privilege only the table's owner + * could. Rather than leave that job broken on 16, an older server keeps + * today's behaviour. + * + * Index drops stay with the owner: `DROP INDEX` belongs to the table's owner + * and MAINTAIN does not include it. When the role is in place this script + * also drops, as the owner, the indexes the maintenance job used to drop + * (retired indexes, and the invalid copies an interrupted concurrent rebuild + * leaves), each under a short lock wait, so a held lock delays the boot by + * seconds and the drop waits for the next boot. + * + * Interface: the URL for the role goes to file descriptor 3 and nowhere else; + * every message goes to stderr, and no message carries a password or a URL. + * The entrypoint reads it with `url="$(node scripts/db-app-role.mjs 3>&1 1>&2)"`. + * Without descriptor 3 (a manual run) the role is still ensured and the URL is + * not printed. Exit 0 in every case the app should start; nothing written to + * descriptor 3 means: stay on the owner. + * + * Plain ESM with no imports beyond Node, `pg` and the shared dump module, so + * it runs from the runtime image like the other entrypoint scripts. + */ +import { createHash, createHmac, pbkdf2Sync, randomBytes } from "node:crypto"; +import { writeSync } from "node:fs"; +import { pathToFileURL } from "node:url"; + +import { connect } from "../src/lib/database-backup/dump-core.mjs"; +import { + CONCURRENT_COPY, + RETIRED_MEASUREMENT_INDEXES, +} from "../src/lib/jobs/retired-measurement-indexes.mjs"; + +/** The role's name. Cluster-wide, like every Postgres role. */ +export const APP_ROLE = "healthlog_app"; + +/** The mark on a role this script created. */ +export const ROLE_COMMENT = + "Managed by HealthLog: the app's least-privilege login (scripts/db-app-role.mjs). Recreated and re-granted on every start."; + +/** The schema pg-boss keeps its queue in (its default; HealthLog sets none). */ +export const QUEUE_SCHEMA = "pgboss"; + +/** Tables the role may maintain (VACUUM, ANALYZE, REINDEX). */ +export const MAINTAINED_TABLES = Object.freeze(["measurements"]); + +/** Tables the role may only read. */ +export const READ_ONLY_TABLES = Object.freeze(["_prisma_migrations"]); + +/** The label the password is derived under. A new label is a new password. */ +const PASSWORD_LABEL = "healthlog database app role v1"; + +/** The first major with the MAINTAIN privilege. */ +export const MIN_SERVER_VERSION_NUM = 170000; + +/** How long the setup waits for a lock before staying on the owner. */ +const SETUP_LOCK_TIMEOUT = "15s"; + +/** How long a boot-time index drop waits for its lock. */ +export const BOOT_DROP_LOCK_TIMEOUT = "5s"; + +/** An identifier this script splices into SQL: lower-case catalog names only. */ +const IDENTIFIER = /^[a-z_][a-z0-9_]{0,62}$/; + +function say(message) { + process.stderr.write(`HealthLog: ${message}\n`); +} + +/** Double-quote an identifier after checking its shape. */ +function ident(name) { + if (!IDENTIFIER.test(name)) { + throw new Error("refusing to splice an identifier of unexpected shape"); + } + return `"${name}"`; +} + +/** The role's password: 64 hex characters, the same in every container. */ +export function appRolePassword(hmacKey) { + if (!hmacKey) throw new Error("API_TOKEN_HMAC_KEY is not set"); + return createHmac("sha256", hmacKey).update(PASSWORD_LABEL).digest("hex"); +} + +/** + * A SCRAM-SHA-256 verifier in the form `pg_authid.rolpassword` stores + * (RFC 5802/7677; the password is ASCII hex, so SASLprep leaves it as is). + * Postgres takes a string of this shape as already hashed. + */ +export function scramVerifier( + password, + salt = randomBytes(16), + iterations = 4096, +) { + const salted = pbkdf2Sync(password, salt, iterations, 32, "sha256"); + const clientKey = createHmac("sha256", salted).update("Client Key").digest(); + const storedKey = createHash("sha256").update(clientKey).digest(); + const serverKey = createHmac("sha256", salted).update("Server Key").digest(); + return `SCRAM-SHA-256$${iterations}:${salt.toString("base64")}$${storedKey.toString("base64")}:${serverKey.toString("base64")}`; +} + +/** The owner's URL with the role's name and password: same host, database and parameters. */ +export function appRoleUrl(ownerUrl, password) { + const url = new URL(ownerUrl); + url.username = APP_ROLE; + url.password = password; + return url.toString(); +} + +/** The user a URL names, for the notices. Never the password. */ +function userOf(databaseUrl) { + try { + return decodeURIComponent(new URL(databaseUrl).username) || "(default)"; + } catch { + return "(unparsable)"; + } +} + +function errorText(err) { + const code = err?.code; + const message = err instanceof Error ? err.message : String(err); + return typeof code === "string" ? `${code} ${message}` : message; +} + +/** + * Whether the connected user can set the role up, and why not when it + * cannot. Read-only. + * + * @param {{ query: Function }} client + * @returns {Promise<{ ok: true, superuser: boolean } | { ok: false, reason: string, detail: string }>} + */ +export async function checkPreconditions(client) { + const { rows } = await client.query( + `SELECT current_setting('server_version_num')::int AS version, + r.rolsuper AS superuser, + r.rolcreaterole AS createrole, + current_user AS me + FROM pg_roles r WHERE r.rolname = current_user`, + ); + const me = rows[0]; + if (me.me === APP_ROLE) { + return { + ok: false, + reason: "already_app_role", + detail: `DATABASE_URL already names ${APP_ROLE}`, + }; + } + if (me.version < MIN_SERVER_VERSION_NUM) { + return { + ok: false, + reason: "server_too_old", + detail: `Postgres ${Math.floor(me.version / 10000)} has no MAINTAIN privilege (17 or newer does)`, + }; + } + if (!me.superuser && !me.createrole) { + return { + ok: false, + reason: "cannot_create_roles", + detail: `the database user ${me.me} may not create roles (a managed or external database, or a user without CREATEROLE)`, + }; + } + const existing = await client.query( + `SELECT shobj_description(r.oid, 'pg_authid') AS comment, + pg_has_role(r.oid, current_user, 'MEMBER') AS member_of_owner, + r.rolsuper OR r.rolreplication OR r.rolbypassrls AS elevated + FROM pg_roles r WHERE r.rolname = $1`, + [APP_ROLE], + ); + const role = existing.rows[0]; + if (role && role.comment !== ROLE_COMMENT) { + return { + ok: false, + reason: "foreign_role", + detail: `a role named ${APP_ROLE} exists that HealthLog did not create`, + }; + } + if (role?.member_of_owner) { + return { + ok: false, + reason: "role_inherits_owner", + detail: `${APP_ROLE} is a member of ${me.me}, so it would hold the owner's rights`, + }; + } + if (role?.elevated && !me.superuser) { + return { + ok: false, + reason: "role_elevated", + detail: `${APP_ROLE} has SUPERUSER, REPLICATION or BYPASSRLS, and only a superuser can take it away`, + }; + } + return { ok: true, superuser: me.superuser }; +} + +/** + * Create or update the role and apply every grant, in one transaction. + * Call it on a client connected as the owner, after the migrations. + * + * @param {{ query: Function }} client + * @param {{ password: string, schema?: string, superuser: boolean }} options + */ +export async function applyAppRole(client, options) { + const schema = options.schema ?? "public"; + const role = ident(APP_ROLE); + const app = ident(schema); + const queue = ident(QUEUE_SCHEMA); + // The verifier is a base64/`$`/`:` string; it never contains a quote. + const verifier = scramVerifier(options.password); + if (verifier.includes("'")) throw new Error("unexpected verifier shape"); + + await client.query("BEGIN"); + try { + await client.query(`SET LOCAL lock_timeout = '${SETUP_LOCK_TIMEOUT}'`); + await client.query( + `SELECT pg_advisory_xact_lock(hashtext('healthlog.db-app-role'))`, + ); + + const { rows: present } = await client.query( + `SELECT 1 FROM pg_roles WHERE rolname = $1`, + [APP_ROLE], + ); + // The attributes a superuser alone may set are named only by one; a + // role a CREATEROLE user creates never has them (checked beforehand). + const elevatedOff = options.superuser + ? " NOSUPERUSER NOREPLICATION NOBYPASSRLS" + : ""; + const attributes = `LOGIN NOCREATEDB NOCREATEROLE NOINHERIT${elevatedOff}`; + if (present.length === 0) { + await client.query( + `CREATE ROLE ${role} ${attributes} PASSWORD '${verifier}'`, + ); + } else { + await client.query( + `ALTER ROLE ${role} ${attributes} PASSWORD '${verifier}'`, + ); + } + await client.query( + `COMMENT ON ROLE ${role} IS '${ROLE_COMMENT.replace(/'/g, "''")}'`, + ); + + // A non-superuser owner hands objects to the role only when it may act + // as it (Postgres 16+), and must keep reading what the role owns: the + // dump before a migration reads the queue schema too. The membership + // runs owner -> role, never the other way. + if (!options.superuser) { + await client.query( + `GRANT ${role} TO CURRENT_USER WITH INHERIT TRUE, SET TRUE`, + ); + } + + const { rows: db } = await client.query( + `SELECT current_database() AS name`, + ); + await client.query( + `GRANT CONNECT, TEMPORARY ON DATABASE "${String(db[0].name).replace(/"/g, '""')}" TO ${role}`, + ); + + // The app's schema: rows, never structure. + await client.query(`GRANT USAGE ON SCHEMA ${app} TO ${role}`); + await client.query(`REVOKE CREATE ON SCHEMA ${app} FROM ${role}`); + const { rows: schemaOwner } = await client.query( + `SELECT pg_has_role(current_user, nspowner, 'USAGE') AS owns + FROM pg_namespace WHERE nspname = $1`, + [schema], + ); + if (options.superuser || schemaOwner[0]?.owns) { + await client.query(`REVOKE CREATE ON SCHEMA ${app} FROM PUBLIC`); + } else { + say( + `notice: the schema ${schema} is not the database user's to change; PUBLIC may still create objects in it`, + ); + } + + const { rows: tables } = await client.query( + `SELECT c.relname AS name + FROM pg_class c + JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE n.nspname = $1 AND c.relkind IN ('r', 'p', 'v', 'm', 'f') + AND c.relname <> ALL($3::text[]) + AND NOT (has_table_privilege($2, c.oid, 'SELECT') + AND has_table_privilege($2, c.oid, 'INSERT') + AND has_table_privilege($2, c.oid, 'UPDATE') + AND has_table_privilege($2, c.oid, 'DELETE'))`, + [schema, APP_ROLE, READ_ONLY_TABLES], + ); + for (const { name } of tables) { + await client.query( + `GRANT SELECT, INSERT, UPDATE, DELETE ON ${app}.${ident(name)} TO ${role}`, + ); + } + for (const name of READ_ONLY_TABLES) { + const { rows } = await client.query( + `SELECT to_regclass($1) IS NOT NULL AS present`, + [`${app}.${ident(name)}`], + ); + if (!rows[0].present) continue; + await client.query( + `REVOKE INSERT, UPDATE, DELETE, TRUNCATE ON ${app}.${ident(name)} FROM ${role}`, + ); + await client.query(`GRANT SELECT ON ${app}.${ident(name)} TO ${role}`); + } + + const { rows: sequences } = await client.query( + `SELECT c.relname AS name + FROM pg_class c + JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE n.nspname = $1 AND c.relkind = 'S' + AND NOT (has_sequence_privilege($2, c.oid, 'USAGE') + AND has_sequence_privilege($2, c.oid, 'SELECT') + AND has_sequence_privilege($2, c.oid, 'UPDATE'))`, + [schema, APP_ROLE], + ); + for (const { name } of sequences) { + await client.query( + `GRANT USAGE, SELECT, UPDATE ON SEQUENCE ${app}.${ident(name)} TO ${role}`, + ); + } + + // Objects the owner creates later in this schema (the migrations of the + // next update) are granted at creation as well. The per-boot pass above + // is what the app relies on; this covers the time between the two. + await client.query( + `ALTER DEFAULT PRIVILEGES IN SCHEMA ${app} GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO ${role}`, + ); + await client.query( + `ALTER DEFAULT PRIVILEGES IN SCHEMA ${app} GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO ${role}`, + ); + + for (const name of MAINTAINED_TABLES) { + const { rows } = await client.query( + `SELECT to_regclass($1) IS NOT NULL AS present`, + [`${app}.${ident(name)}`], + ); + if (rows[0].present) { + await client.query( + `GRANT MAINTAIN ON ${app}.${ident(name)} TO ${role}`, + ); + } + } + + // The queue's schema belongs to the role: pg-boss creates its tables, + // functions and partitions there at runtime. On the first boot after an + // update, and after a restore, everything in it still belongs to the + // owner and is handed over once. + await client.query( + `CREATE SCHEMA IF NOT EXISTS ${queue} AUTHORIZATION ${role}`, + ); + await client.query(`ALTER SCHEMA ${queue} OWNER TO ${role}`); + await transferQueueObjects(client, role); + + await client.query("COMMIT"); + } catch (err) { + await client.query("ROLLBACK").catch(() => {}); + throw err; + } +} + +/** Hand every object in the queue schema the role does not own to it. */ +async function transferQueueObjects(client, role) { + const { rows: relations } = await client.query( + `SELECT c.oid::regclass::text AS name, c.relkind AS kind + FROM pg_class c + JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE n.nspname = $1 + AND c.relowner <> (SELECT oid FROM pg_roles WHERE rolname = $2) + AND c.relkind IN ('r', 'p', 'v', 'm', 'f', 'S') + -- A sequence a column owns follows its table. + AND NOT (c.relkind = 'S' AND EXISTS ( + SELECT 1 FROM pg_depend d + WHERE d.classid = 'pg_class'::regclass AND d.objid = c.oid + AND d.deptype IN ('a', 'i'))) + ORDER BY c.relkind = 'r' DESC, c.oid`, + [QUEUE_SCHEMA, APP_ROLE], + ); + for (const { name, kind } of relations) { + const what = + kind === "S" + ? "SEQUENCE" + : kind === "v" + ? "VIEW" + : kind === "m" + ? "MATERIALIZED VIEW" + : kind === "f" + ? "FOREIGN TABLE" + : "TABLE"; + // `regclass::text` is the catalog's own quoting of the name. + await client.query(`ALTER ${what} ${name} OWNER TO ${role}`); + } + + const { rows: routines } = await client.query( + `SELECT p.oid::regprocedure::text AS signature + FROM pg_proc p + JOIN pg_namespace n ON n.oid = p.pronamespace + WHERE n.nspname = $1 + AND p.proowner <> (SELECT oid FROM pg_roles WHERE rolname = $2)`, + [QUEUE_SCHEMA, APP_ROLE], + ); + for (const { signature } of routines) { + await client.query(`ALTER ROUTINE ${signature} OWNER TO ${role}`); + } + + // Enums, domains and free-standing composite types; a table's row type + // and an array type follow their owner object. + const { rows: types } = await client.query( + `SELECT t.oid::regtype::text AS name, t.typtype AS kind + FROM pg_type t + JOIN pg_namespace n ON n.oid = t.typnamespace + LEFT JOIN pg_class c ON c.oid = t.typrelid + WHERE n.nspname = $1 + AND t.typowner <> (SELECT oid FROM pg_roles WHERE rolname = $2) + AND t.typelem = 0 + AND (t.typtype IN ('e', 'd', 'r') OR (t.typtype = 'c' AND c.relkind = 'c'))`, + [QUEUE_SCHEMA, APP_ROLE], + ); + for (const { name, kind } of types) { + await client.query( + `ALTER ${kind === "d" ? "DOMAIN" : "TYPE"} ${name} OWNER TO ${role}`, + ); + } +} + +/** + * Drop, as the owner, the indexes on `measurements` the maintenance job can + * no longer drop as the role: the retired ones and the invalid copies of a + * concurrent rebuild. Each waits at most {@link BOOT_DROP_LOCK_TIMEOUT}; one + * that does not get its lock is left for the next boot. + * + * @param {{ query: Function }} client a connection outside any transaction + * @returns {Promise<{ dropped: string[], left: string[] }>} + */ +export async function dropOwnerOnlyIndexes(client) { + const dropped = []; + const left = []; + const { rows: table } = await client.query( + `SELECT to_regclass('measurements') IS NOT NULL AS present`, + ); + if (!table[0].present) return { dropped, left }; + const { rows } = await client.query( + `SELECT c.relname AS name, i.indisvalid AS valid + FROM pg_index i + JOIN pg_class c ON c.oid = i.indexrelid + WHERE i.indrelid = 'measurements'::regclass`, + ); + const targets = rows + .filter( + (r) => + RETIRED_MEASUREMENT_INDEXES.includes(r.name) || + (!r.valid && CONCURRENT_COPY.test(r.name)), + ) + .map((r) => r.name); + if (targets.length === 0) return { dropped, left }; + const { rows: setting } = await client.query( + `SELECT current_setting('lock_timeout') AS value`, + ); + try { + await client.query(`SET lock_timeout = '${BOOT_DROP_LOCK_TIMEOUT}'`); + for (const name of targets) { + try { + await client.query(`DROP INDEX CONCURRENTLY IF EXISTS ${ident(name)}`); + dropped.push(name); + } catch (err) { + left.push(name); + say( + `notice: index ${name} on measurements was not dropped (${errorText(err)}); the next start tries again`, + ); + } + } + } finally { + await client.query(`SELECT set_config('lock_timeout', $1, false)`, [ + setting[0]?.value ?? "0", + ]); + } + return { dropped, left }; +} + +/** Log in as the role once, so a pg_hba.conf that refuses it shows here. */ +async function proveLogin(url) { + const client = await connect(url); + try { + const { rows } = await client.query(`SELECT current_user AS me`); + if (rows[0].me !== APP_ROLE) { + throw new Error(`logged in as ${rows[0].me}, not ${APP_ROLE}`); + } + } finally { + await client.end().catch(() => {}); + } +} + +/** Write the URL to descriptor 3 when the caller opened it. */ +function handOver(url) { + try { + writeSync(3, url); + return true; + } catch { + return false; + } +} + +/** + * The boot step. Returns the role's URL when the app should use it, or null + * to stay on the owner. + * + * @param {NodeJS.ProcessEnv} env + * @returns {Promise} + */ +export async function ensureAppRole(env = process.env) { + const ownerUrl = env.DATABASE_URL; + const mode = (env.DB_APP_ROLE ?? "").trim().toLowerCase(); + if (mode === "off" || mode === "false") { + say( + `DB_APP_ROLE=off: the app connects as ${userOf(ownerUrl)}, the user DATABASE_URL names`, + ); + return null; + } + if (mode && mode !== "auto" && mode !== "on") { + say( + `notice: DB_APP_ROLE="${mode}" is not auto or off; treating it as auto`, + ); + } + if (!ownerUrl) return null; + + const user = userOf(ownerUrl); + const fallBack = (detail) => { + say( + `notice: the app connects as ${user}, the user DATABASE_URL names, not as the restricted role ${APP_ROLE}: ${detail}. ` + + "This is how every earlier release ran. " + + `If ${user} is a restricted role you manage yourself, DB_APP_ROLE=off silences this; otherwise see docs/self-hosting/database-role.md.`, + ); + return null; + }; + + let client; + try { + client = await connect(ownerUrl); + } catch (err) { + return fallBack(`could not connect to set it up (${errorText(err)})`); + } + try { + const pre = await checkPreconditions(client); + if (!pre.ok) { + if (pre.reason === "already_app_role") { + say(`DATABASE_URL already names ${APP_ROLE}; nothing to set up`); + return null; + } + return fallBack(pre.detail); + } + const password = appRolePassword(env.API_TOKEN_HMAC_KEY); + const schema = new URL(ownerUrl).searchParams.get("schema") || "public"; + try { + await applyAppRole(client, { + password, + schema, + superuser: pre.superuser, + }); + } catch (err) { + return fallBack(`setting it up failed (${errorText(err)})`); + } + const url = appRoleUrl(ownerUrl, password); + try { + await proveLogin(url); + } catch (err) { + return fallBack( + `${APP_ROLE} cannot log in (${errorText(err)}); a pg_hba.conf that lists users by name needs a line for it`, + ); + } + const { dropped } = await dropOwnerOnlyIndexes(client); + if (dropped.length > 0) { + say(`dropped index ${dropped.join(", ")} on measurements`); + } + say( + `the app connects as ${APP_ROLE}: rows and the queue, no schema changes (DB_APP_ROLE=off turns this off)`, + ); + return url; + } catch (err) { + return fallBack(`the check failed (${errorText(err)})`); + } finally { + await client.end().catch(() => {}); + } +} + +export async function main(env = process.env) { + const url = await ensureAppRole(env); + if (url && !handOver(url)) { + say( + "the role is ready; the URL goes to file descriptor 3, which this run did not open", + ); + } + return 0; +} + +if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) { + main().then( + (code) => process.exit(code), + (err) => { + say(`notice: the database role step failed (${errorText(err)})`); + process.exit(0); + }, + ); +} diff --git a/src/__tests__/container-scripts-ship-guard.test.ts b/src/__tests__/container-scripts-ship-guard.test.ts index 629a62f48..a7db951d9 100644 --- a/src/__tests__/container-scripts-ship-guard.test.ts +++ b/src/__tests__/container-scripts-ship-guard.test.ts @@ -74,9 +74,11 @@ function entrypointScripts(): string[] { const entrypoint = readFileSync(join(ROOT, "docker-entrypoint.sh"), "utf8"); return [ ...new Set( - [...entrypoint.matchAll(/^\s*node\s+(scripts\/[\w.-]+)/gm)].map( - (m) => m[1], - ), + // A line that starts with the call, or a command substitution that + // makes it (`url="$(node scripts/db-app-role.mjs …)"`). + [ + ...entrypoint.matchAll(/(?:^\s*|\$\(\s*)node\s+(scripts\/[\w.-]+)/gm), + ].map((m) => m[1]), ), ].sort(); } @@ -126,6 +128,7 @@ describe("scripts the entrypoint runs ship in the image", () => { it("finds the boot scripts it is meant to check", () => { // An empty match would agree with any Dockerfile. expect(entrypointScripts()).toContain("scripts/pending-migrations.mjs"); + expect(entrypointScripts()).toContain("scripts/db-app-role.mjs"); }); it("copies every boot script and its local imports into the runner stage", () => { diff --git a/tests/integration/db-app-role.test.ts b/tests/integration/db-app-role.test.ts new file mode 100644 index 000000000..0be78cf29 --- /dev/null +++ b/tests/integration/db-app-role.test.ts @@ -0,0 +1,387 @@ +/** + * The app's restricted database role, against a real Postgres. + * + * `scripts/db-app-role.mjs` runs at boot as the owner and hands the app a URL + * for `healthlog_app`. Proven here: the role does what the app does (Prisma + * reads and writes, pg-boss installing its schema and creating a queue + * partition, the measurement maintenance's VACUUM and concurrent rebuild), + * and is refused what it must not do (DDL in the app's schema, roles, + * schemas, server files and programs, the migration history). A second run is + * a no-op that keeps the same password, the index the role may not drop is + * dropped by the next run as the owner, and an owner that may not create + * roles leaves the app on the owner with a notice. + * + * On Postgres 16 (the nightly `INTEGRATION_PG_IMAGE` run) the script stays + * on the owner by design; the file proves that instead. + */ +import { PrismaPg } from "@prisma/adapter-pg"; +import { Client } from "pg"; +import { PgBoss } from "pg-boss"; +import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; + +import { PrismaClient } from "@/generated/prisma/client"; +import { + MAINTENANCE_SESSION_OPTIONS, + RETIRED_MEASUREMENT_INDEXES, + runMeasurementMaintenance, +} from "@/lib/jobs/measurement-maintenance"; +import { + APP_ROLE, + appRolePassword, + ensureAppRole, +} from "../../scripts/db-app-role.mjs"; + +const HMAC_KEY = "a".repeat(64); +const PROBE_OWNER = "app_role_probe_owner"; +const USER_ID = "user-app-role-probe"; +const RETIRED = RETIRED_MEASUREMENT_INDEXES[0]; + +function ownerUrl(): string { + const url = process.env.DATABASE_URL; + if (!url) throw new Error("DATABASE_URL is not set for the integration run"); + return url; +} + +function withoutSchema(url: string): string { + const u = new URL(url); + u.searchParams.delete("schema"); + u.searchParams.delete("pgbouncer"); + return u.toString(); +} + +async function connect(url: string, options?: string): Promise { + const client = new Client({ connectionString: withoutSchema(url), options }); + await client.connect(); + return client; +} + +/** Run the boot step and keep what it says on stderr. */ +async function boot( + env: Record, +): Promise<{ url: string | null; said: string }> { + const lines: string[] = []; + const spy = vi + .spyOn(process.stderr, "write") + .mockImplementation((chunk: string | Uint8Array) => { + lines.push(String(chunk)); + return true; + }); + try { + const url = await ensureAppRole(env as NodeJS.ProcessEnv); + return { url, said: lines.join("") }; + } finally { + spy.mockRestore(); + } +} + +async function sqlState(db: Client, sql: string): Promise { + try { + await db.query(sql); + return null; + } catch (err) { + return (err as { code?: string }).code ?? "unknown"; + } +} + +let owner: Client; +let serverVersion = 0; + +beforeAll(async () => { + owner = await connect(ownerUrl()); + const { rows } = await owner.query<{ v: number }>( + `SELECT current_setting('server_version_num')::int AS v`, + ); + serverVersion = rows[0].v; + // Each file starts from a worker database without the queue schema. + await owner.query(`DROP SCHEMA IF EXISTS pgboss CASCADE`); +}); + +afterAll(async () => { + // The role is cluster-wide: hand back what it owns here and drop it, so no + // other file or run sees it. + for (const role of [APP_ROLE, PROBE_OWNER]) { + const { rows } = await owner.query( + `SELECT 1 FROM pg_roles WHERE rolname = $1`, + [role], + ); + if (rows.length === 0) continue; + await owner.query(`REASSIGN OWNED BY "${role}" TO CURRENT_USER`); + await owner.query(`DROP OWNED BY "${role}"`); + await owner.query(`DROP ROLE "${role}"`); + } + await owner.query(`DROP SCHEMA IF EXISTS pgboss CASCADE`); + await owner.query(`DELETE FROM users WHERE id = $1`, [USER_ID]); + await owner.end(); +}); + +describe("DB_APP_ROLE=off", () => { + it("stays on the owner without touching the database", async () => { + const { url, said } = await boot({ + DATABASE_URL: ownerUrl(), + API_TOKEN_HMAC_KEY: HMAC_KEY, + DB_APP_ROLE: "off", + }); + expect(url).toBeNull(); + expect(said).toContain("DB_APP_ROLE=off"); + const { rows } = await owner.query( + `SELECT 1 FROM pg_roles WHERE rolname = $1`, + [APP_ROLE], + ); + expect(rows).toHaveLength(0); + }); +}); + +describe("an owner that may not create roles", () => { + it("leaves the app on that user, with a notice and no secret in it", async () => { + await owner.query( + `CREATE ROLE ${PROBE_OWNER} LOGIN NOSUPERUSER NOCREATEROLE PASSWORD 'probe-owner-pw'`, + ); + const url = new URL(ownerUrl()); + url.username = PROBE_OWNER; + url.password = "probe-owner-pw"; + const { url: handed, said } = await boot({ + DATABASE_URL: url.toString(), + API_TOKEN_HMAC_KEY: HMAC_KEY, + }); + expect(handed).toBeNull(); + expect(said).toContain(`the app connects as ${PROBE_OWNER}`); + if (serverVersion >= 170000) { + expect(said).toContain("may not create roles"); + } + expect(said).not.toContain("probe-owner-pw"); + expect(said).not.toContain(appRolePassword(HMAC_KEY)); + }); +}); + +describe("Postgres before 17", () => { + it("stays on the owner: no MAINTAIN privilege", async (ctx) => { + if (serverVersion >= 170000) ctx.skip(); + const { url, said } = await boot({ + DATABASE_URL: ownerUrl(), + API_TOKEN_HMAC_KEY: HMAC_KEY, + }); + expect(url).toBeNull(); + expect(said).toContain("no MAINTAIN privilege"); + }); +}); + +describe("the restricted role (Postgres 17+)", () => { + let appUrl: string; + let app: Client; + + beforeAll(async () => { + if (serverVersion < 170000) return; + // A database first created before Postgres 15 still lets PUBLIC create + // objects in `public`; the boot must take that away from the role too. + await owner.query(`GRANT CREATE ON SCHEMA public TO PUBLIC`); + // An index the role may not drop, as an instance that has not run 0416. + await owner.query( + `CREATE INDEX IF NOT EXISTS "${RETIRED}" ON "measurements" ("user_id", "type", "measured_at")`, + ); + const first = await boot({ + DATABASE_URL: ownerUrl(), + API_TOKEN_HMAC_KEY: HMAC_KEY, + }); + expect(first.url, first.said).not.toBeNull(); + expect(first.said).toContain(`the app connects as ${APP_ROLE}`); + expect(first.said).not.toContain(appRolePassword(HMAC_KEY)); + // The first run drops it as the owner; put it back for the role's turn. + expect(first.said).toContain(`dropped index ${RETIRED}`); + await owner.query( + `CREATE INDEX IF NOT EXISTS "${RETIRED}" ON "measurements" ("user_id", "type", "measured_at")`, + ); + appUrl = first.url!; + app = await connect(appUrl); + }); + + afterAll(async () => { + await app?.end(); + }); + + it("keeps the owner's host, database and parameters", async (ctx) => { + if (serverVersion < 170000) ctx.skip(); + const owned = new URL(ownerUrl()); + const handed = new URL(appUrl); + expect(handed.username).toBe(APP_ROLE); + expect(handed.password).toBe(appRolePassword(HMAC_KEY)); + expect(handed.host).toBe(owned.host); + expect(handed.pathname).toBe(owned.pathname); + expect(handed.search).toBe(owned.search); + }); + + it("reads and writes rows through Prisma", async (ctx) => { + if (serverVersion < 170000) ctx.skip(); + const db = new PrismaClient({ + adapter: new PrismaPg({ connectionString: withoutSchema(appUrl) }), + }); + try { + await db.user.create({ + data: { + id: USER_ID, + username: "app-role-probe", + email: "app-role-probe@example.test", + role: "USER", + }, + }); + await db.measurement.create({ + data: { + userId: USER_ID, + type: "WEIGHT", + value: 70, + unit: "kg", + measuredAt: new Date("2026-01-01T08:00:00Z"), + }, + }); + await db.user.update({ + where: { id: USER_ID }, + data: { username: "app-role-probe-2" }, + }); + expect(await db.measurement.count({ where: { userId: USER_ID } })).toBe( + 1, + ); + await db.measurement.deleteMany({ where: { userId: USER_ID } }); + const migrations = await db.$queryRaw<{ n: bigint }[]>` + SELECT count(*) AS n FROM _prisma_migrations`; + expect(Number(migrations[0].n)).toBeGreaterThan(100); + } finally { + await db.$disconnect(); + } + }); + + it("runs pg-boss: install, a queue partition, a job", async (ctx) => { + if (serverVersion < 170000) ctx.skip(); + const boss = new PgBoss({ + connectionString: withoutSchema(appUrl), + createSchema: false, + supervise: false, + schedule: false, + }); + boss.on("error", () => {}); + await boss.start(); + try { + await boss.createQueue("app-role-probe", { policy: "standard" }); + const id = await boss.send("app-role-probe", { n: 1 }); + const [job] = await boss.fetch("app-role-probe"); + expect(job?.id).toBe(id); + await boss.complete("app-role-probe", job.id); + } finally { + await boss.stop({ graceful: false }); + } + const { rows } = await owner.query<{ foreign: string }>( + `SELECT count(*)::text AS foreign FROM pg_class c + JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE n.nspname = 'pgboss' AND c.relkind IN ('r', 'p') + AND pg_get_userbyid(c.relowner) <> $1`, + [APP_ROLE], + ); + expect(rows[0].foreign).toBe("0"); + }); + + it("vacuums and rebuilds measurements, and leaves the drop to the owner", async (ctx) => { + if (serverVersion < 170000) ctx.skip(); + const db = await connect(appUrl, MAINTENANCE_SESSION_OPTIONS); + try { + const summary = await runMeasurementMaintenance(db, { + vacuum: true, + reindex: true, + }); + expect(summary.outcome).toBe("completed"); + // A VACUUM without the privilege only warns and skips the table, so + // the step reads ok either way; the grant is what makes it real. + const { rows } = await db.query<{ maintain: boolean }>( + `SELECT has_table_privilege(current_user, 'measurements', 'MAINTAIN') AS maintain`, + ); + expect(rows[0].maintain).toBe(true); + expect(summary.steps.find((s) => s.step === "vacuum")?.ok).toBe(true); + const rebuilt = summary.steps.filter((s) => s.step === "reindex"); + expect(rebuilt.length).toBeGreaterThan(0); + expect(rebuilt.every((s) => s.ok)).toBe(true); + expect(summary.dropsLeftForBoot).toEqual([RETIRED]); + } finally { + await db.end(); + } + }); + + it("is refused schema changes, roles, schemas, server files and the migration history", async (ctx) => { + if (serverVersion < 170000) ctx.skip(); + const refused = { + dropTable: await sqlState(app, `DROP TABLE "users"`), + alterTable: await sqlState( + app, + `ALTER TABLE "users" ADD COLUMN probe text`, + ), + createTable: await sqlState(app, `CREATE TABLE public.probe (x int)`), + createFunction: await sqlState( + app, + `CREATE FUNCTION public.probe() RETURNS int LANGUAGE sql AS 'SELECT 1'`, + ), + createRole: await sqlState(app, `CREATE ROLE probe_escalation`), + createSchema: await sqlState(app, `CREATE SCHEMA probe`), + readFile: await sqlState(app, `SELECT pg_read_file('postgresql.conf')`), + program: await sqlState(app, `COPY (SELECT 1) TO PROGRAM 'true'`), + dropIndex: await sqlState(app, `DROP INDEX "${RETIRED}"`), + writeMigrations: await sqlState( + app, + `DELETE FROM _prisma_migrations WHERE false`, + ), + becomeOwner: await sqlState( + app, + `SET ROLE ${new URL(ownerUrl()).username}`, + ), + }; + expect(refused).toEqual({ + dropTable: "42501", + alterTable: "42501", + createTable: "42501", + createFunction: "42501", + createRole: "42501", + createSchema: "42501", + readFile: "42501", + program: "42501", + dropIndex: "42501", + writeMigrations: "42501", + becomeOwner: "42501", + }); + const { rows } = await app.query( + `SELECT rolsuper, rolcreaterole, rolcreatedb, rolinherit, rolreplication, rolbypassrls + FROM pg_roles WHERE rolname = current_user`, + ); + expect(rows[0]).toEqual({ + rolsuper: false, + rolcreaterole: false, + rolcreatedb: false, + rolinherit: false, + rolreplication: false, + rolbypassrls: false, + }); + }); + + it("is idempotent: a second boot keeps the password and drops what the role could not", async (ctx) => { + if (serverVersion < 170000) ctx.skip(); + // A table a later migration adds: the next boot grants it. + await owner.query(`CREATE TABLE public.app_role_probe_new (x int)`); + try { + const second = await boot({ + DATABASE_URL: ownerUrl(), + API_TOKEN_HMAC_KEY: HMAC_KEY, + }); + expect(second.url).toBe(appUrl); + expect(second.said).toContain(`dropped index ${RETIRED}`); + // The connection opened before the second boot still works. + await app.query(`INSERT INTO public.app_role_probe_new VALUES (1)`); + const third = await boot({ + DATABASE_URL: ownerUrl(), + API_TOKEN_HMAC_KEY: HMAC_KEY, + }); + expect(third.url).toBe(appUrl); + expect(third.said).not.toContain("dropped index"); + const { rows } = await owner.query( + `SELECT to_regclass($1) IS NULL AS gone`, + [RETIRED], + ); + expect(rows[0].gone).toBe(true); + } finally { + await owner.query(`DROP TABLE public.app_role_probe_new`); + } + }); +}); From c7ff22ab00bfbacdad0b0ec53efdf2b17757a2a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Tue, 3 Nov 2026 20:37:48 +0100 Subject: [PATCH 5/7] chore(env): whitelist DB_APP_ROLE and document it beside the database settings --- .env.example | 6 ++++++ .env.production.example | 8 ++++++++ docker-compose.yml | 5 +++++ scripts/env-manifest.json | 5 +++++ 4 files changed, 24 insertions(+) diff --git a/.env.example b/.env.example index c0efd6658..61a0289df 100644 --- a/.env.example +++ b/.env.example @@ -38,6 +38,12 @@ # echo "POSTGRES_PASSWORD=$(openssl rand -hex 32)" >> .env # Left commented here so that append does not create a duplicate key. Uncomment # and set it directly instead if you prefer to edit the file by hand. + +# The container runs the app as `healthlog_app`, a database role it creates on +# every start that reads and writes rows but cannot change the schema. The +# migrations keep the DATABASE_URL user. "off" keeps the app on that user, if +# you manage database roles yourself (docs/self-hosting/database-role.md). +# DB_APP_ROLE="off" # POSTGRES_PASSWORD="generate-with-openssl-rand-hex-32" diff --git a/.env.production.example b/.env.production.example index b82aae357..9c6ad5dbe 100644 --- a/.env.production.example +++ b/.env.production.example @@ -526,6 +526,14 @@ API_TOKEN_HMAC_KEY="" # Behind such a pooler, set this to 1, true or yes to send no session # settings at all; the pooler's or the database's own defaults then apply. # DATABASE_SESSION_OPTIONS_DISABLED="1" +# +# The app runs as `healthlog_app`, a database role the container creates on +# every start: it reads and writes rows but cannot alter or drop tables or +# create roles. Migrations and restores still use the user in DATABASE_URL. +# Where that user may not create roles (a managed database), the start log +# says so and the app uses DATABASE_URL as given. Set "off" if you manage +# the database roles yourself. See docs/self-hosting/database-role.md. +# DB_APP_ROLE="off" # ----------------------------------------------------------------------------- diff --git a/docker-compose.yml b/docker-compose.yml index b7e7faef3..f10142dc2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -81,6 +81,11 @@ x-healthlog-env: &healthlog_env # 1 sends neither of the two settings above (they ride the libpq `options` # startup parameter, which PgBouncer refuses by default). DATABASE_SESSION_OPTIONS_DISABLED: "${DATABASE_SESSION_OPTIONS_DISABLED:-}" + # The app connects as `healthlog_app`, a role the entrypoint creates that + # reads and writes rows but cannot change the schema; the migrations keep + # the owner above. off keeps the app on the owner (for an operator who + # manages database roles). See docs/self-hosting/database-role.md. + DB_APP_ROLE: "${DB_APP_ROLE:-}" WITHINGS_WEBHOOK_SECRET: "${WITHINGS_WEBHOOK_SECRET:-}" # Overrides the derived `${NEXT_PUBLIC_APP_URL}/api/withings/callback`; # set it when the URL registered with Withings differs from the app's own diff --git a/scripts/env-manifest.json b/scripts/env-manifest.json index 70546fd43..977a50755 100644 --- a/scripts/env-manifest.json +++ b/scripts/env-manifest.json @@ -225,6 +225,11 @@ { "name": "DATABASE_SESSION_OPTIONS_DISABLED", "purpose": "Set to 1, true or yes to send no session settings (statement timeout, work_mem) as the libpq options startup parameter. Needed behind PgBouncer, which refuses that parameter unless it is in ignore_startup_parameters." + }, + { + "name": "DB_APP_ROLE", + "purpose": "auto (default) or off. Auto: after the migrations the entrypoint creates the role healthlog_app, which may read and write rows and run the queue but not change the schema, and starts the app with it in place of the DATABASE_URL user. Off keeps the app on the DATABASE_URL user, for an operator who manages database roles.", + "example": "off" } ] }, From df1e19f1c2fea881f9c86b8139284e2d00cbef59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Tue, 3 Nov 2026 20:38:46 +0100 Subject: [PATCH 6/7] docs: describe the app's database role, its limits and how to opt out --- docs/ops/deploy.md | 16 ++++ docs/ops/disaster-recovery.md | 8 ++ docs/ops/measurement-maintenance.md | 12 ++- docs/self-hosting/database-role.md | 112 ++++++++++++++++++++++++++++ docs/self-hosting/scaling.md | 9 +++ 5 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 docs/self-hosting/database-role.md diff --git a/docs/ops/deploy.md b/docs/ops/deploy.md index 5a12edd69..15ce03a1d 100644 --- a/docs/ops/deploy.md +++ b/docs/ops/deploy.md @@ -140,6 +140,22 @@ An INVALID index whose migration is already recorded applied is not touched at boot. The boot names it on stderr with `WARNING — index … is INVALID`; rebuild it with `REINDEX INDEX CONCURRENTLY "";` when the database is quiet. +### The app's database role + +The steps above run as the owner `DATABASE_URL` names: the dump, the +migrations and, after them, the setup of `healthlog_app`, the role the app +then connects as ([database-role.md](../self-hosting/database-role.md)). The +app never runs a migration. A migration that creates a table needs nothing +more: the setup grants the role every table it lacks before the app starts. + +On the first start of the release that brings the role, the setup hands the +existing `pgboss` tables to it, which takes each table's lock for a moment. +Stop the old container (and a separate worker) before the new one starts, as +for any update with migrations; a lock that does not come within 15 seconds +leaves that start on the owner with a notice, and the next start tries again. +A web container and a worker container that start together wait for each +other on an advisory lock and derive the same password. + ### v1.46.0 notes - **Postgres 18.** The compose file moves the bundled database from diff --git a/docs/ops/disaster-recovery.md b/docs/ops/disaster-recovery.md index 0e9bb9364..a6c40c94c 100644 --- a/docs/ops/disaster-recovery.md +++ b/docs/ops/disaster-recovery.md @@ -147,6 +147,14 @@ different. Sign in. The app's own key check runs at start and refuses to serve on a key that does not open the data. +The restore runs as the owner `DATABASE_URL` names, and the restored tables +belong to it. The app connects as `healthlog_app` +([database-role.md](../self-hosting/database-role.md)): the start in step 5 +grants that role the restored tables and hands it the queue schema before the +app runs. A dump carries grants to `healthlog_app`; `db-restore` leaves them +out (`--no-owner --no-privileges`). Restoring with `pg_restore` by hand, pass +the same two flags, or it fails on a database without the role. + When the dumps are still in the server's `backups` volume (the database volume was lost, the backups volume was not), step 4 is `docker compose run --rm db-restore latest`. diff --git a/docs/ops/measurement-maintenance.md b/docs/ops/measurement-maintenance.md index e0bce3094..53e827ddb 100644 --- a/docs/ops/measurement-maintenance.md +++ b/docs/ops/measurement-maintenance.md @@ -214,7 +214,7 @@ So an index is retired in two halves (v1.43.8, migration 0416, for exactly as the schema does, and `prisma migrate dev` / `migrate diff` see no drift and generate nothing. 2. The index name goes into `RETIRED_MEASUREMENT_INDEXES` in - `src/lib/jobs/measurement-maintenance.ts`. Every run of this job, the + `src/lib/jobs/retired-measurement-indexes.mjs`. Every run of this job, the nightly 03:40 check included, drops each name still present with `DROP INDEX CONCURRENTLY IF EXISTS`, waiting at most 60 seconds for its locks. A run that runs out of time leaves the index (possibly marked @@ -242,6 +242,16 @@ SET lock_timeout = '60s'; DROP INDEX CONCURRENTLY IF EXISTS "measurements_user_id_type_measured_at_idx"; ``` +When the app connects as its restricted role +([database-role.md](../self-hosting/database-role.md)), the job may vacuum and +rebuild (`MAINTAIN`) but not drop: `DROP INDEX` belongs to the table's owner. +A run then skips the drops, of retired indexes and of the invalid copy a +failed rebuild leaves, names what it found under +`maintenance_drops_left_for_boot` with a warning, and the next start drops +each as the owner with `DROP INDEX CONCURRENTLY`, waiting at most five +seconds. Until then a failed rebuild's copy holds its disk; restart the +container, or drop it by hand as the owner with the statement above. + For the next large index, follow the same two halves. `src/__tests__/migration-lock-timeout-guard.test.ts` refuses a `DROP INDEX CONCURRENTLY` in any migration and requires every retired name to diff --git a/docs/self-hosting/database-role.md b/docs/self-hosting/database-role.md new file mode 100644 index 000000000..4b8ea1403 --- /dev/null +++ b/docs/self-hosting/database-role.md @@ -0,0 +1,112 @@ +# The app's database role + +The container connects to Postgres under two names. The user in +`DATABASE_URL` (on the bundled compose file, `healthlog`, which the official +Postgres image makes a superuser) owns the schema: the entrypoint uses it for +the dump before an update, the migrations and the restore. The app itself, +web and worker, connects as `healthlog_app`, a role the entrypoint sets up +after the migrations on every start. Nothing to configure: the first start of +a release with this change creates it. + +The point is the blast radius of a bug. If a dependency of the app were ever +made to run code inside the container, the database connection it found would +be one that can read and write rows, not one that can drop tables, rewrite the +schema or reach the server's files. + +## What the role may and may not do + +| May | May not | +| ------------------------------------------------------------------------- | --------------------------------------------------------------- | +| read, insert, update and delete rows in every table of the app's schema | create, alter or drop tables, indexes, types or functions there | +| use and advance its sequences | write `_prisma_migrations` (it may read it) | +| `VACUUM`, `ANALYZE` and `REINDEX` the `measurements` table (`MAINTAIN`) | create roles, schemas or databases | +| own the `pgboss` schema, where the job queue keeps and changes its tables | act as a superuser: no `COPY … PROGRAM`, no `pg_read_file` | +| create temporary tables | become the owner (`SET ROLE`) | + +The queue schema is the role's because pg-boss creates and migrates its own +tables, functions and per-queue partitions while the app runs. Nothing of the +app's data lives there. The start also takes `CREATE` on the app's schema +away from `PUBLIC`, which Postgres 15 and newer already do for a new database +but a database first created on an older major still allows. + +Its password is derived from `API_TOKEN_HMAC_KEY`, so a separate worker +container ([scaling.md](scaling.md)) arrives at the same one without a new +variable, and it is stored as a SCRAM hash. The owner's URL is replaced, not +kept beside the role's, so it is not in the app's environment. +`docker compose exec app …` still starts with the container's configured +environment, so the operator scripts (`reset-password.mjs` and the others) +run as the owner as before. + +Each start re-applies the grants, so a table the new migrations created is +covered before the app starts, and a restored database is put right on the +start after the restore. + +## What the start log says + +When it is in place: + +```text +HealthLog: the app connects as healthlog_app: rows and the queue, no schema changes (DB_APP_ROLE=off turns this off) +``` + +When it is not, one notice names the reason, and the app runs as the +`DATABASE_URL` user, exactly as every earlier release did: + +```text +HealthLog: notice: the app connects as healthlog, the user DATABASE_URL names, not as the restricted role healthlog_app: . … +``` + +The reasons: + +- **The user may not create roles.** A managed database (RDS, Cloud SQL, + Supabase and the like) or a database server where you gave HealthLog an + account without `CREATEROLE`. Either leave it, or create the split yourself + (below) and set `DB_APP_ROLE=off`. +- **Postgres older than 17.** The maintenance job's `VACUUM` and index rebuild + need the `MAINTAIN` privilege, which Postgres 17 introduced; before it only + the table's owner may run them. The bundled database is Postgres 18; see + [postgres-major-upgrade.md](../ops/postgres-major-upgrade.md) for one you + run yourself. +- **The role cannot log in.** A `pg_hba.conf` that lists users by name needs a + line for `healthlog_app` beside the one for the owner. +- **A role named `healthlog_app` exists that HealthLog did not create.** The + start leaves a role it does not recognise alone. +- **Setting it up failed**, most often a lock it waited 15 seconds for: the + first start hands the existing queue tables to the role, and another + container still using them holds it up. The next start tries again. + +None of these stops the start. + +## Turning it off + +Set `DB_APP_ROLE=off` in `.env` and recreate the container. The app then +connects as the `DATABASE_URL` user. Use it when you manage database roles +yourself, including when `DATABASE_URL` already names a restricted role of +your own; then that role needs what the table above lists, and the +migrations need a user that owns the schema. + +The role stays in the database when you turn it off and does no harm. To +remove it, as the owner: + +```sql +REASSIGN OWNED BY healthlog_app TO healthlog; +DROP OWNED BY healthlog_app; +DROP ROLE healthlog_app; +``` + +## Index drops at start + +`DROP INDEX` belongs to a table's owner; `MAINTAIN` does not include it. The +measurement maintenance therefore no longer drops a retired index or the +invalid copy a failed rebuild leaves; it names them on its run and the next +start drops them as the owner, waiting at most five seconds for each +([measurement-maintenance.md](../ops/measurement-maintenance.md)). + +## Dumps and restores + +The nightly dump runs as the role and reads everything: the app's schema +through its grants, the queue schema as its owner. A dump carries the grants +to `healthlog_app`; `db-restore` leaves them and the ownership out +(`--no-owner --no-privileges`), restores as the owner, and the next start +grants again. A restore of your own with `pg_restore` should do the same, or +it fails on a database without the role. diff --git a/docs/self-hosting/scaling.md b/docs/self-hosting/scaling.md index 6ecac0731..3bc36d23b 100644 --- a/docs/self-hosting/scaling.md +++ b/docs/self-hosting/scaling.md @@ -129,6 +129,15 @@ looks healthy. Deploy both from the same image tag (`HEALTHLOG_IMAGE_REF` is shared by `app` and the `app-worker` block) and restart the worker together with the web containers. +## Database role + +Both containers run the entrypoint, and both set up `healthlog_app`, the +database role the app connects as +([database-role.md](database-role.md)). They take turns on an advisory lock +and derive the same password from `API_TOKEN_HMAC_KEY`, which the shared +environment block gives both, so a worker that starts after the web container +changes nothing the web container relies on. + ## Healthchecks - `app` (web) healthcheck: `wget /api/health` every 30s. From 3d1a6224bab7549e0ae887756fdb4aeaca2d8de7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Tue, 3 Nov 2026 20:49:31 +0100 Subject: [PATCH 7/7] test: drop vi imports the removed schema-mirror mocks left unused --- tests/integration/activity-calendar-read.test.ts | 2 +- tests/integration/auth-ledger-cleanup.test.ts | 2 +- tests/integration/backup-document-streaming.test.ts | 2 +- tests/integration/backup-restore-memory.test.ts | 2 +- .../integration/backup-upload-document-memory.test.ts | 2 +- tests/integration/changepoint-extended.test.ts | 2 +- tests/integration/coach-availability-probe.test.ts | 2 +- tests/integration/coach-stale-rollup-tail.test.ts | 2 +- ...elation-findings-before-seasonal-adjustment.test.ts | 2 +- .../integration/cycle-crosstab-day-aggregates.test.ts | 2 +- tests/integration/daily-digest-today-overview.test.ts | 2 +- tests/integration/daily-series-user-tz.test.ts | 2 +- tests/integration/demo-seed-shape.test.ts | 2 +- tests/integration/demo-seed-sources.test.ts | 2 +- tests/integration/dense-read-bounded.test.ts | 2 +- tests/integration/intake-late-take-future-slot.test.ts | 2 +- .../integration/intake-scheduled-for-targeting.test.ts | 2 +- tests/integration/intake-slot-dedup.test.ts | 2 +- tests/integration/local-day-keys.test.ts | 2 +- tests/integration/mfa-login-flow.test.ts | 2 +- tests/integration/mood-insights-crosstab.test.ts | 2 +- tests/integration/partial-unique-live-rows.test.ts | 2 +- .../integration/pr-detection-cumulative-paged.test.ts | 2 +- tests/integration/pr-detection-end-to-end.test.ts | 2 +- tests/integration/provider-dense-fold.test.ts | 2 +- tests/integration/rest-mode-chronic.test.ts | 2 +- .../integration/restore-consolidation-deadlock.test.ts | 10 +--------- tests/integration/soft-delete-purge.test.ts | 2 +- tests/integration/source-ladder-new-sources.test.ts | 2 +- tests/integration/source-provenance.test.ts | 2 +- tests/integration/tiered-context-local-day.test.ts | 2 +- tests/integration/timeline-sleep-nights.test.ts | 2 +- .../integration/tombstone-cleanup-restore-lock.test.ts | 2 +- 33 files changed, 33 insertions(+), 41 deletions(-) diff --git a/tests/integration/activity-calendar-read.test.ts b/tests/integration/activity-calendar-read.test.ts index 4169cda1e..43bf17c16 100644 --- a/tests/integration/activity-calendar-read.test.ts +++ b/tests/integration/activity-calendar-read.test.ts @@ -8,7 +8,7 @@ * - a workout two devices recorded counts once, its minutes once; * - the window is exact: nothing before `fromKey` or after `toKey`. */ -import { beforeAll, describe, expect, it, vi } from "vitest"; +import { beforeAll, describe, expect, it } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; import { readActivityDays } from "@/lib/activity/calendar-read"; diff --git a/tests/integration/auth-ledger-cleanup.test.ts b/tests/integration/auth-ledger-cleanup.test.ts index be4ddcee4..a1a37931c 100644 --- a/tests/integration/auth-ledger-cleanup.test.ts +++ b/tests/integration/auth-ledger-cleanup.test.ts @@ -7,7 +7,7 @@ * token without an expiry all stay. The delete is batched and says when it * stopped with rows left. */ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it } from "vitest"; import { cleanupAuthLedgers } from "@/lib/jobs/auth-ledger-cleanup"; diff --git a/tests/integration/backup-document-streaming.test.ts b/tests/integration/backup-document-streaming.test.ts index 2768a7644..251f71c68 100644 --- a/tests/integration/backup-document-streaming.test.ts +++ b/tests/integration/backup-document-streaming.test.ts @@ -23,7 +23,7 @@ import { createHash, randomBytes } from "node:crypto"; import v8 from "node:v8"; import vm from "node:vm"; -import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; +import { afterAll, beforeAll, describe, expect, it } from "vitest"; process.env.ENCRYPTION_KEY ??= "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; diff --git a/tests/integration/backup-restore-memory.test.ts b/tests/integration/backup-restore-memory.test.ts index 56648a68b..e9702d61d 100644 --- a/tests/integration/backup-restore-memory.test.ts +++ b/tests/integration/backup-restore-memory.test.ts @@ -31,7 +31,7 @@ import v8 from "node:v8"; import vm from "node:vm"; -import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; +import { afterAll, beforeAll, describe, expect, it } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; diff --git a/tests/integration/backup-upload-document-memory.test.ts b/tests/integration/backup-upload-document-memory.test.ts index 1cf0c8949..6bd936c1e 100644 --- a/tests/integration/backup-upload-document-memory.test.ts +++ b/tests/integration/backup-upload-document-memory.test.ts @@ -42,7 +42,7 @@ import { once } from "node:events"; import v8 from "node:v8"; import vm from "node:vm"; -import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; +import { afterAll, beforeAll, describe, expect, it } from "vitest"; import { cookieJar, headerJar } from "./mock-next-headers"; import { getPrismaClient, truncateAllTables } from "./setup"; diff --git a/tests/integration/changepoint-extended.test.ts b/tests/integration/changepoint-extended.test.ts index 536c9df0c..32ce368c5 100644 --- a/tests/integration/changepoint-extended.test.ts +++ b/tests/integration/changepoint-extended.test.ts @@ -4,7 +4,7 @@ * (`readDayAggregates`); this proves that path finds a step, reports its * segment sizes, and leaves a switched-off module's metric unread. */ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; import { buildExtendedChangepointSignals } from "@/lib/insights/derived/changepoint"; diff --git a/tests/integration/coach-availability-probe.test.ts b/tests/integration/coach-availability-probe.test.ts index 6713eec3f..7a3453725 100644 --- a/tests/integration/coach-availability-probe.test.ts +++ b/tests/integration/coach-availability-probe.test.ts @@ -11,7 +11,7 @@ * The fixture is the reported case: a fortnight of glucose readings from April * 2024, deliberately outside every window the Coach can search. */ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; import { probeCoachAvailability } from "@/lib/ai/coach/tools/availability"; diff --git a/tests/integration/coach-stale-rollup-tail.test.ts b/tests/integration/coach-stale-rollup-tail.test.ts index a9093dcfc..26e13ea4d 100644 --- a/tests/integration/coach-stale-rollup-tail.test.ts +++ b/tests/integration/coach-stale-rollup-tail.test.ts @@ -30,7 +30,7 @@ * either compare a bucket's `computedAt` against the type's in-window * MAX(`updatedAt`), or invalidate on the hard-delete path — and that is a * larger change than this honesty repair. - */ import { beforeEach, describe, expect, it, vi } from "vitest"; + */ import { beforeEach, describe, expect, it } from "vitest"; process.env.ENCRYPTION_KEY ??= "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; diff --git a/tests/integration/correlation-findings-before-seasonal-adjustment.test.ts b/tests/integration/correlation-findings-before-seasonal-adjustment.test.ts index b43a8b830..409fa2563 100644 --- a/tests/integration/correlation-findings-before-seasonal-adjustment.test.ts +++ b/tests/integration/correlation-findings-before-seasonal-adjustment.test.ts @@ -7,7 +7,7 @@ * (`AppSettings.correlationSeasonalEngineSince`), which the first discovery * sync of the new engine stamps before it writes, not by a calendar date. */ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; import { diff --git a/tests/integration/cycle-crosstab-day-aggregates.test.ts b/tests/integration/cycle-crosstab-day-aggregates.test.ts index 3c89c9b56..633853b10 100644 --- a/tests/integration/cycle-crosstab-day-aggregates.test.ts +++ b/tests/integration/cycle-crosstab-day-aggregates.test.ts @@ -6,7 +6,7 @@ * with two sources on the same days, two device types within one source, * dense CGM glucose, summed steps and readings next to Berlin midnight. */ -import { beforeAll, describe, expect, it, vi } from "vitest"; +import { beforeAll, describe, expect, it } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; import { readSourceDayAggregates } from "@/lib/measurements/day-aggregates"; diff --git a/tests/integration/daily-digest-today-overview.test.ts b/tests/integration/daily-digest-today-overview.test.ts index 5b64beffa..1b635764e 100644 --- a/tests/integration/daily-digest-today-overview.test.ts +++ b/tests/integration/daily-digest-today-overview.test.ts @@ -9,7 +9,7 @@ * AI provider is the subject on purpose: the overview has to be whole * without one. */ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it } from "vitest"; import type { Prisma } from "@/generated/prisma/client"; import { loadDailyDigest } from "@/lib/daily/load-digest"; diff --git a/tests/integration/daily-series-user-tz.test.ts b/tests/integration/daily-series-user-tz.test.ts index 7d55f2e75..5b0df68d0 100644 --- a/tests/integration/daily-series-user-tz.test.ts +++ b/tests/integration/daily-series-user-tz.test.ts @@ -16,7 +16,7 @@ * the live instance has it, and asserts that the daily series (both the * rollup-backed read and the live aggregate) buckets by the user's day. */ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; import { diff --git a/tests/integration/demo-seed-shape.test.ts b/tests/integration/demo-seed-shape.test.ts index 92099600f..011f16933 100644 --- a/tests/integration/demo-seed-shape.test.ts +++ b/tests/integration/demo-seed-shape.test.ts @@ -18,7 +18,7 @@ * day-to-day steps around 1 to 2 mmHg outside the climb and the fall). The * peak also has a ceiling: past 148 the chart's padded axis jumps to 50-200. */ -import { beforeAll, describe, expect, it, vi } from "vitest"; +import { beforeAll, describe, expect, it } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; diff --git a/tests/integration/demo-seed-sources.test.ts b/tests/integration/demo-seed-sources.test.ts index 81254beec..bc07ab63f 100644 --- a/tests/integration/demo-seed-sources.test.ts +++ b/tests/integration/demo-seed-sources.test.ts @@ -10,7 +10,7 @@ * holds every Withings and Apple Health row against the type maps those * integrations ingest through. */ -import { beforeAll, describe, expect, it, vi } from "vitest"; +import { beforeAll, describe, expect, it } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; import { APPLE_HEALTH_TYPE_MAP } from "@/lib/measurements/apple-health-mapping"; diff --git a/tests/integration/dense-read-bounded.test.ts b/tests/integration/dense-read-bounded.test.ts index 95821e82b..81f157397 100644 --- a/tests/integration/dense-read-bounded.test.ts +++ b/tests/integration/dense-read-bounded.test.ts @@ -22,7 +22,7 @@ import v8 from "node:v8"; import vm from "node:vm"; -import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; +import { afterAll, beforeAll, describe, expect, it } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; import { buildGradedSeriesWithRollups } from "@/lib/insights/graded-series"; diff --git a/tests/integration/intake-late-take-future-slot.test.ts b/tests/integration/intake-late-take-future-slot.test.ts index 673405c4e..0d1f1a0c8 100644 --- a/tests/integration/intake-late-take-future-slot.test.ts +++ b/tests/integration/intake-late-take-future-slot.test.ts @@ -19,7 +19,7 @@ * dashboard composes (`computeDisplayDue` + the taken/scheduled tally) reflects * the correct state. */ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; diff --git a/tests/integration/intake-scheduled-for-targeting.test.ts b/tests/integration/intake-scheduled-for-targeting.test.ts index e6dcba33a..c9aabf03c 100644 --- a/tests/integration/intake-scheduled-for-targeting.test.ts +++ b/tests/integration/intake-scheduled-for-targeting.test.ts @@ -16,7 +16,7 @@ * 2. Marking the morning dose does NOT mark the evening dose (the evening * slot stays pending) and vice-versa. */ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; diff --git a/tests/integration/intake-slot-dedup.test.ts b/tests/integration/intake-slot-dedup.test.ts index d35b9114e..46db94780 100644 --- a/tests/integration/intake-slot-dedup.test.ts +++ b/tests/integration/intake-slot-dedup.test.ts @@ -17,7 +17,7 @@ * 4. taken > skipped tie-break keeps the taken row. * 5. Boot discovery only matches users that actually hold duplicates. */ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; diff --git a/tests/integration/local-day-keys.test.ts b/tests/integration/local-day-keys.test.ts index 4b9d4e50b..61333afa4 100644 --- a/tests/integration/local-day-keys.test.ts +++ b/tests/integration/local-day-keys.test.ts @@ -8,7 +8,7 @@ * 23:30 UTC is the next day in Berlin and still the same day in Los Angeles, * a reading at 03:00 UTC is the previous day in Los Angeles. */ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; import { readActivityDayKeys } from "@/lib/dashboard/summary-reads"; diff --git a/tests/integration/mfa-login-flow.test.ts b/tests/integration/mfa-login-flow.test.ts index 926c09dc2..aadf2cbd3 100644 --- a/tests/integration/mfa-login-flow.test.ts +++ b/tests/integration/mfa-login-flow.test.ts @@ -6,7 +6,7 @@ * that the recovery-code burn is durable and one-time, and that the challenge * ticket can be claimed exactly once. This exercises all three end to end. */ -import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, it } from "vitest"; import * as OTPAuth from "otpauth"; import { getPrismaClient, truncateAllTables } from "./setup"; diff --git a/tests/integration/mood-insights-crosstab.test.ts b/tests/integration/mood-insights-crosstab.test.ts index fb2576ab9..3b91e8b50 100644 --- a/tests/integration/mood-insights-crosstab.test.ts +++ b/tests/integration/mood-insights-crosstab.test.ts @@ -8,7 +8,7 @@ * Exercises the widened measurement `type: { in: [...] }` filter and the * structured-tag join end-to-end against Postgres. */ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; import { fetchMoodAggregates } from "@/lib/insights/mood-aggregates"; diff --git a/tests/integration/partial-unique-live-rows.test.ts b/tests/integration/partial-unique-live-rows.test.ts index 571744249..238b51a20 100644 --- a/tests/integration/partial-unique-live-rows.test.ts +++ b/tests/integration/partial-unique-live-rows.test.ts @@ -20,7 +20,7 @@ * Postgres cannot arbiter against a partial unique). See migration 0121's * header for the full rationale. */ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it } from "vitest"; process.env.ENCRYPTION_KEY ??= "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; diff --git a/tests/integration/pr-detection-cumulative-paged.test.ts b/tests/integration/pr-detection-cumulative-paged.test.ts index 16abfa6f8..b28910248 100644 --- a/tests/integration/pr-detection-cumulative-paged.test.ts +++ b/tests/integration/pr-detection-cumulative-paged.test.ts @@ -12,7 +12,7 @@ * best day sits in the middle of the history, and no read of the history is * unbounded. */ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; diff --git a/tests/integration/pr-detection-end-to-end.test.ts b/tests/integration/pr-detection-end-to-end.test.ts index 91e8d85e7..4f5beab14 100644 --- a/tests/integration/pr-detection-end-to-end.test.ts +++ b/tests/integration/pr-detection-end-to-end.test.ts @@ -18,7 +18,7 @@ * of going through pg-boss so the integration suite stays free of * out-of-process job-queue plumbing. */ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; diff --git a/tests/integration/provider-dense-fold.test.ts b/tests/integration/provider-dense-fold.test.ts index 095cdd3f1..b786eda3a 100644 --- a/tests/integration/provider-dense-fold.test.ts +++ b/tests/integration/provider-dense-fold.test.ts @@ -16,7 +16,7 @@ * day, deleted outright, expires the account's delta cursors. An Apple * Health fold expires nothing. */ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it } from "vitest"; import { upsertGoogleHealthMeasurements } from "@/lib/google-health/sync-core"; import { runDenseIntradayRetention } from "@/lib/measurements/dense-intraday-retention"; diff --git a/tests/integration/rest-mode-chronic.test.ts b/tests/integration/rest-mode-chronic.test.ts index e335f3c50..413cd9d84 100644 --- a/tests/integration/rest-mode-chronic.test.ts +++ b/tests/integration/rest-mode-chronic.test.ts @@ -10,7 +10,7 @@ * digest's Day N, and the achievements streak freeze, which runs its own * episode query. */ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it } from "vitest"; import type { Prisma } from "@/generated/prisma/client"; import { __resetAllCachesForTests } from "@/lib/cache/server-cache"; diff --git a/tests/integration/restore-consolidation-deadlock.test.ts b/tests/integration/restore-consolidation-deadlock.test.ts index 0e9deeb46..0374db4c0 100644 --- a/tests/integration/restore-consolidation-deadlock.test.ts +++ b/tests/integration/restore-consolidation-deadlock.test.ts @@ -28,15 +28,7 @@ * consolidation's day transaction and the first test fails with the * consolidation rejected on `40P01`. */ -import { - afterAll, - afterEach, - beforeEach, - describe, - expect, - it, - vi, -} from "vitest"; +import { afterAll, afterEach, beforeEach, describe, expect, it } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; import { acceptingBoss } from "./restore-job-driver"; diff --git a/tests/integration/soft-delete-purge.test.ts b/tests/integration/soft-delete-purge.test.ts index ce16cfbf6..3b2db4433 100644 --- a/tests/integration/soft-delete-purge.test.ts +++ b/tests/integration/soft-delete-purge.test.ts @@ -9,7 +9,7 @@ * it; a purged visit cascades its links; a start folded into a live cycle * stays; the coach facts keep their longer horizon. */ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it } from "vitest"; import { TOMBSTONE_RETENTION_DAYS } from "@/lib/auth/native-client"; import { diff --git a/tests/integration/source-ladder-new-sources.test.ts b/tests/integration/source-ladder-new-sources.test.ts index f6a6379b4..406b3dbaf 100644 --- a/tests/integration/source-ladder-new-sources.test.ts +++ b/tests/integration/source-ladder-new-sources.test.ts @@ -9,7 +9,7 @@ * Health, and a day between two sources that were on the ladder before * resolves exactly as it did. */ -import { beforeAll, describe, expect, it, vi } from "vitest"; +import { beforeAll, describe, expect, it } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; import type { diff --git a/tests/integration/source-provenance.test.ts b/tests/integration/source-provenance.test.ts index 42b82c950..5bb8dd767 100644 --- a/tests/integration/source-provenance.test.ts +++ b/tests/integration/source-provenance.test.ts @@ -8,7 +8,7 @@ * day values count asleep stages only, and the comparison lines the two * sources up per day. */ -import { beforeAll, describe, expect, it, vi } from "vitest"; +import { beforeAll, describe, expect, it } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; import { diff --git a/tests/integration/tiered-context-local-day.test.ts b/tests/integration/tiered-context-local-day.test.ts index 2405d5094..e34f22229 100644 --- a/tests/integration/tiered-context-local-day.test.ts +++ b/tests/integration/tiered-context-local-day.test.ts @@ -14,7 +14,7 @@ * readings on that local day, 08:00 PDT (15:00Z) and 23:30 PDT (06:30Z on * the 9th), must form one bucket for the 8th. */ -import { beforeAll, describe, expect, it, vi } from "vitest"; +import { beforeAll, describe, expect, it } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; diff --git a/tests/integration/timeline-sleep-nights.test.ts b/tests/integration/timeline-sleep-nights.test.ts index a4c810e0e..3401039f8 100644 --- a/tests/integration/timeline-sleep-nights.test.ts +++ b/tests/integration/timeline-sleep-nights.test.ts @@ -9,7 +9,7 @@ * call one: CORE + DEEP + REM of the night's winning writer, IN_BED and * AWAKE left out, keyed by its wake day. */ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup"; diff --git a/tests/integration/tombstone-cleanup-restore-lock.test.ts b/tests/integration/tombstone-cleanup-restore-lock.test.ts index 16f5e882f..3fe3737f9 100644 --- a/tests/integration/tombstone-cleanup-restore-lock.test.ts +++ b/tests/integration/tombstone-cleanup-restore-lock.test.ts @@ -17,7 +17,7 @@ * `purgeTombstonesByAccount` and the purge waits out the hold, then deletes * the restoring account's tombstones once the stand-in rolls back. */ -import { afterAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterAll, beforeEach, describe, expect, it } from "vitest"; import { getPrismaClient, truncateAllTables } from "./setup";