alepha 0.14.2 → 0.14.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/audits/index.browser.js +5 -5
- package/dist/api/audits/index.browser.js.map +1 -1
- package/dist/api/audits/index.d.ts +784 -784
- package/dist/api/audits/index.d.ts.map +1 -1
- package/dist/api/audits/index.js +13 -13
- package/dist/api/audits/index.js.map +1 -1
- package/dist/api/files/index.browser.js +5 -5
- package/dist/api/files/index.browser.js.map +1 -1
- package/dist/api/files/index.d.ts +57 -57
- package/dist/api/files/index.d.ts.map +1 -1
- package/dist/api/files/index.js +71 -71
- package/dist/api/files/index.js.map +1 -1
- package/dist/api/jobs/index.browser.js +5 -5
- package/dist/api/jobs/index.browser.js.map +1 -1
- package/dist/api/jobs/index.d.ts +165 -165
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/jobs/index.js +10 -10
- package/dist/api/jobs/index.js.map +1 -1
- package/dist/api/notifications/index.browser.js +10 -10
- package/dist/api/notifications/index.browser.js.map +1 -1
- package/dist/api/notifications/index.d.ts +583 -171
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/notifications/index.js +12 -12
- package/dist/api/notifications/index.js.map +1 -1
- package/dist/api/parameters/index.browser.js +163 -10
- package/dist/api/parameters/index.browser.js.map +1 -1
- package/dist/api/parameters/index.d.ts +281 -276
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/parameters/index.js +196 -91
- package/dist/api/parameters/index.js.map +1 -1
- package/dist/api/users/index.browser.js +19 -19
- package/dist/api/users/index.browser.js.map +1 -1
- package/dist/api/users/index.d.ts +1137 -1123
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +827 -596
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/verifications/index.browser.js +6 -6
- package/dist/api/verifications/index.browser.js.map +1 -1
- package/dist/api/verifications/index.d.ts +13 -13
- package/dist/api/verifications/index.d.ts.map +1 -1
- package/dist/api/verifications/index.js +6 -6
- package/dist/api/verifications/index.js.map +1 -1
- package/dist/bin/index.d.ts +1 -2
- package/dist/bin/index.js +0 -1
- package/dist/bin/index.js.map +1 -1
- package/dist/cli/index.d.ts +137 -112
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +371 -259
- package/dist/cli/index.js.map +1 -1
- package/dist/command/index.d.ts +45 -5
- package/dist/command/index.d.ts.map +1 -1
- package/dist/command/index.js +97 -17
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js +14 -18
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +29 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +14 -18
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +14 -18
- package/dist/core/index.native.js.map +1 -1
- package/dist/fake/index.js +195 -168
- package/dist/fake/index.js.map +1 -1
- package/dist/file/index.d.ts +8 -0
- package/dist/file/index.d.ts.map +1 -1
- package/dist/file/index.js +3 -0
- package/dist/file/index.js.map +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/orm/index.d.ts +32 -32
- package/dist/orm/index.d.ts.map +1 -1
- package/dist/orm/index.js +12 -12
- package/dist/orm/index.js.map +1 -1
- package/dist/security/index.d.ts +1 -1
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +1 -1
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.d.ts +171 -155
- package/dist/server/auth/index.d.ts.map +1 -1
- package/dist/server/auth/index.js +0 -1
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/compress/index.d.ts.map +1 -1
- package/dist/server/compress/index.js +2 -0
- package/dist/server/compress/index.js.map +1 -1
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js +1 -1
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/links/index.browser.js +22 -6
- package/dist/server/links/index.browser.js.map +1 -1
- package/dist/server/links/index.d.ts +46 -44
- package/dist/server/links/index.d.ts.map +1 -1
- package/dist/server/links/index.js +24 -41
- package/dist/server/links/index.js.map +1 -1
- package/dist/server/security/index.d.ts +9 -9
- package/dist/server/swagger/index.d.ts +2 -1
- package/dist/server/swagger/index.d.ts.map +1 -1
- package/dist/server/swagger/index.js +8 -3
- package/dist/server/swagger/index.js.map +1 -1
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +12 -4
- package/dist/vite/index.js.map +1 -1
- package/dist/websocket/index.d.ts +7 -7
- package/package.json +7 -7
- package/src/api/audits/controllers/{AuditController.ts → AdminAuditController.ts} +5 -6
- package/src/api/audits/entities/audits.ts +5 -5
- package/src/api/audits/index.browser.ts +1 -1
- package/src/api/audits/index.ts +3 -3
- package/src/api/audits/primitives/$audit.spec.ts +276 -0
- package/src/api/audits/services/AuditService.spec.ts +495 -0
- package/src/api/files/__tests__/$bucket.spec.ts +91 -0
- package/src/api/files/controllers/AdminFileStatsController.spec.ts +166 -0
- package/src/api/files/controllers/{StorageStatsController.ts → AdminFileStatsController.ts} +2 -2
- package/src/api/files/controllers/FileController.spec.ts +558 -0
- package/src/api/files/controllers/FileController.ts +4 -5
- package/src/api/files/entities/files.ts +5 -5
- package/src/api/files/index.browser.ts +1 -1
- package/src/api/files/index.ts +4 -4
- package/src/api/files/jobs/FileJobs.spec.ts +52 -0
- package/src/api/files/services/FileService.spec.ts +109 -0
- package/src/api/jobs/__tests__/JobController.spec.ts +343 -0
- package/src/api/jobs/controllers/{JobController.ts → AdminJobController.ts} +2 -2
- package/src/api/jobs/entities/jobExecutions.ts +5 -5
- package/src/api/jobs/index.ts +3 -3
- package/src/api/jobs/primitives/$job.spec.ts +476 -0
- package/src/api/notifications/controllers/{NotificationController.ts → AdminNotificationController.ts} +4 -5
- package/src/api/notifications/entities/notifications.ts +5 -5
- package/src/api/notifications/index.browser.ts +1 -1
- package/src/api/notifications/index.ts +4 -4
- package/src/api/parameters/controllers/{ConfigController.ts → AdminConfigController.ts} +46 -107
- package/src/api/parameters/entities/parameters.ts +7 -17
- package/src/api/parameters/index.ts +3 -3
- package/src/api/parameters/primitives/$config.spec.ts +356 -0
- package/src/api/parameters/schemas/activateConfigBodySchema.ts +12 -0
- package/src/api/parameters/schemas/checkScheduledResponseSchema.ts +8 -0
- package/src/api/parameters/schemas/configCurrentResponseSchema.ts +13 -0
- package/src/api/parameters/schemas/configHistoryResponseSchema.ts +9 -0
- package/src/api/parameters/schemas/configNameParamSchema.ts +10 -0
- package/src/api/parameters/schemas/configNamesResponseSchema.ts +8 -0
- package/src/api/parameters/schemas/configTreeNodeSchema.ts +13 -0
- package/src/api/parameters/schemas/configVersionParamSchema.ts +9 -0
- package/src/api/parameters/schemas/configVersionResponseSchema.ts +9 -0
- package/src/api/parameters/schemas/configsByStatusResponseSchema.ts +9 -0
- package/src/api/parameters/schemas/createConfigVersionBodySchema.ts +24 -0
- package/src/api/parameters/schemas/index.ts +15 -0
- package/src/api/parameters/schemas/parameterResponseSchema.ts +26 -0
- package/src/api/parameters/schemas/parameterStatusSchema.ts +13 -0
- package/src/api/parameters/schemas/rollbackConfigBodySchema.ts +15 -0
- package/src/api/parameters/schemas/statusParamSchema.ts +9 -0
- package/src/api/users/__tests__/EmailVerification.spec.ts +369 -0
- package/src/api/users/__tests__/PasswordReset.spec.ts +550 -0
- package/src/api/users/controllers/AdminIdentityController.spec.ts +365 -0
- package/src/api/users/controllers/{IdentityController.ts → AdminIdentityController.ts} +3 -4
- package/src/api/users/controllers/AdminSessionController.spec.ts +274 -0
- package/src/api/users/controllers/{SessionController.ts → AdminSessionController.ts} +3 -4
- package/src/api/users/controllers/AdminUserController.spec.ts +372 -0
- package/src/api/users/controllers/AdminUserController.ts +116 -0
- package/src/api/users/controllers/UserController.ts +4 -107
- package/src/api/users/controllers/UserRealmController.ts +3 -0
- package/src/api/users/entities/identities.ts +6 -6
- package/src/api/users/entities/sessions.ts +6 -6
- package/src/api/users/entities/users.ts +9 -9
- package/src/api/users/index.ts +9 -6
- package/src/api/users/primitives/$userRealm.ts +13 -8
- package/src/api/users/services/CredentialService.spec.ts +509 -0
- package/src/api/users/services/CredentialService.ts +46 -0
- package/src/api/users/services/IdentityService.ts +15 -0
- package/src/api/users/services/RegistrationService.spec.ts +630 -0
- package/src/api/users/services/RegistrationService.ts +18 -0
- package/src/api/users/services/SessionService.spec.ts +301 -0
- package/src/api/users/services/SessionService.ts +110 -1
- package/src/api/users/services/UserService.ts +67 -2
- package/src/api/verifications/__tests__/CodeVerification.spec.ts +318 -0
- package/src/api/verifications/__tests__/LinkVerification.spec.ts +279 -0
- package/src/api/verifications/entities/verifications.ts +6 -6
- package/src/api/verifications/jobs/VerificationJobs.spec.ts +50 -0
- package/src/batch/__tests__/startup-buffering.spec.ts +458 -0
- package/src/batch/primitives/$batch.spec.ts +766 -0
- package/src/batch/providers/BatchProvider.spec.ts +786 -0
- package/src/bin/index.ts +0 -1
- package/src/bucket/__tests__/shared.ts +194 -0
- package/src/bucket/primitives/$bucket.spec.ts +104 -0
- package/src/bucket/providers/FileStorageProvider.spec.ts +13 -0
- package/src/bucket/providers/LocalFileStorageProvider.spec.ts +77 -0
- package/src/bucket/providers/MemoryFileStorageProvider.spec.ts +82 -0
- package/src/cache/core/__tests__/shared.ts +377 -0
- package/src/cache/core/primitives/$cache.spec.ts +111 -0
- package/src/cache/redis/__tests__/cache-redis.spec.ts +70 -0
- package/src/cli/apps/AlephaCli.ts +25 -4
- package/src/cli/commands/dev.ts +19 -7
- package/src/cli/commands/gen/changelog.spec.ts +315 -0
- package/src/cli/commands/{changelog.ts → gen/changelog.ts} +9 -9
- package/src/cli/commands/gen/openapi.ts +71 -0
- package/src/cli/commands/gen.ts +18 -0
- package/src/cli/commands/init.ts +2 -0
- package/src/cli/commands/root.ts +12 -3
- package/src/cli/commands/typecheck.ts +5 -0
- package/src/cli/index.ts +2 -1
- package/src/cli/services/AlephaCliUtils.ts +71 -32
- package/src/cli/services/GitMessageParser.ts +1 -1
- package/src/command/helpers/Asker.spec.ts +127 -0
- package/src/command/helpers/Runner.spec.ts +126 -0
- package/src/command/primitives/$command.spec.ts +1588 -0
- package/src/command/providers/CliProvider.ts +74 -24
- package/src/core/Alepha.ts +45 -0
- package/src/core/__tests__/Alepha-emit.spec.ts +22 -0
- package/src/core/__tests__/Alepha-graph.spec.ts +93 -0
- package/src/core/__tests__/Alepha-has.spec.ts +41 -0
- package/src/core/__tests__/Alepha-inject.spec.ts +93 -0
- package/src/core/__tests__/Alepha-register.spec.ts +81 -0
- package/src/core/__tests__/Alepha-start.spec.ts +176 -0
- package/src/core/__tests__/Alepha-with.spec.ts +14 -0
- package/src/core/__tests__/TypeBox-usecases.spec.ts +35 -0
- package/src/core/__tests__/TypeBoxLocale.spec.ts +15 -0
- package/src/core/__tests__/descriptor.spec.ts +34 -0
- package/src/core/__tests__/fixtures/A.ts +5 -0
- package/src/core/__tests__/pagination.spec.ts +77 -0
- package/src/core/helpers/jsonSchemaToTypeBox.ts +2 -2
- package/src/core/primitives/$atom.spec.ts +43 -0
- package/src/core/primitives/$hook.spec.ts +130 -0
- package/src/core/primitives/$inject.spec.ts +175 -0
- package/src/core/primitives/$module.spec.ts +115 -0
- package/src/core/providers/CodecManager.spec.ts +740 -0
- package/src/core/providers/EventManager.spec.ts +762 -0
- package/src/core/providers/EventManager.ts +4 -0
- package/src/core/providers/StateManager.spec.ts +365 -0
- package/src/core/providers/TypeProvider.spec.ts +1607 -0
- package/src/core/providers/TypeProvider.ts +20 -26
- package/src/datetime/primitives/$interval.spec.ts +103 -0
- package/src/datetime/providers/DateTimeProvider.spec.ts +86 -0
- package/src/email/primitives/$email.spec.ts +175 -0
- package/src/email/providers/LocalEmailProvider.spec.ts +341 -0
- package/src/fake/__tests__/keyName.example.ts +40 -0
- package/src/fake/__tests__/keyName.spec.ts +152 -0
- package/src/fake/__tests__/module.example.ts +32 -0
- package/src/fake/providers/FakeProvider.spec.ts +438 -0
- package/src/file/providers/FileSystemProvider.ts +8 -0
- package/src/file/providers/NodeFileSystemProvider.spec.ts +418 -0
- package/src/file/providers/NodeFileSystemProvider.ts +5 -0
- package/src/file/services/FileDetector.spec.ts +591 -0
- package/src/lock/core/__tests__/shared.ts +190 -0
- package/src/lock/core/providers/MemoryLockProvider.spec.ts +25 -0
- package/src/lock/redis/providers/RedisLockProvider.spec.ts +25 -0
- package/src/logger/__tests__/SimpleFormatterProvider.spec.ts +109 -0
- package/src/logger/primitives/$logger.spec.ts +108 -0
- package/src/logger/services/Logger.spec.ts +295 -0
- package/src/mcp/__tests__/errors.spec.ts +175 -0
- package/src/mcp/__tests__/integration.spec.ts +450 -0
- package/src/mcp/helpers/jsonrpc.spec.ts +380 -0
- package/src/mcp/primitives/$prompt.spec.ts +468 -0
- package/src/mcp/primitives/$resource.spec.ts +390 -0
- package/src/mcp/primitives/$tool.spec.ts +406 -0
- package/src/mcp/providers/McpServerProvider.spec.ts +797 -0
- package/src/orm/__tests__/$repository-crud.spec.ts +276 -0
- package/src/orm/__tests__/$repository-hooks.spec.ts +325 -0
- package/src/orm/__tests__/$repository-orderBy.spec.ts +128 -0
- package/src/orm/__tests__/$repository-pagination-sort.spec.ts +149 -0
- package/src/orm/__tests__/$repository-save.spec.ts +37 -0
- package/src/orm/__tests__/ModelBuilder-integration.spec.ts +490 -0
- package/src/orm/__tests__/ModelBuilder-types.spec.ts +186 -0
- package/src/orm/__tests__/PostgresProvider.spec.ts +46 -0
- package/src/orm/__tests__/delete-returning.spec.ts +256 -0
- package/src/orm/__tests__/deletedAt.spec.ts +80 -0
- package/src/orm/__tests__/enums.spec.ts +315 -0
- package/src/orm/__tests__/execute.spec.ts +72 -0
- package/src/orm/__tests__/fixtures/bigEntitySchema.ts +65 -0
- package/src/orm/__tests__/fixtures/userEntitySchema.ts +27 -0
- package/src/orm/__tests__/joins.spec.ts +1114 -0
- package/src/orm/__tests__/page.spec.ts +287 -0
- package/src/orm/__tests__/primaryKey.spec.ts +87 -0
- package/src/orm/__tests__/query-date-encoding.spec.ts +402 -0
- package/src/orm/__tests__/ref-auto-onDelete.spec.ts +156 -0
- package/src/orm/__tests__/references.spec.ts +102 -0
- package/src/orm/__tests__/security.spec.ts +710 -0
- package/src/orm/__tests__/sqlite.spec.ts +111 -0
- package/src/orm/__tests__/string-operators.spec.ts +429 -0
- package/src/orm/__tests__/timestamps.spec.ts +388 -0
- package/src/orm/__tests__/validation.spec.ts +183 -0
- package/src/orm/__tests__/version.spec.ts +64 -0
- package/src/orm/helpers/parseQueryString.spec.ts +196 -0
- package/src/orm/primitives/$repository.spec.ts +137 -0
- package/src/orm/primitives/$sequence.spec.ts +29 -0
- package/src/orm/primitives/$transaction.spec.ts +82 -0
- package/src/orm/providers/drivers/BunPostgresProvider.ts +3 -3
- package/src/orm/providers/drivers/BunSqliteProvider.ts +1 -1
- package/src/orm/providers/drivers/CloudflareD1Provider.ts +1 -1
- package/src/orm/providers/drivers/DatabaseProvider.ts +1 -1
- package/src/orm/providers/drivers/NodePostgresProvider.ts +3 -3
- package/src/orm/providers/drivers/NodeSqliteProvider.ts +1 -1
- package/src/orm/providers/drivers/PglitePostgresProvider.ts +2 -2
- package/src/orm/services/ModelBuilder.spec.ts +575 -0
- package/src/orm/services/Repository.spec.ts +137 -0
- package/src/queue/core/__tests__/shared.ts +143 -0
- package/src/queue/core/providers/MemoryQueueProvider.spec.ts +23 -0
- package/src/queue/core/providers/WorkerProvider.spec.ts +378 -0
- package/src/queue/redis/providers/RedisQueueProvider.spec.ts +23 -0
- package/src/redis/__tests__/redis.spec.ts +58 -0
- package/src/retry/primitives/$retry.spec.ts +234 -0
- package/src/retry/providers/RetryProvider.spec.ts +438 -0
- package/src/router/__tests__/match.spec.ts +252 -0
- package/src/router/providers/RouterProvider.spec.ts +197 -0
- package/src/scheduler/__tests__/$scheduler-cron.spec.ts +25 -0
- package/src/scheduler/__tests__/$scheduler-interval.spec.ts +25 -0
- package/src/scheduler/__tests__/shared.ts +77 -0
- package/src/security/__tests__/bug-1-wildcard-after-start.spec.ts +229 -0
- package/src/security/__tests__/bug-2-password-validation.spec.ts +245 -0
- package/src/security/__tests__/bug-3-regex-vulnerability.spec.ts +407 -0
- package/src/security/__tests__/bug-4-oauth2-validation.spec.ts +439 -0
- package/src/security/__tests__/multi-layer-permissions.spec.ts +522 -0
- package/src/security/primitives/$permission.spec.ts +30 -0
- package/src/security/primitives/$permission.ts +2 -2
- package/src/security/primitives/$realm.spec.ts +101 -0
- package/src/security/primitives/$role.spec.ts +52 -0
- package/src/security/primitives/$serviceAccount.spec.ts +61 -0
- package/src/security/providers/SecurityProvider.spec.ts +350 -0
- package/src/server/auth/providers/ServerAuthProvider.ts +0 -2
- package/src/server/cache/providers/ServerCacheProvider.spec.ts +942 -0
- package/src/server/compress/providers/ServerCompressProvider.spec.ts +31 -0
- package/src/server/compress/providers/ServerCompressProvider.ts +2 -0
- package/src/server/cookies/providers/ServerCookiesProvider.spec.ts +253 -0
- package/src/server/core/__tests__/ServerRouterProvider-getRoutes.spec.ts +334 -0
- package/src/server/core/__tests__/ServerRouterProvider-requestId.spec.ts +129 -0
- package/src/server/core/primitives/$action.spec.ts +191 -0
- package/src/server/core/primitives/$route.spec.ts +65 -0
- package/src/server/core/providers/ServerBodyParserProvider.spec.ts +93 -0
- package/src/server/core/providers/ServerLoggerProvider.spec.ts +100 -0
- package/src/server/core/providers/ServerProvider.ts +3 -1
- package/src/server/core/services/HttpClient.spec.ts +123 -0
- package/src/server/core/services/UserAgentParser.spec.ts +111 -0
- package/src/server/cors/providers/ServerCorsProvider.spec.ts +481 -0
- package/src/server/health/providers/ServerHealthProvider.spec.ts +22 -0
- package/src/server/helmet/providers/ServerHelmetProvider.spec.ts +105 -0
- package/src/server/links/__tests__/$action.spec.ts +238 -0
- package/src/server/links/__tests__/fixtures/CrudApp.ts +122 -0
- package/src/server/links/__tests__/requestId.spec.ts +120 -0
- package/src/server/links/primitives/$remote.spec.ts +228 -0
- package/src/server/links/providers/LinkProvider.spec.ts +54 -0
- package/src/server/links/providers/LinkProvider.ts +49 -3
- package/src/server/links/providers/ServerLinksProvider.ts +1 -53
- package/src/server/links/schemas/apiLinksResponseSchema.ts +7 -0
- package/src/server/metrics/providers/ServerMetricsProvider.spec.ts +25 -0
- package/src/server/multipart/providers/ServerMultipartProvider.spec.ts +528 -0
- package/src/server/proxy/primitives/$proxy.spec.ts +87 -0
- package/src/server/rate-limit/__tests__/ActionRateLimit.spec.ts +211 -0
- package/src/server/rate-limit/providers/ServerRateLimitProvider.spec.ts +344 -0
- package/src/server/security/__tests__/BasicAuth.spec.ts +684 -0
- package/src/server/security/__tests__/ServerSecurityProvider-realm.spec.ts +388 -0
- package/src/server/security/providers/ServerSecurityProvider.spec.ts +123 -0
- package/src/server/static/primitives/$serve.spec.ts +193 -0
- package/src/server/swagger/__tests__/ui.spec.ts +52 -0
- package/src/server/swagger/primitives/$swagger.spec.ts +193 -0
- package/src/server/swagger/providers/ServerSwaggerProvider.ts +18 -8
- package/src/sms/primitives/$sms.spec.ts +165 -0
- package/src/sms/providers/LocalSmsProvider.spec.ts +224 -0
- package/src/sms/providers/MemorySmsProvider.spec.ts +193 -0
- package/src/thread/primitives/$thread.spec.ts +186 -0
- package/src/topic/core/__tests__/shared.ts +144 -0
- package/src/topic/core/providers/MemoryTopicProvider.spec.ts +23 -0
- package/src/topic/redis/providers/RedisTopicProvider.spec.ts +23 -0
- package/src/vite/plugins/viteAlephaDev.ts +16 -4
- package/src/vite/tasks/runAlepha.ts +7 -1
- package/src/websocket/__tests__/$websocket-new.spec.ts +195 -0
- package/src/websocket/primitives/$channel.spec.ts +30 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { Alepha, t } from "alepha";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { DbEntityNotFoundError } from "../errors/DbEntityNotFoundError.ts";
|
|
4
|
+
import { $entity, $repository } from "../index.ts";
|
|
5
|
+
import { pg } from "../providers/DatabaseTypeProvider.ts";
|
|
6
|
+
|
|
7
|
+
// Test entity
|
|
8
|
+
const userEntity = $entity({
|
|
9
|
+
name: "users",
|
|
10
|
+
schema: t.object({
|
|
11
|
+
id: pg.primaryKey(),
|
|
12
|
+
name: t.string(),
|
|
13
|
+
email: t.email(),
|
|
14
|
+
active: t.boolean(),
|
|
15
|
+
}),
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const postEntity = $entity({
|
|
19
|
+
name: "posts",
|
|
20
|
+
schema: t.object({
|
|
21
|
+
id: pg.primaryKey(),
|
|
22
|
+
userId: t.integer(),
|
|
23
|
+
title: t.string(),
|
|
24
|
+
content: t.text(),
|
|
25
|
+
}),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
class TestApp {
|
|
29
|
+
users = $repository(userEntity);
|
|
30
|
+
posts = $repository(postEntity);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Shared test function that runs on both PostgreSQL and SQLite
|
|
35
|
+
*/
|
|
36
|
+
const testDeleteReturning = async (alepha: Alepha) => {
|
|
37
|
+
const app = alepha.inject(TestApp);
|
|
38
|
+
await alepha.start();
|
|
39
|
+
|
|
40
|
+
// Test 1: deleteById returns the deleted ID
|
|
41
|
+
const user1 = await app.users.create({
|
|
42
|
+
name: "Alice",
|
|
43
|
+
email: "alice@example.com",
|
|
44
|
+
active: true,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const deletedIds = await app.users.deleteById(user1.id);
|
|
48
|
+
expect(deletedIds).toHaveLength(1);
|
|
49
|
+
expect(deletedIds[0]).toBe(user1.id);
|
|
50
|
+
|
|
51
|
+
// Verify the user is deleted
|
|
52
|
+
await expect(app.users.findById(user1.id)).rejects.toThrow(
|
|
53
|
+
DbEntityNotFoundError,
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
// Test 2: deleteById throws error when entity not found
|
|
57
|
+
await expect(app.users.deleteById(999999)).rejects.toThrow(
|
|
58
|
+
DbEntityNotFoundError,
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
// Test 3: deleteMany returns array of deleted IDs
|
|
62
|
+
const users = await Promise.all([
|
|
63
|
+
app.users.create({ name: "Bob", email: "bob@example.com", active: true }),
|
|
64
|
+
app.users.create({
|
|
65
|
+
name: "Charlie",
|
|
66
|
+
email: "charlie@example.com",
|
|
67
|
+
active: false,
|
|
68
|
+
}),
|
|
69
|
+
app.users.create({
|
|
70
|
+
name: "David",
|
|
71
|
+
email: "david@example.com",
|
|
72
|
+
active: true,
|
|
73
|
+
}),
|
|
74
|
+
]);
|
|
75
|
+
|
|
76
|
+
const activeUserIds = users
|
|
77
|
+
.filter((u) => u.active)
|
|
78
|
+
.map((u) => u.id)
|
|
79
|
+
.sort((a, b) => a - b);
|
|
80
|
+
|
|
81
|
+
const deletedActiveIds = await app.users.deleteMany({
|
|
82
|
+
active: { eq: true },
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
expect(deletedActiveIds).toHaveLength(2);
|
|
86
|
+
expect(deletedActiveIds.sort((a, b) => Number(a) - Number(b))).toEqual(
|
|
87
|
+
activeUserIds,
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
// Verify only inactive user remains
|
|
91
|
+
const remaining = await app.users.findMany({});
|
|
92
|
+
expect(remaining).toHaveLength(1);
|
|
93
|
+
expect(remaining[0].name).toBe("Charlie");
|
|
94
|
+
|
|
95
|
+
// Test 4: deleteOne returns array with single ID
|
|
96
|
+
const deletedOneIds = await app.users.deleteOne({
|
|
97
|
+
email: { eq: "charlie@example.com" },
|
|
98
|
+
});
|
|
99
|
+
expect(deletedOneIds).toHaveLength(1);
|
|
100
|
+
expect(deletedOneIds[0]).toBe(users[1].id);
|
|
101
|
+
|
|
102
|
+
// Test 5: clear returns all deleted IDs
|
|
103
|
+
const moreUsers = await Promise.all([
|
|
104
|
+
app.users.create({ name: "Eve", email: "eve@example.com", active: true }),
|
|
105
|
+
app.users.create({
|
|
106
|
+
name: "Frank",
|
|
107
|
+
email: "frank@example.com",
|
|
108
|
+
active: false,
|
|
109
|
+
}),
|
|
110
|
+
app.users.create({
|
|
111
|
+
name: "Grace",
|
|
112
|
+
email: "grace@example.com",
|
|
113
|
+
active: true,
|
|
114
|
+
}),
|
|
115
|
+
]);
|
|
116
|
+
|
|
117
|
+
const allIds = moreUsers.map((u) => u.id).sort((a, b) => a - b);
|
|
118
|
+
const clearedIds = await app.users.clear();
|
|
119
|
+
|
|
120
|
+
expect(clearedIds).toHaveLength(3);
|
|
121
|
+
expect(clearedIds.sort((a, b) => Number(a) - Number(b))).toEqual(allIds);
|
|
122
|
+
|
|
123
|
+
// Verify table is empty
|
|
124
|
+
expect(await app.users.count()).toBe(0);
|
|
125
|
+
|
|
126
|
+
// Test 6: destroy returns the deleted ID
|
|
127
|
+
const user = await app.users.create({
|
|
128
|
+
name: "Helen",
|
|
129
|
+
email: "helen@example.com",
|
|
130
|
+
active: true,
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const destroyedIds = await app.users.destroy(user);
|
|
134
|
+
expect(destroyedIds).toHaveLength(1);
|
|
135
|
+
expect(destroyedIds[0]).toBe(user.id);
|
|
136
|
+
|
|
137
|
+
// Test 7: deleteMany with no matches returns empty array
|
|
138
|
+
const emptyResult = await app.users.deleteMany({
|
|
139
|
+
name: { eq: "NonExistent" },
|
|
140
|
+
});
|
|
141
|
+
expect(emptyResult).toHaveLength(0);
|
|
142
|
+
|
|
143
|
+
// Test 8: Test with composite operations
|
|
144
|
+
const posts = await Promise.all([
|
|
145
|
+
app.posts.create({
|
|
146
|
+
userId: 1,
|
|
147
|
+
title: "Post 1",
|
|
148
|
+
content: "Content 1",
|
|
149
|
+
}),
|
|
150
|
+
app.posts.create({
|
|
151
|
+
userId: 1,
|
|
152
|
+
title: "Post 2",
|
|
153
|
+
content: "Content 2",
|
|
154
|
+
}),
|
|
155
|
+
app.posts.create({
|
|
156
|
+
userId: 2,
|
|
157
|
+
title: "Post 3",
|
|
158
|
+
content: "Content 3",
|
|
159
|
+
}),
|
|
160
|
+
]);
|
|
161
|
+
|
|
162
|
+
const deletedPostIds = await app.posts.deleteMany({
|
|
163
|
+
userId: { eq: 1 },
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
expect(deletedPostIds).toHaveLength(2);
|
|
167
|
+
const sortedDeletedIds = deletedPostIds.sort((a, b) => Number(a) - Number(b));
|
|
168
|
+
const expectedIds = [posts[0].id, posts[1].id].sort((a, b) => a - b);
|
|
169
|
+
expect(sortedDeletedIds).toEqual(expectedIds);
|
|
170
|
+
|
|
171
|
+
// Clean up
|
|
172
|
+
await app.posts.clear({ force: true });
|
|
173
|
+
await app.users.clear({ force: true });
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
describe("Delete methods with returning IDs", () => {
|
|
177
|
+
it("should return deleted IDs in PostgreSQL", async () => {
|
|
178
|
+
await testDeleteReturning(Alepha.create());
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it("should return deleted IDs in SQLite", async () => {
|
|
182
|
+
await testDeleteReturning(
|
|
183
|
+
Alepha.create({
|
|
184
|
+
env: {
|
|
185
|
+
DATABASE_URL: "sqlite://:memory:",
|
|
186
|
+
},
|
|
187
|
+
}),
|
|
188
|
+
);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it("should handle soft delete with returning IDs", async () => {
|
|
192
|
+
// Test entity with soft delete
|
|
193
|
+
const softEntity = $entity({
|
|
194
|
+
name: "soft_delete_items",
|
|
195
|
+
schema: t.object({
|
|
196
|
+
id: pg.primaryKey(),
|
|
197
|
+
name: t.string(),
|
|
198
|
+
deletedAt: pg.deletedAt(),
|
|
199
|
+
}),
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
class SoftDeleteApp {
|
|
203
|
+
items = $repository(softEntity);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const alepha = Alepha.create();
|
|
207
|
+
const app = alepha.inject(SoftDeleteApp);
|
|
208
|
+
await alepha.start();
|
|
209
|
+
|
|
210
|
+
// Create test items
|
|
211
|
+
const item1 = await app.items.create({ name: "Item 1" });
|
|
212
|
+
const item2 = await app.items.create({ name: "Item 2" });
|
|
213
|
+
const item3 = await app.items.create({ name: "Item 3" });
|
|
214
|
+
|
|
215
|
+
// Test soft delete by ID - this should trigger soft delete
|
|
216
|
+
const softDeletedId = await app.items.deleteById(item1.id);
|
|
217
|
+
expect(softDeletedId).toHaveLength(1);
|
|
218
|
+
expect(softDeletedId[0]).toBe(item1.id);
|
|
219
|
+
|
|
220
|
+
// Item should still exist but be soft deleted
|
|
221
|
+
const allAfterSoftDelete = await app.items.findMany({}, { force: true });
|
|
222
|
+
expect(allAfterSoftDelete).toHaveLength(3);
|
|
223
|
+
|
|
224
|
+
const activeAfterSoftDelete = await app.items.findMany({});
|
|
225
|
+
expect(activeAfterSoftDelete).toHaveLength(2);
|
|
226
|
+
expect(activeAfterSoftDelete.map((i) => i.name).sort()).toEqual([
|
|
227
|
+
"Item 2",
|
|
228
|
+
"Item 3",
|
|
229
|
+
]);
|
|
230
|
+
|
|
231
|
+
// Test soft delete multiple items
|
|
232
|
+
const softDeletedIds = await app.items.deleteMany({
|
|
233
|
+
name: { inArray: ["Item 2", "Item 3"] },
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
expect(softDeletedIds).toHaveLength(2);
|
|
237
|
+
const sortedIds = softDeletedIds.sort((a, b) => Number(a) - Number(b));
|
|
238
|
+
const expectedIds = [item2.id, item3.id].sort((a, b) => a - b);
|
|
239
|
+
expect(sortedIds).toEqual(expectedIds);
|
|
240
|
+
|
|
241
|
+
// All items should be soft deleted now
|
|
242
|
+
const allItems = await app.items.findMany({}, { force: true });
|
|
243
|
+
expect(allItems).toHaveLength(3);
|
|
244
|
+
|
|
245
|
+
const activeItems = await app.items.findMany({});
|
|
246
|
+
expect(activeItems).toHaveLength(0);
|
|
247
|
+
|
|
248
|
+
// Force delete returns IDs
|
|
249
|
+
const forceDeletedIds = await app.items.clear({ force: true });
|
|
250
|
+
expect(forceDeletedIds).toHaveLength(3);
|
|
251
|
+
|
|
252
|
+
// Table should be empty now
|
|
253
|
+
const finalItems = await app.items.findMany({}, { force: true });
|
|
254
|
+
expect(finalItems).toHaveLength(0);
|
|
255
|
+
});
|
|
256
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Alepha, t } from "alepha";
|
|
2
|
+
import { DateTimeProvider } from "alepha/datetime";
|
|
3
|
+
import { describe, it } from "vitest";
|
|
4
|
+
import { $entity, $repository, DbEntityNotFoundError, pg } from "../index.ts";
|
|
5
|
+
|
|
6
|
+
describe("deletedAt", () => {
|
|
7
|
+
const entity = $entity({
|
|
8
|
+
name: "test_entity",
|
|
9
|
+
schema: t.object({
|
|
10
|
+
id: pg.primaryKey(),
|
|
11
|
+
deletedAt: pg.deletedAt(),
|
|
12
|
+
name: t.optional(t.text()),
|
|
13
|
+
}),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
class App {
|
|
17
|
+
repository = $repository(entity);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const setup = async () => {
|
|
21
|
+
const alepha = Alepha.create();
|
|
22
|
+
const app = alepha.inject(App);
|
|
23
|
+
await alepha.start();
|
|
24
|
+
const now = alepha.inject(DateTimeProvider).pause();
|
|
25
|
+
return {
|
|
26
|
+
repository: app.repository,
|
|
27
|
+
now: now.toISOString(),
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
it("should update instead of delete", async ({ expect }) => {
|
|
32
|
+
const { repository, now } = await setup();
|
|
33
|
+
|
|
34
|
+
await repository.createMany([{}, {}]);
|
|
35
|
+
const entities = await repository.findMany();
|
|
36
|
+
expect(entities.length).toEqual(2);
|
|
37
|
+
expect(await repository.count()).toEqual(2);
|
|
38
|
+
|
|
39
|
+
await repository.deleteById(entities[0].id);
|
|
40
|
+
expect(await repository.count()).toEqual(1);
|
|
41
|
+
expect(await repository.findMany()).toEqual([{ id: entities[1].id }]);
|
|
42
|
+
|
|
43
|
+
expect(await repository.count({}, { force: true })).toEqual(2);
|
|
44
|
+
expect(await repository.findMany({}, { force: true })).toEqual([
|
|
45
|
+
{ id: entities[1].id },
|
|
46
|
+
{ id: entities[0].id, deletedAt: now },
|
|
47
|
+
]);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("should not update if deletedAt is already set", async ({ expect }) => {
|
|
51
|
+
const { repository, now } = await setup();
|
|
52
|
+
await repository.createMany([{}, {}]);
|
|
53
|
+
const entities = await repository.findMany();
|
|
54
|
+
const it = entities[0];
|
|
55
|
+
await repository.destroy(it);
|
|
56
|
+
it.name = "Toby";
|
|
57
|
+
await expect(() => repository.save(it)).rejects.toThrow(
|
|
58
|
+
DbEntityNotFoundError,
|
|
59
|
+
);
|
|
60
|
+
await repository.save(it, { force: true });
|
|
61
|
+
expect(await repository.findById(it.id, { force: true })).toEqual({
|
|
62
|
+
id: it.id,
|
|
63
|
+
deletedAt: now,
|
|
64
|
+
name: "Toby",
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("should force delete", async ({ expect }) => {
|
|
69
|
+
const { repository } = await setup();
|
|
70
|
+
await repository.createMany([{}, {}]);
|
|
71
|
+
const entities = await repository.findMany();
|
|
72
|
+
const it = entities[0];
|
|
73
|
+
await repository.destroy(it);
|
|
74
|
+
expect(await repository.count()).toEqual(1);
|
|
75
|
+
expect(await repository.count({}, { force: true })).toEqual(2);
|
|
76
|
+
await repository.destroy(it, { force: true });
|
|
77
|
+
expect(await repository.count()).toEqual(1);
|
|
78
|
+
expect(await repository.count({}, { force: true })).toEqual(1);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { Alepha, t } from "alepha";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { $entity, $repository, DrizzleKitProvider, pg } from "../index.ts";
|
|
4
|
+
|
|
5
|
+
// Test 1: Basic enum using t.enum (should map to TEXT column)
|
|
6
|
+
const textEnumEntity = $entity({
|
|
7
|
+
name: "text_enum_test",
|
|
8
|
+
schema: t.object({
|
|
9
|
+
id: pg.primaryKey(),
|
|
10
|
+
status: t.enum(["pending", "active", "archived"]),
|
|
11
|
+
role: t.enum(["user", "admin", "moderator"]),
|
|
12
|
+
}),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// Test 2: Basic enum using pg.enum (should create real PG ENUM type)
|
|
16
|
+
const pgEnumEntity = $entity({
|
|
17
|
+
name: "pg_enum_test",
|
|
18
|
+
schema: t.object({
|
|
19
|
+
id: pg.primaryKey(),
|
|
20
|
+
status: pg.enum(["draft", "published", "deleted"]),
|
|
21
|
+
priority: pg.enum(["low", "medium", "high"]),
|
|
22
|
+
}),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Test 3: Mixed t.enum and pg.enum in the same table
|
|
26
|
+
const mixedEnumEntity = $entity({
|
|
27
|
+
name: "mixed_enum_test",
|
|
28
|
+
schema: t.object({
|
|
29
|
+
id: pg.primaryKey(),
|
|
30
|
+
textStatus: t.enum(["open", "closed"]),
|
|
31
|
+
pgStatus: pg.enum(["new", "in_progress", "done"]),
|
|
32
|
+
}),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Test 4: Shared enum with custom name across two tables
|
|
36
|
+
const sharedEnumEntity1 = $entity({
|
|
37
|
+
name: "shared_enum_test_1",
|
|
38
|
+
schema: t.object({
|
|
39
|
+
id: pg.primaryKey(),
|
|
40
|
+
status: pg.enum(["enabled", "disabled"], { name: "shared_status_enum" }),
|
|
41
|
+
}),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const sharedEnumEntity2 = $entity({
|
|
45
|
+
name: "shared_enum_test_2",
|
|
46
|
+
schema: t.object({
|
|
47
|
+
id: pg.primaryKey(),
|
|
48
|
+
status: pg.enum(["enabled", "disabled"], { name: "shared_status_enum" }),
|
|
49
|
+
}),
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Test 5: Conflicting enum - same name but different values (should throw error)
|
|
53
|
+
const conflictEnumEntity1 = $entity({
|
|
54
|
+
name: "conflict_enum_test_1",
|
|
55
|
+
schema: t.object({
|
|
56
|
+
id: pg.primaryKey(),
|
|
57
|
+
status: pg.enum(["a", "b", "c"], { name: "conflict_status_enum" }),
|
|
58
|
+
}),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const conflictEnumEntity2 = $entity({
|
|
62
|
+
name: "conflict_enum_test_2",
|
|
63
|
+
schema: t.object({
|
|
64
|
+
id: pg.primaryKey(),
|
|
65
|
+
status: pg.enum(["a", "b", "d"], { name: "conflict_status_enum" }), // Different value!
|
|
66
|
+
}),
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe("enums - t.enum (TEXT column)", () => {
|
|
70
|
+
it("should create TEXT columns for t.enum fields", async () => {
|
|
71
|
+
const alepha = Alepha.create();
|
|
72
|
+
|
|
73
|
+
class App {
|
|
74
|
+
repository = $repository(textEnumEntity);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const app = alepha.inject(App);
|
|
78
|
+
const drizzleKit = alepha.inject(DrizzleKitProvider);
|
|
79
|
+
|
|
80
|
+
await alepha.start();
|
|
81
|
+
|
|
82
|
+
// Check generated SQL
|
|
83
|
+
const { statements: sql } = await drizzleKit.generateMigration(
|
|
84
|
+
app.repository.provider,
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
expect(sql).toContainEqual(expect.stringContaining("CREATE TABLE"));
|
|
88
|
+
expect(sql).toContainEqual(expect.stringContaining("text_enum_test"));
|
|
89
|
+
// t.enum should map to TEXT, not a real ENUM type
|
|
90
|
+
expect(sql.some((s) => s.includes('"status" text'))).toBe(true);
|
|
91
|
+
expect(sql.some((s) => s.includes('"role" text'))).toBe(true);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("should allow inserting and querying with t.enum values", async () => {
|
|
95
|
+
const alepha = Alepha.create();
|
|
96
|
+
|
|
97
|
+
class App {
|
|
98
|
+
repository = $repository(textEnumEntity);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const app = alepha.inject(App);
|
|
102
|
+
await alepha.start();
|
|
103
|
+
|
|
104
|
+
const created = await app.repository.create({
|
|
105
|
+
status: "pending",
|
|
106
|
+
role: "user",
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
expect(created.status).toBe("pending");
|
|
110
|
+
expect(created.role).toBe("user");
|
|
111
|
+
expect(created.id).toBeDefined();
|
|
112
|
+
|
|
113
|
+
const found = await app.repository.findOne({ where: { id: created.id } });
|
|
114
|
+
expect(found?.status).toBe("pending");
|
|
115
|
+
expect(found?.role).toBe("user");
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe("enums - pg.enum (real PG ENUM type)", () => {
|
|
120
|
+
it("should create real PostgreSQL ENUM types for pg.enum fields", async () => {
|
|
121
|
+
const alepha = Alepha.create();
|
|
122
|
+
|
|
123
|
+
class App {
|
|
124
|
+
repository = $repository(pgEnumEntity);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const app = alepha.inject(App);
|
|
128
|
+
const drizzleKit = alepha.inject(DrizzleKitProvider);
|
|
129
|
+
|
|
130
|
+
await alepha.start();
|
|
131
|
+
|
|
132
|
+
// Check generated SQL
|
|
133
|
+
const { statements: sql } = await drizzleKit.generateMigration(
|
|
134
|
+
app.repository.provider,
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
expect(sql).toContainEqual(expect.stringContaining("CREATE TABLE"));
|
|
138
|
+
expect(sql).toContainEqual(expect.stringContaining("pg_enum_test"));
|
|
139
|
+
// pg.enum should create real ENUM types
|
|
140
|
+
expect(
|
|
141
|
+
sql.some((s) => s.includes("CREATE TYPE") || s.includes("DO $$ BEGIN")),
|
|
142
|
+
).toBe(true);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("should allow inserting and querying with pg.enum values", async () => {
|
|
146
|
+
const alepha = Alepha.create();
|
|
147
|
+
|
|
148
|
+
class App {
|
|
149
|
+
repository = $repository(pgEnumEntity);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const app = alepha.inject(App);
|
|
153
|
+
await alepha.start();
|
|
154
|
+
|
|
155
|
+
const created = await app.repository.create({
|
|
156
|
+
status: "draft",
|
|
157
|
+
priority: "high",
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
expect(created.status).toBe("draft");
|
|
161
|
+
expect(created.priority).toBe("high");
|
|
162
|
+
expect(created.id).toBeDefined();
|
|
163
|
+
|
|
164
|
+
const found = await app.repository.findOne({ where: { id: created.id } });
|
|
165
|
+
expect(found?.status).toBe("draft");
|
|
166
|
+
expect(found?.priority).toBe("high");
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
describe("enums - mixed t.enum and pg.enum in same table", () => {
|
|
171
|
+
it("should handle both TEXT and PG ENUM types in the same table", async () => {
|
|
172
|
+
const alepha = Alepha.create();
|
|
173
|
+
|
|
174
|
+
class App {
|
|
175
|
+
repository = $repository(mixedEnumEntity);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const app = alepha.inject(App);
|
|
179
|
+
const drizzleKit = alepha.inject(DrizzleKitProvider);
|
|
180
|
+
|
|
181
|
+
await alepha.start();
|
|
182
|
+
|
|
183
|
+
// Check generated SQL
|
|
184
|
+
const { statements: sql } = await drizzleKit.generateMigration(
|
|
185
|
+
app.repository.provider,
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
expect(sql).toContainEqual(expect.stringContaining("CREATE TABLE"));
|
|
189
|
+
expect(sql).toContainEqual(expect.stringContaining("mixed_enum_test"));
|
|
190
|
+
// textStatus should be TEXT
|
|
191
|
+
expect(
|
|
192
|
+
sql.some(
|
|
193
|
+
(s) =>
|
|
194
|
+
s.includes('"text_status" text') || s.includes('"textStatus" text'),
|
|
195
|
+
),
|
|
196
|
+
).toBe(true);
|
|
197
|
+
// pgStatus should create an ENUM type
|
|
198
|
+
expect(
|
|
199
|
+
sql.some((s) => s.includes("CREATE TYPE") || s.includes("DO $$ BEGIN")),
|
|
200
|
+
).toBe(true);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it("should allow inserting and querying with mixed enum values", async () => {
|
|
204
|
+
const alepha = Alepha.create();
|
|
205
|
+
|
|
206
|
+
class App {
|
|
207
|
+
repository = $repository(mixedEnumEntity);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const app = alepha.inject(App);
|
|
211
|
+
await alepha.start();
|
|
212
|
+
|
|
213
|
+
const created = await app.repository.create({
|
|
214
|
+
textStatus: "open",
|
|
215
|
+
pgStatus: "new",
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
expect(created.textStatus).toBe("open");
|
|
219
|
+
expect(created.pgStatus).toBe("new");
|
|
220
|
+
expect(created.id).toBeDefined();
|
|
221
|
+
|
|
222
|
+
const found = await app.repository.findOne({ where: { id: created.id } });
|
|
223
|
+
expect(found?.textStatus).toBe("open");
|
|
224
|
+
expect(found?.pgStatus).toBe("new");
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
describe("enums - shared enum with custom name across tables", () => {
|
|
229
|
+
it("should reuse the same PG ENUM type across multiple tables", async () => {
|
|
230
|
+
const alepha = Alepha.create();
|
|
231
|
+
|
|
232
|
+
class App {
|
|
233
|
+
repository1 = $repository(sharedEnumEntity1);
|
|
234
|
+
repository2 = $repository(sharedEnumEntity2);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const app = alepha.inject(App);
|
|
238
|
+
const drizzleKit = alepha.inject(DrizzleKitProvider);
|
|
239
|
+
|
|
240
|
+
await alepha.start();
|
|
241
|
+
|
|
242
|
+
// Check generated SQL
|
|
243
|
+
const { statements: sql } = await drizzleKit.generateMigration(
|
|
244
|
+
app.repository1.provider,
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
// Count how many times the shared_status_enum is created
|
|
248
|
+
// It should only be created once, even though it's used in two tables
|
|
249
|
+
const enumCreations = sql.filter(
|
|
250
|
+
(s) =>
|
|
251
|
+
s.includes("shared_status_enum") &&
|
|
252
|
+
(s.includes("CREATE TYPE") || s.includes("DO $$ BEGIN")),
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
// Should only create the enum type once
|
|
256
|
+
expect(enumCreations.length).toBeLessThanOrEqual(1);
|
|
257
|
+
|
|
258
|
+
// Both tables should exist
|
|
259
|
+
expect(sql.some((s) => s.includes("shared_enum_test_1"))).toBe(true);
|
|
260
|
+
expect(sql.some((s) => s.includes("shared_enum_test_2"))).toBe(true);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it("should allow both tables to use the shared enum values", async () => {
|
|
264
|
+
const alepha = Alepha.create();
|
|
265
|
+
|
|
266
|
+
class App {
|
|
267
|
+
repository1 = $repository(sharedEnumEntity1);
|
|
268
|
+
repository2 = $repository(sharedEnumEntity2);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const app = alepha.inject(App);
|
|
272
|
+
await alepha.start();
|
|
273
|
+
|
|
274
|
+
const created1 = await app.repository1.create({
|
|
275
|
+
status: "enabled",
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
const created2 = await app.repository2.create({
|
|
279
|
+
status: "disabled",
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
expect(created1.status).toBe("enabled");
|
|
283
|
+
expect(created2.status).toBe("disabled");
|
|
284
|
+
|
|
285
|
+
const found1 = await app.repository1.findOne({
|
|
286
|
+
where: { id: created1.id },
|
|
287
|
+
});
|
|
288
|
+
const found2 = await app.repository2.findOne({
|
|
289
|
+
where: { id: created2.id },
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
expect(found1?.status).toBe("enabled");
|
|
293
|
+
expect(found2?.status).toBe("disabled");
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
describe("enums - conflict detection with different values", () => {
|
|
298
|
+
it("should throw error when same enum name has different values", async () => {
|
|
299
|
+
const alepha = Alepha.create();
|
|
300
|
+
|
|
301
|
+
const load = async () => {
|
|
302
|
+
return alepha.with(() => ({
|
|
303
|
+
r1: $repository(conflictEnumEntity1),
|
|
304
|
+
r2: $repository(conflictEnumEntity2),
|
|
305
|
+
}));
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
expect(() =>
|
|
309
|
+
alepha.with(() => ({
|
|
310
|
+
r1: $repository(conflictEnumEntity1),
|
|
311
|
+
r2: $repository(conflictEnumEntity2),
|
|
312
|
+
})),
|
|
313
|
+
).toThrow(/Enum name conflict/i);
|
|
314
|
+
});
|
|
315
|
+
});
|