@beignet/core
Version:
Core framework primitives for Beignet
1,148 lines • 38.4 kB
TypeScript
import type { MemoryIdempotencyEntry, MemoryIdempotencyStore } from "../idempotency/index.js";
import type { MemoryMailDelivery, NormalizedMailMessage } from "../mail/index.js";
import type { MemoryNotificationDelivery } from "../notifications/index.js";
import type { DrainOutboxResult, OutboxErrorInfo, OutboxMessage, OutboxMessageKind, OutboxMessageStatus } from "../outbox/index.js";
import type { ProviderInstrumentationEventInput, ProviderInstrumentationPort } from "../providers/index.js";
import type { ScheduleDef, ScheduleRunnerPort, ScheduleRunOptions } from "../schedules/index.js";
import { type ActivityActor, type ActivityMetadata, type ActivityMetadataValue, type ActivityResource, type ActivityTenant, type AuditLogEntry, type AuditOutcome } from "./audit.js";
import type { EventBusPort, JobDef, JobDispatcherPort } from "./events.js";
import { type CreateGateOptions, type GateDecision, type GatePort, type PolicyContextFromDefinitions, type PolicyDefinition, type PolicyMapFromDefinitions, type PolicySubjectArgs } from "./policy.js";
import type { StorageObject, StoragePort, StorageVisibility } from "./storage.js";
/**
* A recorded event entry from the recording event bus.
*/
export interface RecordedEvent {
name: string;
payload: unknown;
}
/**
* Expected fields for a recorded event assertion.
*/
export interface RecordedEventExpectation {
/**
* Expected event name.
*/
name?: string;
/**
* Expected event payload. Object values are matched as partial objects.
*/
payload?: unknown;
}
/**
* Create a recording event bus for testing.
*
* This bus records all published events for later assertion,
* but does not support subscription (throws if called).
*
* @example
* ```ts
* const { bus, events } = createRecordingEventBus();
*
* // Inject bus into your use case
* await createUser({ ports: { eventBus: bus } });
*
* // Assert on recorded events
* expect(events).toHaveLength(1);
* expect(events[0].name).toBe("user.registered");
* expect(events[0].payload).toEqual({ userId: "123", email: "test@example.com" });
* ```
*/
export declare function createRecordingEventBus(): {
bus: EventBusPort;
events: RecordedEvent[];
};
/**
* A job dispatch captured by `createRecordingJobDispatcher(...)`.
*/
export interface RecordedJobDispatch {
/**
* Dispatched job name.
*/
name: string;
/**
* Job definition supplied to the dispatcher.
*/
job: JobDef;
/**
* Payload supplied to the dispatcher.
*/
payload: unknown;
}
/**
* Expected fields for a recorded job dispatch assertion.
*/
export interface RecordedJobDispatchExpectation {
/**
* Expected job name.
*/
name?: string;
/**
* Expected job payload. Object values are matched as partial objects.
*/
payload?: unknown;
}
/**
* Create a recording job dispatcher for tests.
*
* The dispatcher records dispatch intent without running the job handler. Use
* this when a use case or listener should enqueue work but the test does not
* need to execute that work inline.
*
* @returns A job dispatcher plus its captured dispatches.
*/
export declare function createRecordingJobDispatcher(): {
jobs: JobDispatcherPort;
dispatchedJobs: RecordedJobDispatch[];
};
/**
* A schedule run captured by `createRecordingScheduleRunner(...)`.
*/
export interface RecordedScheduleRun {
/**
* Schedule name.
*/
name: string;
/**
* Schedule definition supplied to the runner.
*/
schedule: ScheduleDef;
/**
* Payload supplied to the runner, when present.
*/
payload?: unknown;
/**
* Run ID supplied by the provider or test, when present.
*/
id?: string;
/**
* Provider or app source label, when present.
*/
source?: string;
/**
* Scheduled timestamp supplied to the runner, when present.
*/
scheduledAt?: ScheduleRunOptions["scheduledAt"];
/**
* Triggered timestamp supplied to the runner, when present.
*/
triggeredAt?: ScheduleRunOptions["triggeredAt"];
}
/**
* Expected fields for a recorded schedule run assertion.
*/
export interface RecordedScheduleRunExpectation {
/**
* Expected schedule name.
*/
name?: string;
/**
* Expected schedule payload. Object values are matched as partial objects.
*/
payload?: unknown;
/**
* Expected run ID.
*/
id?: string;
/**
* Expected source label.
*/
source?: string;
}
/**
* Expected fields for a provider instrumentation event assertion.
*/
export interface ProviderInstrumentationEventExpectation {
/**
* Expected instrumentation event type.
*/
type?: ProviderInstrumentationEventInput["type"];
/**
* Expected event ID.
*/
id?: string;
/**
* Expected ISO timestamp.
*/
timestamp?: string;
/**
* Expected request correlation ID.
*/
requestId?: string;
/**
* Expected trace ID.
*/
traceId?: string;
/**
* Expected span ID.
*/
spanId?: string;
/**
* Expected parent span ID.
*/
parentSpanId?: string;
/**
* Expected traceparent header value.
*/
traceparent?: string;
/**
* Expected watcher name.
*/
watcher?: string;
/**
* Expected provider name. Matches `providerName` on provider lifecycle events
* and `details.providerName` on provider instrumentation events.
*/
providerName?: string;
/**
* Expected structured details. Object values are matched as partial objects.
*/
details?: unknown;
/**
* Expected request method.
*/
method?: string;
/**
* Expected request path.
*/
path?: string;
/**
* Expected contract name.
*/
contractName?: string;
/**
* Expected status. This matches request status codes as well as job, outbox,
* and schedule status strings.
*/
status?: unknown;
/**
* Expected duration in milliseconds.
*/
durationMs?: number;
/**
* Expected human-readable summary.
*/
summary?: string;
/**
* Expected error message.
*/
message?: string;
/**
* Expected stack trace.
*/
stack?: string;
/**
* Expected use-case name on error events.
*/
useCaseName?: string;
/**
* Expected use-case or custom event name.
*/
name?: string;
/**
* Expected use-case kind.
*/
kind?: "command" | "query";
/**
* Expected use-case phase.
*/
phase?: "start" | "end" | "error";
/**
* Expected error summary.
*/
error?: string;
/**
* Expected event bus event name.
*/
eventName?: string;
/**
* Expected job name.
*/
jobName?: string;
/**
* Expected outbox message ID.
*/
messageId?: string;
/**
* Expected outbox message kind.
*/
messageKind?: "event" | "job";
/**
* Expected outbox message name.
*/
messageName?: string;
/**
* Expected schedule name.
*/
scheduleName?: string;
/**
* Expected schedule cron expression.
*/
cron?: string;
/**
* Expected schedule time zone.
*/
timezone?: string;
/**
* Expected provider lifecycle action.
*/
action?: "setup" | "start" | "stop";
/**
* Expected custom event label.
*/
label?: string;
}
/**
* Source accepted by provider instrumentation assertion helpers.
*/
export type ProviderInstrumentationAssertionSource = readonly ProviderInstrumentationEventInput[] | {
/**
* Recorded provider instrumentation events.
*/
readonly events: readonly ProviderInstrumentationEventInput[];
};
/**
* Create a recording schedule runner for tests.
*
* The runner records schedule run intent without executing the schedule
* handler. Use `createInlineScheduleRunner(...)` when the test should run the
* handler.
*
* @returns A schedule runner plus its captured runs.
*/
export declare function createRecordingScheduleRunner(): {
runner: ScheduleRunnerPort;
runs: RecordedScheduleRun[];
};
/**
* Create a provider instrumentation port for tests.
*
* The port records every event and can optionally disable specific watchers.
* Use the returned `events` array with provider instrumentation assertion
* helpers.
*
* @returns A provider instrumentation port plus its captured events.
*/
export declare function createRecordingProviderInstrumentation(options?: {
enabledWatchers?: readonly string[];
disabledWatchers?: readonly string[];
}): {
instrumentation: ProviderInstrumentationPort;
events: ProviderInstrumentationEventInput[];
};
/**
* Options for creating a test user actor.
*/
export interface CreateTestUserActorOptions extends Omit<ActivityActor, "type" | "id" | "metadata"> {
/**
* Optional role stored as `actor.metadata.role`.
*/
role?: string;
/**
* Additional redaction-safe actor metadata.
*/
metadata?: ActivityMetadata;
}
/**
* Options for creating a test actor that represents impersonated user access.
*/
export interface CreateTestImpersonatedUserActorOptions extends CreateTestUserActorOptions {
/**
* Stable ID for the actor performing the impersonation.
*/
impersonatorId: string;
}
/**
* Options for creating a test tenant.
*/
export type CreateTestTenantOptions = Omit<ActivityTenant, "id">;
/**
* Context fields commonly shared by Beignet tests that exercise audit,
* authorization, route hooks, and use cases.
*/
export interface TestActivityContext {
/**
* Actor under test.
*/
actor: ActivityActor;
/**
* Tenant/account/workspace scope under test.
*/
tenant?: ActivityTenant;
/**
* Stable request ID for assertions.
*/
requestId: string;
/**
* Optional trace ID for assertions.
*/
traceId?: string;
}
/**
* Options for creating a test activity context.
*/
export interface CreateTestActivityContextOptions {
/**
* Actor under test.
*
* @default createTestUserActor()
*/
actor?: ActivityActor;
/**
* Tenant under test. Pass `null` to omit tenant context.
*
* @default createTestTenant()
*/
tenant?: ActivityTenant | null;
/**
* Request ID to expose on the context.
*
* @default "test-request"
*/
requestId?: string;
/**
* Trace ID to expose on the context.
*
* @default "test-trace"
*/
traceId?: string;
}
/**
* Create a predictable user actor for tests.
*
* Use this when authorization or audit assertions need a stable actor shape
* without repeating the `ActivityActor` object in every test.
*
* @param id - Stable user ID for the test actor.
* @param options - Optional display name, role, and metadata.
* @returns A user actor with `type: "user"`.
*/
export declare function createTestUserActor(id?: string, options?: CreateTestUserActorOptions): ActivityActor;
/**
* Create a predictable user actor for tests that exercise impersonation.
*
* The returned actor remains the effective user, with `metadata.impersonatorId`
* recording who initiated the impersonated access.
*
* @param id - Stable user ID being impersonated.
* @param options - Impersonator ID plus optional display name, role, and metadata.
* @returns A user actor with impersonation metadata.
*/
export declare function createTestImpersonatedUserActor(id: string, options: CreateTestImpersonatedUserActorOptions): ActivityActor;
/**
* Create a predictable anonymous actor for tests.
*
* @param options - Optional display name or metadata.
* @returns An anonymous actor with `type: "anonymous"`.
*/
export declare function createTestAnonymousActor(options?: Omit<ActivityActor, "type">): ActivityActor;
/**
* Create a predictable service actor for tests.
*
* @param id - Stable service ID.
* @param options - Optional display name or metadata.
* @returns A service actor with `type: "service"`.
*/
export declare function createTestServiceActor(id?: string, options?: Omit<ActivityActor, "type" | "id">): ActivityActor;
/**
* Create a predictable system actor for tests.
*
* @param id - Stable system actor ID.
* @param options - Optional display name or metadata.
* @returns A system actor with `type: "system"`.
*/
export declare function createTestSystemActor(id?: string, options?: Omit<ActivityActor, "type" | "id">): ActivityActor;
/**
* Create a predictable tenant for tests.
*
* @param id - Stable tenant ID.
* @param options - Optional slug or metadata.
* @returns A tenant descriptor.
*/
export declare function createTestTenant(id?: string, options?: CreateTestTenantOptions): ActivityTenant;
/**
* Create the activity fields commonly copied onto app test contexts.
*
* @param options - Optional actor, tenant, request ID, and trace ID overrides.
* @returns Stable activity context fields for a test.
*/
export declare function createTestActivityContext(options?: CreateTestActivityContextOptions): TestActivityContext;
/**
* Expected audit fields used by audit assertion helpers.
*/
export interface AuditLogEntryExpectation {
/**
* Expected action name.
*/
action?: string;
/**
* Expected actor fields.
*/
actor?: Partial<ActivityActor>;
/**
* Convenience matcher for `entry.actor.id`.
*/
actorId?: string;
/**
* Convenience matcher for `entry.actor.type`.
*/
actorType?: ActivityActor["type"];
/**
* Expected tenant fields.
*/
tenant?: Partial<ActivityTenant>;
/**
* Convenience matcher for `entry.tenant.id`.
*/
tenantId?: string;
/**
* Expected resource fields.
*/
resource?: Partial<ActivityResource>;
/**
* Convenience matcher for `entry.resource.id`.
*/
resourceId?: string;
/**
* Convenience matcher for `entry.resource.type`.
*/
resourceType?: string;
/**
* Expected audit outcome.
*/
outcome?: AuditOutcome;
/**
* Convenience matcher for `entry.metadata.severity`.
*/
severity?: ActivityMetadataValue;
/**
* Expected request ID.
*/
requestId?: string;
/**
* Expected trace ID.
*/
traceId?: string;
/**
* Expected metadata fields. Object values are matched as partial objects.
*/
metadata?: ActivityMetadata;
}
/**
* Expected mail delivery fields used by mail assertion helpers.
*/
export interface MailDeliveryExpectation {
/**
* Expected memory delivery ID.
*/
id?: string;
/**
* Expected subject.
*/
subject?: string;
/**
* Expected recipients.
*/
to?: NormalizedMailMessage["to"];
/**
* Expected sender.
*/
from?: NormalizedMailMessage["from"];
/**
* Expected text body.
*/
text?: string;
/**
* Expected HTML body.
*/
html?: string;
/**
* Expected message headers.
*/
headers?: Record<string, string>;
/**
* Expected normalized message fields.
*/
message?: Partial<NormalizedMailMessage>;
}
/**
* Expected notification delivery fields used by notification assertion helpers.
*/
export interface NotificationDeliveryExpectation {
/**
* Expected memory delivery ID.
*/
id?: string;
/**
* Expected notification name.
*/
notificationName?: string;
/**
* Expected parsed payload. Object values are matched as partial objects.
*/
payload?: unknown;
/**
* Expected selected channels.
*/
channels?: readonly string[];
/**
* Expected delivery metadata. Object values are matched as partial objects.
*/
metadata?: Record<string, unknown>;
}
/**
* Expected storage object fields used by storage assertion helpers.
*/
export interface StorageObjectExpectation {
/**
* Object key to look up.
*/
key: string;
/**
* Expected size in bytes.
*/
size?: number;
/**
* Expected content type.
*/
contentType?: string;
/**
* Expected cache-control value.
*/
cacheControl?: string;
/**
* Expected storage metadata.
*/
metadata?: Record<string, string>;
/**
* Expected visibility.
*/
visibility?: StorageVisibility;
/**
* Expected text body. Cannot be combined with `bytes`.
*/
text?: string;
/**
* Expected object bytes. Cannot be combined with `text`.
*/
bytes?: Uint8Array;
}
/**
* Source accepted by outbox message assertion helpers.
*
* Use a `MemoryOutboxPort` or a snapshot returned by a test adapter. Durable
* SQL adapters should expose app-owned snapshots rather than widening the
* production `OutboxPort` read surface.
*/
export type OutboxMessageAssertionSource = readonly OutboxMessage[] | {
/**
* Current outbox message snapshots.
*/
readonly messages: readonly OutboxMessage[];
};
/**
* Expected outbox message fields used by outbox assertion helpers.
*/
export interface OutboxMessageExpectation {
/**
* Expected message ID.
*/
id?: string;
/**
* Expected message kind.
*/
kind?: OutboxMessageKind;
/**
* Expected event or job name.
*/
name?: string;
/**
* Expected JSON payload. Object values are matched as partial objects.
*/
payload?: unknown;
/**
* Expected delivery status.
*/
status?: OutboxMessageStatus;
/**
* Expected claim attempt count.
*/
attempts?: number;
/**
* Expected maximum delivery attempts.
*/
maxAttempts?: number;
/**
* Expected delivery timestamp.
*/
deliveredAt?: Date | null;
/**
* Expected serialized delivery error. Object values are matched as partial
* objects. Pass `null` to assert no error has been recorded.
*/
lastError?: Partial<OutboxErrorInfo> | null;
}
/**
* Expected fields for one outbox drain result assertion.
*/
export interface OutboxDrainResultExpectation {
/**
* Expected claimed count.
*/
claimed?: number;
/**
* Expected delivered count.
*/
delivered?: number;
/**
* Expected retried count.
*/
retried?: number;
/**
* Expected dead-lettered count.
*/
deadLettered?: number;
}
/**
* Source accepted by idempotency entry assertion helpers.
*
* Use a `MemoryIdempotencyStore` or a snapshot array from an app-owned adapter.
* Durable SQL adapters should expose app-owned snapshots rather than widening
* the production `IdempotencyPort` read surface.
*/
export type IdempotencyEntryAssertionSource = readonly MemoryIdempotencyEntry[] | Pick<MemoryIdempotencyStore, "entries">;
/**
* Expected idempotency entry fields used by idempotency assertion helpers.
*/
export interface IdempotencyEntryExpectation {
/**
* Expected operation namespace.
*/
namespace?: string;
/**
* Expected client-provided idempotency key.
*/
key?: string;
/**
* Expected normalized scope key.
*/
scopeKey?: string;
/**
* Expected request fingerprint.
*/
fingerprint?: string;
/**
* Expected reservation status.
*/
status?: MemoryIdempotencyEntry["status"];
/**
* Expected replay result. Object values are matched as partial objects.
*/
result?: unknown;
/**
* Expected reservation timestamp.
*/
reservedAt?: Date;
/**
* Expected completion timestamp.
*/
completedAt?: Date;
/**
* Expected expiration timestamp, or `null` when no expiration is set.
*/
expiresAt?: Date | null;
}
/**
* Find the first audit entry matching the expected fields.
*
* @param entries - Audit entries captured by a memory or fake audit port.
* @param expectation - Partial audit fields to match.
* @returns The first matching entry, or `undefined`.
*/
export declare function findAuditEntry(entries: readonly AuditLogEntry[], expectation: AuditLogEntryExpectation): AuditLogEntry | undefined;
/**
* Assert that an audit entry exists and return the matching entry.
*
* The helper throws a plain `Error`, so it works with Bun, Vitest, Jest, and
* other test runners.
*
* @param entries - Audit entries captured by a memory or fake audit port.
* @param expectation - Partial audit fields to match.
* @returns The matching audit entry.
* @throws Error when no entry matches.
*/
export declare function assertAuditEntry(entries: readonly AuditLogEntry[], expectation: AuditLogEntryExpectation): AuditLogEntry;
/**
* Assert that no audit entry matches the expected fields.
*
* @param entries - Audit entries captured by a memory or fake audit port.
* @param expectation - Partial audit fields to reject.
* @throws Error when a matching entry exists.
*/
export declare function assertNoAuditEntry(entries: readonly AuditLogEntry[], expectation: AuditLogEntryExpectation): void;
/**
* Find the first recorded event matching the expected fields.
*
* @param events - Events captured by `createRecordingEventBus(...)`.
* @param expectation - Partial event fields to match.
* @returns The first matching event, or `undefined`.
*/
export declare function findRecordedEvent(events: readonly RecordedEvent[], expectation: RecordedEventExpectation): RecordedEvent | undefined;
/**
* Assert that a recorded event exists and return the matching event.
*
* @param events - Events captured by `createRecordingEventBus(...)`.
* @param expectation - Partial event fields to match.
* @returns The matching event.
* @throws Error when no event matches.
*/
export declare function assertRecordedEvent(events: readonly RecordedEvent[], expectation: RecordedEventExpectation): RecordedEvent;
/**
* Assert that no recorded event matches the expected fields.
*
* @param events - Events captured by `createRecordingEventBus(...)`.
* @param expectation - Partial event fields to reject.
* @throws Error when a matching event exists.
*/
export declare function assertNoRecordedEvent(events: readonly RecordedEvent[], expectation: RecordedEventExpectation): void;
/**
* Find the first recorded job dispatch matching the expected fields.
*
* @param jobs - Job dispatches captured by `createRecordingJobDispatcher(...)`.
* @param expectation - Partial job fields to match.
* @returns The first matching dispatch, or `undefined`.
*/
export declare function findDispatchedJob(jobs: readonly RecordedJobDispatch[], expectation: RecordedJobDispatchExpectation): RecordedJobDispatch | undefined;
/**
* Assert that a job was dispatched and return the matching dispatch.
*
* @param jobs - Job dispatches captured by `createRecordingJobDispatcher(...)`.
* @param expectation - Partial job fields to match.
* @returns The matching dispatch.
* @throws Error when no dispatch matches.
*/
export declare function assertDispatchedJob(jobs: readonly RecordedJobDispatch[], expectation: RecordedJobDispatchExpectation): RecordedJobDispatch;
/**
* Assert that no job dispatch matches the expected fields.
*
* @param jobs - Job dispatches captured by `createRecordingJobDispatcher(...)`.
* @param expectation - Partial job fields to reject.
* @throws Error when a matching dispatch exists.
*/
export declare function assertNoDispatchedJob(jobs: readonly RecordedJobDispatch[], expectation: RecordedJobDispatchExpectation): void;
/**
* Find the first recorded schedule run matching the expected fields.
*
* @param runs - Runs captured by `createRecordingScheduleRunner(...)`.
* @param expectation - Partial schedule fields to match.
* @returns The first matching schedule run, or `undefined`.
*/
export declare function findScheduleRun(runs: readonly RecordedScheduleRun[], expectation: RecordedScheduleRunExpectation): RecordedScheduleRun | undefined;
/**
* Find the first provider instrumentation event matching the expected fields.
*
* @param source - Recording instrumentation result or event snapshot array.
* @param expectation - Partial event fields to match.
* @returns The first matching event, or `undefined`.
*/
export declare function findProviderInstrumentationEvent(source: ProviderInstrumentationAssertionSource, expectation: ProviderInstrumentationEventExpectation): ProviderInstrumentationEventInput | undefined;
/**
* Assert that a provider instrumentation event exists and return it.
*
* @param source - Recording instrumentation result or event snapshot array.
* @param expectation - Partial event fields to match.
* @returns The matching event.
* @throws Error when no event matches.
*/
export declare function assertProviderInstrumentationEvent(source: ProviderInstrumentationAssertionSource, expectation: ProviderInstrumentationEventExpectation): ProviderInstrumentationEventInput;
/**
* Assert that no provider instrumentation event matches the expected fields.
*
* @param source - Recording instrumentation result or event snapshot array.
* @param expectation - Partial event fields to reject.
* @throws Error when a matching event exists.
*/
export declare function assertNoProviderInstrumentationEvent(source: ProviderInstrumentationAssertionSource, expectation: ProviderInstrumentationEventExpectation): void;
/**
* Assert that a schedule run was recorded and return the matching run.
*
* @param runs - Runs captured by `createRecordingScheduleRunner(...)`.
* @param expectation - Partial schedule fields to match.
* @returns The matching schedule run.
* @throws Error when no run matches.
*/
export declare function assertScheduleRun(runs: readonly RecordedScheduleRun[], expectation: RecordedScheduleRunExpectation): RecordedScheduleRun;
/**
* Assert that no schedule run matches the expected fields.
*
* @param runs - Runs captured by `createRecordingScheduleRunner(...)`.
* @param expectation - Partial schedule fields to reject.
* @throws Error when a matching run exists.
*/
export declare function assertNoScheduleRun(runs: readonly RecordedScheduleRun[], expectation: RecordedScheduleRunExpectation): void;
/**
* Find the first mail delivery matching the expected fields.
*
* @param deliveries - Deliveries captured by `createMemoryMailer(...)`.
* @param expectation - Partial delivery or message fields to match.
* @returns The first matching delivery, or `undefined`.
*/
export declare function findMailDelivery(deliveries: readonly MemoryMailDelivery[], expectation: MailDeliveryExpectation): MemoryMailDelivery | undefined;
/**
* Assert that a mail delivery exists and return the matching delivery.
*
* @param deliveries - Deliveries captured by `createMemoryMailer(...)`.
* @param expectation - Partial delivery or message fields to match.
* @returns The matching delivery.
* @throws Error when no delivery matches.
*/
export declare function assertMailDelivery(deliveries: readonly MemoryMailDelivery[], expectation: MailDeliveryExpectation): MemoryMailDelivery;
/**
* Assert that no mail delivery matches the expected fields.
*
* @param deliveries - Deliveries captured by `createMemoryMailer(...)`.
* @param expectation - Partial delivery or message fields to reject.
* @throws Error when a matching delivery exists.
*/
export declare function assertNoMailDelivery(deliveries: readonly MemoryMailDelivery[], expectation: MailDeliveryExpectation): void;
/**
* Find the first notification delivery matching the expected fields.
*
* @param deliveries - Deliveries captured by `createMemoryNotificationPort(...)`.
* @param expectation - Partial delivery fields to match.
* @returns The first matching delivery, or `undefined`.
*/
export declare function findNotificationDelivery(deliveries: readonly MemoryNotificationDelivery[], expectation: NotificationDeliveryExpectation): MemoryNotificationDelivery | undefined;
/**
* Assert that a notification delivery exists and return the matching delivery.
*
* @param deliveries - Deliveries captured by `createMemoryNotificationPort(...)`.
* @param expectation - Partial delivery fields to match.
* @returns The matching delivery.
* @throws Error when no delivery matches.
*/
export declare function assertNotificationDelivery(deliveries: readonly MemoryNotificationDelivery[], expectation: NotificationDeliveryExpectation): MemoryNotificationDelivery;
/**
* Assert that no notification delivery matches the expected fields.
*
* @param deliveries - Deliveries captured by `createMemoryNotificationPort(...)`.
* @param expectation - Partial delivery fields to reject.
* @throws Error when a matching delivery exists.
*/
export declare function assertNoNotificationDelivery(deliveries: readonly MemoryNotificationDelivery[], expectation: NotificationDeliveryExpectation): void;
/**
* Assert that a storage object exists and optionally matches metadata/body
* expectations.
*
* This helper works against any `StoragePort`, not only memory storage.
*
* @param storage - Storage port under test.
* @param expectation - Object key and expected fields.
* @returns The matching object metadata.
* @throws Error when the object is missing or does not match.
*/
export declare function assertStorageObject(storage: StoragePort, expectation: StorageObjectExpectation): Promise<StorageObject>;
/**
* Assert that a storage object does not exist.
*
* @param storage - Storage port under test.
* @param key - Object key expected to be absent.
* @throws Error when the object exists.
*/
export declare function assertNoStorageObject(storage: StoragePort, key: string): Promise<void>;
/**
* Find the first outbox message matching expected fields.
*
* @param source - Memory outbox or message snapshot array.
* @param expectation - Partial message fields to match.
* @returns The first matching message, or `undefined`.
*/
export declare function findOutboxMessage(source: OutboxMessageAssertionSource, expectation: OutboxMessageExpectation): OutboxMessage | undefined;
/**
* Find the first idempotency entry matching expected fields.
*
* @param source - Memory idempotency store or entry snapshot array.
* @param expectation - Partial entry fields to match.
* @returns The first matching entry, or `undefined`.
*/
export declare function findIdempotencyEntry(source: IdempotencyEntryAssertionSource, expectation: IdempotencyEntryExpectation): MemoryIdempotencyEntry | undefined;
/**
* Assert that an idempotency entry exists and return it.
*
* @param source - Memory idempotency store or entry snapshot array.
* @param expectation - Partial entry fields to match.
* @returns The matching entry.
* @throws Error when no entry matches.
*/
export declare function assertIdempotencyEntry(source: IdempotencyEntryAssertionSource, expectation: IdempotencyEntryExpectation): MemoryIdempotencyEntry;
/**
* Assert that no idempotency entry matches expected fields.
*
* @param source - Memory idempotency store or entry snapshot array.
* @param expectation - Partial entry fields to reject.
* @throws Error when a matching entry exists.
*/
export declare function assertNoIdempotencyEntry(source: IdempotencyEntryAssertionSource, expectation: IdempotencyEntryExpectation): void;
/**
* Assert that a matching idempotency entry is still in progress.
*
* @param source - Memory idempotency store or entry snapshot array.
* @param expectation - Entry fields to match.
* @returns The matching in-progress entry.
*/
export declare function assertIdempotencyInProgress(source: IdempotencyEntryAssertionSource, expectation?: Omit<IdempotencyEntryExpectation, "status">): MemoryIdempotencyEntry;
/**
* Assert that a matching idempotency entry completed.
*
* @param source - Memory idempotency store or entry snapshot array.
* @param expectation - Entry fields to match.
* @returns The matching completed entry.
*/
export declare function assertIdempotencyCompleted(source: IdempotencyEntryAssertionSource, expectation?: Omit<IdempotencyEntryExpectation, "status">): MemoryIdempotencyEntry;
/**
* Assert that an outbox message exists and return it.
*
* @param source - Memory outbox or message snapshot array.
* @param expectation - Partial message fields to match.
* @returns The matching message.
* @throws Error when no message matches.
*/
export declare function assertOutboxMessage(source: OutboxMessageAssertionSource, expectation: OutboxMessageExpectation): OutboxMessage;
/**
* Assert that no outbox message matches expected fields.
*
* @param source - Memory outbox or message snapshot array.
* @param expectation - Partial message fields to reject.
* @throws Error when a matching message exists.
*/
export declare function assertNoOutboxMessage(source: OutboxMessageAssertionSource, expectation: OutboxMessageExpectation): void;
/**
* Assert that an outbox message is pending.
*
* @param source - Memory outbox or message snapshot array.
* @param expectation - Message fields to match.
* @returns The matching pending message.
*/
export declare function assertOutboxPending(source: OutboxMessageAssertionSource, expectation?: Omit<OutboxMessageExpectation, "status">): OutboxMessage;
/**
* Assert that an outbox message was delivered.
*
* @param source - Memory outbox or message snapshot array.
* @param expectation - Message fields to match.
* @returns The matching delivered message.
*/
export declare function assertOutboxDelivered(source: OutboxMessageAssertionSource, expectation?: Omit<OutboxMessageExpectation, "status">): OutboxMessage;
/**
* Assert that an outbox message is pending after at least one failed attempt.
*
* Use this for retry-scheduled assertions after `drainOutbox(...)` returns a
* retried count.
*
* @param source - Memory outbox or message snapshot array.
* @param expectation - Message fields to match.
* @returns The matching retry-scheduled message.
*/
export declare function assertOutboxRetryScheduled(source: OutboxMessageAssertionSource, expectation?: Omit<OutboxMessageExpectation, "status">): OutboxMessage;
/**
* Assert that an outbox message was dead-lettered.
*
* @param source - Memory outbox or message snapshot array.
* @param expectation - Message fields to match.
* @returns The matching dead-lettered message.
*/
export declare function assertOutboxDeadLettered(source: OutboxMessageAssertionSource, expectation?: Omit<OutboxMessageExpectation, "status">): OutboxMessage;
/**
* Assert that a drain result matches expected counts.
*
* @param result - Result returned by `drainOutbox(...)`.
* @param expectation - Partial count expectation.
* @throws Error when any supplied count differs.
*/
export declare function assertOutboxDrainResult(result: DrainOutboxResult, expectation: OutboxDrainResultExpectation): void;
/**
* Expected outcome for one policy matrix case.
*/
export type PolicyMatrixExpectation = "allow" | "deny";
type PolicyMatrixSubject<TResolver> = PolicySubjectArgs<TResolver> extends [subject: infer Subject] ? {
subject: Subject;
} : {
subject?: never;
};
/**
* One typed authorization matrix case for a policy ability.
*/
export type PolicyMatrixCase<TContext, TPolicies extends readonly PolicyDefinition[] = readonly PolicyDefinition[]> = {
[TAbility in keyof PolicyMapFromDefinitions<TPolicies> & string]: {
name: string;
ctx: TContext;
ability: TAbility;
expected: PolicyMatrixExpectation;
reason?: string;
code?: string;
} & PolicyMatrixSubject<PolicyMapFromDefinitions<TPolicies>[TAbility]>;
}[keyof PolicyMapFromDefinitions<TPolicies> & string];
/**
* Untyped policy matrix case used internally for failure reporting.
*/
export type UntypedPolicyMatrixCase<TContext> = {
name: string;
ctx: TContext;
ability: string;
subject?: unknown;
expected: PolicyMatrixExpectation;
reason?: string;
code?: string;
};
/**
* Result for one evaluated policy matrix case.
*/
export type PolicyMatrixResult<TContext, TPolicies extends readonly PolicyDefinition[] = readonly PolicyDefinition[]> = {
case: PolicyMatrixCase<TContext, TPolicies>;
decision: GateDecision;
passed: boolean;
message?: string;
};
/**
* Test helper for evaluating authorization policies.
*/
export type PolicyTester<TContext, TPolicies extends readonly PolicyDefinition[]> = {
/**
* Gate created from the same policies, useful for direct assertions.
*/
gate: GatePort<TContext, TPolicies>;
/**
* Evaluate cases and return structured pass/fail results.
*/
evaluateMatrix(cases: readonly PolicyMatrixCase<TContext, TPolicies>[]): Promise<PolicyMatrixResult<TContext, TPolicies>[]>;
/**
* Evaluate cases and throw a combined assertion error when any fail.
*/
assertMatrix(cases: readonly PolicyMatrixCase<TContext, TPolicies>[]): Promise<void>;
};
/**
* Create a policy tester from the same options used by `createGate(...)`.
*
* Use this for table-driven authorization tests that document who can perform
* each ability against which subject.
*
* @param options - Policy definitions and optional denial mapper.
* @returns A policy tester with a gate plus matrix helpers.
*/
export declare function createPolicyTester<const TPolicies extends readonly PolicyDefinition[]>(options: CreateGateOptions<PolicyContextFromDefinitions<TPolicies>, TPolicies>): PolicyTester<PolicyContextFromDefinitions<TPolicies>, TPolicies>;
/**
* Evaluate a table of policy cases without throwing.
*
* @param gate - Gate under test.
* @param cases - Matrix cases to evaluate.
* @returns Structured result for each case.
*/
export declare function evaluatePolicyMatrix<TContext, TPolicies extends readonly PolicyDefinition[]>(gate: GatePort<TContext, TPolicies>, cases: readonly PolicyMatrixCase<TContext, TPolicies>[]): Promise<PolicyMatrixResult<TContext, TPolicies>[]>;
/**
* Assert that all policy matrix cases pass.
*
* @param gate - Gate under test.
* @param cases - Matrix cases to evaluate.
* @throws Combined error listing every failed case.
*/
export declare function assertPolicyMatrix<TContext, TPolicies extends readonly PolicyDefinition[]>(gate: GatePort<TContext, TPolicies>, cases: readonly PolicyMatrixCase<TContext, TPolicies>[]): Promise<void>;
export {};
//# sourceMappingURL=testing.d.ts.map