@fedify/testing 2.0.0-dev.221 → 2.0.0-dev.228
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/dist/mod.cjs +137 -5
- package/dist/mod.d.cts +52 -1
- package/dist/mod.d.ts +54 -2
- package/dist/mod.js +134 -5
- package/package.json +7 -3
package/dist/mod.cjs
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
|
|
2
|
+
const { Temporal } = require("@js-temporal/polyfill");
|
|
3
|
+
|
|
1
4
|
//#region rolldown:runtime
|
|
2
5
|
var __create = Object.create;
|
|
3
6
|
var __defProp = Object.defineProperty;
|
|
@@ -21,8 +24,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
21
24
|
}) : target, mod));
|
|
22
25
|
|
|
23
26
|
//#endregion
|
|
24
|
-
const __fedify_fedify_federation = __toESM(require("@fedify/fedify/federation"));
|
|
25
27
|
const __fedify_vocab = __toESM(require("@fedify/vocab"));
|
|
28
|
+
const __fedify_fedify_federation = __toESM(require("@fedify/fedify/federation"));
|
|
29
|
+
const es_toolkit = __toESM(require("es-toolkit"));
|
|
30
|
+
const node_assert_strict = __toESM(require("node:assert/strict"));
|
|
26
31
|
|
|
27
32
|
//#region src/docloader.ts
|
|
28
33
|
const mockDocumentLoader = async (url) => ({
|
|
@@ -205,6 +210,7 @@ var MockFederation = class {
|
|
|
205
210
|
nodeInfoDispatcher;
|
|
206
211
|
webFingerDispatcher;
|
|
207
212
|
actorDispatchers = /* @__PURE__ */ new Map();
|
|
213
|
+
actorKeyPairsDispatcher;
|
|
208
214
|
actorPath;
|
|
209
215
|
inboxPath;
|
|
210
216
|
outboxPath;
|
|
@@ -242,7 +248,10 @@ var MockFederation = class {
|
|
|
242
248
|
this.actorDispatchers.set(path, dispatcher);
|
|
243
249
|
this.actorPath = path;
|
|
244
250
|
return {
|
|
245
|
-
setKeyPairsDispatcher: () =>
|
|
251
|
+
setKeyPairsDispatcher: (keyPairsDispatcher) => {
|
|
252
|
+
this.actorKeyPairsDispatcher = keyPairsDispatcher;
|
|
253
|
+
return this;
|
|
254
|
+
},
|
|
246
255
|
mapHandle: () => this,
|
|
247
256
|
mapAlias: () => this,
|
|
248
257
|
authorize: () => this
|
|
@@ -678,8 +687,25 @@ var MockContext = class MockContext {
|
|
|
678
687
|
}
|
|
679
688
|
return null;
|
|
680
689
|
}
|
|
681
|
-
getActorKeyPairs(
|
|
682
|
-
|
|
690
|
+
async getActorKeyPairs(identifier) {
|
|
691
|
+
if (this.federation instanceof MockFederation && this.federation.actorKeyPairsDispatcher) {
|
|
692
|
+
const keyPairs = await this.federation.actorKeyPairsDispatcher(this, identifier);
|
|
693
|
+
const owner = this.getActorUri(identifier);
|
|
694
|
+
return keyPairs.map((kp) => ({
|
|
695
|
+
...kp,
|
|
696
|
+
cryptographicKey: new __fedify_vocab.CryptographicKey({
|
|
697
|
+
id: kp.keyId,
|
|
698
|
+
owner,
|
|
699
|
+
publicKey: kp.publicKey
|
|
700
|
+
}),
|
|
701
|
+
multikey: new __fedify_vocab.Multikey({
|
|
702
|
+
id: kp.keyId,
|
|
703
|
+
controller: owner,
|
|
704
|
+
publicKey: kp.publicKey
|
|
705
|
+
})
|
|
706
|
+
}));
|
|
707
|
+
}
|
|
708
|
+
return [];
|
|
683
709
|
}
|
|
684
710
|
getDocumentLoader(params) {
|
|
685
711
|
if ("keyId" in params) return this.documentLoader;
|
|
@@ -737,8 +763,114 @@ var MockContext = class MockContext {
|
|
|
737
763
|
}
|
|
738
764
|
};
|
|
739
765
|
|
|
766
|
+
//#endregion
|
|
767
|
+
//#region src/mq-tester.ts
|
|
768
|
+
/**
|
|
769
|
+
* Tests a {@link MessageQueue} implementation with a standard set of tests.
|
|
770
|
+
*
|
|
771
|
+
* This function runs tests for:
|
|
772
|
+
* - `enqueue()`: Basic message enqueueing
|
|
773
|
+
* - `enqueue()` with delay: Delayed message enqueueing
|
|
774
|
+
* - `enqueueMany()`: Bulk message enqueueing
|
|
775
|
+
* - `enqueueMany()` with delay: Delayed bulk message enqueueing
|
|
776
|
+
* - Multiple listeners: Ensures messages are processed by only one listener
|
|
777
|
+
*
|
|
778
|
+
* @example
|
|
779
|
+
* ```typescript ignore
|
|
780
|
+
* import { test } from "@fedify/fixture";
|
|
781
|
+
* import { testMessageQueue } from "@fedify/testing";
|
|
782
|
+
* import { MyMessageQueue } from "./my-mq.ts";
|
|
783
|
+
*
|
|
784
|
+
* test("MyMessageQueue", () =>
|
|
785
|
+
* testMessageQueue(
|
|
786
|
+
* () => new MyMessageQueue(),
|
|
787
|
+
* async ({ mq1, mq2, controller }) => {
|
|
788
|
+
* controller.abort();
|
|
789
|
+
* await mq1.close();
|
|
790
|
+
* await mq2.close();
|
|
791
|
+
* },
|
|
792
|
+
* )
|
|
793
|
+
* );
|
|
794
|
+
* ```
|
|
795
|
+
*
|
|
796
|
+
* @param getMessageQueue A factory function that creates a new message queue
|
|
797
|
+
* instance. It should return a new instance each time
|
|
798
|
+
* to ensure test isolation, but both instances should
|
|
799
|
+
* share the same underlying storage/channel.
|
|
800
|
+
* @param onFinally A cleanup function called after all tests complete.
|
|
801
|
+
* It receives both message queue instances and the abort
|
|
802
|
+
* controller used for the listeners.
|
|
803
|
+
* @returns A promise that resolves when all tests pass.
|
|
804
|
+
*/
|
|
805
|
+
async function testMessageQueue(getMessageQueue, onFinally) {
|
|
806
|
+
const mq1 = await getMessageQueue();
|
|
807
|
+
const mq2 = await getMessageQueue();
|
|
808
|
+
const controller = new AbortController();
|
|
809
|
+
try {
|
|
810
|
+
const messages = [];
|
|
811
|
+
const listening1 = mq1.listen((message) => {
|
|
812
|
+
messages.push(message);
|
|
813
|
+
}, { signal: controller.signal });
|
|
814
|
+
const listening2 = mq2.listen((message) => {
|
|
815
|
+
messages.push(message);
|
|
816
|
+
}, { signal: controller.signal });
|
|
817
|
+
await mq1.enqueue("Hello, world!");
|
|
818
|
+
await waitFor(() => messages.length > 0, 15e3);
|
|
819
|
+
(0, node_assert_strict.deepStrictEqual)(messages, ["Hello, world!"]);
|
|
820
|
+
let started = Date.now();
|
|
821
|
+
await mq1.enqueue("Delayed message", { delay: Temporal.Duration.from({ seconds: 3 }) });
|
|
822
|
+
await waitFor(() => messages.length > 1, 15e3);
|
|
823
|
+
(0, node_assert_strict.deepStrictEqual)(messages, ["Hello, world!", "Delayed message"]);
|
|
824
|
+
(0, node_assert_strict.ok)(Date.now() - started >= 3e3, "Delayed message should be delivered after at least 3 seconds");
|
|
825
|
+
if (mq1.enqueueMany != null) {
|
|
826
|
+
while (messages.length > 0) messages.pop();
|
|
827
|
+
const batchMessages = [
|
|
828
|
+
"First batch message",
|
|
829
|
+
"Second batch message",
|
|
830
|
+
"Third batch message"
|
|
831
|
+
];
|
|
832
|
+
await mq1.enqueueMany(batchMessages);
|
|
833
|
+
await waitFor(() => messages.length >= batchMessages.length, 15e3);
|
|
834
|
+
(0, node_assert_strict.deepStrictEqual)(new Set(messages), new Set(batchMessages));
|
|
835
|
+
while (messages.length > 0) messages.pop();
|
|
836
|
+
started = Date.now();
|
|
837
|
+
const delayedBatchMessages = ["Delayed batch 1", "Delayed batch 2"];
|
|
838
|
+
await mq1.enqueueMany(delayedBatchMessages, { delay: Temporal.Duration.from({ seconds: 2 }) });
|
|
839
|
+
await waitFor(() => messages.length >= delayedBatchMessages.length, 15e3);
|
|
840
|
+
(0, node_assert_strict.deepStrictEqual)(new Set(messages), new Set(delayedBatchMessages));
|
|
841
|
+
(0, node_assert_strict.ok)(Date.now() - started >= 2e3, "Delayed batch messages should be delivered after at least 2 seconds");
|
|
842
|
+
}
|
|
843
|
+
while (messages.length > 0) messages.pop();
|
|
844
|
+
const bulkCount = 100;
|
|
845
|
+
for (let i = 0; i < bulkCount; i++) await mq1.enqueue(`message-${i}`);
|
|
846
|
+
await waitFor(() => messages.length >= bulkCount, 3e4);
|
|
847
|
+
const expectedMessages = new Set(Array.from({ length: bulkCount }, (_, i) => `message-${i}`));
|
|
848
|
+
(0, node_assert_strict.deepStrictEqual)(new Set(messages), expectedMessages);
|
|
849
|
+
controller.abort();
|
|
850
|
+
await listening1;
|
|
851
|
+
await listening2;
|
|
852
|
+
} finally {
|
|
853
|
+
await onFinally({
|
|
854
|
+
mq1,
|
|
855
|
+
mq2,
|
|
856
|
+
controller
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
async function waitFor(predicate, timeoutMs) {
|
|
861
|
+
const started = Date.now();
|
|
862
|
+
while (!predicate()) {
|
|
863
|
+
await (0, es_toolkit.delay)(500);
|
|
864
|
+
if (Date.now() - started > timeoutMs) throw new Error("Timeout");
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
const getRandomKey = (prefix) => `fedify_test_${prefix}_${crypto.randomUUID()}`;
|
|
868
|
+
|
|
740
869
|
//#endregion
|
|
741
870
|
exports.createContext = createContext;
|
|
742
871
|
exports.createFederation = createFederation;
|
|
743
872
|
exports.createInboxContext = createInboxContext;
|
|
744
|
-
exports.createRequestContext = createRequestContext;
|
|
873
|
+
exports.createRequestContext = createRequestContext;
|
|
874
|
+
exports.getRandomKey = getRandomKey;
|
|
875
|
+
exports.testMessageQueue = testMessageQueue;
|
|
876
|
+
exports.waitFor = waitFor;
|
package/dist/mod.d.cts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Context, Federation, InboxContext, RequestContext } from "@fedify/fedify/federation";
|
|
2
2
|
import { Activity } from "@fedify/vocab";
|
|
3
|
+
import { MessageQueue } from "@fedify/fedify";
|
|
3
4
|
|
|
4
5
|
//#region src/context.d.ts
|
|
5
6
|
declare function createContext<TContextData>(values: Partial<Context<TContextData>> & {
|
|
@@ -123,4 +124,54 @@ declare function createFederation<TContextData>(options?: {
|
|
|
123
124
|
tracerProvider?: any;
|
|
124
125
|
}): TestFederation<TContextData>;
|
|
125
126
|
//#endregion
|
|
126
|
-
|
|
127
|
+
//#region src/mq-tester.d.ts
|
|
128
|
+
/**
|
|
129
|
+
* Tests a {@link MessageQueue} implementation with a standard set of tests.
|
|
130
|
+
*
|
|
131
|
+
* This function runs tests for:
|
|
132
|
+
* - `enqueue()`: Basic message enqueueing
|
|
133
|
+
* - `enqueue()` with delay: Delayed message enqueueing
|
|
134
|
+
* - `enqueueMany()`: Bulk message enqueueing
|
|
135
|
+
* - `enqueueMany()` with delay: Delayed bulk message enqueueing
|
|
136
|
+
* - Multiple listeners: Ensures messages are processed by only one listener
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```typescript ignore
|
|
140
|
+
* import { test } from "@fedify/fixture";
|
|
141
|
+
* import { testMessageQueue } from "@fedify/testing";
|
|
142
|
+
* import { MyMessageQueue } from "./my-mq.ts";
|
|
143
|
+
*
|
|
144
|
+
* test("MyMessageQueue", () =>
|
|
145
|
+
* testMessageQueue(
|
|
146
|
+
* () => new MyMessageQueue(),
|
|
147
|
+
* async ({ mq1, mq2, controller }) => {
|
|
148
|
+
* controller.abort();
|
|
149
|
+
* await mq1.close();
|
|
150
|
+
* await mq2.close();
|
|
151
|
+
* },
|
|
152
|
+
* )
|
|
153
|
+
* );
|
|
154
|
+
* ```
|
|
155
|
+
*
|
|
156
|
+
* @param getMessageQueue A factory function that creates a new message queue
|
|
157
|
+
* instance. It should return a new instance each time
|
|
158
|
+
* to ensure test isolation, but both instances should
|
|
159
|
+
* share the same underlying storage/channel.
|
|
160
|
+
* @param onFinally A cleanup function called after all tests complete.
|
|
161
|
+
* It receives both message queue instances and the abort
|
|
162
|
+
* controller used for the listeners.
|
|
163
|
+
* @returns A promise that resolves when all tests pass.
|
|
164
|
+
*/
|
|
165
|
+
declare function testMessageQueue<MQ extends MessageQueue>(getMessageQueue: () => MQ | Promise<MQ>, onFinally: ({
|
|
166
|
+
mq1,
|
|
167
|
+
mq2,
|
|
168
|
+
controller
|
|
169
|
+
}: {
|
|
170
|
+
mq1: MQ;
|
|
171
|
+
mq2: MQ;
|
|
172
|
+
controller: AbortController;
|
|
173
|
+
}) => Promise<void> | void): Promise<void>;
|
|
174
|
+
declare function waitFor(predicate: () => boolean, timeoutMs: number): Promise<void>;
|
|
175
|
+
declare const getRandomKey: (prefix: string) => string;
|
|
176
|
+
//#endregion
|
|
177
|
+
export { createContext, createFederation, createInboxContext, createRequestContext, getRandomKey, testMessageQueue, waitFor };
|
package/dist/mod.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Temporal } from "@js-temporal/polyfill";
|
|
2
2
|
import { Activity } from "@fedify/vocab";
|
|
3
|
+
import { Context, Federation, InboxContext, RequestContext } from "@fedify/fedify/federation";
|
|
4
|
+
import { MessageQueue } from "@fedify/fedify";
|
|
3
5
|
|
|
4
6
|
//#region src/context.d.ts
|
|
5
7
|
declare function createContext<TContextData>(values: Partial<Context<TContextData>> & {
|
|
@@ -123,4 +125,54 @@ declare function createFederation<TContextData>(options?: {
|
|
|
123
125
|
tracerProvider?: any;
|
|
124
126
|
}): TestFederation<TContextData>;
|
|
125
127
|
//#endregion
|
|
126
|
-
|
|
128
|
+
//#region src/mq-tester.d.ts
|
|
129
|
+
/**
|
|
130
|
+
* Tests a {@link MessageQueue} implementation with a standard set of tests.
|
|
131
|
+
*
|
|
132
|
+
* This function runs tests for:
|
|
133
|
+
* - `enqueue()`: Basic message enqueueing
|
|
134
|
+
* - `enqueue()` with delay: Delayed message enqueueing
|
|
135
|
+
* - `enqueueMany()`: Bulk message enqueueing
|
|
136
|
+
* - `enqueueMany()` with delay: Delayed bulk message enqueueing
|
|
137
|
+
* - Multiple listeners: Ensures messages are processed by only one listener
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```typescript ignore
|
|
141
|
+
* import { test } from "@fedify/fixture";
|
|
142
|
+
* import { testMessageQueue } from "@fedify/testing";
|
|
143
|
+
* import { MyMessageQueue } from "./my-mq.ts";
|
|
144
|
+
*
|
|
145
|
+
* test("MyMessageQueue", () =>
|
|
146
|
+
* testMessageQueue(
|
|
147
|
+
* () => new MyMessageQueue(),
|
|
148
|
+
* async ({ mq1, mq2, controller }) => {
|
|
149
|
+
* controller.abort();
|
|
150
|
+
* await mq1.close();
|
|
151
|
+
* await mq2.close();
|
|
152
|
+
* },
|
|
153
|
+
* )
|
|
154
|
+
* );
|
|
155
|
+
* ```
|
|
156
|
+
*
|
|
157
|
+
* @param getMessageQueue A factory function that creates a new message queue
|
|
158
|
+
* instance. It should return a new instance each time
|
|
159
|
+
* to ensure test isolation, but both instances should
|
|
160
|
+
* share the same underlying storage/channel.
|
|
161
|
+
* @param onFinally A cleanup function called after all tests complete.
|
|
162
|
+
* It receives both message queue instances and the abort
|
|
163
|
+
* controller used for the listeners.
|
|
164
|
+
* @returns A promise that resolves when all tests pass.
|
|
165
|
+
*/
|
|
166
|
+
declare function testMessageQueue<MQ extends MessageQueue>(getMessageQueue: () => MQ | Promise<MQ>, onFinally: ({
|
|
167
|
+
mq1,
|
|
168
|
+
mq2,
|
|
169
|
+
controller
|
|
170
|
+
}: {
|
|
171
|
+
mq1: MQ;
|
|
172
|
+
mq2: MQ;
|
|
173
|
+
controller: AbortController;
|
|
174
|
+
}) => Promise<void> | void): Promise<void>;
|
|
175
|
+
declare function waitFor(predicate: () => boolean, timeoutMs: number): Promise<void>;
|
|
176
|
+
declare const getRandomKey: (prefix: string) => string;
|
|
177
|
+
//#endregion
|
|
178
|
+
export { createContext, createFederation, createInboxContext, createRequestContext, getRandomKey, testMessageQueue, waitFor };
|
package/dist/mod.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
+
|
|
2
|
+
import { Temporal } from "@js-temporal/polyfill";
|
|
3
|
+
|
|
4
|
+
import { CryptographicKey, Multikey, lookupObject, traverseCollection } from "@fedify/vocab";
|
|
1
5
|
import { RouterError } from "@fedify/fedify/federation";
|
|
2
|
-
import {
|
|
6
|
+
import { delay } from "es-toolkit";
|
|
7
|
+
import { deepStrictEqual, ok } from "node:assert/strict";
|
|
3
8
|
|
|
4
9
|
//#region src/docloader.ts
|
|
5
10
|
const mockDocumentLoader = async (url) => ({
|
|
@@ -182,6 +187,7 @@ var MockFederation = class {
|
|
|
182
187
|
nodeInfoDispatcher;
|
|
183
188
|
webFingerDispatcher;
|
|
184
189
|
actorDispatchers = /* @__PURE__ */ new Map();
|
|
190
|
+
actorKeyPairsDispatcher;
|
|
185
191
|
actorPath;
|
|
186
192
|
inboxPath;
|
|
187
193
|
outboxPath;
|
|
@@ -219,7 +225,10 @@ var MockFederation = class {
|
|
|
219
225
|
this.actorDispatchers.set(path, dispatcher);
|
|
220
226
|
this.actorPath = path;
|
|
221
227
|
return {
|
|
222
|
-
setKeyPairsDispatcher: () =>
|
|
228
|
+
setKeyPairsDispatcher: (keyPairsDispatcher) => {
|
|
229
|
+
this.actorKeyPairsDispatcher = keyPairsDispatcher;
|
|
230
|
+
return this;
|
|
231
|
+
},
|
|
223
232
|
mapHandle: () => this,
|
|
224
233
|
mapAlias: () => this,
|
|
225
234
|
authorize: () => this
|
|
@@ -655,8 +664,25 @@ var MockContext = class MockContext {
|
|
|
655
664
|
}
|
|
656
665
|
return null;
|
|
657
666
|
}
|
|
658
|
-
getActorKeyPairs(
|
|
659
|
-
|
|
667
|
+
async getActorKeyPairs(identifier) {
|
|
668
|
+
if (this.federation instanceof MockFederation && this.federation.actorKeyPairsDispatcher) {
|
|
669
|
+
const keyPairs = await this.federation.actorKeyPairsDispatcher(this, identifier);
|
|
670
|
+
const owner = this.getActorUri(identifier);
|
|
671
|
+
return keyPairs.map((kp) => ({
|
|
672
|
+
...kp,
|
|
673
|
+
cryptographicKey: new CryptographicKey({
|
|
674
|
+
id: kp.keyId,
|
|
675
|
+
owner,
|
|
676
|
+
publicKey: kp.publicKey
|
|
677
|
+
}),
|
|
678
|
+
multikey: new Multikey({
|
|
679
|
+
id: kp.keyId,
|
|
680
|
+
controller: owner,
|
|
681
|
+
publicKey: kp.publicKey
|
|
682
|
+
})
|
|
683
|
+
}));
|
|
684
|
+
}
|
|
685
|
+
return [];
|
|
660
686
|
}
|
|
661
687
|
getDocumentLoader(params) {
|
|
662
688
|
if ("keyId" in params) return this.documentLoader;
|
|
@@ -715,4 +741,107 @@ var MockContext = class MockContext {
|
|
|
715
741
|
};
|
|
716
742
|
|
|
717
743
|
//#endregion
|
|
718
|
-
|
|
744
|
+
//#region src/mq-tester.ts
|
|
745
|
+
/**
|
|
746
|
+
* Tests a {@link MessageQueue} implementation with a standard set of tests.
|
|
747
|
+
*
|
|
748
|
+
* This function runs tests for:
|
|
749
|
+
* - `enqueue()`: Basic message enqueueing
|
|
750
|
+
* - `enqueue()` with delay: Delayed message enqueueing
|
|
751
|
+
* - `enqueueMany()`: Bulk message enqueueing
|
|
752
|
+
* - `enqueueMany()` with delay: Delayed bulk message enqueueing
|
|
753
|
+
* - Multiple listeners: Ensures messages are processed by only one listener
|
|
754
|
+
*
|
|
755
|
+
* @example
|
|
756
|
+
* ```typescript ignore
|
|
757
|
+
* import { test } from "@fedify/fixture";
|
|
758
|
+
* import { testMessageQueue } from "@fedify/testing";
|
|
759
|
+
* import { MyMessageQueue } from "./my-mq.ts";
|
|
760
|
+
*
|
|
761
|
+
* test("MyMessageQueue", () =>
|
|
762
|
+
* testMessageQueue(
|
|
763
|
+
* () => new MyMessageQueue(),
|
|
764
|
+
* async ({ mq1, mq2, controller }) => {
|
|
765
|
+
* controller.abort();
|
|
766
|
+
* await mq1.close();
|
|
767
|
+
* await mq2.close();
|
|
768
|
+
* },
|
|
769
|
+
* )
|
|
770
|
+
* );
|
|
771
|
+
* ```
|
|
772
|
+
*
|
|
773
|
+
* @param getMessageQueue A factory function that creates a new message queue
|
|
774
|
+
* instance. It should return a new instance each time
|
|
775
|
+
* to ensure test isolation, but both instances should
|
|
776
|
+
* share the same underlying storage/channel.
|
|
777
|
+
* @param onFinally A cleanup function called after all tests complete.
|
|
778
|
+
* It receives both message queue instances and the abort
|
|
779
|
+
* controller used for the listeners.
|
|
780
|
+
* @returns A promise that resolves when all tests pass.
|
|
781
|
+
*/
|
|
782
|
+
async function testMessageQueue(getMessageQueue, onFinally) {
|
|
783
|
+
const mq1 = await getMessageQueue();
|
|
784
|
+
const mq2 = await getMessageQueue();
|
|
785
|
+
const controller = new AbortController();
|
|
786
|
+
try {
|
|
787
|
+
const messages = [];
|
|
788
|
+
const listening1 = mq1.listen((message) => {
|
|
789
|
+
messages.push(message);
|
|
790
|
+
}, { signal: controller.signal });
|
|
791
|
+
const listening2 = mq2.listen((message) => {
|
|
792
|
+
messages.push(message);
|
|
793
|
+
}, { signal: controller.signal });
|
|
794
|
+
await mq1.enqueue("Hello, world!");
|
|
795
|
+
await waitFor(() => messages.length > 0, 15e3);
|
|
796
|
+
deepStrictEqual(messages, ["Hello, world!"]);
|
|
797
|
+
let started = Date.now();
|
|
798
|
+
await mq1.enqueue("Delayed message", { delay: Temporal.Duration.from({ seconds: 3 }) });
|
|
799
|
+
await waitFor(() => messages.length > 1, 15e3);
|
|
800
|
+
deepStrictEqual(messages, ["Hello, world!", "Delayed message"]);
|
|
801
|
+
ok(Date.now() - started >= 3e3, "Delayed message should be delivered after at least 3 seconds");
|
|
802
|
+
if (mq1.enqueueMany != null) {
|
|
803
|
+
while (messages.length > 0) messages.pop();
|
|
804
|
+
const batchMessages = [
|
|
805
|
+
"First batch message",
|
|
806
|
+
"Second batch message",
|
|
807
|
+
"Third batch message"
|
|
808
|
+
];
|
|
809
|
+
await mq1.enqueueMany(batchMessages);
|
|
810
|
+
await waitFor(() => messages.length >= batchMessages.length, 15e3);
|
|
811
|
+
deepStrictEqual(new Set(messages), new Set(batchMessages));
|
|
812
|
+
while (messages.length > 0) messages.pop();
|
|
813
|
+
started = Date.now();
|
|
814
|
+
const delayedBatchMessages = ["Delayed batch 1", "Delayed batch 2"];
|
|
815
|
+
await mq1.enqueueMany(delayedBatchMessages, { delay: Temporal.Duration.from({ seconds: 2 }) });
|
|
816
|
+
await waitFor(() => messages.length >= delayedBatchMessages.length, 15e3);
|
|
817
|
+
deepStrictEqual(new Set(messages), new Set(delayedBatchMessages));
|
|
818
|
+
ok(Date.now() - started >= 2e3, "Delayed batch messages should be delivered after at least 2 seconds");
|
|
819
|
+
}
|
|
820
|
+
while (messages.length > 0) messages.pop();
|
|
821
|
+
const bulkCount = 100;
|
|
822
|
+
for (let i = 0; i < bulkCount; i++) await mq1.enqueue(`message-${i}`);
|
|
823
|
+
await waitFor(() => messages.length >= bulkCount, 3e4);
|
|
824
|
+
const expectedMessages = new Set(Array.from({ length: bulkCount }, (_, i) => `message-${i}`));
|
|
825
|
+
deepStrictEqual(new Set(messages), expectedMessages);
|
|
826
|
+
controller.abort();
|
|
827
|
+
await listening1;
|
|
828
|
+
await listening2;
|
|
829
|
+
} finally {
|
|
830
|
+
await onFinally({
|
|
831
|
+
mq1,
|
|
832
|
+
mq2,
|
|
833
|
+
controller
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
async function waitFor(predicate, timeoutMs) {
|
|
838
|
+
const started = Date.now();
|
|
839
|
+
while (!predicate()) {
|
|
840
|
+
await delay(500);
|
|
841
|
+
if (Date.now() - started > timeoutMs) throw new Error("Timeout");
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
const getRandomKey = (prefix) => `fedify_test_${prefix}_${crypto.randomUUID()}`;
|
|
845
|
+
|
|
846
|
+
//#endregion
|
|
847
|
+
export { createContext, createFederation, createInboxContext, createRequestContext, getRandomKey, testMessageQueue, waitFor };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fedify/testing",
|
|
3
|
-
"version": "2.0.0-dev.
|
|
3
|
+
"version": "2.0.0-dev.228+5c4cd765",
|
|
4
4
|
"description": "Testing utilities for Fedify applications",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fedify",
|
|
@@ -50,14 +50,18 @@
|
|
|
50
50
|
"package.json"
|
|
51
51
|
],
|
|
52
52
|
"peerDependencies": {
|
|
53
|
-
"@fedify/fedify": "^2.0.0-dev.
|
|
53
|
+
"@fedify/fedify": "^2.0.0-dev.228+5c4cd765"
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"es-toolkit": "1.43.0"
|
|
54
57
|
},
|
|
55
58
|
"devDependencies": {
|
|
56
59
|
"@js-temporal/polyfill": "^0.5.1",
|
|
57
60
|
"@std/assert": "npm:@jsr/std__assert@^1.0.13",
|
|
58
61
|
"@std/async": "npm:@jsr/std__async@^1.0.13",
|
|
59
62
|
"tsdown": "^0.12.9",
|
|
60
|
-
"typescript": "^5.9.3"
|
|
63
|
+
"typescript": "^5.9.3",
|
|
64
|
+
"@fedify/fixture": "^2.0.0"
|
|
61
65
|
},
|
|
62
66
|
"scripts": {
|
|
63
67
|
"build": "tsdown",
|