@durable-streams/client-conformance-tests 0.1.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 +451 -0
- package/dist/adapters/typescript-adapter.d.ts +1 -0
- package/dist/adapters/typescript-adapter.js +586 -0
- package/dist/benchmark-runner-C_Yghc8f.js +1333 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +265 -0
- package/dist/index.d.ts +508 -0
- package/dist/index.js +4 -0
- package/dist/protocol-DyEvTHPF.d.ts +472 -0
- package/dist/protocol-qb83AeUH.js +120 -0
- package/dist/protocol.d.ts +2 -0
- package/dist/protocol.js +3 -0
- package/package.json +53 -0
- package/src/adapters/typescript-adapter.ts +848 -0
- package/src/benchmark-runner.ts +860 -0
- package/src/benchmark-scenarios.ts +311 -0
- package/src/cli.ts +294 -0
- package/src/index.ts +50 -0
- package/src/protocol.ts +656 -0
- package/src/runner.ts +1191 -0
- package/src/test-cases.ts +475 -0
- package/test-cases/consumer/cache-headers.yaml +150 -0
- package/test-cases/consumer/error-handling.yaml +108 -0
- package/test-cases/consumer/message-ordering.yaml +209 -0
- package/test-cases/consumer/offset-handling.yaml +209 -0
- package/test-cases/consumer/offset-resumption.yaml +197 -0
- package/test-cases/consumer/read-catchup.yaml +173 -0
- package/test-cases/consumer/read-longpoll.yaml +132 -0
- package/test-cases/consumer/read-sse.yaml +145 -0
- package/test-cases/consumer/retry-resilience.yaml +160 -0
- package/test-cases/consumer/streaming-equivalence.yaml +226 -0
- package/test-cases/lifecycle/dynamic-headers.yaml +147 -0
- package/test-cases/lifecycle/headers-params.yaml +117 -0
- package/test-cases/lifecycle/stream-lifecycle.yaml +148 -0
- package/test-cases/producer/append-data.yaml +142 -0
- package/test-cases/producer/batching.yaml +112 -0
- package/test-cases/producer/create-stream.yaml +87 -0
- package/test-cases/producer/error-handling.yaml +90 -0
- package/test-cases/producer/sequence-ordering.yaml +148 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
id: consumer-errors
|
|
2
|
+
name: Consumer Error Handling
|
|
3
|
+
description: Tests for error handling in consumer operations
|
|
4
|
+
category: consumer
|
|
5
|
+
tags:
|
|
6
|
+
- core
|
|
7
|
+
- errors
|
|
8
|
+
- read
|
|
9
|
+
|
|
10
|
+
tests:
|
|
11
|
+
- id: read-nonexistent-stream
|
|
12
|
+
name: Read non-existent stream
|
|
13
|
+
description: Client should handle 404 when reading non-existent stream
|
|
14
|
+
operations:
|
|
15
|
+
- action: read
|
|
16
|
+
path: /nonexistent-read-stream
|
|
17
|
+
live: false
|
|
18
|
+
expect:
|
|
19
|
+
status: 404
|
|
20
|
+
errorCode: NOT_FOUND
|
|
21
|
+
|
|
22
|
+
- id: read-deleted-stream
|
|
23
|
+
name: Read deleted stream
|
|
24
|
+
description: Client should handle 404 when stream was deleted
|
|
25
|
+
setup:
|
|
26
|
+
- action: create
|
|
27
|
+
as: streamPath
|
|
28
|
+
- action: append
|
|
29
|
+
path: ${streamPath}
|
|
30
|
+
data: "before-delete"
|
|
31
|
+
- action: delete
|
|
32
|
+
path: ${streamPath}
|
|
33
|
+
operations:
|
|
34
|
+
- action: read
|
|
35
|
+
path: ${streamPath}
|
|
36
|
+
live: false
|
|
37
|
+
expect:
|
|
38
|
+
status: 404
|
|
39
|
+
errorCode: NOT_FOUND
|
|
40
|
+
|
|
41
|
+
- id: read-invalid-offset
|
|
42
|
+
name: Read with invalid offset
|
|
43
|
+
description: Client should handle invalid offset format
|
|
44
|
+
setup:
|
|
45
|
+
- action: create
|
|
46
|
+
as: streamPath
|
|
47
|
+
- action: append
|
|
48
|
+
path: ${streamPath}
|
|
49
|
+
data: "data"
|
|
50
|
+
operations:
|
|
51
|
+
- action: read
|
|
52
|
+
path: ${streamPath}
|
|
53
|
+
offset: "not-a-valid-offset-format"
|
|
54
|
+
live: false
|
|
55
|
+
expect:
|
|
56
|
+
status: 400
|
|
57
|
+
errorCode: INVALID_OFFSET
|
|
58
|
+
|
|
59
|
+
- id: read-future-offset
|
|
60
|
+
name: Read with future offset
|
|
61
|
+
description: Client should handle offset beyond stream end
|
|
62
|
+
setup:
|
|
63
|
+
- action: create
|
|
64
|
+
as: streamPath
|
|
65
|
+
- action: append
|
|
66
|
+
path: ${streamPath}
|
|
67
|
+
data: "small"
|
|
68
|
+
operations:
|
|
69
|
+
- action: read
|
|
70
|
+
path: ${streamPath}
|
|
71
|
+
# Use an offset that's definitely beyond the stream
|
|
72
|
+
offset: "ffffffffffffffff"
|
|
73
|
+
live: false
|
|
74
|
+
expect:
|
|
75
|
+
# Server should either return empty or 400
|
|
76
|
+
chunkCount: 0
|
|
77
|
+
|
|
78
|
+
- id: longpoll-nonexistent
|
|
79
|
+
name: Long-poll non-existent stream
|
|
80
|
+
description: Long-poll should fail on non-existent stream
|
|
81
|
+
tags:
|
|
82
|
+
- longpoll
|
|
83
|
+
requires:
|
|
84
|
+
- long-poll
|
|
85
|
+
operations:
|
|
86
|
+
- action: read
|
|
87
|
+
path: /nonexistent-longpoll-stream
|
|
88
|
+
live: long-poll
|
|
89
|
+
timeoutMs: 1000
|
|
90
|
+
expect:
|
|
91
|
+
status: 404
|
|
92
|
+
errorCode: NOT_FOUND
|
|
93
|
+
|
|
94
|
+
- id: sse-nonexistent
|
|
95
|
+
name: SSE non-existent stream
|
|
96
|
+
description: SSE should fail on non-existent stream
|
|
97
|
+
tags:
|
|
98
|
+
- sse
|
|
99
|
+
requires:
|
|
100
|
+
- sse
|
|
101
|
+
operations:
|
|
102
|
+
- action: read
|
|
103
|
+
path: /nonexistent-sse-stream
|
|
104
|
+
live: sse
|
|
105
|
+
timeoutMs: 1000
|
|
106
|
+
expect:
|
|
107
|
+
status: 404
|
|
108
|
+
errorCode: NOT_FOUND
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
id: consumer-message-ordering
|
|
2
|
+
name: Message Ordering
|
|
3
|
+
description: Tests that messages are received in strict offset order
|
|
4
|
+
category: consumer
|
|
5
|
+
tags:
|
|
6
|
+
- core
|
|
7
|
+
- ordering
|
|
8
|
+
- live
|
|
9
|
+
|
|
10
|
+
tests:
|
|
11
|
+
- id: ordering-sequential-appends
|
|
12
|
+
name: Sequential appends maintain order
|
|
13
|
+
description: Messages should be read in the same order they were appended
|
|
14
|
+
setup:
|
|
15
|
+
- action: create
|
|
16
|
+
as: streamPath
|
|
17
|
+
operations:
|
|
18
|
+
- action: append
|
|
19
|
+
path: ${streamPath}
|
|
20
|
+
data: "first"
|
|
21
|
+
- action: append
|
|
22
|
+
path: ${streamPath}
|
|
23
|
+
data: "second"
|
|
24
|
+
- action: append
|
|
25
|
+
path: ${streamPath}
|
|
26
|
+
data: "third"
|
|
27
|
+
- action: append
|
|
28
|
+
path: ${streamPath}
|
|
29
|
+
data: "fourth"
|
|
30
|
+
- action: append
|
|
31
|
+
path: ${streamPath}
|
|
32
|
+
data: "fifth"
|
|
33
|
+
- action: read
|
|
34
|
+
path: ${streamPath}
|
|
35
|
+
expect:
|
|
36
|
+
data: "firstsecondthirdfourthfifth"
|
|
37
|
+
|
|
38
|
+
- id: ordering-longpoll-preserves-order
|
|
39
|
+
name: Long-poll preserves message order
|
|
40
|
+
description: Messages received via long-poll should be in offset order
|
|
41
|
+
setup:
|
|
42
|
+
- action: create
|
|
43
|
+
as: streamPath
|
|
44
|
+
- action: append
|
|
45
|
+
path: ${streamPath}
|
|
46
|
+
data: "a"
|
|
47
|
+
- action: append
|
|
48
|
+
path: ${streamPath}
|
|
49
|
+
data: "b"
|
|
50
|
+
- action: append
|
|
51
|
+
path: ${streamPath}
|
|
52
|
+
data: "c"
|
|
53
|
+
operations:
|
|
54
|
+
- action: read
|
|
55
|
+
path: ${streamPath}
|
|
56
|
+
live: long-poll
|
|
57
|
+
waitForUpToDate: true
|
|
58
|
+
expect:
|
|
59
|
+
data: "abc"
|
|
60
|
+
|
|
61
|
+
- id: ordering-sse-preserves-order
|
|
62
|
+
name: SSE preserves message order
|
|
63
|
+
description: Messages received via SSE should be in offset order
|
|
64
|
+
requires:
|
|
65
|
+
- sse
|
|
66
|
+
setup:
|
|
67
|
+
- action: create
|
|
68
|
+
as: streamPath
|
|
69
|
+
- action: append
|
|
70
|
+
path: ${streamPath}
|
|
71
|
+
data: "x"
|
|
72
|
+
- action: append
|
|
73
|
+
path: ${streamPath}
|
|
74
|
+
data: "y"
|
|
75
|
+
- action: append
|
|
76
|
+
path: ${streamPath}
|
|
77
|
+
data: "z"
|
|
78
|
+
operations:
|
|
79
|
+
- action: read
|
|
80
|
+
path: ${streamPath}
|
|
81
|
+
live: sse
|
|
82
|
+
waitForUpToDate: true
|
|
83
|
+
expect:
|
|
84
|
+
data: "xyz"
|
|
85
|
+
|
|
86
|
+
- id: ordering-offset-monotonic
|
|
87
|
+
name: Offsets are monotonically increasing
|
|
88
|
+
description: Each message's offset should be greater than the previous
|
|
89
|
+
setup:
|
|
90
|
+
- action: create
|
|
91
|
+
as: streamPath
|
|
92
|
+
- action: append
|
|
93
|
+
path: ${streamPath}
|
|
94
|
+
data: "1"
|
|
95
|
+
expect:
|
|
96
|
+
storeOffsetAs: offset1
|
|
97
|
+
- action: append
|
|
98
|
+
path: ${streamPath}
|
|
99
|
+
data: "2"
|
|
100
|
+
expect:
|
|
101
|
+
storeOffsetAs: offset2
|
|
102
|
+
- action: append
|
|
103
|
+
path: ${streamPath}
|
|
104
|
+
data: "3"
|
|
105
|
+
expect:
|
|
106
|
+
storeOffsetAs: offset3
|
|
107
|
+
operations:
|
|
108
|
+
# Read from offset1 should get "23"
|
|
109
|
+
- action: read
|
|
110
|
+
path: ${streamPath}
|
|
111
|
+
offset: ${offset1}
|
|
112
|
+
expect:
|
|
113
|
+
data: "23"
|
|
114
|
+
# Read from offset2 should get "3"
|
|
115
|
+
- action: read
|
|
116
|
+
path: ${streamPath}
|
|
117
|
+
offset: ${offset2}
|
|
118
|
+
expect:
|
|
119
|
+
data: "3"
|
|
120
|
+
# Read from offset3 should get nothing (at end)
|
|
121
|
+
- action: read
|
|
122
|
+
path: ${streamPath}
|
|
123
|
+
offset: ${offset3}
|
|
124
|
+
expect:
|
|
125
|
+
chunkCount: 0
|
|
126
|
+
upToDate: true
|
|
127
|
+
|
|
128
|
+
- id: ordering-live-new-data
|
|
129
|
+
name: Live mode receives new data in order
|
|
130
|
+
description: New data appended during live read should arrive in order
|
|
131
|
+
requires:
|
|
132
|
+
- long-poll
|
|
133
|
+
setup:
|
|
134
|
+
- action: create
|
|
135
|
+
as: streamPath
|
|
136
|
+
- action: append
|
|
137
|
+
path: ${streamPath}
|
|
138
|
+
data: "init"
|
|
139
|
+
expect:
|
|
140
|
+
storeOffsetAs: initialOffset
|
|
141
|
+
operations:
|
|
142
|
+
# Start long-poll in background
|
|
143
|
+
- action: read
|
|
144
|
+
path: ${streamPath}
|
|
145
|
+
offset: ${initialOffset}
|
|
146
|
+
live: long-poll
|
|
147
|
+
timeoutMs: 5000
|
|
148
|
+
maxChunks: 3
|
|
149
|
+
background: true
|
|
150
|
+
as: liveRead
|
|
151
|
+
- action: wait
|
|
152
|
+
ms: 100
|
|
153
|
+
# Append data in order
|
|
154
|
+
- action: server-append
|
|
155
|
+
path: ${streamPath}
|
|
156
|
+
data: "A"
|
|
157
|
+
- action: server-append
|
|
158
|
+
path: ${streamPath}
|
|
159
|
+
data: "B"
|
|
160
|
+
- action: server-append
|
|
161
|
+
path: ${streamPath}
|
|
162
|
+
data: "C"
|
|
163
|
+
# Wait for live read to get the data
|
|
164
|
+
- action: await
|
|
165
|
+
ref: liveRead
|
|
166
|
+
expect:
|
|
167
|
+
dataContains: "ABC"
|
|
168
|
+
|
|
169
|
+
- id: ordering-many-messages
|
|
170
|
+
name: Order preserved with many messages
|
|
171
|
+
description: Order should be maintained even with many sequential appends
|
|
172
|
+
setup:
|
|
173
|
+
- action: create
|
|
174
|
+
as: streamPath
|
|
175
|
+
operations:
|
|
176
|
+
- action: append
|
|
177
|
+
path: ${streamPath}
|
|
178
|
+
data: "01"
|
|
179
|
+
- action: append
|
|
180
|
+
path: ${streamPath}
|
|
181
|
+
data: "02"
|
|
182
|
+
- action: append
|
|
183
|
+
path: ${streamPath}
|
|
184
|
+
data: "03"
|
|
185
|
+
- action: append
|
|
186
|
+
path: ${streamPath}
|
|
187
|
+
data: "04"
|
|
188
|
+
- action: append
|
|
189
|
+
path: ${streamPath}
|
|
190
|
+
data: "05"
|
|
191
|
+
- action: append
|
|
192
|
+
path: ${streamPath}
|
|
193
|
+
data: "06"
|
|
194
|
+
- action: append
|
|
195
|
+
path: ${streamPath}
|
|
196
|
+
data: "07"
|
|
197
|
+
- action: append
|
|
198
|
+
path: ${streamPath}
|
|
199
|
+
data: "08"
|
|
200
|
+
- action: append
|
|
201
|
+
path: ${streamPath}
|
|
202
|
+
data: "09"
|
|
203
|
+
- action: append
|
|
204
|
+
path: ${streamPath}
|
|
205
|
+
data: "10"
|
|
206
|
+
- action: read
|
|
207
|
+
path: ${streamPath}
|
|
208
|
+
expect:
|
|
209
|
+
data: "01020304050607080910"
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
id: consumer-offset-handling
|
|
2
|
+
name: Offset Handling
|
|
3
|
+
description: Tests for offset edge cases and boundary conditions
|
|
4
|
+
category: consumer
|
|
5
|
+
tags:
|
|
6
|
+
- core
|
|
7
|
+
- offset
|
|
8
|
+
- edge-cases
|
|
9
|
+
|
|
10
|
+
tests:
|
|
11
|
+
- id: offset-minus-one-start
|
|
12
|
+
name: Offset -1 reads from start
|
|
13
|
+
description: Using offset -1 should read from the beginning of the stream
|
|
14
|
+
setup:
|
|
15
|
+
- action: create
|
|
16
|
+
as: streamPath
|
|
17
|
+
- action: append
|
|
18
|
+
path: ${streamPath}
|
|
19
|
+
data: "beginning"
|
|
20
|
+
- action: append
|
|
21
|
+
path: ${streamPath}
|
|
22
|
+
data: "middle"
|
|
23
|
+
- action: append
|
|
24
|
+
path: ${streamPath}
|
|
25
|
+
data: "end"
|
|
26
|
+
operations:
|
|
27
|
+
- action: read
|
|
28
|
+
path: ${streamPath}
|
|
29
|
+
offset: "-1"
|
|
30
|
+
expect:
|
|
31
|
+
data: "beginningmiddleend"
|
|
32
|
+
|
|
33
|
+
- id: offset-empty-stream
|
|
34
|
+
name: Read from empty stream
|
|
35
|
+
description: Reading from an empty stream should return no data and up-to-date
|
|
36
|
+
setup:
|
|
37
|
+
- action: create
|
|
38
|
+
as: streamPath
|
|
39
|
+
operations:
|
|
40
|
+
- action: read
|
|
41
|
+
path: ${streamPath}
|
|
42
|
+
expect:
|
|
43
|
+
chunkCount: 0
|
|
44
|
+
upToDate: true
|
|
45
|
+
|
|
46
|
+
- id: offset-at-end
|
|
47
|
+
name: Read from end of stream
|
|
48
|
+
description: Reading from the current end offset should return no new data
|
|
49
|
+
setup:
|
|
50
|
+
- action: create
|
|
51
|
+
as: streamPath
|
|
52
|
+
- action: append
|
|
53
|
+
path: ${streamPath}
|
|
54
|
+
data: "data"
|
|
55
|
+
expect:
|
|
56
|
+
storeOffsetAs: endOffset
|
|
57
|
+
operations:
|
|
58
|
+
- action: read
|
|
59
|
+
path: ${streamPath}
|
|
60
|
+
offset: ${endOffset}
|
|
61
|
+
expect:
|
|
62
|
+
chunkCount: 0
|
|
63
|
+
upToDate: true
|
|
64
|
+
|
|
65
|
+
- id: offset-resume-partial
|
|
66
|
+
name: Resume read from middle offset
|
|
67
|
+
description: Reading from a middle offset should only return subsequent data
|
|
68
|
+
setup:
|
|
69
|
+
- action: create
|
|
70
|
+
as: streamPath
|
|
71
|
+
- action: append
|
|
72
|
+
path: ${streamPath}
|
|
73
|
+
data: "part1"
|
|
74
|
+
expect:
|
|
75
|
+
storeOffsetAs: afterPart1
|
|
76
|
+
- action: append
|
|
77
|
+
path: ${streamPath}
|
|
78
|
+
data: "part2"
|
|
79
|
+
expect:
|
|
80
|
+
storeOffsetAs: afterPart2
|
|
81
|
+
- action: append
|
|
82
|
+
path: ${streamPath}
|
|
83
|
+
data: "part3"
|
|
84
|
+
operations:
|
|
85
|
+
# From afterPart1, should get part2 and part3
|
|
86
|
+
- action: read
|
|
87
|
+
path: ${streamPath}
|
|
88
|
+
offset: ${afterPart1}
|
|
89
|
+
expect:
|
|
90
|
+
data: "part2part3"
|
|
91
|
+
# From afterPart2, should only get part3
|
|
92
|
+
- action: read
|
|
93
|
+
path: ${streamPath}
|
|
94
|
+
offset: ${afterPart2}
|
|
95
|
+
expect:
|
|
96
|
+
data: "part3"
|
|
97
|
+
|
|
98
|
+
- id: offset-stored-correctly
|
|
99
|
+
name: Stored offset can be used to resume
|
|
100
|
+
description: Offsets stored from reads should work for resumption
|
|
101
|
+
setup:
|
|
102
|
+
- action: create
|
|
103
|
+
as: streamPath
|
|
104
|
+
- action: append
|
|
105
|
+
path: ${streamPath}
|
|
106
|
+
data: "chunk1"
|
|
107
|
+
- action: append
|
|
108
|
+
path: ${streamPath}
|
|
109
|
+
data: "chunk2"
|
|
110
|
+
operations:
|
|
111
|
+
# First read stores the offset
|
|
112
|
+
- action: read
|
|
113
|
+
path: ${streamPath}
|
|
114
|
+
expect:
|
|
115
|
+
data: "chunk1chunk2"
|
|
116
|
+
storeOffsetAs: resumeOffset
|
|
117
|
+
# Append more data
|
|
118
|
+
- action: append
|
|
119
|
+
path: ${streamPath}
|
|
120
|
+
data: "chunk3"
|
|
121
|
+
# Resume from stored offset should only get new data
|
|
122
|
+
- action: read
|
|
123
|
+
path: ${streamPath}
|
|
124
|
+
offset: ${resumeOffset}
|
|
125
|
+
expect:
|
|
126
|
+
data: "chunk3"
|
|
127
|
+
|
|
128
|
+
- id: offset-longpoll-resume
|
|
129
|
+
name: Long-poll respects offset for resumption
|
|
130
|
+
description: Long-poll should only return data after the specified offset
|
|
131
|
+
setup:
|
|
132
|
+
- action: create
|
|
133
|
+
as: streamPath
|
|
134
|
+
- action: append
|
|
135
|
+
path: ${streamPath}
|
|
136
|
+
data: "old"
|
|
137
|
+
expect:
|
|
138
|
+
storeOffsetAs: oldOffset
|
|
139
|
+
- action: append
|
|
140
|
+
path: ${streamPath}
|
|
141
|
+
data: "new"
|
|
142
|
+
operations:
|
|
143
|
+
- action: read
|
|
144
|
+
path: ${streamPath}
|
|
145
|
+
offset: ${oldOffset}
|
|
146
|
+
live: long-poll
|
|
147
|
+
timeoutMs: 1000
|
|
148
|
+
expect:
|
|
149
|
+
data: "new"
|
|
150
|
+
upToDate: true
|
|
151
|
+
|
|
152
|
+
- id: offset-sse-resume
|
|
153
|
+
name: SSE respects offset for resumption
|
|
154
|
+
description: SSE should only return data after the specified offset
|
|
155
|
+
requires:
|
|
156
|
+
- sse
|
|
157
|
+
setup:
|
|
158
|
+
- action: create
|
|
159
|
+
as: streamPath
|
|
160
|
+
- action: append
|
|
161
|
+
path: ${streamPath}
|
|
162
|
+
data: "old"
|
|
163
|
+
expect:
|
|
164
|
+
storeOffsetAs: oldOffset
|
|
165
|
+
- action: append
|
|
166
|
+
path: ${streamPath}
|
|
167
|
+
data: "new"
|
|
168
|
+
operations:
|
|
169
|
+
- action: read
|
|
170
|
+
path: ${streamPath}
|
|
171
|
+
offset: ${oldOffset}
|
|
172
|
+
live: sse
|
|
173
|
+
waitForUpToDate: true
|
|
174
|
+
expect:
|
|
175
|
+
data: "new"
|
|
176
|
+
|
|
177
|
+
- id: offset-multiple-reads-same-offset
|
|
178
|
+
name: Multiple reads from same offset
|
|
179
|
+
description: Reading from the same offset multiple times should return the same data
|
|
180
|
+
setup:
|
|
181
|
+
- action: create
|
|
182
|
+
as: streamPath
|
|
183
|
+
- action: append
|
|
184
|
+
path: ${streamPath}
|
|
185
|
+
data: "consistent"
|
|
186
|
+
expect:
|
|
187
|
+
storeOffsetAs: startOffset
|
|
188
|
+
- action: append
|
|
189
|
+
path: ${streamPath}
|
|
190
|
+
data: "data"
|
|
191
|
+
operations:
|
|
192
|
+
# First read
|
|
193
|
+
- action: read
|
|
194
|
+
path: ${streamPath}
|
|
195
|
+
offset: ${startOffset}
|
|
196
|
+
expect:
|
|
197
|
+
data: "data"
|
|
198
|
+
# Second read from same offset should get same result
|
|
199
|
+
- action: read
|
|
200
|
+
path: ${streamPath}
|
|
201
|
+
offset: ${startOffset}
|
|
202
|
+
expect:
|
|
203
|
+
data: "data"
|
|
204
|
+
# Third read to confirm
|
|
205
|
+
- action: read
|
|
206
|
+
path: ${streamPath}
|
|
207
|
+
offset: ${startOffset}
|
|
208
|
+
expect:
|
|
209
|
+
data: "data"
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
id: consumer-offset
|
|
2
|
+
name: Offset Resumption
|
|
3
|
+
description: Tests for offset handling and stream resumption
|
|
4
|
+
category: consumer
|
|
5
|
+
tags:
|
|
6
|
+
- core
|
|
7
|
+
- offset
|
|
8
|
+
- resumption
|
|
9
|
+
|
|
10
|
+
tests:
|
|
11
|
+
- id: offset-monotonic
|
|
12
|
+
name: Offsets are monotonically increasing
|
|
13
|
+
description: Each read should return an offset greater than the previous
|
|
14
|
+
setup:
|
|
15
|
+
- action: create
|
|
16
|
+
as: streamPath
|
|
17
|
+
- action: append
|
|
18
|
+
path: ${streamPath}
|
|
19
|
+
data: "AAA"
|
|
20
|
+
expect:
|
|
21
|
+
storeOffsetAs: afterA
|
|
22
|
+
- action: append
|
|
23
|
+
path: ${streamPath}
|
|
24
|
+
data: "BBB"
|
|
25
|
+
expect:
|
|
26
|
+
storeOffsetAs: afterB
|
|
27
|
+
- action: append
|
|
28
|
+
path: ${streamPath}
|
|
29
|
+
data: "CCC"
|
|
30
|
+
expect:
|
|
31
|
+
storeOffsetAs: afterC
|
|
32
|
+
operations:
|
|
33
|
+
# Read from afterA should get BBB and CCC
|
|
34
|
+
- action: read
|
|
35
|
+
path: ${streamPath}
|
|
36
|
+
offset: ${afterA}
|
|
37
|
+
live: false
|
|
38
|
+
expect:
|
|
39
|
+
data: "BBBCCC"
|
|
40
|
+
storeOffsetAs: readOffset1
|
|
41
|
+
# Read from afterB should get only CCC
|
|
42
|
+
- action: read
|
|
43
|
+
path: ${streamPath}
|
|
44
|
+
offset: ${afterB}
|
|
45
|
+
live: false
|
|
46
|
+
expect:
|
|
47
|
+
data: "CCC"
|
|
48
|
+
storeOffsetAs: readOffset2
|
|
49
|
+
# Read from afterC should get nothing (up-to-date)
|
|
50
|
+
- action: read
|
|
51
|
+
path: ${streamPath}
|
|
52
|
+
offset: ${afterC}
|
|
53
|
+
live: false
|
|
54
|
+
expect:
|
|
55
|
+
upToDate: true
|
|
56
|
+
|
|
57
|
+
- id: offset-exact-resumption
|
|
58
|
+
name: Exact resumption from offset
|
|
59
|
+
description: Reading from an offset should not skip or duplicate data
|
|
60
|
+
setup:
|
|
61
|
+
- action: create
|
|
62
|
+
as: streamPath
|
|
63
|
+
- action: append
|
|
64
|
+
path: ${streamPath}
|
|
65
|
+
data: "AAAA"
|
|
66
|
+
expect:
|
|
67
|
+
storeOffsetAs: afterA
|
|
68
|
+
- action: append
|
|
69
|
+
path: ${streamPath}
|
|
70
|
+
data: "BBBB"
|
|
71
|
+
expect:
|
|
72
|
+
storeOffsetAs: afterB
|
|
73
|
+
- action: append
|
|
74
|
+
path: ${streamPath}
|
|
75
|
+
data: "CCCC"
|
|
76
|
+
operations:
|
|
77
|
+
# Read from beginning, should get all
|
|
78
|
+
- action: read
|
|
79
|
+
path: ${streamPath}
|
|
80
|
+
live: false
|
|
81
|
+
expect:
|
|
82
|
+
data: "AAAABBBBCCCC"
|
|
83
|
+
# Read from after A, should get B and C
|
|
84
|
+
- action: read
|
|
85
|
+
path: ${streamPath}
|
|
86
|
+
offset: ${afterA}
|
|
87
|
+
live: false
|
|
88
|
+
expect:
|
|
89
|
+
data: "BBBBCCCC"
|
|
90
|
+
# Read from after B, should get only C
|
|
91
|
+
- action: read
|
|
92
|
+
path: ${streamPath}
|
|
93
|
+
offset: ${afterB}
|
|
94
|
+
live: false
|
|
95
|
+
expect:
|
|
96
|
+
data: "CCCC"
|
|
97
|
+
|
|
98
|
+
- id: offset-persists-across-sessions
|
|
99
|
+
name: Offset works across client sessions
|
|
100
|
+
description: An offset obtained in one session should work in another
|
|
101
|
+
setup:
|
|
102
|
+
- action: create
|
|
103
|
+
as: streamPath
|
|
104
|
+
- action: append
|
|
105
|
+
path: ${streamPath}
|
|
106
|
+
data: "session1-data"
|
|
107
|
+
expect:
|
|
108
|
+
storeOffsetAs: savedOffset
|
|
109
|
+
- action: append
|
|
110
|
+
path: ${streamPath}
|
|
111
|
+
data: "session2-data"
|
|
112
|
+
operations:
|
|
113
|
+
# Simulate new session by just using the saved offset
|
|
114
|
+
- action: read
|
|
115
|
+
path: ${streamPath}
|
|
116
|
+
offset: ${savedOffset}
|
|
117
|
+
live: false
|
|
118
|
+
expect:
|
|
119
|
+
data: "session2-data"
|
|
120
|
+
upToDate: true
|
|
121
|
+
|
|
122
|
+
- id: offset-from-head
|
|
123
|
+
name: Offset from HEAD request
|
|
124
|
+
description: Offset from HEAD should be usable for reading
|
|
125
|
+
setup:
|
|
126
|
+
- action: create
|
|
127
|
+
as: streamPath
|
|
128
|
+
- action: append
|
|
129
|
+
path: ${streamPath}
|
|
130
|
+
data: "before-head"
|
|
131
|
+
operations:
|
|
132
|
+
- action: head
|
|
133
|
+
path: ${streamPath}
|
|
134
|
+
expect:
|
|
135
|
+
storeAs: headResult
|
|
136
|
+
- action: append
|
|
137
|
+
path: ${streamPath}
|
|
138
|
+
data: "after-head"
|
|
139
|
+
- action: read
|
|
140
|
+
path: ${streamPath}
|
|
141
|
+
offset: ${headResult.offset}
|
|
142
|
+
live: false
|
|
143
|
+
expect:
|
|
144
|
+
data: "after-head"
|
|
145
|
+
|
|
146
|
+
- id: offset-zero-start
|
|
147
|
+
name: Read from offset zero
|
|
148
|
+
description: Reading from offset 0 or empty should start from beginning
|
|
149
|
+
setup:
|
|
150
|
+
- action: create
|
|
151
|
+
as: streamPath
|
|
152
|
+
- action: append
|
|
153
|
+
path: ${streamPath}
|
|
154
|
+
data: "from-the-start"
|
|
155
|
+
operations:
|
|
156
|
+
- action: read
|
|
157
|
+
path: ${streamPath}
|
|
158
|
+
offset: "0"
|
|
159
|
+
live: false
|
|
160
|
+
expect:
|
|
161
|
+
data: "from-the-start"
|
|
162
|
+
- action: read
|
|
163
|
+
path: ${streamPath}
|
|
164
|
+
live: false
|
|
165
|
+
expect:
|
|
166
|
+
data: "from-the-start"
|
|
167
|
+
|
|
168
|
+
- id: offset-byte-exactness
|
|
169
|
+
name: Byte-exact offset resumption
|
|
170
|
+
description: Resuming from offset should not lose or duplicate any bytes
|
|
171
|
+
tags:
|
|
172
|
+
- property
|
|
173
|
+
setup:
|
|
174
|
+
- action: create
|
|
175
|
+
as: streamPath
|
|
176
|
+
- action: append
|
|
177
|
+
path: ${streamPath}
|
|
178
|
+
data: "0123456789"
|
|
179
|
+
expect:
|
|
180
|
+
storeOffsetAs: mid
|
|
181
|
+
- action: append
|
|
182
|
+
path: ${streamPath}
|
|
183
|
+
data: "abcdefghij"
|
|
184
|
+
operations:
|
|
185
|
+
# Full read should have all data
|
|
186
|
+
- action: read
|
|
187
|
+
path: ${streamPath}
|
|
188
|
+
live: false
|
|
189
|
+
expect:
|
|
190
|
+
data: "0123456789abcdefghij"
|
|
191
|
+
# Partial read from mid offset should have only second half
|
|
192
|
+
- action: read
|
|
193
|
+
path: ${streamPath}
|
|
194
|
+
offset: ${mid}
|
|
195
|
+
live: false
|
|
196
|
+
expect:
|
|
197
|
+
data: "abcdefghij"
|