azure-mock 4.3.3 → 4.5.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 +22 -33
- package/dist/index.d.ts +30 -16
- package/dist/index.js +114 -69
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -5,43 +5,26 @@
|
|
|
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
|
-
|
|
8
|
+
It provides mock Azure service classes for local development and testing. They are in-memory implementations of the Azure Table Storage, Blob Storage, Service Bus and EventGrid clients, and they mirror the real Azure SDK interfaces.
|
|
9
|
+
|
|
10
|
+
Design notes live in [the specs](https://github.com/Esposter/Esposter/tree/main/openspec/specs).
|
|
9
11
|
|
|
10
12
|
## Table of Contents
|
|
11
13
|
|
|
12
14
|
- 🚀 [Getting Started](#getting-started)
|
|
13
|
-
-
|
|
15
|
+
- 🧩 [Key Exports](#key-exports)
|
|
14
16
|
- ⚖️ [License](#license)
|
|
15
17
|
|
|
16
18
|
---
|
|
17
19
|
|
|
18
20
|
## <a name="getting-started">🚀 Getting Started</a>
|
|
19
21
|
|
|
22
|
+
Install the package and its Azure peer packages as dev dependencies:
|
|
23
|
+
|
|
20
24
|
```bash
|
|
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
|
|
25
|
+
pnpm i -D azure-mock @azure/core-http-compat @azure/core-rest-pipeline @azure/data-tables @azure/eventgrid @azure/search-documents @azure/service-bus @azure/storage-blob @azure/storage-queue
|
|
22
26
|
```
|
|
23
27
|
|
|
24
|
-
## <a name="documentation">📖 Documentation</a>
|
|
25
|
-
|
|
26
|
-
We highly recommend you take a look at the [documentation](https://github.com/Esposter/Esposter/tree/main/openspec/specs) to level up.
|
|
27
|
-
|
|
28
|
-
### Available Mocks
|
|
29
|
-
|
|
30
|
-
| Class | Azure Equivalent | Description |
|
|
31
|
-
| ------------------------------ | -------------------------- | -------------------------------------------- |
|
|
32
|
-
| `MockTableClient` | `TableClient` | In-memory Azure Table Storage |
|
|
33
|
-
| `MockContainerClient` | `ContainerClient` | In-memory blob container, listing and paging |
|
|
34
|
-
| `MockBlobClient` | `BlobClient` | In-memory blob read, copy and delete |
|
|
35
|
-
| `MockBlockBlobClient` | `BlockBlobClient` | In-memory block blob upload and download |
|
|
36
|
-
| `MockBlobBatchClient` | `BlobBatchClient` | In-memory batched blob deletion |
|
|
37
|
-
| `MockSearchClient` | `SearchClient` | In-memory Azure AI Search index |
|
|
38
|
-
| `MockQueueClient` | `QueueClient` | In-memory Azure Storage Queue |
|
|
39
|
-
| `MockServiceBusSender` | `ServiceBusSender` | In-memory Azure Service Bus |
|
|
40
|
-
| `MockWebPubSubServiceClient` | `WebPubSubServiceClient` | In-memory Azure Web PubSub |
|
|
41
|
-
| `MockEventGridPublisherClient` | `EventGridPublisherClient` | In-memory EventGrid publisher |
|
|
42
|
-
|
|
43
|
-
Each client's state lives in an exported `Mock*Database` map (`MockTableDatabase`, `MockContainerDatabase`, …), so a test resets by clearing the one it seeded.
|
|
44
|
-
|
|
45
28
|
### Usage
|
|
46
29
|
|
|
47
30
|
```ts
|
|
@@ -53,18 +36,24 @@ await mockTableClient.upsertEntity({ partitionKey: "pk", rowKey: "rk", value: 42
|
|
|
53
36
|
const entity = await mockTableClient.getEntity("pk", "rk");
|
|
54
37
|
```
|
|
55
38
|
|
|
56
|
-
Replace real Azure
|
|
39
|
+
Replace each real Azure client with its mock equivalent. Make the replacement in your test setup or in your local `.env` configuration.
|
|
57
40
|
|
|
58
|
-
|
|
41
|
+
## <a name="key-exports">🧩 Key Exports</a>
|
|
59
42
|
|
|
60
|
-
|
|
43
|
+
| Class | Azure Equivalent | Description |
|
|
44
|
+
| ------------------------------ | -------------------------- | ------------------------------------------------------------------------------------------ |
|
|
45
|
+
| `MockBlobBatchClient` | `BlobBatchClient` | In-memory batched blob deletion |
|
|
46
|
+
| `MockBlobClient` | `BlobClient` | In-memory blob read, copy and delete |
|
|
47
|
+
| `MockBlockBlobClient` | `BlockBlobClient` | In-memory block blob upload and download |
|
|
48
|
+
| `MockContainerClient` | `ContainerClient` | In-memory blob container, listing and paging |
|
|
49
|
+
| `MockEventGridPublisherClient` | `EventGridPublisherClient` | In-memory EventGrid publisher |
|
|
50
|
+
| `MockQueueClient` | `QueueClient` | In-memory Azure Storage Queue |
|
|
51
|
+
| `MockSearchClient` | `SearchClient` | In-memory Azure AI Search index |
|
|
52
|
+
| `MockServiceBusSender` | `ServiceBusSender` | In-memory Azure Service Bus |
|
|
53
|
+
| `MockTableClient` | `TableClient` | In-memory Azure Table Storage |
|
|
54
|
+
| `MockWebPubSubServiceClient` | `WebPubSubServiceClient` | In-memory group membership: `group().addUser` and `removeUser`; `sendToAll` reaches nobody |
|
|
61
55
|
|
|
62
|
-
|
|
63
|
-
pnpm build # compile to dist/
|
|
64
|
-
pnpm test # vitest watch mode (coverage is run from the repo root)
|
|
65
|
-
pnpm lint:fix # auto-fix lint
|
|
66
|
-
pnpm typecheck # type check
|
|
67
|
-
```
|
|
56
|
+
Each client stores its state in an exported `Mock*Database` map, such as `MockTableDatabase` or `MockContainerDatabase`. To reset a test, clear the map that the test seeded.
|
|
68
57
|
|
|
69
58
|
## <a name="license">⚖️ License</a>
|
|
70
59
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,20 +1,13 @@
|
|
|
1
1
|
import { HttpHeadersLike, WebResourceLike } from "@azure/core-http-compat";
|
|
2
2
|
import { PipelineRequest, RestError } from "@azure/core-rest-pipeline";
|
|
3
|
-
import { AnonymousCredential, AppendBlobClient, BlobAbortCopyFromURLResponse, BlobBatchClient, BlobBatchDeleteBlobsResponse, BlobBeginCopyFromURLResponse, BlobClient, BlobCopyFromURLResponse, BlobCreateSnapshotResponse, BlobDeleteIfExistsResponse, BlobDeleteImmutabilityPolicyResponse, BlobDeleteOptions, BlobDeleteResponse, BlobDownloadResponseModel, BlobDownloadResponseParsed, BlobGenerateSasUrlOptions, BlobGetAccountInfoResponse, BlobGetPropertiesResponse, BlobGetTagsResponse, BlobHierarchyListSegment, 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, Metadata, PageBlobClient, PollOperationState, PollerLikeWithCancellation, StorageSharedKeyCredential } from "@azure/storage-blob";
|
|
4
3
|
import { BinaryOperator, Clause } from "@esposter/azure";
|
|
4
|
+
import { AnonymousCredential, AppendBlobClient, BlobAbortCopyFromURLResponse, BlobBatchClient, BlobBatchDeleteBlobsResponse, BlobBeginCopyFromURLResponse, BlobClient, BlobCopyFromURLResponse, BlobCreateSnapshotResponse, BlobDeleteIfExistsResponse, BlobDeleteImmutabilityPolicyResponse, BlobDeleteOptions, BlobDeleteResponse, BlobDownloadResponseModel, BlobDownloadResponseParsed, BlobGenerateSasUrlOptions, BlobGetAccountInfoResponse, BlobGetPropertiesResponse, BlobGetTagsResponse, BlobHierarchyListSegment, BlobItem, BlobLeaseClient, BlobPrefix, BlobSetHTTPHeadersResponse, BlobSetImmutabilityPolicyResponse, BlobSetLegalHoldResponse, BlobSetMetadataResponse, BlobSetTagsResponse, BlobSetTierResponse, BlobUndeleteResponse, BlobUploadCommonResponse, BlockBlobClient, BlockBlobCommitBlockListResponse, BlockBlobGetBlockListResponse, BlockBlobPutBlobFromUrlResponse, BlockBlobStageBlockFromURLResponse, BlockBlobStageBlockResponse, BlockBlobUploadOptions, BlockBlobUploadResponse, BlockBlobUploadStreamOptions, ContainerClient, ContainerCreateIfNotExistsResponse, ContainerCreateResponse, ContainerDeleteIfExistsResponse, ContainerDeleteResponse, ContainerFindBlobsByTagsSegmentResponse, ContainerGenerateSasUrlOptions, ContainerGetAccessPolicyResponse, ContainerGetAccountInfoResponse, ContainerGetPropertiesResponse, ContainerListBlobFlatSegmentResponse, ContainerListBlobHierarchySegmentResponse, ContainerListBlobsOptions, ContainerSetAccessPolicyResponse, ContainerSetMetadataResponse, FilterBlobItem, HttpRequestBody, Metadata, PageBlobClient, PollOperationState, PollerLikeWithCancellation, StorageSharedKeyCredential } from "@azure/storage-blob";
|
|
5
|
+
import { Readable } from "node:stream";
|
|
5
6
|
import { EventGridEvent, EventGridPublisherClient } from "@azure/eventgrid";
|
|
6
7
|
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";
|
|
7
8
|
import { AutocompleteResult, CountDocumentsOptions, DeleteDocumentsOptions, GetDocumentOptions, IndexDocumentsBatch, IndexDocumentsOptions, IndexDocumentsResult, MergeDocumentsOptions, MergeOrUploadDocumentsOptions, NarrowedModel, SearchClient, SearchDocumentsResult, SearchOptions, SelectFields, SuggestDocumentsResult, SuggestOptions, UploadDocumentsOptions } from "@azure/search-documents";
|
|
8
9
|
import { ServiceBusMessage, ServiceBusMessageBatch, ServiceBusSender } from "@azure/service-bus";
|
|
9
10
|
import { CreateTableEntityResponse, GetAccessPolicyResponse, GetTableEntityResponse, ListTableEntitiesOptions, TableClient, TableDeleteEntityHeaders, TableEntity, TableEntityResult, TableEntityResultPage, TableMergeEntityHeaders, TableSetAccessPolicyHeaders, TableTransactionResponse, UpdateMode } from "@azure/data-tables";
|
|
10
|
-
export declare const BLOB_NOT_FOUND_ERROR_CODE = "BlobNotFound";
|
|
11
|
-
export declare const BLOB_NOT_FOUND_MESSAGE = "The specified blob does not exist.";
|
|
12
|
-
export declare const CONDITION_NOT_MET_ERROR_CODE = "ConditionNotMet";
|
|
13
|
-
export declare const CONDITION_NOT_MET_MESSAGE = "The condition specified using HTTP conditional header(s) is not met.";
|
|
14
|
-
export declare const MOCK_BLOB_BASE_URL = "https://mockaccount.blob.core.windows.net";
|
|
15
|
-
export declare const MOCK_QUEUE_BASE_URL = "https://mockaccount.queue.core.windows.net";
|
|
16
|
-
export declare const MOCK_SEARCH_BASE_URL = "https://mockaccount.search.windows.net";
|
|
17
|
-
export declare const MOCK_TABLE_BASE_URL = "https://mockaccount.table.core.windows.net";
|
|
18
11
|
export type BlobHierarchyItem = (BlobItem & {
|
|
19
12
|
kind: "blob";
|
|
20
13
|
}) | (BlobPrefix & {
|
|
@@ -769,6 +762,7 @@ type PostPayloadFixed = Except<UserData, 'email'>;
|
|
|
769
762
|
type Except<ObjectType, KeysType extends keyof ObjectType, Options extends ExceptOptions = {}> = _Except<ObjectType, KeysType, ApplyDefaultOptions<ExceptOptions, DefaultExceptOptions, Options>>;
|
|
770
763
|
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>> : {});
|
|
771
764
|
export declare class MockBlobClient implements Except<BlobClient, "accountName"> {
|
|
765
|
+
#private;
|
|
772
766
|
connectionString: string;
|
|
773
767
|
containerName: string;
|
|
774
768
|
credential: AnonymousCredential;
|
|
@@ -779,8 +773,8 @@ export declare class MockBlobClient implements Except<BlobClient, "accountName">
|
|
|
779
773
|
abortCopyFromURL(): Promise<BlobAbortCopyFromURLResponse>;
|
|
780
774
|
beginCopyFromURL(copySource: string): Promise<PollerLikeWithCancellation<PollOperationState<BlobBeginCopyFromURLResponse>, BlobBeginCopyFromURLResponse>>;
|
|
781
775
|
createSnapshot(): Promise<BlobCreateSnapshotResponse>;
|
|
782
|
-
delete(): Promise<BlobDeleteResponse>;
|
|
783
|
-
deleteIfExists(): Promise<BlobDeleteIfExistsResponse>;
|
|
776
|
+
delete(options?: BlobDeleteOptions): Promise<BlobDeleteResponse>;
|
|
777
|
+
deleteIfExists(options?: BlobDeleteOptions): Promise<BlobDeleteIfExistsResponse>;
|
|
784
778
|
deleteImmutabilityPolicy(): Promise<BlobDeleteImmutabilityPolicyResponse>;
|
|
785
779
|
download(): Promise<BlobDownloadResponseParsed>;
|
|
786
780
|
downloadToBuffer(): Promise<Buffer>;
|
|
@@ -824,7 +818,7 @@ export declare class MockBlockBlobClient extends MockBlobClient implements Excep
|
|
|
824
818
|
uploadBrowserData(): Promise<BlobUploadCommonResponse>;
|
|
825
819
|
uploadData(): Promise<BlobUploadCommonResponse>;
|
|
826
820
|
uploadFile(): Promise<BlobUploadCommonResponse>;
|
|
827
|
-
uploadStream(): Promise<BlobUploadCommonResponse>;
|
|
821
|
+
uploadStream(stream: Readable, _bufferSize?: number, _maxConcurrency?: number, options?: BlockBlobUploadStreamOptions): Promise<BlobUploadCommonResponse>;
|
|
828
822
|
}
|
|
829
823
|
/**
|
|
830
824
|
* An interface that tracks the settings for paged iteration
|
|
@@ -924,6 +918,7 @@ export declare const MockQueueDatabase: Map<string, string[]>;
|
|
|
924
918
|
* const messages = await mockQueueClient.peekMessages();
|
|
925
919
|
*/
|
|
926
920
|
export declare class MockQueueClient implements Except<QueueClient, "accountName"> {
|
|
921
|
+
#private;
|
|
927
922
|
connectionString: string;
|
|
928
923
|
name: string;
|
|
929
924
|
url: string;
|
|
@@ -1045,18 +1040,31 @@ export declare class MockTableClient<TEntity extends TableEntity = TableEntity>
|
|
|
1045
1040
|
upsertEntity<T extends object>(entity: TableEntity<T>, mode?: UpdateMode): Promise<TableMergeEntityHeaders>;
|
|
1046
1041
|
}
|
|
1047
1042
|
/**
|
|
1048
|
-
* A minimal mock of the Azure WebPubSubServiceClient
|
|
1049
|
-
*
|
|
1043
|
+
* A minimal in-memory mock of the Azure WebPubSubServiceClient — no service and no network.
|
|
1044
|
+
* A group's members are the user ids added to it, and `sendToAll` reaches nobody, since no connection is held.
|
|
1050
1045
|
*/
|
|
1051
1046
|
export declare class MockWebPubSubServiceClient {
|
|
1052
|
-
group(
|
|
1047
|
+
group(groupName: string): {
|
|
1048
|
+
addUser: (userId: string) => Promise<void>;
|
|
1049
|
+
removeUser: (userId: string) => Promise<void>;
|
|
1053
1050
|
sendToAll: () => Promise<void>;
|
|
1054
1051
|
};
|
|
1055
1052
|
}
|
|
1053
|
+
export declare const BLOB_NOT_FOUND_ERROR_CODE = "BlobNotFound";
|
|
1054
|
+
export declare const BLOB_NOT_FOUND_MESSAGE = "The specified blob does not exist.";
|
|
1055
|
+
export declare const CONDITION_NOT_MET_ERROR_CODE = "ConditionNotMet";
|
|
1056
|
+
export declare const CONDITION_NOT_MET_MESSAGE = "The condition specified using HTTP conditional header(s) is not met.";
|
|
1057
|
+
export declare const INVALID_INPUT_MESSAGE = "One of the request inputs is not valid.";
|
|
1058
|
+
export declare const MOCK_BLOB_BASE_URL = "https://mockaccount.blob.core.windows.net";
|
|
1059
|
+
export declare const MOCK_QUEUE_BASE_URL = "https://mockaccount.queue.core.windows.net";
|
|
1060
|
+
export declare const MOCK_SEARCH_BASE_URL = "https://mockaccount.search.windows.net";
|
|
1061
|
+
export declare const MOCK_TABLE_BASE_URL = "https://mockaccount.table.core.windows.net";
|
|
1062
|
+
export declare const assertMockBlobIfMatch: (containerName: string, blobName: string, ifMatch: string | undefined) => void;
|
|
1056
1063
|
export declare const bodyToBuffer: (body: HttpRequestBody) => Promise<Buffer>;
|
|
1057
1064
|
export declare const checkIsReadableStream: (value: unknown) => value is NodeJS.ReadableStream;
|
|
1058
1065
|
export declare const MOCK_BLOB_SEEDED_DATE: Date;
|
|
1059
1066
|
export declare const MOCK_BLOB_SEEDED_PROPERTIES: MockBlobDates;
|
|
1067
|
+
export declare const decodeSegment: (segment: string) => string;
|
|
1060
1068
|
export declare const deleteMockBlob: (containerName: string, blobName: string) => boolean;
|
|
1061
1069
|
/**
|
|
1062
1070
|
* Generates a standard Azure Storage error XML response body.
|
|
@@ -1097,12 +1105,18 @@ export declare const getMockQueueMessageItem: (messageText: string) => {
|
|
|
1097
1105
|
messageId: string;
|
|
1098
1106
|
messageText: string;
|
|
1099
1107
|
};
|
|
1108
|
+
export declare const compareValues: (firstValue: unknown, secondValue: unknown) => number;
|
|
1109
|
+
export declare const getSearchFieldValues: (value: unknown, pathSegments: string[]) => unknown[];
|
|
1110
|
+
export declare const searchDocuments: (documents: Record<string, unknown>[], searchText: string | undefined, searchFields: readonly string[] | undefined) => Record<string, unknown>[];
|
|
1111
|
+
export declare const sortDocuments: (documents: Record<string, unknown>[], orderBy: string[]) => Record<string, unknown>[];
|
|
1112
|
+
export declare const toComparable: (value: unknown) => number | string;
|
|
1100
1113
|
export declare const createMockResponse: (status: number, url?: string) => MockResponse;
|
|
1101
1114
|
export declare const getMockSasUrl: (url: string, permissions: undefined | {
|
|
1102
1115
|
toString: () => string;
|
|
1103
|
-
}, resourceType?: string) => string;
|
|
1116
|
+
}, resourceType?: string, expiresOn?: Date) => string;
|
|
1104
1117
|
export declare const compareByCompositeKey: (firstEntity: TableEntity, secondEntity: TableEntity) => number;
|
|
1105
1118
|
export declare const MockContainerBlobDatesDatabase: Map<string, MockBlobDates>;
|
|
1106
1119
|
export declare const MockContainerBlobMetadataDatabase: Map<string, Record<string, string>>;
|
|
1107
1120
|
export declare const MockEventGridDatabase: Map<string, EventGridEvent<unknown>[]>;
|
|
1121
|
+
export declare const MockWebPubSubDatabase: Map<string, Set<string>>;
|
|
1108
1122
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
import { ID_SEPARATOR, InvalidOperationError, Operation, checkIsDeepEqual, chunk, exhaustiveGuard, getDecodedUriComponent, getOrCreate, getResult, html, noop, streamToText, takeOne } from "@esposter/shared";
|
|
2
2
|
import { toHttpHeadersLike } from "@azure/core-http-compat";
|
|
3
3
|
import { RestError, createHttpHeaders, createPipelineRequest } from "@azure/core-rest-pipeline";
|
|
4
|
+
import { AZURE_MAX_BATCH_SIZE, AZURE_MAX_PAGE_SIZE, AZURE_MAX_QUEUE_VISIBILITY_TIMEOUT_MS, BinaryOperator, MAX_BLOB_BATCH_DELETIONS, MAX_EVENT_GRID_PUBLISH_EVENT_COUNT, MAX_EVENT_GRID_PUBLISH_REQUEST_BYTES, deserializeClause, deserializeKey, getTableNullClause, serializeClauses } from "@esposter/azure";
|
|
4
5
|
import { AnonymousCredential } from "@azure/storage-blob";
|
|
5
6
|
import { Readable } from "node:stream";
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
// src/models/shared/NotImplementedError.ts
|
|
8
|
+
var NotImplementedError = class extends Error {
|
|
9
|
+
constructor(name) {
|
|
10
|
+
super(`${name} is not implemented in the mock`);
|
|
11
|
+
this.name = "NotImplementedError";
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
// src/services/constants.ts
|
|
8
15
|
const BLOB_NOT_FOUND_ERROR_CODE = "BlobNotFound";
|
|
9
16
|
const BLOB_NOT_FOUND_MESSAGE = "The specified blob does not exist.";
|
|
10
17
|
const CONDITION_NOT_MET_ERROR_CODE = "ConditionNotMet";
|
|
11
18
|
const CONDITION_NOT_MET_MESSAGE = "The condition specified using HTTP conditional header(s) is not met.";
|
|
19
|
+
const INVALID_INPUT_MESSAGE = "One of the request inputs is not valid.";
|
|
12
20
|
const MOCK_BLOB_BASE_URL = "https://mockaccount.blob.core.windows.net";
|
|
13
21
|
const MOCK_QUEUE_BASE_URL = "https://mockaccount.queue.core.windows.net";
|
|
14
22
|
const MOCK_SEARCH_BASE_URL = "https://mockaccount.search.windows.net";
|
|
15
23
|
const MOCK_TABLE_BASE_URL = "https://mockaccount.table.core.windows.net";
|
|
16
|
-
// src/models/shared/NotImplementedError.ts
|
|
17
|
-
var NotImplementedError = class extends Error {
|
|
18
|
-
constructor(name) {
|
|
19
|
-
super(`${name} is not implemented in the mock`);
|
|
20
|
-
this.name = "NotImplementedError";
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
24
|
// src/services/container/getMockBlobKey.ts
|
|
24
25
|
const getMockBlobKey = (containerName, blobName) => `${encodeURIComponent(containerName)}${ID_SEPARATOR}${encodeURIComponent(blobName)}`;
|
|
25
26
|
// src/store/MockContainerBlobDatesDatabase.ts
|
|
@@ -62,15 +63,16 @@ const getAzureErrorXml = (errorCode, errorMessage) => html`<?xml version="1.0" e
|
|
|
62
63
|
<code>${errorCode}</code>
|
|
63
64
|
<Message>${errorMessage}</Message>
|
|
64
65
|
</Error>`;
|
|
65
|
-
// src/services/container/
|
|
66
|
+
// src/services/container/decodeSegment.ts
|
|
66
67
|
const decodeSegment = (segment) => getDecodedUriComponent(segment, segment);
|
|
68
|
+
// src/services/container/getBlobUrlParts.ts
|
|
67
69
|
const getBlobUrlParts = (url) => {
|
|
68
70
|
const parsedUrl = getResult(() => new URL(url)).unwrapOr(void 0);
|
|
69
71
|
if (!parsedUrl) return void 0;
|
|
70
72
|
const pathSegments = parsedUrl.pathname.split("/").filter(Boolean);
|
|
71
73
|
if (pathSegments.length < 2) return void 0;
|
|
72
74
|
return {
|
|
73
|
-
blobName:
|
|
75
|
+
blobName: pathSegments.slice(1).map((segment) => decodeSegment(segment)).join("/"),
|
|
74
76
|
containerName: decodeSegment(takeOne(pathSegments))
|
|
75
77
|
};
|
|
76
78
|
};
|
|
@@ -132,6 +134,7 @@ var MockBlobBatchClient = class {
|
|
|
132
134
|
const { ifUnmodifiedSince, ...unsupportedConditions } = options?.conditions ?? {};
|
|
133
135
|
const unsupportedConditionName = Object.entries(unsupportedConditions).find(([, value]) => value !== void 0)?.[0];
|
|
134
136
|
if (unsupportedConditionName !== void 0) return Promise.reject(new NotImplementedError(`deleteBlobs with ${unsupportedConditionName}`));
|
|
137
|
+
if (urls.length > MAX_BLOB_BATCH_DELETIONS) return Promise.reject(/* @__PURE__ */ new RangeError(`Cannot exceed ${MAX_BLOB_BATCH_DELETIONS} sub requests in a single batch`));
|
|
135
138
|
const subResponses = [];
|
|
136
139
|
let subResponsesSucceededCount = 0;
|
|
137
140
|
let subResponsesFailedCount = 0;
|
|
@@ -204,6 +207,11 @@ var MockRestError = class extends RestError {
|
|
|
204
207
|
this.name = "MockRestError";
|
|
205
208
|
}
|
|
206
209
|
};
|
|
210
|
+
// src/services/container/assertMockBlobIfMatch.ts
|
|
211
|
+
const assertMockBlobIfMatch = (containerName, blobName, ifMatch) => {
|
|
212
|
+
if (ifMatch === void 0) return;
|
|
213
|
+
if (ifMatch !== (getMockContainer(containerName).has(blobName) ? readMockBlobDates(containerName, blobName).etag : void 0)) throw new MockRestError(CONDITION_NOT_MET_MESSAGE, 412);
|
|
214
|
+
};
|
|
207
215
|
// src/services/container/getBlobUrl.ts
|
|
208
216
|
const getBlobUrl = (containerName, blobName) => `${MOCK_BLOB_BASE_URL}/${containerName}/${blobName.split("/").map((segment) => encodeURIComponent(segment)).join("/")}`;
|
|
209
217
|
// src/services/container/storeMockBlobWrite.ts
|
|
@@ -217,7 +225,8 @@ const storeMockBlobWrite = (containerName, blobName, isExisting) => {
|
|
|
217
225
|
});
|
|
218
226
|
};
|
|
219
227
|
// src/services/shared/getMockSasUrl.ts
|
|
220
|
-
const
|
|
228
|
+
const MOCK_SAS_FAR_FUTURE_EXPIRY = /* @__PURE__ */ new Date("2099-12-31T23:59:59Z");
|
|
229
|
+
const getMockSasUrl = (url, permissions, resourceType, expiresOn = MOCK_SAS_FAR_FUTURE_EXPIRY) => `${url}?sv=2025-11-05&${resourceType ? `sr=${resourceType}&` : ""}sig=mock-signature&st=1970-01-01T00:00:00Z&se=${expiresOn.toISOString().replace(/\.\d{3}Z$/u, "Z")}&sp=${permissions?.toString() ?? "r"}`;
|
|
221
230
|
// src/models/container/MockBlobClient.ts
|
|
222
231
|
var MockBlobClient = class {
|
|
223
232
|
connectionString;
|
|
@@ -266,12 +275,12 @@ var MockBlobClient = class {
|
|
|
266
275
|
createSnapshot() {
|
|
267
276
|
throw new NotImplementedError(this.createSnapshot.name);
|
|
268
277
|
}
|
|
269
|
-
delete() {
|
|
270
|
-
if (!
|
|
278
|
+
delete(options) {
|
|
279
|
+
if (!this.#deleteIfPresent(options?.conditions?.ifMatch)) throw new MockRestError(BLOB_NOT_FOUND_MESSAGE, 404);
|
|
271
280
|
return Promise.resolve({ _response: createMockResponse(200) });
|
|
272
281
|
}
|
|
273
|
-
deleteIfExists() {
|
|
274
|
-
const succeeded =
|
|
282
|
+
deleteIfExists(options) {
|
|
283
|
+
const succeeded = this.#deleteIfPresent(options?.conditions?.ifMatch);
|
|
275
284
|
return Promise.resolve({
|
|
276
285
|
_response: createMockResponse(succeeded ? 200 : 404),
|
|
277
286
|
succeeded
|
|
@@ -304,7 +313,7 @@ var MockBlobClient = class {
|
|
|
304
313
|
throw new NotImplementedError(this.generateSasStringToSign.name);
|
|
305
314
|
}
|
|
306
315
|
generateSasUrl(options) {
|
|
307
|
-
return Promise.resolve(getMockSasUrl(this.url, options.permissions, "b"));
|
|
316
|
+
return Promise.resolve(getMockSasUrl(this.url, options.permissions, "b", options.expiresOn));
|
|
308
317
|
}
|
|
309
318
|
generateUserDelegationSasStringToSign() {
|
|
310
319
|
throw new NotImplementedError(this.generateUserDelegationSasStringToSign.name);
|
|
@@ -375,6 +384,11 @@ var MockBlobClient = class {
|
|
|
375
384
|
withVersion() {
|
|
376
385
|
throw new NotImplementedError(this.withVersion.name);
|
|
377
386
|
}
|
|
387
|
+
#deleteIfPresent(ifMatch) {
|
|
388
|
+
if (!this.container.has(this.name)) return false;
|
|
389
|
+
assertMockBlobIfMatch(this.containerName, this.name, ifMatch);
|
|
390
|
+
return deleteMockBlob(this.containerName, this.name);
|
|
391
|
+
}
|
|
378
392
|
};
|
|
379
393
|
// src/services/container/checkIsReadableStream.ts
|
|
380
394
|
const checkIsReadableStream = (value) => value instanceof Readable;
|
|
@@ -429,7 +443,7 @@ var MockBlockBlobClient = class extends MockBlobClient {
|
|
|
429
443
|
async upload(body, _contentLength, options) {
|
|
430
444
|
const buffer = await bodyToBuffer(body);
|
|
431
445
|
if (options?.conditions?.ifNoneMatch === "*" && this.container.has(this.name)) throw new MockRestError("The specified blob already exists.", 409);
|
|
432
|
-
|
|
446
|
+
assertMockBlobIfMatch(this.containerName, this.name, options?.conditions?.ifMatch);
|
|
433
447
|
storeMockBlobWrite(this.containerName, this.name, this.container.has(this.name));
|
|
434
448
|
storeMockBlobMetadata(this.containerName, this.name, options?.metadata);
|
|
435
449
|
this.container.set(this.name, buffer);
|
|
@@ -448,8 +462,10 @@ var MockBlockBlobClient = class extends MockBlobClient {
|
|
|
448
462
|
uploadFile() {
|
|
449
463
|
throw new NotImplementedError(this.uploadFile.name);
|
|
450
464
|
}
|
|
451
|
-
uploadStream() {
|
|
452
|
-
|
|
465
|
+
async uploadStream(stream, _bufferSize, _maxConcurrency, options) {
|
|
466
|
+
const chunks = [];
|
|
467
|
+
for await (const chunk of stream) chunks.push(chunk);
|
|
468
|
+
return this.upload(Buffer.concat(chunks), 0, options);
|
|
453
469
|
}
|
|
454
470
|
};
|
|
455
471
|
// src/services/container/getBlobItemXml.ts
|
|
@@ -548,7 +564,7 @@ var MockContainerClient = class {
|
|
|
548
564
|
throw new NotImplementedError(this.generateSasStringToSign.name);
|
|
549
565
|
}
|
|
550
566
|
generateSasUrl(options) {
|
|
551
|
-
return Promise.resolve(getMockSasUrl(this.url, options.permissions, "c"));
|
|
567
|
+
return Promise.resolve(getMockSasUrl(this.url, options.permissions, "c", options.expiresOn));
|
|
552
568
|
}
|
|
553
569
|
generateUserDelegationSasStringToSign() {
|
|
554
570
|
throw new NotImplementedError(this.generateUserDelegationSasStringToSign.name);
|
|
@@ -707,6 +723,7 @@ var MockEventGridPublisherClient = class {
|
|
|
707
723
|
this.inputSchema = topicType;
|
|
708
724
|
}
|
|
709
725
|
send(newEvents) {
|
|
726
|
+
if (newEvents.length > MAX_EVENT_GRID_PUBLISH_EVENT_COUNT || Buffer.byteLength(JSON.stringify(newEvents)) > MAX_EVENT_GRID_PUBLISH_REQUEST_BYTES) throw new MockRestError("The publish request exceeds the Event Grid publish limits.", 413);
|
|
710
727
|
getOrCreate(MockEventGridDatabase, this.endpointUrl, () => []).push(...newEvents);
|
|
711
728
|
return Promise.resolve();
|
|
712
729
|
}
|
|
@@ -770,7 +787,7 @@ var MockQueueClient = class {
|
|
|
770
787
|
throw new NotImplementedError(this.generateSasStringToSign.name);
|
|
771
788
|
}
|
|
772
789
|
generateSasUrl(options) {
|
|
773
|
-
return getMockSasUrl(this.url, options.permissions);
|
|
790
|
+
return getMockSasUrl(this.url, options.permissions, void 0, options.expiresOn);
|
|
774
791
|
}
|
|
775
792
|
generateUserDelegationSasUrl() {
|
|
776
793
|
throw new NotImplementedError(this.generateUserDelegationSasUrl.name);
|
|
@@ -796,11 +813,7 @@ var MockQueueClient = class {
|
|
|
796
813
|
...getMockQueueMessageItem(text)
|
|
797
814
|
}));
|
|
798
815
|
return Promise.resolve({
|
|
799
|
-
_response:
|
|
800
|
-
...createMockResponse(200, this.url),
|
|
801
|
-
bodyAsText: "",
|
|
802
|
-
parsedBody: peekedMessageItems
|
|
803
|
-
},
|
|
816
|
+
_response: this.#createResponse(peekedMessageItems),
|
|
804
817
|
peekedMessageItems
|
|
805
818
|
});
|
|
806
819
|
}
|
|
@@ -813,11 +826,7 @@ var MockQueueClient = class {
|
|
|
813
826
|
popReceipt: crypto.randomUUID()
|
|
814
827
|
}));
|
|
815
828
|
return Promise.resolve({
|
|
816
|
-
_response:
|
|
817
|
-
...createMockResponse(200, this.url),
|
|
818
|
-
bodyAsText: "",
|
|
819
|
-
parsedBody: receivedMessageItems
|
|
820
|
-
},
|
|
829
|
+
_response: this.#createResponse(receivedMessageItems),
|
|
821
830
|
receivedMessageItems
|
|
822
831
|
});
|
|
823
832
|
}
|
|
@@ -833,11 +842,7 @@ var MockQueueClient = class {
|
|
|
833
842
|
};
|
|
834
843
|
const enqueuedMessages = [enqueuedMessage];
|
|
835
844
|
return Promise.resolve({
|
|
836
|
-
_response:
|
|
837
|
-
...createMockResponse(200, this.url),
|
|
838
|
-
bodyAsText: "",
|
|
839
|
-
parsedBody: enqueuedMessages
|
|
840
|
-
},
|
|
845
|
+
_response: this.#createResponse(enqueuedMessages),
|
|
841
846
|
...enqueuedMessage
|
|
842
847
|
});
|
|
843
848
|
}
|
|
@@ -850,6 +855,13 @@ var MockQueueClient = class {
|
|
|
850
855
|
updateMessage() {
|
|
851
856
|
throw new NotImplementedError(this.updateMessage.name);
|
|
852
857
|
}
|
|
858
|
+
#createResponse(parsedBody) {
|
|
859
|
+
return {
|
|
860
|
+
...createMockResponse(200, this.url),
|
|
861
|
+
bodyAsText: "",
|
|
862
|
+
parsedBody
|
|
863
|
+
};
|
|
864
|
+
}
|
|
853
865
|
};
|
|
854
866
|
// src/services/filter/checkIsNullClause.ts
|
|
855
867
|
const checkIsNullClause = (clause) => {
|
|
@@ -877,9 +889,16 @@ const compare = (operator, leftHandSide, rightHandSide) => {
|
|
|
877
889
|
}
|
|
878
890
|
};
|
|
879
891
|
// src/services/filter/createFilterPredicate.ts
|
|
892
|
+
const LITERAL_REGEX = /'(?:[^']|'')*'/gu;
|
|
893
|
+
const LITERAL_PLACEHOLDER = "";
|
|
894
|
+
const LITERAL_PLACEHOLDER_REGEX = new RegExp(`${LITERAL_PLACEHOLDER}(\\d+)${LITERAL_PLACEHOLDER}`, "gu");
|
|
880
895
|
const createFilterPredicate = (filter) => {
|
|
881
|
-
const
|
|
882
|
-
|
|
896
|
+
const literals = [];
|
|
897
|
+
const orGroups = filter.replaceAll(LITERAL_REGEX, (literal) => {
|
|
898
|
+
literals.push(literal);
|
|
899
|
+
return `${LITERAL_PLACEHOLDER}${literals.length - 1}${LITERAL_PLACEHOLDER}`;
|
|
900
|
+
}).replaceAll(String.raw`(`, " ").replaceAll(String.raw`)`, "").split(/\s+and\s+/iu).filter(Boolean).map((group) => group.split(/\s+or\s+/iu).filter(Boolean).map((clauseString) => {
|
|
901
|
+
const clause = deserializeClause(clauseString.replaceAll(LITERAL_PLACEHOLDER_REGEX, (_placeholder, index) => takeOne(literals, Number(index))));
|
|
883
902
|
return {
|
|
884
903
|
clause,
|
|
885
904
|
isNull: checkIsNullClause(clause)
|
|
@@ -906,20 +925,36 @@ const createFilterPredicate = (filter) => {
|
|
|
906
925
|
return true;
|
|
907
926
|
};
|
|
908
927
|
};
|
|
909
|
-
// src/
|
|
910
|
-
const
|
|
911
|
-
|
|
928
|
+
// src/services/search/getSearchFieldValues.ts
|
|
929
|
+
const getSearchFieldValues = (value, pathSegments) => {
|
|
930
|
+
if (pathSegments.length === 0) return [value];
|
|
931
|
+
else if (Array.isArray(value)) return value.flatMap((item) => getSearchFieldValues(item, pathSegments));
|
|
932
|
+
else if (typeof value === "object" && value !== null) {
|
|
933
|
+
const [field = "", ...remainingPathSegments] = pathSegments;
|
|
934
|
+
return getSearchFieldValues(value[deserializeKey(field)], remainingPathSegments);
|
|
935
|
+
} else return [];
|
|
936
|
+
};
|
|
937
|
+
// src/services/search/searchDocuments.ts
|
|
938
|
+
const searchDocuments = (documents, searchText, searchFields) => {
|
|
939
|
+
if (!searchText || searchText === "*") return documents;
|
|
940
|
+
const normalizedSearchText = searchText.toLocaleLowerCase();
|
|
941
|
+
const searchFieldPaths = searchFields?.map((searchField) => searchField.split("/"));
|
|
942
|
+
return documents.filter((document) => (searchFieldPaths ?? Object.keys(document).map((field) => field.split("/"))).some((pathSegments) => getSearchFieldValues(document, pathSegments).some((value) => typeof value === "string" && value.toLocaleLowerCase().includes(normalizedSearchText))));
|
|
943
|
+
};
|
|
944
|
+
// src/services/search/toComparable.ts
|
|
912
945
|
const toComparable = (value) => {
|
|
913
946
|
if (value instanceof Date) return value.getTime();
|
|
914
947
|
else if (typeof value === "number" || typeof value === "string") return value;
|
|
915
948
|
else return String(value);
|
|
916
949
|
};
|
|
950
|
+
// src/services/search/compareValues.ts
|
|
917
951
|
const compareValues = (firstValue, secondValue) => {
|
|
918
952
|
const firstComparable = toComparable(firstValue);
|
|
919
953
|
const secondComparable = toComparable(secondValue);
|
|
920
954
|
if (typeof firstComparable === "number" && typeof secondComparable === "number") return firstComparable - secondComparable;
|
|
921
955
|
else return String(firstComparable).localeCompare(String(secondComparable));
|
|
922
956
|
};
|
|
957
|
+
// src/services/search/sortDocuments.ts
|
|
923
958
|
const sortDocuments = (documents, orderBy) => {
|
|
924
959
|
const parsedOrderBy = orderBy.map((clause) => {
|
|
925
960
|
const [field = "", direction = "asc"] = clause.split(/\s+/u);
|
|
@@ -936,20 +971,9 @@ const sortDocuments = (documents, orderBy) => {
|
|
|
936
971
|
return 0;
|
|
937
972
|
});
|
|
938
973
|
};
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
else if (typeof value === "object" && value !== null) {
|
|
943
|
-
const [field = "", ...remainingPathSegments] = pathSegments;
|
|
944
|
-
return getSearchFieldValues(value[deserializeKey(field)], remainingPathSegments);
|
|
945
|
-
} else return [];
|
|
946
|
-
};
|
|
947
|
-
const searchDocuments = (documents, searchText, searchFields) => {
|
|
948
|
-
if (!searchText || searchText === "*") return documents;
|
|
949
|
-
const normalizedSearchText = searchText.toLocaleLowerCase();
|
|
950
|
-
const searchFieldPaths = searchFields?.map((searchField) => searchField.split("/"));
|
|
951
|
-
return documents.filter((document) => (searchFieldPaths ?? Object.keys(document).map((field) => field.split("/"))).some((pathSegments) => getSearchFieldValues(document, pathSegments).some((value) => typeof value === "string" && value.toLocaleLowerCase().includes(normalizedSearchText))));
|
|
952
|
-
};
|
|
974
|
+
// src/store/MockSearchDatabase.ts
|
|
975
|
+
const MockSearchDatabase = /* @__PURE__ */ new Map();
|
|
976
|
+
// src/models/search/MockSearchClient.ts
|
|
953
977
|
/**
|
|
954
978
|
* An in-memory mock of the Azure SearchClient — no emulator and no network. It applies the same OData filtering
|
|
955
979
|
* as the other mock clients, so a query behaves the same here as it does against the service.
|
|
@@ -1013,7 +1037,12 @@ var MockSearchClient = class {
|
|
|
1013
1037
|
throw new NotImplementedError(this.suggest.name);
|
|
1014
1038
|
}
|
|
1015
1039
|
uploadDocuments(documents, _options) {
|
|
1016
|
-
|
|
1040
|
+
for (const document of documents) {
|
|
1041
|
+
const key = document.rowKey;
|
|
1042
|
+
const index = this.documents.findIndex((existingDocument) => existingDocument.rowKey === key);
|
|
1043
|
+
if (index === -1) this.documents.push(document);
|
|
1044
|
+
else this.documents[index] = document;
|
|
1045
|
+
}
|
|
1017
1046
|
return Promise.resolve({ results: [] });
|
|
1018
1047
|
}
|
|
1019
1048
|
};
|
|
@@ -1110,8 +1139,7 @@ var MockTableClient = class {
|
|
|
1110
1139
|
}
|
|
1111
1140
|
getEntity(partitionKey, rowKey) {
|
|
1112
1141
|
const key = this.#getCompositeKey(partitionKey, rowKey);
|
|
1113
|
-
const entity = this
|
|
1114
|
-
if (!entity) throw new MockRestError("The specified resource does not exist.", 404);
|
|
1142
|
+
const entity = this.#getExistingEntity(key);
|
|
1115
1143
|
return Promise.resolve({
|
|
1116
1144
|
...entity,
|
|
1117
1145
|
etag: entity.etag ?? this.#getEtag()
|
|
@@ -1129,7 +1157,7 @@ var MockTableClient = class {
|
|
|
1129
1157
|
return {
|
|
1130
1158
|
byPage: ({ maxPageSize } = {}) => (async function* (entities) {
|
|
1131
1159
|
if (maxPageSize !== void 0 && maxPageSize <= 0) throw new RangeError("maxPageSize must be greater than 0.");
|
|
1132
|
-
else if (maxPageSize !== void 0 && maxPageSize > AZURE_MAX_PAGE_SIZE) throw new MockRestError(
|
|
1160
|
+
else if (maxPageSize !== void 0 && maxPageSize > AZURE_MAX_PAGE_SIZE) throw new MockRestError(INVALID_INPUT_MESSAGE, 400);
|
|
1133
1161
|
if (entities.length === 0) return;
|
|
1134
1162
|
else if (!maxPageSize) {
|
|
1135
1163
|
yield await Promise.resolve(entities.map((entity) => withMetadata(entity)));
|
|
@@ -1147,6 +1175,7 @@ var MockTableClient = class {
|
|
|
1147
1175
|
throw new NotImplementedError(this.setAccessPolicy.name);
|
|
1148
1176
|
}
|
|
1149
1177
|
submitTransaction(actions) {
|
|
1178
|
+
if (actions.length > AZURE_MAX_BATCH_SIZE) throw new MockRestError(INVALID_INPUT_MESSAGE, 400);
|
|
1150
1179
|
let partitionKey;
|
|
1151
1180
|
for (const [, entity] of actions) if (partitionKey === void 0) partitionKey = entity.partitionKey;
|
|
1152
1181
|
else if (entity.partitionKey !== partitionKey) throw new MockRestError("All transaction actions must target the same partitionKey.", 400);
|
|
@@ -1203,15 +1232,13 @@ var MockTableClient = class {
|
|
|
1203
1232
|
}
|
|
1204
1233
|
#applyDelete(partitionKey, rowKey, etag) {
|
|
1205
1234
|
const key = this.#getCompositeKey(partitionKey, rowKey);
|
|
1206
|
-
const existingEntity = this
|
|
1207
|
-
if (!existingEntity) throw new MockRestError("The specified resource does not exist.", 404);
|
|
1235
|
+
const existingEntity = this.#getExistingEntity(key);
|
|
1208
1236
|
this.#assertEtag(existingEntity.etag, etag);
|
|
1209
1237
|
this.table.delete(key);
|
|
1210
1238
|
}
|
|
1211
1239
|
#applyUpdate(entity, mode = "Merge", etag) {
|
|
1212
1240
|
const key = this.#getCompositeKey(entity.partitionKey, entity.rowKey);
|
|
1213
|
-
const existingEntity = this
|
|
1214
|
-
if (!existingEntity) throw new MockRestError("The specified resource does not exist.", 404);
|
|
1241
|
+
const existingEntity = this.#getExistingEntity(key);
|
|
1215
1242
|
this.#assertEtag(existingEntity.etag, etag);
|
|
1216
1243
|
this.#writeEntity(key, entity, mode, existingEntity);
|
|
1217
1244
|
}
|
|
@@ -1229,6 +1256,11 @@ var MockTableClient = class {
|
|
|
1229
1256
|
#getEtag() {
|
|
1230
1257
|
return `W/"${crypto.randomUUID()}"`;
|
|
1231
1258
|
}
|
|
1259
|
+
#getExistingEntity(key) {
|
|
1260
|
+
const entity = this.table.get(key);
|
|
1261
|
+
if (!entity) throw new MockRestError("The specified resource does not exist.", 404);
|
|
1262
|
+
return entity;
|
|
1263
|
+
}
|
|
1232
1264
|
#withMetadata(entity) {
|
|
1233
1265
|
return {
|
|
1234
1266
|
...entity,
|
|
@@ -1247,14 +1279,27 @@ var MockTableClient = class {
|
|
|
1247
1279
|
});
|
|
1248
1280
|
}
|
|
1249
1281
|
};
|
|
1282
|
+
// src/store/MockWebPubSubDatabase.ts
|
|
1283
|
+
const MockWebPubSubDatabase = /* @__PURE__ */ new Map();
|
|
1250
1284
|
// src/models/webPubSub/MockWebPubSubServiceClient.ts
|
|
1251
1285
|
/**
|
|
1252
|
-
* A minimal mock of the Azure WebPubSubServiceClient
|
|
1253
|
-
*
|
|
1286
|
+
* A minimal in-memory mock of the Azure WebPubSubServiceClient — no service and no network.
|
|
1287
|
+
* A group's members are the user ids added to it, and `sendToAll` reaches nobody, since no connection is held.
|
|
1254
1288
|
*/
|
|
1255
1289
|
var MockWebPubSubServiceClient = class {
|
|
1256
|
-
group(
|
|
1257
|
-
|
|
1290
|
+
group(groupName) {
|
|
1291
|
+
const getMembers = () => getOrCreate(MockWebPubSubDatabase, groupName, () => /* @__PURE__ */ new Set());
|
|
1292
|
+
return {
|
|
1293
|
+
addUser: (userId) => {
|
|
1294
|
+
getMembers().add(userId);
|
|
1295
|
+
return Promise.resolve();
|
|
1296
|
+
},
|
|
1297
|
+
removeUser: (userId) => {
|
|
1298
|
+
getMembers().delete(userId);
|
|
1299
|
+
return Promise.resolve();
|
|
1300
|
+
},
|
|
1301
|
+
sendToAll: () => Promise.resolve()
|
|
1302
|
+
};
|
|
1258
1303
|
}
|
|
1259
1304
|
};
|
|
1260
1305
|
// src/services/filter/applyFilter.ts
|
|
@@ -1262,4 +1307,4 @@ const applyFilter = (documents, clauses) => {
|
|
|
1262
1307
|
const predicate = createFilterPredicate(serializeClauses(clauses));
|
|
1263
1308
|
return documents.filter((document) => predicate(document));
|
|
1264
1309
|
};
|
|
1265
|
-
export { BLOB_NOT_FOUND_ERROR_CODE, BLOB_NOT_FOUND_MESSAGE, CONDITION_NOT_MET_ERROR_CODE, CONDITION_NOT_MET_MESSAGE, MOCK_BLOB_BASE_URL, MOCK_BLOB_SEEDED_DATE, MOCK_BLOB_SEEDED_PROPERTIES, MOCK_QUEUE_BASE_URL, MOCK_SEARCH_BASE_URL, MOCK_TABLE_BASE_URL, MockBlobBatchClient, MockBlobClient, MockBlockBlobClient, MockContainerBlobDatesDatabase, MockContainerBlobMetadataDatabase, MockContainerClient, MockContainerDatabase, MockEventGridDatabase, MockEventGridPublisherClient, MockQueueClient, MockQueueDatabase, MockRestError, MockSearchClient, MockSearchDatabase, MockServiceBusDatabase, MockServiceBusSender, MockTableClient, MockTableDatabase, MockWebPubSubServiceClient, NotImplementedError, applyFilter, bodyToBuffer, checkIsNullClause, checkIsReadableStream, compare, compareByCompositeKey, createFilterPredicate, createMockResponse, deleteMockBlob, getAzureErrorXml, getBlobItemXml, getBlobPrefixXml, getBlobUrl, getBlobUrlParts, getListBlobsSegmentResponse, getListBlobsXml, getMockBlobKey, getMockContainer, getMockQueueMessageItem, getMockSasUrl, readMockBlobDates, readMockBlobMetadata, storeMockBlobMetadata, storeMockBlobWrite, toWebResourceLike };
|
|
1310
|
+
export { BLOB_NOT_FOUND_ERROR_CODE, BLOB_NOT_FOUND_MESSAGE, CONDITION_NOT_MET_ERROR_CODE, CONDITION_NOT_MET_MESSAGE, INVALID_INPUT_MESSAGE, MOCK_BLOB_BASE_URL, MOCK_BLOB_SEEDED_DATE, MOCK_BLOB_SEEDED_PROPERTIES, MOCK_QUEUE_BASE_URL, MOCK_SEARCH_BASE_URL, MOCK_TABLE_BASE_URL, MockBlobBatchClient, MockBlobClient, MockBlockBlobClient, MockContainerBlobDatesDatabase, MockContainerBlobMetadataDatabase, MockContainerClient, MockContainerDatabase, MockEventGridDatabase, MockEventGridPublisherClient, MockQueueClient, MockQueueDatabase, MockRestError, MockSearchClient, MockSearchDatabase, MockServiceBusDatabase, MockServiceBusSender, MockTableClient, MockTableDatabase, MockWebPubSubDatabase, MockWebPubSubServiceClient, NotImplementedError, applyFilter, assertMockBlobIfMatch, bodyToBuffer, checkIsNullClause, checkIsReadableStream, compare, compareByCompositeKey, compareValues, createFilterPredicate, createMockResponse, decodeSegment, deleteMockBlob, getAzureErrorXml, getBlobItemXml, getBlobPrefixXml, getBlobUrl, getBlobUrlParts, getListBlobsSegmentResponse, getListBlobsXml, getMockBlobKey, getMockContainer, getMockQueueMessageItem, getMockSasUrl, getSearchFieldValues, readMockBlobDates, readMockBlobMetadata, searchDocuments, sortDocuments, storeMockBlobMetadata, storeMockBlobWrite, toComparable, toWebResourceLike };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "azure-mock",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.5.0",
|
|
4
4
|
"description": "A library that contains azure mock classes.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"azure",
|
|
@@ -48,15 +48,15 @@
|
|
|
48
48
|
"lint": "eslint .",
|
|
49
49
|
"lint:fix": "eslint --fix .",
|
|
50
50
|
"test": "vitest",
|
|
51
|
-
"typecheck": "
|
|
51
|
+
"typecheck": "tsrs --fast"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@esposter/azure": "4.
|
|
55
|
-
"@esposter/shared": "4.
|
|
54
|
+
"@esposter/azure": "4.5.0",
|
|
55
|
+
"@esposter/shared": "4.5.0"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
|
-
"@esposter/configuration": "4.
|
|
59
|
-
"tsdown": "^0.23.
|
|
58
|
+
"@esposter/configuration": "4.5.0",
|
|
59
|
+
"tsdown": "^0.23.5",
|
|
60
60
|
"type-fest": "^5.10.0"
|
|
61
61
|
},
|
|
62
62
|
"peerDependencies": {
|
|
@@ -72,5 +72,5 @@
|
|
|
72
72
|
"inlinedDependencies": {
|
|
73
73
|
"type-fest": "5.10.0"
|
|
74
74
|
},
|
|
75
|
-
"gitHead": "
|
|
75
|
+
"gitHead": "e30c001f3b4b15693e75fb53092d62a97ade4d2b"
|
|
76
76
|
}
|