@beignet/core 0.0.2 → 0.0.4
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/CHANGELOG.md +173 -0
- package/README.md +821 -30
- package/dist/application/index.d.ts +28 -2
- package/dist/application/index.d.ts.map +1 -1
- package/dist/application/index.js +140 -12
- package/dist/application/index.js.map +1 -1
- package/dist/client/client.d.ts +2 -2
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +136 -48
- package/dist/client/client.js.map +1 -1
- package/dist/client/error-messages.d.ts +14 -0
- package/dist/client/error-messages.d.ts.map +1 -0
- package/dist/client/error-messages.js +23 -0
- package/dist/client/error-messages.js.map +1 -0
- package/dist/client/index.d.ts +8 -4
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +6 -2
- package/dist/client/index.js.map +1 -1
- package/dist/client/types.d.ts +35 -5
- package/dist/client/types.d.ts.map +1 -1
- package/dist/client-only.d.ts +8 -0
- package/dist/client-only.d.ts.map +1 -0
- package/dist/client-only.js +8 -0
- package/dist/client-only.js.map +1 -0
- package/dist/config/index.d.ts +5 -5
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +2 -2
- package/dist/config/index.js.map +1 -1
- package/dist/contracts/catalog-errors.d.ts +27 -0
- package/dist/contracts/catalog-errors.d.ts.map +1 -0
- package/dist/contracts/catalog-errors.js +69 -0
- package/dist/contracts/catalog-errors.js.map +1 -0
- package/dist/contracts/contract-builder.d.ts +15 -12
- package/dist/contracts/contract-builder.d.ts.map +1 -1
- package/dist/contracts/contract-builder.js +15 -41
- package/dist/contracts/contract-builder.js.map +1 -1
- package/dist/contracts/contract-group.d.ts +11 -8
- package/dist/contracts/contract-group.d.ts.map +1 -1
- package/dist/contracts/contract-group.js +13 -40
- package/dist/contracts/contract-group.js.map +1 -1
- package/dist/contracts/contract-like.d.ts +1 -1
- package/dist/contracts/contract-like.d.ts.map +1 -1
- package/dist/contracts/index.d.ts +13 -9
- package/dist/contracts/index.d.ts.map +1 -1
- package/dist/contracts/index.js +9 -5
- package/dist/contracts/index.js.map +1 -1
- package/dist/contracts/openapi-meta.d.ts +48 -0
- package/dist/contracts/openapi-meta.d.ts.map +1 -1
- package/dist/contracts/openapi-meta.js +3 -0
- package/dist/contracts/openapi-meta.js.map +1 -1
- package/dist/contracts/path-template.d.ts +1 -1
- package/dist/contracts/path-template.js +2 -2
- package/dist/contracts/path-template.js.map +1 -1
- package/dist/contracts/schema-shape.d.ts +37 -0
- package/dist/contracts/schema-shape.d.ts.map +1 -0
- package/dist/contracts/schema-shape.js +61 -0
- package/dist/contracts/schema-shape.js.map +1 -0
- package/dist/contracts/success-status.d.ts +32 -0
- package/dist/contracts/success-status.d.ts.map +1 -0
- package/dist/contracts/success-status.js +18 -0
- package/dist/contracts/success-status.js.map +1 -0
- package/dist/contracts/types.d.ts +25 -5
- package/dist/contracts/types.d.ts.map +1 -1
- package/dist/contracts/types.js.map +1 -1
- package/dist/contracts/utils.d.ts +1 -1
- package/dist/contracts/utils.d.ts.map +1 -1
- package/dist/contracts/utils.js +1 -1
- package/dist/contracts/utils.js.map +1 -1
- package/dist/domain/events.d.ts +1 -1
- package/dist/domain/events.d.ts.map +1 -1
- package/dist/domain/events.js +1 -1
- package/dist/domain/events.js.map +1 -1
- package/dist/domain/index.d.ts +3 -3
- package/dist/domain/index.d.ts.map +1 -1
- package/dist/domain/index.js +3 -3
- package/dist/domain/index.js.map +1 -1
- package/dist/errors/catalog.d.ts +9 -1
- package/dist/errors/catalog.d.ts.map +1 -1
- package/dist/errors/catalog.js +7 -1
- package/dist/errors/catalog.js.map +1 -1
- package/dist/errors/http.d.ts +10 -0
- package/dist/errors/http.d.ts.map +1 -1
- package/dist/errors/http.js +11 -1
- package/dist/errors/http.js.map +1 -1
- package/dist/errors/index.d.ts +4 -4
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +4 -4
- package/dist/errors/index.js.map +1 -1
- package/dist/errors/response.d.ts +4 -1
- package/dist/errors/response.d.ts.map +1 -1
- package/dist/errors/response.js.map +1 -1
- package/dist/events/index.d.ts +10 -12
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js +10 -10
- package/dist/events/index.js.map +1 -1
- package/dist/idempotency/index.d.ts +5 -3
- package/dist/idempotency/index.d.ts.map +1 -1
- package/dist/idempotency/index.js.map +1 -1
- package/dist/jobs/index.d.ts +148 -16
- package/dist/jobs/index.d.ts.map +1 -1
- package/dist/jobs/index.js +174 -14
- package/dist/jobs/index.js.map +1 -1
- package/dist/notifications/index.d.ts +14 -16
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +14 -14
- package/dist/notifications/index.js.map +1 -1
- package/dist/openapi/index.d.ts +8 -3
- package/dist/openapi/index.d.ts.map +1 -1
- package/dist/openapi/index.js +41 -29
- package/dist/openapi/index.js.map +1 -1
- package/dist/openapi/schema-introspector.d.ts +37 -0
- package/dist/openapi/schema-introspector.d.ts.map +1 -1
- package/dist/openapi/schema-introspector.js +23 -17
- package/dist/openapi/schema-introspector.js.map +1 -1
- package/dist/outbox/index.d.ts +18 -4
- package/dist/outbox/index.d.ts.map +1 -1
- package/dist/outbox/index.js +104 -4
- package/dist/outbox/index.js.map +1 -1
- package/dist/ports/audit.d.ts +56 -10
- package/dist/ports/audit.d.ts.map +1 -1
- package/dist/ports/audit.js +71 -3
- package/dist/ports/audit.js.map +1 -1
- package/dist/ports/auth.d.ts +92 -0
- package/dist/ports/auth.d.ts.map +1 -1
- package/dist/ports/auth.js +92 -0
- package/dist/ports/auth.js.map +1 -1
- package/dist/ports/events.d.ts +2 -2
- package/dist/ports/events.d.ts.map +1 -1
- package/dist/ports/index.d.ts +62 -33
- package/dist/ports/index.d.ts.map +1 -1
- package/dist/ports/index.js +28 -34
- package/dist/ports/index.js.map +1 -1
- package/dist/ports/policy.d.ts +32 -3
- package/dist/ports/policy.d.ts.map +1 -1
- package/dist/ports/policy.js +13 -2
- package/dist/ports/policy.js.map +1 -1
- package/dist/ports/testing.d.ts +1030 -2
- package/dist/ports/testing.d.ts.map +1 -1
- package/dist/ports/testing.js +1031 -1
- package/dist/ports/testing.js.map +1 -1
- package/dist/ports/unbound.d.ts +21 -0
- package/dist/ports/unbound.d.ts.map +1 -0
- package/dist/ports/unbound.js +57 -0
- package/dist/ports/unbound.js.map +1 -0
- package/dist/ports/unit-of-work.d.ts +1 -1
- package/dist/ports/unit-of-work.d.ts.map +1 -1
- package/dist/ports/unit-of-work.js +1 -1
- package/dist/ports/unit-of-work.js.map +1 -1
- package/dist/providers/index.d.ts +3 -2
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +3 -2
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/instrumentation.d.ts +46 -5
- package/dist/providers/instrumentation.d.ts.map +1 -1
- package/dist/providers/instrumentation.js +25 -6
- package/dist/providers/instrumentation.js.map +1 -1
- package/dist/providers/metadata.d.ts +39 -0
- package/dist/providers/metadata.d.ts.map +1 -0
- package/dist/providers/metadata.js +169 -0
- package/dist/providers/metadata.js.map +1 -0
- package/dist/providers/provider.d.ts +114 -9
- package/dist/providers/provider.d.ts.map +1 -1
- package/dist/providers/provider.js +3 -20
- package/dist/providers/provider.js.map +1 -1
- package/dist/schedules/index.d.ts +94 -13
- package/dist/schedules/index.d.ts.map +1 -1
- package/dist/schedules/index.js +66 -12
- package/dist/schedules/index.js.map +1 -1
- package/dist/server/audit-context.d.ts +29 -0
- package/dist/server/audit-context.d.ts.map +1 -0
- package/dist/server/audit-context.js +44 -0
- package/dist/server/audit-context.js.map +1 -0
- package/dist/server/context.d.ts +141 -0
- package/dist/server/context.d.ts.map +1 -0
- package/dist/server/context.js +39 -0
- package/dist/server/context.js.map +1 -0
- package/dist/server/contract-like.d.ts +1 -1
- package/dist/server/contract-like.d.ts.map +1 -1
- package/dist/server/contract-like.js +1 -1
- package/dist/server/contract-like.js.map +1 -1
- package/dist/server/health.d.ts +2 -2
- package/dist/server/health.d.ts.map +1 -1
- package/dist/server/hooks/auth.d.ts +89 -65
- package/dist/server/hooks/auth.d.ts.map +1 -1
- package/dist/server/hooks/auth.js +84 -55
- package/dist/server/hooks/auth.js.map +1 -1
- package/dist/server/hooks/cors.d.ts +1 -1
- package/dist/server/hooks/cors.d.ts.map +1 -1
- package/dist/server/hooks/errors.d.ts +2 -2
- package/dist/server/hooks/errors.d.ts.map +1 -1
- package/dist/server/hooks/errors.js +2 -2
- package/dist/server/hooks/errors.js.map +1 -1
- package/dist/server/hooks/idempotency.d.ts +78 -0
- package/dist/server/hooks/idempotency.d.ts.map +1 -0
- package/dist/server/hooks/idempotency.js +154 -0
- package/dist/server/hooks/idempotency.js.map +1 -0
- package/dist/server/hooks/index.d.ts +8 -7
- package/dist/server/hooks/index.d.ts.map +1 -1
- package/dist/server/hooks/index.js +6 -5
- package/dist/server/hooks/index.js.map +1 -1
- package/dist/server/hooks/logging.d.ts +2 -2
- package/dist/server/hooks/logging.d.ts.map +1 -1
- package/dist/server/hooks/logging.js +1 -1
- package/dist/server/hooks/logging.js.map +1 -1
- package/dist/server/hooks/rate-limit.d.ts +25 -7
- package/dist/server/hooks/rate-limit.d.ts.map +1 -1
- package/dist/server/hooks/rate-limit.js +47 -12
- package/dist/server/hooks/rate-limit.js.map +1 -1
- package/dist/server/hooks.d.ts +1 -1
- package/dist/server/hooks.d.ts.map +1 -1
- package/dist/server/hooks.js +1 -1
- package/dist/server/hooks.js.map +1 -1
- package/dist/server/http.d.ts +84 -6
- package/dist/server/http.d.ts.map +1 -1
- package/dist/server/index.d.ts +36 -12
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +24 -8
- package/dist/server/index.js.map +1 -1
- package/dist/server/instrumentation.d.ts +108 -0
- package/dist/server/instrumentation.d.ts.map +1 -0
- package/dist/server/instrumentation.js +297 -0
- package/dist/server/instrumentation.js.map +1 -0
- package/dist/server/openapi.d.ts +3 -3
- package/dist/server/openapi.d.ts.map +1 -1
- package/dist/server/openapi.js +1 -1
- package/dist/server/openapi.js.map +1 -1
- package/dist/server/providers/index.d.ts +3 -3
- package/dist/server/providers/index.d.ts.map +1 -1
- package/dist/server/providers/index.js +3 -3
- package/dist/server/providers/index.js.map +1 -1
- package/dist/server/providers/loadProviderConfig.d.ts +2 -2
- package/dist/server/providers/loadProviderConfig.d.ts.map +1 -1
- package/dist/server/providers/loadProviderConfig.js +2 -2
- package/dist/server/providers/loadProviderConfig.js.map +1 -1
- package/dist/server/request-context.d.ts +67 -0
- package/dist/server/request-context.d.ts.map +1 -0
- package/dist/server/request-context.js +79 -0
- package/dist/server/request-context.js.map +1 -0
- package/dist/server/server-context.d.ts +38 -0
- package/dist/server/server-context.d.ts.map +1 -0
- package/dist/server/server-context.js +38 -0
- package/dist/server/server-context.js.map +1 -0
- package/dist/server/server.d.ts +148 -35
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +482 -145
- package/dist/server/server.js.map +1 -1
- package/dist/server/types.d.ts +2 -2
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/types.js +2 -2
- package/dist/server/types.js.map +1 -1
- package/dist/server/use-case-route.d.ts +263 -0
- package/dist/server/use-case-route.d.ts.map +1 -0
- package/dist/server/use-case-route.js +77 -0
- package/dist/server/use-case-route.js.map +1 -0
- package/dist/server-only.d.ts +8 -0
- package/dist/server-only.d.ts.map +1 -0
- package/dist/server-only.js +8 -0
- package/dist/server-only.js.map +1 -0
- package/dist/tasks/index.d.ts +139 -0
- package/dist/tasks/index.d.ts.map +1 -0
- package/dist/tasks/index.js +98 -0
- package/dist/tasks/index.js.map +1 -0
- package/dist/testing/index.d.ts +611 -5
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +434 -4
- package/dist/testing/index.js.map +1 -1
- package/dist/tracing/index.d.ts +89 -0
- package/dist/tracing/index.d.ts.map +1 -0
- package/dist/tracing/index.js +101 -0
- package/dist/tracing/index.js.map +1 -0
- package/dist/uploads/client.d.ts +278 -0
- package/dist/uploads/client.d.ts.map +1 -0
- package/dist/uploads/client.js +428 -0
- package/dist/uploads/client.js.map +1 -0
- package/dist/uploads/index.d.ts +361 -0
- package/dist/uploads/index.d.ts.map +1 -0
- package/dist/uploads/index.js +543 -0
- package/dist/uploads/index.js.map +1 -0
- package/package.json +34 -3
- package/src/application/index.ts +193 -10
- package/src/client/client.ts +148 -150
- package/src/client/error-messages.ts +35 -0
- package/src/client/index.ts +12 -4
- package/src/client/types.ts +44 -5
- package/src/client-only.ts +7 -0
- package/src/config/index.ts +6 -6
- package/src/contracts/catalog-errors.ts +115 -0
- package/src/contracts/contract-builder.ts +39 -76
- package/src/contracts/contract-group.ts +33 -68
- package/src/contracts/contract-like.ts +1 -1
- package/src/contracts/index.ts +24 -11
- package/src/contracts/openapi-meta.ts +55 -0
- package/src/contracts/path-template.ts +2 -2
- package/src/contracts/schema-shape.ts +75 -0
- package/src/contracts/success-status.ts +68 -0
- package/src/contracts/types.ts +32 -5
- package/src/contracts/utils.ts +5 -2
- package/src/domain/events.ts +6 -2
- package/src/domain/index.ts +3 -3
- package/src/errors/catalog.ts +9 -1
- package/src/errors/http.ts +11 -1
- package/src/errors/index.ts +4 -4
- package/src/errors/response.ts +4 -1
- package/src/events/index.ts +12 -26
- package/src/idempotency/index.ts +5 -3
- package/src/jobs/index.ts +340 -29
- package/src/notifications/index.ts +17 -27
- package/src/openapi/index.ts +73 -38
- package/src/openapi/schema-introspector.ts +68 -17
- package/src/outbox/index.ts +151 -6
- package/src/ports/audit.ts +120 -11
- package/src/ports/auth.ts +132 -0
- package/src/ports/events.ts +2 -2
- package/src/ports/index.ts +104 -35
- package/src/ports/policy.ts +50 -3
- package/src/ports/testing.ts +2220 -33
- package/src/ports/unbound.ts +64 -0
- package/src/ports/unit-of-work.ts +6 -2
- package/src/providers/index.ts +16 -3
- package/src/providers/instrumentation.ts +93 -8
- package/src/providers/metadata.ts +234 -0
- package/src/providers/provider.ts +168 -9
- package/src/schedules/index.ts +173 -23
- package/src/server/audit-context.ts +45 -0
- package/src/server/context.ts +224 -0
- package/src/server/contract-like.ts +1 -1
- package/src/server/health.ts +2 -2
- package/src/server/hooks/auth.ts +175 -158
- package/src/server/hooks/cors.ts +1 -1
- package/src/server/hooks/errors.ts +7 -4
- package/src/server/hooks/idempotency.ts +263 -0
- package/src/server/hooks/index.ts +15 -12
- package/src/server/hooks/logging.ts +3 -3
- package/src/server/hooks/rate-limit.ts +85 -17
- package/src/server/hooks.ts +1 -1
- package/src/server/http.ts +112 -6
- package/src/server/index.ts +63 -12
- package/src/server/instrumentation.ts +470 -0
- package/src/server/openapi.ts +4 -4
- package/src/server/providers/index.ts +6 -3
- package/src/server/providers/loadProviderConfig.ts +4 -4
- package/src/server/request-context.ts +116 -0
- package/src/server/server-context.ts +44 -0
- package/src/server/server.ts +1045 -229
- package/src/server/types.ts +2 -2
- package/src/server/use-case-route.ts +430 -0
- package/src/server-only.ts +7 -0
- package/src/tasks/index.ts +275 -0
- package/src/testing/index.ts +1153 -6
- package/src/tracing/index.ts +176 -0
- package/src/uploads/client.ts +861 -0
- package/src/uploads/index.ts +1071 -0
- package/dist/ports/mailer.d.ts +0 -6
- package/dist/ports/mailer.d.ts.map +0 -1
- package/dist/ports/mailer.js +0 -2
- package/dist/ports/mailer.js.map +0 -1
- package/dist/ports/schedules.d.ts +0 -9
- package/dist/ports/schedules.d.ts.map +0 -1
- package/dist/ports/schedules.js +0 -2
- package/dist/ports/schedules.js.map +0 -1
package/dist/ports/testing.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createAnonymousActor, createServiceActor, createSystemActor, createTenant, createUserActor, } from "./audit.js";
|
|
2
|
+
import { createGate, } from "./policy.js";
|
|
2
3
|
/**
|
|
3
4
|
* Create a recording event bus for testing.
|
|
4
5
|
*
|
|
@@ -30,6 +31,667 @@ export function createRecordingEventBus() {
|
|
|
30
31
|
};
|
|
31
32
|
return { bus, events };
|
|
32
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Create a recording job dispatcher for tests.
|
|
36
|
+
*
|
|
37
|
+
* The dispatcher records dispatch intent without running the job handler. Use
|
|
38
|
+
* this when a use case or listener should enqueue work but the test does not
|
|
39
|
+
* need to execute that work inline.
|
|
40
|
+
*
|
|
41
|
+
* @returns A job dispatcher plus its captured dispatches.
|
|
42
|
+
*/
|
|
43
|
+
export function createRecordingJobDispatcher() {
|
|
44
|
+
const dispatchedJobs = [];
|
|
45
|
+
const jobs = {
|
|
46
|
+
dispatch(job, payload) {
|
|
47
|
+
dispatchedJobs.push({ name: job.name, job, payload });
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
return { jobs, dispatchedJobs };
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Create a recording schedule runner for tests.
|
|
54
|
+
*
|
|
55
|
+
* The runner records schedule run intent without executing the schedule
|
|
56
|
+
* handler. Use `createInlineScheduleRunner(...)` when the test should run the
|
|
57
|
+
* handler.
|
|
58
|
+
*
|
|
59
|
+
* @returns A schedule runner plus its captured runs.
|
|
60
|
+
*/
|
|
61
|
+
export function createRecordingScheduleRunner() {
|
|
62
|
+
const runs = [];
|
|
63
|
+
const runner = {
|
|
64
|
+
async run(schedule, options = {}) {
|
|
65
|
+
runs.push({
|
|
66
|
+
name: schedule.name,
|
|
67
|
+
schedule,
|
|
68
|
+
...(Object.hasOwn(options, "payload")
|
|
69
|
+
? { payload: options.payload }
|
|
70
|
+
: {}),
|
|
71
|
+
...(options.id !== undefined ? { id: options.id } : {}),
|
|
72
|
+
...(options.source !== undefined ? { source: options.source } : {}),
|
|
73
|
+
...(options.scheduledAt !== undefined
|
|
74
|
+
? { scheduledAt: options.scheduledAt }
|
|
75
|
+
: {}),
|
|
76
|
+
...(options.triggeredAt !== undefined
|
|
77
|
+
? { triggeredAt: options.triggeredAt }
|
|
78
|
+
: {}),
|
|
79
|
+
});
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
return { runner, runs };
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Create a provider instrumentation port for tests.
|
|
86
|
+
*
|
|
87
|
+
* The port records every event and can optionally disable specific watchers.
|
|
88
|
+
* Use the returned `events` array with provider instrumentation assertion
|
|
89
|
+
* helpers.
|
|
90
|
+
*
|
|
91
|
+
* @returns A provider instrumentation port plus its captured events.
|
|
92
|
+
*/
|
|
93
|
+
export function createRecordingProviderInstrumentation(options = {}) {
|
|
94
|
+
const events = [];
|
|
95
|
+
const enabledWatchers = options.enabledWatchers
|
|
96
|
+
? new Set(options.enabledWatchers)
|
|
97
|
+
: null;
|
|
98
|
+
const disabledWatchers = new Set(options.disabledWatchers ?? []);
|
|
99
|
+
const instrumentation = {
|
|
100
|
+
record(event) {
|
|
101
|
+
events.push(event);
|
|
102
|
+
},
|
|
103
|
+
isWatcherEnabled(name) {
|
|
104
|
+
if (disabledWatchers.has(name))
|
|
105
|
+
return false;
|
|
106
|
+
return enabledWatchers ? enabledWatchers.has(name) : true;
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
return { instrumentation, events };
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Create a predictable user actor for tests.
|
|
113
|
+
*
|
|
114
|
+
* Use this when authorization or audit assertions need a stable actor shape
|
|
115
|
+
* without repeating the `ActivityActor` object in every test.
|
|
116
|
+
*
|
|
117
|
+
* @param id - Stable user ID for the test actor.
|
|
118
|
+
* @param options - Optional display name, role, and metadata.
|
|
119
|
+
* @returns A user actor with `type: "user"`.
|
|
120
|
+
*/
|
|
121
|
+
export function createTestUserActor(id = "user_test", options = {}) {
|
|
122
|
+
const { role, metadata, ...actorOptions } = options;
|
|
123
|
+
const mergedMetadata = mergeRoleMetadata(metadata, role);
|
|
124
|
+
return createUserActor(id, {
|
|
125
|
+
...actorOptions,
|
|
126
|
+
...(mergedMetadata ? { metadata: mergedMetadata } : {}),
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Create a predictable user actor for tests that exercise impersonation.
|
|
131
|
+
*
|
|
132
|
+
* The returned actor remains the effective user, with `metadata.impersonatorId`
|
|
133
|
+
* recording who initiated the impersonated access.
|
|
134
|
+
*
|
|
135
|
+
* @param id - Stable user ID being impersonated.
|
|
136
|
+
* @param options - Impersonator ID plus optional display name, role, and metadata.
|
|
137
|
+
* @returns A user actor with impersonation metadata.
|
|
138
|
+
*/
|
|
139
|
+
export function createTestImpersonatedUserActor(id, options) {
|
|
140
|
+
const { impersonatorId, ...actorOptions } = options;
|
|
141
|
+
return createTestUserActor(id, {
|
|
142
|
+
...actorOptions,
|
|
143
|
+
metadata: {
|
|
144
|
+
...actorOptions.metadata,
|
|
145
|
+
impersonatorId,
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Create a predictable anonymous actor for tests.
|
|
151
|
+
*
|
|
152
|
+
* @param options - Optional display name or metadata.
|
|
153
|
+
* @returns An anonymous actor with `type: "anonymous"`.
|
|
154
|
+
*/
|
|
155
|
+
export function createTestAnonymousActor(options = {}) {
|
|
156
|
+
return createAnonymousActor(options);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Create a predictable service actor for tests.
|
|
160
|
+
*
|
|
161
|
+
* @param id - Stable service ID.
|
|
162
|
+
* @param options - Optional display name or metadata.
|
|
163
|
+
* @returns A service actor with `type: "service"`.
|
|
164
|
+
*/
|
|
165
|
+
export function createTestServiceActor(id = "service_test", options = {}) {
|
|
166
|
+
return createServiceActor(id, options);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Create a predictable system actor for tests.
|
|
170
|
+
*
|
|
171
|
+
* @param id - Stable system actor ID.
|
|
172
|
+
* @param options - Optional display name or metadata.
|
|
173
|
+
* @returns A system actor with `type: "system"`.
|
|
174
|
+
*/
|
|
175
|
+
export function createTestSystemActor(id = "system_test", options = {}) {
|
|
176
|
+
return createSystemActor(id, options);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Create a predictable tenant for tests.
|
|
180
|
+
*
|
|
181
|
+
* @param id - Stable tenant ID.
|
|
182
|
+
* @param options - Optional slug or metadata.
|
|
183
|
+
* @returns A tenant descriptor.
|
|
184
|
+
*/
|
|
185
|
+
export function createTestTenant(id = "tenant_test", options = {}) {
|
|
186
|
+
return createTenant(id, options);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Create the activity fields commonly copied onto app test contexts.
|
|
190
|
+
*
|
|
191
|
+
* @param options - Optional actor, tenant, request ID, and trace ID overrides.
|
|
192
|
+
* @returns Stable activity context fields for a test.
|
|
193
|
+
*/
|
|
194
|
+
export function createTestActivityContext(options = {}) {
|
|
195
|
+
return {
|
|
196
|
+
actor: options.actor ?? createTestUserActor(),
|
|
197
|
+
...(options.tenant === null
|
|
198
|
+
? {}
|
|
199
|
+
: { tenant: options.tenant ?? createTestTenant() }),
|
|
200
|
+
requestId: options.requestId ?? "test-request",
|
|
201
|
+
traceId: options.traceId ?? "test-trace",
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Find the first audit entry matching the expected fields.
|
|
206
|
+
*
|
|
207
|
+
* @param entries - Audit entries captured by a memory or fake audit port.
|
|
208
|
+
* @param expectation - Partial audit fields to match.
|
|
209
|
+
* @returns The first matching entry, or `undefined`.
|
|
210
|
+
*/
|
|
211
|
+
export function findAuditEntry(entries, expectation) {
|
|
212
|
+
return entries.find((entry) => auditEntryMatches(entry, expectation));
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Assert that an audit entry exists and return the matching entry.
|
|
216
|
+
*
|
|
217
|
+
* The helper throws a plain `Error`, so it works with Bun, Vitest, Jest, and
|
|
218
|
+
* other test runners.
|
|
219
|
+
*
|
|
220
|
+
* @param entries - Audit entries captured by a memory or fake audit port.
|
|
221
|
+
* @param expectation - Partial audit fields to match.
|
|
222
|
+
* @returns The matching audit entry.
|
|
223
|
+
* @throws Error when no entry matches.
|
|
224
|
+
*/
|
|
225
|
+
export function assertAuditEntry(entries, expectation) {
|
|
226
|
+
const entry = findAuditEntry(entries, expectation);
|
|
227
|
+
if (entry)
|
|
228
|
+
return entry;
|
|
229
|
+
throw new Error(`Expected audit entry matching ${formatAuditExpectation(expectation)}, but only found ${entries.length} entr${entries.length === 1 ? "y" : "ies"}.`);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Assert that no audit entry matches the expected fields.
|
|
233
|
+
*
|
|
234
|
+
* @param entries - Audit entries captured by a memory or fake audit port.
|
|
235
|
+
* @param expectation - Partial audit fields to reject.
|
|
236
|
+
* @throws Error when a matching entry exists.
|
|
237
|
+
*/
|
|
238
|
+
export function assertNoAuditEntry(entries, expectation) {
|
|
239
|
+
const entry = findAuditEntry(entries, expectation);
|
|
240
|
+
if (!entry)
|
|
241
|
+
return;
|
|
242
|
+
throw new Error(`Expected no audit entry matching ${formatAuditExpectation(expectation)}, but found ${entry.action}.`);
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Find the first recorded event matching the expected fields.
|
|
246
|
+
*
|
|
247
|
+
* @param events - Events captured by `createRecordingEventBus(...)`.
|
|
248
|
+
* @param expectation - Partial event fields to match.
|
|
249
|
+
* @returns The first matching event, or `undefined`.
|
|
250
|
+
*/
|
|
251
|
+
export function findRecordedEvent(events, expectation) {
|
|
252
|
+
return events.find((event) => recordedEventMatches(event, expectation));
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Assert that a recorded event exists and return the matching event.
|
|
256
|
+
*
|
|
257
|
+
* @param events - Events captured by `createRecordingEventBus(...)`.
|
|
258
|
+
* @param expectation - Partial event fields to match.
|
|
259
|
+
* @returns The matching event.
|
|
260
|
+
* @throws Error when no event matches.
|
|
261
|
+
*/
|
|
262
|
+
export function assertRecordedEvent(events, expectation) {
|
|
263
|
+
const event = findRecordedEvent(events, expectation);
|
|
264
|
+
if (event)
|
|
265
|
+
return event;
|
|
266
|
+
throw new Error(`Expected recorded event matching ${formatTestingExpectation(expectation)}, but only found ${events.length} ${pluralize("event", events.length)}.`);
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Assert that no recorded event matches the expected fields.
|
|
270
|
+
*
|
|
271
|
+
* @param events - Events captured by `createRecordingEventBus(...)`.
|
|
272
|
+
* @param expectation - Partial event fields to reject.
|
|
273
|
+
* @throws Error when a matching event exists.
|
|
274
|
+
*/
|
|
275
|
+
export function assertNoRecordedEvent(events, expectation) {
|
|
276
|
+
const event = findRecordedEvent(events, expectation);
|
|
277
|
+
if (!event)
|
|
278
|
+
return;
|
|
279
|
+
throw new Error(`Expected no recorded event matching ${formatTestingExpectation(expectation)}, but found ${event.name}.`);
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Find the first recorded job dispatch matching the expected fields.
|
|
283
|
+
*
|
|
284
|
+
* @param jobs - Job dispatches captured by `createRecordingJobDispatcher(...)`.
|
|
285
|
+
* @param expectation - Partial job fields to match.
|
|
286
|
+
* @returns The first matching dispatch, or `undefined`.
|
|
287
|
+
*/
|
|
288
|
+
export function findDispatchedJob(jobs, expectation) {
|
|
289
|
+
return jobs.find((job) => recordedJobMatches(job, expectation));
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Assert that a job was dispatched and return the matching dispatch.
|
|
293
|
+
*
|
|
294
|
+
* @param jobs - Job dispatches captured by `createRecordingJobDispatcher(...)`.
|
|
295
|
+
* @param expectation - Partial job fields to match.
|
|
296
|
+
* @returns The matching dispatch.
|
|
297
|
+
* @throws Error when no dispatch matches.
|
|
298
|
+
*/
|
|
299
|
+
export function assertDispatchedJob(jobs, expectation) {
|
|
300
|
+
const job = findDispatchedJob(jobs, expectation);
|
|
301
|
+
if (job)
|
|
302
|
+
return job;
|
|
303
|
+
throw new Error(`Expected dispatched job matching ${formatTestingExpectation(expectation)}, but only found ${jobs.length} ${pluralize("job", jobs.length)}.`);
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Assert that no job dispatch matches the expected fields.
|
|
307
|
+
*
|
|
308
|
+
* @param jobs - Job dispatches captured by `createRecordingJobDispatcher(...)`.
|
|
309
|
+
* @param expectation - Partial job fields to reject.
|
|
310
|
+
* @throws Error when a matching dispatch exists.
|
|
311
|
+
*/
|
|
312
|
+
export function assertNoDispatchedJob(jobs, expectation) {
|
|
313
|
+
const job = findDispatchedJob(jobs, expectation);
|
|
314
|
+
if (!job)
|
|
315
|
+
return;
|
|
316
|
+
throw new Error(`Expected no dispatched job matching ${formatTestingExpectation(expectation)}, but found ${job.name}.`);
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Find the first recorded schedule run matching the expected fields.
|
|
320
|
+
*
|
|
321
|
+
* @param runs - Runs captured by `createRecordingScheduleRunner(...)`.
|
|
322
|
+
* @param expectation - Partial schedule fields to match.
|
|
323
|
+
* @returns The first matching schedule run, or `undefined`.
|
|
324
|
+
*/
|
|
325
|
+
export function findScheduleRun(runs, expectation) {
|
|
326
|
+
return runs.find((run) => recordedScheduleRunMatches(run, expectation));
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Find the first provider instrumentation event matching the expected fields.
|
|
330
|
+
*
|
|
331
|
+
* @param source - Recording instrumentation result or event snapshot array.
|
|
332
|
+
* @param expectation - Partial event fields to match.
|
|
333
|
+
* @returns The first matching event, or `undefined`.
|
|
334
|
+
*/
|
|
335
|
+
export function findProviderInstrumentationEvent(source, expectation) {
|
|
336
|
+
return getProviderInstrumentationEvents(source).find((event) => providerInstrumentationEventMatches(event, expectation));
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Assert that a provider instrumentation event exists and return it.
|
|
340
|
+
*
|
|
341
|
+
* @param source - Recording instrumentation result or event snapshot array.
|
|
342
|
+
* @param expectation - Partial event fields to match.
|
|
343
|
+
* @returns The matching event.
|
|
344
|
+
* @throws Error when no event matches.
|
|
345
|
+
*/
|
|
346
|
+
export function assertProviderInstrumentationEvent(source, expectation) {
|
|
347
|
+
const event = findProviderInstrumentationEvent(source, expectation);
|
|
348
|
+
if (event)
|
|
349
|
+
return event;
|
|
350
|
+
const events = getProviderInstrumentationEvents(source);
|
|
351
|
+
throw new Error(`Expected provider instrumentation event matching ${formatTestingExpectation(expectation)}, but only found ${events.length} ${pluralize("event", events.length)}.`);
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Assert that no provider instrumentation event matches the expected fields.
|
|
355
|
+
*
|
|
356
|
+
* @param source - Recording instrumentation result or event snapshot array.
|
|
357
|
+
* @param expectation - Partial event fields to reject.
|
|
358
|
+
* @throws Error when a matching event exists.
|
|
359
|
+
*/
|
|
360
|
+
export function assertNoProviderInstrumentationEvent(source, expectation) {
|
|
361
|
+
const event = findProviderInstrumentationEvent(source, expectation);
|
|
362
|
+
if (!event)
|
|
363
|
+
return;
|
|
364
|
+
throw new Error(`Expected no provider instrumentation event matching ${formatTestingExpectation(expectation)}, but found ${event.type}.`);
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Assert that a schedule run was recorded and return the matching run.
|
|
368
|
+
*
|
|
369
|
+
* @param runs - Runs captured by `createRecordingScheduleRunner(...)`.
|
|
370
|
+
* @param expectation - Partial schedule fields to match.
|
|
371
|
+
* @returns The matching schedule run.
|
|
372
|
+
* @throws Error when no run matches.
|
|
373
|
+
*/
|
|
374
|
+
export function assertScheduleRun(runs, expectation) {
|
|
375
|
+
const run = findScheduleRun(runs, expectation);
|
|
376
|
+
if (run)
|
|
377
|
+
return run;
|
|
378
|
+
throw new Error(`Expected schedule run matching ${formatTestingExpectation(expectation)}, but only found ${runs.length} ${pluralize("run", runs.length)}.`);
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Assert that no schedule run matches the expected fields.
|
|
382
|
+
*
|
|
383
|
+
* @param runs - Runs captured by `createRecordingScheduleRunner(...)`.
|
|
384
|
+
* @param expectation - Partial schedule fields to reject.
|
|
385
|
+
* @throws Error when a matching run exists.
|
|
386
|
+
*/
|
|
387
|
+
export function assertNoScheduleRun(runs, expectation) {
|
|
388
|
+
const run = findScheduleRun(runs, expectation);
|
|
389
|
+
if (!run)
|
|
390
|
+
return;
|
|
391
|
+
throw new Error(`Expected no schedule run matching ${formatTestingExpectation(expectation)}, but found ${run.name}.`);
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Find the first mail delivery matching the expected fields.
|
|
395
|
+
*
|
|
396
|
+
* @param deliveries - Deliveries captured by `createMemoryMailer(...)`.
|
|
397
|
+
* @param expectation - Partial delivery or message fields to match.
|
|
398
|
+
* @returns The first matching delivery, or `undefined`.
|
|
399
|
+
*/
|
|
400
|
+
export function findMailDelivery(deliveries, expectation) {
|
|
401
|
+
return deliveries.find((delivery) => mailDeliveryMatches(delivery, expectation));
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Assert that a mail delivery exists and return the matching delivery.
|
|
405
|
+
*
|
|
406
|
+
* @param deliveries - Deliveries captured by `createMemoryMailer(...)`.
|
|
407
|
+
* @param expectation - Partial delivery or message fields to match.
|
|
408
|
+
* @returns The matching delivery.
|
|
409
|
+
* @throws Error when no delivery matches.
|
|
410
|
+
*/
|
|
411
|
+
export function assertMailDelivery(deliveries, expectation) {
|
|
412
|
+
const delivery = findMailDelivery(deliveries, expectation);
|
|
413
|
+
if (delivery)
|
|
414
|
+
return delivery;
|
|
415
|
+
throw new Error(`Expected mail delivery matching ${formatTestingExpectation(expectation)}, but only found ${deliveries.length} ${pluralize("delivery", deliveries.length)}.`);
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Assert that no mail delivery matches the expected fields.
|
|
419
|
+
*
|
|
420
|
+
* @param deliveries - Deliveries captured by `createMemoryMailer(...)`.
|
|
421
|
+
* @param expectation - Partial delivery or message fields to reject.
|
|
422
|
+
* @throws Error when a matching delivery exists.
|
|
423
|
+
*/
|
|
424
|
+
export function assertNoMailDelivery(deliveries, expectation) {
|
|
425
|
+
const delivery = findMailDelivery(deliveries, expectation);
|
|
426
|
+
if (!delivery)
|
|
427
|
+
return;
|
|
428
|
+
throw new Error(`Expected no mail delivery matching ${formatTestingExpectation(expectation)}, but found ${delivery.message.subject}.`);
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Find the first notification delivery matching the expected fields.
|
|
432
|
+
*
|
|
433
|
+
* @param deliveries - Deliveries captured by `createMemoryNotificationPort(...)`.
|
|
434
|
+
* @param expectation - Partial delivery fields to match.
|
|
435
|
+
* @returns The first matching delivery, or `undefined`.
|
|
436
|
+
*/
|
|
437
|
+
export function findNotificationDelivery(deliveries, expectation) {
|
|
438
|
+
return deliveries.find((delivery) => notificationDeliveryMatches(delivery, expectation));
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Assert that a notification delivery exists and return the matching delivery.
|
|
442
|
+
*
|
|
443
|
+
* @param deliveries - Deliveries captured by `createMemoryNotificationPort(...)`.
|
|
444
|
+
* @param expectation - Partial delivery fields to match.
|
|
445
|
+
* @returns The matching delivery.
|
|
446
|
+
* @throws Error when no delivery matches.
|
|
447
|
+
*/
|
|
448
|
+
export function assertNotificationDelivery(deliveries, expectation) {
|
|
449
|
+
const delivery = findNotificationDelivery(deliveries, expectation);
|
|
450
|
+
if (delivery)
|
|
451
|
+
return delivery;
|
|
452
|
+
throw new Error(`Expected notification delivery matching ${formatTestingExpectation(expectation)}, but only found ${deliveries.length} ${pluralize("delivery", deliveries.length)}.`);
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Assert that no notification delivery matches the expected fields.
|
|
456
|
+
*
|
|
457
|
+
* @param deliveries - Deliveries captured by `createMemoryNotificationPort(...)`.
|
|
458
|
+
* @param expectation - Partial delivery fields to reject.
|
|
459
|
+
* @throws Error when a matching delivery exists.
|
|
460
|
+
*/
|
|
461
|
+
export function assertNoNotificationDelivery(deliveries, expectation) {
|
|
462
|
+
const delivery = findNotificationDelivery(deliveries, expectation);
|
|
463
|
+
if (!delivery)
|
|
464
|
+
return;
|
|
465
|
+
throw new Error(`Expected no notification delivery matching ${formatTestingExpectation(expectation)}, but found ${delivery.notificationName}.`);
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Assert that a storage object exists and optionally matches metadata/body
|
|
469
|
+
* expectations.
|
|
470
|
+
*
|
|
471
|
+
* This helper works against any `StoragePort`, not only memory storage.
|
|
472
|
+
*
|
|
473
|
+
* @param storage - Storage port under test.
|
|
474
|
+
* @param expectation - Object key and expected fields.
|
|
475
|
+
* @returns The matching object metadata.
|
|
476
|
+
* @throws Error when the object is missing or does not match.
|
|
477
|
+
*/
|
|
478
|
+
export async function assertStorageObject(storage, expectation) {
|
|
479
|
+
if (expectation.text !== undefined && expectation.bytes !== undefined) {
|
|
480
|
+
throw new Error(`Expected storage object "${expectation.key}" to check either text or bytes, not both.`);
|
|
481
|
+
}
|
|
482
|
+
const object = expectation.text !== undefined || expectation.bytes !== undefined
|
|
483
|
+
? await storage.get(expectation.key)
|
|
484
|
+
: await storage.stat(expectation.key);
|
|
485
|
+
if (!object) {
|
|
486
|
+
throw new Error(`Expected storage object "${expectation.key}" to exist.`);
|
|
487
|
+
}
|
|
488
|
+
if (!storageObjectMatches(object, expectation)) {
|
|
489
|
+
throw new Error(`Expected storage object "${expectation.key}" to match ${formatTestingExpectation(omitStorageBodyExpectation(expectation))}.`);
|
|
490
|
+
}
|
|
491
|
+
if (expectation.text !== undefined) {
|
|
492
|
+
const actualText = hasStorageTextReader(object)
|
|
493
|
+
? await object.text()
|
|
494
|
+
: undefined;
|
|
495
|
+
if (actualText !== expectation.text) {
|
|
496
|
+
throw new Error(`Expected storage object "${expectation.key}" text to match ${JSON.stringify(expectation.text)}.`);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
else if (expectation.bytes !== undefined) {
|
|
500
|
+
const actualBytes = hasStorageBytesReader(object)
|
|
501
|
+
? await object.bytes()
|
|
502
|
+
: undefined;
|
|
503
|
+
if (!uint8ArrayMatches(actualBytes, expectation.bytes)) {
|
|
504
|
+
throw new Error(`Expected storage object "${expectation.key}" bytes to match.`);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
return object;
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Assert that a storage object does not exist.
|
|
511
|
+
*
|
|
512
|
+
* @param storage - Storage port under test.
|
|
513
|
+
* @param key - Object key expected to be absent.
|
|
514
|
+
* @throws Error when the object exists.
|
|
515
|
+
*/
|
|
516
|
+
export async function assertNoStorageObject(storage, key) {
|
|
517
|
+
const exists = await storage.exists(key);
|
|
518
|
+
if (!exists)
|
|
519
|
+
return;
|
|
520
|
+
throw new Error(`Expected storage object "${key}" not to exist.`);
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Find the first outbox message matching expected fields.
|
|
524
|
+
*
|
|
525
|
+
* @param source - Memory outbox or message snapshot array.
|
|
526
|
+
* @param expectation - Partial message fields to match.
|
|
527
|
+
* @returns The first matching message, or `undefined`.
|
|
528
|
+
*/
|
|
529
|
+
export function findOutboxMessage(source, expectation) {
|
|
530
|
+
return getOutboxMessages(source).find((message) => outboxMessageMatches(message, expectation));
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* Find the first idempotency entry matching expected fields.
|
|
534
|
+
*
|
|
535
|
+
* @param source - Memory idempotency store or entry snapshot array.
|
|
536
|
+
* @param expectation - Partial entry fields to match.
|
|
537
|
+
* @returns The first matching entry, or `undefined`.
|
|
538
|
+
*/
|
|
539
|
+
export function findIdempotencyEntry(source, expectation) {
|
|
540
|
+
return getIdempotencyEntries(source).find((entry) => idempotencyEntryMatches(entry, expectation));
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Assert that an idempotency entry exists and return it.
|
|
544
|
+
*
|
|
545
|
+
* @param source - Memory idempotency store or entry snapshot array.
|
|
546
|
+
* @param expectation - Partial entry fields to match.
|
|
547
|
+
* @returns The matching entry.
|
|
548
|
+
* @throws Error when no entry matches.
|
|
549
|
+
*/
|
|
550
|
+
export function assertIdempotencyEntry(source, expectation) {
|
|
551
|
+
const entry = findIdempotencyEntry(source, expectation);
|
|
552
|
+
if (entry)
|
|
553
|
+
return entry;
|
|
554
|
+
const entries = getIdempotencyEntries(source);
|
|
555
|
+
throw new Error(`Expected idempotency entry matching ${formatTestingExpectation(expectation)}, but only found ${entries.length} ${pluralize("entry", entries.length)}.`);
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Assert that no idempotency entry matches expected fields.
|
|
559
|
+
*
|
|
560
|
+
* @param source - Memory idempotency store or entry snapshot array.
|
|
561
|
+
* @param expectation - Partial entry fields to reject.
|
|
562
|
+
* @throws Error when a matching entry exists.
|
|
563
|
+
*/
|
|
564
|
+
export function assertNoIdempotencyEntry(source, expectation) {
|
|
565
|
+
const entry = findIdempotencyEntry(source, expectation);
|
|
566
|
+
if (!entry)
|
|
567
|
+
return;
|
|
568
|
+
throw new Error(`Expected no idempotency entry matching ${formatTestingExpectation(expectation)}, but found ${entry.namespace}:${entry.key}.`);
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Assert that a matching idempotency entry is still in progress.
|
|
572
|
+
*
|
|
573
|
+
* @param source - Memory idempotency store or entry snapshot array.
|
|
574
|
+
* @param expectation - Entry fields to match.
|
|
575
|
+
* @returns The matching in-progress entry.
|
|
576
|
+
*/
|
|
577
|
+
export function assertIdempotencyInProgress(source, expectation = {}) {
|
|
578
|
+
return assertIdempotencyEntry(source, {
|
|
579
|
+
...expectation,
|
|
580
|
+
status: "in-progress",
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Assert that a matching idempotency entry completed.
|
|
585
|
+
*
|
|
586
|
+
* @param source - Memory idempotency store or entry snapshot array.
|
|
587
|
+
* @param expectation - Entry fields to match.
|
|
588
|
+
* @returns The matching completed entry.
|
|
589
|
+
*/
|
|
590
|
+
export function assertIdempotencyCompleted(source, expectation = {}) {
|
|
591
|
+
return assertIdempotencyEntry(source, {
|
|
592
|
+
...expectation,
|
|
593
|
+
status: "completed",
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Assert that an outbox message exists and return it.
|
|
598
|
+
*
|
|
599
|
+
* @param source - Memory outbox or message snapshot array.
|
|
600
|
+
* @param expectation - Partial message fields to match.
|
|
601
|
+
* @returns The matching message.
|
|
602
|
+
* @throws Error when no message matches.
|
|
603
|
+
*/
|
|
604
|
+
export function assertOutboxMessage(source, expectation) {
|
|
605
|
+
const message = findOutboxMessage(source, expectation);
|
|
606
|
+
if (message)
|
|
607
|
+
return message;
|
|
608
|
+
const messages = getOutboxMessages(source);
|
|
609
|
+
throw new Error(`Expected outbox message matching ${formatTestingExpectation(expectation)}, but only found ${messages.length} ${pluralize("message", messages.length)}.`);
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Assert that no outbox message matches expected fields.
|
|
613
|
+
*
|
|
614
|
+
* @param source - Memory outbox or message snapshot array.
|
|
615
|
+
* @param expectation - Partial message fields to reject.
|
|
616
|
+
* @throws Error when a matching message exists.
|
|
617
|
+
*/
|
|
618
|
+
export function assertNoOutboxMessage(source, expectation) {
|
|
619
|
+
const message = findOutboxMessage(source, expectation);
|
|
620
|
+
if (!message)
|
|
621
|
+
return;
|
|
622
|
+
throw new Error(`Expected no outbox message matching ${formatTestingExpectation(expectation)}, but found ${message.kind} "${message.name}".`);
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Assert that an outbox message is pending.
|
|
626
|
+
*
|
|
627
|
+
* @param source - Memory outbox or message snapshot array.
|
|
628
|
+
* @param expectation - Message fields to match.
|
|
629
|
+
* @returns The matching pending message.
|
|
630
|
+
*/
|
|
631
|
+
export function assertOutboxPending(source, expectation = {}) {
|
|
632
|
+
return assertOutboxMessage(source, {
|
|
633
|
+
...expectation,
|
|
634
|
+
status: "pending",
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Assert that an outbox message was delivered.
|
|
639
|
+
*
|
|
640
|
+
* @param source - Memory outbox or message snapshot array.
|
|
641
|
+
* @param expectation - Message fields to match.
|
|
642
|
+
* @returns The matching delivered message.
|
|
643
|
+
*/
|
|
644
|
+
export function assertOutboxDelivered(source, expectation = {}) {
|
|
645
|
+
return assertOutboxMessage(source, {
|
|
646
|
+
...expectation,
|
|
647
|
+
status: "delivered",
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Assert that an outbox message is pending after at least one failed attempt.
|
|
652
|
+
*
|
|
653
|
+
* Use this for retry-scheduled assertions after `drainOutbox(...)` returns a
|
|
654
|
+
* retried count.
|
|
655
|
+
*
|
|
656
|
+
* @param source - Memory outbox or message snapshot array.
|
|
657
|
+
* @param expectation - Message fields to match.
|
|
658
|
+
* @returns The matching retry-scheduled message.
|
|
659
|
+
*/
|
|
660
|
+
export function assertOutboxRetryScheduled(source, expectation = {}) {
|
|
661
|
+
const message = assertOutboxMessage(source, {
|
|
662
|
+
...expectation,
|
|
663
|
+
status: "pending",
|
|
664
|
+
});
|
|
665
|
+
if (message.attempts < 1 || !message.lastError) {
|
|
666
|
+
throw new Error(`Expected outbox message "${message.id}" to have a recorded failed attempt.`);
|
|
667
|
+
}
|
|
668
|
+
return message;
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Assert that an outbox message was dead-lettered.
|
|
672
|
+
*
|
|
673
|
+
* @param source - Memory outbox or message snapshot array.
|
|
674
|
+
* @param expectation - Message fields to match.
|
|
675
|
+
* @returns The matching dead-lettered message.
|
|
676
|
+
*/
|
|
677
|
+
export function assertOutboxDeadLettered(source, expectation = {}) {
|
|
678
|
+
return assertOutboxMessage(source, {
|
|
679
|
+
...expectation,
|
|
680
|
+
status: "deadLettered",
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
/**
|
|
684
|
+
* Assert that a drain result matches expected counts.
|
|
685
|
+
*
|
|
686
|
+
* @param result - Result returned by `drainOutbox(...)`.
|
|
687
|
+
* @param expectation - Partial count expectation.
|
|
688
|
+
* @throws Error when any supplied count differs.
|
|
689
|
+
*/
|
|
690
|
+
export function assertOutboxDrainResult(result, expectation) {
|
|
691
|
+
if (!outboxDrainResultMatches(result, expectation)) {
|
|
692
|
+
throw new Error(`Expected outbox drain result matching ${formatTestingExpectation(expectation)}, but received ${formatTestingExpectation(result)}.`);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
33
695
|
/**
|
|
34
696
|
* Create a policy tester from the same options used by `createGate(...)`.
|
|
35
697
|
*
|
|
@@ -125,4 +787,372 @@ function policyMatrixFailureMessage(matrixCase, decision) {
|
|
|
125
787
|
.join(", ");
|
|
126
788
|
return `${matrixCase.name}: expected ${expected.join(", ")}, received ${actual}.`;
|
|
127
789
|
}
|
|
790
|
+
function mergeRoleMetadata(metadata, role) {
|
|
791
|
+
if (!role)
|
|
792
|
+
return metadata;
|
|
793
|
+
return {
|
|
794
|
+
...metadata,
|
|
795
|
+
role,
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
function auditEntryMatches(entry, expectation) {
|
|
799
|
+
if (expectation.action !== undefined && entry.action !== expectation.action) {
|
|
800
|
+
return false;
|
|
801
|
+
}
|
|
802
|
+
if (expectation.outcome !== undefined &&
|
|
803
|
+
entry.outcome !== expectation.outcome) {
|
|
804
|
+
return false;
|
|
805
|
+
}
|
|
806
|
+
if (expectation.requestId !== undefined &&
|
|
807
|
+
entry.requestId !== expectation.requestId) {
|
|
808
|
+
return false;
|
|
809
|
+
}
|
|
810
|
+
if (expectation.traceId !== undefined &&
|
|
811
|
+
entry.traceId !== expectation.traceId) {
|
|
812
|
+
return false;
|
|
813
|
+
}
|
|
814
|
+
if (expectation.actorId !== undefined &&
|
|
815
|
+
entry.actor.id !== expectation.actorId) {
|
|
816
|
+
return false;
|
|
817
|
+
}
|
|
818
|
+
if (expectation.actorType !== undefined &&
|
|
819
|
+
entry.actor.type !== expectation.actorType) {
|
|
820
|
+
return false;
|
|
821
|
+
}
|
|
822
|
+
if (expectation.tenantId !== undefined &&
|
|
823
|
+
entry.tenant?.id !== expectation.tenantId) {
|
|
824
|
+
return false;
|
|
825
|
+
}
|
|
826
|
+
if (expectation.resourceId !== undefined &&
|
|
827
|
+
entry.resource?.id !== expectation.resourceId) {
|
|
828
|
+
return false;
|
|
829
|
+
}
|
|
830
|
+
if (expectation.resourceType !== undefined &&
|
|
831
|
+
entry.resource?.type !== expectation.resourceType) {
|
|
832
|
+
return false;
|
|
833
|
+
}
|
|
834
|
+
if (expectation.severity !== undefined &&
|
|
835
|
+
entry.metadata?.severity !== expectation.severity) {
|
|
836
|
+
return false;
|
|
837
|
+
}
|
|
838
|
+
return (partialObjectMatches(entry.actor, expectation.actor) &&
|
|
839
|
+
partialObjectMatches(entry.tenant, expectation.tenant) &&
|
|
840
|
+
partialObjectMatches(entry.resource, expectation.resource) &&
|
|
841
|
+
partialObjectMatches(entry.metadata, expectation.metadata));
|
|
842
|
+
}
|
|
843
|
+
function recordedEventMatches(event, expectation) {
|
|
844
|
+
if (expectation.name !== undefined && event.name !== expectation.name) {
|
|
845
|
+
return false;
|
|
846
|
+
}
|
|
847
|
+
return expectation.payload === undefined
|
|
848
|
+
? true
|
|
849
|
+
: valueMatches(event.payload, expectation.payload);
|
|
850
|
+
}
|
|
851
|
+
function recordedJobMatches(job, expectation) {
|
|
852
|
+
if (expectation.name !== undefined && job.name !== expectation.name) {
|
|
853
|
+
return false;
|
|
854
|
+
}
|
|
855
|
+
return expectation.payload === undefined
|
|
856
|
+
? true
|
|
857
|
+
: valueMatches(job.payload, expectation.payload);
|
|
858
|
+
}
|
|
859
|
+
function recordedScheduleRunMatches(run, expectation) {
|
|
860
|
+
if (expectation.name !== undefined && run.name !== expectation.name) {
|
|
861
|
+
return false;
|
|
862
|
+
}
|
|
863
|
+
if (expectation.id !== undefined && run.id !== expectation.id) {
|
|
864
|
+
return false;
|
|
865
|
+
}
|
|
866
|
+
if (expectation.source !== undefined && run.source !== expectation.source) {
|
|
867
|
+
return false;
|
|
868
|
+
}
|
|
869
|
+
return expectation.payload === undefined
|
|
870
|
+
? true
|
|
871
|
+
: valueMatches(run.payload, expectation.payload);
|
|
872
|
+
}
|
|
873
|
+
function getProviderInstrumentationEvents(source) {
|
|
874
|
+
return "events" in source ? source.events : source;
|
|
875
|
+
}
|
|
876
|
+
function providerInstrumentationEventMatches(event, expectation) {
|
|
877
|
+
const eventRecord = event;
|
|
878
|
+
const expectationRecord = expectation;
|
|
879
|
+
const directKeys = [
|
|
880
|
+
"type",
|
|
881
|
+
"id",
|
|
882
|
+
"timestamp",
|
|
883
|
+
"requestId",
|
|
884
|
+
"traceId",
|
|
885
|
+
"spanId",
|
|
886
|
+
"parentSpanId",
|
|
887
|
+
"traceparent",
|
|
888
|
+
"watcher",
|
|
889
|
+
"method",
|
|
890
|
+
"path",
|
|
891
|
+
"contractName",
|
|
892
|
+
"status",
|
|
893
|
+
"durationMs",
|
|
894
|
+
"summary",
|
|
895
|
+
"message",
|
|
896
|
+
"stack",
|
|
897
|
+
"useCaseName",
|
|
898
|
+
"name",
|
|
899
|
+
"kind",
|
|
900
|
+
"phase",
|
|
901
|
+
"error",
|
|
902
|
+
"eventName",
|
|
903
|
+
"jobName",
|
|
904
|
+
"messageId",
|
|
905
|
+
"messageKind",
|
|
906
|
+
"messageName",
|
|
907
|
+
"scheduleName",
|
|
908
|
+
"cron",
|
|
909
|
+
"timezone",
|
|
910
|
+
"action",
|
|
911
|
+
"label",
|
|
912
|
+
];
|
|
913
|
+
for (const key of directKeys) {
|
|
914
|
+
if (Object.hasOwn(expectation, key) &&
|
|
915
|
+
!valueMatches(eventRecord[key], expectationRecord[key])) {
|
|
916
|
+
return false;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
if (expectation.providerName !== undefined &&
|
|
920
|
+
providerNameFromInstrumentationEvent(event) !== expectation.providerName) {
|
|
921
|
+
return false;
|
|
922
|
+
}
|
|
923
|
+
return expectation.details === undefined
|
|
924
|
+
? true
|
|
925
|
+
: valueMatches(event.details, expectation.details);
|
|
926
|
+
}
|
|
927
|
+
function providerNameFromInstrumentationEvent(event) {
|
|
928
|
+
if (event.type === "provider")
|
|
929
|
+
return event.providerName;
|
|
930
|
+
if (!event.details || typeof event.details !== "object")
|
|
931
|
+
return undefined;
|
|
932
|
+
return event.details.providerName;
|
|
933
|
+
}
|
|
934
|
+
function mailDeliveryMatches(delivery, expectation) {
|
|
935
|
+
if (expectation.id !== undefined && delivery.id !== expectation.id) {
|
|
936
|
+
return false;
|
|
937
|
+
}
|
|
938
|
+
if (expectation.subject !== undefined &&
|
|
939
|
+
delivery.message.subject !== expectation.subject) {
|
|
940
|
+
return false;
|
|
941
|
+
}
|
|
942
|
+
if (expectation.to !== undefined &&
|
|
943
|
+
!valueMatches(delivery.message.to, expectation.to)) {
|
|
944
|
+
return false;
|
|
945
|
+
}
|
|
946
|
+
if (expectation.from !== undefined &&
|
|
947
|
+
!valueMatches(delivery.message.from, expectation.from)) {
|
|
948
|
+
return false;
|
|
949
|
+
}
|
|
950
|
+
if (expectation.text !== undefined &&
|
|
951
|
+
delivery.message.text !== expectation.text) {
|
|
952
|
+
return false;
|
|
953
|
+
}
|
|
954
|
+
if (expectation.html !== undefined &&
|
|
955
|
+
delivery.message.html !== expectation.html) {
|
|
956
|
+
return false;
|
|
957
|
+
}
|
|
958
|
+
return (partialObjectMatches(delivery.message.headers, expectation.headers) &&
|
|
959
|
+
partialObjectMatches(delivery.message, expectation.message));
|
|
960
|
+
}
|
|
961
|
+
function notificationDeliveryMatches(delivery, expectation) {
|
|
962
|
+
if (expectation.id !== undefined && delivery.id !== expectation.id) {
|
|
963
|
+
return false;
|
|
964
|
+
}
|
|
965
|
+
if (expectation.notificationName !== undefined &&
|
|
966
|
+
delivery.notificationName !== expectation.notificationName) {
|
|
967
|
+
return false;
|
|
968
|
+
}
|
|
969
|
+
if (expectation.channels !== undefined &&
|
|
970
|
+
!valueMatches(delivery.channels, expectation.channels)) {
|
|
971
|
+
return false;
|
|
972
|
+
}
|
|
973
|
+
if (expectation.payload !== undefined &&
|
|
974
|
+
!valueMatches(delivery.payload, expectation.payload)) {
|
|
975
|
+
return false;
|
|
976
|
+
}
|
|
977
|
+
return partialObjectMatches(delivery.metadata, expectation.metadata);
|
|
978
|
+
}
|
|
979
|
+
function storageObjectMatches(object, expectation) {
|
|
980
|
+
if (object.key !== expectation.key)
|
|
981
|
+
return false;
|
|
982
|
+
if (expectation.size !== undefined && object.size !== expectation.size) {
|
|
983
|
+
return false;
|
|
984
|
+
}
|
|
985
|
+
if (expectation.contentType !== undefined &&
|
|
986
|
+
object.contentType !== expectation.contentType) {
|
|
987
|
+
return false;
|
|
988
|
+
}
|
|
989
|
+
if (expectation.cacheControl !== undefined &&
|
|
990
|
+
object.cacheControl !== expectation.cacheControl) {
|
|
991
|
+
return false;
|
|
992
|
+
}
|
|
993
|
+
if (expectation.visibility !== undefined &&
|
|
994
|
+
object.visibility !== expectation.visibility) {
|
|
995
|
+
return false;
|
|
996
|
+
}
|
|
997
|
+
return partialObjectMatches(object.metadata, expectation.metadata);
|
|
998
|
+
}
|
|
999
|
+
function getOutboxMessages(source) {
|
|
1000
|
+
return "messages" in source ? source.messages : source;
|
|
1001
|
+
}
|
|
1002
|
+
function getIdempotencyEntries(source) {
|
|
1003
|
+
if (Array.isArray(source))
|
|
1004
|
+
return source;
|
|
1005
|
+
return source.entries;
|
|
1006
|
+
}
|
|
1007
|
+
function idempotencyEntryMatches(entry, expectation) {
|
|
1008
|
+
if (expectation.namespace !== undefined &&
|
|
1009
|
+
entry.namespace !== expectation.namespace) {
|
|
1010
|
+
return false;
|
|
1011
|
+
}
|
|
1012
|
+
if (expectation.key !== undefined && entry.key !== expectation.key) {
|
|
1013
|
+
return false;
|
|
1014
|
+
}
|
|
1015
|
+
if (expectation.scopeKey !== undefined &&
|
|
1016
|
+
entry.scopeKey !== expectation.scopeKey) {
|
|
1017
|
+
return false;
|
|
1018
|
+
}
|
|
1019
|
+
if (expectation.fingerprint !== undefined &&
|
|
1020
|
+
entry.fingerprint !== expectation.fingerprint) {
|
|
1021
|
+
return false;
|
|
1022
|
+
}
|
|
1023
|
+
if (expectation.status !== undefined && entry.status !== expectation.status) {
|
|
1024
|
+
return false;
|
|
1025
|
+
}
|
|
1026
|
+
if (expectation.result !== undefined &&
|
|
1027
|
+
!valueMatches(entry.result, expectation.result)) {
|
|
1028
|
+
return false;
|
|
1029
|
+
}
|
|
1030
|
+
if (expectation.reservedAt !== undefined &&
|
|
1031
|
+
!valueMatches(entry.reservedAt, expectation.reservedAt)) {
|
|
1032
|
+
return false;
|
|
1033
|
+
}
|
|
1034
|
+
if (expectation.completedAt !== undefined &&
|
|
1035
|
+
!valueMatches(entry.completedAt, expectation.completedAt)) {
|
|
1036
|
+
return false;
|
|
1037
|
+
}
|
|
1038
|
+
if (expectation.expiresAt !== undefined &&
|
|
1039
|
+
!valueMatches(entry.expiresAt, expectation.expiresAt)) {
|
|
1040
|
+
return false;
|
|
1041
|
+
}
|
|
1042
|
+
return true;
|
|
1043
|
+
}
|
|
1044
|
+
function outboxMessageMatches(message, expectation) {
|
|
1045
|
+
if (expectation.id !== undefined && message.id !== expectation.id) {
|
|
1046
|
+
return false;
|
|
1047
|
+
}
|
|
1048
|
+
if (expectation.kind !== undefined && message.kind !== expectation.kind) {
|
|
1049
|
+
return false;
|
|
1050
|
+
}
|
|
1051
|
+
if (expectation.name !== undefined && message.name !== expectation.name) {
|
|
1052
|
+
return false;
|
|
1053
|
+
}
|
|
1054
|
+
if (expectation.status !== undefined &&
|
|
1055
|
+
message.status !== expectation.status) {
|
|
1056
|
+
return false;
|
|
1057
|
+
}
|
|
1058
|
+
if (expectation.attempts !== undefined &&
|
|
1059
|
+
message.attempts !== expectation.attempts) {
|
|
1060
|
+
return false;
|
|
1061
|
+
}
|
|
1062
|
+
if (expectation.maxAttempts !== undefined &&
|
|
1063
|
+
message.maxAttempts !== expectation.maxAttempts) {
|
|
1064
|
+
return false;
|
|
1065
|
+
}
|
|
1066
|
+
if (expectation.deliveredAt !== undefined &&
|
|
1067
|
+
!valueMatches(message.deliveredAt, expectation.deliveredAt)) {
|
|
1068
|
+
return false;
|
|
1069
|
+
}
|
|
1070
|
+
if (expectation.lastError !== undefined &&
|
|
1071
|
+
!valueMatches(message.lastError, expectation.lastError)) {
|
|
1072
|
+
return false;
|
|
1073
|
+
}
|
|
1074
|
+
return expectation.payload === undefined
|
|
1075
|
+
? true
|
|
1076
|
+
: valueMatches(message.payload, expectation.payload);
|
|
1077
|
+
}
|
|
1078
|
+
function outboxDrainResultMatches(result, expectation) {
|
|
1079
|
+
if (expectation.claimed !== undefined &&
|
|
1080
|
+
result.claimed !== expectation.claimed) {
|
|
1081
|
+
return false;
|
|
1082
|
+
}
|
|
1083
|
+
if (expectation.delivered !== undefined &&
|
|
1084
|
+
result.delivered !== expectation.delivered) {
|
|
1085
|
+
return false;
|
|
1086
|
+
}
|
|
1087
|
+
if (expectation.retried !== undefined &&
|
|
1088
|
+
result.retried !== expectation.retried) {
|
|
1089
|
+
return false;
|
|
1090
|
+
}
|
|
1091
|
+
if (expectation.deadLettered !== undefined &&
|
|
1092
|
+
result.deadLettered !== expectation.deadLettered) {
|
|
1093
|
+
return false;
|
|
1094
|
+
}
|
|
1095
|
+
return true;
|
|
1096
|
+
}
|
|
1097
|
+
function hasStorageTextReader(object) {
|
|
1098
|
+
return typeof object.text === "function";
|
|
1099
|
+
}
|
|
1100
|
+
function hasStorageBytesReader(object) {
|
|
1101
|
+
return typeof object.bytes === "function";
|
|
1102
|
+
}
|
|
1103
|
+
function omitStorageBodyExpectation(expectation) {
|
|
1104
|
+
const { bytes: _bytes, text: _text, ...metadataExpectation } = expectation;
|
|
1105
|
+
return metadataExpectation;
|
|
1106
|
+
}
|
|
1107
|
+
function partialObjectMatches(actual, expected) {
|
|
1108
|
+
if (!expected)
|
|
1109
|
+
return true;
|
|
1110
|
+
if (!actual || typeof actual !== "object")
|
|
1111
|
+
return false;
|
|
1112
|
+
const actualRecord = actual;
|
|
1113
|
+
for (const [key, expectedValue] of Object.entries(expected)) {
|
|
1114
|
+
if (!valueMatches(actualRecord[key], expectedValue))
|
|
1115
|
+
return false;
|
|
1116
|
+
}
|
|
1117
|
+
return true;
|
|
1118
|
+
}
|
|
1119
|
+
function valueMatches(actual, expected) {
|
|
1120
|
+
if (actual instanceof Date || expected instanceof Date) {
|
|
1121
|
+
return (actual instanceof Date &&
|
|
1122
|
+
expected instanceof Date &&
|
|
1123
|
+
actual.getTime() === expected.getTime());
|
|
1124
|
+
}
|
|
1125
|
+
if (actual instanceof Uint8Array || expected instanceof Uint8Array) {
|
|
1126
|
+
return uint8ArrayMatches(actual, expected);
|
|
1127
|
+
}
|
|
1128
|
+
if (expected &&
|
|
1129
|
+
typeof expected === "object" &&
|
|
1130
|
+
!Array.isArray(expected) &&
|
|
1131
|
+
actual &&
|
|
1132
|
+
typeof actual === "object" &&
|
|
1133
|
+
!Array.isArray(actual)) {
|
|
1134
|
+
return partialObjectMatches(actual, expected);
|
|
1135
|
+
}
|
|
1136
|
+
if (Array.isArray(expected) || Array.isArray(actual)) {
|
|
1137
|
+
return JSON.stringify(actual) === JSON.stringify(expected);
|
|
1138
|
+
}
|
|
1139
|
+
return Object.is(actual, expected);
|
|
1140
|
+
}
|
|
1141
|
+
function uint8ArrayMatches(actual, expected) {
|
|
1142
|
+
if (!(actual instanceof Uint8Array) || !(expected instanceof Uint8Array)) {
|
|
1143
|
+
return false;
|
|
1144
|
+
}
|
|
1145
|
+
if (actual.byteLength !== expected.byteLength)
|
|
1146
|
+
return false;
|
|
1147
|
+
return actual.every((byte, index) => byte === expected[index]);
|
|
1148
|
+
}
|
|
1149
|
+
function pluralize(word, count) {
|
|
1150
|
+
return count === 1 ? word : `${word}s`;
|
|
1151
|
+
}
|
|
1152
|
+
function formatTestingExpectation(expectation) {
|
|
1153
|
+
return JSON.stringify(expectation);
|
|
1154
|
+
}
|
|
1155
|
+
function formatAuditExpectation(expectation) {
|
|
1156
|
+
return formatTestingExpectation(expectation);
|
|
1157
|
+
}
|
|
128
1158
|
//# sourceMappingURL=testing.js.map
|