azure-mock 2.27.0 → 2.29.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 +11 -0
- package/dist/index.d.ts +63 -18
- package/dist/index.js +171 -46
- package/package.json +12 -12
package/README.md
CHANGED
|
@@ -47,6 +47,17 @@ const entity = await mockTableClient.getEntity("pk", "rk");
|
|
|
47
47
|
|
|
48
48
|
Replace real Azure clients with their mock equivalents by swapping them in your test setup or local `.env` configuration.
|
|
49
49
|
|
|
50
|
+
### Commands
|
|
51
|
+
|
|
52
|
+
Run from `packages/azure-mock/`:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pnpm build # compile to dist/
|
|
56
|
+
pnpm test # vitest watch mode (coverage is run from the repo root)
|
|
57
|
+
pnpm lint:fix # auto-fix lint
|
|
58
|
+
pnpm typecheck # type check
|
|
59
|
+
```
|
|
60
|
+
|
|
50
61
|
## <a name="license">⚖️ License</a>
|
|
51
62
|
|
|
52
63
|
This project is licensed under the [Apache-2.0 license](https://github.com/Esposter/Esposter/blob/main/LICENSE).
|
package/dist/index.d.ts
CHANGED
|
@@ -6,10 +6,12 @@ import { BinaryOperator, Clause } from "@esposter/db-schema";
|
|
|
6
6
|
import { EventGridEvent, EventGridPublisherClient } from "@azure/eventgrid";
|
|
7
7
|
import { CreateTableEntityResponse, GetAccessPolicyResponse, GetTableEntityResponse, ListTableEntitiesOptions, TableClient, TableDeleteEntityHeaders, TableEntity, TableEntityResult, TableEntityResultPage, TableMergeEntityHeaders, TableSetAccessPolicyHeaders, TableTransactionResponse, UpdateMode } from "@azure/data-tables";
|
|
8
8
|
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
|
+
import { AutocompleteResult, CountDocumentsOptions, DeleteDocumentsOptions, GetDocumentOptions, IndexDocumentsBatch, IndexDocumentsOptions, IndexDocumentsResult, MergeDocumentsOptions, MergeOrUploadDocumentsOptions, NarrowedModel, SearchClient, SearchDocumentsResult, SearchOptions, SelectFields, SuggestDocumentsResult, SuggestOptions, UploadDocumentsOptions } from "@azure/search-documents";
|
|
9
10
|
|
|
10
11
|
//#region src/constants.d.ts
|
|
11
12
|
declare const MOCK_BLOB_BASE_URL = "https://mockaccount.blob.core.windows.net";
|
|
12
13
|
declare const MOCK_QUEUE_BASE_URL = "https://mockaccount.queue.core.windows.net";
|
|
14
|
+
declare const MOCK_SEARCH_BASE_URL = "https://mockaccount.search.windows.net";
|
|
13
15
|
declare const MOCK_TABLE_BASE_URL = "https://mockaccount.table.core.windows.net";
|
|
14
16
|
//#endregion
|
|
15
17
|
//#region src/models/MockRestError.d.ts
|
|
@@ -61,6 +63,9 @@ declare const MockEventGridDatabase: Map<string, EventGridEvent<unknown>[]>;
|
|
|
61
63
|
//#region src/store/MockQueueDatabase.d.ts
|
|
62
64
|
declare const MockQueueDatabase: Map<string, string[]>;
|
|
63
65
|
//#endregion
|
|
66
|
+
//#region src/store/MockSearchDatabase.d.ts
|
|
67
|
+
declare const MockSearchDatabase: Map<string, object[]>;
|
|
68
|
+
//#endregion
|
|
64
69
|
//#region src/store/MockTableDatabase.d.ts
|
|
65
70
|
declare const MockTableDatabase: Map<string, Map<string, TableEntity>>;
|
|
66
71
|
//#endregion
|
|
@@ -939,6 +944,7 @@ declare class MockBlockBlobClient extends MockBlobClient implements Except<Block
|
|
|
939
944
|
* const content = await blockBlobClient.downloadToBuffer();
|
|
940
945
|
*/
|
|
941
946
|
declare class MockContainerClient implements Except<ContainerClient, "accountName"> {
|
|
947
|
+
#private;
|
|
942
948
|
connectionString: string;
|
|
943
949
|
containerName: string;
|
|
944
950
|
credential: AnonymousCredential;
|
|
@@ -973,18 +979,18 @@ declare class MockContainerClient implements Except<ContainerClient, "accountNam
|
|
|
973
979
|
blockBlobClient: BlockBlobClient;
|
|
974
980
|
response: BlockBlobUploadResponse;
|
|
975
981
|
}>;
|
|
976
|
-
private getBlobHierarchyItemIterator;
|
|
977
|
-
private getBlobItemIterator;
|
|
978
982
|
}
|
|
979
983
|
//#endregion
|
|
980
984
|
//#region src/models/eventGrid/MockEventGridPublisherClient.d.ts
|
|
981
985
|
/**
|
|
982
986
|
* An in-memory mock of the Azure EventGridPublisherClient.
|
|
983
987
|
* It uses a Map to simulate event grid storage and correctly implements the EventGridPublisherClient interface.
|
|
988
|
+
* `client` is excluded because it is a private member of the real class and cannot be satisfied structurally.
|
|
984
989
|
*/
|
|
985
|
-
declare class MockEventGridPublisherClient implements Except<EventGridPublisherClient<"EventGrid">, "apiVersion"
|
|
986
|
-
|
|
987
|
-
|
|
990
|
+
declare class MockEventGridPublisherClient implements Except<EventGridPublisherClient<"EventGrid">, "apiVersion"> {
|
|
991
|
+
readonly endpointUrl: string;
|
|
992
|
+
readonly inputSchema: "EventGrid";
|
|
993
|
+
readonly topicType: "EventGrid";
|
|
988
994
|
constructor(endpoint: string, topicType: "EventGrid");
|
|
989
995
|
send(newEvents: EventGridEvent<unknown>[]): Promise<void>;
|
|
990
996
|
}
|
|
@@ -1029,6 +1035,36 @@ declare class MockQueueClient implements Except<QueueClient, "accountName"> {
|
|
|
1029
1035
|
updateMessage(): Promise<QueueUpdateMessageResponse>;
|
|
1030
1036
|
}
|
|
1031
1037
|
//#endregion
|
|
1038
|
+
//#region src/models/search/MockSearchClient.d.ts
|
|
1039
|
+
/**
|
|
1040
|
+
* An in-memory mock of the Azure SearchClient.
|
|
1041
|
+
* It uses a Map to simulate the search index and applies the same OData filtering as the other mock clients.
|
|
1042
|
+
*
|
|
1043
|
+
* @example
|
|
1044
|
+
* const mockSearchClient = new MockSearchClient(SearchIndex.Messages);
|
|
1045
|
+
* await mockSearchClient.uploadDocuments([{ ... }]);
|
|
1046
|
+
* const { count, results } = await mockSearchClient.search("*", { filter, includeTotalCount: true });
|
|
1047
|
+
*/
|
|
1048
|
+
declare class MockSearchClient<TModel extends object = Record<string, unknown>> implements Except<SearchClient<TModel>, "pipeline"> {
|
|
1049
|
+
readonly apiVersion: string;
|
|
1050
|
+
readonly endpoint: string;
|
|
1051
|
+
readonly indexName: string;
|
|
1052
|
+
readonly serviceVersion: string;
|
|
1053
|
+
get documents(): MapValue<typeof MockSearchDatabase>;
|
|
1054
|
+
constructor(indexName: string);
|
|
1055
|
+
autocomplete(): Promise<AutocompleteResult>;
|
|
1056
|
+
deleteDocuments(documents: TModel[], options?: DeleteDocumentsOptions): Promise<IndexDocumentsResult>;
|
|
1057
|
+
deleteDocuments(keyName: keyof TModel, keyValues: string[], options?: DeleteDocumentsOptions): Promise<IndexDocumentsResult>;
|
|
1058
|
+
getDocument<TFields extends SelectFields<TModel>>(_key: string, _options?: GetDocumentOptions<TModel, TFields>): Promise<NarrowedModel<TModel, TFields>>;
|
|
1059
|
+
getDocumentsCount(_options?: CountDocumentsOptions): Promise<number>;
|
|
1060
|
+
indexDocuments(_batch: IndexDocumentsBatch<TModel>, _options?: IndexDocumentsOptions): Promise<IndexDocumentsResult>;
|
|
1061
|
+
mergeDocuments(_documents: TModel[], _options?: MergeDocumentsOptions): Promise<IndexDocumentsResult>;
|
|
1062
|
+
mergeOrUploadDocuments(_documents: TModel[], _options?: MergeOrUploadDocumentsOptions): Promise<IndexDocumentsResult>;
|
|
1063
|
+
search<TFields extends SelectFields<TModel>>(searchText?: string, options?: SearchOptions<TModel, TFields>): Promise<SearchDocumentsResult<TModel, TFields>>;
|
|
1064
|
+
suggest<TFields extends SelectFields<TModel> = never>(_searchText: string, _suggesterName: string, _options?: SuggestOptions<TModel, TFields>): Promise<SuggestDocumentsResult<TModel, TFields>>;
|
|
1065
|
+
uploadDocuments(documents: TModel[], _options?: UploadDocumentsOptions): Promise<IndexDocumentsResult>;
|
|
1066
|
+
}
|
|
1067
|
+
//#endregion
|
|
1032
1068
|
//#region src/models/table/MockTableClient.d.ts
|
|
1033
1069
|
/**
|
|
1034
1070
|
* An in-memory mock of the Azure TableClient.
|
|
@@ -1039,7 +1075,9 @@ declare class MockQueueClient implements Except<QueueClient, "accountName"> {
|
|
|
1039
1075
|
* await mockTableClient.createEntity({ partitionKey: "partitionKey", rowKey: "rowKey" });
|
|
1040
1076
|
* const entity = await mockTableClient.getEntity("partitionKey", "rowKey");
|
|
1041
1077
|
*/
|
|
1042
|
-
declare class MockTableClient implements Except<TableClient, "pipeline"> {
|
|
1078
|
+
declare class MockTableClient<TEntity extends TableEntity = TableEntity> implements Except<TableClient, "pipeline"> {
|
|
1079
|
+
#private;
|
|
1080
|
+
entityType: TEntity;
|
|
1043
1081
|
tableName: string;
|
|
1044
1082
|
url: string;
|
|
1045
1083
|
get table(): MapValue<typeof MockTableDatabase>;
|
|
@@ -1055,10 +1093,17 @@ declare class MockTableClient implements Except<TableClient, "pipeline"> {
|
|
|
1055
1093
|
submitTransaction(actions: Parameters<TableClient["submitTransaction"]>[0]): Promise<TableTransactionResponse>;
|
|
1056
1094
|
updateEntity<T extends object>(entity: TableEntity<T>, mode?: UpdateMode): Promise<TableMergeEntityHeaders>;
|
|
1057
1095
|
upsertEntity<T extends object>(entity: TableEntity<T>, mode?: UpdateMode): Promise<TableMergeEntityHeaders>;
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1096
|
+
}
|
|
1097
|
+
//#endregion
|
|
1098
|
+
//#region src/models/webPubSub/MockWebPubSubServiceClient.d.ts
|
|
1099
|
+
/**
|
|
1100
|
+
* A minimal mock of the Azure WebPubSubServiceClient for testing purposes.
|
|
1101
|
+
* Only implements `group().sendToAll` — the only method used in azure-functions tests.
|
|
1102
|
+
*/
|
|
1103
|
+
declare class MockWebPubSubServiceClient {
|
|
1104
|
+
group(_groupName: string): {
|
|
1105
|
+
sendToAll: () => Promise<void>;
|
|
1106
|
+
};
|
|
1062
1107
|
}
|
|
1063
1108
|
//#endregion
|
|
1064
1109
|
//#region src/services/container/bodyToBuffer.d.ts
|
|
@@ -1097,16 +1142,16 @@ declare const isReadableStream: (value: unknown) => value is NodeJS.ReadableStre
|
|
|
1097
1142
|
//#region src/services/container/toWebResourceLike.d.ts
|
|
1098
1143
|
declare const toWebResourceLike: (request: PipelineRequest) => WebResourceLike;
|
|
1099
1144
|
//#endregion
|
|
1100
|
-
//#region src/services/
|
|
1101
|
-
declare const
|
|
1145
|
+
//#region src/services/filter/applyFilter.d.ts
|
|
1146
|
+
declare const applyFilter: <T extends Record<string, unknown>>(documents: T[], clauses: Clause<T>[]) => T[];
|
|
1102
1147
|
//#endregion
|
|
1103
|
-
//#region src/services/
|
|
1148
|
+
//#region src/services/filter/compare.d.ts
|
|
1104
1149
|
declare const compare: <T>(operator: BinaryOperator, leftHandSide: T, rightHandSide: null | T) => boolean;
|
|
1105
1150
|
//#endregion
|
|
1106
|
-
//#region src/services/
|
|
1107
|
-
declare const
|
|
1151
|
+
//#region src/services/filter/createFilterPredicate.d.ts
|
|
1152
|
+
declare const createFilterPredicate: (filter: string) => ((document: Record<string, unknown>) => boolean);
|
|
1108
1153
|
//#endregion
|
|
1109
|
-
//#region src/services/
|
|
1110
|
-
declare const
|
|
1154
|
+
//#region src/services/filter/isNullClause.d.ts
|
|
1155
|
+
declare const isNullClause: (clause: Clause<Record<string, unknown>>) => boolean;
|
|
1111
1156
|
//#endregion
|
|
1112
|
-
export { BlobHierarchyItem, MOCK_BLOB_BASE_URL, MOCK_QUEUE_BASE_URL, MOCK_TABLE_BASE_URL, MockBlobBatchClient, MockBlobClient, MockBlockBlobClient, MockContainerClient, MockContainerDatabase, MockEventGridDatabase, MockEventGridPublisherClient, MockQueueClient, MockQueueDatabase, MockRestError, MockTableClient, MockTableDatabase, PageSettings, PagedAsyncIterableIterator,
|
|
1157
|
+
export { BlobHierarchyItem, 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, PageSettings, PagedAsyncIterableIterator, applyFilter, bodyToBuffer, compare, createFilterPredicate, getAzureErrorXml, getBlobItemXml, getBlobPrefixXml, getListBlobsXml, isNullClause, isReadableStream, toWebResourceLike };
|
package/dist/index.js
CHANGED
|
@@ -3,8 +3,8 @@ import { toHttpHeadersLike } from "@azure/core-http-compat";
|
|
|
3
3
|
import { createHttpHeaders, createPipelineRequest } from "@azure/core-rest-pipeline";
|
|
4
4
|
import { AnonymousCredential } from "@azure/storage-blob";
|
|
5
5
|
import { Readable } from "node:stream";
|
|
6
|
+
import { MAX_QUEUE_VISIBILITY_TIMEOUT_MS, deserializeClause, deserializeKey, getTableNullClause, serializeClauses } from "@esposter/db";
|
|
6
7
|
import { BinaryOperator } from "@esposter/db-schema";
|
|
7
|
-
import { deserializeClause, getTableNullClause, serializeClauses } from "@esposter/db";
|
|
8
8
|
//#region \0rolldown/runtime.js
|
|
9
9
|
var __create = Object.create;
|
|
10
10
|
var __defProp = Object.defineProperty;
|
|
@@ -31,6 +31,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
31
31
|
//#region src/constants.ts
|
|
32
32
|
const MOCK_BLOB_BASE_URL = "https://mockaccount.blob.core.windows.net";
|
|
33
33
|
const MOCK_QUEUE_BASE_URL = "https://mockaccount.queue.core.windows.net";
|
|
34
|
+
const MOCK_SEARCH_BASE_URL = "https://mockaccount.search.windows.net";
|
|
34
35
|
const MOCK_TABLE_BASE_URL = "https://mockaccount.table.core.windows.net";
|
|
35
36
|
//#endregion
|
|
36
37
|
//#region src/models/MockRestError.ts
|
|
@@ -52,6 +53,9 @@ const MockEventGridDatabase = /* @__PURE__ */ new Map();
|
|
|
52
53
|
//#region src/store/MockQueueDatabase.ts
|
|
53
54
|
const MockQueueDatabase = /* @__PURE__ */ new Map();
|
|
54
55
|
//#endregion
|
|
56
|
+
//#region src/store/MockSearchDatabase.ts
|
|
57
|
+
const MockSearchDatabase = /* @__PURE__ */ new Map();
|
|
58
|
+
//#endregion
|
|
55
59
|
//#region src/store/MockTableDatabase.ts
|
|
56
60
|
const MockTableDatabase = /* @__PURE__ */ new Map();
|
|
57
61
|
//#endregion
|
|
@@ -560,7 +564,7 @@ var MockContainerClient = class {
|
|
|
560
564
|
throw new Error("Method not implemented.");
|
|
561
565
|
}
|
|
562
566
|
listBlobsByHierarchy(delimiter, options) {
|
|
563
|
-
const blobHierarchyItemIterator = this
|
|
567
|
+
const blobHierarchyItemIterator = this.#getBlobHierarchyItemIterator(delimiter, options);
|
|
564
568
|
return {
|
|
565
569
|
byPage: () => async function* () {
|
|
566
570
|
const allBlobItems = [];
|
|
@@ -603,7 +607,7 @@ var MockContainerClient = class {
|
|
|
603
607
|
};
|
|
604
608
|
}
|
|
605
609
|
listBlobsFlat() {
|
|
606
|
-
const blobItemIterator = this
|
|
610
|
+
const blobItemIterator = this.#getBlobItemIterator();
|
|
607
611
|
return {
|
|
608
612
|
byPage: () => async function* () {
|
|
609
613
|
const allBlobItems = [];
|
|
@@ -653,7 +657,7 @@ var MockContainerClient = class {
|
|
|
653
657
|
response: await blockBlobClient.upload(body, contentLength)
|
|
654
658
|
};
|
|
655
659
|
}
|
|
656
|
-
async
|
|
660
|
+
async *#getBlobHierarchyItemIterator(delimiter, options) {
|
|
657
661
|
const prefix = options?.prefix ?? "";
|
|
658
662
|
const uniqueSubprefixes = /* @__PURE__ */ new Set();
|
|
659
663
|
const blobsInCurrentLevel = [];
|
|
@@ -689,7 +693,7 @@ var MockContainerClient = class {
|
|
|
689
693
|
...blobItem
|
|
690
694
|
});
|
|
691
695
|
}
|
|
692
|
-
async
|
|
696
|
+
async *#getBlobItemIterator() {
|
|
693
697
|
for (const [name, buffer] of this.container.entries()) yield await Promise.resolve({
|
|
694
698
|
deleted: false,
|
|
695
699
|
name,
|
|
@@ -711,18 +715,21 @@ var MockContainerClient = class {
|
|
|
711
715
|
/**
|
|
712
716
|
* An in-memory mock of the Azure EventGridPublisherClient.
|
|
713
717
|
* It uses a Map to simulate event grid storage and correctly implements the EventGridPublisherClient interface.
|
|
718
|
+
* `client` is excluded because it is a private member of the real class and cannot be satisfied structurally.
|
|
714
719
|
*/
|
|
715
720
|
var MockEventGridPublisherClient = class {
|
|
716
|
-
|
|
721
|
+
endpointUrl;
|
|
722
|
+
inputSchema;
|
|
717
723
|
topicType;
|
|
718
724
|
constructor(endpoint, topicType) {
|
|
719
|
-
this.
|
|
725
|
+
this.endpointUrl = endpoint;
|
|
720
726
|
this.topicType = topicType;
|
|
727
|
+
this.inputSchema = topicType;
|
|
721
728
|
}
|
|
722
729
|
send(newEvents) {
|
|
723
|
-
const events = MockEventGridDatabase.get(this.
|
|
730
|
+
const events = MockEventGridDatabase.get(this.endpointUrl) ?? [];
|
|
724
731
|
events.push(...newEvents);
|
|
725
|
-
MockEventGridDatabase.set(this.
|
|
732
|
+
MockEventGridDatabase.set(this.endpointUrl, events);
|
|
726
733
|
return Promise.resolve();
|
|
727
734
|
}
|
|
728
735
|
};
|
|
@@ -804,7 +811,7 @@ var MockQueueClient = class {
|
|
|
804
811
|
peekMessages(_options) {
|
|
805
812
|
const peekedMessageItems = this.queue.map((text) => ({
|
|
806
813
|
dequeueCount: 0,
|
|
807
|
-
expiresOn: new Date(Date.now() +
|
|
814
|
+
expiresOn: new Date(Date.now() + MAX_QUEUE_VISIBILITY_TIMEOUT_MS),
|
|
808
815
|
insertedOn: /* @__PURE__ */ new Date(),
|
|
809
816
|
messageId: crypto.randomUUID(),
|
|
810
817
|
messageText: text
|
|
@@ -824,7 +831,7 @@ var MockQueueClient = class {
|
|
|
824
831
|
receiveMessages(_options) {
|
|
825
832
|
const receivedMessageItems = this.queue.splice(0).map((text) => ({
|
|
826
833
|
dequeueCount: 1,
|
|
827
|
-
expiresOn: new Date(Date.now() +
|
|
834
|
+
expiresOn: new Date(Date.now() + MAX_QUEUE_VISIBILITY_TIMEOUT_MS),
|
|
828
835
|
insertedOn: /* @__PURE__ */ new Date(),
|
|
829
836
|
messageId: crypto.randomUUID(),
|
|
830
837
|
messageText: text,
|
|
@@ -846,7 +853,7 @@ var MockQueueClient = class {
|
|
|
846
853
|
sendMessage(messageText, _options) {
|
|
847
854
|
this.queue.push(messageText);
|
|
848
855
|
const now = /* @__PURE__ */ new Date();
|
|
849
|
-
const expiresOn = new Date(now.getTime() +
|
|
856
|
+
const expiresOn = new Date(now.getTime() + MAX_QUEUE_VISIBILITY_TIMEOUT_MS);
|
|
850
857
|
const insertedOn = now;
|
|
851
858
|
const messageId = crypto.randomUUID();
|
|
852
859
|
const nextVisibleOn = now;
|
|
@@ -885,7 +892,7 @@ var MockQueueClient = class {
|
|
|
885
892
|
}
|
|
886
893
|
};
|
|
887
894
|
//#endregion
|
|
888
|
-
//#region src/services/
|
|
895
|
+
//#region src/services/filter/compare.ts
|
|
889
896
|
const compare = (operator, leftHandSide, rightHandSide) => {
|
|
890
897
|
if (rightHandSide === null || rightHandSide === void 0) {
|
|
891
898
|
if (operator !== BinaryOperator.eq) throw new InvalidOperationError(Operation.Read, compare.name, JSON.stringify({
|
|
@@ -893,7 +900,7 @@ const compare = (operator, leftHandSide, rightHandSide) => {
|
|
|
893
900
|
operator,
|
|
894
901
|
rightHandSide
|
|
895
902
|
}));
|
|
896
|
-
return leftHandSide ===
|
|
903
|
+
return leftHandSide === null || leftHandSide === void 0;
|
|
897
904
|
}
|
|
898
905
|
switch (operator) {
|
|
899
906
|
case BinaryOperator.eq: return leftHandSide === rightHandSide;
|
|
@@ -906,7 +913,7 @@ const compare = (operator, leftHandSide, rightHandSide) => {
|
|
|
906
913
|
}
|
|
907
914
|
};
|
|
908
915
|
//#endregion
|
|
909
|
-
//#region src/services/
|
|
916
|
+
//#region src/services/filter/isNullClause.ts
|
|
910
917
|
var import_fast_deep_equal = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
911
918
|
module.exports = function equal(a, b) {
|
|
912
919
|
if (a === b) return true;
|
|
@@ -935,21 +942,21 @@ var import_fast_deep_equal = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJS
|
|
|
935
942
|
return a !== a && b !== b;
|
|
936
943
|
};
|
|
937
944
|
})))(), 1);
|
|
938
|
-
const
|
|
945
|
+
const isNullClause = (clause) => {
|
|
939
946
|
return (0, import_fast_deep_equal.default)(clause, getTableNullClause(clause.key));
|
|
940
947
|
};
|
|
941
948
|
//#endregion
|
|
942
|
-
//#region src/services/
|
|
943
|
-
const
|
|
949
|
+
//#region src/services/filter/createFilterPredicate.ts
|
|
950
|
+
const createFilterPredicate = (filter) => {
|
|
944
951
|
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));
|
|
945
|
-
return (
|
|
952
|
+
return (document) => {
|
|
946
953
|
for (const orGroup of orGroups) {
|
|
947
954
|
let isGroupMatched = false;
|
|
948
955
|
for (const group of orGroup) {
|
|
949
956
|
const clause = deserializeClause(group);
|
|
950
|
-
const value = takeOne(
|
|
951
|
-
let isMatched
|
|
952
|
-
if (
|
|
957
|
+
const value = takeOne(document, clause.key);
|
|
958
|
+
let isMatched;
|
|
959
|
+
if (isNullClause(clause)) isMatched = compare(BinaryOperator.eq, value, null);
|
|
953
960
|
else {
|
|
954
961
|
const comparisonResult = compare(clause.operator, value, clause.value);
|
|
955
962
|
isMatched = clause.not ? !comparisonResult : comparisonResult;
|
|
@@ -965,6 +972,112 @@ const createTableFilterPredicate = (filter) => {
|
|
|
965
972
|
};
|
|
966
973
|
};
|
|
967
974
|
//#endregion
|
|
975
|
+
//#region src/models/search/MockSearchClient.ts
|
|
976
|
+
const toComparable = (value) => {
|
|
977
|
+
if (value instanceof Date) return value.getTime();
|
|
978
|
+
else if (typeof value === "number" || typeof value === "string") return value;
|
|
979
|
+
else return String(value);
|
|
980
|
+
};
|
|
981
|
+
const compareValues = (leftHandSide, rightHandSide) => {
|
|
982
|
+
const left = toComparable(leftHandSide);
|
|
983
|
+
const right = toComparable(rightHandSide);
|
|
984
|
+
if (typeof left === "number" && typeof right === "number") return left - right;
|
|
985
|
+
return String(left).localeCompare(String(right));
|
|
986
|
+
};
|
|
987
|
+
const sortDocuments = (documents, orderBy) => [...documents].toSorted((leftHandSide, rightHandSide) => {
|
|
988
|
+
for (const clause of orderBy) {
|
|
989
|
+
const [field = "", direction = "asc"] = clause.split(/\s+/u);
|
|
990
|
+
const key = deserializeKey(field);
|
|
991
|
+
const comparison = compareValues(leftHandSide[key], rightHandSide[key]);
|
|
992
|
+
if (comparison !== 0) return direction === "desc" ? -comparison : comparison;
|
|
993
|
+
}
|
|
994
|
+
return 0;
|
|
995
|
+
});
|
|
996
|
+
const getSearchFieldValues = (value, pathSegments) => {
|
|
997
|
+
if (pathSegments.length === 0) return [value];
|
|
998
|
+
if (Array.isArray(value)) return value.flatMap((item) => getSearchFieldValues(item, pathSegments));
|
|
999
|
+
if (typeof value !== "object" || value === null) return [];
|
|
1000
|
+
const [field = "", ...remainingPathSegments] = pathSegments;
|
|
1001
|
+
return getSearchFieldValues(value[deserializeKey(field)], remainingPathSegments);
|
|
1002
|
+
};
|
|
1003
|
+
const searchDocuments = (documents, searchText, searchFields) => {
|
|
1004
|
+
if (!searchText || searchText === "*") return documents;
|
|
1005
|
+
const normalizedSearchText = searchText.toLocaleLowerCase();
|
|
1006
|
+
return documents.filter((document) => (searchFields ?? Object.keys(document)).some((searchField) => getSearchFieldValues(document, searchField.split("/")).some((value) => value !== null && value !== void 0 && String(value).toLocaleLowerCase().includes(normalizedSearchText))));
|
|
1007
|
+
};
|
|
1008
|
+
/**
|
|
1009
|
+
* An in-memory mock of the Azure SearchClient.
|
|
1010
|
+
* It uses a Map to simulate the search index and applies the same OData filtering as the other mock clients.
|
|
1011
|
+
*
|
|
1012
|
+
* @example
|
|
1013
|
+
* const mockSearchClient = new MockSearchClient(SearchIndex.Messages);
|
|
1014
|
+
* await mockSearchClient.uploadDocuments([{ ... }]);
|
|
1015
|
+
* const { count, results } = await mockSearchClient.search("*", { filter, includeTotalCount: true });
|
|
1016
|
+
*/
|
|
1017
|
+
var MockSearchClient = class {
|
|
1018
|
+
apiVersion = "";
|
|
1019
|
+
endpoint = MOCK_SEARCH_BASE_URL;
|
|
1020
|
+
indexName;
|
|
1021
|
+
serviceVersion = "";
|
|
1022
|
+
get documents() {
|
|
1023
|
+
let documents = MockSearchDatabase.get(this.indexName);
|
|
1024
|
+
if (!documents) {
|
|
1025
|
+
documents = [];
|
|
1026
|
+
MockSearchDatabase.set(this.indexName, documents);
|
|
1027
|
+
}
|
|
1028
|
+
return documents;
|
|
1029
|
+
}
|
|
1030
|
+
constructor(indexName) {
|
|
1031
|
+
this.indexName = indexName;
|
|
1032
|
+
}
|
|
1033
|
+
autocomplete() {
|
|
1034
|
+
throw new Error("Method not implemented.");
|
|
1035
|
+
}
|
|
1036
|
+
deleteDocuments() {
|
|
1037
|
+
throw new Error("Method not implemented.");
|
|
1038
|
+
}
|
|
1039
|
+
getDocument(_key, _options) {
|
|
1040
|
+
throw new Error("Method not implemented.");
|
|
1041
|
+
}
|
|
1042
|
+
getDocumentsCount(_options) {
|
|
1043
|
+
return Promise.resolve(this.documents.length);
|
|
1044
|
+
}
|
|
1045
|
+
indexDocuments(_batch, _options) {
|
|
1046
|
+
throw new Error("Method not implemented.");
|
|
1047
|
+
}
|
|
1048
|
+
mergeDocuments(_documents, _options) {
|
|
1049
|
+
throw new Error("Method not implemented.");
|
|
1050
|
+
}
|
|
1051
|
+
mergeOrUploadDocuments(_documents, _options) {
|
|
1052
|
+
throw new Error("Method not implemented.");
|
|
1053
|
+
}
|
|
1054
|
+
search(searchText, options) {
|
|
1055
|
+
const { filter, includeTotalCount, orderBy, searchFields, skip = 0, top } = options ?? {};
|
|
1056
|
+
let documents = searchDocuments(this.documents, searchText, searchFields);
|
|
1057
|
+
if (filter) {
|
|
1058
|
+
const predicate = createFilterPredicate(filter);
|
|
1059
|
+
documents = documents.filter((document) => predicate(document));
|
|
1060
|
+
}
|
|
1061
|
+
if (orderBy) documents = sortDocuments(documents, orderBy);
|
|
1062
|
+
const count = documents.length;
|
|
1063
|
+
const paginatedDocuments = documents.slice(skip, top === void 0 ? void 0 : skip + top);
|
|
1064
|
+
const results = { async *[Symbol.asyncIterator]() {
|
|
1065
|
+
for (const document of paginatedDocuments) yield { document };
|
|
1066
|
+
} };
|
|
1067
|
+
return Promise.resolve({
|
|
1068
|
+
...includeTotalCount ? { count } : {},
|
|
1069
|
+
results
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
suggest(_searchText, _suggesterName, _options) {
|
|
1073
|
+
throw new Error("Method not implemented.");
|
|
1074
|
+
}
|
|
1075
|
+
uploadDocuments(documents, _options) {
|
|
1076
|
+
this.documents.push(...documents);
|
|
1077
|
+
return Promise.resolve({ results: [] });
|
|
1078
|
+
}
|
|
1079
|
+
};
|
|
1080
|
+
//#endregion
|
|
968
1081
|
//#region src/models/table/MockTableClient.ts
|
|
969
1082
|
/**
|
|
970
1083
|
* An in-memory mock of the Azure TableClient.
|
|
@@ -991,19 +1104,19 @@ var MockTableClient = class {
|
|
|
991
1104
|
this.url = `${MOCK_TABLE_BASE_URL}/${this.tableName}`;
|
|
992
1105
|
}
|
|
993
1106
|
createEntity(entity) {
|
|
994
|
-
const key = this
|
|
1107
|
+
const key = this.#getCompositeKey(entity.partitionKey, entity.rowKey);
|
|
995
1108
|
if (this.table.has(key)) throw new MockRestError("The specified entity already exists.", 409);
|
|
996
1109
|
this.table.set(key, entity);
|
|
997
1110
|
return Promise.resolve({
|
|
998
1111
|
date: /* @__PURE__ */ new Date(),
|
|
999
|
-
etag: this
|
|
1112
|
+
etag: this.#getEtag()
|
|
1000
1113
|
});
|
|
1001
1114
|
}
|
|
1002
1115
|
createTable() {
|
|
1003
1116
|
throw new Error("Method not implemented.");
|
|
1004
1117
|
}
|
|
1005
1118
|
deleteEntity(partitionKey, rowKey) {
|
|
1006
|
-
const key = this
|
|
1119
|
+
const key = this.#getCompositeKey(partitionKey, rowKey);
|
|
1007
1120
|
if (!this.table.has(key)) throw new MockRestError("The specified resource does not exist.", 404);
|
|
1008
1121
|
this.table.delete(key);
|
|
1009
1122
|
return Promise.resolve({});
|
|
@@ -1015,19 +1128,20 @@ var MockTableClient = class {
|
|
|
1015
1128
|
throw new Error("Method not implemented.");
|
|
1016
1129
|
}
|
|
1017
1130
|
getEntity(partitionKey, rowKey) {
|
|
1018
|
-
const key = this
|
|
1131
|
+
const key = this.#getCompositeKey(partitionKey, rowKey);
|
|
1019
1132
|
const entity = this.table.get(key);
|
|
1020
1133
|
if (!entity) throw new MockRestError("The specified resource does not exist.", 404);
|
|
1021
1134
|
return Promise.resolve({
|
|
1022
1135
|
...entity,
|
|
1023
|
-
etag: this
|
|
1136
|
+
etag: this.#getEtag()
|
|
1024
1137
|
});
|
|
1025
1138
|
}
|
|
1026
1139
|
listEntities(options) {
|
|
1027
|
-
const withMetadata = this
|
|
1140
|
+
const withMetadata = this.#withMetadata.bind(this);
|
|
1028
1141
|
const filter = options?.queryOptions?.filter;
|
|
1029
1142
|
const tableEntities = [...this.table.values()];
|
|
1030
|
-
const
|
|
1143
|
+
const predicate = filter ? createFilterPredicate(filter) : void 0;
|
|
1144
|
+
const resultTableEntities = predicate ? tableEntities.filter((e) => predicate(e)) : tableEntities;
|
|
1031
1145
|
return {
|
|
1032
1146
|
byPage: ({ maxPageSize } = {}) => (async function* (entities) {
|
|
1033
1147
|
if (maxPageSize !== void 0 && maxPageSize <= 0) throw new RangeError("maxPageSize must be greater than 0.");
|
|
@@ -1084,58 +1198,69 @@ var MockTableClient = class {
|
|
|
1084
1198
|
};
|
|
1085
1199
|
}
|
|
1086
1200
|
updateEntity(entity, mode = "Merge") {
|
|
1087
|
-
const key = this
|
|
1201
|
+
const key = this.#getCompositeKey(entity.partitionKey, entity.rowKey);
|
|
1088
1202
|
const existingEntity = this.table.get(key);
|
|
1089
1203
|
if (!existingEntity) throw new MockRestError("The specified resource does not exist.", 404);
|
|
1090
|
-
else if (mode === "Merge") return this
|
|
1204
|
+
else if (mode === "Merge") return this.#mergeEntity(key, existingEntity, entity);
|
|
1091
1205
|
else {
|
|
1092
1206
|
this.table.set(key, entity);
|
|
1093
1207
|
return Promise.resolve({
|
|
1094
1208
|
date: /* @__PURE__ */ new Date(),
|
|
1095
|
-
etag: this
|
|
1209
|
+
etag: this.#getEtag()
|
|
1096
1210
|
});
|
|
1097
1211
|
}
|
|
1098
1212
|
}
|
|
1099
1213
|
upsertEntity(entity, mode = "Merge") {
|
|
1100
|
-
const key = this
|
|
1214
|
+
const key = this.#getCompositeKey(entity.partitionKey, entity.rowKey);
|
|
1101
1215
|
const existingEntity = this.table.get(key);
|
|
1102
|
-
if (existingEntity && mode === "Merge") return this
|
|
1216
|
+
if (existingEntity && mode === "Merge") return this.#mergeEntity(key, existingEntity, entity);
|
|
1103
1217
|
else {
|
|
1104
1218
|
this.table.set(key, entity);
|
|
1105
1219
|
return Promise.resolve({
|
|
1106
1220
|
date: /* @__PURE__ */ new Date(),
|
|
1107
|
-
etag: this
|
|
1221
|
+
etag: this.#getEtag()
|
|
1108
1222
|
});
|
|
1109
1223
|
}
|
|
1110
1224
|
}
|
|
1111
|
-
getCompositeKey(partitionKey, rowKey) {
|
|
1225
|
+
#getCompositeKey(partitionKey, rowKey) {
|
|
1112
1226
|
return `${partitionKey}${ID_SEPARATOR}${rowKey}`;
|
|
1113
1227
|
}
|
|
1114
|
-
getEtag() {
|
|
1228
|
+
#getEtag() {
|
|
1115
1229
|
return `W/"datetime'${(/* @__PURE__ */ new Date()).toISOString()}'"`;
|
|
1116
1230
|
}
|
|
1117
|
-
mergeEntity(key, entity, entityToMerge) {
|
|
1231
|
+
#mergeEntity(key, entity, entityToMerge) {
|
|
1118
1232
|
this.table.set(key, {
|
|
1119
1233
|
...entity,
|
|
1120
1234
|
...entityToMerge
|
|
1121
1235
|
});
|
|
1122
1236
|
return Promise.resolve({
|
|
1123
1237
|
date: /* @__PURE__ */ new Date(),
|
|
1124
|
-
etag: this
|
|
1238
|
+
etag: this.#getEtag()
|
|
1125
1239
|
});
|
|
1126
1240
|
}
|
|
1127
|
-
withMetadata(entity) {
|
|
1241
|
+
#withMetadata(entity) {
|
|
1128
1242
|
return {
|
|
1129
1243
|
...entity,
|
|
1130
|
-
etag: this
|
|
1244
|
+
etag: this.#getEtag()
|
|
1131
1245
|
};
|
|
1132
1246
|
}
|
|
1133
1247
|
};
|
|
1134
1248
|
//#endregion
|
|
1135
|
-
//#region src/
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1249
|
+
//#region src/models/webPubSub/MockWebPubSubServiceClient.ts
|
|
1250
|
+
/**
|
|
1251
|
+
* A minimal mock of the Azure WebPubSubServiceClient for testing purposes.
|
|
1252
|
+
* Only implements `group().sendToAll` — the only method used in azure-functions tests.
|
|
1253
|
+
*/
|
|
1254
|
+
var MockWebPubSubServiceClient = class {
|
|
1255
|
+
group(_groupName) {
|
|
1256
|
+
return { sendToAll: () => Promise.resolve() };
|
|
1257
|
+
}
|
|
1258
|
+
};
|
|
1259
|
+
//#endregion
|
|
1260
|
+
//#region src/services/filter/applyFilter.ts
|
|
1261
|
+
const applyFilter = (documents, clauses) => {
|
|
1262
|
+
const predicate = createFilterPredicate(serializeClauses(clauses));
|
|
1263
|
+
return documents.filter((document) => predicate(document));
|
|
1139
1264
|
};
|
|
1140
1265
|
//#endregion
|
|
1141
|
-
export { MOCK_BLOB_BASE_URL, MOCK_QUEUE_BASE_URL, MOCK_TABLE_BASE_URL, MockBlobBatchClient, MockBlobClient, MockBlockBlobClient, MockContainerClient, MockContainerDatabase, MockEventGridDatabase, MockEventGridPublisherClient, MockQueueClient, MockQueueDatabase, MockRestError, MockTableClient, MockTableDatabase,
|
|
1266
|
+
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, getAzureErrorXml, getBlobItemXml, getBlobPrefixXml, getListBlobsXml, isNullClause, isReadableStream, toWebResourceLike };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "azure-mock",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.29.0",
|
|
4
4
|
"description": "A library that contains azure mock classes.",
|
|
5
5
|
"homepage": "https://github.com/Esposter/Esposter#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -23,7 +23,6 @@
|
|
|
23
23
|
},
|
|
24
24
|
"scripts": {
|
|
25
25
|
"build": "pnpm export:gen && rolldown --config rolldown.config.ts",
|
|
26
|
-
"coverage": "vitest run --coverage",
|
|
27
26
|
"export:gen": "ctix build --config ../configuration/.ctirc-ts",
|
|
28
27
|
"format": "oxfmt",
|
|
29
28
|
"format:check": "oxfmt --check",
|
|
@@ -33,26 +32,27 @@
|
|
|
33
32
|
"typecheck": "tsgo"
|
|
34
33
|
},
|
|
35
34
|
"dependencies": {
|
|
36
|
-
"@esposter/db": "2.
|
|
37
|
-
"@esposter/db-schema": "2.
|
|
38
|
-
"@esposter/shared": "2.
|
|
35
|
+
"@esposter/db": "2.29.0",
|
|
36
|
+
"@esposter/db-schema": "2.29.0",
|
|
37
|
+
"@esposter/shared": "2.29.0",
|
|
39
38
|
"fast-deep-equal": "^3.1.3"
|
|
40
39
|
},
|
|
41
40
|
"devDependencies": {
|
|
42
|
-
"@esposter/configuration": "2.
|
|
43
|
-
"@vitest/coverage-v8": "^4.1.
|
|
41
|
+
"@esposter/configuration": "2.29.0",
|
|
42
|
+
"@vitest/coverage-v8": "^4.1.9",
|
|
44
43
|
"ctix": "^2.8.1",
|
|
45
|
-
"rolldown": "^1.1.
|
|
44
|
+
"rolldown": "^1.1.2",
|
|
46
45
|
"type-fest": "^5.7.0",
|
|
47
|
-
"vitest": "^4.1.
|
|
46
|
+
"vitest": "^4.1.9"
|
|
48
47
|
},
|
|
49
48
|
"peerDependencies": {
|
|
50
49
|
"@azure/core-http-compat": "^2.4.0",
|
|
51
|
-
"@azure/core-rest-pipeline": "^1.
|
|
50
|
+
"@azure/core-rest-pipeline": "^1.24.0",
|
|
52
51
|
"@azure/data-tables": "^13.3.2",
|
|
53
52
|
"@azure/eventgrid": "^5.12.0",
|
|
53
|
+
"@azure/search-documents": "^13.0.0",
|
|
54
54
|
"@azure/storage-blob": "^12.32.0",
|
|
55
|
-
"@azure/storage-queue": "^12.
|
|
55
|
+
"@azure/storage-queue": "^12.30.0"
|
|
56
56
|
},
|
|
57
|
-
"gitHead": "
|
|
57
|
+
"gitHead": "0166c5a9bea8b089ce37499f66b8d8095f4b4b23"
|
|
58
58
|
}
|