insforge 1.2.10 → 1.3.0

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 (335) hide show
  1. package/.claude-plugin/marketplace.json +20 -20
  2. package/.dockerignore +60 -60
  3. package/.env.example +83 -77
  4. package/.github/ISSUE_TEMPLATE/bug_report.yml +36 -36
  5. package/.github/ISSUE_TEMPLATE/config.yml +11 -11
  6. package/.github/ISSUE_TEMPLATE/feature_request.yml +26 -26
  7. package/.github/PULL_REQUEST_TEMPLATE.md +7 -7
  8. package/.github/copilot-instructions.md +146 -146
  9. package/.github/workflows/build-image.yml +65 -65
  10. package/.github/workflows/ci-premerge-check.yml +23 -23
  11. package/.github/workflows/e2e.yml +63 -63
  12. package/.github/workflows/lint-and-format.yml +32 -32
  13. package/.prettierignore +64 -64
  14. package/CHANGELOG.md +44 -44
  15. package/CLAUDE_PLUGIN.md +104 -104
  16. package/CODE_OF_CONDUCT.md +128 -128
  17. package/CONTRIBUTING.md +125 -125
  18. package/Dockerfile +30 -30
  19. package/GITHUB_OAUTH_SETUP.md +49 -49
  20. package/GOOGLE_OAUTH_SETUP.md +148 -148
  21. package/LICENSE +201 -201
  22. package/README.md +182 -182
  23. package/assets/Dark.svg +23 -23
  24. package/auth/package.json +28 -28
  25. package/auth/src/lib/broadcastService.ts +117 -115
  26. package/auth/src/pages/SignInPage.tsx +60 -57
  27. package/auth/src/pages/SignUpPage.tsx +60 -57
  28. package/auth/tsconfig.json +32 -32
  29. package/auth/tsconfig.node.json +11 -11
  30. package/backend/package.json +78 -75
  31. package/backend/src/api/routes/ai/index.routes.ts +3 -3
  32. package/backend/src/api/routes/auth/index.routes.ts +667 -570
  33. package/backend/src/api/routes/auth/oauth.routes.ts +473 -448
  34. package/backend/src/api/routes/database/advance.routes.ts +37 -16
  35. package/backend/src/api/routes/database/index.routes.ts +78 -1
  36. package/backend/src/api/routes/database/records.routes.ts +10 -10
  37. package/backend/src/api/routes/database/tables.routes.ts +0 -14
  38. package/backend/src/api/routes/docs/index.routes.ts +75 -76
  39. package/backend/src/api/routes/email/index.routes.ts +35 -0
  40. package/backend/src/api/routes/functions/index.routes.ts +18 -12
  41. package/backend/src/api/routes/metadata/index.routes.ts +12 -0
  42. package/backend/src/api/routes/realtime/channels.routes.ts +81 -0
  43. package/backend/src/api/routes/realtime/index.routes.ts +12 -0
  44. package/backend/src/api/routes/realtime/messages.routes.ts +48 -0
  45. package/backend/src/api/routes/realtime/permissions.routes.ts +19 -0
  46. package/backend/src/api/routes/storage/index.routes.ts +18 -12
  47. package/backend/src/api/routes/usage/index.routes.ts +6 -4
  48. package/backend/src/infra/database/database.manager.ts +14 -1
  49. package/backend/src/infra/database/migrations/000_create-base-tables.sql +141 -141
  50. package/backend/src/infra/database/migrations/001_create-helper-functions.sql +40 -40
  51. package/backend/src/infra/database/migrations/002_rename-auth-tables.sql +29 -29
  52. package/backend/src/infra/database/migrations/003_create-users-table.sql +55 -55
  53. package/backend/src/infra/database/migrations/004_add-reload-postgrest-func.sql +23 -23
  54. package/backend/src/infra/database/migrations/005_enable-project-admin-modify-users.sql +29 -29
  55. package/backend/src/infra/database/migrations/006_modify-ai-usage-table.sql +24 -24
  56. package/backend/src/infra/database/migrations/007_drop-metadata-table.sql +1 -1
  57. package/backend/src/infra/database/migrations/008_add-system-tables.sql +76 -76
  58. package/backend/src/infra/database/migrations/009_add-function-secrets.sql +23 -23
  59. package/backend/src/infra/database/migrations/010_modify-ai-config-modalities.sql +93 -93
  60. package/backend/src/infra/database/migrations/011_refactor-secrets-table.sql +15 -15
  61. package/backend/src/infra/database/migrations/012_add-storage-uploaded-by.sql +7 -7
  62. package/backend/src/infra/database/migrations/013_create-auth-schema-functions.sql +44 -44
  63. package/backend/src/infra/database/migrations/014_add-updated-at-trigger-user-table.sql +7 -7
  64. package/backend/src/infra/database/migrations/015_create-auth-config-and-email-otp-tables.sql +59 -59
  65. package/backend/src/infra/database/migrations/016_update-auth-config-and-email-otp.sql +24 -24
  66. package/backend/src/infra/database/migrations/017_create-realtime-schema.sql +233 -0
  67. package/backend/src/infra/realtime/realtime.manager.ts +246 -0
  68. package/backend/src/infra/realtime/webhook-sender.ts +82 -0
  69. package/backend/src/infra/security/token.manager.ts +219 -125
  70. package/backend/src/infra/socket/socket.manager.ts +198 -64
  71. package/backend/src/providers/ai/openrouter.provider.ts +12 -9
  72. package/backend/src/providers/email/base.provider.ts +4 -7
  73. package/backend/src/providers/email/cloud.provider.ts +84 -0
  74. package/backend/src/providers/oauth/apple.provider.ts +266 -0
  75. package/backend/src/providers/oauth/index.ts +1 -0
  76. package/backend/src/server.ts +317 -284
  77. package/backend/src/services/ai/ai-model.service.ts +5 -5
  78. package/backend/src/services/ai/chat-completion.service.ts +4 -4
  79. package/backend/src/services/ai/image-generation.service.ts +3 -3
  80. package/backend/src/services/auth/auth.service.ts +14 -0
  81. package/backend/src/services/database/database-table.service.ts +0 -9
  82. package/backend/src/services/database/database.service.ts +127 -0
  83. package/backend/src/services/email/email.service.ts +5 -7
  84. package/backend/src/services/realtime/index.ts +3 -0
  85. package/backend/src/services/realtime/realtime-auth.service.ts +104 -0
  86. package/backend/src/services/realtime/realtime-channel.service.ts +237 -0
  87. package/backend/src/services/realtime/realtime-message.service.ts +260 -0
  88. package/backend/src/types/auth.ts +11 -0
  89. package/backend/src/types/realtime.ts +18 -0
  90. package/backend/src/types/socket.ts +7 -31
  91. package/backend/src/utils/cookies.ts +35 -0
  92. package/backend/src/utils/s3-config-loader.ts +64 -0
  93. package/backend/src/utils/seed.ts +301 -298
  94. package/backend/src/utils/sql-parser.ts +90 -0
  95. package/backend/tests/README.md +133 -133
  96. package/backend/tests/cleanup-all-test-data.sh +230 -230
  97. package/backend/tests/cloud/test-s3-multitenant.sh +131 -131
  98. package/backend/tests/local/comprehensive-curl-tests.sh +155 -155
  99. package/backend/tests/local/test-ai-config.sh +129 -129
  100. package/backend/tests/local/test-ai-usage.sh +80 -80
  101. package/backend/tests/local/test-auth-router.sh +143 -143
  102. package/backend/tests/local/test-database-router.sh +222 -222
  103. package/backend/tests/local/test-e2e.sh +240 -240
  104. package/backend/tests/local/test-fk-errors.sh +96 -96
  105. package/backend/tests/local/test-functions.sh +123 -123
  106. package/backend/tests/local/test-id-field.sh +200 -200
  107. package/backend/tests/local/test-logs.sh +132 -132
  108. package/backend/tests/local/test-public-bucket.sh +264 -264
  109. package/backend/tests/local/test-secrets.sh +249 -249
  110. package/backend/tests/local/test-serverless-functions.sh.disabled +325 -325
  111. package/backend/tests/local/test-traditional-rest.sh +208 -208
  112. package/backend/tests/manual/README.md +50 -50
  113. package/backend/tests/manual/create-large-table-simple.sql +10 -10
  114. package/backend/tests/manual/seed-large-table.sql +100 -100
  115. package/backend/tests/manual/setup-large-table-extras.sql +33 -33
  116. package/backend/tests/manual/test-bulk-upsert.sh +409 -409
  117. package/backend/tests/manual/test-database-advance.sh +296 -296
  118. package/backend/tests/manual/test-postgrest-stability.sh +191 -191
  119. package/backend/tests/manual/test-rawsql-export-import.sh +411 -411
  120. package/backend/tests/manual/test-rawsql-modes.sh +244 -244
  121. package/backend/tests/manual/test-universal-storage.sh +263 -263
  122. package/backend/tests/manual/test-users.sql +17 -17
  123. package/backend/tests/run-all-tests.sh +139 -139
  124. package/backend/tests/setup.ts +0 -0
  125. package/backend/tests/test-config.sh +338 -338
  126. package/backend/tests/unit/analyze-query.test.ts +697 -0
  127. package/backend/tsconfig.json +22 -22
  128. package/claude-plugin/.claude-plugin/plugin.json +24 -24
  129. package/claude-plugin/README.md +133 -133
  130. package/claude-plugin/skills/insforge-schema-patterns/SKILL.md +270 -270
  131. package/docker-compose.prod.yml +204 -200
  132. package/docker-compose.yml +232 -228
  133. package/docker-init/db/db-init.sql +97 -97
  134. package/docker-init/db/jwt.sql +5 -5
  135. package/docker-init/db/postgresql.conf +16 -16
  136. package/docker-init/logs/vector.yml +236 -236
  137. package/docs/README.md +44 -44
  138. package/docs/agent-docs/real-time.md +269 -0
  139. package/docs/changelog.mdx +119 -67
  140. package/docs/core-concepts/ai/architecture.mdx +372 -372
  141. package/docs/core-concepts/ai/sdk.mdx +213 -213
  142. package/docs/core-concepts/authentication/architecture.mdx +278 -278
  143. package/docs/core-concepts/authentication/sdk.mdx +414 -414
  144. package/docs/core-concepts/authentication/ui-components/customization.mdx +529 -529
  145. package/docs/core-concepts/authentication/ui-components/nextjs.mdx +221 -221
  146. package/docs/core-concepts/authentication/ui-components/react-router.mdx +184 -184
  147. package/docs/core-concepts/authentication/ui-components/react.mdx +129 -129
  148. package/docs/core-concepts/database/architecture.mdx +255 -255
  149. package/docs/core-concepts/database/sdk.mdx +382 -382
  150. package/docs/core-concepts/email/architecture.mdx +101 -0
  151. package/docs/core-concepts/email/sdk.mdx +53 -0
  152. package/docs/core-concepts/functions/architecture.mdx +105 -105
  153. package/docs/core-concepts/functions/sdk.mdx +184 -184
  154. package/docs/core-concepts/realtime/architecture.mdx +446 -0
  155. package/docs/core-concepts/realtime/sdk.mdx +409 -0
  156. package/docs/core-concepts/storage/architecture.mdx +243 -243
  157. package/docs/core-concepts/storage/sdk.mdx +253 -253
  158. package/docs/deployment/README.md +94 -94
  159. package/docs/deployment/deploy-to-aws-ec2.md +564 -564
  160. package/docs/deployment/deploy-to-azure-virtual-machines.md +312 -312
  161. package/docs/deployment/deploy-to-google-cloud-compute-engine.md +613 -613
  162. package/docs/deployment/deploy-to-render.md +441 -441
  163. package/docs/deprecated/insforge-auth-api.md +214 -214
  164. package/docs/deprecated/insforge-auth-sdk.md +99 -99
  165. package/docs/deprecated/insforge-db-api.md +358 -358
  166. package/docs/deprecated/insforge-db-sdk.md +139 -139
  167. package/docs/deprecated/insforge-debug-sdk.md +156 -156
  168. package/docs/deprecated/insforge-debug.md +64 -64
  169. package/docs/deprecated/insforge-instructions.md +123 -123
  170. package/docs/deprecated/insforge-project.md +117 -117
  171. package/docs/deprecated/insforge-storage-api.md +278 -278
  172. package/docs/deprecated/insforge-storage-sdk.md +158 -158
  173. package/docs/docs.json +232 -210
  174. package/docs/examples/framework-guides/nextjs.mdx +131 -131
  175. package/docs/examples/framework-guides/nuxt.mdx +165 -165
  176. package/docs/examples/framework-guides/react.mdx +165 -165
  177. package/docs/examples/framework-guides/svelte.mdx +153 -153
  178. package/docs/examples/framework-guides/vue.mdx +159 -159
  179. package/docs/examples/overview.mdx +67 -67
  180. package/docs/favicon.svg +19 -19
  181. package/docs/images/changelog/dec-2025/ai-integration.png +0 -0
  182. package/docs/images/changelog/dec-2025/ai-models.webp +0 -0
  183. package/docs/images/changelog/dec-2025/alipay-payment.webp +0 -0
  184. package/docs/images/changelog/dec-2025/apple-login.jpg +0 -0
  185. package/docs/images/changelog/dec-2025/mcp-installer.png +0 -0
  186. package/docs/images/changelog/dec-2025/realtime-module.jpg +0 -0
  187. package/docs/images/icons/ai.svg +4 -4
  188. package/docs/images/logos/nextjs.svg +4 -4
  189. package/docs/images/logos/nuxt.svg +4 -4
  190. package/docs/images/logos/react.svg +5 -5
  191. package/docs/images/logos/svelte.svg +4 -4
  192. package/docs/images/logos/vue.svg +5 -5
  193. package/docs/insforge-instructions-sdk.md +89 -88
  194. package/docs/introduction.mdx +45 -45
  195. package/docs/logo/dark.svg +22 -22
  196. package/docs/logo/light.svg +20 -20
  197. package/docs/partnership.mdx +651 -646
  198. package/docs/quickstart.mdx +82 -82
  199. package/docs/showcase.mdx +52 -52
  200. package/docs/snippets/sdk-installation.mdx +21 -21
  201. package/docs/snippets/service-icons.mdx +27 -27
  202. package/examples/oauth/frontend-oauth-example.html +250 -250
  203. package/examples/response-examples.md +443 -443
  204. package/frontend/components.json +17 -17
  205. package/frontend/package.json +69 -69
  206. package/frontend/src/assets/icons/checkbox_checked.svg +6 -6
  207. package/frontend/src/assets/icons/checkbox_undetermined.svg +6 -6
  208. package/frontend/src/assets/icons/checked.svg +3 -3
  209. package/frontend/src/assets/icons/connected.svg +3 -3
  210. package/frontend/src/assets/icons/error.svg +3 -3
  211. package/frontend/src/assets/icons/loader.svg +9 -9
  212. package/frontend/src/assets/icons/pencil.svg +4 -4
  213. package/frontend/src/assets/icons/refresh.svg +4 -4
  214. package/frontend/src/assets/icons/step_active.svg +3 -3
  215. package/frontend/src/assets/icons/step_inactive.svg +11 -11
  216. package/frontend/src/assets/icons/warning.svg +3 -3
  217. package/frontend/src/assets/logos/apple.svg +3 -3
  218. package/frontend/src/assets/logos/claude_code.svg +3 -3
  219. package/frontend/src/assets/logos/cline.svg +6 -6
  220. package/frontend/src/assets/logos/cursor.svg +20 -20
  221. package/frontend/src/assets/logos/discord.svg +8 -8
  222. package/frontend/src/assets/logos/facebook.svg +3 -3
  223. package/frontend/src/assets/logos/gemini.svg +19 -19
  224. package/frontend/src/assets/logos/github.svg +5 -5
  225. package/frontend/src/assets/logos/google.svg +13 -13
  226. package/frontend/src/assets/logos/grok.svg +10 -10
  227. package/frontend/src/assets/logos/insforge_dark.svg +15 -15
  228. package/frontend/src/assets/logos/insforge_light.svg +15 -15
  229. package/frontend/src/assets/logos/instagram.svg +1 -1
  230. package/frontend/src/assets/logos/linkedin.svg +3 -3
  231. package/frontend/src/assets/logos/openai.svg +10 -10
  232. package/frontend/src/assets/logos/roo_code.svg +9 -9
  233. package/frontend/src/assets/logos/spotify.svg +16 -16
  234. package/frontend/src/assets/logos/tiktok.svg +5 -5
  235. package/frontend/src/assets/logos/trae.svg +3 -3
  236. package/frontend/src/assets/logos/windsurf.svg +10 -10
  237. package/frontend/src/assets/logos/x.svg +3 -3
  238. package/frontend/src/components/layout/AppHeader.tsx +9 -10
  239. package/frontend/src/features/auth/components/OAuthConfigDialog.tsx +1 -0
  240. package/frontend/src/features/auth/components/UsersDataGrid.tsx +6 -0
  241. package/frontend/src/features/auth/helpers.tsx +8 -0
  242. package/frontend/src/features/auth/{page → pages}/UsersPage.tsx +0 -28
  243. package/frontend/src/features/database/components/SQLModal.tsx +75 -0
  244. package/frontend/src/features/database/components/TableForm.tsx +0 -4
  245. package/frontend/src/features/database/hooks/useDatabase.ts +66 -0
  246. package/frontend/src/features/database/hooks/useTables.ts +32 -28
  247. package/frontend/src/features/database/index.ts +1 -0
  248. package/frontend/src/features/database/{page → pages}/FunctionsPage.tsx +29 -37
  249. package/frontend/src/features/database/{page → pages}/IndexesPage.tsx +35 -47
  250. package/frontend/src/features/database/{page → pages}/PoliciesPage.tsx +43 -54
  251. package/frontend/src/features/database/{page → pages}/TablesPage.tsx +0 -42
  252. package/frontend/src/features/database/{page → pages}/TriggersPage.tsx +35 -47
  253. package/frontend/src/features/database/services/advance.service.ts +0 -26
  254. package/frontend/src/features/database/services/database.service.ts +55 -0
  255. package/frontend/src/features/database/services/table.service.ts +0 -6
  256. package/frontend/src/features/functions/{page → pages}/FunctionsPage.tsx +21 -44
  257. package/frontend/src/features/functions/{page → pages}/SecretsPage.tsx +11 -9
  258. package/frontend/src/features/logs/hooks/useMcpUsage.ts +13 -66
  259. package/frontend/src/features/realtime/components/ChannelRow.tsx +83 -0
  260. package/frontend/src/features/realtime/components/EditChannelModal.tsx +246 -0
  261. package/frontend/src/features/realtime/components/MessageRow.tsx +85 -0
  262. package/frontend/src/features/realtime/components/RealtimeEmptyState.tsx +30 -0
  263. package/frontend/src/features/realtime/hooks/useRealtime.ts +218 -0
  264. package/frontend/src/features/realtime/index.ts +11 -0
  265. package/frontend/src/features/realtime/pages/RealtimeChannelsPage.tsx +172 -0
  266. package/frontend/src/features/realtime/pages/RealtimeMessagesPage.tsx +211 -0
  267. package/frontend/src/features/realtime/pages/RealtimePermissionsPage.tsx +191 -0
  268. package/frontend/src/features/realtime/services/realtime.service.ts +107 -0
  269. package/frontend/src/features/storage/{page → pages}/StoragePage.tsx +1 -29
  270. package/frontend/src/features/visualizer/components/SchemaVisualizer.tsx +3 -3
  271. package/frontend/src/features/visualizer/{page → pages}/VisualizerPage.tsx +1 -35
  272. package/frontend/src/lib/contexts/SocketContext.tsx +119 -75
  273. package/frontend/src/lib/routing/AppRoutes.tsx +35 -20
  274. package/frontend/src/lib/utils/cloudMessaging.ts +1 -1
  275. package/frontend/src/lib/utils/menuItems.ts +24 -0
  276. package/frontend/src/lib/utils/utils.ts +14 -1
  277. package/frontend/tsconfig.json +25 -25
  278. package/frontend/tsconfig.node.json +9 -9
  279. package/functions/deno.json +24 -24
  280. package/functions/server.ts +315 -315
  281. package/i18n/README.ar.md +130 -130
  282. package/i18n/README.de.md +130 -130
  283. package/i18n/README.es.md +154 -154
  284. package/i18n/README.fr.md +134 -134
  285. package/i18n/README.hi.md +129 -129
  286. package/i18n/README.ja.md +174 -174
  287. package/i18n/README.ko.md +136 -136
  288. package/i18n/README.pt-BR.md +131 -131
  289. package/i18n/README.ru.md +129 -129
  290. package/i18n/README.zh-CN.md +133 -133
  291. package/openapi/ai.yaml +715 -715
  292. package/openapi/auth.yaml +1244 -1244
  293. package/openapi/email.yaml +158 -0
  294. package/openapi/functions.yaml +475 -475
  295. package/openapi/health.yaml +29 -29
  296. package/openapi/logs.yaml +223 -223
  297. package/openapi/metadata.yaml +177 -177
  298. package/openapi/realtime.yaml +699 -0
  299. package/openapi/records.yaml +381 -381
  300. package/openapi/secrets.yaml +370 -370
  301. package/openapi/storage.yaml +875 -875
  302. package/openapi/tables.yaml +463 -463
  303. package/package.json +97 -97
  304. package/shared-schemas/package.json +31 -31
  305. package/shared-schemas/src/ai.schema.ts +63 -59
  306. package/shared-schemas/src/auth-api.schema.ts +352 -339
  307. package/shared-schemas/src/auth.schema.ts +1 -1
  308. package/shared-schemas/src/database-api.schema.ts +32 -1
  309. package/shared-schemas/src/database.schema.ts +39 -0
  310. package/shared-schemas/src/docs.schema.ts +26 -0
  311. package/shared-schemas/src/email-api.schema.ts +30 -0
  312. package/shared-schemas/src/index.ts +4 -0
  313. package/shared-schemas/src/metadata.schema.ts +9 -0
  314. package/shared-schemas/src/realtime-api.schema.ts +111 -0
  315. package/shared-schemas/src/realtime.schema.ts +143 -0
  316. package/shared-schemas/tsconfig.json +21 -21
  317. package/tsconfig.json +7 -7
  318. package/zeabur/README.md +13 -13
  319. package/zeabur/template.yml +1032 -1032
  320. package/.cursor/rules/cursor-rules.mdc +0 -94
  321. package/frontend/src/features/database/hooks/useFullMetadata.ts +0 -18
  322. package/test-gemini.sh +0 -35
  323. package/test-usage-admin.sh +0 -57
  324. package/test-usage.sh +0 -50
  325. /package/frontend/src/features/ai/{page → pages}/AIPage.tsx +0 -0
  326. /package/frontend/src/features/auth/{page → pages}/AuthMethodsPage.tsx +0 -0
  327. /package/frontend/src/features/auth/{page → pages}/ConfigurationPage.tsx +0 -0
  328. /package/frontend/src/features/dashboard/{page → pages}/DashboardPage.tsx +0 -0
  329. /package/frontend/src/features/database/{page → pages}/SQLEditorPage.tsx +0 -0
  330. /package/frontend/src/features/database/{page → pages}/TemplatesPage.tsx +0 -0
  331. /package/frontend/src/features/login/{page → pages}/CloudLoginPage.tsx +0 -0
  332. /package/frontend/src/features/login/{page → pages}/LoginPage.tsx +0 -0
  333. /package/frontend/src/features/logs/{page → pages}/AuditsPage.tsx +0 -0
  334. /package/frontend/src/features/logs/{page → pages}/LogsPage.tsx +0 -0
  335. /package/frontend/src/features/logs/{page → pages}/MCPLogsPage.tsx +0 -0
@@ -1,339 +1,339 @@
1
- #!/bin/bash
2
-
3
- # Test configuration file
4
- # Source this file in test scripts to get consistent configuration
5
-
6
- # API Configuration
7
- export TEST_API_BASE="${TEST_API_BASE:-http://localhost:7130/api}"
8
-
9
- # Admin credentials - can be overridden by environment variables
10
- export TEST_ADMIN_EMAIL="${TEST_ADMIN_EMAIL:-${ADMIN_EMAIL:-admin@example.com}}"
11
- export TEST_ADMIN_PASSWORD="${TEST_ADMIN_PASSWORD:-${ADMIN_PASSWORD:-change-this-password}}"
12
-
13
- # User test credentials
14
- export TEST_USER_EMAIL_PREFIX="${TEST_USER_EMAIL_PREFIX:-testuser_}"
15
-
16
- # Colors for output
17
- export RED='\033[0;31m'
18
- export GREEN='\033[0;32m'
19
- export YELLOW='\033[1;33m'
20
- export BLUE='\033[0;34m'
21
- export NC='\033[0m' # No Color
22
-
23
- # Utility functions
24
- print_success() {
25
- echo -e "${GREEN}✅ $1${NC}"
26
- }
27
-
28
- print_fail() {
29
- echo -e "${RED}❌ $1${NC}"
30
- track_test_failure
31
- }
32
-
33
- print_info() {
34
- echo -e "${YELLOW}$1${NC}"
35
- }
36
-
37
- print_blue() {
38
- echo -e "${BLUE}$1${NC}"
39
- }
40
-
41
- # Function to login as admin and get token
42
- get_admin_token() {
43
- # Use JWT admin endpoint
44
- local response=$(curl -s -X POST "$TEST_API_BASE/auth/admin/sessions" \
45
- -H "Content-Type: application/json" \
46
- -d "{\"email\":\"$TEST_ADMIN_EMAIL\",\"password\":\"$TEST_ADMIN_PASSWORD\"}")
47
-
48
- if echo "$response" | grep -q '"accessToken"'; then
49
- echo "$response" | grep -o '"accessToken":"[^"]*"' | cut -d'"' -f4
50
- else
51
- echo ""
52
- fi
53
- }
54
-
55
- # Function to get admin API key
56
- get_admin_api_key() {
57
- # Only use ACCESS_API_KEY environment variable
58
- if [ -n "$ACCESS_API_KEY" ]; then
59
- echo "$ACCESS_API_KEY"
60
- else
61
- echo ""
62
- fi
63
- }
64
-
65
- # Check if required tools are installed
66
- check_requirements() {
67
- if ! command -v curl &> /dev/null; then
68
- print_fail "curl is required but not installed"
69
- exit 1
70
- fi
71
-
72
- if ! command -v jq &> /dev/null; then
73
- print_info "jq is recommended for JSON parsing"
74
- fi
75
- }
76
-
77
- # Array to track test tables created
78
- declare -a TEST_TABLES_CREATED=()
79
-
80
- # Array to track test users created
81
- declare -a TEST_USERS_CREATED=()
82
-
83
- # Array to track test buckets created
84
- declare -a TEST_BUCKETS_CREATED=()
85
-
86
- # Array to track test AI configs created
87
- declare -a TEST_AI_CONFIGS_CREATED=()
88
-
89
- # Function to register a table for cleanup
90
- register_test_table() {
91
- local table_name=$1
92
- TEST_TABLES_CREATED+=("$table_name")
93
- }
94
-
95
- # Function to register a user for cleanup
96
- register_test_user() {
97
- local user_email=$1
98
- TEST_USERS_CREATED+=("$user_email")
99
- }
100
-
101
- # Function to register a bucket for cleanup
102
- register_test_bucket() {
103
- local bucket_name=$1
104
- TEST_BUCKETS_CREATED+=("$bucket_name")
105
- }
106
-
107
- # Function to register an AI config for cleanup
108
- register_test_ai_config() {
109
- local config_id=$1
110
- TEST_AI_CONFIGS_CREATED+=("$config_id")
111
- }
112
-
113
- # Function to register a user with Better Auth
114
- register_user() {
115
- local email=$1
116
- local password=$2
117
- local name=${3:-"Test User"}
118
-
119
- # Use JWT registration
120
- curl -s -X POST "$TEST_API_BASE/auth/users" \
121
- -H "Content-Type: application/json" \
122
- -d "{\"email\":\"$email\",\"password\":\"$password\",\"name\":\"$name\"}"
123
- }
124
-
125
- # Function to login a user with Better Auth
126
- login_user() {
127
- local email=$1
128
- local password=$2
129
-
130
- # Use JWT login
131
- curl -s -X POST "$TEST_API_BASE/auth/sessions" \
132
- -H "Content-Type: application/json" \
133
- -d "{\"email\":\"$email\",\"password\":\"$password\"}"
134
- }
135
-
136
- # Function to get user profile with auth token
137
- get_user_profile() {
138
- local token=$1
139
-
140
- # Use JWT profile endpoint
141
- curl -s -X GET "$TEST_API_BASE/auth/sessions/current" \
142
- -H "Authorization: Bearer $token"
143
- }
144
-
145
- # Function to delete a table
146
- delete_table() {
147
- local table_name=$1
148
- local token=$2
149
-
150
- curl -s -X DELETE "$TEST_API_BASE/database/tables/$table_name" \
151
- -H "Authorization: Bearer $token" \
152
- -H "Content-Type: application/json" > /dev/null 2>&1
153
- }
154
-
155
- # Function to cleanup all test data
156
- cleanup_test_data() {
157
- # Always attempt cleanup, even if some parts fail
158
- print_info "🧹 Cleaning up test data..."
159
-
160
- local cleanup_failed=0
161
-
162
- # Try to get credentials - but continue cleanup even if they fail
163
- local admin_token=$(get_admin_token 2>/dev/null || echo "")
164
- local api_key=$(get_admin_api_key 2>/dev/null || echo "")
165
-
166
- if [ -z "$admin_token" ]; then
167
- print_info "No admin token available - some cleanup may be limited"
168
- fi
169
-
170
- if [ -z "$api_key" ]; then
171
- print_info "No API key available - bucket cleanup may be limited"
172
- fi
173
-
174
- # 1. Delete all registered test tables
175
- if [ ${#TEST_TABLES_CREATED[@]} -gt 0 ]; then
176
- if [ -n "$admin_token" ]; then
177
- print_info "Deleting test tables..."
178
- for table in "${TEST_TABLES_CREATED[@]}"; do
179
- print_info " - Deleting table: $table"
180
- if ! delete_table "$table" "$admin_token"; then
181
- echo " ✗ Failed to delete"
182
- cleanup_failed=1
183
- else
184
- echo " ✓ Deleted"
185
- fi
186
- done
187
- else
188
- print_fail "Cannot delete tables without admin token"
189
- print_info "Tables to delete manually:"
190
- for table in "${TEST_TABLES_CREATED[@]}"; do
191
- echo " - $table"
192
- done
193
- cleanup_failed=1
194
- fi
195
- fi
196
-
197
- # 2. Delete all test users
198
- if [ ${#TEST_USERS_CREATED[@]} -gt 0 ] && [ -n "$admin_token" ]; then
199
- print_info "Deleting test users..."
200
-
201
- # Get all users to find IDs of test users
202
- local users_response=$(curl -s -X GET "$TEST_API_BASE/auth/users?limit=100" \
203
- -H "Authorization: Bearer $admin_token" \
204
- -H "Content-Type: application/json" 2>/dev/null || echo "")
205
-
206
- if [ -n "$users_response" ] && echo "$users_response" | grep -q '"data"'; then
207
- local user_ids=()
208
-
209
- # Extract data array from response (new format uses "data" instead of "users")
210
- local users_json=$(echo "$users_response" | grep -o '"data":\[[^]]*\]' | sed 's/"data"://')
211
-
212
- # Find IDs of test users by email
213
- for test_email in "${TEST_USERS_CREATED[@]}"; do
214
- local user_id=$(echo "$users_json" | grep -B2 -A2 "\"email\":\"$test_email\"" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
215
- if [ -n "$user_id" ]; then
216
- user_ids+=("$user_id")
217
- print_info " - Found test user: $test_email (ID: $user_id)"
218
- fi
219
- done
220
-
221
- # Bulk delete test users
222
- if [ ${#user_ids[@]} -gt 0 ]; then
223
- local delete_response=$(curl -s -X DELETE "$TEST_API_BASE/auth/users" \
224
- -H "Authorization: Bearer $admin_token" \
225
- -H "Content-Type: application/json" \
226
- -d "{\"userIds\": [$(printf '"%s",' "${user_ids[@]}" | sed 's/,$//' )]}")
227
-
228
- if ! echo "$delete_response" | grep -q '"error"'; then
229
- print_success " Deleted ${#user_ids[@]} test users"
230
- else
231
- print_fail " Failed to delete test users"
232
- echo " Response: $delete_response"
233
- fi
234
- fi
235
- else
236
- print_fail " Could not list users for cleanup"
237
- fi
238
- fi
239
-
240
- # 3. Delete all test buckets
241
- if [ ${#TEST_BUCKETS_CREATED[@]} -gt 0 ]; then
242
- if [ -n "$api_key" ]; then
243
- print_info "Deleting test buckets..."
244
- for bucket in "${TEST_BUCKETS_CREATED[@]}"; do
245
- print_info " - Deleting bucket: $bucket"
246
- delete_response=$(curl -s -w "\n%{http_code}" -X DELETE "$TEST_API_BASE/storage/buckets/$bucket" \
247
- -H "Authorization: Bearer $api_key" 2>/dev/null || echo "500")
248
- status=$(echo "$delete_response" | tail -n 1)
249
- if [ "$status" -ge 200 ] && [ "$status" -lt 300 ]; then
250
- echo " ✓ Deleted"
251
- else
252
- echo " ✗ Failed (status: $status)"
253
- cleanup_failed=1
254
- fi
255
- done
256
- else
257
- print_fail "Cannot delete buckets without API key"
258
- print_info "Buckets to delete manually:"
259
- for bucket in "${TEST_BUCKETS_CREATED[@]}"; do
260
- echo " - $bucket"
261
- done
262
- cleanup_failed=1
263
- fi
264
- fi
265
-
266
- # 4. Delete all test AI configurations
267
- if [ ${#TEST_AI_CONFIGS_CREATED[@]} -gt 0 ]; then
268
- if [ -n "$admin_token" ]; then
269
- print_info "Deleting test AI configurations..."
270
- for config_id in "${TEST_AI_CONFIGS_CREATED[@]}"; do
271
- print_info " - Deleting AI config: $config_id"
272
- delete_response=$(curl -s -w "\n%{http_code}" -X DELETE "$TEST_API_BASE/ai/configurations/$config_id" \
273
- -H "Authorization: Bearer $admin_token" 2>/dev/null || echo "500")
274
- status=$(echo "$delete_response" | tail -n 1)
275
- # 404 is OK - means already deleted
276
- if [ "$status" -ge 200 ] && [ "$status" -lt 300 ] || [ "$status" -eq 404 ]; then
277
- echo " ✓ Deleted (or already gone)"
278
- else
279
- echo " ✗ Failed (status: $status)"
280
- cleanup_failed=1
281
- fi
282
- done
283
- else
284
- print_fail "Cannot delete AI configs without admin token"
285
- print_info "AI configs to delete manually:"
286
- for config_id in "${TEST_AI_CONFIGS_CREATED[@]}"; do
287
- echo " - $config_id"
288
- done
289
- cleanup_failed=1
290
- fi
291
- fi
292
-
293
- if [ $cleanup_failed -eq 1 ]; then
294
- print_fail "Cleanup completed with errors - some resources may need manual cleanup"
295
- else
296
- print_success "Cleanup completed successfully"
297
- fi
298
- }
299
-
300
- # Test failure tracking
301
- TEST_FAILED=0
302
-
303
- # Function to track test failures
304
- track_test_failure() {
305
- TEST_FAILED=1
306
- }
307
-
308
- # Function to exit with proper code
309
- exit_with_status() {
310
- if [ $TEST_FAILED -eq 1 ]; then
311
- exit 1
312
- else
313
- exit 0
314
- fi
315
- }
316
-
317
- # Set error handling
318
- set -E # Inherit ERR trap in functions
319
-
320
- # Function to handle script termination
321
- handle_exit() {
322
- local exit_code=$?
323
-
324
- # Run cleanup
325
- cleanup_test_data
326
-
327
- # Exit with the original exit code or our tracked failure status
328
- if [ $TEST_FAILED -eq 1 ] || [ $exit_code -ne 0 ]; then
329
- exit 1
330
- else
331
- exit 0
332
- fi
333
- }
334
-
335
- # Trap to ensure cleanup runs on any exit condition
336
- trap handle_exit EXIT
337
- trap handle_exit ERR
338
- trap handle_exit INT
1
+ #!/bin/bash
2
+
3
+ # Test configuration file
4
+ # Source this file in test scripts to get consistent configuration
5
+
6
+ # API Configuration
7
+ export TEST_API_BASE="${TEST_API_BASE:-http://localhost:7130/api}"
8
+
9
+ # Admin credentials - can be overridden by environment variables
10
+ export TEST_ADMIN_EMAIL="${TEST_ADMIN_EMAIL:-${ADMIN_EMAIL:-admin@example.com}}"
11
+ export TEST_ADMIN_PASSWORD="${TEST_ADMIN_PASSWORD:-${ADMIN_PASSWORD:-change-this-password}}"
12
+
13
+ # User test credentials
14
+ export TEST_USER_EMAIL_PREFIX="${TEST_USER_EMAIL_PREFIX:-testuser_}"
15
+
16
+ # Colors for output
17
+ export RED='\033[0;31m'
18
+ export GREEN='\033[0;32m'
19
+ export YELLOW='\033[1;33m'
20
+ export BLUE='\033[0;34m'
21
+ export NC='\033[0m' # No Color
22
+
23
+ # Utility functions
24
+ print_success() {
25
+ echo -e "${GREEN}✅ $1${NC}"
26
+ }
27
+
28
+ print_fail() {
29
+ echo -e "${RED}❌ $1${NC}"
30
+ track_test_failure
31
+ }
32
+
33
+ print_info() {
34
+ echo -e "${YELLOW}$1${NC}"
35
+ }
36
+
37
+ print_blue() {
38
+ echo -e "${BLUE}$1${NC}"
39
+ }
40
+
41
+ # Function to login as admin and get token
42
+ get_admin_token() {
43
+ # Use JWT admin endpoint
44
+ local response=$(curl -s -X POST "$TEST_API_BASE/auth/admin/sessions" \
45
+ -H "Content-Type: application/json" \
46
+ -d "{\"email\":\"$TEST_ADMIN_EMAIL\",\"password\":\"$TEST_ADMIN_PASSWORD\"}")
47
+
48
+ if echo "$response" | grep -q '"accessToken"'; then
49
+ echo "$response" | grep -o '"accessToken":"[^"]*"' | cut -d'"' -f4
50
+ else
51
+ echo ""
52
+ fi
53
+ }
54
+
55
+ # Function to get admin API key
56
+ get_admin_api_key() {
57
+ # Only use ACCESS_API_KEY environment variable
58
+ if [ -n "$ACCESS_API_KEY" ]; then
59
+ echo "$ACCESS_API_KEY"
60
+ else
61
+ echo ""
62
+ fi
63
+ }
64
+
65
+ # Check if required tools are installed
66
+ check_requirements() {
67
+ if ! command -v curl &> /dev/null; then
68
+ print_fail "curl is required but not installed"
69
+ exit 1
70
+ fi
71
+
72
+ if ! command -v jq &> /dev/null; then
73
+ print_info "jq is recommended for JSON parsing"
74
+ fi
75
+ }
76
+
77
+ # Array to track test tables created
78
+ declare -a TEST_TABLES_CREATED=()
79
+
80
+ # Array to track test users created
81
+ declare -a TEST_USERS_CREATED=()
82
+
83
+ # Array to track test buckets created
84
+ declare -a TEST_BUCKETS_CREATED=()
85
+
86
+ # Array to track test AI configs created
87
+ declare -a TEST_AI_CONFIGS_CREATED=()
88
+
89
+ # Function to register a table for cleanup
90
+ register_test_table() {
91
+ local table_name=$1
92
+ TEST_TABLES_CREATED+=("$table_name")
93
+ }
94
+
95
+ # Function to register a user for cleanup
96
+ register_test_user() {
97
+ local user_email=$1
98
+ TEST_USERS_CREATED+=("$user_email")
99
+ }
100
+
101
+ # Function to register a bucket for cleanup
102
+ register_test_bucket() {
103
+ local bucket_name=$1
104
+ TEST_BUCKETS_CREATED+=("$bucket_name")
105
+ }
106
+
107
+ # Function to register an AI config for cleanup
108
+ register_test_ai_config() {
109
+ local config_id=$1
110
+ TEST_AI_CONFIGS_CREATED+=("$config_id")
111
+ }
112
+
113
+ # Function to register a user with Better Auth
114
+ register_user() {
115
+ local email=$1
116
+ local password=$2
117
+ local name=${3:-"Test User"}
118
+
119
+ # Use JWT registration
120
+ curl -s -X POST "$TEST_API_BASE/auth/users" \
121
+ -H "Content-Type: application/json" \
122
+ -d "{\"email\":\"$email\",\"password\":\"$password\",\"name\":\"$name\"}"
123
+ }
124
+
125
+ # Function to login a user with Better Auth
126
+ login_user() {
127
+ local email=$1
128
+ local password=$2
129
+
130
+ # Use JWT login
131
+ curl -s -X POST "$TEST_API_BASE/auth/sessions" \
132
+ -H "Content-Type: application/json" \
133
+ -d "{\"email\":\"$email\",\"password\":\"$password\"}"
134
+ }
135
+
136
+ # Function to get user profile with auth token
137
+ get_user_profile() {
138
+ local token=$1
139
+
140
+ # Use JWT profile endpoint
141
+ curl -s -X GET "$TEST_API_BASE/auth/sessions/current" \
142
+ -H "Authorization: Bearer $token"
143
+ }
144
+
145
+ # Function to delete a table
146
+ delete_table() {
147
+ local table_name=$1
148
+ local token=$2
149
+
150
+ curl -s -X DELETE "$TEST_API_BASE/database/tables/$table_name" \
151
+ -H "Authorization: Bearer $token" \
152
+ -H "Content-Type: application/json" > /dev/null 2>&1
153
+ }
154
+
155
+ # Function to cleanup all test data
156
+ cleanup_test_data() {
157
+ # Always attempt cleanup, even if some parts fail
158
+ print_info "🧹 Cleaning up test data..."
159
+
160
+ local cleanup_failed=0
161
+
162
+ # Try to get credentials - but continue cleanup even if they fail
163
+ local admin_token=$(get_admin_token 2>/dev/null || echo "")
164
+ local api_key=$(get_admin_api_key 2>/dev/null || echo "")
165
+
166
+ if [ -z "$admin_token" ]; then
167
+ print_info "No admin token available - some cleanup may be limited"
168
+ fi
169
+
170
+ if [ -z "$api_key" ]; then
171
+ print_info "No API key available - bucket cleanup may be limited"
172
+ fi
173
+
174
+ # 1. Delete all registered test tables
175
+ if [ ${#TEST_TABLES_CREATED[@]} -gt 0 ]; then
176
+ if [ -n "$admin_token" ]; then
177
+ print_info "Deleting test tables..."
178
+ for table in "${TEST_TABLES_CREATED[@]}"; do
179
+ print_info " - Deleting table: $table"
180
+ if ! delete_table "$table" "$admin_token"; then
181
+ echo " ✗ Failed to delete"
182
+ cleanup_failed=1
183
+ else
184
+ echo " ✓ Deleted"
185
+ fi
186
+ done
187
+ else
188
+ print_fail "Cannot delete tables without admin token"
189
+ print_info "Tables to delete manually:"
190
+ for table in "${TEST_TABLES_CREATED[@]}"; do
191
+ echo " - $table"
192
+ done
193
+ cleanup_failed=1
194
+ fi
195
+ fi
196
+
197
+ # 2. Delete all test users
198
+ if [ ${#TEST_USERS_CREATED[@]} -gt 0 ] && [ -n "$admin_token" ]; then
199
+ print_info "Deleting test users..."
200
+
201
+ # Get all users to find IDs of test users
202
+ local users_response=$(curl -s -X GET "$TEST_API_BASE/auth/users?limit=100" \
203
+ -H "Authorization: Bearer $admin_token" \
204
+ -H "Content-Type: application/json" 2>/dev/null || echo "")
205
+
206
+ if [ -n "$users_response" ] && echo "$users_response" | grep -q '"data"'; then
207
+ local user_ids=()
208
+
209
+ # Extract data array from response (new format uses "data" instead of "users")
210
+ local users_json=$(echo "$users_response" | grep -o '"data":\[[^]]*\]' | sed 's/"data"://')
211
+
212
+ # Find IDs of test users by email
213
+ for test_email in "${TEST_USERS_CREATED[@]}"; do
214
+ local user_id=$(echo "$users_json" | grep -B2 -A2 "\"email\":\"$test_email\"" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
215
+ if [ -n "$user_id" ]; then
216
+ user_ids+=("$user_id")
217
+ print_info " - Found test user: $test_email (ID: $user_id)"
218
+ fi
219
+ done
220
+
221
+ # Bulk delete test users
222
+ if [ ${#user_ids[@]} -gt 0 ]; then
223
+ local delete_response=$(curl -s -X DELETE "$TEST_API_BASE/auth/users" \
224
+ -H "Authorization: Bearer $admin_token" \
225
+ -H "Content-Type: application/json" \
226
+ -d "{\"userIds\": [$(printf '"%s",' "${user_ids[@]}" | sed 's/,$//' )]}")
227
+
228
+ if ! echo "$delete_response" | grep -q '"error"'; then
229
+ print_success " Deleted ${#user_ids[@]} test users"
230
+ else
231
+ print_fail " Failed to delete test users"
232
+ echo " Response: $delete_response"
233
+ fi
234
+ fi
235
+ else
236
+ print_fail " Could not list users for cleanup"
237
+ fi
238
+ fi
239
+
240
+ # 3. Delete all test buckets
241
+ if [ ${#TEST_BUCKETS_CREATED[@]} -gt 0 ]; then
242
+ if [ -n "$api_key" ]; then
243
+ print_info "Deleting test buckets..."
244
+ for bucket in "${TEST_BUCKETS_CREATED[@]}"; do
245
+ print_info " - Deleting bucket: $bucket"
246
+ delete_response=$(curl -s -w "\n%{http_code}" -X DELETE "$TEST_API_BASE/storage/buckets/$bucket" \
247
+ -H "Authorization: Bearer $api_key" 2>/dev/null || echo "500")
248
+ status=$(echo "$delete_response" | tail -n 1)
249
+ if [ "$status" -ge 200 ] && [ "$status" -lt 300 ]; then
250
+ echo " ✓ Deleted"
251
+ else
252
+ echo " ✗ Failed (status: $status)"
253
+ cleanup_failed=1
254
+ fi
255
+ done
256
+ else
257
+ print_fail "Cannot delete buckets without API key"
258
+ print_info "Buckets to delete manually:"
259
+ for bucket in "${TEST_BUCKETS_CREATED[@]}"; do
260
+ echo " - $bucket"
261
+ done
262
+ cleanup_failed=1
263
+ fi
264
+ fi
265
+
266
+ # 4. Delete all test AI configurations
267
+ if [ ${#TEST_AI_CONFIGS_CREATED[@]} -gt 0 ]; then
268
+ if [ -n "$admin_token" ]; then
269
+ print_info "Deleting test AI configurations..."
270
+ for config_id in "${TEST_AI_CONFIGS_CREATED[@]}"; do
271
+ print_info " - Deleting AI config: $config_id"
272
+ delete_response=$(curl -s -w "\n%{http_code}" -X DELETE "$TEST_API_BASE/ai/configurations/$config_id" \
273
+ -H "Authorization: Bearer $admin_token" 2>/dev/null || echo "500")
274
+ status=$(echo "$delete_response" | tail -n 1)
275
+ # 404 is OK - means already deleted
276
+ if [ "$status" -ge 200 ] && [ "$status" -lt 300 ] || [ "$status" -eq 404 ]; then
277
+ echo " ✓ Deleted (or already gone)"
278
+ else
279
+ echo " ✗ Failed (status: $status)"
280
+ cleanup_failed=1
281
+ fi
282
+ done
283
+ else
284
+ print_fail "Cannot delete AI configs without admin token"
285
+ print_info "AI configs to delete manually:"
286
+ for config_id in "${TEST_AI_CONFIGS_CREATED[@]}"; do
287
+ echo " - $config_id"
288
+ done
289
+ cleanup_failed=1
290
+ fi
291
+ fi
292
+
293
+ if [ $cleanup_failed -eq 1 ]; then
294
+ print_fail "Cleanup completed with errors - some resources may need manual cleanup"
295
+ else
296
+ print_success "Cleanup completed successfully"
297
+ fi
298
+ }
299
+
300
+ # Test failure tracking
301
+ TEST_FAILED=0
302
+
303
+ # Function to track test failures
304
+ track_test_failure() {
305
+ TEST_FAILED=1
306
+ }
307
+
308
+ # Function to exit with proper code
309
+ exit_with_status() {
310
+ if [ $TEST_FAILED -eq 1 ]; then
311
+ exit 1
312
+ else
313
+ exit 0
314
+ fi
315
+ }
316
+
317
+ # Set error handling
318
+ set -E # Inherit ERR trap in functions
319
+
320
+ # Function to handle script termination
321
+ handle_exit() {
322
+ local exit_code=$?
323
+
324
+ # Run cleanup
325
+ cleanup_test_data
326
+
327
+ # Exit with the original exit code or our tracked failure status
328
+ if [ $TEST_FAILED -eq 1 ] || [ $exit_code -ne 0 ]; then
329
+ exit 1
330
+ else
331
+ exit 0
332
+ fi
333
+ }
334
+
335
+ # Trap to ensure cleanup runs on any exit condition
336
+ trap handle_exit EXIT
337
+ trap handle_exit ERR
338
+ trap handle_exit INT
339
339
  trap handle_exit TERM