@libredb/studio 0.9.7 → 0.9.13

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.
Files changed (688) hide show
  1. package/dist/chunk-34YQUUCM.mjs +319 -0
  2. package/dist/chunk-34YQUUCM.mjs.map +1 -0
  3. package/dist/chunk-4LVB3K53.mjs +37 -0
  4. package/dist/chunk-4LVB3K53.mjs.map +1 -0
  5. package/dist/chunk-6DRZXXNT.mjs +100 -0
  6. package/dist/chunk-6DRZXXNT.mjs.map +1 -0
  7. package/dist/chunk-CPF7XWV5.mjs +1289 -0
  8. package/dist/chunk-CPF7XWV5.mjs.map +1 -0
  9. package/dist/chunk-CZVV3JJB.mjs +160 -0
  10. package/dist/chunk-CZVV3JJB.mjs.map +1 -0
  11. package/dist/chunk-D4WVWWWF.js +332 -0
  12. package/dist/chunk-D4WVWWWF.js.map +1 -0
  13. package/dist/chunk-DY3KXE44.mjs +3 -0
  14. package/dist/chunk-DY3KXE44.mjs.map +1 -0
  15. package/dist/chunk-FYSE52VB.js +242 -0
  16. package/dist/chunk-FYSE52VB.js.map +1 -0
  17. package/dist/chunk-G3S66G64.mjs +6673 -0
  18. package/dist/chunk-G3S66G64.mjs.map +1 -0
  19. package/dist/chunk-G4WYE6TI.js +4 -0
  20. package/dist/chunk-G4WYE6TI.js.map +1 -0
  21. package/dist/chunk-HGPD6PWV.js +1310 -0
  22. package/dist/chunk-HGPD6PWV.js.map +1 -0
  23. package/dist/chunk-JZO5KRZN.js +165 -0
  24. package/dist/chunk-JZO5KRZN.js.map +1 -0
  25. package/dist/chunk-KV356UXJ.js +253 -0
  26. package/dist/chunk-KV356UXJ.js.map +1 -0
  27. package/dist/chunk-PPODO6HX.mjs +237 -0
  28. package/dist/chunk-PPODO6HX.mjs.map +1 -0
  29. package/dist/chunk-PTIRB2JO.js +258 -0
  30. package/dist/chunk-PTIRB2JO.js.map +1 -0
  31. package/dist/chunk-Q6LRDBK7.js +42 -0
  32. package/dist/chunk-Q6LRDBK7.js.map +1 -0
  33. package/dist/chunk-QJP5FZRY.mjs +255 -0
  34. package/dist/chunk-QJP5FZRY.mjs.map +1 -0
  35. package/dist/chunk-R3POCJK6.mjs +248 -0
  36. package/dist/chunk-R3POCJK6.mjs.map +1 -0
  37. package/dist/chunk-RCQB4FCE.js +186 -0
  38. package/dist/chunk-RCQB4FCE.js.map +1 -0
  39. package/dist/chunk-SR5DRGBX.mjs +174 -0
  40. package/dist/chunk-SR5DRGBX.mjs.map +1 -0
  41. package/dist/chunk-VLCRUZX7.js +102 -0
  42. package/dist/chunk-VLCRUZX7.js.map +1 -0
  43. package/dist/chunk-Y52UIFEX.js +6741 -0
  44. package/dist/chunk-Y52UIFEX.js.map +1 -0
  45. package/dist/components.d.mts +273 -0
  46. package/dist/components.d.ts +273 -0
  47. package/dist/components.js +59 -0
  48. package/dist/components.js.map +1 -0
  49. package/dist/components.mjs +6 -0
  50. package/dist/components.mjs.map +1 -0
  51. package/dist/custom-BNDOYC5P.js +134 -0
  52. package/dist/custom-BNDOYC5P.js.map +1 -0
  53. package/dist/custom-S2EKFMP3.mjs +132 -0
  54. package/dist/custom-S2EKFMP3.mjs.map +1 -0
  55. package/dist/gemini-4ASHNK4H.js +81 -0
  56. package/dist/gemini-4ASHNK4H.js.map +1 -0
  57. package/dist/gemini-C5RBLQEJ.mjs +79 -0
  58. package/dist/gemini-C5RBLQEJ.mjs.map +1 -0
  59. package/dist/index.d.mts +6 -0
  60. package/dist/index.d.ts +6 -0
  61. package/dist/index.js +95 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/index.mjs +10 -0
  64. package/dist/index.mjs.map +1 -0
  65. package/dist/mongodb-XMZEZA4A.mjs +748 -0
  66. package/dist/mongodb-XMZEZA4A.mjs.map +1 -0
  67. package/dist/mongodb-YQJJTLX3.js +750 -0
  68. package/dist/mongodb-YQJJTLX3.js.map +1 -0
  69. package/dist/mssql-PMOU4D36.js +916 -0
  70. package/dist/mssql-PMOU4D36.js.map +1 -0
  71. package/{src/lib/db/providers/sql/mssql.ts → dist/mssql-ZH5VP2C5.mjs} +268 -423
  72. package/dist/mssql-ZH5VP2C5.mjs.map +1 -0
  73. package/{src/lib/db/providers/sql/mysql.ts → dist/mysql-I3WJQXN2.mjs} +277 -428
  74. package/dist/mysql-I3WJQXN2.mjs.map +1 -0
  75. package/dist/mysql-Y3MSA5QY.js +833 -0
  76. package/dist/mysql-Y3MSA5QY.js.map +1 -0
  77. package/dist/ollama-26BYLVEV.mjs +115 -0
  78. package/dist/ollama-26BYLVEV.mjs.map +1 -0
  79. package/dist/ollama-HVWAGKQC.js +117 -0
  80. package/dist/ollama-HVWAGKQC.js.map +1 -0
  81. package/dist/openai-4U56KPG7.mjs +111 -0
  82. package/dist/openai-4U56KPG7.mjs.map +1 -0
  83. package/dist/openai-AK3R37BS.js +113 -0
  84. package/dist/openai-AK3R37BS.js.map +1 -0
  85. package/dist/oracle-L6VEAVXO.js +917 -0
  86. package/dist/oracle-L6VEAVXO.js.map +1 -0
  87. package/{src/lib/db/providers/sql/oracle.ts → dist/oracle-P2G7T4P4.mjs} +321 -454
  88. package/dist/oracle-P2G7T4P4.mjs.map +1 -0
  89. package/{src/lib/db/providers/sql/postgres.ts → dist/postgres-O5KOQUVP.mjs} +261 -471
  90. package/dist/postgres-O5KOQUVP.mjs.map +1 -0
  91. package/dist/postgres-RLCWNFFX.js +971 -0
  92. package/dist/postgres-RLCWNFFX.js.map +1 -0
  93. package/dist/providers.d.mts +149 -0
  94. package/dist/providers.d.ts +149 -0
  95. package/dist/providers.js +44 -0
  96. package/dist/providers.js.map +1 -0
  97. package/dist/providers.mjs +7 -0
  98. package/dist/providers.mjs.map +1 -0
  99. package/dist/redis-4WMQOVLX.mjs +435 -0
  100. package/dist/redis-4WMQOVLX.mjs.map +1 -0
  101. package/dist/redis-QVQ6YU62.js +441 -0
  102. package/dist/redis-QVQ6YU62.js.map +1 -0
  103. package/dist/sqlite-4I2P2OGQ.js +554 -0
  104. package/dist/sqlite-4I2P2OGQ.js.map +1 -0
  105. package/dist/sqlite-OA4YJX5S.mjs +531 -0
  106. package/dist/sqlite-OA4YJX5S.mjs.map +1 -0
  107. package/dist/types-BJvJfxSY.d.mts +141 -0
  108. package/dist/types-BJvJfxSY.d.ts +141 -0
  109. package/dist/types-ClAg_v5k.d.mts +343 -0
  110. package/dist/types-Der_X8E8.d.ts +343 -0
  111. package/dist/types.d.mts +2 -0
  112. package/dist/types.d.ts +2 -0
  113. package/dist/types.js +6 -0
  114. package/dist/types.js.map +1 -0
  115. package/dist/types.mjs +3 -0
  116. package/dist/types.mjs.map +1 -0
  117. package/dist/workspace.d.mts +80 -0
  118. package/dist/workspace.d.ts +80 -0
  119. package/dist/workspace.js +4174 -0
  120. package/dist/workspace.js.map +1 -0
  121. package/dist/workspace.mjs +4147 -0
  122. package/dist/workspace.mjs.map +1 -0
  123. package/package.json +60 -5
  124. package/.claude/settings.local.json +0 -127
  125. package/.cursorrules +0 -426
  126. package/.devin/wiki.json +0 -143
  127. package/.dockerignore +0 -80
  128. package/.env.example +0 -159
  129. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -49
  130. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -29
  131. package/.github/PULL_REQUEST_TEMPLATE.md +0 -57
  132. package/.github/workflows/ci.yml +0 -185
  133. package/.github/workflows/codeql.yml +0 -57
  134. package/.github/workflows/docker-build-push.yml +0 -118
  135. package/.github/workflows/helm-release.yml +0 -113
  136. package/CLAUDE.md +0 -265
  137. package/CODE_OF_CONDUCT.md +0 -124
  138. package/CONTRIBUTING.md +0 -154
  139. package/Dockerfile +0 -73
  140. package/SECURITY.md +0 -107
  141. package/artifacthub-repo.yml +0 -4
  142. package/bun.lock +0 -1714
  143. package/bunfig.toml +0 -3
  144. package/charts/libredb-studio/.helmignore +0 -11
  145. package/charts/libredb-studio/Chart.lock +0 -6
  146. package/charts/libredb-studio/Chart.yaml +0 -50
  147. package/charts/libredb-studio/README.md +0 -206
  148. package/charts/libredb-studio/templates/NOTES.txt +0 -59
  149. package/charts/libredb-studio/templates/_helpers.tpl +0 -135
  150. package/charts/libredb-studio/templates/configmap.yaml +0 -37
  151. package/charts/libredb-studio/templates/deployment.yaml +0 -184
  152. package/charts/libredb-studio/templates/hpa.yaml +0 -32
  153. package/charts/libredb-studio/templates/ingress.yaml +0 -41
  154. package/charts/libredb-studio/templates/networkpolicy.yaml +0 -50
  155. package/charts/libredb-studio/templates/pdb.yaml +0 -18
  156. package/charts/libredb-studio/templates/pvc.yaml +0 -23
  157. package/charts/libredb-studio/templates/secret.yaml +0 -30
  158. package/charts/libredb-studio/templates/seed-configmap.yaml +0 -11
  159. package/charts/libredb-studio/templates/service.yaml +0 -22
  160. package/charts/libredb-studio/templates/serviceaccount.yaml +0 -13
  161. package/charts/libredb-studio/values.schema.json +0 -246
  162. package/charts/libredb-studio/values.yaml +0 -286
  163. package/components.json +0 -22
  164. package/conductor/code_styleguides/typescript.md +0 -43
  165. package/conductor/product-guidelines.md +0 -43
  166. package/conductor/product.md +0 -3
  167. package/conductor/setup_state.json +0 -1
  168. package/conductor/tech-stack.md +0 -39
  169. package/conductor/tracks/enhance_postgres_monitoring_20251227/metadata.json +0 -8
  170. package/conductor/tracks/enhance_postgres_monitoring_20251227/plan.md +0 -44
  171. package/conductor/tracks/enhance_postgres_monitoring_20251227/spec.md +0 -31
  172. package/conductor/tracks.md +0 -8
  173. package/conductor/workflow.md +0 -333
  174. package/database-compose.yml +0 -55
  175. package/docker/postgres-init/01-extensions.sql +0 -10
  176. package/docker/postgres-init/02-sample-data.sql +0 -585
  177. package/docker/postgres.yml +0 -68
  178. package/docker-compose.yml +0 -38
  179. package/docs/AI_PLAN.md +0 -74
  180. package/docs/API_DOCS.md +0 -875
  181. package/docs/ARCHITECTURE.md +0 -218
  182. package/docs/DATABASE_PROVIDERS.md +0 -358
  183. package/docs/FEATURES.md +0 -116
  184. package/docs/HELM_CHART.md +0 -252
  185. package/docs/LOGIN_PAGE.md +0 -178
  186. package/docs/MONACO_EDITOR_PERFORMANCE.md +0 -315
  187. package/docs/OIDC_ARCH.md +0 -681
  188. package/docs/OIDC_SETUP.md +0 -322
  189. package/docs/POSTGRES_METRICS.md +0 -516
  190. package/docs/QUERY_OPTIMIZATION.md +0 -370
  191. package/docs/SEED_CONNECTIONS.md +0 -468
  192. package/docs/SQL_ALIAS_COMPLETION.md +0 -190
  193. package/docs/STORAGE_ARCHITECTURE.md +0 -565
  194. package/docs/STORAGE_QUICK_SETUP.md +0 -419
  195. package/docs/TECHNICAL_PLAN.md +0 -36
  196. package/docs/THEMING.md +0 -345
  197. package/docs/adding-a-new-database-provider.md +0 -642
  198. package/docs/backlogs/000-PLATFORM_DATA_SYNC_DATABASE.md +0 -360
  199. package/docs/backlogs/001-INLINE_DATA_EDITING.md +0 -118
  200. package/docs/backlogs/002-DATA_IMPORT.md +0 -215
  201. package/docs/backlogs/003-QUERY_TIME_MACHINE.md +0 -183
  202. package/docs/backlogs/004-AI_DATA_STORYTELLER.md +0 -292
  203. package/docs/backlogs/005-QUERY_PLAYGROUND.md +0 -352
  204. package/docs/backlogs/006-DATA_MASKING.md +0 -418
  205. package/docs/enterprise-features.md +0 -718
  206. package/docs/kubernetes-helm-chart-artifacthub-plan.md +0 -803
  207. package/docs/medium-koyeb-article-en.md +0 -215
  208. package/docs/plans/test-plans.md +0 -445
  209. package/docs/releases/RELEASE.V0.3.0.md +0 -22
  210. package/docs/releases/RELEASE.V0.4.0.md +0 -154
  211. package/docs/releases/RELEASE.V0.5.0.md +0 -252
  212. package/docs/releases/RELEASE_v0.5.6.md +0 -145
  213. package/docs/releases/RELEASE_v0.6.1.md +0 -303
  214. package/docs/releases/RELEASE_v0.6.7.md +0 -292
  215. package/docs/releases/RELEASE_v0.7.0.md +0 -332
  216. package/docs/releases/RELEASE_v0.8.0.md +0 -521
  217. package/docs/sampledb/titanic.sql +0 -1379
  218. package/docs/superpowers/plans/2026-03-25-seed-connections.md +0 -1362
  219. package/docs/superpowers/specs/2026-03-25-seed-connections-design.md +0 -590
  220. package/e2e/admin-dashboard.spec.ts +0 -64
  221. package/e2e/connection-management.spec.ts +0 -58
  222. package/e2e/export.spec.ts +0 -34
  223. package/e2e/login.spec.ts +0 -85
  224. package/e2e/query-execution.spec.ts +0 -35
  225. package/e2e/tab-management.spec.ts +0 -64
  226. package/eslint.config.mjs +0 -28
  227. package/fly.toml +0 -43
  228. package/next.config.ts +0 -32
  229. package/playwright.config.ts +0 -34
  230. package/postcss.config.mjs +0 -7
  231. package/public/favicon-32x32.png +0 -0
  232. package/public/favicon.ico +0 -0
  233. package/public/file.svg +0 -1
  234. package/public/globe.svg +0 -1
  235. package/public/logo.svg +0 -32
  236. package/public/next.svg +0 -1
  237. package/public/screenshots/code-generator.png +0 -0
  238. package/public/screenshots/connection-modal.png +0 -0
  239. package/public/screenshots/data-profiler.png +0 -0
  240. package/public/screenshots/erd-diagram.png +0 -0
  241. package/public/screenshots/hero-editor.png +0 -0
  242. package/public/screenshots/nl2sql.png +0 -0
  243. package/public/vercel.svg +0 -1
  244. package/public/window.svg +0 -1
  245. package/render.yaml +0 -58
  246. package/scripts/merge-lcov.mjs +0 -239
  247. package/sonar-project.properties +0 -16
  248. package/src/app/admin/error.tsx +0 -46
  249. package/src/app/admin/page.tsx +0 -10
  250. package/src/app/api/admin/audit/route.ts +0 -52
  251. package/src/app/api/admin/fleet-health/route.ts +0 -81
  252. package/src/app/api/ai/autopilot/route.ts +0 -105
  253. package/src/app/api/ai/chat/route.ts +0 -132
  254. package/src/app/api/ai/describe-schema/route.ts +0 -52
  255. package/src/app/api/ai/explain/route.ts +0 -86
  256. package/src/app/api/ai/impact/route.ts +0 -97
  257. package/src/app/api/ai/index-advisor/route.ts +0 -98
  258. package/src/app/api/ai/nl2sql/route.ts +0 -87
  259. package/src/app/api/ai/query-safety/route.ts +0 -87
  260. package/src/app/api/auth/login/route.ts +0 -62
  261. package/src/app/api/auth/logout/route.ts +0 -25
  262. package/src/app/api/auth/me/route.ts +0 -10
  263. package/src/app/api/auth/oidc/callback/route.ts +0 -82
  264. package/src/app/api/auth/oidc/login/route.ts +0 -43
  265. package/src/app/api/connections/managed/route.ts +0 -35
  266. package/src/app/api/db/cancel/route.ts +0 -42
  267. package/src/app/api/db/disconnect/route.ts +0 -28
  268. package/src/app/api/db/health/route.ts +0 -49
  269. package/src/app/api/db/maintenance/route.ts +0 -72
  270. package/src/app/api/db/monitoring/route.ts +0 -62
  271. package/src/app/api/db/multi-query/route.ts +0 -116
  272. package/src/app/api/db/pool-stats/route.ts +0 -37
  273. package/src/app/api/db/profile/route.ts +0 -144
  274. package/src/app/api/db/provider-meta/route.ts +0 -49
  275. package/src/app/api/db/query/route.ts +0 -50
  276. package/src/app/api/db/schema/route.ts +0 -47
  277. package/src/app/api/db/schema-snapshot/route.ts +0 -42
  278. package/src/app/api/db/test-connection/route.ts +0 -55
  279. package/src/app/api/db/transaction/route.ts +0 -111
  280. package/src/app/api/storage/[collection]/route.ts +0 -67
  281. package/src/app/api/storage/config/route.ts +0 -17
  282. package/src/app/api/storage/migrate/route.ts +0 -45
  283. package/src/app/api/storage/route.ts +0 -32
  284. package/src/app/error.tsx +0 -49
  285. package/src/app/global-error.tsx +0 -55
  286. package/src/app/globals.css +0 -146
  287. package/src/app/icon.svg +0 -42
  288. package/src/app/layout.tsx +0 -34
  289. package/src/app/login/login-form.tsx +0 -301
  290. package/src/app/login/page.tsx +0 -11
  291. package/src/app/monitoring/page.tsx +0 -8
  292. package/src/app/not-found.tsx +0 -29
  293. package/src/app/page.tsx +0 -5
  294. package/src/components/AIAutopilotPanel.tsx +0 -238
  295. package/src/components/CodeGenerator.tsx +0 -271
  296. package/src/components/CommandPalette.tsx +0 -227
  297. package/src/components/ConnectionModal.tsx +0 -759
  298. package/src/components/CreateTableModal.tsx +0 -281
  299. package/src/components/DataCharts.tsx +0 -962
  300. package/src/components/DataImportModal.tsx +0 -582
  301. package/src/components/DataProfiler.tsx +0 -335
  302. package/src/components/DatabaseDocs.tsx +0 -251
  303. package/src/components/MaskingSettings.tsx +0 -414
  304. package/src/components/MobileNav.tsx +0 -50
  305. package/src/components/NL2SQLPanel.tsx +0 -281
  306. package/src/components/PivotTable.tsx +0 -257
  307. package/src/components/QueryEditor.tsx +0 -760
  308. package/src/components/QueryHistory.tsx +0 -344
  309. package/src/components/QuerySafetyDialog.tsx +0 -290
  310. package/src/components/ResultsGrid.tsx +0 -644
  311. package/src/components/SaveQueryModal.tsx +0 -104
  312. package/src/components/SavedQueries.tsx +0 -128
  313. package/src/components/SchemaDiagram.tsx +0 -473
  314. package/src/components/SchemaDiff.tsx +0 -473
  315. package/src/components/SnapshotTimeline.tsx +0 -116
  316. package/src/components/Studio.tsx +0 -639
  317. package/src/components/TestDataGenerator.tsx +0 -261
  318. package/src/components/VisualExplain.tsx +0 -820
  319. package/src/components/admin/AdminDashboard.tsx +0 -163
  320. package/src/components/admin/tabs/AuditTab.tsx +0 -531
  321. package/src/components/admin/tabs/MonitoringEmbed.tsx +0 -11
  322. package/src/components/admin/tabs/OperationsTab.tsx +0 -646
  323. package/src/components/admin/tabs/OverviewTab.tsx +0 -1328
  324. package/src/components/admin/tabs/SecurityTab.tsx +0 -284
  325. package/src/components/community-section.tsx +0 -92
  326. package/src/components/icons/db-icons.tsx +0 -84
  327. package/src/components/libredb-logo.tsx +0 -61
  328. package/src/components/monitoring/MonitoringDashboard.tsx +0 -345
  329. package/src/components/monitoring/tabs/MetricChart.tsx +0 -82
  330. package/src/components/monitoring/tabs/OverviewTab.tsx +0 -263
  331. package/src/components/monitoring/tabs/PerformanceTab.tsx +0 -254
  332. package/src/components/monitoring/tabs/PoolTab.tsx +0 -174
  333. package/src/components/monitoring/tabs/QueriesTab.tsx +0 -287
  334. package/src/components/monitoring/tabs/SessionsTab.tsx +0 -316
  335. package/src/components/monitoring/tabs/StorageTab.tsx +0 -335
  336. package/src/components/monitoring/tabs/TablesTab.tsx +0 -300
  337. package/src/components/results-grid/ResultCard.tsx +0 -111
  338. package/src/components/results-grid/RowDetailSheet.tsx +0 -178
  339. package/src/components/results-grid/StatsBar.tsx +0 -201
  340. package/src/components/results-grid/index.ts +0 -1
  341. package/src/components/results-grid/utils.ts +0 -23
  342. package/src/components/schema-explorer/ColumnList.tsx +0 -53
  343. package/src/components/schema-explorer/SchemaExplorer.tsx +0 -182
  344. package/src/components/schema-explorer/TableItem.tsx +0 -210
  345. package/src/components/schema-explorer/index.ts +0 -1
  346. package/src/components/sidebar/ConnectionItem.tsx +0 -105
  347. package/src/components/sidebar/ConnectionsList.tsx +0 -62
  348. package/src/components/sidebar/Sidebar.tsx +0 -130
  349. package/src/components/sidebar/index.ts +0 -2
  350. package/src/components/studio/BottomPanel.tsx +0 -286
  351. package/src/components/studio/QueryToolbar.tsx +0 -180
  352. package/src/components/studio/StudioDesktopHeader.tsx +0 -114
  353. package/src/components/studio/StudioMobileHeader.tsx +0 -340
  354. package/src/components/studio/StudioTabBar.tsx +0 -82
  355. package/src/components/studio/index.ts +0 -5
  356. package/src/components/ui/accordion.tsx +0 -66
  357. package/src/components/ui/alert-dialog.tsx +0 -157
  358. package/src/components/ui/alert.tsx +0 -66
  359. package/src/components/ui/aspect-ratio.tsx +0 -11
  360. package/src/components/ui/avatar.tsx +0 -53
  361. package/src/components/ui/badge.tsx +0 -46
  362. package/src/components/ui/breadcrumb.tsx +0 -109
  363. package/src/components/ui/button-group.tsx +0 -83
  364. package/src/components/ui/button.tsx +0 -60
  365. package/src/components/ui/calendar.tsx +0 -216
  366. package/src/components/ui/card.tsx +0 -92
  367. package/src/components/ui/carousel.tsx +0 -241
  368. package/src/components/ui/chart.tsx +0 -357
  369. package/src/components/ui/checkbox.tsx +0 -32
  370. package/src/components/ui/collapsible.tsx +0 -33
  371. package/src/components/ui/command.tsx +0 -184
  372. package/src/components/ui/context-menu.tsx +0 -252
  373. package/src/components/ui/dialog.tsx +0 -143
  374. package/src/components/ui/drawer.tsx +0 -135
  375. package/src/components/ui/dropdown-menu.tsx +0 -257
  376. package/src/components/ui/empty.tsx +0 -104
  377. package/src/components/ui/field.tsx +0 -248
  378. package/src/components/ui/form.tsx +0 -167
  379. package/src/components/ui/hover-card.tsx +0 -44
  380. package/src/components/ui/input-group.tsx +0 -170
  381. package/src/components/ui/input-otp.tsx +0 -77
  382. package/src/components/ui/input.tsx +0 -21
  383. package/src/components/ui/item.tsx +0 -193
  384. package/src/components/ui/kbd.tsx +0 -28
  385. package/src/components/ui/label.tsx +0 -24
  386. package/src/components/ui/menubar.tsx +0 -276
  387. package/src/components/ui/navigation-menu.tsx +0 -168
  388. package/src/components/ui/pagination.tsx +0 -127
  389. package/src/components/ui/popover.tsx +0 -48
  390. package/src/components/ui/progress.tsx +0 -31
  391. package/src/components/ui/radio-group.tsx +0 -45
  392. package/src/components/ui/resizable.tsx +0 -56
  393. package/src/components/ui/scroll-area.tsx +0 -58
  394. package/src/components/ui/select.tsx +0 -187
  395. package/src/components/ui/separator.tsx +0 -28
  396. package/src/components/ui/sheet.tsx +0 -139
  397. package/src/components/ui/sidebar.tsx +0 -726
  398. package/src/components/ui/skeleton.tsx +0 -13
  399. package/src/components/ui/slider.tsx +0 -63
  400. package/src/components/ui/sonner.tsx +0 -40
  401. package/src/components/ui/spinner.tsx +0 -16
  402. package/src/components/ui/switch.tsx +0 -31
  403. package/src/components/ui/table.tsx +0 -116
  404. package/src/components/ui/tabs.tsx +0 -66
  405. package/src/components/ui/textarea.tsx +0 -18
  406. package/src/components/ui/toggle-group.tsx +0 -83
  407. package/src/components/ui/toggle.tsx +0 -47
  408. package/src/components/ui/tooltip.tsx +0 -61
  409. package/src/exports/components.ts +0 -15
  410. package/src/exports/index.ts +0 -4
  411. package/src/exports/providers.ts +0 -4
  412. package/src/exports/types.ts +0 -26
  413. package/src/hooks/use-ai-chat.ts +0 -182
  414. package/src/hooks/use-all-connections.ts +0 -66
  415. package/src/hooks/use-api-call.ts +0 -71
  416. package/src/hooks/use-auth.ts +0 -51
  417. package/src/hooks/use-connection-form.ts +0 -349
  418. package/src/hooks/use-connection-manager.ts +0 -169
  419. package/src/hooks/use-connection-payload.ts +0 -15
  420. package/src/hooks/use-inline-editing.ts +0 -109
  421. package/src/hooks/use-mobile.ts +0 -20
  422. package/src/hooks/use-monitoring-data.ts +0 -270
  423. package/src/hooks/use-provider-metadata.ts +0 -62
  424. package/src/hooks/use-query-execution.ts +0 -478
  425. package/src/hooks/use-storage-sync.ts +0 -259
  426. package/src/hooks/use-tab-manager.ts +0 -231
  427. package/src/hooks/use-toast.ts +0 -20
  428. package/src/hooks/use-transaction-control.ts +0 -64
  429. package/src/lib/api/error-codes.ts +0 -30
  430. package/src/lib/api/errors.ts +0 -236
  431. package/src/lib/api/with-error-handler.ts +0 -41
  432. package/src/lib/audit.ts +0 -105
  433. package/src/lib/auth.ts +0 -87
  434. package/src/lib/connection-string-parser.ts +0 -172
  435. package/src/lib/data-masking.ts +0 -385
  436. package/src/lib/db/base-provider.ts +0 -325
  437. package/src/lib/db/errors.ts +0 -317
  438. package/src/lib/db/factory.ts +0 -324
  439. package/src/lib/db/index.ts +0 -123
  440. package/src/lib/db/providers/document/index.ts +0 -6
  441. package/src/lib/db/providers/document/mongodb.ts +0 -992
  442. package/src/lib/db/providers/keyvalue/redis.ts +0 -554
  443. package/src/lib/db/providers/sql/index.ts +0 -11
  444. package/src/lib/db/providers/sql/sql-base.ts +0 -174
  445. package/src/lib/db/providers/sql/sqlite.ts +0 -721
  446. package/src/lib/db/types.ts +0 -437
  447. package/src/lib/db/utils/pool-manager.ts +0 -287
  448. package/src/lib/db/utils/query-limiter.ts +0 -239
  449. package/src/lib/db-ui-config.ts +0 -86
  450. package/src/lib/editor/mongodb-completions.ts +0 -172
  451. package/src/lib/editor/sql-completions.ts +0 -280
  452. package/src/lib/llm/base-provider.ts +0 -117
  453. package/src/lib/llm/factory.ts +0 -102
  454. package/src/lib/llm/index.ts +0 -90
  455. package/src/lib/llm/providers/custom.ts +0 -181
  456. package/src/lib/llm/providers/gemini.ts +0 -126
  457. package/src/lib/llm/providers/ollama.ts +0 -154
  458. package/src/lib/llm/providers/openai.ts +0 -146
  459. package/src/lib/llm/types.ts +0 -173
  460. package/src/lib/llm/utils/config.ts +0 -187
  461. package/src/lib/llm/utils/retry.ts +0 -119
  462. package/src/lib/llm/utils/streaming.ts +0 -202
  463. package/src/lib/logger.ts +0 -127
  464. package/src/lib/monitoring-thresholds.ts +0 -44
  465. package/src/lib/oidc.ts +0 -262
  466. package/src/lib/query-generators.ts +0 -61
  467. package/src/lib/schema-diff/diff-engine.ts +0 -273
  468. package/src/lib/schema-diff/migration-generator.ts +0 -208
  469. package/src/lib/schema-diff/types.ts +0 -55
  470. package/src/lib/seed/config-loader.ts +0 -79
  471. package/src/lib/seed/connection-filter.ts +0 -49
  472. package/src/lib/seed/credential-resolver.ts +0 -62
  473. package/src/lib/seed/index.ts +0 -40
  474. package/src/lib/seed/resolve-connection.ts +0 -57
  475. package/src/lib/seed/types.ts +0 -69
  476. package/src/lib/sql/alias-extractor.ts +0 -267
  477. package/src/lib/sql/index.ts +0 -8
  478. package/src/lib/sql/statement-splitter.ts +0 -167
  479. package/src/lib/sql/types.ts +0 -40
  480. package/src/lib/ssh/tunnel.ts +0 -142
  481. package/src/lib/storage/factory.ts +0 -84
  482. package/src/lib/storage/index.ts +0 -14
  483. package/src/lib/storage/local-storage.ts +0 -99
  484. package/src/lib/storage/providers/postgres.ts +0 -225
  485. package/src/lib/storage/providers/sqlite.ts +0 -153
  486. package/src/lib/storage/storage-facade.ts +0 -272
  487. package/src/lib/storage/types.ts +0 -75
  488. package/src/lib/time-series-buffer.ts +0 -58
  489. package/src/lib/types.ts +0 -173
  490. package/src/lib/utils.ts +0 -6
  491. package/src/proxy.ts +0 -104
  492. package/src/types/db-drivers.d.ts +0 -23
  493. package/src/types/html2canvas.d.ts +0 -9
  494. package/tests/api/admin/audit.test.ts +0 -178
  495. package/tests/api/admin/fleet-health.test.ts +0 -183
  496. package/tests/api/ai/autopilot.test.ts +0 -174
  497. package/tests/api/ai/chat.test.ts +0 -250
  498. package/tests/api/ai/describe-schema.test.ts +0 -266
  499. package/tests/api/ai/explain.test.ts +0 -199
  500. package/tests/api/ai/impact.test.ts +0 -168
  501. package/tests/api/ai/index-advisor.test.ts +0 -171
  502. package/tests/api/ai/nl2sql.test.ts +0 -202
  503. package/tests/api/ai/query-safety.test.ts +0 -196
  504. package/tests/api/auth/login.test.ts +0 -170
  505. package/tests/api/auth/logout.test.ts +0 -140
  506. package/tests/api/auth/me.test.ts +0 -73
  507. package/tests/api/auth/oidc-callback.test.ts +0 -215
  508. package/tests/api/auth/oidc-login.test.ts +0 -127
  509. package/tests/api/db/cancel.test.ts +0 -198
  510. package/tests/api/db/disconnect.test.ts +0 -124
  511. package/tests/api/db/health.test.ts +0 -222
  512. package/tests/api/db/maintenance.test.ts +0 -263
  513. package/tests/api/db/monitoring.test.ts +0 -221
  514. package/tests/api/db/multi-query.test.ts +0 -316
  515. package/tests/api/db/pool-stats.test.ts +0 -135
  516. package/tests/api/db/profile.test.ts +0 -330
  517. package/tests/api/db/provider-meta.test.ts +0 -193
  518. package/tests/api/db/query.test.ts +0 -314
  519. package/tests/api/db/schema-snapshot.test.ts +0 -170
  520. package/tests/api/db/schema.test.ts +0 -191
  521. package/tests/api/db/test-connection.test.ts +0 -185
  522. package/tests/api/db/transaction.test.ts +0 -314
  523. package/tests/api/proxy.test.ts +0 -191
  524. package/tests/api/seed/managed-route.test.ts +0 -113
  525. package/tests/api/storage/config.test.ts +0 -42
  526. package/tests/api/storage/storage-routes.test.ts +0 -309
  527. package/tests/components/AIAutopilotPanel.test.tsx +0 -756
  528. package/tests/components/AdminPage.test.tsx +0 -33
  529. package/tests/components/CodeGenerator.test.tsx +0 -182
  530. package/tests/components/CommandPalette.test.tsx +0 -428
  531. package/tests/components/CommunitySection.test.tsx +0 -91
  532. package/tests/components/ConnectionModal.mobile.test.tsx +0 -284
  533. package/tests/components/ConnectionModal.test.tsx +0 -570
  534. package/tests/components/CreateTableModal.test.tsx +0 -383
  535. package/tests/components/DataCharts.test.tsx +0 -739
  536. package/tests/components/DataImportModal.test.tsx +0 -751
  537. package/tests/components/DataProfiler.test.tsx +0 -589
  538. package/tests/components/DatabaseDocs.test.tsx +0 -353
  539. package/tests/components/LoginPage.test.tsx +0 -163
  540. package/tests/components/LoginPageOIDC.test.tsx +0 -92
  541. package/tests/components/MaskingSettings.test.tsx +0 -498
  542. package/tests/components/MobileNav.test.tsx +0 -30
  543. package/tests/components/MonitoringPage.test.tsx +0 -32
  544. package/tests/components/NL2SQLPanel.test.tsx +0 -621
  545. package/tests/components/Page.test.tsx +0 -33
  546. package/tests/components/PivotTable.test.tsx +0 -350
  547. package/tests/components/QueryEditor.test.tsx +0 -1730
  548. package/tests/components/QueryHistory.test.tsx +0 -572
  549. package/tests/components/QuerySafetyDialog.test.tsx +0 -586
  550. package/tests/components/ResultsGrid.test.tsx +0 -804
  551. package/tests/components/RootLayout.test.tsx +0 -83
  552. package/tests/components/SaveQueryModal.test.tsx +0 -25
  553. package/tests/components/SavedQueries.test.tsx +0 -43
  554. package/tests/components/SchemaDiagram.test.tsx +0 -1034
  555. package/tests/components/SchemaDiff.test.tsx +0 -906
  556. package/tests/components/SnapshotTimeline.test.tsx +0 -174
  557. package/tests/components/Studio.test.tsx +0 -1030
  558. package/tests/components/TestDataGenerator.test.tsx +0 -291
  559. package/tests/components/VisualExplain.test.tsx +0 -704
  560. package/tests/components/admin/AdminDashboard.test.tsx +0 -205
  561. package/tests/components/admin/AuditTab.test.tsx +0 -220
  562. package/tests/components/admin/MonitoringEmbed.test.tsx +0 -58
  563. package/tests/components/admin/OperationsTab.test.tsx +0 -975
  564. package/tests/components/admin/OverviewTab.test.tsx +0 -254
  565. package/tests/components/admin/SecurityTab.test.tsx +0 -467
  566. package/tests/components/monitoring/MetricChart.test.tsx +0 -111
  567. package/tests/components/monitoring/MonitoringDashboard.test.tsx +0 -259
  568. package/tests/components/monitoring/OverviewTab.test.tsx +0 -78
  569. package/tests/components/monitoring/PerformanceTab.test.tsx +0 -87
  570. package/tests/components/monitoring/PoolTab.test.tsx +0 -42
  571. package/tests/components/monitoring/QueriesTab.test.tsx +0 -80
  572. package/tests/components/monitoring/SessionsTab.test.tsx +0 -154
  573. package/tests/components/monitoring/StorageTab.test.tsx +0 -127
  574. package/tests/components/monitoring/TablesTab.test.tsx +0 -153
  575. package/tests/components/results-grid/ResultCard.test.tsx +0 -105
  576. package/tests/components/results-grid/RowDetailSheet.test.tsx +0 -308
  577. package/tests/components/results-grid/StatsBar.test.tsx +0 -162
  578. package/tests/components/schema-explorer/ColumnList.test.tsx +0 -151
  579. package/tests/components/schema-explorer/SchemaExplorer.test.tsx +0 -461
  580. package/tests/components/schema-explorer/TableItem.test.tsx +0 -415
  581. package/tests/components/sidebar/ConnectionItem.test.tsx +0 -201
  582. package/tests/components/sidebar/ConnectionsList.test.tsx +0 -176
  583. package/tests/components/sidebar/Sidebar.test.tsx +0 -187
  584. package/tests/components/studio/BottomPanel.test.tsx +0 -383
  585. package/tests/components/studio/QueryToolbar.test.tsx +0 -321
  586. package/tests/components/studio/StudioDesktopHeader.test.tsx +0 -377
  587. package/tests/components/studio/StudioMobileHeader.test.tsx +0 -198
  588. package/tests/components/studio/StudioTabBar.test.tsx +0 -331
  589. package/tests/fixtures/connections.ts +0 -96
  590. package/tests/fixtures/masking-configs.ts +0 -86
  591. package/tests/fixtures/query-results.ts +0 -71
  592. package/tests/fixtures/schemas.ts +0 -64
  593. package/tests/fixtures/seed-connections/invalid-config.yaml +0 -7
  594. package/tests/fixtures/seed-connections/minimal-config.yaml +0 -8
  595. package/tests/fixtures/seed-connections/mixed-credentials.yaml +0 -23
  596. package/tests/fixtures/seed-connections/multi-role-config.yaml +0 -30
  597. package/tests/fixtures/seed-connections/valid-config.json +0 -15
  598. package/tests/fixtures/seed-connections/valid-config.yaml +0 -51
  599. package/tests/helpers/mock-fetch.ts +0 -59
  600. package/tests/helpers/mock-monaco.ts +0 -112
  601. package/tests/helpers/mock-navigation.ts +0 -28
  602. package/tests/helpers/mock-next.ts +0 -80
  603. package/tests/helpers/mock-provider.ts +0 -133
  604. package/tests/helpers/mock-sonner.ts +0 -29
  605. package/tests/helpers/render-with-providers.tsx +0 -19
  606. package/tests/hooks/use-ai-chat.test.ts +0 -600
  607. package/tests/hooks/use-auth.test.ts +0 -371
  608. package/tests/hooks/use-connection-form.test.ts +0 -743
  609. package/tests/hooks/use-connection-manager.test.ts +0 -466
  610. package/tests/hooks/use-inline-editing.test.ts +0 -321
  611. package/tests/hooks/use-mobile.test.ts +0 -177
  612. package/tests/hooks/use-monitoring-data.test.ts +0 -819
  613. package/tests/hooks/use-provider-metadata.test.ts +0 -228
  614. package/tests/hooks/use-query-execution.test.ts +0 -1212
  615. package/tests/hooks/use-tab-manager.test.ts +0 -756
  616. package/tests/hooks/use-toast.test.ts +0 -74
  617. package/tests/hooks/use-transaction-control.test.ts +0 -211
  618. package/tests/integration/db/mongodb-provider.test.ts +0 -698
  619. package/tests/integration/db/mssql-provider.test.ts +0 -840
  620. package/tests/integration/db/mysql-provider.test.ts +0 -872
  621. package/tests/integration/db/oracle-provider.test.ts +0 -843
  622. package/tests/integration/db/postgres-provider.test.ts +0 -1382
  623. package/tests/integration/db/redis-provider.test.ts +0 -526
  624. package/tests/integration/db/sqlite-provider.test.ts +0 -480
  625. package/tests/integration/seed/seed-pipeline.test.ts +0 -102
  626. package/tests/isolated/factory-singleton.test.ts +0 -150
  627. package/tests/isolated/use-storage-sync.test.ts +0 -389
  628. package/tests/run-components.sh +0 -196
  629. package/tests/setup-dom.ts +0 -58
  630. package/tests/setup.ts +0 -40
  631. package/tests/unit/api-errors.test.ts +0 -210
  632. package/tests/unit/code-generator-functions.test.ts +0 -271
  633. package/tests/unit/components/column-list.test.tsx +0 -190
  634. package/tests/unit/components/data-import-modal.test.tsx +0 -441
  635. package/tests/unit/components/studio-mobile-header.test.tsx +0 -327
  636. package/tests/unit/data-charts-functions.test.ts +0 -496
  637. package/tests/unit/data-import-functions.test.ts +0 -320
  638. package/tests/unit/data-import-utils.test.ts +0 -125
  639. package/tests/unit/db/base-provider.test.ts +0 -517
  640. package/tests/unit/db/errors.test.ts +0 -403
  641. package/tests/unit/db/factory.test.ts +0 -436
  642. package/tests/unit/db/pool-manager.test.ts +0 -440
  643. package/tests/unit/db/query-limiter.test.ts +0 -387
  644. package/tests/unit/db/sql-base.test.ts +0 -438
  645. package/tests/unit/lib/api/error-codes.test.ts +0 -39
  646. package/tests/unit/lib/audit.test.ts +0 -326
  647. package/tests/unit/lib/auth.test.ts +0 -146
  648. package/tests/unit/lib/connection-string-parser.test.ts +0 -424
  649. package/tests/unit/lib/data-masking.test.ts +0 -583
  650. package/tests/unit/lib/db-icons.test.tsx +0 -41
  651. package/tests/unit/lib/monitoring-thresholds.test.ts +0 -133
  652. package/tests/unit/lib/oidc.test.ts +0 -509
  653. package/tests/unit/lib/query-generators.test.ts +0 -127
  654. package/tests/unit/lib/storage/factory.test.ts +0 -71
  655. package/tests/unit/lib/storage/local-storage.test.ts +0 -114
  656. package/tests/unit/lib/storage/providers/postgres.test.ts +0 -312
  657. package/tests/unit/lib/storage/providers/sqlite.test.ts +0 -232
  658. package/tests/unit/lib/storage/storage-facade-extended.test.ts +0 -331
  659. package/tests/unit/lib/storage/storage-facade.test.ts +0 -184
  660. package/tests/unit/lib/storage.test.ts +0 -317
  661. package/tests/unit/lib/time-series-buffer.test.ts +0 -212
  662. package/tests/unit/lib/utils.test.ts +0 -24
  663. package/tests/unit/llm/base-provider.test.ts +0 -238
  664. package/tests/unit/llm/config.test.ts +0 -262
  665. package/tests/unit/llm/custom-provider.test.ts +0 -281
  666. package/tests/unit/llm/gemini-provider.test.ts +0 -248
  667. package/tests/unit/llm/llm-factory.test.ts +0 -155
  668. package/tests/unit/llm/ollama-provider.test.ts +0 -288
  669. package/tests/unit/llm/openai-provider.test.ts +0 -324
  670. package/tests/unit/llm/retry.test.ts +0 -180
  671. package/tests/unit/llm/streaming.test.ts +0 -355
  672. package/tests/unit/logger.test.ts +0 -198
  673. package/tests/unit/mongodb-completions.test.ts +0 -516
  674. package/tests/unit/pivot-table-functions.test.ts +0 -76
  675. package/tests/unit/query-cancelled-error.test.ts +0 -81
  676. package/tests/unit/schema-diff/diff-engine.test.ts +0 -367
  677. package/tests/unit/schema-diff/migration-generator.test.ts +0 -513
  678. package/tests/unit/seed/config-loader.test.ts +0 -73
  679. package/tests/unit/seed/connection-filter.test.ts +0 -91
  680. package/tests/unit/seed/credential-resolver.test.ts +0 -85
  681. package/tests/unit/seed/index.test.ts +0 -72
  682. package/tests/unit/seed/resolve-connection.test.ts +0 -74
  683. package/tests/unit/seed/types.test.ts +0 -129
  684. package/tests/unit/sql/alias-extractor.test.ts +0 -444
  685. package/tests/unit/sql/statement-splitter.test.ts +0 -348
  686. package/tests/unit/sql-completions.test.ts +0 -463
  687. package/tests/unit/ssh-tunnel.test.ts +0 -465
  688. package/tsconfig.json +0 -42
@@ -1,975 +0,0 @@
1
- import '../../setup-dom';
2
- import '../../helpers/mock-sonner';
3
- import '../../helpers/mock-navigation';
4
-
5
- import { mock } from 'bun:test';
6
- import { setupRechartssMock, setupFramerMotionMock } from '../../helpers/mock-monaco';
7
-
8
- setupRechartssMock();
9
- setupFramerMotionMock();
10
-
11
- // ---- Trackable mock functions ----
12
- const mockRefresh = mock(() => {});
13
- const mockKillSession = mock(() => true);
14
- const mockRunMaintenance = mock(() => true);
15
-
16
- // ---- Override objects ----
17
- let monitoringOverride: Record<string, unknown> = {};
18
- let mockConnectionsList: Record<string, unknown>[] = [
19
- {
20
- id: 'c1',
21
- name: 'PG Dev',
22
- type: 'postgres',
23
- host: 'localhost',
24
- port: 5432,
25
- database: 'dev',
26
- createdAt: new Date(),
27
- },
28
- ];
29
- let mockActiveConnectionId: string | null = 'c1';
30
-
31
- const defaultSessions = [
32
- {
33
- pid: 1234,
34
- user: 'admin',
35
- state: 'active',
36
- query: 'SELECT 1',
37
- duration: '00:01:00',
38
- durationMs: 60000,
39
- database: 'dev',
40
- },
41
- ];
42
-
43
- const defaultTables = [
44
- {
45
- tableName: 'users',
46
- schemaName: 'public',
47
- rowCount: 1000,
48
- tableSize: '16 MB',
49
- totalSize: '20 MB',
50
- bloatRatio: 5,
51
- },
52
- ];
53
-
54
- mock.module('@/hooks/use-monitoring-data', () => ({
55
- useMonitoringData: mock(() => ({
56
- data: {
57
- activeSessions: defaultSessions,
58
- tables: defaultTables,
59
- },
60
- loading: false,
61
- error: null,
62
- refresh: mockRefresh,
63
- killSession: mockKillSession,
64
- runMaintenance: mockRunMaintenance,
65
- ...monitoringOverride,
66
- })),
67
- }));
68
-
69
- mock.module('@/lib/storage', () => ({
70
- storage: {
71
- getConnections: mock(() => mockConnectionsList),
72
- getActiveConnectionId: mock(() => mockActiveConnectionId),
73
- },
74
- }));
75
-
76
- mock.module('@/lib/db-ui-config', () => ({
77
- getDBIcon: () => {
78
- // eslint-disable-next-line @typescript-eslint/no-require-imports
79
- const React = require('react');
80
- return (props: Record<string, unknown>) => React.createElement('span', { ...props, 'data-testid': 'db-icon' });
81
- },
82
- getDBColor: () => 'text-blue-400',
83
- }));
84
-
85
- import { describe, test, expect, beforeEach, afterEach } from 'bun:test';
86
- import { render, act, cleanup, fireEvent } from '@testing-library/react';
87
- import React from 'react';
88
-
89
- import { OperationsTab } from '@/components/admin/tabs/OperationsTab';
90
-
91
- // =============================================================================
92
- // Test data
93
- // =============================================================================
94
-
95
- const multiSessions = [
96
- { pid: 100, user: 'admin', state: 'active', query: 'SELECT 1', duration: '00:00:05', durationMs: 5000, database: 'dev' },
97
- { pid: 101, user: 'user1', state: 'idle', query: '', duration: '00:00:10', durationMs: 10000, database: 'dev' },
98
- { pid: 102, user: 'user2', state: 'idle in transaction', query: 'UPDATE users SET x=1', duration: '00:02:00', durationMs: 120000, database: 'dev' },
99
- { pid: 103, user: 'user3', state: 'idle in transaction (aborted)', query: 'INSERT INTO t', duration: '00:00:30', durationMs: 30000, database: 'dev' },
100
- { pid: 104, user: 'user4', state: 'fastpath function call', query: '', duration: '00:00:01', durationMs: 1000, database: 'dev', waitEventType: 'Lock' },
101
- ];
102
-
103
- const multiTables = [
104
- { tableName: 'users', schemaName: 'public', rowCount: 1000, tableSize: '16 MB', totalSize: '20 MB', bloatRatio: 5 },
105
- { tableName: 'orders', schemaName: 'public', rowCount: 50000, tableSize: '128 MB', totalSize: '200 MB', bloatRatio: 25 },
106
- { tableName: 'products', schemaName: 'public', rowCount: 200, tableSize: '2 MB', totalSize: '3 MB', bloatRatio: 0 },
107
- ];
108
-
109
- // =============================================================================
110
- // OperationsTab Tests
111
- // =============================================================================
112
-
113
- describe('OperationsTab', () => {
114
- beforeEach(() => {
115
- // Reset overrides
116
- monitoringOverride = {};
117
- mockConnectionsList = [
118
- { id: 'c1', name: 'PG Dev', type: 'postgres', host: 'localhost', port: 5432, database: 'dev', createdAt: new Date() },
119
- ];
120
- mockActiveConnectionId = 'c1';
121
-
122
- // Clear mocks
123
- mockRefresh.mockClear();
124
- mockKillSession.mockClear();
125
- mockKillSession.mockImplementation(() => true);
126
- mockRunMaintenance.mockClear();
127
- mockRunMaintenance.mockImplementation(() => true);
128
- });
129
-
130
- afterEach(() => {
131
- cleanup();
132
- });
133
-
134
- // =========================================================================
135
- // Existing rendering tests
136
- // =========================================================================
137
-
138
- test('renders connection selector', async () => {
139
- let renderResult: ReturnType<typeof render>;
140
- await act(async () => {
141
- renderResult = render(<OperationsTab />);
142
- });
143
- const { queryByText } = renderResult!;
144
- expect(queryByText('PG Dev')).not.toBeNull();
145
- });
146
-
147
- test('shows global operations section', async () => {
148
- let renderResult: ReturnType<typeof render>;
149
- await act(async () => {
150
- renderResult = render(<OperationsTab />);
151
- });
152
- const { queryByText } = renderResult!;
153
- expect(queryByText('Global Operations')).not.toBeNull();
154
- expect(queryByText('Update Statistics')).not.toBeNull();
155
- expect(queryByText('Reclaim Space')).not.toBeNull();
156
- expect(queryByText('Rebuild Indexes')).not.toBeNull();
157
- });
158
-
159
- test('shows tables panel with table list', async () => {
160
- let renderResult: ReturnType<typeof render>;
161
- await act(async () => {
162
- renderResult = render(<OperationsTab />);
163
- });
164
- const { queryByText } = renderResult!;
165
- expect(queryByText('Tables (1)')).not.toBeNull();
166
- expect(queryByText('users')).not.toBeNull();
167
- });
168
-
169
- test('shows sessions panel with session list', async () => {
170
- let renderResult: ReturnType<typeof render>;
171
- await act(async () => {
172
- renderResult = render(<OperationsTab />);
173
- });
174
- const { queryByText } = renderResult!;
175
- expect(queryByText('Sessions (1)')).not.toBeNull();
176
- expect(queryByText('1234')).not.toBeNull();
177
- });
178
-
179
- test('maintenance buttons present', async () => {
180
- let renderResult: ReturnType<typeof render>;
181
- await act(async () => {
182
- renderResult = render(<OperationsTab />);
183
- });
184
- const { queryByText } = renderResult!;
185
- expect(queryByText('Run Analyze')).not.toBeNull();
186
- expect(queryByText('Run Vacuum')).not.toBeNull();
187
- expect(queryByText('Run Reindex')).not.toBeNull();
188
- });
189
-
190
- test('warning card present', async () => {
191
- let renderResult: ReturnType<typeof render>;
192
- await act(async () => {
193
- renderResult = render(<OperationsTab />);
194
- });
195
- const { queryByText } = renderResult!;
196
- expect(queryByText('Warning')).not.toBeNull();
197
- expect(queryByText(/resource-intensive/)).not.toBeNull();
198
- });
199
-
200
- test('shows table size information', async () => {
201
- let renderResult: ReturnType<typeof render>;
202
- await act(async () => {
203
- renderResult = render(<OperationsTab />);
204
- });
205
- const { queryByText } = renderResult!;
206
- expect(queryByText('16 MB')).not.toBeNull();
207
- });
208
-
209
- test('shows session user info', async () => {
210
- let renderResult: ReturnType<typeof render>;
211
- await act(async () => {
212
- renderResult = render(<OperationsTab />);
213
- });
214
- const { queryByText } = renderResult!;
215
- expect(queryByText('admin')).not.toBeNull();
216
- });
217
-
218
- test('shows session query info', async () => {
219
- let renderResult: ReturnType<typeof render>;
220
- await act(async () => {
221
- renderResult = render(<OperationsTab />);
222
- });
223
- const { container } = renderResult!;
224
- expect(container.textContent).toContain('SELECT 1');
225
- });
226
-
227
- test('shows session duration', async () => {
228
- let renderResult: ReturnType<typeof render>;
229
- await act(async () => {
230
- renderResult = render(<OperationsTab />);
231
- });
232
- const { container } = renderResult!;
233
- expect(container.textContent).toContain('00:01:00');
234
- });
235
-
236
- test('shows row count for tables', async () => {
237
- let renderResult: ReturnType<typeof render>;
238
- await act(async () => {
239
- renderResult = render(<OperationsTab />);
240
- });
241
- const { container } = renderResult!;
242
- expect(container.textContent).toMatch(/1,?000/);
243
- });
244
-
245
- test('shows connection type in selector', async () => {
246
- let renderResult: ReturnType<typeof render>;
247
- await act(async () => {
248
- renderResult = render(<OperationsTab />);
249
- });
250
- const { container } = renderResult!;
251
- expect(container.textContent).toContain('(postgres)');
252
- });
253
-
254
- test('shows session state as Active badge', async () => {
255
- let renderResult: ReturnType<typeof render>;
256
- await act(async () => {
257
- renderResult = render(<OperationsTab />);
258
- });
259
- const { container } = renderResult!;
260
- expect(container.textContent).toContain('Active');
261
- });
262
-
263
- // =========================================================================
264
- // Empty state: no connections
265
- // =========================================================================
266
-
267
- test('shows empty state when no connections', async () => {
268
- mockConnectionsList = [];
269
- let renderResult: ReturnType<typeof render>;
270
- await act(async () => {
271
- renderResult = render(<OperationsTab />);
272
- });
273
- const { queryByText } = renderResult!;
274
- expect(queryByText('No Database Connections')).not.toBeNull();
275
- expect(queryByText(/add a database connection/i)).not.toBeNull();
276
- });
277
-
278
- // =========================================================================
279
- // Error state
280
- // =========================================================================
281
-
282
- test('shows error message when error and no data', async () => {
283
- monitoringOverride = { data: null, error: 'Connection refused' };
284
- let renderResult: ReturnType<typeof render>;
285
- await act(async () => {
286
- renderResult = render(<OperationsTab />);
287
- });
288
- const { queryByText } = renderResult!;
289
- expect(queryByText('Connection refused')).not.toBeNull();
290
- });
291
-
292
- // =========================================================================
293
- // Loading state
294
- // =========================================================================
295
-
296
- test('shows loading skeletons when loading with no data', async () => {
297
- monitoringOverride = {
298
- data: { activeSessions: [], tables: [] },
299
- loading: true,
300
- };
301
- let renderResult: ReturnType<typeof render>;
302
- await act(async () => {
303
- renderResult = render(<OperationsTab />);
304
- });
305
- const { container } = renderResult!;
306
- // At minimum, the component renders skeleton placeholders when loading
307
- expect(container.textContent).toContain('Tables (0)');
308
- expect(container.textContent).toContain('Sessions (0)');
309
- });
310
-
311
- // =========================================================================
312
- // Empty sessions / empty tables
313
- // =========================================================================
314
-
315
- test('shows no sessions message when empty', async () => {
316
- monitoringOverride = {
317
- data: { activeSessions: [], tables: defaultTables },
318
- };
319
- let renderResult: ReturnType<typeof render>;
320
- await act(async () => {
321
- renderResult = render(<OperationsTab />);
322
- });
323
- const { queryByText } = renderResult!;
324
- expect(queryByText('No active sessions found.')).not.toBeNull();
325
- });
326
-
327
- // =========================================================================
328
- // Table search filter
329
- // =========================================================================
330
-
331
- test('filters tables by search input', async () => {
332
- monitoringOverride = {
333
- data: { activeSessions: defaultSessions, tables: multiTables },
334
- };
335
- let renderResult: ReturnType<typeof render>;
336
- await act(async () => {
337
- renderResult = render(<OperationsTab />);
338
- });
339
- const { container, queryByText } = renderResult!;
340
-
341
- // All 3 tables visible initially
342
- expect(queryByText('Tables (3)')).not.toBeNull();
343
- expect(queryByText('users')).not.toBeNull();
344
- expect(queryByText('orders')).not.toBeNull();
345
- expect(queryByText('products')).not.toBeNull();
346
-
347
- // Type in filter input
348
- const filterInput = container.querySelector('input[placeholder="Filter..."]') as HTMLInputElement;
349
- expect(filterInput).not.toBeNull();
350
- await act(async () => {
351
- fireEvent.change(filterInput, { target: { value: 'ord' } });
352
- });
353
-
354
- // Only 'orders' should match
355
- expect(queryByText('orders')).not.toBeNull();
356
- expect(queryByText('users')).toBeNull();
357
- expect(queryByText('products')).toBeNull();
358
- });
359
-
360
- test('shows no tables found when filter matches nothing', async () => {
361
- let renderResult: ReturnType<typeof render>;
362
- await act(async () => {
363
- renderResult = render(<OperationsTab />);
364
- });
365
- const { container, queryByText } = renderResult!;
366
-
367
- const filterInput = container.querySelector('input[placeholder="Filter..."]') as HTMLInputElement;
368
- await act(async () => {
369
- fireEvent.change(filterInput, { target: { value: 'zzz_nonexistent' } });
370
- });
371
- expect(queryByText('No tables found.')).not.toBeNull();
372
- });
373
-
374
- // =========================================================================
375
- // Bloat ratio badge
376
- // =========================================================================
377
-
378
- test('shows bloat ratio badge for high-bloat tables', async () => {
379
- monitoringOverride = {
380
- data: { activeSessions: defaultSessions, tables: multiTables },
381
- };
382
- let renderResult: ReturnType<typeof render>;
383
- await act(async () => {
384
- renderResult = render(<OperationsTab />);
385
- });
386
- const { container } = renderResult!;
387
-
388
- // 'orders' table has 25% bloat (>10%) — should show badge
389
- expect(container.textContent).toContain('25% bloat');
390
- // 'products' table has 0% bloat — no bloat badge (only one bloat badge total)
391
- const bloatBadges = container.textContent!.match(/\d+% bloat/g) || [];
392
- expect(bloatBadges).toEqual(['25% bloat']);
393
- });
394
-
395
- // =========================================================================
396
- // Session state badge variants
397
- // =========================================================================
398
-
399
- test('renders correct badges for different session states', async () => {
400
- monitoringOverride = {
401
- data: { activeSessions: multiSessions, tables: defaultTables },
402
- };
403
- let renderResult: ReturnType<typeof render>;
404
- await act(async () => {
405
- renderResult = render(<OperationsTab />);
406
- });
407
- const { container } = renderResult!;
408
- const text = container.textContent || '';
409
-
410
- expect(text).toContain('Active');
411
- expect(text).toContain('Idle');
412
- expect(text).toContain('Idle TX');
413
- expect(text).toContain('Abort');
414
- // Default state — 'fastpath function call'
415
- expect(text).toContain('fastpath function call');
416
- });
417
-
418
- // =========================================================================
419
- // Session summary counts
420
- // =========================================================================
421
-
422
- test('shows correct session summary counts', async () => {
423
- monitoringOverride = {
424
- data: { activeSessions: multiSessions, tables: defaultTables },
425
- };
426
- let renderResult: ReturnType<typeof render>;
427
- await act(async () => {
428
- renderResult = render(<OperationsTab />);
429
- });
430
- const { container } = renderResult!;
431
- const text = container.textContent || '';
432
-
433
- // multiSessions: 1 active, 1 idle, 2 idle in tx (one normal, one aborted), 1 waiting
434
- expect(text).toContain('Sessions (5)');
435
- });
436
-
437
- // =========================================================================
438
- // Refresh button
439
- // =========================================================================
440
-
441
- test('refresh button calls refresh', async () => {
442
- let renderResult: ReturnType<typeof render>;
443
- await act(async () => {
444
- renderResult = render(<OperationsTab />);
445
- });
446
- const { queryByText } = renderResult!;
447
-
448
- const refreshBtn = queryByText('Refresh');
449
- expect(refreshBtn).not.toBeNull();
450
- await act(async () => {
451
- fireEvent.click(refreshBtn!.closest('button')!);
452
- });
453
- expect(mockRefresh).toHaveBeenCalledTimes(1);
454
- });
455
-
456
- // =========================================================================
457
- // handleRunMaintenance — success
458
- // =========================================================================
459
-
460
- test('handleRunMaintenance success adds success log entry', async () => {
461
- let renderResult: ReturnType<typeof render>;
462
- await act(async () => {
463
- renderResult = render(<OperationsTab />);
464
- });
465
- const { queryByText } = renderResult!;
466
-
467
- // Click "Run Analyze"
468
- const analyzeBtn = queryByText('Run Analyze');
469
- expect(analyzeBtn).not.toBeNull();
470
- await act(async () => {
471
- fireEvent.click(analyzeBtn!.closest('button')!);
472
- });
473
-
474
- expect(mockRunMaintenance).toHaveBeenCalledWith('analyze', undefined);
475
- // Operation log should appear with success
476
- expect(queryByText('Operation Log (this session)')).not.toBeNull();
477
- expect(queryByText('ANALYZE')).not.toBeNull();
478
- });
479
-
480
- test('handleRunMaintenance vacuum', async () => {
481
- let renderResult: ReturnType<typeof render>;
482
- await act(async () => {
483
- renderResult = render(<OperationsTab />);
484
- });
485
- const { queryByText } = renderResult!;
486
-
487
- const vacuumBtn = queryByText('Run Vacuum');
488
- await act(async () => {
489
- fireEvent.click(vacuumBtn!.closest('button')!);
490
- });
491
- expect(mockRunMaintenance).toHaveBeenCalledWith('vacuum', undefined);
492
- expect(queryByText('VACUUM')).not.toBeNull();
493
- });
494
-
495
- test('handleRunMaintenance reindex', async () => {
496
- let renderResult: ReturnType<typeof render>;
497
- await act(async () => {
498
- renderResult = render(<OperationsTab />);
499
- });
500
- const { queryByText } = renderResult!;
501
-
502
- const reindexBtn = queryByText('Run Reindex');
503
- await act(async () => {
504
- fireEvent.click(reindexBtn!.closest('button')!);
505
- });
506
- expect(mockRunMaintenance).toHaveBeenCalledWith('reindex', undefined);
507
- expect(queryByText('REINDEX')).not.toBeNull();
508
- });
509
-
510
- // =========================================================================
511
- // handleRunMaintenance — failure (returns false)
512
- // =========================================================================
513
-
514
- test('handleRunMaintenance failure shows failure in log', async () => {
515
- mockRunMaintenance.mockImplementation(() => false);
516
- let renderResult: ReturnType<typeof render>;
517
- await act(async () => {
518
- renderResult = render(<OperationsTab />);
519
- });
520
- const { queryByText, container } = renderResult!;
521
-
522
- const analyzeBtn = queryByText('Run Analyze');
523
- await act(async () => {
524
- fireEvent.click(analyzeBtn!.closest('button')!);
525
- });
526
-
527
- expect(queryByText('ANALYZE')).not.toBeNull();
528
- // The log entry should show — the component uses XCircle icon for failure
529
- // We verify the log appears
530
- expect(queryByText('Operation Log (this session)')).not.toBeNull();
531
- // Target is 'all' for global operation
532
- expect(container.textContent).toContain('all');
533
- });
534
-
535
- // =========================================================================
536
- // handleRunMaintenance — exception (catch block)
537
- // =========================================================================
538
-
539
- test('handleRunMaintenance exception adds failure log entry', async () => {
540
- mockRunMaintenance.mockImplementation(() => { throw new Error('DB error'); });
541
- let renderResult: ReturnType<typeof render>;
542
- await act(async () => {
543
- renderResult = render(<OperationsTab />);
544
- });
545
- const { queryByText } = renderResult!;
546
-
547
- const analyzeBtn = queryByText('Run Analyze');
548
- await act(async () => {
549
- fireEvent.click(analyzeBtn!.closest('button')!);
550
- });
551
-
552
- // Log should appear with failure entry
553
- expect(queryByText('Operation Log (this session)')).not.toBeNull();
554
- expect(queryByText('ANALYZE')).not.toBeNull();
555
- });
556
-
557
- // =========================================================================
558
- // handleRunMaintenance — per-table operation
559
- // =========================================================================
560
-
561
- test('handleRunMaintenance for specific table', async () => {
562
- let renderResult: ReturnType<typeof render>;
563
- await act(async () => {
564
- renderResult = render(<OperationsTab />);
565
- });
566
- const { container } = renderResult!;
567
-
568
- // Find the table row for 'users' and click its analyze button (first icon button)
569
- const tableRows = container.querySelectorAll('.divide-y > div');
570
- const usersRow = Array.from(tableRows).find(row => row.textContent?.includes('users'));
571
- expect(usersRow).not.toBeNull();
572
-
573
- const buttons = usersRow!.querySelectorAll('button');
574
- // First button is Analyze, second is Vacuum
575
- expect(buttons.length).toBeGreaterThanOrEqual(2);
576
- await act(async () => {
577
- fireEvent.click(buttons[0]!);
578
- });
579
-
580
- expect(mockRunMaintenance).toHaveBeenCalledWith('analyze', 'users');
581
- });
582
-
583
- test('per-table vacuum button calls runMaintenance with table name', async () => {
584
- let renderResult: ReturnType<typeof render>;
585
- await act(async () => {
586
- renderResult = render(<OperationsTab />);
587
- });
588
- const { container } = renderResult!;
589
-
590
- const tableRows = container.querySelectorAll('.divide-y > div');
591
- const usersRow = Array.from(tableRows).find(row => row.textContent?.includes('users'));
592
- const buttons = usersRow!.querySelectorAll('button');
593
- await act(async () => {
594
- fireEvent.click(buttons[1]!);
595
- });
596
-
597
- expect(mockRunMaintenance).toHaveBeenCalledWith('vacuum', 'users');
598
- });
599
-
600
- // =========================================================================
601
- // Kill session flow
602
- // =========================================================================
603
-
604
- test('kill button opens confirmation dialog', async () => {
605
- let renderResult: ReturnType<typeof render>;
606
- await act(async () => {
607
- renderResult = render(<OperationsTab />);
608
- });
609
- const { container, baseElement } = renderResult!;
610
-
611
- // Find the session row by PID 1234
612
- const cells = container.querySelectorAll('td');
613
- const pidCell = Array.from(cells).find(td => td.textContent?.includes('1234'));
614
- expect(pidCell).not.toBeNull();
615
- const row = pidCell!.closest('tr');
616
- const killBtn = row!.querySelector('td:last-child button');
617
- expect(killBtn).not.toBeNull();
618
-
619
- await act(async () => {
620
- fireEvent.click(killBtn!);
621
- });
622
-
623
- // Confirmation dialog should appear (may be portaled)
624
- const dialogText = baseElement.textContent || '';
625
- expect(dialogText).toContain('Terminate Session?');
626
- expect(dialogText).toContain('1234');
627
- });
628
-
629
- test('confirming kill calls killSession and adds log entry', async () => {
630
- let renderResult: ReturnType<typeof render>;
631
- await act(async () => {
632
- renderResult = render(<OperationsTab />);
633
- });
634
- const { container, baseElement } = renderResult!;
635
-
636
- // Click kill button
637
- const cells = container.querySelectorAll('td');
638
- const pidCell = Array.from(cells).find(td => td.textContent?.includes('1234'));
639
- const row = pidCell!.closest('tr');
640
- const killBtn = row!.querySelector('td:last-child button');
641
- await act(async () => {
642
- fireEvent.click(killBtn!);
643
- });
644
-
645
- // Find and click "Terminate" button in the dialog
646
- const allButtons = baseElement.querySelectorAll('button');
647
- const terminateBtn = Array.from(allButtons).find(btn => btn.textContent?.trim() === 'Terminate');
648
- expect(terminateBtn).not.toBeNull();
649
- await act(async () => {
650
- fireEvent.click(terminateBtn!);
651
- });
652
-
653
- expect(mockKillSession).toHaveBeenCalledWith(1234);
654
- // Log entry should appear
655
- expect(baseElement.textContent).toContain('KILL');
656
- expect(baseElement.textContent).toContain('PID:1234');
657
- });
658
-
659
- test('cancel kill dialog does not call killSession', async () => {
660
- let renderResult: ReturnType<typeof render>;
661
- await act(async () => {
662
- renderResult = render(<OperationsTab />);
663
- });
664
- const { container, baseElement } = renderResult!;
665
-
666
- // Click kill button
667
- const cells = container.querySelectorAll('td');
668
- const pidCell = Array.from(cells).find(td => td.textContent?.includes('1234'));
669
- const row = pidCell!.closest('tr');
670
- const killBtn = row!.querySelector('td:last-child button');
671
- await act(async () => {
672
- fireEvent.click(killBtn!);
673
- });
674
-
675
- // Find and click "Cancel" button
676
- const allButtons = baseElement.querySelectorAll('button');
677
- const cancelBtn = Array.from(allButtons).find(btn => btn.textContent?.trim() === 'Cancel');
678
- expect(cancelBtn).not.toBeNull();
679
- await act(async () => {
680
- fireEvent.click(cancelBtn!);
681
- });
682
-
683
- expect(mockKillSession).not.toHaveBeenCalled();
684
- });
685
-
686
- // =========================================================================
687
- // Session duration badge variants
688
- // =========================================================================
689
-
690
- test('session with >60s shows destructive duration badge', async () => {
691
- monitoringOverride = {
692
- data: {
693
- activeSessions: [
694
- { pid: 200, user: 'u1', state: 'active', query: 'Q', duration: '00:02:00', durationMs: 120000, database: 'dev' },
695
- { pid: 201, user: 'u2', state: 'idle', query: '', duration: '00:00:05', durationMs: 5000, database: 'dev' },
696
- ],
697
- tables: defaultTables,
698
- },
699
- };
700
- let renderResult: ReturnType<typeof render>;
701
- await act(async () => {
702
- renderResult = render(<OperationsTab />);
703
- });
704
- const { container } = renderResult!;
705
- const text = container.textContent || '';
706
- expect(text).toContain('00:02:00');
707
- expect(text).toContain('00:00:05');
708
- });
709
-
710
- // =========================================================================
711
- // Connection selection with saved active ID
712
- // =========================================================================
713
-
714
- test('selects saved active connection on mount', async () => {
715
- mockConnectionsList = [
716
- { id: 'c1', name: 'PG Dev', type: 'postgres', host: 'localhost', port: 5432, database: 'dev', createdAt: new Date() },
717
- { id: 'c2', name: 'MySQL Prod', type: 'mysql', host: 'localhost', port: 3306, database: 'prod', createdAt: new Date() },
718
- ];
719
- mockActiveConnectionId = 'c2';
720
- let renderResult: ReturnType<typeof render>;
721
- await act(async () => {
722
- renderResult = render(<OperationsTab />);
723
- });
724
- const { queryByText } = renderResult!;
725
- // Should show MySQL Prod as selected (savedId matches c2)
726
- expect(queryByText('MySQL Prod')).not.toBeNull();
727
- });
728
-
729
- test('falls back to first connection when savedId not found', async () => {
730
- mockActiveConnectionId = 'nonexistent';
731
- let renderResult: ReturnType<typeof render>;
732
- await act(async () => {
733
- renderResult = render(<OperationsTab />);
734
- });
735
- const { queryByText } = renderResult!;
736
- expect(queryByText('PG Dev')).not.toBeNull();
737
- });
738
-
739
- test('falls back to first connection when no savedId', async () => {
740
- mockActiveConnectionId = null;
741
- let renderResult: ReturnType<typeof render>;
742
- await act(async () => {
743
- renderResult = render(<OperationsTab />);
744
- });
745
- const { queryByText } = renderResult!;
746
- expect(queryByText('PG Dev')).not.toBeNull();
747
- });
748
-
749
- // =========================================================================
750
- // Session with no query shows dash
751
- // =========================================================================
752
-
753
- test('session with no query shows dash', async () => {
754
- monitoringOverride = {
755
- data: {
756
- activeSessions: [
757
- { pid: 300, user: 'admin', state: 'idle', query: '', duration: '00:00:01', durationMs: 1000, database: 'dev' },
758
- ],
759
- tables: defaultTables,
760
- },
761
- };
762
- let renderResult: ReturnType<typeof render>;
763
- await act(async () => {
764
- renderResult = render(<OperationsTab />);
765
- });
766
- const { container } = renderResult!;
767
- // When query is empty, component shows '-'
768
- const cells = container.querySelectorAll('td');
769
- const queryCell = Array.from(cells).find(td => td.textContent?.trim() === '-');
770
- expect(queryCell).not.toBeNull();
771
- });
772
-
773
- // =========================================================================
774
- // Loading skeletons — tables panel
775
- // =========================================================================
776
-
777
- test('shows loading skeletons in tables panel when loading=true and tables empty', async () => {
778
- monitoringOverride = {
779
- data: { activeSessions: defaultSessions, tables: [] },
780
- loading: true,
781
- };
782
- let renderResult: ReturnType<typeof render>;
783
- await act(async () => {
784
- renderResult = render(<OperationsTab />);
785
- });
786
- const { container } = renderResult!;
787
-
788
- // The tables panel shows 5 Skeleton divs when loading && tables.length === 0
789
- const allSkeletons = container.querySelectorAll('[data-slot="skeleton"]');
790
- // Tables panel renders 5 skeletons; sessions panel has data so no skeletons there
791
- expect(allSkeletons.length).toBe(5);
792
- // Tables count header still shows 0
793
- expect(container.textContent).toContain('Tables (0)');
794
- // Sessions should render normally (not skeletons) since sessions have data
795
- expect(container.textContent).toContain('Sessions (1)');
796
- });
797
-
798
- // =========================================================================
799
- // Loading skeletons — sessions panel
800
- // =========================================================================
801
-
802
- test('shows loading skeletons in sessions panel when loading=true and sessions empty', async () => {
803
- monitoringOverride = {
804
- data: { activeSessions: [], tables: defaultTables },
805
- loading: true,
806
- };
807
- let renderResult: ReturnType<typeof render>;
808
- await act(async () => {
809
- renderResult = render(<OperationsTab />);
810
- });
811
- const { container } = renderResult!;
812
-
813
- // The sessions panel shows 4 Skeleton divs when loading && sessions.length === 0
814
- const allSkeletons = container.querySelectorAll('[data-slot="skeleton"]');
815
- expect(allSkeletons.length).toBe(4);
816
- // Sessions count header still shows 0
817
- expect(container.textContent).toContain('Sessions (0)');
818
- // Tables should render normally since tables have data
819
- expect(container.textContent).toContain('Tables (1)');
820
- expect(container.textContent).toContain('users');
821
- });
822
-
823
- // =========================================================================
824
- // handleConnectionChange with non-existent connection id (guard)
825
- // =========================================================================
826
-
827
- test('handleConnectionChange with non-existent id does not change selection', async () => {
828
- // Use a non-existent savedId to test the guard in handleConnectionChange
829
- // When savedId doesn't match any connection, it falls back to first connection
830
- mockConnectionsList = [
831
- { id: 'c1', name: 'PG Dev', type: 'postgres', host: 'localhost', port: 5432, database: 'dev', createdAt: new Date() },
832
- { id: 'c2', name: 'MySQL Prod', type: 'mysql', host: 'localhost', port: 3306, database: 'prod', createdAt: new Date() },
833
- ];
834
- // Set savedId to a non-existent id
835
- mockActiveConnectionId = 'nonexistent-id';
836
- let renderResult: ReturnType<typeof render>;
837
- await act(async () => {
838
- renderResult = render(<OperationsTab />);
839
- });
840
- const { container } = renderResult!;
841
-
842
- // Since savedId doesn't match any connection, the guard falls back to first connection (PG Dev)
843
- expect(container.textContent).toContain('PG Dev');
844
- expect(container.textContent).toContain('(postgres)');
845
-
846
- // The component should not crash and should still render the monitoring data
847
- expect(container.textContent).toContain('Global Operations');
848
- expect(container.textContent).toContain('Sessions');
849
- expect(container.textContent).toContain('Tables');
850
- });
851
-
852
- // =========================================================================
853
- // Session duration badge outline variant (10s-60s range)
854
- // =========================================================================
855
-
856
- test('session with 10s-60s duration shows outline variant badge', async () => {
857
- monitoringOverride = {
858
- data: {
859
- activeSessions: [
860
- { pid: 400, user: 'u1', state: 'active', query: 'SELECT slow()', duration: '00:00:30', durationMs: 30000, database: 'dev' },
861
- { pid: 401, user: 'u2', state: 'idle', query: '', duration: '00:00:05', durationMs: 5000, database: 'dev' },
862
- { pid: 402, user: 'u3', state: 'active', query: 'SELECT very_slow()', duration: '00:02:00', durationMs: 120000, database: 'dev' },
863
- ],
864
- tables: defaultTables,
865
- },
866
- };
867
- let renderResult: ReturnType<typeof render>;
868
- await act(async () => {
869
- renderResult = render(<OperationsTab />);
870
- });
871
- const { container } = renderResult!;
872
-
873
- // Duration badge variant logic:
874
- // PID 400: durationMs=30000 (>10000, <=60000) -> variant="outline"
875
- // PID 401: durationMs=5000 (<=10000) -> variant="secondary"
876
- // PID 402: durationMs=120000 (>60000) -> variant="destructive"
877
-
878
- // Badge component renders as <span data-slot="badge">
879
- const allBadges = Array.from(container.querySelectorAll('span[data-slot="badge"]'));
880
-
881
- // Find the badge containing '00:00:30' (outline variant for 10s-60s range)
882
- const durationBadge400 = allBadges.find(
883
- badge => badge.textContent?.includes('00:00:30')
884
- );
885
- expect(durationBadge400).toBeDefined();
886
- // Outline variant: has text-foreground, does NOT have bg-destructive or bg-secondary
887
- expect(durationBadge400!.className).toContain('text-foreground');
888
- expect(durationBadge400!.className).not.toContain('bg-destructive');
889
- expect(durationBadge400!.className).not.toContain('bg-secondary');
890
- expect(durationBadge400!.className).not.toContain('border-transparent');
891
-
892
- // Find the badge containing '00:02:00' (destructive variant for >60s)
893
- const durationBadge402 = allBadges.find(
894
- badge => badge.textContent?.includes('00:02:00')
895
- );
896
- expect(durationBadge402).toBeDefined();
897
- expect(durationBadge402!.className).toContain('bg-destructive');
898
-
899
- // Find the badge containing '00:00:05' (secondary variant for <=10s)
900
- const durationBadge401 = allBadges.find(
901
- badge => badge.textContent?.includes('00:00:05')
902
- );
903
- expect(durationBadge401).toBeDefined();
904
- expect(durationBadge401!.className).toContain('bg-secondary');
905
- });
906
-
907
- // =========================================================================
908
- // Kill dialog shows user and state in description
909
- // =========================================================================
910
-
911
- test('kill dialog shows user and state in description', async () => {
912
- monitoringOverride = {
913
- data: {
914
- activeSessions: [
915
- { pid: 500, user: 'db_admin', state: 'idle in transaction', query: 'UPDATE t SET x=1', duration: '00:05:00', durationMs: 300000, database: 'dev' },
916
- ],
917
- tables: defaultTables,
918
- },
919
- };
920
- let renderResult: ReturnType<typeof render>;
921
- await act(async () => {
922
- renderResult = render(<OperationsTab />);
923
- });
924
- const { container, baseElement } = renderResult!;
925
-
926
- // Find and click the kill button for PID 500
927
- const cells = container.querySelectorAll('td');
928
- const pidCell = Array.from(cells).find(td => td.textContent?.includes('500'));
929
- expect(pidCell).not.toBeNull();
930
- const row = pidCell!.closest('tr');
931
- const killBtn = row!.querySelector('td:last-child button');
932
- expect(killBtn).not.toBeNull();
933
-
934
- await act(async () => {
935
- fireEvent.click(killBtn!);
936
- });
937
-
938
- // Dialog should be open and show user and state info
939
- const dialogText = baseElement.textContent || '';
940
- expect(dialogText).toContain('Terminate Session?');
941
- expect(dialogText).toContain('500');
942
- // User is shown in the description
943
- expect(dialogText).toContain('db_admin');
944
- // State is shown in the description
945
- expect(dialogText).toContain('idle in transaction');
946
- // Also verify the warning about uncommitted transactions
947
- expect(dialogText).toContain('uncommitted transactions');
948
- });
949
-
950
- // =========================================================================
951
- // Error hidden when both error AND data present
952
- // =========================================================================
953
-
954
- test('error message is hidden when both error and data are present', async () => {
955
- monitoringOverride = {
956
- data: { activeSessions: defaultSessions, tables: defaultTables },
957
- error: 'Intermittent connection error',
958
- };
959
- let renderResult: ReturnType<typeof render>;
960
- await act(async () => {
961
- renderResult = render(<OperationsTab />);
962
- });
963
- const { queryByText, container } = renderResult!;
964
-
965
- // The error message should NOT be displayed because data is present
966
- // (source code: `error && !data` — data is truthy, so error div is skipped)
967
- expect(queryByText('Intermittent connection error')).toBeNull();
968
-
969
- // But data should still render normally
970
- expect(queryByText('Sessions (1)')).not.toBeNull();
971
- expect(queryByText('Tables (1)')).not.toBeNull();
972
- expect(container.textContent).toContain('users');
973
- expect(container.textContent).toContain('1234');
974
- });
975
- });