@durable-streams/server-conformance-tests 0.1.8 → 0.2.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/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/{src-CfXXlBaO.cjs → src-CMFxsR_X.cjs} +129 -95
- package/dist/{src-GWuAOela.js → src-Pd7PHJOG.js} +129 -95
- package/dist/test-runner.cjs +1 -1
- package/dist/test-runner.js +1 -1
- package/package.json +2 -2
- package/src/index.ts +206 -131
package/dist/index.cjs
CHANGED
package/dist/index.js
CHANGED
|
@@ -894,6 +894,8 @@ function runConformanceTests(options) {
|
|
|
894
894
|
(0, vitest.expect)(text).toBe(``);
|
|
895
895
|
(0, vitest.expect)(response.headers.get(__durable_streams_client.STREAM_UP_TO_DATE_HEADER)).toBe(`true`);
|
|
896
896
|
(0, vitest.expect)(response.headers.get(__durable_streams_client.STREAM_OFFSET_HEADER)).toBeDefined();
|
|
897
|
+
const cacheControl = response.headers.get(`cache-control`);
|
|
898
|
+
(0, vitest.expect)(cacheControl).toContain(`no-store`);
|
|
897
899
|
});
|
|
898
900
|
(0, vitest.test)(`should return correct tail offset for offset=now`, async () => {
|
|
899
901
|
const streamPath = `/v1/stream/offset-now-tail-test-${Date.now()}`;
|
|
@@ -1014,7 +1016,7 @@ function runConformanceTests(options) {
|
|
|
1014
1016
|
const tailOffset = readResponse.headers.get(__durable_streams_client.STREAM_OFFSET_HEADER);
|
|
1015
1017
|
const { response, received } = await fetchSSE(`${getBaseUrl()}${streamPath}?offset=now&live=sse`, { untilContent: `"upToDate"` });
|
|
1016
1018
|
(0, vitest.expect)(response.status).toBe(200);
|
|
1017
|
-
const controlMatch = received.match(/event: control\s*\n\s*data:
|
|
1019
|
+
const controlMatch = received.match(/event: control\s*\n\s*data:({[^}]+})/);
|
|
1018
1020
|
(0, vitest.expect)(controlMatch).toBeDefined();
|
|
1019
1021
|
if (controlMatch && controlMatch[1]) {
|
|
1020
1022
|
const controlData = JSON.parse(controlMatch[1]);
|
|
@@ -1066,7 +1068,7 @@ function runConformanceTests(options) {
|
|
|
1066
1068
|
});
|
|
1067
1069
|
const { response, received } = await fetchSSE(`${getBaseUrl()}${streamPath}?offset=now&live=sse`, { untilContent: `"upToDate"` });
|
|
1068
1070
|
(0, vitest.expect)(response.status).toBe(200);
|
|
1069
|
-
const controlMatch = received.match(/event: control\s*\n\s*data:
|
|
1071
|
+
const controlMatch = received.match(/event: control\s*\n\s*data:({[^}]+})/);
|
|
1070
1072
|
(0, vitest.expect)(controlMatch).toBeDefined();
|
|
1071
1073
|
if (controlMatch && controlMatch[1]) {
|
|
1072
1074
|
const controlData = JSON.parse(controlMatch[1]);
|
|
@@ -1343,6 +1345,20 @@ function runConformanceTests(options) {
|
|
|
1343
1345
|
(0, vitest.expect)(cursor).not.toBeNull();
|
|
1344
1346
|
(0, vitest.expect)(/^\d+$/.test(cursor)).toBe(true);
|
|
1345
1347
|
});
|
|
1348
|
+
(0, vitest.test)(`should return immediately with Stream-Up-To-Date when data exists at offset`, async () => {
|
|
1349
|
+
const streamPath = `/v1/stream/longpoll-immediate-uptodate-test-${Date.now()}`;
|
|
1350
|
+
await fetch(`${getBaseUrl()}${streamPath}`, {
|
|
1351
|
+
method: `PUT`,
|
|
1352
|
+
headers: { "Content-Type": `text/plain` },
|
|
1353
|
+
body: `existing data`
|
|
1354
|
+
});
|
|
1355
|
+
const response = await fetch(`${getBaseUrl()}${streamPath}?offset=-1&live=long-poll`, { method: `GET` });
|
|
1356
|
+
(0, vitest.expect)(response.status).toBe(200);
|
|
1357
|
+
const text = await response.text();
|
|
1358
|
+
(0, vitest.expect)(text).toBe(`existing data`);
|
|
1359
|
+
(0, vitest.expect)(response.headers.get(__durable_streams_client.STREAM_UP_TO_DATE_HEADER)).toBe(`true`);
|
|
1360
|
+
(0, vitest.expect)(response.headers.get(__durable_streams_client.STREAM_OFFSET_HEADER)).toBeDefined();
|
|
1361
|
+
});
|
|
1346
1362
|
(0, vitest.test)(`should echo cursor and handle collision with jitter`, async () => {
|
|
1347
1363
|
const streamPath = `/v1/stream/longpoll-cursor-collision-test-${Date.now()}`;
|
|
1348
1364
|
await fetch(`${getBaseUrl()}${streamPath}`, {
|
|
@@ -1969,7 +1985,7 @@ function runConformanceTests(options) {
|
|
|
1969
1985
|
});
|
|
1970
1986
|
const { response, received } = await fetchSSE(`${getBaseUrl()}${streamPath}?offset=-1&live=sse`, { untilContent: `streamCursor` });
|
|
1971
1987
|
(0, vitest.expect)(response.status).toBe(200);
|
|
1972
|
-
const controlMatch = received.match(/event: control\s*\ndata:
|
|
1988
|
+
const controlMatch = received.match(/event: control\s*\ndata:({[^}]+})/);
|
|
1973
1989
|
(0, vitest.expect)(controlMatch).toBeDefined();
|
|
1974
1990
|
const controlData = JSON.parse(controlMatch[1]);
|
|
1975
1991
|
(0, vitest.expect)(controlData.streamCursor).toBeDefined();
|
|
@@ -1983,11 +1999,11 @@ function runConformanceTests(options) {
|
|
|
1983
1999
|
body: `test data`
|
|
1984
2000
|
});
|
|
1985
2001
|
const { received: received1 } = await fetchSSE(`${getBaseUrl()}${streamPath}?offset=-1&live=sse`, { untilContent: `streamCursor` });
|
|
1986
|
-
const controlMatch1 = received1.match(/event: control\s*\ndata:
|
|
2002
|
+
const controlMatch1 = received1.match(/event: control\s*\ndata:({[^}]+})/);
|
|
1987
2003
|
(0, vitest.expect)(controlMatch1).toBeDefined();
|
|
1988
2004
|
const cursor1 = JSON.parse(controlMatch1[1]).streamCursor;
|
|
1989
2005
|
const { received: received2 } = await fetchSSE(`${getBaseUrl()}${streamPath}?offset=-1&live=sse&cursor=${cursor1}`, { untilContent: `streamCursor` });
|
|
1990
|
-
const controlMatch2 = received2.match(/event: control\s*\ndata:
|
|
2006
|
+
const controlMatch2 = received2.match(/event: control\s*\ndata:({[^}]+})/);
|
|
1991
2007
|
(0, vitest.expect)(controlMatch2).toBeDefined();
|
|
1992
2008
|
const cursor2 = JSON.parse(controlMatch2[1]).streamCursor;
|
|
1993
2009
|
(0, vitest.expect)(parseInt(cursor2, 10)).toBeGreaterThan(parseInt(cursor1, 10));
|
|
@@ -2027,9 +2043,9 @@ function runConformanceTests(options) {
|
|
|
2027
2043
|
const { response, received } = await fetchSSE(`${getBaseUrl()}${streamPath}?offset=-1&live=sse`, { untilContent: `event: control` });
|
|
2028
2044
|
(0, vitest.expect)(response.status).toBe(200);
|
|
2029
2045
|
(0, vitest.expect)(received).toContain(`event: control`);
|
|
2030
|
-
const controlLine = received.split(`\n`).find((l) => l.startsWith(`data
|
|
2046
|
+
const controlLine = received.split(`\n`).find((l) => l.startsWith(`data:`) && l.includes(`streamNextOffset`));
|
|
2031
2047
|
(0, vitest.expect)(controlLine).toBeDefined();
|
|
2032
|
-
const controlPayload = controlLine.slice(`data
|
|
2048
|
+
const controlPayload = controlLine.slice(`data:`.length);
|
|
2033
2049
|
const controlData = JSON.parse(controlPayload);
|
|
2034
2050
|
(0, vitest.expect)(controlData[`streamNextOffset`]).toBe(httpOffset);
|
|
2035
2051
|
});
|
|
@@ -2042,9 +2058,9 @@ function runConformanceTests(options) {
|
|
|
2042
2058
|
});
|
|
2043
2059
|
const { response, received } = await fetchSSE(`${getBaseUrl()}${streamPath}?offset=-1&live=sse`, { untilContent: `"upToDate"` });
|
|
2044
2060
|
(0, vitest.expect)(response.status).toBe(200);
|
|
2045
|
-
const controlLine = received.split(`\n`).find((l) => l.startsWith(`data
|
|
2061
|
+
const controlLine = received.split(`\n`).find((l) => l.startsWith(`data:`) && l.includes(`streamNextOffset`));
|
|
2046
2062
|
(0, vitest.expect)(controlLine).toBeDefined();
|
|
2047
|
-
const controlPayload = controlLine.slice(`data
|
|
2063
|
+
const controlPayload = controlLine.slice(`data:`.length);
|
|
2048
2064
|
const controlData = JSON.parse(controlPayload);
|
|
2049
2065
|
(0, vitest.expect)(controlData.upToDate).toBe(true);
|
|
2050
2066
|
});
|
|
@@ -2072,9 +2088,9 @@ function runConformanceTests(options) {
|
|
|
2072
2088
|
const { response, received } = await fetchSSE(`${getBaseUrl()}${streamPath}?offset=-1&live=sse`, { untilContent: `event: control` });
|
|
2073
2089
|
(0, vitest.expect)(response.status).toBe(200);
|
|
2074
2090
|
(0, vitest.expect)(received).toContain(`event: data`);
|
|
2075
|
-
(0, vitest.expect)(received).toContain(`data:
|
|
2076
|
-
(0, vitest.expect)(received).toContain(`data:
|
|
2077
|
-
(0, vitest.expect)(received).toContain(`data:
|
|
2091
|
+
(0, vitest.expect)(received).toContain(`data:line1`);
|
|
2092
|
+
(0, vitest.expect)(received).toContain(`data:line2`);
|
|
2093
|
+
(0, vitest.expect)(received).toContain(`data:line3`);
|
|
2078
2094
|
});
|
|
2079
2095
|
(0, vitest.test)(`should prevent CRLF injection in payloads - embedded event boundaries become literal data`, async () => {
|
|
2080
2096
|
const streamPath = `/v1/stream/sse-crlf-injection-test-${Date.now()}`;
|
|
@@ -2171,10 +2187,10 @@ function runConformanceTests(options) {
|
|
|
2171
2187
|
});
|
|
2172
2188
|
const { response, received } = await fetchSSE(`${getBaseUrl()}${streamPath}?offset=-1&live=sse`, { untilContent: `event: control` });
|
|
2173
2189
|
(0, vitest.expect)(response.status).toBe(200);
|
|
2174
|
-
const controlLines = received.split(`\n`).filter((l) => l.startsWith(`data
|
|
2190
|
+
const controlLines = received.split(`\n`).filter((l) => l.startsWith(`data:`) && l.includes(`streamNextOffset`));
|
|
2175
2191
|
const offsets = [];
|
|
2176
2192
|
for (const line of controlLines) {
|
|
2177
|
-
const payload = line.slice(`data
|
|
2193
|
+
const payload = line.slice(`data:`.length);
|
|
2178
2194
|
const data = JSON.parse(payload);
|
|
2179
2195
|
offsets.push(data[`streamNextOffset`]);
|
|
2180
2196
|
}
|
|
@@ -2195,8 +2211,8 @@ function runConformanceTests(options) {
|
|
|
2195
2211
|
let lastOffset = null;
|
|
2196
2212
|
const { response: response1, received: received1 } = await fetchSSE(`${getBaseUrl()}${streamPath}?offset=-1&live=sse`, { untilContent: `event: control` });
|
|
2197
2213
|
(0, vitest.expect)(response1.status).toBe(200);
|
|
2198
|
-
const controlLine = received1.split(`\n`).find((l) => l.startsWith(`data
|
|
2199
|
-
const controlPayload = controlLine.slice(`data
|
|
2214
|
+
const controlLine = received1.split(`\n`).find((l) => l.startsWith(`data:`) && l.includes(`streamNextOffset`));
|
|
2215
|
+
const controlPayload = controlLine.slice(`data:`.length);
|
|
2200
2216
|
lastOffset = JSON.parse(controlPayload)[`streamNextOffset`];
|
|
2201
2217
|
(0, vitest.expect)(lastOffset).toBeDefined();
|
|
2202
2218
|
await fetch(`${getBaseUrl()}${streamPath}`, {
|
|
@@ -2260,7 +2276,7 @@ function runConformanceTests(options) {
|
|
|
2260
2276
|
url: `${getBaseUrl()}${streamPath}`,
|
|
2261
2277
|
contentType: `application/json`
|
|
2262
2278
|
});
|
|
2263
|
-
await stream.append({ message: `hello` });
|
|
2279
|
+
await stream.append(JSON.stringify({ message: `hello` }));
|
|
2264
2280
|
const response = await fetch(`${getBaseUrl()}${streamPath}`);
|
|
2265
2281
|
const data = await response.json();
|
|
2266
2282
|
(0, vitest.expect)(Array.isArray(data)).toBe(true);
|
|
@@ -2272,11 +2288,11 @@ function runConformanceTests(options) {
|
|
|
2272
2288
|
url: `${getBaseUrl()}${streamPath}`,
|
|
2273
2289
|
contentType: `application/json`
|
|
2274
2290
|
});
|
|
2275
|
-
await stream.append([
|
|
2291
|
+
await stream.append(JSON.stringify([
|
|
2276
2292
|
{ id: 1 },
|
|
2277
2293
|
{ id: 2 },
|
|
2278
2294
|
{ id: 3 }
|
|
2279
|
-
]);
|
|
2295
|
+
]));
|
|
2280
2296
|
const response = await fetch(`${getBaseUrl()}${streamPath}`);
|
|
2281
2297
|
const data = await response.json();
|
|
2282
2298
|
(0, vitest.expect)(Array.isArray(data)).toBe(true);
|
|
@@ -2292,9 +2308,9 @@ function runConformanceTests(options) {
|
|
|
2292
2308
|
url: `${getBaseUrl()}${streamPath}`,
|
|
2293
2309
|
contentType: `application/json`
|
|
2294
2310
|
});
|
|
2295
|
-
await stream.append({ event: `first` });
|
|
2296
|
-
await stream.append({ event: `second` });
|
|
2297
|
-
await stream.append({ event: `third` });
|
|
2311
|
+
await stream.append(JSON.stringify({ event: `first` }));
|
|
2312
|
+
await stream.append(JSON.stringify({ event: `second` }));
|
|
2313
|
+
await stream.append(JSON.stringify({ event: `third` }));
|
|
2298
2314
|
const response = await fetch(`${getBaseUrl()}${streamPath}`);
|
|
2299
2315
|
const data = await response.json();
|
|
2300
2316
|
(0, vitest.expect)(Array.isArray(data)).toBe(true);
|
|
@@ -2310,15 +2326,15 @@ function runConformanceTests(options) {
|
|
|
2310
2326
|
url: `${getBaseUrl()}${streamPath}`,
|
|
2311
2327
|
contentType: `application/json`
|
|
2312
2328
|
});
|
|
2313
|
-
await stream.append({ type: `single` });
|
|
2314
|
-
await stream.append([{
|
|
2329
|
+
await stream.append(JSON.stringify({ type: `single` }));
|
|
2330
|
+
await stream.append(JSON.stringify([{
|
|
2315
2331
|
type: `array`,
|
|
2316
2332
|
id: 1
|
|
2317
2333
|
}, {
|
|
2318
2334
|
type: `array`,
|
|
2319
2335
|
id: 2
|
|
2320
|
-
}]);
|
|
2321
|
-
await stream.append({ type: `single-again` });
|
|
2336
|
+
}]));
|
|
2337
|
+
await stream.append(JSON.stringify({ type: `single-again` }));
|
|
2322
2338
|
const response = await fetch(`${getBaseUrl()}${streamPath}`);
|
|
2323
2339
|
const data = await response.json();
|
|
2324
2340
|
(0, vitest.expect)(data).toEqual([
|
|
@@ -2353,16 +2369,16 @@ function runConformanceTests(options) {
|
|
|
2353
2369
|
url: `${getBaseUrl()}${streamPath}`,
|
|
2354
2370
|
contentType: `application/json`
|
|
2355
2371
|
});
|
|
2356
|
-
await stream.append(`string value`);
|
|
2357
|
-
await stream.append(42);
|
|
2358
|
-
await stream.append(true);
|
|
2359
|
-
await stream.append(null);
|
|
2360
|
-
await stream.append({ object: `value` });
|
|
2361
|
-
await stream.append([
|
|
2372
|
+
await stream.append(JSON.stringify(`string value`));
|
|
2373
|
+
await stream.append(JSON.stringify(42));
|
|
2374
|
+
await stream.append(JSON.stringify(true));
|
|
2375
|
+
await stream.append(JSON.stringify(null));
|
|
2376
|
+
await stream.append(JSON.stringify({ object: `value` }));
|
|
2377
|
+
await stream.append(JSON.stringify([
|
|
2362
2378
|
1,
|
|
2363
2379
|
2,
|
|
2364
2380
|
3
|
|
2365
|
-
]);
|
|
2381
|
+
]));
|
|
2366
2382
|
const response = await fetch(`${getBaseUrl()}${streamPath}`);
|
|
2367
2383
|
const data = await response.json();
|
|
2368
2384
|
(0, vitest.expect)(data).toEqual([
|
|
@@ -2384,14 +2400,14 @@ function runConformanceTests(options) {
|
|
|
2384
2400
|
url: `${getBaseUrl()}${streamPath}`,
|
|
2385
2401
|
contentType: `application/json`
|
|
2386
2402
|
});
|
|
2387
|
-
await stream.append({
|
|
2403
|
+
await stream.append(JSON.stringify({
|
|
2388
2404
|
user: {
|
|
2389
2405
|
id: 123,
|
|
2390
2406
|
name: `Alice`,
|
|
2391
2407
|
tags: [`admin`, `verified`]
|
|
2392
2408
|
},
|
|
2393
2409
|
timestamp: `2024-01-01T00:00:00Z`
|
|
2394
|
-
});
|
|
2410
|
+
}));
|
|
2395
2411
|
const response = await fetch(`${getBaseUrl()}${streamPath}`);
|
|
2396
2412
|
const data = await response.json();
|
|
2397
2413
|
(0, vitest.expect)(data).toEqual([{
|
|
@@ -2409,9 +2425,9 @@ function runConformanceTests(options) {
|
|
|
2409
2425
|
url: `${getBaseUrl()}${streamPath}`,
|
|
2410
2426
|
contentType: `application/json`
|
|
2411
2427
|
});
|
|
2412
|
-
await stream.append({ id: 1 });
|
|
2413
|
-
await stream.append({ id: 2 });
|
|
2414
|
-
await stream.append({ id: 3 });
|
|
2428
|
+
await stream.append(JSON.stringify({ id: 1 }));
|
|
2429
|
+
await stream.append(JSON.stringify({ id: 2 }));
|
|
2430
|
+
await stream.append(JSON.stringify({ id: 3 }));
|
|
2415
2431
|
const res = await stream.stream({ live: false });
|
|
2416
2432
|
const items = await res.json();
|
|
2417
2433
|
(0, vitest.expect)(items).toEqual([
|
|
@@ -2440,7 +2456,7 @@ function runConformanceTests(options) {
|
|
|
2440
2456
|
url: `${getBaseUrl()}${streamPath}`,
|
|
2441
2457
|
contentType: `application/json`
|
|
2442
2458
|
});
|
|
2443
|
-
await stream.append([[1, 2], [3, 4]]);
|
|
2459
|
+
await stream.append(JSON.stringify([[1, 2], [3, 4]]));
|
|
2444
2460
|
const response = await fetch(`${getBaseUrl()}${streamPath}`);
|
|
2445
2461
|
const data = await response.json();
|
|
2446
2462
|
(0, vitest.expect)(data).toEqual([[[1, 2], [3, 4]]]);
|
|
@@ -2451,11 +2467,11 @@ function runConformanceTests(options) {
|
|
|
2451
2467
|
url: `${getBaseUrl()}${streamPath}`,
|
|
2452
2468
|
contentType: `application/json`
|
|
2453
2469
|
});
|
|
2454
|
-
await stream.append([[
|
|
2470
|
+
await stream.append(JSON.stringify([[
|
|
2455
2471
|
1,
|
|
2456
2472
|
2,
|
|
2457
2473
|
3
|
|
2458
|
-
]]);
|
|
2474
|
+
]]));
|
|
2459
2475
|
const response = await fetch(`${getBaseUrl()}${streamPath}`);
|
|
2460
2476
|
const data = await response.json();
|
|
2461
2477
|
(0, vitest.expect)(data).toEqual([[[
|
|
@@ -2471,16 +2487,16 @@ function runConformanceTests(options) {
|
|
|
2471
2487
|
url: `${getBaseUrl()}${streamPath}`,
|
|
2472
2488
|
contentType: `application/json`
|
|
2473
2489
|
});
|
|
2474
|
-
await stream.append([
|
|
2490
|
+
await stream.append(JSON.stringify([
|
|
2475
2491
|
1,
|
|
2476
2492
|
2,
|
|
2477
2493
|
3
|
|
2478
|
-
]);
|
|
2479
|
-
await stream.append([
|
|
2494
|
+
]));
|
|
2495
|
+
await stream.append(JSON.stringify([
|
|
2480
2496
|
`a`,
|
|
2481
2497
|
`b`,
|
|
2482
2498
|
`c`
|
|
2483
|
-
]);
|
|
2499
|
+
]));
|
|
2484
2500
|
const response = await fetch(`${getBaseUrl()}${streamPath}`);
|
|
2485
2501
|
const data = await response.json();
|
|
2486
2502
|
(0, vitest.expect)(data).toEqual([[
|
|
@@ -2499,10 +2515,10 @@ function runConformanceTests(options) {
|
|
|
2499
2515
|
url: `${getBaseUrl()}${streamPath}`,
|
|
2500
2516
|
contentType: `application/json`
|
|
2501
2517
|
});
|
|
2502
|
-
await stream.append({ single: 1 });
|
|
2503
|
-
await stream.append([{ batch: 2 }, { batch: 3 }]);
|
|
2504
|
-
await stream.append([[`nested`, `array`]]);
|
|
2505
|
-
await stream.append(42);
|
|
2518
|
+
await stream.append(JSON.stringify({ single: 1 }));
|
|
2519
|
+
await stream.append(JSON.stringify([{ batch: 2 }, { batch: 3 }]));
|
|
2520
|
+
await stream.append(JSON.stringify([[`nested`, `array`]]));
|
|
2521
|
+
await stream.append(JSON.stringify(42));
|
|
2506
2522
|
const response = await fetch(`${getBaseUrl()}${streamPath}`);
|
|
2507
2523
|
const data = await response.json();
|
|
2508
2524
|
(0, vitest.expect)(data).toEqual([
|
|
@@ -2516,7 +2532,25 @@ function runConformanceTests(options) {
|
|
|
2516
2532
|
});
|
|
2517
2533
|
(0, vitest.describe)(`Property-Based Tests (fast-check)`, () => {
|
|
2518
2534
|
(0, vitest.describe)(`Byte-Exactness Property`, () => {
|
|
2519
|
-
|
|
2535
|
+
const NUM_CONCURRENT_READERS = 7;
|
|
2536
|
+
async function readEntireStream(streamPath) {
|
|
2537
|
+
const accumulated = [];
|
|
2538
|
+
let currentOffset = null;
|
|
2539
|
+
for (let i = 0; i < 100; i++) {
|
|
2540
|
+
const url = currentOffset ? `${getBaseUrl()}${streamPath}?offset=${encodeURIComponent(currentOffset)}` : `${getBaseUrl()}${streamPath}`;
|
|
2541
|
+
const response = await fetch(url, { method: `GET` });
|
|
2542
|
+
(0, vitest.expect)(response.status).toBe(200);
|
|
2543
|
+
const data = new Uint8Array(await response.arrayBuffer());
|
|
2544
|
+
accumulated.push(...data);
|
|
2545
|
+
const nextOffset = response.headers.get(__durable_streams_client.STREAM_OFFSET_HEADER);
|
|
2546
|
+
const upToDate = response.headers.get(__durable_streams_client.STREAM_UP_TO_DATE_HEADER);
|
|
2547
|
+
if (upToDate === `true` && data.length === 0) break;
|
|
2548
|
+
if (nextOffset === currentOffset) break;
|
|
2549
|
+
currentOffset = nextOffset;
|
|
2550
|
+
}
|
|
2551
|
+
return new Uint8Array(accumulated);
|
|
2552
|
+
}
|
|
2553
|
+
(0, vitest.test)(`concurrent readers see consistent data during writes`, async () => {
|
|
2520
2554
|
await fast_check.assert(fast_check.asyncProperty(
|
|
2521
2555
|
// Generate 1-10 chunks of arbitrary bytes (1-500 bytes each)
|
|
2522
2556
|
fast_check.array(fast_check.uint8Array({
|
|
@@ -2537,46 +2571,40 @@ function runConformanceTests(options) {
|
|
|
2537
2571
|
201,
|
|
2538
2572
|
204
|
|
2539
2573
|
]).toContain(createResponse.status);
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
const upToDate = response.headers.get(__durable_streams_client.STREAM_UP_TO_DATE_HEADER);
|
|
2568
|
-
if (upToDate === `true` && data.length === 0) break;
|
|
2569
|
-
if (nextOffset === currentOffset) break;
|
|
2570
|
-
currentOffset = nextOffset;
|
|
2574
|
+
const expected = Uint8Array.from(chunks.flatMap((c) => [...c]));
|
|
2575
|
+
let writesComplete = false;
|
|
2576
|
+
const readerPromises = Array.from({ length: NUM_CONCURRENT_READERS }, async () => {
|
|
2577
|
+
const snapshots = [];
|
|
2578
|
+
while (!writesComplete) {
|
|
2579
|
+
const response = await fetch(`${getBaseUrl()}${streamPath}`);
|
|
2580
|
+
snapshots.push(new Uint8Array(await response.arrayBuffer()));
|
|
2581
|
+
await new Promise((r) => setTimeout(r, Math.random() * 3));
|
|
2582
|
+
}
|
|
2583
|
+
return snapshots;
|
|
2584
|
+
});
|
|
2585
|
+
const writerPromise = (async () => {
|
|
2586
|
+
for (const chunk of chunks) {
|
|
2587
|
+
await new Promise((r) => setTimeout(r, Math.random() * 5));
|
|
2588
|
+
const response = await fetch(`${getBaseUrl()}${streamPath}`, {
|
|
2589
|
+
method: `POST`,
|
|
2590
|
+
headers: { "Content-Type": `application/octet-stream` },
|
|
2591
|
+
body: chunk
|
|
2592
|
+
});
|
|
2593
|
+
(0, vitest.expect)(response.status).toBe(204);
|
|
2594
|
+
}
|
|
2595
|
+
writesComplete = true;
|
|
2596
|
+
})();
|
|
2597
|
+
const [readerResults] = await Promise.all([Promise.all(readerPromises), writerPromise]);
|
|
2598
|
+
for (const snapshots of readerResults) for (const snapshot of snapshots) {
|
|
2599
|
+
(0, vitest.expect)(snapshot.length).toBeLessThanOrEqual(expected.length);
|
|
2600
|
+
for (let i = 0; i < snapshot.length; i++) (0, vitest.expect)(snapshot[i]).toBe(expected[i]);
|
|
2571
2601
|
}
|
|
2572
|
-
const
|
|
2573
|
-
(0, vitest.expect)(
|
|
2574
|
-
for (let i = 0; i < expected.length; i++) (0, vitest.expect)(result[i]).toBe(expected[i]);
|
|
2575
|
-
return true;
|
|
2602
|
+
const finalResult = await readEntireStream(streamPath);
|
|
2603
|
+
(0, vitest.expect)(finalResult).toEqual(expected);
|
|
2576
2604
|
}
|
|
2577
2605
|
), { numRuns: 20 });
|
|
2578
2606
|
});
|
|
2579
|
-
(0, vitest.test)(`single byte values cover full range (0-255)`, async () => {
|
|
2607
|
+
(0, vitest.test)(`single byte values cover full range (0-255) with concurrent readers during write`, async () => {
|
|
2580
2608
|
await fast_check.assert(fast_check.asyncProperty(
|
|
2581
2609
|
// Generate a byte value from 0-255
|
|
2582
2610
|
fast_check.integer({
|
|
@@ -2589,18 +2617,24 @@ function runConformanceTests(options) {
|
|
|
2589
2617
|
method: `PUT`,
|
|
2590
2618
|
headers: { "Content-Type": `application/octet-stream` }
|
|
2591
2619
|
});
|
|
2592
|
-
const
|
|
2593
|
-
|
|
2620
|
+
const expected = new Uint8Array([byteValue]);
|
|
2621
|
+
const readerPromises = Array.from({ length: NUM_CONCURRENT_READERS }, async () => {
|
|
2622
|
+
await new Promise((r) => setTimeout(r, Math.random() * 5));
|
|
2623
|
+
const response = await fetch(`${getBaseUrl()}${streamPath}`);
|
|
2624
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
2625
|
+
});
|
|
2626
|
+
const writerPromise = fetch(`${getBaseUrl()}${streamPath}`, {
|
|
2594
2627
|
method: `POST`,
|
|
2595
2628
|
headers: { "Content-Type": `application/octet-stream` },
|
|
2596
|
-
body:
|
|
2629
|
+
body: new Uint8Array([byteValue])
|
|
2597
2630
|
});
|
|
2598
|
-
const
|
|
2599
|
-
const
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2631
|
+
const [readerResults] = await Promise.all([Promise.all(readerPromises), writerPromise]);
|
|
2632
|
+
for (const result of readerResults) {
|
|
2633
|
+
(0, vitest.expect)(result.length).toBeLessThanOrEqual(1);
|
|
2634
|
+
if (result.length === 1) (0, vitest.expect)(result[0]).toBe(byteValue);
|
|
2635
|
+
}
|
|
2636
|
+
const finalResult = await readEntireStream(streamPath);
|
|
2637
|
+
(0, vitest.expect)(finalResult).toEqual(expected);
|
|
2604
2638
|
}
|
|
2605
2639
|
), { numRuns: 50 });
|
|
2606
2640
|
});
|