@cosmicdrift/kumiko-framework 0.14.0 → 0.16.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/auth-routes.ts +2 -5
- 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 +842 -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/compliance/profiles.ts +1 -4
- 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__/cursor.test.ts +8 -32
- 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__/migrate-generator.test.ts +71 -0
- package/src/db/__tests__/migrate-runner.test.ts +19 -0
- 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__/pg-error.test.ts +43 -0
- 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 +54 -46
- 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__/duration-utils.test.ts +16 -0
- 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-access.test.ts +38 -0
- 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__/no-return-guard.test.ts +17 -0
- 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__/unmanaged-table.test.ts +98 -0
- 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 +37 -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/feature-ast/extractors/shared.ts +2 -3
- 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 +21 -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 +47 -2
- package/src/engine/types/fields.ts +4 -5
- package/src/engine/types/index.ts +2 -0
- 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__/error-helpers.test.ts +44 -0
- package/src/errors/__tests__/field-issue-compat.test.ts +16 -0
- package/src/errors/__tests__/write-failures.test.ts +1 -1
- package/src/errors/classes.ts +5 -19
- package/src/errors/field-issue.ts +11 -0
- package/src/errors/index.ts +1 -0
- package/src/errors/zod-bridge.ts +3 -2
- 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 +11 -56
- 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-utils.test.ts +107 -0
- 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-keys.test.ts +12 -0
- 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-utils.ts +8 -7
- 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 +10 -9
- 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__/case.test.ts +16 -0
- package/src/utils/__tests__/env-parse.test.ts +1 -1
- package/src/utils/__tests__/is-plain-object.test.ts +16 -0
- package/src/utils/__tests__/parse-string-array-json.test.ts +16 -0
- package/src/utils/__tests__/safe-json.test.ts +22 -0
- package/src/utils/case.ts +6 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/is-plain-object.ts +4 -0
- package/src/utils/parse-string-array-json.ts +14 -0
- package/CHANGELOG.md +0 -474
- 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,15 +1,19 @@
|
|
|
1
|
-
import { describe, expect,
|
|
1
|
+
import { describe, expect, mock, test } from "bun:test";
|
|
2
2
|
import type { StoredEvent } from "../../event-store/event-store";
|
|
3
3
|
import { setFields } from "../projection-helpers";
|
|
4
4
|
import type { ProjectionTable } from "../types/projection";
|
|
5
5
|
|
|
6
|
-
// Minimal fake table: only the `id` column is needed for setFields,
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// doesn't know user table shapes at compile time, so real Drizzle tables
|
|
10
|
-
// go through the same `any`-ish generic parameter.
|
|
6
|
+
// Minimal fake table: only the `id` column is needed for setFields, plus
|
|
7
|
+
// the kumiko:schema:Name + kumiko:schema:Columns symbols that bun-db introspects for
|
|
8
|
+
// table-name + column-mapping. We don't run real SQL — unsafe() is mocked.
|
|
11
9
|
const fakeIdCol = { name: "id" };
|
|
12
|
-
const fakeTable =
|
|
10
|
+
const fakeTable = Object.assign(
|
|
11
|
+
{ id: fakeIdCol },
|
|
12
|
+
{
|
|
13
|
+
[Symbol.for("kumiko:schema:Name")]: "fake_table",
|
|
14
|
+
[Symbol.for("kumiko:schema:Columns")]: { id: fakeIdCol },
|
|
15
|
+
},
|
|
16
|
+
) as unknown as ProjectionTable;
|
|
13
17
|
|
|
14
18
|
function makeFakeEvent(overrides: Partial<StoredEvent> = {}): StoredEvent {
|
|
15
19
|
return {
|
|
@@ -29,30 +33,34 @@ function makeFakeEvent(overrides: Partial<StoredEvent> = {}): StoredEvent {
|
|
|
29
33
|
};
|
|
30
34
|
}
|
|
31
35
|
|
|
32
|
-
//
|
|
36
|
+
// bun-db path: setFields calls updateMany(tx, table, set, where) which lands
|
|
37
|
+
// on asRawClient(tx).unsafe(sqlText, params). Capture the SQL + params.
|
|
33
38
|
function makeFakeTx() {
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
return { fakeTx: { update } as never, update, set, where };
|
|
39
|
+
const unsafe = mock(async (_sqlText: string, _params: unknown[]) => [] as unknown[]);
|
|
40
|
+
const fakeTx = { unsafe, begin: mock() } as never;
|
|
41
|
+
return { fakeTx, unsafe };
|
|
38
42
|
}
|
|
39
43
|
|
|
40
44
|
describe("setFields", () => {
|
|
41
45
|
test("returns an apply fn that UPDATEs the passed fields WHERE id = aggregateId", async () => {
|
|
42
46
|
const apply = setFields(fakeTable, { status: "sent" });
|
|
43
|
-
const { fakeTx,
|
|
47
|
+
const { fakeTx, unsafe } = makeFakeTx();
|
|
44
48
|
await apply(makeFakeEvent(), fakeTx);
|
|
45
|
-
expect(
|
|
46
|
-
|
|
49
|
+
expect(unsafe).toHaveBeenCalledTimes(1);
|
|
50
|
+
const [sqlText, params] = unsafe.mock.calls[0]!;
|
|
51
|
+
expect(sqlText).toMatch(/UPDATE "fake_table" SET "status" = \$1.*WHERE "id" = \$2/);
|
|
52
|
+
expect(params).toEqual(["sent", "agg-42"]);
|
|
47
53
|
});
|
|
48
54
|
|
|
49
55
|
test("fields as a function receives the event and returns the field values", async () => {
|
|
50
56
|
const apply = setFields(fakeTable, (event) => ({
|
|
51
57
|
status: (event.payload as { newStatus: string }).newStatus,
|
|
52
58
|
}));
|
|
53
|
-
const { fakeTx,
|
|
59
|
+
const { fakeTx, unsafe } = makeFakeTx();
|
|
54
60
|
await apply(makeFakeEvent({ payload: { newStatus: "cancelled" } }), fakeTx);
|
|
55
|
-
expect(
|
|
61
|
+
expect(unsafe).toHaveBeenCalledTimes(1);
|
|
62
|
+
const [, params] = unsafe.mock.calls[0]!;
|
|
63
|
+
expect(params).toEqual(["cancelled", "agg-42"]);
|
|
56
64
|
});
|
|
57
65
|
|
|
58
66
|
test("throws at construction time when the table has no 'id' column", () => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { integer, type SchemaTable, table, uuid } from "../../db/dialect";
|
|
3
3
|
import type { ProjectionDefinition } from "../../engine/types";
|
|
4
4
|
import { defineFeature } from "../define-feature";
|
|
5
5
|
import { createEntity, createTextField } from "../factories";
|
|
@@ -9,10 +9,10 @@ import { createRegistry } from "../registry";
|
|
|
9
9
|
// the projection itself (apply, TX semantics) is covered by the MietNomade
|
|
10
10
|
// integration suite; this file focuses on the registrar/registry contracts
|
|
11
11
|
// that fire BEFORE any write happens.
|
|
12
|
-
const testTable =
|
|
12
|
+
const testTable = table("test_projection", {
|
|
13
13
|
id: uuid("id").primaryKey(),
|
|
14
14
|
count: integer("count").notNull().default(0),
|
|
15
|
-
});
|
|
15
|
+
}) as unknown as SchemaTable;
|
|
16
16
|
|
|
17
17
|
function exampleEntity(name = "unit") {
|
|
18
18
|
return createEntity({
|
|
@@ -2,16 +2,17 @@
|
|
|
2
2
|
// aggregation. Full DB roundtrip (setupTestStack pushes the table → INSERT
|
|
3
3
|
// / SELECT) lives in src/__tests__/raw-table.integration.ts.
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { describe, expect, test } from "bun:test";
|
|
6
|
+
import type { SchemaTable } from "../../db/dialect";
|
|
7
|
+
import { table, text } from "../../db/dialect";
|
|
7
8
|
import { defineFeature } from "../define-feature";
|
|
8
9
|
import { createRegistry } from "../registry";
|
|
9
10
|
import type { ProjectionDefinition } from "../types";
|
|
10
11
|
|
|
11
|
-
const probeTable =
|
|
12
|
+
const probeTable = table("rt_probe_table", {
|
|
12
13
|
id: text("id").primaryKey(),
|
|
13
14
|
});
|
|
14
|
-
const probeTableTwo =
|
|
15
|
+
const probeTableTwo = table("rt_probe_table_two", {
|
|
15
16
|
id: text("id").primaryKey(),
|
|
16
17
|
});
|
|
17
18
|
|
|
@@ -107,9 +108,9 @@ describe("createRegistry — rawTable aggregation", () => {
|
|
|
107
108
|
// when one role writes (primary cache) and a second role reads
|
|
108
109
|
// (alias view). Push-time dedupe keeps the boot to a single CREATE;
|
|
109
110
|
// the registry keeps both entries so callers can resolve either name.
|
|
110
|
-
const sharedT =
|
|
111
|
+
const sharedT = table("rt_shared_dedupe", {
|
|
111
112
|
id: text("id").primaryKey(),
|
|
112
|
-
});
|
|
113
|
+
}) as unknown as SchemaTable;
|
|
113
114
|
const feat = defineFeature("dedupe", (r) => {
|
|
114
115
|
r.rawTable("primary", sharedT, { reason: "main writer" });
|
|
115
116
|
r.rawTable("alias", sharedT, { reason: "alias for read consumers" });
|
|
@@ -126,9 +127,9 @@ describe("createRegistry — rawTable aggregation", () => {
|
|
|
126
127
|
// r.projection() is almost always an authoring bug — two owners on
|
|
127
128
|
// the same physical table, one event-sourced, one bypass. Boot
|
|
128
129
|
// throws so the failure points at the misconfiguration site.
|
|
129
|
-
const sharedT =
|
|
130
|
+
const sharedT = table("rt_collision_phys", {
|
|
130
131
|
id: text("id").primaryKey(),
|
|
131
|
-
});
|
|
132
|
+
}) as unknown as SchemaTable;
|
|
132
133
|
const projection: ProjectionDefinition = {
|
|
133
134
|
name: "shared-summary",
|
|
134
135
|
source: "shared",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect,
|
|
1
|
+
import { describe, expect, mock, test } from "bun:test";
|
|
2
2
|
import { createTenantConfig, createUserConfig } from "../config-helpers";
|
|
3
3
|
import { type ClampInfo, resolveConfigOrParam } from "../resolve-config-or-param";
|
|
4
4
|
import type {
|
|
@@ -25,7 +25,7 @@ function makeCtx(entries: Record<string, { def: KeyEntry; fallback: unknown }>)
|
|
|
25
25
|
getConfigKey: (key: string) => entries[key]?.def,
|
|
26
26
|
} as unknown as Registry;
|
|
27
27
|
|
|
28
|
-
const configFn =
|
|
28
|
+
const configFn = mock(async <T extends ConfigKeyType>(handle: ConfigKeyHandle<T>) => {
|
|
29
29
|
return entries[handle.name]?.fallback as ConfigValue<T> | undefined;
|
|
30
30
|
});
|
|
31
31
|
|
|
@@ -273,14 +273,14 @@ describe("resolveConfigOrParam — onClamp audit hook", () => {
|
|
|
273
273
|
|
|
274
274
|
test("onClamp does NOT fire when value is within bounds", async () => {
|
|
275
275
|
const { ctx } = makeCtx({ k: { def: boundedDef, fallback: 10 } });
|
|
276
|
-
const onClamp =
|
|
276
|
+
const onClamp = mock();
|
|
277
277
|
await resolveConfigOrParam(ctx, handleFor("k", "number"), 50, { onClamp });
|
|
278
278
|
expect(onClamp).not.toHaveBeenCalled();
|
|
279
279
|
});
|
|
280
280
|
|
|
281
281
|
test("onClamp does NOT fire on exact boundary values", async () => {
|
|
282
282
|
const { ctx } = makeCtx({ k: { def: boundedDef, fallback: 10 } });
|
|
283
|
-
const onClamp =
|
|
283
|
+
const onClamp = mock();
|
|
284
284
|
await resolveConfigOrParam(ctx, handleFor("k", "number"), 1, { onClamp });
|
|
285
285
|
await resolveConfigOrParam(ctx, handleFor("k", "number"), 100, { onClamp });
|
|
286
286
|
expect(onClamp).not.toHaveBeenCalled();
|
|
@@ -288,7 +288,7 @@ describe("resolveConfigOrParam — onClamp audit hook", () => {
|
|
|
288
288
|
|
|
289
289
|
test("onClamp does NOT fire when value is coerced to NaN (no clamp happens, config fallback used)", async () => {
|
|
290
290
|
const { ctx } = makeCtx({ k: { def: boundedDef, fallback: 10 } });
|
|
291
|
-
const onClamp =
|
|
291
|
+
const onClamp = mock();
|
|
292
292
|
await resolveConfigOrParam(ctx, handleFor("k", "number"), "abc", { onClamp });
|
|
293
293
|
expect(onClamp).not.toHaveBeenCalled();
|
|
294
294
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect, test } from "
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
2
|
import { createEntity, createRegistry, defineFeature } from "../index";
|
|
3
3
|
import type { SearchPayloadContributorFn } from "../types";
|
|
4
4
|
|
|
@@ -21,7 +21,7 @@ describe("searchPayloadExtension registration", () => {
|
|
|
21
21
|
r.searchPayloadExtension(thing, noop);
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
-
const entry = feature.searchPayloadExtensions["thing"];
|
|
24
|
+
const entry = feature.searchPayloadExtensions!["thing"];
|
|
25
25
|
expect(entry).toHaveLength(1);
|
|
26
26
|
});
|
|
27
27
|
|
|
@@ -35,7 +35,7 @@ describe("searchPayloadExtension registration", () => {
|
|
|
35
35
|
r.searchPayloadExtension(thing, c2);
|
|
36
36
|
});
|
|
37
37
|
|
|
38
|
-
expect(feature.searchPayloadExtensions["thing"]).toHaveLength(2);
|
|
38
|
+
expect(feature.searchPayloadExtensions!["thing"]).toHaveLength(2);
|
|
39
39
|
});
|
|
40
40
|
});
|
|
41
41
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it,
|
|
1
|
+
import { beforeEach, describe, expect, it, mock } from "bun:test";
|
|
2
2
|
import { getStep } from "../define-step";
|
|
3
3
|
import { buildAggregateAppendEventStep } from "../steps/aggregate-append-event";
|
|
4
4
|
import type { PipelineCtx } from "../types/step";
|
|
5
5
|
|
|
6
|
-
const mockUnsafeAppendEvent =
|
|
6
|
+
const mockUnsafeAppendEvent = mock();
|
|
7
7
|
|
|
8
8
|
const mockCtx = {
|
|
9
9
|
unsafeAppendEvent: mockUnsafeAppendEvent,
|
|
@@ -39,7 +39,7 @@ describe("buildAggregateAppendEventStep", () => {
|
|
|
39
39
|
|
|
40
40
|
describe("aggregate.appendEvent run", () => {
|
|
41
41
|
beforeEach(() => {
|
|
42
|
-
|
|
42
|
+
mock.clearAllMocks();
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
it("calls ctx.unsafeAppendEvent with resolved aggregateId and payload", async () => {
|
|
@@ -56,7 +56,7 @@ describe("aggregate.appendEvent run", () => {
|
|
|
56
56
|
mockCtx,
|
|
57
57
|
);
|
|
58
58
|
|
|
59
|
-
expect(mockUnsafeAppendEvent).
|
|
59
|
+
expect(mockUnsafeAppendEvent).toHaveBeenCalledTimes(1);
|
|
60
60
|
const call = mockUnsafeAppendEvent.mock.calls[0]![0];
|
|
61
61
|
expect(call).toMatchObject({
|
|
62
62
|
aggregateId: "abc-123",
|
|
@@ -68,9 +68,9 @@ describe("aggregate.appendEvent run", () => {
|
|
|
68
68
|
|
|
69
69
|
it("resolves function resolvers before calling unsafeAppendEvent", async () => {
|
|
70
70
|
const stepDef = getStep("aggregate.appendEvent");
|
|
71
|
-
const idFn =
|
|
72
|
-
const payloadFn =
|
|
73
|
-
const headersFn =
|
|
71
|
+
const idFn = mock(() => "dynamic-id");
|
|
72
|
+
const payloadFn = mock(() => ({ note: "dynamic" }));
|
|
73
|
+
const headersFn = mock(() => ({ key: "val" }));
|
|
74
74
|
|
|
75
75
|
await stepDef!.run(
|
|
76
76
|
{
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it,
|
|
1
|
+
import { beforeEach, describe, expect, it, mock } from "bun:test";
|
|
2
2
|
import type { EventStoreExecutor } from "../../db/event-store-executor";
|
|
3
3
|
import { getStep } from "../define-step";
|
|
4
4
|
import { buildAggregateCreateStep } from "../steps/aggregate-create";
|
|
5
5
|
import type { PipelineCtx } from "../types/step";
|
|
6
6
|
|
|
7
|
-
const mockCreate =
|
|
7
|
+
const mockCreate = mock();
|
|
8
8
|
const mockExecutor = { create: mockCreate } as unknown as EventStoreExecutor & {
|
|
9
9
|
create: typeof mockCreate;
|
|
10
10
|
};
|
|
@@ -39,7 +39,7 @@ describe("buildAggregateCreateStep", () => {
|
|
|
39
39
|
|
|
40
40
|
describe("aggregate.create run", () => {
|
|
41
41
|
beforeEach(() => {
|
|
42
|
-
|
|
42
|
+
mock.clearAllMocks();
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
it("resolves data and calls executor.create with user and db", async () => {
|
|
@@ -64,7 +64,7 @@ describe("aggregate.create run", () => {
|
|
|
64
64
|
|
|
65
65
|
it("resolves a function data resolver before calling executor.create", async () => {
|
|
66
66
|
const stepDef = getStep("aggregate.create");
|
|
67
|
-
const dataFn =
|
|
67
|
+
const dataFn = mock((ctx: PipelineCtx) => ({
|
|
68
68
|
label: (ctx.event.payload as { label: string }).label,
|
|
69
69
|
}));
|
|
70
70
|
mockExecutor.create.mockResolvedValue({
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it,
|
|
1
|
+
import { beforeEach, describe, expect, it, mock } from "bun:test";
|
|
2
2
|
import type { EventStoreExecutor } from "../../db/event-store-executor";
|
|
3
3
|
import { getStep } from "../define-step";
|
|
4
4
|
import { buildAggregateUpdateStep } from "../steps/aggregate-update";
|
|
5
5
|
import type { PipelineCtx } from "../types/step";
|
|
6
6
|
|
|
7
|
-
const mockUpdate =
|
|
7
|
+
const mockUpdate = mock();
|
|
8
8
|
const mockExecutor = { update: mockUpdate } as unknown as EventStoreExecutor & {
|
|
9
9
|
update: typeof mockUpdate;
|
|
10
10
|
};
|
|
@@ -61,7 +61,7 @@ describe("buildAggregateUpdateStep", () => {
|
|
|
61
61
|
|
|
62
62
|
describe("aggregate.update run", () => {
|
|
63
63
|
beforeEach(() => {
|
|
64
|
-
|
|
64
|
+
mock.clearAllMocks();
|
|
65
65
|
});
|
|
66
66
|
|
|
67
67
|
it("resolves id, changes, version and calls executor.update", async () => {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it,
|
|
1
|
+
import { beforeEach, describe, expect, it, mock } from "bun:test";
|
|
2
2
|
import { getStep } from "../define-step";
|
|
3
3
|
import { buildCallFeatureStep } from "../steps/call-feature";
|
|
4
4
|
import type { PipelineCtx } from "../types/step";
|
|
5
5
|
|
|
6
|
-
const mockWrite =
|
|
7
|
-
const mockWriteAs =
|
|
6
|
+
const mockWrite = mock();
|
|
7
|
+
const mockWriteAs = mock();
|
|
8
8
|
|
|
9
9
|
const mockCtx = {
|
|
10
10
|
write: mockWrite,
|
|
@@ -37,7 +37,7 @@ describe("buildCallFeatureStep", () => {
|
|
|
37
37
|
|
|
38
38
|
describe("callFeature run", () => {
|
|
39
39
|
beforeEach(() => {
|
|
40
|
-
|
|
40
|
+
mock.clearAllMocks();
|
|
41
41
|
});
|
|
42
42
|
|
|
43
43
|
it("calls ctx.write with the resolved payload and handler name", async () => {
|
|
@@ -64,7 +64,7 @@ describe("callFeature run", () => {
|
|
|
64
64
|
|
|
65
65
|
it("resolves a function payload resolver before calling ctx.write", async () => {
|
|
66
66
|
const stepDef = getStep("callFeature");
|
|
67
|
-
const payloadFn =
|
|
67
|
+
const payloadFn = mock((ctx: PipelineCtx) => ({
|
|
68
68
|
title: (ctx.event.payload as { title: string }).title,
|
|
69
69
|
}));
|
|
70
70
|
mockWrite.mockResolvedValue({
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it,
|
|
1
|
+
import { beforeEach, describe, expect, it, mock } from "bun:test";
|
|
2
2
|
import { getStep } from "../define-step";
|
|
3
3
|
import {
|
|
4
4
|
STEP_DISPATCH_AGGREGATE_TYPE,
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
import { buildMailSendStep } from "../steps/mail-send";
|
|
8
8
|
import type { PipelineCtx } from "../types/step";
|
|
9
9
|
|
|
10
|
-
const mockUnsafeAppendEvent =
|
|
10
|
+
const mockUnsafeAppendEvent = mock();
|
|
11
11
|
|
|
12
12
|
const mockCtx = {
|
|
13
13
|
unsafeAppendEvent: mockUnsafeAppendEvent,
|
|
@@ -61,7 +61,7 @@ describe("buildMailSendStep", () => {
|
|
|
61
61
|
|
|
62
62
|
describe("mail.send run", () => {
|
|
63
63
|
beforeEach(() => {
|
|
64
|
-
|
|
64
|
+
mock.clearAllMocks();
|
|
65
65
|
});
|
|
66
66
|
|
|
67
67
|
it("appends a step.dispatch-requested system event with the mail spec", async () => {
|
|
@@ -78,7 +78,7 @@ describe("mail.send run", () => {
|
|
|
78
78
|
mockCtx,
|
|
79
79
|
);
|
|
80
80
|
|
|
81
|
-
expect(mockUnsafeAppendEvent).
|
|
81
|
+
expect(mockUnsafeAppendEvent).toHaveBeenCalledTimes(1);
|
|
82
82
|
const eventArg = mockUnsafeAppendEvent.mock.calls[0]![0];
|
|
83
83
|
|
|
84
84
|
expect(eventArg.aggregateType).toBe(STEP_DISPATCH_AGGREGATE_TYPE);
|
|
@@ -93,9 +93,9 @@ describe("mail.send run", () => {
|
|
|
93
93
|
|
|
94
94
|
it("resolves function-based resolvers", async () => {
|
|
95
95
|
const stepDef = getStep("mail.send");
|
|
96
|
-
const toFn =
|
|
97
|
-
const subjectFn =
|
|
98
|
-
const bodyFn =
|
|
96
|
+
const toFn = mock(() => "resolved@example.com");
|
|
97
|
+
const subjectFn = mock(() => "Resolved Subject");
|
|
98
|
+
const bodyFn = mock(() => "Resolved Body");
|
|
99
99
|
|
|
100
100
|
await stepDef!.run({ to: toFn, subject: subjectFn, body: bodyFn, mode: "deferred" }, mockCtx);
|
|
101
101
|
|
|
@@ -1,36 +1,27 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
1
|
+
import { beforeEach, describe, expect, it, mock } from "bun:test";
|
|
2
|
+
import { table, text, uuid } from "../../db/dialect";
|
|
4
3
|
import { getStep } from "../define-step";
|
|
5
4
|
import { buildReadFindManyStep } from "../steps/read-find-many";
|
|
6
5
|
import { buildReadFindOneStep } from "../steps/read-find-one";
|
|
7
6
|
import type { PipelineCtx } from "../types/step";
|
|
8
7
|
|
|
9
|
-
const testTable =
|
|
8
|
+
const testTable = table("test_read", {
|
|
10
9
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
11
10
|
tenantId: uuid("tenant_id").notNull(),
|
|
12
11
|
label: text("label"),
|
|
13
12
|
});
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (rows) this.rows = rows;
|
|
24
|
-
const promise = Promise.resolve(this.rows);
|
|
25
|
-
// biome-ignore lint/suspicious/noThenProperty: see above
|
|
26
|
-
this.then = promise.then.bind(promise);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const mockDb = { select: vi.fn(() => ({ from: vi.fn(() => new MockQuery([])) })) };
|
|
14
|
+
// bun-db path: read-find-many/one call selectMany(ctx.db.raw, table, where, opts)
|
|
15
|
+
// which goes through asRawClient(ctx.db.raw).unsafe(sqlText, params).
|
|
16
|
+
// Mock the .unsafe() return value to feed back rows.
|
|
17
|
+
const unsafeMock = mock(
|
|
18
|
+
async (_sqlText: string, _params: unknown[]): Promise<Record<string, unknown>[]> => [],
|
|
19
|
+
);
|
|
20
|
+
const rawDb = { unsafe: unsafeMock, begin: mock() };
|
|
21
|
+
const ctxDb = { raw: rawDb };
|
|
31
22
|
|
|
32
23
|
const mockCtx = {
|
|
33
|
-
db:
|
|
24
|
+
db: ctxDb,
|
|
34
25
|
event: { type: "test", payload: {} },
|
|
35
26
|
steps: {},
|
|
36
27
|
scope: {},
|
|
@@ -40,7 +31,7 @@ describe("buildReadFindOneStep", () => {
|
|
|
40
31
|
it("returns a StepInstance with kind read.findOne", () => {
|
|
41
32
|
const step = buildReadFindOneStep("myLookup", {
|
|
42
33
|
table: testTable,
|
|
43
|
-
where:
|
|
34
|
+
where: { id: "x" },
|
|
44
35
|
});
|
|
45
36
|
expect(step.kind).toBe("read.findOne");
|
|
46
37
|
expect((step.args as { name: string }).name).toBe("myLookup");
|
|
@@ -49,7 +40,7 @@ describe("buildReadFindOneStep", () => {
|
|
|
49
40
|
it("stores the result key from the name arg", () => {
|
|
50
41
|
const step = buildReadFindOneStep("lookupResult", {
|
|
51
42
|
table: testTable,
|
|
52
|
-
where:
|
|
43
|
+
where: { id: "x" },
|
|
53
44
|
});
|
|
54
45
|
const def = getStep("read.findOne");
|
|
55
46
|
expect(def?.resultKey?.(step.args as { name: string })).toBe("lookupResult");
|
|
@@ -58,16 +49,16 @@ describe("buildReadFindOneStep", () => {
|
|
|
58
49
|
|
|
59
50
|
describe("read.findOne run", () => {
|
|
60
51
|
beforeEach(() => {
|
|
61
|
-
|
|
52
|
+
mock.clearAllMocks();
|
|
53
|
+
unsafeMock.mockResolvedValue([]);
|
|
62
54
|
});
|
|
63
55
|
|
|
64
56
|
it("returns null when no row is found", async () => {
|
|
65
57
|
const stepDef = getStep("read.findOne");
|
|
66
|
-
|
|
67
|
-
mockDb.select.mockReturnValue({ from: vi.fn().mockReturnValue(query) });
|
|
58
|
+
unsafeMock.mockResolvedValueOnce([]);
|
|
68
59
|
|
|
69
60
|
const result = await stepDef!.run(
|
|
70
|
-
{ name: "lookup", table: testTable, where:
|
|
61
|
+
{ name: "lookup", table: testTable, where: { id: "x" } },
|
|
71
62
|
mockCtx,
|
|
72
63
|
);
|
|
73
64
|
|
|
@@ -77,11 +68,10 @@ describe("read.findOne run", () => {
|
|
|
77
68
|
it("returns the first row when found", async () => {
|
|
78
69
|
const stepDef = getStep("read.findOne");
|
|
79
70
|
const row = { id: "abc", tenantId: "t1", label: "hello" };
|
|
80
|
-
|
|
81
|
-
mockDb.select.mockReturnValue({ from: vi.fn().mockReturnValue(query) });
|
|
71
|
+
unsafeMock.mockResolvedValueOnce([row]);
|
|
82
72
|
|
|
83
73
|
const result = await stepDef!.run(
|
|
84
|
-
{ name: "lookup", table: testTable, where:
|
|
74
|
+
{ name: "lookup", table: testTable, where: { id: "abc" } },
|
|
85
75
|
mockCtx,
|
|
86
76
|
);
|
|
87
77
|
|
|
@@ -90,14 +80,17 @@ describe("read.findOne run", () => {
|
|
|
90
80
|
|
|
91
81
|
it("resolves a function where-clause before querying", async () => {
|
|
92
82
|
const stepDef = getStep("read.findOne");
|
|
93
|
-
const whereFn =
|
|
94
|
-
|
|
95
|
-
mockDb.select.mockReturnValue({ from: vi.fn().mockReturnValue(query) });
|
|
83
|
+
const whereFn = mock(() => ({ tenantId: "dyn-tenant" }));
|
|
84
|
+
unsafeMock.mockResolvedValueOnce([]);
|
|
96
85
|
|
|
97
86
|
await stepDef!.run({ name: "lookup", table: testTable, where: whereFn }, mockCtx);
|
|
98
87
|
|
|
99
88
|
expect(whereFn).toHaveBeenCalledWith(mockCtx);
|
|
100
|
-
expect(
|
|
89
|
+
expect(unsafeMock).toHaveBeenCalledTimes(1);
|
|
90
|
+
const [sqlText, params] = unsafeMock.mock.calls[0]!;
|
|
91
|
+
expect(sqlText).toMatch(/SELECT \* FROM "test_read"/);
|
|
92
|
+
expect(sqlText).toMatch(/"tenant_id" = \$1/);
|
|
93
|
+
expect(params).toEqual(["dyn-tenant"]);
|
|
101
94
|
});
|
|
102
95
|
});
|
|
103
96
|
|
|
@@ -116,13 +109,13 @@ describe("buildReadFindManyStep", () => {
|
|
|
116
109
|
|
|
117
110
|
describe("read.findMany run", () => {
|
|
118
111
|
beforeEach(() => {
|
|
119
|
-
|
|
112
|
+
mock.clearAllMocks();
|
|
113
|
+
unsafeMock.mockResolvedValue([]);
|
|
120
114
|
});
|
|
121
115
|
|
|
122
116
|
it("returns an empty array when no rows exist", async () => {
|
|
123
117
|
const stepDef = getStep("read.findMany");
|
|
124
|
-
|
|
125
|
-
mockDb.select.mockReturnValue({ from: vi.fn().mockReturnValue(query) });
|
|
118
|
+
unsafeMock.mockResolvedValueOnce([]);
|
|
126
119
|
|
|
127
120
|
const result = await stepDef!.run({ name: "list", table: testTable }, mockCtx);
|
|
128
121
|
|
|
@@ -132,11 +125,12 @@ describe("read.findMany run", () => {
|
|
|
132
125
|
it("applies the limit when specified", async () => {
|
|
133
126
|
const stepDef = getStep("read.findMany");
|
|
134
127
|
const rows = [{ id: "a" }, { id: "b" }];
|
|
135
|
-
|
|
136
|
-
mockDb.select.mockReturnValue({ from: vi.fn().mockReturnValue(query) });
|
|
128
|
+
unsafeMock.mockResolvedValueOnce(rows);
|
|
137
129
|
|
|
138
130
|
await stepDef!.run({ name: "list", table: testTable, limit: 2 }, mockCtx);
|
|
139
131
|
|
|
140
|
-
expect(
|
|
132
|
+
expect(unsafeMock).toHaveBeenCalledTimes(1);
|
|
133
|
+
const [sqlText] = unsafeMock.mock.calls[0]!;
|
|
134
|
+
expect(sqlText).toMatch(/LIMIT 2/);
|
|
141
135
|
});
|
|
142
136
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect, it,
|
|
1
|
+
import { describe, expect, it, mock } from "bun:test";
|
|
2
2
|
import { resolveOptional, resolveRequired } from "../steps/_resolver-utils";
|
|
3
3
|
import type { PipelineCtx } from "../types/step";
|
|
4
4
|
|
|
@@ -13,15 +13,15 @@ describe("resolveRequired", () => {
|
|
|
13
13
|
});
|
|
14
14
|
|
|
15
15
|
it("calls a function resolver with the ctx and returns its result", () => {
|
|
16
|
-
const fn =
|
|
16
|
+
const fn = mock((_ctx: PipelineCtx) => "from-fn");
|
|
17
17
|
expect(resolveRequired(fn, dummyCtx)).toBe("from-fn");
|
|
18
|
-
expect(fn).
|
|
18
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
19
19
|
expect(fn).toHaveBeenCalledWith(dummyCtx);
|
|
20
20
|
});
|
|
21
21
|
|
|
22
22
|
it("passes the full ctx to the resolver function", () => {
|
|
23
23
|
const ctx = { event: { type: "test" }, steps: { x: 1 }, scope: {} } as unknown as PipelineCtx;
|
|
24
|
-
const fn =
|
|
24
|
+
const fn = mock((c: PipelineCtx) => c.event.type);
|
|
25
25
|
expect(resolveRequired(fn, ctx)).toBe("test");
|
|
26
26
|
});
|
|
27
27
|
});
|
|
@@ -39,9 +39,9 @@ describe("resolveOptional", () => {
|
|
|
39
39
|
});
|
|
40
40
|
|
|
41
41
|
it("calls a function resolver when defined", () => {
|
|
42
|
-
const fn =
|
|
42
|
+
const fn = mock(() => "resolved");
|
|
43
43
|
expect(resolveOptional(fn, dummyCtx)).toBe("resolved");
|
|
44
|
-
expect(fn).
|
|
44
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
it("returns undefined for undefined function resolver", () => {
|