@durable-streams/client-conformance-tests 0.1.9 → 0.2.1
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/adapters/typescript-adapter.cjs +109 -33
- package/dist/adapters/typescript-adapter.js +110 -34
- package/dist/{benchmark-runner-DliEfq9k.cjs → benchmark-runner-BQiarXdy.cjs} +80 -2
- package/dist/{benchmark-runner-81waaCzs.js → benchmark-runner-IGT51RTF.js} +80 -2
- package/dist/cli.cjs +2 -2
- package/dist/cli.js +2 -2
- package/dist/index.cjs +2 -2
- package/dist/index.d.cts +91 -3
- package/dist/index.d.ts +91 -3
- package/dist/index.js +2 -2
- package/dist/{protocol-BxZTqJmO.d.cts → protocol-9WN0gRRQ.d.ts} +97 -3
- package/dist/{protocol-1p0soayz.js → protocol-BnqUAMKe.js} +1 -0
- package/dist/{protocol-JuFzdV5x.d.ts → protocol-COHkkGmU.d.cts} +97 -3
- package/dist/{protocol-IioVPNaP.cjs → protocol-sDk3deGa.cjs} +1 -0
- package/dist/protocol.cjs +1 -1
- package/dist/protocol.d.cts +2 -2
- package/dist/protocol.d.ts +2 -2
- package/dist/protocol.js +1 -1
- package/package.json +3 -3
- package/src/adapters/typescript-adapter.ts +175 -36
- package/src/protocol.ts +108 -0
- package/src/runner.ts +143 -0
- package/src/test-cases.ts +102 -0
- package/test-cases/consumer/message-ordering.yaml +1 -0
- package/test-cases/consumer/offset-handling.yaml +3 -0
- package/test-cases/consumer/read-sse-base64.yaml +663 -0
- package/test-cases/consumer/read-sse.yaml +6 -1
- package/test-cases/consumer/sse-parsing-errors.yaml +4 -0
- package/test-cases/consumer/streaming-equivalence.yaml +6 -0
- package/test-cases/lifecycle/stream-closure.yaml +759 -0
package/src/protocol.ts
CHANGED
|
@@ -41,6 +41,10 @@ export interface CreateCommand {
|
|
|
41
41
|
expiresAt?: string
|
|
42
42
|
/** Custom headers to include */
|
|
43
43
|
headers?: Record<string, string>
|
|
44
|
+
/** Create the stream in closed state */
|
|
45
|
+
closed?: boolean
|
|
46
|
+
/** Initial body data to include on creation */
|
|
47
|
+
data?: string
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
/**
|
|
@@ -112,6 +116,38 @@ export interface IdempotentAppendBatchCommand {
|
|
|
112
116
|
headers?: Record<string, string>
|
|
113
117
|
}
|
|
114
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Close a stream via IdempotentProducer (uses producer headers for idempotency).
|
|
121
|
+
*/
|
|
122
|
+
export interface IdempotentCloseCommand {
|
|
123
|
+
type: `idempotent-close`
|
|
124
|
+
path: string
|
|
125
|
+
/** Producer ID */
|
|
126
|
+
producerId: string
|
|
127
|
+
/** Producer epoch */
|
|
128
|
+
epoch: number
|
|
129
|
+
/** Optional final message to append atomically with close */
|
|
130
|
+
data?: string
|
|
131
|
+
/** Auto-claim epoch on 403 */
|
|
132
|
+
autoClaim: boolean
|
|
133
|
+
/** Custom headers to include */
|
|
134
|
+
headers?: Record<string, string>
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Detach an IdempotentProducer (stop without closing stream).
|
|
139
|
+
*/
|
|
140
|
+
export interface IdempotentDetachCommand {
|
|
141
|
+
type: `idempotent-detach`
|
|
142
|
+
path: string
|
|
143
|
+
/** Producer ID */
|
|
144
|
+
producerId: string
|
|
145
|
+
/** Producer epoch */
|
|
146
|
+
epoch: number
|
|
147
|
+
/** Custom headers to include */
|
|
148
|
+
headers?: Record<string, string>
|
|
149
|
+
}
|
|
150
|
+
|
|
115
151
|
/**
|
|
116
152
|
* Read from a stream (GET request).
|
|
117
153
|
*/
|
|
@@ -150,6 +186,35 @@ export interface DeleteCommand {
|
|
|
150
186
|
headers?: Record<string, string>
|
|
151
187
|
}
|
|
152
188
|
|
|
189
|
+
/**
|
|
190
|
+
* Close a stream (no more appends allowed).
|
|
191
|
+
*/
|
|
192
|
+
export interface CloseCommand {
|
|
193
|
+
type: `close`
|
|
194
|
+
/** Stream path */
|
|
195
|
+
path: string
|
|
196
|
+
/** Optional final message to append */
|
|
197
|
+
data?: string
|
|
198
|
+
/** Content type for the final message */
|
|
199
|
+
contentType?: string
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Close a stream via direct HTTP (bypasses client adapter).
|
|
204
|
+
* Used for testing server-side stream closure behavior.
|
|
205
|
+
*/
|
|
206
|
+
export interface ServerCloseCommand {
|
|
207
|
+
type: `server-close`
|
|
208
|
+
/** Stream path */
|
|
209
|
+
path: string
|
|
210
|
+
/** Whether stream should be closed (always true for this command) */
|
|
211
|
+
streamClosed: true
|
|
212
|
+
/** Optional body data */
|
|
213
|
+
data?: string
|
|
214
|
+
/** Content type for the body */
|
|
215
|
+
contentType?: string
|
|
216
|
+
}
|
|
217
|
+
|
|
153
218
|
/**
|
|
154
219
|
* Shutdown the client adapter gracefully.
|
|
155
220
|
*/
|
|
@@ -334,9 +399,13 @@ export type TestCommand =
|
|
|
334
399
|
| AppendCommand
|
|
335
400
|
| IdempotentAppendCommand
|
|
336
401
|
| IdempotentAppendBatchCommand
|
|
402
|
+
| IdempotentCloseCommand
|
|
403
|
+
| IdempotentDetachCommand
|
|
337
404
|
| ReadCommand
|
|
338
405
|
| HeadCommand
|
|
339
406
|
| DeleteCommand
|
|
407
|
+
| CloseCommand
|
|
408
|
+
| ServerCloseCommand
|
|
340
409
|
| ShutdownCommand
|
|
341
410
|
| SetDynamicHeaderCommand
|
|
342
411
|
| SetDynamicParamCommand
|
|
@@ -459,6 +528,26 @@ export interface IdempotentAppendBatchResult {
|
|
|
459
528
|
producerSeq?: number
|
|
460
529
|
}
|
|
461
530
|
|
|
531
|
+
/**
|
|
532
|
+
* Successful idempotent-close result.
|
|
533
|
+
*/
|
|
534
|
+
export interface IdempotentCloseResult {
|
|
535
|
+
type: `idempotent-close`
|
|
536
|
+
success: true
|
|
537
|
+
status: number
|
|
538
|
+
/** Final stream offset after close */
|
|
539
|
+
finalOffset?: string
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Successful idempotent-detach result.
|
|
544
|
+
*/
|
|
545
|
+
export interface IdempotentDetachResult {
|
|
546
|
+
type: `idempotent-detach`
|
|
547
|
+
success: true
|
|
548
|
+
status: number
|
|
549
|
+
}
|
|
550
|
+
|
|
462
551
|
/**
|
|
463
552
|
* A chunk of data read from the stream.
|
|
464
553
|
*/
|
|
@@ -484,6 +573,8 @@ export interface ReadResult {
|
|
|
484
573
|
offset?: string
|
|
485
574
|
/** Whether stream is up-to-date (caught up to head) */
|
|
486
575
|
upToDate?: boolean
|
|
576
|
+
/** Whether the stream has been permanently closed (no more appends) */
|
|
577
|
+
streamClosed?: boolean
|
|
487
578
|
/** Cursor value if provided */
|
|
488
579
|
cursor?: string
|
|
489
580
|
/** Response headers */
|
|
@@ -509,6 +600,8 @@ export interface HeadResult {
|
|
|
509
600
|
ttlSeconds?: number
|
|
510
601
|
/** Absolute expiry (ISO 8601) */
|
|
511
602
|
expiresAt?: string
|
|
603
|
+
/** Whether the stream has been permanently closed (no more appends) */
|
|
604
|
+
streamClosed?: boolean
|
|
512
605
|
headers?: Record<string, string>
|
|
513
606
|
}
|
|
514
607
|
|
|
@@ -522,6 +615,16 @@ export interface DeleteResult {
|
|
|
522
615
|
headers?: Record<string, string>
|
|
523
616
|
}
|
|
524
617
|
|
|
618
|
+
/**
|
|
619
|
+
* Successful close result.
|
|
620
|
+
*/
|
|
621
|
+
export interface CloseResult {
|
|
622
|
+
type: `close`
|
|
623
|
+
success: true
|
|
624
|
+
/** Final offset after closing (may include final message) */
|
|
625
|
+
finalOffset: string
|
|
626
|
+
}
|
|
627
|
+
|
|
525
628
|
/**
|
|
526
629
|
* Successful shutdown result.
|
|
527
630
|
*/
|
|
@@ -612,9 +715,12 @@ export type TestResult =
|
|
|
612
715
|
| AppendResult
|
|
613
716
|
| IdempotentAppendResult
|
|
614
717
|
| IdempotentAppendBatchResult
|
|
718
|
+
| IdempotentCloseResult
|
|
719
|
+
| IdempotentDetachResult
|
|
615
720
|
| ReadResult
|
|
616
721
|
| HeadResult
|
|
617
722
|
| DeleteResult
|
|
723
|
+
| CloseResult
|
|
618
724
|
| ShutdownResult
|
|
619
725
|
| SetDynamicHeaderResult
|
|
620
726
|
| SetDynamicParamResult
|
|
@@ -683,6 +789,8 @@ export const ErrorCodes = {
|
|
|
683
789
|
NOT_FOUND: `NOT_FOUND`,
|
|
684
790
|
/** Sequence number conflict (409) */
|
|
685
791
|
SEQUENCE_CONFLICT: `SEQUENCE_CONFLICT`,
|
|
792
|
+
/** Stream is closed (409 with Stream-Closed header) */
|
|
793
|
+
STREAM_CLOSED: `STREAM_CLOSED`,
|
|
686
794
|
/** Invalid offset format */
|
|
687
795
|
INVALID_OFFSET: `INVALID_OFFSET`,
|
|
688
796
|
/** Server returned unexpected status */
|
package/src/runner.ts
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
import type { Interface as ReadlineInterface } from "node:readline"
|
|
22
22
|
import type {
|
|
23
23
|
AppendResult,
|
|
24
|
+
CloseResult,
|
|
24
25
|
ErrorResult,
|
|
25
26
|
HeadResult,
|
|
26
27
|
ReadResult,
|
|
@@ -273,6 +274,8 @@ async function executeOperation(
|
|
|
273
274
|
ttlSeconds: op.ttlSeconds,
|
|
274
275
|
expiresAt: op.expiresAt,
|
|
275
276
|
headers: op.headers,
|
|
277
|
+
closed: op.closed,
|
|
278
|
+
data: op.data,
|
|
276
279
|
},
|
|
277
280
|
commandTimeout
|
|
278
281
|
)
|
|
@@ -441,6 +444,55 @@ async function executeOperation(
|
|
|
441
444
|
return { result }
|
|
442
445
|
}
|
|
443
446
|
|
|
447
|
+
case `idempotent-close`: {
|
|
448
|
+
const path = resolveVariables(op.path, variables)
|
|
449
|
+
const data = op.data ? resolveVariables(op.data, variables) : undefined
|
|
450
|
+
|
|
451
|
+
const result = await client.send(
|
|
452
|
+
{
|
|
453
|
+
type: `idempotent-close`,
|
|
454
|
+
path,
|
|
455
|
+
producerId: op.producerId,
|
|
456
|
+
epoch: op.epoch ?? 0,
|
|
457
|
+
data,
|
|
458
|
+
autoClaim: op.autoClaim ?? false,
|
|
459
|
+
headers: op.headers,
|
|
460
|
+
},
|
|
461
|
+
commandTimeout
|
|
462
|
+
)
|
|
463
|
+
|
|
464
|
+
if (verbose) {
|
|
465
|
+
console.log(
|
|
466
|
+
` idempotent-close ${path}: ${result.success ? `ok` : `failed`}`
|
|
467
|
+
)
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return { result }
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
case `idempotent-detach`: {
|
|
474
|
+
const path = resolveVariables(op.path, variables)
|
|
475
|
+
|
|
476
|
+
const result = await client.send(
|
|
477
|
+
{
|
|
478
|
+
type: `idempotent-detach`,
|
|
479
|
+
path,
|
|
480
|
+
producerId: op.producerId,
|
|
481
|
+
epoch: op.epoch ?? 0,
|
|
482
|
+
headers: op.headers,
|
|
483
|
+
},
|
|
484
|
+
commandTimeout
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
if (verbose) {
|
|
488
|
+
console.log(
|
|
489
|
+
` idempotent-detach ${path}: ${result.success ? `ok` : `failed`}`
|
|
490
|
+
)
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
return { result }
|
|
494
|
+
}
|
|
495
|
+
|
|
444
496
|
case `read`: {
|
|
445
497
|
const path = resolveVariables(op.path, variables)
|
|
446
498
|
const offset = op.offset
|
|
@@ -546,6 +598,72 @@ async function executeOperation(
|
|
|
546
598
|
return { result }
|
|
547
599
|
}
|
|
548
600
|
|
|
601
|
+
case `close`: {
|
|
602
|
+
const path = resolveVariables(op.path, variables)
|
|
603
|
+
|
|
604
|
+
const result = await client.send(
|
|
605
|
+
{
|
|
606
|
+
type: `close`,
|
|
607
|
+
path,
|
|
608
|
+
data: op.data,
|
|
609
|
+
contentType: op.contentType,
|
|
610
|
+
},
|
|
611
|
+
commandTimeout
|
|
612
|
+
)
|
|
613
|
+
|
|
614
|
+
if (verbose) {
|
|
615
|
+
console.log(` close ${path}: ${result.success ? `ok` : `failed`}`)
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
return { result }
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
case `server-close`: {
|
|
622
|
+
// Direct HTTP POST to server with Stream-Closed: true header
|
|
623
|
+
// Used for testing server-side stream closure behavior
|
|
624
|
+
const path = resolveVariables(op.path, variables)
|
|
625
|
+
|
|
626
|
+
try {
|
|
627
|
+
// Build headers including Stream-Closed
|
|
628
|
+
const headers: Record<string, string> = {
|
|
629
|
+
"Stream-Closed": `true`,
|
|
630
|
+
...op.headers,
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// Set content-type if body is provided
|
|
634
|
+
if (op.data && op.contentType) {
|
|
635
|
+
headers[`content-type`] = op.contentType
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
const response = await fetch(`${ctx.serverUrl}${path}`, {
|
|
639
|
+
method: `POST`,
|
|
640
|
+
body: op.data,
|
|
641
|
+
headers,
|
|
642
|
+
})
|
|
643
|
+
|
|
644
|
+
const status = response.status
|
|
645
|
+
const finalOffset =
|
|
646
|
+
response.headers.get(`Stream-Next-Offset`) ?? undefined
|
|
647
|
+
|
|
648
|
+
if (verbose) {
|
|
649
|
+
console.log(` server-close ${path}: status=${status}`)
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// Build result for expectation verification
|
|
653
|
+
const result: TestResult = {
|
|
654
|
+
type: `close`,
|
|
655
|
+
success: true,
|
|
656
|
+
finalOffset: finalOffset ?? ``,
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
return { result }
|
|
660
|
+
} catch (err) {
|
|
661
|
+
return {
|
|
662
|
+
error: `Server close failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
549
667
|
case `wait`: {
|
|
550
668
|
await new Promise((resolve) => setTimeout(resolve, op.ms))
|
|
551
669
|
return {}
|
|
@@ -884,6 +1002,10 @@ function isHeadResult(result: TestResult): result is HeadResult {
|
|
|
884
1002
|
return result.type === `head` && result.success
|
|
885
1003
|
}
|
|
886
1004
|
|
|
1005
|
+
function isCloseResult(result: TestResult): result is CloseResult {
|
|
1006
|
+
return result.type === `close` && result.success
|
|
1007
|
+
}
|
|
1008
|
+
|
|
887
1009
|
function isErrorResult(result: TestResult): result is ErrorResult {
|
|
888
1010
|
return result.type === `error` && !result.success
|
|
889
1011
|
}
|
|
@@ -976,6 +1098,27 @@ function validateExpectation(
|
|
|
976
1098
|
}
|
|
977
1099
|
}
|
|
978
1100
|
|
|
1101
|
+
// Check streamClosed (for read results)
|
|
1102
|
+
if (expect.streamClosed !== undefined && isReadResult(result)) {
|
|
1103
|
+
if (result.streamClosed !== expect.streamClosed) {
|
|
1104
|
+
return `Expected streamClosed=${expect.streamClosed}, got ${result.streamClosed}`
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
// Check streamClosed (for head results)
|
|
1109
|
+
if (expect.streamClosed !== undefined && isHeadResult(result)) {
|
|
1110
|
+
if (result.streamClosed !== expect.streamClosed) {
|
|
1111
|
+
return `Expected streamClosed=${expect.streamClosed}, got ${result.streamClosed}`
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
// Check finalOffset (for close results)
|
|
1116
|
+
if (expect.finalOffset !== undefined && isCloseResult(result)) {
|
|
1117
|
+
if (result.finalOffset !== expect.finalOffset) {
|
|
1118
|
+
return `Expected finalOffset=${expect.finalOffset}, got ${result.finalOffset}`
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
|
|
979
1122
|
// Check chunkCount
|
|
980
1123
|
if (expect.chunkCount !== undefined && isReadResult(result)) {
|
|
981
1124
|
if (result.chunks.length !== expect.chunkCount) {
|
package/src/test-cases.ts
CHANGED
|
@@ -92,6 +92,10 @@ export interface CreateOperation {
|
|
|
92
92
|
expiresAt?: string
|
|
93
93
|
/** Custom headers */
|
|
94
94
|
headers?: Record<string, string>
|
|
95
|
+
/** Create stream in closed state */
|
|
96
|
+
closed?: boolean
|
|
97
|
+
/** Initial body data to include on creation */
|
|
98
|
+
data?: string
|
|
95
99
|
/** Expected result */
|
|
96
100
|
expect?: CreateExpectation
|
|
97
101
|
}
|
|
@@ -206,6 +210,55 @@ export interface IdempotentAppendBatchExpectation extends BaseExpectation {
|
|
|
206
210
|
allSucceed?: boolean
|
|
207
211
|
}
|
|
208
212
|
|
|
213
|
+
/**
|
|
214
|
+
* Close a stream via IdempotentProducer (uses producer headers for idempotency).
|
|
215
|
+
*/
|
|
216
|
+
export interface IdempotentCloseOperation {
|
|
217
|
+
action: `idempotent-close`
|
|
218
|
+
path: string
|
|
219
|
+
/** Producer ID */
|
|
220
|
+
producerId: string
|
|
221
|
+
/** Producer epoch */
|
|
222
|
+
epoch?: number
|
|
223
|
+
/** Optional final message to append atomically with close */
|
|
224
|
+
data?: string
|
|
225
|
+
/** Auto-claim epoch on 403 */
|
|
226
|
+
autoClaim?: boolean
|
|
227
|
+
headers?: Record<string, string>
|
|
228
|
+
expect?: IdempotentCloseExpectation
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Expectation for idempotent-close operation.
|
|
233
|
+
*/
|
|
234
|
+
export interface IdempotentCloseExpectation extends BaseExpectation {
|
|
235
|
+
/** Store the final offset */
|
|
236
|
+
storeOffsetAs?: string
|
|
237
|
+
/** Expected finalOffset */
|
|
238
|
+
finalOffset?: string
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Detach an IdempotentProducer (stop without closing stream).
|
|
243
|
+
*/
|
|
244
|
+
export interface IdempotentDetachOperation {
|
|
245
|
+
action: `idempotent-detach`
|
|
246
|
+
path: string
|
|
247
|
+
/** Producer ID */
|
|
248
|
+
producerId: string
|
|
249
|
+
/** Producer epoch */
|
|
250
|
+
epoch?: number
|
|
251
|
+
headers?: Record<string, string>
|
|
252
|
+
expect?: IdempotentDetachExpectation
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Expectation for idempotent-detach operation.
|
|
257
|
+
*/
|
|
258
|
+
export interface IdempotentDetachExpectation extends BaseExpectation {
|
|
259
|
+
// No specific expectations beyond status
|
|
260
|
+
}
|
|
261
|
+
|
|
209
262
|
/**
|
|
210
263
|
* Read from a stream.
|
|
211
264
|
*/
|
|
@@ -250,6 +303,35 @@ export interface DeleteOperation {
|
|
|
250
303
|
expect?: DeleteExpectation
|
|
251
304
|
}
|
|
252
305
|
|
|
306
|
+
/**
|
|
307
|
+
* Close a stream (no more appends allowed).
|
|
308
|
+
*/
|
|
309
|
+
export interface CloseOperation {
|
|
310
|
+
action: `close`
|
|
311
|
+
path: string
|
|
312
|
+
/** Optional final message to append */
|
|
313
|
+
data?: string
|
|
314
|
+
/** Content type for the final message */
|
|
315
|
+
contentType?: string
|
|
316
|
+
headers?: Record<string, string>
|
|
317
|
+
expect?: CloseExpectation
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Close a stream via direct HTTP (bypasses client adapter).
|
|
322
|
+
* Used for testing server-side stream closure behavior.
|
|
323
|
+
*/
|
|
324
|
+
export interface ServerCloseOperation {
|
|
325
|
+
action: `server-close`
|
|
326
|
+
path: string
|
|
327
|
+
/** Optional body data */
|
|
328
|
+
data?: string
|
|
329
|
+
/** Content type for the body */
|
|
330
|
+
contentType?: string
|
|
331
|
+
headers?: Record<string, string>
|
|
332
|
+
expect?: ServerCloseExpectation
|
|
333
|
+
}
|
|
334
|
+
|
|
253
335
|
/**
|
|
254
336
|
* Wait for a duration (for timing-sensitive tests).
|
|
255
337
|
*/
|
|
@@ -467,9 +549,13 @@ export type TestOperation =
|
|
|
467
549
|
| AppendBatchOperation
|
|
468
550
|
| IdempotentAppendOperation
|
|
469
551
|
| IdempotentAppendBatchOperation
|
|
552
|
+
| IdempotentCloseOperation
|
|
553
|
+
| IdempotentDetachOperation
|
|
470
554
|
| ReadOperation
|
|
471
555
|
| HeadOperation
|
|
472
556
|
| DeleteOperation
|
|
557
|
+
| CloseOperation
|
|
558
|
+
| ServerCloseOperation
|
|
473
559
|
| WaitOperation
|
|
474
560
|
| SetOperation
|
|
475
561
|
| AssertOperation
|
|
@@ -556,6 +642,8 @@ export interface ReadExpectation extends BaseExpectation {
|
|
|
556
642
|
maxChunks?: number
|
|
557
643
|
/** Should be up-to-date after read */
|
|
558
644
|
upToDate?: boolean
|
|
645
|
+
/** Whether the stream has been permanently closed */
|
|
646
|
+
streamClosed?: boolean
|
|
559
647
|
/** Store final offset */
|
|
560
648
|
storeOffsetAs?: string
|
|
561
649
|
/** Store all data concatenated */
|
|
@@ -572,12 +660,26 @@ export interface HeadExpectation extends BaseExpectation {
|
|
|
572
660
|
contentType?: string
|
|
573
661
|
/** Should have an offset */
|
|
574
662
|
hasOffset?: boolean
|
|
663
|
+
/** Whether the stream has been permanently closed */
|
|
664
|
+
streamClosed?: boolean
|
|
575
665
|
}
|
|
576
666
|
|
|
577
667
|
export interface DeleteExpectation extends BaseExpectation {
|
|
578
668
|
status?: 200 | 204 | 404 | number
|
|
579
669
|
}
|
|
580
670
|
|
|
671
|
+
export interface CloseExpectation extends BaseExpectation {
|
|
672
|
+
/** Expected final offset after closing */
|
|
673
|
+
finalOffset?: string
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
export interface ServerCloseExpectation {
|
|
677
|
+
/** Expected HTTP status code */
|
|
678
|
+
status?: number
|
|
679
|
+
/** Expected final offset after closing */
|
|
680
|
+
finalOffset?: string
|
|
681
|
+
}
|
|
682
|
+
|
|
581
683
|
/**
|
|
582
684
|
* Load all test suites from a directory.
|
|
583
685
|
*/
|
|
@@ -121,6 +121,7 @@ tests:
|
|
|
121
121
|
setup:
|
|
122
122
|
- action: create
|
|
123
123
|
as: streamPath
|
|
124
|
+
contentType: text/plain
|
|
124
125
|
operations:
|
|
125
126
|
# SSE with offset=now on empty stream
|
|
126
127
|
- action: read
|
|
@@ -217,6 +218,7 @@ tests:
|
|
|
217
218
|
setup:
|
|
218
219
|
- action: create
|
|
219
220
|
as: streamPath
|
|
221
|
+
contentType: text/plain
|
|
220
222
|
- action: append
|
|
221
223
|
path: ${streamPath}
|
|
222
224
|
data: "historicalsse"
|
|
@@ -366,6 +368,7 @@ tests:
|
|
|
366
368
|
setup:
|
|
367
369
|
- action: create
|
|
368
370
|
as: streamPath
|
|
371
|
+
contentType: text/plain
|
|
369
372
|
- action: append
|
|
370
373
|
path: ${streamPath}
|
|
371
374
|
data: "old"
|