@cosmicdrift/kumiko-framework 0.13.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 +7 -7
- 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 -472
- 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,13 +1,13 @@
|
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, it, mock } from "bun:test";
|
|
1
2
|
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
2
3
|
import { join } from "node:path";
|
|
3
|
-
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
|
4
4
|
|
|
5
5
|
// Bun.Glob is only used by scanForCandidates / runCodemod, not by
|
|
6
6
|
// analyzeFile or convertFile. Those higher-level functions are tested
|
|
7
7
|
// in the integration suite; unit tests here focus on file-level
|
|
8
8
|
// operations which don't need Glob. A stub class prevents import
|
|
9
|
-
// errors in
|
|
10
|
-
|
|
9
|
+
// errors in bun (which runs on Node, not Bun).
|
|
10
|
+
mock.module("bun", () => {
|
|
11
11
|
class StubGlob {
|
|
12
12
|
scanSync(): never {
|
|
13
13
|
throw new Error("Bun.Glob is not available in unit-test mode");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect,
|
|
1
|
+
import { describe, expect, mock, test } from "bun:test";
|
|
2
2
|
import {
|
|
3
3
|
createEntityExecutor,
|
|
4
4
|
defineEntityCreateHandler,
|
|
@@ -170,7 +170,7 @@ describe("defineProjectionQueryHandler", () => {
|
|
|
170
170
|
);
|
|
171
171
|
const fakeRows = [{ customer: "a", totalCents: 100 }];
|
|
172
172
|
const ctx = {
|
|
173
|
-
queryProjection:
|
|
173
|
+
queryProjection: mock().mockResolvedValue(fakeRows),
|
|
174
174
|
};
|
|
175
175
|
const result = await def.handler(
|
|
176
176
|
// biome-ignore lint/suspicious/noExplicitAny: test shim — handler only touches ctx.queryProjection here.
|
|
@@ -191,7 +191,7 @@ describe("defineProjectionQueryHandler", () => {
|
|
|
191
191
|
"showcase:projection:customer-revenue",
|
|
192
192
|
{ unsafeAllTenants: true },
|
|
193
193
|
);
|
|
194
|
-
const ctx = { queryProjection:
|
|
194
|
+
const ctx = { queryProjection: mock().mockResolvedValue([]) };
|
|
195
195
|
await def.handler(
|
|
196
196
|
// biome-ignore lint/suspicious/noExplicitAny: test shim.
|
|
197
197
|
{ type: "revenue:list", user: {} as any, payload: {} },
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect,
|
|
1
|
+
import { describe, expect, mock, test } from "bun:test";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { emitEvent, typedPayload } from "../event-helpers";
|
|
4
4
|
import type { EventDef } from "../types/handlers";
|
|
@@ -11,7 +11,7 @@ describe("emitEvent", () => {
|
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
test("delegates to ctx.appendEvent with eventDef.name as the type", async () => {
|
|
14
|
-
const ctx = { unsafeAppendEvent:
|
|
14
|
+
const ctx = { unsafeAppendEvent: mock().mockResolvedValue(undefined) };
|
|
15
15
|
await emitEvent(ctx, orderPlaced, {
|
|
16
16
|
aggregateId: "agg-1",
|
|
17
17
|
aggregateType: "pubsub-order",
|
|
@@ -26,7 +26,7 @@ describe("emitEvent", () => {
|
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
test("payload type is inferred from the eventDef — wrong shape is a compile error", async () => {
|
|
29
|
-
const ctx = { unsafeAppendEvent:
|
|
29
|
+
const ctx = { unsafeAppendEvent: mock().mockResolvedValue(undefined) };
|
|
30
30
|
// Runtime check: compile-time narrowing is the real win, but we also
|
|
31
31
|
// make sure the value flows through unchanged.
|
|
32
32
|
await emitEvent(ctx, orderPlaced, {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect,
|
|
1
|
+
import { describe, expect, mock, test } from "bun:test";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { createEntity, createRegistry, createTextField, defineFeature } from "../index";
|
|
4
4
|
|
|
@@ -24,7 +24,7 @@ describe("extendsRegistrar", () => {
|
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
test("registry calls onRegister for each extension usage", () => {
|
|
27
|
-
const onRegister =
|
|
27
|
+
const onRegister = mock();
|
|
28
28
|
|
|
29
29
|
const ext = defineFeature("tags", (r) => {
|
|
30
30
|
r.extendsRegistrar("tags", { onRegister });
|
|
@@ -99,7 +99,7 @@ describe("extendsRegistrar", () => {
|
|
|
99
99
|
});
|
|
100
100
|
|
|
101
101
|
test("extension preSave hooks fire for entity-scoped handlers", () => {
|
|
102
|
-
const preSaveFn =
|
|
102
|
+
const preSaveFn = mock(async (changes: Record<string, unknown>) => changes);
|
|
103
103
|
|
|
104
104
|
const ext = defineFeature("audit", (r) => {
|
|
105
105
|
r.extendsRegistrar("audited", {
|
|
@@ -137,7 +137,7 @@ describe("extendsRegistrar", () => {
|
|
|
137
137
|
});
|
|
138
138
|
|
|
139
139
|
test("extension postSave hooks are entity hooks", () => {
|
|
140
|
-
const postSaveFn =
|
|
140
|
+
const postSaveFn = mock(async () => {});
|
|
141
141
|
|
|
142
142
|
const ext = defineFeature("audit", (r) => {
|
|
143
143
|
r.extendsRegistrar("audited", {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// source-code / markdown / blog-posts — explicitly NOT sortable /
|
|
9
9
|
// searchable / filterable. Type-level enforcement statt soft-defaults.
|
|
10
10
|
|
|
11
|
-
import { describe, expect, expectTypeOf, test } from "
|
|
11
|
+
import { describe, expect, expectTypeOf, test } from "bun:test";
|
|
12
12
|
import { createLongTextField } from "../factories";
|
|
13
13
|
import type { LongTextFieldDef } from "../types";
|
|
14
14
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// echte TZ-Konvertierung (Wall-Clock ↔ UTC) testen wir später beim
|
|
6
6
|
// DB-Wrapper-Schritt.
|
|
7
7
|
|
|
8
|
-
import { describe, expect, test } from "
|
|
8
|
+
import { describe, expect, test } from "bun:test";
|
|
9
9
|
import {
|
|
10
10
|
createLocatedTimestampField,
|
|
11
11
|
createTimestampField,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect, test } from "
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { createEntity, createRegistry, defineFeature } from "../index";
|
|
4
4
|
import type { PostSaveHookFn, PreDeleteHookFn, PreSaveHookFn, SaveContext } from "../types";
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { describe, expect, test } from "vitest";
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
3
2
|
import {
|
|
4
3
|
buildOwnershipClause,
|
|
5
4
|
from,
|
|
@@ -268,14 +267,14 @@ describe("userCanWriteFieldRow() — Straddle-attack prevention", () => {
|
|
|
268
267
|
// --- buildOwnershipClause() — SQL WHERE builder ---
|
|
269
268
|
|
|
270
269
|
describe("buildOwnershipClause() — SQL WHERE builder", () => {
|
|
271
|
-
// Fake
|
|
272
|
-
//
|
|
273
|
-
//
|
|
274
|
-
// assert on the
|
|
275
|
-
const fakeTable = {
|
|
276
|
-
teamId:
|
|
277
|
-
assigneeId:
|
|
278
|
-
tenantId:
|
|
270
|
+
// Fake table — buildOwnershipClause checks (table[field] !== undefined)
|
|
271
|
+
// and falls back to snake_case mapping for column names. The fixture
|
|
272
|
+
// shape mirrors drizzle's pgTable for the purposes of the assertions
|
|
273
|
+
// here; we don't assert on the serialized SQL text.
|
|
274
|
+
const fakeTable: Record<string, unknown> = {
|
|
275
|
+
teamId: { name: "team_id" },
|
|
276
|
+
assigneeId: { name: "assignee_id" },
|
|
277
|
+
tenantId: { name: "tenant_id" },
|
|
279
278
|
};
|
|
280
279
|
|
|
281
280
|
test("undefined access map → pass (public entity)", () => {
|
|
@@ -372,7 +371,7 @@ describe("buildOwnershipClause() — SQL WHERE builder", () => {
|
|
|
372
371
|
test("where-rule escape hatch: caller's SQL passed through", () => {
|
|
373
372
|
const user = mkUser({ roles: ["Auditor"] });
|
|
374
373
|
const map: OwnershipMap = {
|
|
375
|
-
Auditor: { kind: "where", where: () =>
|
|
374
|
+
Auditor: { kind: "where", where: () => ({ sqlText: "custom_expr_42 = 1", params: [] }) },
|
|
376
375
|
};
|
|
377
376
|
const clause = buildOwnershipClause(user, map, fakeTable);
|
|
378
377
|
expect(clause.kind).toBe("sql");
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// Sub-step-builders (branch, forEach) live in pipeline-sub-pipelines.test.ts;
|
|
4
4
|
// boot-validator in validate-projection-allowlist.test.ts.
|
|
5
5
|
|
|
6
|
+
import { describe, expect, it } from "bun:test";
|
|
6
7
|
import { randomUUID } from "node:crypto";
|
|
7
|
-
import { describe, expect, it } from "vitest";
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
import { TestUsers } from "../../stack";
|
|
10
10
|
import { defineWriteHandler } from "../define-handler";
|
|
@@ -43,12 +43,12 @@
|
|
|
43
43
|
// Unit-side tests in pipeline-vertical-slice.test.ts cover the same
|
|
44
44
|
// surface against an empty ctx mock; this file is the real-stack gate.
|
|
45
45
|
|
|
46
|
-
import {
|
|
47
|
-
import { pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
|
|
48
|
-
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "vitest";
|
|
46
|
+
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
49
47
|
import { z } from "zod";
|
|
48
|
+
import { table, text, timestamp, uuid } from "../../db/dialect";
|
|
50
49
|
import { createEventStoreExecutor } from "../../db/event-store-executor";
|
|
51
|
-
import {
|
|
50
|
+
import { asRawClient, selectMany } from "../../db/query";
|
|
51
|
+
import { buildEntityTable } from "../../db/table-builder";
|
|
52
52
|
import { eventsTable } from "../../event-store";
|
|
53
53
|
import {
|
|
54
54
|
setupTestStack,
|
|
@@ -129,7 +129,7 @@ const compoundHandler = defineWriteHandler({
|
|
|
129
129
|
|
|
130
130
|
// Read-side projection-table for the unsafeProjectionUpsert handler.
|
|
131
131
|
// Plain pgTable (not r.entity) — it's a read-side log, not an aggregate.
|
|
132
|
-
const pipelineDemoLogTable =
|
|
132
|
+
const pipelineDemoLogTable = table("pipeline_demo_log", {
|
|
133
133
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
134
134
|
tenantId: uuid("tenant_id").notNull(),
|
|
135
135
|
correlationId: text("correlation_id").notNull().unique(),
|
|
@@ -172,7 +172,7 @@ const widgetEntity = createEntity({
|
|
|
172
172
|
table: "pipeline_widget",
|
|
173
173
|
fields: { label: createTextField({ required: true }) },
|
|
174
174
|
});
|
|
175
|
-
const widgetTable =
|
|
175
|
+
const widgetTable = buildEntityTable("widget", widgetEntity);
|
|
176
176
|
const widgetExecutor = createEventStoreExecutor(widgetTable, widgetEntity, {
|
|
177
177
|
entityName: "widget",
|
|
178
178
|
});
|
|
@@ -341,7 +341,7 @@ const lookupHandler = defineWriteHandler({
|
|
|
341
341
|
({ event, r }) => [
|
|
342
342
|
r.step.read.findOne("widget", {
|
|
343
343
|
table: widgetTable,
|
|
344
|
-
where: () =>
|
|
344
|
+
where: () => ({ id: event.payload.id }),
|
|
345
345
|
}),
|
|
346
346
|
r.step.return(({ steps }) => {
|
|
347
347
|
const row = steps["widget"] as { label?: string } | null;
|
|
@@ -391,7 +391,7 @@ const purgeLogHandler = defineWriteHandler({
|
|
|
391
391
|
perform: pipeline<z.infer<typeof purgeLogSchema>, { ok: true }>(({ event, r }) => [
|
|
392
392
|
r.step.unsafeProjectionDelete({
|
|
393
393
|
table: pipelineDemoLogTable,
|
|
394
|
-
where: () =>
|
|
394
|
+
where: () => ({ correlationId: event.payload.correlationId }),
|
|
395
395
|
}),
|
|
396
396
|
r.step.return({ isSuccess: true as const, data: { ok: true } }),
|
|
397
397
|
]),
|
|
@@ -496,8 +496,9 @@ describe("defineWriteHandler({ perform: pipeline(...) }) — real dispatcher pat
|
|
|
496
496
|
// across runs — order-independent assertions are the only kind that
|
|
497
497
|
// stay green when test-files grow (Memory `feedback_jsdom_lies_*`
|
|
498
498
|
// analog: order-dependent tests are flaky-in-waiting).
|
|
499
|
-
|
|
500
|
-
await stack.db.
|
|
499
|
+
// biome-ignore lint/suspicious/noExplicitAny: test cast
|
|
500
|
+
await asRawClient(stack.db).unsafe(`DELETE FROM "${(pipelineDemoLogTable as any).tableName}"`);
|
|
501
|
+
await asRawClient(stack.db).unsafe(`DELETE FROM "${widgetTable.tableName}"`);
|
|
501
502
|
});
|
|
502
503
|
|
|
503
504
|
test("HTTP write call goes through dispatcher → pipeline-runner → r.step.return", async () => {
|
|
@@ -571,7 +572,7 @@ describe("defineWriteHandler({ perform: pipeline(...) }) — real dispatcher pat
|
|
|
571
572
|
);
|
|
572
573
|
expect(res.status).toBe(200);
|
|
573
574
|
|
|
574
|
-
const rows = await stack.db
|
|
575
|
+
const rows = await selectMany(stack.db, pipelineDemoLogTable);
|
|
575
576
|
expect(rows).toHaveLength(1);
|
|
576
577
|
expect(rows[0]).toMatchObject({
|
|
577
578
|
correlationId: "corr-1",
|
|
@@ -592,10 +593,7 @@ describe("defineWriteHandler({ perform: pipeline(...) }) — real dispatcher pat
|
|
|
592
593
|
admin,
|
|
593
594
|
);
|
|
594
595
|
|
|
595
|
-
const rows = await stack.db
|
|
596
|
-
.select()
|
|
597
|
-
.from(pipelineDemoLogTable)
|
|
598
|
-
.where(eq(pipelineDemoLogTable.correlationId, "corr-2"));
|
|
596
|
+
const rows = await selectMany(stack.db, pipelineDemoLogTable, { correlationId: "corr-2" });
|
|
599
597
|
expect(rows).toHaveLength(1);
|
|
600
598
|
expect(rows[0]?.message).toBe("v2 — overwritten");
|
|
601
599
|
});
|
|
@@ -614,7 +612,7 @@ describe("defineWriteHandler({ perform: pipeline(...) }) — real dispatcher pat
|
|
|
614
612
|
|
|
615
613
|
// Verify the projection-row landed too — the executor's inline
|
|
616
614
|
// projection writes into the widget table in the same TX.
|
|
617
|
-
const rows = await stack.db
|
|
615
|
+
const rows = await selectMany(stack.db, widgetTable);
|
|
618
616
|
expect(rows).toHaveLength(1);
|
|
619
617
|
expect(rows[0]).toMatchObject({ label: "first widget", id: body.data.id });
|
|
620
618
|
});
|
|
@@ -640,7 +638,7 @@ describe("defineWriteHandler({ perform: pipeline(...) }) — real dispatcher pat
|
|
|
640
638
|
expect(updatedBody.data.id).toBe(widgetId);
|
|
641
639
|
|
|
642
640
|
// Projection-row reflects the new label.
|
|
643
|
-
const rows = await stack.db
|
|
641
|
+
const rows = await selectMany(stack.db, widgetTable, { id: widgetId });
|
|
644
642
|
expect(rows).toHaveLength(1);
|
|
645
643
|
expect(rows[0]).toMatchObject({ id: widgetId, label: "after" });
|
|
646
644
|
});
|
|
@@ -658,10 +656,7 @@ describe("defineWriteHandler({ perform: pipeline(...) }) — real dispatcher pat
|
|
|
658
656
|
// The aggregate stream should carry both the auto-generated CRUD
|
|
659
657
|
// event (widget.created) AND the appended annotated event. Direct
|
|
660
658
|
// event-store query is the simplest assertion.
|
|
661
|
-
const events = await stack.db
|
|
662
|
-
.select()
|
|
663
|
-
.from(eventsTable)
|
|
664
|
-
.where(eq(eventsTable.aggregateId, widgetId));
|
|
659
|
+
const events = await selectMany(stack.db, eventsTable, { aggregateId: widgetId });
|
|
665
660
|
const types = events.map((e) => e["type"]);
|
|
666
661
|
expect(types).toContain("demo-pipeline:event:annotated");
|
|
667
662
|
expect(types.length).toBeGreaterThanOrEqual(2); // created + annotated
|
|
@@ -679,7 +674,7 @@ describe("defineWriteHandler({ perform: pipeline(...) }) — real dispatcher pat
|
|
|
679
674
|
// Capture row-count before — beforeEach doesn't truncate widget,
|
|
680
675
|
// earlier tests in this run leave rows. We assert "broken did not
|
|
681
676
|
// add a new row" by comparing before/after.
|
|
682
|
-
const before = await stack.db
|
|
677
|
+
const before = await selectMany(stack.db, widgetTable);
|
|
683
678
|
|
|
684
679
|
const res = await stack.http.write("demo-pipeline:write:widget:create-broken", {}, admin);
|
|
685
680
|
expect(res.status).not.toBe(200);
|
|
@@ -688,7 +683,7 @@ describe("defineWriteHandler({ perform: pipeline(...) }) — real dispatcher pat
|
|
|
688
683
|
expect(body.isSuccess).toBe(false);
|
|
689
684
|
expect(body.error.code).toBeDefined();
|
|
690
685
|
|
|
691
|
-
const after = await stack.db
|
|
686
|
+
const after = await selectMany(stack.db, widgetTable);
|
|
692
687
|
expect(after).toHaveLength(before.length);
|
|
693
688
|
});
|
|
694
689
|
|
|
@@ -728,7 +723,7 @@ describe("defineWriteHandler({ perform: pipeline(...) }) — real dispatcher pat
|
|
|
728
723
|
});
|
|
729
724
|
|
|
730
725
|
test("read.findMany returns the row array (count matches table state)", async () => {
|
|
731
|
-
const before = await stack.db
|
|
726
|
+
const before = await selectMany(stack.db, widgetTable);
|
|
732
727
|
const res = await stack.http.write("demo-pipeline:write:widget:list", {}, admin);
|
|
733
728
|
expect(res.status).toBe(200);
|
|
734
729
|
const body = (await res.json()) as { isSuccess: true; data: { count: number } };
|
|
@@ -739,7 +734,7 @@ describe("defineWriteHandler({ perform: pipeline(...) }) — real dispatcher pat
|
|
|
739
734
|
// Seed enough widgets so a limit-1 result is verifiably truncated.
|
|
740
735
|
await stack.http.write("demo-pipeline:write:widget:create", { label: "limit-test-1" }, admin);
|
|
741
736
|
await stack.http.write("demo-pipeline:write:widget:create", { label: "limit-test-2" }, admin);
|
|
742
|
-
const total = await stack.db
|
|
737
|
+
const total = await selectMany(stack.db, widgetTable);
|
|
743
738
|
expect(total.length).toBeGreaterThanOrEqual(2);
|
|
744
739
|
|
|
745
740
|
const res = await stack.http.write("demo-pipeline:write:widget:list-one", {}, admin);
|
|
@@ -750,10 +745,9 @@ describe("defineWriteHandler({ perform: pipeline(...) }) — real dispatcher pat
|
|
|
750
745
|
|
|
751
746
|
test("unsafeProjectionDelete is a silent no-op when the where-clause matches no rows", async () => {
|
|
752
747
|
// Precondition: nothing with this correlation-id exists.
|
|
753
|
-
const before = await stack.db
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
.where(eq(pipelineDemoLogTable.correlationId, "no-match-id"));
|
|
748
|
+
const before = await selectMany(stack.db, pipelineDemoLogTable, {
|
|
749
|
+
correlationId: "no-match-id",
|
|
750
|
+
});
|
|
757
751
|
expect(before).toHaveLength(0);
|
|
758
752
|
|
|
759
753
|
// Purge anyway — drizzle's DELETE-WHERE with no match commits silently.
|
|
@@ -774,10 +768,7 @@ describe("defineWriteHandler({ perform: pipeline(...) }) — real dispatcher pat
|
|
|
774
768
|
{ correlationId: "to-purge", message: "delete-me" },
|
|
775
769
|
admin,
|
|
776
770
|
);
|
|
777
|
-
const seeded = await stack.db
|
|
778
|
-
.select()
|
|
779
|
-
.from(pipelineDemoLogTable)
|
|
780
|
-
.where(eq(pipelineDemoLogTable.correlationId, "to-purge"));
|
|
771
|
+
const seeded = await selectMany(stack.db, pipelineDemoLogTable, { correlationId: "to-purge" });
|
|
781
772
|
expect(seeded).toHaveLength(1);
|
|
782
773
|
|
|
783
774
|
const res = await stack.http.write(
|
|
@@ -787,10 +778,7 @@ describe("defineWriteHandler({ perform: pipeline(...) }) — real dispatcher pat
|
|
|
787
778
|
);
|
|
788
779
|
expect(res.status).toBe(200);
|
|
789
780
|
|
|
790
|
-
const after = await stack.db
|
|
791
|
-
.select()
|
|
792
|
-
.from(pipelineDemoLogTable)
|
|
793
|
-
.where(eq(pipelineDemoLogTable.correlationId, "to-purge"));
|
|
781
|
+
const after = await selectMany(stack.db, pipelineDemoLogTable, { correlationId: "to-purge" });
|
|
794
782
|
expect(after).toHaveLength(0);
|
|
795
783
|
});
|
|
796
784
|
|
|
@@ -801,10 +789,9 @@ describe("defineWriteHandler({ perform: pipeline(...) }) — real dispatcher pat
|
|
|
801
789
|
admin,
|
|
802
790
|
);
|
|
803
791
|
expect(res.status).toBe(200);
|
|
804
|
-
const rows = await stack.db
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
.where(eq(pipelineDemoLogTable.correlationId, "branch-truthy"));
|
|
792
|
+
const rows = await selectMany(stack.db, pipelineDemoLogTable, {
|
|
793
|
+
correlationId: "branch-truthy",
|
|
794
|
+
});
|
|
808
795
|
expect(rows).toHaveLength(1);
|
|
809
796
|
expect(rows[0]?.message).toBe("real");
|
|
810
797
|
});
|
|
@@ -816,10 +803,9 @@ describe("defineWriteHandler({ perform: pipeline(...) }) — real dispatcher pat
|
|
|
816
803
|
admin,
|
|
817
804
|
);
|
|
818
805
|
expect(res.status).toBe(200);
|
|
819
|
-
const rows = await stack.db
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
.where(eq(pipelineDemoLogTable.correlationId, "branch-falsy"));
|
|
806
|
+
const rows = await selectMany(stack.db, pipelineDemoLogTable, {
|
|
807
|
+
correlationId: "branch-falsy",
|
|
808
|
+
});
|
|
823
809
|
expect(rows).toHaveLength(0);
|
|
824
810
|
});
|
|
825
811
|
|
|
@@ -834,7 +820,7 @@ describe("defineWriteHandler({ perform: pipeline(...) }) — real dispatcher pat
|
|
|
834
820
|
const body = (await res.json()) as { isSuccess: true; data: { count: number } };
|
|
835
821
|
expect(body.data.count).toBe(3);
|
|
836
822
|
|
|
837
|
-
const rows = await stack.db
|
|
823
|
+
const rows = await selectMany(stack.db, pipelineDemoLogTable);
|
|
838
824
|
const correlationIds = rows.map((r) => r["correlationId"]).sort();
|
|
839
825
|
expect(correlationIds).toEqual(ids.slice().sort());
|
|
840
826
|
});
|
|
@@ -858,8 +844,8 @@ describe("defineWriteHandler({ perform: pipeline(...) }) — real dispatcher pat
|
|
|
858
844
|
// projection-row insert that aggregate.create performed must
|
|
859
845
|
// disappear with the rollback. Otherwise pipeline-form handlers
|
|
860
846
|
// silently leak partial state on mid-pipeline failures.
|
|
861
|
-
const beforeWidgets = await stack.db
|
|
862
|
-
const beforeEvents = await stack.db
|
|
847
|
+
const beforeWidgets = await selectMany(stack.db, widgetTable);
|
|
848
|
+
const beforeEvents = await selectMany(stack.db, eventsTable);
|
|
863
849
|
|
|
864
850
|
const res = await stack.http.write(
|
|
865
851
|
"demo-pipeline:write:widget:create-then-throw",
|
|
@@ -868,8 +854,8 @@ describe("defineWriteHandler({ perform: pipeline(...) }) — real dispatcher pat
|
|
|
868
854
|
);
|
|
869
855
|
expect(res.status).toBe(500);
|
|
870
856
|
|
|
871
|
-
const afterWidgets = await stack.db
|
|
872
|
-
const afterEvents = await stack.db
|
|
857
|
+
const afterWidgets = await selectMany(stack.db, widgetTable);
|
|
858
|
+
const afterEvents = await selectMany(stack.db, eventsTable);
|
|
873
859
|
expect(afterWidgets.length).toBe(beforeWidgets.length);
|
|
874
860
|
expect(afterEvents.length).toBe(beforeEvents.length);
|
|
875
861
|
});
|
|
@@ -879,7 +865,7 @@ describe("defineWriteHandler({ perform: pipeline(...) }) — real dispatcher pat
|
|
|
879
865
|
// its own scope save/restore + sub-step-list runner. A regression
|
|
880
866
|
// there would let iteration-1's aggregate.create commit while
|
|
881
867
|
// iteration-2's throw aborts the rest, leaving partial state.
|
|
882
|
-
const beforeWidgets = await stack.db
|
|
868
|
+
const beforeWidgets = await selectMany(stack.db, widgetTable);
|
|
883
869
|
|
|
884
870
|
const res = await stack.http.write(
|
|
885
871
|
"demo-pipeline:write:widget:foreach-then-throw",
|
|
@@ -888,7 +874,7 @@ describe("defineWriteHandler({ perform: pipeline(...) }) — real dispatcher pat
|
|
|
888
874
|
);
|
|
889
875
|
expect(res.status).toBe(500);
|
|
890
876
|
|
|
891
|
-
const afterWidgets = await stack.db
|
|
877
|
+
const afterWidgets = await selectMany(stack.db, widgetTable);
|
|
892
878
|
expect(afterWidgets.length).toBe(beforeWidgets.length);
|
|
893
879
|
});
|
|
894
880
|
});
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// every step inside a pipeline-form handler would be untraceable —
|
|
14
14
|
// and we wouldn't notice until a prod incident lacks the breadcrumbs.
|
|
15
15
|
|
|
16
|
-
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "
|
|
16
|
+
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
17
17
|
import { z } from "zod";
|
|
18
18
|
import { setupTestStack, type TestStack, TestUsers, unsafeCreateEntityTable } from "../../stack";
|
|
19
19
|
import { createRecordingProvider, type RecordingProvider } from "../../testing";
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
// gate; if a future regression suspect needs that breakdown, extend
|
|
23
23
|
// here with a longer-pipeline handler variant.
|
|
24
24
|
|
|
25
|
-
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "
|
|
25
|
+
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
26
26
|
import { z } from "zod";
|
|
27
27
|
import { setupTestStack, type TestStack, TestUsers, unsafeCreateEntityTable } from "../../stack";
|
|
28
28
|
import { defineFeature } from "../define-feature";
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// to steps/_no-return-guard.ts). Tests cover happy paths, scope
|
|
6
6
|
// hygiene, error-propagation and build-time guards.
|
|
7
7
|
|
|
8
|
-
import { describe, expect, it } from "
|
|
8
|
+
import { describe, expect, it } from "bun:test";
|
|
9
9
|
import { z } from "zod";
|
|
10
10
|
import { TestUsers } from "../../stack";
|
|
11
11
|
import { defineWriteHandler } from "../define-handler";
|
|
@@ -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({
|