azure-mock 2.34.2 → 2.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/index.d.ts +61 -58
- package/dist/index.js +194 -94
- package/package.json +14 -12
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[![NPM downloads][badge-npm-downloads]][url-npm]
|
|
6
6
|
[![NPM Unpacked Size (with version)][badge-npm-unpacked-size]][url-npm]
|
|
7
7
|
|
|
8
|
-
Mock Azure service classes for local development and testing. Provides in-memory implementations of Azure Table Storage, Blob Storage,
|
|
8
|
+
Mock Azure service classes for local development and testing. Provides in-memory implementations of Azure Table Storage, Blob Storage, Service Bus, and EventGrid clients that mirror the real Azure SDK interfaces.
|
|
9
9
|
|
|
10
10
|
## Table of Contents
|
|
11
11
|
|
|
@@ -18,7 +18,7 @@ Mock Azure service classes for local development and testing. Provides in-memory
|
|
|
18
18
|
## <a name="getting-started">🚀 Getting Started</a>
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
|
-
pnpm i -D azure-mock @azure/core-http-compat @azure/core-rest-pipeline @azure/data-tables @azure/eventgrid @azure/
|
|
21
|
+
pnpm i -D azure-mock @azure/core-http-compat @azure/core-rest-pipeline @azure/data-tables @azure/eventgrid @azure/service-bus @azure/storage-blob
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
## <a name="documentation">📖 Documentation</a>
|
|
@@ -31,7 +31,7 @@ We highly recommend you take a look at the [documentation](https://esposter.com/
|
|
|
31
31
|
| ------------------------------ | -------------------------- | ----------------------------- |
|
|
32
32
|
| `MockTableClient` | `TableClient` | In-memory Azure Table Storage |
|
|
33
33
|
| `MockBlobServiceClient` | `BlobServiceClient` | In-memory Azure Blob Storage |
|
|
34
|
-
| `
|
|
34
|
+
| `MockServiceBusSender` | `ServiceBusSender` | In-memory Azure Service Bus |
|
|
35
35
|
| `MockEventGridPublisherClient` | `EventGridPublisherClient` | No-op EventGrid publisher |
|
|
36
36
|
|
|
37
37
|
### Usage
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { PipelineRequest, RestError } from "@azure/core-rest-pipeline";
|
|
1
2
|
import { HttpHeadersLike, WebResourceLike } from "@azure/core-http-compat";
|
|
2
3
|
import { MapValue } from "@esposter/shared";
|
|
3
|
-
import {
|
|
4
|
-
import { AnonymousCredential, AppendBlobClient, BlobAbortCopyFromURLResponse, BlobBatchClient, BlobBatchDeleteBlobsResponse, BlobBeginCopyFromURLResponse, BlobClient, BlobCopyFromURLResponse, BlobCreateSnapshotResponse, BlobDeleteIfExistsResponse, BlobDeleteImmutabilityPolicyResponse, BlobDeleteOptions, BlobDeleteResponse, BlobDownloadResponseModel, BlobDownloadResponseParsed, BlobGenerateSasUrlOptions, BlobGetAccountInfoResponse, BlobGetPropertiesResponse, BlobGetTagsResponse, BlobItem, BlobLeaseClient, BlobPrefix, BlobSetHTTPHeadersResponse, BlobSetImmutabilityPolicyResponse, BlobSetLegalHoldResponse, BlobSetMetadataResponse, BlobSetTagsResponse, BlobSetTierResponse, BlobUndeleteResponse, BlobUploadCommonResponse, BlockBlobClient, BlockBlobCommitBlockListResponse, BlockBlobGetBlockListResponse, BlockBlobPutBlobFromUrlResponse, BlockBlobStageBlockFromURLResponse, BlockBlobStageBlockResponse, BlockBlobUploadResponse, ContainerClient, ContainerCreateIfNotExistsResponse, ContainerCreateResponse, ContainerDeleteIfExistsResponse, ContainerDeleteResponse, ContainerFindBlobsByTagsSegmentResponse, ContainerGenerateSasUrlOptions, ContainerGetAccessPolicyResponse, ContainerGetAccountInfoResponse, ContainerGetPropertiesResponse, ContainerListBlobFlatSegmentResponse, ContainerListBlobHierarchySegmentResponse, ContainerListBlobsOptions, ContainerSetAccessPolicyResponse, ContainerSetMetadataResponse, FilterBlobItem, HttpRequestBody, PageBlobClient, PollOperationState, PollerLikeWithCancellation, StorageSharedKeyCredential } from "@azure/storage-blob";
|
|
4
|
+
import { AnonymousCredential, AppendBlobClient, BlobAbortCopyFromURLResponse, BlobBatchClient, BlobBatchDeleteBlobsResponse, BlobBeginCopyFromURLResponse, BlobClient, BlobCopyFromURLResponse, BlobCreateSnapshotResponse, BlobDeleteIfExistsResponse, BlobDeleteImmutabilityPolicyResponse, BlobDeleteOptions, BlobDeleteResponse, BlobDownloadResponseModel, BlobDownloadResponseParsed, BlobGenerateSasUrlOptions, BlobGetAccountInfoResponse, BlobGetPropertiesResponse, BlobGetTagsResponse, BlobItem, BlobLeaseClient, BlobPrefix, BlobSetHTTPHeadersResponse, BlobSetImmutabilityPolicyResponse, BlobSetLegalHoldResponse, BlobSetMetadataResponse, BlobSetTagsResponse, BlobSetTierResponse, BlobUndeleteResponse, BlobUploadCommonResponse, BlockBlobClient, BlockBlobCommitBlockListResponse, BlockBlobGetBlockListResponse, BlockBlobPutBlobFromUrlResponse, BlockBlobStageBlockFromURLResponse, BlockBlobStageBlockResponse, BlockBlobUploadOptions, BlockBlobUploadResponse, ContainerClient, ContainerCreateIfNotExistsResponse, ContainerCreateResponse, ContainerDeleteIfExistsResponse, ContainerDeleteResponse, ContainerFindBlobsByTagsSegmentResponse, ContainerGenerateSasUrlOptions, ContainerGetAccessPolicyResponse, ContainerGetAccountInfoResponse, ContainerGetPropertiesResponse, ContainerListBlobFlatSegmentResponse, ContainerListBlobHierarchySegmentResponse, ContainerListBlobsOptions, ContainerSetAccessPolicyResponse, ContainerSetMetadataResponse, FilterBlobItem, HttpRequestBody, PageBlobClient, PollOperationState, PollerLikeWithCancellation, StorageSharedKeyCredential } from "@azure/storage-blob";
|
|
5
5
|
import { BinaryOperator, Clause } from "@esposter/db-schema";
|
|
6
6
|
import { EventGridEvent, EventGridPublisherClient } from "@azure/eventgrid";
|
|
7
|
+
import { ServiceBusMessage, ServiceBusMessageBatch, ServiceBusSender } from "@azure/service-bus";
|
|
7
8
|
import { CreateTableEntityResponse, GetAccessPolicyResponse, GetTableEntityResponse, ListTableEntitiesOptions, TableClient, TableDeleteEntityHeaders, TableEntity, TableEntityResult, TableEntityResultPage, TableMergeEntityHeaders, TableSetAccessPolicyHeaders, TableTransactionResponse, UpdateMode } from "@azure/data-tables";
|
|
8
9
|
import { QueueClearMessagesResponse, QueueClient, QueueCreateIfNotExistsResponse, QueueCreateOptions, QueueCreateResponse, QueueDeleteIfExistsResponse, QueueDeleteMessageResponse, QueueDeleteResponse, QueueGenerateSasUrlOptions, QueueGetAccessPolicyResponse, QueueGetPropertiesResponse, QueueItem, QueuePeekMessagesOptions, QueuePeekMessagesResponse, QueueReceiveMessageOptions, QueueReceiveMessageResponse, QueueSendMessageOptions, QueueSendMessageResponse, QueueServiceProperties, QueueSetAccessPolicyResponse, QueueSetMetadataResponse, QueueUpdateMessageResponse, SignedIdentifier } from "@azure/storage-queue";
|
|
9
10
|
import { AutocompleteResult, CountDocumentsOptions, DeleteDocumentsOptions, GetDocumentOptions, IndexDocumentsBatch, IndexDocumentsOptions, IndexDocumentsResult, MergeDocumentsOptions, MergeOrUploadDocumentsOptions, NarrowedModel, SearchClient, SearchDocumentsResult, SearchOptions, SelectFields, SuggestDocumentsResult, SuggestOptions, UploadDocumentsOptions } from "@azure/search-documents";
|
|
@@ -17,8 +18,7 @@ interface MockResponse {
|
|
|
17
18
|
request: WebResourceLike;
|
|
18
19
|
status: number;
|
|
19
20
|
}
|
|
20
|
-
declare class MockRestError extends
|
|
21
|
-
statusCode: number;
|
|
21
|
+
declare class MockRestError extends RestError {
|
|
22
22
|
constructor(message: string, statusCode: number);
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
@@ -55,10 +55,14 @@ declare const createMockResponse: (status: number, url?: string) => MockResponse
|
|
|
55
55
|
declare const getMockSasUrl: (url: string, permissions: undefined | {
|
|
56
56
|
toString: () => string;
|
|
57
57
|
}, resourceType?: string) => string;
|
|
58
|
+
declare const MockContainerCreatedOnDatabase: Map<string, Date>;
|
|
59
|
+
declare const MOCK_BLOB_SEEDED_CREATED_ON: Date;
|
|
60
|
+
declare const getMockContainerCreatedOnKey: (containerName: string, blobName: string) => string;
|
|
58
61
|
declare const MockContainerDatabase: Map<string, Map<string, Buffer>>;
|
|
59
62
|
declare const MockEventGridDatabase: Map<string, EventGridEvent<unknown>[]>;
|
|
60
63
|
declare const MockQueueDatabase: Map<string, string[]>;
|
|
61
64
|
declare const MockSearchDatabase: Map<string, object[]>;
|
|
65
|
+
declare const MockServiceBusDatabase: Map<string, ServiceBusMessage[]>;
|
|
62
66
|
declare const MockTableDatabase: Map<string, Map<string, TableEntity>>;
|
|
63
67
|
type BlobHierarchyItem = (BlobItem & {
|
|
64
68
|
kind: "blob";
|
|
@@ -181,8 +185,8 @@ const update2: UpdateOperation<User> = {
|
|
|
181
185
|
@category Utilities
|
|
182
186
|
*/
|
|
183
187
|
type OptionalKeysOf<Type extends object> = Type extends unknown // For distributing `Type`
|
|
184
|
-
? (keyof { [Key in keyof Type as IsOptionalKeyOf<Type, Key> extends false ? never : Key]: never }) & keyof Type // Intersect with `keyof Type` to ensure result of `OptionalKeysOf<Type>` is always assignable to `keyof Type`
|
|
185
|
-
: never;
|
|
188
|
+
? (keyof { [Key in keyof Type as IsOptionalKeyOf<Type, Key> extends false ? never : Key]: never; }) & keyof Type // Intersect with `keyof Type` to ensure result of `OptionalKeysOf<Type>` is always assignable to `keyof Type`
|
|
189
|
+
: never; // Should never happen
|
|
186
190
|
/**
|
|
187
191
|
Extract all required keys from the given type.
|
|
188
192
|
|
|
@@ -214,7 +218,7 @@ const validator3 = createValidation<User>('luckyNumber', value => value > 0);
|
|
|
214
218
|
@category Utilities
|
|
215
219
|
*/
|
|
216
220
|
type RequiredKeysOf<Type extends object> = Type extends unknown // For distributing `Type`
|
|
217
|
-
? Exclude<keyof Type, OptionalKeysOf<Type>> : never;
|
|
221
|
+
? Exclude<keyof Type, OptionalKeysOf<Type>> : never; // Should never happen
|
|
218
222
|
/**
|
|
219
223
|
Returns a boolean for whether the given type is `never`.
|
|
220
224
|
|
|
@@ -420,7 +424,7 @@ fn(someInterface as Simplify<SomeInterface>); // Good: transform an `interface`
|
|
|
420
424
|
@see {@link SimplifyDeep}
|
|
421
425
|
@category Object
|
|
422
426
|
*/
|
|
423
|
-
type Simplify<T> = { [KeyType in keyof T]: T[KeyType] } & {};
|
|
427
|
+
type Simplify<T> = { [KeyType in keyof T]: T[KeyType]; } & {};
|
|
424
428
|
/**
|
|
425
429
|
Returns a boolean for whether the two given types are equal.
|
|
426
430
|
|
|
@@ -541,7 +545,7 @@ type ExampleWithoutIndexSignatures = OmitIndexSignature<Example>;
|
|
|
541
545
|
@see {@link PickIndexSignature}
|
|
542
546
|
@category Object
|
|
543
547
|
*/
|
|
544
|
-
type OmitIndexSignature<ObjectType> = { [KeyType in keyof ObjectType as {} extends Record<KeyType, unknown> ? never : KeyType]: ObjectType[KeyType] };
|
|
548
|
+
type OmitIndexSignature<ObjectType> = { [KeyType in keyof ObjectType as {} extends Record<KeyType, unknown> ? never : KeyType]: ObjectType[KeyType]; };
|
|
545
549
|
/**
|
|
546
550
|
Pick only index signatures from the given object type, leaving out all explicitly defined properties.
|
|
547
551
|
|
|
@@ -587,9 +591,9 @@ type ExampleIndexSignature = PickIndexSignature<Example>;
|
|
|
587
591
|
@see {@link OmitIndexSignature}
|
|
588
592
|
@category Object
|
|
589
593
|
*/
|
|
590
|
-
type PickIndexSignature<ObjectType> = { [KeyType in keyof ObjectType as {} extends Record<KeyType, unknown> ? KeyType : never]: ObjectType[KeyType] };
|
|
594
|
+
type PickIndexSignature<ObjectType> = { [KeyType in keyof ObjectType as {} extends Record<KeyType, unknown> ? KeyType : never]: ObjectType[KeyType]; };
|
|
591
595
|
// Merges two objects without worrying about index signatures.
|
|
592
|
-
type SimpleMerge<Destination, Source> = Simplify<{ [Key in keyof Destination as Key extends keyof Source ? never : Key]: Destination[Key] } & Source>;
|
|
596
|
+
type SimpleMerge<Destination, Source> = Simplify<{ [Key in keyof Destination as Key extends keyof Source ? never : Key]: Destination[Key]; } & Source>;
|
|
593
597
|
/**
|
|
594
598
|
Merge two types into a new type. Keys of the second type overrides keys of the first type.
|
|
595
599
|
|
|
@@ -653,10 +657,9 @@ Note: If you want a merge type that more accurately reflects the runtime behavio
|
|
|
653
657
|
@category Object
|
|
654
658
|
*/
|
|
655
659
|
type Merge<Destination, Source> = Destination extends unknown // For distributing `Destination`
|
|
656
|
-
? Source extends unknown // For distributing `Source`
|
|
657
|
-
? If<IsEqual<Destination, Source>, Destination, _Merge<Destination, Source>> : never // Should never happen
|
|
658
|
-
: never;
|
|
659
|
-
// Should never happen
|
|
660
|
+
? Source extends unknown // For distributing `Source`
|
|
661
|
+
? If<IsEqual<Destination, Source>, Destination, _Merge<Destination, Source>> : never // Should never happen
|
|
662
|
+
: never; // Should never happen
|
|
660
663
|
type _Merge<Destination, Source> = Simplify<SimpleMerge<PickIndexSignature<Destination>, PickIndexSignature<Source>> & SimpleMerge<OmitIndexSignature<Destination>, OmitIndexSignature<Source>>>;
|
|
661
664
|
/**
|
|
662
665
|
Merges user specified options with default options.
|
|
@@ -710,36 +713,9 @@ type Result = ApplyDefaultOptions<PathsOptions, DefaultPathsOptions, SpecifiedOp
|
|
|
710
713
|
// Types of property 'leavesOnly' are incompatible. Type 'string' is not assignable to type 'boolean'.
|
|
711
714
|
```
|
|
712
715
|
*/
|
|
713
|
-
type ApplyDefaultOptions<Options extends object, Defaults extends Simplify<Omit<Required<Options>, RequiredKeysOf<Options>> & Partial<Record<RequiredKeysOf<Options>, never>>>, SpecifiedOptions extends Options> = _ApplyDefaultOptions<Options, Defaults, SpecifiedOptions> extends infer Result extends Required<Options> // `extends Required<Options>` ensures that `ApplyDefaultOptions<SomeOption, ...>` is always assignable to `Required<SomeOption>`
|
|
714
|
-
? Result : never;
|
|
715
|
-
type _ApplyDefaultOptions<Options, Defaults, SpecifiedOptions> = If<IsAny<SpecifiedOptions>, Defaults, If<IsNever<SpecifiedOptions>, Defaults, Merge<Defaults, { [Key in keyof SpecifiedOptions as undefined extends Required<Options>[Key & keyof Options] ? Key : undefined extends SpecifiedOptions[Key] ? never : Key]: SpecifiedOptions[Key] }>>>;
|
|
716
|
-
/**
|
|
717
|
-
Collapses literal types in a union into their corresponding primitive types, when possible. For example, `CollapseLiterals<'foo' | 'bar' | (string & {})>` returns `string`.
|
|
718
|
-
|
|
719
|
-
Note: This doesn't collapse literals within tagged types. For example, `CollapseLiterals<Tagged<'foo' | (string & {}), 'Tag'>>` returns `("foo" & Tag<"Tag", never>) | (string & Tag<"Tag", never>)` and not `string & Tag<"Tag", never>`.
|
|
720
|
-
|
|
721
|
-
Use-case: For collapsing unions created using {@link LiteralUnion}.
|
|
722
|
-
|
|
723
|
-
@example
|
|
724
|
-
```
|
|
725
|
-
import type {LiteralUnion} from 'type-fest';
|
|
726
|
-
|
|
727
|
-
type A = CollapseLiterals<'foo' | 'bar' | (string & {})>;
|
|
728
|
-
//=> string
|
|
729
|
-
|
|
730
|
-
type B = CollapseLiterals<LiteralUnion<1 | 2 | 3, number>>;
|
|
731
|
-
//=> number
|
|
732
|
-
|
|
733
|
-
type C = CollapseLiterals<LiteralUnion<'onClick' | 'onChange', `on${string}`>>;
|
|
734
|
-
//=> `on${string}`
|
|
735
|
-
|
|
736
|
-
type D = CollapseLiterals<'click' | 'change' | (`on${string}` & {})>;
|
|
737
|
-
//=> 'click' | 'change' | `on${string}`
|
|
738
|
-
|
|
739
|
-
type E = CollapseLiterals<LiteralUnion<'foo' | 'bar', string> | null | undefined>;
|
|
740
|
-
//=> string | null | undefined
|
|
741
|
-
```
|
|
742
|
-
*/
|
|
716
|
+
type ApplyDefaultOptions<Options extends object, Defaults extends Simplify<Omit<Required<Options>, RequiredKeysOf<Options>> & Partial<Record<RequiredKeysOf<Options>, never>>>, SpecifiedOptions extends Options> = _ApplyDefaultOptions<Options, Defaults, SpecifiedOptions> extends (infer Result extends Required<Options> // `extends Required<Options>` ensures that `ApplyDefaultOptions<SomeOption, ...>` is always assignable to `Required<SomeOption>`
|
|
717
|
+
) ? Result : never;
|
|
718
|
+
type _ApplyDefaultOptions<Options, Defaults, SpecifiedOptions> = If<IsAny<SpecifiedOptions>, Defaults, If<IsNever<SpecifiedOptions>, Defaults, Merge<Defaults, { [Key in keyof SpecifiedOptions as undefined extends Required<Options>[Key & keyof Options] ? Key : undefined extends SpecifiedOptions[Key] ? never : Key]: SpecifiedOptions[Key]; }>>>;
|
|
743
719
|
/**
|
|
744
720
|
Filter out keys from an object.
|
|
745
721
|
|
|
@@ -770,10 +746,12 @@ type Filtered = Filter<'bar', 'foo'>;
|
|
|
770
746
|
type Filter<KeyType, ExcludeType> = IsEqual<KeyType, ExcludeType> extends true ? never : (KeyType extends ExcludeType ? never : KeyType);
|
|
771
747
|
type ExceptOptions = {
|
|
772
748
|
/**
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
749
|
+
Disallow assigning non-specified properties.
|
|
750
|
+
|
|
751
|
+
Note that any omitted properties in the resulting type will be present in autocomplete as `undefined`.
|
|
752
|
+
|
|
753
|
+
@default false
|
|
754
|
+
*/
|
|
777
755
|
requireExactProps?: boolean;
|
|
778
756
|
};
|
|
779
757
|
type DefaultExceptOptions = {
|
|
@@ -835,7 +813,7 @@ type PostPayloadFixed = Except<UserData, 'email'>;
|
|
|
835
813
|
@category Object
|
|
836
814
|
*/
|
|
837
815
|
type Except<ObjectType, KeysType extends keyof ObjectType, Options extends ExceptOptions = {}> = _Except<ObjectType, KeysType, ApplyDefaultOptions<ExceptOptions, DefaultExceptOptions, Options>>;
|
|
838
|
-
type _Except<ObjectType, KeysType extends keyof ObjectType, Options extends Required<ExceptOptions>> = { [KeyType in keyof ObjectType as Filter<KeyType, KeysType>]: ObjectType[KeyType] } & (Options['requireExactProps'] extends true ? Partial<Record<KeysType, never>> : {});
|
|
816
|
+
type _Except<ObjectType, KeysType extends keyof ObjectType, Options extends Required<ExceptOptions>> = { [KeyType in keyof ObjectType as Filter<KeyType, KeysType>]: ObjectType[KeyType]; } & (Options['requireExactProps'] extends true ? Partial<Record<KeysType, never>> : {});
|
|
839
817
|
declare class MockBlobClient implements Except<BlobClient, "accountName"> {
|
|
840
818
|
connectionString: string;
|
|
841
819
|
containerName: string;
|
|
@@ -883,7 +861,7 @@ declare class MockBlockBlobClient extends MockBlobClient implements Except<Block
|
|
|
883
861
|
stageBlock(): Promise<BlockBlobStageBlockResponse>;
|
|
884
862
|
stageBlockFromURL(): Promise<BlockBlobStageBlockFromURLResponse>;
|
|
885
863
|
syncUploadFromURL(): Promise<BlockBlobPutBlobFromUrlResponse>;
|
|
886
|
-
upload(body: HttpRequestBody, _contentLength: number): Promise<BlockBlobUploadResponse>;
|
|
864
|
+
upload(body: HttpRequestBody, _contentLength: number, options?: BlockBlobUploadOptions): Promise<BlockBlobUploadResponse>;
|
|
887
865
|
uploadBrowserData(): Promise<BlobUploadCommonResponse>;
|
|
888
866
|
uploadData(): Promise<BlobUploadCommonResponse>;
|
|
889
867
|
uploadFile(): Promise<BlobUploadCommonResponse>;
|
|
@@ -928,7 +906,7 @@ declare class MockContainerClient implements Except<ContainerClient, "accountNam
|
|
|
928
906
|
getPageBlobClient(): PageBlobClient;
|
|
929
907
|
getProperties(): Promise<ContainerGetPropertiesResponse>;
|
|
930
908
|
listBlobsByHierarchy(delimiter: string, options?: ContainerListBlobsOptions): PagedAsyncIterableIterator<BlobHierarchyItem, ContainerListBlobHierarchySegmentResponse>;
|
|
931
|
-
listBlobsFlat(): PagedAsyncIterableIterator<BlobItem, ContainerListBlobFlatSegmentResponse>;
|
|
909
|
+
listBlobsFlat(options?: ContainerListBlobsOptions): PagedAsyncIterableIterator<BlobItem, ContainerListBlobFlatSegmentResponse>;
|
|
932
910
|
setAccessPolicy(): Promise<ContainerSetAccessPolicyResponse>;
|
|
933
911
|
setMetadata(): Promise<ContainerSetMetadataResponse>;
|
|
934
912
|
uploadBlockBlob(blobName: string, body: HttpRequestBody, contentLength: number): Promise<{
|
|
@@ -1014,6 +992,26 @@ declare class MockSearchClient<TModel extends object = Record<string, unknown>>
|
|
|
1014
992
|
suggest<TFields extends SelectFields<TModel> = never>(_searchText: string, _suggesterName: string, _options?: SuggestOptions<TModel, TFields>): Promise<SuggestDocumentsResult<TModel, TFields>>;
|
|
1015
993
|
uploadDocuments(documents: TModel[], _options?: UploadDocumentsOptions): Promise<IndexDocumentsResult>;
|
|
1016
994
|
}
|
|
995
|
+
/**
|
|
996
|
+
* An in-memory mock of the Azure ServiceBusSender.
|
|
997
|
+
* It uses a Map to simulate a Service Bus queue and correctly implements the ServiceBusSender interface.
|
|
998
|
+
*
|
|
999
|
+
* @example
|
|
1000
|
+
* const mockServiceBusSender = new MockServiceBusSender("hello world");
|
|
1001
|
+
* await mockServiceBusSender.scheduleMessages({ body: "hello world" }, new Date());
|
|
1002
|
+
*/
|
|
1003
|
+
declare class MockServiceBusSender implements ServiceBusSender {
|
|
1004
|
+
entityPath: string;
|
|
1005
|
+
identifier: string;
|
|
1006
|
+
isClosed: boolean;
|
|
1007
|
+
get queue(): MapValue<typeof MockServiceBusDatabase>;
|
|
1008
|
+
constructor(queueName: string);
|
|
1009
|
+
cancelScheduledMessages(): Promise<void>;
|
|
1010
|
+
close(): Promise<void>;
|
|
1011
|
+
createMessageBatch(): Promise<ServiceBusMessageBatch>;
|
|
1012
|
+
scheduleMessages(messages: ServiceBusMessage | ServiceBusMessage[], scheduledEnqueueTimeUtc: Date): Promise<never[]>;
|
|
1013
|
+
sendMessages(messages: ServiceBusMessage | ServiceBusMessage[] | ServiceBusMessageBatch): Promise<void>;
|
|
1014
|
+
}
|
|
1017
1015
|
/**
|
|
1018
1016
|
* An in-memory mock of the Azure TableClient.
|
|
1019
1017
|
* It uses a Map to simulate table storage and correctly implements the TableClient interface.
|
|
@@ -1039,7 +1037,9 @@ declare class MockTableClient<TEntity extends TableEntity = TableEntity> impleme
|
|
|
1039
1037
|
listEntities<T extends object>(options?: ListTableEntitiesOptions): PagedAsyncIterableIterator<TableEntityResult<T>, TableEntityResultPage<T>>;
|
|
1040
1038
|
setAccessPolicy(): Promise<TableSetAccessPolicyHeaders>;
|
|
1041
1039
|
submitTransaction(actions: Parameters<TableClient["submitTransaction"]>[0]): Promise<TableTransactionResponse>;
|
|
1042
|
-
updateEntity<T extends object>(entity: TableEntity<T>, mode?: UpdateMode
|
|
1040
|
+
updateEntity<T extends object>(entity: TableEntity<T>, mode?: UpdateMode, options?: {
|
|
1041
|
+
etag?: string;
|
|
1042
|
+
}): Promise<TableMergeEntityHeaders>;
|
|
1043
1043
|
upsertEntity<T extends object>(entity: TableEntity<T>, mode?: UpdateMode): Promise<TableMergeEntityHeaders>;
|
|
1044
1044
|
}
|
|
1045
1045
|
/**
|
|
@@ -1059,11 +1059,13 @@ declare const bodyToBuffer: (body: HttpRequestBody) => Promise<Buffer>;
|
|
|
1059
1059
|
* @returns A formatted XML string.
|
|
1060
1060
|
*/
|
|
1061
1061
|
declare const getAzureErrorXml: (errorCode: string, errorMessage: string) => string;
|
|
1062
|
-
declare const getBlobItemXml: ({
|
|
1063
|
-
name,
|
|
1064
|
-
properties
|
|
1065
|
-
}: BlobItem) => string;
|
|
1062
|
+
declare const getBlobItemXml: ({ name, properties }: BlobItem) => string;
|
|
1066
1063
|
declare const getBlobPrefixXml: (name: string) => string;
|
|
1064
|
+
declare const getBlobUrl: (containerName: string, blobName: string) => string;
|
|
1065
|
+
declare const getBlobUrlParts: (url: string) => undefined | {
|
|
1066
|
+
blobName: string;
|
|
1067
|
+
containerName: string;
|
|
1068
|
+
};
|
|
1067
1069
|
/**
|
|
1068
1070
|
* Generates a standard Azure Storage list blobs XML response body.
|
|
1069
1071
|
* @param containerName The container name.
|
|
@@ -1077,4 +1079,5 @@ declare const applyFilter: <T extends Record<string, unknown>>(documents: T[], c
|
|
|
1077
1079
|
declare const compare: <T>(operator: BinaryOperator, leftHandSide: T, rightHandSide: null | T) => boolean;
|
|
1078
1080
|
declare const createFilterPredicate: (filter: string) => ((document: Record<string, unknown>) => boolean);
|
|
1079
1081
|
declare const isNullClause: (clause: Clause<Record<string, unknown>>) => boolean;
|
|
1080
|
-
|
|
1082
|
+
declare const compareByCompositeKey: (a: TableEntity, b: TableEntity) => number;
|
|
1083
|
+
export { BlobHierarchyItem, MOCK_BLOB_BASE_URL, MOCK_BLOB_SEEDED_CREATED_ON, MOCK_QUEUE_BASE_URL, MOCK_SEARCH_BASE_URL, MOCK_TABLE_BASE_URL, MockBlobBatchClient, MockBlobClient, MockBlockBlobClient, MockContainerClient, MockContainerCreatedOnDatabase, MockContainerDatabase, MockEventGridDatabase, MockEventGridPublisherClient, MockQueueClient, MockQueueDatabase, MockResponse, MockRestError, MockSearchClient, MockSearchDatabase, MockServiceBusDatabase, MockServiceBusSender, MockTableClient, MockTableDatabase, MockWebPubSubServiceClient, PageSettings, PagedAsyncIterableIterator, applyFilter, bodyToBuffer, compare, compareByCompositeKey, createFilterPredicate, createMockResponse, getAzureErrorXml, getBlobItemXml, getBlobPrefixXml, getBlobUrl, getBlobUrlParts, getListBlobsXml, getMockContainerCreatedOnKey, getMockSasUrl, isNullClause, isReadableStream, toWebResourceLike };
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { RestError, createHttpHeaders, createPipelineRequest } from "@azure/core-rest-pipeline";
|
|
1
2
|
import { toHttpHeadersLike } from "@azure/core-http-compat";
|
|
2
|
-
import { ID_SEPARATOR, InvalidOperationError, Operation, exhaustiveGuard, getOrCreate,
|
|
3
|
-
import { createHttpHeaders, createPipelineRequest } from "@azure/core-rest-pipeline";
|
|
3
|
+
import { ID_SEPARATOR, InvalidOperationError, Operation, exhaustiveGuard, getOrCreate, getResult, html, noop, streamToText, takeOne } from "@esposter/shared";
|
|
4
4
|
import { AnonymousCredential } from "@azure/storage-blob";
|
|
5
5
|
import { Readable } from "node:stream";
|
|
6
6
|
import { MAX_QUEUE_VISIBILITY_TIMEOUT_MS, deserializeClause, deserializeKey, getTableNullClause, serializeClauses } from "@esposter/db";
|
|
7
|
-
import { BinaryOperator } from "@esposter/db-schema";
|
|
7
|
+
import { AZURE_MAX_PAGE_SIZE, BinaryOperator } from "@esposter/db-schema";
|
|
8
8
|
var __create = Object.create;
|
|
9
9
|
var __defProp = Object.defineProperty;
|
|
10
10
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -22,7 +22,7 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
22
22
|
}
|
|
23
23
|
return to;
|
|
24
24
|
};
|
|
25
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
25
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
|
|
26
26
|
value: mod,
|
|
27
27
|
enumerable: true
|
|
28
28
|
}) : target, mod));
|
|
@@ -30,12 +30,10 @@ const MOCK_BLOB_BASE_URL = "https://mockaccount.blob.core.windows.net";
|
|
|
30
30
|
const MOCK_QUEUE_BASE_URL = "https://mockaccount.queue.core.windows.net";
|
|
31
31
|
const MOCK_SEARCH_BASE_URL = "https://mockaccount.search.windows.net";
|
|
32
32
|
const MOCK_TABLE_BASE_URL = "https://mockaccount.table.core.windows.net";
|
|
33
|
-
var MockRestError = class extends
|
|
34
|
-
statusCode;
|
|
33
|
+
var MockRestError = class extends RestError {
|
|
35
34
|
constructor(message, statusCode) {
|
|
36
|
-
super(message);
|
|
35
|
+
super(message, { statusCode });
|
|
37
36
|
this.name = "MockRestError";
|
|
38
|
-
this.statusCode = statusCode;
|
|
39
37
|
}
|
|
40
38
|
};
|
|
41
39
|
const toWebResourceLike = (request) => ({
|
|
@@ -70,10 +68,20 @@ const createMockResponse = (status, url = "") => ({
|
|
|
70
68
|
status
|
|
71
69
|
});
|
|
72
70
|
const getMockSasUrl = (url, permissions, resourceType) => `${url}?sv=2025-11-05&${resourceType ? `sr=${resourceType}&` : ""}sig=mock-signature&st=1970-01-01T00:00:00Z&se=2099-12-31T23:59:59Z&sp=${permissions?.toString() ?? "r"}`;
|
|
73
|
-
const
|
|
71
|
+
const MockContainerCreatedOnDatabase = /* @__PURE__ */ new Map();
|
|
72
|
+
const MOCK_BLOB_SEEDED_CREATED_ON = /* @__PURE__ */ new Date(-864e13);
|
|
73
|
+
const getMockContainerCreatedOnKey = (containerName, blobName) => `${containerName}/${blobName}`;
|
|
74
|
+
var MockContainerDatabaseMap = class extends Map {
|
|
75
|
+
clear() {
|
|
76
|
+
super.clear();
|
|
77
|
+
MockContainerCreatedOnDatabase.clear();
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
const MockContainerDatabase = new MockContainerDatabaseMap();
|
|
74
81
|
const MockEventGridDatabase = /* @__PURE__ */ new Map();
|
|
75
82
|
const MockQueueDatabase = /* @__PURE__ */ new Map();
|
|
76
83
|
const MockSearchDatabase = /* @__PURE__ */ new Map();
|
|
84
|
+
const MockServiceBusDatabase = /* @__PURE__ */ new Map();
|
|
77
85
|
const MockTableDatabase = /* @__PURE__ */ new Map();
|
|
78
86
|
/**
|
|
79
87
|
* Generates a standard Azure Storage error XML response body.
|
|
@@ -86,6 +94,17 @@ const getAzureErrorXml = (errorCode, errorMessage) => html`<?xml version="1.0" e
|
|
|
86
94
|
<code>${errorCode}</code>
|
|
87
95
|
<Message>${errorMessage}</Message>
|
|
88
96
|
</Error>`;
|
|
97
|
+
const decodeSegment = (segment) => getResult(() => decodeURIComponent(segment)).unwrapOr(segment);
|
|
98
|
+
const getBlobUrlParts = (url) => {
|
|
99
|
+
const parsedUrl = getResult(() => new URL(url)).unwrapOr(void 0);
|
|
100
|
+
if (!parsedUrl) return void 0;
|
|
101
|
+
const pathSegments = parsedUrl.pathname.split("/").filter(Boolean);
|
|
102
|
+
if (pathSegments.length < 2) return void 0;
|
|
103
|
+
return {
|
|
104
|
+
blobName: decodeSegment(pathSegments.slice(1).join("/")),
|
|
105
|
+
containerName: decodeSegment(takeOne(pathSegments))
|
|
106
|
+
};
|
|
107
|
+
};
|
|
89
108
|
var MockBlobBatchClient = class {
|
|
90
109
|
url;
|
|
91
110
|
constructor(url) {
|
|
@@ -102,8 +121,8 @@ var MockBlobBatchClient = class {
|
|
|
102
121
|
let subResponsesSucceededCount = 0;
|
|
103
122
|
let subResponsesFailedCount = 0;
|
|
104
123
|
for (const url of urls) {
|
|
105
|
-
const
|
|
106
|
-
if (
|
|
124
|
+
const urlParts = getBlobUrlParts(url);
|
|
125
|
+
if (!urlParts) {
|
|
107
126
|
const errorCode = "InvalidUri";
|
|
108
127
|
const statusMessage = "Invalid blob URL format.";
|
|
109
128
|
subResponses.push({
|
|
@@ -120,11 +139,11 @@ var MockBlobBatchClient = class {
|
|
|
120
139
|
subResponsesFailedCount++;
|
|
121
140
|
continue;
|
|
122
141
|
}
|
|
123
|
-
const containerName =
|
|
124
|
-
const blobName = pathSegments.slice(1).join("/");
|
|
142
|
+
const { blobName, containerName } = urlParts;
|
|
125
143
|
const container = this.getContainer(containerName);
|
|
126
144
|
if (container.has(blobName)) {
|
|
127
145
|
container.delete(blobName);
|
|
146
|
+
MockContainerCreatedOnDatabase.delete(getMockContainerCreatedOnKey(containerName, blobName));
|
|
128
147
|
subResponses.push({
|
|
129
148
|
_request: {
|
|
130
149
|
credential,
|
|
@@ -169,6 +188,7 @@ var MockBlobBatchClient = class {
|
|
|
169
188
|
return getOrCreate(MockContainerDatabase, containerName, () => /* @__PURE__ */ new Map());
|
|
170
189
|
}
|
|
171
190
|
};
|
|
191
|
+
const getBlobUrl = (containerName, blobName) => `${MOCK_BLOB_BASE_URL}/${containerName}/${blobName.split("/").map((segment) => encodeURIComponent(segment)).join("/")}`;
|
|
172
192
|
var MockBlobClient = class {
|
|
173
193
|
connectionString;
|
|
174
194
|
containerName;
|
|
@@ -182,21 +202,21 @@ var MockBlobClient = class {
|
|
|
182
202
|
this.connectionString = connectionString;
|
|
183
203
|
this.containerName = containerName;
|
|
184
204
|
this.name = blobName;
|
|
185
|
-
this.url =
|
|
205
|
+
this.url = getBlobUrl(this.containerName, this.name);
|
|
186
206
|
}
|
|
187
207
|
abortCopyFromURL() {
|
|
188
208
|
throw new Error("Method not implemented.");
|
|
189
209
|
}
|
|
190
210
|
beginCopyFromURL(copySource) {
|
|
191
|
-
const
|
|
192
|
-
if (
|
|
193
|
-
const sourceContainerName =
|
|
194
|
-
const sourceBlobName = pathSegments.slice(1).join("/");
|
|
211
|
+
const sourceParts = getBlobUrlParts(copySource);
|
|
212
|
+
if (!sourceParts) throw new MockRestError("Invalid copy source URL format", 400);
|
|
213
|
+
const { blobName: sourceBlobName, containerName: sourceContainerName } = sourceParts;
|
|
195
214
|
const sourceContainer = MockContainerDatabase.get(sourceContainerName);
|
|
196
215
|
if (!sourceContainer) throw new MockRestError("Source container not found", 404);
|
|
197
216
|
const sourceData = sourceContainer.get(sourceBlobName);
|
|
198
217
|
if (!sourceData) throw new MockRestError("Source blob not found", 404);
|
|
199
218
|
this.container.set(this.name, Buffer.from(sourceData));
|
|
219
|
+
MockContainerCreatedOnDatabase.set(getMockContainerCreatedOnKey(this.containerName, this.name), /* @__PURE__ */ new Date());
|
|
200
220
|
const response = { _response: createMockResponse(202, `${this.url}?comp=copy`) };
|
|
201
221
|
return Promise.resolve({
|
|
202
222
|
cancelOperation: () => Promise.resolve(),
|
|
@@ -219,11 +239,15 @@ var MockBlobClient = class {
|
|
|
219
239
|
delete() {
|
|
220
240
|
if (!this.container.has(this.name)) throw new MockRestError("The specified blob does not exist.", 404);
|
|
221
241
|
this.container.delete(this.name);
|
|
242
|
+
MockContainerCreatedOnDatabase.delete(getMockContainerCreatedOnKey(this.containerName, this.name));
|
|
222
243
|
return Promise.resolve({ _response: createMockResponse(200) });
|
|
223
244
|
}
|
|
224
245
|
deleteIfExists() {
|
|
225
246
|
const succeeded = this.container.has(this.name);
|
|
226
|
-
if (succeeded)
|
|
247
|
+
if (succeeded) {
|
|
248
|
+
this.container.delete(this.name);
|
|
249
|
+
MockContainerCreatedOnDatabase.delete(getMockContainerCreatedOnKey(this.containerName, this.name));
|
|
250
|
+
}
|
|
227
251
|
return Promise.resolve({
|
|
228
252
|
_response: createMockResponse(succeeded ? 200 : 404),
|
|
229
253
|
succeeded
|
|
@@ -248,7 +272,7 @@ var MockBlobClient = class {
|
|
|
248
272
|
throw new Error("Method not implemented.");
|
|
249
273
|
}
|
|
250
274
|
exists() {
|
|
251
|
-
|
|
275
|
+
return Promise.resolve(this.container.has(this.name));
|
|
252
276
|
}
|
|
253
277
|
generateSasStringToSign() {
|
|
254
278
|
throw new Error("Method not implemented.");
|
|
@@ -361,8 +385,11 @@ var MockBlockBlobClient = class extends MockBlobClient {
|
|
|
361
385
|
syncUploadFromURL() {
|
|
362
386
|
throw new Error("Method not implemented.");
|
|
363
387
|
}
|
|
364
|
-
async upload(body, _contentLength) {
|
|
365
|
-
|
|
388
|
+
async upload(body, _contentLength, options) {
|
|
389
|
+
const buffer = await bodyToBuffer(body);
|
|
390
|
+
if (options?.conditions?.ifNoneMatch === "*" && this.container.has(this.name)) throw new MockRestError("The specified blob already exists.", 409);
|
|
391
|
+
this.container.set(this.name, buffer);
|
|
392
|
+
MockContainerCreatedOnDatabase.set(getMockContainerCreatedOnKey(this.containerName, this.name), /* @__PURE__ */ new Date());
|
|
366
393
|
return { _response: createMockResponse(201) };
|
|
367
394
|
}
|
|
368
395
|
uploadBrowserData() {
|
|
@@ -437,7 +464,8 @@ var MockContainerClient = class {
|
|
|
437
464
|
deleteBlob(blobName) {
|
|
438
465
|
if (!this.container.has(blobName)) throw new MockRestError("The specified blob does not exist.", 404);
|
|
439
466
|
this.container.delete(blobName);
|
|
440
|
-
|
|
467
|
+
MockContainerCreatedOnDatabase.delete(getMockContainerCreatedOnKey(this.containerName, blobName));
|
|
468
|
+
return Promise.resolve({ _response: createMockResponse(200, getBlobUrl(this.containerName, blobName)) });
|
|
441
469
|
}
|
|
442
470
|
deleteIfExists() {
|
|
443
471
|
throw new Error("Method not implemented.");
|
|
@@ -527,8 +555,8 @@ var MockContainerClient = class {
|
|
|
527
555
|
}
|
|
528
556
|
};
|
|
529
557
|
}
|
|
530
|
-
listBlobsFlat() {
|
|
531
|
-
const blobItemIterator = this.#getBlobItemIterator();
|
|
558
|
+
listBlobsFlat(options) {
|
|
559
|
+
const blobItemIterator = this.#getBlobItemIterator(options);
|
|
532
560
|
return {
|
|
533
561
|
byPage: () => async function* () {
|
|
534
562
|
const allBlobItems = [];
|
|
@@ -544,14 +572,14 @@ var MockContainerClient = class {
|
|
|
544
572
|
parsedBody: {
|
|
545
573
|
containerName: this.containerName,
|
|
546
574
|
marker: "",
|
|
547
|
-
prefix: "",
|
|
575
|
+
prefix: options?.prefix ?? "",
|
|
548
576
|
segment: { blobItems: allBlobItems },
|
|
549
577
|
serviceEndpoint: ""
|
|
550
578
|
}
|
|
551
579
|
},
|
|
552
580
|
containerName: this.containerName,
|
|
553
581
|
marker: "",
|
|
554
|
-
prefix: "",
|
|
582
|
+
prefix: options?.prefix ?? "",
|
|
555
583
|
segment: { blobItems: allBlobItems },
|
|
556
584
|
serviceEndpoint: ""
|
|
557
585
|
});
|
|
@@ -590,6 +618,7 @@ var MockContainerClient = class {
|
|
|
590
618
|
blobType: "BlockBlob",
|
|
591
619
|
contentLength: buffer.length,
|
|
592
620
|
contentType: "application/octet-stream",
|
|
621
|
+
createdOn: MockContainerCreatedOnDatabase.get(getMockContainerCreatedOnKey(this.containerName, name)) ?? MOCK_BLOB_SEEDED_CREATED_ON,
|
|
593
622
|
etag: `"${crypto.randomUUID()}"`,
|
|
594
623
|
lastModified: /* @__PURE__ */ new Date(),
|
|
595
624
|
leaseState: "available",
|
|
@@ -611,21 +640,26 @@ var MockContainerClient = class {
|
|
|
611
640
|
...blobItem
|
|
612
641
|
});
|
|
613
642
|
}
|
|
614
|
-
async *#getBlobItemIterator() {
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
name
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
643
|
+
async *#getBlobItemIterator(options) {
|
|
644
|
+
const prefix = options?.prefix ?? "";
|
|
645
|
+
for (const [name, buffer] of this.container.entries()) {
|
|
646
|
+
if (!name.startsWith(prefix)) continue;
|
|
647
|
+
yield await Promise.resolve({
|
|
648
|
+
deleted: false,
|
|
649
|
+
name,
|
|
650
|
+
properties: {
|
|
651
|
+
blobType: "BlockBlob",
|
|
652
|
+
contentLength: buffer.length,
|
|
653
|
+
contentType: "application/octet-stream",
|
|
654
|
+
createdOn: MockContainerCreatedOnDatabase.get(getMockContainerCreatedOnKey(this.containerName, name)) ?? MOCK_BLOB_SEEDED_CREATED_ON,
|
|
655
|
+
etag: `"${crypto.randomUUID()}"`,
|
|
656
|
+
lastModified: /* @__PURE__ */ new Date(),
|
|
657
|
+
leaseState: "available",
|
|
658
|
+
leaseStatus: "unlocked"
|
|
659
|
+
},
|
|
660
|
+
snapshot: ""
|
|
661
|
+
});
|
|
662
|
+
}
|
|
629
663
|
}
|
|
630
664
|
};
|
|
631
665
|
/**
|
|
@@ -836,7 +870,8 @@ var import_fast_deep_equal = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJS
|
|
|
836
870
|
};
|
|
837
871
|
})))(), 1);
|
|
838
872
|
const isNullClause = (clause) => {
|
|
839
|
-
|
|
873
|
+
const tableNullClause = getTableNullClause(clause.key);
|
|
874
|
+
return (0, import_fast_deep_equal.default)(clause, tableNullClause);
|
|
840
875
|
};
|
|
841
876
|
const createFilterPredicate = (filter) => {
|
|
842
877
|
const orGroups = filter.replaceAll(String.raw`(`, " ").replaceAll(String.raw`)`, "").split(/\s+and\s+/iu).filter(Boolean).map((group) => group.split(/\s+or\s+/iu).filter(Boolean));
|
|
@@ -962,6 +997,53 @@ var MockSearchClient = class {
|
|
|
962
997
|
}
|
|
963
998
|
};
|
|
964
999
|
/**
|
|
1000
|
+
* An in-memory mock of the Azure ServiceBusSender.
|
|
1001
|
+
* It uses a Map to simulate a Service Bus queue and correctly implements the ServiceBusSender interface.
|
|
1002
|
+
*
|
|
1003
|
+
* @example
|
|
1004
|
+
* const mockServiceBusSender = new MockServiceBusSender("hello world");
|
|
1005
|
+
* await mockServiceBusSender.scheduleMessages({ body: "hello world" }, new Date());
|
|
1006
|
+
*/
|
|
1007
|
+
var MockServiceBusSender = class {
|
|
1008
|
+
entityPath;
|
|
1009
|
+
identifier;
|
|
1010
|
+
isClosed = false;
|
|
1011
|
+
get queue() {
|
|
1012
|
+
return getOrCreate(MockServiceBusDatabase, this.entityPath, () => []);
|
|
1013
|
+
}
|
|
1014
|
+
constructor(queueName) {
|
|
1015
|
+
this.entityPath = queueName;
|
|
1016
|
+
this.identifier = queueName;
|
|
1017
|
+
}
|
|
1018
|
+
cancelScheduledMessages() {
|
|
1019
|
+
throw new Error("Method not implemented.");
|
|
1020
|
+
}
|
|
1021
|
+
close() {
|
|
1022
|
+
this.isClosed = true;
|
|
1023
|
+
return Promise.resolve();
|
|
1024
|
+
}
|
|
1025
|
+
createMessageBatch() {
|
|
1026
|
+
throw new Error("Method not implemented.");
|
|
1027
|
+
}
|
|
1028
|
+
scheduleMessages(messages, scheduledEnqueueTimeUtc) {
|
|
1029
|
+
for (const message of Array.isArray(messages) ? messages : [messages]) this.queue.push({
|
|
1030
|
+
...message,
|
|
1031
|
+
scheduledEnqueueTimeUtc
|
|
1032
|
+
});
|
|
1033
|
+
return Promise.resolve([]);
|
|
1034
|
+
}
|
|
1035
|
+
sendMessages(messages) {
|
|
1036
|
+
if (!Array.isArray(messages) && "tryAddMessage" in messages) throw new Error("Method not implemented.");
|
|
1037
|
+
this.queue.push(...Array.isArray(messages) ? messages : [messages]);
|
|
1038
|
+
return Promise.resolve();
|
|
1039
|
+
}
|
|
1040
|
+
};
|
|
1041
|
+
const compareByCompositeKey = (a, b) => {
|
|
1042
|
+
if (a.partitionKey !== b.partitionKey) return a.partitionKey < b.partitionKey ? -1 : 1;
|
|
1043
|
+
else if (a.rowKey === b.rowKey) return 0;
|
|
1044
|
+
else return a.rowKey < b.rowKey ? -1 : 1;
|
|
1045
|
+
};
|
|
1046
|
+
/**
|
|
965
1047
|
* An in-memory mock of the Azure TableClient.
|
|
966
1048
|
* It uses a Map to simulate table storage and correctly implements the TableClient interface.
|
|
967
1049
|
*
|
|
@@ -981,9 +1063,7 @@ var MockTableClient = class {
|
|
|
981
1063
|
this.url = `${MOCK_TABLE_BASE_URL}/${this.tableName}`;
|
|
982
1064
|
}
|
|
983
1065
|
createEntity(entity) {
|
|
984
|
-
|
|
985
|
-
if (this.table.has(key)) throw new MockRestError("The specified entity already exists.", 409);
|
|
986
|
-
this.table.set(key, entity);
|
|
1066
|
+
this.#applyCreate(entity);
|
|
987
1067
|
return Promise.resolve({
|
|
988
1068
|
date: /* @__PURE__ */ new Date(),
|
|
989
1069
|
etag: this.#getEtag()
|
|
@@ -993,9 +1073,7 @@ var MockTableClient = class {
|
|
|
993
1073
|
throw new Error("Method not implemented.");
|
|
994
1074
|
}
|
|
995
1075
|
deleteEntity(partitionKey, rowKey) {
|
|
996
|
-
|
|
997
|
-
if (!this.table.has(key)) throw new MockRestError("The specified resource does not exist.", 404);
|
|
998
|
-
this.table.delete(key);
|
|
1076
|
+
this.#applyDelete(partitionKey, rowKey);
|
|
999
1077
|
return Promise.resolve({});
|
|
1000
1078
|
}
|
|
1001
1079
|
deleteTable() {
|
|
@@ -1010,18 +1088,22 @@ var MockTableClient = class {
|
|
|
1010
1088
|
if (!entity) throw new MockRestError("The specified resource does not exist.", 404);
|
|
1011
1089
|
return Promise.resolve({
|
|
1012
1090
|
...entity,
|
|
1013
|
-
etag: this.#getEtag()
|
|
1091
|
+
etag: entity.etag ?? this.#getEtag()
|
|
1014
1092
|
});
|
|
1015
1093
|
}
|
|
1016
1094
|
listEntities(options) {
|
|
1017
1095
|
const withMetadata = this.#withMetadata.bind(this);
|
|
1018
1096
|
const filter = options?.queryOptions?.filter;
|
|
1019
|
-
const tableEntities = [...this.table.values()];
|
|
1097
|
+
const tableEntities = [...this.table.values()].toSorted(compareByCompositeKey);
|
|
1020
1098
|
const predicate = filter ? createFilterPredicate(filter) : void 0;
|
|
1021
1099
|
const resultTableEntities = predicate ? tableEntities.filter((e) => predicate(e)) : tableEntities;
|
|
1100
|
+
const entityIterator = (async function* (entities) {
|
|
1101
|
+
for (const entity of entities) yield await Promise.resolve(withMetadata(entity));
|
|
1102
|
+
})(resultTableEntities);
|
|
1022
1103
|
return {
|
|
1023
1104
|
byPage: ({ maxPageSize } = {}) => (async function* (entities) {
|
|
1024
1105
|
if (maxPageSize !== void 0 && maxPageSize <= 0) throw new RangeError("maxPageSize must be greater than 0.");
|
|
1106
|
+
else if (maxPageSize !== void 0 && maxPageSize > AZURE_MAX_PAGE_SIZE) throw new MockRestError("One of the request inputs is not valid.", 400);
|
|
1025
1107
|
const allEntitiesWithMetadata = entities.map((e) => withMetadata(e));
|
|
1026
1108
|
if (allEntitiesWithMetadata.length === 0) return;
|
|
1027
1109
|
else if (!maxPageSize) {
|
|
@@ -1030,9 +1112,7 @@ var MockTableClient = class {
|
|
|
1030
1112
|
}
|
|
1031
1113
|
for (let i = 0; i < allEntitiesWithMetadata.length; i += maxPageSize) yield await Promise.resolve(allEntitiesWithMetadata.slice(i, i + maxPageSize));
|
|
1032
1114
|
})(resultTableEntities),
|
|
1033
|
-
next: () => (
|
|
1034
|
-
for (const entity of entities) yield await Promise.resolve(withMetadata(entity));
|
|
1035
|
-
})(resultTableEntities).next(),
|
|
1115
|
+
next: () => entityIterator.next(),
|
|
1036
1116
|
[Symbol.asyncIterator]() {
|
|
1037
1117
|
return this;
|
|
1038
1118
|
}
|
|
@@ -1041,24 +1121,24 @@ var MockTableClient = class {
|
|
|
1041
1121
|
setAccessPolicy() {
|
|
1042
1122
|
throw new Error("Method not implemented.");
|
|
1043
1123
|
}
|
|
1044
|
-
|
|
1124
|
+
submitTransaction(actions) {
|
|
1045
1125
|
let partitionKey;
|
|
1046
1126
|
for (const [, entity] of actions) if (partitionKey === void 0) partitionKey = entity.partitionKey;
|
|
1047
1127
|
else if (entity.partitionKey !== partitionKey) throw new MockRestError("All transaction actions must target the same partitionKey.", 400);
|
|
1048
1128
|
const snapshot = new Map(this.table);
|
|
1049
|
-
|
|
1129
|
+
getResult(() => {
|
|
1050
1130
|
for (const [type, entity, updateMode] of actions) switch (type) {
|
|
1051
1131
|
case "create":
|
|
1052
|
-
|
|
1132
|
+
this.#applyCreate(entity);
|
|
1053
1133
|
break;
|
|
1054
1134
|
case "delete":
|
|
1055
|
-
|
|
1135
|
+
this.#applyDelete(entity.partitionKey, entity.rowKey);
|
|
1056
1136
|
break;
|
|
1057
1137
|
case "update":
|
|
1058
|
-
|
|
1138
|
+
this.#applyUpdate(entity, updateMode);
|
|
1059
1139
|
break;
|
|
1060
1140
|
case "upsert":
|
|
1061
|
-
|
|
1141
|
+
this.#applyUpsert(entity, updateMode);
|
|
1062
1142
|
break;
|
|
1063
1143
|
default: exhaustiveGuard(type);
|
|
1064
1144
|
}
|
|
@@ -1068,57 +1148,77 @@ var MockTableClient = class {
|
|
|
1068
1148
|
}).match(noop, (error) => {
|
|
1069
1149
|
throw error;
|
|
1070
1150
|
});
|
|
1071
|
-
return {
|
|
1072
|
-
getResponseForEntity:
|
|
1151
|
+
return Promise.resolve({
|
|
1152
|
+
getResponseForEntity: () => void 0,
|
|
1073
1153
|
status: 202,
|
|
1074
1154
|
subResponses: []
|
|
1075
|
-
};
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1157
|
+
updateEntity(entity, mode = "Merge", options) {
|
|
1158
|
+
this.#applyUpdate(entity, mode, options?.etag);
|
|
1159
|
+
return Promise.resolve({
|
|
1160
|
+
date: /* @__PURE__ */ new Date(),
|
|
1161
|
+
etag: this.#getEtag()
|
|
1162
|
+
});
|
|
1163
|
+
}
|
|
1164
|
+
upsertEntity(entity, mode = "Merge") {
|
|
1165
|
+
this.#applyUpsert(entity, mode);
|
|
1166
|
+
return Promise.resolve({
|
|
1167
|
+
date: /* @__PURE__ */ new Date(),
|
|
1168
|
+
etag: this.#getEtag()
|
|
1169
|
+
});
|
|
1170
|
+
}
|
|
1171
|
+
#applyCreate(entity) {
|
|
1172
|
+
const key = this.#getCompositeKey(entity.partitionKey, entity.rowKey);
|
|
1173
|
+
if (this.table.has(key)) throw new MockRestError("The specified entity already exists.", 409);
|
|
1174
|
+
this.table.set(key, {
|
|
1175
|
+
...entity,
|
|
1176
|
+
etag: this.#getEtag()
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
#applyDelete(partitionKey, rowKey) {
|
|
1180
|
+
const key = this.#getCompositeKey(partitionKey, rowKey);
|
|
1181
|
+
if (!this.table.has(key)) throw new MockRestError("The specified resource does not exist.", 404);
|
|
1182
|
+
this.table.delete(key);
|
|
1076
1183
|
}
|
|
1077
|
-
|
|
1184
|
+
#applyUpdate(entity, mode = "Merge", etag) {
|
|
1078
1185
|
const key = this.#getCompositeKey(entity.partitionKey, entity.rowKey);
|
|
1079
1186
|
const existingEntity = this.table.get(key);
|
|
1080
1187
|
if (!existingEntity) throw new MockRestError("The specified resource does not exist.", 404);
|
|
1081
|
-
else if (
|
|
1082
|
-
else {
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1188
|
+
else if (etag !== void 0 && etag !== "*" && etag !== existingEntity.etag) throw new MockRestError("The update condition specified in the request was not satisfied.", 412);
|
|
1189
|
+
else if (mode === "Merge") this.table.set(key, {
|
|
1190
|
+
...existingEntity,
|
|
1191
|
+
...entity,
|
|
1192
|
+
etag: this.#getEtag()
|
|
1193
|
+
});
|
|
1194
|
+
else this.table.set(key, {
|
|
1195
|
+
...entity,
|
|
1196
|
+
etag: this.#getEtag()
|
|
1197
|
+
});
|
|
1089
1198
|
}
|
|
1090
|
-
|
|
1199
|
+
#applyUpsert(entity, mode = "Merge") {
|
|
1091
1200
|
const key = this.#getCompositeKey(entity.partitionKey, entity.rowKey);
|
|
1092
1201
|
const existingEntity = this.table.get(key);
|
|
1093
|
-
if (existingEntity && mode === "Merge")
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1202
|
+
if (existingEntity && mode === "Merge") this.table.set(key, {
|
|
1203
|
+
...existingEntity,
|
|
1204
|
+
...entity,
|
|
1205
|
+
etag: this.#getEtag()
|
|
1206
|
+
});
|
|
1207
|
+
else this.table.set(key, {
|
|
1208
|
+
...entity,
|
|
1209
|
+
etag: this.#getEtag()
|
|
1210
|
+
});
|
|
1101
1211
|
}
|
|
1102
1212
|
#getCompositeKey(partitionKey, rowKey) {
|
|
1103
1213
|
return `${partitionKey}${ID_SEPARATOR}${rowKey}`;
|
|
1104
1214
|
}
|
|
1105
1215
|
#getEtag() {
|
|
1106
|
-
return `W/"
|
|
1107
|
-
}
|
|
1108
|
-
#mergeEntity(key, entity, entityToMerge) {
|
|
1109
|
-
this.table.set(key, {
|
|
1110
|
-
...entity,
|
|
1111
|
-
...entityToMerge
|
|
1112
|
-
});
|
|
1113
|
-
return Promise.resolve({
|
|
1114
|
-
date: /* @__PURE__ */ new Date(),
|
|
1115
|
-
etag: this.#getEtag()
|
|
1116
|
-
});
|
|
1216
|
+
return `W/"${crypto.randomUUID()}"`;
|
|
1117
1217
|
}
|
|
1118
1218
|
#withMetadata(entity) {
|
|
1119
1219
|
return {
|
|
1120
1220
|
...entity,
|
|
1121
|
-
etag: this.#getEtag()
|
|
1221
|
+
etag: entity.etag ?? this.#getEtag()
|
|
1122
1222
|
};
|
|
1123
1223
|
}
|
|
1124
1224
|
};
|
|
@@ -1135,4 +1235,4 @@ const applyFilter = (documents, clauses) => {
|
|
|
1135
1235
|
const predicate = createFilterPredicate(serializeClauses(clauses));
|
|
1136
1236
|
return documents.filter((document) => predicate(document));
|
|
1137
1237
|
};
|
|
1138
|
-
export { MOCK_BLOB_BASE_URL, MOCK_QUEUE_BASE_URL, MOCK_SEARCH_BASE_URL, MOCK_TABLE_BASE_URL, MockBlobBatchClient, MockBlobClient, MockBlockBlobClient, MockContainerClient, MockContainerDatabase, MockEventGridDatabase, MockEventGridPublisherClient, MockQueueClient, MockQueueDatabase, MockRestError, MockSearchClient, MockSearchDatabase, MockTableClient, MockTableDatabase, MockWebPubSubServiceClient, applyFilter, bodyToBuffer, compare, createFilterPredicate, createMockResponse, getAzureErrorXml, getBlobItemXml, getBlobPrefixXml, getListBlobsXml, getMockSasUrl, isNullClause, isReadableStream, toWebResourceLike };
|
|
1238
|
+
export { MOCK_BLOB_BASE_URL, MOCK_BLOB_SEEDED_CREATED_ON, MOCK_QUEUE_BASE_URL, MOCK_SEARCH_BASE_URL, MOCK_TABLE_BASE_URL, MockBlobBatchClient, MockBlobClient, MockBlockBlobClient, MockContainerClient, MockContainerCreatedOnDatabase, MockContainerDatabase, MockEventGridDatabase, MockEventGridPublisherClient, MockQueueClient, MockQueueDatabase, MockRestError, MockSearchClient, MockSearchDatabase, MockServiceBusDatabase, MockServiceBusSender, MockTableClient, MockTableDatabase, MockWebPubSubServiceClient, applyFilter, bodyToBuffer, compare, compareByCompositeKey, createFilterPredicate, createMockResponse, getAzureErrorXml, getBlobItemXml, getBlobPrefixXml, getBlobUrl, getBlobUrlParts, getListBlobsXml, getMockContainerCreatedOnKey, getMockSasUrl, isNullClause, isReadableStream, toWebResourceLike };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "azure-mock",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.36.0",
|
|
4
4
|
"description": "A library that contains azure mock classes.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"azure",
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"eventgrid",
|
|
11
11
|
"mock",
|
|
12
12
|
"search",
|
|
13
|
+
"service-bus",
|
|
13
14
|
"storage-queue",
|
|
14
15
|
"table-storage",
|
|
15
16
|
"testing",
|
|
@@ -46,27 +47,28 @@
|
|
|
46
47
|
"typecheck": "tsgo"
|
|
47
48
|
},
|
|
48
49
|
"dependencies": {
|
|
49
|
-
"@esposter/db": "2.
|
|
50
|
-
"@esposter/db-schema": "2.
|
|
51
|
-
"@esposter/shared": "2.
|
|
50
|
+
"@esposter/db": "2.36.0",
|
|
51
|
+
"@esposter/db-schema": "2.36.0",
|
|
52
|
+
"@esposter/shared": "2.36.0",
|
|
52
53
|
"fast-deep-equal": "^3.1.3"
|
|
53
54
|
},
|
|
54
55
|
"devDependencies": {
|
|
55
|
-
"@esposter/configuration": "2.
|
|
56
|
-
"ctix": "^2.8.
|
|
57
|
-
"rolldown": "^1.1
|
|
56
|
+
"@esposter/configuration": "2.36.0",
|
|
57
|
+
"ctix": "^2.8.2",
|
|
58
|
+
"rolldown": "^1.2.1",
|
|
58
59
|
"type-fest": "^5.8.0",
|
|
59
|
-
"typedoc": "^0.28.
|
|
60
|
-
"vitest": "^4.1.
|
|
60
|
+
"typedoc": "^0.28.20",
|
|
61
|
+
"vitest": "^4.1.10"
|
|
61
62
|
},
|
|
62
63
|
"peerDependencies": {
|
|
63
|
-
"@azure/core-http-compat": "^2.
|
|
64
|
-
"@azure/core-rest-pipeline": "^1.
|
|
64
|
+
"@azure/core-http-compat": "^2.5.0",
|
|
65
|
+
"@azure/core-rest-pipeline": "^1.25.0",
|
|
65
66
|
"@azure/data-tables": "^13.3.2",
|
|
66
67
|
"@azure/eventgrid": "^5.12.0",
|
|
67
68
|
"@azure/search-documents": "^13.0.0",
|
|
69
|
+
"@azure/service-bus": "^7.9.5",
|
|
68
70
|
"@azure/storage-blob": "^12.33.0",
|
|
69
71
|
"@azure/storage-queue": "^12.31.0"
|
|
70
72
|
},
|
|
71
|
-
"gitHead": "
|
|
73
|
+
"gitHead": "786902aca40292b2883de2ad1eb876ca32f6ac75"
|
|
72
74
|
}
|