@libredb/studio 0.9.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +127 -0
- package/.cursorrules +426 -0
- package/.devin/wiki.json +143 -0
- package/.dockerignore +80 -0
- package/.env.example +159 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +49 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +29 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +57 -0
- package/.github/workflows/ci.yml +185 -0
- package/.github/workflows/codeql.yml +57 -0
- package/.github/workflows/docker-build-push.yml +118 -0
- package/.github/workflows/helm-release.yml +113 -0
- package/CLAUDE.md +265 -0
- package/CODE_OF_CONDUCT.md +124 -0
- package/CONTRIBUTING.md +154 -0
- package/Dockerfile +73 -0
- package/LICENSE +21 -0
- package/README.md +614 -0
- package/SECURITY.md +107 -0
- package/artifacthub-repo.yml +4 -0
- package/bun.lock +1714 -0
- package/bunfig.toml +3 -0
- package/charts/libredb-studio/.helmignore +11 -0
- package/charts/libredb-studio/Chart.lock +6 -0
- package/charts/libredb-studio/Chart.yaml +50 -0
- package/charts/libredb-studio/README.md +206 -0
- package/charts/libredb-studio/templates/NOTES.txt +59 -0
- package/charts/libredb-studio/templates/_helpers.tpl +135 -0
- package/charts/libredb-studio/templates/configmap.yaml +37 -0
- package/charts/libredb-studio/templates/deployment.yaml +184 -0
- package/charts/libredb-studio/templates/hpa.yaml +32 -0
- package/charts/libredb-studio/templates/ingress.yaml +41 -0
- package/charts/libredb-studio/templates/networkpolicy.yaml +50 -0
- package/charts/libredb-studio/templates/pdb.yaml +18 -0
- package/charts/libredb-studio/templates/pvc.yaml +23 -0
- package/charts/libredb-studio/templates/secret.yaml +30 -0
- package/charts/libredb-studio/templates/seed-configmap.yaml +11 -0
- package/charts/libredb-studio/templates/service.yaml +22 -0
- package/charts/libredb-studio/templates/serviceaccount.yaml +13 -0
- package/charts/libredb-studio/values.schema.json +246 -0
- package/charts/libredb-studio/values.yaml +286 -0
- package/components.json +22 -0
- package/conductor/code_styleguides/typescript.md +43 -0
- package/conductor/product-guidelines.md +43 -0
- package/conductor/product.md +3 -0
- package/conductor/setup_state.json +1 -0
- package/conductor/tech-stack.md +39 -0
- package/conductor/tracks/enhance_postgres_monitoring_20251227/metadata.json +8 -0
- package/conductor/tracks/enhance_postgres_monitoring_20251227/plan.md +44 -0
- package/conductor/tracks/enhance_postgres_monitoring_20251227/spec.md +31 -0
- package/conductor/tracks.md +8 -0
- package/conductor/workflow.md +333 -0
- package/database-compose.yml +55 -0
- package/docker/postgres-init/01-extensions.sql +10 -0
- package/docker/postgres-init/02-sample-data.sql +585 -0
- package/docker/postgres.yml +68 -0
- package/docker-compose.yml +38 -0
- package/docs/AI_PLAN.md +74 -0
- package/docs/API_DOCS.md +875 -0
- package/docs/ARCHITECTURE.md +218 -0
- package/docs/DATABASE_PROVIDERS.md +358 -0
- package/docs/FEATURES.md +116 -0
- package/docs/HELM_CHART.md +252 -0
- package/docs/LOGIN_PAGE.md +178 -0
- package/docs/MONACO_EDITOR_PERFORMANCE.md +315 -0
- package/docs/OIDC_ARCH.md +681 -0
- package/docs/OIDC_SETUP.md +322 -0
- package/docs/POSTGRES_METRICS.md +516 -0
- package/docs/QUERY_OPTIMIZATION.md +370 -0
- package/docs/SEED_CONNECTIONS.md +468 -0
- package/docs/SQL_ALIAS_COMPLETION.md +190 -0
- package/docs/STORAGE_ARCHITECTURE.md +565 -0
- package/docs/STORAGE_QUICK_SETUP.md +419 -0
- package/docs/TECHNICAL_PLAN.md +36 -0
- package/docs/THEMING.md +345 -0
- package/docs/adding-a-new-database-provider.md +642 -0
- package/docs/backlogs/000-PLATFORM_DATA_SYNC_DATABASE.md +360 -0
- package/docs/backlogs/001-INLINE_DATA_EDITING.md +118 -0
- package/docs/backlogs/002-DATA_IMPORT.md +215 -0
- package/docs/backlogs/003-QUERY_TIME_MACHINE.md +183 -0
- package/docs/backlogs/004-AI_DATA_STORYTELLER.md +292 -0
- package/docs/backlogs/005-QUERY_PLAYGROUND.md +352 -0
- package/docs/backlogs/006-DATA_MASKING.md +418 -0
- package/docs/enterprise-features.md +718 -0
- package/docs/kubernetes-helm-chart-artifacthub-plan.md +803 -0
- package/docs/medium-koyeb-article-en.md +215 -0
- package/docs/plans/test-plans.md +445 -0
- package/docs/releases/RELEASE.V0.3.0.md +22 -0
- package/docs/releases/RELEASE.V0.4.0.md +154 -0
- package/docs/releases/RELEASE.V0.5.0.md +252 -0
- package/docs/releases/RELEASE_v0.5.6.md +145 -0
- package/docs/releases/RELEASE_v0.6.1.md +303 -0
- package/docs/releases/RELEASE_v0.6.7.md +292 -0
- package/docs/releases/RELEASE_v0.7.0.md +332 -0
- package/docs/releases/RELEASE_v0.8.0.md +521 -0
- package/docs/sampledb/titanic.sql +1379 -0
- package/docs/superpowers/plans/2026-03-25-seed-connections.md +1362 -0
- package/docs/superpowers/specs/2026-03-25-seed-connections-design.md +590 -0
- package/e2e/admin-dashboard.spec.ts +64 -0
- package/e2e/connection-management.spec.ts +58 -0
- package/e2e/export.spec.ts +34 -0
- package/e2e/login.spec.ts +85 -0
- package/e2e/query-execution.spec.ts +35 -0
- package/e2e/tab-management.spec.ts +64 -0
- package/eslint.config.mjs +28 -0
- package/fly.toml +43 -0
- package/next.config.ts +32 -0
- package/package.json +130 -0
- package/playwright.config.ts +34 -0
- package/postcss.config.mjs +7 -0
- package/public/favicon-32x32.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/file.svg +1 -0
- package/public/globe.svg +1 -0
- package/public/logo.svg +32 -0
- package/public/next.svg +1 -0
- package/public/screenshots/code-generator.png +0 -0
- package/public/screenshots/connection-modal.png +0 -0
- package/public/screenshots/data-profiler.png +0 -0
- package/public/screenshots/erd-diagram.png +0 -0
- package/public/screenshots/hero-editor.png +0 -0
- package/public/screenshots/nl2sql.png +0 -0
- package/public/vercel.svg +1 -0
- package/public/window.svg +1 -0
- package/render.yaml +58 -0
- package/scripts/merge-lcov.mjs +239 -0
- package/sonar-project.properties +16 -0
- package/src/app/admin/error.tsx +46 -0
- package/src/app/admin/page.tsx +10 -0
- package/src/app/api/admin/audit/route.ts +52 -0
- package/src/app/api/admin/fleet-health/route.ts +81 -0
- package/src/app/api/ai/autopilot/route.ts +105 -0
- package/src/app/api/ai/chat/route.ts +132 -0
- package/src/app/api/ai/describe-schema/route.ts +52 -0
- package/src/app/api/ai/explain/route.ts +86 -0
- package/src/app/api/ai/impact/route.ts +97 -0
- package/src/app/api/ai/index-advisor/route.ts +98 -0
- package/src/app/api/ai/nl2sql/route.ts +87 -0
- package/src/app/api/ai/query-safety/route.ts +87 -0
- package/src/app/api/auth/login/route.ts +62 -0
- package/src/app/api/auth/logout/route.ts +25 -0
- package/src/app/api/auth/me/route.ts +10 -0
- package/src/app/api/auth/oidc/callback/route.ts +82 -0
- package/src/app/api/auth/oidc/login/route.ts +43 -0
- package/src/app/api/connections/managed/route.ts +35 -0
- package/src/app/api/db/cancel/route.ts +42 -0
- package/src/app/api/db/disconnect/route.ts +28 -0
- package/src/app/api/db/health/route.ts +49 -0
- package/src/app/api/db/maintenance/route.ts +72 -0
- package/src/app/api/db/monitoring/route.ts +62 -0
- package/src/app/api/db/multi-query/route.ts +116 -0
- package/src/app/api/db/pool-stats/route.ts +37 -0
- package/src/app/api/db/profile/route.ts +144 -0
- package/src/app/api/db/provider-meta/route.ts +49 -0
- package/src/app/api/db/query/route.ts +50 -0
- package/src/app/api/db/schema/route.ts +47 -0
- package/src/app/api/db/schema-snapshot/route.ts +42 -0
- package/src/app/api/db/test-connection/route.ts +55 -0
- package/src/app/api/db/transaction/route.ts +111 -0
- package/src/app/api/storage/[collection]/route.ts +67 -0
- package/src/app/api/storage/config/route.ts +17 -0
- package/src/app/api/storage/migrate/route.ts +45 -0
- package/src/app/api/storage/route.ts +32 -0
- package/src/app/error.tsx +49 -0
- package/src/app/global-error.tsx +55 -0
- package/src/app/globals.css +146 -0
- package/src/app/icon.svg +42 -0
- package/src/app/layout.tsx +34 -0
- package/src/app/login/login-form.tsx +301 -0
- package/src/app/login/page.tsx +11 -0
- package/src/app/monitoring/page.tsx +8 -0
- package/src/app/not-found.tsx +29 -0
- package/src/app/page.tsx +5 -0
- package/src/components/AIAutopilotPanel.tsx +238 -0
- package/src/components/CodeGenerator.tsx +271 -0
- package/src/components/CommandPalette.tsx +227 -0
- package/src/components/ConnectionModal.tsx +759 -0
- package/src/components/CreateTableModal.tsx +281 -0
- package/src/components/DataCharts.tsx +962 -0
- package/src/components/DataImportModal.tsx +582 -0
- package/src/components/DataProfiler.tsx +335 -0
- package/src/components/DatabaseDocs.tsx +251 -0
- package/src/components/MaskingSettings.tsx +414 -0
- package/src/components/MobileNav.tsx +50 -0
- package/src/components/NL2SQLPanel.tsx +281 -0
- package/src/components/PivotTable.tsx +257 -0
- package/src/components/QueryEditor.tsx +760 -0
- package/src/components/QueryHistory.tsx +344 -0
- package/src/components/QuerySafetyDialog.tsx +290 -0
- package/src/components/ResultsGrid.tsx +644 -0
- package/src/components/SaveQueryModal.tsx +104 -0
- package/src/components/SavedQueries.tsx +128 -0
- package/src/components/SchemaDiagram.tsx +473 -0
- package/src/components/SchemaDiff.tsx +473 -0
- package/src/components/SnapshotTimeline.tsx +116 -0
- package/src/components/Studio.tsx +639 -0
- package/src/components/TestDataGenerator.tsx +261 -0
- package/src/components/VisualExplain.tsx +820 -0
- package/src/components/admin/AdminDashboard.tsx +163 -0
- package/src/components/admin/tabs/AuditTab.tsx +531 -0
- package/src/components/admin/tabs/MonitoringEmbed.tsx +11 -0
- package/src/components/admin/tabs/OperationsTab.tsx +646 -0
- package/src/components/admin/tabs/OverviewTab.tsx +1328 -0
- package/src/components/admin/tabs/SecurityTab.tsx +284 -0
- package/src/components/community-section.tsx +92 -0
- package/src/components/icons/db-icons.tsx +84 -0
- package/src/components/libredb-logo.tsx +61 -0
- package/src/components/monitoring/MonitoringDashboard.tsx +345 -0
- package/src/components/monitoring/tabs/MetricChart.tsx +82 -0
- package/src/components/monitoring/tabs/OverviewTab.tsx +263 -0
- package/src/components/monitoring/tabs/PerformanceTab.tsx +254 -0
- package/src/components/monitoring/tabs/PoolTab.tsx +174 -0
- package/src/components/monitoring/tabs/QueriesTab.tsx +287 -0
- package/src/components/monitoring/tabs/SessionsTab.tsx +316 -0
- package/src/components/monitoring/tabs/StorageTab.tsx +335 -0
- package/src/components/monitoring/tabs/TablesTab.tsx +300 -0
- package/src/components/results-grid/ResultCard.tsx +111 -0
- package/src/components/results-grid/RowDetailSheet.tsx +178 -0
- package/src/components/results-grid/StatsBar.tsx +201 -0
- package/src/components/results-grid/index.ts +1 -0
- package/src/components/results-grid/utils.ts +23 -0
- package/src/components/schema-explorer/ColumnList.tsx +53 -0
- package/src/components/schema-explorer/SchemaExplorer.tsx +182 -0
- package/src/components/schema-explorer/TableItem.tsx +210 -0
- package/src/components/schema-explorer/index.ts +1 -0
- package/src/components/sidebar/ConnectionItem.tsx +105 -0
- package/src/components/sidebar/ConnectionsList.tsx +62 -0
- package/src/components/sidebar/Sidebar.tsx +130 -0
- package/src/components/sidebar/index.ts +2 -0
- package/src/components/studio/BottomPanel.tsx +286 -0
- package/src/components/studio/QueryToolbar.tsx +180 -0
- package/src/components/studio/StudioDesktopHeader.tsx +114 -0
- package/src/components/studio/StudioMobileHeader.tsx +340 -0
- package/src/components/studio/StudioTabBar.tsx +82 -0
- package/src/components/studio/index.ts +5 -0
- package/src/components/ui/accordion.tsx +66 -0
- package/src/components/ui/alert-dialog.tsx +157 -0
- package/src/components/ui/alert.tsx +66 -0
- package/src/components/ui/aspect-ratio.tsx +11 -0
- package/src/components/ui/avatar.tsx +53 -0
- package/src/components/ui/badge.tsx +46 -0
- package/src/components/ui/breadcrumb.tsx +109 -0
- package/src/components/ui/button-group.tsx +83 -0
- package/src/components/ui/button.tsx +60 -0
- package/src/components/ui/calendar.tsx +216 -0
- package/src/components/ui/card.tsx +92 -0
- package/src/components/ui/carousel.tsx +241 -0
- package/src/components/ui/chart.tsx +357 -0
- package/src/components/ui/checkbox.tsx +32 -0
- package/src/components/ui/collapsible.tsx +33 -0
- package/src/components/ui/command.tsx +184 -0
- package/src/components/ui/context-menu.tsx +252 -0
- package/src/components/ui/dialog.tsx +143 -0
- package/src/components/ui/drawer.tsx +135 -0
- package/src/components/ui/dropdown-menu.tsx +257 -0
- package/src/components/ui/empty.tsx +104 -0
- package/src/components/ui/field.tsx +248 -0
- package/src/components/ui/form.tsx +167 -0
- package/src/components/ui/hover-card.tsx +44 -0
- package/src/components/ui/input-group.tsx +170 -0
- package/src/components/ui/input-otp.tsx +77 -0
- package/src/components/ui/input.tsx +21 -0
- package/src/components/ui/item.tsx +193 -0
- package/src/components/ui/kbd.tsx +28 -0
- package/src/components/ui/label.tsx +24 -0
- package/src/components/ui/menubar.tsx +276 -0
- package/src/components/ui/navigation-menu.tsx +168 -0
- package/src/components/ui/pagination.tsx +127 -0
- package/src/components/ui/popover.tsx +48 -0
- package/src/components/ui/progress.tsx +31 -0
- package/src/components/ui/radio-group.tsx +45 -0
- package/src/components/ui/resizable.tsx +56 -0
- package/src/components/ui/scroll-area.tsx +58 -0
- package/src/components/ui/select.tsx +187 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/sheet.tsx +139 -0
- package/src/components/ui/sidebar.tsx +726 -0
- package/src/components/ui/skeleton.tsx +13 -0
- package/src/components/ui/slider.tsx +63 -0
- package/src/components/ui/sonner.tsx +40 -0
- package/src/components/ui/spinner.tsx +16 -0
- package/src/components/ui/switch.tsx +31 -0
- package/src/components/ui/table.tsx +116 -0
- package/src/components/ui/tabs.tsx +66 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/toggle-group.tsx +83 -0
- package/src/components/ui/toggle.tsx +47 -0
- package/src/components/ui/tooltip.tsx +61 -0
- package/src/exports/components.ts +15 -0
- package/src/exports/index.ts +4 -0
- package/src/exports/providers.ts +4 -0
- package/src/exports/types.ts +26 -0
- package/src/hooks/use-ai-chat.ts +182 -0
- package/src/hooks/use-all-connections.ts +66 -0
- package/src/hooks/use-api-call.ts +71 -0
- package/src/hooks/use-auth.ts +51 -0
- package/src/hooks/use-connection-form.ts +349 -0
- package/src/hooks/use-connection-manager.ts +169 -0
- package/src/hooks/use-connection-payload.ts +15 -0
- package/src/hooks/use-inline-editing.ts +109 -0
- package/src/hooks/use-mobile.ts +20 -0
- package/src/hooks/use-monitoring-data.ts +270 -0
- package/src/hooks/use-provider-metadata.ts +62 -0
- package/src/hooks/use-query-execution.ts +478 -0
- package/src/hooks/use-storage-sync.ts +259 -0
- package/src/hooks/use-tab-manager.ts +231 -0
- package/src/hooks/use-toast.ts +20 -0
- package/src/hooks/use-transaction-control.ts +64 -0
- package/src/lib/api/error-codes.ts +30 -0
- package/src/lib/api/errors.ts +236 -0
- package/src/lib/api/with-error-handler.ts +41 -0
- package/src/lib/audit.ts +105 -0
- package/src/lib/auth.ts +87 -0
- package/src/lib/connection-string-parser.ts +172 -0
- package/src/lib/data-masking.ts +385 -0
- package/src/lib/db/base-provider.ts +325 -0
- package/src/lib/db/errors.ts +317 -0
- package/src/lib/db/factory.ts +324 -0
- package/src/lib/db/index.ts +123 -0
- package/src/lib/db/providers/document/index.ts +6 -0
- package/src/lib/db/providers/document/mongodb.ts +992 -0
- package/src/lib/db/providers/keyvalue/redis.ts +554 -0
- package/src/lib/db/providers/sql/index.ts +11 -0
- package/src/lib/db/providers/sql/mssql.ts +1065 -0
- package/src/lib/db/providers/sql/mysql.ts +978 -0
- package/src/lib/db/providers/sql/oracle.ts +1044 -0
- package/src/lib/db/providers/sql/postgres.ts +1179 -0
- package/src/lib/db/providers/sql/sql-base.ts +174 -0
- package/src/lib/db/providers/sql/sqlite.ts +721 -0
- package/src/lib/db/types.ts +437 -0
- package/src/lib/db/utils/pool-manager.ts +287 -0
- package/src/lib/db/utils/query-limiter.ts +239 -0
- package/src/lib/db-ui-config.ts +86 -0
- package/src/lib/editor/mongodb-completions.ts +172 -0
- package/src/lib/editor/sql-completions.ts +280 -0
- package/src/lib/llm/base-provider.ts +117 -0
- package/src/lib/llm/factory.ts +102 -0
- package/src/lib/llm/index.ts +90 -0
- package/src/lib/llm/providers/custom.ts +181 -0
- package/src/lib/llm/providers/gemini.ts +126 -0
- package/src/lib/llm/providers/ollama.ts +154 -0
- package/src/lib/llm/providers/openai.ts +146 -0
- package/src/lib/llm/types.ts +173 -0
- package/src/lib/llm/utils/config.ts +187 -0
- package/src/lib/llm/utils/retry.ts +119 -0
- package/src/lib/llm/utils/streaming.ts +202 -0
- package/src/lib/logger.ts +127 -0
- package/src/lib/monitoring-thresholds.ts +44 -0
- package/src/lib/oidc.ts +262 -0
- package/src/lib/query-generators.ts +61 -0
- package/src/lib/schema-diff/diff-engine.ts +273 -0
- package/src/lib/schema-diff/migration-generator.ts +208 -0
- package/src/lib/schema-diff/types.ts +55 -0
- package/src/lib/seed/config-loader.ts +79 -0
- package/src/lib/seed/connection-filter.ts +49 -0
- package/src/lib/seed/credential-resolver.ts +62 -0
- package/src/lib/seed/index.ts +40 -0
- package/src/lib/seed/resolve-connection.ts +57 -0
- package/src/lib/seed/types.ts +69 -0
- package/src/lib/sql/alias-extractor.ts +267 -0
- package/src/lib/sql/index.ts +8 -0
- package/src/lib/sql/statement-splitter.ts +167 -0
- package/src/lib/sql/types.ts +40 -0
- package/src/lib/ssh/tunnel.ts +142 -0
- package/src/lib/storage/factory.ts +84 -0
- package/src/lib/storage/index.ts +14 -0
- package/src/lib/storage/local-storage.ts +99 -0
- package/src/lib/storage/providers/postgres.ts +225 -0
- package/src/lib/storage/providers/sqlite.ts +153 -0
- package/src/lib/storage/storage-facade.ts +272 -0
- package/src/lib/storage/types.ts +75 -0
- package/src/lib/time-series-buffer.ts +58 -0
- package/src/lib/types.ts +173 -0
- package/src/lib/utils.ts +6 -0
- package/src/proxy.ts +104 -0
- package/src/types/db-drivers.d.ts +23 -0
- package/src/types/html2canvas.d.ts +9 -0
- package/tests/api/admin/audit.test.ts +178 -0
- package/tests/api/admin/fleet-health.test.ts +183 -0
- package/tests/api/ai/autopilot.test.ts +174 -0
- package/tests/api/ai/chat.test.ts +250 -0
- package/tests/api/ai/describe-schema.test.ts +266 -0
- package/tests/api/ai/explain.test.ts +199 -0
- package/tests/api/ai/impact.test.ts +168 -0
- package/tests/api/ai/index-advisor.test.ts +171 -0
- package/tests/api/ai/nl2sql.test.ts +202 -0
- package/tests/api/ai/query-safety.test.ts +196 -0
- package/tests/api/auth/login.test.ts +170 -0
- package/tests/api/auth/logout.test.ts +140 -0
- package/tests/api/auth/me.test.ts +73 -0
- package/tests/api/auth/oidc-callback.test.ts +215 -0
- package/tests/api/auth/oidc-login.test.ts +127 -0
- package/tests/api/db/cancel.test.ts +198 -0
- package/tests/api/db/disconnect.test.ts +124 -0
- package/tests/api/db/health.test.ts +222 -0
- package/tests/api/db/maintenance.test.ts +263 -0
- package/tests/api/db/monitoring.test.ts +221 -0
- package/tests/api/db/multi-query.test.ts +316 -0
- package/tests/api/db/pool-stats.test.ts +135 -0
- package/tests/api/db/profile.test.ts +330 -0
- package/tests/api/db/provider-meta.test.ts +193 -0
- package/tests/api/db/query.test.ts +314 -0
- package/tests/api/db/schema-snapshot.test.ts +170 -0
- package/tests/api/db/schema.test.ts +191 -0
- package/tests/api/db/test-connection.test.ts +185 -0
- package/tests/api/db/transaction.test.ts +314 -0
- package/tests/api/proxy.test.ts +191 -0
- package/tests/api/seed/managed-route.test.ts +113 -0
- package/tests/api/storage/config.test.ts +42 -0
- package/tests/api/storage/storage-routes.test.ts +309 -0
- package/tests/components/AIAutopilotPanel.test.tsx +756 -0
- package/tests/components/AdminPage.test.tsx +33 -0
- package/tests/components/CodeGenerator.test.tsx +182 -0
- package/tests/components/CommandPalette.test.tsx +428 -0
- package/tests/components/CommunitySection.test.tsx +91 -0
- package/tests/components/ConnectionModal.mobile.test.tsx +284 -0
- package/tests/components/ConnectionModal.test.tsx +570 -0
- package/tests/components/CreateTableModal.test.tsx +383 -0
- package/tests/components/DataCharts.test.tsx +739 -0
- package/tests/components/DataImportModal.test.tsx +751 -0
- package/tests/components/DataProfiler.test.tsx +589 -0
- package/tests/components/DatabaseDocs.test.tsx +353 -0
- package/tests/components/LoginPage.test.tsx +163 -0
- package/tests/components/LoginPageOIDC.test.tsx +92 -0
- package/tests/components/MaskingSettings.test.tsx +498 -0
- package/tests/components/MobileNav.test.tsx +30 -0
- package/tests/components/MonitoringPage.test.tsx +32 -0
- package/tests/components/NL2SQLPanel.test.tsx +621 -0
- package/tests/components/Page.test.tsx +33 -0
- package/tests/components/PivotTable.test.tsx +350 -0
- package/tests/components/QueryEditor.test.tsx +1730 -0
- package/tests/components/QueryHistory.test.tsx +572 -0
- package/tests/components/QuerySafetyDialog.test.tsx +586 -0
- package/tests/components/ResultsGrid.test.tsx +804 -0
- package/tests/components/RootLayout.test.tsx +83 -0
- package/tests/components/SaveQueryModal.test.tsx +25 -0
- package/tests/components/SavedQueries.test.tsx +43 -0
- package/tests/components/SchemaDiagram.test.tsx +1034 -0
- package/tests/components/SchemaDiff.test.tsx +906 -0
- package/tests/components/SnapshotTimeline.test.tsx +174 -0
- package/tests/components/Studio.test.tsx +1030 -0
- package/tests/components/TestDataGenerator.test.tsx +291 -0
- package/tests/components/VisualExplain.test.tsx +704 -0
- package/tests/components/admin/AdminDashboard.test.tsx +205 -0
- package/tests/components/admin/AuditTab.test.tsx +220 -0
- package/tests/components/admin/MonitoringEmbed.test.tsx +58 -0
- package/tests/components/admin/OperationsTab.test.tsx +975 -0
- package/tests/components/admin/OverviewTab.test.tsx +254 -0
- package/tests/components/admin/SecurityTab.test.tsx +467 -0
- package/tests/components/monitoring/MetricChart.test.tsx +111 -0
- package/tests/components/monitoring/MonitoringDashboard.test.tsx +259 -0
- package/tests/components/monitoring/OverviewTab.test.tsx +78 -0
- package/tests/components/monitoring/PerformanceTab.test.tsx +87 -0
- package/tests/components/monitoring/PoolTab.test.tsx +42 -0
- package/tests/components/monitoring/QueriesTab.test.tsx +80 -0
- package/tests/components/monitoring/SessionsTab.test.tsx +154 -0
- package/tests/components/monitoring/StorageTab.test.tsx +127 -0
- package/tests/components/monitoring/TablesTab.test.tsx +153 -0
- package/tests/components/results-grid/ResultCard.test.tsx +105 -0
- package/tests/components/results-grid/RowDetailSheet.test.tsx +308 -0
- package/tests/components/results-grid/StatsBar.test.tsx +162 -0
- package/tests/components/schema-explorer/ColumnList.test.tsx +151 -0
- package/tests/components/schema-explorer/SchemaExplorer.test.tsx +461 -0
- package/tests/components/schema-explorer/TableItem.test.tsx +415 -0
- package/tests/components/sidebar/ConnectionItem.test.tsx +201 -0
- package/tests/components/sidebar/ConnectionsList.test.tsx +176 -0
- package/tests/components/sidebar/Sidebar.test.tsx +187 -0
- package/tests/components/studio/BottomPanel.test.tsx +383 -0
- package/tests/components/studio/QueryToolbar.test.tsx +321 -0
- package/tests/components/studio/StudioDesktopHeader.test.tsx +377 -0
- package/tests/components/studio/StudioMobileHeader.test.tsx +198 -0
- package/tests/components/studio/StudioTabBar.test.tsx +331 -0
- package/tests/fixtures/connections.ts +96 -0
- package/tests/fixtures/masking-configs.ts +86 -0
- package/tests/fixtures/query-results.ts +71 -0
- package/tests/fixtures/schemas.ts +64 -0
- package/tests/fixtures/seed-connections/invalid-config.yaml +7 -0
- package/tests/fixtures/seed-connections/minimal-config.yaml +8 -0
- package/tests/fixtures/seed-connections/mixed-credentials.yaml +23 -0
- package/tests/fixtures/seed-connections/multi-role-config.yaml +30 -0
- package/tests/fixtures/seed-connections/valid-config.json +15 -0
- package/tests/fixtures/seed-connections/valid-config.yaml +51 -0
- package/tests/helpers/mock-fetch.ts +59 -0
- package/tests/helpers/mock-monaco.ts +112 -0
- package/tests/helpers/mock-navigation.ts +28 -0
- package/tests/helpers/mock-next.ts +80 -0
- package/tests/helpers/mock-provider.ts +133 -0
- package/tests/helpers/mock-sonner.ts +29 -0
- package/tests/helpers/render-with-providers.tsx +19 -0
- package/tests/hooks/use-ai-chat.test.ts +600 -0
- package/tests/hooks/use-auth.test.ts +371 -0
- package/tests/hooks/use-connection-form.test.ts +743 -0
- package/tests/hooks/use-connection-manager.test.ts +466 -0
- package/tests/hooks/use-inline-editing.test.ts +321 -0
- package/tests/hooks/use-mobile.test.ts +177 -0
- package/tests/hooks/use-monitoring-data.test.ts +819 -0
- package/tests/hooks/use-provider-metadata.test.ts +228 -0
- package/tests/hooks/use-query-execution.test.ts +1212 -0
- package/tests/hooks/use-tab-manager.test.ts +756 -0
- package/tests/hooks/use-toast.test.ts +74 -0
- package/tests/hooks/use-transaction-control.test.ts +211 -0
- package/tests/integration/db/mongodb-provider.test.ts +698 -0
- package/tests/integration/db/mssql-provider.test.ts +840 -0
- package/tests/integration/db/mysql-provider.test.ts +872 -0
- package/tests/integration/db/oracle-provider.test.ts +843 -0
- package/tests/integration/db/postgres-provider.test.ts +1382 -0
- package/tests/integration/db/redis-provider.test.ts +526 -0
- package/tests/integration/db/sqlite-provider.test.ts +480 -0
- package/tests/integration/seed/seed-pipeline.test.ts +102 -0
- package/tests/isolated/factory-singleton.test.ts +150 -0
- package/tests/isolated/use-storage-sync.test.ts +389 -0
- package/tests/run-components.sh +196 -0
- package/tests/setup-dom.ts +58 -0
- package/tests/setup.ts +40 -0
- package/tests/unit/api-errors.test.ts +210 -0
- package/tests/unit/code-generator-functions.test.ts +271 -0
- package/tests/unit/components/column-list.test.tsx +190 -0
- package/tests/unit/components/data-import-modal.test.tsx +441 -0
- package/tests/unit/components/studio-mobile-header.test.tsx +327 -0
- package/tests/unit/data-charts-functions.test.ts +496 -0
- package/tests/unit/data-import-functions.test.ts +320 -0
- package/tests/unit/data-import-utils.test.ts +125 -0
- package/tests/unit/db/base-provider.test.ts +517 -0
- package/tests/unit/db/errors.test.ts +403 -0
- package/tests/unit/db/factory.test.ts +436 -0
- package/tests/unit/db/pool-manager.test.ts +440 -0
- package/tests/unit/db/query-limiter.test.ts +387 -0
- package/tests/unit/db/sql-base.test.ts +438 -0
- package/tests/unit/lib/api/error-codes.test.ts +39 -0
- package/tests/unit/lib/audit.test.ts +326 -0
- package/tests/unit/lib/auth.test.ts +146 -0
- package/tests/unit/lib/connection-string-parser.test.ts +424 -0
- package/tests/unit/lib/data-masking.test.ts +583 -0
- package/tests/unit/lib/db-icons.test.tsx +41 -0
- package/tests/unit/lib/monitoring-thresholds.test.ts +133 -0
- package/tests/unit/lib/oidc.test.ts +509 -0
- package/tests/unit/lib/query-generators.test.ts +127 -0
- package/tests/unit/lib/storage/factory.test.ts +71 -0
- package/tests/unit/lib/storage/local-storage.test.ts +114 -0
- package/tests/unit/lib/storage/providers/postgres.test.ts +312 -0
- package/tests/unit/lib/storage/providers/sqlite.test.ts +232 -0
- package/tests/unit/lib/storage/storage-facade-extended.test.ts +331 -0
- package/tests/unit/lib/storage/storage-facade.test.ts +184 -0
- package/tests/unit/lib/storage.test.ts +317 -0
- package/tests/unit/lib/time-series-buffer.test.ts +212 -0
- package/tests/unit/lib/utils.test.ts +24 -0
- package/tests/unit/llm/base-provider.test.ts +238 -0
- package/tests/unit/llm/config.test.ts +262 -0
- package/tests/unit/llm/custom-provider.test.ts +281 -0
- package/tests/unit/llm/gemini-provider.test.ts +248 -0
- package/tests/unit/llm/llm-factory.test.ts +155 -0
- package/tests/unit/llm/ollama-provider.test.ts +288 -0
- package/tests/unit/llm/openai-provider.test.ts +324 -0
- package/tests/unit/llm/retry.test.ts +180 -0
- package/tests/unit/llm/streaming.test.ts +355 -0
- package/tests/unit/logger.test.ts +198 -0
- package/tests/unit/mongodb-completions.test.ts +516 -0
- package/tests/unit/pivot-table-functions.test.ts +76 -0
- package/tests/unit/query-cancelled-error.test.ts +81 -0
- package/tests/unit/schema-diff/diff-engine.test.ts +367 -0
- package/tests/unit/schema-diff/migration-generator.test.ts +513 -0
- package/tests/unit/seed/config-loader.test.ts +73 -0
- package/tests/unit/seed/connection-filter.test.ts +91 -0
- package/tests/unit/seed/credential-resolver.test.ts +85 -0
- package/tests/unit/seed/index.test.ts +72 -0
- package/tests/unit/seed/resolve-connection.test.ts +74 -0
- package/tests/unit/seed/types.test.ts +129 -0
- package/tests/unit/sql/alias-extractor.test.ts +444 -0
- package/tests/unit/sql/statement-splitter.test.ts +348 -0
- package/tests/unit/sql-completions.test.ts +463 -0
- package/tests/unit/ssh-tunnel.test.ts +465 -0
- package/tsconfig.json +42 -0
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
import { describe, test, expect } from 'bun:test';
|
|
2
|
+
import {
|
|
3
|
+
DatabaseError,
|
|
4
|
+
DatabaseConfigError,
|
|
5
|
+
ConnectionError,
|
|
6
|
+
AuthenticationError,
|
|
7
|
+
PoolExhaustedError,
|
|
8
|
+
QueryError,
|
|
9
|
+
TimeoutError,
|
|
10
|
+
QueryCancelledError,
|
|
11
|
+
isDatabaseError,
|
|
12
|
+
isConnectionError,
|
|
13
|
+
isQueryError,
|
|
14
|
+
isTimeoutError,
|
|
15
|
+
isAuthenticationError,
|
|
16
|
+
isRetryableError,
|
|
17
|
+
mapDatabaseError,
|
|
18
|
+
} from '@/lib/db/errors';
|
|
19
|
+
import { ApiErrorCode } from '@/lib/api/error-codes';
|
|
20
|
+
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// Error Classes
|
|
23
|
+
// ============================================================================
|
|
24
|
+
|
|
25
|
+
describe('DatabaseError', () => {
|
|
26
|
+
test('sets name to "DatabaseError"', () => {
|
|
27
|
+
const err = new DatabaseError('test message');
|
|
28
|
+
expect(err.name).toBe('DatabaseError');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('stores message, provider, code, and query', () => {
|
|
32
|
+
const err = new DatabaseError('msg', 'postgres', ApiErrorCode.DATABASE_ERROR, 'SELECT 1');
|
|
33
|
+
expect(err.message).toBe('msg');
|
|
34
|
+
expect(err.provider).toBe('postgres');
|
|
35
|
+
expect(err.code).toBe(ApiErrorCode.DATABASE_ERROR);
|
|
36
|
+
expect(err.query).toBe('SELECT 1');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('is instanceof Error', () => {
|
|
40
|
+
const err = new DatabaseError('test');
|
|
41
|
+
expect(err).toBeInstanceOf(Error);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('toJSON() returns structured object', () => {
|
|
45
|
+
const err = new DatabaseError('test', 'mysql', ApiErrorCode.QUERY_ERROR, 'SELECT 1');
|
|
46
|
+
const json = err.toJSON();
|
|
47
|
+
expect(json).toEqual({
|
|
48
|
+
name: 'DatabaseError',
|
|
49
|
+
message: 'test',
|
|
50
|
+
provider: 'mysql',
|
|
51
|
+
code: ApiErrorCode.QUERY_ERROR,
|
|
52
|
+
query: 'SELECT 1...',
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('toJSON() truncates query to 100 chars', () => {
|
|
57
|
+
const longQuery = 'A'.repeat(200);
|
|
58
|
+
const err = new DatabaseError('msg', 'postgres', undefined, longQuery);
|
|
59
|
+
const json = err.toJSON();
|
|
60
|
+
expect((json.query as string).length).toBe(103); // substring(0,100) + '...'
|
|
61
|
+
expect((json.query as string).endsWith('...')).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('toJSON() returns undefined query when no query provided', () => {
|
|
65
|
+
const err = new DatabaseError('msg');
|
|
66
|
+
const json = err.toJSON();
|
|
67
|
+
expect(json.query).toBeUndefined();
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe('DatabaseConfigError', () => {
|
|
72
|
+
test('sets name to "DatabaseConfigError"', () => {
|
|
73
|
+
const err = new DatabaseConfigError('bad config');
|
|
74
|
+
expect(err.name).toBe('DatabaseConfigError');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('sets code to CONFIG_ERROR', () => {
|
|
78
|
+
const err = new DatabaseConfigError('bad config', 'postgres');
|
|
79
|
+
expect(err.code).toBe('CONFIG_ERROR');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('is instanceof DatabaseError and Error', () => {
|
|
83
|
+
const err = new DatabaseConfigError('bad config');
|
|
84
|
+
expect(err).toBeInstanceOf(DatabaseError);
|
|
85
|
+
expect(err).toBeInstanceOf(Error);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('ConnectionError', () => {
|
|
90
|
+
test('sets name to "ConnectionError"', () => {
|
|
91
|
+
const err = new ConnectionError('conn fail');
|
|
92
|
+
expect(err.name).toBe('ConnectionError');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('stores host and port', () => {
|
|
96
|
+
const err = new ConnectionError('fail', 'postgres', 'localhost', 5432);
|
|
97
|
+
expect(err.host).toBe('localhost');
|
|
98
|
+
expect(err.port).toBe(5432);
|
|
99
|
+
expect(err.code).toBe('CONNECTION_ERROR');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('is instanceof DatabaseError', () => {
|
|
103
|
+
const err = new ConnectionError('fail');
|
|
104
|
+
expect(err).toBeInstanceOf(DatabaseError);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe('AuthenticationError', () => {
|
|
109
|
+
test('sets name to "AuthenticationError"', () => {
|
|
110
|
+
const err = new AuthenticationError('auth fail');
|
|
111
|
+
expect(err.name).toBe('AuthenticationError');
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test('sets code to AUTH_ERROR', () => {
|
|
115
|
+
const err = new AuthenticationError('auth fail', 'mysql');
|
|
116
|
+
expect(err.code).toBe('AUTH_ERROR');
|
|
117
|
+
expect(err.provider).toBe('mysql');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test('is instanceof DatabaseError', () => {
|
|
121
|
+
const err = new AuthenticationError('fail');
|
|
122
|
+
expect(err).toBeInstanceOf(DatabaseError);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
describe('PoolExhaustedError', () => {
|
|
127
|
+
test('sets name to "PoolExhaustedError"', () => {
|
|
128
|
+
const err = new PoolExhaustedError('pool full');
|
|
129
|
+
expect(err.name).toBe('PoolExhaustedError');
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test('stores poolSize', () => {
|
|
133
|
+
const err = new PoolExhaustedError('pool full', 'postgres', 10);
|
|
134
|
+
expect(err.poolSize).toBe(10);
|
|
135
|
+
expect(err.code).toBe('POOL_EXHAUSTED');
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test('is instanceof DatabaseError', () => {
|
|
139
|
+
const err = new PoolExhaustedError('fail');
|
|
140
|
+
expect(err).toBeInstanceOf(DatabaseError);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
describe('QueryError', () => {
|
|
145
|
+
test('sets name to "QueryError"', () => {
|
|
146
|
+
const err = new QueryError('syntax error');
|
|
147
|
+
expect(err.name).toBe('QueryError');
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test('stores position and detail', () => {
|
|
151
|
+
const err = new QueryError('fail', 'postgres', 'SELECT * FORM', 12, 'near FORM');
|
|
152
|
+
expect(err.position).toBe(12);
|
|
153
|
+
expect(err.detail).toBe('near FORM');
|
|
154
|
+
expect(err.query).toBe('SELECT * FORM');
|
|
155
|
+
expect(err.code).toBe('QUERY_ERROR');
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test('is instanceof DatabaseError', () => {
|
|
159
|
+
const err = new QueryError('fail');
|
|
160
|
+
expect(err).toBeInstanceOf(DatabaseError);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
describe('TimeoutError', () => {
|
|
165
|
+
test('sets name to "TimeoutError"', () => {
|
|
166
|
+
const err = new TimeoutError('timed out');
|
|
167
|
+
expect(err.name).toBe('TimeoutError');
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test('stores timeout and query', () => {
|
|
171
|
+
const err = new TimeoutError('timed out', 'postgres', 30000, 'SELECT pg_sleep(60)');
|
|
172
|
+
expect(err.timeout).toBe(30000);
|
|
173
|
+
expect(err.query).toBe('SELECT pg_sleep(60)');
|
|
174
|
+
expect(err.code).toBe('TIMEOUT_ERROR');
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test('is instanceof DatabaseError', () => {
|
|
178
|
+
const err = new TimeoutError('fail');
|
|
179
|
+
expect(err).toBeInstanceOf(DatabaseError);
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// ============================================================================
|
|
184
|
+
// Type Guards
|
|
185
|
+
// ============================================================================
|
|
186
|
+
|
|
187
|
+
describe('isDatabaseError', () => {
|
|
188
|
+
test('returns true for DatabaseError instances', () => {
|
|
189
|
+
expect(isDatabaseError(new DatabaseError('err'))).toBe(true);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test('returns true for subclass instances', () => {
|
|
193
|
+
expect(isDatabaseError(new ConnectionError('err'))).toBe(true);
|
|
194
|
+
expect(isDatabaseError(new QueryError('err'))).toBe(true);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test('returns false for plain Error', () => {
|
|
198
|
+
expect(isDatabaseError(new Error('plain'))).toBe(false);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test('returns false for non-error values', () => {
|
|
202
|
+
expect(isDatabaseError('string')).toBe(false);
|
|
203
|
+
expect(isDatabaseError(null)).toBe(false);
|
|
204
|
+
expect(isDatabaseError(undefined)).toBe(false);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
describe('isConnectionError', () => {
|
|
209
|
+
test('returns true for ConnectionError', () => {
|
|
210
|
+
expect(isConnectionError(new ConnectionError('err'))).toBe(true);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test('returns false for other DatabaseError subclasses', () => {
|
|
214
|
+
expect(isConnectionError(new QueryError('err'))).toBe(false);
|
|
215
|
+
expect(isConnectionError(new DatabaseError('err'))).toBe(false);
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
describe('isQueryError', () => {
|
|
220
|
+
test('returns true for QueryError', () => {
|
|
221
|
+
expect(isQueryError(new QueryError('err'))).toBe(true);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
test('returns false for non-QueryError', () => {
|
|
225
|
+
expect(isQueryError(new ConnectionError('err'))).toBe(false);
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
describe('isTimeoutError', () => {
|
|
230
|
+
test('returns true for TimeoutError', () => {
|
|
231
|
+
expect(isTimeoutError(new TimeoutError('err'))).toBe(true);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
test('returns false for non-TimeoutError', () => {
|
|
235
|
+
expect(isTimeoutError(new ConnectionError('err'))).toBe(false);
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
describe('isAuthenticationError', () => {
|
|
240
|
+
test('returns true for AuthenticationError', () => {
|
|
241
|
+
expect(isAuthenticationError(new AuthenticationError('err'))).toBe(true);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test('returns false for non-AuthenticationError', () => {
|
|
245
|
+
expect(isAuthenticationError(new DatabaseError('err'))).toBe(false);
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// ============================================================================
|
|
250
|
+
// isRetryableError
|
|
251
|
+
// ============================================================================
|
|
252
|
+
|
|
253
|
+
describe('isRetryableError', () => {
|
|
254
|
+
test('ConnectionError is retryable', () => {
|
|
255
|
+
expect(isRetryableError(new ConnectionError('fail'))).toBe(true);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
test('TimeoutError is retryable', () => {
|
|
259
|
+
expect(isRetryableError(new TimeoutError('timed out'))).toBe(true);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
test('AuthenticationError is NOT retryable', () => {
|
|
263
|
+
expect(isRetryableError(new AuthenticationError('bad creds'))).toBe(false);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
test('DatabaseConfigError is NOT retryable', () => {
|
|
267
|
+
expect(isRetryableError(new DatabaseConfigError('bad config'))).toBe(false);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
test('QueryError with position is NOT retryable', () => {
|
|
271
|
+
expect(isRetryableError(new QueryError('syntax', 'postgres', 'q', 5))).toBe(false);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
test('plain Error is NOT retryable', () => {
|
|
275
|
+
expect(isRetryableError(new Error('generic'))).toBe(false);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
test('TypeError with "fetch" IS retryable (network error)', () => {
|
|
279
|
+
expect(isRetryableError(new TypeError('fetch failed'))).toBe(true);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
test('PoolExhaustedError is retryable (inherits from DatabaseError)', () => {
|
|
283
|
+
expect(isRetryableError(new PoolExhaustedError('pool'))).toBe(true);
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
// ============================================================================
|
|
288
|
+
// mapDatabaseError
|
|
289
|
+
// ============================================================================
|
|
290
|
+
|
|
291
|
+
describe('mapDatabaseError', () => {
|
|
292
|
+
test('returns existing DatabaseError as-is', () => {
|
|
293
|
+
const original = new ConnectionError('already mapped');
|
|
294
|
+
const result = mapDatabaseError(original, 'postgres');
|
|
295
|
+
expect(result).toBe(original);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
test('non-Error input is converted to DatabaseError with String()', () => {
|
|
299
|
+
const result = mapDatabaseError('raw string error', 'postgres');
|
|
300
|
+
expect(result).toBeInstanceOf(DatabaseError);
|
|
301
|
+
expect(result.message).toBe('raw string error');
|
|
302
|
+
expect(result.provider).toBe('postgres');
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
test('ECONNREFUSED maps to ConnectionError', () => {
|
|
306
|
+
const err = new Error('connect ECONNREFUSED 127.0.0.1:5432');
|
|
307
|
+
const result = mapDatabaseError(err, 'postgres');
|
|
308
|
+
expect(result).toBeInstanceOf(ConnectionError);
|
|
309
|
+
expect(result.message).toContain('Failed to connect');
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
test('"password" in message maps to AuthenticationError', () => {
|
|
313
|
+
const err = new Error('password authentication failed for user "test"');
|
|
314
|
+
const result = mapDatabaseError(err, 'postgres');
|
|
315
|
+
expect(result).toBeInstanceOf(AuthenticationError);
|
|
316
|
+
expect(result.message).toContain('Authentication failed');
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
test('"timeout" in message maps to TimeoutError', () => {
|
|
320
|
+
const err = new Error('query timeout exceeded');
|
|
321
|
+
const result = mapDatabaseError(err, 'mysql', 'SELECT SLEEP(100)');
|
|
322
|
+
const result2 = result as TimeoutError;
|
|
323
|
+
expect(result2).toBeInstanceOf(TimeoutError);
|
|
324
|
+
expect(result2.query).toBe('SELECT SLEEP(100)');
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
test('Oracle ORA-01017 maps to AuthenticationError', () => {
|
|
328
|
+
const err = new Error('ORA-01017: invalid username/password; logon denied');
|
|
329
|
+
const result = mapDatabaseError(err, 'oracle');
|
|
330
|
+
expect(result).toBeInstanceOf(AuthenticationError);
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
test('Oracle ORA-12541 maps to ConnectionError', () => {
|
|
334
|
+
const err = new Error('ORA-12541: TNS:no listener');
|
|
335
|
+
const result = mapDatabaseError(err, 'oracle');
|
|
336
|
+
expect(result).toBeInstanceOf(ConnectionError);
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
test('Oracle ORA-12154 maps to ConnectionError', () => {
|
|
340
|
+
const err = new Error('ORA-12154: TNS:could not resolve the connect identifier');
|
|
341
|
+
const result = mapDatabaseError(err, 'oracle');
|
|
342
|
+
expect(result).toBeInstanceOf(ConnectionError);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
test('Oracle ORA-00942 maps to QueryError', () => {
|
|
346
|
+
const err = new Error('ORA-00942: table or view does not exist');
|
|
347
|
+
const query = 'SELECT * FROM nonexistent';
|
|
348
|
+
const result = mapDatabaseError(err, 'oracle', query);
|
|
349
|
+
expect(result).toBeInstanceOf(QueryError);
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
test('MSSQL "login failed" maps to AuthenticationError', () => {
|
|
353
|
+
const err = new Error('Login failed for user "sa"');
|
|
354
|
+
const result = mapDatabaseError(err, 'mssql');
|
|
355
|
+
expect(result).toBeInstanceOf(AuthenticationError);
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
test('MSSQL "cannot open database" maps to ConnectionError', () => {
|
|
359
|
+
const err = new Error('Cannot open database "testdb" requested by the login');
|
|
360
|
+
const result = mapDatabaseError(err, 'mssql');
|
|
361
|
+
expect(result).toBeInstanceOf(ConnectionError);
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
test('"syntax error" maps to QueryError', () => {
|
|
365
|
+
const err = new Error('syntax error at or near "FORM"');
|
|
366
|
+
const result = mapDatabaseError(err, 'postgres', 'SELECT * FORM users');
|
|
367
|
+
expect(result).toBeInstanceOf(QueryError);
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
test('"pool" in message maps to PoolExhaustedError', () => {
|
|
371
|
+
const err = new Error('pool is full, cannot allocate connection');
|
|
372
|
+
const result = mapDatabaseError(err, 'postgres');
|
|
373
|
+
expect(result).toBeInstanceOf(PoolExhaustedError);
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
test('generic error maps to base DatabaseError', () => {
|
|
377
|
+
const err = new Error('something unexpected happened');
|
|
378
|
+
const result = mapDatabaseError(err, 'sqlite', 'SELECT 1');
|
|
379
|
+
expect(result).toBeInstanceOf(DatabaseError);
|
|
380
|
+
expect(result.message).toBe('something unexpected happened');
|
|
381
|
+
expect(result.provider).toBe('sqlite');
|
|
382
|
+
expect(result.query).toBe('SELECT 1');
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
test('"connection refused" maps to ConnectionError', () => {
|
|
386
|
+
const err = new Error('connection refused to host');
|
|
387
|
+
const result = mapDatabaseError(err, 'mysql');
|
|
388
|
+
expect(result).toBeInstanceOf(ConnectionError);
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
test('"access denied" maps to AuthenticationError', () => {
|
|
392
|
+
const err = new Error('Access denied for user');
|
|
393
|
+
const result = mapDatabaseError(err, 'mysql');
|
|
394
|
+
expect(result).toBeInstanceOf(AuthenticationError);
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
test('returns QueryCancelledError for "kill query" message', () => {
|
|
398
|
+
const error = new Error('kill query 12345');
|
|
399
|
+
const result = mapDatabaseError(error, 'mysql');
|
|
400
|
+
expect(result).toBeInstanceOf(QueryCancelledError);
|
|
401
|
+
expect(result.message).toBe('Query was cancelled');
|
|
402
|
+
});
|
|
403
|
+
});
|