@cosmicdrift/kumiko-framework 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +159 -0
- package/package.json +91 -0
- package/src/__tests__/anonymous-access.integration.ts +325 -0
- package/src/__tests__/error-contract.integration.ts +435 -0
- package/src/__tests__/field-access.integration.ts +269 -0
- package/src/__tests__/full-stack.integration.ts +914 -0
- package/src/__tests__/ownership.integration.ts +449 -0
- package/src/__tests__/reference-data.integration.ts +198 -0
- package/src/__tests__/transition-guard.integration.ts +340 -0
- package/src/api/__tests__/api.test.ts +337 -0
- package/src/api/__tests__/auth-middleware-transport.test.ts +80 -0
- package/src/api/__tests__/auth-routes-cookie.test.ts +179 -0
- package/src/api/__tests__/batch.integration.ts +404 -0
- package/src/api/__tests__/body-limit.test.ts +88 -0
- package/src/api/__tests__/csrf-middleware.test.ts +97 -0
- package/src/api/__tests__/dispatcher-live.integration.ts +216 -0
- package/src/api/__tests__/metrics-endpoint.test.ts +126 -0
- package/src/api/__tests__/nested-write.integration.ts +213 -0
- package/src/api/__tests__/readiness.test.ts +76 -0
- package/src/api/__tests__/request-id-middleware.test.ts +72 -0
- package/src/api/__tests__/sse-broker.test.ts +58 -0
- package/src/api/__tests__/sse-route.test.ts +112 -0
- package/src/api/anonymous-cookie.ts +60 -0
- package/src/api/api-constants.ts +64 -0
- package/src/api/auth-middleware.ts +418 -0
- package/src/api/auth-routes.ts +982 -0
- package/src/api/csrf-middleware.ts +77 -0
- package/src/api/index.ts +31 -0
- package/src/api/jwt.ts +66 -0
- package/src/api/observability-middleware.ts +89 -0
- package/src/api/readiness.ts +132 -0
- package/src/api/request-context.ts +49 -0
- package/src/api/request-id-middleware.ts +50 -0
- package/src/api/route-registrars.ts +195 -0
- package/src/api/routes.ts +135 -0
- package/src/api/server.ts +640 -0
- package/src/api/sse-broker.ts +71 -0
- package/src/api/sse-route.ts +62 -0
- package/src/api/tokens.ts +16 -0
- package/src/db/__tests__/apply-entity-event-tenant.integration.ts +159 -0
- package/src/db/__tests__/compound-types.test.ts +114 -0
- package/src/db/__tests__/connection-options.test.ts +68 -0
- package/src/db/__tests__/cursor.test.ts +41 -0
- package/src/db/__tests__/db-helpers.test.ts +369 -0
- package/src/db/__tests__/dialect-instant.test.ts +50 -0
- package/src/db/__tests__/drizzle-helpers.integration.ts +186 -0
- package/src/db/__tests__/drizzle-table-types.test.ts +162 -0
- package/src/db/__tests__/encryption.test.ts +39 -0
- package/src/db/__tests__/event-store-executor-list.integration.ts +313 -0
- package/src/db/__tests__/event-store-executor.integration.ts +235 -0
- package/src/db/__tests__/implicit-projection-equivalence.integration.ts +304 -0
- package/src/db/__tests__/located-timestamp.test.ts +184 -0
- package/src/db/__tests__/money.test.ts +199 -0
- package/src/db/__tests__/multi-row-insert.integration.ts +76 -0
- package/src/db/__tests__/parse-auto-verb.test.ts +70 -0
- package/src/db/__tests__/required-not-null-migration-safety.integration.ts +105 -0
- package/src/db/__tests__/row-helpers.test.ts +59 -0
- package/src/db/__tests__/schema-migration.integration.ts +273 -0
- package/src/db/__tests__/table-builder-indexes.test.ts +153 -0
- package/src/db/__tests__/table-builder-required.test.ts +216 -0
- package/src/db/__tests__/tenant-db.integration.ts +606 -0
- package/src/db/__tests__/unique-violation-mapping.integration.ts +166 -0
- package/src/db/apply-entity-event.ts +188 -0
- package/src/db/assert-exists-in.ts +59 -0
- package/src/db/compound-types.ts +47 -0
- package/src/db/connection.ts +104 -0
- package/src/db/cursor.ts +83 -0
- package/src/db/dialect.ts +109 -0
- package/src/db/eagerload.ts +174 -0
- package/src/db/encryption.ts +39 -0
- package/src/db/event-store-executor.ts +906 -0
- package/src/db/index.ts +55 -0
- package/src/db/located-timestamp.ts +114 -0
- package/src/db/money.ts +120 -0
- package/src/db/pg-error.ts +46 -0
- package/src/db/reference-data.ts +77 -0
- package/src/db/row-helpers.ts +53 -0
- package/src/db/schema-inspection.ts +25 -0
- package/src/db/table-builder.ts +475 -0
- package/src/db/tenant-db.ts +434 -0
- package/src/engine/__tests__/auth-claims-registrar.test.ts +74 -0
- package/src/engine/__tests__/boot-validator-located-timestamps.test.ts +108 -0
- package/src/engine/__tests__/boot-validator.test.ts +1865 -0
- package/src/engine/__tests__/build-app-schema.test.ts +154 -0
- package/src/engine/__tests__/claim-keys.test.ts +274 -0
- package/src/engine/__tests__/config-helpers.test.ts +236 -0
- package/src/engine/__tests__/effective-features.test.ts +86 -0
- package/src/engine/__tests__/engine.test.ts +1461 -0
- package/src/engine/__tests__/entity-handlers.test.ts +274 -0
- package/src/engine/__tests__/event-helpers.test.ts +68 -0
- package/src/engine/__tests__/extends-registrar.test.ts +159 -0
- package/src/engine/__tests__/factories-long-text.test.ts +84 -0
- package/src/engine/__tests__/factories-time.test.ts +158 -0
- package/src/engine/__tests__/field-predicates.test.ts +48 -0
- package/src/engine/__tests__/hook-phases.test.ts +132 -0
- package/src/engine/__tests__/identifiers.test.ts +35 -0
- package/src/engine/__tests__/lifecycle-hooks.test.ts +237 -0
- package/src/engine/__tests__/nav.test.ts +267 -0
- package/src/engine/__tests__/ownership.test.ts +421 -0
- package/src/engine/__tests__/parse-ref-target.test.ts +43 -0
- package/src/engine/__tests__/projection-helpers.test.ts +62 -0
- package/src/engine/__tests__/projection.test.ts +191 -0
- package/src/engine/__tests__/qualified-name.test.ts +264 -0
- package/src/engine/__tests__/resolve-config-or-param.test.ts +315 -0
- package/src/engine/__tests__/run-in.test.ts +38 -0
- package/src/engine/__tests__/schema-builder.test.ts +380 -0
- package/src/engine/__tests__/screen.test.ts +408 -0
- package/src/engine/__tests__/state-machine.test.ts +148 -0
- package/src/engine/__tests__/system-user.test.ts +57 -0
- package/src/engine/__tests__/validation-hooks.test.ts +71 -0
- package/src/engine/access.ts +23 -0
- package/src/engine/boot-validator.ts +1528 -0
- package/src/engine/build-app-schema.ts +125 -0
- package/src/engine/config-helpers.ts +115 -0
- package/src/engine/constants.ts +85 -0
- package/src/engine/create-app.ts +98 -0
- package/src/engine/define-feature.ts +702 -0
- package/src/engine/define-handler.ts +78 -0
- package/src/engine/define-roles.ts +19 -0
- package/src/engine/effective-features.ts +87 -0
- package/src/engine/entity-handlers.ts +364 -0
- package/src/engine/event-helpers.ts +73 -0
- package/src/engine/factories.ts +328 -0
- package/src/engine/feature-ast/__tests__/canonical-form.test.ts +416 -0
- package/src/engine/feature-ast/__tests__/parse-happy-path.test.ts +197 -0
- package/src/engine/feature-ast/__tests__/parse-real-features.test.ts +128 -0
- package/src/engine/feature-ast/__tests__/parse.test.ts +888 -0
- package/src/engine/feature-ast/__tests__/patch.test.ts +360 -0
- package/src/engine/feature-ast/__tests__/patcher.test.ts +469 -0
- package/src/engine/feature-ast/__tests__/render-roundtrip.test.ts +287 -0
- package/src/engine/feature-ast/extractors.ts +2562 -0
- package/src/engine/feature-ast/index.ts +105 -0
- package/src/engine/feature-ast/parse.ts +369 -0
- package/src/engine/feature-ast/patch.ts +525 -0
- package/src/engine/feature-ast/patcher.ts +518 -0
- package/src/engine/feature-ast/patterns.ts +434 -0
- package/src/engine/feature-ast/render.ts +602 -0
- package/src/engine/feature-ast/source-location.ts +45 -0
- package/src/engine/field-access.ts +120 -0
- package/src/engine/index.ts +254 -0
- package/src/engine/ownership.ts +337 -0
- package/src/engine/parse-ref-target.ts +22 -0
- package/src/engine/pattern-library/__tests__/library.test.ts +351 -0
- package/src/engine/pattern-library/index.ts +24 -0
- package/src/engine/pattern-library/library.ts +1117 -0
- package/src/engine/pattern-library/types.ts +255 -0
- package/src/engine/projection-helpers.ts +85 -0
- package/src/engine/qualified-name.ts +122 -0
- package/src/engine/read-claim.ts +31 -0
- package/src/engine/registry.ts +1325 -0
- package/src/engine/resolve-config-or-param.ts +153 -0
- package/src/engine/run-in.ts +29 -0
- package/src/engine/schema-builder.ts +175 -0
- package/src/engine/screen-filter-ops.ts +51 -0
- package/src/engine/state-machine.ts +70 -0
- package/src/engine/system-user.ts +32 -0
- package/src/engine/types/config.ts +306 -0
- package/src/engine/types/event-type-map.ts +37 -0
- package/src/engine/types/feature.ts +574 -0
- package/src/engine/types/fields.ts +422 -0
- package/src/engine/types/handlers.ts +742 -0
- package/src/engine/types/hooks.ts +142 -0
- package/src/engine/types/http-route.ts +54 -0
- package/src/engine/types/identifiers.ts +47 -0
- package/src/engine/types/index.ts +208 -0
- package/src/engine/types/nav.ts +46 -0
- package/src/engine/types/projection.ts +132 -0
- package/src/engine/types/relations.ts +51 -0
- package/src/engine/types/screen.ts +452 -0
- package/src/engine/types/workspace.ts +42 -0
- package/src/engine/validation.ts +33 -0
- package/src/entrypoint/__tests__/entrypoint-job-wiring.integration.ts +173 -0
- package/src/entrypoint/__tests__/split-deploy.integration.ts +297 -0
- package/src/entrypoint/index.ts +442 -0
- package/src/errors/__tests__/classes.test.ts +371 -0
- package/src/errors/__tests__/write-failures.test.ts +109 -0
- package/src/errors/classes.ts +249 -0
- package/src/errors/i18n/de.yaml +83 -0
- package/src/errors/i18n/en.yaml +80 -0
- package/src/errors/index.ts +41 -0
- package/src/errors/kumiko-error.ts +67 -0
- package/src/errors/reasons.ts +36 -0
- package/src/errors/serialize.ts +136 -0
- package/src/errors/transition-details.ts +30 -0
- package/src/errors/write-error-info.ts +123 -0
- package/src/errors/zod-bridge.ts +49 -0
- package/src/event-store/__tests__/admin-api.integration.ts +361 -0
- package/src/event-store/__tests__/event-store.integration.ts +584 -0
- package/src/event-store/__tests__/get-stream-version-perf.integration.ts +83 -0
- package/src/event-store/__tests__/perf.integration.ts +255 -0
- package/src/event-store/__tests__/snapshot.integration.ts +267 -0
- package/src/event-store/__tests__/upcaster-dead-letter.integration.ts +204 -0
- package/src/event-store/__tests__/upcaster.integration.ts +460 -0
- package/src/event-store/admin-api.ts +257 -0
- package/src/event-store/archive.ts +106 -0
- package/src/event-store/errors.ts +35 -0
- package/src/event-store/event-store.ts +405 -0
- package/src/event-store/events-schema.ts +90 -0
- package/src/event-store/index.ts +50 -0
- package/src/event-store/snapshot.ts +210 -0
- package/src/event-store/upcaster-dead-letter.ts +119 -0
- package/src/event-store/upcaster.ts +147 -0
- package/src/files/__tests__/content-disposition.test.ts +123 -0
- package/src/files/__tests__/file-field-column.integration.ts +103 -0
- package/src/files/__tests__/file-field-pipeline.integration.ts +211 -0
- package/src/files/__tests__/file-handle.test.ts +122 -0
- package/src/files/__tests__/files.integration.ts +830 -0
- package/src/files/__tests__/storage-tracking.integration.ts +153 -0
- package/src/files/content-disposition.ts +55 -0
- package/src/files/file-handle.ts +63 -0
- package/src/files/file-ref-table.ts +22 -0
- package/src/files/file-routes.ts +353 -0
- package/src/files/in-memory-provider.ts +62 -0
- package/src/files/index.ts +29 -0
- package/src/files/local-provider.ts +35 -0
- package/src/files/storage-tracking.ts +60 -0
- package/src/files/types.ts +118 -0
- package/src/i18n/__tests__/i18n.test.ts +72 -0
- package/src/i18n/index.ts +29 -0
- package/src/jobs/__tests__/job-event-trigger.integration.ts +172 -0
- package/src/jobs/__tests__/job-multi-trigger.integration.ts +144 -0
- package/src/jobs/__tests__/jobs.integration.ts +566 -0
- package/src/jobs/index.ts +2 -0
- package/src/jobs/job-runner.ts +574 -0
- package/src/lifecycle/__tests__/create-test-lifecycle.ts +19 -0
- package/src/lifecycle/__tests__/lifecycle-server.integration.ts +108 -0
- package/src/lifecycle/__tests__/lifecycle.test.ts +212 -0
- package/src/lifecycle/__tests__/signal-handlers.test.ts +106 -0
- package/src/lifecycle/index.ts +13 -0
- package/src/lifecycle/lifecycle.ts +160 -0
- package/src/lifecycle/signal-handlers.ts +62 -0
- package/src/logging/__tests__/pino-trace-bridge.test.ts +50 -0
- package/src/logging/index.ts +3 -0
- package/src/logging/pino-logger.ts +64 -0
- package/src/logging/types.ts +7 -0
- package/src/migrations/__tests__/compare-snapshots.test.ts +150 -0
- package/src/migrations/__tests__/detect-drift.integration.ts +320 -0
- package/src/migrations/__tests__/detect-projections-to-rebuild.integration.ts +134 -0
- package/src/migrations/__tests__/rebuild-marker.test.ts +79 -0
- package/src/migrations/index.ts +28 -0
- package/src/migrations/projection-detection.ts +149 -0
- package/src/migrations/rebuild-marker.ts +64 -0
- package/src/migrations/schema-drift.ts +395 -0
- package/src/observability/__tests__/console-provider.test.ts +67 -0
- package/src/observability/__tests__/metric-validator.test.ts +87 -0
- package/src/observability/__tests__/noop-provider.test.ts +82 -0
- package/src/observability/__tests__/observability.integration.ts +559 -0
- package/src/observability/__tests__/prometheus-meter.test.ts +144 -0
- package/src/observability/__tests__/recording-meter.test.ts +101 -0
- package/src/observability/__tests__/recording-tracer.test.ts +110 -0
- package/src/observability/__tests__/sensitive-filter.test.ts +98 -0
- package/src/observability/console-provider.ts +130 -0
- package/src/observability/context.ts +26 -0
- package/src/observability/fallback.ts +34 -0
- package/src/observability/ids.ts +25 -0
- package/src/observability/index.ts +79 -0
- package/src/observability/metric-validator.ts +86 -0
- package/src/observability/metrics-handle.ts +56 -0
- package/src/observability/noop-provider.ts +146 -0
- package/src/observability/prometheus-meter.ts +284 -0
- package/src/observability/recording-meter.ts +156 -0
- package/src/observability/recording-tracer.ts +198 -0
- package/src/observability/redis-wrapper.ts +132 -0
- package/src/observability/sensitive-filter.ts +108 -0
- package/src/observability/standard-metrics.ts +213 -0
- package/src/observability/types/index.ts +29 -0
- package/src/observability/types/metric.ts +56 -0
- package/src/observability/types/provider.ts +32 -0
- package/src/observability/types/span.ts +64 -0
- package/src/pipeline/__tests__/archive-stream.integration.ts +220 -0
- package/src/pipeline/__tests__/auth-claims-resolver.test.ts +279 -0
- package/src/pipeline/__tests__/cascade-handler.integration.ts +419 -0
- package/src/pipeline/__tests__/cascade-handler.test.ts +52 -0
- package/src/pipeline/__tests__/causation-chain.integration.ts +206 -0
- package/src/pipeline/__tests__/ctx-bridge.integration.ts +234 -0
- package/src/pipeline/__tests__/dispatcher.test.ts +379 -0
- package/src/pipeline/__tests__/distributed-lock.integration.ts +67 -0
- package/src/pipeline/__tests__/domain-events-projections.integration.ts +323 -0
- package/src/pipeline/__tests__/event-dedup.integration.ts +153 -0
- package/src/pipeline/__tests__/event-define-event-strict.integration.ts +202 -0
- package/src/pipeline/__tests__/event-dispatcher-lifecycle.integration.ts +220 -0
- package/src/pipeline/__tests__/event-dispatcher-multi-instance.integration.ts +423 -0
- package/src/pipeline/__tests__/event-dispatcher-pg-listen.integration.ts +123 -0
- package/src/pipeline/__tests__/event-dispatcher-recovery.integration.ts +202 -0
- package/src/pipeline/__tests__/event-dispatcher-second-audit.integration.ts +290 -0
- package/src/pipeline/__tests__/event-dispatcher-strict.test.ts +65 -0
- package/src/pipeline/__tests__/event-dispatcher.integration.ts +287 -0
- package/src/pipeline/__tests__/event-retention.integration.ts +239 -0
- package/src/pipeline/__tests__/fetch-for-writing.integration.ts +281 -0
- package/src/pipeline/__tests__/lifecycle-pipeline.test.ts +430 -0
- package/src/pipeline/__tests__/load-aggregate-query.integration.ts +266 -0
- package/src/pipeline/__tests__/msp-error-mode.integration.ts +149 -0
- package/src/pipeline/__tests__/msp-multi-hop.integration.ts +228 -0
- package/src/pipeline/__tests__/msp-rebuild.integration.ts +368 -0
- package/src/pipeline/__tests__/multi-stream-projection.integration.ts +341 -0
- package/src/pipeline/__tests__/perf-rebuild.integration.ts +147 -0
- package/src/pipeline/__tests__/projection-rebuild.integration.ts +551 -0
- package/src/pipeline/__tests__/query-projection.integration.ts +201 -0
- package/src/pipeline/__tests__/redis-pipeline.integration.ts +306 -0
- package/src/pipeline/append-event-core.ts +117 -0
- package/src/pipeline/auth-claims-resolver.ts +103 -0
- package/src/pipeline/cascade-handler.ts +113 -0
- package/src/pipeline/dispatcher.ts +1585 -0
- package/src/pipeline/distributed-lock.ts +37 -0
- package/src/pipeline/entity-cache.ts +113 -0
- package/src/pipeline/event-consumer-state.ts +108 -0
- package/src/pipeline/event-dedup.ts +23 -0
- package/src/pipeline/event-dispatcher.ts +1016 -0
- package/src/pipeline/event-retention.ts +154 -0
- package/src/pipeline/idempotency.ts +76 -0
- package/src/pipeline/index.ts +66 -0
- package/src/pipeline/lifecycle-pipeline.ts +409 -0
- package/src/pipeline/msp-rebuild.ts +242 -0
- package/src/pipeline/multi-stream-apply-context.ts +115 -0
- package/src/pipeline/projection-rebuild.ts +334 -0
- package/src/pipeline/projection-state.ts +72 -0
- package/src/pipeline/projections-runner.ts +56 -0
- package/src/pipeline/redis-keys.ts +11 -0
- package/src/pipeline/system-hooks.ts +190 -0
- package/src/random/__tests__/generate.test.ts +149 -0
- package/src/random/generate.ts +141 -0
- package/src/random/index.ts +8 -0
- package/src/random/words.ts +392 -0
- package/src/rate-limit/__tests__/dispatcher-l3.integration.ts +111 -0
- package/src/rate-limit/__tests__/middleware.integration.ts +189 -0
- package/src/rate-limit/__tests__/resolver.integration.ts +189 -0
- package/src/rate-limit/bucket.ts +36 -0
- package/src/rate-limit/index.ts +14 -0
- package/src/rate-limit/middleware.ts +152 -0
- package/src/rate-limit/resolver.ts +267 -0
- package/src/redis/__tests__/redis-options.test.ts +54 -0
- package/src/redis/index.ts +74 -0
- package/src/search/__tests__/meilisearch-adapter.integration.ts +236 -0
- package/src/search/__tests__/search-adapter.test.ts +256 -0
- package/src/search/in-memory-adapter.ts +123 -0
- package/src/search/index.ts +12 -0
- package/src/search/meilisearch-adapter.ts +106 -0
- package/src/search/types.ts +39 -0
- package/src/secrets/__tests__/dek-cache.test.ts +213 -0
- package/src/secrets/__tests__/env-master-key-provider.test.ts +119 -0
- package/src/secrets/__tests__/envelope.test.ts +74 -0
- package/src/secrets/__tests__/leak-guard.test.ts +92 -0
- package/src/secrets/__tests__/rotation.test.ts +149 -0
- package/src/secrets/dek-cache.ts +116 -0
- package/src/secrets/env-master-key-provider.ts +162 -0
- package/src/secrets/envelope.ts +55 -0
- package/src/secrets/index.ts +19 -0
- package/src/secrets/leak-guard.ts +87 -0
- package/src/secrets/rotation.ts +34 -0
- package/src/secrets/types.ts +107 -0
- package/src/stack/db.ts +104 -0
- package/src/stack/event-collector.ts +23 -0
- package/src/stack/index.ts +32 -0
- package/src/stack/redis.ts +44 -0
- package/src/stack/request-helper.ts +168 -0
- package/src/stack/table-helpers.ts +104 -0
- package/src/stack/test-stack.ts +357 -0
- package/src/stack/test-users.ts +37 -0
- package/src/testing/__tests__/e2e-generator.test.ts +230 -0
- package/src/testing/__tests__/ensure-entity-table.integration.ts +54 -0
- package/src/testing/access-assertions.ts +15 -0
- package/src/testing/assertions.ts +35 -0
- package/src/testing/e2e-generator.ts +465 -0
- package/src/testing/expect-error.ts +25 -0
- package/src/testing/handler-context.ts +125 -0
- package/src/testing/http-cookies.ts +52 -0
- package/src/testing/index.ts +41 -0
- package/src/testing/late-bound.ts +39 -0
- package/src/testing/mutable-master-key-provider.ts +31 -0
- package/src/testing/observability-recorder.ts +54 -0
- package/src/testing/shared-entities.ts +49 -0
- package/src/testing/utils.ts +1 -0
- package/src/testing/wait-for.ts +31 -0
- package/src/time/__tests__/polyfill.test.ts +73 -0
- package/src/time/__tests__/tz-context.test.ts +121 -0
- package/src/time/index.ts +21 -0
- package/src/time/polyfill.ts +70 -0
- package/src/time/tz-context.ts +107 -0
- package/src/ui-types/app-schema.ts +57 -0
- package/src/ui-types/index.ts +65 -0
- package/src/utils/__tests__/assert.test.ts +17 -0
- package/src/utils/__tests__/env-parse.test.ts +54 -0
- package/src/utils/assert.ts +18 -0
- package/src/utils/env-parse.ts +16 -0
- package/src/utils/ids.ts +16 -0
- package/src/utils/index.ts +5 -0
- package/src/utils/safe-json.ts +30 -0
- package/src/utils/serialization.ts +7 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// Type-Tests für DrizzleTable<E>: pinned das vertragliche Verhalten der
|
|
2
|
+
// Generic-Inferenz-Pipeline createEntity → buildDrizzleTable. Wenn ein
|
|
3
|
+
// Branch in `fieldToColumns` driftet aber `ColumnsForField` im Type-
|
|
4
|
+
// System nicht mitkommt (oder umgekehrt), schlagen diese Tests fehl
|
|
5
|
+
// BEVOR ein Consumer in den Wald läuft.
|
|
6
|
+
//
|
|
7
|
+
// Ausschnitt — nicht jede Field-Art-Permutation getestet, sondern
|
|
8
|
+
// repräsentative Fälle: required-Literal, required-Default, money-Pair,
|
|
9
|
+
// locatedTimestamp-Pair, multiSelect-jsonb-default, idType-Variation.
|
|
10
|
+
|
|
11
|
+
import { describe, expect, expectTypeOf, test } from "vitest";
|
|
12
|
+
import {
|
|
13
|
+
createBooleanField,
|
|
14
|
+
createDateField,
|
|
15
|
+
createEntity,
|
|
16
|
+
createLocatedTimestampField,
|
|
17
|
+
createMoneyField,
|
|
18
|
+
createMultiSelectField,
|
|
19
|
+
createNumberField,
|
|
20
|
+
createSelectField,
|
|
21
|
+
createTextField,
|
|
22
|
+
createTimestampField,
|
|
23
|
+
createTzField,
|
|
24
|
+
} from "../../engine";
|
|
25
|
+
import { buildDrizzleTable } from "../table-builder";
|
|
26
|
+
|
|
27
|
+
describe("DrizzleTable<E> — Property-Names existieren", () => {
|
|
28
|
+
const sampleEntity = createEntity({
|
|
29
|
+
table: "x",
|
|
30
|
+
fields: {
|
|
31
|
+
title: createTextField({ required: true }),
|
|
32
|
+
done: createBooleanField({ default: false }),
|
|
33
|
+
priority: createSelectField({ options: ["low", "high"] as const }),
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
const t = buildDrizzleTable("sample", sampleEntity);
|
|
37
|
+
|
|
38
|
+
test("base-columns sind getypt", () => {
|
|
39
|
+
expectTypeOf(t.id).not.toBeNever();
|
|
40
|
+
expectTypeOf(t.tenantId).not.toBeNever();
|
|
41
|
+
expectTypeOf(t.version).not.toBeNever();
|
|
42
|
+
expectTypeOf(t.insertedAt).not.toBeNever();
|
|
43
|
+
expectTypeOf(t.modifiedAt).not.toBeNever();
|
|
44
|
+
expectTypeOf(t.insertedById).not.toBeNever();
|
|
45
|
+
expectTypeOf(t.modifiedById).not.toBeNever();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("field-columns sind getypt", () => {
|
|
49
|
+
expectTypeOf(t.title).not.toBeNever();
|
|
50
|
+
expectTypeOf(t.done).not.toBeNever();
|
|
51
|
+
expectTypeOf(t.priority).not.toBeNever();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("nicht-existierender Spaltenname ist Compile-Error", () => {
|
|
55
|
+
// @ts-expect-error — `nonExistent` ist nicht in der Entity deklariert.
|
|
56
|
+
// Wenn die Zeile compiles, war der Type-Refactor zu lasch (= Index-
|
|
57
|
+
// Signature wieder da). Das @ts-expect-error wird selbst zum Failure
|
|
58
|
+
// wenn der Error verschwindet → der Test bricht dann zur Compile-Zeit.
|
|
59
|
+
const x = t.nonExistent;
|
|
60
|
+
expect(x).toBeUndefined();
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe("DrizzleTable<E> — Money produces two columns", () => {
|
|
65
|
+
const ent = createEntity({
|
|
66
|
+
table: "invoice",
|
|
67
|
+
fields: {
|
|
68
|
+
amount: createMoneyField({ required: true }),
|
|
69
|
+
shipping: createMoneyField(),
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
const t = buildDrizzleTable("invoice", ent);
|
|
73
|
+
|
|
74
|
+
test("money-amount column existiert", () => {
|
|
75
|
+
expectTypeOf(t.amount).not.toBeNever();
|
|
76
|
+
expectTypeOf(t.shipping).not.toBeNever();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("money-currency Zwilling existiert", () => {
|
|
80
|
+
expectTypeOf(t.amountCurrency).not.toBeNever();
|
|
81
|
+
expectTypeOf(t.shippingCurrency).not.toBeNever();
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe("DrizzleTable<E> — locatedTimestamp produces Utc + Tz", () => {
|
|
86
|
+
const ent = createEntity({
|
|
87
|
+
table: "delivery",
|
|
88
|
+
fields: {
|
|
89
|
+
pickup: createLocatedTimestampField({ required: true }),
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
const t = buildDrizzleTable("delivery", ent);
|
|
93
|
+
|
|
94
|
+
test("Utc und Tz Spalten existieren", () => {
|
|
95
|
+
expectTypeOf(t.pickupUtc).not.toBeNever();
|
|
96
|
+
expectTypeOf(t.pickupTz).not.toBeNever();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("Original-name (ohne Suffix) ist KEIN column", () => {
|
|
100
|
+
// @ts-expect-error — locatedTimestamp expandiert zu pickupUtc + pickupTz, nicht `pickup`.
|
|
101
|
+
// Wenn `pickup` kompiliert, ist FieldColumnNames-Map kaputt.
|
|
102
|
+
const x = t.pickup;
|
|
103
|
+
expect(x).toBeUndefined();
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe("DrizzleTable<E> — files/images produzieren keine columns", () => {
|
|
108
|
+
// files/images werden über fileRefsTable resolved, nicht in der entity-table.
|
|
109
|
+
// Ein Type-Test der das pinnt würde createFilesField verlangen — wir lassen
|
|
110
|
+
// das hier weg, weil das Authoring-Pattern "createXField" ohne file-helper
|
|
111
|
+
// ist (file/image gehen durch createFileField/createImageField mit single
|
|
112
|
+
// mode, was eine column hat). multi-Mode (files/images) ist Edge-Case.
|
|
113
|
+
test("kein column für files-typed field — separate Test-Sprint", () => {
|
|
114
|
+
expect(true).not.toBe(false);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe("DrizzleTable<E> — verschiedene Feld-Typen existieren", () => {
|
|
119
|
+
const ent = createEntity({
|
|
120
|
+
table: "many",
|
|
121
|
+
fields: {
|
|
122
|
+
txt: createTextField(),
|
|
123
|
+
num: createNumberField(),
|
|
124
|
+
dt: createDateField(),
|
|
125
|
+
ts: createTimestampField(),
|
|
126
|
+
tz: createTzField(),
|
|
127
|
+
tags: createMultiSelectField({ options: ["a", "b"] as const }),
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
const t = buildDrizzleTable("many", ent);
|
|
131
|
+
|
|
132
|
+
test("alle Felder als columns sichtbar", () => {
|
|
133
|
+
expectTypeOf(t.txt).not.toBeNever();
|
|
134
|
+
expectTypeOf(t.num).not.toBeNever();
|
|
135
|
+
expectTypeOf(t.dt).not.toBeNever();
|
|
136
|
+
expectTypeOf(t.ts).not.toBeNever();
|
|
137
|
+
expectTypeOf(t.tz).not.toBeNever();
|
|
138
|
+
expectTypeOf(t.tags).not.toBeNever();
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
describe("DrizzleTable<E> — idType wirkt", () => {
|
|
143
|
+
const uuidEnt = createEntity({
|
|
144
|
+
table: "uuid_ent",
|
|
145
|
+
fields: { name: createTextField() },
|
|
146
|
+
// default: idType: "uuid"
|
|
147
|
+
});
|
|
148
|
+
const serialEnt = createEntity({
|
|
149
|
+
table: "serial_ent",
|
|
150
|
+
fields: { name: createTextField() },
|
|
151
|
+
idType: "serial",
|
|
152
|
+
});
|
|
153
|
+
const tu = buildDrizzleTable("uuid_ent", uuidEnt);
|
|
154
|
+
const ts = buildDrizzleTable("serial_ent", serialEnt);
|
|
155
|
+
|
|
156
|
+
test("uuid-Entity exposed id existiert", () => {
|
|
157
|
+
expectTypeOf(tu.id).not.toBeNever();
|
|
158
|
+
});
|
|
159
|
+
test("serial-Entity exposed id existiert", () => {
|
|
160
|
+
expectTypeOf(ts.id).not.toBeNever();
|
|
161
|
+
});
|
|
162
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import { createEncryptionProvider } from "../encryption";
|
|
3
|
+
|
|
4
|
+
// 32 bytes base64-encoded for AES-256
|
|
5
|
+
const TEST_KEY = Buffer.from("a]bJm#kP9xQ2@wN!vL$hR5yT8eU0iO3f").toString("base64");
|
|
6
|
+
|
|
7
|
+
describe("EncryptionProvider", () => {
|
|
8
|
+
test("encrypt + decrypt roundtrip returns original", () => {
|
|
9
|
+
const provider = createEncryptionProvider(TEST_KEY);
|
|
10
|
+
const ciphertext = provider.encrypt("hello world");
|
|
11
|
+
expect(provider.decrypt(ciphertext)).toBe("hello world");
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test("same plaintext produces different ciphertexts (random IV)", () => {
|
|
15
|
+
const provider = createEncryptionProvider(TEST_KEY);
|
|
16
|
+
const a = provider.encrypt("same");
|
|
17
|
+
const b = provider.encrypt("same");
|
|
18
|
+
expect(a).not.toBe(b);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("decrypt with different key throws", () => {
|
|
22
|
+
const key2 = Buffer.from("x]bJm#kP9xQ2@wN!vL$hR5yT8eU0iO3f").toString("base64");
|
|
23
|
+
const p1 = createEncryptionProvider(TEST_KEY);
|
|
24
|
+
const p2 = createEncryptionProvider(key2);
|
|
25
|
+
const ciphertext = p1.encrypt("secret");
|
|
26
|
+
expect(() => p2.decrypt(ciphertext)).toThrow();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("handles unicode and emoji", () => {
|
|
30
|
+
const provider = createEncryptionProvider(TEST_KEY);
|
|
31
|
+
const ciphertext = provider.encrypt("Ünïcödé 🔐");
|
|
32
|
+
expect(provider.decrypt(ciphertext)).toBe("Ünïcödé 🔐");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("throws on invalid key length", () => {
|
|
36
|
+
const shortKey = Buffer.from("too-short").toString("base64");
|
|
37
|
+
expect(() => createEncryptionProvider(shortKey)).toThrow(/32 bytes/);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
// Direkter Coverage für die Tier-2.6d-Erweiterung von executor.list:
|
|
2
|
+
// offset, totalCount, sowie das "cursor wins über offset" Verhalten.
|
|
3
|
+
// Vor dieser Suite waren die drei Branches nur indirekt über
|
|
4
|
+
// items-create.integration im Showcase abgedeckt — nicht ausreichend
|
|
5
|
+
// für Framework-Code der von jeder App genutzt wird.
|
|
6
|
+
|
|
7
|
+
import { sql } from "drizzle-orm";
|
|
8
|
+
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "vitest";
|
|
9
|
+
import { createEntity, createNumberField, createTextField } from "../../engine";
|
|
10
|
+
import { createEventsTable } from "../../event-store";
|
|
11
|
+
import { createEntityTable, createTestDb, type TestDb, TestUsers } from "../../stack";
|
|
12
|
+
import { createEventStoreExecutor } from "../event-store-executor";
|
|
13
|
+
import { buildDrizzleTable } from "../table-builder";
|
|
14
|
+
import { createTenantDb, type TenantDb } from "../tenant-db";
|
|
15
|
+
|
|
16
|
+
const entity = createEntity({
|
|
17
|
+
table: "read_pager_items",
|
|
18
|
+
fields: {
|
|
19
|
+
title: createTextField({ required: true, sortable: true }),
|
|
20
|
+
rank: createNumberField({ sortable: true }),
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
const table = buildDrizzleTable("pagerItem", entity);
|
|
24
|
+
|
|
25
|
+
let testDb: TestDb;
|
|
26
|
+
let tdb: TenantDb;
|
|
27
|
+
const admin = TestUsers.admin;
|
|
28
|
+
|
|
29
|
+
beforeAll(async () => {
|
|
30
|
+
testDb = await createTestDb();
|
|
31
|
+
await createEntityTable(testDb.db, entity, "pagerItem");
|
|
32
|
+
await createEventsTable(testDb.db);
|
|
33
|
+
tdb = createTenantDb(testDb.db, admin.tenantId);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
afterAll(async () => {
|
|
37
|
+
await testDb.cleanup();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
beforeEach(async () => {
|
|
41
|
+
await testDb.db.execute(sql`TRUNCATE kumiko_events, read_pager_items RESTART IDENTITY CASCADE`);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe("event-store-executor.list — offset + totalCount (Tier 2.6d)", () => {
|
|
45
|
+
const exec = createEventStoreExecutor(table, entity, { entityName: "pagerItem" });
|
|
46
|
+
|
|
47
|
+
async function seed(n: number): Promise<void> {
|
|
48
|
+
for (let i = 0; i < n; i++) {
|
|
49
|
+
await exec.create({ title: `item-${String(i).padStart(3, "0")}`, rank: i }, admin, tdb);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
test("ohne totalCount: response hat KEIN total-Feld (extra COUNT gespart)", async () => {
|
|
54
|
+
await seed(5);
|
|
55
|
+
const res = await exec.list({ limit: 50 }, admin, tdb);
|
|
56
|
+
expect(res.rows).toHaveLength(5);
|
|
57
|
+
expect("total" in res).toBe(false);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("mit totalCount=true: response hat total = N", async () => {
|
|
61
|
+
await seed(7);
|
|
62
|
+
const res = await exec.list({ limit: 50, totalCount: true }, admin, tdb);
|
|
63
|
+
expect(res.rows).toHaveLength(7);
|
|
64
|
+
expect(res.total).toBe(7);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("offset paginiert deterministisch (sort=rank asc + offset=2 → rows 3-5)", async () => {
|
|
68
|
+
await seed(10);
|
|
69
|
+
const res = await exec.list(
|
|
70
|
+
{ limit: 3, offset: 2, sort: "rank", sortDirection: "asc", totalCount: true },
|
|
71
|
+
admin,
|
|
72
|
+
tdb,
|
|
73
|
+
);
|
|
74
|
+
expect(res.rows.map((r) => r["rank"])).toEqual([2, 3, 4]);
|
|
75
|
+
expect(res.total).toBe(10);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("offset >= total: leere rows, total bleibt korrekt", async () => {
|
|
79
|
+
await seed(3);
|
|
80
|
+
const res = await exec.list(
|
|
81
|
+
{ limit: 10, offset: 100, sort: "rank", sortDirection: "asc", totalCount: true },
|
|
82
|
+
admin,
|
|
83
|
+
tdb,
|
|
84
|
+
);
|
|
85
|
+
expect(res.rows).toHaveLength(0);
|
|
86
|
+
expect(res.total).toBe(3);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("cursor wins über offset (kombination ist Caller-bug, defensiv)", async () => {
|
|
90
|
+
// Wenn der Caller versehentlich BEIDE setzt — z.B. ein Migrations-
|
|
91
|
+
// Skript das Cursor-Pagination + Page-Number mischt — soll cursor
|
|
92
|
+
// gewinnen weil DB-stable. Offset wird ignoriert.
|
|
93
|
+
await seed(10);
|
|
94
|
+
const first = await exec.list({ limit: 3, sort: "rank", sortDirection: "asc" }, admin, tdb);
|
|
95
|
+
expect(first.rows.map((r) => r["rank"])).toEqual([0, 1, 2]);
|
|
96
|
+
const cursor = first.nextCursor;
|
|
97
|
+
expect(cursor).not.toBeNull();
|
|
98
|
+
if (cursor === null) return;
|
|
99
|
+
// cursor + offset:50 — cursor sollte gewinnen, nicht das offset.
|
|
100
|
+
// Note: cursor-pagination hier ist NICHT row-3 → row-4-stable, weil
|
|
101
|
+
// die UUIDs zwar UUIDv7 sind aber innerhalb derselben Millisekunde
|
|
102
|
+
// generiert die Sub-Sort nicht garantiert mit `rank` korreliert. Wir
|
|
103
|
+
// pinnen nur "cursor wird benutzt → kein offset:50-Skip auf row-50
|
|
104
|
+
// (die's gar nicht gibt)".
|
|
105
|
+
const next = await exec.list(
|
|
106
|
+
{ limit: 3, cursor, offset: 50, sort: "rank", sortDirection: "asc" },
|
|
107
|
+
admin,
|
|
108
|
+
tdb,
|
|
109
|
+
);
|
|
110
|
+
// Ohne cursor-wins-Branch wäre das offset=50 → leeres Result.
|
|
111
|
+
// Mit cursor-wins läuft der gt(id, cursor)-Filter und liefert
|
|
112
|
+
// rows die NACH dem cursor kommen → mindestens 1 Eintrag.
|
|
113
|
+
expect(next.rows.length).toBeGreaterThan(0);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("totalCount auf empty-result: total=0, rows=[]", async () => {
|
|
117
|
+
const res = await exec.list({ limit: 50, totalCount: true }, admin, tdb);
|
|
118
|
+
expect(res.rows).toHaveLength(0);
|
|
119
|
+
expect(res.total).toBe(0);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
describe("event-store-executor.list — filter (Tier 2.7c)", () => {
|
|
124
|
+
const exec = createEventStoreExecutor(table, entity, { entityName: "pagerItem" });
|
|
125
|
+
|
|
126
|
+
async function seed(n: number): Promise<void> {
|
|
127
|
+
for (let i = 0; i < n; i++) {
|
|
128
|
+
await exec.create({ title: `item-${String(i).padStart(3, "0")}`, rank: i }, admin, tdb);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
test("filter eq: nur die rank=5 row", async () => {
|
|
133
|
+
await seed(10);
|
|
134
|
+
const res = await exec.list(
|
|
135
|
+
{
|
|
136
|
+
limit: 50,
|
|
137
|
+
sort: "rank",
|
|
138
|
+
sortDirection: "asc",
|
|
139
|
+
filter: { field: "rank", op: "eq", value: 5 },
|
|
140
|
+
},
|
|
141
|
+
admin,
|
|
142
|
+
tdb,
|
|
143
|
+
);
|
|
144
|
+
expect(res.rows.map((r) => r["rank"])).toEqual([5]);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test("filter ne: alle außer rank=2", async () => {
|
|
148
|
+
await seed(5);
|
|
149
|
+
const res = await exec.list(
|
|
150
|
+
{
|
|
151
|
+
limit: 50,
|
|
152
|
+
sort: "rank",
|
|
153
|
+
sortDirection: "asc",
|
|
154
|
+
filter: { field: "rank", op: "ne", value: 2 },
|
|
155
|
+
},
|
|
156
|
+
admin,
|
|
157
|
+
tdb,
|
|
158
|
+
);
|
|
159
|
+
expect(res.rows.map((r) => r["rank"])).toEqual([0, 1, 3, 4]);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test("filter lt: rank < 3 → 0,1,2", async () => {
|
|
163
|
+
await seed(6);
|
|
164
|
+
const res = await exec.list(
|
|
165
|
+
{
|
|
166
|
+
limit: 50,
|
|
167
|
+
sort: "rank",
|
|
168
|
+
sortDirection: "asc",
|
|
169
|
+
filter: { field: "rank", op: "lt", value: 3 },
|
|
170
|
+
},
|
|
171
|
+
admin,
|
|
172
|
+
tdb,
|
|
173
|
+
);
|
|
174
|
+
expect(res.rows.map((r) => r["rank"])).toEqual([0, 1, 2]);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("filter gt: rank > 7 → 8,9", async () => {
|
|
178
|
+
await seed(10);
|
|
179
|
+
const res = await exec.list(
|
|
180
|
+
{
|
|
181
|
+
limit: 50,
|
|
182
|
+
sort: "rank",
|
|
183
|
+
sortDirection: "asc",
|
|
184
|
+
filter: { field: "rank", op: "gt", value: 7 },
|
|
185
|
+
},
|
|
186
|
+
admin,
|
|
187
|
+
tdb,
|
|
188
|
+
);
|
|
189
|
+
expect(res.rows.map((r) => r["rank"])).toEqual([8, 9]);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test("filter in: rank in [1,3,5]", async () => {
|
|
193
|
+
await seed(10);
|
|
194
|
+
const res = await exec.list(
|
|
195
|
+
{
|
|
196
|
+
limit: 50,
|
|
197
|
+
sort: "rank",
|
|
198
|
+
sortDirection: "asc",
|
|
199
|
+
filter: { field: "rank", op: "in", value: [1, 3, 5] },
|
|
200
|
+
},
|
|
201
|
+
admin,
|
|
202
|
+
tdb,
|
|
203
|
+
);
|
|
204
|
+
expect(res.rows.map((r) => r["rank"])).toEqual([1, 3, 5]);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test("filter in mit empty-array: leeres Resultat (keine Match-All-Falle)", async () => {
|
|
208
|
+
await seed(5);
|
|
209
|
+
const res = await exec.list(
|
|
210
|
+
{
|
|
211
|
+
limit: 50,
|
|
212
|
+
sort: "rank",
|
|
213
|
+
sortDirection: "asc",
|
|
214
|
+
filter: { field: "rank", op: "in", value: [] },
|
|
215
|
+
},
|
|
216
|
+
admin,
|
|
217
|
+
tdb,
|
|
218
|
+
);
|
|
219
|
+
expect(res.rows).toHaveLength(0);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
test("filter unknown-field: silent skip — kein Crash, alle rows zurück", async () => {
|
|
223
|
+
// Boot-Validator pinst das normalerweise; Runtime-Defense für den
|
|
224
|
+
// Fall dass ein Test/Caller direkt am executor vorbei ein bogus-
|
|
225
|
+
// Field schickt. Lieber alle rows als Crash-Loop.
|
|
226
|
+
await seed(3);
|
|
227
|
+
const res = await exec.list(
|
|
228
|
+
{
|
|
229
|
+
limit: 50,
|
|
230
|
+
sort: "rank",
|
|
231
|
+
sortDirection: "asc",
|
|
232
|
+
filter: { field: "doesNotExist", op: "eq", value: 1 },
|
|
233
|
+
},
|
|
234
|
+
admin,
|
|
235
|
+
tdb,
|
|
236
|
+
);
|
|
237
|
+
expect(res.rows).toHaveLength(3);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
test("filter + totalCount: COUNT respektiert filter", async () => {
|
|
241
|
+
await seed(10);
|
|
242
|
+
const res = await exec.list(
|
|
243
|
+
{
|
|
244
|
+
limit: 50,
|
|
245
|
+
totalCount: true,
|
|
246
|
+
filter: { field: "rank", op: "lt", value: 4 },
|
|
247
|
+
},
|
|
248
|
+
admin,
|
|
249
|
+
tdb,
|
|
250
|
+
);
|
|
251
|
+
expect(res.rows).toHaveLength(4);
|
|
252
|
+
expect(res.total).toBe(4);
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
describe("event-store-executor.list — runtime SearchAdapter (Tier 2.7e Audit-Fix #1)", () => {
|
|
257
|
+
// Pinst dass der executor einen searchAdapter aus runtimeOptions
|
|
258
|
+
// akzeptiert wenn er beim Build keinen über options.searchAdapter
|
|
259
|
+
// bekommen hat. defaultEntityQueryHandler nutzt diesen Pfad weil
|
|
260
|
+
// der executor zur Definition-Time keinen ctx-Adapter kennt.
|
|
261
|
+
const exec = createEventStoreExecutor(table, entity, { entityName: "pagerItem" });
|
|
262
|
+
|
|
263
|
+
test("ohne searchAdapter: search-Param ist no-op (alle rows zurück)", async () => {
|
|
264
|
+
for (let i = 0; i < 3; i++) {
|
|
265
|
+
await exec.create({ title: `item-${i}`, rank: i }, admin, tdb);
|
|
266
|
+
}
|
|
267
|
+
const res = await exec.list({ limit: 50, search: "irgendwas" }, admin, tdb);
|
|
268
|
+
expect(res.rows.length).toBe(3);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
test("mit runtimeOptions.searchAdapter: search filtert auf returned IDs", async () => {
|
|
272
|
+
for (let i = 0; i < 3; i++) {
|
|
273
|
+
await exec.create({ title: `item-${i}`, rank: i }, admin, tdb);
|
|
274
|
+
}
|
|
275
|
+
const allRows = await exec.list({ limit: 50 }, admin, tdb);
|
|
276
|
+
const matchedId = allRows.rows[1]?.["id"] as string;
|
|
277
|
+
expect(matchedId).toBeDefined();
|
|
278
|
+
|
|
279
|
+
// Mock-Adapter: returnt genau eine ID — nur die wird durchgelassen.
|
|
280
|
+
const mockAdapter = {
|
|
281
|
+
configure: async () => {},
|
|
282
|
+
index: async () => {},
|
|
283
|
+
indexBatch: async () => {},
|
|
284
|
+
remove: async () => {},
|
|
285
|
+
search: async () => [{ entityType: "pagerItem", entityId: matchedId }],
|
|
286
|
+
reset: async () => {},
|
|
287
|
+
} as never;
|
|
288
|
+
|
|
289
|
+
const res = await exec.list({ limit: 50, search: "x" }, admin, tdb, {
|
|
290
|
+
searchAdapter: mockAdapter,
|
|
291
|
+
});
|
|
292
|
+
expect(res.rows).toHaveLength(1);
|
|
293
|
+
expect(res.rows[0]?.["id"]).toBe(matchedId);
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
test("mit search ohne match (Adapter returnt []): leere rows + no DB-Query", async () => {
|
|
297
|
+
for (let i = 0; i < 3; i++) {
|
|
298
|
+
await exec.create({ title: `item-${i}`, rank: i }, admin, tdb);
|
|
299
|
+
}
|
|
300
|
+
const mockAdapter = {
|
|
301
|
+
configure: async () => {},
|
|
302
|
+
index: async () => {},
|
|
303
|
+
indexBatch: async () => {},
|
|
304
|
+
remove: async () => {},
|
|
305
|
+
search: async () => [],
|
|
306
|
+
reset: async () => {},
|
|
307
|
+
} as never;
|
|
308
|
+
const res = await exec.list({ limit: 50, search: "no-match" }, admin, tdb, {
|
|
309
|
+
searchAdapter: mockAdapter,
|
|
310
|
+
});
|
|
311
|
+
expect(res.rows).toHaveLength(0);
|
|
312
|
+
});
|
|
313
|
+
});
|