@cosmicdrift/kumiko-framework 0.14.0 → 0.15.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/package.json +6 -6
- package/src/__tests__/{anonymous-access.integration.ts → anonymous-access.integration.test.ts} +12 -9
- package/src/__tests__/{error-contract.integration.ts → error-contract.integration.test.ts} +5 -4
- package/src/__tests__/{field-access.integration.ts → field-access.integration.test.ts} +3 -3
- package/src/__tests__/{full-stack.integration.ts → full-stack.integration.test.ts} +7 -16
- package/src/__tests__/{ownership.integration.ts → ownership.integration.test.ts} +3 -2
- package/src/__tests__/{raw-table.integration.ts → raw-table.integration.test.ts} +18 -30
- package/src/__tests__/{reference-data.integration.ts → reference-data.integration.test.ts} +24 -11
- package/src/__tests__/{transition-guard.integration.ts → transition-guard.integration.test.ts} +12 -10
- package/src/api/__tests__/api.test.ts +1 -1
- package/src/api/__tests__/auth-middleware-transport.test.ts +1 -1
- package/src/api/__tests__/auth-routes-cookie.test.ts +1 -1
- package/src/api/__tests__/{batch.integration.ts → batch.integration.test.ts} +30 -30
- package/src/api/__tests__/body-limit.test.ts +1 -1
- package/src/api/__tests__/csrf-middleware.test.ts +1 -1
- package/src/api/__tests__/{dispatcher-live.integration.ts → dispatcher-live.integration.test.ts} +10 -9
- package/src/api/__tests__/metrics-endpoint.test.ts +1 -1
- package/src/api/__tests__/{nested-write.integration.ts → nested-write.integration.test.ts} +13 -16
- package/src/api/__tests__/readiness.test.ts +1 -1
- package/src/api/__tests__/request-id-middleware.test.ts +1 -1
- package/src/api/__tests__/sse-broker.test.ts +12 -12
- package/src/api/__tests__/sse-route.test.ts +1 -1
- package/src/api/readiness.ts +2 -2
- package/src/auth/__tests__/roles.test.ts +2 -2
- package/src/bun-db/__tests__/PATTERN.md +73 -0
- package/src/bun-db/__tests__/_helpers.ts +103 -0
- package/src/bun-db/__tests__/batch-methods.integration.test.ts +143 -0
- package/src/bun-db/__tests__/batch-methods.test.ts +20 -0
- package/src/bun-db/__tests__/bun-test-db.ts +19 -0
- package/src/bun-db/__tests__/bun-test-stack.ts +6 -0
- package/src/bun-db/__tests__/column-types.integration.test.ts +132 -0
- package/src/bun-db/__tests__/compound-types.integration.test.ts +134 -0
- package/src/bun-db/__tests__/jsonb-edge-cases.integration.test.ts +235 -0
- package/src/bun-db/__tests__/smoke.integration.test.ts +43 -0
- package/src/bun-db/__tests__/sql-methods.integration.test.ts +231 -0
- package/src/bun-db/__tests__/where-patterns.integration.test.ts +185 -0
- package/src/bun-db/connection.ts +84 -0
- package/src/bun-db/index.ts +31 -0
- package/src/bun-db/query.ts +845 -0
- package/src/compliance/__tests__/duration-spec.test.ts +1 -1
- package/src/compliance/__tests__/profiles.test.ts +1 -1
- package/src/compliance/__tests__/sub-processors.test.ts +1 -1
- package/src/db/__tests__/{apply-entity-event-tenant.integration.ts → apply-entity-event-tenant.integration.test.ts} +13 -11
- package/src/db/__tests__/big-int-field.test.ts +15 -14
- package/src/db/__tests__/column-ddl.integration.test.ts +113 -0
- package/src/db/__tests__/compound-types.test.ts +1 -1
- package/src/db/__tests__/{config-seed.integration.ts → config-seed.integration.test.ts} +32 -27
- package/src/db/__tests__/connection-options.test.ts +1 -1
- package/src/db/__tests__/dialect-instant.test.ts +1 -1
- package/src/db/__tests__/encryption.test.ts +1 -1
- package/src/db/__tests__/{drizzle-table-types.test.ts → entity-table-types.test.ts} +16 -16
- package/src/db/__tests__/{event-store-executor-list.integration.ts → event-store-executor-list.integration.test.ts} +12 -7
- package/src/db/__tests__/{event-store-executor.integration.ts → event-store-executor.integration.test.ts} +19 -12
- package/src/db/__tests__/{implicit-projection-equivalence.integration.ts → implicit-projection-equivalence.integration.test.ts} +35 -29
- package/src/db/__tests__/located-timestamp.test.ts +1 -1
- package/src/db/__tests__/money.test.ts +1 -1
- package/src/db/__tests__/{multi-row-insert.integration.ts → multi-row-insert.integration.test.ts} +18 -11
- package/src/db/__tests__/parse-auto-verb.test.ts +1 -1
- package/src/db/__tests__/{required-not-null-migration-safety.integration.ts → required-not-null-migration-safety.integration.test.ts} +28 -24
- package/src/db/__tests__/{schema-migration.integration.ts → schema-migration.integration.test.ts} +32 -28
- package/src/db/__tests__/sql-inventory.test.ts +56 -0
- package/src/db/__tests__/table-builder-indexes.test.ts +30 -11
- package/src/db/__tests__/table-builder-required.test.ts +20 -22
- package/src/db/__tests__/{tenant-db.integration.ts → tenant-db.integration.test.ts} +106 -144
- package/src/db/__tests__/{unique-violation-mapping.integration.ts → unique-violation-mapping.integration.test.ts} +13 -8
- package/src/db/api.ts +46 -0
- package/src/db/apply-entity-event.ts +45 -36
- package/src/db/assert-exists-in.ts +5 -16
- package/src/db/bun-provider.ts +37 -0
- package/src/db/config-seed.ts +4 -4
- package/src/db/connection.ts +14 -57
- package/src/db/cursor.ts +5 -56
- package/src/db/dialect.ts +472 -99
- package/src/db/eagerload.ts +5 -12
- package/src/db/entity-table-meta.ts +390 -0
- package/src/db/event-store-executor.ts +158 -100
- package/src/db/index.ts +33 -5
- package/src/db/migrate-generator.ts +350 -0
- package/src/db/migrate-runner.ts +206 -0
- package/src/db/postgres-provider.ts +25 -0
- package/src/db/queries/entity-read.ts +15 -0
- package/src/db/queries/es-ops.ts +17 -0
- package/src/db/queries/event-consumer.ts +170 -0
- package/src/db/queries/event-store-admin.ts +127 -0
- package/src/db/queries/event-store.ts +155 -0
- package/src/db/queries/projection-rebuild.ts +59 -0
- package/src/db/queries/raw-sql.ts +15 -0
- package/src/db/queries/schema-drift.ts +35 -0
- package/src/db/queries/seed-context.ts +58 -0
- package/src/db/queries/table-ops.ts +11 -0
- package/src/db/queries/test-stack.ts +56 -0
- package/src/db/query-api.ts +22 -0
- package/src/db/query.ts +30 -0
- package/src/db/reference-data.ts +19 -22
- package/src/db/render-ddl.ts +57 -0
- package/src/db/row-helpers.ts +3 -52
- package/src/db/schema-inspection.ts +17 -4
- package/src/db/sql-inventory.ts +208 -0
- package/src/db/table-builder.ts +48 -40
- package/src/db/tenant-db.ts +105 -326
- package/src/engine/__tests__/auth-claims-registrar.test.ts +1 -1
- package/src/engine/__tests__/boot-validator-api-exposure.test.ts +3 -3
- package/src/engine/__tests__/boot-validator-located-timestamps.test.ts +1 -1
- package/src/engine/__tests__/boot-validator-pii-retention.test.ts +5 -5
- package/src/engine/__tests__/boot-validator-s0-integration.test.ts +3 -3
- package/src/engine/__tests__/boot-validator.test.ts +4 -3
- package/src/engine/__tests__/build-app-schema.test.ts +1 -1
- package/src/engine/__tests__/build-target.test.ts +1 -1
- package/src/engine/__tests__/claim-keys.test.ts +1 -1
- package/src/engine/__tests__/codemod-pipeline.test.ts +3 -3
- package/src/engine/__tests__/config-helpers.test.ts +1 -1
- package/src/engine/__tests__/effective-features.test.ts +1 -1
- package/src/engine/__tests__/engine.test.ts +1 -1
- package/src/engine/__tests__/entity-handlers.test.ts +3 -3
- package/src/engine/__tests__/event-helpers.test.ts +3 -3
- package/src/engine/__tests__/extends-registrar.test.ts +4 -4
- package/src/engine/__tests__/factories-long-text.test.ts +1 -1
- package/src/engine/__tests__/factories-time.test.ts +1 -1
- package/src/engine/__tests__/field-predicates.test.ts +1 -1
- package/src/engine/__tests__/hook-phases.test.ts +1 -1
- package/src/engine/__tests__/identifiers.test.ts +1 -1
- package/src/engine/__tests__/lifecycle-hooks.test.ts +1 -1
- package/src/engine/__tests__/nav.test.ts +1 -1
- package/src/engine/__tests__/ownership.test.ts +10 -11
- package/src/engine/__tests__/parse-ref-target.test.ts +1 -1
- package/src/engine/__tests__/pipeline-engine.test.ts +1 -1
- package/src/engine/__tests__/{pipeline-handler.integration.ts → pipeline-handler.integration.test.ts} +38 -52
- package/src/engine/__tests__/{pipeline-observability.integration.ts → pipeline-observability.integration.test.ts} +1 -1
- package/src/engine/__tests__/{pipeline-performance.integration.ts → pipeline-performance.integration.test.ts} +1 -1
- package/src/engine/__tests__/pipeline-sub-pipelines.test.ts +1 -1
- package/src/engine/__tests__/post-query-hook.test.ts +1 -1
- package/src/engine/__tests__/projection-helpers.test.ts +25 -17
- package/src/engine/__tests__/projection.test.ts +4 -4
- package/src/engine/__tests__/qualified-name.test.ts +1 -1
- package/src/engine/__tests__/raw-table.test.ts +9 -8
- package/src/engine/__tests__/resolve-config-or-param.test.ts +5 -5
- package/src/engine/__tests__/run-in.test.ts +1 -1
- package/src/engine/__tests__/schema-builder.test.ts +1 -1
- package/src/engine/__tests__/screen.test.ts +1 -1
- package/src/engine/__tests__/search-payload-extension.test.ts +3 -3
- package/src/engine/__tests__/state-machine.test.ts +1 -1
- package/src/engine/__tests__/steps-aggregate-append-event.test.ts +7 -7
- package/src/engine/__tests__/steps-aggregate-create.test.ts +4 -4
- package/src/engine/__tests__/steps-aggregate-update.test.ts +3 -3
- package/src/engine/__tests__/steps-call-feature.test.ts +5 -5
- package/src/engine/__tests__/steps-mail-send.test.ts +7 -7
- package/src/engine/__tests__/steps-read.test.ts +34 -40
- package/src/engine/__tests__/steps-resolver-utils.test.ts +6 -6
- package/src/engine/__tests__/steps-unsafe-projection-delete.test.ts +24 -19
- package/src/engine/__tests__/steps-unsafe-projection-upsert.test.ts +28 -17
- package/src/engine/__tests__/steps-webhook-send.test.ts +6 -6
- package/src/engine/__tests__/steps-workflow.test.ts +7 -7
- package/src/engine/__tests__/system-user.test.ts +1 -1
- package/src/engine/__tests__/validate-projection-allowlist.test.ts +4 -5
- package/src/engine/__tests__/validation-hooks.test.ts +1 -1
- package/src/engine/__tests__/visual-tree-patterns.test.ts +1 -1
- package/src/engine/boot-validator/entity-handler.ts +3 -3
- package/src/engine/boot-validator/ownership.ts +1 -1
- package/src/engine/define-feature.ts +1 -2
- package/src/engine/entity-handlers.ts +5 -5
- package/src/engine/factories.ts +1 -1
- package/src/engine/feature-ast/__tests__/canonical-form.test.ts +1 -1
- package/src/engine/feature-ast/__tests__/parse-happy-path.test.ts +1 -1
- package/src/engine/feature-ast/__tests__/parse-real-features.test.ts +2 -2
- package/src/engine/feature-ast/__tests__/parse.test.ts +1 -1
- package/src/engine/feature-ast/__tests__/patch.test.ts +1 -1
- package/src/engine/feature-ast/__tests__/patcher.test.ts +1 -1
- package/src/engine/feature-ast/__tests__/render-roundtrip.test.ts +1 -1
- package/src/engine/feature-ast/__tests__/visual-tree-parse.test.ts +1 -1
- package/src/engine/ownership.ts +113 -41
- package/src/engine/pattern-library/__tests__/library.test.ts +2 -2
- package/src/engine/projection-helpers.ts +2 -11
- package/src/engine/registry.ts +2 -2
- package/src/engine/steps/read-find-many.ts +13 -13
- package/src/engine/steps/read-find-one.ts +7 -9
- package/src/engine/steps/unsafe-projection-delete.ts +4 -5
- package/src/engine/steps/unsafe-projection-upsert.ts +63 -31
- package/src/engine/types/feature.ts +7 -2
- package/src/engine/types/fields.ts +4 -5
- package/src/engine/types/step.ts +10 -10
- package/src/engine/validate-projection-allowlist.ts +23 -3
- package/src/entrypoint/__tests__/{entrypoint-job-wiring.integration.ts → entrypoint-job-wiring.integration.test.ts} +4 -3
- package/src/entrypoint/__tests__/{split-deploy.integration.ts → split-deploy.integration.test.ts} +4 -3
- package/src/env/__tests__/compose-env-schema.test.ts +1 -1
- package/src/env/__tests__/dry-run.test.ts +1 -1
- package/src/errors/__tests__/classes.test.ts +1 -1
- package/src/errors/__tests__/write-failures.test.ts +1 -1
- package/src/es-ops/__tests__/{context.integration.ts → context.integration.test.ts} +43 -29
- package/src/es-ops/__tests__/{runner.integration.ts → runner.integration.test.ts} +25 -23
- package/src/es-ops/__tests__/runner.test.ts +29 -19
- package/src/es-ops/context.ts +9 -43
- package/src/es-ops/operations-schema.ts +2 -2
- package/src/es-ops/runner.ts +12 -26
- package/src/event-store/__tests__/{admin-api.integration.ts → admin-api.integration.test.ts} +71 -45
- package/src/event-store/__tests__/{event-store.integration.ts → event-store.integration.test.ts} +7 -5
- package/src/event-store/__tests__/{get-stream-version-perf.integration.ts → get-stream-version-perf.integration.test.ts} +5 -3
- package/src/event-store/__tests__/{perf.integration.ts → perf.integration.test.ts} +24 -16
- package/src/event-store/__tests__/{snapshot.integration.ts → snapshot.integration.test.ts} +34 -28
- package/src/event-store/__tests__/{upcaster-dead-letter.integration.ts → upcaster-dead-letter.integration.test.ts} +11 -12
- package/src/event-store/__tests__/{upcaster.integration.ts → upcaster.integration.test.ts} +19 -32
- package/src/event-store/admin-api.ts +55 -83
- package/src/event-store/archive.ts +15 -39
- package/src/event-store/event-store.ts +92 -86
- package/src/event-store/events-schema.ts +2 -1
- package/src/event-store/index.ts +1 -0
- package/src/event-store/snapshot.ts +26 -24
- package/src/event-store/upcaster-dead-letter.ts +19 -18
- package/src/files/__tests__/content-disposition.test.ts +1 -1
- package/src/files/__tests__/{file-field-pipeline.integration.ts → file-field-pipeline.integration.test.ts} +8 -5
- package/src/files/__tests__/file-handle.test.ts +1 -1
- package/src/files/__tests__/{files.integration.ts → files.integration.test.ts} +32 -17
- package/src/files/__tests__/read-stream.test.ts +1 -1
- package/src/files/__tests__/{storage-tracking.integration.ts → storage-tracking.integration.test.ts} +26 -30
- package/src/files/__tests__/write-stream.test.ts +1 -1
- package/src/files/__tests__/zip-stream.test.ts +1 -1
- package/src/files/file-ref-table.ts +2 -2
- package/src/files/file-routes.ts +7 -9
- package/src/files/storage-tracking.ts +9 -17
- package/src/i18n/__tests__/i18n.test.ts +1 -1
- package/src/jobs/__tests__/{job-event-trigger.integration.ts → job-event-trigger.integration.test.ts} +6 -3
- package/src/jobs/__tests__/{job-multi-trigger.integration.ts → job-multi-trigger.integration.test.ts} +6 -3
- package/src/jobs/__tests__/{jobs.integration.ts → jobs.integration.test.ts} +5 -7
- package/src/lifecycle/__tests__/{lifecycle-server.integration.ts → lifecycle-server.integration.test.ts} +1 -1
- package/src/lifecycle/__tests__/lifecycle.test.ts +6 -6
- package/src/lifecycle/__tests__/signal-handlers.test.ts +6 -6
- package/src/logging/__tests__/pino-trace-bridge.test.ts +1 -1
- package/src/migrations/__tests__/compare-snapshots.test.ts +1 -1
- package/src/migrations/__tests__/{detect-drift.integration.ts → detect-drift.integration.test.ts} +34 -26
- package/src/migrations/__tests__/{detect-projections-to-rebuild.integration.ts → detect-projections-to-rebuild.integration.test.ts} +1 -1
- package/src/migrations/__tests__/rebuild-marker.test.ts +1 -1
- package/src/migrations/projection-detection.ts +12 -1
- package/src/migrations/schema-drift.ts +7 -23
- package/src/observability/__tests__/console-provider.test.ts +1 -1
- package/src/observability/__tests__/metric-validator.test.ts +1 -1
- package/src/observability/__tests__/noop-provider.test.ts +1 -1
- package/src/observability/__tests__/{observability.integration.ts → observability.integration.test.ts} +5 -8
- package/src/observability/__tests__/prometheus-meter.test.ts +1 -1
- package/src/observability/__tests__/recording-meter.test.ts +1 -1
- package/src/observability/__tests__/recording-tracer.test.ts +1 -1
- package/src/observability/__tests__/sensitive-filter.test.ts +1 -1
- package/src/pipeline/__tests__/{archive-stream.integration.ts → archive-stream.integration.test.ts} +3 -3
- package/src/pipeline/__tests__/auth-claims-resolver.test.ts +9 -9
- package/src/pipeline/__tests__/{cascade-handler.integration.ts → cascade-handler.integration.test.ts} +18 -15
- package/src/pipeline/__tests__/cascade-handler.test.ts +1 -1
- package/src/pipeline/__tests__/{causation-chain.integration.ts → causation-chain.integration.test.ts} +12 -13
- package/src/pipeline/__tests__/{ctx-bridge.integration.ts → ctx-bridge.integration.test.ts} +12 -11
- package/src/pipeline/__tests__/dispatcher.test.ts +2 -2
- package/src/pipeline/__tests__/{distributed-lock.integration.ts → distributed-lock.integration.test.ts} +1 -1
- package/src/pipeline/__tests__/{domain-events-projections.integration.ts → domain-events-projections.integration.test.ts} +13 -15
- package/src/pipeline/__tests__/{event-dedup.integration.ts → event-dedup.integration.test.ts} +1 -1
- package/src/pipeline/__tests__/{event-define-event-strict.integration.ts → event-define-event-strict.integration.test.ts} +6 -16
- package/src/pipeline/__tests__/{event-dispatcher-lifecycle.integration.ts → event-dispatcher-lifecycle.integration.test.ts} +1 -1
- package/src/pipeline/__tests__/{event-dispatcher-multi-instance.integration.ts → event-dispatcher-multi-instance.integration.test.ts} +3 -2
- package/src/pipeline/__tests__/{event-dispatcher-pg-listen.integration.ts → event-dispatcher-pg-listen.integration.test.ts} +1 -1
- package/src/pipeline/__tests__/{event-dispatcher-recovery.integration.ts → event-dispatcher-recovery.integration.test.ts} +2 -2
- package/src/pipeline/__tests__/{event-dispatcher-second-audit.integration.ts → event-dispatcher-second-audit.integration.test.ts} +17 -16
- package/src/pipeline/__tests__/event-dispatcher-strict.test.ts +14 -12
- package/src/pipeline/__tests__/{event-dispatcher.integration.ts → event-dispatcher.integration.test.ts} +8 -15
- package/src/pipeline/__tests__/{event-retention.integration.ts → event-retention.integration.test.ts} +28 -25
- package/src/pipeline/__tests__/{fetch-for-writing.integration.ts → fetch-for-writing.integration.test.ts} +6 -6
- package/src/pipeline/__tests__/lifecycle-pipeline.test.ts +4 -4
- package/src/pipeline/__tests__/{load-aggregate-query.integration.ts → load-aggregate-query.integration.test.ts} +9 -5
- package/src/pipeline/__tests__/{msp-error-mode.integration.ts → msp-error-mode.integration.test.ts} +1 -1
- package/src/pipeline/__tests__/{msp-multi-hop.integration.ts → msp-multi-hop.integration.test.ts} +9 -8
- package/src/pipeline/__tests__/{msp-rebuild.integration.ts → msp-rebuild.integration.test.ts} +47 -55
- package/src/pipeline/__tests__/{multi-stream-projection.integration.ts → multi-stream-projection.integration.test.ts} +19 -53
- package/src/pipeline/__tests__/{perf-rebuild.integration.ts → perf-rebuild.integration.test.ts} +36 -34
- package/src/pipeline/__tests__/{post-query-hook.integration.ts → post-query-hook.integration.test.ts} +1 -1
- package/src/pipeline/__tests__/{projection-rebuild.integration.ts → projection-rebuild.integration.test.ts} +21 -30
- package/src/pipeline/__tests__/{query-projection.integration.ts → query-projection.integration.test.ts} +6 -5
- package/src/pipeline/__tests__/{redis-pipeline.integration.ts → redis-pipeline.integration.test.ts} +3 -1
- package/src/pipeline/cascade-handler.ts +13 -21
- package/src/pipeline/dispatcher.ts +43 -48
- package/src/pipeline/event-consumer-state.ts +11 -2
- package/src/pipeline/event-dispatcher.ts +86 -146
- package/src/pipeline/event-retention.ts +14 -24
- package/src/pipeline/msp-rebuild.ts +54 -78
- package/src/pipeline/projection-rebuild.ts +65 -67
- package/src/pipeline/projection-state.ts +2 -2
- package/src/random/__tests__/generate.test.ts +13 -13
- package/src/rate-limit/__tests__/{dispatcher-l3.integration.ts → dispatcher-l3.integration.test.ts} +1 -1
- package/src/rate-limit/__tests__/{middleware.integration.ts → middleware.integration.test.ts} +1 -1
- package/src/rate-limit/__tests__/{resolver.integration.ts → resolver.integration.test.ts} +1 -1
- package/src/redis/__tests__/redis-options.test.ts +1 -1
- package/src/search/__tests__/{meilisearch-adapter.integration.ts → meilisearch-adapter.integration.test.ts} +1 -1
- package/src/search/__tests__/search-adapter.test.ts +1 -1
- package/src/secrets/__tests__/dek-cache.test.ts +1 -3
- package/src/secrets/__tests__/env-master-key-provider.test.ts +1 -1
- package/src/secrets/__tests__/envelope.test.ts +1 -1
- package/src/secrets/__tests__/leak-guard.test.ts +1 -1
- package/src/secrets/__tests__/rotation.test.ts +1 -1
- package/src/stack/db.ts +25 -48
- package/src/stack/push-entity-projection-tables.ts +2 -4
- package/src/stack/table-helpers.ts +98 -61
- package/src/stack/test-stack.ts +8 -7
- package/src/testing/__tests__/db-cleanup.test.ts +40 -0
- package/src/testing/__tests__/e2e-generator.test.ts +1 -1
- package/src/testing/__tests__/{ensure-entity-table.integration.ts → ensure-entity-table.integration.test.ts} +7 -14
- package/src/testing/db-cleanup.ts +44 -0
- package/src/testing/expect-error.ts +1 -1
- package/src/testing/index.ts +2 -0
- package/src/testing/multipart-helper.ts +94 -0
- package/src/testing/shared-entities.ts +5 -5
- package/src/time/__tests__/polyfill.test.ts +1 -1
- package/src/time/__tests__/tz-context.test.ts +1 -1
- package/src/utils/__tests__/assert.test.ts +1 -1
- package/src/utils/__tests__/env-parse.test.ts +1 -1
- package/CHANGELOG.md +0 -474
- package/src/db/__tests__/cursor.test.ts +0 -41
- package/src/db/__tests__/db-helpers.test.ts +0 -369
- package/src/db/__tests__/drizzle-helpers.integration.ts +0 -186
- package/src/db/__tests__/row-helpers.test.ts +0 -59
- package/src/engine/steps/_drizzle-boundary.ts +0 -19
- package/src/files/__tests__/file-field-column.integration.ts +0 -103
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "
|
|
1
|
+
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { createEventStoreExecutor } from "../../db/event-store-executor";
|
|
4
|
-
import {
|
|
4
|
+
import { asRawClient, insertOne, selectMany } from "../../db/query";
|
|
5
|
+
import { buildEntityTable } from "../../db/table-builder";
|
|
5
6
|
import {
|
|
6
7
|
createEntity,
|
|
7
8
|
createNumberField,
|
|
@@ -23,7 +24,7 @@ const itemEntity = createEntity({
|
|
|
23
24
|
},
|
|
24
25
|
});
|
|
25
26
|
|
|
26
|
-
const itemTable =
|
|
27
|
+
const itemTable = buildEntityTable("item", itemEntity);
|
|
27
28
|
|
|
28
29
|
// Second entity used by an inTransaction hook to prove that hook DB writes
|
|
29
30
|
// roll back with the main transaction.
|
|
@@ -34,7 +35,7 @@ const auditEntity = createEntity({
|
|
|
34
35
|
itemId: createTextField({ required: true }),
|
|
35
36
|
},
|
|
36
37
|
});
|
|
37
|
-
const auditTable =
|
|
38
|
+
const auditTable = buildEntityTable("audit", auditEntity);
|
|
38
39
|
|
|
39
40
|
// Hook invocation logs — reset per test. Captures which phase each hook saw.
|
|
40
41
|
const inTxHookLog: Array<{ id: EntityId; name: string }> = [];
|
|
@@ -92,10 +93,7 @@ const itemFeature = defineFeature("batch", (r) => {
|
|
|
92
93
|
item,
|
|
93
94
|
async (result, ctx) => {
|
|
94
95
|
if (!ctx.db) return;
|
|
95
|
-
await ctx.db
|
|
96
|
-
.insert(auditTable)
|
|
97
|
-
.values({ action: "item_saved", itemId: result.id })
|
|
98
|
-
.returning();
|
|
96
|
+
await ctx.db.insertOne(auditTable, { action: "item_saved", itemId: result.id });
|
|
99
97
|
},
|
|
100
98
|
{ phase: HookPhases.inTransaction },
|
|
101
99
|
);
|
|
@@ -166,8 +164,8 @@ beforeEach(async () => {
|
|
|
166
164
|
afterCommitShouldThrow = false;
|
|
167
165
|
parallelismWindows.length = 0;
|
|
168
166
|
stack.events.reset();
|
|
169
|
-
await stack.db.
|
|
170
|
-
await stack.db.
|
|
167
|
+
await asRawClient(stack.db).unsafe(`DELETE FROM "${itemTable.tableName}"`);
|
|
168
|
+
await asRawClient(stack.db).unsafe(`DELETE FROM "${auditTable.tableName}"`);
|
|
171
169
|
});
|
|
172
170
|
|
|
173
171
|
describe("POST /api/batch", () => {
|
|
@@ -213,16 +211,18 @@ describe("POST /api/batch", () => {
|
|
|
213
211
|
expect(afterCommitHookLog.map((h) => h.name)).toEqual(["alpha", "beta", "gamma"]);
|
|
214
212
|
|
|
215
213
|
// Rows actually persisted
|
|
216
|
-
const rows = await stack.db
|
|
214
|
+
const rows = await selectMany(stack.db, itemTable);
|
|
217
215
|
expect(rows).toHaveLength(3);
|
|
218
216
|
});
|
|
219
217
|
|
|
220
218
|
test("mid-batch failure: all writes roll back, afterCommit hooks do NOT fire", async () => {
|
|
221
219
|
// Seed with one existing item so we can verify the batch didn't persist anything
|
|
222
|
-
await stack.db
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
220
|
+
await insertOne(stack.db, itemTable, {
|
|
221
|
+
name: "seed",
|
|
222
|
+
counter: 0,
|
|
223
|
+
tenantId: "00000000-0000-4000-8000-000000000001",
|
|
224
|
+
});
|
|
225
|
+
const seedCount = (await selectMany(stack.db, itemTable)).length;
|
|
226
226
|
|
|
227
227
|
const res = await stack.http.batch(
|
|
228
228
|
[
|
|
@@ -252,7 +252,7 @@ describe("POST /api/batch", () => {
|
|
|
252
252
|
expect(afterCommitHookLog).toEqual([]);
|
|
253
253
|
|
|
254
254
|
// DB: only the seed row remains, the batch's first successful write rolled back
|
|
255
|
-
const rows = await stack.db
|
|
255
|
+
const rows = await selectMany(stack.db, itemTable);
|
|
256
256
|
expect(rows).toHaveLength(seedCount);
|
|
257
257
|
expect((rows[0] as { name: string }).name).toBe("seed");
|
|
258
258
|
});
|
|
@@ -264,13 +264,13 @@ describe("POST /api/batch", () => {
|
|
|
264
264
|
admin,
|
|
265
265
|
);
|
|
266
266
|
expect((await okRes.json()).isSuccess).toBe(true);
|
|
267
|
-
const auditAfterOk = await stack.db
|
|
267
|
+
const auditAfterOk = await selectMany(stack.db, auditTable);
|
|
268
268
|
expect(auditAfterOk).toHaveLength(1);
|
|
269
269
|
expect((auditAfterOk[0] as { action: string }).action).toBe("item_saved");
|
|
270
270
|
|
|
271
271
|
// Reset — new batch fails mid-way. Both entity rows AND audit rows must roll back.
|
|
272
|
-
await stack.db.
|
|
273
|
-
await stack.db.
|
|
272
|
+
await asRawClient(stack.db).unsafe(`DELETE FROM "${itemTable.tableName}"`);
|
|
273
|
+
await asRawClient(stack.db).unsafe(`DELETE FROM "${auditTable.tableName}"`);
|
|
274
274
|
|
|
275
275
|
const failRes = await stack.http.batch(
|
|
276
276
|
[
|
|
@@ -283,8 +283,8 @@ describe("POST /api/batch", () => {
|
|
|
283
283
|
|
|
284
284
|
// Both tables are empty — the inTransaction audit hook's write rolled back
|
|
285
285
|
// together with the item row.
|
|
286
|
-
const itemsAfterFail = await stack.db
|
|
287
|
-
const auditAfterFail = await stack.db
|
|
286
|
+
const itemsAfterFail = await selectMany(stack.db, itemTable);
|
|
287
|
+
const auditAfterFail = await selectMany(stack.db, auditTable);
|
|
288
288
|
expect(itemsAfterFail).toHaveLength(0);
|
|
289
289
|
expect(auditAfterFail).toHaveLength(0);
|
|
290
290
|
});
|
|
@@ -328,7 +328,7 @@ describe("POST /api/batch", () => {
|
|
|
328
328
|
expect(body.isSuccess).toBe(true);
|
|
329
329
|
|
|
330
330
|
// DB row persisted (tx committed)
|
|
331
|
-
const rows = await stack.db
|
|
331
|
+
const rows = await selectMany(stack.db, itemTable);
|
|
332
332
|
expect(rows).toHaveLength(1);
|
|
333
333
|
|
|
334
334
|
// The hook AFTER the throwing one still ran — errors don't cascade
|
|
@@ -344,7 +344,7 @@ describe("POST /api/batch", () => {
|
|
|
344
344
|
expect(firstBody.isSuccess).toBe(true);
|
|
345
345
|
expect(firstBody.results).toHaveLength(1);
|
|
346
346
|
|
|
347
|
-
const rowsAfterFirst = await stack.db
|
|
347
|
+
const rowsAfterFirst = await selectMany(stack.db, itemTable);
|
|
348
348
|
expect(rowsAfterFirst).toHaveLength(1);
|
|
349
349
|
|
|
350
350
|
// Hook logs reflect one execution
|
|
@@ -359,7 +359,7 @@ describe("POST /api/batch", () => {
|
|
|
359
359
|
expect(secondBody.results).toEqual(firstBody.results);
|
|
360
360
|
|
|
361
361
|
// DB still has only one row (no double-insert)
|
|
362
|
-
const rowsAfterSecond = await stack.db
|
|
362
|
+
const rowsAfterSecond = await selectMany(stack.db, itemTable);
|
|
363
363
|
expect(rowsAfterSecond).toHaveLength(1);
|
|
364
364
|
|
|
365
365
|
// Hooks didn't fire a second time
|
|
@@ -376,8 +376,8 @@ describe("POST /api/write (single write runs in its own transaction)", () => {
|
|
|
376
376
|
|
|
377
377
|
// Both the item row AND the audit row exist — proves the single write
|
|
378
378
|
// went through a transaction and the inTx hook shared it.
|
|
379
|
-
const items = await stack.db
|
|
380
|
-
const audits = await stack.db
|
|
379
|
+
const items = await selectMany(stack.db, itemTable);
|
|
380
|
+
const audits = await selectMany(stack.db, auditTable);
|
|
381
381
|
expect(items).toHaveLength(1);
|
|
382
382
|
expect(audits).toHaveLength(1);
|
|
383
383
|
});
|
|
@@ -385,8 +385,8 @@ describe("POST /api/write (single write runs in its own transaction)", () => {
|
|
|
385
385
|
test("handler throw rolls back inTransaction hook writes too", async () => {
|
|
386
386
|
// First a successful write so there's something to compare against
|
|
387
387
|
await stack.http.write("batch:write:item:create", { name: "survivor" }, admin);
|
|
388
|
-
const beforeItems = await stack.db
|
|
389
|
-
const beforeAudits = await stack.db
|
|
388
|
+
const beforeItems = await selectMany(stack.db, itemTable);
|
|
389
|
+
const beforeAudits = await selectMany(stack.db, auditTable);
|
|
390
390
|
expect(beforeItems).toHaveLength(1);
|
|
391
391
|
expect(beforeAudits).toHaveLength(1);
|
|
392
392
|
|
|
@@ -395,8 +395,8 @@ describe("POST /api/write (single write runs in its own transaction)", () => {
|
|
|
395
395
|
const body = await res.json();
|
|
396
396
|
expect(body.isSuccess).toBe(false);
|
|
397
397
|
|
|
398
|
-
const afterItems = await stack.db
|
|
399
|
-
const afterAudits = await stack.db
|
|
398
|
+
const afterItems = await selectMany(stack.db, itemTable);
|
|
399
|
+
const afterAudits = await selectMany(stack.db, auditTable);
|
|
400
400
|
// Counts unchanged — no partial commit
|
|
401
401
|
expect(afterItems).toHaveLength(beforeItems.length);
|
|
402
402
|
expect(afterAudits).toHaveLength(beforeAudits.length);
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// layers authMiddleware → csrfMiddleware → handler. Covers the paths that
|
|
3
3
|
// matter in production: cookie + state-changing, cookie + safe, bearer.
|
|
4
4
|
|
|
5
|
+
import { describe, expect, test } from "bun:test";
|
|
5
6
|
import { Hono } from "hono";
|
|
6
|
-
import { describe, expect, test } from "vitest";
|
|
7
7
|
import { TestUsers } from "../../stack";
|
|
8
8
|
import {
|
|
9
9
|
AUTH_COOKIE_NAME,
|
package/src/api/__tests__/{dispatcher-live.integration.ts → dispatcher-live.integration.test.ts}
RENAMED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
1
2
|
import { createLiveDispatcher } from "@cosmicdrift/kumiko-dispatcher-live";
|
|
2
|
-
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "vitest";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { generateToken } from "../../api/tokens";
|
|
5
5
|
import { createEventStoreExecutor } from "../../db/event-store-executor";
|
|
6
|
-
import {
|
|
6
|
+
import { asRawClient, insertOne, selectMany } from "../../db/query";
|
|
7
|
+
import { buildEntityTable } from "../../db/table-builder";
|
|
7
8
|
import { createEntity, createTextField, defineFeature } from "../../engine";
|
|
8
9
|
import { setupTestStack, type TestStack, TestUsers, unsafeCreateEntityTable } from "../../stack";
|
|
9
10
|
import { generateId } from "../../utils";
|
|
@@ -23,7 +24,7 @@ const itemEntity = createEntity({
|
|
|
23
24
|
table: "dispatcher_live_items",
|
|
24
25
|
fields: { name: createTextField({ required: true }) },
|
|
25
26
|
});
|
|
26
|
-
const itemTable =
|
|
27
|
+
const itemTable = buildEntityTable("item", itemEntity);
|
|
27
28
|
|
|
28
29
|
const itemFeature = defineFeature("dlive", (r) => {
|
|
29
30
|
r.entity("item", itemEntity);
|
|
@@ -42,7 +43,7 @@ const itemFeature = defineFeature("dlive", (r) => {
|
|
|
42
43
|
"item:list",
|
|
43
44
|
z.object({}).optional(),
|
|
44
45
|
async (_event, ctx) => {
|
|
45
|
-
return ctx.db
|
|
46
|
+
return selectMany(ctx.db, itemTable);
|
|
46
47
|
},
|
|
47
48
|
{ access: { roles: ["Admin"] } },
|
|
48
49
|
);
|
|
@@ -105,7 +106,7 @@ afterAll(async () => {
|
|
|
105
106
|
});
|
|
106
107
|
|
|
107
108
|
beforeEach(async () => {
|
|
108
|
-
await stack.db.
|
|
109
|
+
await asRawClient(stack.db).unsafe(`DELETE FROM "${itemTable.tableName}"`);
|
|
109
110
|
});
|
|
110
111
|
|
|
111
112
|
describe("dispatcher-live (integration) — full path against Kumiko server", () => {
|
|
@@ -123,7 +124,7 @@ describe("dispatcher-live (integration) — full path against Kumiko server", ()
|
|
|
123
124
|
expect(result.isSuccess).toBe(true);
|
|
124
125
|
|
|
125
126
|
// Prove the server actually persisted.
|
|
126
|
-
const rows = await stack.db
|
|
127
|
+
const rows = await selectMany(stack.db, itemTable);
|
|
127
128
|
expect(rows).toHaveLength(1);
|
|
128
129
|
expect(rows[0]?.["name"]).toBe("hello-live");
|
|
129
130
|
});
|
|
@@ -158,7 +159,7 @@ describe("dispatcher-live (integration) — full path against Kumiko server", ()
|
|
|
158
159
|
|
|
159
160
|
test("query: dispatches GET-style-POST (Kumiko uses POST for query too), returns data", async () => {
|
|
160
161
|
// Seed a row first.
|
|
161
|
-
await stack.db
|
|
162
|
+
await insertOne(stack.db, itemTable, {
|
|
162
163
|
id: generateId(),
|
|
163
164
|
tenantId: admin.tenantId,
|
|
164
165
|
name: "seed",
|
|
@@ -188,7 +189,7 @@ describe("dispatcher-live (integration) — full path against Kumiko server", ()
|
|
|
188
189
|
|
|
189
190
|
expect(result.isSuccess).toBe(true);
|
|
190
191
|
|
|
191
|
-
const rows = await stack.db
|
|
192
|
+
const rows = await selectMany(stack.db, itemTable);
|
|
192
193
|
expect(rows).toHaveLength(3);
|
|
193
194
|
const names = rows.map((r) => r["name"]).sort();
|
|
194
195
|
expect(names).toEqual(["a", "b", "c"]);
|
|
@@ -210,7 +211,7 @@ describe("dispatcher-live (integration) — full path against Kumiko server", ()
|
|
|
210
211
|
}
|
|
211
212
|
|
|
212
213
|
// DB must be empty — prior success within a failed batch rolls back.
|
|
213
|
-
const rows = await stack.db
|
|
214
|
+
const rows = await selectMany(stack.db, itemTable);
|
|
214
215
|
expect(rows).toHaveLength(0);
|
|
215
216
|
});
|
|
216
217
|
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "vitest";
|
|
1
|
+
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
3
2
|
import { z } from "zod";
|
|
4
3
|
import { createEventStoreExecutor } from "../../db/event-store-executor";
|
|
5
|
-
import {
|
|
4
|
+
import { asRawClient, selectMany } from "../../db/query";
|
|
5
|
+
import { buildEntityTable } from "../../db/table-builder";
|
|
6
6
|
import { createEntity, createTextField, defineFeature } from "../../engine";
|
|
7
7
|
import { setupTestStack, type TestStack, TestUsers, unsafeCreateEntityTable } from "../../stack";
|
|
8
8
|
|
|
@@ -14,7 +14,7 @@ const projectEntity = createEntity({
|
|
|
14
14
|
table: "nested_projects",
|
|
15
15
|
fields: { name: createTextField({ required: true }) },
|
|
16
16
|
});
|
|
17
|
-
const projectTable =
|
|
17
|
+
const projectTable = buildEntityTable("project", projectEntity);
|
|
18
18
|
|
|
19
19
|
const taskEntity = createEntity({
|
|
20
20
|
table: "nested_tasks",
|
|
@@ -23,7 +23,7 @@ const taskEntity = createEntity({
|
|
|
23
23
|
title: createTextField({ required: true }),
|
|
24
24
|
},
|
|
25
25
|
});
|
|
26
|
-
const taskTable =
|
|
26
|
+
const taskTable = buildEntityTable("task", taskEntity);
|
|
27
27
|
|
|
28
28
|
const nestedFeature = defineFeature("nested", (r) => {
|
|
29
29
|
r.entity("project", projectEntity);
|
|
@@ -78,8 +78,8 @@ afterAll(async () => {
|
|
|
78
78
|
});
|
|
79
79
|
|
|
80
80
|
beforeEach(async () => {
|
|
81
|
-
await stack.db.
|
|
82
|
-
await stack.db.
|
|
81
|
+
await asRawClient(stack.db).unsafe(`DELETE FROM "${taskTable.tableName}"`);
|
|
82
|
+
await asRawClient(stack.db).unsafe(`DELETE FROM "${projectTable.tableName}"`);
|
|
83
83
|
});
|
|
84
84
|
|
|
85
85
|
describe("POST /api/write — nested-write (Welle M1)", () => {
|
|
@@ -111,11 +111,8 @@ describe("POST /api/write — nested-write (Welle M1)", () => {
|
|
|
111
111
|
expect(parent.tasks[1].title).toBe("t2");
|
|
112
112
|
|
|
113
113
|
// DB reflects both writes.
|
|
114
|
-
const dbProjects = await stack.db
|
|
115
|
-
const dbTasks = await stack.db
|
|
116
|
-
.select()
|
|
117
|
-
.from(taskTable)
|
|
118
|
-
.where(eq(taskTable["projectId"], parent.id));
|
|
114
|
+
const dbProjects = await selectMany(stack.db, projectTable);
|
|
115
|
+
const dbTasks = await selectMany(stack.db, taskTable, { projectId: parent.id });
|
|
119
116
|
expect(dbProjects).toHaveLength(1);
|
|
120
117
|
expect(dbTasks).toHaveLength(2);
|
|
121
118
|
});
|
|
@@ -137,8 +134,8 @@ describe("POST /api/write — nested-write (Welle M1)", () => {
|
|
|
137
134
|
expect(body.isSuccess).toBe(false);
|
|
138
135
|
|
|
139
136
|
// DB empty — prior sub-task and parent both rolled back.
|
|
140
|
-
const dbProjects = await stack.db
|
|
141
|
-
const dbTasks = await stack.db
|
|
137
|
+
const dbProjects = await selectMany(stack.db, projectTable);
|
|
138
|
+
const dbTasks = await selectMany(stack.db, taskTable);
|
|
142
139
|
expect(dbProjects).toHaveLength(0);
|
|
143
140
|
expect(dbTasks).toHaveLength(0);
|
|
144
141
|
});
|
|
@@ -182,7 +179,7 @@ describe("POST /api/write — nested-write (Welle M1)", () => {
|
|
|
182
179
|
|
|
183
180
|
// Parent did not persist — the pre-flight check runs before the parent
|
|
184
181
|
// write, so the TX never opened on a malformed nested key.
|
|
185
|
-
const dbProjects = await stack.db
|
|
182
|
+
const dbProjects = await selectMany(stack.db, projectTable);
|
|
186
183
|
expect(dbProjects).toHaveLength(0);
|
|
187
184
|
});
|
|
188
185
|
|
|
@@ -207,7 +204,7 @@ describe("POST /api/write — nested-write (Welle M1)", () => {
|
|
|
207
204
|
expect(body.error.details.fields[0].path).toMatch(/tasks\.0\.projectId/);
|
|
208
205
|
|
|
209
206
|
// DB empty.
|
|
210
|
-
const dbProjects = await stack.db
|
|
207
|
+
const dbProjects = await selectMany(stack.db, projectTable);
|
|
211
208
|
expect(dbProjects).toHaveLength(0);
|
|
212
209
|
});
|
|
213
210
|
});
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { describe, expect,
|
|
1
|
+
import { describe, expect, mock, test } from "bun:test";
|
|
2
2
|
import { createSseBroker, type SseEvent } from "../sse-broker";
|
|
3
3
|
|
|
4
4
|
describe("SSE broker", () => {
|
|
5
5
|
test("adds client and tracks count", () => {
|
|
6
6
|
const broker = createSseBroker();
|
|
7
|
-
broker.addClient("ch1",
|
|
8
|
-
broker.addClient("ch1",
|
|
9
|
-
broker.addClient("ch2",
|
|
7
|
+
broker.addClient("ch1", mock(), mock());
|
|
8
|
+
broker.addClient("ch1", mock(), mock());
|
|
9
|
+
broker.addClient("ch2", mock(), mock());
|
|
10
10
|
|
|
11
11
|
expect(broker.getClientCount("ch1")).toBe(2);
|
|
12
12
|
expect(broker.getClientCount("ch2")).toBe(1);
|
|
@@ -15,13 +15,13 @@ describe("SSE broker", () => {
|
|
|
15
15
|
|
|
16
16
|
test("pushToChannel sends to all clients on channel", () => {
|
|
17
17
|
const broker = createSseBroker();
|
|
18
|
-
const send1 =
|
|
19
|
-
const send2 =
|
|
20
|
-
const sendOther =
|
|
18
|
+
const send1 = mock();
|
|
19
|
+
const send2 = mock();
|
|
20
|
+
const sendOther = mock();
|
|
21
21
|
|
|
22
|
-
broker.addClient("users", send1,
|
|
23
|
-
broker.addClient("users", send2,
|
|
24
|
-
broker.addClient("other", sendOther,
|
|
22
|
+
broker.addClient("users", send1, mock());
|
|
23
|
+
broker.addClient("users", send2, mock());
|
|
24
|
+
broker.addClient("other", sendOther, mock());
|
|
25
25
|
|
|
26
26
|
const event: SseEvent = { type: "user.created", data: { id: 1 } };
|
|
27
27
|
broker.pushToChannel("users", event);
|
|
@@ -33,9 +33,9 @@ describe("SSE broker", () => {
|
|
|
33
33
|
|
|
34
34
|
test("removeClient stops delivery", () => {
|
|
35
35
|
const broker = createSseBroker();
|
|
36
|
-
const send =
|
|
36
|
+
const send = mock();
|
|
37
37
|
|
|
38
|
-
const clientId = broker.addClient("ch", send,
|
|
38
|
+
const clientId = broker.addClient("ch", send, mock());
|
|
39
39
|
broker.removeClient("ch", clientId);
|
|
40
40
|
|
|
41
41
|
broker.pushToChannel("ch", { type: "test", data: {} });
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
1
2
|
import { Hono } from "hono";
|
|
2
|
-
import { describe, expect, test } from "vitest";
|
|
3
3
|
import { TestUsers } from "../../stack";
|
|
4
4
|
import { authMiddleware } from "../auth-middleware";
|
|
5
5
|
import { createJwtHelper } from "../jwt";
|
package/src/api/readiness.ts
CHANGED
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
// - Checks run in parallel — the probe is called on every kubelet/ALB poll,
|
|
12
12
|
// so total latency must stay ≈ slowest check, not sum.
|
|
13
13
|
|
|
14
|
-
import { sql } from "drizzle-orm";
|
|
15
14
|
import type Redis from "ioredis";
|
|
16
15
|
import type { DbConnection } from "../db/connection";
|
|
16
|
+
import { pingDatabase } from "../db/queries/raw-sql";
|
|
17
17
|
import { getAllConsumerProgress } from "../pipeline/event-dispatcher";
|
|
18
18
|
|
|
19
19
|
export type ReadinessCheck = {
|
|
@@ -90,7 +90,7 @@ export function dbPingCheck(db: DbConnection): ReadinessCheck {
|
|
|
90
90
|
return {
|
|
91
91
|
name: "db",
|
|
92
92
|
run: async () => {
|
|
93
|
-
await db
|
|
93
|
+
await pingDatabase(db);
|
|
94
94
|
},
|
|
95
95
|
};
|
|
96
96
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Snapshot-Tests fuer ROLES — faengt stille Drift ab.
|
|
2
2
|
|
|
3
|
-
import { describe, expect, test } from "
|
|
3
|
+
import { describe, expect, test } from "bun:test";
|
|
4
4
|
import { ROLES } from "../roles";
|
|
5
5
|
|
|
6
6
|
describe("ROLES constants", () => {
|
|
@@ -18,7 +18,7 @@ describe("ROLES constants", () => {
|
|
|
18
18
|
|
|
19
19
|
test("ROLES-Werte sind identisch zu den Keys (keine Drift im Mapping)", () => {
|
|
20
20
|
for (const [key, value] of Object.entries(ROLES)) {
|
|
21
|
-
expect(value).toBe(key);
|
|
21
|
+
expect(value as string).toBe(key);
|
|
22
22
|
}
|
|
23
23
|
});
|
|
24
24
|
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Pattern: Provider-agnostische DB-Integration-Tests
|
|
2
|
+
|
|
3
|
+
## Architektur
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
db/api.ts createConnection() — liest DB_PROVIDER env
|
|
7
|
+
db/postgres-provider.ts postgres-js Factory (default, stabil)
|
|
8
|
+
db/bun-provider.ts Bun.SQL Factory (DB_PROVIDER=bun, experimentell)
|
|
9
|
+
stack/db.ts createTestDb() — provider-agnostisch via createConnection
|
|
10
|
+
stack/test-stack.ts setupTestStack() — provider-agnostisch via createTestDb
|
|
11
|
+
bun-db/__tests__/bun-test-stack.ts Alias: setupBunTestStack → setupTestStack
|
|
12
|
+
bun-db/__tests__/bun-test-db.ts Alias: createBunTestDb → createTestDb
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Snippet: Integration-Test
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { setupTestStack, type TestStack } from "../stack";
|
|
19
|
+
import { defineFeature } from "../engine";
|
|
20
|
+
|
|
21
|
+
const feature = defineFeature("example", (r) => { /* ... */ });
|
|
22
|
+
|
|
23
|
+
let stack: TestStack;
|
|
24
|
+
|
|
25
|
+
beforeAll(async () => {
|
|
26
|
+
stack = await setupTestStack({ features: [feature] });
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
afterAll(async () => {
|
|
30
|
+
await stack.cleanup();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("works", async () => {
|
|
34
|
+
// stack.db → provider-agnostic (asRawClient für .unsafe/.begin)
|
|
35
|
+
// stack.http → RequestHelper (writeOk, queryOk, raw)
|
|
36
|
+
// stack.redis → TestRedis
|
|
37
|
+
// stack.events → EventCollector
|
|
38
|
+
});
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Nur DB (ohne HTTP/Stack):
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import { createTestDb, type TestDb } from "../stack";
|
|
45
|
+
import { asRawClient } from "../bun-db/query";
|
|
46
|
+
|
|
47
|
+
let td: TestDb;
|
|
48
|
+
beforeAll(async () => { td = await createTestDb(); });
|
|
49
|
+
afterAll(async () => { await td.cleanup(); });
|
|
50
|
+
test("works", async () => {
|
|
51
|
+
await asRawClient(td.db).unsafe("SELECT 1");
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Switching Driver
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# Default: postgres-js
|
|
59
|
+
bun test ./src/**/*.integration.ts
|
|
60
|
+
|
|
61
|
+
# Bun.SQL (experimentell)
|
|
62
|
+
DB_PROVIDER=bun bun test ./src/**/*.integration.ts
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Bekannte Issues
|
|
66
|
+
|
|
67
|
+
- **Bun.SQL Extended-Query-Protocol Bug**: PostgresError "bind message has 11 result
|
|
68
|
+
formats but query has 1 columns" bei sequentiellen Queries mit unterschiedlicher
|
|
69
|
+
Spaltenzahl innerhalb derselben Transaction. Nur bei `DB_PROVIDER=bun`.
|
|
70
|
+
- **Temporal-Polyfill**: `createTestDb()` ruft jetzt `ensureTemporalPolyfill()` auf.
|
|
71
|
+
- **TenantDb.insertOne**: Immer `tdb.insertOne(table, ...)` verwenden (Methode),
|
|
72
|
+
NICHT `insertOne(tdb, table, ...)` (standalone aus bun-db/query — bypassed
|
|
73
|
+
tenant-injection via asRawClient).
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// Helpers für bun-db SQL-Layer-Integration-Tests.
|
|
2
|
+
// Provider-agnostic via createConnection (DB_PROVIDER env).
|
|
3
|
+
//
|
|
4
|
+
// Pattern: pro Test eine eigene Tabelle mit unique-Name (random-Suffix),
|
|
5
|
+
// damit concurrency=8 tests sich nicht in die Quere kommen. CREATE TABLE
|
|
6
|
+
// im before, DROP TABLE im after (auch bei test-fail damit Test-DB clean
|
|
7
|
+
// bleibt). KEINE TEMP TABLES — die wären connection-bound und pools
|
|
8
|
+
// reused connections, was tests in zwei verschiedenen Pool-Slots
|
|
9
|
+
// in verschiedene "TEMP-Welten" stecken würde.
|
|
10
|
+
//
|
|
11
|
+
// Schema-Tabellen bestehen aus einer id (uuid) + den getesteten Spalten.
|
|
12
|
+
|
|
13
|
+
import { randomUUID } from "node:crypto";
|
|
14
|
+
import { createConnection } from "../../db/api";
|
|
15
|
+
import type { ColumnMeta, EntityTableMeta } from "../../db/entity-table-meta";
|
|
16
|
+
|
|
17
|
+
const DATABASE_URL =
|
|
18
|
+
process.env["TEST_DATABASE_URL"] ??
|
|
19
|
+
process.env["DATABASE_URL"] ??
|
|
20
|
+
"postgresql://kumiko:kumiko@localhost:15432/kumiko_test";
|
|
21
|
+
|
|
22
|
+
let dbInstance: { db: unknown; close: () => Promise<void> } | undefined;
|
|
23
|
+
|
|
24
|
+
export async function getDb(): Promise<unknown> {
|
|
25
|
+
return ensureDb();
|
|
26
|
+
}
|
|
27
|
+
export async function ensureDb(): Promise<unknown> {
|
|
28
|
+
if (!dbInstance) {
|
|
29
|
+
dbInstance = await createConnection(DATABASE_URL, { maxConnections: 4 });
|
|
30
|
+
}
|
|
31
|
+
return dbInstance.db;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function closeDb(): Promise<void> {
|
|
35
|
+
if (dbInstance) {
|
|
36
|
+
await dbInstance.close();
|
|
37
|
+
dbInstance = undefined;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Erzeugt einen pseudo-zufälligen Tabellen-Namen. snake-case, lower,
|
|
42
|
+
// nur a-z0-9_, max 50 chars (Postgres-Identifier-Limit ist 63).
|
|
43
|
+
export function uniqueTableName(prefix = "sqltest"): string {
|
|
44
|
+
const suffix = randomUUID().replace(/-/g, "").slice(0, 12);
|
|
45
|
+
return `${prefix}_${suffix}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// EntityTableMeta-Helper. id (uuid primary key) wird IMMER gesetzt; der
|
|
49
|
+
// Caller liefert nur die zusätzlichen Test-Spalten.
|
|
50
|
+
export function makeTableMeta(
|
|
51
|
+
tableName: string,
|
|
52
|
+
extraColumns: readonly ColumnMeta[],
|
|
53
|
+
): EntityTableMeta {
|
|
54
|
+
return {
|
|
55
|
+
tableName,
|
|
56
|
+
source: "unmanaged",
|
|
57
|
+
indexes: [],
|
|
58
|
+
columns: [
|
|
59
|
+
{
|
|
60
|
+
name: "id",
|
|
61
|
+
pgType: "uuid",
|
|
62
|
+
notNull: true,
|
|
63
|
+
primaryKey: true,
|
|
64
|
+
defaultSql: "gen_random_uuid()",
|
|
65
|
+
},
|
|
66
|
+
...extraColumns,
|
|
67
|
+
],
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// CREATE TABLE-Statement aus EntityTableMeta. Minimaler DDL-Renderer —
|
|
72
|
+
// kein Index-Support, kein Komplex-Default. Genug für die Test-Matrix.
|
|
73
|
+
export function renderCreateTable(meta: EntityTableMeta): string {
|
|
74
|
+
const cols = meta.columns
|
|
75
|
+
.map((c) => {
|
|
76
|
+
const parts = [`"${c.name}"`, c.pgType];
|
|
77
|
+
if (c.notNull) parts.push("NOT NULL");
|
|
78
|
+
if (c.defaultSql) parts.push(`DEFAULT ${c.defaultSql}`);
|
|
79
|
+
if (c.primaryKey) parts.push("PRIMARY KEY");
|
|
80
|
+
return parts.join(" ");
|
|
81
|
+
})
|
|
82
|
+
.join(", ");
|
|
83
|
+
return `CREATE TABLE "${meta.tableName}" (${cols})`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Test-Wrapper: Tabelle anlegen, fn aufrufen, Tabelle droppen.
|
|
87
|
+
// fn bekommt {db, meta} — alle SQL-Operationen laufen direkt darauf.
|
|
88
|
+
export async function withTable<T>(
|
|
89
|
+
columns: readonly ColumnMeta[],
|
|
90
|
+
fn: (ctx: { db: unknown; meta: EntityTableMeta }) => Promise<T>,
|
|
91
|
+
prefix?: string,
|
|
92
|
+
): Promise<T> {
|
|
93
|
+
const db = await getDb();
|
|
94
|
+
const tableName = uniqueTableName(prefix);
|
|
95
|
+
const meta = makeTableMeta(tableName, columns);
|
|
96
|
+
const { asRawClient } = await import("../query");
|
|
97
|
+
await asRawClient(db).unsafe(renderCreateTable(meta));
|
|
98
|
+
try {
|
|
99
|
+
return await fn({ db, meta });
|
|
100
|
+
} finally {
|
|
101
|
+
await asRawClient(db).unsafe(`DROP TABLE IF EXISTS "${tableName}"`);
|
|
102
|
+
}
|
|
103
|
+
}
|