@tstdl/base 0.93.181 → 0.93.183
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/api/server/api-request-token.provider.js +1 -1
- package/api/server/gateway.js +8 -3
- package/authentication/authentication.api.d.ts +13 -40
- package/authentication/authentication.api.js +5 -14
- package/authentication/client/authentication.service.d.ts +6 -14
- package/authentication/client/authentication.service.js +22 -4
- package/authentication/client/module.d.ts +1 -1
- package/authentication/client/module.js +4 -4
- package/authentication/models/index.d.ts +1 -0
- package/authentication/models/index.js +1 -0
- package/authentication/models/totp-results.model.d.ts +11 -0
- package/authentication/models/totp-results.model.js +37 -0
- package/authentication/server/authentication.api-controller.d.ts +3 -3
- package/authentication/server/authentication.api-controller.js +31 -4
- package/authentication/server/authentication.service.d.ts +5 -14
- package/authentication/server/authentication.service.js +6 -4
- package/core.d.ts +0 -5
- package/core.js +0 -8
- package/document-management/api/document-management.api.d.ts +2 -2
- package/document-management/service-models/document.service-model.d.ts +1 -1
- package/examples/config.d.ts +25 -0
- package/examples/config.js +26 -0
- package/notification/server/module.d.ts +1 -1
- package/notification/server/module.js +1 -1
- package/package.json +5 -5
- package/signals/api.d.ts +5 -1
- package/signals/api.js +3 -1
- package/signals/implementation/api.d.ts +13 -5
- package/signals/implementation/api.js +7 -1
- package/signals/implementation/asserts.d.ts +2 -2
- package/signals/implementation/asserts.js +3 -3
- package/signals/implementation/computed.d.ts +7 -34
- package/signals/implementation/computed.js +14 -83
- package/signals/implementation/configure.js +6 -2
- package/signals/implementation/effect.d.ts +65 -46
- package/signals/implementation/effect.js +97 -62
- package/signals/implementation/index.d.ts +2 -4
- package/signals/implementation/index.js +2 -4
- package/signals/implementation/linked_signal.d.ts +36 -0
- package/signals/implementation/linked_signal.js +34 -0
- package/signals/implementation/primitive/computed.d.ts +55 -0
- package/signals/implementation/primitive/computed.js +107 -0
- package/signals/implementation/primitive/effect.d.ts +26 -0
- package/signals/implementation/primitive/effect.js +31 -0
- package/signals/implementation/{equality.d.ts → primitive/equality.d.ts} +1 -1
- package/signals/implementation/{equality.js → primitive/equality.js} +1 -1
- package/signals/implementation/primitive/errors.d.ts +10 -0
- package/signals/implementation/{errors.js → primitive/errors.js} +3 -4
- package/signals/implementation/primitive/formatter.d.ts +19 -0
- package/signals/implementation/primitive/formatter.js +136 -0
- package/signals/implementation/{graph.d.ts → primitive/graph.d.ts} +68 -36
- package/signals/implementation/primitive/graph.js +386 -0
- package/signals/implementation/primitive/linked_signal.d.ts +46 -0
- package/signals/implementation/primitive/linked_signal.js +110 -0
- package/signals/implementation/primitive/signal.d.ts +31 -0
- package/signals/implementation/primitive/signal.js +80 -0
- package/signals/implementation/primitive/untracked.d.ts +12 -0
- package/signals/implementation/primitive/untracked.js +23 -0
- package/signals/implementation/{watch.d.ts → primitive/watch.d.ts} +1 -2
- package/signals/implementation/{watch.js → primitive/watch.js} +22 -16
- package/signals/implementation/resource/api.d.ts +275 -0
- package/signals/implementation/resource/api.js +26 -0
- package/signals/implementation/resource/debounce.d.ts +13 -0
- package/signals/implementation/resource/debounce.js +113 -0
- package/signals/implementation/resource/from_snapshots.d.ts +16 -0
- package/signals/implementation/resource/from_snapshots.js +44 -0
- package/signals/implementation/resource/index.d.ts +11 -0
- package/signals/implementation/resource/index.js +11 -0
- package/signals/implementation/resource/resource.d.ts +110 -0
- package/signals/implementation/resource/resource.js +402 -0
- package/signals/implementation/root_effect_scheduler.d.ts +50 -0
- package/signals/implementation/root_effect_scheduler.js +66 -0
- package/signals/implementation/signal.d.ts +42 -18
- package/signals/implementation/signal.js +29 -49
- package/signals/implementation/to-observable.d.ts +12 -5
- package/signals/implementation/to-observable.js +12 -2
- package/signals/implementation/to-signal.d.ts +9 -18
- package/signals/implementation/to-signal.js +46 -13
- package/signals/implementation/untracked.d.ts +1 -1
- package/signals/implementation/untracked.js +3 -11
- package/signals/operators/debounce.d.ts +8 -0
- package/signals/operators/debounce.js +19 -0
- package/signals/operators/derive-async.js +43 -15
- package/signals/operators/index.d.ts +2 -0
- package/signals/operators/index.js +2 -0
- package/signals/operators/throttle.d.ts +8 -0
- package/signals/operators/throttle.js +31 -0
- package/ai/genkit/tests/multi-region.test.d.ts +0 -2
- package/ai/genkit/tests/multi-region.test.js +0 -179
- package/ai/genkit/tests/token-limit-fallback.test.d.ts +0 -2
- package/ai/genkit/tests/token-limit-fallback.test.js +0 -209
- package/ai/prompts/tests/prompt-builder.test.d.ts +0 -1
- package/ai/prompts/tests/prompt-builder.test.js +0 -22
- package/ai/tests/instructions-formatter.test.d.ts +0 -1
- package/ai/tests/instructions-formatter.test.js +0 -116
- package/ai/tests/steering.test.d.ts +0 -1
- package/ai/tests/steering.test.js +0 -37
- package/api/client/tests/api-client.test.d.ts +0 -1
- package/api/client/tests/api-client.test.js +0 -194
- package/api/server/tests/csrf.middleware.test.d.ts +0 -1
- package/api/server/tests/csrf.middleware.test.js +0 -91
- package/authentication/tests/authentication-password-requirements.validator.test.d.ts +0 -1
- package/authentication/tests/authentication-password-requirements.validator.test.js +0 -29
- package/authentication/tests/authentication.api-controller.test.d.ts +0 -1
- package/authentication/tests/authentication.api-controller.test.js +0 -156
- package/authentication/tests/authentication.api-request-token.provider.test.d.ts +0 -1
- package/authentication/tests/authentication.api-request-token.provider.test.js +0 -48
- package/authentication/tests/authentication.client-error-handling.test.d.ts +0 -1
- package/authentication/tests/authentication.client-error-handling.test.js +0 -123
- package/authentication/tests/authentication.client-middleware.test.d.ts +0 -1
- package/authentication/tests/authentication.client-middleware.test.js +0 -118
- package/authentication/tests/authentication.client-service-methods.test.d.ts +0 -1
- package/authentication/tests/authentication.client-service-methods.test.js +0 -177
- package/authentication/tests/authentication.client-service-refresh.test.d.ts +0 -1
- package/authentication/tests/authentication.client-service-refresh.test.js +0 -153
- package/authentication/tests/authentication.client-service.test.d.ts +0 -1
- package/authentication/tests/authentication.client-service.test.js +0 -76
- package/authentication/tests/authentication.refresh-busy-loop.test.d.ts +0 -1
- package/authentication/tests/authentication.refresh-busy-loop.test.js +0 -84
- package/authentication/tests/authentication.service.test.d.ts +0 -1
- package/authentication/tests/authentication.service.test.js +0 -167
- package/authentication/tests/authentication.test-ancillary-service.d.ts +0 -9
- package/authentication/tests/authentication.test-ancillary-service.js +0 -27
- package/authentication/tests/brute-force-protection.test.d.ts +0 -1
- package/authentication/tests/brute-force-protection.test.js +0 -211
- package/authentication/tests/helper.test.d.ts +0 -1
- package/authentication/tests/helper.test.js +0 -122
- package/authentication/tests/password-requirements.error.test.d.ts +0 -1
- package/authentication/tests/password-requirements.error.test.js +0 -14
- package/authentication/tests/remember.api.test.d.ts +0 -1
- package/authentication/tests/remember.api.test.js +0 -117
- package/authentication/tests/remember.service.test.d.ts +0 -1
- package/authentication/tests/remember.service.test.js +0 -83
- package/authentication/tests/subject.service.test.d.ts +0 -1
- package/authentication/tests/subject.service.test.js +0 -140
- package/authentication/tests/suspended-subject.test.d.ts +0 -1
- package/authentication/tests/suspended-subject.test.js +0 -120
- package/authentication/tests/totp.enrollment.test.d.ts +0 -1
- package/authentication/tests/totp.enrollment.test.js +0 -123
- package/authentication/tests/totp.login.test.d.ts +0 -1
- package/authentication/tests/totp.login.test.js +0 -213
- package/authentication/tests/totp.recovery-codes.test.d.ts +0 -1
- package/authentication/tests/totp.recovery-codes.test.js +0 -97
- package/authentication/tests/totp.status.test.d.ts +0 -1
- package/authentication/tests/totp.status.test.js +0 -72
- package/cancellation/tests/coverage.test.d.ts +0 -1
- package/cancellation/tests/coverage.test.js +0 -49
- package/cancellation/tests/leak.test.d.ts +0 -1
- package/cancellation/tests/leak.test.js +0 -35
- package/cancellation/tests/token.test.d.ts +0 -1
- package/cancellation/tests/token.test.js +0 -136
- package/circuit-breaker/tests/circuit-breaker.test.d.ts +0 -1
- package/circuit-breaker/tests/circuit-breaker.test.js +0 -116
- package/cryptography/tests/cryptography.test.d.ts +0 -1
- package/cryptography/tests/cryptography.test.js +0 -175
- package/cryptography/tests/jwt.test.d.ts +0 -1
- package/cryptography/tests/jwt.test.js +0 -54
- package/cryptography/tests/modern.test.d.ts +0 -1
- package/cryptography/tests/modern.test.js +0 -105
- package/cryptography/tests/module.test.d.ts +0 -1
- package/cryptography/tests/module.test.js +0 -100
- package/cryptography/tests/totp.test.d.ts +0 -1
- package/cryptography/tests/totp.test.js +0 -108
- package/document-management/tests/ai-config-hierarchy.test.d.ts +0 -1
- package/document-management/tests/ai-config-hierarchy.test.js +0 -59
- package/document-management/tests/ai-config-integration.test.d.ts +0 -1
- package/document-management/tests/ai-config-integration.test.js +0 -125
- package/document-management/tests/ai-config-merge.test.d.ts +0 -1
- package/document-management/tests/ai-config-merge.test.js +0 -46
- package/document-management/tests/document-management-ai-overrides.test.d.ts +0 -1
- package/document-management/tests/document-management-ai-overrides.test.js +0 -63
- package/document-management/tests/document-management-core.test.d.ts +0 -1
- package/document-management/tests/document-management-core.test.js +0 -157
- package/document-management/tests/document-management.api.test.d.ts +0 -1
- package/document-management/tests/document-management.api.test.js +0 -101
- package/document-management/tests/document-statistics.service.test.d.ts +0 -1
- package/document-management/tests/document-statistics.service.test.js +0 -498
- package/document-management/tests/document-validation-ai-overrides.test.d.ts +0 -1
- package/document-management/tests/document-validation-ai-overrides.test.js +0 -87
- package/document-management/tests/document.service.test.d.ts +0 -1
- package/document-management/tests/document.service.test.js +0 -143
- package/document-management/tests/enum-helpers.test.d.ts +0 -1
- package/document-management/tests/enum-helpers.test.js +0 -452
- package/document-management/tests/helper.d.ts +0 -24
- package/document-management/tests/helper.js +0 -39
- package/errors/tests/format.test.d.ts +0 -1
- package/errors/tests/format.test.js +0 -84
- package/http/tests/server-timing.test.d.ts +0 -1
- package/http/tests/server-timing.test.js +0 -42
- package/injector/tests/advanced.test.d.ts +0 -1
- package/injector/tests/advanced.test.js +0 -116
- package/injector/tests/async-init.test.d.ts +0 -1
- package/injector/tests/async-init.test.js +0 -77
- package/injector/tests/basic.test.d.ts +0 -1
- package/injector/tests/basic.test.js +0 -114
- package/injector/tests/hierarchical.test.d.ts +0 -1
- package/injector/tests/hierarchical.test.js +0 -59
- package/injector/tests/leak.test.d.ts +0 -1
- package/injector/tests/leak.test.js +0 -45
- package/injector/tests/lifecycles.test.d.ts +0 -1
- package/injector/tests/lifecycles.test.js +0 -109
- package/logger/tests/pretty-print.test.d.ts +0 -1
- package/logger/tests/pretty-print.test.js +0 -60
- package/notification/tests/notification-api.test.d.ts +0 -1
- package/notification/tests/notification-api.test.js +0 -124
- package/notification/tests/notification-client.test.d.ts +0 -1
- package/notification/tests/notification-client.test.js +0 -101
- package/notification/tests/notification-flow.test.d.ts +0 -1
- package/notification/tests/notification-flow.test.js +0 -296
- package/notification/tests/notification-sse.service.test.d.ts +0 -1
- package/notification/tests/notification-sse.service.test.js +0 -43
- package/notification/tests/notification-type.service.test.d.ts +0 -1
- package/notification/tests/notification-type.service.test.js +0 -41
- package/object-storage/s3/tests/s3.object-storage.integration.test.d.ts +0 -1
- package/object-storage/s3/tests/s3.object-storage.integration.test.js +0 -303
- package/orm/tests/build-jsonb.test.d.ts +0 -1
- package/orm/tests/build-jsonb.test.js +0 -39
- package/orm/tests/data-types.test.d.ts +0 -1
- package/orm/tests/data-types.test.js +0 -39
- package/orm/tests/database-extension.test.d.ts +0 -1
- package/orm/tests/database-extension.test.js +0 -63
- package/orm/tests/database-migration.test.d.ts +0 -1
- package/orm/tests/database-migration.test.js +0 -83
- package/orm/tests/decorators.test.d.ts +0 -1
- package/orm/tests/decorators.test.js +0 -77
- package/orm/tests/encryption.test.d.ts +0 -1
- package/orm/tests/encryption.test.js +0 -31
- package/orm/tests/query-complex.test.d.ts +0 -1
- package/orm/tests/query-complex.test.js +0 -172
- package/orm/tests/query-converter-complex.test.d.ts +0 -1
- package/orm/tests/query-converter-complex.test.js +0 -131
- package/orm/tests/query-converter.test.d.ts +0 -1
- package/orm/tests/query-converter.test.js +0 -123
- package/orm/tests/repository-advanced.test.d.ts +0 -1
- package/orm/tests/repository-advanced.test.js +0 -189
- package/orm/tests/repository-attributes.test.d.ts +0 -1
- package/orm/tests/repository-attributes.test.js +0 -83
- package/orm/tests/repository-compound-primary-key.test.d.ts +0 -2
- package/orm/tests/repository-compound-primary-key.test.js +0 -226
- package/orm/tests/repository-comprehensive.test.d.ts +0 -1
- package/orm/tests/repository-comprehensive.test.js +0 -162
- package/orm/tests/repository-coverage.test.d.ts +0 -2
- package/orm/tests/repository-coverage.test.js +0 -242
- package/orm/tests/repository-cti-complex.test.d.ts +0 -1
- package/orm/tests/repository-cti-complex.test.js +0 -151
- package/orm/tests/repository-cti-embedded.test.d.ts +0 -1
- package/orm/tests/repository-cti-embedded.test.js +0 -178
- package/orm/tests/repository-cti-extensive.test.d.ts +0 -2
- package/orm/tests/repository-cti-extensive.test.js +0 -279
- package/orm/tests/repository-cti-mapping.test.d.ts +0 -2
- package/orm/tests/repository-cti-mapping.test.js +0 -108
- package/orm/tests/repository-cti-search.test.d.ts +0 -1
- package/orm/tests/repository-cti-search.test.js +0 -141
- package/orm/tests/repository-cti-soft-delete.test.d.ts +0 -2
- package/orm/tests/repository-cti-soft-delete.test.js +0 -103
- package/orm/tests/repository-cti-transactions.test.d.ts +0 -1
- package/orm/tests/repository-cti-transactions.test.js +0 -112
- package/orm/tests/repository-cti-upsert-many.test.d.ts +0 -2
- package/orm/tests/repository-cti-upsert-many.test.js +0 -115
- package/orm/tests/repository-cti.test.d.ts +0 -2
- package/orm/tests/repository-cti.test.js +0 -390
- package/orm/tests/repository-edge-cases.test.d.ts +0 -1
- package/orm/tests/repository-edge-cases.test.js +0 -178
- package/orm/tests/repository-expiration.test.d.ts +0 -2
- package/orm/tests/repository-expiration.test.js +0 -140
- package/orm/tests/repository-extra-coverage.test.d.ts +0 -2
- package/orm/tests/repository-extra-coverage.test.js +0 -402
- package/orm/tests/repository-mapping.test.d.ts +0 -2
- package/orm/tests/repository-mapping.test.js +0 -65
- package/orm/tests/repository-regression.test.d.ts +0 -1
- package/orm/tests/repository-regression.test.js +0 -288
- package/orm/tests/repository-search-coverage.test.d.ts +0 -1
- package/orm/tests/repository-search-coverage.test.js +0 -107
- package/orm/tests/repository-search.test.d.ts +0 -1
- package/orm/tests/repository-search.test.js +0 -105
- package/orm/tests/repository-soft-delete.test.d.ts +0 -1
- package/orm/tests/repository-soft-delete.test.js +0 -118
- package/orm/tests/repository-transactions-nested.test.d.ts +0 -1
- package/orm/tests/repository-transactions-nested.test.js +0 -178
- package/orm/tests/repository-types.test.d.ts +0 -1
- package/orm/tests/repository-types.test.js +0 -184
- package/orm/tests/repository-undelete.test.d.ts +0 -2
- package/orm/tests/repository-undelete.test.js +0 -201
- package/orm/tests/schema-converter.test.d.ts +0 -1
- package/orm/tests/schema-converter.test.js +0 -82
- package/orm/tests/schema-generation.test.d.ts +0 -2
- package/orm/tests/schema-generation.test.js +0 -174
- package/orm/tests/sql-helpers.test.d.ts +0 -1
- package/orm/tests/sql-helpers.test.js +0 -67
- package/orm/tests/transaction-safety.test.d.ts +0 -1
- package/orm/tests/transaction-safety.test.js +0 -81
- package/orm/tests/transactional.test.d.ts +0 -1
- package/orm/tests/transactional.test.js +0 -215
- package/orm/tests/utils.test.d.ts +0 -1
- package/orm/tests/utils.test.js +0 -70
- package/pdf/tests/utils.test.d.ts +0 -1
- package/pdf/tests/utils.test.js +0 -187
- package/process/tests/spawn.test.d.ts +0 -1
- package/process/tests/spawn.test.js +0 -182
- package/rate-limit/tests/postgres-rate-limiter.test.d.ts +0 -1
- package/rate-limit/tests/postgres-rate-limiter.test.js +0 -84
- package/renderer/tests/renderer.test.d.ts +0 -1
- package/renderer/tests/renderer.test.js +0 -88
- package/rpc/tests/rpc.integration.test.d.ts +0 -1
- package/rpc/tests/rpc.integration.test.js +0 -615
- package/signals/implementation/errors.d.ts +0 -2
- package/signals/implementation/graph.js +0 -312
- package/signals/implementation/writable-signal.d.ts +0 -48
- package/signals/implementation/writable-signal.js +0 -32
- package/task-queue/tests/coverage-branch.test.d.ts +0 -1
- package/task-queue/tests/coverage-branch.test.js +0 -395
- package/task-queue/tests/coverage-enhancement.test.d.ts +0 -1
- package/task-queue/tests/coverage-enhancement.test.js +0 -150
- package/task-queue/tests/dag.test.d.ts +0 -1
- package/task-queue/tests/dag.test.js +0 -188
- package/task-queue/tests/dependencies.test.d.ts +0 -1
- package/task-queue/tests/dependencies.test.js +0 -296
- package/task-queue/tests/enqueue-batch.test.d.ts +0 -1
- package/task-queue/tests/enqueue-batch.test.js +0 -125
- package/task-queue/tests/enqueue-item.test.d.ts +0 -1
- package/task-queue/tests/enqueue-item.test.js +0 -12
- package/task-queue/tests/fan-out-spawning.test.d.ts +0 -1
- package/task-queue/tests/fan-out-spawning.test.js +0 -94
- package/task-queue/tests/idempotent-replacement.test.d.ts +0 -1
- package/task-queue/tests/idempotent-replacement.test.js +0 -114
- package/task-queue/tests/missing-idempotent-tasks.test.d.ts +0 -1
- package/task-queue/tests/missing-idempotent-tasks.test.js +0 -39
- package/task-queue/tests/optimization-edge-cases.test.d.ts +0 -1
- package/task-queue/tests/optimization-edge-cases.test.js +0 -124
- package/task-queue/tests/queue-generic.test.d.ts +0 -1
- package/task-queue/tests/queue-generic.test.js +0 -8
- package/task-queue/tests/queue.test.d.ts +0 -1
- package/task-queue/tests/queue.test.js +0 -756
- package/task-queue/tests/shutdown.test.d.ts +0 -1
- package/task-queue/tests/shutdown.test.js +0 -41
- package/task-queue/tests/task-context.test.d.ts +0 -1
- package/task-queue/tests/task-context.test.js +0 -7
- package/task-queue/tests/task-union.test.d.ts +0 -1
- package/task-queue/tests/task-union.test.js +0 -18
- package/task-queue/tests/transactions.test.d.ts +0 -1
- package/task-queue/tests/transactions.test.js +0 -47
- package/task-queue/tests/typing.test.d.ts +0 -1
- package/task-queue/tests/typing.test.js +0 -9
- package/task-queue/tests/worker.test.d.ts +0 -1
- package/task-queue/tests/worker.test.js +0 -258
- package/task-queue/tests/zombie-parent.test.d.ts +0 -1
- package/task-queue/tests/zombie-parent.test.js +0 -45
- package/task-queue/tests/zombie-recovery.test.d.ts +0 -1
- package/task-queue/tests/zombie-recovery.test.js +0 -51
- package/utils/tests/backoff.test.d.ts +0 -1
- package/utils/tests/backoff.test.js +0 -41
- package/utils/tests/retry-with-backoff.test.d.ts +0 -1
- package/utils/tests/retry-with-backoff.test.js +0 -49
|
@@ -1,303 +0,0 @@
|
|
|
1
|
-
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
|
2
|
-
import { setupIntegrationTest } from '../../../testing/index.js';
|
|
3
|
-
import { readBinaryStream } from '../../../utils/stream/stream-reader.js';
|
|
4
|
-
import { isUndefined } from '../../../utils/type-guards.js';
|
|
5
|
-
import { S3ObjectStorage } from '../s3.object-storage.js';
|
|
6
|
-
describe('S3ObjectStorage Integration', () => {
|
|
7
|
-
let storage;
|
|
8
|
-
beforeAll(async () => {
|
|
9
|
-
const { injector } = await setupIntegrationTest({
|
|
10
|
-
modules: { objectStorage: true },
|
|
11
|
-
});
|
|
12
|
-
storage = await injector.resolveAsync(S3ObjectStorage, 'test-module');
|
|
13
|
-
});
|
|
14
|
-
afterAll(async () => {
|
|
15
|
-
if (isUndefined(storage)) {
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
const objects = await storage.getObjects();
|
|
19
|
-
for (const obj of objects) {
|
|
20
|
-
await storage.deleteObject(obj.key);
|
|
21
|
-
}
|
|
22
|
-
});
|
|
23
|
-
it('should upload and check existence', async () => {
|
|
24
|
-
const key = 'test-upload.txt';
|
|
25
|
-
const content = new TextEncoder().encode('hello world');
|
|
26
|
-
await storage.uploadObject(key, content, { contentType: 'text/plain' });
|
|
27
|
-
const exists = await storage.exists(key);
|
|
28
|
-
expect(exists).toBe(true);
|
|
29
|
-
const stat = await storage.statObject(key);
|
|
30
|
-
expect(stat.size).toBe(content.length);
|
|
31
|
-
});
|
|
32
|
-
it('should download content', async () => {
|
|
33
|
-
const key = 'test-download.txt';
|
|
34
|
-
const content = new TextEncoder().encode('download me');
|
|
35
|
-
await storage.uploadObject(key, content);
|
|
36
|
-
const downloaded = await storage.getContent(key);
|
|
37
|
-
expect(new TextDecoder().decode(downloaded)).toBe('download me');
|
|
38
|
-
});
|
|
39
|
-
it('should download content as stream', async () => {
|
|
40
|
-
const key = 'test-stream.txt';
|
|
41
|
-
const content = new TextEncoder().encode('stream me');
|
|
42
|
-
await storage.uploadObject(key, content);
|
|
43
|
-
const stream = storage.getContentStream(key);
|
|
44
|
-
const downloaded = await readBinaryStream(stream);
|
|
45
|
-
expect(new TextDecoder().decode(downloaded)).toBe('stream me');
|
|
46
|
-
});
|
|
47
|
-
it('should list objects', async () => {
|
|
48
|
-
const key = 'test-list.txt';
|
|
49
|
-
await storage.uploadObject(key, new TextEncoder().encode('list me'));
|
|
50
|
-
const objects = await storage.getObjects();
|
|
51
|
-
const keys = objects.map((o) => o.key);
|
|
52
|
-
expect(keys).toContain(key);
|
|
53
|
-
});
|
|
54
|
-
it('should copy object', async () => {
|
|
55
|
-
const sourceKey = 'source.txt';
|
|
56
|
-
const destKey = 'dest.txt';
|
|
57
|
-
await storage.uploadObject(sourceKey, new TextEncoder().encode('copy source'));
|
|
58
|
-
await storage.copyObject(sourceKey, destKey);
|
|
59
|
-
const exists = await storage.exists(destKey);
|
|
60
|
-
expect(exists).toBe(true);
|
|
61
|
-
const content = await storage.getContent(destKey);
|
|
62
|
-
expect(new TextDecoder().decode(content)).toBe('copy source');
|
|
63
|
-
});
|
|
64
|
-
it('should move object', async () => {
|
|
65
|
-
const sourceKey = 'move-source.txt';
|
|
66
|
-
const destKey = 'move-dest.txt';
|
|
67
|
-
await storage.uploadObject(sourceKey, new TextEncoder().encode('move me'));
|
|
68
|
-
await storage.moveObject(sourceKey, destKey);
|
|
69
|
-
expect(await storage.exists(sourceKey)).toBe(false);
|
|
70
|
-
expect(await storage.exists(destKey)).toBe(true);
|
|
71
|
-
});
|
|
72
|
-
it('should generate signed download URL', async () => {
|
|
73
|
-
const key = 'signed-download.txt';
|
|
74
|
-
await storage.uploadObject(key, new TextEncoder().encode('signed download'));
|
|
75
|
-
const url = await storage.getDownloadUrl(key, Date.now() + 60000);
|
|
76
|
-
expect(url).toMatch(/http:\/\/(127\.0\.0\.1|localhost):19552/);
|
|
77
|
-
const response = await fetch(url);
|
|
78
|
-
expect(response.status).toBe(200);
|
|
79
|
-
expect(await response.text()).toBe('signed download');
|
|
80
|
-
});
|
|
81
|
-
it('should generate signed upload URL', async () => {
|
|
82
|
-
const key = 'signed-upload.txt';
|
|
83
|
-
const url = await storage.getUploadUrl(key, Date.now() + 60000, { contentType: 'text/plain' });
|
|
84
|
-
const content = 'upload via signed url';
|
|
85
|
-
const response = await fetch(url, {
|
|
86
|
-
method: 'PUT',
|
|
87
|
-
body: content,
|
|
88
|
-
headers: { 'Content-Type': 'text/plain' },
|
|
89
|
-
});
|
|
90
|
-
expect(response.status).toBe(200);
|
|
91
|
-
expect(await storage.exists(key)).toBe(true);
|
|
92
|
-
expect(new TextDecoder().decode(await storage.getContent(key))).toBe(content);
|
|
93
|
-
});
|
|
94
|
-
it('should configure bucket lifecycle', async () => {
|
|
95
|
-
await storage.configureBucket({
|
|
96
|
-
lifecycle: {
|
|
97
|
-
expiration: {
|
|
98
|
-
after: 86400, // 1 day
|
|
99
|
-
},
|
|
100
|
-
},
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
it('should delete objects', async () => {
|
|
104
|
-
const key1 = 'delete1.txt';
|
|
105
|
-
const key2 = 'delete2.txt';
|
|
106
|
-
await storage.uploadObject(key1, new Uint8Array([1]));
|
|
107
|
-
await storage.uploadObject(key2, new Uint8Array([2]));
|
|
108
|
-
await storage.deleteObjects([key1, key2]);
|
|
109
|
-
expect(await storage.exists(key1)).toBe(false);
|
|
110
|
-
expect(await storage.exists(key2)).toBe(false);
|
|
111
|
-
});
|
|
112
|
-
it('should handle non-existent objects gracefully', async () => {
|
|
113
|
-
const key = `non-existent-${Math.random()}`;
|
|
114
|
-
expect(await storage.exists(key)).toBe(false);
|
|
115
|
-
await expect(storage.statObject(key)).rejects.toThrow();
|
|
116
|
-
await expect(storage.getContent(key)).rejects.toThrow();
|
|
117
|
-
});
|
|
118
|
-
it('should handle large uploads via streams', async () => {
|
|
119
|
-
const key = 'large-stream.bin';
|
|
120
|
-
const size = 10 * 1024 * 1024; // 10MB
|
|
121
|
-
const content = new Uint8Array(size).fill(0x42);
|
|
122
|
-
const stream = new ReadableStream({
|
|
123
|
-
start(controller) {
|
|
124
|
-
controller.enqueue(content);
|
|
125
|
-
controller.close();
|
|
126
|
-
},
|
|
127
|
-
});
|
|
128
|
-
await storage.uploadObject(key, stream, { contentLength: size, contentType: 'application/octet-stream' });
|
|
129
|
-
const stat = await storage.statObject(key);
|
|
130
|
-
expect(stat.size).toBe(size);
|
|
131
|
-
const downloaded = await storage.getContent(key);
|
|
132
|
-
expect(downloaded.length).toBe(size);
|
|
133
|
-
expect(downloaded[0]).toBe(0x42);
|
|
134
|
-
});
|
|
135
|
-
it('should preserve and update metadata', async () => {
|
|
136
|
-
const key = 'metadata.txt';
|
|
137
|
-
const metadata = { 'test-key': 'test-value', 'another-key': 'another-value' };
|
|
138
|
-
await storage.uploadObject(key, new TextEncoder().encode('metadata content'), { metadata });
|
|
139
|
-
let stat = await storage.statObject(key);
|
|
140
|
-
expect(stat.metadata).toMatchObject(metadata);
|
|
141
|
-
const destKey = 'metadata-copy.txt';
|
|
142
|
-
const newMetadata = { 'test-key': 'updated-value', 'extra-key': 'extra-value' };
|
|
143
|
-
await storage.copyObject(key, destKey, { metadata: newMetadata });
|
|
144
|
-
stat = await storage.statObject(destKey);
|
|
145
|
-
expect(stat.metadata['test-key']).toBe('updated-value');
|
|
146
|
-
expect(stat.metadata['another-key']).toBe('another-value');
|
|
147
|
-
expect(stat.metadata['extra-key']).toBe('extra-value');
|
|
148
|
-
});
|
|
149
|
-
it('should work with bucket per module', async () => {
|
|
150
|
-
const { injector } = await setupIntegrationTest({
|
|
151
|
-
modules: { objectStorage: true },
|
|
152
|
-
});
|
|
153
|
-
const moduleName = `test-bucket-per-module-${Math.floor(Math.random() * 1000000)}`;
|
|
154
|
-
const perModuleStorage = await injector.resolveAsync(S3ObjectStorage, moduleName);
|
|
155
|
-
const key = 'test.txt';
|
|
156
|
-
await perModuleStorage.uploadObject(key, new TextEncoder().encode('hello'));
|
|
157
|
-
expect(await perModuleStorage.exists(key)).toBe(true);
|
|
158
|
-
await perModuleStorage.deleteObject(key);
|
|
159
|
-
});
|
|
160
|
-
it('should cover S3Object methods', async () => {
|
|
161
|
-
const key = 's3-object-test.txt';
|
|
162
|
-
const content = new TextEncoder().encode('s3 object');
|
|
163
|
-
const metadata = { 's3-test': 'true' };
|
|
164
|
-
await storage.uploadObject(key, content, { metadata });
|
|
165
|
-
const obj = await storage.getObject(key);
|
|
166
|
-
expect(await obj.getResourceUri()).toBe(`s3://test-module/${key}`);
|
|
167
|
-
expect(await obj.getContentLength()).toBe(content.length);
|
|
168
|
-
expect(await obj.getMetadata()).toMatchObject(metadata);
|
|
169
|
-
expect(new TextDecoder().decode(await obj.getContent())).toBe('s3 object');
|
|
170
|
-
const stream = obj.getContentStream();
|
|
171
|
-
const downloaded = await readBinaryStream(stream);
|
|
172
|
-
expect(new TextDecoder().decode(downloaded)).toBe('s3 object');
|
|
173
|
-
});
|
|
174
|
-
it('should handle bucket configuration with existing rules', async () => {
|
|
175
|
-
await storage.configureBucket({
|
|
176
|
-
lifecycle: {
|
|
177
|
-
expiration: {
|
|
178
|
-
after: 86400 * 2, // 2 days
|
|
179
|
-
},
|
|
180
|
-
},
|
|
181
|
-
});
|
|
182
|
-
await storage.configureBucket({
|
|
183
|
-
lifecycle: {
|
|
184
|
-
expiration: {
|
|
185
|
-
after: 86400 * 2, // same
|
|
186
|
-
},
|
|
187
|
-
},
|
|
188
|
-
});
|
|
189
|
-
await storage.configureBucket({
|
|
190
|
-
lifecycle: {
|
|
191
|
-
expiration: {
|
|
192
|
-
after: undefined, // remove
|
|
193
|
-
},
|
|
194
|
-
},
|
|
195
|
-
});
|
|
196
|
-
});
|
|
197
|
-
it('should handle Forbidden error in ensureBucketExists', async () => {
|
|
198
|
-
// We can't easily trigger a real Forbidden error without complex setup,
|
|
199
|
-
// but we can mock the client for this specific test case if needed.
|
|
200
|
-
// However, let's try to trigger other branches first.
|
|
201
|
-
});
|
|
202
|
-
it('should handle different upload content types (Uint8Array)', async () => {
|
|
203
|
-
const key = 'uint8-upload.txt';
|
|
204
|
-
const content = new Uint8Array([72, 101, 108, 108, 111]); // "Hello"
|
|
205
|
-
await storage.uploadObject(key, content);
|
|
206
|
-
expect(await storage.exists(key)).toBe(true);
|
|
207
|
-
});
|
|
208
|
-
it('should handle Blob in getContentStream', async () => {
|
|
209
|
-
// This depends on the environment (Node.js vs Browser) and how S3 SDK returns the body.
|
|
210
|
-
// In our current Node.js setup, it usually returns a Readable.
|
|
211
|
-
});
|
|
212
|
-
it('should cover moveObject with string source', async () => {
|
|
213
|
-
const sourceKey = 'move-str-source.txt';
|
|
214
|
-
const destKey = 'move-str-dest.txt';
|
|
215
|
-
await storage.uploadObject(sourceKey, new TextEncoder().encode('move me str'));
|
|
216
|
-
await storage.moveObject(sourceKey, destKey);
|
|
217
|
-
expect(await storage.exists(destKey)).toBe(true);
|
|
218
|
-
});
|
|
219
|
-
it('should cover copyObject with S3Object source', async () => {
|
|
220
|
-
const sourceKey = 'copy-obj-source.txt';
|
|
221
|
-
const destKey = 'copy-obj-dest.txt';
|
|
222
|
-
await storage.uploadObject(sourceKey, new TextEncoder().encode('copy me obj'));
|
|
223
|
-
const sourceObj = await storage.getObject(sourceKey);
|
|
224
|
-
await storage.copyObject(sourceObj, destKey);
|
|
225
|
-
expect(await storage.exists(destKey)).toBe(true);
|
|
226
|
-
});
|
|
227
|
-
it('should generate signed download URL with Expires header', async () => {
|
|
228
|
-
const key = 'signed-download-expires.txt';
|
|
229
|
-
await storage.uploadObject(key, new TextEncoder().encode('signed download expires'));
|
|
230
|
-
const url = await storage.getDownloadUrl(key, Date.now() + 60000, {
|
|
231
|
-
Expires: new Date(Date.now() + 60000).toUTCString(),
|
|
232
|
-
});
|
|
233
|
-
expect(url).toMatch(/http:\/\/(127\.0\.0\.1|localhost):19552/);
|
|
234
|
-
const response = await fetch(url);
|
|
235
|
-
expect(response.status).toBe(200);
|
|
236
|
-
});
|
|
237
|
-
it('should handle Forbidden error in ensureBucketExists with wrong credentials', async () => {
|
|
238
|
-
const { injector } = await setupIntegrationTest({
|
|
239
|
-
modules: { objectStorage: true },
|
|
240
|
-
s3: {
|
|
241
|
-
accessKey: 'wrong',
|
|
242
|
-
secretKey: 'wrong',
|
|
243
|
-
},
|
|
244
|
-
});
|
|
245
|
-
try {
|
|
246
|
-
await injector.resolveAsync(S3ObjectStorage, 'forbidden-bucket');
|
|
247
|
-
expect.fail('Should have thrown');
|
|
248
|
-
}
|
|
249
|
-
catch (error) {
|
|
250
|
-
expect(error).toBeDefined();
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
it('should copy object between different storages', async () => {
|
|
254
|
-
const { injector } = await setupIntegrationTest({
|
|
255
|
-
modules: { objectStorage: true },
|
|
256
|
-
});
|
|
257
|
-
const anotherStorage = await injector.resolveAsync(S3ObjectStorage, 'another-bucket');
|
|
258
|
-
const sourceKey = 'cross-storage-source.txt';
|
|
259
|
-
const destKey = 'cross-storage-dest.txt';
|
|
260
|
-
await storage.uploadObject(sourceKey, new TextEncoder().encode('cross storage content'));
|
|
261
|
-
await storage.copyObject(sourceKey, [anotherStorage, destKey]);
|
|
262
|
-
expect(await anotherStorage.exists(destKey)).toBe(true);
|
|
263
|
-
const content = await anotherStorage.getContent(destKey);
|
|
264
|
-
expect(new TextDecoder().decode(content)).toBe('cross storage content');
|
|
265
|
-
});
|
|
266
|
-
it('should cover ensureBucketExists with region', async () => {
|
|
267
|
-
const { injector } = await setupIntegrationTest({
|
|
268
|
-
modules: { objectStorage: true },
|
|
269
|
-
});
|
|
270
|
-
const perModuleStorage = await injector.resolveAsync(S3ObjectStorage, `region-test-${Math.floor(Math.random() * 1000000)}`);
|
|
271
|
-
await perModuleStorage.ensureBucketExists('us-east-1', { objectLocking: true });
|
|
272
|
-
});
|
|
273
|
-
it('should cover moveObject with S3Object source', async () => {
|
|
274
|
-
const sourceKey = 'move-obj-source.txt';
|
|
275
|
-
const destKey = 'move-obj-dest.txt';
|
|
276
|
-
await storage.uploadObject(sourceKey, new TextEncoder().encode('move me obj'));
|
|
277
|
-
const sourceObj = await storage.getObject(sourceKey);
|
|
278
|
-
await storage.moveObject(sourceObj, destKey);
|
|
279
|
-
expect(await storage.exists(destKey)).toBe(true);
|
|
280
|
-
expect(await storage.exists(sourceKey)).toBe(false);
|
|
281
|
-
});
|
|
282
|
-
it('should cover S3Object methods more extensively', async () => {
|
|
283
|
-
const key = 's3-object-ext.txt';
|
|
284
|
-
await storage.uploadObject(key, new TextEncoder().encode('ext'));
|
|
285
|
-
const obj = await storage.getObject(key);
|
|
286
|
-
// Trigger lazy loading of contentLength and metadata
|
|
287
|
-
expect(await obj.getContentLength()).toBe(3);
|
|
288
|
-
expect(await obj.getMetadata()).toBeDefined();
|
|
289
|
-
// Trigger cached values
|
|
290
|
-
expect(await obj.getContentLength()).toBe(3);
|
|
291
|
-
expect(await obj.getMetadata()).toBeDefined();
|
|
292
|
-
});
|
|
293
|
-
it('should cover error branches in configureBucket', async () => {
|
|
294
|
-
// Already did some, but let's try more variations
|
|
295
|
-
await storage.configureBucket({
|
|
296
|
-
lifecycle: {
|
|
297
|
-
expiration: {
|
|
298
|
-
after: 86400 * 3,
|
|
299
|
-
},
|
|
300
|
-
},
|
|
301
|
-
});
|
|
302
|
-
});
|
|
303
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { PgDialect } from 'drizzle-orm/pg-core';
|
|
2
|
-
import { describe, expect, test } from 'vitest';
|
|
3
|
-
import { buildJsonb } from '../sqls/sqls.js';
|
|
4
|
-
describe('buildJsonb', () => {
|
|
5
|
-
const dialect = new PgDialect();
|
|
6
|
-
test('should build jsonb from simple object', () => {
|
|
7
|
-
const query = buildJsonb({ a: 1, b: 'foo' });
|
|
8
|
-
const { sql, params } = dialect.sqlToQuery(query);
|
|
9
|
-
expect(sql).toBe('jsonb_build_object($1::text, to_jsonb($2::numeric), $3::text, to_jsonb($4::text))');
|
|
10
|
-
expect(params).toEqual(['a', 1, 'b', 'foo']);
|
|
11
|
-
});
|
|
12
|
-
test('should build jsonb from object with non-simple keys', () => {
|
|
13
|
-
const query = buildJsonb({ 'Betriebs-Nr.': '18182952' });
|
|
14
|
-
const { sql, params } = dialect.sqlToQuery(query);
|
|
15
|
-
// This is what failed before: it lacked the ::text cast
|
|
16
|
-
expect(sql).toBe('jsonb_build_object($1::text, to_jsonb($2::text))');
|
|
17
|
-
expect(params).toEqual(['Betriebs-Nr.', '18182952']);
|
|
18
|
-
});
|
|
19
|
-
test('should build jsonb from nested structures', () => {
|
|
20
|
-
const query = buildJsonb({
|
|
21
|
-
additionalData: { 'Betriebs-Nr.': '18182952' },
|
|
22
|
-
tags: ['a', 'b'],
|
|
23
|
-
});
|
|
24
|
-
const { sql, params } = dialect.sqlToQuery(query);
|
|
25
|
-
expect(sql).toBe('jsonb_build_object($1::text, jsonb_build_object($2::text, to_jsonb($3::text)), $4::text, jsonb_build_array(to_jsonb($5::text), to_jsonb($6::text)))');
|
|
26
|
-
expect(params).toEqual(['additionalData', 'Betriebs-Nr.', '18182952', 'tags', 'a', 'b']);
|
|
27
|
-
});
|
|
28
|
-
test('should handle numbers correctly', () => {
|
|
29
|
-
const query = buildJsonb({ score: 0.5 });
|
|
30
|
-
const { sql, params } = dialect.sqlToQuery(query);
|
|
31
|
-
expect(sql).toBe('jsonb_build_object($1::text, to_jsonb($2::numeric))');
|
|
32
|
-
expect(params).toEqual(['score', 0.5]);
|
|
33
|
-
});
|
|
34
|
-
test('should handle null and empty structures', () => {
|
|
35
|
-
expect(dialect.sqlToQuery(buildJsonb(null)).sql).toBe('\'null\'::jsonb');
|
|
36
|
-
expect(dialect.sqlToQuery(buildJsonb({})).sql).toBe('\'{}\'::jsonb');
|
|
37
|
-
expect(dialect.sqlToQuery(buildJsonb([])).sql).toBe('\'[]\'::jsonb');
|
|
38
|
-
});
|
|
39
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'vitest';
|
|
2
|
-
import { dateToNumericDate, numericDateToDateObject } from '../../utils/date-time.js';
|
|
3
|
-
describe('ORM Data Types - numericDate Logic', () => {
|
|
4
|
-
test('dateToNumericDate should return days since epoch', () => {
|
|
5
|
-
const date = new Date(Date.UTC(2024, 0, 1));
|
|
6
|
-
expect(dateToNumericDate(date)).toBe(19723);
|
|
7
|
-
});
|
|
8
|
-
test('numericDateToDateObject should convert days since epoch to date components', () => {
|
|
9
|
-
const { year, month, day } = numericDateToDateObject(19723);
|
|
10
|
-
expect(year).toBe(2024);
|
|
11
|
-
expect(month).toBe(1);
|
|
12
|
-
expect(day).toBe(1);
|
|
13
|
-
});
|
|
14
|
-
test('leap year handling (2024-02-29)', () => {
|
|
15
|
-
const leapDate = new Date(Date.UTC(2024, 1, 29));
|
|
16
|
-
const numeric = dateToNumericDate(leapDate);
|
|
17
|
-
expect(numeric).toBe(19782);
|
|
18
|
-
const { year, month, day } = numericDateToDateObject(19782);
|
|
19
|
-
expect(year).toBe(2024);
|
|
20
|
-
expect(month).toBe(2);
|
|
21
|
-
expect(day).toBe(29);
|
|
22
|
-
});
|
|
23
|
-
test('epoch start (1970-01-01)', () => {
|
|
24
|
-
const epoch = new Date(Date.UTC(1970, 0, 1));
|
|
25
|
-
expect(dateToNumericDate(epoch)).toBe(0);
|
|
26
|
-
const { year, month, day } = numericDateToDateObject(0);
|
|
27
|
-
expect(year).toBe(1970);
|
|
28
|
-
expect(month).toBe(1);
|
|
29
|
-
expect(day).toBe(1);
|
|
30
|
-
});
|
|
31
|
-
test('before epoch (1969-12-31)', () => {
|
|
32
|
-
const beforeEpoch = new Date(Date.UTC(1969, 11, 31));
|
|
33
|
-
expect(dateToNumericDate(beforeEpoch)).toBe(-1);
|
|
34
|
-
const { year, month, day } = numericDateToDateObject(-1);
|
|
35
|
-
expect(year).toBe(1969);
|
|
36
|
-
expect(month).toBe(12);
|
|
37
|
-
expect(day).toBe(31);
|
|
38
|
-
});
|
|
39
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { sql } from 'drizzle-orm';
|
|
2
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
-
import { APPLICATION_INITIALIZER } from '../../application/application.js';
|
|
4
|
-
import { runInInjectionContext } from '../../injector/inject.js';
|
|
5
|
-
import { bootstrapOrm, provideOrm } from '../../orm/server/bootstrap.js';
|
|
6
|
-
import { Database } from '../../orm/server/database.js';
|
|
7
|
-
import { DATABASE_EXTENSION, registerDatabaseExtension, runDatabaseExtensions } from '../../orm/server/extension.js';
|
|
8
|
-
import { DATABASE_MIGRATION } from '../../orm/server/migration.js';
|
|
9
|
-
import { setupIntegrationTest } from '../../testing/index.js';
|
|
10
|
-
describe('Database Extension Registration', () => {
|
|
11
|
-
it('should have DATABASE_EXTENSION token defined', () => {
|
|
12
|
-
expect(DATABASE_EXTENSION).toBeDefined();
|
|
13
|
-
});
|
|
14
|
-
it('should provide ORM bootstrap as application initializer', () => {
|
|
15
|
-
const provider = provideOrm();
|
|
16
|
-
expect(provider.provide).toBe(APPLICATION_INITIALIZER);
|
|
17
|
-
expect(provider.useValue).toBe(bootstrapOrm);
|
|
18
|
-
expect(provider.multi).toBe(true);
|
|
19
|
-
});
|
|
20
|
-
it('should register an extension', async () => {
|
|
21
|
-
const { injector } = await setupIntegrationTest();
|
|
22
|
-
registerDatabaseExtension('btree_gin', { injector });
|
|
23
|
-
const extensions = injector.resolveAll(DATABASE_EXTENSION);
|
|
24
|
-
expect(extensions).toHaveLength(1);
|
|
25
|
-
expect(extensions[0].name).toBe('btree_gin');
|
|
26
|
-
});
|
|
27
|
-
it('should run extensions', async () => {
|
|
28
|
-
const { injector } = await setupIntegrationTest();
|
|
29
|
-
const database = injector.resolve(Database);
|
|
30
|
-
const executeSpy = vi.spyOn(database, 'execute').mockResolvedValue({});
|
|
31
|
-
registerDatabaseExtension('btree_gin', { injector });
|
|
32
|
-
registerDatabaseExtension('uuid-ossp', { injector });
|
|
33
|
-
await runInInjectionContext(injector, async () => {
|
|
34
|
-
await runDatabaseExtensions();
|
|
35
|
-
});
|
|
36
|
-
expect(executeSpy).toHaveBeenCalledWith(sql.raw('CREATE EXTENSION IF NOT EXISTS "btree_gin"'));
|
|
37
|
-
expect(executeSpy).toHaveBeenCalledWith(sql.raw('CREATE EXTENSION IF NOT EXISTS "uuid-ossp"'));
|
|
38
|
-
});
|
|
39
|
-
it('should bootstrap ORM (extensions and migrations)', async () => {
|
|
40
|
-
const { injector } = await setupIntegrationTest();
|
|
41
|
-
const database = injector.resolve(Database);
|
|
42
|
-
const executeSpy = vi.spyOn(database, 'execute').mockResolvedValue({});
|
|
43
|
-
registerDatabaseExtension('btree_gin', { injector });
|
|
44
|
-
let migrated = false;
|
|
45
|
-
const migration = {
|
|
46
|
-
name: 'test-migration',
|
|
47
|
-
migrate: async () => {
|
|
48
|
-
migrated = true;
|
|
49
|
-
},
|
|
50
|
-
};
|
|
51
|
-
injector.register(DATABASE_MIGRATION, { useValue: migration }, { multi: true });
|
|
52
|
-
await runInInjectionContext(injector, async () => {
|
|
53
|
-
await bootstrapOrm();
|
|
54
|
-
});
|
|
55
|
-
// Check extension call
|
|
56
|
-
expect(executeSpy).toHaveBeenCalledWith(sql.raw('CREATE EXTENSION IF NOT EXISTS "btree_gin"'));
|
|
57
|
-
// Check migration ran
|
|
58
|
-
expect(migrated).toBe(true);
|
|
59
|
-
// Check locking
|
|
60
|
-
expect(executeSpy).toHaveBeenCalledWith(sql `SELECT pg_advisory_lock(${123456789})`);
|
|
61
|
-
expect(executeSpy).toHaveBeenCalledWith(sql `SELECT pg_advisory_unlock(${123456789})`);
|
|
62
|
-
});
|
|
63
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { APPLICATION_INITIALIZER } from '../../application/application.js';
|
|
2
|
-
import { runInInjectionContext } from '../../injector/inject.js';
|
|
3
|
-
import { bootstrapOrm, provideOrm } from '../../orm/server/bootstrap.js';
|
|
4
|
-
import { DATABASE_MIGRATION } from '../../orm/server/migration.js';
|
|
5
|
-
import { setupIntegrationTest } from '../../testing/index.js';
|
|
6
|
-
import { describe, expect, it } from 'vitest';
|
|
7
|
-
describe('Database Migration Orchestration', () => {
|
|
8
|
-
it('should have DATABASE_MIGRATION token defined', () => {
|
|
9
|
-
expect(DATABASE_MIGRATION).toBeDefined();
|
|
10
|
-
});
|
|
11
|
-
it('should provide ORM bootstrap as application initializer', () => {
|
|
12
|
-
const provider = provideOrm();
|
|
13
|
-
expect(provider.provide).toBe(APPLICATION_INITIALIZER);
|
|
14
|
-
expect(provider.useValue).toBe(bootstrapOrm);
|
|
15
|
-
expect(provider.multi).toBe(true);
|
|
16
|
-
});
|
|
17
|
-
it('should register and resolve a migration', async () => {
|
|
18
|
-
const { injector } = await setupIntegrationTest();
|
|
19
|
-
let migrated = false;
|
|
20
|
-
const migration = {
|
|
21
|
-
name: 'test-migration',
|
|
22
|
-
migrate: async () => {
|
|
23
|
-
migrated = true;
|
|
24
|
-
},
|
|
25
|
-
};
|
|
26
|
-
injector.register(DATABASE_MIGRATION, { useValue: migration }, { multi: true });
|
|
27
|
-
const migrations = injector.resolveAll(DATABASE_MIGRATION);
|
|
28
|
-
expect(migrations).toHaveLength(1);
|
|
29
|
-
const firstMigration = migrations[0];
|
|
30
|
-
expect(firstMigration.name).toBe('test-migration');
|
|
31
|
-
await firstMigration.migrate();
|
|
32
|
-
expect(migrated).toBe(true);
|
|
33
|
-
});
|
|
34
|
-
it('should run migrations in the correct order based on dependencies', async () => {
|
|
35
|
-
const { injector } = await setupIntegrationTest();
|
|
36
|
-
const executionOrder = [];
|
|
37
|
-
const migrationA = {
|
|
38
|
-
name: 'migration-a',
|
|
39
|
-
migrate: async () => {
|
|
40
|
-
executionOrder.push('a');
|
|
41
|
-
},
|
|
42
|
-
dependencies: ['migration-b'],
|
|
43
|
-
};
|
|
44
|
-
const migrationB = {
|
|
45
|
-
name: 'migration-b',
|
|
46
|
-
migrate: async () => {
|
|
47
|
-
executionOrder.push('b');
|
|
48
|
-
},
|
|
49
|
-
};
|
|
50
|
-
const migrationC = {
|
|
51
|
-
name: 'migration-c',
|
|
52
|
-
migrate: async () => {
|
|
53
|
-
executionOrder.push('c');
|
|
54
|
-
},
|
|
55
|
-
dependencies: ['migration-a'],
|
|
56
|
-
};
|
|
57
|
-
injector.register(DATABASE_MIGRATION, { useValue: migrationA }, { multi: true });
|
|
58
|
-
injector.register(DATABASE_MIGRATION, { useValue: migrationB }, { multi: true });
|
|
59
|
-
injector.register(DATABASE_MIGRATION, { useValue: migrationC }, { multi: true });
|
|
60
|
-
await runInInjectionContext(injector, async () => {
|
|
61
|
-
await bootstrapOrm();
|
|
62
|
-
});
|
|
63
|
-
expect(executionOrder).toEqual(['b', 'a', 'c']);
|
|
64
|
-
});
|
|
65
|
-
it('should throw on circular dependency', async () => {
|
|
66
|
-
const { injector } = await setupIntegrationTest();
|
|
67
|
-
const migrationA = {
|
|
68
|
-
name: 'migration-a',
|
|
69
|
-
migrate: async () => { },
|
|
70
|
-
dependencies: ['migration-b'],
|
|
71
|
-
};
|
|
72
|
-
const migrationB = {
|
|
73
|
-
name: 'migration-b',
|
|
74
|
-
migrate: async () => { },
|
|
75
|
-
dependencies: ['migration-a'],
|
|
76
|
-
};
|
|
77
|
-
injector.register(DATABASE_MIGRATION, { useValue: migrationA }, { multi: true });
|
|
78
|
-
injector.register(DATABASE_MIGRATION, { useValue: migrationB }, { multi: true });
|
|
79
|
-
await runInInjectionContext(injector, async () => {
|
|
80
|
-
await expect(bootstrapOrm()).rejects.toThrow('Circular dependency detected');
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
-
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
-
};
|
|
7
|
-
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
-
};
|
|
10
|
-
import { describe, expect, test } from 'vitest';
|
|
11
|
-
import { reflectionRegistry } from '../../reflection/index.js';
|
|
12
|
-
import { Entity } from '../entity.js';
|
|
13
|
-
import { Inheritance, ChildEntity, Column } from '../decorators.js';
|
|
14
|
-
import { StringProperty } from '../../schema/index.js';
|
|
15
|
-
describe('ORM Inheritance Decorators', () => {
|
|
16
|
-
test('should register inheritance metadata on parent', () => {
|
|
17
|
-
let BaseUser = class BaseUser extends Entity {
|
|
18
|
-
kind;
|
|
19
|
-
};
|
|
20
|
-
__decorate([
|
|
21
|
-
StringProperty(),
|
|
22
|
-
Column({}),
|
|
23
|
-
__metadata("design:type", String)
|
|
24
|
-
], BaseUser.prototype, "kind", void 0);
|
|
25
|
-
BaseUser = __decorate([
|
|
26
|
-
Inheritance({ strategy: 'joined', discriminatorColumn: 'kind' })
|
|
27
|
-
], BaseUser);
|
|
28
|
-
const metadata = reflectionRegistry.getMetadata(BaseUser);
|
|
29
|
-
const ormData = metadata?.data.tryGet('orm');
|
|
30
|
-
expect(ormData?.inheritance).toBeDefined();
|
|
31
|
-
expect(ormData?.inheritance?.discriminatorColumn).toBe('kind');
|
|
32
|
-
expect(ormData?.inheritance?.subclasses).toBeInstanceOf(Array);
|
|
33
|
-
});
|
|
34
|
-
test('should register child entity metadata and link to parent', () => {
|
|
35
|
-
let Vehicle = class Vehicle extends Entity {
|
|
36
|
-
type;
|
|
37
|
-
};
|
|
38
|
-
__decorate([
|
|
39
|
-
StringProperty(),
|
|
40
|
-
Column({}),
|
|
41
|
-
__metadata("design:type", String)
|
|
42
|
-
], Vehicle.prototype, "type", void 0);
|
|
43
|
-
Vehicle = __decorate([
|
|
44
|
-
Inheritance({ strategy: 'joined', discriminatorColumn: 'type' })
|
|
45
|
-
], Vehicle);
|
|
46
|
-
let Car = class Car extends Vehicle {
|
|
47
|
-
};
|
|
48
|
-
Car = __decorate([
|
|
49
|
-
ChildEntity('car')
|
|
50
|
-
], Car);
|
|
51
|
-
const parentMetadata = reflectionRegistry.getMetadata(Vehicle);
|
|
52
|
-
const parentOrmData = parentMetadata?.data.tryGet('orm');
|
|
53
|
-
const childMetadata = reflectionRegistry.getMetadata(Car);
|
|
54
|
-
const childOrmData = childMetadata?.data.tryGet('orm');
|
|
55
|
-
// Check Child Metadata
|
|
56
|
-
expect(childOrmData?.childEntity).toBeDefined();
|
|
57
|
-
expect(childOrmData?.childEntity?.discriminatorValue).toBe('car');
|
|
58
|
-
// Check Parent Link
|
|
59
|
-
expect(parentOrmData?.inheritance?.subclasses).toContain(Car);
|
|
60
|
-
});
|
|
61
|
-
test('should use default discriminator column if not provided', () => {
|
|
62
|
-
let Animal = class Animal extends Entity {
|
|
63
|
-
type;
|
|
64
|
-
};
|
|
65
|
-
__decorate([
|
|
66
|
-
StringProperty(),
|
|
67
|
-
Column({}),
|
|
68
|
-
__metadata("design:type", String)
|
|
69
|
-
], Animal.prototype, "type", void 0);
|
|
70
|
-
Animal = __decorate([
|
|
71
|
-
Inheritance({ strategy: 'joined' })
|
|
72
|
-
], Animal);
|
|
73
|
-
const metadata = reflectionRegistry.getMetadata(Animal);
|
|
74
|
-
const ormData = metadata?.data.tryGet('orm');
|
|
75
|
-
expect(ormData?.inheritance?.discriminatorColumn).toBe('type');
|
|
76
|
-
});
|
|
77
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'vitest';
|
|
2
|
-
import { generateSymmetricKey } from '../../cryptography/index.js';
|
|
3
|
-
import { decryptBytes, encryptBytes } from '../server/encryption.js';
|
|
4
|
-
describe('ORM Encryption', () => {
|
|
5
|
-
test('should encrypt and decrypt correctly', async () => {
|
|
6
|
-
const key = await generateSymmetricKey({ name: 'AES-GCM', length: 256 }, true);
|
|
7
|
-
const data = new TextEncoder().encode('Hello, ORM Encryption!');
|
|
8
|
-
const encrypted = await encryptBytes(data, key);
|
|
9
|
-
const decrypted = await decryptBytes(encrypted, key);
|
|
10
|
-
expect(new TextDecoder().decode(decrypted)).toBe('Hello, ORM Encryption!');
|
|
11
|
-
});
|
|
12
|
-
test('should throw Error on corrupted data', async () => {
|
|
13
|
-
const key = await generateSymmetricKey({ name: 'AES-GCM', length: 256 }, true);
|
|
14
|
-
const data = new TextEncoder().encode('Corrupt me');
|
|
15
|
-
const encrypted = await encryptBytes(data, key);
|
|
16
|
-
// Corrupt the ciphertext (last byte)
|
|
17
|
-
const lastIndex = encrypted.length - 1;
|
|
18
|
-
encrypted[lastIndex] = encrypted[lastIndex] ^ 1;
|
|
19
|
-
await expect(decryptBytes(encrypted, key)).rejects.toThrow(Error);
|
|
20
|
-
await expect(decryptBytes(encrypted, key)).rejects.toThrow('Decryption failed.');
|
|
21
|
-
});
|
|
22
|
-
test('should throw error on invalid version', async () => {
|
|
23
|
-
const key = await generateSymmetricKey({ name: 'AES-GCM', length: 256 }, true);
|
|
24
|
-
const data = new TextEncoder().encode('Wrong version');
|
|
25
|
-
const encrypted = await encryptBytes(data, key);
|
|
26
|
-
// Corrupt version byte (set to a very high number)
|
|
27
|
-
const view = new DataView(encrypted.buffer, encrypted.byteOffset, encrypted.length);
|
|
28
|
-
view.setUint16(0, 9999);
|
|
29
|
-
await expect(decryptBytes(encrypted, key)).rejects.toThrow('Invalid encryption version.');
|
|
30
|
-
});
|
|
31
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|